前期内容提要:
- 【树莓派-网络监控(1)分析准备】基于树莓派搭建可视化可远程遥控网络监控——工程分析及前期准备
- 【树莓派-网络监控(2)画面传输】基于mjpg-stream实现监控画面的传输
- 【树莓派-网络监控(3)角度遥控】基于python3控制两自由度舵机,实现摄像头拍摄角度的遥控
在基于python3成功控制两自由度舵机,实现了摄像头拍摄角度的遥控后,这一章需要解决的问题是如何搭建一个能够控制摄像头的网页,实现前后端的数据交互,完成网络摄像头控制功能雏形。更进一步来理解,即如何通过操控Web网页的方式给direction赋值,实现网页对摄像头拍摄角度的实时操控。

基本思路:
网络监控摄像头的控制端应当允许多用户同时对监控予以查看和操作,摄像头监控画面角度的调整常常也需要一系列实时遥控后才能达到预期角度。这就决定了控制端应当能够承受大量的http持久连接以及瞬时的高并发(C10K问题)。这是通常基于多线程的服务器很难应对的。基于此工程选择Python异步io框架Tornado(服务器和框架的集合体)。
- Tornado作为一款轻量级的Web框架,类似于Python web框架
Web.py,拥有异步非阻塞IO的处理方式。 - Tornado作为一款有着优异性能的Web服务器,具有较为出色的抗负载能力。
据此,工程配套使用Tornado框架及其服务器以最大化利用Tornado性能;此外,将新建一个网页监控Web控制页面(/index.html),调用JavaScript和jQuery方法,监听Web端操作;最后则是使用Ajax将控制页面(/index.html)命令(给direction赋的值)传输回摄像头控制主程式(/index.py)并予以执行。
需求
实现工具
需要修改(新建)的文档
搭建Web服务(解决控制端存在的C10K问题)
Tornado框架及其配套服务器
在原index.py中加工
搭建Web控制页面,监听用户控制命令
JavaScript(键盘) + jQuery(鼠标、手指触摸)
新建index.html
发送Web端控制命令至摄像头控制主程式
Ajax
index.py、index.html
一、基于Tornado搭建Web服务(Liunx)
sudo pip install tornadowget https://pypi.python.org/packages/source/t/tornado/tornado-4.3.tar.gztar xvzf tornado-4.3.tar.gzcd tornado-4.3python setup.py buildsudo python setup.py install

二、搭建Tornado框架(/index.py)
值得注意,由于实现摄像头拍摄角度的遥控中我们使用的是python3环境,为了工程的正常开展,在这里我们同样选择在python3环境下搭建Tornado框架。
sudo apt-get install python3-tornado

在index.py文件最前面填加代码块前六行内容,其中第六行指定了访问端口default=80;在文件末尾加上如下一个程式执行入口,一个Tornado框架的雏形就搭建完成了。
from tornado.web import Application, RequestHandlerfrom tornado.ioloop import IOLoopfrom tornado.options import define, options, parse_command_linefrom tornado.httpserver import HTTPServerimport os.pathdefine("port",default=80,type=int)if __name__ == '__main__':tornado.options.parse_command_line()app = tornado.web.Application(handlers=[(r"/",IndexHandler)],static_path=os.path.join(os.path.dirname(__file__), "static"),)http_server = tornado.httpserver.HTTPServer(app)http_server.listen(options.port)tornado.ioloop.IOLoop.instance().start()
三、搭建Web控制页面(/index.html)
Web控制页面需要通过接受用户的输入,向摄像头控制主程式(/index.py)传输对应的direction值。为便于理解,我们先将这一系列行为简化为一个动作:go(direction值)
go(‘w’)-向上;go(‘s’)-向下;go(‘a’)-向左;go(‘d’)-向右;go(‘q’)-复位
由于工程需要搭建一个响应式的兼容移动端和电脑端的多平台控制页面,因此除按键控制和鼠标点击按钮控制舵机外,还需要加入触摸事件以适配移动端的操作,归纳起来Web控制方式和实现方法如下:
需求
实现方法
实现工具
(键盘)按键控制——PC端
调用onkeydown事件后通过window.event获取按键编码
JavaScript
(鼠标)点击控制——PC端
调用mousedown、mouseup事件
jQuery
(手指)按压控制——移动端
使用touchstart、touchend触摸事件
jQuery
1. 通过JavaScript(onkeydown)实现(键盘)按键控制
监控拍摄画面角度上下左右和复位,我选用了键盘的W(上)S(下)A(左)D(右)R(复位)按键进行操控。主要方式是通过onkeydown事件,通过window.event获取按键编码后,调用abc方法。
<script type="text/javascript">function go(k){$.post('/',{k:k},function(){});}$(function(){window.document.onkeydown = abc;function abc(env){env = (env) ? env : window.event;if(env.keyCode=='87'){go('w');}if(env.keyCode=='83'){go('s');}if(env.keyCode=='65'){go('a');}if(env.keyCode=='68'){go('d');}if(env.keyCode=='81'){go('q');}}});</script>
按键可以通过
console.log(env.keyCode)查看。常用按键编码:w—87;s—83;a—65;d—68;q—81;e—69;z—90;x—88。
2. 通过jQuery(mousedown、mouseup)实现(鼠标)点击控制
根据工程需求,(鼠标)点击控制至少应该有5个按钮,点击后分别控制舵机的水平(左、右),垂直(上、下)移动和复位(reset)。由于点击按钮需要图形可视化,因此首先需要建立一个图形界面,在index.html写入:
<!DOCTYPE html><html><!--[if IE 9 ]><html class="ie9"><![endif]--><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" /><meta name="format-detection" content="telephone=no"><meta charset="UTF-8"><script src="http://libs.baidu.com/jquery/1.9.0/jquery.js"></script><title>Camera PLATFORM</title><style type="text/css">#main{width: 150px;height: 150px;background: #ccc;}#main span{width: 50px;height: 50px;float: left;z-index: 999;}#main span.on2{background: #ff00ff;}</style><body><div style="position: absolute;left: 50%;transform:translateX(-50%)" ><span></span><span class="on2 before"></span><span></span><span class="on2 left"></span><span class="on2 reset" style="background: yellow"></span><span class="on2 right"></span><span></span><span class="on2 cabk"></span><span></span></div></body></html>

其次使用jQuery调用mousedown(点击)、mouseup(释放)事件。i用来存储定时器。发送频率设置为0.1秒(100毫秒)一次。
var i = null;$('.before').mousedown(function(){i = setInterval(function(){go('w');},100);});$('.left').mousedown(function(){i = setInterval(function(){go('a');},100);});$('.right').mousedown(function(){i = setInterval(function(){go('d');},100);});$('.cabk').mousedown(function(){i = setInterval(function(){go('s');},100);});$('.reset').mousedown(function(){i = setInterval(function(){go('q');},100);});$('#main span').mouseup(function(){clearInterval(i);});
3. 通过jQuery(touchstart、touchend)实现(手指)按压控制
实现方式和mousedown、mouseup类似,但语法有些许差别,注意分辨。
var i = null;$('.before').on('touchstart',function(e) {i = setInterval(function(){go('w');},100);});$('.left').on('touchstart',function(e) {i = setInterval(function(){go('a');},100);});$('.right').on('touchstart',function(e) {i = setInterval(function(){go('d');},100);});$('.cabk').on('touchstart',function(e) {i = setInterval(function(){go('s');},100);});$('.reset').on('touchstart',function(e) {i = setInterval(function(){go('q');},100);});$('#main span').on('touchend',function(e) {clearInterval(i);});
四、主程式(/index.py)接收Ajax传递过来的direction值并予以执行
在Tornado框架中,请求和响应全部封装在RequestHandler类中了,因此仅需要在index.py中创建控制器IndexHandler,用于接受控制页面(/index.html)通过Ajax发送过来的direction值,在其进行按键判断后(go('w')-向上;go('s')-向下;go('a')-向左;go('d')-向右;go('q')-复位),调用对应的舵机转动方法(在上一篇文章中为程式写了一个run(dir)执行方法)。
class IndexHandler(tornado.web.RequestHandler):def get(self):self.render("index.html",encoding="utf8")def post(self):arg = self.get_argument('k')if(arg=='w'):dir = "W"run(dir)elif(arg=='s'):dir = "S"run(dir)elif(arg=='a'):dir = "A"run(dir)elif(arg=='d'):dir = "D"run(dir)elif(arg=='q'):dir = "Q"run(dir)else:return Falseself.write(arg)
注意: Python3环境下,在get index.html 后 需要申明编码格式:encoding=“utf8”
至此,一个网络摄像头的雏形已搭建完成,调试运行:
sudo python3 index.py
由于之前已经设置:define(“port”,default=80,type=int),因此浏览器输入树莓派IP:80访问摄像头网页控制端。

通过按键、鼠标点击、移动端手指按压,可以看到摄像头已经可以正常旋转了。如果同时打开网页树莓派IP:8080以及网页树莓派IP:80,基本实现了在局域网环境下对摄像头拍摄角度的实时控制。

调试常见错误:端口占用:
在停止调试后,我没有使用 Ctrl+C中止进程,而是使用 Ctrl+Z跳出,这导致端口被持续占用,下次的调试无法正常开启,报错:

- 解决方法:
使用lsof命令,查看端口(TCP、UDP)对应的进程,手动结束,之后调试恢复正常。
sudo apt-get install lsof

sudo lsof -i:80sudo kill -9 进程号

至此,我们基于python异步io框架Tornado,结合JavaScript的onkeydown事件、 jQuery的mousedown、mouseup、touchstart、touchend事件以及Ajax,实现了监控遥控命令与web网页指定按键和鼠标(屏幕)点击事件的绑定,同时也实现了局域网环境下通过网页对摄像头拍摄角度的实时控制(相关源码已上传),完成了前后端的数据交互。下一章的主要目标是将监控实时画面与遥控功能集成到一个网页中,并进一步优化前端控制页面,完成内网环境下摄像头的全部搭建和调试。
后期内容提要:
- 【树莓派-网络监控(5)前端搭建】基于iframe标签,集成监控实时画面与遥控功能,制作并优化响应式控制页面
- 【树莓派-网络监控(6)远程访问】基于内网穿透实现树莓派监控的公网远程访问与遥控
如果您有任何疑问或者好的建议,期待你的留言与评论!
