PS3手柄控制EV3

在ev3dev官方网站提供的爱好者Projects中,开心的发现了其中有对PS3手柄的支持。

于是根据project顺藤摸瓜,研究了一下PS3手柄的调用方式,如下:

一、连接手柄

打开EV3里面的蓝牙功能,然后将PS3手柄开启,首次使用配对需要用USB线连接EV3和PS3手柄。此时EV3屏幕会显示找到PS3手柄蓝牙设备,是否配对,Accept后连接成功。

二、调用方式

利用evdev模块的InputDevice()函数可以调用PS3手柄。这个evdev.InputDevice()函数可以调用所有EV3支持的输入设备,只是需要传入设备文件名作为函数参数。

如何知道PS3手柄的设备文件名?evdev模块的list_devices()函数可以列出所有连接的设备,而不同设备的名称并不相同,注意这里的设备名称与设备文件名不同。熟悉Linux的话,可以知道设备文件名是设备映射到系统中/dev目录下的某个系统文件。而设备名称则是给人看的设备描述,例如:PLAYSTATION(R)3 Controller就是PS3手柄的设备名称。这样就可以通过设备名称反向获得设备文件名,从而实现InputDevice()的参数传入。

写了这么多有点绕,不如Code直接一些:

import evdev

# 获取所有已连接的输入设备
devices = [evdev.InputDevice(fn) for fn in evdev.list_devices()]

# 找出PS3手柄对应的输入设备文件名
for device in devices:
  if device.name == 'PLAYSTATION(R)3 Controller':
  ps3dev = device.fn

# 连接PS3手柄
gamepad = evdev.InputDevice(ps3dev)

三、获取手柄动作

PS3手柄有2个摇杆,17个按键(摇杆也可以按),在使用上分为摇杆模式和按键模式,当然这两种模式可以共存,但ev3dev在调取摇杆和按键的读值时会有一定区别。

在获取PS3手柄动作上,共定义了3个变量,分别是type, code, value。

  • type:手柄所使用的模式:摇杆or按键
  • code:摇杆or按键代码
  • value:摇杆or按键读值

来张图可以很清晰看

PS3手柄对应ev3dev参数

PS3手柄对应ev3dev参数

在value上按键和摇杆还是有区别的:按键只是按下(1)和抬起(0)两个动作;摇杆更复杂些,需要精确读取X和Y方向上的位置,精度为256,读值范围:0-255。

更新:还是将Anton Vanhoucke做好的图贴出来吧,更好看一些……我也test好久才画出来的好不: (

PS3 event codes

PS3 event codes

获取参数的方法,是利用read_loop()函数持续读取所有按键信息,之后可通过type、code、value读取。还是用Code解释:

for event in gamepad.read_loop():
  print event.type, event.code, event.value

四、EV3机器人

设计一个EV3小车,用PS3手柄控制他的移动,EV3搭建采用Lauren Valk的EXPLOR3R机器人,搭建图在这里

explor3r

explor3r

五、程序设计

#!/usr/bin/python

import evdev
import ev3dev.auto as ev3
import threading
import time

#Helpers

#def clamp(n, (minn, maxn)):
def clamp(n, *mn):
  """
  Given a number and a range, return the number, or the extreme it is closest to.
  :param n: number
  :param *mn: mn[0] minn, mn[1] maxn
  :return: number
  """
  return max(min(mn[1], n), mn[0])


def scale(val, src, dst):
  """
  Scale the given value from the scale of src to the scale of dst.
  val: float or int
  src: tuple
  dst: tuple
  example: print scale(99, (0.0, 99.0), (-1.0, +1.0))
  """
  return (float(val - src[0]) / (src[1] - src[0])) * (dst[1] - dst[0]) + dst[0]

# 将0~255范围的值 按比例 转换为-100~100范围的值
def scalestick(value):
  return scale(value,(0,255),(-100,100))

# 确保转换后的值 始终在-100~100范围内
def dc_clamp(value):
  duty_cycle_range = (-100, 100)
  return clamp(value, *duty_cycle_range)

print "Finding ps3 controller..."
devices = [evdev.InputDevice(fn) for fn in evdev.list_devices()]
for device in devices:
  if device.name == 'PLAYSTATION(R)3 Controller':
  ps3dev = device.fn

gamepad = evdev.InputDevice(ps3dev)


turn_speed = 0
fwd_speed = 0
running = True

class MotorThread(threading.Thread):
  def __init__(self):
    # 两个马达分别接在A和D口
    self.right_motor = ev3.LargeMotor(ev3.OUTPUT_A)
    self.left_motor = ev3.LargeMotor(ev3.OUTPUT_D)
    threading.Thread.__init__(self)

  def run(self):
    print "Engines running!"
    while running:
      # 2个马达分别控制2个轮子,所以跟1个马达控制转向、1个马达控制前进后退不同
      # 在前进和后退时,相互配合转弯的控制相反
      if fwd_speed < 0:
        self.left_motor.run_forever(duty_cycle_sp = dc_clamp(-fwd_speed-turn_speed))
        self.right_motor.run_forever(duty_cycle_sp = dc_clamp(-fwd_speed+turn_speed))
      else:
        self.left_motor.run_forever(duty_cycle_sp = dc_clamp(-fwd_speed+turn_speed))
        self.right_motor.run_forever(duty_cycle_sp = dc_clamp(-fwd_speed-turn_speed))

    self.left_motor.stop()
    self.right_motor.stop()

if __name__ == "__main__":
  motor_thread = MotorThread()
  motor_thread.setDaemon(True)
  motor_thread.start()

  for event in gamepad.read_loop(): #this loops infinitely
    if event.type == 3: #A stick is moved

      if event.code == 1: #Y axis on left stick
        fwd_speed = scalestick(event.value)

      if event.code == 0: #X axis on left stick
        turn_speed = scalestick(event.value)

    if event.type == 1 and event.code == 302 and event.value == 1:
      print "X button is pressed. Break."
      running = False
      time.sleep(0.5) # Wait for the motor thread to finish
      break

发表评论

电子邮件地址不会被公开。 必填项已用*标注