资讯动态

智能车竞赛“走马观碑”:未加入视觉阶段的调试实战

发布时间:2026/8/29 8:39:19 来源:尧图企业网站定制
第21届智能车竞赛“走马观碑”赛题的热度持续走高很多队伍在寒假就开始焊车、调传感器、录运行视频。这里想分享一段“未加入视觉”状态下的车模运行视频背后常用的一套调试思路。所谓“未加入视觉”就是不依赖摄像头图像识别车的感知来自电磁、红外、编码器等非视觉传感器先让车跑起来再把视觉算法叠加进去。这样的好处是底盘响应、控制参数、传感器标定都可以在更少的变量下验证。本文会从赛题背景、硬件准备、最小可运行框架、感知与控制逻辑、运行视频录制、常见故障排查这几个角度展开尽量让后来者少走弯路。1. “走马观碑”与“未加入视觉”到底意味着什么1.1 赛题名称背后的技术含义“走马观碑”是历年智能车竞赛中比较考验综合能力的题目风格。单从字面上看“走着马看碑”对应车模运行中识别路侧目标物“碑”通常指向赛道上的特殊元素可能是立柱、标牌或带有图形/字符的物体。竞赛规则经常会要求车模在高速巡线过程中对这种特殊元素做出反应例如变道、停车、绕行或只做短暂检测。不同年份、不同组别的规则细节会有差异但工程上解决的问题是一致的车辆需要在一个连续的任务里同时完成稳定循迹和目标事件检测。对于很多队伍而言第一步并不是直接上视觉而是先把“稳定循迹”和“基础事件反应”做扎实。本文说的“未加入视觉”就是指这一阶段。1.2 为什么先不加视觉很多新队员容易犯一个错误一上来就想用摄像头跑完全程最后陷入调曝光、调阈值、调神经网络的时间黑洞。摄像头视觉链路很长包括图像采集、畸变矫正、透视变换、赛道线提取、目标物分类、串口通信或内存共享每一步都可能引入新的问题。如果底盘本身都不稳定摄像头画面里出现的每个抖动都会被放大视觉程序根本分不清是车身姿态导致的图像运动还是赛道真实变化。先把视觉模块屏蔽掉只用电磁或红外传感器进行赛道检测能把问题规模缩小到“车能不能稳定跟着赛道走”。这个阶段车模跑起来后的视频可以直接看出机械结构、舵机响应、电机控制、传感器布局是否有问题。视频里的每次抖动、每次偏移、每次冲出赛道都是后续视觉模块加入前必须解决的隐患。1.3 未加入视觉时感知与控制闭环如何组成未加入视觉不代表没有感知。一场完整的车辆运行闭环至少需要三类信息赛道位置信息通过电磁传感器或红外对管判断车模相对赛道中心线的偏移。车辆运动信息通过编码器读取当前速度通过陀螺仪/加速度计如果加了获取角速度和加速度。状态切换信息通过额外的传感器组合判断是否到达“碑”元素或特殊区域。控制端则把这几类信息汇入一个有限状态机。默认状态是“巡线”当满足一定条件时切换到“检测到碑”或“特殊处理”状态执行动作后再回到巡线状态。视频中看到的流畅运行实际上是感知、决策、执行三个模块以毫秒级周期循环工作的结果。2. 硬件与软件环境准备2.1 车模平台与主控选型第21届竞赛常见的车模平台有 A 型车、B 型车、C 型车等不同车型的机械结构和允许改装程度不同。以常见的 C 型车模为例车身自带两个驱动轮、一个或多个万向轮转向由舵机控制动力由直流电机提供。主控芯片应根据队伍熟悉的平台选择。常见组合如下主控平台特点适合场景STM32F407资料多HAL库和标准库都有ADC/PWM功能齐全新手入门、快速验证英飞凌 TC264竞赛常用性能强但需要熟悉 AURIX 开发环境需要更高实时性和复杂算法的队伍RT1064NXP 系列主频高适合视觉控制同步运行后期要加入视觉且希望单主控完成处理无论选择哪块主控都要先确认引脚数量、ADC 通道、PWM 定时器资源是否够用。很多队伍“未加入视觉”时只用了 4 路电磁、1 路编码器、1 路舵机、1 路电机但后期加入视觉后会需要更多串口或 DMA 资源选型时要预留余量。2.2 传感器布局与连接未加入视觉阶段的核心传感器是电磁或红外。电磁传感器的原理是赛道中心铺设有引导线引导线中通有交变电流会在周围产生磁场。车模前后左右放置多个电感线圈通过感应电压的大小和差异判断车模与引导线的相对位置。常见布局是水平方向 2 到 4 个电感横排布置在车头。竖直方向 1 到 2 个电感用于检测坡道或远距离信号。每个电感经过运放放大、检波后接入主控 ADC。红外传感器则常用在边界检测或特殊元素检测例如使用对射式红外管判断是否经过立柱。红外信号受环境光影响较大使用时要加遮光罩和固定阈值。传感器连接建议传感器类型信号类型主控接口注意点电磁电感模拟电压已检波ADC 通道需要滤波和归一化红外对管模拟电压或数字电平ADC 或 GPIO环境光干扰大编码器正交信号或单边沿脉冲定时器编码器模式/外部中断需要共地2.3 软件环境与基础库开发环境根据主控型号选择STM32Keil MDK 或 STM32CubeIDE。TC264AURIX Development Studio 或 Tasking。RT1064IAR 或 MCUXpresso。除此之外很多队伍使用逐飞科技提供的开源库这些库封装了底层驱动可以直接调用adc_init、pwm_init、encoder_init等函数。使用开源库能大幅缩短开发时间但必须检查库版本和芯片型号是否匹配。建议在项目文件夹下准备一份环境检查清单驱动库能否编译通过示例工程。主控板能否正常下载程序。电池电压是否在 7.2V 至 8.4V 范围内。舵机和电机驱动是否共地。串口能否打印数据。ADC 通道电压读数是否随传感器位置变化。3. 最小可运行框架先让车模动起来很多队伍一上来就写完整控制算法结果车一动就失控。正确做法是先做最小可运行框架验证每一路输出都正确再逐步加入感知和决策。3.1 引脚初始化与基础输出以 STM32 加常见驱动库为例初始化主要包括三部分PWM 输出、ADC 采样、编码器计数。void Hardware_Init(void) { // 舵机控制 PWM频率 50Hz占空比由后续角度值决定 pwm_init(PWM_PIN_SERVO, 50, 7500); // 50Hz, 初始占空比 7.5% // 电机 PWM频率 10kHz初始占空比 0 pwm_init(PWM_PIN_MOTOR, 10000, 0); // ADC 初始化用于读取电磁传感器 adc_init(ADC_GROUP_1, ADC_CH_A0); adc_init(ADC_GROUP_1, ADC_CH_A1); adc_init(ADC_GROUP_1, ADC_CH_A2); adc_init(ADC_GROUP_1, ADC_CH_A3); // 编码器接口初始化 encoder_init(ENCODER_TIMER_1); }这段代码的关键是明确区分舵机和电机的 PWM 频率。舵机通常使用 50Hz 方波对应脉冲宽度 0.5ms 到 2.5ms 之间的角度范围电机驱动则使用更高频率例如 10kHz 到 20kHz频率过低容易产生电流噪声和电机啸叫。3.2 舵机中值与电机方向标定在摄像头和复杂算法之前先做两个最容易出错的手动标定。第一个是舵机中值。舵机中值指的是舵机臂位于正中间时对应的 PWM 占空比。每个舵机安装后齿轮角度和摆臂位置都不一定完全一致必须通过调试确定。// 手动标定舵机中值通过按键或串口修改 Duty直到舵机臂完全垂直 void Servo_Calibrate(uint16_t duty) { pwm_set_duty(PWM_PIN_SERVO, duty); }第二个是电机正反转方向。上电后给定一个恒定 PWM观察电机转向是否和预期一致。如果方向反了可以通过修改 PWM 引脚组合或换向函数解决。不要靠改硬件线来适应软件秩序要清晰。3.3 读取传感器与编码器并串口输出为了让运行视频有“可复盘”数据最少要能实时输出传感器值和速度值。typedef struct { uint16_t adc[4]; int16_t speed; } CarData; void Data_Update(CarData *data) { >typedef struct { float norm[4]; float weight[4]; } SensorNormalize; float Sensor_CalculatePosition(uint16_t *raw) { SensorNormalize sn; float sum 0.0f; float weighted 0.0f; // 权重设定左边为负右边为正 float weight[4] {-2.0f, -1.0f, 1.0f, 2.0f}; for (int i 0; i 4; i) { sn.norm[i] (float)raw[i] / 4095.0f; // 假设12位ADC sn.weight[i] weight[i]; sum sn.norm[i]; weighted sn.norm[i] * sn.weight[i]; } if (sum 0.001f) { return 0.0f; // 无信号时回中 } return weighted / sum; // 返回 -2 到 2 之间的偏差 }这个偏差值position是后续方向控制的输入。position 0表示车模中心偏左需要向右修正position 0表示偏右需要向左修正。4.2 状态机从巡线到“碑”检测未加入视觉不代表无法处理“碑”元素。常见的非视觉检测方式有两种使用红外对管检测物体遮挡。当车模经过“碑”附近时红外线被遮挡GPIO 电平变化。使用电磁信号突变。若“碑”附近没有引导线或引导线形状变化电磁传感器会检测到明显的信号波动。状态机可以这样设计typedef enum { STATE_TRACK 0, // 巡线状态 STATE_OBSTACLE, // 检测到碑/障碍物 STATE_RECOVER // 恢复状态 } CarState; CarState car_state STATE_TRACK; void State_Machine_Update(float position, uint8_t obstacle_flag) { switch (car_state) { case STATE_TRACK: if (obstacle_flag) { car_state STATE_OBSTACLE; } else { Servo_Control(position); } break; case STATE_OBSTACLE: // 执行减速或避让动作 Motor_SetSpeed(20); if (!obstacle_flag position 0.3f) { car_state STATE_RECOVER; } break; case STATE_RECOVER: // 回到巡线状态 Servo_Control(position); Motor_SetSpeed(60); car_state STATE_TRACK; break; default: car_state STATE_TRACK; break; } }这里要注意状态切换条件是整车稳定性的核心。不要只依赖单一阈值要加入连续多帧判断或持续计数避免传感器抖动导致误触发。4.3 方向控制从“差比和”到 PID未加入视觉阶段的经典方向控制是“差比和”int16_t Servo_Control(float position) { int16_t servo_pwm SERVO_MID; // 中值 float kp 20.0f; // 比例系数 int16_t delta (int16_t)(position * kp); servo_pwm delta; // 限幅 if (servo_pwm SERVO_MAX) servo_pwm SERVO_MAX; if (servo_pwm SERVO_MIN) servo_pwm SERVO_MIN; pwm_set_duty(PWM_PIN_SERVO, servo_pwm); return servo_pwm; }比例控制实现简单但弯道中往往存在稳态误差表现为过弯时总是偏内或偏外。更平滑的方式是加入微分项形成 PD 控制float error_prev 0.0f; int16_t Servo_Control_PD(float position) { float kp 18.0f; float kd 6.0f; float error position; float derivative error - error_prev; int16_t servo_pwm SERVO_MID (int16_t)(error * kp derivative * kd); error_prev error; if (servo_pwm SERVO_MAX) servo_pwm SERVO_MAX; if (servo_pwm SERVO_MIN) servo_pwm SERVO_MIN; pwm_set_duty(PWM_PIN_SERVO, servo_pwm); return servo_pwm; }微分项能抑制车头来回摆动但也会放大传感器噪声。建议在计算偏差前先对 ADC 做一次均值滤波或低通滤波否则差分结果会非常尖锐。4.4 速度控制基于编码器的闭环速度控制同样需要闭环。开环控制时电池电压下降、电机温度升高都会让速度不满足预期。使用编码器读取实际转速再通过 PID 调节 PWM 占空比是稳定运行的基础。typedef struct { float kp; float ki; float kd; float integral; float last_error; } PidObject; int16_t Speed_Control_Inner(int16_t target_speed, int16_t current_speed) { static PidObject speed_pid {1.2f, 0.05f, 0.0f, 0.0f, 0.0f}; int16_t error target_speed - current_speed; speed_pid.integral error; // 积分限幅 if (speed_pid.integral 100.0f) speed_pid.integral 100.0f; if (speed_pid.integral -100.0f) speed_pid.integral -100.0f; float output error * speed_pid.kp speed_pid.integral * speed_pid.ki (error - speed_pid.last_error) * speed_pid.kd; speed_pid.last_error error; if (output 100.0f) output 100.0f; if (output -100.0f) output -100.0f; // 将百分比输出映射到 PWM 占空比 int16_t pwm (int16_t)(output * MOTOR_PWM_FULL / 100.0f); pwm_set_duty(PWM_PIN_MOTOR, pwm); return pwm; }速度闭环的周期建议控制在 5ms 到 20ms 之间不要和方向控制使用完全相同的周期。方向控制通常更快例如 5ms速度环可以慢一些例如 10ms 或 20ms。5. 运行视频录制与数据验证5.1 为什么录运行视频比现场看一眼更重要很多队伍调车时只看车跑了没有没跑就停下来改代码跑了就收工。这样无法定位问题。运行视频可以反复回放而且可以在视频里叠加调试信息把传感器状态和实际车身动作对齐。录制“未加入视觉”阶段的车模运行视频目标不是展示“跑得多快”而是记录以下内容车模是否稳定地贴着赛道中线行驶。过弯时是否出现明显甩尾或切弯。“碑”元素附近传感器是否产生预期的状态变化。速度是否在直道和弯道有明显波动。5.2 推荐录制视角与信息叠加推荐两类视角视角拍摄方式用途全景视角手机固定在三脚架或场地外看车模整体轨迹和姿态车载视角摄像头装在车模顶部或车头看传感器和路面接触状态为了让视频更有分析价值可以把串口发送的数据通过 OLED、无线数传或上位机叠加显示。如果使用无线串口可以边录视频边记录电脑上的数据流后期用时间轴对齐。5.3 视频中该观察的信号特征不要只盯着车看要同时关注以下特征车身是否周期性左右摆动。如果有方向控制微分过大或传感器滤波不足。直道是否速度均匀。如果不均匀速度 PID 参数可能需要重新调整。到“碑”前是否有明显减速或错误动作。如果没有反应需要检查检测信号和状态机触发条件。出弯后是否恢复到中线。如果恢复慢的增益偏低。可以建立一个表格把每一圈视频编号记录问题现象和对应参数方便后续改动对比。5.4 串口日志与上位机回放运行视频只能给出“现象”串口日志才能给出“数据”。推荐在代码中增加数据记录功能void Log_Record(CarData *data, float pos, int16_t servo, int16_t speed_target, int16_t speed_real) { // 将数据格式化后发送到无线串口 printf(POS:%.2f SERVO:%d TGT:%d REAL:%d\r\n, pos, servo, speed_target, speed_real); }在电脑端使用串口助手将数据保存为文件然后用 Python 或 Excel 画曲线可以清楚看到控制量是否平滑、执行机构是否饱和。6. 未加入视觉阶段的高频问题排查6.1 车模总是跑偏或画龙现象车模在直道上反复左右修正幅度越来越大视频里看像“画龙”。可能原因传感器左右不对称。舵机中值标定不准。方向控制比例增益过大。传感器数据没有滤波。检查方式静止状态下读取 4 路 ADC 值确认居中时左右对称。打印舵机 PWM 中值确认车模直线行驶时舵机臂方向。减小kp再测试。对position做低通滤波。推荐做法float pos_filter 0.0f; float pos_target 0.0f; void Update_Position_Filter(float new_pos) { pos_target new_pos; pos_filter pos_filter * 0.7f pos_target * 0.3f; }6.2 舵机不响应或 PWM 输出异常现象舵机不动或剧烈抖动。可能原因舵机 PWM 频率错误应使用 50Hz。PWM 占空比范围超出舵机有效范围。舵机供电不足或主控与舵机未共地。控制周期异常导致舵机频繁收指令。检查方式用示波器或逻辑分析仪看 PWM 波形。单独写一个固定占空比程序看舵机是否转到特定角度。接上电池后测量舵机电源电压是否稳定。6.3 ADC 值跳变严重现象串口打印的 ADC 值上下跳动车模容易误判。可能原因电感到主控的模拟线太长或没有屏蔽。电机噪声通过电源耦合到 ADC。采样时间没有避开 PWM 开关瞬间。没有做软件滤波。处理建议模拟线尽量短使用双绞线。ADC 采样后做多次采样取平均。在电源端并联去耦电容例如 100nF 陶瓷电容和 220uF 电解电容。6.4 车模到达“碑”区域时反应不符合预期现象车模检测不到“碑”或者远远就误触发。可能原因红外传感器的阈值设置不合理。传感器安装高度、角度不合理。状态机切换条件漏掉了持续帧判断。电磁传感器在“碑”附近也发生了突变没有和红外信号做联合判断。处理建议用上位机标定传感器在“无碑”和“有碑”时的信号范围设置中间阈值。状态机中加入连续 3 到 5 帧满足条件才切换。记录视频时把触发标志位置为 1 或 0方便对齐时间轴。6.5 视频里看到电机转速忽快忽慢现象车模运行中速度有明显顿挫感。可能原因速度 PID 积分参数过大引起振荡。编码器安装松动信号忽高忽低。电池电压波动导致开环 PWM 下转速不一致。电机换向时死区补偿不完善。检查方式打印实际速度曲线。检查编码器固定螺丝。在电池充满和快没电时分别测试相同速度指令。7. 未加入视觉阶段的最佳实践与扩展方向7.1 开发顺序与调试清单建议按以下顺序推进舵机中值标定。电机方向与速度开环验证。串口输出功能。ADC 传感器信号读取与滤波。方向闭环。速度闭环。状态机与特殊元素处理。录制视频并回放。每次改动只动一个变量。不要同时调比例系数和微分系数否则出了问题无法定位。7.2 运行视频录制前检查表录制前建议检查一下电池电压是否足够跑完整圈。赛道元素是否摆放完整。摄像头/手机电量是否充足。无线串口是否连接稳定。程序编译时间是否已经注入了当天日期或版本号。是否有备用轮胎和螺丝刀。在视频开头用手势或白板写下版本号和日期后期整理时会非常方便。7.3 从“未加视觉”到“加入视觉”的过渡策略当前阶段运行稳定后视觉模块可以按以下方式切入先单独采集图像不参与控制只做记录和显示。再用视觉计算出的偏差替代电磁偏差作为方向输入。最后加入视觉目标检测识别“碑”上的图形或文字。过渡期间仍然保留电磁或红外数据作为视觉异常时的兜底。摄像头图像处理周期通常比传感器读取周期长因此控制周期需要重新规划不能让控制任务等待图像结果。7.4 对新手最有价值的练习建议不要急着追求全组第一的速度。先跑完一圈不冲出赛道再谈加速。每次调参后录一段 10 到 15 秒的稳定运行视频把视频和串口数据存档。这样即使第二天代码改坏了也有一条明确的回退参考。另外建议给主控板增加一个 LED 或 OLED 显示当前状态机状态这样录视频时不需要额外串口线也能看到程序到底认为自己在哪里。“走马观碑”这类赛题的核心不在于某个传感器是否高级而在于感知、决策和执行之间能否稳定配合。未加入视觉阶段正是检验这种配合的最好人手点。把这个阶段的问题解决干净后续加入视觉时才会知道哪些问题是图像算法引起的哪些其实是底盘控制还没有到位。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价