查看: 5449|回复: 2

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

[复制链接]
  • TA的每日心情

    2020-3-6 09:52
  • 签到天数: 13 天

    连续签到: 1 天

    [LV.3]偶尔看看II

    发表于 2018-10-12 14:51:33 | 显示全部楼层 |阅读模式
    分享到:
    0×01 所需材料

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

    2.无线键盘。
    2.jpg

    0×02 方案

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

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

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

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

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

    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


    回复

    使用道具 举报

    该用户从未签到

    发表于 2018-11-24 01:48:40 | 显示全部楼层
    前排沙发

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

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

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



    手机版|小黑屋|与非网

    GMT+8, 2024-3-28 17:18 , Processed in 0.160104 second(s), 23 queries , MemCache On.

    ICP经营许可证 苏B2-20140176  苏ICP备14012660号-2   苏州灵动帧格网络科技有限公司 版权所有.

    苏公网安备 32059002001037号

    Powered by Discuz! X3.4

    Copyright © 2001-2020, Tencent Cloud.