Android手機控制樹莓派製作的四驅小車
-------更新
完整的程式碼放在Github上了:
-------全文
年初的時候看到使用樹莓派製作的遠端開門器》後,覺得硬體程式設計似乎沒有想象的難。 之前認為硬體程式設計可能需要學習新的程式語言,需要特別的程式設計環境。然而樹莓派使用Linux作業系統環境,只要Linux支援的程式語言 ,都可以成為你的選擇。當語言環境不是問題的時候,對於我來說,我最感興趣的部分是如何用樹莓派來控制一些低速的外部裝置,例如 :繼電器、小馬達。 一般的PC並不提供這些通用介面,PC只提供一些高速裝置的介面如USB。 而樹莓派不止提供了USB介面,還提供了GPIO介面,有了這個介面使得控制通用的外部裝置得以實現。
- import RPi.GPIO as GPIO
- GPIO.setup(7, GPIO.OUT)
- GPIO.output(7, True)
- GPIO.output(7,False)
程式碼中樹莓派通過指定GPIO介面向外部發送訊號,如果從外部向樹莓派輸入訊號,則指定GPIO.input。整個小車需要的部件就是四個輪子,可以單獨控制,所以下面我們只說說如何來控制其中的一個電機。
接通VCC,GND 模組電源指示燈亮
IA1輸入高電平,IA1輸入低電平,【OA1 OB1】電機正轉;
IA1輸入低電平,IA1輸入高電平,【OA1 OB1】電機反轉;
IA2
IA2輸入低電平,IA2輸入高電平,【OA2 OB2】電機反轉;
為了簡化電路設計,考慮用驅動模組控制。這是我在淘寶購買的兩路電機驅動 H橋 L9110 電機驅動模組 ,接上它,你只需要下面簡單的連線,就可以讓樹莓派來控制電機了。驅動模組有電源、訊號輸入介面以及電源輸出介面:
- 電源輸入,VCC,GND分別是輸入電源的正負極,可以用電池組來供電。 注意不能接反,否則驅動模組可能短時間內發燙,甚至燒壞。
- 訊號輸入,IA1 IB1, IA2 IB2分別是兩對訊號輸入介面,接受來自樹莓派訊號的控制驅動模組的電源輸出,達到電機正轉反正的目的。
- 電源輸出,電源輸出介面OA1 OB1,OA2 OB2 (綠色部分)分別是兩對輸出電流到電機的介面,通過他們為電機供電。
- 當你連線好這些介面後,模組上的連線也就全部完成了,接下來就要把模組上的IA1 IB1, IA2 IB2連線到樹莓派程式指定的GPIO。
BOARD模式下的介面定義
第二部分:連線GPIO
要使用樹莓派為GPIO提供連個設定模式,BOARD和BCM, 模式的不同GPIO的每一個介面的定義也不同(上圖是BOARD模式下的定義),使用時必須在程式碼中必須明確指定他的模式:
- import RPi.GPIO as GPIO #GPIO package
- GPIO.setmode(GPIO.BOARD) #設定模式
- GPIO.setup(13, GPIO.OUT) #指定介面是輸出還是輸入
- GPIO.setup(15, GPIO.OUT) #指定介面是輸出還是輸入
- GPIO.output(13, GPIO.HIGH) #輸出高電平
- GPIO.output(15, GPIO.LOW) #輸出低電平
當然,為程式碼更好讀,可以專門寫一個Wheel類來控制輪子。單個輪子只有三個操作, 前進、後退、停止,現在來封裝這些操作:
第三部分:封裝輪子
- class Wheel:
- pins ={'a':[13,15],'b':[16,18],'c':[19,21],'d':[22,24]}# 這裡指定了四個輪子所使用的8個GPIO介面
- def __init__(self,name):
- self.name = name
- self.pin = Wheel.pins[self.name]
- GPIO.setmode(GPIO.BOARD)
- GPIO.setup(self.pin[0],GPIO.OUT)
- GPIO.setup(self.pin[1],GPIO.OUT)
- self.stop()
- def forward(self):
- GPIO.output(self.pin[0],GPIO.HIGH)
- GPIO.output(self.pin[1],GPIO.LOW)
- def stop(self):
- GPIO.output(self.pin[0],False)
- GPIO.output(self.pin[1],False)
- def back(self):
- GPIO.output(self.pin[0],False)
- GPIO.output(self.pin[1],True)
- Wheel('a').forward() #a,b,c,d是四個輪子的名字
通過呼叫每個Wheel例項,可以對他們自由操作。由於整個車是由四個輪子協同來工作的,我們需要同時來讓四個輪子一起工作,對此對這種協同工作進行封裝,讓我們不必在關心怎樣驅動4個輪子就可以前進了:
第四部分: 封裝車子
我們希望車子能夠前進、後退、左轉、右轉,於是可以這樣來封裝一下程式碼:
- class Car:
- wheels=[Wheel('a'),Wheel('b'),Wheel('c'),Wheel('d')]
- @staticmethod
- def init():
- GPIO.setmode(GPIO.BOARD)
- @staticmethod
- def forward():
- for wheel in Car.wheels:
- wheel.forward()
- @staticmethod
- def back():
- for wheel in Car.wheels:
- wheel.back()
- @staticmethod
- def left():
- Car.wheels[0].forward()
- Car.wheels[1].forward()
- Car.wheels[3].back()
- Car.wheels[2].back()
- @staticmethod
- def right():
- Car.wheels[2].forward()
- Car.wheels[3].forward()
- Car.wheels[0].back()
- Car.wheels[1].back()
- @staticmethod
- def stop():
- Car.wheel[0].stop()
- Car.wheel[1].stop()
- Car.wheel[3].stop()
- Car.wheel[2].stop()
Car是一個靜態類,它提供的五個方式分別對應到小車的前、後、左、右、停。現在我們考慮遠端遙控小車,因此小車必須提供和外部遙控裝置的通訊介面:
第五部分:通訊程式
小車和外界的通訊方式其實很多,紅外、藍芽、Wifi等等。根據我的裝置清單我就選擇了Wifi的方式,所以使用socket作為介面最直接不過了:
- rom socket import *
- import sys
- import time
- import car
- commands ={'forward':Car.forward,
- 'back':Car.back,
- 'stop':Car.stop,
- 'left':Car.left,
- 'right':Car.right
- }
- def execute(command):
- print command
- commands[command]()
- HOST ='192.168.2.101'#the ip of rapberry pi
- PORT = 8888
- s= socket(AF_INET, SOCK_STREAM)
- s.bind((HOST, PORT))
- s.listen(1)
- print ('listening on 8888')
- while1:
- conn, addr = s.accept()
- print ('Connected by:', addr)
- while1:
- command= conn.recv(1024).replace('\n','')
- ifnot command:break
- execute(command)
- conn.close()
> sudo python server.py 之後,樹莓派會監聽8888埠一旦有訊息傳遞過來,根據命令引數呼叫相應的方法。小車的服務端介面就相當一個執行者,接受到命令就立刻執行,此次只要可以建立和小車的socket連線,便可以輕鬆控制,我們打算用Android手機來發送這個訊息:
第六部分: Android手機操作小車
通過手機來操作,實際上就通過socket和樹莓派進行通訊,當樹莓派處於listening狀態,對於手機來說,它要做的最重要的事情就是傳送訊息到樹莓派,一個小車的指揮者:
- package com.simplexk;
- import java.io.PrintWriter;
- import java.net.Socket;
- publicclass Commander {
- publicstatic String HOST ="192.168.2.101"; //the ip of raspberry pi
- publicstaticint PORT =8888;
- publicstaticvoid send(Command forward) throws Exception {
- Socket socket = new Socket(HOST, PORT);
- PrintWriter writer = new PrintWriter(socket.getOutputStream());
- writer.println(forward.toString());
- writer.flush();
- socket.close();
- }
- }
當然這僅僅是手機向樹莓派發送訊息的部分,手機發送什麼的命令,你還需要程式設計額外的使用者介面程式來完成。最簡單的,你可以放上四個按鈕來操作小車的四個運動方向。
---------------結束---------------