自寫app與樹莓派製作智慧小車
阿新 • • 發佈:2018-12-31
實現的功能有:
1.實現小車的前進,後退,左轉,右轉。
2.實時視訊的傳回,檢視小車周圍的情況。
3.攝像頭的上下左右轉動,使用舵機雲臺來實現。
需要的材料:
1.樹莓派一個(帶有python環境,現在的好像都有自帶python環境)。
2.安卓手機一部,用於安裝寫的APP。
3.杜邦線若干。
4.L298N電機驅動模組(某寶9塊錢一個)。
5.舵機雲臺一個(某寶有賣散件,買來自己裝,20幾塊錢)。
6.車架一個(同樣某寶…).
7.充電寶一個,用於樹莓派供電,和L298N電機驅動模組供電,雖然L298N標的是12V的電源,但是5V也能使用。
8.電腦一臺。
首先要實現手機app與樹莓派的通訊,這樣才能實現對樹莓派小車的控制。首先想到的就是socket通訊,簡單易懂,但是寫的時候還是遇見了很多的問題,下面會說到。
這篇文章只介紹實現app控制小車的前進後退左轉右轉。
整體的思路就是:手機app向樹莓派發送不同的指令,樹莓派根據不同的指令進行指定的相應,來控制車體的運動。
一、車體的組裝以及連線這裡就不仔細介紹了,這種東西還是比較簡單的。要注意的是將從樹莓派引出的GPIO引腳要記好,程式設計的時候還要用。其中L298N驅動的作用就是來為帶動小車運動的電機提供足夠的電源。
二、下面就是app進行程式設計了,這裡使用的是Android studio,如何安裝,與配置環境百度一下都有,這裡就不介紹了。下面拿控制小車前進作為一個例子進行解釋一下:
@Override
public void onClick(View v)
{
if (v.getId() == R.id.forward)
{
forward.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent)
{
//擡起操作
if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
sendRequestWithHttpURLConnection1();
}
//按下操作
if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
sendRequestWithHttpURLConnection();
}
//移動操作
if (motionEvent.getAction() == MotionEvent.ACTION_MOVE) {
}
return false;
}
});
}
}
上面的程式碼是來監控你在app中按下了那個按鍵,如果是前進鍵,按下時呼叫sendRequestWithHttpURLConnection()函式向樹莓派發送指令,擡起按鍵時向樹莓派在此傳送指令。
其中sendRequestWithHttpURLConnection()這個函式很重要,在寫時遇到了很多的問題,主要就是在UI執行緒和通訊socket執行緒的處理上,UI執行緒中不能使用socket通訊必須要重新建立一個執行緒來進行通訊。
重點內容
private void sendRequestWithHttpURLConnection()
{
new Thread(new Runnable()
{
@Override
public void run() {
int PORT = 1;
InetAddress addr = null;
try
{
addr = InetAddress.getByName("192.168.31.6");//此處是樹莓派的IP地址。
}
catch (UnknownHostException e)
{
e.printStackTrace();
}
Socket socket = new Socket();
try
{
socket.connect(new InetSocketAddress(addr, PORT), 30000);
socket.setSendBufferSize(100);
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
out.write("forward");
out.flush();
}
catch (SocketException e)
{
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
StringBuilder response = new StringBuilder();
String line="Iot";
response.append(line);
showResponse(response.toString());
}
finally
{
try
{
socket.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
}).start();
}
上面的程式碼就是socket程式設計的關鍵所在,這只是前進指令的傳送程式碼,還有其他的指令的傳送程式碼,幾乎都是相同的,只是把write()括號中的內容改一下就行了。每次按下一個按鍵,就傳送一個指令,使得樹莓派的GPIO改變狀態。每次擡起按鍵都發送停止的指令,使得小車停下來。
三、樹莓派部分的程式碼
樹莓派的程式碼使用python寫的,它的作用是來對手機發送來的指令進行處理。
分為兩部分,一部分是用來封裝小車的控制程式碼,一部分是用來識別接收到的指令,呼叫第一部分的程式碼。
第一部分程式碼:
重點內容
import threading
import RPi.GPIO as GPIO
import time
class Info:
@staticmethod
def p(message):
print 'Info: '+message
class Wheel:
pins ={'a':[13,15],'b':[16,18]}
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 st(self):
print 'ss'
def forward(self):
Info.p('wheel ' + self.name + ' is forwarding')
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):
Info.p('wheel ' +self.name + ' is backing')
GPIO.output(self.pin[0],False)
GPIO.output(self.pin[1],True)
class Car:
wheel=[Wheel('a'),Wheel('b'),Wheel('c'),Wheel('d')]
@staticmethod
def init():
GPIO.setmode(GPIO.BOARD)
Info.p('initialize the smart car ....')
Info.p('Smart car is ready to fly!')
@staticmethod
def forward():
Info.p('go straight forward')
for wheel in Car.wheel:
wheel.forward()
@staticmethod
def left():
Info.p('turn left ')
Car.wheel[0].forward()
Car.wheel[1].back()
@staticmethod
def right():
Info.p('turn left ')
Car.wheel[0].back()
Car.wheel[1].forward()
@staticmethod
def bleft():
Info.p('turn left ')
Car.wheel[2].back()
@staticmethod
def stop():
Info.p('turn left ')
Car.wheel[0].stop()
Car.wheel[1].stop()
@staticmethod
def back():
Info.p('go straight back')
for wheel in Car.wheel:
wheel.back()
@staticmethod
def lforward():
Info.p('go forward and turn left')
@staticmethod
def rforward():
Info.p('go forward and turn right')
@staticmethod
def lback():
Info.p('go back and turn left')
@staticmethod
def rback():
Info.p('go back and turn right')
@staticmethod
def stop():
Info.p('try to stop the car ...')
for wheel in Car.wheel:
wheel.stop()
@staticmethod
def bark():
Info.p('try to slow down the car ...')
@staticmethod
def speedup():
Info.p('try to speed up the car ...')
if __name__ =='__main__':
print '....'
第二部分程式碼:
```python
from socket import *
import sys
import time
from car import *
def run_raspberry():
commands ={'forward':Car.forward,'back': Car.back, 'stop': Car.stop,'left': Car.left,'right': Car.right}
PORT = 1
s= socket(AF_INET, SOCK_STREAM)
s.bind(("", PORT)) #對所有的ip都能接收。
s.listen(1)
print ('raspberry pi is listening on port 8888')
while 1:
conn, addr = s.accept()
print ('Connected by:', addr)
while 1:
command= conn.recv(1024).replace('\n','')
if not command or command not in commands:break
commands[command]()
conn.close()
if __name__ == '__main__':
run_raspberry()
```
只需執行run_raspberry.py這個程式碼就可以了。
後續功能的實現,將在後面的部落格中更新。