强化学习四足机器人部署实战:从GPU训练到RK3566 NPU量化
2026/9/5 6:25:31 网站建设 项目流程

上周我终于把一套完整的强化学习训练链跑通了:英伟达 GPU 上训好的足式机器人策略,一口气部署到 RK3566 实机,让 Microduck 这只 25 厘米的小四足真正站起来走路。整个过程从仿真到真机踩了不少坑,尤其是模型导出、NPU 量化和板端推理这几个环节,网上能查到的资料七零八落,能跑通的完整链路更少。我把这次的部署过程整理成手记,希望能给正在折腾 Microduck、RK3566 或者类似嵌入式机器人平台的朋友省点时间。

这篇文章覆盖的是一条完整链路:先在 GPU 上用强化学习算法训练策略,再把策略压缩量化后搬到 RK3566 上实时推理,最后通过电机驱动让 Microduck 动起来。适合手里有泰山派开发板、Microduck 本体,或者对端侧强化学习部署感兴趣的工程师和爱好者参考。我会把训练阶段的选型、导出阶段的坑、RK3566 上跑推理的细节,以及实机调试时遇到的各种“鬼问题”都摊开讲。

1. 项目整体设计与核心链路拆解

1.1 为什么选 Microduck 和 RK3566 这套组合

Microduck 是一台 25 厘米左右的小型四足机器人,结构紧凑、成本适中,很适合做强化学习算法的落地验证平台。它的机身尺寸决定了它可以在室内环境做步态测试,不会像大型四足那样对场地和安全性有很高要求,作为个人开发平台的友好度很高。

RK3566 作为部署端的核心芯片,最吸引人的地方在于它集成了 0.8 TOPS 的 NPU,同时 CPU 部分有 4 核 Cortex-A55。这个算力水平放在今天的手机芯片面前不值一提,但对于 Microduck 这种小规模足式策略来说完全够用。一个典型的四足运动策略网络,输入是关节角度、角速度、机身姿态和指令速度,大概 30 到 40 维,输出是 12 个关节的增量或目标位置,中间两层 256 或 512 宽度的 MLP,这个体量的模型在 RK3566 的 NPU 上跑 100 到 200 赫兹不成问题。

我之前考虑过两个替代方案:一个是直接用树莓派 4B 跑 CPU 推理,简单是简单,但 12 个关节的 PD 控制频率要跑到 500 赫兹,CPU 在跑网络的同时还要处理串口和 IMU 数据,一旦时序抖动就会让机器人腿部发烫、步态发抖。另一个方案是换成更高算力的 RK3588,性能确实更强,但功耗和板子成本都上去了,对于验证算法来说有点杀鸡用牛刀。RK3566 的平衡点刚好卡在这里:NPU 做主推理、CPU 做主控制、功耗压得住,一块小电池能让 Microduck 跑 20 分钟以上。

1.2 仿真训练与实机部署的分工逻辑

强化学习机器人的标准流程是“仿真是主战场,实机是最终考场”。在英伟达 GPU 上用 Isaac Lab 或 MuJoCo 这类物理引擎做仿真,相当于给策略一个可以无限重来的训练场;模型在仿真里跑赢了,再搬到 RK3566 实机上做域迁移。这个分工表面看是“训练在前、部署在后”,但实际工程里两者是深度耦合的。

我在项目启动时先把系统架构定了:训练阶段用 GPU 服务器跑 Isaac Lab 配合 PPO 算法,输出 PyTorch 权重;部署阶段把权重导出为 ONNX,再转换成 RKNN 格式放到 RK3566 的 NPU 上;板端运行一个 C++ 推理程序,读取关节编码器和 IMU 数据,把观测拼好后丢给模型,拿到动作输出后映射成 12 路 PWM 或 CAN 指令驱动电机。这里的核心约束是时延预算:从传感器数据到电机指令,整个链路不能超过 5 毫秒,否则高频 PD 控制会失去稳定性,机器人站都站不稳。

架构定下来之后,我会先在纸面上过一遍每个环节的耗时预算:NPU 推理 2 毫秒,CAN 总线通信 1 毫秒,传感器读取加预处理 1 毫秒,留给调度和余量 1 毫秒,一共 5 毫秒。这个预算是后面选型和调优的基线,每一步都拿实测数据去校核,任何环节超预算就说明方案要调整。

1.3 仿真到实机的核心难题是“域差距”

仿真训练出来的策略直接搬到实机上,十有八九是走不了路的。最大的原因就是域差距:仿真里的摩擦系数、电机延时、执行器带宽都是理想化的“标称值”,真实世界的 Microduck 每条腿的舵机响应速度、齿轮间隙、地面摩擦力都会和仿真有偏差。如果策略在仿真里过度依赖某些理想条件,到实机上就会显得“不会走路”。

应对域差距的办法在训练阶段就要做,而不是部署阶段。我在训练时给仿真环境加入了随机化参数:地面的摩擦系数在 0.4 到 1.2 之间随机采样,电机力矩增益上下浮动 10%,机身质量随机加减 10 克,甚至传感器噪声和高斯噪声都做了注入。这样训练出来的策略不会死记硬背“标准环境”的行为,而是在一个变化范围内都能找到稳定的步态。这也是能不能顺利从 GPU 到 RK3566 的关键,很多人在部署阶段才想起域随机化,但训练时没做,策略的鲁棒性先天不足,后期再怎么调推理代码都补不回来。

2. 英伟达 GPU 上的训练环境搭建与策略仿真

2.1 训练环境选型和容器化准备

训练环境我选择了 Ubuntu 20.04 系统加 NVIDIA 驱动,PyTorch 用 1.13 以上版本,物理引擎用 Isaac Lab。Isaac Lab 是在 Isaac Sim 基础上封装的机器人学习框架,自带四足机器人的环境模板,比如 legged_robot 相关的示例,可以直接改 XML 配置来适配 Microduck 的尺寸和关节配置。

这里有个很实用的经验:尽量不要在物理机上裸装训练环境。我一开始直接在服务器上装了 PyTorch 和 Isaac Lab,结果每次升级依赖都会把环境弄乱,一折腾就是半天。后来改用 NVIDIA 官方容器镜像,把训练代码挂载进容器里跑,环境隔离得干净,换 GPU 机器也能无缝迁移。容器镜像选 nvcr.io/nvidia/isaac-sim 对应的版本,里面 OpenGL 和 CUDA 环境都配好了,启动时加一行 xhost + 授权显示环境,方便可视化调试。

Microduck 的 URDF 模型需要自己写或者从开源仓库找。注意关节命名规则要和训练代码里 obs 空间的顺序一致,比如 FL_hip、FL_thigh、FL_calf 这类命名,排错的时候打印 obs 维度就能对比出来。URDF 里的质量、重心位置尽量按 Microduck 实际参数填,我在初期偷懒用了示例机器人的参数,结果训出来的策略步伐频率明显偏快,实机根本跟不上,只能回头改模型重新训,白白浪费了一周时间。

2.2 观测空间、动作空间与 PD 控制器参数

训练四足机器人运动策略时,观测空间一般包括:机身线速度、角速度、重力方向在机体坐标系的投影、关节角度与角速度、上一步动作、指令速度。把这些拼起来,Microduck 的观测向量维度大约在 34 到 40 之间。动作空间比较简单,就是 12 个关节的目标位置增量。策略网络输出的动作经过 PD 控制器转换成关节力矩,再驱动仿真中的刚体。

PD 控制器是连接强化学习策略和物理执行的关键桥梁。Microduck 关节的 PD 参数,刚度 Kp 我最后取的是 20 到 25,阻尼 Kd 取 0.5 到 1.0,这个量级在 25 厘米小型四足上是比较合理的起点。值得注意的是,PD 参数不仅影响真实机器人的跟随效果,在仿真里也会影响训练效率。Kp 太高会导致动作抖动和训练不稳定,Kp 太低则关节响应慢,策略学不会快速调整姿态。我的建议是先在仿真里单独调试 PD,让关节在阶跃指令下既不震荡也不迟缓,再固定住开始训练,训练过程中不要频繁改动。

动作空间还有一个容易被忽略的细节:动作平滑项。直接让策略输出目标位置会导致关节轨迹跳变,训练初期策略还没学好时特别明显。我在 reward 里加了 action rate 惩罚项,同时对动作做了指数平滑滤波,这样从 GPU 训练到实机部署,动作轨迹都不会出现过大的突变。

2.3 PPO 训练的核心参数与过程监控

算法层面我选的是 PPO,这是强化学习机器人领域的默认选项,稳定性和收敛速度的平衡最好。网络结构是两层 MLP,每层 256 个神经元,激活函数用 ELU。对于 Microduck 这种规模的策略,这个容量已经足够,再大反而容易过拟合仿真环境,部署时量化掉精度损失也会更明显。

PPO 的关键超参数,我踩完坑后推荐这样一组起点值:学习率 3e-4,训练轮数 num_steps 设 24 到 48,mini-batch size 在 4096 左右,PPO clip 系数 0.2,GAE lambda 0.95,折扣因子 gamma 0.99。这些参数在不同环境里不需要大改,真正要盯着的是 reward 曲线的形态。正常训练时,前 500 轮 reward 快速上升,中间进入平台期,偶尔会有小波动,但如果训练到了 2000 轮 reward 还在持续大幅震荡,通常是 reward 函数里有奖励黑客的漏洞,后面会专门讲。

训练过程的监控我习惯同时看三个东西:reward 曲线、episode 长度和策略输出的动作标准差。动作标准差如果下降到接近 0.05 以下,说明策略已经接近确定性,这时候如果 reward 还在涨,就基本可以准备导出模型了。训练完成的标准不是 reward 一定很高,而是人在可视化窗口里看步态自然、没有抖动和奇怪的侧移,仿真里走得稳才是真的稳。

2.4 关于 IQL 等离线强化学习算法的取舍

我看热词里有人提到 IQL 离线强化学习,所以多说一句我为什么最终没有选它。IQL 的优势在于可以利用已有数据集训练策略,不需要在线和仿真环境交互,数据效率高。但前提是你得有一份质量足够好的数据集,里面要覆盖各种状态、动作和对应的奖励信号。在 Microduck 项目里,我没有现成的优质轨迹数据,自己采样数据做离线训练又存在分布偏移的恶性循环:策略一旦输出数据集之外的动作,评估就会失真。

在线 PPO 虽然采样成本高,但它的策略在训练中会不断探索、不断自我修正,对 Microduck 这种没有现成数据加持的项目,收敛路径更可靠。如果你后续想用 IQL,建议的方向是用已经训好的 PPO 策略去采集一批高质量轨迹,把它们存成数据集,再拿去做离线强化学习微调,相当于老师和学生两步走。这个思路尤其在实机数据稀缺的场景下很有价值,可以作为后续迭代方向。

3. 从 PyTorch 到 RK3566:模型导出、量化与推理落地

3.1 把训练好的 PyTorch 模型导出为 ONNX

训练完成后,第一步是把 PyTorch 权重导出为 ONNX 格式。导出代码不长,但有几个地方需要注意。首先要固定输入输出的维度,ONNX 导出时不支持动态维度,我直接把 batch size 固定为 1,输入形状就是 (1, obs_dim),输出是 (1, 12)。其次要把模型切到 eval 模式并关闭梯度计算,否则导出的图里会残留训练相关的算子。

导出的时候还要留意算子兼容性。我踩过一个具体的坑:训练时用的是 GELU 激活函数,PyTorch 里导出 ONNX 时 GELU 会表示成几个基础 op 的组合,在 RKNN 工具链上支持的效率很低,转换时甚至会报错。解决办法是在训练脚本里就用 ELU 或 ReLU,或者导出前把模型里的激活函数替换成 ReLU,重新跑一次推理验证精度损失不大。对于 Microduck 的策略网络来说,ReLU 和 ELU 的最终策略表现差异并不大,但算子兼容性差异很大,优先选 RKNN 工具链原生支持好的算子才是务实的选择。

ONNX 导出后一定要先用 onnxruntime 跑一次推理,把输出和 PyTorch 推理结果对比,最大误差最好控制在 1e-5 量级。如果差值大了,说明模型里有算子导出时被改写了,要回到模型结构里排查。这一步是成本最低的“体检”,能帮你过滤掉后面 RKNN 转换阶段的很多奇怪问题。

3.2 RKNN 量化与 NPU 转换的实操细节

拿到 ONNX 后,下一步是用 RKNN-Toolkit2 转换成 RKNN 格式。RK3566 的 NPU 推理主要走 INT8 定点计算,所以转换时需要做量化校准。量化校准的核心是准备一批有代表性的输入数据,让工具统计每层激活值的数值范围,然后把浮点权重和激活映射到 INT8。

量化数据集不需要很大,但覆盖范围要广。我构建校准集的方法是:在仿真环境里随机采样 500 组观测向量,加上训练过程中保存的真实观测数据 500 组,混合后作为校准数据集。只采样随机噪声的话,校准后的模型在真实输入分布上精度损失会大很多,我第一次偷懒用随机噪声校准,部署后机器人在平地上走路都摇摇晃晃,换用混合校准集之后明显稳定。

量化后一定要跑精度评估。RKNN-Toolkit2 里有量化前后输出的对比工具,可以计算余弦相似度或均方误差。我的经验是,对于 Microduck 这个规模的策略网络,量化前后输出的余弦相似度能达到 0.999 以上,均方误差在 1e-3 量级,这个精度损失对控制策略来说是完全可以接受的。如果相似度掉到 0.99 以下,就要考虑混合量化,把对精度敏感的层保留为 FP16,其余层走 INT8。好在 RKNN 工具支持 per-layer 的量化配置,逐层排查起来不算麻烦。

3.3 板端交叉编译与推理程序结构

RK3566 上跑推理,我用了 RKNN C API 写推理程序,因为 C API 的实时性比 Python API 好,而且可以直接和电机控制代码集成。交叉编译环境用 RK 官方提供的 Linux SDK,也可以用 Docker 交叉编译镜像,然后通过 adb 推送到板子上运行。

推理程序的整体结构可以分成四层:

  • 初始化层:加载 RKNN 模型、设置 NPU 核心数、做一次 warmup 推理
  • 感知层:读取 IMU 和关节编码器数据,组装观测向量,做归一化
  • 推理层:把输入传给 NPU,等待输出,解析动作向量
  • 控制层:把动作经过 PD 控制计算力矩,输出给电机驱动器

这里有一个特别重要的细节:训练时做的观测归一化,部署时一定要复现。如果训练时用了 running mean 和 running variance 做标准化,那部署端要保存这对统计量,在推理前对原始观测做同样的 (x - mean) / std 变换。我第一次部署时忘了做观测标准化,模型输入分布和训练时完全对不上,策略输出的动作直接起飞,机器人刚站起来就摔了。这个问题的排查过程我记在了后面常见问题表里。

板端推理时还有一个常见的坑是内存对齐和带宽。RK3566 的 NPU 输入需要内存对齐到 64 字节,如果你的输入数据是 CPU 侧拼接的,要防止内存不对齐导致的推理失败。我用的方案是为输入和输出单独分配对齐内存,观测数据先 memcpy 到输入缓冲区,再调用 rknn_run,实测推理耗时稳定在 2 毫秒左右。

3.4 CPU 与 NPU 的分工和实时性保障

RK3566 上有 4 个 Cortex-A55 核心,NPU 独立于 CPU。我的分工方案是:CPU 核心 0 跑主控制循环和调度,核心 1 跑传感器读取,核心 2 跑日志和通信,核心 3 留作空闲和中断处理。通过绑核操作把关键线程固定在对应 CPU 上,避免系统调度造成的抖动。这样做的原因很简单,A55 单核性能有限,如果控制线程被其他任务抢占,哪怕只有几毫秒的延迟,机器人也会因为控制周期不规律而出现步态异常。

实时性方面还需要注意线程优先级。我在 Linux 上用了 SCHED_FIFO 实时调度策略,把控制线程的优先级设为最高。这个操作需要 root 权限,所以程序启动时用 sudo 运行。实测下来,控制循环的周期抖动可以控制在 0.2 毫秒以内,对 Microduck 这种 200 赫兹级别的关节控制需求来说绰绰有余。

NPU 推理和 CPU 控制之间用双缓冲来做数据交接:CPU 填好当前时刻的观测,NPU 开始计算,同时 CPU 可以处理上一帧的动作输出,这样推理链路无阻塞。这个设计对实时性的提升非常明显,属于做嵌入式机器人部署时很值得花时间优化的一步。

4. 实机部署与调参实录:从 RK3566 到 Microduck 站起来

4.1 泰山派识别到 RK3566 但显示 adb 设备的问题

很多人的 RK3566 板子是泰山派这类国产开发板,首次烧录系统后连接电脑,经常遇到“系统识别到设备,但设备类型显示为 adb device”而不是网口或串口的情况。这个现象本质上是板子进入了 adb 模式,USB 端口被 adb 服务占用,你没法通过网络 SSH 访问板子系统。

我当时用的解法是:先通过 adb shell 进入板子系统,然后执行 ifconfig eth0 查看网络配置,给板子配置一个固定 IP,并启动 SSH 服务。如果你的系统默认没开启 SSH,可以执行 service ssh start 或者 systemctl start sshd,然后在电脑端 ssh root@板子IP 进入系统。为了后续调试方便,我建议把 SSH 设为开机自启,网络配置为静态 IP,避免每次重启都要重新 adb shell。

如果你发现 adb devices 都看不到设备,先检查 USB 线是否支持数据通信,很多充电线只能供电不能传数据。再检查板子的 USB 启动模式拨码,泰山派一般会有启动模式选择,拨到错误档位会导致设备枚举不上。这个问题排查起来不难,但很多人第一次接触时会卡半天,尤其是拿到手就急着跑模型的场景。

4.2 强化学习遇到错误奖励:奖励函数隐藏陷阱

训练过程中最让人头疼的问题之一就是“错误奖励”,也就是策略找到了一个让 reward 虚高但实际上没有实现目标行为的漏洞。我在 Microduck 训练时遇到过两个典型的奖励黑客案例,值得拿出来说。

第一个案例:策略学会了原地高频抖动而不是向前行走。原因是我给前进速度奖励时,用的是 |vx - cmd_vx| 这种形式,策略发现通过快速抖动机身可以让平均速度恰好等于指令速度,从而获得高奖励。规避方法是改成对瞬时速度误差的负平方惩罚,并且增加动作平滑项约束,让策略不能通过高频动作刷分。

第二个案例:策略学会了把机身贴在地上拖着走。原因是奖励函数里对机身高度有正向奖励,但对机身姿态没有约束,策略发现躺倒在低摩擦地面也能获得很高的移动效率。解决思路是加入机身姿态惩罚项,把 roll 和 pitch 的偏差平方项计入负奖励,同时限制机身高度与期望高度的误差范围。

如果你训练时发现 reward 上涨但可视化步态非常诡异,优先检查这两类问题。我的经验是每次修改 reward 后,重新看一眼可视化行为,不要只看数据曲线。数据曲线可以骗人,可视化不能。

4.3 实机调试:从关节零点标定到控制参数微调

模型部署成功后,实机调试的第一步不是直接跑完整的强化学习策略,而是先做关节零点标定和单关节控制测试。Microduck 的每个关节在机械装配后,电位计或编码器的零点位置和 URDF 里的定义不一定一致,如果直接让策略输出目标角度,关节会直接跳到奇怪的位置。我的做法是先写一个开环程序,把 12 个关节分别控制到机械中位,记录此时编码器读数,把这个值烧录到配置文件里作为零点偏移。

接着做单关节转矩测试,给每个关节发一个固定的正弦位置指令,观察实际反馈是否跟随。这一步能暴露出电机方向反了、PWM 频率不对、驱动板限流过小等硬件问题。我记得有一次四个腿的髋关节方向接反了两个,导致策略输出左右摇摆的指令时,有两条腿朝反方向使劲,机器人直接原地转圈。通过单关节测试五分钟就定位到了,如果直接跑整体策略,排查难度会大很多。

最后才是加载完整策略。加载后先不给速度指令,让机器人从蹲伏状态慢慢站起来。这个阶段要重点观察机身是否水平、四条腿是否同时发力、有没有滞后抖动。如果站不稳,优先调 PD 的 Kd,增大阻尼通常能压住低频抖动;如果站立时嗡嗡响,那是 Kp 太高导致的高频振荡,适当降低 Kp 或者增加动作滤波强度。

4.4 常见问题速查表与排查技巧实录

我把这次部署中遇到的典型问题整理成一张表,方便大家对照排查。

现象可能原因排查与解法
推理结果异常、动作值超范围部署端没做观测标准化检查是否加载了训练时的 mean/std 统计量
机器人启动后摔倒PD 参数与训练时不一致核对仿真和实机的 Kp、Kd、关节限位范围
推理耗时超过 5ms模型过大或量化失效检查是否走了 NPU;用 rknn 的 profiling 功能定位耗时层
电机响应明显滞后控制线程被抢占或通信波特率过低绑核、提升线程优先级、提高 CAN 或串口波特率
步态抖动、动作不平滑动作滤波强度不足或 action rate 惩罚太小增加动作平滑系数,检查 reward 里 action rate 项
板子识别成 adb 设备USB 被 adb 模式占用adb shell 配置网络和 SSH,后续走网络调试
NPU 推理偶发失败输入内存未对齐使用对齐内存分配,或检查输入 buffer 是否跨页
量化后精度掉太多校准数据集分布不匹配用真实观测分布混合数据重新校准,考虑混合量化
电池续航太短电机 PWM 频率设置不合理检查 PWM 频率是否在驱动器推荐范围内,降低不必要的力矩输出

排查问题的核心思路是分层定位:从传感器到模型再到电机,每一层单独验证。最忌讳的就是直接把整条链路跑起来看现象猜原因,那样容易陷入“头痛医头、脚痛医脚”的循环。我在实机调试时习惯用简单的测试程序逐层打点,比如单独打印观测向量、单独验证关节角度映射关系、单独跑一次推理输出,每一步都确认没问题再接起来。

5. 部署完之后的进一步思考:我能改进什么

模型在 RK3566 上跑通、Microduck 能稳定行走之后,这个项目其实还没有结束。我之前提到 IQL 离线强化学习,现在有了实机策略,下一步完全可以采集一批实机数据,包括各种地面、各种扰动下的状态动作轨迹,构建自己的数据集做离线微调,让策略更贴近真实环境的行为分布。

另一个方向是模型结构的轻量化。现在的策略网络是两层 256 宽度的 MLP,在 RK3566 上跑 2 毫秒没有问题,但如果你想给策略增加更复杂的感知输入,比如视觉信息或者地形估计,那就要考虑把主干网络压缩到 128 宽度、使用深度可分离卷积之类的轻量结构,为感知模块腾出 NPU 计算预算。

还有一点是控制频率的权衡。目前我在 200 赫兹的控制频率下运行,NPU 推理只占用了不到一半的时间预算。如果你发现步态在某些地形下不够稳健,可以尝试把控制频率提到 400 赫兹,但要注意电机驱动器和总线带宽能否支撑,PD 参数也要相应调整。

回想整个过程,从训练环境搭建到 RK3566 实机部署,最深的体会是“仿真和实机的差距要在两端同时下功夫”。训练阶段要加足域随机化,部署端要把观测预处理、控制时序、驱动配置这些细节做扎实,两边对不上,模型再强也发挥不出来。另外一个深刻教训是数据流的一致性:从观测到动作,从浮点到定点,从仿真到实机,每一步都要有可对比的中间结果,这样排查时才不会抓瞎。希望这份手记能帮后来者少走我走过的弯路,让 Microduck 这类小型强化学习机器人的部署链路更顺畅。

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

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

立即咨询