0%

倒车雷达(二):硬件搭建

硬件是软件运行的基础,因此有必要建立良好的硬件系统供软件的运行。因此,针对通信功能的蓝牙模块、制动功能的电机模块和测距功能的超声波模块三大模块,建立电路连接,提供电路连接图,并编写程序。

本文也提供部分代码,代码虽然都是核心代码但不能够独立运行。程序都附注释,有一丢丢开发基础都能看懂。希望看官能有所思考后取得所需代码,而不是无脑的复制粘贴。完整代码参考这里。且代码仅以我的接线方式为标准。

通信功能

通信功能选取了HC-05蓝牙模块,某购物平台25RMB左右。对于Arduino而言,还是能很轻易的做到Arduino与HC-05蓝牙模块的连接。连接的电路图如下所示:

  • 5V连接VCC,GND相互连接构成回路
  • HC-05的发射端TXD连接Arduino的接收端RXD
  • HC-05的接收端RXD连接Arduino的发射端TXD
  • 以此来构成通信的回路。

但需要注意的是,在给Arduino烧录程序时,需要断开Arduino与HC-05的RXD和TXD连接。否则Arduni的TXD和RXD处于连接状态而非悬空状态时,程序无法烧录。(也可能是我用Vscode做IDE的原因)

PC向Arduino发送数据

而在完成电路的连接后,需要编写程序来测试连接是否正确。首先Arduino处于接收端,电脑处于发送端。接通电路,电脑开启蓝牙并连接。对于接收端,当available()返回值大于0时,表明串口有数据发送到Arduino,此时调用read方法读取即可。

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
void setup() 
{
// 设置硬件串口通信的波特率
Serial.begin(9600);
// 先清空串口缓存
while(Serial.available())
Serial.read();
}

void loop()
{
// 读取串口数据
if (Serial.available() > 0)
{
delay(500);
char val;
// 读取数据
val = Serial.read();
// 进入倒车模式
if (val == '5')
{
flag = 1;
}
if (val == '6')
{
flag = 0;
}
}
}

而在发送端,选择了使用python作为发射信号和读取信号的工具。借助pyserial串口,很容易实现这个功能。(pip install pyserial)

1
2
3
4
5
6
7
8
9
10
import serial
# 读取串口数据的类

s = serial.Serial(port='COM6',
baudrate=9600,
stopbits=serial.STOPBITS_ONE)

# 发送数据
s.write(b"5")
s.write(b"6")

至于为什么要用b""这样的形式,因为serial这个库发送和读取的数据都是字节类型。蓝牙HC-05的使用是我经历的最难的一部分,主要的坑是:只有在程序连接蓝牙时才算连接,而仅仅在Win10蓝牙中连接是不够的。

Arduino向PC发送数据

首先来看Arduino端的程序,一般是调用println方法完成发送,调用flush方法等待数据的发送完毕。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void setup() 
{
// 设置硬件串口通信的波特率
Serial.begin(9600);
// 先清空串口缓存
while(Serial.available())
Serial.read();
}

void loop()
{
// 设置发送数据
string str = "asd"
// 参数可以是任何类型 不一定都是字符串
Serial.println(str);
// 等待串口发送完毕
Serial.flush();
}

而在python端完成读取数据的功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import serial
# 读取串口数据的类

s = serial.Serial(port='COM6',
baudrate=9600,
stopbits=serial.STOPBITS_ONE)

def recv():
# 读取数据
data = s.readline()
if data == '':
continue
else:
break
return data

同样读取的数据为字节类型,通常为这样的格式:

1
2
3
4
5
6
7
b'1\r\n'
b'1\r\n'
b'1\r\n'
b'1\r\n'
b'1\r\n'
b'1\r\n'
b'1\r\n'

所以可以str(data[2:-5])这样取出其中的数值部分。

测距功能

选用了HC-SR04模块作为超声测距模块,购物平台5RMB左右。超声波测距的原理较为简单,首先发送特定频率的声波,在接收对应频率的声波。记录声波传输的时间,再结合声波传输的速度,很容易得到当前与障碍物的距离。测距原理图如下所示:

而具体到硬件而言,测距方法如下。HC-SR04有两个喇叭,Trig发送声波,Echo接收声波。Trig是启动信号引脚,使用的时候先给他10us以上的高电平,再拉低,SR04就会发送八个40khz的超声波。此时对Echo拉低。然后就开始等待Echo啥时候变高了我们就开始计时 (开始接收到反馈的声波), 直到他变低为止 (声波接收完毕),当它变低的时候,我们就可以根据这个时间计算距离了。

这个时间是声波在空气中传播的时间,因为是往返的,所以计算过程需要除以2,变成单程的时间,再将这个单程时间乘以声速即340米/秒,得到的就是从当前位置到对面障碍物的距离。因此,得到的电路连接方式如下:

也很容易得到相应的程序,但也有相关的注意事项:

  • 使用pulseIn函数记录Echo持续高电平的时间即可。
  • 在完成一次测距后一定要延时一秒左右。因为程序的执行速度和硬件的执行速度是不一样的,程序执行完了要缓一缓,等待硬件执行完。不延时的情况下HC-SR04是不能工作的。
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
int trigBack = 2;
int echoBack = A0;

void setup()
{
// 定义超声波的输入输出模式 -----------------------------
pinMode(trigBack, OUTPUT);
pinMode(echoBack, INPUT);
}

void loop()
{
// 先拉底做初始化
digitalWrite(trigBack, LOW);
delayMicroseconds(5);
// 拉高十毫秒
digitalWrite(trigBack, HIGH);
delayMicroseconds(10);
// 在拉低
digitalWrite(trigBack, LOW);
// 根据高电平持续的时间计算与障碍物的距离
cmB = (pulseIn(echoBack, HIGH) / 2) / 29.1;
Serial.println(cmB);
// 一定要有延时
delay(2000);
}

制动功能

车辆最基本的功能还是实现前进、后退、左转和右转,而这些功能的实现离不开电机模块。选用的电机模块是HC02-48,便宜到忘记了它的价格。但是Arduino不能直接驱动HC02-48电机完成工作,因为驱动电机需要较大电流,而Arduino的作用在于高低电平的控制而不是电流的输出。Arduino的IO口输出电流在40mA左右,无法驱动最低要求150mA的电机。因此需要借助电机驱动模块L298N来放大驱动电流。

L298N驱动直流电机时可选择任何额定电压在4.8~35V和电流小于2A范围内的电机。而HC02-48电机的工作电压在3-6V,且L298N的输出电流恰好能驱动电机模块,因此在硬件层面可完成驱动。这个好像也很便宜,大约10RMB左右。

将L298N的四个入口引脚IN1,IN2,IN3和IN4接Arduino的控制引脚,此时Out1,Out2连接一个电机,Out3,Out4出口引脚连接另外的电机。通过对4个入口引脚赋值来更改出口引脚的电平输出,进而控制电机的正反转,完成前进、后退和转向的功能。此外可以通过总开关ENA,ENB两个使能端控制两个电机能否转动和转速。电路连接方式下:

而L298N的+12V和GND接口,需要单独的电池供电。正如上文所说,Arduino的核心功能是控制电平的输出而不是提供电压,因此仅将Arduino的IO口和L298N的使能端连接即可,L298N的供电部分需要另外的电池。

转速调节

在倒车的过程中,车辆不能够以过高的速度倒车,否则容易发生碰撞。因此,需要PWM调制的手段将车速给降下来。想必在做的看官应该不是自动化专业的,所以我准备通俗的解释PWM的含义:

  • 在一个时间段内,一直给电机供电,那么电机会持续转动,此时是满电的输出;
  • 在一个时间段内,一半的时间供电,一半的时间不供电,那么电机只会在一半的时间内转动,此时的速度会低于满电输出;
  • 在一个时间段内,75%的时间不供电,25%的时间供电,那么电机会在1/4的时间内转动,此时速度会更低;
  • 如果将一个时间段取极限无限的减小,那么电机就是时而供电时而没电,此时便会减速。如下图所示:

而在Arduino实现这个并不是很难,只需要调用analogWrite方法对L298N的使能端进行PWM调制即可。其中,analogWrite方法接收一个参数,参数的取值范围是[0, 255],取值越大,输出电压越大,电机转速越快。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 定义两个电机驱动的使能端
int en1 = 9;
int en2 = 6;
// 定义行驶速度
int speed = 95;
void setup()
{
// 使能端为输出模式
pinMode(en1, OUTPUT);
pinMode(en2, OUTPUT);
}

void loop()
{
// speed 就是行走的速度
analogWrite(en1, speed);
analogWrite(en2, speed);
}

需要注意的是:电机的实际转速不仅和参数speed有关,还和供电的电压有关。即使speed的取值为255,但电池没电了,电机还是会转的很慢。

电机转动

在能够对电机的转速进行调节后,下一步就是实现电机的转动了:

  • 四个电机都向前转,那么车辆会向前行进;
  • 四个电机都向后转,那么车辆会向后行进;
  • 左侧的电机向前转,右侧的电机向后转,那么车辆会向右行进;
  • 左侧的电机向后转,右侧的电机向前转,那么车辆会向左行进;

仅需要digitalWrite方法对电机两侧输入不同的电平即可完成电机的转动,因此代码如下:

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
// 右前轮 -----------------------------------------
// 定义 uno 的 pin 8 向 input1 输出
int input1 = 8;
// 定义 uno 的 pin 12 向 input2 输出
int input2 = 10;
// 左前轮 -----------------------------------------
// 定义 uno 的 pin 11 向 input3 输出
int input3 = 11;
// 定义 uno 的 pin 12 向 input4 输出
int input4 = 12;
// 右后轮 -----------------------------------------
// 定义 uno 的 pin 3 向 input5 输出
int input5 = 3;
// 定义 uno 的 pin 4 向 input6 输出
int input6 = 4;
// 左后轮 -----------------------------------------
// 定义 uno 的 pin 5 向 input7 输出
int input7 = 5;
// 定义 uno 的 pin 6 向 input8 输出
int input8 = 7;

void setup()
{
// 使能端
pinMode(en1, OUTPUT);
pinMode(en2, OUTPUT);
// 右前
pinMode(input1, OUTPUT);
pinMode(input2, OUTPUT);
// 左前
pinMode(input3, OUTPUT);
pinMode(input4, OUTPUT);
// 右后
pinMode(input5, OUTPUT);
pinMode(input6, OUTPUT);
// 左后
pinMode(input7, OUTPUT);
pinMode(input8, OUTPUT);
}

// 前进函数 ------------------------------------------
void forward()
{
analogWrite(en1, speed);
analogWrite(en2, speed);
// 右前轮
digitalWrite(input1, LOW);
digitalWrite(input2, HIGH);
// 右后轮
digitalWrite(input5, HIGH);
digitalWrite(input6, LOW);
// 左后轮
digitalWrite(input7, HIGH);
digitalWrite(input8, LOW);
// 左前轮
digitalWrite(input3, LOW);
digitalWrite(input4, HIGH);
}

// 停止函数 ------------------------------------------
void stop()
{
// 左前轮
digitalWrite(input1, LOW);
digitalWrite(input2, LOW);
// 左后轮
digitalWrite(input7, LOW);
digitalWrite(input8, LOW);
// 右前轮
digitalWrite(input3, LOW);
digitalWrite(input4, LOW);
// 右后轮
digitalWrite(input5, LOW);
digitalWrite(input6, LOW);
}

// 后退函数 ------------------------------------------
void back()
{
analogWrite(en1, speed);
analogWrite(en2, speed);
// 右前轮
digitalWrite(input1, HIGH);
digitalWrite(input2, LOW);
// 右后轮
digitalWrite(input5, LOW);
digitalWrite(input6, HIGH);
// 左后轮
digitalWrite(input7, LOW);
digitalWrite(input8, HIGH);
// 左前轮
digitalWrite(input3, HIGH);
digitalWrite(input4, LOW);
}

// 左转函数 ------------------------------------------
void left()
{
analogWrite(en1, speed);
analogWrite(en2, speed);
// 左后轮
digitalWrite(input7, LOW);
digitalWrite(input8, HIGH);
// 左前轮
digitalWrite(input3, HIGH);
digitalWrite(input4, LOW);
// 右前轮
digitalWrite(input1, LOW);
digitalWrite(input2, HIGH);
// 右后轮
digitalWrite(input5, HIGH);
digitalWrite(input6, LOW);
}

// 右转函数 ------------------------------------------
void right()
{
analogWrite(en1, speed);
analogWrite(en2, speed);
// 左后轮
digitalWrite(input7, HIGH);
digitalWrite(input8, LOW);
// 左前轮
digitalWrite(input3, LOW);
digitalWrite(input4, HIGH);
// 右前轮
digitalWrite(input1, HIGH);
digitalWrite(input2, LOW);
// 右后轮
digitalWrite(input5, LOW);
digitalWrite(input6, HIGH);
}

void loop()
{
delay(3000);
forward();
delay(3000);
back();
delay(3000);
stop();
delay(3000);
right();
}

当然,电平高低的输出需要根据实际的接线情况来判断。

供电系统

全部系统的供电系统分为两部分,一个是给Arduino供电,一个是给L298N电机供电。

  • 对于给Arduino供电,我选择了使用电源接口为Arduino供电。通过此方法为Arduino开发板供电时,直流电源电压为9V ~ 12V,买一个9V电池和转接线即可。使用低于9V的电源电压可能导致Arduino工作不稳定,使用高于12V电源电压存在着毁坏Arduino开发板的风险。

  • 对于给L298N供电,选择的是3.7V 18650可充电锂电池,在配一个电池盒即可。电池盒要求是串联的,而不是并联,否则电压不够。将电池盒自带的红色线与L298N的+12V连接,将黑色线与L298N的GND连接即可完成电机部分的供电。

结语

接线并不是很复杂,我都是用的杜邦线接起来的,大胆的去尝试就好了。有问题也欢迎在下方留言,毕竟计算机搞这些可能也是一头雾水,上文都是我一点点摸索出来的经验。

参考

  1. http://www.taichi-maker.com/homepage/arduino-projects-index/arduino-power-supply/
  2. http://www.taichi-maker.com/homepage/reference-index/arduino-code-reference/
  3. http://www.taichi-maker.com/?s=HC-SR04
感谢上学期间打赏我的朋友们。赛博乞讨:我,秦始皇,打钱。

欢迎订阅我的文章