尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

智能车竞赛三轮摄像头组实战:图像处理与控制策略全解析

智能车竞赛三轮摄像头组实战:图像处理与控制策略全解析 简介本资源是第十八届全国大学生智能车竞赛三轮摄像头组的完整嵌入式开发代码包面向参赛学生、嵌入式初学者及图像处理与自动控制方向的学习者聚焦于基于摄像头视觉的实时路径识别与运动控制问题。压缩包共1183个文件涵盖217个C源文件核心算法与驱动逻辑、217个头文件硬件抽象与函数声明、217个编译中间文件.o、94个Makefile构建脚本以及.lsl任务调度配置、.cproject/.project工程配置等整体大小为10.85MB结构清晰适配Infineon AURIX TC2xx系列平台。已有521人学习下载资源包含车道线识别Canny霍夫变换、环岛/坡道/短路/避障等赛道场景决策模块集成位置式与增量式PID电机控制策略并提供zf_driver_spi.c等底层驱动、IfxQspi_SpiMaster.c等芯片外设库及FFT查表等关键数学工具可直接用于工程复现与算法调优。 第十八届全国大学生智能车竞赛三轮摄像头组代码——从备赛到国赛的完整实战复盘如果你正在准备十九届或者往后几届的智能车竞赛又恰好选了摄像头组那这篇文章应该能帮你少走不少弯路。我写的是十八届三轮摄像头组的技术方案虽然新规则每年都在变但一个车能跑得稳的核心逻辑从来都没变过图像看得清、控制算得准、状态切得顺、代码结构经得起现场摧残。这一篇我不打算写成那种“从入门到跑完”的教科书而是按照我们当时从零开始、一直打磨到国赛的完整思路来复盘重点放在代码架构、图像处理链路、控制策略和调参方法上。先说清楚背景。十八届的三轮摄像头组规则上是车模限定为三轮结构允许使用摄像头作为主要感知传感器芯片平台没有强制绑定英飞凌了但我们用的是TC264原因很简单资料多、库函数全、跑图像处理够用。三轮车和两轮车最大的区别在于——它没有自平衡负担所以可以把全部注意力放在速度和寻迹上但相应地三轮车对转向的响应和机械重心偏移特别敏感。代码写不好再好的机械也白搭代码写好了机械上略微粗糙也能跑出不错的成绩。整车的软件系统我按模块分成了五块底层驱动、图像采集与处理、赛道元素识别、控制策略、状态机与逻辑调度。每一块单独拎出来都不难但它们之间的协作关系以及如何在真实赛道上稳定运行才是真正考验人的地方。1. 十八届三轮摄像头组的赛题特点与整体架构设计1.1 三轮结构对代码风格的潜在影响先讲一个容易被忽略的问题机械结构对代码设计的影响。三轮车通常是一个转向舵机在前、两个驱动轮在后也有倒三轮的结构但主流的正三轮居多。这意味着它转向时前轮负责横向偏转后轮负责纵向驱动转弯半径天生比四轮大一点对舵机的响应速度要求也更高。这一点直接决定了我的控制思路**不能像四轮车那样等看到弯道再打方向而是要提前预判用前瞻距离配合图像信息实现“早打、慢回”的策略。**摄像头装在车前部高度25到30厘米俯仰角向下倾斜大概10到15度这样能看到车前方1米到1米5的范围正好匹配三轮车在2.5米/秒左右速度下的有效制动距离。1.2 代码整体分层而不是堆在一起我们最终的代码结构是这样分的底层驱动层负责PWM波生成、编码器读取、串口通信、Flash存储等硬件操作全部封装成独立函数不掺任何业务逻辑。比如encoder_get_speed()、servo_set_angle()、motor_set_duty()。图像处理层负责从摄像头拿到原始图像做灰度转换、二值化、中线提取输出一个数组——每一行的赛道中线位置、赛道宽度、左右边界。元素识别层根据中线数组的特征判断当前是直道、弯道、十字、环岛还是坡道输出一个当前赛道状态。控制策略层根据状态和图像数据计算舵机转角与电机PWM占空比。状态机调度层管理整车在“起跑线前等待—正常巡线—环岛处理—坡道处理—冲线停车”等状态之间的切换。分层最大的好处不是看起来整洁而是调试时可以单独验证每一层的正确性。比如图像处理层有问题我可以在上位机上直接把二值化结果打出来看控制策略有问题我可以在赛道旁直接把舵机目标值和实际值打印出来对比。如果所有逻辑全堆在一个死循环里现场出问题你连从哪里开始查都不知道。2. 图像采集与处理的代码实现细节2.1 摄像头选型的底层原因与初始化配置当时我们也纠结过用灰度摄像头还是RGB摄像头。结论是用灰度——不是RGB不好而是对于赛道寻迹这个场景颜色信息里90%都是冗余的反而会拖慢处理速度、增加曝光干扰。三轮车追求的是稳定和速度灰度图在光照变化下的鲁棒性调好了比彩图好用得多。我们用的是总钻风摄像头灰度输出通过DVP接口和TC264连接。采集分辨率设置成188×120这是反复权衡的结果分辨率再高TC264跑二值化加中线提取就会超过单帧处理时间上限再低的话远处赛道信息又不够前瞻距离会被压缩。188的宽度保证了十字、环岛这些关键元素的左右边界能被完整捕捉到120的高度则刚好覆盖从车头前30厘米到1米5左右的视野范围。初始化的时候有几个关键参数必须显式设置// 摄像头初始化示例TC264 总钻风灰度 void camera_init(void) { dvp_init(DVP_D0_D7); // 8位灰度数据 dvp_set_resolution(188, 120); // 宽度、高度 dvp_enable_interrupt(DVP_DMA_FINISH_IRQ); // 曝光时间、增益需要根据场地光线调整 dvp_set_exposure_time(150); // 单位通常是微秒级别后期调参重点 dvp_set_gain(10); // 增益 }这里有个坑曝光时间和增益不是一成不变的比赛现场的光线和实验室差很多。我们最终的做法是在调试阶段做一个“一键调曝光”的小功能按一下按键单片机自动采集10帧图像计算平均亮度如果整体偏暗就自动增加曝光时间偏亮就减小。国赛现场我们就是靠这个功能在30秒内完成了曝光调整。2.2 大津法二值化而不是固定阈值图像处理的第一步是把灰度图变成二值图——赛道是白色的背景是深蓝色的地毡分开它们才能提取中线。问题在于用固定阈值比如灰度大于150就算白在光线均匀的实验室很稳但一到比赛现场灯光从不同角度打下来赛道表面会有反光有的区域阴影很明显固定阈值会直接把阴影里的赛道截断。我们用大津法Otsu做自适应阈值。大津法的核心思想很简单把所有像素按灰度值分成两类找到一个阈值让类内方差最小、类间方差最大自动适应整体亮度的变化。实现起来代码也不长// 大津法计算自适应阈值 uint8 otsu_threshold(uint8 *gray_image, uint16 size) { uint32 hist[256] {0}; for (uint16 i 0; i size; i) { hist[gray_image[i]]; } uint32 total size; float sum 0, sumB 0, wB 0, wF 0, varBetween 0, maxVar 0; uint8 threshold 0; for (uint16 i 0; i 256; i) sum i * hist[i]; for (uint16 i 0; i 256; i) { wB hist[i]; if (wB 0) continue; wF total - wB; if (wF 0) break; sumB i * hist[i]; float mB sumB / wB; float mF (sum - sumB) / wF; varBetween wB * wF * (mB - mF) * (mB - mF); if (varBetween maxVar) { maxVar varBetween; threshold i; } } return threshold; }实测下来大津法在十八届赛道上处理反光和阴影的效果相当好。但要注意一点大津法假设图像中有明显的前景和背景分布如果一帧图像里大面积的赛道白加上非常少的背景深蓝或者反过来阈值可能会偏。所以我们在二值化之前还会做一个简单的“像素裁剪”只处理图像中间的ROI区域把最边缘的干扰去掉。2.3 中线提取的多种策略与边界处理拿到二值图之后最核心的任务就是提取中线。中线的定义是每一行图像上赛道左边界的列坐标和右边界的列坐标的中点。看起来简单但真正做起来有不少细节。我们用了剥离式提取从图像底部离车最近往上扫描每一行// 逐行提取左右边界与中线 typedef struct { uint8 valid; // 该行是否有效 uint8 left_edge; // 左边界列坐标 uint8 right_edge; // 右边界列坐标 uint8 center; // 中线列坐标 uint8 width; // 赛道宽度 } line_row_t; line_row_t line[ROW_IMAGE]; // ROW_IMAGE 120 void extract_center_line(uint8 *binary_image) { for (uint8 row 0; row ROW_IMAGE; row) { uint8 left 0, right 0; uint8 found_left 0, found_right 0; for (uint8 col 0; col COL_IMAGE; col) { if (binary_image[row * COL_IMAGE col] 255) { left col; found_left 1; break; } } for (uint8 col COL_IMAGE - 1; col 0; col--) { if (binary_image[row * COL_IMAGE col] 255) { right col; found_right 1; break; } } if (found_left found_right) { line[row].valid 1; line[row].left_edge left; line[row].right_edge right; line[row].center (left right) / 2; line[row].width right - left; } else { line[row].valid 0; line[row].center 94; // 图像宽度一半默认值 line[row].width 0; } } }这里有个很实际的经验**图像最底部的1到2行不要用。**因为车头前方离摄像头最近的位置会有车体本身的遮挡或者因为透视关系图像底部会出现大片白色噪点直接拉偏边界。我们处理时直接从第3行开始扫描。顶部90到120行也不要全信远处赛道边缘在几十像素之内边界提取误差很大。另一个细节是行与行之间的突变处理。如果第50行中线在80列第51行突然跳到20列那要么是边界提取错误噪声导致要么是真的遇到了极端弯道。我们增加了一个简单的平滑滤波从底部向上如果相邻两行的中线偏差超过40像素就认为异常把当前行中点修正为上一行的中点再加一个固定偏移量。这个处理在“图像远端的弯道边缘丢失”场景下非常有用。3. 转向控制与差速策略——三轮车怎么过弯才又快又稳3.1 从图像误差到舵机转角的前馈加PID控制这一层我见过太多同学直接用一个比例系数把中线偏差映射成舵机角度实际跑起来非常僵硬弯道一急就甩出赛道。我的方案是前馈控制加位置式PD。前馈量来自当前行的赛道弯曲程度。我在提取中线时顺便计算了一个“弯曲度”指标取第20行、第50行、第80行三个位置的中线偏差值的加权和偏差方向一致且持续增大说明是弯道提前打一个方向基础量。这个基础量直接叠加在PD输出上让舵机在进入弯心之前就有一个初始转角而不是等车身偏了再纠正。// 舵机控制前馈 PD int16 servo_ctrl(line_row_t *line, uint8 current_state) { uint8 base_row 10; // 靠近车身的参考行 uint8 mid_row 45; // 中景参考行 uint8 far_row 80; // 远景参考行 int32 error (int32)line[base_row].center - IMAGE_CENTER; int32 error_mid (int32)line[mid_row].center - IMAGE_CENTER; int32 error_far (int32)line[far_row].center - IMAGE_CENTER; // 前馈量远景误差加权弯道时提前补偿 int32 feedforward error_far * FEEDFORWARD_K; // 位置式PDD项用远近偏差差分近似 int32 p_out error * KP_SERVO; int32 d_out (error - error_prev) * KD_SERVO; error_prev error; int32 output feedforward p_out d_out; // 限幅 if (output MAX_SERVO_DELTA) output MAX_SERVO_DELTA; if (output -MAX_SERVO_DELTA) output -MAX_SERVO_DELTA; return BASE_SERVO_ANGLE output; }这里需要注意区分BASE_SERVO_ANGLE和MAX_SERVO_DELTA。前者是舵机在中位时的PWM占空比对应的角度值后者是允许偏离中位的最大幅度。三轮车前轮转向半径有限如果输出值超过机械极限舵机就会堵转发热且容易损坏齿轮。所以限幅不是简单的代码保护它本质上是在保护硬件。3.2 差速实现直道不差弯道靠内轮减速三轮车的后轮是左右两个驱动轮过弯时如果不差速外侧轮和内侧轮的线速度一样整车就会被强迫转向不足或轮子打滑。很多三轮车方案会用一个电子差速器根据舵机转角计算内外轮的转速差。我的做法比较直接——**不主动给外侧轮加速而是给内侧轮减速。**原因有两点第一电机正向PWM占空比到达极限时再往上加已经没有空间了而减速永远有空间第二减速带来的整车动能损失比加速更可控弯道中内侧轮减速反而会形成一个向弯心旋转的力矩帮助车头“钻”进弯道。具体代码如下// 电子差速 void motor_diff_ctrl(int16 servo_output) { // 根据舵机输出归一化到 -1.0 ~ 1.0 float steer_ratio (float)(servo_output - BASE_SERVO_ANGLE) / MAX_SERVO_DELTA; // speed 是主控目标速度对应的PWM占空比 int16 base_duty speed_ctrl.current_duty; int16 left_duty base_duty; int16 right_duty base_duty; // 左转时steer_ratio为负左轮减速 if (steer_ratio 0) { left_duty base_duty - (int16)((-steer_ratio) * MAX_DIFF_DUTY); } else { right_duty base_duty - (int16)((steer_ratio) * MAX_DIFF_DUTY); } // 限幅并输出 left_duty constrain(left_duty, MIN_PWM_DUTY, MAX_PWM_DUTY); right_duty constrain(right_duty, MIN_PWM_DUTY, MAX_PWM_DUTY); motor_set_left_duty(left_duty); motor_set_right_duty(right_duty); }MAX_DIFF_DUTY这个参数比较关键它决定差速的力度。当初我把它设成总占空比的25%左右在2.5米/秒速度下过1米半径的弯道刚刚好。如果你发现弯道里内侧轮虽然减速了车身还是往外推把这个参数往上加如果发现车在弯道里反而有点“栽头”甚至打转那就减小它。这个参数和机械重心、轮胎摩擦力都有关系需要实测去调。3.3 速度控制不是速度越快越好而是能刹得住速度环我们用增量式PID编码器每5毫秒读一次左右轮速度取平均作为实际速度与目标速度对比后调节PWM占空比。这套代码本身没什么稀奇的重点在于目标速度的规划。**直线可以加速入弯前必须减速出弯后再加速。**我们做了一个根据前方赛道宽度和弯曲度决定目标速度的策略// 根据图像前瞻信息规划目标速度 void plan_target_speed(line_row_t *line) { uint8 far_row 80; uint8 far_width line[far_row].width; int16 far_error (int16)line[far_row].center - IMAGE_CENTER; // 宽度判断正常直道宽度约100像素如果远端正宽度骤减说明是弯道或道岔 if (far_width 60 || abs(far_error) 35) { speed_ctrl.target_speed SPEED_TURN_SLOW; // 入弯减速档 } else { speed_ctrl.target_speed SPEED_STRAIGHT_FAST; // 直道加速档 } }这里有一个特别重要的经验**不要试图把减速点算得太精准宁可提前减速也不要在弯道里急刹。**三轮车的重心偏高急刹时前轮载荷转移非常明显舵机的响应会变得迟钝。与其在弯心边缘试探物理极限不如在直道末端就降到安全速度弯中维持一个稳定的油门出弯之后再全速加速。国赛上很多队伍就是在弯道里刹车过度导致后轮抱死横滑出去的。4. 赛道元素识别与状态机调度——从“能跑”到“不犯傻”4.1 十字、环岛、坡道的图像特征与判定逻辑第十八届的赛道元素包括十字交叉、环岛、坡道、断路虚线等。每个元素的处理方式不同但共同点是它们都会在一瞬间破坏正常的“左白右白中间黑”图像结构如果你不提前识别并切换状态巡线就会直接失效。我们总结的判定特征如下表元素图像特征判定代码逻辑十字中景行出现大面积白块左右边界突然外扩接近图像边缘且持续多行line[row].width WIDTH_THRESHOLD连续超过8行环岛入口一侧边界出现长距离转弯趋势另一侧边界有白块延伸环岛边缘环岛方向判定 单侧边界持续偏移坡道整幅图像灰度分布突变赛道宽度在垂向上变化异常远端出现大片阴影灰度均值突变 宽度方差增大断路虚线某几行找不到有效边界或边界宽度极窄line[row].valid 0连续出现若干行以十字为例正常巡线时赛道的宽度在大部分行都比较接近。当车靠近十字图像中会出现一片连续的白色交叉区域表现为左右边界间距突然变大且不再按透视规律缩小。我们的判定是底部往上第30行到第60行区间内如果连续8行以上的宽度都大过正常直道宽度阈值就进入十字状态。进入十字后舵机保持进十字之前的角度不变直接冲过去直到所有行的宽度恢复正常再切回正常巡线。环岛是这届比赛里最容易翻车的点。环岛分左环和右环图像上比较明显的特征是环岛一侧会出现一条圆环形的白色赛道从主赛道边沿伸出并弯过去。识别到环岛入口之后我们记录当前舵机转角然后强制偏转到一个预设的“入环角度”同时降低速度车子沿着环道圆弧跑一圈直到摄像头再次看到主赛道的横向边沿出环口再恢复到正常巡线。这里最要注意的是**环岛内巡线不能用正常中线提取因为环岛内赛道宽度、边界形态和外面完全不同必须用单独的策略。**我们在环岛状态下直接控制舵机按固定角度开环跑只在出环口附近结合图像纠正。4.2 清理脏数据和增加状态机的滞后保护比赛现场什么情况都可能发生比如灯光闪一下、有观众手机闪光灯扫过来、有飘落的碎纸屑盖住赛道边缘。如果状态机对每一帧图像都即时响应那就会被这些瞬间噪声反复跳变轻则速度抖动重则直接冲出赛道。我们给状态机加了两道护栏时间滤波进入新的赛道元素状态需要连续满足判定条件至少3帧大约30毫秒到50毫秒退出该状态也需要连续5帧满足退出条件。这个滞后回环可以有效避免单帧噪声误触发。状态互锁比如当前正在环岛状态中即使十字判定条件满足了也不会跳转必须等环岛状态正常退出。这个逻辑简单但极其管用现场出问题最多的场景就是“状态乱跳”一个车刚刚还在环岛里突然因为光斑被判成十字转弯逻辑全乱了。状态机的代码结构大概是这样的typedef enum { ST_IDLE, // 等待触发 ST_NORMAL, // 正常巡线 ST_CROSS, // 十字 ST_RING_LEFT, // 左环岛 ST_RING_RIGHT, // 右环岛 ST_RAMP, // 坡道 ST_FINISH // 冲线 } sys_state_t; void state_machine_run(void) { switch (current_state) { case ST_NORMAL: if (check_cross()) { state_enter(ST_CROSS); } else if (check_ring_left()) { state_enter(ST_RING_LEFT); } else if (check_ramp()) { state_enter(ST_RAMP); } else if (check_finish_line()) { state_enter(ST_FINISH); } break; case ST_CROSS: if (cross_exit_condition()) { state_exit(ST_NORMAL); } break; // ... 其他状态类似 } }4.3 起跑线检测与冲线停车这个看起来鸡毛蒜皮但实际比赛中真有人在这上面丢分。起跑线是一条横向的黑线在图像中表现为某几行的赛道中心位置突然出现黑色横条。冲线之后车要能自动停下不能撞到终点围挡。我们冲线检测的代码逻辑很简单在正常巡线状态中从第20行到第60行之间如果连续5行以上的line[row].center附近出现了像素值为0黑的连续区域就认为检测到冲线。然后进入ST_FINISH状态电机PWM直接清零同时舵机回中位。这里要加一点防抖冲线触发后延迟200毫秒再切状态防止把赛道上的贴纸或阴影误判成冲线。// 冲线检测 uint8 check_finish_line(void) { uint8 count 0; for (uint8 row 20; row 60; row) { if (!line[row].valid) continue; // 检查中线附近是否有黑线像素 uint8 c line[row].center; uint8 black_count 0; for (uint8 col c - 5; col c 5; col) { if (binary_image[row * COL_IMAGE col] 0) { black_count; } } if (black_count 5) { count; if (count 5) return 1; } else { count 0; } } return 0; }5. 调参与优化——从“能跑完”到“跑得快”的必经之路5.1 图像参数与控制参数分开调很多队伍调车时习惯把所有参数混在一起调车跑不稳就随便拧几个系数最后越调越乱。我的建议是严格按照“图像优先、速度其次、转向最后”的顺序分阶段调参。第一个阶段只调图像在上位机上实时显示二值化图像和中线把曝光、增益、大津法的ROI范围调好确保不同光线下中线都稳定。车可以放在赛道上静止测试用手推着过各种元素观察图像处理结果是否稳定。第二个阶段调速度固定舵机控制参数只调节速度环的PID在直道上做加减速测试看速度是否平稳、有无震荡。第三个阶段才调转向先给一个较慢的速度比如1.5米/秒把舵机PD参数和前馈系数调好再逐步提速。每调一个阶段都单独记录参数和时间方便回溯。我们的调参记录表长这样日期速度档曝光增益KP_SERVOKD_SERVOFEEDFORWARD_K效果Day11.5m/s1501035815弯道甩尾Day21.5m/s1501030520稳定Day32.0m/s160830520入弯稳定出弯抖这样你就能清楚地看到参数变化带来的影响而不是凭感觉乱试。5.2 现场光线的普适性问题大津法虽然能自适应亮度变化但还是有一个问题如果赛道和背景的灰度分布过于接近比如场地灯光特别强地毡颜色被照得发白大津法就会把一部分赛道丢到背景里。针对这种情况我们准备了两个拉普拉斯边缘检测的备用线索——不依赖具体灰度值而是依赖“赛道边缘是锐利的边界”这一事实。在图像灰度对比度很差、二值化失效的情况下用边缘信息兜底提取赛道边界。这部分代码没有放上车的完整版但思路值得分享// 边缘检测兜底用Sobel算子提取赛道边缘 void sobel_edge_detect(uint8 *gray, uint8 *edge_out) { for (uint8 row 1; row ROW_IMAGE - 1; row) { for (uint8 col 1; col COL_IMAGE - 1; col) { int16 gx -gray[(row-1)*COL_IMAGE col-1] gray[(row-1)*COL_IMAGE col1] - 2*gray[row*COL_IMAGE col-1] 2*gray[row*COL_IMAGE col1] - gray[(row1)*COL_IMAGE col-1] gray[(row1)*COL_IMAGE col1]; int16 gy -gray[(row-1)*COL_IMAGE col-1] - gray[(row-1)*COL_IMAGE col] - gray[(row-1)*COL_IMAGE col1] gray[(row1)*COL_IMAGE col-1] gray[(row1)*COL_IMAGE col] gray[(row1)*COL_IMAGE col1]; int16 mag abs(gx) abs(gy); edge_out[row*COL_IMAGE col] (mag EDGE_THRESHOLD) ? 255 : 0; } } }在光线正常的实验室里你不需要它但在比赛现场多一个备用方案就是多一份心安。5.3 现场调试的实战技巧最后分享几个我们在现场调试总结出来的小技巧都是血泪换来的永远准备一套“保守参数”。比赛前如果时间来不及调优就把之前跑得最稳的一套参数写死在Flash里选择菜单里一键载入。赛场上最怕的不是跑不快而是车跑不完——保守参数至少能保证完赛。在车上加一个“参数热调整”按钮。通过无线串口模块用电脑上的上位机在跑赛道的同时实时调整PID参数。这比改一次代码烧录一次快太多了节省的时间可以多跑好几圈找感觉。保存多份不同速度档的完整配置。比如低速测试档、中速调优档、高速冲击档。每个档位的内容包括曝光、增益、PID、前馈、差速力度、状态机判定阈值。现场根据赛道的摩擦力和光线条件快速切换档位。留意电池电压对速度环的影响。电池从满电8.4V放到7.2V电机同样的PWM占空比转速会差很多。如果速度环不补偿电池电压你上午调好的车下午就变慢了。我们在速度环里加了一个简单的线性电压补偿voltage_compensation (nominal_voltage - actual_voltage) * k把它叠加到目标转速上。6. 代码规范、版本管理与团队协作的隐性成败点6.1 模块化开发让四个人不吵架智能车竞赛一个队通常有三到四人分工一般是机械、电路、算法、调试。如果你把所有代码都写在一个main.c文件里四个人同时改代码一天到晚都在解决冲突。我们比较早地定了模块化约定每个人负责一个模块模块之间只通过头文件里的接口函数通信不直接访问其他模块的全局变量。比如图像模块只输出line数组和binary_image数组状态机模块只输出current_state控制模块只读这些量不反过去修改图像模块的内部状态。这样一来负责图像的人只需要保证接口输出正确负责控制的人只需要根据接口输出做调试两边互不干扰。到了后期再联调时问题定位也很快。6.2 版本管理Git是救命稻草智能车竞赛的代码看起来小但是每天改来改去改到后面你会忘记今天到底动了哪些地方。我们用Git做版本管理每天跑完赛道之后提交一次提交信息写上“改了环岛出口判定阈值测试效果XXX”。如果第二天发现参数改坏了直接git checkout回滚比手动改回去快得多。另外烧录之前一定要确认当前工作目录的代码和编译出来的固件完全对应。我们有过一次惨痛的经历上午跑了3.1秒的好成绩下午发现固件和源码不对应——因为有人改了源码忘了重新编译而另一个人也用同一台电脑烧了旧固件导致半天都在排查一个不存在的问题。6.3 提高代码健壮性的几个小习惯所有除法先判断分母不为零。所有数组访问前检查索引范围。对编译器的栈空间做一次评估图像缓冲区如果放在局部变量里很容易爆栈。我们把大数组全部定义成全局或静态变量。加看门狗。跑车过程中如果程序跑飞看门狗自动重启至少车子不会原地失控冲出去能停在赛道边上。这些习惯看起来都是小事但在比赛现场高压环境下任何一个小问题都可能让你失去整轮调试机会。从第十八届三轮摄像头组备赛到国赛我最大的感受是**智能车竞赛拼到最后拼的不是某一个神奇算法而是一整套能稳定运行的系统能力。**图像处理占30%控制策略占30%剩下的40%都是代码架构、调试方法、团队协作和对细节的把控。三轮车本身结构和控制上不如四轮那么“宽容”但只要你在图像稳定性和状态机可靠性上下足功夫它跑出来的成绩完全可以惊艳全场。希望这篇复盘能给你一些启发少踩几个我们踩过的坑让你的車也能稳稳地冲过终点线。本文还有配套的精品资源点击获取
返回列表