基于STM32与神经网络的50Hz双足机器鸭闭环控制实战
2026/9/20 17:28:56 网站建设 项目流程

1. 项目缘起:为什么我要做一只机器鸭

1.1 从"能走"到"走得稳"的鸿沟

双足机器人这个领域,外行看热闹,内行看门道。很多人第一次看到双足机器人走路视频,第一反应是"哇,好厉害",但真正做过的人都知道,让一个双足机构在平地上迈出第一步,和让它在受到扰动后还能稳住重心,中间隔着的不是一行代码,而是一整套控制体系的搭建。MicroDuck 这个项目,就是我在踩了无数坑之后,决定从零搭一套完整神经控制闭环的产物。

它的核心目标很明确:用一只体型小巧的双足机器鸭,跑通从传感器采集、神经网络推理、关节指令下发到姿态反馈修正的完整闭环,并且把整个控制回路的运行频率稳定在 50Hz。50Hz 这个数字不是随便定的,它意味着每 20 毫秒就要完成一次"感知—决策—执行"的循环,这对算力分配、通信延迟、滤波设计都提出了实打实的要求。

1.2 这只鸭子到底能做什么

MicroDuck 不是玩具,也不是那种只能做预设动作的演示品。它具备的能力包括:实时读取 IMU 姿态数据、通过训练好的轻量神经网络输出关节目标角度、驱动多个舵机或电机完成步态、在受到侧向推力时通过闭环反馈调整重心。适合谁来参考?如果你正在做双足机器人、四足机器人、或者任何需要高频闭环控制的嵌入式项目,这套思路可以直接迁移。哪怕你是刚入门的学生,只要有一点 Python 和嵌入式基础,跟着走也能把框架搭起来。

我之所以选择"鸭"这个形态,是因为它的重心分布和腿部结构与常见的双足人形有相似之处,但体积更小、成本更低、摔了不心疼。这一点在早期调试阶段极其重要——你不会想拿一台几万块的平台去试一个还没验证过的控制参数。

1.3 50Hz 这个数字背后的取舍

很多人会问,为什么不是 100Hz 或者 200Hz?理论上频率越高,控制越平滑,但实际工程里,频率越高意味着每个周期可用的计算时间越短。50Hz 给出 20ms 的预算,其中 IMU 读取和滤波大概占 2-3ms,神经网络推理占 5-8ms,剩下的留给通信和余量。如果硬上 100Hz,推理时间就被压缩到 5ms 以内,普通 MCU 根本扛不住,除非上更贵的算力平台。

提示:控制频率的选择要和你的执行器响应速度匹配。舵机的响应延迟通常在 10-20ms 量级,你控制频率再高,执行器也跟不上,反而浪费算力。

所以 50Hz 是一个在算力、延迟、执行器特性之间取得平衡的甜点值。这也是我在这个项目里反复强调的一个理念:不要盲目追求参数上的漂亮,要让每个数字都有工程依据。

2. 整体架构设计:闭环是怎么转起来的

2.1 神经控制闭环的四个环节

MicroDuck 的控制闭环可以拆成四个环节,环环相扣。第一个环节是感知,IMU 以固定频率采集加速度和角速度,经过滤波后得到相对干净的姿态角。第二个环节是推理,把姿态角、关节当前角度、上一周期的动作等拼成一个状态向量,喂给训练好的神经网络,网络输出下一时刻各关节的目标角度。第三个环节是执行,把目标角度通过总线下发给舵机或电机驱动器。第四个环节是反馈,读取关节的实际角度和新的 IMU 数据,进入下一轮循环。

这四个环节必须在 20ms 内跑完,任何一个环节超时,整个闭环的稳定性都会受影响。我在早期版本里就吃过亏——IMU 读取用了阻塞式等待,结果偶尔卡住 30ms,鸭子直接原地抽搐。

2.2 为什么用神经网络而不是传统 PID

传统 PID 控制双足机器人不是不行,但调参极其痛苦。双足系统是非线性的、强耦合的,一个关节的角度变化会影响整个身体的姿态,PID 的三个参数在不同步态阶段需要不同的值,你得做增益调度,工作量巨大。神经网络的优势在于,它可以通过训练直接学到"当前状态到目标动作"的映射,把复杂的耦合关系隐式地编码在权重里。

当然,神经网络也不是银弹。它的输出没有可解释性,出问题的时候你很难定位是哪个环节的错。所以我在设计上做了一个折中:神经网络负责生成目标角度,底层仍然保留一个轻量的位置环 PID 来保证关节能准确跟踪目标。这样上层负责"决策",下层负责"执行精度",各司其职。

2.3 硬件选型的考量

硬件这块我列个表,把关键部件的选型和理由说清楚。

部件选型选型理由
主控STM32F4 系列有 FPU,跑轻量神经网络够用,生态成熟
IMU六轴陀螺仪加速度计成本低,50Hz 下噪声可接受,配合滤波够用
执行器串行总线舵机支持角度回读,方便做闭环反馈
通信板载串口 + 上位机调试阶段需要实时看数据
电源2S 锂电池体积小,能给舵机提供足够瞬时电流

选 STM32F4 而不是更便宜的 F1,核心原因就是 FPU。神经网络推理涉及大量浮点运算,F1 没有硬件浮点单元,纯软件浮点跑一次推理要几十毫秒,根本进不了 50Hz 的预算。F4 带 FPU,同样的网络推理能压到 5-8ms,这就是差距。

注意:舵机的瞬时电流可能达到堵转电流的好几倍,电源一定要留足余量,否则一迈步电压就被拉垮,主控直接复位。

3. 核心细节解析:50Hz 闭环里的关键环节

3.1 IMU 数据采集与姿态解算

IMU 是整个闭环的眼睛,它的数据质量直接决定了控制效果。六轴 IMU 输出的是三轴加速度和三轴角速度,要得到姿态角,需要做姿态解算。常见的方法有互补滤波、Mahony 滤波、Madgwick 滤波。我在 MicroDuck 上用的是互补滤波,原因是它计算量小,参数直观,适合在 MCU 上跑。

互补滤波的核心思想很简单:加速度计在静态下能给出准确的重力方向,但动态下噪声大;陀螺仪积分能得到平滑的角度,但会漂移。把两者按频率特性互补——高频信陀螺仪,低频信加速度计——就能得到相对稳定的姿态角。公式大致是:角度 = α × (角度 + 陀螺仪角速度 × dt) + (1-α) × 加速度计解算角度。α 一般取 0.95 到 0.98 之间。

这里有个坑我必须提醒:加速度计解算角度时,如果机器人正在加速运动,加速度计测到的不只是重力,还有运动加速度,解算出来的角度就是错的。所以互补滤波的 α 不能取太小,否则动态下姿态会被运动加速度带偏。我实测下来 α 取 0.97 比较稳。

3.2 50Hz 陷波器的设计与实现

这是整个项目里最容易被忽视、但影响最大的一个环节。舵机在运动时会产生周期性的机械振动,这个振动会耦合到 IMU 上,让姿态数据出现周期性波动。如果这个波动频率接近控制频率的整数倍,就会和控制回路形成共振,鸭子会越走越抖。

50Hz 陷波器的作用,就是把特定频率的干扰信号滤掉。陷波器的原理是在频域上挖一个"坑",让某个窄带频率的信号无法通过。设计一个数字陷波器,核心参数有三个:中心频率、带宽、采样频率。中心频率就是你要滤掉的干扰频率,带宽决定了坑的宽度,采样频率就是你的控制频率。

我用的是一种双 T 型陷波滤波器结构,它的传递函数在中心频率处增益为零,能有效抑制该频率的干扰。离散化之后,每个采样周期只需要几次乘加运算,计算量很小,完全能塞进 50Hz 的预算里。

提示:陷波器的中心频率要和实际干扰频率对准,偏了效果会大打折扣。建议先用上位机把 IMU 原始数据录下来,做一次频谱分析,找到干扰的主频,再设陷波器参数。

3.3 神经网络的结构与推理优化

MicroDuck 上跑的神经网络不能太大,我的目标是参数量控制在几千到一万级别,推理时间控制在 8ms 以内。网络结构用的是全连接网络,输入层接收状态向量,中间两到三个隐藏层,输出层输出各关节的目标角度。

输入状态向量的构成很关键,我用了这些量:身体俯仰角和横滚角、身体角速度、各关节当前角度、各关节当前角速度、上一周期的关节目标角度、一个相位信号。相位信号用来告诉网络当前处于步态的哪个阶段,这样网络可以学到分阶段的控制策略。

推理优化上,我做了几件事。第一,把权重和偏置量化成定点数,减少浮点运算量。第二,把激活函数从 tanh 换成计算更快的近似函数。第三,把矩阵乘法展开成循环,避免动态内存分配。这几招下来,推理时间从最初的 15ms 压到了 6ms 左右。

3.4 关节指令下发与总线通信

舵机通过串行总线通信,每个舵机有独立的 ID,主控按 ID 逐个下发目标角度。这里有个细节:总线通信是串行的,舵机越多,一轮通信耗时越长。MicroDuck 有 8 个关节,如果每个舵机通信耗时 1ms,一轮就是 8ms,加上推理和滤波,20ms 的预算就很紧张了。

我的优化办法是把舵机通信做成非阻塞的,主控发出指令后不等待回复,直接进入下一轮循环,舵机的实际角度在下一轮循环里读取。这样通信时间就被"隐藏"在了控制周期里,不占用关键路径。代价是反馈有一拍的延迟,但在 50Hz 下,20ms 的延迟对双足平衡影响可以接受。

4. 实操过程:从零搭起闭环

4.1 开发环境搭建

先说环境。主控端我用的是 STM32CubeIDE,配合 HAL 库开发。上位机用 Python,主要做数据可视化和神经网络训练。训练框架用 PyTorch,训练好的模型导出成权重文件,再转成 C 数组烧进主控。

这里有个流程上的建议:不要在主控上直接调神经网络,先在 PC 上把网络训好、验证好,再移植。PC 上可以用 MuJoCo 做仿真,把训练好的策略在仿真里跑通,再上真机。这样能省下大量真机调试时间,也能避免真机被摔坏。

4.2 MuJoCo 仿真与回放

MuJoCo 是这个项目里我最推荐的仿真工具。它的物理引擎精度高,接触模型处理得好,适合做双足机器人的仿真。我在 MuJoCo 里建了 MicroDuck 的模型,把质量、惯量、关节限位都按真机参数设置好,然后在仿真里训练和验证控制策略。

仿真回放这个功能特别有用。每次真机跑完,我把真机的传感器数据录下来,在 MuJoCo 里重新播放,看看仿真里的表现和真机是否一致。如果不一致,说明仿真模型和真机有偏差,需要调整模型参数。这个"仿真—真机—回放—修正"的循环,是我把控制策略调稳的核心方法。

提示:MuJoCo 的 viewer 支持重新播放录制的轨迹,这个功能在排查问题时极其好用。你可以逐帧看机器人在哪个时刻姿态开始发散,然后针对性分析。

4.3 神经网络训练流程

训练数据的来源有两个:一是 MuJoCo 仿真里用传统控制器生成的成功步态数据,二是真机上人工遥控采集的数据。前者量大、干净,后者量少、但更贴近真实。我把两者混合起来训练,效果比单用仿真数据好很多。

训练的目标函数设计也很讲究。不能只让网络模仿目标角度,还要加上姿态稳定的惩罚项。我的损失函数包含三部分:关节角度跟踪误差、身体姿态偏差、动作平滑度惩罚。第三项是为了避免网络输出剧烈跳变的动作,那种动作在真机上会让舵机抖得厉害。

训练完之后,我会在仿真里做扰动测试,给机器人施加侧向推力,看它能不能恢复。能恢复的策略才拿去真机验证。这个筛选过程能过滤掉大部分不鲁棒的策略。

4.4 真机调试与参数整定

真机调试是最考验耐心的环节。我的流程是:先让机器人悬空,验证关节能按指令运动;再放到地面上,用手扶着,验证步态节奏;最后放手,让它自己走。

参数整定上,我重点调三个东西:控制频率的实际稳定性、陷波器的中心频率、神经网络的输出缩放系数。控制频率用示波器或者 GPIO 翻转来测,确保每个周期真的是 20ms。陷波器中心频率通过频谱分析确定。输出缩放系数决定了网络输出的动作幅度,太小走不动,太大直接摔。

这里分享一个我踩过的坑:早期我为了让鸭子走得快,把输出缩放系数调得很大,结果鸭子一迈步就往前扑。后来才明白,双足行走的稳定性比速度重要得多,宁可走慢一点,也要保证每一步都稳。把速度降下来之后,反而能走得更远。

5. 常见问题与排查技巧实录

5.1 姿态数据抖动严重

这是最常见的问题。表现是 IMU 读出来的姿态角一直在小幅震荡,导致网络输出也跟着抖。排查思路:先看原始数据,如果原始数据就抖,那是硬件或滤波问题;如果原始数据干净但解算后抖,那是解算参数问题。

硬件层面,检查 IMU 是否固定牢靠,有没有和振动源刚性连接。滤波层面,检查互补滤波的 α 值是否合适,陷波器中心频率是否对准。我遇到过一种情况,是舵机电源和主控电源共地不良,导致 IMU 数据里混入了电源噪声,重新布线之后就干净了。

5.2 控制周期不稳定

50Hz 要求每个周期严格 20ms,但实际跑起来经常是 18ms 到 25ms 之间跳。这种抖动对控制效果影响很大。排查方法是在每个周期的开始和结束翻转一个 GPIO,用示波器看周期长度。

常见原因是某个环节耗时不稳定,比如串口通信偶尔阻塞、神经网络推理遇到分支预测失败、或者中断嵌套太深。我的解决办法是把耗时长的操作拆成多个周期分步执行,保证每个周期的负载均衡。另外,把不关键的中断优先级降下来,避免打断控制循环。

5.3 神经网络输出异常

有时候网络会输出明显不合理的角度,比如超过关节限位,或者相邻两帧之间跳变很大。这种情况通常是输入状态向量里有异常值。排查方法是把输入向量打印出来,逐个检查是否有 NaN、无穷大、或者超出正常范围的值。

我遇到过一次,是 IMU 在某个姿态下解算出了 NaN,原因是加速度计读数恰好让某个除法分母为零。后来加了一个保护,检测到异常值就用上一帧的值替代。这种边界情况的处理,在真机上非常必要。

5.4 常见问题速查表

问题现象可能原因排查方向解决思路
姿态角持续漂移陀螺仪零偏未校准静止时读陀螺仪输出上电时做零偏校准
步态节奏紊乱控制周期抖动GPIO 翻转测周期均衡各周期负载
侧向推力后无法恢复网络鲁棒性不足仿真扰动测试增加扰动训练数据
舵机发热严重目标角度频繁跳变看网络输出曲线加动作平滑惩罚
通信偶发丢包总线负载过高统计通信错误率降低通信频率或分帧

5.5 独家避坑经验

第一条,永远先在仿真里验证,再上真机。真机摔一次,可能就要修半天,仿真里摔一万次都没事。第二条,所有参数都要有记录,改了哪个参数、效果怎么变,都记下来。双足控制的参数空间很大,不记录的话,改着改着就忘了之前哪个组合效果好。第三条,控制频率宁低勿高,先把 50Hz 跑稳,再考虑往上提。频率不稳的高频控制,还不如频率稳定的低频控制。

第四条,也是我最想强调的:不要迷信神经网络。神经网络是工具,不是目的。如果某个环节用传统方法能解决得更好,就用传统方法。MicroDuck 里底层的位置环就是传统 PID,它比神经网络更可靠、更可预测。把合适的方法用在合适的地方,才是工程思维。

6. 拆解资料与后续扩展方向

6.1 拆解资料的整理思路

做这种项目,拆解资料的价值不亚于代码本身。我整理资料的习惯是按模块分文件夹:硬件资料、仿真模型、训练代码、主控固件、调试记录。每个文件夹里放一个 README,说明这个模块的作用、关键参数、以及踩过的坑。

硬件资料里,我会放接线图、电源分配图、机械尺寸图。仿真模型里,放 MuJoCo 的 XML 文件和对应的参数说明。训练代码里,放网络结构定义、训练脚本、以及训练好的权重。主控固件里,放各个模块的驱动代码和控制主循环。调试记录里,放每次真机测试的数据和结论。

这样整理的好处是,过几个月回头看,或者别人接手,都能快速理解整个项目。我见过太多项目,代码写得不错,但没有任何文档,最后只能烂在硬盘里。

6.2 后续可以扩展的方向

MicroDuck 目前跑通了基本的行走闭环,但还有很多可以扩展的地方。第一个方向是增加地形适应能力,让鸭子能在轻微不平的地面上行走。这需要在状态向量里加入足底接触信息,并重新训练网络。第二个方向是提高控制频率,把 50Hz 提到 100Hz,这需要换更强的算力平台,或者进一步优化网络推理。

第三个方向是加入视觉,让鸭子能感知前方障碍。这属于更高层次的任务规划,和底层的平衡控制是两套系统,需要设计好两者之间的接口。第四个方向是做多鸭协同,研究多个双足机器人之间的协调控制。这个方向偏研究性质,但很有意思。

6.3 我个人的一些体会

做 MicroDuck 这个项目,最大的收获不是最后鸭子能走多稳,而是整个过程中建立起来的一套方法论:先仿真后真机、先稳定后速度、先记录后优化。这套方法论放到任何机器人项目里都适用。

另外一点体会是,50Hz 这个数字看起来简单,但背后涉及的每一个环节——滤波、推理、通信、执行——都需要仔细打磨。工程上的事情,往往不是某个环节特别难,而是所有环节都要同时达标,任何一个短板都会拖垮整体。这也是为什么我花了大量时间在陷波器和周期稳定性上,而不是一味追求网络结构的复杂。

最后分享一个小技巧:如果你也在做类似的双足项目,建议从最简单的单腿站立开始,把单腿的平衡闭环跑通,再扩展到双腿。单腿站立时,控制目标单一,容易定位问题。等单腿稳了,双腿行走就是水到渠成的事。我当初就是跳过单腿直接做行走,结果在姿态耦合上卡了很久,回头补单腿的课才理顺。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询