在系列文章第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
智能小车控制端
前进
后退
左前拐
右前拐
停止
如果你的树莓派上安装了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而连的是家里的路由器,则可以通过路由器转发,远程控制小车。
最后还是上一张实物图: