2026/9/6 11:27:39

四足机器人强化学习从GPU训练到RK3566实机部署全流程解析

四足机器人强化学习从GPU训练到RK3566实机部署全流程解析 市面上聊四足机器人强化学习的教程不少但绝大多数都停在“用 Isaac Lab 训一个能跑的 policy”这一步。真正做过部署的人都知道从 GPU 上的仿真环境搬到 RK3566 这种边缘小板子上中间的坑一个接一个每一步都在挑战你对“能跑”的定义。这篇手记不是概念科普也不是跑通即止的 demo而是我完整走完 Microduck 25 厘米强化学习机器人从英伟达 GPU 训练到 RK3566 实机部署全流程的逐层记录。适合手里已经有一台四足机器人、想自己做 sim-to-real 迁移或者单纯想搞清楚“训练和部署到底差在哪”的开发者。1. 为什么是“GPU 训练 RK3566 部署”模型能训板子不一定能跑1.1 Microduck 和 RK3566 的定位Microduck 是一台 25 厘米级别的桌面四足机器人整体结构和常见的 12 自由度小狗类似但小尺寸意味着电机选型、关节刚度、整机重量分配都更敏感。它的定位从来不是“重型负载平台”而是“低成本、低门槛的强化学习验证平台”。选择 RK3566 作为部署端核心原因有三价格便宜整板成本远低于 Jetson 系列生态相对成熟瑞芯微的 RKNN-Toolkit2 和 rknn-toolkit-lite 能覆盖从模型转换到板端推理的全链路整机功耗低和 Microduck 这种小尺寸机器人很匹配。但代价很现实RK3566 的 NPU 算力只有 1 TOPS 左右INT8CPU 是四核 Cortex-A55主频最高 2.0 GHz。这个算力水平跑轻量分类模型绰绰有余跑一个带历史观测窗口的强化学习策略网络就需要在模型结构、量化方式、推理频率之间做取舍。训练时在 GPU 上怎么折腾都行部署时每一毫秒都是钱。1.2 从训练到部署的完整数据流我在实际项目中走的链路是这样的仿真训练阶段用英伟达 GPU这里用的是 RTX 4070跑 Isaac Lab 或 Legged Gym训练一个基于 PPO 的策略网络。输入是机器人当前的状态观测输出是 12 个关节的目标位置或力矩增量。这里的网络通常是一个 256x128x64 的 MLP输出经过 tanh 或线性层映射到关节范围。训练完成后导出 .pth 权重然后把 PyTorch 模型转成 ONNX再通过 RKNN-Toolkit2 转成 RK3566 能跑的 .rknn 模型。如果是 CPU 部署也可以直接转 ONNX Runtime 格式但实测下来 NPU 的 INT8 推理延迟优势明显。板端运行时RK3566 通过串口或 USB 与底层的电机控制板通信。推理出的关节目标通过协议下发到电机控制板电机控制板负责 PID 闭环机器人才能真正动起来。整个过程里强化学习模型只是“决策大脑”它输出的是目标位置不是 PWM 占空比。这里有个很多新手容易误解的点强化学习策略可以直接输出力矩也可以输出位置增量具体取决于你在训练时怎么设计 action 空间。Microduck 这类小机器人实机部署时更稳妥的是把 action 定义为关节目标位置的偏移量让底层 PID 去跟踪位置。这样即使策略输出有一点抖动底层 PID 和电机本身的惯性也会过滤掉一部分高频噪声。1.3 部署前先想清楚你的推理频率到底能做到多少训练时 PPO 的 rollout 频率不是关键指标但部署时推理频率直接决定控制效果。四足机器人的运动控制关节指令更新频率至少得 50Hz理想是 100Hz。低于 30Hz机器人基本走不稳因为底层电机在两次指令之间只能盲调。RK3566 在跑一个 256x128x64 的 MLP、输入观测维度在 50 左右时NPU 的 INT8 单次推理大概在 3 到 6 毫秒CPU 的 FP32 推理大约 8 到 15 毫秒。看起来都够 100Hz但别忘了整个链路的耗时还包括观测数据采集IMU 读取、关节编码器解析、预处理归一化维度转换、推理、后处理输出范围裁剪、平移变换、串口发送。全链路测下来NPU 方案能做到 60 到 80Hz 稳定运行CPU 方案容易掉到 40Hz 以下。所以我最终的方案选择是模型转 INT8 量化的 RKNN 格式跑 NPU同时把 CPU 上的进程拆了两个线程一个负责传感器采集和状态估计一个负责推理和指令下发。这种架构下实测能稳定跑到 80 到 90Hz。2. 训练侧的准备让策略网络一出生就适合部署2.1 仿真环境与训练配置Microduck 的训练环境我推荐直接用 Isaac Lab 或者 Legged Gym两者都是 NVIDIA 生态下的开源项目和 GPU 配合最好。我用的是 Isaac Lab 的 legged_robot 示例改的因为它的环境抽象比较干净替换 urdf 和配置就行。关键配置参数如下控制频率仿真内 200Hz策略推理 50Hz也就是说每 4 个控制步执行一次策略推理。最大步数默认 1000 步每 episode这个对训四足够用。奖励函数Legged Gym 里默认的包括线速度跟踪、角速度跟踪、姿态惩罚、关节力矩惩罚、动作平滑惩罚。我在 Microduck 上额外加了一个“末端轨迹平滑项”因为小机器人的腿短抖动影响特别明显。Domain Randomization摩擦系数 0.4 到 1.2 随机电机力矩增益 0.8 到 1.2 随机质量增减 ±20%。这一项至关重要直接影响 sim-to-real 的迁移成功率。这里有个值得强调的点仿真里把质量、摩擦都调成随机之后策略网络会自动学得更加鲁棒但代价是训练收敛变慢。如果用的 GPU 是 4070 这种中端卡建议先把 domain randomization 关掉确认策略能收敛到走路效果再逐步打开随机化。2.2 网络结构的选择MLP 偏置部署友好训练端最常用的策略网络是三层 MLP隐藏层 256、128、64。这个结构在 GPU 上训练很快收敛效果也够用而且部署到 RK3566 上时算子简单RKNN 转换几乎不会出问题。有段时间我尝试过在策略里加入 LSTM 或 Transformer 编码历史信息效果确实提升了一些比如更抗扰动、转向更平滑但代价是模型参数量翻倍RK3566 上 NPU 对 LSTM 的支持不够好只能退到 CPU 推理频率直接腰斩。如果一定要用历史信息建议只把最近 2 到 3 帧观测拼接到当前观测向量里而不是用循环网络结构。隐藏层大小也建议克制。128x64x32 和 256x128x64 在 RK3566 上的推理延迟差距大概有 1.5 到 2 倍而训练效果差异在实机上很难感知。我最终用的是 256x128x64主要是为了保留一点量化精度损失后的余量。2.3 训练中的三个经验privileged 信息、奖励权重、终止条件训练部署到实机能用的策略有几个点必须提前考虑第一尽量用 privileged learning 思路也就是训练时用仿真完全状态部署时只用部分观测。具体做法是先在完整状态上训一个 teacher 策略再训一个只依赖机器人自身传感器信息的 student 策略去模仿它。这样做的好处是student 策略自动学会了从观测中提取关键信息比单纯裁剪输入维度更鲁棒。第二奖励权重里一定要给“动作平滑”足够的分量。部署时你会发现GPU 上训练过拟合的策略输出会有肉眼可见的抖动实机上表现为电机嗡嗡响、机身发抖。动作平滑项权重设 0.01 到 0.05 之间能明显缓解再加一个“关节加速度惩罚”效果更好。第三终止条件里不要只设跌倒检测。把“关节超限”和“机身长时间悬空”都算作终止否则策略会学到很多偷懒行为比如躺着不动、反复蹭地这类行为在部署时表现会很差。2.4 部署前先在仿真里“预演”一遍部署环境这一步很多人忽略但我建议一定要做。在导出模型前先把训练好的 checkpoint 放回仿真环境里关闭 domain randomization使用和部署时相同的观测向量和 action 映射跑一圈看看输出曲线是否平滑步态是否稳定。我在这一步发现过一个很隐蔽的问题训练时观测维度顺序和部署时不一致。训练代码里列的是 [线速度、角速度、姿态角、关节位置、关节速度]部署代码里图方便排成了 [姿态角、线速度、角速度、关节位置、关节速度]。按理说等价的输入对应等价的输出但模型本身对特征顺序敏感特征顺序一变输出就完全乱了。这个 bug 在仿真里没发现因为仿真端用的也是同一套预处理代码上了实机才暴露出来排查了很久。3. 从 PyTorch 到 RKNN模型导出每一步的细节3.1 PyTorch 转 ONNX导出不是一条命令的事训练好的模型是 .pth 权重转 RKNN 前必须先转成 ONNX。这一步常见的坑有三个动态轴、opset 版本、以及 PyTorch 算子的 ONNX 兼容性。我的导出脚本长这样import torch import torch.nn as nn class PolicyMLP(nn.Module): def __init__(self, obs_dim48, act_dim12): super().__init__() self.net nn.Sequential( nn.Linear(obs_dim, 256), nn.ReLU(), nn.Linear(256, 128), nn.ReLU(), nn.Linear(128, 64), nn.ReLU(), nn.Linear(64, act_dim), ) def forward(self, obs): return self.net(obs) model PolicyMLP() checkpoint torch.load(model.pt, map_locationcpu) model.load_state_dict(checkpoint[model_state_dict]) model.eval() dummy_input torch.randn(1, 48) torch.onnx.export( model, dummy_input, policy.onnx, export_paramsTrue, opset_version11, input_names[obs], output_names[action], dynamic_axes{obs: {0: batch_size}, action: {0: batch_size}}, )这里必须强调一点opset_version 推荐 11不要用太高比如 17、18。RKNN-Toolkit2 对高版本 ONNX 的算子覆盖并不完美遇到不支持的算子你会多花很多时间。RNN 场景涉及动态轴但这里 MLP 模型推理时 batch_size 恒为 1所以其实可以干脆把 dynamic_axes 去掉输入输出都固定形状进一步降低转换风险。导出后一定要用 onnx.checker 验证一下再用 onnxsim 做一次简化python -m onnxsim policy.onnx policy_sim.onnxonnxsim 会把一些重复的节点合并把常量折叠出来的模型更干净RKNN 转换时通过率更高。3.2 RKNN-Toolkit2 转换流程与量化细节环境准备阶段在 PC 上安装 RKNN-Toolkit2官方建议 Python 3.8 到 3.10我用的是 Ubuntu 20.04 Python 3.8没问题。转换脚本核心部分如下from rknn.api import RKNN rknn RKNN() rknn.config( mean_values[[0, 0, 0]], std_values[[1, 1, 1]], target_platformrk3566, quantized_dtypew8a8, ) rknn.load_onnx(modelpolicy_sim.onnx) rknn.build(do_quantizationTrue, dataset./quant_data.txt) rknn.export_rknn(policy.rknn)量化这一步是部署成败的关键。PyTorch 模型权重是 FP32RK3566 的 NPU 优势在 INT8所以量化是必经之路。量化需要准备一个校准数据集dataset 文件里每行是一个 npy 文件路径这些 npy 里存放的是从仿真或实机采集的观测向量样本。收集校准数据时我建议从训练环境里随机采集 500 到 1000 条观测向量覆盖站立、行走、转向、被推倒等各种状态。数据要足够杂不然量化后的模型在少见的观测上可能输出异常。另外要注意RKNN-Toolkit2 的量化默认是全 INT8如果发现量化后模型在关键动作上精度损失明显可以在 config 里打开混合量化quantized_dtypew8a8 保持全量化后面用optimize_level调整或者对部分层关闭量化。我实测下来 256x128x64 这种小网络全 INT8 量化损失基本可控延迟从 FP32 的 8 到 10ms 降到 3 到 5ms收益很大。3.3 板内仿真模拟转换完先别急着上板RKNN-Toolkit2 提供了 PC 端模拟功能可以先把 .rknn 模型加载用模拟器跑一遍看输出是否正常。这一步虽然没有实机参考价值但能提前暴露算子兼容性问题。rknn.init_runtime(targetNone) outputs rknn.inference(inputs[obs_numpy]) print(outputs)把输出的 action 和 PyTorch 模型直接 forward 的结果对比一下如果误差在 ±0.1 以内说明量化正常。如果某个维度偏差特别大优先检查该维度对应的观测分量是否在量化数据集里有足够覆盖。4. RK3566 实机部署推理框架与稳定运行4.1 板端系统与运行环境准备RK3566 我用的系统是 Ubuntu 22.04第三方移植镜像内核支持 NPU 驱动。部署前需要确认三件事NPU 驱动环境是否正常也就是/dev/rknpu节点要存在系统里能正常调用rknn-toolkit-lite或者 RKNN RuntimePython 环境是 3.8 到 3.10配合板端对应的 rknn runtime 版本。瑞芯微官方提供了两套部署方式一是完整版 RKNN-Toolkit2 直接跑在板端但这个对板端内存要求高不建议二是 RKNN Runtime rknn-toolkit-lite 的轻量部署方案这才是实机部署推荐路线。只需要把转换好的 .rknn 模型文件和 rknn-toolkit-lite 的 Python 库拷贝到板子上就能用。注意板端的 rknn runtime 版本一定要和 PC 端 RKNN-Toolkit2 版本对应。比如 PC 端用的 1.6.0板端就要装 1.6.0 的 runtime版本不匹配会直接报RKNN_ERR_MODEL_INVALID或者加载失败这类问题排查起来很烦但通常就是版本对不上。4.2 推理框架RKNN 驱动 NPUONNX Runtime 保底我前期做过一个小测试分别用 ONNX RuntimeFP32和 RKNN RuntimeINT8在同一条输入上跑同一版模型对比延迟和输出误差。结果如表所示推理方式平均延迟最大延迟输出最大偏差RKNN Runtime (INT8, NPU)3.8 ms5.1 ms0.12RKNN Runtime (FP32 模拟, NPU)9.4 ms11.2 ms0ONNX Runtime (FP32, CPU)11.6 ms14.9 ms0输出最大偏差是按 single batch 多组输入对比计算的INT8 的量化误差在 0.1 左右对于机器人关节控制这类场景完全能接受。关键是 INT8 在 NPU 上的延迟优势非常明显几乎能省出五到六成的时间给其他环节。另外我保留了一个 CPU 侧的保底方案把同一个网络导出为 ONNX在板端用 ONNX Runtime 跑作为 fallback万一 NPU 驱动出问题机器人不至于完全瘫掉。开发调试时我也会用这个方案来交叉验证 RKNN 输出的正确性。4.3 控制频率与链路设计80Hz 全链路如何做到部署架构方面我最终采用了双进程方案进程 A传感器采集与状态估计。负责读取 IMU用 I2C、读取关节编码器通过串口与电机控制板通信计算出滚转角、俯仰角、关节位置和关节速度组成观测向量。这个进程固定 100Hz 循环。进程 B策略推理与控制指令下发。从共享内存里取最新观测向量做归一化喂给 RKNN 推理得到 12 维动作经过逆映射和裁剪后通过串口下发给电机控制板。这个进程跑 100Hz 循环但由于准备数据有时延实际稳定 80 到 90Hz。为什么用双进程而不是双线程因为 GIL 的存在Python 的多线程在 CPU 密集型任务上不能真正并行。而 RKNN Runtime 底层的 NPU 调用会释放 GIL受限于 Python 的执行模型同一时刻还是只能有一个 Python 字节码流在跑。双进程虽然没有共享内存那样天然数据同步但可以做到真正的并行执行。进程间用multiprocessing的共享内存模块传递 ndarray延迟只有几十微秒完全不影响控制频率。两个进程各自挂了定时器做周期同步经过长时间运行测试没有出现明显的漂移。4.4 从模型输出到电机动作最后一米的映射逻辑训练时的 action 定义决定了部署时的输出映射。我用的 action 定义是目标关节位置的偏移量也就是策略输出的 action 加上当前关节位置才是控制板要执行的目标位置。部署端的核心代码逻辑如下# 假设 rknn 已初始化obs 已拼好维度 48 obs np.expand_dims(obs, axis0).astype(np.float32) action rknn.inference(inputs[obs])[0].flatten() # 从训练配置反推 action 的映射范围和幅度 MOJO_SCALE 0.25 # 这个值在训练配置里设置部署保持一致 target_pos current_joint_pos action * MOJO_SCALE # 裁剪到关节物理限位 target_pos np.clip(target_pos, joint_lower, joint_upper) # 通过串口下发 cmd_frame pack_joint_command(target_pos, timestamp) serial_port.write(cmd_frame)这里最容易出错的地方是 MOJO_SCALE。训练时如果 action 缩放了 0.25那么部署时得到的 action 一定要乘回 0.25 再加到当前关节位置上一个环节漏了机器人要么动作幅度小到看不出在走要么直接抽搐。当年踩过这个坑实机上机器人一整条腿都在剧烈抖动排查了半天才发现是部署端忘了乘这个系数。此外输出给控制板的指令要做低通滤波。虽然训练时加过动作平滑项但在 80Hz 推理频率下还是会有少量高频抖动残留我在指令下发前加了一个一阶低通smoothed 0.6 * target_pos 0.4 * prev_target_pos prev_target_pos smoothed这个滤波对稳定性帮助很大代价是延迟增加一小节但完全在可接受范围内。5. 实机调试从原地爬不起来到走路稳定5.1 第一次上电先验证模型输出的方向对不对第一次把模型放到 RK3566 上、机器人上电我建议不要一上来就运行完整推理先做一次“开环方向测试”。具体做法是手动把机器人悬空记录当前关节位置作为默认姿态然后喂一个固定的观测向量观察模型输出的 action 是让关节向哪边动。这一步非常关键。因为强化学习模型不像传统控制它的输出的正负号、映射方式都是在训练时定义的搞反一个方向实机上机器人可能直接把自己扭成拧麻花。我当时上电后遇到的一个问题是模型输出的 action 方向是反的。训练时仿真里关节正转方向对应某条腿向前伸但实机上电机接线方向反了导致输出正动作时腿往后踢。解决方法是把控制板上对应电机的方向标记位改一下而不是去改模型权重。5.2 仿真与实机的差距摩擦、重心、电池电压仿真里设定摩擦系数 0.8真实地面可能是 0.5 或者更滑仿真里的电池电压恒定实机上随着电量下降电机力矩会逐步减弱。这些差距叠加起来最常见的现象是机器人走路时容易打滑尤其是快速转向时站立时轻微晃动某个腿偶尔虚力电池电量从满电到 3.7V 附近时步态明显变软甚至出现单腿拖地。针对这些问题我的处理思路是先在训练端把 domain randomization 调到更大范围摩擦 0.3 到 1.5再在部署端加一层输出修正也就是把 action 的幅度乘一个自适应系数电量低时系数放大一点补偿力矩下降。实时读取电池电压、动态调整系数相当于给训练好的 RL 策略加一个“外挂”的增益补偿实测下来效果很好满电和低电状态下步态差别小了很多。5.3 电机 PID 参数与控制周期强化学习策略输出目标位置底层还是要依赖 PID 环去跟踪。Microduck 用的电机控制板一般内置位置环和速度环需要自己调三个参数位置环 P、位置环 D以及速度环 P。我调参时的起点是参数起始值最终稳定值位置环 Kp0.10.05位置环 Kd0.0050.002速度环 Kp0.050.03RL 策略本身在关节空间输出目标位置如果底层 PID 太激进跟随速度过快反而会把 RL 策略里已经学好的平滑轨迹“吃”掉导致电机发烫和抖动。所以调试时我尽量让 PID 响应比策略输出平滑一点这样整体会更稳。控制周期方面我建议先用 50Hz 起步确认机器人能走再逐步提到 80Hz 以上。频率越高底层的 PID 压力越大电机发热越快但步行姿态也越自然。5.4 RK3566 上容易踩的“坑中坑”CPU 频率调度实测发现RK3566 默认的 CPU 调频策略是interactive或者schedutil而不是固定的高性能模式。推理进程的线程会被迁移到低频核上导致实际推理延迟比测试时高出一倍。这个问题很隐蔽因为单次测试时 CPU 会短暂拉高频但持续运行时频率会掉下来。解决方法是固定 CPU 频率上限把核心调到性能模式sudo cpupower frequency-set -g performance再把推理进程绑定到大核RK3566 有四个 A55 大核实际没有区分大小核但可以绑核避免迁移开销taskset -c 2,3 python deploy.py这个优化让推理延迟整整降了 30% 左右效果非常明显。6. 常见问题速查部署现场踩坑记录6.1 模型加载失败或初始化异常现象可能原因解决方式RKNN 模型加载失败报错RKNN_ERR_MODEL_INVALID板端 runtime 版本与 PC 端转换版本不一致两边版本对齐重新转换初始化耗时很久RK3566 的 NPU 驱动第一次初始化要加载固件属正常现象初始化放到启动流程最前面加载后推理结果全为 0 或 NaN输入数据格式不对shape 或 dtype 不匹配检查输入是否是 float32、是否 expand_dims与训练时代码一致6.2 实机运行时输出抖动这个我在前面已经提过几个原因归纳一下排查顺序先看原始模型输出在 PC 端用仿真数据跑一遍 PyTorch 模型输出平滑吗再看 RKNN 输出跑同一份数据对比 RKNN 推理和 PyTorch 推理偏差大吗然后看指令链路从 RKNN 输出到串口下发中间有没有截断、滤波或类型转换错误最后看电机反馈电机控制板返回的关节位置和指令有没有持续追踪误差6.3 量化后精度损失明显如果量化后的模型在某个关节上的输出偏差非常大不要先怀疑数据集不够。先看该分量对应的观测在量化数据集中是否覆盖了所有动态范围。比如行走时俯仰角主要在 -0.3 到 0.3 弧度之间波动如果校准数据里这个范围采样不足量化后的模型对极端俯仰角的响应就会失真。解决办法专门采集几组包含剧烈姿态变化的观测数据补充进 dataset 文件重新量化。6.4 CPU 比 NPU 快检查是不是量化没生效有时候会出现 CPU 推理反而比 NPU 快的情况多数原因是 NPU 没有真正运行 INT8 模型或者输入数据类型没有对齐导致 NPU 内部做了 data copy。检查方式很简单在 rknn.init_runtime 时的 log 里看输入输出的格式确认输入是 uint8 或 float32 对应量化配置、模型实际跑在 NPU 上日志里有 npu 字样。7. 板端性能实测与调试脚本记录7.1 推理延迟基准测试我写了一个简单的板端延迟测试脚本用于量化每一步耗时import time import numpy as np from rknn.api import RKNN rknn RKNN() rknn.load_rknn(policy.rknn) rknn.init_runtime(targetrk3566) obs np.random.randn(1, 48).astype(np.float32) # 预热 for _ in range(10): rknn.inference(inputs[obs]) times [] for _ in range(200): t0 time.perf_counter() out rknn.inference(inputs[obs]) t1 time.perf_counter() times.append((t1 - t0) * 1000) print(favg {np.mean(times):.2f} ms, max {np.max(times):.2f} ms)注意第一次推理通常会额外慢 1 到 2 毫秒是 NPU 上下文初始化的开销所以要预热。实测结果200 次推理平均 3.8ms最大 5.1ms换算成控制频率单个推理阶段能到 200Hz 以上但全链路受限于传感器采集和串口发送稳定在 80Hz 左右。7.2 板端状态记录回放工具调试强化学习部署最头疼的就是状态不可复现。我写了一个简单的“状态录制回放”工具在板端把每次的观测向量、推理输出、目标位置、实际位置、时间戳都存成 npz 文件。后期在 PC 端读取这些文件可以逐帧回放分析每一步机器人执行的情况。import numpy as np def record_step(timestamp, obs, action, target_pos, actual_pos, file_pathtrace.npz): data { timestamp: timestamp, obs: obs, action: action, target_pos: target_pos, actual_pos: actual_pos, } # 用追加方式存或者缓存后整体写入有了回放数据遇到实机表现异常时就能快速定位是策略本身的问题还是推理噪声、指令丢失、机械结构松动的问题。7.3 安全调试遥控急停和本地开关这一点必须放在靠后的位置说因为只有真正上了实机才会明白安全机制多重要。RL 策略输出的动作范围如果没限制好可能让机器人做出超出机械极限的动作轻则关节咔咔响重则舵机扫齿。我加了两层保护软件层每次指令下发前检查目标位置是否在关节限位内超出就直接截断硬件层保留一个物理急停开关直接切断电机电源防止极端情况。另外第一次实机测试时我的建议是找人用手托着机器人抬离地面 1 到 2 厘米让电机空转但不承受体重。确认步态动作正常后再落地。这个“悬空测试”能避免机器人一落地就因为策略异常乱窜撞坏东西。8. 多环境负载联合调试把训练和部署串成一条链路8.1 训练机、实机、PC 端三端协同部署调试期间我的工作环境通常有三台设备同时工作英伟达 GPU 的训练机负责训模型、RK3566 实机负责跑模型、PC 端开发机负责代码编辑和远程调试。为了减少来回拷贝我把三端用局域网连通在训练机上训练完成后通过 scp 把导出的 ONNX 和 RKNN 模型直接推送到 RK3566省去 U 盘倒腾的步骤。scp policy.onnx rk3566192.168.1.100:~/deploy/ scp policy.rknn rk3566192.168.1.100:~/deploy/这样模型更新流程只需要几秒钟实机测试一失败马上回训练机调参重训再推模型整个迭代速度提升很多。8.2 日志采集与周期性问题定位板端长时间运行后偶尔会出现“起步正常但跑一阵后步态失衡”的问题。这类问题靠肉眼很难抓必须在板端持续记录日志。我在部署代码里加了一个循环缓冲区保存最近 2000 条推理日志出问题后一键导出。分析后经常发现问题出在某个时间段 IMU 读数异常比如 I2C 总线时序问题导致姿态估计跳变进而让策略输出大幅度动作。8.3 训练参数的小迭代建议最后给一个实用的迭代策略不要在 RK3566 上反复调部署参数把精力集中在训练端。部署端能调的只有 PID、滤波系数、推理频率这些参数调节范围有限。真正影响机器人动态表现的是训练时的奖励函数、网络结构、观测维度。尽量把训练端和部署端的调试节奏分开每天固定一到两轮完整迭代每一轮都记录模型参数和实机表现这样才能有效收敛。9. 部署性能提升的进阶玩法剪枝、算子替换与多模型切换如果你觉得 3.8ms 不够快或者想在 RK3566 上跑更复杂一点的策略可以考虑几个进阶方向。第一通道剪枝。把隐藏层从 256 剪到 192甚至 160推理延迟会明显下降但精度损失需要实测。剪枝的工具链在 PyTorch 侧有 torch.nn.utils.prune剪完重新训练几百步回血再导出 ONNX 转换 RKNN。第二算子替换。某些算子比如 LayerNorm在 RKNN 上支持不理想时会退到 CPU导致 NPU 推理变成“CPU 的半吊子”。操作方法是把 LayerNorm 换成 BatchNorm因为 RL 策略的观测分布基本是稳态的BatchNorm 用推理时的 running stats 也能做。第三多模型切换。RK3566 的 NPU 加载两个模型是可以的只要显存够。可以训一个正常步态模型和一个转向/避障模型部署端根据遥控指令动态切换。我试过同时加载两个 256x128x64 的 INT8 模型NPU 内存占用大概 60MB 左右加上系统内存完全够。从英伟达 GPU 到 RK3566 实机这条链路其实是一个持续做减法的过程减算力、减精度、减延迟换来的是真实世界的行走能力。花两天时间调底层 PID不如花两小时回到训练端把网络结构和奖励调得更适合部署。算力不够不是做不了强化学习机器人的理由关键是让模型从一开始就为部署场景设计。