电赛经验总结(纯个人版)
写在前面
在备赛的时候就常常在想写一篇文档,总结一下自己的经历和经验,如果有些许能帮助到后来人也算是我的幸运。本文档完全是基于我自己个人的经验,只适合参考并不具有普适性;本人也只是一个普通的大二学生,说出的话并不权威也很有可能是不正确的,望各位读者审慎视之。又由于本人是负责软件的,对于硬件和视觉部分并不了解,在文中也就不表。
0 要用好的硬件
之所以把这一项放在第一个位置,且用 0 标注,是因为我觉得这是我备赛以来最重要的部分,也是我觉得自己做过的最正确的决定。“巧妇难为无米之炊”,大概就是这个道理。如果你有足够的软件技术,那么硬件部分的硬伤是完全可以弥补的,但是从效率上而言,甚至可能比不上一个技术不强但是硬件很好的队伍。
舍得花钱
以我们队今年参加的赛题 2025年E题 为例,我们最后的作品,总计价值超过一千元。那么在备赛周期中,我们的准备更是大幅超过这个数,总计可能超过3k甚至4k。我个人用过不少产品,有些很便宜有些很贵,大部分时候,贵的产品是远远比便宜的产品好用的。比方说逻辑分析仪,我使用的是正点原子 (挣点元子)的标配400+元。相比那些几十块的小分析仪不知道要好多少。在调试的时候,拿出逻辑分析仪一测,很快就能定位问题所在,相比之下之前没有逻辑分析仪的苦日子我是真的不想再过了。这400块,是我个人认为我备赛周期最正确的付款。
又比如二维云台,我们一开始使用的是舵机,用于 2023年E题(激光追踪) 的练手。即使我调出来一个非常好的PID用于追踪色块,可是由于舵机本身的精度限制,我们还是遇到了不少困难。相比之下,我同学的队使用伺服电机,直接就把这道题变成了“水题”,可见一个好的硬件是多么重要。
尝试新东西
一般来说,新的产品是要比旧的产品更好用。例如陀螺仪,我们一开始用的是经典的MPU6050,配合其DMP库的姿态解算可以得到一个初始漂移仅3度左右的Yaw角,在静止8秒之后Yaw角就可以几乎稳定下来(其DMP库的特性)。但是由于MPU6050本身已经停产,市面产品不是国产仿造就是翻新,良莠不齐,所幸我们买到的都是可以正常使用的,但是我同学的队买的2个6050都是完全不能使用,Yaw角漂移可以当做秒表使用了。后来我们决定抛弃6050,尝试使用比较新的产品ICM42688。配合一点软件补偿角速度漂移,使用之前同样的四元数姿态解算算法,我们几乎得到了一个无初始漂移的结果。这并不是说不能使用MPU6050,相反对于电赛的要求而言,6050的精度是完全足够的,参加电赛的队伍普遍使用的都是6050.但是它作为旧时代的残党,我们现在有了更好的选择,何乐而不为呢?
1 要有好队友
这一项的重要性也不需要我多说。如果你的队友够强,你甚至可以摆4天3夜让你的队友带你捧杯(但是我极力反对和谴责这种不要脸的行为)。但是很多时候大家的水平差不多,这个时候才是体现出“好”的作用的时候——相处融洽、分工明确。
关于分工
我们队的分工是非常清晰的:硬件、软件、视觉。 这里的软件和视觉之所以也要分开,是因为通常而言摄像头可以被视为“上位机”,不直接参与底层的控制,例如小车的行驶,云台的转动等等。通过这样的分工,每个人确定好自己的工作就可以实现功能上的解耦(在软件中我们叫这个 “高内聚低耦合”)。当你不需要去管自己分外的事情,专注于一件事的时候,效率会提升不少。当然比赛过程中也不乏视觉、硬件都做完了只看软件的情况,此时就需要三个人合作同一件事了。
2 重视基础
上学的时候老师就一直强调 “基础不牢,地动山摇”,在面对任何问题时实际上都是这样。上了大学之后很多人学期内不学习,把所有的任务都压给期末前一周,指望速通(我并不是谴责这种行为,因为我本人也这样),你当然也可以把这一套挪用到电赛来,“速通”电赛小车之类的。但是我极力反对你使用这种方法,因为电赛并不是一成不变的,近几年出的题目灵活性都较高,用“速通”的办法只会让你思维僵化。如果你提前一个月备赛,你完全可以抽出10天的时间,先深入学习单片机在电赛可能用到的所有外设,整理成笔记,然后动手实操,写一些类似于电机驱动、编码器等等普适性的模块,这不仅是为比赛可能出到的题目做准备,也是增进了你对单片机外设的认识,解决问题时也就更轻松。
3 善用AI(大语言模型、Agent)
对于一个现代的大学生而言,使用AI是一项必不可缺的技能了。但是什么时候用、怎么用,这里面也有讲究。
写程序的时候
AI强大的编码能力,使得他可以轻松生成大段的代码,但是很多时候他可能不是准确无误的,而且很有可能他和你的思路完全不一样,导致你要在这里浪费很多时间。我使用AI的思路是,先完全确定好这一部分的编程思路,具体要用到哪一些外设或方法,然后告诉AI,让AI生成一些初步的代码,我再检查和修改。一定不能所有的代码全交给AI写,不然这样下去你的代码很可能就变成了 “💩山”,连你自己都看不懂了。
调试的时候
当你面对一个棘手的问题,不知道问题的原因是哪里的时候,你也可以尝试去问问AI。很多时候如果你不知道问题的原因是哪里,那就是这个问题已经超出了你的知识范围了(前提是你已经检查了所有你能检查的地方,这一点在下面会讲到),此时去问问AI,也许他就能给出解决的办法。
4 调试的黄金准则
这是我自己亲身经历,用“血与泪”总结出来的教训。如果你能使用这种准则去调试,相信BUG很快就能被你精准定位并解决。
例:二维云台绑上OpenMV追踪色块实验,调PID调一万年也调不好,最后发现上下舵机的PWM通道是反的。
自下而上,逐层验证
当系统不按预期工作时,永远不要直接去怀疑最顶层的算法。要像一个侦探一样,从最基础的物理层开始,一层一层地向上排查。
第零层:物理层
这是最常被忽略,但也最致命的一层。在触摸键盘前,先拿起万用表。
- 电源检查: 模块的VCC和GND引脚,电压是否正确?电池电压是否足够?
- 连接检查: 信号线是否虚焊?插头是否松动?有没有一根线插错了引脚?(比如例子,如果用逻辑分析仪测量两个舵机的PWM引脚,会立刻发现信号与预期不符)
第一层:驱动层
在这一层,我们要验证MCU能否“一对一”地精确控制每一个硬件。这是在验证你和硬件之间的“契约”。
- 最小化测试: 放弃所有上层逻辑,写一个最简单的测试程序。比如,只调用最底层的PWM输出函数,
Set_PWM_For_Channel_5(duty_cycle)
,然后去测量5号通道的引脚,看波形是否正确。 - 隔离变量: 比如例子,就应该写一个独立的测试函数,如
Test_Servos()
,在里面分别调用Set_Upper_Servo_Angle(90)
和Set_Lower_Servo_Angle(90)
,然后观察哪个舵机动了。问题会立刻暴露。 - 验证返回值: 官方的HAL库或DriverLib函数,通常会有返回值(如
HAL_OK
)。永远不要想当然地认为函数调用成功了,必须检查返回值。
第二层:功能层
在这一层,我们验证封装好的函数和模块的内部逻辑。
- 单元测试: 对每一个功能函数进行测试。比如,你有一个将角度(0-180度)转换为PWM占空比(500-2500us)的函数
map_angle_to_pwm()
,你要单独调用它,传入0
,90
,180
等边界值,看它的返回值是否符合预期。 - 接口检查: 检查函数之间传递的参数。单位对不对?数据类型对不对?正负号对不对?(比如,一个函数需要弧度,你却传入了角度)。
第三层:算法层
只有当前三层都100%确认无误后,你才有资格怀疑你的算法。
- 逻辑审查: 这时才开始分析你的PID逻辑、状态机跳转条件等。
- 数据可视化: 使用串口或逻辑分析仪,将算法的所有输入、输出和中间变量全部打印或显示出来,观察它们的动态变化,判断算法行为是否符合理论。
调试的三个核心心态
- 怀疑一切 : 不要相信任何一行你没有亲手验证过的代码,无论是你自己写的,还是从网上复制的。不要假设任何一个模块是好的,直到你亲手测试过它。
- 分而治之 : 当一个大系统不工作时,尝试将它拆分成几个小块,或者注释掉一部分功能。如果注释掉某段代码后,系统恢复正常了,那问题就一定在这段代码里。
- 小黄鸭调试法 : 把你的问题,清晰地、逐行地,讲给一个不会说话的物体听(比如桌上的一个橡皮鸭子),或者讲给一个对你项目完全不了解的同学听。在你试图把混乱的思路组织成语言时,往往自己就能发现逻辑上的漏洞。
写在后面
想说的很多,但是写成文章又觉难以下笔,只能写这么多了。最后想聊聊参加电赛的意义问题。我一开始也不太清楚我打电赛真正的目的是什么,曾经我也想过如果做不出来要不要去找代做等等问题。但是在我备赛的过程中,我慢慢意识到实际上电赛拿不拿奖并不是一件多么重要的事情。就好比高考,我曾经为了高考努力了3年,但是在高考结束的刹那,高考考的怎么样就并不是一件很重要的事情了,重要的是你的人生的一个新阶段开始了,重要的是你在这个过程中经历了什么。电赛也是一样的,它督促我学习,让我从一个半月前只会点灯的小白,到现在可以有自信独立完成一个项目,这才是我真正的收获——成长。很久以前我是一个很讨厌这种大话的人,但是现在我自己经过了这种过程之后,我觉得这话说的真是有道理。这才是我认为参加电赛真正的意义,他比什么能写进简历里的一个奖项重要的不止多了一点。