ARTICLE DETAIL

资讯详情

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

RK3566实机部署全记录:强化学习四足机器人从仿真到落地

RK3566实机部署全记录:强化学习四足机器人从仿真到落地 RK3566 实机部署全记录搞强化学习机器人这几年我算是在仿真里泡大的。训练用的卡从RTX 3090换到A100仿真环境从MuJoCo换到Isaac Gym跑出来的策略在渲染画面里一个比一个丝滑。但说句实在话把策略真正塞进一台25厘米的小机器人里让它在地板上自己站起来、走起来——这个坎我卡了将近两个月。Microduck这个名字玩四足机器人的朋友应该不陌生。一台25厘米级别、带12个自由度的小型四足机器人硬件上用的是RK3566这颗国产SoC作为主控。它的特别之处在于它不是一个纯粹的“舵机玩具”而是正经能跑端侧推理的强化学习验证平台。我这次要做的事情通俗点讲就是在英伟达GPU上完成一套强化学习训练把训练好的策略模型压缩、转换、部署到RK3566上让Microduck在完全离线的状态下靠自身的算力实时推理出运动控制指令。这个过程不是简单的“训练完导个模型”就能完事。它牵涉到仿真环境的搭建、策略蒸馏与量化、RK3566上的推理引擎适配、实机调试、稳定性调参每一步都有自己独特的坑。这篇文章我准备把我完整的部署手记整理出来给正在做类似工作的朋友一条能直接踩的路。1. 为什么偏偏是 RK3566微型四足机器人的算力选型逻辑先说清楚为什么Microduck不用更常见的树莓派也不用性能更强的Jetson系列而是选了RK3566。这个选型背后其实有一整套关于功耗、成本、生态和实时性的权衡逻辑。1.1 从英伟达生态到 Arm 平台的算力落差我在工作站上训练用的GPU是英伟达的A100和RTX 4090训练环境是标准的PyTorch CUDA 12.x Isaac Gym。而RK3566这颗芯片CPU部分是四核Cortex-A55主频最高2.0GHz集成的GPU是Mali-G52此外还有一个0.8 TOPS算力的NPU不过我们这次没有用到它。从A100到A55算力差距是数量级的。这意味着你在GPU上跑的浮点计算密集型网络绝无可能直接搬到RK3566的CPU上实时运行。这里牵扯到两个核心矛盾计算量训练时的策略网络可能包含上百个节点、上千个参数在GPU上毫秒级完成一次前向传播但在A55上可能需要几十毫秒甚至更久。实时性四足机器人的运动控制对时延极其敏感控制频率需要达到50Hz到100Hz也就是每10到20毫秒必须输出一组关节指令。如果推理时延超过控制周期机器人就会站不稳甚至摔倒。所以Microduck的部署工作本质上是一场模型瘦身推理加速的组合拳。我最终的目标很明确把训练好的策略网络压缩到推理时延低于10毫秒、内存占用低于50MB、精度损失控制在可接受范围内的状态然后塞进RK3566里跑起来。1.2 RK3566作为控制板的实际体验在实际调试过程中RK3566给我的整体感觉是“够用但不宽裕”。它的优势在于接口丰富原生支持CAN、UART、I2C、SPI、GPIO等连接舵机驱动板、IMU传感器极其方便。系统成熟可以跑Buildroot安卓和Debian社区生态在国内比较活跃遇到问题容易找到相关经验。成本可控单板价格远低于Jetson Nano批量采购优势更明显。但它也有明显的短板CPU单核性能一般A55核心适合并行度低但吞吐量大的任务对于单线程推理这种计算密集任务并不友好。没有成熟的GPU计算栈Mali-G52在图形渲染上还行但要用来做通用计算GPGPUOpenCL的支持和优化都很麻烦。NPU工具链封闭RKNN只支持特定格式的模型转换对本周这种自定义策略网络的转换兼容性不佳。试过几条路线之后我的结论是对于Microduck这种对时延敏感、模型量级又不大的场景最稳妥的方案是模型量化纯CPU推理。2. 仿真训练的关键决策用哪套环境、怎么训、怎么避免“仿真和实机差距过大”2.1 从Isaac Gym到MuJoCo仿真器的迁移理由我最早是在英伟达Isaac Gym里训练的原因很直接它支持GPU并行采样成千上万个环境同时跑训练速度极快。一套标准的RL训练流程在Isaac Gym里可能只需要两三个小时就能收敛换到纯CPU的MuJoCo可能要跑一个通宵。但问题在于Isaac Gym生态对“导出现有策略并部署到其他平台”这件事支持得并不好。它更多的是为研究和训练服务模型导出后的格式和工具链相对封闭。而我在Microduck上准备使用的控制框架底层的仿真环境是MuJoCo推理端用的是ONNX Runtime。所以我做了个关键的架构调整训练端保留Isaac Gym做RL探索和策略收敛利用GPU加速导出处在训练结束后把策略网络导出为ONNX格式验证端用MuJoCo加载同一个ONNX模型做轻量级的仿真验证、Domain Randomization测试并快速验证输出是否合理。这一步如果省略后边的部署会非常痛苦。因为Isaac Gym和MuJoCo对四足机器人模型的定义方式、坐标系、关节限位等细节并不完全一致你需要确保导出的策略在MuJoCo里也能跑出类似Sim的训练效果才能放心地进行下一步。2.2 训练细节与超参数笔记为了让大家能直接上手这里把我最终跑通的训练方案整理成表格包含关键超参数参数项数值说明算法PPO (Proximal Policy Optimization)稳定、可复现最适合机器人控制起步环境数量4096Isaac Gym并行采样用最大训练步数2000万大概跑2.5小时RTX 4090策略网络结构两层MLP隐藏层256x256输入168维输出24维激活函数ELU比ReLU更平滑利于控制值网络结构两层MLP隐藏层256x256与策略网络分开GAE lambda0.95优势估计的衰减系数学习率3e-4使用Adam优化器含学习率衰减clip范围0.2PPO裁剪范围训练奖励函数速度跟踪姿态稳定能耗惩罚需要根据实机反馈持续调整奖励函数是这中间最需要花心思的部分。我在初版训练中发现速度跟踪跑得很好但实机一测试Microduck原地打转、姿态飘忽。原因在于仿真下的速度跟踪等价于“整体位移”机器人可以利用对称的腿部摆动“磨洋工”。后来我在奖励里加入了身体朝向惩罚和关节加速度惩罚才勉强让策略学到真正有用的步态。2.3 Domain Randomization让策略从“仿真学霸”变成“实机可用”的关键仿真和实机之间的差距行业内叫Sim-to-Real gap。缩不缩这个差距直接决定了倒出来的策略是纸上谈兵还是能落地。我使用的具体做法是随机化地形摩擦系数从0.3到1.5之间随机采样模拟地毯、木地板、瓷砖等各种表面。随机化电机力矩系数让策略适应不同强度的电机反应。随机化传感器噪声在IMU读数、关节编码器读数上叠加高斯噪声。随机化初始状态让机器人从不同的初始姿态开始学习增强抗干扰能力。这套操作之后策略在MuJoCo仿真里表现出的状态非常稳定。就实际表现看同样的策略在未做Domain Randomization时实机测试不到5秒就会摔倒做了之后能稳定走起来。3. 策略导出与精简从 PyTorch 到 ONNX 的工程化链路训练好模型只是第一步真正麻烦的是怎么让这个模型在RK3566上高效跑起来。这一章节是整个部署过程的技术核心。3.1 网络结构固定与输入输出处理我的策略网络输入是168维向量包括机身姿态四元数4维机身角速度3维机身线速度3维关节角度12维关节角速度12维上一时刻的动作12维目标速度指令3维额外扩展的周期信号如相位等共119维输出是12个关节的目标角度增量或目标位置。这里有一个容易踩坑的细节ONNX导出时必须固定输入张量的shape。训练时我使用的是batch size为1但为了后续推理优化我保留了一个batch维度。ONNX Runtime在RK3566上对动态shape的支持不友好固定shape是最稳妥的选择。3.2 ONNX导出中的算子和精度坑PyTorch导出ONNX其实很简单几行代码就能完成import torch import torch.nn as nn # 假设 policy_net 是已训练好的策略网络 policy_net.eval() # 构造一个符合输入维度的示例张量 dummy_input torch.randn(1, 168) # 导出ONNX torch.onnx.export( policy_net, dummy_input, microduck_policy.onnx, export_paramsTrue, opset_version12, input_names[obs], output_names[action], dynamic_axesNone )看起来很简单对不对但真正导出来之后我遇到了两个问题算子兼容性问题训练时我用的是PyTorch的nn.ELU导出后ONNX会转成对应的ELU算子但ONNX Runtime在不同平台上对ELU的支持不太一样。RK3566的CPU推理时没有报错但在某些旧版本ONNX Runtime上会提示unsupported operator。精度丢失问题默认FP32精度导出在RK3566上推理时会有轻微的数值抖动影响关节输出平滑度。如果把模型量化为INT8精度损失会进一步放大。最终我的方案是导出ONNX时使用FP32但在RK3566推理时使用ONNX Runtime的FP32模式不做INT8量化。原因有二一是INT8量化对自定义网络结构需要额外的校准流程过程繁琐且不一定可靠二是Microduck的策略网络本身不大FP32模型文件大约只有800KBCPU推理速度完全来得及。3.3 模型输入的归一化操作别忘了这一层这是很多人部署时会踩的大坑。训练时我用了RunningMeanStd对输入状态做了归一化也就是把每个维度的输入从原始值转换到均值为0、方差为1的分布。这一步在仿真里是自动处理的但在导出ONNX时如果把归一化参数遗忘在模型外面实机推理的数据分布就和训练时不一致策略会瞬间失效。正确的做法有两种第一种把归一化操作作为网络第一层直接打包进ONNX模型推荐第二种在部署端侧单独实现归一化推理前先处理输入数据。我选择了第一种把归一化参数作为常量写入ONNX模型。这样部署在RK3566上的时候只需要喂原始输入模型内部自动完成归一化省去了一堆端侧代码也减少了出错概率。3.4 输出限幅与安全逻辑训练时策略网络输出的动作是未经限幅的原始值仿真器内部会对关节力矩和角度做限幅。到了实机上这个限幅必须由控制代码实现。我在ONNX输出之后加了这样一段逻辑把输出动作裁剪到[-0.5, 0.5]的关节角度增量范围叠加当前关节位置生成目标位置将目标位置通过CAN总线发给舵机驱动板。这段逻辑看起来简单但如果不做Microduck在启动瞬间会因为输出突变而冲向极限位置轻则损坏舵机重则翻车摔坏机械结构。4. RK3566 实机部署从系统到推理框架的完整搭建4.1 系统与交叉编译环境准备我用的开发板是Microduck官方搭载的RK3566核心板系统选了Debian 1164位。在开始部署之前需要准备RK3566开发板带WiFi模块用于远程调试USB转串口模块用于系统烧录和日志查看交叉编译工具链aarch64-linux-gnu-gccONNX Runtime AArch64版本Linux交叉编译环境搭建这里不展开直接上操作# 安装交叉编译工具链 sudo apt-get install gcc-aarch64-linux-gnu g-aarch64-linux-gnu # 下载ONNX Runtime源码或者直接使用预编译的AArch64版本 # 如果是预编译版本直接解压后拷贝到开发板如果你不想自己编译ONNX Runtime编译过程很耗时我在服务器上跑了40分钟可以直接用官方提供的AArch64预编译包。把libonnxruntime.so拷贝到开发板/usr/local/lib把头文件和可执行文件对应放好即可。4.2 推理代码的编写与优化推理代码的性能直接决定控制频率。我最终写了一套C推理程序主要逻辑分为三步读取传感器数据、组装输入向量、调用ONNX Runtime推理。核心代码简版如下#include onnxruntime_cxx_api.h #include vector // 初始化session Ort::Env env(ORT_LOGGING_LEVEL_WARNING, microduck_inference); Ort::SessionOptions session_options; session_options.SetIntraOpNumThreads(4); session_options.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_ALL); Ort::Session session(env, microduck_policy.onnx, session_options); // 获取输入输出名称 auto input_names session.GetInputNames(); auto output_names session.GetOutputNames(); // 组装输入张量 std::vectorfloat input_data(168); // ... 从传感器读取数据并填充 input_data ... Ort::MemoryInfo memory_info Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault); Ort::Value input_tensor Ort::Value::CreateTensorfloat(memory_info, input_data.data(), 168, input_shape.data(), input_shape.size()); // 推理 auto output_tensors session.Run(Ort::RunOptions{nullptr}, input_names.data(), input_tensor, 1, output_names.data(), 1); float* output_data output_tensors.front().GetTensorMutableDatafloat(); // output_data 即为12维关节动作增量有几个注意要点线程数设为4与RK3566的四核A55对应实测推理时延可以压到4-6毫秒。开启所有图优化ORT_ENABLE_ALLONNX Runtime会自动合并算子、消除冗余计算。内存分配每次推理重复使用同一块输入输出buffer避免反复malloc造成延迟抖动。实测下来RK3566上的FP32推理时延基本稳定在5毫秒左右控制频率可以达到100Hz以上完全满足四足机器人的实时控制需求。4.3 实机联调从趴着到站起来硬件初始状态是所有关节角度都处于零位。Microduck的站立状态需要所有腿弯曲到一定角度。控制程序里必须有一个**“上电复位”流程**先把所有关节的PWM占空比打到中位附近让舵机进入保持状态缓慢增加目标位置让机器人从趴着的状态过渡到站立姿态站立平衡1-2秒后再启动强化学习控制循环。如果跳过这个流程直接启动RL策略输入数据里的关节角度和角速度是零值但策略期望的是站立姿态的数据分布会导致输出指令突变机器人瞬间弹跳。实机联调的第一步我是这样做的先用一个简单的正弦波测试12个关节是否都能响应位置指令。确认没有问题后再用手托住Microduck的机架让策略运行观察输出是否合理。这个“托举测试”太重要了它能让你在没有落地的情况下就发现数据方向、符号是否反了。果然第一次托举测试就发现了问题左右腿的动作方向反了。原因是仿真器里左腿和右腿的关节方向定义与实际硬件相反导致同一个策略输出在左右腿上产生镜像效果。解决方法是修改部署代码里的关节映射表将仿真输出的关节顺序映射到实际硬件物理顺序上。4.4 传感器噪声与零漂处理Microduck使用的是IMU惯性测量单元我使用的是板载BMI088。实机上IMU的数据噪声比仿真里大得多直接喂给策略会导致动作抖动。处理办法对IMU角速度做低通滤波截止频率大约50Hz对IMU加速度和四元数用互补滤波融合得到稳定的机身姿态对关节编码器做平滑差分计算角速度而不是直接用原始差分噪声太大会导致速度估计抖动剧烈。这些信号处理代码虽然不起眼但它决定了策略实机表现的上限。我在最初部署时没有做这些滤波结果Microduck站在地板上就像在跳机械舞抖得厉害。加上滤波后站姿明显稳定了许多。5. 避坑实录部署过程中最折磨人的三个问题这部分是全文的重头戏把我在部署中踩过的坑一一拆解希望你能绕开。5.1 舵机力矩不够仿真里跑得好好的实机站不起来怎么办起初我以为仿真的策略可以直接上实机结果第一次让Microduck尝试站起来它腿部抖得厉害然后直接趴了下去。检查舵机电流发现输出力矩远达不到保持站姿的要求。原因有两方面控制频率不够我当时控制频率只有30Hz远低于仿真里的100Hz导致策略还没来得及响应机器人就已经失衡倾斜。舵机力矩限制仿真里用的理想力矩模型实际舵机在低速大扭矩场景会产生明显的力矩下降扭矩-速度特性曲线。解决方案把控制频率从30Hz提升到100Hz并优化CAN总线通信减少每帧的传输耗时给舵机增加软启动逻辑在启动阶段逐步增加PWM占空比避免舵机过流保护触发在策略输入中加入历史动作信息让策略能感知到当前舵机的实际响应程度。5.2 模型输出抖动FP32精度下关节指令微抖该怎么压住FP32推理已经很快了但实机测试中Microduck的姿态仍有小幅度的高频抖动尤其在单腿站立测试时特别明显。深入排查后发现问题出在策略网络对微小输入变化的敏感度上。因为在训练中加入了Domain Randomization策略网络对噪声的鲁棒性已经很强但部署端侧如果IMU数据有轻微波动策略仍会放大这些波动产生高频抖动。解决办法是加一级输出平滑smoothed_action 0.7 * previous_action 0.3 * raw_action;这个一阶低通滤波能有效压住高频抖动同时不会明显滞后输出。如果抖动仍然明显可以调整系数到0.85/0.15但注意不要过度平滑否则会拖慢响应速度导致机器人感觉“软绵绵”的。5.3 RK3566上ONNX Runtime推理速度慢怎么办如果推理时延超过15毫秒控制频率只能做到60Hz左右这可能导致不稳定。排查方法确认是否使用了正确的ONNX Runtime版本一定要用AArch64架构版本x86版本无法运行检查线程数设置SetIntraOpNumThreads改成4不要用默认值默认值可能只用到单核看模型是否有动态shape动态shape会触发不必要的重规划固定shape能明显提速检查模型算子某些PyTorch算子如aten::gelu在ONNX里效率较低换成nn.SiLU或tanh会好一些关掉CPU亲和性限制让进程可以使用所有核避免被系统调度到小核上。我最终在单核A55上测出FP32推理时延约7毫秒四核并行后稳定在4-5毫秒完全满足100Hz控制循环。6. 性能调优与稳定性验证让 Microduck 真正能跑起来6.1 推理时延测试的完整数据在RK3566上我用多次推理取平均值的方式测了不同线程数下的时延线程数FP32推理时延ms备注17.2CPU单核满载25.8有轻度提升44.6综合最优64.8线程切换开销显现补充模型文件大小约800KBFP32内存占用约3.2MB整个控制进程的总内存占用约15MB对RK3566来说非常宽裕。从表格可以明显看到线程数超过4后时延不再下降反而略有上升。这是因为A55核心本身不支持超线程线程过多时上下文切换的开销会吃掉性能收益。所以设定4线程就是最优值。6.2 稳定性测试场景与结果我做了三类稳定性测试静态站立测试Microduck 保持站立姿态10分钟记录机身俯仰角和横滚角的漂移情况。结果俯仰角稳定在正负1度以内横滚角稳定在正负1.5度以内满足预期。原地踏步测试在落地状态下执行原地踏步动作持续3分钟观察呼吸式漂移。结果策略输出稳定无异常跳变。抗扰动测试用手推Microduck的机架模拟外部冲击。结果策略能在0.5秒内恢复到稳定站立状态没有出现不可控摔倒。6.3 目标速度跟踪实地跑测最后的终极测试是让Microduck在Ruger地毯上按不同目标速度跑动。目标速度(cm/s)实测速度(cm/s)均方根误差(cm/s)姿态稳定性109.31.1稳定2018.71.8稳定3028.02.7轻微晃动4037.53.2轻微晃动理论上40cm/s的目标速度下Microduck已经跑到它机械结构所能支持的极限。如果想要更高速率需要重新设计腿部连杆比例或者增大舵机功率这不是算法层面能解决的问题。7. 手记之外训练到部署的整体方法论整套流程走下来我最大的体会是强化学习机器人部署最大的成本并不是训练而是搭建从仿真到实机的整套转换链路。我整理了一份自己的部署检查清单供后来者参考仿真环境一致性训练用的仿真器与验证用的仿真器尽量保持一致的物理参数和关节定义减少迁移难度归一化参数导出千万不要把归一化遗忘在模型外务必打包进ONNX固定输出处理限幅、滤波、映射必须作为标准模板固化下来实机托举测试先托着让策略跑起来观察输出方向与大小快速定位映射错误控制频率优先优先压推理时延再优化模型精度控制频率是稳定性基石数据记录调参在实机上记录输入输出数据离线回放分析比现场盲调效率高太多。这条链路最初我从零开始摸索用了近一个月才跑通。但一旦通了后面做不同地形、不同任务、不同算法的迁移速度快了很多。在写这篇手记时Microduck还在我桌面上安静地站着。我时不时用手推它一下看它踉跄几步然后重新站稳——每次看到这个场景还是会觉得当初那些熬到凌晨的排查和调试都值了。如果你也在做类似的部署工作希望这篇手记能帮你少走几条弯路。有问题欢迎交流我会尽量回复。
返回列表