2回答

1收藏

[教程] 如何用无线键盘控制树莓派小车

Raspberry Pi Raspberry Pi 6958 人阅读 | 2 人回复 | 2018-10-12

0×01 所需材料

1.树莓派小车。(树莓派小车的安装不是本文重点,如果读者不熟悉小车的安装,请自行搜索。)


2.无线键盘。


0×02 方案

在树莓派系统上搭建两个服务:键盘监听服务和小车转向控制服务。

键盘监听服务主要用于监听键盘的按键,并将按键发送给小车转向控制服务。

小车转向控制服务主要用于驱动小车转向。

说明:本文中小车安装的是raspbian系统,是基于linux内核的debian系统。

按键与小车动作映射关系如下:


0×03 键盘监听服务设计
首先确定键盘对应的event,可以输入如下命令查询。
  1. cat /proc/bus/input/devices
复制代码
查询结果如下:
  1. 省略 …

  2. I: Bus=0003 Vendor=03f0 Product=034a Version=0110

  3. N: Name=”Chicony HP Elite USB Keyboard”

  4. P: Phys=usb-0000:00:14.0-5/input1

  5. S: Sysfs=/devices/pci0000:00/0000:00:14.0/usb1/1-5/1-5:1.1/0003:03F0:034A.0003/input/input9

  6. U: Uniq=

  7. H: Handlers=kbd event6

  8. B: PROP=0

  9. B: EV=1f

  10. B: KEY=3f0003007f 0 0 483ffff17aff32d bf54444600000000 1 130f938b17c000 677bfad941dfed 9ed68000004400 10000002

  11. B: REL=40

  12. B: ABS=100000000

  13. B: MSC=10

  14. 省略 …
复制代码

我的设备中键盘对应的是event6(注意:不同设备对应的event号是不同的)。

键盘监听核心代码:
  1. #define KEYSTATUS_IS_UP   (0)   //键盘按键抬起

  2. void *listenKeyboardThread(void *arg) {

  3.     int keys_fd;
  4.     char ret[2];
  5.     struct input_event t;
  6.     keys_fd = open("/dev/input/event6", O_RDWR);
  7.     if (keys_fd <= 0)
  8.     {
  9.         printf("open /dev/input/event6 device error!\n");
  10.         return 0;
  11.     }

  12.     while (1)
  13.     {
  14.         if (read(keys_fd, &t, sizeof (t)) == sizeof (t))
  15.         {
  16.             if (t.type == EV_KEY )
  17.             {
  18. //                printf("\r\nkey:%d %d %d \r\n", t.type, t.code, t.value);

  19.                 // 上键
  20.                 if ( KEY_UP==t.code&&KEYSTATUS_IS_UP!=t.value) {
  21.                     // 前进
  22.                     std::cout << "command: CARRUN FORWARD"<< std::endl;
  23.         
  24.                     DirectionReq *req = new DirectionReq();
  25.                     req->setValue(DIRECTION_FORWARD);
  26.                     ControlManager::instance()->postActionReq(req);
  27.                 }
  28.                 else if ( KEY_UP==t.code&&KEYSTATUS_IS_UP==t.value) {
  29.                     // 停车
  30.                     std::cout << "command: CARRUN STOP"<< std::endl;
  31.         
  32.                     StatusReq *req = new StatusReq();
  33.                     ControlManager::instance()->postStatusReq(req);
  34.                 }

  35.                 // 下键
  36.                 if ( KEY_DOWN==t.code&&KEYSTATUS_IS_UP!=t.value) {
  37.                     // 后退
  38.                     std::cout << "command: CARRUN BACK"<< std::endl;
  39.         
  40.                     DirectionReq *req = new DirectionReq();
  41.                     req->setValue(DIRECTION_BACK);
  42.                     ControlManager::instance()->postActionReq(req);
  43.                 }
  44.                 else if ( KEY_DOWN==t.code&&KEYSTATUS_IS_UP==t.value) {
  45.                     // 停车
  46.                     std::cout << "command: CARRUN STOP"<< std::endl;
  47.         
  48.                     StatusReq *req = new StatusReq();
  49.                     ControlManager::instance()->postStatusReq(req);
  50.                 }

  51.                 // 左键
  52.                 if ( KEY_LEFT==t.code&&KEYSTATUS_IS_UP!=t.value) {
  53.                     // 左转
  54.                     std::cout << "command: CARRUN LEFT"<< std::endl;
  55.         
  56.                     DirectionReq *req = new DirectionReq();
  57.                     req->setValue(DIRECTION_LEFT);
  58.                     ControlManager::instance()->postActionReq(req);   
  59.                 }
  60.                 else if ( KEY_LEFT==t.code&&KEYSTATUS_IS_UP==t.value) {
  61.                     // 停车
  62.                     std::cout << "command: CARRUN STOP"<< std::endl;
  63.         
  64.                     StatusReq *req = new StatusReq();
  65.                     ControlManager::instance()->postStatusReq(req);
  66.                 }

  67.                 // 右键
  68.                 if ( KEY_RIGHT==t.code&&KEYSTATUS_IS_UP!=t.value) {
  69.                     // 右转
  70.                     std::cout << "command: CARRUN RIGHT"<< std::endl;
  71.         
  72.                     DirectionReq *req = new DirectionReq();
  73.                     req->setValue(DIRECTION_RIGHT);
  74.                     ControlManager::instance()->postActionReq(req);   
  75.                 }
  76.                 else if ( KEY_RIGHT==t.code&&KEYSTATUS_IS_UP==t.value) {
  77.                     // 停车
  78.                     std::cout << "command: CARRUN STOP"<< std::endl;
  79.         
  80.                     StatusReq *req = new StatusReq();
  81.                     ControlManager::instance()->postStatusReq(req);
  82.                 }

  83.             }
  84.         }
  85.     }

  86.     close(keys_fd);
  87. }
复制代码

0×04 小车转向控制服务设计

小车转向控制服务采用C++语言和python语言混合编程实现。

python语言程序只用于控制小车的动作:前进、后退、左转、右转、停止。

C++语言程序是整个控制系统的核心,用于控制小车动作的逻辑控制。

用python控制小车动作的代码如下:
  1. #!/usr/bin/Python
  2. # -*- coding: UTF-8 -*-

  3. #引入gpio的模块
  4. import RPi.GPIO as GPIO
  5. import time


  6. #设置in1到in4接口
  7. IN1 = 12
  8. IN2 = 16
  9. IN3 = 18
  10. IN4 = 22

  11. #初始化接口
  12. def car_init():
  13.     #设置GPIO模式
  14.     GPIO.setmode(GPIO.BOARD)

  15.     GPIO.setup(IN1,GPIO.OUT)
  16.     GPIO.setup(IN2,GPIO.OUT)
  17.     GPIO.setup(IN3,GPIO.OUT)
  18.     GPIO.setup(IN4,GPIO.OUT)

  19. #前进的代码
  20. def car_forward():
  21.     GPIO.output(IN1,GPIO.HIGH)
  22.     GPIO.output(IN2,GPIO.LOW)
  23.     GPIO.output(IN3,GPIO.HIGH)
  24.     GPIO.output(IN4,GPIO.LOW)
  25.     time.sleep(0.15)
  26.     GPIO.cleanup()

  27. #后退
  28. def car_back():
  29.     GPIO.output(IN1,GPIO.LOW)
  30.     GPIO.output(IN2,GPIO.HIGH)
  31.     GPIO.output(IN3,GPIO.LOW)
  32.     GPIO.output(IN4,GPIO.HIGH)
  33.     time.sleep(0.15)
  34.     GPIO.cleanup()

  35. #左转
  36. def car_left():
  37.     GPIO.output(IN1,False)
  38.     GPIO.output(IN2,False)
  39.     GPIO.output(IN3,GPIO.HIGH)
  40.     GPIO.output(IN4,GPIO.LOW)
  41.     time.sleep(0.15)
  42.     GPIO.cleanup()

  43. #右转
  44. def car_right():
  45.     GPIO.output(IN1,GPIO.HIGH)
  46.     GPIO.output(IN2,GPIO.LOW)
  47.     GPIO.output(IN3,False)
  48.     GPIO.output(IN4,False)
  49.     time.sleep(0.15)
  50.     GPIO.cleanup()

  51. #停止
  52. def car_stop():
  53.     GPIO.output(IN1,GPIO.LOW)
  54.     GPIO.output(IN2,GPIO.LOW)
  55.     GPIO.output(IN3,GPIO.LOW)
  56.     GPIO.output(IN4,GPIO.LOW)
  57.     GPIO.cleanup()
复制代码

控制系统的代码就不粘贴了,只把设计过程中遇到的问题与大家分享下。

控制系统在设计过程中遇到这样一个问题:
如果按键一直按下,当按键抬起时小车不会立刻停止,而是过一下才会停止。

导致问题发生的原因:
由于按键一直按下会有大量的按键请求发送过来,而小车的动作响应要慢于键盘按键响应,会有大量的按键按下请求堆积在处理线程中,而按键抬起请求处于队列最末尾,是最后执行的,所以当按键抬起时小车才不会立刻停止。

修正方案:
按键抬起事件要最优先处理,处理完按键抬起事件后将堆积的按键按下队列清空。

0×05 结束
到此整个小车控制系统就介绍完了。
最后,整套代码已经发到了百度网盘上。

游客,如果您要查看本帖隐藏内容请回复


本文作者:xutiejun,转载自 FreeBuf


关注下面的标签,发现更多相似文章
分享到:
回复

使用道具 举报

回答|共 2 个

倒序浏览

沙发

sdadaf

发表于 2018-11-24 01:48:40 | 只看该作者

前排沙发

由于按键一直按下会有大量的按键请求发送过来,而小车的动作响应要慢于键盘按键响应,会有大量的按键按下请求堆积在处理线程中,而按键抬起请求处于队列最末尾,是最后执行的,所以当按键抬起时小车才不会立刻停止。
您需要登录后才可以回帖 注册/登录

本版积分规则

关闭

站长推荐上一条 /3 下一条