恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
电磁循迹小车PID调节实战:从电感采集到舵机打角闭环
首页
资讯中心
/
电磁循迹小车PID调节实战:从电感采集到舵机打角闭环
电磁循迹小车PID调节实战:从电感采集到舵机打角闭环
发布时间:2026/10/21 12:08:46
简介这份资源面向参加恩智浦智能车竞赛及学习嵌入式控制的学生与工程师聚焦基于STM32的四轮电磁循迹小车PID调节方案帮助解决电感采集、舵机与电机协同控制中的参数整定难题。压缩包共192个文件约5.76MB以C源码与头文件为主体涵盖电感信号采集、MPU6050姿态解算、LCD显示、定时器与Flash驱动等模块并附带Keil工程文件、编译中间产物及可烧录的hex固件便于直接编译验证与二次开发。目前已有4689人学习下载热度较高。资源完整呈现了从传感器数据读取到PID输出控制舵机角度与电机转速的实现链路读者可据此理解比例、积分、微分三项对循迹稳定性的影响并参考工程结构快速搭建调试环境是备赛与课程实践中较实用的参考案例。1. 电磁循迹小车PID调节从电感采集到舵机打角的闭环实战很多人第一次接触电磁循迹都会卡在同一个地方电感值读回来了滤波也做了归一化也算了可小车一上赛道要么画龙要么在直道突然甩头。问题往往不在算法本身而在 PID 参数和采集链路没有对齐。这份资源围绕恩智浦智能车竞赛里最常见的电磁组方案展开核心是 STM32 主控 工字电感采集 舵机转向 双电机差速把「电感采集 → 偏差计算 → PID 输出 → 舵机打角」这条闭环链路完整跑通。适合正在调车、准备校赛、或者想把电磁循迹方案移植到自己板子上的开发者尤其是那些 PID 调了半天还是抖的选手。2. 硬件链路与工程结构先看清信号从哪来、到哪去2.1 电磁循迹的物理链路电磁循迹的本质是赛道中心通电导线产生交变磁场车头布置的工字电感感应出微弱交流信号经过选频、放大、检波后变成直流电压再送进 STM32 的 ADC。常见做法是左右各一个水平电感中间一个竖直电感三个通道一起判断车体相对导线的横向偏移和方向。这里有个容易被忽略的点电感采集的不是「位置」而是「磁场强度」。水平电感离导线越近感应电压越大竖直电感在导线正上方时感应最强偏离后迅速衰减。所以偏差计算不是简单做差而是要先归一化再根据赛道宽度和电感布局做加权。工程里一般会这样组织采集链路// 电感采集与预处理常见于 ADC 中断或定时任务中 #define INDUCTOR_NUM 3 #define FILTER_DEPTH 8 static uint16_t adc_raw[INDUCTOR_NUM]; static uint32_t filter_buf[INDUCTOR_NUM][FILTER_DEPTH]; static uint8_t filter_idx 0; // 滑动平均滤波抑制电磁信号的高频抖动 void inductor_sample(void) { for (int i 0; i INDUCTOR_NUM; i) { adc_raw[i] adc_read_channel(i); // 读取原始 ADC filter_buf[i][filter_idx] adc_raw[i]; } filter_idx (filter_idx 1) % FILTER_DEPTH; } uint16_t inductor_get(uint8_t ch) { uint32_t sum 0; for (int i 0; i FILTER_DEPTH; i) { sum filter_buf[ch][i]; } return (uint16_t)(sum / FILTER_DEPTH); // 返回滤波后的值 }这段代码的关键在于FILTER_DEPTH决定平滑程度太大响应慢太小抖动大。电磁组常用 8 到 16 之间的滑动窗口。adc_read_channel需要根据实际板子的 ADC 通道映射来写注意采样时间要够长否则高阻信号源会采不准。2.2 工程目录与模块划分拿到这份资源后先别急着编译。打开目录看一眼结构能省掉后面很多找文件的麻烦。典型的电磁循迹工程会按下面这种方式组织目录/文件作用Core/Src/main.c主循环、初始化、任务调度Core/Src/adc.cADC 多通道采集配置Core/Src/tim.cPWM 输出、编码器接口、定时中断Hardware/inductor.c电感滤波、归一化、偏差计算Hardware/motor.c电机 PWM 与方向控制Hardware/servo.c舵机打角控制Control/pid.cPID 结构体与计算函数Control/control.c循迹主控制逻辑这种划分的好处是采集、执行、控制三层分开调 PID 的时候只动Control目录不会把底层驱动搅乱。如果你用的是 CubeMX 生成的工程Core目录基本是自动生成的Hardware和Control是自己加的移植时把这两个目录拷过去再改一下引脚定义就能用。2.3 偏差计算归一化比滤波更重要很多人滤波做得很漂亮偏差还是跳原因是没做归一化。电感值会随电池电压、赛道高度、环境温度变化直接拿原始值做差今天调好的参数明天就废了。常见做法是先把三个电感值归一化到 0 到 1000 之间// 归一化把电感值映射到统一量纲消除供电和环境漂移 #define NORM_MAX 1000 static uint16_t norm[INDUCTOR_NUM]; void inductor_normalize(void) { uint16_t max_val 0, min_val 0xFFFF; for (int i 0; i INDUCTOR_NUM; i) { uint16_t v inductor_get(i); if (v max_val) max_val v; if (v min_val) min_val v; } if (max_val - min_val 50) { // 防止除零和全暗场景 for (int i 0; i INDUCTOR_NUM; i) norm[i] 0; return; } for (int i 0; i INDUCTOR_NUM; i) { norm[i] (uint32_t)(inductor_get(i) - min_val) * NORM_MAX / (max_val - min_val); } }归一化之后再用左右水平电感的差值算横向偏差用竖直电感判断是否在十字或环岛区域。偏差公式常见的是// 偏差计算左电感减右电感正负代表偏移方向 float error (float)norm[LEFT] - (float)norm[RIGHT];如果车头电感布局不对称还要加一个加权系数。这个系数不是拍脑袋定的而是把车放在赛道中心读几次左右电感值取比值作为基准。3. PID 调节实战从参数整定到舵机打角3.1 位置式 PID 的结构与实现电磁循迹里最常用的是位置式 PID输出直接对应舵机打角。积分项用来消除静态偏差微分项用来抑制过冲。工程里一般会把 PID 参数做成结构体方便在线调// pid.h typedef struct { float kp; float ki; float kd; float integral; float prev_error; float integral_limit; // 积分限幅防止饱和 float output_limit; // 输出限幅保护舵机 } PID_Handle; float pid_calc(PID_Handle *pid, float error, float dt);// pid.c float pid_calc(PID_Handle *pid, float error, float dt) { float derivative; pid-integral error * dt; if (pid-integral pid-integral_limit) pid-integral pid-integral_limit; else if (pid-integral -pid-integral_limit) pid-integral -pid-integral_limit; derivative (error - pid-prev_error) / dt; pid-prev_error error; float output pid-kp * error pid-ki * pid-integral pid-kd * derivative; if (output pid-output_limit) output pid-output_limit; else if (output -pid-output_limit) output -pid-output_limit; return output; }dt是控制周期电磁组常用 5ms 到 10ms。integral_limit一般取输出限幅的 1/3 到 1/2太大积分会拖慢响应太小消不掉静差。output_limit对应舵机最大打角比如 ±300具体看舵机行程和机械结构。3.2 参数整定的顺序与血泪经验调 PID 最忌讳三个参数一起动。我一般按这个顺序来先只开kpki和kd置零。从小往大加直到小车开始等幅振荡记下这个临界值然后取它的 60% 到 70% 作为初始kp。这一步决定小车能不能大致跟着线走。然后加kd。微分项是抑制振荡的关键电磁组里kd往往比kp还大。加kd的时候观察直道末端和入弯瞬间如果车头开始「提前打角」说明微分起作用了如果出现高频抖动说明kd太大把电感噪声放大了。最后加ki而且要给得很小。电磁循迹对静态偏差不敏感ki主要是消除弯道里的持续偏移。很多翻车案例都是ki给大了直道上积分饱和入弯直接甩出去。提示每次改完参数至少跑三圈再判断。单圈偶然性太大电池电压变化、赛道光线都会影响手感。3.3 舵机与电机的协同舵机负责转向电机负责速度两者必须配合。常见做法是舵机 PID 输出打角电机用编码器做速度闭环直道加速、弯道减速。如果只调舵机不管速度入弯速度太高再好的 PID 也拉不回来。速度环一般用增量式 PI// 增量式 PI用于电机速度闭环 typedef struct { float kp; float ki; float prev_error; float output; } SpeedPI; float speed_pi_calc(SpeedPI *pi, float target, float actual) { float error target - actual; pi-output pi-kp * (error - pi-prev_error) pi-ki * error; pi-prev_error error; if (pi-output 1000) pi-output 1000; if (pi-output -1000) pi-output -1000; return pi-output; }速度目标值可以根据舵机打角动态调整打角越大目标速度越低。这样入弯自动减速出弯自动加速比固定速度稳得多。4. 避坑与排查调车现场最常见的五个问题4.1 电感值跳变车在直道画龙现象是直道上舵机左右小幅摆动车头走 S 形。原因通常是电感采集没有滤波或者滤波窗口太小ADC 噪声直接进了 PID。解决方法是加大滑动平均窗口或者在 ADC 后加一级一阶低通滤波。另外检查电感运放电路的供电是否干净电机 PWM 干扰会串进模拟通道。4.2 入弯反应慢车冲出去现象是直道正常一到弯道就来不及打角。原因多半是kd太小或者控制周期太长。电磁组建议控制周期不超过 10ms最好 5ms。如果kd已经很大还是慢检查偏差计算是不是用了滤波后的滞后值归一化窗口太长也会拖响应。4.3 舵机抖动发出嗡嗡声现象是舵机在原地高频抖动车不动或者走得很别扭。原因是 PID 输出变化太快或者kd把噪声放大了。解决方法是给 PID 输出加一阶低通或者降低kd同时检查舵机供电是否独立电机电流拉低电压会导致舵机复位。4.4 电池电压下降后参数失效现象是刚充满电跑得好跑几圈后开始抖或者反应迟钝。原因是电感归一化没有做或者速度环目标值没有随电压补偿。解决方法是确保归一化在每次控制周期都执行速度环可以加一个电压前馈电池电压低时适当降低目标速度。4.5 十字和环岛误判现象是直道过十字时舵机突然打角或者环岛进不去。原因是只靠水平电感算偏差没有用竖直电感做区域判断。常见做法是竖直电感值超过阈值时进入特殊区域处理十字区域强制直行环岛区域按固定方向打角直到出环。注意特殊区域的阈值要在赛道上实测不同赛道高度和导线电流都会影响别直接抄别人的数。5. 二次开发与移植把这套方案改成你自己的车5.1 换主控怎么移植这套工程的核心逻辑在Hardware和Control两个目录和 STM32 的耦合主要在 ADC 读取、PWM 输出和定时器中断。换到别的 MCU 时只需要重写这三个接口接口作用移植要点adc_read_channel读电感 ADC改成目标平台的 ADC 驱动pwm_set_duty设电机/舵机 PWM注意频率和分辨率get_dt获取控制周期用定时器或系统滴答PID 和归一化逻辑是纯 C直接拷过去就能用。我一般会先把这三个接口用空函数占位编译通过后再逐个填实现这样能快速定位是驱动问题还是算法问题。5.2 改电感布局后的参数重调如果你把三个电感改成五个或者把水平电感换成斜置偏差公式和 PID 参数都要重调。偏差公式可以改成加权和// 五电感布局的偏差计算示例 float error 0; error norm[FAR_LEFT] * (-2.0f); error norm[LEFT] * (-1.0f); error norm[RIGHT] * ( 1.0f); error norm[FAR_RIGHT] * ( 2.0f); // 中间竖直电感不参与偏差只做区域判断权重系数根据电感间距和赛道宽度定间距越大外侧权重越高。改完布局后kp和kd都要重新从小往大调不能沿用旧参数。5.3 加遥测把黑匣子打开调车最痛苦的是不知道 PID 中间量。我习惯在工程里加一个简单的遥测通过串口把偏差、PID 输出、电感值发到上位机。不用很复杂一个 10ms 的定时任务把几个 float 打包发出去就行// 简易遥测每 10ms 发一帧 void telemetry_send(void) { uint8_t buf[16]; int16_t err (int16_t)(pid_steer.prev_error * 100); int16_t out (int16_t)(servo_output); int16_t ind (int16_t)(norm[LEFT] - norm[RIGHT]); memcpy(buf[0], err, 2); memcpy(buf[2], out, 2); memcpy(buf[4], ind, 2); uart_send(buf, 6); }上位机用串口示波器一画PID 哪一项在抖、哪个电感先响应一目了然。这比盯着车猜快十倍。5.4 一个我踩过的坑最早调车的时候我总觉得 PID 参数是「调」出来的后来才发现是「测」出来的。有一次车在直道怎么都调不稳换了三组参数都没用最后用遥测一看左电感在电机启动瞬间会掉 30% 的值原因是电感和电机共用了一路电源电机电流把运放供电拉低了。把电感供电单独加一级 LDO 之后同样的 PID 参数直接就跑稳了。从那以后我每次调车都强制走一遍先看遥测确认采集链路干净再动 PID。参数不是玄学是采集质量的镜子。希望这份资源能帮你少走点弯路把车先跑起来再谈调得快不快。本文还有配套的精品资源点击获取