From 78ed1b6b4d7646866f3a29081e549061f39d97a2 Mon Sep 17 00:00:00 2001 From: blank885 <1160821687@qq.com> Date: Mon, 22 Sep 2025 11:17:05 +0800 Subject: [PATCH 1/6] =?UTF-8?q?=E6=A8=A1=E4=BB=BF=E5=AD=A6=E4=B9=A0?= =?UTF-8?q?=E5=9F=BA=E5=87=86=E4=B8=93=E6=B3=A8=E4=BA=8E=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=20MuJoCo=20=E6=89=A7=E8=A1=8C=E5=A4=8D=E6=9D=82=E7=9A=84?= =?UTF-8?q?=E8=BF=90=E5=8A=A8=E4=BB=BB=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/LocoMuJoCo/README.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/LocoMuJoCo/README.md diff --git a/src/LocoMuJoCo/README.md b/src/LocoMuJoCo/README.md new file mode 100644 index 0000000000..e69de29bb2 From 3fdb5a9e292cf9d868266b42e156c6e72ef86a95 Mon Sep 17 00:00:00 2001 From: blank885 <1160821687@qq.com> Date: Mon, 20 Oct 2025 10:50:37 +0800 Subject: [PATCH 2/6] update --- src/LocoMuJoCo/main.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/LocoMuJoCo/main.py diff --git a/src/LocoMuJoCo/main.py b/src/LocoMuJoCo/main.py new file mode 100644 index 0000000000..82a9044162 --- /dev/null +++ b/src/LocoMuJoCo/main.py @@ -0,0 +1,12 @@ +from loco_mujoco.task_factories import ImitationFactory, LAFAN1DatasetConf, DefaultDatasetConf, AMASSDatasetConf + + +# # example --> you can add as many datasets as you want in the lists! +env = ImitationFactory.make("UnitreeH1", + default_dataset_conf=DefaultDatasetConf(["squat"]), + lafan1_dataset_conf=LAFAN1DatasetConf(["dance2_subject4", "walk1_subject1"]), + # if SMPL and AMASS are installed, you can use the following: + #amass_dataset_conf=AMASSDatasetConf(["DanceDB/DanceDB/20120911_TheodorosSourmelis/Capoeira_Theodoros_v2_C3D_poses"]) + ) + +env.play_trajectory(n_episodes=3, n_steps_per_episode=500, render=True) From fbef448340bee959727004d291b0d5104fe0ad2c Mon Sep 17 00:00:00 2001 From: blank885 <1160821687@qq.com> Date: Mon, 24 Nov 2025 11:43:55 +0800 Subject: [PATCH 3/6] update --- src/LocoMuJoCo/main.py | 199 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 198 insertions(+), 1 deletion(-) diff --git a/src/LocoMuJoCo/main.py b/src/LocoMuJoCo/main.py index 82a9044162..8c71898e55 100644 --- a/src/LocoMuJoCo/main.py +++ b/src/LocoMuJoCo/main.py @@ -9,4 +9,201 @@ #amass_dataset_conf=AMASSDatasetConf(["DanceDB/DanceDB/20120911_TheodorosSourmelis/Capoeira_Theodoros_v2_C3D_poses"]) ) -env.play_trajectory(n_episodes=3, n_steps_per_episode=500, render=True) +env.play_trajectory(n_episodes=3, n_steps_per_episode=1000, render=True) + + +import numpy as np +import gymnasium as gym +from gymnasium import spaces +import mujoco +from loco_mujoco.task_factories import ImitationFactory, LAFAN1DatasetConf, DefaultDatasetConf + +# 自定义机器人行走环境(彻底修复所有报错) +class WalkerEnv(gym.Env): + metadata = {"render_modes": ["human"], "render_fps": 30} + + def __init__(self, render_mode=None): + super().__init__() + # 创建UnitreeH1环境(简化数据集,加速加载) + self.env = ImitationFactory.make( + "UnitreeH1", + default_dataset_conf=DefaultDatasetConf(["walk"]), + lafan1_dataset_conf=LAFAN1DatasetConf(["walk1_subject1"]) + ) + + # 修复Box空间精度警告:显式转换为float32 + obs_low = self.env.info.observation_space.low.astype(np.float32) + obs_high = self.env.info.observation_space.high.astype(np.float32) + obs_shape = self.env.info.observation_space.shape + act_low = self.env.info.action_space.low.astype(np.float32) + act_high = self.env.info.action_space.high.astype(np.float32) + act_shape = self.env.info.action_space.shape + + self.observation_space = spaces.Box(low=obs_low, high=obs_high, shape=obs_shape, dtype=np.float32) + self.action_space = spaces.Box(low=act_low, high=act_high, shape=act_shape, dtype=np.float32) + + # 核心属性(避免访问self.env.sim) + self.render_mode = render_mode + self.robot_model = self.env.model # 直接获取模型 + self.robot_data = self.env.data # 直接获取数据 + + # 脚部和地面几何ID(容错获取) + self.foot_ids = [] + for name in ["FL_foot", "FR_foot", "RL_foot", "RR_foot"]: + geom_id = mujoco.mj_name2id(self.robot_model, mujoco.mjtObj.mjOBJ_GEOM, name) + if geom_id != -1: + self.foot_ids.append(geom_id) + self.ground_id = mujoco.mj_name2id(self.robot_model, mujoco.mjtObj.mjOBJ_GEOM, "floor") + + # 状态跟踪 + self.episode_steps = 0 + self.max_episode_steps = 500 # 缩短单episode步数 + + def step(self, action): + # 执行动作 + observation, _, _, _, info = self.env.step(action) + + # 获取机器人关键状态 + x_vel = self.robot_data.qvel[0] + torso_z = self.robot_data.qpos[2] + torso_angle = self.robot_data.qpos[3:6] + + # 优化后的奖励函数 + total_reward = 0.0 + total_reward += x_vel * 15.0 # 前进奖励 + total_reward += -abs(torso_z - 1.1) * 3.0 # 直立奖励 + total_reward += -np.sum(np.square(torso_angle)) * 3.0 # 姿态稳定奖励 + total_reward += -np.sum(np.square(action)) * 0.005 # 动作平滑惩罚 + total_reward += 1.5 # 存活奖励 + + # 触地惩罚 + if self.ground_id != -1: + for i in range(self.robot_data.ncon): + contact = self.robot_data.contact[i] + geom1, geom2 = contact.geom1, contact.geom2 + is_foot_contact = (geom1 in self.foot_ids) or (geom2 in self.foot_ids) + is_ground_contact = (geom1 == self.ground_id) or (geom2 == self.ground_id) + if is_ground_contact and not is_foot_contact: + total_reward -= 40.0 + + # 终止条件 + self.episode_steps += 1 + done = False + if torso_z < 0.6 or np.any(np.abs(torso_angle) > 0.8) or self.episode_steps >= self.max_episode_steps: + done = True + if torso_z < 0.6 or np.any(np.abs(torso_angle) > 0.8): + total_reward -= 80.0 + + # 渲染 + if self.render_mode == "human": + self.render() + + return observation, total_reward, done, False, info + + def reset(self, seed=None, options=None): + super().reset(seed=seed) + observation = self.env.reset() + self.episode_steps = 0 + return observation, {} + + def render(self): + self.env.render() + + def close(self): + # 彻底容错的资源释放(不访问任何可能不存在的属性) + try: + self.env.close() + except AttributeError: + # 1. 不访问self.env.sim,直接跳过渲染窗口关闭(避免报错) + # (loco-mujoco的渲染窗口会自动随程序退出释放) + pass + + # 2. 安全释放Mujoco模型和数据(先检查属性是否存在) + if hasattr(self, 'robot_model') and self.robot_model is not None: + try: + mujoco.mj_deleteModel(self.robot_model) + except: + pass + del self.robot_model + + if hasattr(self, 'robot_data') and self.robot_data is not None: + try: + mujoco.mj_deleteData(self.robot_data) + except: + pass + del self.robot_data + + # 3. 清空环境引用 + if hasattr(self, 'env'): + del self.env + +# 优化后的训练函数(30万步,20-30分钟) +def train_ppo(): + env = WalkerEnv(render_mode=None) + + from stable_baselines3 import PPO + from stable_baselines3.common.callbacks import CheckpointCallback, EvalCallback + + # 检查点回调 + checkpoint_callback = CheckpointCallback( + save_freq=5000, + save_path="./logs/", + name_prefix="walker_model", + save_replay_buffer=False, + save_vecnormalize=False, + ) + + # 评估回调 + eval_env = WalkerEnv(render_mode=None) + eval_callback = EvalCallback( + eval_env, + eval_freq=10000, + n_eval_episodes=3, + deterministic=True, + verbose=1 + ) + + # 优化后的PPO超参数 + model = PPO( + "MlpPolicy", + env, + verbose=1, + learning_rate=5e-4, + n_steps=1024, + batch_size=32, + n_epochs=5, + gamma=0.98, + gae_lambda=0.92, + clip_range=0.25, + ent_coef=0.02, + device="auto" + ) + + print("开始训练(优化版,预计20-30分钟)...") + model.learn( + total_timesteps=300000, + callback=[checkpoint_callback, eval_callback], + progress_bar=True + ) + + model.save("unitree_h1_walker_optimized") + print("训练完成!模型已保存为 unitree_h1_walker_optimized") + +# 修复后的测试函数 +def test_ppo(): + from stable_baselines3 import PPO + # 使用上下文管理器自动管理环境(避免手动调用close()) + with WalkerEnv(render_mode="human") as env: + model = PPO.load("unitree_h1_walker_optimized", env=env) + print("开始测试...(按ESC退出)") + obs, _ = env.reset() + for _ in range(10000): + action, _ = model.predict(obs, deterministic=True) + obs, reward, done, _, _ = env.step(action) + if done: + obs, _ = env.reset() + # 退出with块自动调用close(),无需手动调用 + +if __name__ == "__main__": + train_ppo() + test_ppo() \ No newline at end of file From 39b6d3e2424ae83e67215a4cf52306ef02e66862 Mon Sep 17 00:00:00 2001 From: blank885 <1160821687@qq.com> Date: Wed, 10 Dec 2025 23:34:33 +0800 Subject: [PATCH 4/6] =?UTF-8?q?=E4=BF=AE=E6=94=B9main.py=EF=BC=9A=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E4=BA=86ppo=E7=AE=97=E6=B3=95=E5=88=9D=E8=AF=95?= =?UTF-8?q?=E6=9C=BA=E5=99=A8=E4=BA=BA=E8=A1=8C=E8=B5=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/LocoMuJoCo/main.py | 43 ++++++++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/src/LocoMuJoCo/main.py b/src/LocoMuJoCo/main.py index 8c71898e55..3ea9971ebc 100644 --- a/src/LocoMuJoCo/main.py +++ b/src/LocoMuJoCo/main.py @@ -1,33 +1,42 @@ +import numpy as np from loco_mujoco.task_factories import ImitationFactory, LAFAN1DatasetConf, DefaultDatasetConf, AMASSDatasetConf # # example --> you can add as many datasets as you want in the lists! env = ImitationFactory.make("UnitreeH1", - default_dataset_conf=DefaultDatasetConf(["squat"]), - lafan1_dataset_conf=LAFAN1DatasetConf(["dance2_subject4", "walk1_subject1"]), + default_dataset_conf=DefaultDatasetConf(["squat", "walk"]), + lafan1_dataset_conf=LAFAN1DatasetConf(["dance2_subject4"]), # if SMPL and AMASS are installed, you can use the following: - #amass_dataset_conf=AMASSDatasetConf(["DanceDB/DanceDB/20120911_TheodorosSourmelis/Capoeira_Theodoros_v2_C3D_poses"]) - ) - -env.play_trajectory(n_episodes=3, n_steps_per_episode=1000, render=True) + # amass_dataset_conf=AMASSDatasetConf(["DanceDB/DanceDB/20120911_TheodorosSourmelis/Capoeira_Theodoros_v2_C3D_poses", + # "KIT/12/WalkInClockwiseCircle11_poses", + # "HUMAN4D/HUMAN4D/Subject3_Medhi/INF_JumpingJack_S3_01_poses", + # 'KIT/359/walking_fast05_poses']), + n_substeps=20) +env.play_trajectory(n_episodes=3, n_steps_per_episode=500, render=True) import numpy as np import gymnasium as gym from gymnasium import spaces import mujoco +import ssl +import urllib3 +# 禁用SSL验证,解决huggingface证书问题 +ssl._create_default_https_context = ssl._create_unverified_context +urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) + from loco_mujoco.task_factories import ImitationFactory, LAFAN1DatasetConf, DefaultDatasetConf -# 自定义机器人行走环境(彻底修复所有报错) +# -------------------------- 1. 正确定义WalkerEnv类(无内部实例化) -------------------------- class WalkerEnv(gym.Env): metadata = {"render_modes": ["human"], "render_fps": 30} def __init__(self, render_mode=None): super().__init__() - # 创建UnitreeH1环境(简化数据集,加速加载) + # 创建UnitreeH1环境(简化数据集,减少网络请求) self.env = ImitationFactory.make( "UnitreeH1", - default_dataset_conf=DefaultDatasetConf(["walk"]), + default_dataset_conf=DefaultDatasetConf(["walk"]), # 仅保留walk,减少下载 lafan1_dataset_conf=LAFAN1DatasetConf(["walk1_subject1"]) ) @@ -110,15 +119,13 @@ def render(self): self.env.render() def close(self): - # 彻底容错的资源释放(不访问任何可能不存在的属性) + # 彻底容错的资源释放 try: self.env.close() except AttributeError: - # 1. 不访问self.env.sim,直接跳过渲染窗口关闭(避免报错) - # (loco-mujoco的渲染窗口会自动随程序退出释放) pass - # 2. 安全释放Mujoco模型和数据(先检查属性是否存在) + # 安全释放Mujoco模型和数据 if hasattr(self, 'robot_model') and self.robot_model is not None: try: mujoco.mj_deleteModel(self.robot_model) @@ -133,12 +140,12 @@ def close(self): pass del self.robot_data - # 3. 清空环境引用 if hasattr(self, 'env'): del self.env -# 优化后的训练函数(30万步,20-30分钟) +# -------------------------- 2. 训练函数(类定义完成后再实例化) -------------------------- def train_ppo(): + # 类定义完成后,再创建实例(核心修复NameError) env = WalkerEnv(render_mode=None) from stable_baselines3 import PPO @@ -189,10 +196,10 @@ def train_ppo(): model.save("unitree_h1_walker_optimized") print("训练完成!模型已保存为 unitree_h1_walker_optimized") -# 修复后的测试函数 +# -------------------------- 3. 测试函数 -------------------------- def test_ppo(): from stable_baselines3 import PPO - # 使用上下文管理器自动管理环境(避免手动调用close()) + # 使用上下文管理器自动管理环境 with WalkerEnv(render_mode="human") as env: model = PPO.load("unitree_h1_walker_optimized", env=env) print("开始测试...(按ESC退出)") @@ -202,8 +209,8 @@ def test_ppo(): obs, reward, done, _, _ = env.step(action) if done: obs, _ = env.reset() - # 退出with块自动调用close(),无需手动调用 +# -------------------------- 4. 主函数(执行训练+测试) -------------------------- if __name__ == "__main__": train_ppo() test_ppo() \ No newline at end of file From 6bfa5b91acb56cdad69f69f3616eab053715eb7e Mon Sep 17 00:00:00 2001 From: blank885 <1160821687@qq.com> Date: Thu, 11 Dec 2025 23:34:34 +0800 Subject: [PATCH 5/6] update --- src/LocoMuJoCo/main.py | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/LocoMuJoCo/main.py b/src/LocoMuJoCo/main.py index da885ea2cf..d3bdcec8be 100644 --- a/src/LocoMuJoCo/main.py +++ b/src/LocoMuJoCo/main.py @@ -1,13 +1,8 @@ - HEAD import numpy as np - - af08a8c49629fc2df898bd91626fe49b58bfbfab from loco_mujoco.task_factories import ImitationFactory, LAFAN1DatasetConf, DefaultDatasetConf, AMASSDatasetConf - # # example --> you can add as many datasets as you want in the lists! env = ImitationFactory.make("UnitreeH1", -HEAD default_dataset_conf=DefaultDatasetConf(["squat", "walk"]), lafan1_dataset_conf=LAFAN1DatasetConf(["dance2_subject4"]), # if SMPL and AMASS are installed, you can use the following: @@ -218,12 +213,3 @@ def test_ppo(): if __name__ == "__main__": train_ppo() test_ppo() -======= - default_dataset_conf=DefaultDatasetConf(["squat"]), - lafan1_dataset_conf=LAFAN1DatasetConf(["dance2_subject4", "walk1_subject1"]), - # if SMPL and AMASS are installed, you can use the following: - #amass_dataset_conf=AMASSDatasetConf(["DanceDB/DanceDB/20120911_TheodorosSourmelis/Capoeira_Theodoros_v2_C3D_poses"]) - ) - -env.play_trajectory(n_episodes=3, n_steps_per_episode=500, render=True) ->>>>>>> af08a8c49629fc2df898bd91626fe49b58bfbfab From 7aece60ba7416bb64d4835873bc8fe49678b068f Mon Sep 17 00:00:00 2001 From: blank885 <1160821687@qq.com> Date: Tue, 16 Dec 2025 08:48:04 +0800 Subject: [PATCH 6/6] =?UTF-8?q?Unitree=20h1=E6=A8=A1=E5=9E=8B=E6=96=87?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/LocoMuJoCo/h1.xml | 263 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 263 insertions(+) create mode 100644 src/LocoMuJoCo/h1.xml diff --git a/src/LocoMuJoCo/h1.xml b/src/LocoMuJoCo/h1.xml new file mode 100644 index 0000000000..04e533d550 --- /dev/null +++ b/src/LocoMuJoCo/h1.xml @@ -0,0 +1,263 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +