
简介本资源是2024年全国大学生电子设计竞赛H题‘自动驾驶小车系统’的高分参赛方案面向计算机、自动化、电子信息等专业学生专为课程设计、毕业设计及电赛备赛提供可落地的完整工程实践参考。项目经导师指导并获评审99分代码功能完备、注释清晰配套原理图与软硬件协同设计文档小白用户亦可基于说明快速编译烧录并实车运行。压缩包共144个文件含84个头文件.h定义模块接口与寄存器配置38个源文件.c实现主控逻辑、MPU姿态解算、CAN通信、Flash存储、AES加密及定时器调度等核心功能另有PDF原理图、Keil工程配置.uvprojx/.uvoptx、系统配置.syscfg及调试辅助文件.axf/.map总大小10.26MB。目前已有2163人学习下载涵盖从传感器融合到路径决策的全链路实现特别适合作为嵌入式系统综合实训与竞赛复盘的高质量参考范例。 2024年电赛H题我做了一台自动驾驶小车最后拿了省一。这套系统的源代码和原理图我都整理好了准备按模块拆开讲从题目分析、硬件选型到图像处理和控制算法再到现场调试踩过的坑一条线走完希望能给后面参赛的同学省点弯路。先说结论H题的核心不是“做个能跑的小车”而是“在限定规则下稳定完成多重任务”——出库、循迹、环岛、避障、停车、入库全都要在摄像头闭环下自动完成。很多队伍死在稳定性和鲁棒性上代码能跑和跑得稳完全是两码事。这套方案里我用STM32F407做为主控OV7725灰度摄像头采集赛道图像DRV8701驱动直流减速电机前轮舵机转向靠一套改过的图像中线提取算法和PD控制器完成了全任务闭环。下面把关键设计逐个拆开聊。1. H题题目解读与整体方案设计1.1 2024年H题到底在考什么电赛H题这类自动驾驶小车题目表面看是“做个车走赛道”但实际考的是三个层次的能力你能不能把视觉信息稳定还原成赛道模型能不能把赛道模型转化成平滑的转向和速度控制以及能不能在有限时间内稳定完成所有比赛环节。题目一般给一条带边线的赛道车要从车库出发沿赛道跑若干圈中间可能设置环岛、障碍或者停车位。评分时看完成度和稳定性而完成度靠的是边缘检测和任务状态机稳定性靠的是控制器的鲁棒性。这里要特别提醒一下H题有个隐含的高分要素是“停车精度”。很多队伍车速很快但最后停车入库的位置误差大直接扣大分。所以控制系统必须在高速循迹和低速到位之间平滑切换而不是靠一套参数跑到底。我的整体思路是摄像头负责收集赛道信息主控负责把图像变成控制量底层电机和舵机负责执行。三层架构清晰分开每一层都可以单独调参和测试这是后面调试效率高的根本原因。这个思路看起来朴素但很多队伍翻车就是因为三层揉在一起改一个参数全乱套。1.2 系统整体架构与模块划分整个系统我按功能拆成五个模块主控电路、图像采集、电机驱动、转向执行、电源管理。主控用STM32F407VET6Cortex-M4内核168MHz主频跑320x240的灰度图像二值化加中线提取帧率能到60fps左右这个性能余量非常重要因为电赛现场光线变化大算法可能要加自适应阈值多出来的算力刚好够用。图像采集模块用OV7725传感器DVP接口直连主控的DCMI外设配合FIFO缓存不占用CPU去等像素数据。选OV7725而不是更高分辨率的摄像头是因为赛道识别根本不需要太多细节分辨率高反而让二值化噪声变多、处理变慢。电机驱动用DRV8701加外部MOS管方案相比TB6612这类集成H桥DRV8701的内阻更低持续电流能力更强长时间跑下来发热小很多。这个是我们实测对比后换上去的TB6612在连续跑三圈之后会有明显的性能衰减。电源部分用2S锂电池7.4V一路直接给电机供电另一路经5V LDO给单片机、摄像头和舵机供电。注意舵机和电机工作在同一个电压域时启动瞬间压降很大所以我把舵机供电独立出来加了滤波电容避免复位问题。2. 硬件系统原理图与电路设计要点2.1 主控最小系统设计要点主控最小系统看似都是标准电路但电赛环境下有几个细节特别容易被忽略。第一是晶振我用了8MHz无源晶振负载电容选22pFPCB布局时晶振靠近MCU引脚走线尽量短这能避免起振不稳或者电磁干扰导致的死机。STM32F407内部有PLL倍频到168MHz稳定性没问题。第二是复位电路很多同学直接画一个10k电阻加0.1uF电容但实际在电机启动瞬间电源噪声很容易触发复位。我的做法是把复位引脚加一个RC延时电路同时在靠近MCU的电源引脚密集放置100nF去耦电容并且单独加一颗10uF钽电容给模拟部分滤波。第三是调试接口SWD四线SWDIO、SWCLK、GND、3V3一定要引出来而且要放到板边容易插的位置。电赛现场需要反复下载程序如果调试口被结构件挡住你会想砸车。我还额外引出了两路串口一路用来打印调试信息一路备用接蓝牙模块做无线参数调节这个在后面PID整定环节帮了大忙。原理图设计时建议所有IO口都仔细确认5V容忍属性OV7725的DVP接口是3.3V电平但有些摄像头模块带了电平转换如果不确认直接把5V信号灌进MCU可能会烧引脚。另外编码器接口要接上拉电阻霍尔编码器是开漏输出不加上拉读不到正确的脉冲。2.2 摄像头选型与接口电路这里重点讲为什么选OV7725灰度摄像头。彩色摄像是“看起来很美”但实际做赛道识别时我们需要的是稳定的灰度边界而不是颜色分类。灰度图在二值化时只需要一个阈值就能把赛道和背景分开处理快而且对光照的鲁棒性可以通过自适应阈值来弥补。OV7725最高支持640x480但我实际只用320x240每帧图像320x24076800个像素在168MHz主频下处理时间在8ms左右留足时间给控制算法。DVP接口接线看似简单其实有讲究。PCLK像素时钟和HREF行参考必须用短走线尽量靠近MCU引脚因为像素时钟在320x240分辨率下大约24MHz如果走线过长信号完整性会变差图像会出现横条纹。另外VSYNC帧同步信号最好接在MCU的外部中断引脚上这样每帧图像到来时触发一次DMA传输CPU不需要轮询等待。FIFO缓存芯片AL422B我建议加上。虽然F407的DCMI可以直接接收数据但DMA传输会占用总线带宽如果同时还要跑PID计算帧率会被拉低。用了FIFO之后摄像头数据先写入FIFO主控空闲时再批量读取实测帧率稳定在60fpsCPU占用率只有40%左右。2.3 电机驱动与电源树电机驱动是整个硬件里最容易出问题的地方。我用DRV8701驱动外部N-MOS管构成全桥栅极驱动电流充足适合驱动大电流直流电机。原理图上要注意几点第一半桥的高端MOS管需要自举电容这个电容要靠近芯片的BH引脚否则高边栅极驱动电压不足MOS管无法完全导通发热会急剧上升。第二电流采样电阻要用低阻值大功率电阻比如0.05欧姆2W采样信号经过RC滤波后进芯片的电流放大引脚用于限流保护。电源树设计方面我单独画了一张表来规划电流走向电源轨来源负载峰值电流VBAT7.4V2S锂电池电机驱动、DRV87013~5A5V5V LDOAMS1117-5.0舵机、编码器1~1.5A3.3V3.3V LDOAMS1117-3.3MCU、摄像头、FIFO0.3A舵机的启动电流很大如果和单片机共用一路5V会引起电压跌落导致MCU复位。所以我把舵机供电从主5V分出来单独接一个大容量电容并且在PCB上直接用粗铜皮走线减小线路阻抗。实测这样改完舵机猛打方向时MCU电压纹波控制在0.2V以内。2.4 原理图设计避坑清单画原理图这件事很多队伍喜欢赶时间直接套开发板图纸但电赛环境下稳定性要求高有几个坑必须避开。地线处理电机驱动的地PGND和数字地GND要单点连接最好是在电源输入端用0欧电阻或者磁珠连接。如果直接大面积铺地连在一起电机换向时产生的反向电动势会通过地线干扰MCU轻则图像花屏重则程序跑飞。去耦电容每个IC的电源引脚都要放0.1uF电容电容到引脚的距离不要超过3mm。钽电容主要用于电源入口的储能容量可以按负载电流的10倍估算。电机驱动部分的栅极电阻要靠近MOS管的栅极否则开关瞬间会产生振铃导致MOS管发热甚至击穿。接口防护比赛现场经常插拔线缆建议所有外接接口摄像头、编码器、舵机、电机都加TVS管或者RC滤波。我们第一版没有加在测试时因为插拔瞬间的静电直接烧了一块F407血泪教训。元器件选型电容尽量选X7R或者C0G材质不要用Y5V因为Y5V电容在直流偏压下容量会衰减到标称值的20%以下滤波效果大打折扣。MOS管选型要看导通电阻Rds(on)尽量选低一点的比如30V/5A的N-MOS管Rds(on)在10m欧姆左右比较合适。3. 软件系统图像处理与控制算法实现3.1 图像采集与二值化处理软件部分是整个项目最核心的得分点。摄像头采集到的是灰度图首先要做二值化把赛道白色区域和背景深色区域分开。我用了两种二值化策略一种是固定阈值适合光照稳定的室内环境另一种是大津法OTSU自适应阈值适合比赛现场光照不确定的情况。大津法本质上是在找类间方差最大的阈值它不依赖人为设定而是根据图像本身的灰度分布自动计算。实现代码我在下面贴出来uint8_t otsu_threshold(uint8_t *gray_img, uint16_t size) { // 统计灰度直方图 uint32_t hist[256] {0}; for (uint16_t i 0; i size; i) { hist[gray_img[i]]; } // 计算全局平均灰度 float sum_all 0; for (int i 0; i 256; i) { sum_all i * hist[i]; } // 遍历所有阈值找类间方差最大的 float sum_bg 0; uint32_t weight_bg 0; float max_variance 0; uint8_t best_threshold 0; for (int t 0; t 256; t) { weight_bg hist[t]; if (weight_bg 0) continue; uint32_t weight_fg size - weight_bg; if (weight_fg 0) break; sum_bg t * hist[t]; float mean_bg sum_bg / weight_bg; float mean_fg (sum_all - sum_bg) / weight_fg; float variance (float)weight_bg * weight_fg * (mean_bg - mean_fg) * (mean_bg - mean_fg); if (variance max_variance) { max_variance variance; best_threshold t; } } return best_threshold; }大津法在每帧图像上跑一遍耗时大约1ms完全可接受。实测下来大津法在光线从亮到暗切换时二值化结果依然稳定而固定阈值往往会因为反光或者阴影出现大片误判。需要注意的是大津法对双峰明显的图像效果好如果赛道背景复杂可以先做高斯模糊降噪再二值化我用了一个简单的3x3均值滤波成本很低但效果明显。二值化之后图像变成黑白两色。很多新手直接在这一步做边缘检测结果会提取出一堆噪点。我的做法是先把图像按列扫描从左往右找第一个白色像素作为左边界从右往左找第一个白色像素作为右边界只有连续超过5个白点才认为是有效边线这样能过滤掉绝大部分噪声。3.2 中线提取与丢线处理算法有了左右边界中线就是左右边界的中点。但实际赛道图像里不是每一行都能同时看到左右边界比如出弯时车体倾斜内侧边线会丢。这时候需要丢线处理逻辑。我的丢线处理分三个等级第一级左右边界都正常直接取中点计算偏差。第二级一侧丢线另一侧正常。这时候不直接用另一侧边界的一半作为中线而是用上一行的有效宽度作为参考估算丢线侧的边界位置。为什么要这样做因为直接用单侧边界一半会导致中线在丢线瞬间突然跳变控制器输出跳变车就会猛打一下方向。第三级两侧都丢线说明进入了非常规路况比如环岛或者大曲率弯道。这时候调用特殊逻辑比如“记忆最后一帧的赛道宽度向丢线方向继续延伸”。中线偏差的计算我这里用了一个加权平均的方式取图像底部区域离车最近最可信的偏差占70%图像顶部区域占30%因为顶部图像是远处的赛道透视变形严重直接平均会引入误差。在实际调车过程中我发现中线提取的稳定性比提取精度更关键。哪怕中线有10个像素的误差只要每帧都稳定PID控制器也能把车拉回来但如果有1帧的中线突然跳变50个像素车就会瞬间偏离赛道。3.3 转向PD控制与速度PI调节转向控制我用的是经典的PD控制器。输入是图像中计算出的中线偏差error单位像素输出是舵机PWM占空比。为什么用PD而不用PID因为有积分项后系统响应会变慢而且积分容易在过弯时产生超调导致冲出赛道。自动驾驶小车的赛道跟踪本质上是一个跟踪问题稳态误差可以通过P项本身消除因为偏差本身就是误差所以I项不需要。D项用于抑制转向时的抖动让方向变化更平滑。float pid_steer(float error) { float p_term steer_kp * error; // 偏差变化量用于阻尼 static float last_error 0; float d_term steer_kd * (error - last_error); last_error error; // 输出限幅到舵机PWM范围 float output p_term d_term; if (output STEER_MAX) output STEER_MAX; if (output STEER_MIN) output STEER_MIN; return output; }速度控制我用的是PI调节通过编码器反馈当前速度和目标速度比较后输出PWM占空比给电机。速度环的P参数不用太大因为赛道阻力变化不大I参数用来消除摩擦力差异导致的静态误差。这里有个关键点速度环的采样周期要稳定我用10ms定时中断做速度采样和控制更新不要用主循环里“尽力而为”的定时这样在不同负载下速度波动会很大。转向环和速度环是解耦的我通过一个目标速度表让它们协同工作直道目标速度1.8m/s弯道目标速度降到1.0m/s环岛区域速度降得更低。目标速度表的值是基于当前前方赛道的曲率估算出来的我在图像处理里计算了顶部区域中线偏移量的均值作为曲率参考。3.4 比赛任务状态机设计单靠循迹算法不能让车完成比赛因为题目里还有其他任务节点比如出库、环岛、停车入库。我设计了一个有限状态机来管理这个流程typedef enum { STATE_INIT, // 上电初始化 STATE_EXIT_GARAGE,// 出库 STATE_TRACKING, // 正常循迹 STATE_ROUNDABOUT, // 环岛 STATE_AVOID, // 避障 STATE_PARK, // 停车入库 STATE_FINISH // 完成 } sys_state_t;状态机看起来很简单但实现时有个容易翻车的地方状态之间的切换条件必须非常可靠。比如环岛识别我用了一个“计数器模式匹配”的方案连续检测到图像顶部有分叉特征且分叉方向持续比如持续5帧才确认进入环岛状态这样避免单帧误判导致状态混乱。停车入库这个状态我单独设计了控制逻辑因为这个环节对位置精度要求很高。我用编码器累计里程来确定停车点而不是单纯靠图像。当车经过起始线时图像中的“起跑线特征”触发里程计数行驶到设定距离后开始减速停车。实测这个方案在1m/s速度下停车位置误差能控制在3cm以内。状态切换时的参数平滑非常关键可以避免车在切换瞬间“抽搐”。我的做法是在状态切换时把目标速度和目标舵机角度做斜坡渐变比如从2m/s减到0.3m/s用了200ms的斜坡时间这样车会自然减速而不是急刹。4. 整机调试与实测过程4.1 摄像头参数标定与视野调整摄像头安装位置和角度决定了图像质量上限。我第一版把摄像头装在车头正中央高度10cm俯仰角向下45度结果视野里能看到车头而且远处赛道信息太少车到了弯道口才看到弯根本来不及转向。后来我把摄像头升高到15cm俯仰角调到60度视野中心对准前方30cm处这样既能兼顾近处的精确控制和远处30cm以上的预判。标定时我打印了一组辅助线叠加在图像上方便确认视野范围。方法是通过串口发送二值化图像到上位机用简单的Python脚本显示每20帧打印一次这样在调试时能直观看到算法眼中的赛道。摄像头还涉及一个曝光参数问题。OV7725默认自动曝光但比赛现场的光照如果频繁变化自动曝光会让图像忽明忽暗影响大津法阈值。我的做法是把曝光设为手动先测得现场环境光的合适曝光值设置固定参数然后给大津法留一点自适应空间。这样图像亮度基本稳定二值化结果也更一致。4.2 PID参数整定的具体过程PID调参是最耗时的环节我用了分段调参的方式先把快慢速分开调。第一步先调转向PD把车速固定在一个很慢的0.5m/s用小幅度Kp比如1.2让车能勉强走直线然后逐步加大Kp直到车在直道上出现轻微摆动再往回调20%。这时候车的直道稳定了。我实测我的车上Kp在2.2左右就会出现高频抖动所以最终Kp定在1.8。第二步加入Kd。把Kd从0开始慢慢加观察过弯是否更平滑。加Kd后你会发现摆动变小了但过大Kd会让方向对噪声特别敏感有一点点干扰就猛打方向。我的车Kd在6.5左右最合适再大就出现反向抖动。第三步调速度PI。先把目标速度固定调P让车在直道速度波动小然后加I消除坡道和地毯阻力带来的速度跌落。I项调起来有一点技巧从小往大加每次加0.05观察速度曲线是否出现低频振荡如果振荡就退回上一档。调参过程中我无线串口模块帮了大忙。我通过蓝牙模块实时修改PID参数不用每次编译下载直接在手机上看速度曲线和舵机输出调参效率至少提升一倍。4.3 场地实测与关键数据在正式比赛前我们模拟了赛道环境多次测试。下面是几组关键数据来自我最后的稳定参数版本场景目标速度实际平均速度最大偏差像素结果直线道1.8 m/s1.76 m/s8稳定走中线90度弯1.0 m/s0.95 m/s22稳定过弯环岛0.8 m/s0.78 m/s30稳定绕环停车入库0.4 m/s-3cm一次到位这个数据显示我的转向PD控制器的偏差在弯道处最高达到30像素体感上就是车略微贴近弯道内侧但从来没压线。停车入库的精度3cm是我通过里程计和图像双重确认后的结果已经完全满足题目要求。整套系统在满载状态下连续运行10分钟主控温度上升不到10度电机驱动MOS管温度不超过60度说明硬件方案留足了余量。5. 电赛现场常见问题与避坑指南5.1 硬件排查速查表现象可能原因排查方法解决方案单片机反复重启舵机或电机启动电流导致电压跌落示波器抓3.3V和5V电压波形舵机独立供电加强滤波图像花屏或横条纹DVP接口走线过长/信号串扰检查PCLK、HREF走线长度缩短走线增加GND包地电机不转但有嗡嗡声PWM频率不合适或MOS管栅极驱动不足检查PWM频率设10kHz以上优化自举电容布局电池电量下降快电机堵转或驱动管没有完全导通测MOS管温度温度高就是没完全导通检查栅极驱动电压编码器读数乱跳上拉电阻缺失或信号线受干扰用示波器看编码器波形加10k上拉电换双绞线5.2 软件调试高频问题图像二值化后赛道断裂最常见原因是光照不均匀导致局部过曝。我用高斯滤波加大津法组合后基本解决但如果你用的是固定阈值可以试试在二值化前先对图像做局部直方图均衡化。车在快速直道上蛇形走位这是D项太小或者响应周期太长。检查你的控制周期是不是稳定在10ms如果控制周期抖动PD输出的阻尼效果会大打折扣。环岛识别误触发表现为车在普通弯道上突然进入“环岛状态”打大方向。我把条件改成“分叉特征连续5帧出现且方向一致”后彻底解决。另外环岛内补线逻辑要单独写不要和普通丢线逻辑混用否则环岛出口容易走错方向。停车入库位置不准需要考虑从状态切换到停车模式时车的初始速度对停车距离的影响。我的做法是进入停车状态后先用斜坡减速到固定低速再按里程计数停车这样初始速度不同也能精确停位。5.3 高分项目区别于普通项目的细节赛后复盘我总结出高分项目队和平庸队伍的几个明显区别第一是硬件冗余设计够不够。比如我在电机驱动上加了大电流保护和RC吸收网络虽然比赛时没触发但整套系统在连续跑多圈后依然稳稳工作。很多队伍跑到第三圈就开始随机死机多半是硬件余量不够。第二是调试体系完不完善。我有无线调参、上位机显示、数据回放三个调试工具任何问题都能快速定位。有的队伍调参还在“改代码-下载-拔线-看效果”的循环里效率差着几倍。第三是对边缘情况的处理。我花了大量时间处理“丢线”、“逆光”、“起跑线误检”这类极端场景比赛中真正拉开差距的不是正常赛段的圈速而是面对意外情况还能不能稳定跑完全程。最后分享一个实战心得电赛H题这类项目前期方案越稳后期调试越轻松。我们第一版用了复杂的自适应控制策略后来发现赛道模型没那么复杂老老实实把PID调好反而分数更高。稳定、鲁棒、能跑完永远比炫技重要。如果你现在还在为算法复杂度纠结听我一句劝先让车用简单的方案完整跑一圈再谈优化。本文还有配套的精品资源点击获取