ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

智能车竞赛实战:基于RISC-V与CMOS摄像头的嵌入式系统开发全解析

智能车竞赛实战:基于RISC-V与CMOS摄像头的嵌入式系统开发全解析 1. 从赛道到代码一场智能车竞赛的完整复盘刚结束的第十六届全国大学生智能汽车竞赛像一场持续了数月的技术马拉松终于冲过了终点线。从最初拿到赛题规则时的茫然到中期调车调参的焦头烂额再到最后赛场上的惊心动魄整个过程浓缩了嵌入式开发、自动控制、机器视觉和团队协作的几乎所有核心挑战。我所在的队伍选择了基础四轮组主控芯片没有随大流用STM32而是尝试了基于RISC-V架构的CH32V103R8T6这让我们在硬件和底层驱动上多花了不少功夫但也收获了更深入的理解。这篇总结我想抛开那些官方的技术报告模板从一个一线队员的视角聊聊我们是怎么把一堆芯片、传感器和代码变成一辆能在赛道上自主狂奔的智能车的。无论你是准备参赛的新手还是对嵌入式AI小车感兴趣的爱好者希望这些踩过的坑和总结的经验能给你一条更清晰的路径。2. 整体方案设计与核心思路拆解2.1 赛题理解与组别选择背后的逻辑第十六届的规则在继承经典元素的同时也引入了一些新变化。比如基础四轮组赛道元素更加复杂增加了环岛、十字路口、坡道等组合对路径识别的鲁棒性和控制算法的适应性提出了更高要求。我们选择基础四轮组主要是基于团队技术栈的考量摄像头图像处理是我们的强项而平衡节能、双车接力等组别对机械、控制的要求维度不同。选择往往比努力更重要在备赛初期花时间吃透规则、评估自身团队在视觉、控制、机械、电路等方面的能力长短板是决定后续所有工作方向的基础。注意不要盲目追求“热门”或“高端”组别。比如摄像头三轮组对图像处理和控制耦合要求极高节能组对硬件能效和软件优化是极致考验。选择与团队核心能力最匹配的组别才能最大化备赛效率。2.2 “RISC-VCMOS摄像头”核心架构选型我们的硬件核心是CH32V103R8T6 MCU和一款全局快门的MT9V034 数字摄像头。选型理由如下MCU为什么是CH32V103R8T6性价比与可控性在芯片普遍紧缺和价格上涨的背景下这款基于RISC-V内核的MCU提供了不错的性能108MHz主频和丰富的外设ADC、定时器、DMA等且价格相对稳定。更重要的是其开发环境MounRiver Studio和底层库相对简洁迫使我们去更深入地理解寄存器配置和时序而不是过度依赖黑盒化的HAL库。这对于学习嵌入式本质是有益的。挑战与机遇挑战在于社区资源远不如STM32丰富很多驱动需要自己移植或从头编写。机遇也在于此没有现成的“保姆式”代码逼着我们搞清楚了从时钟树配置、GPIO初始化到中断优先级管理的每一个细节这种能力在赛后看来是无价的。传感器摄像头 vs. 电磁我们坚定选择了摄像头方案。虽然电磁方案受光线影响小稳定性高但摄像头方案的信息密度是碾压级的。它能识别赛道边界、中心线、元素环岛、十字等为控制算法提供前瞻性和预判能力。MT9V034全局快门可以有效避免果冻效应在车体高速晃动时依然能获取清晰的图像这是成功的关键。核心思路将复杂的赛道环境通过摄像头转化为二值化的图像矩阵再从中提取出用于决策的“特征”——通常是左右赛道边界线。一切控制都基于这个抽象的“赛道模型”。2.3 系统框架从像素到电机PWM的信号流我们的软件系统是一个典型的“感知-决策-控制”闭环但具体实现上分层清晰[图像采集 (DMA)] - [图像处理 (二值化寻线)] - [路径计算 (中线曲率)] - [控制决策 (PID 状态机)] - [执行输出 (电机PWM 舵机PWM)]每一层都通过清晰的接口变量、结构体耦合方便单独调试和优化。例如图像处理模块只负责输出一个包含左右边线位置的数组控制模块不关心这个数组是如何得来的。这种模块化设计在后期调参时节省了大量时间。3. 核心模块实现与关键技术细节3.1 CH32V103R8T6的底层驱动构建这是所有工作的地基。由于缺乏成熟的生态我们相当于自己搭建了一个微型“操作系统”。时钟系统配置CH32V103最高可运行在108MHz。我们选择使用外部8MHz晶振通过PLL倍频到96MHz作为系统主频。这一步需要在启动文件startup_ch32v10x.S和系统初始化函数中正确配置相关的时钟控制寄存器。关键点必须确保为外设如定时器、ADC、摄像头接口分配的时钟源和分频系数正确否则后续所有时序都会错乱。我们曾因为ADC时钟配置错误导致采样值完全不可信排查了大半天。GPIO与中断配置摄像头的数据引脚D0-D7、行场同步信号VSYNC, HREF需要配置为浮空输入模式并开启上升沿/下降沿中断。DMA应用这是提升性能的关键。我们配置了DMA通道将摄像头数据端口GPIO组直接搬运到内存中的图像缓冲区。在VSYNC帧同步中断中启动DMA在HREF行同步中断中配合DMA完成一行数据的接收。这样CPU几乎不参与数据搬运解放出来进行图像处理。定时器与PWM生成使用高级定时器TIM1产生四路PWM分别控制两个电机的正反转和速度实际上用了两路PWM加两个GPIO方向控制实现双极性驱动。使用通用定时器TIM2配置为编码器模式直接读取电机编码器的脉冲数用于计算实际速度构成速度闭环。PID计算中断另一个通用定时器TIM3被配置为固定频率如1kHz的中断在这个中断服务函数中进行速度PID和方向PID的计算并更新PWM占空比。中断优先级要合理设置确保控制周期稳定。3.2 图像采集与二值化处理优化图像处理的速度和稳定性直接决定了车的上限。稳定采集除了使用DMA还要注意信号消抖。摄像头VSYNC和HREF信号在硬件连接较长时可能有毛刺。我们在中断服务函数入口添加了简单的延时判断连续采样几次引脚状态确认是稳定电平变化后才执行后续操作避免了误触发。动态阈值二值化固定阈值在光线变化时效果很差。我们采用了大津法OTSU或自适应局部阈值。大津法在每帧图像开始处理前对整帧或感兴趣区域ROI的灰度直方图进行计算自动得出一个最佳分割阈值。计算量稍大但全局效果稳定。自适应阈值将图像分成若干小网格对每个网格单独计算阈值如取均值或中值。这种方法对光照不均的赛道适应性更强。我们最终采用了网格自适应因为赛场顶光往往会造成赛道中间亮、两边暗。优化技巧不必对整幅图像例如188*120进行阈值计算。可以只对图像下方几行车近处的赛道和上方几行远处的前瞻分别计算阈值既能适应光照变化又减少了计算量。3.3 赛道元素识别与状态机设计识别出直道、弯道、十字、环岛等元素是进行智能决策的前提。边界搜索与中线提取采用经典的“爬边线”算法。从图像底部中心开始向左向右搜索黑白跳变点作为初始边线。然后逐行向上以上一行的边线位置为起点进行小范围搜索得到当前行的边线。将所有边线点拟合成两条曲线其中间线即为引导线。丢线处理当某一行搜索不到边线时不能简单停止。我们采用“记忆外推”策略用之前几行边线的斜率或曲率预测当前行的可能位置扩大搜索窗口。如果连续多行丢线则触发“丢线状态”控制策略切换到保守模式如减速、按上一有效曲率转向。元素识别逻辑十字路口当左右边线同时大幅向外发散且中间区域在一定行数内持续为白色赛道色时判定为十字。我们的策略是进入十字后保持进入前的角度和速度直行通过同时抑制边线搜索防止误搜到十字的横向边线。环岛这是难点。我们通过识别边线的“突变”来判断环岛入口。当单边边线例如右边线突然向内凹陷形成弧口而另一边线相对正常时初步判定为环岛入口。随后进入一个专门的“环岛状态机”。环岛状态机设计状态1进入识别到入口特征开始记录内侧边线轨迹。状态2循迹不再跟踪外侧边线而是以识别到的内侧弧线作为单边引导线同时结合陀螺仪MPU6050的Z轴角速度积分判断车身是否已绕行约270度。状态3出岛当角度积分达到预设值且图像重新出现正常的双边线时切换回普通循迹模式。关键出岛判断必须图像和陀螺仪数据融合单靠任何一个都容易出错提前出岛或卡在岛内。3.4 控制算法PID与更高级的策略双闭环PID控制速度环内环输入是目标速度由路径曲率映射得到弯道慢直道快和编码器反馈的实际速度输出是电机PWM。使用PI控制器即可积分项能消除静差但要注意积分饱和需要做抗饱和处理。方向环外环输入是期望路径的横向偏差中线与图像中心的偏差和偏差变化率输出是舵机目标角度或直接是舵机PWM。使用PD控制器。微分项能预测偏差趋势让转向更平滑抑制过冲。参数整定心得先调方向环再调速度环。在车静止时用手推动小车产生横向偏差观察舵机能否快速、无超调地将车轮回正。然后低速在直道上跑微调参数使车能沿直线行驶。最后上弯道重点调整微分系数抑制振荡。前瞻与曲率预瞄简单的偏差控制是“滞后”的因为它基于车当前位置的误差。我们引入了“前瞻点”概念。从图像中线上选取一个距离车头一定距离的点例如图像顶部往下20行计算该点与图像中心的横向偏差。用这个前瞻偏差来控制舵机相当于让车提前转向过弯更加流畅自然。曲率计算通过拟合出的中线计算其曲率。曲率可以直接映射为目标速度大曲率-低速也可以作为前馈量加入到方向环控制中实现“弯道提前打舵”。4. 系统调试与性能优化全记录4.1 开发调试环境搭建硬件调试器我们使用WCH-Link通过SWD接口对CH32V103进行程序下载和在线调试。MounRiver Studio内置的调试功能基本够用可以查看变量、设置断点。软件调试“后门”无线串口在车上加装一个蓝牙串口模块如HC-05将关键数据如图像行数据、边线位置、PID输出、状态机状态实时发送到电脑上位机。上位机软件我们基于Python的PyQt5和OpenCV自己编写了一个简单的上位机。它能绘制出摄像头看到的二值化图像、提取的边线、计算出的中线并以波形图形式显示速度、偏差等数据。这是调试效率提升十倍的关键。你可以直观地看到车“眼中”的世界以及控制算法是如何理解的。参数在线调参在上位机中制作滑动条通过无线串口动态修改车上的PID参数、阈值等并立即观察效果避免了反复修改代码、编译、下载的繁琐过程。4.2 机械结构与参数调校“软件决定上限机械决定下限。”再好的算法如果车装得歪歪扭扭也跑不好。重心与陀螺仪安装电池、主板等重物尽量放低、居中。MPU6050陀螺仪模块必须用海绵胶牢牢固定在车体中心并与车身轴线平行避免振动干扰。前轮前束与主销内倾对于舵机转向的车前轮通常设置为微小的“内八字”前束这有助于提高直行稳定性。这些微调需要耐心每次只动一点然后上路测试直行是否跑偏。轮胎处理新轮胎表面光滑抓地力不足。我们用细砂纸轻轻打磨轮胎表面增加摩擦力。胎压也要一致不能一软一硬。4.3 代码级优化技巧当算法逻辑正确后优化就是为了跑得更快。减少计算量缩小ROI车在高速运行时远处图像细节来不及处理。我们动态调整图像处理的行数车速越快处理的图像行数越少只关注近处赛道保证控制周期稳定在5ms以内。查表法将一些频繁计算的结果预先算好存入数组。例如atan2、sqrt函数非常耗时我们可以根据偏差和偏差变化率预先计算一个PD控制量的二维查找表。整数运算在MCU上浮点运算速度远慢于整数运算。我们将所有PID参数、误差等变量都乘以一个放大系数如1024用int32_t类型进行整数运算最后输出时再缩小。精度完全足够。提高系统稳定性看门狗一定要开启独立看门狗IWDG。在main函数循环和关键任务中定期“喂狗”。一旦程序跑飞或陷入死循环看门狗能复位系统至少让车停下来而不是撞毁。堆栈大小设置在启动文件中适当调大堆栈Stack_Size。复杂的函数调用和局部变量可能造成栈溢出导致各种难以复现的诡异错误。5. 赛场实战问题排查与应急方案无论实验室跑得多好赛场永远是另一回事。以下是我们遇到和观察到的高频问题问题现象可能原因排查步骤与解决方案发车后原地不动或抽搐1. 电机驱动桥未使能或损坏。2. 编码器接线松动速度反馈为0导致PID输出异常。3. 程序未正常进入主循环。1. 检查电机驱动芯片的使能引脚电平测量电机两端电压。2. 晃动编码器接线在调试器查看编码器计数值是否变化。3. 检查启动代码添加LED闪烁指示确认程序运行。直道左右摇摆振荡1. 方向环PID微分系数D太小或为负。2. 机械松动舵机连杆有虚位。3. 图像处理延时过大控制滞后。1.首要降低P值增加D值。D是抑制振荡的关键。2. 用手轻轻晃动前轮检查是否有松动。3. 通过上位机查看从图像采集到舵机输出的总延时优化代码。过弯时冲出赛道1. 弯道速度过快。2. 前瞻距离设置太短转向不及时。3. 图像在弯道丢线策略未正确处理。1. 建立曲率-速度映射表弯道曲率越大目标速度越低。2. 增加前瞻点距离让车“看得更远”。3. 加强弯道处的丢线处理逻辑如使用预测线。元素十字、环岛误识别或漏识别1. 光线变化导致二值化阈值失效。2. 元素识别条件阈值设置不合理。3. 车体经过元素时姿态不稳定图像抖动。1. 必须使用动态阈值。2. 在赛场不同光照下大量测试记录数据调整识别条件的阈值如白色连续行数、边线发散角度。3. 在元素识别期间适当降低速度提高图像稳定性。跑着跑着突然复位1. 电源不稳定电压跌落触发欠压复位。2. 程序堆栈溢出。3. 看门狗未及时喂狗。1. 用示波器监测电池电压大电流负载时是否跌落到MCU最低工作电压以下。加大电容稳压。2. 增加堆栈大小检查是否有大型局部数组。3. 检查看门狗喂狗函数是否在所有可能的主循环路径中都被调用。无线调试突然断开1. 赛场无线环境复杂2.4GHz频段干扰。2. 蓝牙模块供电不足。1. 准备备用方案将关键数据通过IO口输出到逻辑分析仪或使用SD卡离线记录数据。2. 为蓝牙模块单独使用LDO供电避免电机启动时拉低电压。赛场最后24小时检查清单硬件所有螺丝点胶固定所有线缆用扎带或热熔胶固定避免松脱电池电量满格电极片用酒精擦拭干净轮胎清洁无灰尘传感器镜头擦拭干净。软件准备多个版本的固件激进速度版、稳定完赛版确认拨码开关或按键可以切换版本将最重要的PID参数、速度映射表放在易修改的位置如通过按键加减调整。心理制定比赛策略。前一两圈求稳确保完赛拿到基础成绩。后面几圈再尝试逐步提升速度。永远把稳定性放在速度前面。回顾整个备赛过程最大的收获不是奖状而是这套从问题定义、方案设计、模块实现、集成调试到现场排错的完整工程实践能力。选择CH32V103让我们被迫深入底层虽然过程痛苦但回过头看那些对着数据手册调寄存器的夜晚才是真正理解嵌入式系统如何工作的时刻。智能车竞赛就像一个微缩的机器人项目它教会你的远不止如何让一辆小车跑起来而是如何让一个复杂的软硬件系统可靠、智能地工作。如果再来一次我可能还是会选那个“麻烦”的RISC-V芯片因为你知道你吃透的每一点都是别人拿不走的资本。最后一个小建议尽早开始做“系统集成”不要等每个模块都完美了再拼起来。车只有跑起来你才知道真正的问题在哪里。
返回列表