!!!注意这只是一个半半半成品,开学之后会迁移主控到新平台并完善功能与添加模块。

嗯,很久之前挖的一个坑,因为疫情没有材料,于是顺手拆了一个玩具车。
半成品如下:小车

材料

主控板是学长画的,MCU为南京沁恒的CH548(和CH340一家),两颗L9110驱动两个电机。测距模块使用HC-SR04,搭配有源蜂鸣器测距报警,另有一块ESP32-Cam搭配200W的OV2640负责直播推流到局域网与无线(WIFI/BLE)控制。当视频流推送到局域网后再通过PC推流到哔哩哔哩,实现公网流媒体传输与数据展示。

具体实现思路

基础姿态控制

主控板负责车辆姿态的调整,例如前进,后退,左转,右转,以上四种实现方式比较暴力,参见下表:

电机A极 电机B极 马达状态
高(电平) 前进
后退
静止
静止

当A/B两级电势差存在时,电机才可开始转动,电势差大小决定转动快慢,正负决定转动方向。

左电机 右电机 小车状态
前进 前进 前进
后退 后退 后退
静止 前进 左转
前进 静止 右转

如上,暴力实现实现前进,后退,左转,右转。

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
//LA代表左电机A极,RA代表右电机A极。
void left()
{
LA=0;
LB=0;
RA=1;
RB=0;
}
void right()
{
LA=1;
LB=0;
RA=0;
RB=0;
}
void ahead()
{
LA=1;
LB=0;
RA=1;
RB=0;
}
void back()
{
LA=0;
LB=1;
RA=0;
RB=1;
}

另有前进加速,后退加速,使用PWM调整,实现车辆均匀加速,但是由于主控板输出电压的一点问题,没有做的特别均匀与完整。

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 speed_up()
{
up++;
mDelaymS(20);//每20ms调高PWM占空比
SetPWM3Dat(up);
LB=0;
SetPWM5Dat(up);
LB=0;
while(up==250)//当达到250时,小车开始前进。
{
ahead();
up=150;
}
}

void back_speed_up()
{
down++;
mDelaymS(20);
LA=0;
SetPWM4Dat(down);
RA=0;
SetPWM2Dat(down);
while(down==250)
{
back();
down=150;
}
}

避障模块

手头上没有舵机,所以就只能固定超声波模块来测量小车正前方的障碍物,有材料之后再考虑添加。

HC-SR04超声波测距模块可提供2cm-400cm的非接触式距离感测功能,测距精度可达高到3mm;模块包括超声波发射器、接收器与控制电路。

基本工作原理:

(1)采用IO口TRIG触发测距,给至少10us的高电平信号;

(2)模块自动发送8个40khz的方波,自动检测是否有信号返回;

(3)有信号返回,通过IO口ECHO输出一个高电平,高电平持续的时间就是超声波从发射到返回的时间。测试距离=(高电平时间*声速(340M/S))/2;

我们需要做的只有:触发测距与读取ECHO高电平时间。如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void measure()
{
mTimer0Clk12DivFsys();//CH548的时钟分频设置
TMOD=0X01;//设定模式为16位计时器
TL0=0X00;
TH0=0X00;
ET0 = 1;
EA = 1;

TX=1;
mDelayuS(15);
TX=0;//保持15us的高电平时间以触发超声波测距

while(!RX);
TR0=1;
while(RX);
TR0=0;//当RX为高电平时,开启计时器,变为低电平时关闭计时器,TH0与TL0的值即为高电平持续的时间。
time=(TH0*256+TL0)/2;
dis=(time*1.78)/100;
}

无线控制模块

这里我采用了Blinker包装好的SDK来进行二次开发,具体可参考点灯科技。通过WIFI连接使用MQTT协议读取App端的按键信息。连接方式为ESP32的IO13对应CH548的P12,ESP32的IO14对应CH548的P13。分为两部分程序与App端配置:

Arduino(ESP32-Cam)

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
#define BLINKER_WIFI//宏定义,使用WIFI进行连接

#include <Blinker.h>

#define AHEAD 13
#define BACK 14

char auth[] = "Key";
char ssid[] = "SSID";
char pswd[] = "PASSWD";

// 新建组件对象
BlinkerButton Button1("ahead");
BlinkerButton Button2("back");
int counter = 0;

// 设置按键1的回调函数
void button1_callback(const String & state)
{
BLINKER_LOG("ahead : ", state);
digitalWrite(AHEAD,1);
digitalWrite(BACK,0);
}
//设置按键2的回调函数
void button2_callback(const String & state)
{
BLINKER_LOG("back : ", state);
digitalWrite(BACK,1);
digitalWrite(AHEAD,0);
}

// 如果未绑定的组件被触发,则会执行其中内容
void dataRead(const String & data)
{
BLINKER_LOG("Blinker readString: ", data);
counter++;
}

void setup()
{
// 初始化串口
Serial.begin(115200);
BLINKER_DEBUG.stream(Serial);

// 初始化有LED的IO,上电默认为前进状态
pinMode(AHEAD, OUTPUT);
digitalWrite(AHEAD, LOW);

pinMode(BACK, OUTPUT);
digitalWrite(BACK, LOW);
// 初始化blinker
Blinker.begin(auth, ssid, pswd);
Blinker.attachData(dataRead);
//将组件对象与回调函数绑定
Button1.attach(button1_callback);
Button2.attach(button2_callback);
}

void loop() {
Blinker.run();
}

CH548

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//这是一个简单粗暴的通信协议
sbit AHEAD=P1^2;
sbit BACK=P1^3;

void control()
{
if(AHEAD==1&&BACK==0)
{
ahead();
}
if(AHEAD==0&&BACK==1)
{
back();
}
}

App端配置

通过按键的键名与类型配置,达到向ESP32推送信息的自定义。
Blinker App

你问我视频流?参见:示例-ESP32-Camera-CameraWebServer,使用哔哩哔哩直播姬即可推流到公网。

后期待完善

  • 更加精细的速度控制,使用PWM/PID来进行速度更加顺滑的加速减速乃至速度调整。
  • 更加全面避障算法,添加舵机使超声波模块能够测量周围的物体情况。
  • 添加GUI界面,把一块ST7789的LCD屏幕接到主控板上,实时展示数据。
  • 前端制作,数据实时通过MQTT推送到Blinker或者自建MQTT服务器实现数据的收集与前端展示。
  • 红外模块,可以实现基础的小车绕黑线走的循迹功能。

emm…看起来像一个很大的工程呢。不管怎样,蓝桥杯过后我估计会开始STM32/FPGA的作死路,51单片机的资源确实太少了。