使用EV3主机上的按键控制小车还是比较麻烦,能不能通过遥控来控制小车呢?

当然可以!

乐高提供了一个红外线传感器和遥控器:

但是红外遥控器需要很强的方向性,而且那个遥控器太简陋了,连个摇杆都没有。

还有一种方式是使用蓝牙手柄,这样我们就可以找一个游戏手柄直接通过蓝牙控制小车,太方便了有木有!

我找了一个任天堂的游戏手柄:

理论上支持蓝牙的游戏手柄都是可用的,标准游戏手柄按键如下:

┌──────┐ ┌──────┐

┌┴─────┐│ ┌┴─────┐│

┌─┴──────┴┴──────────────────┴──────┴┴─┐

│ ┌─┐ ┌─┐ ┌─┐ │

│ ┌─┐ └─┘ └─┘ ┌─┐│X│ │

│ ┌─┘ └─┐ - + │Y│└─┘┌─┐ │

│ └─┐ ┌─┘ ┌─┐ ┌─┐ └─┘┌─┐│A│ │

│ └─┘ └─┘ └─┘ │B│└─┘ │

│ ┌─┐ S H ┌─┐ └─┘ │

│ ┌─┘ └─┐ ┌─┘ └─┐ │

│ └─┐ ┌─┘ └─┐ ┌─┘ │

│ └─┘ └─┘ │

│ ──────────────────────── │

│ / \ │

│ / \ │

└────/ \────┘

把游戏手柄和EV3主机用蓝牙连起来很简单,但是怎么用Python程序读取手柄的输入?

一般来说,从外部设备读取输入时,应用程序并不直接与外设打交道,而是由操作系统通过驱动程序连接外设,然后,通过操作系统提供的API读取输入。Windows程序可以通过DirectX访问外设,运行在网页的JavaScript程序可以通过浏览器提供的Gamepad API。

EV3主机运行的是Debian Linux,那么Python程序如何在Linux下读取手柄的输入?

在Linux中,系统把每个外设都映射为文件,每个设备的输入也被映射为文件。/proc目录挂载的就是Linux的虚拟文件系统,映射Linux的进程信息和设备信息。我们登录到EV3,查看/proc/bus/input/devices文件:

$ cat /proc/bus/input/devices

I: Bus=0000 Vendor=0000 Product=0000 Version=0000

N: Name="LEGO MINDSTORMS EV3 Speaker"

P: Phys=

S: Sysfs=/devices/platform/sound/input/input0

U: Uniq=

H: Handlers=kbd event0

B: PROP=0

B: EV=40001

B: SND=6

I: Bus=0019 Vendor=0001 Product=0001 Version=0100

N: Name="EV3 Brick Buttons"

P: Phys=gpio-keys/input0

S: Sysfs=/devices/platform/gpio_keys/input/input1

U: Uniq=

H: Handlers=kbd event1

B: PROP=0

B: EV=3

B: KEY=1680 0 0 10004000

这个文件列出了目前系统可用的输入设备,上述两段分别代表扬声器和按钮设备。如果我们把蓝牙手柄连接到EV3,再查看文件,发现多了一段内容:

I: Bus=0005 Vendor=057e Product=2009 Version=0001

N: Name="Pro Controller"

P: Phys=00:17:ec:13:d8:1d

S: Sysfs=/devices/platform/soc@1c00000/serial8250.2/tty/ttyS2/hci0/hci0:2/0005:057E:2009.0003/input/input4

U: Uniq=00:90:e3:9b:ec:e9

H: Handlers=event2

B: PROP=0

B: EV=10001b

B: KEY=ffff0000 0 0 0 0 0 0 0 0 0

B: ABS=3001b

B: MSC=10

我们通过N: Name="xxx"来搜索手柄设备,然后,通过H: Handlers=xxx获取设备的输入文件。例如,上述蓝牙手柄的名称是Pro Controller,输入是event2,对应到系统文件就是/dev/input/event2。Python代码实现如下:

定义InputDevice类表示输入设备:

class InputDevice():

def __init__(self):

self.name = ''

self.handler = ''

def __str__(self):

return '' % (self.name, self.handler)

def setName(self, name):

if len(name) >= 2 and name.startswith('"') and name.endswith('"'):

name = name[1:len(name)-1]

self.name = name

def setHandler(self, handlers):

for handler in handlers.split(' '):

if handler.startswith('event'):

self.handler = handler

定义函数listDevices()通过读取/proc/bus/input/devices文件获取所有设备:

def listDevices():

devices = []

with open('/proc/bus/input/devices', 'r') as f:

device = None

while True:

s = f.readline()

if s == '':

break

s = s.strip()

if s == '':

devices.append(device)

device = None

else:

if device is None:

device = InputDevice()

if s.startswith('N: Name='):

device.setName(s[8:])

elif s.startswith('H: Handlers='):

device.setHandler(s[12:])

return devices

定义函数detectJoystick()通过名字模糊查找手柄设备:

def detectJoystick(joystickNames):

for device in listDevices():

for joystickName in joystickNames:

if joystickName in device.name:

# 返回输入文件:

return '/dev/input/%s' % device.handler

# 未找到返回None:

return None

搜索到手柄设备后打开文件读取输入:

eventFile = detectJoystick(['Controller'])

if eventFile:

with open(eventFile, 'rb') as infile:

while True:

# 读取输入

现在最关键的问题来了:eventX文件应该以何种格式读取?

让我们搜索一下Linux文档,在input.txt中详细说明了eventX文件的输入格式。Linux系统把每一个输入事件都封装为一个C结构体让我们直接读取:

struct input_event {

struct timeval time; // 16字节时间戳

unsigned short type; // 2字节事件类型

unsigned short code; // 2字节事件代码

unsigned int value; // 4字节事件值

};

每次读取24字节并按照C的struct类型解码,即可得到手柄输入的全部信息。在Python程序中,可以用struct读取:

FORMAT = 'llHHI'

EVENT_SIZE = struct.calcsize(FORMAT)

with open(eventFile, 'rb') as infile:

while True:

event = infile.read(EVENT_SIZE)

_, _, t, c, v = struct.unpack(FORMAT, event)

print('t = %s, c = %s, v = %s' % (t, c, v))

注意到unpack()方法的返回值,我们丢弃了前两个8字节整数,保留了t、c、v,分别是2字节无符号整数、2字节无符号整数和4字节无符号整数。

根据Linux文档,再打印出每个事件的详细数据,把手柄的按键和摇杆都按一遍,就可以得到按键编码如下:

t==1时表示按键,此时v==1表示按下,v==0表示释放,v==2表示持续按下,对应的c表示按键编码。要检测A、B按钮按下,可以这么写:

if t == 1 and v == 1:

if c == 305:

# Button A pressed

pass

if c == 304:

# Button B pressed

pass

摇杆数据则比较复杂,类型t==3表示摇杆,如果c==0,表示左摇杆的左右移动,如果c==1,表示左摇杆的上下移动,v的值介于0~65535,32768表示中心值,越往两侧偏移越多则越接近最大和最小值:

0

0 65535

65535

现在我们就可以通过一个读取手柄的无限循环来控制小车:

def joystickLoop(robot, eventFile):

FORMAT = 'llHHI'

EVENT_SIZE = struct.calcsize(FORMAT)

with open(eventFile, 'rb') as infile:

while True:

event = infile.read(EVENT_SIZE)

_, _, t, c, v = struct.unpack(FORMAT, event)

if t == 1 and v == 1:

if c == 305:

# A键加速:

robot.setSpeed(1)

elif c == 304:

# B键减速:

robot.setSpeed(-1)

elif c == 307:

# X键退出:

return robot.inactive()

elif t == 3:

if c == 1:

# 左摇杆上下移动:

speed = 0

if v < 32768:

# 加速:

speed = 1

elif v > 32768:

# 减速:

speed = -1

robot.setSpeed(speed)

但是另一个问题来了:主线程通过死循环读取手柄输入,那么怎么读取超声波传感器的数据?

可以利用Python的threading启动多线程,在另一个线程中不断检测超声波传感器,并在条件达到的时候自动停车:

def autoStopLoop(robot):

while robot.active:

if robot.speed > 0 and robot.ultrasonic.distance() < 200:

robot.setSpeed(0)

wait(100)

joystickEvent = detectJoystick(['Controller'])

robot = Robot()

t = threading.Thread(target=autoStopLoop, args=(robot,))

t.start()

joystickLoop(robot, joystickEvent)

到此为止,一个蓝牙手柄控制的机器人程序就宣告完成。试试效果:

参考源码

python遥控汽车玩具_遥控小车相关推荐

  1. python遥控汽车玩具_分享 | 撞坏遥控车后,有个技术大牛爸爸是种怎样的体验

    最近,Mapbox 的 Android 工程师 Antonio 使用计算机视觉和机器学习技术,为他的女儿 Violeta 重新制作了一台遥控车.接下来我们看看 Antonio 是如何实现是: 今年上半 ...

  2. python遥控汽车玩具_[详细实例]MicroPython拼插编程实战:DIY一台会思考的壁障车...

    (转载请注明文章来源,更多教程可自助参考www.tpyboard.com,QQ技术交流群:157816561,公众号:MicroPython玩家汇) 在日常生活中,大家会经常见到各种各样的遥控车,它需 ...

  3. python遥控汽车玩具_ESP32MicroPython 手机遥控小车

    MicroPython是Python的精简版,但保留了python的基本特色.是学习python的工具之一. 烧录软件用的是uPyCraft. 本案例用了ESP32 和 L298N电机驱动.以及两个直 ...

  4. python就是玩具_极客老爹的玩具DIY之路:Python + Kids + Building Stuff == Fun

    注:本文素材来自于PyCon 2013大会上的一个ppt,作者是David Beazley(Twitter:@dabeaz).David是著名的Python极客,著有<Python参考手册> ...

  5. 1.树莓派、Python、STM32、上位机、局域网、PC智能遥控小车(含源码)

    整体功能:电脑上观看小车前方画面,通过电脑方向键控制小车前后运动.左右转弯,如前进后退键,按下前进或后退,松开停车,左右同理 关键技术部分:PC端:使用pygame编写上位机,作为服务器 树莓派端:图 ...

  6. 新朋实验室之玩具遥控车改装为ARDUINO蓝牙遥控小车(代码及制作过程开源)

    新朋实验室之玩具遥控车改装为ARDUINO蓝牙遥控小车(代码及制作过程开源) 2017年08月21日 12:55:25 XPLab 阅读数:4008 你家里的玩具遥控车坏了?遥控器丢了?先不要急着丢掉 ...

  7. 开源免费代码_02_单摇杆远程遥控小车,基于Arduino的ESP-NOW,ESP32发送指令、ESP32接收指令,实现小车毫秒级完美控制_公羽兴

    发文希望能够帮忙物联网爱好者少走弯路,少被割韭菜.如果觉得此文对您有帮助的话帮忙点个赞,感谢!!! 哔哩哔哩网址:开源免费代码_02_单摇杆远程遥控小车,基于Arduino的ESP-NOW,ESP32 ...

  8. [树莓派] 轻松制作一个遥控小车(C++,Socket)

    最近闲来无事,便开始倒腾一些小玩具.我想从比较简单的开始入手,就先是树莓派遥控小汽车吧! 本文用来分享和总结经验.从0开始,小白级教程. 一.必备材料: 1.树莓派 1 个(什么版本都可以) 2.智能 ...

  9. 树莓派制作遥控小车教程

    树莓派制作遥控小车教程 一.成品图 二.准备材料: 三.开始制作 1.接线 2.写程序 一.成品图 二.准备材料: 1.树莓派 2.充电宝 3.小车套件(底盘,车轮,电机,电池座) 4.导线,杜邦线 ...

最新文章

  1. 标准出现问题,人工智能正在走向错误的方向
  2. python编程入门指南怎么样-如果想学python怎么入门?
  3. Docker 私服Registry简介与使用Docker-Compose安装Registry
  4. OpenCV使用Kinect和其他OpenNI兼容的深度传感器
  5. tomcat服务器文件被清空,SpringBoot内置Tomcat缓存文件目录被意外删除导致异常
  6. 微信小程序黑客马拉松即将开始,来做最酷的 Mini Program Creators!
  7. 研究笔记:iOS中使用WebViewProxy拦截URL请求
  8. rank,dense_rank,row_number使用和区别
  9. 比较交换/(选择)排序法和冒泡排序法(C语言)
  10. 一款动态跑路html源码,简单实用,上传解压就完事了
  11. golang 一段代码不甚明白
  12. web前端html怎么求最大值和最小值,第8篇-JavaScript专题之如何求数组的最大值和最小值...
  13. eclipse 最全快捷键(网络收集)
  14. 分享一个selenium jar包 的下载地址,各版本都有,包括selenium-server-standalone.jar、selenium、selenium-server
  15. PHP 变量 与 运算符
  16. mac使用之必备神器
  17. 小学生应该学习编程语言
  18. pscc2019滤镜抽出_Photoshop(ps)cc2019 已经发现你啦!
  19. 印象笔记 网易云笔记 腾讯文档 对比
  20. linux node安装菜鸟教程,手把手告诉你如何安装多个版本的node

热门文章

  1. linux添加java环境变量
  2. form6i支持 oracle 11g,使用Form6i保存文件到Oracle中的Blob字段
  3. html隐藏visibility,HTML DOM Style visibility 属性 | 菜鸟教程
  4. netlist compile速记
  5. Excel调整行高,鼠标右键直接选中一行进行调整了
  6. whose引导的定语从句,先行词是复数形式,从句里谓语动词用单数还是复数?
  7. 软件项目经理在面试的时候会问到哪些问题?
  8. 26 岁数学天才回国任教,刚以中科大教授之名攻破世界级难题
  9. java 内存 回收_java内存回收
  10. 图像处理:双边滤波算法