news 2026/9/11 18:43:52

microduck_rl(MuJoCo+PPO+WandB+UV训练机器人)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
microduck_rl(MuJoCo+PPO+WandB+UV训练机器人)
  • MuJoCo:
  • MuJoCo Warp:
  • mjlab:
  • rsl_rl:
  • PPO:
  • WandB:
  • UV:

一、win11安装wsl ubuntu环境

1. windows开启wsl,查看wsl版本: 启动或关闭windows功能,勾选[虚拟机平台]+[适用于Linux的windows子系统] 2. 管理员权限打开powershell,查看wsl版本 wsl --version 3. 更新wsl: wsl --update 4. 查看可安装的系统: wsl --list --online 5. wsl安装ubuntu2204: wsl --install -d Ubuntu-22.04 --location D:\WSL\ubuntu2204x64 6. 查看已安装的系统 wsl -l -v wsl --list --verbose 7. 启动指定系统 wsl -d Ubuntu-24.04 8. 设置默认启动系统 wsl --set-default Ubuntu-24.04 9. 卸载已安装系统 wsl --unregister Ubuntu-22.04 10. 关闭wsl wsl --shutdown 11. 备份wsl系统 wsl --shutdown wsl --export Ubuntu-22.04 D:\WSL_Backup\Ubuntu2204.tar 12. 恢复wsl系统 wsl --import Ubuntu-22.04 D:\WSL\Ubuntu-22.04 D:\WSL_Backup\Ubuntu2204.tar --version 2 1. 更新: sudo apt update && sudo apt upgrade 2. 检查wsl.conf存在 cat /etc/wsl.conf 3. 安装Ubuntu Desktop: sudo apt install ubuntu-desktop -y 4. 安装XRDP: sudo apt install xrdp -y 5. 启动XRDP: sudo service xrdp start 6. 查看IP ip addr 7. 主机远程登录ubuntu界面 mstsc

二、在wsl ubuntu环境使用GPU CUDA

三、训练步骤

Step 1:准备 Ubuntu + NVIDIA GPU nvidia-smi Step 2:安装 uv curl -LsSf https://astral.sh/uv/install.sh | sh Step 3:下载代码 git clone https://github.com/pollen-robotics/microduck_rl cd microduck_rl Step 4:安装依赖 //uv sync //uv sync -i https://pypi.tuna.tsinghua.edu.cn/simple uv sync -i https://mirrors.aliyun.com/pypi/simple/ //uv sync -i https://pypi.mirrors.ustc.edu.cn/simple/ Step 5:登录 WandB ./.venv/bin/wandb login 此处需要输入wandb key,需要登陆wandb官网登陆后创建key Step 6:查看任务 uv run list-envs Step 7:跑 smoke test uv run train Mjlab-Velocity-Flat-MicroDuck \ --env.scene.num-envs 64 \ --agent.max_iterations 5 Step 8:正式训练 uv run train Mjlab-Velocity-Flat-MicroDuck \ --env.scene.num-envs 4096 查看训练过程:uv run play Mjlab-Velocity-Flat-MicroDuck --checkpoint-file /home/chunyangzhang/microduck_rl-develop/wandb/run-20260910_020602-unkowd3z/files/model_250.pt --viewer viser 在浏览器查看:http://localhost:8080/ 官方给出的经验是,4096 environments 下,得到一个可用 gait 大约需要 1–2 小时,具体当然取决于 GPU。 Step 9:查看训练结果 uv run play \ Mjlab-Velocity-Flat-MicroDuck \ --wandb-run-path <entity/project/run_id> Step 10:导出 uv run scripts/export.py \ Mjlab-Velocity-Flat-MicroDuck \ --wandb-run-path <entity/project/run_id> 得到: output.onnx Step 11:CPU仿真验证 uv run scripts/infer_policy.py \ --walking output.onnx Step 12:再考虑部署到真实 Microduck output.onnx ▼ Microduck policy/runtime ▼ robotd ▼ 真实机器人

四、创建自定义动作

五、创建自定义训练任务

六、创建自定义机器人

七、PPO训练详解(结合cuda+GPU/GPGPU)

以下是一个倒立摆使用Pytorch编写的PPO训练及推理代码,与microduck_rl不同,microduck_rl使用MuJoCo Warp+PPO框架,这个示例直接编写代码,但原理一样,作为参考:
train.py:

import gymnasium as gym import numpy as np import torch import torch.nn as nn import torch.optim as optim from torch.distributions import Categorical # ============================================================ # 1. Actor-Critic 网络 # ============================================================ class PolicyNetwork(nn.Module): def __init__(self, state_dim, action_dim): super().__init__() # Actor 和 Critic 共享的特征提取网络 self.shared = nn.Sequential( nn.Linear(state_dim, 64), nn.Tanh(), nn.Linear(64, 64), nn.Tanh() ) # Actor self.policy_head = nn.Linear(64, action_dim) # Critic self.value_head = nn.Linear(64, 1) def forward(self, x): feature = self.shared(x) logits = self.policy_head(feature) value = self.value_head(feature).squeeze(-1) return logits, value # 获取 action、log_prob、value def get_action_and_value(self, x, action=None): logits, value = self.forward(x) dist = Categorical(logits=logits) if action is None: action = dist.sample() log_prob = dist.log_prob(action) entropy = dist.entropy() return action, log_prob, entropy, value # ============================================================ # 2. RolloutBuffer # ============================================================ class RolloutBuffer: def __init__(self): self.states = [] self.actions = [] self.rewards = [] self.dones = [] self.values = [] self.log_probs = [] def clear(self): self.states.clear() self.actions.clear() self.rewards.clear() self.dones.clear() self.values.clear() self.log_probs.clear() # ============================================================ # 3. GAE # ============================================================ def compute_gae( rewards, values, dones, next_value, gamma=0.99, lam=0.95 ): advantages = np.zeros(len(rewards), dtype=np.float32) last_gae = 0.0 for t in reversed(range(len(rewards))): if t == len(rewards) - 1: next_non_terminal = 1.0 - dones[t] next_val = next_value else: next_non_terminal = 1.0 - dones[t + 1] next_val = values[t + 1] delta = ( rewards[t] + gamma * next_val * next_non_terminal - values[t] ) last_gae = ( delta + gamma * lam * next_non_terminal * last_gae ) advantages[t] = last_gae returns = advantages + np.array( values, dtype=np.float32 ) return advantages, returns # ============================================================ # 4. PPO Update # ============================================================ def ppo_update( model, optimizer, states, actions, old_log_probs, advantages, returns, clip_eps=0.2, value_coef=0.5, entropy_coef=0.01, epochs=4, batch_size=64 ): # Advantage标准化 advantages = ( advantages - advantages.mean() ) / (advantages.std() + 1e-8) dataset_size = states.shape[0] for _ in range(epochs): indices = torch.randperm(dataset_size) for start in range(0, dataset_size, batch_size): end = start + batch_size batch_idx = indices[start:end] batch_states = states[batch_idx] batch_actions = actions[batch_idx] batch_old_log_probs = old_log_probs[batch_idx] batch_advantages = advantages[batch_idx] batch_returns = returns[batch_idx] # 当前策略重新计算 _, new_log_probs, entropy, values = ( model.get_action_and_value( batch_states, batch_actions ) ) # PPO ratio ratio = torch.exp( new_log_probs - batch_old_log_probs ) # PPO clipping surr1 = ratio * batch_advantages surr2 = torch.clamp( ratio, 1.0 - clip_eps, 1.0 + clip_eps ) * batch_advantages policy_loss = -torch.min( surr1, surr2 ).mean() # Value loss value_loss = ( (values - batch_returns) ** 2 ).mean() # Entropy entropy_loss = entropy.mean() # 总Loss loss = ( policy_loss + value_coef * value_loss - entropy_coef * entropy_loss ) optimizer.zero_grad() loss.backward() torch.nn.utils.clip_grad_norm_( model.parameters(), 0.5 ) optimizer.step() # ============================================================ # 5. 导出 ONNX # ============================================================ def export_onnx(model, state_dim, filename): # 创建一个只包含 Actor 的网络 class ActorOnly(nn.Module): def __init__(self, policy): super().__init__() self.shared = policy.shared self.policy_head = policy.policy_head def forward(self, x): x = self.shared(x) logits = self.policy_head(x) return logits actor = ActorOnly(model) actor.eval() # 创建假的输入 dummy_input = torch.randn( 1, state_dim, dtype=torch.float32 ) # 导出 ONNX torch.onnx.export( actor, dummy_input, filename, input_names=["observation"], output_names=["logits"], dynamic_axes={ "observation": { 0: "batch_size" }, "logits": { 0: "batch_size" } }, opset_version=17 ) print(f"ONNX模型已保存:{filename}") # ============================================================ # 6. PPO训练 # ============================================================ def train(): device = torch.device( "cuda" if torch.cuda.is_available() else "cpu" ) print("Device:", device) env = gym.make("CartPole-v1") state_dim = env.observation_space.shape[0] action_dim = env.action_space.n print("State dimension:", state_dim) print("Action dimension:", action_dim) model = PolicyNetwork( state_dim, action_dim ).to(device) optimizer = optim.Adam( model.parameters(), lr=3e-4 ) buffer = RolloutBuffer() num_updates = 500 rollout_steps = 1024 for update in range(num_updates): buffer.clear() state, info = env.reset() episode_reward = 0 # ==================================================== # Rollout # ==================================================== for step in range(rollout_steps): state_tensor = torch.tensor( state, dtype=torch.float32, device=device ).unsqueeze(0) with torch.no_grad(): action, log_prob, _, value = ( model.get_action_and_value( state_tensor ) ) action_value = action.item() next_state, reward, terminated, truncated, info = ( env.step(action_value) ) done = terminated or truncated # 保存数据 buffer.states.append(state) buffer.actions.append(action_value) buffer.rewards.append(reward) buffer.dones.append(done) buffer.values.append( value.item() ) buffer.log_probs.append( log_prob.item() ) episode_reward += reward state = next_state if done: state, info = env.reset() episode_reward = 0 # ==================================================== # Bootstrap # ==================================================== state_tensor = torch.tensor( state, dtype=torch.float32, device=device ).unsqueeze(0) with torch.no_grad(): _, next_value = model.forward( state_tensor ) next_value = next_value.item() # ==================================================== # GAE # ==================================================== advantages, returns = compute_gae( buffer.rewards, buffer.values, buffer.dones, next_value, gamma=0.99, lam=0.95 ) # ==================================================== # NumPy → PyTorch # ==================================================== states = torch.tensor( np.array(buffer.states), dtype=torch.float32, device=device ) actions = torch.tensor( buffer.actions, dtype=torch.long, device=device ) old_log_probs = torch.tensor( buffer.log_probs, dtype=torch.float32, device=device ) advantages = torch.tensor( advantages, dtype=torch.float32, device=device ) returns = torch.tensor( returns, dtype=torch.float32, device=device ) # ==================================================== # PPO Update # ==================================================== ppo_update( model, optimizer, states, actions, old_log_probs, advantages, returns ) if update % 10 == 0: print( f"Update {update}/{num_updates}" ) # ======================================================== # 保存 PyTorch 模型 # ======================================================== torch.save( model.state_dict(), "cartpole_ppo.pt" ) print("PyTorch模型已保存:cartpole_ppo.pt") # ======================================================== # 导出 ONNX # ======================================================== export_onnx( model, state_dim, "cartpole_policy.onnx" ) env.close() # ============================================================ # 7. Main # ============================================================ if __name__ == "__main__": train()

run.py:

import gymnasium as gym import numpy as np import onnxruntime as ort def main(): # 1. 创建 CartPole 环境 env = gym.make("CartPole-v1", render_mode="human") # 2. 加载 ONNX 模型 session = ort.InferenceSession( "cartpole_policy.onnx", providers=["CPUExecutionProvider"] ) # 获取 ONNX 输入/输出名称 input_name = session.get_inputs()[0].name output_name = session.get_outputs()[0].name print("ONNX input :", input_name) print("ONNX output:", output_name) # 3. 开始一个 episode state, info = env.reset() total_reward = 0 while True: # -------------------------------- # state 是 CartPole 的 4 维状态 # -------------------------------- # [cart_position, # cart_velocity, # pole_angle, # pole_angular_velocity] state_input = np.asarray( state, dtype=np.float32 ).reshape(1, 4) # -------------------------------- # ONNX Actor 推理 # -------------------------------- outputs = session.run( [output_name], {input_name: state_input} ) logits = outputs[0] # logits: # [[logit_action_0, logit_action_1]] # # 选择 logits 最大的动作 action = int(np.argmax(logits, axis=1)[0]) # -------------------------------- # 执行动作 # -------------------------------- next_state, reward, terminated, truncated, info = env.step(action) total_reward += reward state = next_state # -------------------------------- # episode 是否结束 # -------------------------------- if terminated or truncated: print("Episode reward:", total_reward) state, info = env.reset() total_reward = 0 if __name__ == "__main__": main()

八、.onnx文件格式详解

.onnx文件使用Protobuf Decoder格式存储,即直接将结构体保存为二进制文件,数据结构为onnx定义的固定格式,如下如所示:

相关连接

如何快速上手mjlab:从安装到运行第一个强化学习环境的完整指南

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/11 18:43:07

gcr 镜像拉不动?前缀换成 m.daocloud.io 就通了

gcr 镜像拉不动&#xff1f;前缀换成 m.daocloud.io 就通了 【免费下载链接】public-image-mirror 很多镜像都在国外。比如 gcr 。国内下载很慢&#xff0c;需要加速。致力于提供连接全世界的稳定可靠安全的容器镜像服务。 项目地址: https://gitcode.com/GitHub_Trending/pu…

作者头像 李华
网站建设 2026/9/11 18:42:33

2026年SOHO猎头行业趋势与盈利模式分析

1. SOHO猎头行业的现状与挑战 2026年的SOHO猎头行业将面临比现在更为复杂的市场环境。随着AI招聘工具的普及和大型猎头平台的扩张&#xff0c;独立猎头顾问的生存空间正在被不断挤压。根据最新行业数据显示&#xff0c;2023年已有超过30%的中低端岗位招聘被AI面试系统取代&…

作者头像 李华
网站建设 2026/9/11 18:42:27

MCP和A2A之后,机器人网络里还缺一层开放通信协议

过去两年数字 Agent 世界发生了两件确立标准的事。Anthropic 在 2024 年底发布 Model Context Protocol&#xff08;MCP&#xff09;&#xff0c;定义了 Agent 怎么标准化地调用外部工具和数据源&#xff1b;Google 在 2025 年发布 Agent2Agent Protocol&#xff08;A2A&#x…

作者头像 李华