在准备电赛这类综合性极强的电子设计竞赛时,很多同学都会卡在“循迹”这个看似基础实则考验综合能力的环节上。尤其是在“车载平衡滚球运动控制系统”这类复杂题目中,如何让小车稳定、精准地沿预定轨迹跑完一圈,往往成为决定成败的关键。网上资料虽多,但要么过于零散,要么只讲传感器原理,缺乏从硬件选型、算法调试到系统联调的完整闭环方案。
本文将围绕“26电赛H题车载平衡滚球运动控制系统 任务二循迹一圈”这一核心需求,为你拆解一套从零到一的完整实战方案。无论你是初次接触电赛的新手,还是希望优化现有方案的同学,都能从中获得可直接复用的代码、清晰的调试思路以及避坑指南。我们将重点讲解五路循迹传感器的应用、PID控制算法的实现、以及如何与滚球平衡系统协同工作,最终实现稳定的一圈循迹。
1. 系统概述与核心需求分析
在开始动手之前,我们必须清晰地理解题目要求和系统构成。这有助于我们选择正确的技术路径,避免在错误的方向上浪费宝贵时间。
1.1 题目任务解读:车载平衡滚球运动控制系统
“车载平衡滚球运动控制系统”是一个典型的机电一体化综合设计题目。它通常包含两个核心子系统:
- 平衡系统:控制一个平台(或“车”),使其上的滚球能够保持平衡或完成特定轨迹运动。这涉及到姿态感知(如陀螺仪、加速度计)和电机驱动控制。
- 循迹系统:控制承载平衡系统的移动底盘,使其能够沿着地面铺设的黑色轨迹线自动行驶。
任务二“循迹一圈”,明确要求移动底盘(即“车”)部分具备自主循迹能力。这意味着我们需要在平衡系统可能持续工作的背景下,叠加一个精确的路径跟踪控制层。两个子系统之间存在耦合:循迹时的启停、转弯会影响车的姿态,进而干扰滚球的平衡。因此,系统设计必须考虑两者的协同与解耦。
1.2 循迹任务的核心技术指标
一个成功的循迹一圈任务,不能仅仅满足于“能跑完”,而应追求稳定、快速、精准。我们可以将其分解为以下几个可量化的技术指标:
- 稳定性:小车在直道和弯道上行驶平稳,无明显左右摇摆或冲出轨迹线的“画龙”现象。
- 循迹精度:能够始终将轨迹线保持在车体中心附近,对十字路口、锐角弯等复杂路径有良好的处理能力。
- 速度适应性:在保证稳定性的前提下,尽可能提高行驶速度。通常需要实现速度的闭环控制。
- 系统兼容性:循迹控制算法不能与上层的滚球平衡控制算法产生严重冲突,需共享MCU资源且互不干扰。
- 抗干扰能力:能适应环境光线变化、地面轻微不平整等实际情况。
1.3 总体方案设计思路
基于以上分析,一个典型的系统方案框图如下:
[传感器层] --> [信号处理层] --> [控制决策层] --> [执行层] | | | | 五路循迹模块 ADC读取/滤波 PID控制器 电机驱动 编码器(可选) 速度计算 路径决策 PWM输出 | | |---[上层系统:滚球平衡控制]---|工作流程:
- 感知:五路循迹传感器阵列实时检测地面黑线位置,编码器(可选)测量实际车速。
- 处理:MCU读取传感器模拟/数字值,经过滤波处理后,计算出车体偏离轨迹中心的“误差”。
- 决策:将“误差”输入PID控制器,计算出用于纠正方向的转向控制量。同时,根据路径情况(如直道、弯道)和编码器反馈,综合决策目标速度。
- 执行:将计算出的转向控制量和速度控制量,转换为左右电机的PWM占空比,通过电机驱动模块驱动小车。
- 协同:整个循迹控制循环在一个固定的定时器中断中运行,与滚球平衡控制的周期错开或分优先级处理,确保系统实时性。
2. 硬件选型与电路设计
硬件是算法落地的基础。正确的选型和可靠的电路设计,能极大降低后期调试的难度。
2.1 核心传感器:五路循迹模块详解
五路循迹模块是本次任务的核心传感器。与三路、七路相比,五路在成本、精度和复杂度之间取得了良好平衡。
- 工作原理:模块上并列安装5对红外发射管和接收管。红外光照射到地面,黑色轨迹线吸收大部分红外光,反射弱;白色地面反射强。接收管根据接收到的反射光强度输出不同的模拟电压(或经比较器后输出数字高低电平)。
- 优点分析:
- 检测范围宽:5个探头横向分布,能覆盖更宽的路径,提前感知弯道。
- 误差分辨率高:可以计算出更精细的横向位置偏差,而不仅仅是“左偏”或“右偏”。例如,通过加权算法,可以将5个传感器的状态映射为一个连续的偏差值。
- 应对复杂路径:对十字交叉线、断续线有更好的识别能力。
- 模块接口:通常输出5路模拟信号(AO1~AO5)和5路数字信号(DO1~DO5)。我们强烈推荐使用模拟信号,因为它能提供灰度信息,使控制更加平滑。数字信号只有0/1,在临界点时容易抖动。
- 安装要点:
- 传感器离地高度通常在1-3cm,需根据实际模块调整。
- 探头间距应略小于轨迹线宽度,以确保任何时候至少有1-2个探头能检测到黑线。
- 安装平面要平整,且与地面平行。
2.2 主控单元与电源模块
- 主控MCU:STM32F4系列(如F407、F429)或STM32G4系列是电赛热门选择。它们拥有充足的定时器(用于PWM生成和编码器接口)、ADC通道(用于读取模拟传感器)和运算能力(运行浮点PID)。对于更复杂的双系统协同,其性能绰绰有余。
- 电机驱动模块:推荐使用集成芯片方案,如TB6612FNG或DRV8833。它们支持双路直流电机控制,内置防共态导通逻辑,驱动能力强,体积小。绝对避免使用L298N,其发热大、效率低。
- 电源模块:这是系统稳定的基石!必须重视。
- 需求分析:MCU(3.3V/5V)、传感器(5V)、电机驱动(需根据电机额定电压,常用7.4V或12V)。电机启动瞬间电流很大。
- 设计方案:
- 使用一块大容量(如3000mAh以上)的2S或3S锂电池作为总电源。
- 采用DC-DC降压模块(如LM2596、MP1584)从电池电压降压得到电机驱动所需电压(如12V)和5V总线电压。
- 从5V总线再通过LDO(如AMS1117-3.3)得到纯净的3.3V给MCU和数字电路。
- 关键点:电机电源与MCU/传感器电源必须隔离或做充分滤波(大电容+磁珠),防止电机噪声干扰控制系统导致程序跑飞或传感器读数异常。
2.3 辅助传感器与执行器
- 编码器:虽非强制,但强烈建议为驱动电机加装增量式编码器。它可以提供精确的车速和行走距离反馈,是实现速度闭环、精确位置控制(如停车)的关键。
- 车体结构:建议使用差速转向的小车底盘。结构简单,控制模型清晰。轮胎选择抓地力好的,避免打滑。
3. 软件架构与核心算法实现
软件是系统的灵魂。一个清晰的架构和稳健的算法,是实现高性能循迹的保证。
3.1 软件整体架构设计
建议采用“前后台”或“多任务”架构,确保实时性。
- 高速中断服务程序:
- 定时器中断:用于执行核心控制循环。例如,设置一个1ms或2ms的定时器中断,在其中依次执行:读取传感器、计算偏差、运行PID、更新PWM。这个中断的优先级应设为最高。
- 编码器接口中断:用于捕获脉冲,计算速度。
- 主循环:
- 执行非实时性任务:如读取按键、更新OLED显示、通过串口发送调试数据、处理上位机指令等。
- 管理状态机:控制小车的“启动”、“循迹”、“停车”等状态切换。
3.2 循迹偏差计算算法
如何将5个传感器的读数转化为一个代表横向偏差的数值,是算法的第一步。这里介绍两种常用方法:
方法一:加权平均法(适用于模拟传感器)假设5个传感器从左到右编号为S0, S1, S2, S3, S4,安装在车头中心对称位置。S2是中间传感器。
- 读取每个传感器的模拟值
ADC_Value[i]。 - 由于黑线上读数小,白地上读数大,我们将其反相或归一化处理,得到一个“黑线强度”
Line_Strength[i] = MAX_ADC - ADC_Value[i]。 - 为每个传感器分配一个位置权重,例如:
Weight[i] = {-2, -1, 0, 1, 2},对应其相对于车中心的位置。 - 计算加权偏差
Error:
这样得到的// 示例代码片段 float sum_strength = 0; float sum_weighted = 0; for (int i = 0; i < 5; i++) { line_strength[i] = ADC_MAX - adc_value[i]; // 假设ADC_MAX为白地值 sum_strength += line_strength[i]; sum_weighted += line_strength[i] * position_weight[i]; // position_weight = {-2, -1, 0, 1, 2} } if (sum_strength > THRESHOLD) { // 防止除零,且确保检测到线 current_error = sum_weighted / sum_strength; // 归一化的偏差,范围大约在[-2, 2] } else { // 丢失黑线处理策略,例如保持上一次偏差或执行搜索 }current_error是一个浮点数。0表示黑线在正中心,负值表示偏左,正值表示偏右。数值大小代表偏离程度。
方法二:状态机法(适用于数字传感器)将5位数字信号(0/1)映射为有限的状态,如“偏左大”、“偏左小”、“居中”、“偏右小”、“偏右大”、“十字路口”、“丢失”等。根据当前状态和上一状态来决定转向策略。这种方法更依赖于逻辑判断。
3.3 PID控制算法实现与调参
PID是循迹控制的核心。我们将使用位置式PID,并分别对转向和速度进行控制。
1. 转向PID控制控制目标是使偏差Error趋于0。
// pid.h typedef struct { float Kp, Ki, Kd; // PID参数 float integral; // 积分项 float prev_error; // 上一次误差 float output_max; // 输出限幅 float output_min; } PID_Controller; void PID_Init(PID_Controller *pid, float kp, float ki, float kd, float out_max, float out_min); float PID_Calculate(PID_Controller *pid, float error);// pid.c #include "pid.h" void PID_Init(PID_Controller *pid, float kp, float ki, float kd, float out_max, float out_min) { pid->Kp = kp; pid->Ki = ki; pid->Kd = kd; pid->integral = 0; pid->prev_error = 0; pid->output_max = out_max; pid->output_min = out_min; } float PID_Calculate(PID_Controller *pid, float error) { float proportional = pid->Kp * error; pid->integral += error; // 积分抗饱和 float integral_term = pid->Ki * pid->integral; if (integral_term > pid->output_max) { integral_term = pid->output_max; pid->integral = pid->output_max / pid->Ki; // 反向计算饱和时的积分值 } else if (integral_term < pid->output_min) { integral_term = pid->output_min; pid->integral = pid->output_min / pid->Ki; } float derivative = pid->Kd * (error - pid->prev_error); pid->prev_error = error; float output = proportional + integral_term + derivative; // 输出限幅 if (output > pid->output_max) output = pid->output_max; if (output < pid->output_min) output = pid->output_min; return output; }2. 速度PID控制(需编码器)控制目标是使实际车速逼近目标车速。
- 速度测量:在定时中断中,读取编码器累计的脉冲数,计算间隔时间内的速度。
- 目标速度设定:可以是一个固定值,也可以根据路径动态调整(如直道加速、弯道减速)。
- 实现一个独立的PID控制器,其输出作为电机的基础PWM值。
3. 最终PWM合成转向PID的输出steer_output和速度PID的输出speed_base共同决定左右轮PWM。
// 假设 speed_base 是正数,steer_output 正代表需要右转(左轮加速,右轮减速) float pwm_left = speed_base - steer_output; float pwm_right = speed_base + steer_output; // 对 pwm_left, pwm_right 进行限幅(0~PWM_MAX) set_motor_pwm(MOTOR_LEFT, pwm_left); set_motor_pwm(MOTOR_RIGHT, pwm_right);4. PID调参“三步法”
- 调Kp(比例):将Ki和Kd设为0。逐渐增大Kp,直到小车在直道上出现小幅、高频的振荡。此时Kp约为临界值的60%-70%。
- 调Kd(微分):加入Kd可以抑制振荡,提高稳定性。逐渐增大Kd,直到高频振荡消失,过弯时响应迅速且平稳。Kd过大会引入高频噪声,可能需要传感器滤波。
- 调Ki(积分):用于消除静态误差(如长期偏向一侧)。Ki值通常很小。慢慢增加Ki,观察小车能否在长直道最终精确居中。Ki太大会导致系统响应迟钝和超调。
3.4 复杂路径处理策略
- 十字路口识别:当中间三个传感器(S1, S2, S3)同时检测到黑线,且持续一段时间,可判定为十字路口。策略可以是:保持原方向直行一段固定时间或距离,然后恢复循迹。
- 锐角弯/急弯:当偏差
Error突然变得很大,且持续增大,可能进入急弯。此时可以临时增大PID参数(尤其是Kp)或直接给一个固定的较大转向PWM。 - 丢线处理:当所有传感器都检测不到黑线(
sum_strength过小),启动丢线处理。例如,根据最后已知的偏差方向,让小车向那个方向原地缓慢旋转,直到重新检测到黑线。
4. 完整实战:从零搭建循迹系统
本章节将提供一个基于STM32 HAL库的简化版完整代码框架,帮助你快速搭建项目。
4.1 工程创建与基础配置
- 使用STM32CubeMX创建工程,选择你的MCU型号。
- 时钟配置:配置到最高主频。
- 引脚配置:
- ADC:启用5路ADC通道(IN0~IN4),对应连接5个循迹传感器模拟输出。配置为扫描模式、连续转换。
- 定时器:
- 启用一个高级定时器(如TIM1)的两个通道,生成PWM输出给电机驱动。
- 启用一个通用定时器(如TIM2)用于编码器接口模式,连接左右编码器。
- 启用一个基本定时器(如TIM6)用于产生1ms的中断,作为控制周期。
- UART:启用一个串口,用于调试信息输出。
- 生成代码,使用IDE(Keil或IAR)打开工程。
4.2 核心模块驱动代码
1. ADC读取与滤波 (adc.c)
// adc.h #define TRACE_SENSOR_NUM 5 extern volatile uint16_t g_adc_raw[TRACE_SENSOR_NUM]; extern float g_line_strength[TRACE_SENSOR_NUM]; void TraceSensor_Init(void); void TraceSensor_UpdateValues(void); // 在定时中断中调用 float TraceSensor_GetError(void);// adc.c #include "adc.h" #include "main.h" // 包含HAL库和ADC句柄定义 extern ADC_HandleTypeDef hadc1; // 假设你的ADC句柄 volatile uint16_t g_adc_raw[TRACE_SENSOR_NUM] = {0}; float g_line_strength[TRACE_SENSOR_NUM] = {0}; const float POSITION_WEIGHT[TRACE_SENSOR_NUM] = {-2.0f, -1.0f, 0.0f, 1.0f, 2.0f}; #define WHITE_BASE 3500 // 白地ADC基准值,需校准 #define BLACK_BASE 500 // 黑线ADC基准值,需校准 void TraceSensor_Init(void) { HAL_ADC_Start_DMA(&hadc1, (uint32_t*)g_adc_raw, TRACE_SENSOR_NUM); } void TraceSensor_UpdateValues(void) { // DMA自动更新g_adc_raw,这里进行滤波和计算 static float filtered_adc[TRACE_SENSOR_NUM] = {0}; const float alpha = 0.3f; // 一阶低通滤波系数 for(int i=0; i<TRACE_SENSOR_NUM; i++) { // 1. 低通滤波 filtered_adc[i] = alpha * g_adc_raw[i] + (1-alpha) * filtered_adc[i]; // 2. 计算黑线强度 (0~1.0) float strength = (WHITE_BASE - filtered_adc[i]) / (WHITE_BASE - BLACK_BASE); if(strength < 0) strength = 0; if(strength > 1.0f) strength = 1.0f; g_line_strength[i] = strength; } } float TraceSensor_GetError(void) { float sum_weighted = 0, sum_strength = 0; for(int i=0; i<TRACE_SENSOR_NUM; i++) { sum_weighted += g_line_strength[i] * POSITION_WEIGHT[i]; sum_strength += g_line_strength[i]; } if(sum_strength > 0.5f) { // 检测到有效黑线 return sum_weighted / sum_strength; } else { // 丢线处理:返回一个极大值或特殊值,由上层处理 return 999.0f; } }2. 电机控制与PID (motor.c/control.c)
// control.h void Control_Init(void); void Control_Loop(void); // 在1ms定时中断中调用// control.c #include "control.h" #include "pid.h" #include "adc.h" #include "motor.h" // 假设有设置PWM的函数 PID_Controller steer_pid; PID_Controller speed_pid; float target_speed = 30.0f; // 目标速度 cm/s float current_speed = 0.0f; // 由编码器计算得到 void Control_Init(void) { // 初始化转向PID,参数需调试 PID_Init(&steer_pid, 10.0f, 0.05f, 2.0f, 30.0f, -30.0f); // 初始化速度PID PID_Init(&speed_pid, 1.0f, 0.01f, 0.05f, 10.0f, -10.0f); Motor_Init(); // 初始化电机PWM等 } void Control_Loop(void) { // 1. 更新传感器值 TraceSensor_UpdateValues(); // 2. 获取循迹偏差 float trace_error = TraceSensor_GetError(); // 3. 丢线判断与处理 if(trace_error > 100.0f) { // 假设999为丢线 // 执行停车或搜索策略 Motor_SetPWM(0, 0); return; } // 4. 计算转向控制量 float steer_out = PID_Calculate(&steer_pid, trace_error); // 5. 更新当前速度 (此处需实现编码器速度计算函数) // current_speed = Encoder_GetSpeed(); // 假设我们暂时使用开环速度 static float speed_base = 20.0f; // 基础PWM值 // 6. 速度闭环 (如果启用编码器) // float speed_error = target_speed - current_speed; // float speed_adjust = PID_Calculate(&speed_pid, speed_error); // speed_base += speed_adjust; // 7. 合成最终PWM float pwm_left = speed_base - steer_out; float pwm_right = speed_base + steer_out; // 8. PWM限幅 (假设PWM范围0~100) if(pwm_left > 100) pwm_left = 100; if(pwm_left < 0) pwm_left = 0; if(pwm_right > 100) pwm_right = 100; if(pwm_right < 0) pwm_right = 0; // 9. 输出到电机 Motor_SetPWM(pwm_left, pwm_right); }3. 定时器中断服务函数
// 在stm32f4xx_it.c中找到对应的中断函数 void TIM6_DAC_IRQHandler(void) { if(__HAL_TIM_GET_FLAG(&htim6, TIM_FLAG_UPDATE) != RESET) { __HAL_TIM_CLEAR_IT(&htim6, TIM_IT_UPDATE); Control_Loop(); // 核心控制循环 } }4.3 主函数与调试信息输出
// main.c #include "main.h" #include "adc.h" #include "control.h" #include <stdio.h> UART_HandleTypeDef huart2; // 假设调试串口 int __io_putchar(int ch) { HAL_UART_Transmit(&huart2, (uint8_t*)&ch, 1, 1000); return ch; } int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_ADC1_Init(); MX_TIM1_Init(); // PWM定时器 MX_TIM2_Init(); // 编码器定时器 MX_TIM6_Init(); // 控制周期定时器 MX_USART2_UART_Init(); TraceSensor_Init(); Control_Init(); // 启动控制周期定时器中断 HAL_TIM_Base_Start_IT(&htim6); // 启动PWM输出 HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1); // 左电机 HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2); // 右电机 printf("System Start!\r\n"); while (1) { // 主循环处理非实时任务 // 例如:每100ms通过串口发送一次传感器数据和PID输出,用于上位机绘图调试 static uint32_t last_tick = 0; if(HAL_GetTick() - last_tick > 100) { last_tick = HAL_GetTick(); float err = TraceSensor_GetError(); printf("Err:%.2f\r\n", err); // 发送给上位机(如SerialPlot) } HAL_Delay(10); } }5. 系统调试与性能优化
代码写完后,调试才是真正的开始。遵循科学的调试步骤,可以事半功倍。
5.1 分模块调试流程
传感器调试:
- 将小车抬起,用串口打印出5路传感器的原始ADC值。
- 分别将传感器对准白纸和黑线,记录数值。确定
WHITE_BASE和BLACK_BASE。 - 移动黑线,观察计算出的
Error值变化是否连续、平滑。这是后续控制的基础。
电机与开环调试:
- 注释掉PID控制,手动给左右电机设置固定的PWM值,测试小车能否前进、后退、左转、右转。
- 确保电机接线正确,左右轮转向逻辑正确。
转向PID调试(开环速度):
- 将速度设定为固定值,只启用转向PID。
- 先将
Ki,Kd设为0,只调Kp。将小车放在直道上,观察其是否振荡。 - 逐步加入
Kd抑制振荡,最后加入微小的Ki消除稳态误差。
速度闭环调试(需编码器):
- 固定转向(如让小车空转或走直线),调试速度PID,使其能快速、稳定地达到目标速度。
系统联调:
- 将转向环和速度环结合,在简单圆形或8字形轨迹上测试。
- 逐步提高目标速度,观察稳定性。
5.2 上位机调试工具的使用
串口打印数字不直观,强烈建议使用上位机软件进行图形化调试。
- 推荐工具:SerialPlot、VOFA+、匿名上位机等。
- 方法:在控制循环中,通过串口发送关键数据,如
Error、steer_out、pwm_left、pwm_right。 - 好处:可以实时绘制曲线,清晰看到偏差变化、PID输出、系统响应速度,极大提高调参效率。
5.3 常见问题与解决方案
| 问题现象 | 可能原因 | 排查与解决思路 |
|---|---|---|
| 小车剧烈振荡,画龙 | 1. Kp过大 2. 传感器安装过高或过低 3. 控制周期太慢 | 1. 减小Kp 2. 调整传感器高度,确保信号跳变清晰 3. 缩短定时中断周期(如1ms) |
| 过弯时冲出轨 | 1. 弯道速度过快 2. Kd太小或Ki太大 3. 传感器检测不到弯道内侧线 | 1. 弯道减速(根据Error大小动态调整目标速度) 2. 调整PID参数,增加微分作用 3. 检查传感器间距是否合适,尝试增加探头数量 |
| 响应迟钝,纠偏慢 | 1. Kp太小 2. 电机PWM最大值限制过低 3. 机械结构松动 | 1. 适当增大Kp 2. 提高PWM限幅值 3. 紧固轮子、联轴器等 |
| 十字路口误动作 | 识别逻辑不准确或处理策略不当 | 增加十字路口的判定条件(如中间多路传感器持续检测到线的时间),并优化直行通过策略 |
| 突然丢线后乱跑 | 丢线处理策略缺失或不当 | 实现完善的丢线处理状态机,例如根据历史偏差进行小角度旋转搜索 |
| 代码运行一段时间后卡死 | 1. 中断嵌套或优先级问题 2. 栈溢出 3. 电源干扰 | 1. 检查中断优先级,控制循环中断优先级最高但不宜过长 2. 增大堆栈大小 3. 加强电源滤波,电机电源与数字电源隔离 |
6. 与滚球平衡系统的协同与进阶优化
完成基础的循迹后,需要考虑如何与题目另一核心——滚球平衡系统——协同工作。
6.1 系统资源分配与任务调度
两个控制系统共享一个MCU,必须做好资源规划和任务调度。
- 传感器资源:循迹用GPIO/ADC,平衡用I2C/SPI读取陀螺仪。通常无冲突。
- 执行器资源:循迹控制底盘两个电机,平衡控制平台的两个舵机或电机。需使用不同的定时器/PWM通道。
- 计算资源:两者都需要运行PID。确保CPU负载足够,如果计算复杂,可以考虑使用带FPU的MCU。
- 任务调度策略:
- 优先级:平衡控制的优先级应高于循迹控制。因为一旦球掉落,任务直接失败。可以将平衡控制放在更高优先级的中断中。
- 时间片:为两者分配固定的控制周期。例如,平衡控制每1ms执行一次,循迹控制每2ms执行一次。使用不同定时器触发。
- 数据共享:如果平衡状态(如平台倾角)需要影响循迹决策(如减速),需要通过全局变量(加临界区保护)或消息队列进行通信。
6.2 动态参数调整与自适应控制
为了让小车表现更智能,可以引入动态调整策略:
- 变PID参数:根据偏差大小动态调整PID参数。大偏差时用大Kp快速纠正,小偏差时用小Kp配合Kd保持稳定。
- 弯道减速:根据偏差的绝对值或变化率来动态降低目标速度,
target_speed = base_speed - k * fabs(error)。 - 路径记忆与预测:对于固定赛道,可以记录第一次慢速运行时的传感器数据或电机指令,第二次运行时进行预判,实现更优控制。
6.3 稳定性与鲁棒性提升
- 软件滤波:除了ADC的一阶滤波,可以对计算出的
Error再进行一次滑动平均滤波,平滑突变。 - 死区处理:当偏差
Error的绝对值小于一个很小阈值时,将误差视为0,避免电机高频微振。 - 输出平滑:对PID的输出进行限幅和变化率限制,防止电机指令突变。
- 故障安全:增加看门狗,在程序跑飞时自动复位。关键数据(如PID参数)异常时,使用默认安全值。
循迹一圈是电赛中经典且富有挑战性的任务。成功的关键不在于算法多么高深,而在于对每个模块的深入理解、细致的调试和系统的整合能力。从传感器的校准、PID参数的整定,到与平衡系统的协同,每一步都需要耐心和实践。建议你先在平坦地面上用简单轨迹调试通过,再逐步增加难度和速度。