39

使用无线键盘控制树莓派小车

 5 years ago
source link: http://www.freebuf.com/geek/179458.html?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

*本文作者:xutiejun,本文属 FreeBuf 原创奖励计划,未经许可禁止转载。

网上有很多介绍树莓派小车的控制方案,但是搜索了一圈却发现没有无线键盘的控制方案。挑战未知,才更有趣。

0×01 所需材料

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

ziIF3mZ.jpg!web

2.无线键盘。

eyEzEvY.jpg!web

0×02 方案

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

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

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

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

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

按键事件 小车动作 方向键上按下 小车前进 方向键上抬起 小车停止 方向键下按下 小车后退 方向键下抬起 小车停止 方向键左按下 小车左转 方向键左抬起 小车停止 方向键右按下 小车右转 方向键右抬起 小车停止

0×03 键盘监听服务设计

首先确定键盘对应的event,可以输入如下命令查询。

cat /proc/bus/input/devices 

查询结果如下:

省略 …

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

N: Name=”Chicony HP Elite USB Keyboard”

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

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

U: Uniq=

H: Handlers=kbd event6 

B: PROP=0

B: EV=1f

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

B: REL=40

B: ABS=100000000

B: MSC=10

省略 …

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

键盘监听核心代码:

#define KEYSTATUS_IS_UP   (0)   //键盘按键抬起

void *listenKeyboardThread(void *arg) {

    int keys_fd;
    char ret[2];
    struct input_event t;
    keys_fd = open("/dev/input/event6", O_RDWR);
    if (keys_fd <= 0)
    {
        printf("open /dev/input/event6 device error!\n");
        return 0;
    }

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

                // 上键
                if ( KEY_UP==t.code&&KEYSTATUS_IS_UP!=t.value) {
                    // 前进
                    std::cout << "command: CARRUN FORWARD"<< std::endl;
        
                    DirectionReq *req = new DirectionReq();
                    req->setValue(DIRECTION_FORWARD);
                    ControlManager::instance()->postActionReq(req);
                }
                else if ( KEY_UP==t.code&&KEYSTATUS_IS_UP==t.value) {
                    // 停车
                    std::cout << "command: CARRUN STOP"<< std::endl;
        
                    StatusReq *req = new StatusReq();
                    ControlManager::instance()->postStatusReq(req);
                }

                // 下键
                if ( KEY_DOWN==t.code&&KEYSTATUS_IS_UP!=t.value) {
                    // 后退
                    std::cout << "command: CARRUN BACK"<< std::endl;
        
                    DirectionReq *req = new DirectionReq();
                    req->setValue(DIRECTION_BACK);
                    ControlManager::instance()->postActionReq(req);
                }
                else if ( KEY_DOWN==t.code&&KEYSTATUS_IS_UP==t.value) {
                    // 停车
                    std::cout << "command: CARRUN STOP"<< std::endl;
        
                    StatusReq *req = new StatusReq();
                    ControlManager::instance()->postStatusReq(req);
                }

                // 左键
                if ( KEY_LEFT==t.code&&KEYSTATUS_IS_UP!=t.value) {
                    // 左转
                    std::cout << "command: CARRUN LEFT"<< std::endl;
        
                    DirectionReq *req = new DirectionReq();
                    req->setValue(DIRECTION_LEFT);
                    ControlManager::instance()->postActionReq(req);    
                }
                else if ( KEY_LEFT==t.code&&KEYSTATUS_IS_UP==t.value) {
                    // 停车
                    std::cout << "command: CARRUN STOP"<< std::endl;
        
                    StatusReq *req = new StatusReq();
                    ControlManager::instance()->postStatusReq(req);
                }

                // 右键
                if ( KEY_RIGHT==t.code&&KEYSTATUS_IS_UP!=t.value) {
                    // 右转
                    std::cout << "command: CARRUN RIGHT"<< std::endl;
        
                    DirectionReq *req = new DirectionReq();
                    req->setValue(DIRECTION_RIGHT);
                    ControlManager::instance()->postActionReq(req);    
                }
                else if ( KEY_RIGHT==t.code&&KEYSTATUS_IS_UP==t.value) {
                    // 停车
                    std::cout << "command: CARRUN STOP"<< std::endl;
        
                    StatusReq *req = new StatusReq();
                    ControlManager::instance()->postStatusReq(req);
                }

            }
        }
    }

    close(keys_fd);
}

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

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

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

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

用python控制小车动作的代码如下:

 #!/usr/bin/Python
# -*- coding: UTF-8 -*-

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


#设置in1到in4接口
IN1 = 12
IN2 = 16
IN3 = 18
IN4 = 22

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

    GPIO.setup(IN1,GPIO.OUT)
    GPIO.setup(IN2,GPIO.OUT)
    GPIO.setup(IN3,GPIO.OUT)
    GPIO.setup(IN4,GPIO.OUT)

#前进的代码
def car_forward():
    GPIO.output(IN1,GPIO.HIGH)
    GPIO.output(IN2,GPIO.LOW)
    GPIO.output(IN3,GPIO.HIGH)
    GPIO.output(IN4,GPIO.LOW)
    time.sleep(0.15)
    GPIO.cleanup()

#后退
def car_back():
    GPIO.output(IN1,GPIO.LOW)
    GPIO.output(IN2,GPIO.HIGH)
    GPIO.output(IN3,GPIO.LOW)
    GPIO.output(IN4,GPIO.HIGH)
    time.sleep(0.15)
    GPIO.cleanup()

#左转
def car_left():
    GPIO.output(IN1,False)
    GPIO.output(IN2,False)
    GPIO.output(IN3,GPIO.HIGH)
    GPIO.output(IN4,GPIO.LOW)
    time.sleep(0.15)
    GPIO.cleanup()

#右转
def car_right():
    GPIO.output(IN1,GPIO.HIGH)
    GPIO.output(IN2,GPIO.LOW)
    GPIO.output(IN3,False)
    GPIO.output(IN4,False)
    time.sleep(0.15)
    GPIO.cleanup()

#停止
def car_stop():
    GPIO.output(IN1,GPIO.LOW)
    GPIO.output(IN2,GPIO.LOW)
    GPIO.output(IN3,GPIO.LOW)
    GPIO.output(IN4,GPIO.LOW)
    GPIO.cleanup()

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

控制系统在设计过程中遇到这样一个问题:

如果按键一直按下,当按键抬起时小车不会立刻停止,而是过一下才会停止。

导致问题发生的原因:

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

修正方案:

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

0×05 结束

到此整个小车控制系统就介绍完了。

最后,整套代码已经发到了百度网盘上。

链接: https://pan.baidu.com/s/1sA8t9mCH_TJegjdE5ggXMg 提取码: w3s2

*本文作者:xutiejun,本文属 FreeBuf 原创奖励计划,未经许可禁止转载。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK