使用無線鍵盤控制樹莓派小車
*本文作者:xutiejun,本文屬 FreeBuf 原創獎勵計劃,未經許可禁止轉載。
網上有很多介紹樹莓派小車的控制方案,但是搜尋了一圈卻發現沒有無線鍵盤的控制方案。挑戰未知,才更有趣。
0×01 所需材料
1.樹莓派小車。(樹莓派小車的安裝不是本文重點,如果讀者不熟悉小車的安裝,請自行搜尋。)
2.無線鍵盤。
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 結束
到此整個小車控制系統就介紹完了。
最後,整套程式碼已經發到了百度網盤上。
連結: ofollow,noindex" target="_blank">https://pan.baidu.com/s/1sA8t9mCH_TJegjdE5ggXMg 提取碼: w3s2
*本文作者:xutiejun,本文屬 FreeBuf 原創獎勵計劃,未經許可禁止轉載。