1. 程式人生 > >自寫app與樹莓派製作智慧小車

自寫app與樹莓派製作智慧小車

實現的功能有:
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這個程式碼就可以了。
 後續功能的實現,將在後面的部落格中更新。