匿名
未登录
登录
丢石头百科
搜索
查看“树莓派智能车AlphaBot教程9:python-bottle”的源代码
来自丢石头百科
名字空间
页面
讨论
更多
更多
页面选项
查看
查看源代码
历史
←
树莓派智能车AlphaBot教程9:python-bottle
因为以下原因,您没有权限编辑本页:
您所请求的操作仅限于该用户组的用户使用:
用户
您可以查看与复制此页面的源代码。
上两章我们介绍了用webiopi实现网页控制,对网页控制也有了一定的了解。这一章我们介绍通过Python bottle实现网页控制。Bottle是一个非常小巧的微型Python web 框架。 Bottle 是一个非常小巧但高效的微型 Python Web 框架,它被设计为仅仅只有一个文件的Python模块,并且除Python标准库外,它不依赖于任何第三方模块。* 路由(Routing):将请求映射到函数,可以创建十分优雅的 URL* 模板(Templates):Pythonic 并且快速的 Python 内置模板引擎,同时还支持 mako, jinja2, cheetah 等第三方模板引擎* 工具集(Utilites):快速的读取 form 数据,上传文件,访问 cookies,headers 或者其它 HTTP 相关的 metadata* 服务器(Server):内置HTTP开发服务器,并且支持 paste, fapws3, bjoern, Google App Engine, Cherrypy 或者其它任何 WSGI HTTP 服务器 安装 Bottle <syntaxhighlight lang="python"> sudo apt-get install python-bottle</syntaxhighlight> 一个Hello World 程序 新建一个HelloWorld.py文件,并输入如下代码保存。 <syntaxhighlight lang="python"> #!/usr/bin/python # -*- conding:utf-8 -*- from bottle import * @route('/helloworld/:yourwords') def hello(yourwords): return 'hello world. ' + yourwords run(host='0.0.0.0', port=8080)</syntaxhighlight> 运行程序: <syntaxhighlight lang="python"> sudo python HelloWorld.py</syntaxhighlight> 在浏览器中输入:http://192.168.6.115:8080/helloworld/Bottle (IP地址改为树莓派实际地址) 就会显示如下页面。(改变helloworld后面的字符串,显示也不会不一样) [[File:170553rea8qe78qvtz10vq.png]] 程序中用到两个Bottle组件,route()和run()函数。 route() 可以将一个函数与一个URL进行绑定,在上面的示例中,route 将 “/hello/:yourwords’ 这个URL地址绑定到了 hello(yourwords) 这个函数上. 我们获得请求后,hello() 函数返回简单的字符串. 最后,run() 函数启动服务器,并且我们设置它在 “localhost” 和 8080 端口上运行 通过Web控制RGB LED。 上面只是小试牛刀,下面再来一个酷炫的。通过网页控制RGB 彩灯,下面以RGB LED HAL模块的实力程序为例。 本程序一共包含四个文件color_picker.png color_range.png index.html main.py。其中前面两个文件为图片,index.html为HTML网页文件,main.py为脚本程序。 === main.py: ===<syntaxhighlight lang="python"> #!/usr/bin/python from bottle import get,request, route, run, static_file,template import time, threading from neopixel import * # LED strip configuration: LED_COUNT = 4 # Number of LED pixels. LED_PIN = 18 # GPIO pin connected to the pixels (must support PWM!). LED_FREQ_HZ = 800000 # LED signal frequency in hertz (usually 800khz) LED_DMA = 5 # DMA channel to use for generating signal (try 5) LED_BRIGHTNESS = 255 # Set to 0 for darkest and 255 for brightest LED_INVERT = False # True to invert the signal (when using NPN transistor level shift) # Create NeoPixel object with appropriate configuration. strip = Adafruit_NeoPixel(LED_COUNT, LED_PIN, LED_FREQ_HZ, LED_DMA, LED_INVERT, LED_BRIGHTNESS) # Intialize the library (must be called once before other functions). strip.begin() strip.show() rgb = 0 light_type = 'static' #'static' 'breath' 'flash' #Access the file root directory @get("/") def index(): global rgb, light_type rgb = 0xffffff light_type = 'static' return static_file('index.html', './') #Static files on the page need to be processed @route('/<filename>') def server_static(filename): return static_file(filename, root='./') #get the rgb value by POST @route('/rgb', method='POST') def rgbLight(): red = request.POST.get('red') green = request.POST.get('green') blue = request.POST.get('blue') #print('red='+ red +', green='+ green +', blue='+ blue) red = int(red) green = int(green) blue = int(blue) if 0 <= red <= 255 and 0 <= green <= 255 and 0 <= blue <= 255: global rgb rgb = (red<<8) | (green<<16) | blue #get the type by POST @route('/lightType', method='POST') def lightType(): global light_type light_type = request.POST.get('type') print("lightType="+light_type) #Light cycle detection control def lightLoop(): global rgb, light_type flashTime = [0.3, 0.2, 0.1, 0.05, 0.05, 0.1, 0.2, 0.5, 0.2] #Blink time flashTimeIndex = 0 #flash time index f = lambda x: (-1/10000.0)*x*x + (1/50.0)*x #Simulate the breathing light with parabola x = 0 while True: if light_type == 'static': for i in range(0,strip.numPixels()): strip.setPixelColor(i, rgb) strip.show() time.sleep(0.05) elif light_type == 'breath': red = int(((rgb & 0x00ff00)>>8) * f(x)) green = int(((rgb & 0xff0000) >> 16) * f(x)) blue = int((rgb & 0x0000ff) * f(x)) _rgb = int((red << 8) | (green << 16) | blue) for i in range(0,strip.numPixels()): strip.setPixelColor(i, _rgb) strip.show() time.sleep(0.02) x += 1 if x >= 200: x = 0 elif light_type == 'flash': for i in range(0,strip.numPixels()): strip.setPixelColor(i, rgb) strip.show() time.sleep(flashTime[flashTimeIndex]) for i in range(0,strip.numPixels()): strip.setPixelColor(i, 0) strip.show() time.sleep(flashTime[flashTimeIndex]) flashTimeIndex += 1 if flashTimeIndex >= len(flashTime): flashTimeIndex = 0 #Open a new thread for rgb light display t = threading.Thread(target = lightLoop) t.setDaemon(True) t.start() #Set the server ip address and port (hint:you set your raspberry ip address before use ) run(host="0.0.0.0", port=8000)</syntaxhighlight> 这里采用的是W2812B灯珠,关于这个控制在这里就不在讲了。 @get("/"), 这作用是创建一个网页静态文件传输通道。流浪器访问时会打开目录下的index,html文件, @route('/<filename>') 这个是用来传输静态文件,两张图片的。 @route('/rgb', method='POST'),@route('/lightType', method='POST')是创建两个URL,分别用来传输RGB的值,和灯控制类型的。 获取到值是分别储存在reg,green,blue和light_type 中。 threading.Thread另外创建一个python线程,执行lightLoop()函数,实时处RGB LED的状态。RGB LED有静态显示,闪烁显示已经呼吸灯显示三种显示方式。 === index.html代码: ===<syntaxhighlight lang="python"> <!doctype html> <html> <head> <meta charset="utf-8"> <!--Adapt to mobile phone size, not allowed to zoom--> <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"> <title>web rgb</title> <script src="http://code.jquery.com/jquery.js"></script> <style type="text/css"> body,div,img{ border:0; margin:0; padding:0;} </style> </head> <body> <div style="width:100%; height:40px; line-height:40px; text-align:center; font-size:20px; color:white; background-color:blue; margin:auto"> Controlling RGB LED with the web </div> <img width="300" height="300" src="color_range.png" id="myimg" style="display:none" alt="range"/> <div style="width:300px; height:300px; position:relative; text-align:center; margin:auto; margin-top:20px; margin-bottom:40px;" id="colorRange"> <canvas id="mycanvas" width="300" height="300"> Your browser does not support the html5 Canvas element </canvas> <img width="30" height="30" src="color_picker.png" id="picker" style="position:absolute; top:135px; left:135px;" alt="picker" /> </div> <div style="font-size:20px;align:center;text-align:center;margin:auto; border:1px solid gray; border-radius:10px; width:320px; height:40px; line-height:40px;"> <div> <input type="radio" name="radio1" value="static" checked/>static <input type="radio" name="radio1" value="breath"/>breath <input type="radio" name="radio1" value="flash"/>flash </div> </div> </body> <script> var RadiusRange = 150; var RadiusPicker = 15; var offsetX = window.screen.width / 2 - RadiusRange; var offsetY = 60; var centerX = offsetX + RadiusRange; var centerY = offsetY + RadiusRange; var colorRange = $('#colorRange')[0]; var colorPicker = $('#picker')[0]; var myCanvas = $('#mycanvas')[0]; var myImg = $('#myimg')[0]; var ctx = myCanvas.getContext('2d'); myImg.onload = function(){ctx.drawImage(myImg, 0, 0);} colorRange.addEventListener('touchstart', touch, false); colorRange.addEventListener('touchmove', touch, false); function touch(e) { var X = e.touches[0].clientX; var Y = e.touches[0].clientY; var x = X - centerX; var y = Y - centerY; if(Math.sqrt(x*x + y*y) < RadiusRange-5) { colorPicker.style.left = X - offsetX - RadiusPicker +'px'; colorPicker.style.top = Y - offsetY - RadiusPicker +'px'; var rgba = ctx.getImageData(X-offsetX, Y-offsetY, 1, 1).data; var red = rgba['0']; var green = rgba['1']; var blue = rgba['2']; $.post('/rgb', {red: red, green: green, blue: blue}); } event.preventDefault(); } $('input').click(function() { var type = this.value; $.post('/lightType', {type: type});; }); </script> </html></syntaxhighlight> 分析: 【1】 index.html文件包含<head>,<body>,<script>三部分 <head>部分中<script src="http://code.jquery.com/jquery.js"></script> 这个语句是通过网页链接引入jquery.js库文件,这个文件和上一张webiopi中的那个文件是一样的。 【2】 <body>部分是设置网页界面,包含一个网页标题,彩色图片,以及三个LED 显示类型选择按键。 【3】 <sritpt>为脚本,脚本中对touchstart事件和touchmove事件监听。这两个事件只在手机端起作用,所以在pc端访问时拖动鼠标,是不能选中颜色的。pc端相对应的事件为:onmousedown、onmousemove。 当事件触发时会调用touch()函数,获取当前的颜色并POST方式发送到/rgb。服务器端接受到传过来的数据就会触发main.py中的rgbLight()函数。 $('input').click(function() 这里是注册按键点击事件。但选择按键被按下时会触发函数,将当前的按键ID通过POST方式发送到/lightType 。从而会触发main.py中的lightType()函数。 [[File:170917y4vxnvmez0zn4v0v.jpeg]] 通过web控制AlphaBot2智能车 下载AlphaBot的程序,程序中web_Control目录即通过Bottle控制小车的。 工程目录下包含AlphaBot2.py,PCA9685.py,index.html,main.py四个文件。其中AlphaBot2.py为小车控制库文件,PCA9685.py这个是舵机控制库文件。主要看index.html和main.py这两个文件。 === main.py代码: ===<syntaxhighlight lang="python"> #!/usr/bin/python # -*- coding:utf-8 -*- from bottle import get,post,run,request,template from AlphaBot import AlphaBot from PCA9685 import PCA9685 import threading Ab = AlphaBot() pwm = PCA9685(0x40) pwm.setPWMFreq(50) #Set the Horizontal servo parameters HPulse = 1500 #Sets the initial Pulse HStep = 0 #Sets the initial step length pwm.setServoPulse(0,HPulse) #Set the vertical servo parameters VPulse = 1500 #Sets the initial Pulse VStep = 0 #Sets the initial step length pwm.setServoPulse(1,VPulse) @get("/") def index(): return template("index") @post("/cmd") def cmd(): global HStep,VStep code = request.body.read().decode() print(code) if code == "stop": HStep = 0 VStep = 0 Ab.stop() elif code == "forward": Ab.forward() elif code == "backward": Ab.backward() elif code == "turnleft": Ab.left() elif code == "turnright": Ab.right() elif code == "up": VStep = -5 elif code == "down": VStep = 5 elif code == "left": HStep = 5 elif code == "right": HStep = -5 return "OK" def timerfunc(): global HPulse,VPulse,HStep,VStep,pwm if(HStep != 0): HPulse += HStep if(HPulse >= 2500): HPulse = 2500 if(HPulse <= 500): HPulse = 500 #set channel 2, the Horizontal servo pwm.setServoPulse(0,HPulse) if(VStep != 0): VPulse += VStep if(VPulse >= 2500): VPulse = 2500 if(VPulse <= 500): VPulse = 500 #set channel 3, the vertical servo pwm.setServoPulse(1,VPulse) global t #Notice: use global variable! t = threading.Timer(0.02, timerfunc) t.start() t = threading.Timer(0.02, timerfunc) t.setDaemon(True) t.start() run(host="0.0.0.0",port="8000")</syntaxhighlight> 程序分析; @get("/") 创建一个创建一个网页文件传输通道,传输index.html文件。 @post("/cmd") 创建一下URL,对接受到的命令做出个中反应。其中forward,backward,turnleft,turnright,stop分别控制小车前进,后退,左转,右转,停止。up,down,left,right,控制舵机上下左右移动。 timerfunc()函数用来处理舵机转动的定时函数。 最后调用run() 函数启动服务器,并且我们设置它在 “localhost” 和 8080 端口上运行。 === index.hmtl代码: ===<syntaxhighlight lang="python"> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>AlphaBot</title> <link href="//cdn.bootcss.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet" media="screen"> <script src="http://code.jquery.com/jquery.js"></script> <script> $(function(){ var isTouchDevice = "ontouchstart" in document.documentElement ? true : false; var BUTTON_DOWN = isTouchDevice ? "touchstart" : "mousedown"; var BUTTON_UP = isTouchDevice ? "touchend" : "mouseup"; $("button").bind(BUTTON_DOWN,function(){ $.post("/cmd",this.id,function(data,status){ }); }); $("button").bind(BUTTON_UP,function(){ $.post("/cmd","stop",function(data,status){ }); }); }); </script> <style type="text/css"> button { margin: 10px 15px 10px 15px; width: 50px; height: 50px; } input { margin: 10px 15px 10px 15px; width: 50px; height: 50px; } </style> </head> <body> <div id="container" class="container" align="center"> <img width="320" height="240" src="http://192.168.10.130:8080/?action=stream"><br/> <table align="center"> <tr> <td align="center"><b>Motor Contrl</b></td> <td align="center"><b>Servo Contrl</b></td> </tr> <tr> <td> <div align="center"> <button id="forward" class="btn btn-lg btn-primary glyphicon glyphicon-circle-arrow-up"></button> </div> <div align="center"> <button id='turnleft' class="btn btn-lg btn-primary glyphicon glyphicon-circle-arrow-left"></button> <button id='turnright' class="btn btn-lg btn-primary glyphicon glyphicon-circle-arrow-right"></button> </div> <div align="center"> <button id='backward' class="btn btn-lg btn-primary glyphicon glyphicon-circle-arrow-down"></button> </div> </td> <td> <div align="center"> <button id="up" class="btn btn-lg btn-primary glyphicon glyphicon-circle-arrow-up"></button> </div> <div align="center"> <button id='left' class="btn btn-lg btn-primary glyphicon glyphicon-circle-arrow-left"></button> <!--<button id='stop' class="btn btn-lg btn-primary glyphicon glyphicon-stop"></button>--> <button id='right' class="btn btn-lg btn-primary glyphicon glyphicon-circle-arrow-right"></button> </div align="center"> <div align="center"> <button id='down' class="btn btn-lg btn-primary glyphicon glyphicon-circle-arrow-down"></button> </div> </td> </tr> </table> <input type="range" min="0.0" max="100.0", style="width:300px";> </div> </body> </html></syntaxhighlight> 分析: 头文件中通过网页链接引入一个css样式文件,以及jquery.js文件。 脚本中首先判断是移动端还是PC端。如果是移动端则注册"touchstart",“touchend”事件,如果是PC端则注册“mousedown”,mouseup事件。 当按键按下时,发送按键的id号到/cmd。当按键释放时,发送停止命令“stop”到/cmd。 网页主体中通过引入mjpgs-streamer的链接引入视频窗口。设置图像大小为高240,宽320。 <img width="320" height="240" src="http://192.168.10.130:8080/?action=stream"> 此ip地址以及端口号须改为树莓派实际的ip地址和端口号。
返回至
树莓派智能车AlphaBot教程9:python-bottle
。
导航
导航
首页
最近更改
随机页面
MediaWiki帮助
首页
首页
树莓派
主机
配件包
外壳
键鼠
电源
扩展板
显示屏
墨水屏
摄像模块
通信模块
继电器
电机驱动板
游戏机
产品分类
树莓派
Arduino
micro:bit
STM32
Espressif
WiFi模块
蓝牙模块
无线模块
LoRa模块
4G模块
GSM
GPRS
以太网
导航模块
北斗卫星
GPS
LCD
墨水屏
OLED
摄像头
USB模块
串口模块
RS232
RS485
CAN
传感器
温度模块
湿度模块
气压模块
继电器
电机模块
指纹模块
电平转换
音频模块
编程器
Wiki工具
Wiki工具
特殊页面
页面工具
页面工具
用户页面工具
更多
链入页面
相关更改
页面信息
页面日志