之前写了一篇手把手树莓派小车教程(二)之——基于Tornado框架的网页控制小车(按键+鼠标点击)。当时只记录了如何通过Tornado框架能让网页控制小车跑起来(以恒定速度),但是项目中小车还是需要变速行驶的。
基于这样的需求,我在之前教程(二) 的基础上,初步实现了网页控制小车速度可调(顺序执行)。
实现小车的变速有多种方法。像之前笔者介绍的L298N驱动模块中有一对pwm控制的引脚,当时买的时候如图一样是用跳线帽连起来的。这种方法(通过PWM控制)也在csdn上搜到了很多教程树莓派 Python GPIO编程控制小车的运动。
但这种方法的话还要多设置频率、多引出两条杜邦线,做起来会比较麻烦。
因此笔者采用了通过 控制高低电平交错输出时间 来达到变速的目的。
之前的代码中每次循环输出的电平都为高电平。所以只要在循环中增加低电平的输出,即可达到速度的减缓效果。且低电平占比越高,速度会越慢。但低电平输出时间不能过大,否则小车会出现边开边卡的现象。
首先定义低电平输出的函数:
#变速
def change(tf):
GPIO.output(IN1,False)
GPIO.output(IN2,False)
GPIO.output(IN3,False)
GPIO.output(IN4,False)
time.sleep(tf)
#GPIO.cleanup()
可以看到所有的输出都为False。若小车需要增加停止行驶按键的话可以直接引用这个函数。
基于变速函数与上一个教程手把手树莓派小车教程(二)之——基于Tornado框架的网页控制小车(按键+鼠标点击)给出的代码。我们可以给出如下的控制代码。
xiaoche.py:
# coding:utf-8
import RPi.GPIO as GPIO
import time
import sys
import tornado.ioloop
import tornado.web
import tornado.httpserver
import tornado.options
from tornado.options import define,options
#GPIO.setmode(GPIO.BOARD)
#端口号定义
define("port",default=8080,help="run on the given port",type=int)
#IO口定义
IN1 = 11
IN2 = 12
IN3 = 13
IN4 = 15
#IO口初始化
def init():
GPIO.setmode(GPIO.BOARD)
GPIO.setwarnings(False)
GPIO.setup(IN1,GPIO.OUT)
GPIO.setup(IN2,GPIO.OUT)
GPIO.setup(IN3,GPIO.OUT)
GPIO.setup(IN4,GPIO.OUT)
#向右
def right(tf):
GPIO.output(IN1,GPIO.HIGH)
GPIO.output(IN2,GPIO.LOW)
GPIO.output(IN3,False)
GPIO.output(IN4,False)
time.sleep(tf)
#GPIO.cleanup()
#向左
def left(tf):
GPIO.output(IN1,GPIO.LOW)
GPIO.output(IN2,GPIO.HIGH)
GPIO.output(IN3,False)
GPIO.output(IN4,False)
time.sleep(tf)
#GPIO.cleanup()
#向前
def before(tf):
GPIO.output(IN1,False)
GPIO.output(IN2,False)
GPIO.output(IN3,GPIO.HIGH)
GPIO.output(IN4,GPIO.LOW)
time.sleep(tf)
#GPIO.cleanup()
#向后
def cabk(tf):
GPIO.output(IN1,False)
GPIO.output(IN2,False)
GPIO.output(IN3,GPIO.LOW)
GPIO.output(IN4,GPIO.HIGH)
time.sleep(tf)
#GPIO.cleanup()
#左上
def zuoshang(tf):
GPIO.output(IN1,GPIO.HIGH)
GPIO.output(IN2,GPIO.LOW)
GPIO.output(IN3,GPIO.HIGH)
GPIO.output(IN4,GPIO.LOW)
time.sleep(tf)
#GPIO.cleanup()
#右上
def youshang(tf):
GPIO.output(IN1,GPIO.HIGH)
GPIO.output(IN2,GPIO.LOW)
GPIO.output(IN3,GPIO.LOW)
GPIO.output(IN4,GPIO.HIGH)
time.sleep(tf)
#GPIO.cleanup()
#右下
def youxia(tf):
GPIO.output(IN1,GPIO.LOW)
GPIO.output(IN2,GPIO.HIGH)
GPIO.output(IN3,GPIO.LOW)
GPIO.output(IN4,GPIO.HIGH)
time.sleep(tf)
#GPIO.cleanup()
#右上
def zuoxia(tf):
GPIO.output(IN1,GPIO.LOW)
GPIO.output(IN2,GPIO.HIGH)
GPIO.output(IN3,GPIO.HIGH)
GPIO.output(IN4,GPIO.LOW)
time.sleep(tf)
#GPIO.cleanup()
#变速
def change(tf):
GPIO.output(IN1,False)
GPIO.output(IN2,False)
GPIO.output(IN3,False)
GPIO.output(IN4,False)
time.sleep(tf)
#GPIO.cleanup()
change_time=0.01# IO输出低电平初始时间
class IndexHandler(tornado.web.RequestHandler):
def get(self):
self.render("xiaoche.html")
def post(self):
init()
sleep_time=0.1 # IO输出高电平时间
global change_time # 引用上面的change_time 若直接在这里定义会导致变量一直更新在0.01 无法变速
arg=self.get_argument('k')
if(arg=='w'):
before(sleep_time)
change(change_time) #通过高低电平交错实现小车的变速
#print("1")
elif(arg=='x'):
cabk(sleep_time)
change(change_time)
elif(arg=='a'):
left(sleep_time)
change(change_time)
elif(arg=='d'):
right(sleep_time)
change(change_time)
elif(arg=='q'):
zuoshang(sleep_time)
change(change_time)
elif(arg=='z'):
youshang(sleep_time)
change(change_time)
elif(arg=='e'):
zuoxia(sleep_time)
change(change_time)
elif(arg=='c'):
youxia(sleep_time)
change(change_time)
elif(arg=='j'):
if(change_time>0.01):
change_time=change_time-0.001;
elif(arg=='k'):
if(change_time<0.03):
change_time=change_time+0.001;
else:
return False
self.write(arg)
if __name__ == '__main__':
tornado.options.parse_command_line()
app = tornado.web.Application(handlers=[(r"/",IndexHandler)])
http_server = tornado.httpserver.HTTPServer(app)
http_server.listen(options.port)
print("Demo is runing at 192.168.1.102:8888")
tornado.ioloop.IOLoop.instance().start()
对应网页给出的xiaoche.html(与上面代码中render内的名称一样 且代码放同一目录下):
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="http://libs.baidu.com/jquery/1.9.0/jquery.js">script>
<title>小车title>
head>
<body>
<script type="text/javascript">
function go(k){
$.post('/',{
k:k},function(){
});
}
$(function(){
window.document.onkeydown = abc;
function abc(ev){
ev = (ev) ? ev : window.event;
// 指定方向键 ,w(上-->87),a(左-->83),s(下-->65),d(右-->67)
if(ev.keyCode=='87'){
<!-- console.log('w'); -->
go('w');
}
if(ev.keyCode=='65'){
<!-- console.log('a'); -->
go('a');
}
if(ev.keyCode=='88'){
<!-- console.log('s'); -->
go('x');
}
if(ev.keyCode=='68'){
<!-- console.log('d'); -->
go('d');
}
if(ev.keyCode=='81'){
<!-- console.log('w'); -->
go('q');
}
if(ev.keyCode=='69'){
<!-- console.log('w'); -->
go('e');
}
if(ev.keyCode=='90'){
<!-- console.log('w'); -->
go('z');
}
if(ev.keyCode=='67'){
<!-- console.log('w'); -->
go('c');
}
if(ev.keyCode=='74'){
<!-- console.log('j'); -->
go('j');
}
if(ev.keyCode=='75'){
<!-- console.log('k'); -->
go('k');
}
}
var i= null;
$('.before').mousedown(function(){
i = setInterval(function(){
<!-- console.log('w'); -->
go('w');
},50);
});
$('.left').mousedown(function(){
i = setInterval(function(){
<!-- console.log('a'); -->
go('a');
},50);
});
$('.cabk').mousedown(function(){
i = setInterval(function(){
<!-- console.log('x'); -->
go('x');
},50);
});
$('.right').mousedown(function(){
i = setInterval(function(){
<!-- console.log('d'); -->
go('d');
},50);
});
$('.zuoshang').mousedown(function(){
i = setInterval(function(){
<!-- console.log('q'); -->
go('q');
},50);
});
$('.youshang').mousedown(function(){
i = setInterval(function(){
<!-- console.log('e'); -->
go('z');
},50);
});
$('.zuoxia').mousedown(function(){
i = setInterval(function(){
<!-- console.log('z'); -->
go('e');
},50);
});
$('.youxia').mousedown(function(){
i = setInterval(function(){
<!-- console.log('c'); -->
go('c');
},50);
});
$('.jiasu').mousedown(function(){
i = setInterval(function(){
<!-- console.log('j'); -->
go('j');
},100);
});
$('.jiansu').mousedown(function(){
i = setInterval(function(){
<!-- console.log('k'); -->
go('k');
},100);
});
$('#main span').mouseup(function(){
clearInterval(i);
});
});
script>
<style type="text/css">
#main{
width: 150px;height: 150px;background: #ccc;}
#main span{
width: 50px;height: 50px;float: left;}
#main span.on2{
background: #ff00ff;}
#main span.on3{
background: #555555;position: absolute;left: 8px;top: 8px;}
#main span.on4{
background: #555555;position: absolute;left: 108px;top: 8px;}
#main span.on5{
background: #555555;position: absolute;left: 8px;top: 108px;}
#main span.on6{
background: #555555;position: absolute;left: 108px;top: 108px;}
#main span.on7{
background: #10e62a;position: absolute;left: 300px;top: 58px;}
#main span.on8{
background: #f10606;position: absolute;left: 370px;top: 58px;}
style>
<div id="main">
<span>span>
<span class="on2 before">span>
<span>span>
<span class="on2 left">span>
<span>span>
<span class="on2 right">span>
<span>span>
<span class="on2 cabk">span>
<span>span>
<span class="on3 zuoshang">span>
<span>span>
<span class="on4 zuoxia">span>
<span>span>
<span class="on5 youshang">span>
<span>span>
<span class="on6 youxia">span>
<span>span>
<span class="on7 jiasu">span>
<span>span>
<span class="on8 jiansu">span>
<span>span>
div>
body>
html>
网页显示内容如下图所示(无标注):
该网页较之前多了两个按键用来控制加速与减速。对应的键盘按键为j与k。运行python文件后在网页上输入IP+端口号,即可出现如下图所示的结果。
无论是按键还是鼠标都可以令python文件(2与3版本都可以)出现如下图所示的记录:
完成上面这些步骤后,小车基本可以通过按键与鼠标事件来控制小车变速行驶了。但是这种方法按键还不能一起捕捉(鼠标更不用说),所以小车还必须停下后才能变速,导致返回的数据都是单个的,小车只能顺序执行每一个事件,还不能实现同步加减速的效果。
树莓派4B-Python-控制L298N
树莓派 Python GPIO编程控制小车的运动
手把手树莓派小车教程(一)之——小车跑起来
手把手树莓派小车教程(二)之——基于Tornado框架的网页控制小车(按键+鼠标点击)
总的来说至少能实现小车变速行驶了,不仅是不用通过PWM的方式,还相对于PWM方式方便了很多。接下来就是实现同步的问题了。希望我也能够尽快实现,出来下一个教程。
感谢各位观看,如有不足,欢迎在评论内留言与讨论。如果觉得写得好的,可以给我点赞+收藏+关注哦,再次感谢各位!