折腾·树莓派小车

来源: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模块,命令如下:

1
sudo apt-get install python3-rpi.gpio

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

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#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块以上】

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

1
vcgencmd get_camera

检测摄像头

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

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

先打开树莓派的配置:

1
sudo raspi-config

image-20220708231413128

选择Interface Options:

再选择Legacy Camera:

img

在这个页面回答Yes。

系统会显示如下信息:

img

回到主菜单,点击Finish:

img

重启树莓派。

1
sudo reboot

无法使用摄像头时方法二

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

image-20220708231748648


测试摄像头

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

测试摄像头代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 该程序打开摄像头,显示视频,按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

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

  • 编辑/etc/motion/motion.conf文件

1
sudo nano /etc/motion/motion.conf

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#将deamon off 改成 deamon on
deamon on

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

#视频帧率
framerate 24
stream_maxrate 30

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

启动motion

1
2
sudo systemctl start motion
sudo motion

安装gpac

1
sudo apt install gpac -y

安装bottle

1
pip3 install bottle

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

1
python3 test.py

再安装Start.py,命令

1
python3 Start.py

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

结束motion进程

1
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 返回测距信息

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#导入 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

1
sudo apt install python2-pip -y

或者安装pip3

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

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

  • 安装bottle
1
pip3 install bottle

代码

1. 超声波测距模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 超声波测距模块 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. 电机驱动模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# 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端口开启了视频输出,可以直接调用,这里不再写有关摄像头模块的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# 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使用率

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 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
功能 前进 后退 左转 右转 加速 减速 左微调 右微调
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
<!-- 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秒刷新一次

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!-- 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. 启动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# 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()

实测

启动

1
2
3
sudo motion # 打开摄像头

python3 Start.py

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

1
http://树莓派ip:8088

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

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