这次不是在云端 GPU 上跑通强化学习,而是把 MicroDuck 的 RL 训练流程搬到了英伟达 Thor(NVIDIA Thor / Jetson AGX Thor)上。MicroDuck 是 Hugging Face 上非常有名的入门级开源强化学习项目,核心目标是让一只仿真 Duckiebot 小车在 Duckietown 城市环境中,通过 PPO 算法学会自主导航。它的训练成本被压得非常低,属于“几分钟跑一轮、网页上看曲线、模型传到 Hub”的轻量级 RL 例子,也因此成为很多人入门具身智能和深度强化学习的第一站。
这篇文章会围绕“在英伟达 Thor 上实现 MicroDuck 强化学习”这一主线展开。先看这个项目解决什么问题,再梳理核心能力、硬件门槛和使用边界,然后给出一套可落地的部署流程:环境准备、容器启动、训练脚本、效果验证、模型上传,最后补充资源占用观察、常见问题和批量调参建议。虽然示例以 MicroDuck 为主,但整套思路对其它 SB3 / Gymnasium 类强化学习项目同样适用。
1. MicroDuck 核心能力速览
| 能力项 | 说明 |
|---|---|
| 项目定位 | Hugging Face 上用于入门深度强化学习(RL)的微型自动驾驶训练示例 |
| 技术栈 | Stable-Baselines3、PPO 算法、Duckietown 仿真环境、Hugging Face Hub |
| 核心功能 | 在仿真城市环境中训练 Duckiebot 自主导航,训练完成后可保存并上传模型 |
| 运行平台 | CPU 可训练,GPU 可加速;英伟达 Thor / Jetson 平台属于典型端侧部署环境 |
| 显存需求 | 轻量级训练场景,具体显存占用需按模型版本、分辨率、批大小实测 |
| 启动方式 | 命令行启动训练脚本,可选 TensorBoard 观察训练曲线 |
| 是否支持 API | 项目本身以训练为主,不提供标准 HTTP API;训练产物可用于后续推理接口封装 |
| 是否支持批量任务 | 支持通过脚本循环执行超参数扫描、批量训练和模型版本管理 |
| 学习成本 | 低,适合首次接触 PPO 和具身智能的开发者 |
需要强调一点:MicroDuck 的“轻”是它最大的优势。训练环境是低分辨率图像仿真,策略网络也是图像输入的小规模 CNN,不需要像大语言模型那样依赖大规模 GPU 集群。从项目资源需求来看,它完全适合在边缘计算设备上做端侧训练和验证,这也是它能在英伟达 Thor 上跑通的价值点:不需要数据中心 GPU,一块嵌入式 AI 计算平台就能完成整个 RL 实验闭环。
2. 适用场景与使用边界
MicroDuck 适合四类人。第一类是刚开始学强化学习的开发者,想摆脱 CartPole 这类玩具环境,但能力还没到训练真实机械臂的程度,Duckietown 仿真刚好在两者之间。第二类是具身智能方向的科研工程人员,需要一个可复现、可二次开发的基础导航策略,Proto 训练之后再迁移到真实小车。第三类是关注端侧落地的工程师,想在英伟达 Thor 这类设备上验证“训练能不能跑、跑得稳不稳、推理性能够不够”。第四类是课程学习者,Hugging Face 的 Deep RL 课程里就有基于 MicroDuck 的实践作业,本地跑通意味着课程练习能完成闭环。
它不能解决什么问题也要说清楚。MicroDuck 解决的是“仿真环境下小型移动体的导航决策”,不是高保真工业级自动驾驶方案,也不是机械臂操作任务。Duckietown 仿真环境对真实物理世界的模拟有限,训练出来的策略直接部署到现实机器人之前,必须做域随机化、sim-to-real 迁移测试,不能默认“仿真里能跑,真车就一定也能跑”。
合规性同样需要注意。MicroDuck 本身是开放源码、开放模型权重,用于学习和技术验证没有问题,但要注意三点:一是如果要在商用产品中使用,需要核对 Hugging Face 仓库的 License 和 Duckietown 相关依赖的授权条款;二是如果后续加入真实环境数据、真实车辆信息或人脸行人图像,必须做好数据脱敏和隐私保护;三是涉及真实机器人部署时,要确保在安全隔离环境测试,避免造成人员或财产损失。
3. 环境准备与前置条件
在英伟达 Thor 上部署 MicroDuck,本质上是在 ARM + L4T 的嵌入式平台上搭建一套 Python 强化学习环境。由于 Thor 是 NVIDIA 自家的机器人计算平台,最常见的运行方式是通过官方 L4T 容器进入开发环境,这样能避免自己手动编译 PyTorch、CUDA 依赖的兼容性问题。
从通用准备角度,建议先确认以下检查项:
- 操作系统与驱动:Thor 设备已刷好 JetPack / L4T 系统,系统自带 NVIDIA GPU 驱动可正常工作。
- 容器或 Python 环境:推荐使用 NVIDIA L4T PyTorch 官方容器,容器内已经预装 PyTorch、CUDA 相关依赖。
- Python 版本:建议 Python 3.8 或更高版本,MicroDuck 依赖的 Stable-Baselines3、Gymnasium 都能在这个范围内运行。
- 磁盘空间:至少预留 10GB 左右空间,包含 PyTorch 容器、项目代码、依赖包和训练日志;如果还要保存多个模型权重,再多留 5GB 以上。
- 网络连接:需要访问 GitHub、Hugging Face Hub 或镜像站点来拉取代码和模型。
- 端口可用:如果使用 TensorBoard 或 Jupyter,需要确认 6006、8888 这类端口没有被占用。
进入容器后,第一件事是验证 PyTorch 是否能识别 GPU 设备。下面的命令在任何 L4T 容器里都能使用:
python -c "import torch; print(torch.__version__, torch.cuda.is_available())"如果输出True,说明 GPU 设备可达;如果输出False,不要急着装 MicroDuck,先检查容器是否启用 GPU 访问、驱动版本和 PyTorch 编译版本,否则后续训练会退化成 CPU 模式,性能差异会非常大。
还需要确认 Hugging Face Hub 的访问方式。国内访问 Hugging Face 有时不稳定,更稳妥的办法是通过环境变量指定镜像站点。下面这段配置适用于从 Hub 下载模型、数据集和上传训练结果:
export HF_ENDPOINT=https://hf-mirror.com把这一行写入容器的~/.bashrc或~/.profile,后续huggingface_hub的下载、上传请求都会走镜像地址,能在很大程度上避免模型下载超时、连接失败的问题。如果你的网络访问 Hub 本身没问题,也可以不设置镜像。
4. 在英伟达 Thor 上部署启动 MicroDuck
既然是在 Thor 上部署,最稳妥的路径不是直接在系统层用 pip 装一堆包,而是先进入官方 L4T PyTorch 容器,再在容器里创建独立训练环境。这样可以避免污染系统 Python,也方便随时删除重建。
先启动 L4T PyTorch 容器,将 MicroDuck 项目目录挂载到容器内:
docker run --runtime nvidia -it --rm \ -e HF_ENDPOINT=https://hf-mirror.com \ -v $(pwd)/microduck:/workspace/microduck \ nvcr.io/nvidia/l4t-pytorch:latest \ /bin/bash说明一下,$(pwd)/microduck是宿主机项目目录,容器内会映射到/workspace/microduck,实际路径需要按你的设备环境调整。镜像 tag 建议以官方仓库最新版本为准,这里用latest只是演示用法。
进入容器后,拉取 MicroDuck 项目代码并安装训练依赖。MicroDuck 依赖 Stable-Baselines3、Gymnasium、huggingface-sb3 等库,命令如下:
cd /workspace/microduck git clone https://github.com/huggingface/micro-duck.git . pip install --upgrade pip pip install stable-baselines3[extra] gymnasium huggingface-sb3 duckietown-sim # 如果环境提供 setup.py 或 requirements.txt,优先使用项目自带的安装方式 # pip install -e . 或 pip install -r requirements.txt这里的安装命令是通用模板,实际包名和版本请以 MicroDuck 仓库的 README 为准。跑完pip install之后,可以用一个最短训练命令验证环境是否完整:
python -c "from stable_baselines3 import PPO; print('SB3 OK')" python -c "import gymnasium; print('Gymnasium OK')"到这里,MicroDuck 的运行环境就搭好了。重点不是命令本身,而是思路:先在官方容器里确认 PyTorch + CUDA 正常,再在隔离环境里装 RL 依赖,最后用最小命令验证环境完整性。这样后续训练脚本出现问题时,能快速判断是环境问题还是业务逻辑问题。
5. MicroDuck 功能测试与效果验证
环境准备好之后,不要直接跑几百轮训练。先做一轮小规模训练,验证环境、算法、数据流和日志输出是否正常。这个思路和普通软件开发里的“冒烟测试”一致,也能避免因为一个小错误浪费几个小时的训练时间。
5.1 基础训练测试
先写一个最小训练脚本,使用 PPO + CNN 策略,在 Duckietown 环境里训练几万步。脚本逻辑参考下面的结构:
from stable_baselines3 import PPO from stable_baselines3.common.monitor import Monitor from stable_baselines3.common.vec_env import DummyVecEnv # 根据 MicroDuck 项目实际环境构造 Duckietown 环境 def make_env(): from duckietown_env import DuckietownEnv env = DuckietownEnv( map_name="udem1", max_steps=1000, camera_width=160, camera_height=120, domain_rand=False, ) return Monitor(env) env = DummyVecEnv([make_env]) model = PPO( "CnnPolicy", env, verbose=1, learning_rate=3e-4, n_steps=1024, batch_size=128, n_epochs=10, ) model.learn(total_timesteps=50_000) model.save("microduck_ppo_smoke_test") print("训练完成,模型已保存")这段脚本里做了两个重要决定:一是把 camera_width 和 camera_height 降到 160x120,这样显存占用和计算量都会小很多;二是只训练 5 万步,主要用于验证流程。判断训练是否正常的标准有三个:训练进程没有报错、终端能看到 rollout/eval 指标、模型文件能正常保存。
如果这一步报错,不要盲目增加训练量,先处理环境注册、依赖版本和 CUDA 可见性问题。常见的报错集中在DuckietownEnv导入失败、Monitor接口不匹配、CnnPolicy输入尺寸异常。这些都属于环境适配问题,和算法本身关系不大。
5.2 效果评估测试
训练不能只盯着 loss 数字,还要实际评估策略在环境中的表现。Stable-Baselines3 提供了evaluate_policy工具,可以指定评估轮数,统计平均回报和成功率指标:
from stable_baselines3.common.evaluation import evaluate_policy model = PPO.load("microduck_ppo_smoke_test") env = make_env() mean_reward, std_reward = evaluate_policy( model, env, n_eval_episodes=10, deterministic=True, ) print(f"评估平均收益: {mean_reward:.2f} ± {std_reward:.2f}")如果平均收益明显高于随机策略,说明模型已经学到一定导航能力;如果收益没有明显提升,也不能断言训练失败,可能是因为总步数太少、环境奖励设计稀疏或者超参数不理想。建议同时打开 TensorBoard,观察 reward、policy loss 和 entropy 三条曲线:
tensorboard --logdir=./logs --port=6006然后在浏览器访问http://localhost:6006,就能看到训练过程中每条曲线的变化趋势。曲线平滑上升、熵值稳定下降,是比较健康的训练信号;如果 loss 剧烈震荡或者 reward 长时间不涨,就该考虑降低学习率、增加 n_steps 或调整奖励函数。
5.3 模型上传 Hugging Face Hub
MicroDuck 的价值有一半在 Hugging Face 生态里,训练完的模型可以推送到 Hub,方便随时下载和对比版本。上传前先配置 HF 登录 token:
huggingface-cli login然后使用huggingface_sb3工具上传模型权重:
from huggingface_sb3 import push_to_hub push_to_hub( repo_id="你的用户名/microduck-ppo-smoke-test", filename="microduck_ppo_smoke_test.zip", commit_message="PPO training on NVIDIA Thor", )上传成功后,在 Hugging Face 网站的模型仓库里就能看到对应的权重文件。以后再在别的设备上部署,可以直接load_from_hub加载,不需要重新训练。
5.4 恢复训练与长训练测试
冒烟测试和短评估都通过之后,可以进入正式训练阶段。推荐继续使用之前的模型作为起点,而不是重新从随机权重开始:
model = PPO.load("microduck_ppo_smoke_test") model.set_env(env) model.learn(total_timesteps=500_000, reset_num_timesteps=False) model.save("microduck_ppo_final")长训练阶段要重点观察训练是否稳定、环境是否偶尔卡住、显存占用是否缓慢上升。如果中途断电或手动中断,也不要慌,SB3 每次保存 checkpoints 后都能从最近的权重恢复,只需要把PPO.load的路径改成最新的 checkpoint 文件即可。
6. 训练结果的接口封装与批量任务
很多人关心一个问题:MicroDuck 训练完能不能提供 API?严格来说,MicroDuck 是训练项目,没有自带 REST API,但训练完成的策略可以通过 Stable-Baselines3 快速封装成一个推理函数,再借助 FastAPI 暴露成 HTTP 服务,供上位机或其它模块调用。
下面是一个最小推理服务的示例思路,接住图像输入,返回动作:
from fastapi import FastAPI import numpy as np from stable_baselines3 import PPO app = FastAPI() model = PPO.load("microduck_ppo_final") @app.post("/predict") async def predict(data: dict): # data["image"] 为 Duckietown 仿真环境输出的图像数组 obs = np.array(data["image"], dtype=np.float32) action, _ = model.predict(obs, deterministic=True) return {"action": action.tolist()}启动服务:
uvicorn api_server:app --host 0.0.0.0 --port 8000这样就把 RL 训练产物变成了一个可调用的推理接口。需要注意,这只适合在可信内网环境中开启,如果挂到公网,必须增加鉴权、限流和输入校验,否则任何人都能调用你的模型服务消耗设备资源。
批量任务同样是强化学习实验里的高频需求。MicroDuck 训练一轮很快,非常适合做超参数网格扫描。可以用 Python 脚本批量训练多个种子下的不同配置:
import itertools import subprocess param_grid = { "learning_rate": [1e-4, 3e-4, 1e-3], "n_steps": [1024, 2048], "seed": [0, 1, 2], } keys = list(param_grid.keys()) for values in itertools.product(*param_grid.values()): config = dict(zip(keys, values)) tag = f"lr{config['learning_rate']}_nsteps{config['n_steps']}_seed{config['seed']}" print(f"启动训练: {tag}") subprocess.run([ "python", "train.py", "--learning_rate", str(config["learning_rate"]), "--n_steps", str(config["n_steps"]), "--seed", str(config["seed"]), "--tag", tag, ])批量扫描的关键不只是把训练脚本跑多份,而是要在训练脚本里把日志、模型权重、TensorBoard 输出全部按照队名/参数/种子/时间戳的目录结构存放,保证每个实验的结果可回溯、可对比。否则批量任务跑完,数据堆成一团,根本没法分析。
另一个工程重点是失败重试。真实批量任务里,单次训练很可能因为网络波动、显存峰值、日志写入冲突而中断,建议在脚本外层加入“失败保存日志 + 断点续跑”的逻辑。最简单的方式是每个子实验启动前先检查该实验的模型文件是否已经存在,存在就直接跳过,不存在才重新训练,这样批量任务可以随时重启而不浪费算力。
7. 资源占用与性能观察
在英伟达 Thor 上训练 MicroDuck,资源占用是大家最关心的问题,但这里不能给出一个“固定显存值”,因为占用取决于三个因素:仿真图像分辨率、模型批量大小、是否开启 TensorBoard 渲染和域随机化。更稳妥的方法是自己在训练过程中实测观察。
推荐在训练运行时,另开一个终端持续监控 GPU 状态:
# NVIDIA 平台通用 watch -n 2 nvidia-smi # Jetson 平台可用 sudo tegrastatsnvidia-smi能显示显存占用、GPU 利用率和功耗;tegrastats更适合记录嵌入式平台的 CPU/GPU 温度、频率、显存和内存占用。训练开始前记录一次空闲占用,训练中每 5 分钟记录一次,结束再记录一次,这样就能得到一条完整的资源使用曲线。
MicroDuck 的训练负载大头在仿真环境渲染和策略网络前向反向传播。如果你想降低资源占用,优先调整三处:第一,降低 camera 输入分辨率,从 640x480 降到 160x120,显存和计算量立刻下降;第二,降低n_steps和batch_size,这会减少单次 gradient update 的最大显存需求;第三,关闭不必要的渲染选项,比如draw_bbox=False、domain_rand=False,可以节省大量 CPU 和 GPU 开销。
CPU 推理和 GPU 训练的差异也需要理性看待。MicroDuck 初期环境验证阶段,CPU 训练是可以跑的,但迭代速度会比较慢。如果在 Thor GPU 上训练,主要加速点在于神经网络的卷积计算和梯度更新。实际项目中的建议是:小规模调试用 CPU 或低分辨率 GPU 模式,正式训练用 GPU + TensorBoard 监控,不要在 CPU 模式下跑长训练流程,否则容易把时间浪费在等待上。
如果训练过程中出现显存不足,优先做减法:降低分辨率、缩小 batch size、减少环境并行数量,而不是立刻换一台更高显存的设备。对于 MicroDuck 这种轻量级项目,绝大多数资源问题只需要调整参数就能解决,不涉及硬件升级。
8. 常见问题与排查方法
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 启动训练脚本报 ModuleNotFoundError | Python 环境缺少依赖 | 检查当前 Python 环境与安装命令 | 重装 stable-baselines3、gymnasium、huggingface-sb3 等依赖 |
| Hugging Face 仓库无法下载模型 | 网络连接 Hub 不稳定 | 检查能否访问 Hub,查看下载日志 | 设置HF_ENDPOINT=https://hf-mirror.com后重试 |
| PyTorch 报 CUDA 不可用 | 容器未启用 GPU 访问或驱动版本不匹配 | 运行python -c "import torch; print(torch.cuda.is_available())" | 使用--runtime nvidia启动容器,重新映射 GPU 设备 |
| 训练过程中显存不足 | 分辨率、批次大小或并行环境数过高 | 观察 nvidia-smi 显存变化 | 调低 camera_width/height、batch_size、n_steps |
| TensorBoard 页面打不开 | 端口被占用或服务未启动 | 检查浏览器访问地址与进程 | 换端口启动,或使用--host 0.0.0.0暴露服务 |
| DuckietownEnv 导入失败 | 仿真环境包版本与代码不兼容 | 查看 import 报错栈 | 按项目 README 要求安装指定版本 duckietown-sim |
| 训练 reward 一直不涨 | 超参不合适或训练步数太少 | 观察 TensorBoard 曲线 | 降低学习率、增加 n_steps、延长训练轮次 |
| 模型上传 Hub 返回 401 | 未登录或 token 无效 | 运行huggingface-cli whoami | 重新执行huggingface-cli login |
| 批量任务跑到一半中断 | 网络波动或日志写入冲突 | 查看子进程日志与退出码 | 加入断点续跑逻辑,模型文件存在则跳过当前实验 |
| 强化学习训练结果每次不同 | 种子未固定 | 检查初始化 seed | 在模型和环境初始化时设置相同 seed |
这里最容易被忽略的是“模型每次训练结果不一致”。强化学习本身带有随机性,环境初始化、网络权重初始化、动作采样都会引入不确定性,所以对比实验时一定要固定 seed,否则无法判断某一个超参改动是真的有效,还是仅仅因为运气好。建议把所有 seed 参数写进实验配置,和模型文件放在一起。
9. 最佳实践与使用建议
从 MicroDuck 初版跑通,到在 Thor 上做完整的强化学习实验,有几个工程习惯值得养成。
第一,第一次训练永远先跑小规模冒烟测试。哪怕总步数只有 2 万步,只要能在几分钟内跑完并保存模型,后面的长训练才有意义。把「先冒烟、再长训」固化下来,能省下大量反复排错的时间。
第二,把「环境依赖」和「项目代码」分开管理。项目代码放在挂载目录里,可以随时从宿主机编辑;依赖包、缓存、日志放在容器内或独立数据目录,避免因为重建容器导致重要实验结果丢失。
第三,模型文件、训练日志、TensorBoard 事件、上传脚本要分目录管理。推荐这样的目录结构:
microduck/ ├── checkpoints/ # 训练中间权重 │ └── microduck_ppo_smoke_test.zip ├── logs/ # TensorBoard 和运行日志 ├── scripts/ # 训练、评估、上传脚本 ├── configs/ # 超参数配置文件 └── outputs/ # 最终模型和推理结果第四,批量实验必须加日志、固定 seed、支持断点续跑。日志没有记录就等于没跑;seed 不固定就等于对比无效;不能续跑的批量任务,在长训练场景下会非常痛苦。
第五,注意 Hugging Face 账号和模型仓库的访问权限。如果只是个人学习,用私有仓库保存实验版本即可,避免公开仓库里堆积大量中间权重。涉及公司业务或真实数据的训练结果,更要严格控制上传范围,必要时只导出本地模型不和 Hub 同步。
第六,强化学习训练本身是计算密集型任务。在 Thor 上跑这种端侧实验,建议把不必要的图形界面、桌面服务、后台进程都关掉,减少非训练任务的资源争抢,这样训练指标的参考价值更高。
10. 总结与下一步
MicroDuck 在英伟达 Thor 上跑通强化学习,最有价值的点不是“刷了个 Demo”,而是验证了端侧设备具备完整 RL 训练闭环的能力:环境构建、PPO 训练、结果评估、模型上传、推理服务封装,全部在一台嵌入式 AI 平台上完成。对于想在具身智能、机器人导航方向深入的人,这条链路就是最小可行的实践路径。
建议第一步先验证最基础的训练流程,把官方示例跑起来;第二步调整 camera 分辨率、训练步数和学习率,理解每个超参对训练曲线的影响;第三步把模型上传到 Hugging Face Hub,在另一台设备上加载推理,走通“训练到部署”的完整链路;第四步再基于 Duckietown 地图做真实导航任务优化。
最容易踩的坑还是环境适配:Thor 上的 Python、PyTorch、仿真环境版本必须匹配,用官方 L4T 容器会省掉很多麻烦;Hugging Face 下载不稳定就配置国内镜像端点,不用蛮力重试。把这几个点处理好,MicroDuck 的强化学习实验基本一次就能跑通。后面如果要继续扩展,可以考虑把 Duckietown 里的策略迁移到真实小车上,这时要重点关注域随机化、sim-to-real 迁移测试和安全边界控制,不要直接把仿真权重放到真实设备上使用。