ARTICLE DETAIL

资讯详情

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

平衡球PID控制实战:从算法原理到硬件选型与调参经验

平衡球PID控制实战:从算法原理到硬件选型与调参经验 平衡球这个题目看起来像个玩具实际上是个能把控制理论、嵌入式开发、机械结构和调试经验一次性拉通的综合项目。我前后做过三版平衡球第一版用舵机加电阻屏抖得像筛糠第二版换成步进电机加红外对管勉强能停住但一碰就飞第三版才把PID真正调明白球能在板子上稳稳停住还能沿着设定轨迹慢慢走。这个过程踩的坑比想象中多得多很多细节在教科书里根本不会提。下面我把整个项目的算法原理、硬件选型、代码实现和调参经验完整拆一遍适合正在做课程设计的学生、想入门控制算法的嵌入式开发者以及任何对PID感兴趣但一直没搞明白怎么落地的人。1. 平衡球到底在控制什么从物理模型到控制目标1.1 被控对象的本质是一个双积分环节很多人一上来就写PID结果调半天没反应根本原因是没搞清楚平衡球这个被控对象的数学特性。平衡球系统的基本结构是一块平板通过两个电机分别控制X轴和Y轴的倾斜角度球在重力作用下在板上滚动。我们真正能控制的量是板的倾角θ而想要控制的量是球的位置(x, y)。球在斜面上的滚动忽略摩擦和球的转动惯量其加速度与倾角的关系近似为a (5/7) * g * sin(θ)其中g是重力加速度5/7这个系数来自实心球滚动时转动惯量的修正。当θ很小时sin(θ)≈θ所以加速度近似与倾角成正比。对位置求两次积分才能得到位置这意味着系统是一个双积分环节传递函数为G(s) K / s²双积分环节的相位裕度天生很差开环传递函数在穿越频率处的相位是-180度这意味着纯比例控制必然振荡必须加微分项来提供相位超前。这就是为什么平衡球比电机调速难调得多——电机是一阶惯性环节加个P就能转平衡球不加D根本稳不住。1.2 为什么位置式PID比增量式更适合平衡球热词里有人搜位置式pid 增量式pid 抗噪声这个问题在平衡球项目里特别关键。增量式PID的输出是控制量的增量天然带有积分作用适合步进电机这类需要累积脉冲的场合。但平衡球的位置控制有个特点我们需要的是绝对倾角而不是倾角的增量。我第一版用增量式PID控制舵机发现球在目标位置附近会持续低频摆动原因是增量式PID的积分项在误差反向时会累积反向增量导致过冲。换成位置式PID后输出直接对应目标倾角配合微分项抑制振荡效果明显好很多。具体对比如下对比项位置式PID增量式PID输出含义直接输出控制量倾角输出控制量的增量积分处理显式累加误差隐含在增量计算中抗噪声能力较差误差直接进入积分较好增量平滑适用场景平衡球位置控制电机速度控制参数整定难度较难需要处理积分饱和较易但动态响应慢实际项目中我在位置式PID的基础上加了积分分离和抗积分饱和才解决了球在远距离移动时的过冲问题。1.3 双轴耦合X轴和Y轴能不能独立控制理论上如果板的倾角很小X轴和Y轴的运动可以近似解耦分别用两套PID独立控制。但实际中当球快速移动时X轴的倾斜会通过重力分量影响Y轴的运动产生耦合。我试过完全独立控制球在走对角线时轨迹会明显偏离。解决方案有两种一是加入前馈补偿根据X轴的倾角计算对Y轴的干扰量并提前补偿二是降低整体响应速度让耦合影响在可接受范围内。我最终采用的是第一种方案在Y轴PID的输出上叠加一个与X轴倾角成正比的前馈项系数通过实验标定。这个前馈项的加入让对角线轨迹的偏差从原来的3厘米降到了0.5厘米以内。2. 硬件选型为什么我最终放弃了舵机方案2.1 执行机构对比舵机、步进电机与音圈电机平衡球的执行机构直接决定了控制精度的上限。我前后试过三种方案踩了不少坑舵机方案便宜、驱动简单但问题是舵机的死区大、响应慢、角度分辨率低。我用的是MG996R标称分辨率0.1度实际测试发现死区有0.5度左右球在板上会持续抖动。而且舵机的PWM控制频率只有50Hz对于需要快速响应的平衡球来说太低了。步进电机方案精度高、响应快配合同步带或连杆可以精确控制倾角。我最终用的是42步进电机加1/16细分理论分辨率达到0.011度。但步进电机的问题是低速时会有振动需要加细分和加减速曲线。另外步进电机是开环控制如果负载过大可能丢步不过平衡球的负载很轻这个问题不明显。音圈电机方案响应最快、精度最高但价格贵、驱动复杂适合工业级应用。我用不起但如果你预算充足这是最好的选择。最终我选择步进电机方案配合TMC2209驱动芯片静音且细分高。实测下来球的定位精度可以达到±2毫米。2.2 位置检测电阻屏、红外对管与视觉方案位置检测是平衡球的另一个核心。我试过三种方案电阻屏方案成本最低直接用四线电阻屏读取球的位置。但问题是电阻屏需要压力触发球太轻时读不到而且响应速度慢采样率只有100Hz左右。我第一版用这个方案球稍微轻一点就丢失位置。红外对管阵列方案在板子边缘布置红外发射和接收对管通过遮挡判断球的位置。成本低、响应快但分辨率取决于对管密度而且只能检测到球的大致区域。我第二版用了这个方案分辨率只有1厘米球停住时会在两个对管之间来回跳。视觉方案用摄像头从上方拍摄通过图像处理识别球的位置。分辨率高、响应快但需要处理图像对主控算力有要求。我最终用的是OpenMV模块直接输出球心的坐标通过串口发给主控。采样率可以做到50Hz分辨率达到亚像素级别。三种方案的对比方案成本分辨率响应速度实现难度电阻屏低5mm慢简单红外对管低10mm快中等视觉中0.5mm中等较难视觉方案虽然实现难度稍高但综合性能最好我最终选择了这个方案。2.3 主控选型STM32还是树莓派主控需要同时处理位置检测、PID计算和电机控制。我试过STM32F103和树莓派Zero两种方案STM32F103的主频是72MHz跑PID和步进电机控制绰绰有余但处理图像就力不从心了。所以我用STM32做下位机负责PID计算和电机控制OpenMV做上位机负责图像处理两者通过串口通信。树莓派Zero可以同时处理图像和PID但实时性不如STM32Linux系统下PID的定时周期会有抖动。如果你用树莓派建议用Python的RPi.GPIO库配合硬件定时器或者用SPI接口的专用PID控制器。我最终用的是STM32F407主频168MHz带FPU跑浮点PID没有压力。串口波特率设为115200每20ms接收一次位置数据PID计算周期也是20ms对应50Hz的控制频率。3. PID算法的代码实现从理论公式到可运行的C代码3.1 位置式PID的完整实现先给出我实际项目中使用的PID结构体和函数。这个实现包含了积分分离、抗积分饱和和微分先行三个改进点都是实际调试中必须的。typedef struct { float Kp; // 比例系数 float Ki; // 积分系数 float Kd; // 微分系数 float integral; // 积分累加值 float prev_error; // 上一次误差 float prev_measure; // 上一次测量值用于微分先行 float output_max; // 输出上限 float output_min; // 输出下限 float integral_max; // 积分限幅 float deadband; // 死区 } PID_TypeDef; float PID_Compute(PID_TypeDef *pid, float setpoint, float measure) { float error setpoint - measure; // 死区处理误差很小时不调节避免抖动 if (fabs(error) pid-deadband) { error 0; } // 比例项 float P pid-Kp * error; // 积分项积分分离误差大时不积分 if (fabs(error) 50.0f) { // 50mm以内才积分 pid-integral error; // 抗积分饱和 if (pid-integral pid-integral_max) pid-integral pid-integral_max; else if (pid-integral -pid-integral_max) pid-integral -pid-integral_max; } float I pid-Ki * pid-integral; // 微分项微分先行对测量值微分而非误差 float D pid-Kd * (pid-prev_measure - measure); pid-prev_measure measure; // 计算输出 float output P I D; // 输出限幅 if (output pid-output_max) output pid-output_max; else if (output pid-output_min) output pid-output_min; return output; }这段代码有几个关键点需要解释积分分离当误差大于50mm时不累加积分。这是因为球在远距离移动时如果积分持续累加会导致到达目标位置时积分项过大产生严重过冲。我实测发现不加积分分离时球从边缘移动到中心会过冲5厘米以上。抗积分饱和即使误差在50mm以内积分值也不能无限累加。我把积分限幅设为输出上限的1.5倍这样即使积分饱和退出饱和的速度也很快。微分先行标准的PID微分项是对误差微分但误差突变时比如目标位置改变微分项会产生尖峰。微分先行是对测量值微分避免了设定值突变带来的微分冲击。这个改进让球在目标位置改变时的响应平滑了很多。3.2 串级PID位置环加角度环单环PID控制的是球的位置输出是板的倾角。但板的倾角本身也需要控制如果步进电机的响应不够快倾角会滞后于PID的输出。这时候就需要串级PID外环是位置环输出目标倾角内环是角度环控制实际倾角跟踪目标倾角。我一开始觉得串级PID是多余的因为步进电机的响应已经很快了。但实测发现当球快速移动时板的倾角会有明显的滞后导致球的加速度不足响应变慢。加入角度环后倾角的跟踪误差从5度降到了1度以内球的响应速度明显提升。串级PID的实现逻辑// 外环位置环输出目标倾角 float target_angle PID_Compute(pid_pos, target_pos, current_pos); // 内环角度环输出电机速度 float motor_speed PID_Compute(pid_angle, target_angle, current_angle); // 设置电机速度 Stepper_SetSpeed(motor_speed);角度环的采样周期需要比位置环快我用的是5ms位置环是20ms。角度环的Kp可以设得比较大因为角度控制是刚性的不容易振荡。3.3 前馈控制让球走得更准热词里有人搜pid前馈怎么使用前馈在平衡球项目里非常有用。前馈的核心思想是根据目标位置的变化提前给出一个控制量而不是等误差出现后再调节。比如当目标位置从A变到B时球需要产生一个加速度。根据物理模型加速度与倾角成正比所以我们可以直接计算出一个前馈倾角float feedforward Kff * (target_pos - prev_target_pos) / dt;这个前馈项直接叠加到PID输出上让球在目标位置改变时立即响应而不是等误差累积。我实测发现加入前馈后球的轨迹跟踪误差降低了60%以上。前馈系数的标定方法先不加前馈让球走一个阶跃响应记录球的运动轨迹。然后根据轨迹计算需要的加速度反推前馈系数。我最终用的Kff是0.3这个值需要根据实际机械结构微调。4. 调参实战从振荡到稳定的完整过程4.1 调参顺序先P后D再I但平衡球要反过来常规的PID调参顺序是先调P再加D最后加I。但平衡球不一样因为双积分环节的相位特性纯P控制必然振荡所以必须先加D把系统稳定下来再加P提高响应速度最后加I消除稳态误差。我的调参步骤第一步只加D。把Kp和Ki设为0Kd从0开始慢慢增加直到球不再快速振荡。这时候球会缓慢地向一个方向漂移但不会抖。我用的Kd初始值是0.5实际调到1.2时球基本不抖了。第二步加P。在Kd固定的情况下慢慢增加Kp直到球开始出现小幅振荡。然后回调Kp到振荡消失。我用的Kp是2.0回调到1.5。第三步加I。Ki从0开始慢慢增加直到球的稳态误差消除。注意Ki不能太大否则会引起低频振荡。我用的Ki是0.05。第四步微调。在球能稳定停住的基础上微调三个参数让球的响应速度和超调达到平衡。我最终用的参数是Kp1.8, Ki0.04, Kd1.5。4.2 常见问题与排查方法调参过程中会遇到各种问题我整理了一个排查表现象可能原因解决方法球持续高频抖动Kd太小或Kp太大增大Kd减小Kp球低频摆动Ki太大减小Ki或加积分分离球向一个方向漂移机械安装不平校准水平加积分项球到达目标后过冲积分饱和加抗积分饱和球响应慢Kp太小或前馈不足增大Kp加前馈球走对角线偏离轴间耦合加前馈补偿这个表是我踩了无数坑总结出来的基本上覆盖了90%的调参问题。4.3 用MATLAB仿真辅助调参热词里有人搜matlab pid tuner和sumlink仿真pid我强烈建议在实调之前先用MATLAB仿真一遍。平衡球的模型很简单用Simulink搭一个双积分环节加PID控制器几分钟就能跑出响应曲线。仿真的好处是可以快速试不同的参数组合不用担心把硬件调坏。我一般在仿真里把参数调到差不多再上实物微调。仿真和实物的差异主要在摩擦和延迟仿真里没有摩擦实物有摩擦所以实物的Kp需要比仿真大一些。MATLAB的PID Tuner工具可以直接根据传递函数自动整定参数对于平衡球这种双积分环节PID Tuner给出的参数是一个很好的起点。我一般用PID Tuner算出初始参数然后在实物上微调。5. 那些教科书不会告诉你的实操细节5.1 机械结构比算法更重要我做了三版平衡球最大的体会是机械结构决定了控制精度的上限算法只是逼近这个上限。第一版用舵机加3D打印的支架间隙大、刚性差球怎么调都抖。第三版用铝合金加工件加轴承支撑间隙控制在0.1mm以内同样的PID参数球的稳定性提升了一个数量级。几个机械设计的关键点板的刚性板不能太软否则球滚动时板会变形影响倾角精度。我用的是3mm厚的亚克力板背面加了两条铝加强筋。轴承间隙转轴的间隙要小我用的是微型深沟球轴承配合精加工的轴间隙小于0.05mm。重心位置板的重心要尽量低这样倾角变化时板的惯性小响应快。我把电机和驱动板都装在板的下方。球的材质不同材质的球摩擦系数不同影响控制效果。我试过钢球、玻璃球和塑料球最终用的是钢球摩擦小、滚动顺畅。5.2 电源噪声对PID的影响这个问题我调了两天才发现。一开始球在目标位置附近有轻微的高频抖动我以为是PID参数问题调了半天没效果。后来用示波器看电源纹波发现步进电机启停时电源上有200mV的尖峰这个尖峰通过电源耦合到了位置检测电路导致位置数据跳动。解决方案给电机驱动和主控分别供电中间加磁珠和电容滤波。另外位置检测的模拟部分要远离电机驱动部分走线要短。加了滤波后位置数据的噪声从±3mm降到了±0.5mm球的高频抖动自然消失了。5.3 串口通信的实时性问题OpenMV和STM32之间通过串口通信如果通信延迟不稳定PID的采样周期就会抖动影响控制效果。我一开始用查询方式接收数据发现偶尔会丢帧导致PID计算用了旧数据。解决方案用DMA加空闲中断接收串口数据确保每帧数据都能及时处理。另外在协议里加了帧头和校验防止数据错位。通信协议我用的很简单帧头(0xAA 0x55) X坐标(2字节) Y坐标(2字节) 校验和(1字节)STM32收到数据后先校验校验通过才更新位置。这样即使偶尔丢帧也不会用错误的数据计算PID。5.4 步进电机的加减速曲线步进电机如果直接给高速脉冲会丢步或堵转。我一开始没加减速曲线球快速移动时电机偶尔会丢步导致板的角度突然变化球就飞了。解决方案给步进电机加S形加减速曲线。我用的是TMC2209驱动芯片内部自带加减速控制只需要设置最大速度和加速度即可。如果没有专用驱动芯片可以在代码里实现简单的梯形加减速void Stepper_MoveTo(float target_speed) { float accel 1000.0f; // 加速度单位步/秒² float dt 0.005f; // 定时周期5ms if (current_speed target_speed) { current_speed accel * dt; if (current_speed target_speed) current_speed target_speed; } else if (current_speed target_speed) { current_speed - accel * dt; if (current_speed target_speed) current_speed target_speed; } Stepper_SetPulseRate(current_speed); }这个简单的加减速让电机的丢步问题彻底解决了。5.5 温度漂移与长期稳定性平衡球运行一段时间后电机和驱动芯片会发热导致参数漂移。我实测发现运行30分钟后球的稳态位置会偏移2-3毫米。原因是步进电机的电阻随温度变化同样的脉冲频率下实际转速会略有不同。解决方案在PID里加一个慢速的积分项专门消除这种慢漂移。或者定期重新校准水平位置。我采用的是前者把积分时间常数设得比较大这样既能消除漂移又不会引起振荡。6. 从平衡球延伸出去这些经验还能用在哪平衡球项目里用到的技术在很多其他场景里也能直接复用。比如倒立摆本质上也是双积分环节加PID调参思路完全一样。再比如无人机的定点悬停位置环加角度环的串级结构和平衡球的串级PID是一个道理。热词里有人搜buck电路pid闭环控制和pid温度控制这些场景虽然被控对象不同但PID的实现框架是通用的。我写的那个PID结构体改几个参数就能用在温度控制上只需要把微分项的时间常数调大因为温度系统的惯性比平衡球大得多。还有人搜模糊pid和qlearning自适应强化学习pid控制器这些进阶算法在平衡球上也有应用空间。我试过模糊PID根据误差和误差变化率动态调整PID参数效果比固定参数好一些但实现复杂度高了不少。对于初学者我建议先把固定参数PID调明白再考虑进阶算法。最后说一个我踩过的最大的坑不要一上来就追求完美的参数。我第一版平衡球调了两周总想让球纹丝不动结果越调越差。后来降低要求允许球有±2毫米的微小晃动反而很快调稳了。控制系统的目标是稳定不是完美。接受一定的不完美才能把项目做完。
返回列表