平衡车——PID算法优化

发布时间:2026/10/11 17:26:07
平衡车——PID算法优化 主要的PID算法优化有以上几种为了找出适用于平衡车的改进方法可以一个一个尝试。输出偏移、微分先行、积分限幅比较有用。输出偏移目前平衡车在直立时会小距离来回移动无法较好地静止原因一是平衡车使用的是直流减速电机减速箱的齿轮有间隙当电机没有转动时轮子也可以凭借齿轮间隙而小幅度转动所以控制不精确二是当输出PWM较小时控制力度小到无法克服轮子摩擦力需要PID积分项不断增大才能控制造成响应有延迟。所以可以使用输出偏移让PID输出值直接跳过控制力度小的阶段除了输出0以外所有输出值都能控制小车。输出偏移作用于角度环其输出值控制平均PWM。在PID结构体中新定义一个输出偏移Outoffsettypedef struct { //定义PID结构体变量类型 float Target; //目标值由用户设定 float Actual; //实际值从传感器读取 float Actual1; //上次实际值 float Out; //输出值作用于执行器 float Kp; //比例项权重 float Ki; //积分项权重 float Kd; //微分项权重 float Error0; //本次误差 float Error1; //上次误差 float ErrorInt; //误差积分 float ErrorIntMax; //误差积分的最大值 float ErrorIntMin; //误差积分的最小值 float OutMax; //输出限幅的最大值 float OutMin; //输出限幅的最小值 float OutOffset; //输出偏移 } PID_t;随后在角度环的结构体赋值中加入out offset的赋值后面再来调参。PID_t AnglePID { //角度环PID结构体变量定义的时候同时给部分成员赋初值 .Kp 5, //比例项权重 .Ki 0.1, //积分项权重 .Kd 5, //微分项权重 .OutMax 100, //输出限幅的最大值 .OutMin -100, //输出限幅的最小值 .OutOffset 3, //输出偏移 };接下来是最关键的一步在角度环PID计算出输出值之后 或者 输出值赋值给平均PWM之前加入输出偏移/*输出偏移*/ if (p-Out 0) {p-Out p-OutOffset;} //如果输出值为正则加上结构体指定的OutOffset if (p-Out 0) {p-Out - p-OutOffset;} //如果输出值为负则减去结构体指定的OutOffset最后就是调参这里依然使用小程序的滑杆调参并在OLED屏幕显示一下out offset。else if (strcmp(Name, Offset) 0) //如果滑杆名称是Offset { AnglePID.OutOffset atof(Value); //则把滑杆值赋值给OutOffset }OLED_Printf(56, 56, OLED_6X8, Offset:%02.0f, AnglePID.OutOffset); //显示输出偏移值一边用滑杆调参一边观察小车情况最后将合适参数写入角度环结构体。同时还可以加大角度环的K项和D项最后小车已经可以较好地静止了但不能做到完全静止这是齿轮间隙的缘故如果换成无刷电机就可以完全静止因为它没有齿轮可以直接驱动。微分先行微分先行也作用于角度环因为只有它调节了D项。在PID结构体中新定义一个上次实际值Actual1typedef struct { //定义PID结构体变量类型 float Target; //目标值由用户设定 float Actual; //实际值从传感器读取 float Actual1; //上次实际值 float Out; //输出值作用于执行器 float Kp; //比例项权重 float Ki; //积分项权重 float Kd; //微分项权重 float Error0; //本次误差 float Error1; //上次误差 float ErrorInt; //误差积分 float ErrorIntMax; //误差积分的最大值 float ErrorIntMin; //误差积分的最小值 float OutMax; //输出限幅的最大值 float OutMin; //输出限幅的最小值 float OutOffset; //输出偏移 } PID_t;随后在计算PID的PID更新函数中加入获取Actual1以及微分先行的过程。由于每次进入函数时实际值的数值已经更新所以不能像误差一样在函数开头就加入Actual1必须在最后加入。/*PID计算*/ /*使用位置式PID公式计算得到输出值*/ p-Out p-Kp * p-Error0 p-Ki * p-ErrorInt // p-Kd * (p-Error0 - p-Error1); //普通PID计算公式 - p-Kd * (p-Actual - p-Actual1); //微分先行计算公式 /*输出偏移*/ if (p-Out 0) {p-Out p-OutOffset;} //如果输出值为正则加上结构体指定的OutOffset if (p-Out 0) {p-Out - p-OutOffset;} //如果输出值为负则减去结构体指定的OutOffset /*输出限幅*/ if (p-Out p-OutMax) {p-Out p-OutMax;} //限制输出值最大为结构体指定的OutMax if (p-Out p-OutMin) {p-Out p-OutMin;} //限制输出值最小为结构体指定的OutMin /*获取上次实际值*/ p-Actual1 p-Actual; //本轮计算后进行变量传递下轮计算时Actual1即为上次实际值 }最后在PID初始化函数中加入Actual1并清零/** * 函 数PID初始化 * 参 数p 指定结构体的地址 * 返 回 值无 */ void PID_Init(PID_t *p) { /*把PID表示状态的参数清零避免之前遗留的参数对本次启动造成影响*/ p-Target 0; p-Actual 0; p-Actual1 0; p-Out 0; p-Error0 0; p-Error1 0; p-ErrorInt 0; }改进后控制小车表面上没有什么明显的作用但实际上对小车的控制变得更加顺滑了。积分限幅如果一边操控摇杆让小车前进一边又按住小车不让它动那么此时误差疯狂累计积分项已经很大了一松手小车就会因为积分项的大力调控而冲出去不止速度环角度环和转向环也需要此类改进。在PID结构体中定义误差积分的最值typedef struct { //定义PID结构体变量类型 float Target; //目标值由用户设定 float Actual; //实际值从传感器读取 float Actual1; //上次实际值 float Out; //输出值作用于执行器 float Kp; //比例项权重 float Ki; //积分项权重 float Kd; //微分项权重 float Error0; //本次误差 float Error1; //上次误差 float ErrorInt; //误差积分 float ErrorIntMax; //误差积分的最大值 float ErrorIntMin; //误差积分的最小值 float OutMax; //输出限幅的最大值 float OutMin; //输出限幅的最小值 float OutOffset; //输出偏移 } PID_t;在计算PID的更新函数中计算积分项后加入积分限幅/*外环误差积分累加*/ /*如果Ki不为0才进行误差积分这样做的目的是便于调试*/ /*因为在调试时我们可能先把Ki设置为0这时积分项无作用误差消除不了误差积分会积累到很大的值*/ /*后续一旦Ki不为0那么因为误差积分已经积累到很大的值了这就导致积分项疯狂输出不利于调试*/ if (p-Ki ! 0) //如果Ki不为0 { p-ErrorInt p-Error0; //进行误差积分 /*误差积分限幅*/ if (p-ErrorInt p-ErrorIntMax) {p-ErrorInt p-ErrorIntMax;} //限制误差积分最大为结构体指定的ErrorIntMax if (p-ErrorInt p-ErrorIntMin) {p-ErrorInt p-ErrorIntMin;} //限制误差积分最小为结构体指定的ErrorIntMin } else //否则 { p-ErrorInt 0; //误差积分直接归0 }在三个环的结构体中都加入误差积分的最值PID_t AnglePID { //角度环PID结构体变量定义的时候同时给部分成员赋初值 .Kp 5, //比例项权重 .Ki 0.1, //积分项权重 .Kd 5, //微分项权重 .OutMax 100, //输出限幅的最大值 .OutMin -100, //输出限幅的最小值 .OutOffset 3, //输出偏移 .ErrorIntMax 600, //误差积分的最大值 .ErrorIntMin -600, //误差积分的最小值 }; PID_t SpeedPID { //角度环PID结构体变量定义的时候同时给部分成员赋初值 .Kp 2, //比例项权重 .Ki 0.05, //积分项权重 .Kd 0, //微分项权重 .OutMax 20, //输出限幅的最大值 .OutMin -20, //输出限幅的最小值 .ErrorIntMax 150, //误差积分的最大值 .ErrorIntMin -150, //误差积分的最小值 }; PID_t TurnPID { //转向环PID结构体变量定义的时候同时给部分成员赋初值 .Kp 4, //比例项权重 .Ki 3, //积分项权重 .Kd 0, //微分项权重 .OutMax 50, //输出限幅的最大值 .OutMin -50, //输出限幅的最小值 .ErrorIntMax 20, //误差积分的最大值 .ErrorIntMin -20, //误差积分的最小值 };最值的确定可以通过串口打印或绘图等方式先观察正常情况下的速度值、角度值、转向值最后在此基础上扩大一定范围来定最值。加入后如果再出现开始那种情况积分就不会大到离谱系统恢复时也不会失控了。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询