资讯动态

直线型一阶倒立摆软件设计:从实时控制到LQR实现

发布时间:2026/10/13 1:06:02 来源:尧图企业网站定制
1. 从硬件到灵魂直线型一阶倒立摆的软件架构总览搞过倒立摆的朋友都清楚机械结构和电机驱动只是搭了个骨架真正让它“活”起来、能稳稳立住的那口气全在软件里。直线型一阶倒立摆这个平台硬件上无非就是导轨、滑块、连杆、编码器和电机但软件设计的好坏直接决定了它是像醉汉一样东倒西歪还是像钉子一样钉在目标位置纹丝不动。我见过太多人硬件焊得漂漂亮亮一上电就疯狂振荡最后发现是控制周期没对齐、滤波没做对、或者状态机写得一塌糊涂。这篇内容就是围绕“直线型一阶倒立摆”的软件设计展开把从底层驱动到上层控制算法的完整链路拆开揉碎讲清楚。不管你是刚接触控制算法的学生还是想快速复现一个稳定倒立摆的工程师这里面的思路和代码框架都能直接拿去用。核心关键词就几个实时控制周期、编码器数据处理、状态观测、PID与LQR的取舍、状态机设计。我会按实际工程落地的顺序从架构设计讲到代码实现再讲到调试时踩过的坑尽量把每个“为什么这么做”都说明白。先给个整体印象直线型一阶倒立摆的软件本质上是一个硬实时闭环系统。它要在毫秒级的时间窗口里完成“读传感器→算状态→算控制量→写执行器”这一整套动作而且不能有一次迟到。迟到一次摆杆可能就倒了。所以软件设计的第一原则不是功能多花哨而是确定性——每个周期该干的事必须按时干完不能有不可预测的延迟。这也是为什么很多人在电脑上仿真跑得好好的一上实物就崩因为电脑上的操作系统调度、内存管理、中断响应都充满了不确定性。下面我会从架构分层开始一层层往下挖把每个模块的设计考量和实现细节都摊开来讲。2. 软件架构分层与实时性设计2.1 为什么必须做分层架构倒立摆的软件如果全堆在一个while循环里初期可能跑得通但一旦要加功能——比如加个上位机波形显示、加个参数在线整定、加个不同的控制模式——代码就会变成一团乱麻。我自己的习惯是分成四层硬件抽象层、驱动层、控制层、应用层。硬件抽象层把GPIO、定时器、ADC、编码器接口这些寄存器操作封装成函数换芯片时只改这一层驱动层负责把原始传感器数据转换成物理量比如编码器脉冲数转成角度和位移控制层是核心跑控制算法和状态估计应用层处理人机交互、模式切换、数据记录。这么分的好处是调试的时候可以逐层验证。比如先确认编码器读数对不对再确认角度计算准不准最后才去调PID参数。如果混在一起出了问题你根本不知道是传感器错了还是算法错了。另一个好处是实时性分析更清晰——控制层必须放在定时器中断里应用层可以放在主循环里慢慢跑互不干扰。2.2 控制周期的选择与计算控制周期是倒立摆软件设计里第一个要拍板的参数。选大了系统响应慢摆杆倒了控制器还没反应过来选小了CPU负担重而且编码器差分计算噪声会放大。那到底选多少合适这里有个经验公式控制周期至少要比系统最快动态的时间常数小一个数量级。一阶倒立摆的不稳定极点大概在 (\sqrt{g/l}) 附近摆杆半长取0.2米的话(\sqrt{9.8/0.2} \approx 7) rad/s对应时间常数约0.14秒。按十分之一算控制周期应该在14毫秒以下。但实际工程中考虑到编码器测速需要足够的脉冲数、以及留出安全裕度我一般选5毫秒或者2毫秒。5毫秒对应200Hz对大多数MCU来说绰绰有余2毫秒对应500Hz适合性能好一点的芯片控制效果会更顺滑。选好周期后必须用硬件定时器来严格保证。不能用软件延时或者系统滴答因为那些都会被其他中断打断。我通常用一个高级定时器配置成向上计数模式自动重装载值根据主频算好在更新中断里执行控制逻辑。中断优先级要设成最高的那一档确保它不会被其他中断抢占。注意控制周期一旦确定整个软件里所有跟时间相关的计算都必须基于这个周期不能一会儿用实际测量时间一会儿用标称值否则积分项和微分项会乱掉。2.3 中断服务程序的设计原则中断服务程序ISR里能做的事很有限原则就是快进快出。读编码器、算控制量、写PWM这些必须做的动作要放在ISR里但像串口打印、Flash存储、复杂的状态判断这些耗时操作绝对不能放进去。我见过有人在ISR里调用printf结果控制周期直接从5毫秒抖到20毫秒摆杆疯狂振荡。具体来说我的ISR里通常只做四件事第一读取编码器计数并计算增量第二调用状态估计函数更新角度和角速度第三调用控制算法计算输出第四更新PWM占空比。整个流程控制在几百微秒以内。如果某个环节特别耗时比如浮点运算多可以考虑用定点数或者查表法优化或者把部分计算挪到主循环里做预处理。另外ISR里访问的全局变量要加volatile修饰防止编译器优化出问题。如果主循环也要读写这些变量还得考虑临界区保护最简单的办法是在读写时关中断但关中断时间要尽可能短。3. 传感器数据处理与状态估计3.1 编码器信号的采集与方向判别直线型倒立摆一般用两个编码器一个测滑块位移一个测摆杆角度。增量式编码器输出A、B两相正交信号通过定时器的编码器模式可以直接硬件解码省去软件判向的麻烦。以常见的STM32为例把定时器配置成编码器模式A相接CH1B相接CH2计数器就会根据旋转方向自动加减。读的时候直接取CNT寄存器的值两次读数相减就是增量。这里有个细节计数器是16位的正转到65535后会溢出回0反转则从0下溢到65535。所以做差分的时候必须处理溢出否则会出现巨大的跳变。我的做法是用一个有符号32位变量累积每次差分时判断差值是否超过32767超过就说明发生了溢出手动修正。代码大概长这样int32_t delta (int16_t)(current_cnt - last_cnt); total_count delta; last_cnt current_cnt;把差值强制转成int16_t利用补码特性自动处理正负和溢出非常简洁。这个技巧在编码器处理里屡试不爽。3.2 从脉冲数到物理量的换算编码器读出来的是脉冲数要转换成角度和位移才能给控制算法用。换算系数取决于编码器线数、减速比如果有、传动轮直径或连杆长度。以摆杆角度为例假设编码器直接安装在摆杆转轴上线数是1000线四倍频后是4000脉冲每转那么每个脉冲对应的角度就是 (360/4000 0.09) 度换成弧度是 (2\pi/4000 \approx 0.00157) rad。位移的换算类似如果编码器通过同步带连接滑块带轮周长是P编码器每转对应滑块移动P那么每个脉冲对应位移就是 (P/4000)。这些系数最好在代码里用宏定义或者常量表示方便后期调整。我习惯在初始化时把这些系数算好存起来运行时直接乘避免每次重复计算。实操心得换算系数一定要用实际测量来校准不能只信理论值。比如带轮周长理论算出来和实际跑一圈测出来的可能有几个毫米的偏差累积起来对控制精度影响很大。校准方法很简单手动把滑块从一端推到另一端记录编码器总脉冲数和实际距离一除就得到真实系数。3.3 角速度的获取差分还是观测器角度有了角速度怎么来最直接的办法是对角度做差分(\omega (\theta_k - \theta_{k-1}) / T)。但编码器信号有量化噪声差分会把噪声放大尤其是周期小的时候。假设角度分辨率是0.00157 rad周期5毫秒那速度噪声就是0.314 rad/s相当可观。直接拿这个速度去做微分控制电机会抖得厉害。解决办法有两个一是加低通滤波二是用状态观测器。低通滤波简单一阶滤波公式是 (\omega_f \alpha \omega (1-\alpha) \omega_f)(\alpha) 越小滤波越强但延迟越大。我一般取截止频率在20到50Hz之间根据实际噪声情况调。观测器方法更优雅比如用卡尔曼滤波或者龙伯格观测器把角度和角速度一起估计出来噪声抑制效果好而且相位延迟小。但观测器需要系统模型调参也麻烦一些。对于一阶倒立摆我的建议是如果只是做课程设计或者快速验证一阶低通滤波足够了如果追求更好的控制性能可以上卡尔曼滤波。卡尔曼的Q和R矩阵可以根据编码器噪声方差和过程噪声来整定网上有现成的公式这里不展开。3.4 状态估计中的常见陷阱状态估计这块有几个坑我踩过不止一次。第一个是角度零点漂移。编码器安装时不可能绝对精确摆杆垂直时读数不一定为零。所以每次启动前要做一次零点校准把当前读数记为偏移量后面所有角度都减去这个偏移。但要注意校准的时候摆杆必须真的垂直最好用水平仪辅助。第二个是速度估计的初始值。如果上电时摆杆在晃动初始速度估计不为零控制器一上来就会输出很大的控制量。我的做法是启动后先等几百毫秒让滤波器收敛同时把速度估计强制置零等系统稳定后再切入闭环。第三个是多传感器融合的时序问题。如果用了额外的陀螺仪或者加速度计不同传感器的采样时刻可能不同步融合时要做时间对齐。简单的做法是在同一个控制周期里依次读取所有传感器认为它们是同一时刻的对于慢速系统来说误差可以接受。4. 控制算法的软件实现4.1 PID控制在倒立摆上的局限与改进很多人第一个想到的就是PID角度环用PD位置环用PID串级控制。这确实能让倒立摆立起来但效果往往差强人意。原因在于倒立摆是个欠驱动、非线性、不稳定的系统PID的线性假设在偏离平衡点较远时不成立。而且角度环和位置环之间存在耦合参数整定非常麻烦。如果非要用PID我的经验是角度环只用PD不要加积分因为积分会引入相位滞后容易导致振荡位置环用PI就够了微分项作用不大。角度环的P决定回复力大小P越大立得越稳但抖动也越大D决定阻尼D太小会振荡D太大会引入高频噪声。整定顺序是先调角度环PD让摆杆能立住几秒钟再加位置环PI让滑块慢慢回到中心。但说实话PID调好了也只是“能立”离“立得稳、抗干扰”还有距离。我实测下来同样的硬件LQR的控制效果明显优于PID尤其是受到扰动后的恢复速度和超调量。4.2 LQR控制器的设计与参数整定LQR线性二次型调节器是倒立摆控制的主流方案。它的核心思想是找一个状态反馈矩阵K使得代价函数 (J \int (x^T Q x u^T R u) dt) 最小。Q矩阵惩罚状态偏差R矩阵惩罚控制量。Q越大系统越倾向于快速消除偏差R越大控制量越保守。对于直线型一阶倒立摆状态向量一般取四个滑块位置、滑块速度、摆杆角度、摆杆角速度。Q矩阵是对角阵四个对角元素分别对应这四个状态的权重。我的整定经验是角度和角速度的权重给大一些位置和速度的权重给小一些因为首要目标是保持摆杆直立。比如Q diag(1, 1, 100, 10)R 1这样算出来的K矩阵通常能让系统稳定。K矩阵的计算可以在MATLAB里用lqr函数也可以自己写Riccati方程求解。算出来之后直接硬编码到MCU里运行时只做矩阵乘法。四个状态乘四个系数再求和计算量很小5毫秒周期完全够用。注意LQR是基于线性化模型设计的只在平衡点附近有效。如果摆杆初始角度偏得太多比如超过30度线性模型误差太大控制器可能拉不回来。所以实际使用时要加一个“起摆”逻辑先用能量法把摆杆甩到平衡点附近再切入LQR。4.3 状态反馈的代码实现细节LQR算出来的控制量是 (u -Kx)其中x是四个状态的向量。代码实现就是一次点乘float u -(K[0]*pos K[1]*vel K[2]*angle K[3]*ang_vel);看起来简单但有几个细节要注意。第一状态量的单位和量纲要统一比如角度用弧度、位移用米否则K的数值会差好几个数量级整定起来很别扭。第二控制量要限幅防止电机过载或者机械结构受损。限幅值根据电机和导轨的实际能力来定我一般设成额定电压的80%左右。第三如果用了积分项来消除稳态误差积分限幅也要做防止积分饱和。另外K矩阵的数值最好用浮点数存储虽然MCU上浮点运算比定点慢但现代Cortex-M4以上的芯片都有硬件FPU几个乘加运算也就几十个时钟周期完全不影响5毫秒的控制周期。4.4 起摆策略的软件实现倒立摆从下垂位置开始需要先“起摆”到平衡点附近才能切入稳定控制。常见的起摆方法有能量法和摇起法。能量法的思路是给系统注入能量让摆杆的机械能逐渐增加到直立位置对应的势能。具体实现时控制滑块做往复运动每次摆杆经过最低点时加速经过最高点时减速能量就会逐渐累积。软件上起摆阶段用一个独立的状态机来管理。检测摆杆角度当角度进入平衡点±10度范围内且角速度较小时切换到LQR控制。切换瞬间要做状态初始化把积分项清零、滤波器状态重置避免切换冲击。我试过不做初始化直接切结果摆杆会猛地抖一下有时候直接抖倒了。起摆的参数需要根据实际机械特性调主要是往复运动的幅度和频率。幅度太小能量注入慢幅度太大滑块可能撞到限位。我的经验是先从小幅慢频开始试观察摆杆摆幅是否逐渐增大如果不是就调整相位关系。5. 软件调试与问题排查实录5.1 调试工具链的搭建倒立摆调试离不开实时数据可视化。我的标配是MCU通过串口或者无线模块把关键数据发到上位机上位机用Python或者LabVIEW画实时曲线。数据包括角度、角速度、位移、速度、控制量、控制周期实际值。这些曲线能直观反映系统状态比盯着摆杆看强多了。串口波特率至少115200数据格式用二进制比ASCII效率高。每帧数据打包成固定长度帧头帧尾加校验防止错位。上位机收到后解析并绘图同时存盘方便回看。我一般还会加一个“触发录制”功能检测到异常时自动把前后几秒的数据存下来方便事后分析。控制周期的实际值一定要监测。在ISR入口翻转一个GPIO用示波器看高电平持续时间就知道ISR执行了多久。如果接近控制周期说明代码需要优化了。5.2 典型问题与排查思路下面这张表是我这些年遇到过的典型问题以及对应的排查方向基本覆盖了90%的故障场景。现象可能原因排查方法上电后摆杆剧烈振荡控制周期不稳定、微分噪声大示波器看ISR时长检查滤波参数摆杆能立但缓慢漂移位置环积分不足、零点漂移检查角度零点校准增大位置环积分起摆失败摆杆摆幅不增起摆相位反了、能量注入不足调整起摆方向增大往复幅度切换LQR瞬间倒下状态未初始化、K参数错误检查切换逻辑核对K矩阵数值电机发热严重控制量限幅过大、高频抖动降低限幅值加强速度滤波编码器读数跳变溢出未处理、接线干扰检查差分溢出逻辑加屏蔽线排查的基本原则是先分后总先确认传感器数据对不对再确认控制量算得对不对最后看执行器响应对不对。很多时候问题出在最底层比如编码器线松了、电源纹波大但表现出来像是算法问题。5.3 参数整定的实操技巧整定LQR的Q和R矩阵我的方法是从保守到激进。先给R一个较大的值让控制量很小系统响应很慢但不会振荡。然后逐渐减小R观察响应速度和超调。找到临界点后再把R调回去一点留裕度。Q矩阵的调整类似先调角度权重让摆杆立稳再调位置权重让滑块回中。整定过程中一定要有量化指标不能凭感觉。我通常记录三个指标稳定时间从扰动到恢复的时间、超调量最大偏差、稳态误差最终偏差。每次调参后测一遍画成表格对比很快就能找到最优区域。还有一个技巧是分步验证。先把位置环断开只让角度环工作这时候滑块会乱跑但摆杆能立住。确认角度环没问题后再接入位置环。这样出问题时能快速定位是哪一环的问题。5.4 代码优化与实时性保障最后聊聊代码优化。倒立摆的代码量不大但实时性要求高。几个关键点第一ISR里避免浮点除法除法很慢能改成乘法就改第二查表代替实时计算比如三角函数可以预先算好表第三用DMA搬运串口数据不占用CPU第四编译器优化等级开到-O2但要注意volatile变量不会被优化掉。如果控制周期实在压不下来可以考虑把状态估计的一部分放到主循环里做ISR里只做最紧急的控制计算。但这样会引入延迟需要重新评估稳定性。我的原则是宁可换更快的芯片也不要在ISR里做妥协因为倒立摆对延迟太敏感了。我个人在实际操作中的体会是倒立摆的软件设计三分靠算法七分靠工程细节。算法书上都有但编码器溢出处理、控制周期抖动、状态切换冲击这些细节只有亲手调过才知道有多坑。把每个细节都做到位系统自然就稳了。后续如果想进一步提升可以尝试自适应控制或者模型预测控制但那是另一个话题了。

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

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

免费获取报价 →
↑