项目场景:
前两天笔者写了一份手把手树莓派小车教程(一)之——小车跑起来的教程。当时只是让小车能动起来,不过项目中的小车肯定是需要一个用户界面能够操控的。
能看到这一部分的读者们可能基本都会简单的控制小车了。接下来这篇博客我们详细讲解一下如何用网页web端来控制小车运行。
Tornado框架:
笔者对网页控制小车需要用到基于python的异步io框架Tornado。首先需要在树莓派上搭建基于Tornado搭建Web服务(Liunx):
sudo pip install tornado
wget https://pypi.python.org/packages/source/t/tornado/tornado-4.3.tar.gz
tar xvzf tornado-4.3.tar.gz
cd tornado-4.3
python setup.py build
sudo python setup.py install
笔者是用的python3环境。为了工程的正常开展,在这里我们同样选择在python3环境下搭建Tornado框架。
sudo apt-get install python3-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)
IN1 = 11
IN2 = 12
IN3 = 13
IN4 = 15
def init():
GPIO.setmode(GPIO.BOARD)
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()
class IndexHandler(tornado.web.RequestHandler):
def get(self):
self.render("xiaoche.html")
def post(self):
init()
sleep_time=0.1
arg=self.get_argument('k')
if(arg=='w'):
before(sleep_time)
#print("1")
elif(arg=='x'):
cabk(sleep_time)
elif(arg=='a'):
left(sleep_time)
elif(arg=='d'):
right(sleep_time)
elif(arg=='q'):
zuoshang(sleep_time)
elif(arg=='z'):
youshang(sleep_time)
elif(arg=='e'):
zuoxia(sleep_time)
elif(arg=='c'):
youxia(sleep_time)
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)
#print("1")
http_server.listen(options.port)
print("Demo is runing at 192.168.1.102:8080")
tornado.ioloop.IOLoop.instance().start()
#print("1")
可以看到与之前的代码是多了对tornado框架的运用,若想了解该框架原本如何运用,可以参考树莓派4B上 tornado的安装 以及访问传参。可以看到我的代码是要控制8个不同的方向的,需要用到的按键是键盘上绕s一圈的英文字母qweadzxc。中间有部分注释掉的代码是用来调试的。后面有个print(“Demo is runing at 192.168.1.102:8080”)。中间的192.168.1.102是我树莓派连接时的IP地址,后面8080是自己设定的端口号。
接下来是web网页的代码编写。网页文件名称需要和self.render()里面的名称一样。且与python文件放到同一目录下。
xiaoche.html:
<!DOCTYPE html>
<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>
<!--keyCode 键盘码 在键盘事件发生的时候 记录对应按的哪个键-->
<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');
}
}
var i= null;
$('.before').mousedown(function(){
i = setInterval(function(){
<!-- console.log('w'); -->
go('w');
},40);
});
$('.left').mousedown(function(){
i = setInterval(function(){
<!-- console.log('a'); -->
go('a');
},40);
});
$('.cabk').mousedown(function(){
i = setInterval(function(){
<!-- console.log('x'); -->
go('x');
},40);
});
$('.right').mousedown(function(){
i = setInterval(function(){
<!-- console.log('d'); -->
go('d');
},40);
});
$('.zuoshang').mousedown(function(){
i = setInterval(function(){
<!-- console.log('q'); -->
go('q');
},40);
});
$('.youshang').mousedown(function(){
i = setInterval(function(){
<!-- console.log('e'); -->
go('z');
},40);
});
$('.zuoxia').mousedown(function(){
i = setInterval(function(){
<!-- console.log('z'); -->
go('e');
},40);
});
$('.youxia').mousedown(function(){
i = setInterval(function(){
<!-- console.log('c'); -->
go('c');
},40);
});
$('#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;}
</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>
</div>
</body>
</html>
如下图所示,我的html文件是这个样子的。这里请注意,我现在是直接从如图所示文件目录下打开的xiaoche.html文件。此时的文件不具备控制能力。
上面的代码中除了定义了这几个方块外,还创立了键盘控制函数、鼠标点击事件函数,go()函数用来接收前面两个函数传来的参数;可以注意到键盘是定义了几个数字,用来对应相应的键盘字母。
$(function(){
window.document.onkeydown = abc;
function abc(ev){
ev = (ev) ? ev : window.event;
寻找字母方法:写到如上图函数的时候。点开html文件。先按键盘F12,在弹出来窗口内找到如下图所示console处。按下任何键盘都会给出相应的数字。记录后即可使用。
注意:
最后关于鼠标点击事件上的数字40,与python文件中主函数有的time.sleep(0.1)是对小车接收信号速度的调整。接收信号过多(过快)可能导致小车一直在跑无法停止。过少(过慢)则会边开边卡,尽量自己多测试,取到适合的位置。
运行调试:
起初看教程的时候我以为直接写完代码,运行之后直接打开html文件即可。但仔细查看资料后才发现是需要登录IP+端口号才行,网页如下图所示:
上图是运行python代码之后用IP+端口号登录的结果,可以看到网页地址与之前的不一样了。此时无论是按键还是点击事件小车都可以做出相应的行为。而且每做一次行为,python运行环境的shell中会多出很多记录:
出现这样的数据,且小车运行不异常。说明小车基本可以由网页直接控制了。希望读者在看完我的记录后能有启发。也能最终调试出这样的一串记录。
参考:
公开课 树莓派教程
树莓派小车调试记录
20-2 树莓派搭建服务器 Tornado Web服务器
树莓派4B上 tornado的安装 以及访问传参
手把手树莓派小车教程(一)之——小车跑起来
树莓派-网络监控(4)数据交互 基于python异步io框架Tornado
总结:
希望我教程能够帮助到在读的各位。本篇文章还是有些局限性的。比如小车现在是恒定速度行驶的,且现在还是最快速度行驶,遥控起来不免有些急促。所以接下来需要将其改为能够调节速度的网页遥控小车。后面会给大家讲解如何实现。
感谢各位观看,如有不足,欢迎在评论内留言与讨论。如果觉得写得好的,可以给我点赞+收藏+关注哦,再次感谢各位!