1. 项目缘起与整体设计思路
1.1 为什么一个智能车战队要开源全套代码
21届智能车竞赛的疯狂电路组,本质上是一场在限定车模、限定传感器框架下,把电路设计、信号处理、控制算法和视觉感知压榨到极限的工程竞赛。Soberup战队选择把整套方案开源出来,这个决定在圈子里其实不算常见——大多数队伍赛后要么把代码锁进硬盘,要么只放出几张赛道照片和一段跑车视频。原因很现实:智能车竞赛的备赛周期通常只有几个月,从零搭一套能稳定跑完全程的系统,光是调参就能耗掉大半时间。如果每一届都从头造轮子,整个圈子的技术水平提升会非常缓慢。
Soberup这套开源目录的价值,不在于它拿了什么名次,而在于它把“一个能跑的车队到底需要哪些模块、每个模块之间怎么衔接、哪些参数是真正影响成绩的”这些问题,用可运行的代码和可复现的配置回答了一遍。对于第一次参加智能车竞赛的新手队伍,这份目录相当于一份带注释的参考答案;对于有经验的老队伍,它提供了一个可以对比和借鉴的工程框架。
从技术栈来看,这套方案覆盖了智能车竞赛疯狂电路组的几个核心环节:视觉赛道元素识别、惯导与编码器融合的位姿估计、电磁信号采集与处理、电机与舵机的闭环控制,以及上位机调参和日志回传。这些模块不是孤立存在的,它们通过一个实时调度框架串联起来,在有限的算力平台上跑出稳定的控制周期。
1.2 疯狂电路组的核心难点与方案选型
疯狂电路组和普通电磁组、摄像头组最大的区别在于:赛道元素更复杂,信号环境更恶劣,而且对电路的自主设计能力有硬性要求。你不仅要会写代码,还要会画板子、焊电路、调运放。很多队伍代码写得不错,但栽在电磁信号调理电路上——运放选型不对、滤波参数没调好、ADC采样被电机噪声干扰,这些问题在实验室里可能不明显,一到赛道上就原形毕露。
Soberup的方案在选型上有几个值得说的决策。主控方面,他们选择了双核架构:一个核专门跑视觉图像处理和赛道元素识别,另一个核负责惯导数据融合、电磁信号处理和底层控制。这种分工的好处是,视觉算法的耗时波动不会直接打断控制环,控制周期可以保持相对稳定。代价是两个核之间的数据同步需要仔细设计,共享内存的读写时序如果处理不好,会出现视觉结果延迟或者控制指令用错帧的情况。
传感器配置上,他们用了总钻风摄像头做赛道图像采集,配合ICM20602六轴惯导模块做姿态解算,电磁部分则是工字电感加运放调理电路。这个组合在疯狂电路组里算是比较经典的配置,但Soberup在惯导和视觉的融合策略上做了一些自己的处理——不是简单地把两个传感器的数据加权平均,而是根据赛道元素的识别结果动态调整融合权重。比如在直道上更信任惯导的积分结果,在弯道和元素区则更依赖视觉的实时修正。
注意:双核架构下,核间通信的延迟必须实测。很多队伍在仿真里跑得好好的,上车就发现视觉结果比实际赛道晚了二三十毫秒,过弯时直接冲出去。这个延迟不是靠调参能解决的,必须从数据链路设计上优化。
1.3 开源目录的结构与阅读顺序
这份开源目录的组织方式是按功能模块分文件夹,每个文件夹里包含源码、配置文件和使用说明。根目录下有一个总的README,说明了编译环境、依赖库版本和烧录步骤。对于想快速上手的读者,建议的阅读顺序是:先看控制主循环的调度逻辑,理解整个系统的时序框架;然后看视觉处理部分,搞清楚图像从采集到输出赛道元素结果经过了哪些步骤;接着看惯导和电磁的融合策略;最后再看具体的控制算法和参数整定方法。
这种顺序的好处是,你先建立了系统的全局观,再深入细节时不会迷失在某个模块的实现里。很多新手拿到开源代码后,直接打开最复杂的视觉算法文件开始读,读了几百行还不知道这些代码在整个系统里处于什么位置,效率很低。
2. 核心模块拆解与实操要点
2.1 视觉赛道元素识别的实现细节
疯狂电路组的赛道元素包括直道、弯道、十字路口、环岛、坡道、断路区等,每种元素的识别策略不同。Soberup的视觉方案没有用深度学习,而是基于传统的图像处理加特征判断。这个选择在智能车竞赛里很务实:主控算力有限,深度学习模型推理耗时不可控,而且赛道元素是已知的、有限的,用规则方法反而更稳定。
图像处理的第一步是二值化。他们用的是大津法自适应阈值,但做了一些改进:不是对整幅图像算一个全局阈值,而是把图像分成上下两部分分别计算。原因是赛道图像的上半部分(远处)和下半部分(近处)光照条件差异很大,全局阈值会导致远处赛道丢失或者近处噪声过多。这个细节在实验室里可能不明显,但在赛场灯光不均匀的情况下,效果差异很大。
二值化之后是边缘提取和赛道线拟合。他们用的是八邻域爬线法,从图像底部中心开始,沿着赛道边缘向上搜索。爬线法的好处是计算量小,适合实时处理;缺点是抗噪能力弱,如果二值化后有孤立噪点,爬线可能会跑偏。Soberup的处理方式是在爬线之前先做一次中值滤波,去掉明显的孤立噪点,同时在爬线过程中加入方向约束——如果下一步的方向和当前方向偏差超过阈值,就认为遇到了噪声,跳过该点继续搜索。
元素识别的核心逻辑是状态机。系统维护一个当前赛道状态的变量,根据视觉特征在不同状态之间切换。比如从直道进入弯道时,赛道线的曲率会逐渐增大,当曲率超过阈值时切换到弯道状态,控制策略随之调整。十字路口的识别则是看赛道线是否在某个区域突然消失,同时左右两侧出现对称的边线。环岛的识别更复杂一些,需要判断赛道线是否出现圆弧特征,并且圆弧的圆心在赛道内侧还是外侧。
实操心得:元素识别的阈值不要设得太死。赛场的光照、赛道材质、甚至摄像头的高度和角度微调,都会影响图像特征。建议把关键阈值做成可在线调整的参数,通过上位机实时修改,边跑边调。Soberup的代码里有一个参数表结构体,所有阈值都放在里面,通过无线串口可以动态更新。
2.2 惯导与编码器的位姿融合策略
惯导模块用的是ICM20602,这是一颗六轴传感器,包含三轴加速度计和三轴陀螺仪。在智能车上,惯导的主要作用是提供车身的姿态角和角速度,辅助视觉做赛道元素的判断,同时在视觉丢失时短时间维持位姿估计。
陀螺仪的输出是角速度,积分得到角度。但陀螺仪有零偏,积分会漂移。Soberup的处理方式是:在车辆静止时采集陀螺仪的零偏,之后在运行过程中用加速度计的数据做修正。加速度计可以测量重力方向,从而得到车身的俯仰角和横滚角。但加速度计对振动很敏感,电机转动和赛道颠簸都会引入噪声。他们的做法是对加速度计数据做低通滤波,截止频率设在20Hz左右,既能滤掉高频振动,又不会引入太大的相位延迟。
编码器提供的是车轮转速,积分得到行驶距离。编码器和惯导的融合用的是扩展卡尔曼滤波。状态量包括位置、速度、姿态角,观测量是编码器速度和惯导角度。这个融合方案在直道上效果很好,但在弯道和打滑情况下,编码器的积分误差会变大,这时候惯导的角速度积分更可靠。Soberup的做法是根据视觉识别的赛道曲率动态调整观测噪声矩阵——直道上更信任编码器,弯道上更信任惯导。
这里有一个容易被忽略的细节:惯导和视觉的时间戳对齐。摄像头采集一帧图像需要时间,图像处理也需要时间,所以视觉输出的赛道元素结果实际上对应的是几十毫秒之前的赛道状态。如果直接用这个结果做控制,相当于用过去的信息控制现在的车,在高速情况下会导致控制滞后。Soberup的做法是在视觉结果上叠加一个基于惯导和编码器数据的预测补偿,把赛道元素的位置往前推一个处理延迟的时间。
2.3 电磁信号调理与采样处理
疯狂电路组的电磁部分需要自己设计调理电路,这是很多队伍的痛点。工字电感感应到的信号很微弱,而且随着距离导线的远近变化,信号幅值变化范围很大。如果直接用ADC采样,近处信号饱和、远处信号淹没在噪声里。
Soberup的调理电路用了两级放大:第一级是仪表放大器,做差分放大,抑制共模噪声;第二级是可调增益放大器,根据信号幅值自动调整增益。自动增益的控制逻辑是:先以固定增益采样一次,判断信号幅值是否在ADC量程的合适范围内,如果太强就降低增益,太弱就提高增益,然后再采样一次作为最终结果。这个逻辑在代码里是一个状态机,每个控制周期执行一次。
采样时机也很关键。电机PWM开关会产生电磁噪声,如果ADC采样和PWM开关同步,噪声会直接混入信号。Soberup的做法是把ADC采样安排在PWM周期的中点,这时候开关噪声已经衰减得差不多了。同时,ADC采样用DMA传输,不占用CPU时间,采样完成后触发中断,在中断里做信号处理。
注意:电磁信号的滤波参数需要根据实际赛道调试。截止频率设得太低,信号延迟大,过弯时反应慢;设得太高,噪声滤不干净,控制输出会抖动。建议先用示波器看原始信号,确定噪声的主要频率成分,再设计滤波器。
3. 实操过程与核心环节实现
3.1 开发环境搭建与工程编译
Soberup的开源工程是基于IAR Embedded Workbench开发的,主控芯片是某国产32位MCU。如果你用的是其他IDE,比如Keil或者GCC,需要自己移植启动文件和链接脚本。工程目录下有一个docs文件夹,里面有一份环境搭建的详细说明,包括IAR的版本要求、器件支持包的安装方法、以及编译选项的配置。
编译之前需要确认几个事情:第一,MCU的型号和Flash/RAM大小要和工程配置一致,否则链接会报错;第二,工程依赖的库文件(比如CMSIS、外设驱动库)要放在正确的路径下;第三,如果用了视觉模块,摄像头的驱动文件需要根据实际使用的摄像头型号做适配。Soberup用的是总钻风摄像头,驱动里已经包含了DMA采集和图像格式转换的代码,换成其他摄像头的话这部分需要重写。
烧录方式支持SWD和串口两种。SWD需要J-Link或者DAP-Link调试器,串口烧录则需要MCU内置的Bootloader。建议开发阶段用SWD,可以在线调试和单步执行;比赛时用串口烧录,减少接线。
3.2 关键参数整定与调试流程
参数整定是智能车备赛里最耗时间的环节。Soberup的代码里把所有可调参数集中在一个param.h文件里,用宏定义或者结构体常量的形式组织。这样做的好处是,调参时只需要改一个文件,不用在多个源文件之间跳来跳去。
调试的顺序建议是:先调视觉,确保赛道元素识别准确;再调惯导和编码器的融合,确保位姿估计不漂移;然后调电磁信号的调理和采样,确保信号干净;最后调控制参数,让车能稳定跑完全程。
视觉调试时,先把车放在赛道上,通过上位机看二值化后的图像和赛道线拟合结果。如果二值化效果不好,调整阈值;如果赛道线拟合跑偏,检查爬线算法的起始点和方向约束。元素识别的调试需要实际摆放对应的赛道元素,比如用胶带在地上贴一个十字路口,看识别逻辑是否触发。
惯导调试时,先把车静止放置,采集陀螺仪的零偏,写入参数表。然后用手转动车模,看姿态角输出是否跟手。如果角度漂移快,检查加速度计的滤波参数和融合权重。
控制参数整定用的是经典的PID。Soberup的方案里,舵机控制用的是位置式PID,电机控制用的是增量式PID。舵机PID的输出限幅要根据舵机的机械行程来设,太大容易烧舵机,太小过弯不够。电机PID的输出限幅要考虑电池电压和电机驱动能力。
实操心得:调参时不要一次改多个参数。每次只改一个,观察效果,记录下来。很多队伍调参调到最后,自己都不知道哪个参数起了作用。Soberup的代码里有一个参数记录功能,每次修改都会把旧值和新值写入日志,方便回溯。
3.3 赛道实测与数据回传分析
实验室调好的参数,到赛道上往往需要重新微调。原因是赛道的摩擦系数、光照条件、电磁环境都和实验室不同。Soberup的做法是在车上装一个无线串口模块,把关键数据实时回传到上位机,包括视觉识别的赛道元素、惯导的姿态角、编码器速度、电磁信号幅值、控制输出等。
上位机软件是他们自己写的,用Python加PyQt做界面,用Matplotlib画实时曲线。数据回传的格式是自定义的二进制协议,每帧数据包含时间戳和各个变量的值。上位机收到数据后解析并绘图,同时保存到CSV文件,方便赛后分析。
赛道实测时,先低速跑几圈,看数据曲线是否正常。重点关注几个地方:过弯时视觉识别的赛道曲率是否和实际弯道一致;惯导的角度积分在跑完一圈后是否回到零附近;电磁信号在直道和弯道上的幅值变化是否符合预期。如果发现异常,先排查传感器和电路,再调算法参数。
高速跑车时,数据的采样率要足够高,否则会丢失细节。Soberup的回传频率是100Hz,对于控制周期1ms的系统来说,相当于每10个控制周期回传一次数据。这个频率在调试时够用,但如果要看高频抖动,需要提高到500Hz甚至更高。
4. 常见问题与排查技巧实录
4.1 视觉识别不稳定怎么排查
视觉识别不稳定的表现有很多种:有时候能识别到赛道线,有时候识别不到;元素识别的触发时有时无;赛道线拟合的结果跳变。排查的思路是从图像源头开始,逐级往后查。
先看原始图像是否正常。如果原始图像就偏暗或者过曝,那是摄像头曝光参数的问题。总钻风摄像头支持自动曝光,但在赛场灯光变化快的情况下,自动曝光可能跟不上。建议改成手动曝光,根据现场光照调好固定值。
如果原始图像正常,看二值化后的图像。二值化效果差通常是阈值问题。大津法在赛道和背景对比度低的时候效果不好,可以改成固定阈值加动态补偿。Soberup的代码里两种模式都支持,通过参数切换。
如果二值化正常,看赛道线拟合的结果。爬线跑偏通常是起始点选错了,或者方向约束太松。起始点应该在图像底部中心,对应车模正前方的赛道位置。方向约束的阈值要根据赛道曲率来调,太松容易受噪声影响,太紧过弯时跟不上。
元素识别的问题更复杂一些。十字路口的识别依赖赛道线消失和对称边线出现,如果赛道上有阴影或者反光,可能误触发。环岛的识别依赖圆弧特征,如果摄像头角度不对,圆弧可能被压缩成椭圆,识别率下降。这些问题的通用解法是增加确认机制:不是一帧识别到就触发,而是连续多帧都识别到才确认。
4.2 惯导积分漂移的抑制方法
惯导积分漂移是必然的,只能抑制,不能消除。漂移的来源有三个:陀螺仪零偏、加速度计噪声、积分累积误差。抑制的方法也对应这三个来源。
陀螺仪零偏在每次上电时重新标定。标定时车必须静止,采集几百个样本取平均。如果车在标定时有微小振动,零偏估计就不准,积分漂移会很快。Soberup的代码里有一个零偏标定函数,上电后延时两秒开始标定,标定期间LED闪烁提示。
加速度计噪声用低通滤波抑制。滤波器的截止频率要根据实际振动情况调。截止频率太低,姿态角响应慢;太高,噪声滤不干净。建议先用示波器或者数据回传看加速度计的原始波形,确定噪声的主要频率,再设截止频率。
积分累积误差用视觉修正。当视觉识别到赛道元素时,可以根据元素的位置反推车模的位姿,用来修正惯导的积分结果。这个修正不是直接覆盖,而是用卡尔曼滤波做融合,避免视觉的噪声引入新的抖动。
注意:惯导和视觉的融合权重需要根据赛道元素动态调整。直道上视觉特征少,更信任惯导;弯道和元素区视觉特征丰富,更信任视觉。这个权重表在Soberup的代码里是一个数组,根据当前赛道状态查表得到。
4.3 电磁信号受电机干扰的处理
电机干扰是电磁组的老大难问题。PWM开关的谐波会通过电源线和空间辐射耦合到电磁信号链里。表现是电磁信号的采样值上叠加了高频噪声,严重时甚至淹没有效信号。
处理电机干扰要从几个方面入手。硬件上,电机的电源线和信号线要分开走,避免平行走线。电机的驱动电路要加续流二极管和滤波电容,减少开关噪声。电磁信号的调理电路要加屏蔽罩,减少空间辐射耦合。
软件上,ADC采样要和PWM开关错开。Soberup的做法是把ADC采样安排在PWM周期的中点,这时候开关噪声已经衰减。同时,ADC采样用多次采样取平均的方式,进一步抑制随机噪声。如果干扰仍然严重,可以在软件里加一个陷波滤波器,专门滤除PWM频率及其谐波。
还有一个容易被忽略的点:电磁信号的参考地。如果参考地和电机的地共用,电机电流在地线上的压降会直接叠加到电磁信号上。建议电磁信号的调理电路用独立的参考地,通过单点连接到主地。
4.4 常见问题速查表
| 问题现象 | 可能原因 | 排查方法 | 解决措施 |
|---|---|---|---|
| 视觉识别时有时无 | 光照变化、阈值不当 | 回传原始图像和二值化图像 | 改手动曝光,调整阈值 |
| 元素识别误触发 | 赛道阴影、反光 | 回传识别状态和图像 | 增加多帧确认机制 |
| 惯导角度漂移快 | 零偏标定不准、振动大 | 静止时看角度变化 | 重新标定,加低通滤波 |
| 电磁信号噪声大 | 电机干扰、地线共用 | 示波器看原始信号 | 错开采样时机,独立参考地 |
| 控制输出抖动 | PID参数不当、信号噪声 | 回传控制输出和传感器数据 | 调整PID,增加滤波 |
| 过弯冲出去 | 视觉延迟、控制滞后 | 回传视觉时间戳和控制周期 | 加预测补偿,优化数据链路 |
| 直道跑偏 | 惯导漂移、舵机中值不对 | 回传姿态角和舵机输出 | 修正惯导,重设舵机中值 |
| 烧录后不运行 | 时钟配置错误、启动文件不对 | 检查时钟树和链接脚本 | 对照工程配置逐项检查 |
5. 开源协作与后续扩展方向
5.1 如何参与开源贡献与反馈问题
Soberup的开源目录用的是Git做版本管理,托管在代码平台上。如果你想贡献代码,建议先fork一份到自己的账号下,在本地修改和测试,确认没问题后再提交Pull Request。提交时写清楚修改的内容和原因,如果是修复bug,附上复现步骤;如果是新增功能,说明使用方法和测试结果。
反馈问题时,尽量提供完整的信息:使用的硬件配置、软件版本、编译环境、问题现象、复现步骤、以及你尝试过的排查方法。只写一句“跑不起来”的问题,很难得到有效的帮助。Soberup的issue模板里有一个检查清单,提交前对照填一下,能省很多沟通时间。
代码风格方面,Soberup的工程里有一个.clang-format文件,定义了代码格式规范。提交前建议用clang-format格式化一下,保持风格一致。变量命名用英文,注释可以用中文,但关键算法和接口的注释建议中英双语,方便更多人阅读。
5.2 从这套方案可以延伸的技术点
这套开源方案虽然是为疯狂电路组设计的,但里面的很多模块可以迁移到其他场景。视觉赛道元素识别的框架,稍作修改就可以用于其他循迹任务,比如仓库AGV的路径识别、扫地机器人的边界检测。惯导和编码器的融合策略,是移动机器人位姿估计的通用方法,换到ROS平台上也能用。
如果你想在这套方案基础上做扩展,有几个方向值得尝试。一是把视觉处理从传统方法换成轻量级神经网络,比如用TinyML在MCU上跑一个小的卷积网络做赛道元素分类。二是把控制算法从PID换成模型预测控制,在高速情况下可能有更好的表现。三是增加车与车之间的通信,做多车协同或者编队行驶。
实操心得:扩展功能时,建议先在仿真环境里验证算法,再上车实测。智能车的调试成本很高,每次上车都要重新摆赛道、调参数。用Python或者MATLAB搭一个简单的仿真环境,把车辆运动学和传感器模型建进去,算法先在仿真里跑通,能省很多时间。
5.3 给下一届参赛队伍的建议
如果你正在准备下一届智能车竞赛,这套开源方案可以帮你省掉很多从零搭建的时间,但不要直接照搬。每个队伍的硬件配置、赛道环境、甚至车模的机械结构都有差异,直接抄参数大概率跑不好。建议把这份代码当作参考框架,理解每个模块的设计思路,然后根据自己的实际情况做适配。
备赛的时间分配上,建议把视觉和惯导的调试放在前面,因为这两个模块的调试周期最长,而且相互影响。电磁和电路部分如果基础扎实,反而可以快一些。控制参数的整定放在最后,因为前面的模块不稳定,控制参数调了也是白调。
最后说一点:智能车竞赛的乐趣在于工程实践中的取舍和优化。开源代码给你的是起点,不是终点。真正让你拿成绩的,是你对赛道和车的理解,以及在这个理解基础上做出的每一个设计决策。Soberup的这套方案里,最有价值的不是某一行代码,而是代码背后那些“为什么这么选”的思考。把这些思考搞明白了,你就能在自己的车上做出更好的选择。