1. 程式人生 > >(14)樹莓派B+使用L298N驅動控制四驅車並實現一個簡單的web控制端

(14)樹莓派B+使用L298N驅動控制四驅車並實現一個簡單的web控制端

在系列文章第12篇中提到了L298N,它是H橋雙路直流電機驅動,可以使雙路直流電機實現正轉或者反轉,並且通過ENDA和ENDB輸入PWM訊號,還可以實現加減速。本文用2塊L298N驅動板成功驅動了4個直流電機,實現了4輪同時向前、向後以及前向左轉、前向右轉甚至是後向左轉和後向右轉的功能,最後通過python的BaseHTTPServer模組(python3中是http.server模組)實現了一個控制小車的HTTP介面,另外用lighttpd啟動一個網頁,通過網頁中的ajax呼叫控制小車的HTTP介面,最終實現通過web的方式來控制智慧小車。 首先介紹一下L298N驅動的使用方法:
電源端(12V、GND、5V),輸入5V直流電到L298N的5V介面時,直流電機幾乎不能正常運轉,當我們輸入7-12V接到L298N的12V引腳時,該驅動板還可以在5V引腳輸入電壓供外部使用,所以輸入驅動板的電壓就接在12V引腳即可,經測試,5V輸入到12V引腳可以正常工作,小車的直流電機也可以正常工作。 邏輯輸入端(ENDA IN1 IN2 IN3 IN4 ENDB),分別連線樹莓派的GPIO介面,控制邏輯如下圖,在本人測試過程中,ENDB端是和IN1 IN2配合使用控制M1電機的,還請大家使用時先測試好:
關於加減速的PWM,我們先忽略,在實現了前進、後退和功能後,這個只是錦上添花的功能,而且可有可無。 接下來,我們就可以連線線路了,如果想要小車移動方便,最好使用移動電源來給樹莓派、小車供電(用4節1.5V電池也可以),本人使用的是重達1斤的2萬毫安時雙輸出口移動電源,正好可以給樹莓派和外接電路供電。如果暫時沒有移動電源,可以只做好測試工作,把小車架空,不要讓其隨意亂走。 樹莓派GPIO和L298N連線關係如下(BOARD編號模式下的GPIO引腳-L298N輸入引腳,都是高電平使能,其它引腳低電平): 前輪驅動:18-ENDB, 22-ENDA, 11-左前前, 13-左前後, 15-右前前, 29-右前後 後輪驅動:38-ENDB, 40-ENDA, 31-左後前, 33-左後後, 35-右後前, 37-右後後 電源輸出:5V 1A- 樹莓派,5V 2.1A-外部電路(麵包板,2塊L298N驅動) L298N和電機:按照使用方法中的邏輯關係,每塊的OUT1 OUT2接一個電機,OUT3 OUT4接另外一個。 最好在連線前,就已經測試好電機的正負極,L298N連線時都保持一定的先後次序,如IN1 IN2 ENDB 控制 OUT1 OUT2從而控制電機1,這樣在寫程式測試時會方便很多。 手繪電路圖如下,其實比較簡單,就是線多而已:

L298N控制4驅直流電機的驅動如下(python2/python3): L298N_car2.py #!/usr/bin/python2
#coding=utf-8
import RPi.GPIO as GPIO
import time

'''
使用2塊L298N驅動控制4個直流電機
前輪驅動:18-ENDB, 22-ENDA, 11-左前前, 13-左前後, 15-右前前, 29-右前後
後輪驅動:38-ENDB, 40-ENDA, 31-左後前, 33-左後後, 35-右後前, 37-右後後
'''
# 初始化設定引腳輸出
def init():
        GPIO.setmode(GPIO.BOARD)
        GPIO.setup(18, GPIO.OUT)
        GPIO.setup(11, GPIO.OUT)
        GPIO.setup(13, GPIO.OUT)
        GPIO.setup(22, GPIO.OUT)
        GPIO.setup(15, GPIO.OUT)
        GPIO.setup(29, GPIO.OUT)


        GPIO.setup(38, GPIO.OUT)
        GPIO.setup(31, GPIO.OUT)
        GPIO.setup(33, GPIO.OUT)
        GPIO.setup(40, GPIO.OUT)
        GPIO.setup(35, GPIO.OUT)
        GPIO.setup(37, GPIO.OUT)
# 所有引腳置低電平,用於復位、停止執行的功能
def reset():
        GPIO.output(18, GPIO.LOW)
        GPIO.output(11, GPIO.LOW)
        GPIO.output(13, GPIO.LOW)
        GPIO.output(22, GPIO.LOW)
        GPIO.output(15, GPIO.LOW)
        GPIO.output(29, GPIO.LOW)
        GPIO.output(38, GPIO.LOW)
        GPIO.output(31, GPIO.LOW)
        GPIO.output(33, GPIO.LOW)
        GPIO.output(40, GPIO.LOW)
        GPIO.output(35, GPIO.LOW)
        GPIO.output(37, GPIO.LOW)
# 左前輪向前轉
def front_left_forward():
        GPIO.output(18, GPIO.HIGH)
        GPIO.output(11, GPIO.HIGH)
        GPIO.output(13, GPIO.LOW)
# 右前輪向前轉
def front_right_forward():
        GPIO.output(22, GPIO.HIGH)
        GPIO.output(15, GPIO.HIGH)
        GPIO.output(29, GPIO.LOW)
# 左後輪向前轉
def rear_left_forward():
        GPIO.output(38, GPIO.HIGH)
        GPIO.output(31, GPIO.HIGH)
        GPIO.output(33, GPIO.LOW)
# 右後輪向前轉
def rear_right_forward():
        GPIO.output(40, GPIO.HIGH)
        GPIO.output(35, GPIO.HIGH)
        GPIO.output(37, GPIO.LOW)

def front_left_back():
        GPIO.output(18, GPIO.HIGH)
        GPIO.output(11, GPIO.LOW)
        GPIO.output(13, GPIO.HIGH)

def front_right_back():
        GPIO.output(22, GPIO.HIGH)
        GPIO.output(15, GPIO.LOW)
        GPIO.output(29, GPIO.HIGH)

def rear_left_back():
        GPIO.output(38, GPIO.HIGH)
        GPIO.output(31, GPIO.LOW)
        GPIO.output(33, GPIO.HIGH)

def rear_right_back():
        GPIO.output(40, GPIO.HIGH)
        GPIO.output(35, GPIO.LOW)
        GPIO.output(37, GPIO.HIGH)
# 前進,4輪全部向前轉
def forward():
        reset()
        front_left_forward()
        front_right_forward()
        rear_left_forward()
        rear_right_forward()
# 後退,4輪全部向後轉
def back():
        reset()
        front_left_back()
        front_right_back()
        rear_left_back()
        rear_right_back()
# 前向左轉,右邊兩個輪子向前轉
def front_left_turn():
        reset()
        front_right_forward()
        rear_right_forward()
        time.sleep(0.3)
        reset()
# 前向右轉
def front_right_turn():
        reset()
        front_left_forward()
        rear_left_forward()
        time.sleep(0.3)
        reset()
# 後向左轉
def rear_left_turn():
        reset()
        rear_left_back()
        front_left_back()
        time.sleep(0.3)
        reset()
# 後向右轉
def rear_right_turn():
        reset()
        rear_right_back()
        front_right_back()
        time.sleep(0.3)
        reset()
# 停止
def stop():
        reset()
# 測試各個功能點
if __name__ == "__main__":
        init()
        reset()
        #front_left_forward()
        #front_right_forward()
        #rear_left_forward()
        #rear_right_forward()
        forward()
        time.sleep(2)
        back()
        time.sleep(2)
        front_left_turn()
        time.sleep(1)
        front_right_turn()
        time.sleep(1)
        rear_left_turn()
        time.sleep(1)
        rear_right_turn()
        stop()
        GPIO.cleanup()
驅動程式測試完畢以後,我們就可以寫一個HTTP server來提供通過HTTP的方式呼叫驅動介面。python實現HTTP Server不做詳細介紹,我們直接使用最簡單的方式即可,畢竟不會涉及多執行緒、併發。 參考程式如下: rasphttp2.py #!/usr/bin/python2
#coding=utf-8
from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
import urllib
import L298N_car2 as car
from abc import ABCMeta, abstractmethod

class DispatcherHandler(BaseHTTPRequestHandler):
        def do_GET(self):
                print 'client:', self.client_address, 'reuest path:', self.path, \
                                'command:', self.command
                #query = urllib.splitquery(self.path)
                query= self.path.split('?', 1)
                action = query[0]
                params = {}
                if len(query) == 2:
                        for key_value in query[1].split('&'):
                                kv = key_value.split('=')
                                if len(kv) == 2:
                                        params[kv[0]] = urllib.unquote(kv[1]).decode("utf-8", "ignore")                 runCar = RunCar()
                buf = {}
                if self.path.startswith("/car?"):
                        buf["return"] = runCar.action(params)
                else:
                        buf["return"] = -1
                self.protocal_version = "HTTP/1.1"
                self.send_response(200)
                self.send_header("Content-type", "application/json; charset=UTF-8")
                #self.send_header("Content-type", "test/html; charset=UTF-8")
                self.send_header("Pragma", "no-cache")
                self.send_header("Cache-Control", "no-cache")
                self.end_headers()
                self.wfile.write(buf)

        def do_POST(self):
                self.send_error(404)

class Job():
        __metaclass__ = ABCMeta
        @abstractmethod
        def action(self, params):
                pass
class RunCar(Job):
        def __init__(self):
                car.init()
        #子類必須實現父類的抽象方法,否則例項化時會報錯
        def action(self, params):
                print params
                act = int(params['a'])
                if act == 1:
                        car.forward()
                        return 1
                if act == 2:
                        car.back()
                        return 1
                if act == 3:
                        car.front_left_turn()
                        return 1
                if act == 4:
                        car.front_right_turn()
                        return 1
                if act == 5:
                        car.rear_left_turn()
                        return 1
                if act == 6:
                        car.rear_right_turn()
                        return 1
                if act == 0:
                        car.stop()
                        return 1
                else:
                        return -1

if __name__ == "__main__":
        PORT_NUM = 8899
        serverAddress = ("", PORT_NUM)
        server = HTTPServer(serverAddress, DispatcherHandler)
        print 'Started httpserver on port: ', PORT_NUM
        try:
                server.serve_forever()         except KeyboardInterrupt, e:
                pass
        finally:
                server.socket.close()
                print 'Exit...' 執行rasphttp2.py程式(python2 rasphttp2.py或者chmod a+x rasphttp2.py && ./rasphttp2.py
),成功啟動一個HTTP server以後,就可以在瀏覽器中測試介面了,比如:http://raspberryIP:8899/car?a=1 表示測試小車向前進的介面。可以通過HTTP server的後臺輸出觀察控制資訊。
最後,為了更加方便的操作小車,我們可以提供一個簡單的網頁,點選網頁上的按鈕就可以操作我們的小車了。 參考程式碼: car.html <html>
<head>
<meta http-equiv="content-type" content="test/html; charset=UTF-8" />
<title>智慧小車控制端</title>
<script type="text/javascript" src="/jquery.js"></script>
<script type="text/javascript">
$(document).ready(function(){
        $("#forward").click(function(){
                $.ajax({
                        type: "GET",
                        url: "http://192.168.1.111:8899/car",
                        data: {"a": 1},
                        dataType: "json",
                        cache: "false",
                        success: function(data){},
                        error: function(data){}
                });
        });
        $("#stop").click(function(){
                $.ajax({
                        type: "GET",
                        url: "http://192.168.1.111:8899/car",
                        data: {"a": 0},
                        dataType: "json",
                        cache: "false",
                        success: function(data){},
                        error: function(data){}
                });
        });
        $("#back").click(function(){
                $.ajax({
                        type: "GET",
                        url: "http://192.168.1.111:8899/car",
                        data: {"a": 2},
                        dataType: "json",
                        cache: "false",
                        success: function(data){},
                        error: function(data){}
                });
        });
        $("#front_left_turn").click(function(){
                $.ajax({
                        type: "GET",
                        url: "http://192.168.1.111:8899/car",
                        data: {"a": 3},
                        dataType: "json",
                        cache: "false",
                        success: function(data){},
                        error: function(data){}
                });
        });
        $("#front_right_turn").click(function(){
                $.ajax({
                        type: "GET",
                        url: "http://192.168.1.111:8899/car",
                        data: {"a": 4},
                        dataType: "json",
                        cache: "false",
                        success: function(data){},
                        error: function(data){}
                });
        });
});
</script>
</head>
<body>
<button id="forward" name="forward">前進</button>
<button id="back" name="back">後退</button>
<button id="front_left_turn" name="front_left_turn">左前拐</button>
<button id="front_right_turn" name="front_right_turn">右前拐</button>
<button id="stop" name="stop">停止</button>
</body>
</html> 如果你的樹莓派上安裝了HTTP伺服器,則配置一下伺服器根目錄,正確訪問car.hrml即可。我的是lighttpd,參考我的系列文章第6篇,配置好lighttpd.conf檔案以及car.html的許可權(檔案所屬比如是www:www,放在伺服器根目錄等等),通過lighttpd -f /path/lighttpd.conf
即可。
另外,需要注意的是樹莓派的防火牆中要允許使用的埠的網路連線;直接Ctrl+C 終止掉lighttpd在控制檯的執行後,發現它的程序還在,可能是配置檔案中沒有相關的配置;樹莓派每次啟動後,在沒有對GPIO進行設定前,如果接通L198N驅動的電源,小車有2個輪子會直接執行,可能和預設的GPIO輸出有關係;我的PI現在還連的是網線,結合前面的建立無線AP的文章,現在可以用無線網絡卡發射AP,手機連上後訪問控制頁面來進行控制小車了,有點像遙控車,如果無線網絡卡不發射AP而連的是家裡的路由器,則可以通過路由器轉發,遠端控制小車。 最後還是上一張實物圖: