Catch Up
今年笔者的电赛比赛经历比较曲折: 先是遇到疫情,导致天津赛区延期,后来又赶在两门实训课结课前一周开赛。本博客记录时间是 2022-10-17 08/18/50 比赛结束后的第二天,两门实训课即将结束的一周,然而结课作业都没做完,估计又得要连续熬夜了吧(悲)。
实际上针对这次比赛,我们已经做了一些预案,毕竟去年 2021年电赛 全国赛比得十分狼狈,要啥啥没有,什么都是现用现查现弄,结果导致没有完赛。今年我们参考了 7月 赛题,决定全力准备与小车相关的内容。结果,结果!为什么会出现倒车入库和侧方停车?????????看到题的一刻我人直接傻掉,因为我们准备的是金属框架的四轮车,前轮没有转向,无线电那道题使用这辆车,重量则会成为较大的问题,五道题有四道还是三道来着都要求使用 TI公司 的 MCU 。笔者直接傻了,我们前期准备的是 STM32 相关的程序,其他的均未涉及,前期准备的 YoloV5 运行与训练环境都白准备了,K210 也都使用不上,这就意味着我们前期准备基本白费且直接没有零件可用。没办法,硬着头皮上,无论如何得要做出来,下面则是比赛的纪实。
Chapter 1 分析题目
早上八点整,我们拿到了题目,看了一下,目测只有 A题(无线充电可循迹电动小车) 和 D题(盲盒识别装置) 可以做,其它题目直接 pass 掉。后来经过分析,我们选择了 A题。
A题 的题目如下所示:


题目分析
- 电动小车必须为四轮小车;
- 小车的主控必须使用 TI 公司的 MCU 平台;
- 投影面积不大于 A4 纸,高度与重量不限;
- 自制无线电充电装置;
- 发射器由恒流、恒压模式自动切换的直流稳压电源供电;
- 发射器部分供电参数 15V 电流不大于 0.5A;
- 小车储能使用 法拉电容;
- 小车使用到 DC-DC 模块供电;
- 小车上有发光管指示供电状态;
- 充电 1分钟 后小车自动启动;
- 第二个测试可以循线也可以不循线,但行驶距离应大于 50 厘米;
- 第三个测试在自动行驶之后可移除发射线圈;
- 测试时自带地面轨迹装置;
参数计算
我们手上只有两种法拉电容,第一种的规格是 ELNA 2.5V 100F ,使用三电容串联方式,容量降低到三分之一。

第二种的规格是 HCCCAP 8.1V 3.3F ,使用两电容并联的方式,容量增倍。

使用下面的公式计算一遍(笔者是软件专业,非计科专业,没学过大学物理,高中物理的东西已经忘干净了,还好 iPad 里有高中时记的电子笔记),如有公式使用错误,还请向笔者指出错误。
电容器充满电时的电荷量
Q = C*U
理想状态下单位时间内可充入的电荷量
Q = I*t
计算出来的结果如下:
2.5V 三电容串联方案
Q = C*U = 33.3F x 7.5V = 251.25C
8.1V 两电容并联方案
Q = C*U = 6.6F x 8.1V = 53.46C
理想状态下单位时间内可充入的电荷量
Q = I*t = 0.825A x 60s = 49.5C
可以看到,使用 8.1V 两电容并联方案时的电荷量与可充入的电荷量较为匹配,所以在储电结束后的电压可以保持在一个较高位。因此选择了 8.1V 方案。
Chapter 2 材料
可能是被 21年电赛 啥都没有的状态整怕了,今年比赛前一段时间都在疯狂买东西,有的模块自己开板子制作,导致今年的题目大部分材料与模块手里都有,堆起来能堆满两张桌子,甚至还买了一张 RTX 3060 来应对去年算力不足的问题(笑)。
虽然手里有电容,但是数量刚刚好一辆车,没有冗余,这太危险了,因此又买了一堆,好在这个生产厂商就在天津,当天就拿到东西了。
又买了一些漆包线、软导线做接收线圈,虽然有 3D 打印的辅助缠绕工具,但还是缠的笔者手疼。
剩下的就是像是 502 或者是热熔胶棒这样的耗材了。
Chapter 3 循迹
循迹在整个题目中是最基本、第二重要的部分、但也是最简单的部分。
我们使用三路循迹光电传感器进行循迹。
/************接线******************/
//OLED:
// SCL--P3.5,SDA--P3.6
//
//L298N:
// 左电机:IN1--P1.4,IN2--P1.5,PWMA--P1.2
// 右电机:IN3--P3.3,IN4--P3.4,PWMB--P1.3
//
//双路寻迹传感器: 左:---P4.1
// 右:---P4.0
#include <msp430.h>
#define CPU_F ((double)1000000)
#define delay_ms(x) __delay_cycles((long)(CPU_F*(double)x/1000.0))
int mode = 0;
int fast_mode = 27000;//32000;
int slow_mode = 0;//12000;
int left_mode = 1000;
int right_mode = 200;
void pwm(int s1, int s2){
// Set PWM Pin Number and duty cycle
// Right Motor
TA0CCTL1 = OUTMOD_7; // CCR1 reset/set
TA0CCR1 = s2; // CCR1 PWM duty cycle
// Left Motor
TA0CCTL2 = OUTMOD_7; // CCR2 reset/set
TA0CCR2 = s1; // CCR2 PWM duty cycle
}
void Forward(int s1, int s2){
pwm(s1,s2);
}
void liteLeft(int s1, int s2){
// lite left
pwm(s1, s2);
// delay_ms(100);
// if (s1==1000){
// pwm(fast_mode,fast_mode);
// }
}
void liteRight(int s1, int s2){
// lite right
pwm(s1, s2);
}
void stop_car (int s1, int s2)
{
pwm(s1,s2);
}
void findLine_init(){
// init Sensors
P4DIR = (BIT0+BIT1+BIT2); // Set pin mode into output
P4SEL = (BIT0+BIT1+BIT2);
}
void findLine(){
// (P4IN & BIT0 Left_Sensor
// (P4IN & BIT1) Center_Sensor
// (P4IN & BIT2) Right_Sensor
// Forward 1
// Left 0; Center 1; Right 0;
if ( (P4IN & BIT0) == 0 && (P4IN & BIT1) == 1 && (P4IN & BIT2) == 0 ){
Forward(fast_mode,fast_mode);
}
// Forward 2
// Left 0; Center 0; Right 0;
else if ((P4IN & BIT0) == 0 && (P4IN & BIT1) == 0 && (P4IN & BIT2) == 0 )
{
//delay_ms(1000); //延时1秒
Forward(fast_mode,fast_mode);
// stop_car(0,0);
// liteLeft(left_mode,right_mode);
}
// Turn Left 1
// Left 1; Center 0; Right 0;
else if ( (P4IN & BIT0) == 1 && (P4IN & BIT1) == 0 && (P4IN & BIT2) == 0 ){
liteLeft(slow_mode,fast_mode);
}
// Turn Left 2
// Left 1; Center 1; Right 0;
else if ( (P4IN & BIT0) == 1 && (P4IN & BIT1) == 1 && (P4IN & BIT2) == 0 ){
liteLeft(slow_mode,fast_mode);
}
// Turn Right 1
// Left 0; Center 0; Right 1;
else if ( (P4IN & BIT0) == 0 && (P4IN & BIT1) == 0 && (P4IN & BIT2) == 1 ){
liteRight(fast_mode,slow_mode);
}
// Turn Right 2
// Left 0; Center 1; Right 1;
else if ( (P4IN & BIT0) == 0 && (P4IN & BIT1) == 1 && (P4IN & BIT2) == 1 ){
liteRight(fast_mode,slow_mode);
}
// else if ();
}
int main(void){
WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
// init Motor
P1DIR |= (BIT2+BIT3+BIT4+BIT5); // IN1, IN2, IN3, IN4
P1SEL |= (BIT2+BIT3+BIT4+BIT5);
// Timer
TA0CCR0 = 45000;
TA0CCTL1 = OUTMOD_7; // Set PWM Communicate mode into OUTMODE_7
TA0CCTL2 = OUTMOD_7;
TA0CTL=TASSEL_2+MC_1;
// init sensors
findLine_init();
pwm(fast_mode,fast_mode);
while(1){
findLine();
}
}
循迹这块发现一个比较奇怪的现象,使用占空比不为 100% 的 PWM 信号驱动电机比使用占空比为 100% 或直接使用高电平驱动电机要费电。无论使用 L298N 还是 BTN7971B 都有这个现象的发生。希望有大佬能够给予解答。
Chapter 4 驱动电路
电机驱动模块最开始选择的是 L298N 四路驱动模块,因为在充电模块制作完成之前,我们一直使用的是锂电池进行供电,因此对于电压的需求并不敏感。
我们使用的L298N模块的最低电压为 7V,低于 7V 不工作。对于电压的要求如此之高,肯定不能满足需求。除了电压高这一槽点之外,还有该模块的待机功耗也不低,毕竟是使用了快20年的古董技术了,运行时的效率也不高,唯一优点就是简易耐用还便宜。不过我们也不需要这些优点,我们更换成了工作效率更高、对电压要求相对较低的BTN7971B半桥模块。双路需要使用四颗芯片。下面是电路原理图和我们的PCB图。


Chapter 5 储电电路
这部分电路安装在小车上,包括了超级电容器、Buck-Boost升降压模块、输入端电压检测模块、稳压电路和几个容量较大的电容。




当时手绘的电路示意图


不过真实制作的时候,笔者在电压检测电路之后加入了一个二极管,用于在无线充电结束之后防止电容的电流反向对接收器造成伤害,同时加入二极管之后,在充电结束后电压检测部分是没有电压的,方便MSP 430判断是否充电结束从而自动开始循迹。
5V 稳压部分使用的是 7805 芯片进行稳压,因为Buck-Boost 模块输出的是7.5V 或 7V电压,与目标 5V 电压相差不大,因此在发热和其他能量损耗上会小一些,外部电路也灭有那么复杂,因此选择这个方法输出 5V 给 MAP430 供电。
7.5 或 7 V 是用于给电机驱动供电的,因此在输出线路上并联了几个有这较大容量的电容,以防止电机突然停转带来的反向电压对整个电路造成破坏。实际制作的时候,由于实验室没有目标容量的电池,因此换了容量更大的 450nF 电容。
Chapter 6 发射电路控制器
题目只说小车使用 TI公司的芯片,其他的没有要求,因此发射电路控制器使用的是 Arduino 进行控制,通过感应按钮按下后的高电平控制继电器的开启与自动关闭。

Arduino 由一个 DC-DC 稳压模块进行供电,将 15V 转为 5V,这里使用 DC-DC 方案而非 LDO 方案,因为在如此之大的压差之下,LDO 方法效率会有所降低,发热严重,因此使用 DC-DC 方案。
电源使用题目要求的 具有恒流恒压模式启动切换的直流稳压电源进行供电。
Chapter 7 车体结构
测试车不太注意零部件关系,就随意堆在那里,并且使用电池供电。

车底盘用的是之前废掉的小车的地盘,主要是用来测试循迹。但是这个车有一个槽点,就是轮子的摩擦力不太够。在测试完循迹之后,是时候构建小车的最终形态了。我们在备赛的时候,买过一辆全金属的小车,不过不过那辆车太重了,我们把顶部的平台卸掉,把后轮的橡胶去掉,只保留驱动轮的橡胶,我们后轮的两个 TT 电机的马达、多余的齿轮都卸掉以减少不必要的做功,这样就可以。寻迹模块安装在车前,无线充电模块安装在两个 TT 电机底部,尽可能靠近地面的无线充电线圈。

经过测试,去掉橡胶的大的后轮相对比于塑料小轮,对转弯时的干扰更小。
Chapter 8 无线发射与接收模块
这两个模块最开始打算完全自己制作的:使用 555 产生时钟信号,控制电机驱动板对电机正转反转,模拟交流电。下面这个是笔者手搓的线圈,由于没学过大学物理,也没有相关制作经验,只是按照图片照葫芦画瓢一般缠了俩线圈。当然没有成功了。

没办法只能用现有的模块进行改装,发射端原先最大只能支持 12V 输入,我们对电路修改,能支持 15V 输入。接收端原先只有 5V,我们对电路更换、加装了部分元件,以支持输出 9V。这样也算是符合题目要求的“自制”吧,毕竟没说要求自制多少内容。
Summary

这次比赛虽然说做了一些准备,但还是准备的不够充分,过分地将比赛题目都压在了智能寻迹小车上,没有考虑到其他的题目。一些模块该做的都没做出来:MPU6050 模块对于角度计算等常用方法的封装、舵机的调试、HC-05蓝牙通信的功能封装、寻迹功能的封装、YoloV5 Lite 在树莓派上的环境的配置等。
比赛过程还是有一些混乱:由于前期材料没有准备充足,导致比赛前两天基本处于闲置状态;把大部分活儿都积压到了最后一天;没有充足的时间做测试的情况依然制作了两版不同的小车和充电装置。
特别是参赛成员对于一些词语的认知标准不同,下面就举一个例子:
- “程序能调通”和“能运行”这个词代表的是:将一些常用功能分别测试成功后,再封装成函数后,依然能调用成功且对应部件可以以正确的方式有所相应。并非将网络上的程序简单地下载下来,不做任何修改、不做任何封装,在本机上跑起来,这叫“成功将他人程序运行起来”;或只是简单的对部件进行编程使用而未形成可用的
function;
还有在编程需要报错时,需要参赛成员自行上网寻找解决方案,简单的问题超过一个小时或两个小时若还未解决,需要立即更换方案,而不是在一个简单的问题上死磕;遇到报错时,请在所有渠道自行搜索解决方案,包括但不限于:Google,Bing,CSDN,cnblogs,Stack Overflow 或各开发板的大型论坛。不要总是在百度的前十几页搜索东西,以笔者个人经验,超过5页还未搜索出想要的结果,就需要更换搜索渠道了;也请不要在报错时,直接呼叫他人来解决问题,他人也需要使用搜索引擎来寻找解决方法。
任务完成后,请立即着手于下一个未完成的任务,或协助其他成员一同解决问题。
不过就结果而言,还是可以的,大体上完成了题目要求的内容,小车本体也基本完成了制作,第三题最多也就6圈,截止收笔时,笔者已经见到网上有跑出15圈以上的人了,至于结果怎么样,就要看天津市整体的发挥了。还是希望能有一个好的成绩。
评论区