折腾·树莓派小车

来源:https://www.valieo.com/archives/36/

怕原网站失效,于是复制一份

小车需求简要介绍

  • 采用双轮差速方式移动

双轮方式需要的零件相对较少,省钱即是王道。

  • 通过网络远程控制小车

可通过wifi远程控制,或者配置内网穿透实现任意地点控制。

  • 能够实时传送图像

安装摄像头模块

  • 超声波测距

安装超声波测距模块

材料

  • 智能小车x1 ,淘宝买2WD【包含车子底座、两个电机、两个轮子、电池模块】,20块钱
  • 电机驱动x1,TB6612FNG直流电机驱动模块,30块钱【只能驱动两个轮子】
  • 杜邦线若干
  • 摄像头x1【可选】20块钱
  • 树莓派4B

第一部分安装

小车底座安装

  • 将各个模块安装好,并预留好两个电机的四个线出来
安装小车底座
  • 将智能小车底座安装好
小车底座安装好

TB6612FNG与树莓派连接

不看原理,直接跳到“具体接线图”

TB6612FNG驱动

TB6612FNG驱动如下图所示

img

接口说明:

  1. VM:电机驱动电源输入(2.74-10.8V)
  2. VCC:逻辑电平输入(5.0V)
  3. AO1和AO2:接直流电机A
  4. BO2和BO1:接直流电机B
  5. PWMA:控制电机A的转速
  6. AIN1和AIN2:控制电机A(停止、正转、反转)
  7. STBY:正常工作/待机状态控制端
  8. PWMB:控制电机B的转速
  9. BIN1和BIN2:控制电机B(停止、正转、反转)
  10. GND:接地

AIN1和AIN2用于控制电机A停止、正转、反转,真值表如下

AIN1

0

0

1

AIN2

0

1

0

状态

停止

正转

反转

BIN1和BIN2的真值表同上

树莓派40PIN引脚对照表··

img

树莓派GPIO和TB6612FNG的接线情况如下表

树莓派GPIO接口(BCM编码)

TB6612FNG驱动接口

说明

27

STBY

设置TB6612FNG驱动模块的状态(工作/待机)

18

PWMA

基于PWM控制电机A转速

14

AIN1

控制电机A停止、正转、反转

15

AIN2

控制电机A停止、正转、反转

19

PWMB

基于PWM控制电机B转速

23

BIN1

控制电机B停止、正转、反转

24

BIN2

控制电机B停止、正转、反转

电机驱动和树莓派4B共用一个5V、3A的电源

具体接线图

img

树莓派测试控制电机

安装好后,树莓派测试电机是否可以被正常控制,前进后退左转右转是否正常

先安装RPi.GPIO模块,命令如下:

sudo apt-get install python3-rpi.gpio

安装好后,将下面的代码保存到test.py中,运行测试脚本python3 test.py,如果电机可以运转则正常!!!

代码如下:

#motorTest.py
#导入 GPIO库
import RPi.GPIO as GPIO
import time

#设置 GPIO 模式为 BCM
GPIO.setmode(GPIO.BCM)

#定义引脚
STBY = 27
PWMA = 18
AIN1 = 14
AIN2 = 15
PWMB = 19
BIN1 = 23
BIN2 = 24

#设置 GPIO 的工作方式
GPIO.setup(STBY, GPIO.OUT)
GPIO.setup(PWMA, GPIO.OUT)
GPIO.setup(AIN1, GPIO.OUT)
GPIO.setup(AIN2, GPIO.OUT)
GPIO.setup(PWMB, GPIO.OUT)
GPIO.setup(BIN1, GPIO.OUT)
GPIO.setup(BIN2, GPIO.OUT)
pwma = GPIO.PWM(PWMA,300)
pwmb = GPIO.PWM(PWMB,300)


# 前进或后退(大于零前进,小于零后退)
def goForward(speed):
    if(speed>=0):
        GPIO.output(AIN1,GPIO.LOW)
        GPIO.output(AIN2,GPIO.HIGH)
        GPIO.output(BIN1,GPIO.LOW)
        GPIO.output(BIN2,GPIO.HIGH)
        pwma.start(speed)
        pwmb.start(speed)
        time.sleep(0.02)
    else:
        GPIO.output(AIN2,GPIO.LOW)
        GPIO.output(AIN1,GPIO.HIGH)
        GPIO.output(BIN2,GPIO.LOW)
        GPIO.output(BIN1,GPIO.HIGH)
        pwma.start(-speed)
        pwmb.start(-speed)
        time.sleep(0.02)

# 左转或右转(大于零左转,小于零右转)
def turnLeft(speed):
    if(speed>=0):
        GPIO.output(AIN2,GPIO.LOW)
        GPIO.output(AIN1,GPIO.HIGH)
        GPIO.output(BIN1,GPIO.LOW)
        GPIO.output(BIN2,GPIO.HIGH)
        pwma.start(speed)
        pwmb.start(speed)
        time.sleep(0.02)
    else:
        GPIO.output(AIN1,GPIO.LOW)
        GPIO.output(AIN2,GPIO.HIGH)
        GPIO.output(BIN2,GPIO.LOW)
        GPIO.output(BIN1,GPIO.HIGH)
        pwma.start(-speed)
        pwmb.start(-speed)
        time.sleep(0.02)

def motorStop():
    GPIO.output(AIN1,GPIO.LOW)
    GPIO.output(AIN2,GPIO.LOW)

GPIO.output(STBY,GPIO.HIGH)
#以60%的速度前进
goForward(60)
time.sleep(1)
#以60%的速度后退
goForward(-60)
time.sleep(1)
#左转
turnLeft(60)
time.sleep(2)
#右转
turnLeft(-60)
time.sleep(2)
#停止
motorStop()

pwma.stop()
pwmb.stop()
GPIO.cleanup()

摄像头安装

我使用的是CSI视频接口的摄像头,500万像素【淘宝上买只要20块,不过后面有官方推出的V2摄像头800万像素,要120块以上】

关机、断电、给摄像头安装摄像头。安装好后启动树莓派,输入命令:

vcgencmd get_camera

检测摄像头

  • 如果输出以下内容,则可以直接到“测试摄像头”
supported=1 detected=1, libcamera interfaces=0
  • 如果输出以下内容,则需要一些设置后,才能使用
supported=0 detected=0, libcamera interfaces=0

无法使用摄像头时方法一:

先打开树莓派的配置:

sudo raspi-config
image-20220708231413128

选择Interface Options:

再选择Legacy Camera:

img

在这个页面回答Yes。

系统会显示如下信息:

img

回到主菜单,点击Finish:

img

重启树莓派。

sudo reboot

​

无法使用摄像头时方法二

  • 将方法一中,Legacy Camera再关闭掉,先别重启
  • 再编辑/boot/config.txt,找到gpu_men,设置成128或者更高,我的原来就是128,所以改成了256,然后重启树莓派,可能是显存过低导致的。
  • start_x=1,并重启树莓派
image-20220708231748648

​

​


测试摄像头

打开VNC桌面,将下面代码保存为testcamera.py,并在VNC桌面运行python3 testcamera.py,如果跳出来一个摄像头窗口,则正常!!!

测试摄像头代码:

# 该程序打开摄像头,显示视频,按q拍摄一张照片并储存下来
import cv2

cap = cv2.VideoCapture(0)

while(1):
    # 获得图片
    ret, frame = cap.read()
    # 展示图片
    cv2.imshow("capture", frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        # 存储图片
        cv2.imwrite("camera.jpg", frame)
        break

cap.release()
cv2.destroyAllWindows()
注意:
  • cv2看之前一篇安装opencv文章

完整代码

下载:https://wwd.lanzouy.com/iJp5207kqd9g 密码:7qp0

代码下载好后,安装motion【一个将摄像头运行在后台的web服务】

安装motion

sudo apt install motion -y
  • 编辑/etc/default/motion文件,开启守护进程
sudo nano /etc/default/motion
  • 设置start_motion_daemon=yes
  • 编辑/etc/motion/motion.conf文件
sudo nano /etc/motion/motion.conf

以下仅列出部分配置,详细的配置文档见Motion - Config File Options

#将deamon off 改成 deamon on
deamon on

#设置视频分辨率
width 800
height 600

#视频帧率
framerate 24
stream_maxrate 30

#允许非本机访问总控制页面
webcontrol_localhost off
#允许非本机查看视频监控
stream_localhost off

启动motion

sudo systemctl start motion
sudo motion

安装gpac

sudo apt install gpac -y

安装bottle

pip3 install bottle

先运行一遍上面步骤中的test.py,命令

python3 test.py

再安装Start.py,命令

python3 Start.py

在浏览器中访问树莓派地址:8088

结束motion进程

sudo killall -TERM motion

HC-SR04超声波模块

【此模块我没有做,直接抄的】

HC-SR04实图如下,其有四个引脚,分别为Vcc、Trig、Echo、End

img

HC-SR04模块具体参数如下图(淘宝)

img

HC-SR04模块的工作原理

(1)树莓派向 Trig 脚发送一个 10us 的脉冲信号。
(2) HC-SR04 接收到信号,开始发送超声波,并把 Echo置为高电平,然后准备接收返回的超声波。
(3) HC-SR04 接收到返回的超声波,把 Echo 置为低电平。
(4)Echo 高电平持续的时间就是超声波从发射到返回的时间间隔。
(5)计算距离:
距离(单位:m) = (startTime - endTime) * 声波速度 / 2
声波速度取 343m/s 。

接线

HC-SR04只有4个引脚,接线比较简单,具体见下表

HC-SR04引脚

树莓派GPIO(BCM编码)

说明

Vcc

-

5V电源,可直接用树莓派供电

Gnd

-

接地

Trig

任意GPIO,我使用的是GPIO5

接收树莓派控制信号

Echo

任意GPIO,我使用的是GPIO6

返回测距信息

代码

#导入 GPIO库
import RPi.GPIO as GPIO
import time
  
#设置 GPIO 模式为 BCM
GPIO.setmode(GPIO.BCM)
  
#定义 GPIO 引脚使用BCM编码
TRIG = 5
ECHO = 6
  
#设置 GPIO 的工作方式 (IN / OUT)
GPIO.setup(TRIG, GPIO.OUT)
GPIO.setup(ECHO, GPIO.IN)

# 获取距离信息
def getDistance():
    # 向Trig引脚发送10us的脉冲信号
    GPIO.output(TRIG, GPIO.HIGH)
    time.sleep(0.00001)
    GPIO.output(TRIG, GPIO.LOW)
    
    # 开始发送超声波的时刻
    while GPIO.input(ECHO)==0:
        pass
    startTime=time.time()
 
    # 收到返回超声波的时刻
    while GPIO.input(ECHO)==1:
        pass
    endTime=time.time()

    # 计算距离 距离=(声波的往返时间*声速)/2
    timeDelta = endTime - startTime
    distance = (timeDelta * 34300) / 2
  
    return distance


if __name__ == '__main__':
    try:
        while True:
            dist = getDistance()
            print("Distance = {:.2f} cm".format(dist))
            time.sleep(1)       # 每间隔1秒测量一次
  
        
    except KeyboardInterrupt:
        print("Stopped")
        GPIO.cleanup()

第二部分web控制

Web控制

以下所有代码均已上传至github

valieo/Raspberry-Car

安装bottle库

  • 安装pip

pip是特定于Python的软件包管理器,没有安装pip的按照下述方式安装,安装了跳过此步

安装pip

sudo apt install python2-pip -y

或者安装pip3

sudo apt install python3-pip -y
  • 如果系统中只安装了Python2,那么就只能使用pip。
  • 如果系统中只安装了Python3,那么既可以使用pip也可以使用pip3,二者是等价的。
  • 如果系统中同时安装了Python2和Python3,则pip默认给Python2用,pip3指定给Python3用。

我在这里安装的是pip3,以下代码也仅在python3下测试通过

  • 安装bottle
pip3 install bottle

代码

1. 超声波测距模块

# 超声波测距模块 HC-SR04
import RPi.GPIO as GPIO
import time

class Measure(object):

    def __init__(self, GPIO_TRIG, GPIO_ECHO) -> None:
        super().__init__()
        self.GPIO_TRIG = GPIO_TRIG
        self.GPIO_ECHO = GPIO_ECHO

        GPIO.setmode(GPIO.BCM)
        #设置 GPIO 的工作方式 (IN / OUT)
        GPIO.setup(GPIO_TRIG, GPIO.OUT)
        GPIO.setup(GPIO_ECHO, GPIO.IN)


    def getDistance(self):
        # 向Trig引脚发送10us的脉冲信号
        GPIO.output(self.GPIO_TRIG, True)
        time.sleep(0.00001)
        GPIO.output(self.GPIO_TRIG, False)

        # 开始发送超声波的时刻
        while GPIO.input(self.GPIO_ECHO)==0:
            pass
        startTime=time.time()
 
        # 收到返回超声波的时刻
        while GPIO.input(self.GPIO_ECHO)==1:
            pass
        endTime=time.time()

        # 计算距离 距离=(声波的往返时间*声速)/2
        timeDelta = endTime - startTime
        distance = (timeDelta * 34300) / 2
  
        return round(distance,2)

2. 电机驱动模块

# MotorControl.py
# TB6612FNG电机驱动
# 电机控制

import RPi.GPIO as GPIO
'''
self.GPIO_PWM
self.GPIO_IN1
self.GPIO_IN2
self.freq

self.last_pwm
self.pwm
'''
class Motor(object):
    
    def __init__(self, GPIO_PWM, GPIO_IN1, GPIO_IN2, freq=300) -> None:
        super().__init__()
        self.GPIO_PWM = GPIO_PWM
        self.GPIO_IN1 = GPIO_IN1
        self.GPIO_IN2 = GPIO_IN2
        self.freq = freq

        GPIO.setmode(GPIO.BCM)
        #GPIO.setwarnings(False)
        
        GPIO.setup(self.GPIO_PWM, GPIO.OUT)
        GPIO.setup(self.GPIO_IN1, GPIO.OUT)
        GPIO.setup(self.GPIO_IN2, GPIO.OUT)

        self.pwm = GPIO.PWM(self.GPIO_PWM, self.freq)
        self.last_pwm = 0
        self.pwm.start(self.last_pwm)

    ''' 静态方法 TB6612FNG的STBY引脚
    1. GPIO_STBY int BCM编码号
    2. status bool 为true则TB6612FNG工作,反之待机 默认为false
    '''
    @staticmethod
    def standby(GPIO_STBY,status=False):
        GPIO.setmode(GPIO.BCM)
        #GPIO.setwarnings(False)
        if status:
            GPIO.setup(status, GPIO.OUT)
            GPIO.output(status, True)
        else:
            GPIO.output(status, False)
    
    ''' 设置PWM占空比
    1. dc 占空比 [0,100]
    '''
    def __setPWM(self, dc):
        if dc != self.last_pwm:
            self.pwm.ChangeDutyCycle(dc)
            self.last_pwm = dc


    ''' 启动
    1. speed int 范围[-100,100] 正数则正转,负数则反转
    '''
    def run(self, speed):
        if(speed>=0):
            GPIO.output(self.GPIO_IN1, False)
            GPIO.output(self.GPIO_IN2, True)
            self.__setPWM(speed)
        else:
            GPIO.output(self.GPIO_IN1, True)
            GPIO.output(self.GPIO_IN2, False)
            self.__setPWM(-speed)
        

    '''停止'''
    def stop(self):
        GPIO.output(self.GPIO_IN1, False)
        GPIO.output(self.GPIO_IN2, False)
        self.__setPWM(0)


    def cleanup(self):
        self.stop()
        self.pwm.stop()

3. 小车控制

考虑到两个电机的转速可能不完全一致,所以需要一个微调功能。

摄像头模块在DIY树莓派小车(二)树莓派4B安装并使用摄像头和超声波模块中使用motion在8081端口开启了视频输出,可以直接调用,这里不再写有关摄像头模块的代码

# CarControl.py
# 控制小车移动 (前进 后退 左转 右转)
# 驱动:TB6612FNG
import RPi.GPIO as GPIO
from MotorControl import Motor
from Distance import Measure

''' 
TB6612FNG 接口
        # STBY
        GPIO_STBY = 27
        # 左边电机
        GPIO_PWMA = 18
        GPIO_AIN1 = 14
        GPIO_AIN2 = 15
        # 右边电机
        GPIO_PWMB = 19
        GPIO_BIN1 = 23
        GPIO_BIN2 = 24

HC-SR04 接口
        GPIO_TRIG = 5
        GPIO_ECHO = 6

小车功能
        1. 微调
        2. 变速
        3. 前进
        4. 后退
        5. 左转
        6. 右转
        7. 停车
        8. 测距
'''


class Car(object):
    def __init__(self) -> None:
        super().__init__()

        '''电机模块'''
        # STBY引脚定义
        self.GPIO_STBY = 27
        GPIO.setmode(GPIO.BCM)
        #GPIO.setwarnings(False)
        GPIO.setup(self.GPIO_STBY, GPIO.OUT)

        # 左右两个电机
        self.motor_left = Motor(18,14,15)
        self.motor_right = Motor(19,23,24)

        # STBY = True  TB6612FNG开始工作
        Motor.standby(self.GPIO_STBY,True)

        # 速度  占空比
        self.motor_speed = 60
        self.motor_left_speed = 60
        self.motor_right_speed = 60
        # 速度系数 用于微调
        self.motor_left_coefficient = 1.0
        self.motor_right_coefficient = 1.0

        '''超声波测距模块'''
        self.measure = Measure(5, 6)


    # 微调 使两个电机转速一致  left=True向左微调 反之向右
    def fineTuning(self, left):
        if left:
            # 向左微调 左边的电机减速 右边的电机加速
            self.motor_left_coefficient -= 0.05
            self.motor_right_coefficient += 0.05
        else:
            # 向右边微调 左边的电机加速 右边的电机减速
            self.motor_left_coefficient += 0.05
            self.motor_right_coefficient -= 0.05

    # 更改速度 [0,100]
    def setSpeed(self, speed):
        if speed>100:
            speed = 100
        if speed<0:
            speed = 0
        self.motor_speed = speed
        self.motor_left_speed = self.motor_left_coefficient * speed
        self.motor_right_speed = self.motor_right_coefficient * speed

    # 前进
    def forward(self):
        self.motor_left.run(self.motor_left_speed)
        self.motor_right.run(self.motor_right_speed)

    # 后退
    def backward(self):
        self.motor_left.run(-self.motor_left_speed)
        self.motor_right.run(-self.motor_right_speed)
    
    # 左转
    def turnLeft(self):
        self.motor_left.run(-self.motor_left_speed)
        self.motor_right.run(self.motor_right_speed)

    # 右转
    def turnRight(self):
        self.motor_left.run(self.motor_left_speed)
        self.motor_right.run(-self.motor_right_speed)

    # 停止
    def stop(self):
        self.motor_left.stop()
        self.motor_right.stop()

    # 测距
    def getDistance(self):
        return self.measure.getDistance()

    def getSpeed(self):
        return self.motor_speed

    # 释放资源
    def cleanup(self):
        self.motor_left.cleanup()
        self.motor_right.cleanup()
        GPIO.cleanup()

4. 获取树莓派信息

以下代码用于获取树莓派CPU温度、CPU使用率、RAM使用率

# RaspberryInfo.py
# 获取树莓派 CPU温度 内存使用率 CPU使用率

import os

# CPU温度
def getCpuTemp():
    res = os.popen('vcgencmd measure_temp').readline()
    return(res.replace("temp=","").replace("'C\n",""))

# CPU使用率
def getCpuUsage():
    return(str(os.popen("top -n1 | awk '/Cpu\(s\):/ {print $2}'").readline().strip()))

# RAM信息
def getRAMinfo():
    p = os.popen('free')
    i = 0
    while 1:
        i = i + 1
        line = p.readline()
        if i==2:
            return(line.split()[1:4])

# 内存使用率
def getRamUsage():
    RAM_stats = getRAMinfo()
    return round(int(RAM_stats[1]) / int(RAM_stats[0])*100,1)

5. 小车Web控制面板

通过按钮或者键盘来控制小车移动

键盘操作方法见下表

按键

W

S

A

D

Q

E

Z

C

功能

前进

后退

左转

右转

加速

减速

左微调

右微调

<!-- index.html -->
<!-- 树莓派小车控制面板 -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Raspberry Car Control Panel</title>
    <link href="http://cdn.bootcss.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet" media="screen">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
    <style type="text/css">
        #front {
            margin-left: 55px;
            margin-bottom: 3px;
        }
        #rear{
            margin-top: 3px;
            margin-left: 55px;
        }
        .btn{
             background: #62559f;
            }
    </style>
    <script>

        $(function(){
            $("button").mousedown(function(){
                console.log(this.id + "mouse down");
                $.post("/cmd",this.id,function(data,status){});
            });
            // 鼠标弹起则停止移动
            $("button").mouseup(function(){
                console.log(this.id + "mouse up");
                $.post("/cmd","stop",function(data,status){});
            })


            // 键盘控制小车移动
            $(document).keydown(function(event){
                switch(event.keyCode){
                    case 87://w
                        console.log("press W");
                        $.post("/cmd","forward",function(data,status){});
                        break;
                    case 83://s
                        console.log("press S");
                        $.post("/cmd","backward",function(data,status){});
                        break;
                    case 65://a
                        console.log("press A");
                        $.post("/cmd","turnLeft",function(data,status){});
                        break;
                    case 68://d
                        console.log("press D");
                        $.post("/cmd","turnRight",function(data,status){});
                        break;
                    case 81://q
                        console.log("press Q");
                        $.post("/cmd","speedUp",function(data,status){});
                        break;
                    case 69://e
                        console.log("press E");
                        $.post("/cmd","slowDown",function(data,status){});
                        break;
                    case 90://z
                        console.log("press Z");
                        $.post("/cmd","leftFineTuning",function(data,status){});
                        break;
                    case 67://c
                        console.log("press C");
                        $.post("/cmd","rightFineTuning",function(data,status){});
                        break;
                }
            });

            $(document).keyup(function(event){
                switch(event.keyCode){
                    case 87://w
                    case 83://s
                    case 65://a
                    case 68://d
                    case 81://q
                        console.log("key up");
                        $.post("/cmd","stop",function(data,status){});
                        break;
                }
            });

        });

    </script>
</head>
<body>
<div class="row">
    <div class="col-xs-12 col-sm-6 col-md-7">
        <div class="panel panel-default">
            <div class="panel-heading">
                <h3 class="panel-title">实时画面</h3>
            </div>
            <div class="panel-body">
                <iframe src="http://192.168.0.100:8081/" width="820" height="620"   frameborder="1"  name="name"  scrolling="auto"></iframe>
            </div>
        </div>
    </div>
         
          
    <div class="col-xs-6 col-md-4">
        <div class="panel panel-default">
            <div class="panel panel-default">
                <div class="panel-heading">
                    <h3 class="panel-title">信息</h3>
                </div>
                <div class="panel-body" >
                    <iframe id="car-info"  width="320" height="195" src="http://192.168.0.100:8088/info"></iframe>
                </div>
            </div>
              
              
            <div class="panel-heading">
                <h3 class="panel-title">控制</h3>
            </div>
            <div class="panel-body" style="margin: 30px;margin-left: 40px">
                <div class="row" >
                    <div class="col-md-2"></div>
                    <div class="col-md-2"><button id="forward" class="btn btn-large btn-primary" type="button"><span class="glyphicon glyphicon-triangle-top" aria-hidden="true"></span><br/>前进</button></div>
                    <div class="col-md-2"></div>
                </div>

                <br/>

                <div class="row">
                    <div class="col-md-2"><button id="turnLeft" class="btn btn-large btn-primary" type="button"><span class="glyphicon glyphicon-triangle-left" aria-hidden="true"></span><br/>左转</button></div>
                    <div class="col-md-2"><button id="backward" class="btn btn-large btn-primary" type="button"><span class="glyphicon glyphicon-triangle-bottom" aria-hidden="true"></span><br/>后退</button></div>
                    <div class="col-md-2"><button id="turnRight" class="btn btn-large btn-primary" type="button"><span class="glyphicon glyphicon-triangle-right" aria-hidden="true"></span><br/>右转</button></div>
                </div>
                   
                <br/>
                      
                <div class="row">
                    <div class="col-md-2"><button id="speedUp" class="btn btn-large btn-primary" type="button"><span class="glyphicon glyphicon glyphicon-plus-sign" aria-hidden="true"></span><br/>加速</button></div>
                    <div class="col-md-2"></div>
                    <div class="col-md-2"><button id="slowDown" class="btn btn-large btn-primary" type="button"><span class="glyphicon glyphicon glyphicon-minus-sign" aria-hidden="true"></span><br/>减速</button></div>
                </div>

                <br>

                <div class="row">
                    <div class="col-md-2"><button id="leftFineTuning" class="btn btn-large btn-primary" type="button"><span class="glyphicon glyphicon-triangle-left" aria-hidden="true"></span><br/>左微调</button></div>
                    <div class="col-md-2"></div>
                    <div class="col-md-2"><button id="rightFineTuning" class="btn btn-large btn-primary" type="button"><span class="glyphicon glyphicon-triangle-right" aria-hidden="true"></span><br/>右微调</button></div>
                </div>

            </div>
    </div>
</div>

</body>
</html>

6. 树莓派小车信息页面

此页面嵌入到小车Web控制面板页面中,显示小车速度(PWM占空比)、距离、CPU温度、CPU使用率、内存使用率

此页面1秒刷新一次

<!-- info.html -->
<!-- 树莓派小车信息面板 -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <!--每1秒刷新一次-->
    <meta http-equiv="refresh" content="1"> 
    <title>信息</title>
</head>
<body>
    <p>Speed:&nbsp;{{speed}}</p>
    <p>Distance:&nbsp;{{distance}}cm</p>
    <p>CPU Temp:&nbsp;{{cpuTemp}}&#8451</p>
    <p>CPU Uasge:&nbsp;{{cpuUsage}}%</p>
    <p>RAM Uasge:&nbsp;{{ramUsage}}%</p>
</body>
</html>

7. 启动

# Start.py
from bottle import get,post,run,request,template
from CarControl import Car
from RaspberryInfo import *

car = Car()

def main(status):
    print("Event: "+status)
    if status == "forward":
        car.forward()
    elif status == "backward":
        car.backward()
    elif status == "turnLeft":
        car.turnLeft()
    elif status == "turnRight":
        car.turnRight()
    elif status == "speedUp":
        car.setSpeed(car.getSpeed() + 5)
    elif status == "slowDown":
        car.setSpeed(car.getSpeed() - 5)
    elif status == "leftFineTuning":
        car.fineTuning(True)
    elif status == "rightFineTuning":
        car.fineTuning(False)
    elif status == "stop":
        car.stop()


# 控制台
@get("/")
def index():
    print("request index.html")
    return template("index.html")

# 控制小车
@post("/cmd")
def cmd():
    adss=request.body.read().decode()
    main(adss)
    return "OK"

# 小车信息
@get("/info")
def info():
    print("Update status Information")
    return template("info.html", speed=car.getSpeed(), distance=car.getDistance(), cpuTemp=getCpuTemp(), cpuUsage=getCpuUsage(), ramUsage=getRamUsage())

run(host='0.0.0.0', port=8088, debug=False)

car.cleanup()

实测

启动

sudo motion # 打开摄像头

python3 Start.py

在浏览器中输入如下Url进入树莓派小车控制页面

http://树莓派ip:8088

Web页面如下,可以通过网页上的按钮或者键盘控制小车(局域网内网络延迟很低,画面几乎实时显示)

控制面板右侧显示的是树莓派小车信息,其中Speed是PWM占空比,范围是[0,100],Distance是超声波测距的结果