v0.1.2
This commit is contained in:
11
.gitignore
vendored
11
.gitignore
vendored
@@ -1 +1,10 @@
|
||||
mytest
|
||||
# Debug
|
||||
mytest*
|
||||
debug*
|
||||
|
||||
# Python
|
||||
*.egg-info
|
||||
__pycache__/
|
||||
|
||||
# Logger
|
||||
logs/
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 wty
|
||||
Copyright (c) 2025 tianyang wu
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -12,6 +12,11 @@
|
||||
| - | - | - |
|
||||
| Unitree go2 | 四足 | ✅ |
|
||||
|
||||
## 目录结构
|
||||
- `robogauge/scripts`: 启动指标测试程序
|
||||
- `robogauge/tasks`: 定义测试任务
|
||||
- `robogauge/utils`: 常用工具
|
||||
|
||||
## 指标
|
||||
指标的计算方法是通过在环境中发送固定指令及持续时长, 通过Mujoco获取所需参数并计算.
|
||||
|
||||
|
||||
10
UPDATE.md
Normal file
10
UPDATE.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# UPDATE
|
||||
|
||||
## 20251127
|
||||
### v0.1.2
|
||||
1. 修改logger
|
||||
|
||||
## 20251124-25
|
||||
### v0.1.1
|
||||
1. 完成初始化
|
||||
2. 架构设计
|
||||
3
pyproject.toml
Normal file
3
pyproject.toml
Normal file
@@ -0,0 +1,3 @@
|
||||
[build-system]
|
||||
requires = ["setuptools>=61.0", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
4
robogauge/__init__.py
Normal file
4
robogauge/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
||||
from pathlib import Path
|
||||
|
||||
__version__ = "0.1.0"
|
||||
ROBOGAUGE_ROOT_DIR = str(Path(__file__).parent)
|
||||
BIN
robogauge/resources/models/go2/go2_cts_83501.pt
Normal file
BIN
robogauge/resources/models/go2/go2_cts_83501.pt
Normal file
Binary file not shown.
36346
robogauge/resources/robots/go2/assets/base_0.obj
Normal file
36346
robogauge/resources/robots/go2/assets/base_0.obj
Normal file
File diff suppressed because it is too large
Load Diff
23131
robogauge/resources/robots/go2/assets/base_1.obj
Normal file
23131
robogauge/resources/robots/go2/assets/base_1.obj
Normal file
File diff suppressed because it is too large
Load Diff
8367
robogauge/resources/robots/go2/assets/base_2.obj
Normal file
8367
robogauge/resources/robots/go2/assets/base_2.obj
Normal file
File diff suppressed because it is too large
Load Diff
10932
robogauge/resources/robots/go2/assets/base_3.obj
Normal file
10932
robogauge/resources/robots/go2/assets/base_3.obj
Normal file
File diff suppressed because it is too large
Load Diff
199205
robogauge/resources/robots/go2/assets/base_4.obj
Normal file
199205
robogauge/resources/robots/go2/assets/base_4.obj
Normal file
File diff suppressed because it is too large
Load Diff
24148
robogauge/resources/robots/go2/assets/calf_0.obj
Normal file
24148
robogauge/resources/robots/go2/assets/calf_0.obj
Normal file
File diff suppressed because it is too large
Load Diff
9243
robogauge/resources/robots/go2/assets/calf_1.obj
Normal file
9243
robogauge/resources/robots/go2/assets/calf_1.obj
Normal file
File diff suppressed because it is too large
Load Diff
24145
robogauge/resources/robots/go2/assets/calf_mirror_0.obj
Normal file
24145
robogauge/resources/robots/go2/assets/calf_mirror_0.obj
Normal file
File diff suppressed because it is too large
Load Diff
9252
robogauge/resources/robots/go2/assets/calf_mirror_1.obj
Normal file
9252
robogauge/resources/robots/go2/assets/calf_mirror_1.obj
Normal file
File diff suppressed because it is too large
Load Diff
30786
robogauge/resources/robots/go2/assets/foot.obj
Normal file
30786
robogauge/resources/robots/go2/assets/foot.obj
Normal file
File diff suppressed because it is too large
Load Diff
76960
robogauge/resources/robots/go2/assets/hip_0.obj
Normal file
76960
robogauge/resources/robots/go2/assets/hip_0.obj
Normal file
File diff suppressed because it is too large
Load Diff
73961
robogauge/resources/robots/go2/assets/hip_1.obj
Normal file
73961
robogauge/resources/robots/go2/assets/hip_1.obj
Normal file
File diff suppressed because it is too large
Load Diff
75706
robogauge/resources/robots/go2/assets/thigh_0.obj
Normal file
75706
robogauge/resources/robots/go2/assets/thigh_0.obj
Normal file
File diff suppressed because it is too large
Load Diff
40483
robogauge/resources/robots/go2/assets/thigh_1.obj
Normal file
40483
robogauge/resources/robots/go2/assets/thigh_1.obj
Normal file
File diff suppressed because it is too large
Load Diff
75572
robogauge/resources/robots/go2/assets/thigh_mirror_0.obj
Normal file
75572
robogauge/resources/robots/go2/assets/thigh_mirror_0.obj
Normal file
File diff suppressed because it is too large
Load Diff
40450
robogauge/resources/robots/go2/assets/thigh_mirror_1.obj
Normal file
40450
robogauge/resources/robots/go2/assets/thigh_mirror_1.obj
Normal file
File diff suppressed because it is too large
Load Diff
291
robogauge/resources/robots/go2/go2.xml
Normal file
291
robogauge/resources/robots/go2/go2.xml
Normal file
@@ -0,0 +1,291 @@
|
||||
<mujoco model="go2">
|
||||
<compiler angle="radian" meshdir="assets" autolimits="true" />
|
||||
|
||||
<option cone="elliptic" impratio="100" />
|
||||
|
||||
<default>
|
||||
<default class="go2">
|
||||
<geom friction="0.4" margin="0.001" condim="1"/>
|
||||
<joint axis="0 1 0" damping="0.1" armature="0.01" frictionloss="0.2"/>
|
||||
<motor ctrlrange="-23.7 23.7"/>
|
||||
<default class="abduction">
|
||||
<joint axis="1 0 0" range="-1.0472 1.0472"/>
|
||||
</default>
|
||||
<default class="hip">
|
||||
<default class="front_hip">
|
||||
<joint range="-1.5708 3.4907"/>
|
||||
</default>
|
||||
<default class="back_hip">
|
||||
<joint range="-0.5236 4.5379"/>
|
||||
</default>
|
||||
</default>
|
||||
<default class="knee">
|
||||
<joint range="-2.7227 -0.83776"/>
|
||||
<motor ctrlrange="-45.43 45.43"/>
|
||||
</default>
|
||||
<default class="visual">
|
||||
<geom type="mesh" contype="0" conaffinity="0" group="2"/>
|
||||
</default>
|
||||
<default class="collision">
|
||||
<geom group="3"/>
|
||||
<default class="foot">
|
||||
<geom size="0.022" pos="-0.002 0 -0.213" priority="1" condim="6"
|
||||
friction="0.4 0.02 0.01"/>
|
||||
</default>
|
||||
</default>
|
||||
</default>
|
||||
</default>
|
||||
|
||||
<asset>
|
||||
<material name="metal" rgba=".9 .95 .95 1" />
|
||||
<material name="black" rgba="0 0 0 1" />
|
||||
<material name="white" rgba="1 1 1 1" />
|
||||
<!-- <material name="white" rgba="1 1 0 1" /> -->
|
||||
<material name="gray" rgba="0.671705 0.692426 0.774270 1" />
|
||||
|
||||
<mesh file="base_0.obj" />
|
||||
<mesh file="base_1.obj" />
|
||||
<mesh file="base_2.obj" />
|
||||
<mesh file="base_3.obj" />
|
||||
<mesh file="base_4.obj" />
|
||||
<mesh file="hip_0.obj" />
|
||||
<mesh file="hip_1.obj" />
|
||||
<mesh file="thigh_0.obj" />
|
||||
<mesh file="thigh_1.obj" />
|
||||
<mesh file="thigh_mirror_0.obj" />
|
||||
<mesh file="thigh_mirror_1.obj" />
|
||||
<mesh file="calf_0.obj" />
|
||||
<mesh file="calf_1.obj" />
|
||||
<mesh file="calf_mirror_0.obj" />
|
||||
<mesh file="calf_mirror_1.obj" />
|
||||
<mesh file="foot.obj" />
|
||||
</asset>
|
||||
|
||||
<worldbody>
|
||||
<body name="base_link" pos="0 0 0.445" childclass="go2">
|
||||
<!-- <body name="base_link" pos="-5 1.2 0.445" childclass="go2"> -->
|
||||
<inertial pos="0.021112 0 -0.005366" quat="-0.000543471 0.713435 -0.00173769 0.700719"
|
||||
mass="6.921"
|
||||
diaginertia="0.107027 0.0980771 0.0244531" />
|
||||
<freejoint />
|
||||
<geom mesh="base_0" material="black" class="visual" />
|
||||
<geom mesh="base_1" material="black" class="visual" />
|
||||
<geom mesh="base_2" material="black" class="visual" />
|
||||
<geom mesh="base_3" material="white" class="visual" />
|
||||
<geom mesh="base_4" material="gray" class="visual" />
|
||||
<geom size="0.1881 0.04675 0.057" type="box" class="collision" />
|
||||
<geom size="0.05 0.045" pos="0.285 0 0.01" type="cylinder" class="collision" />
|
||||
<geom size="0.047" pos="0.293 0 -0.06" class="collision" />
|
||||
<site name="imu" pos="-0.02557 0 0.04232" />
|
||||
<body name="FL_hip" pos="0.1934 0.0465 0">
|
||||
<inertial pos="-0.0054 0.00194 -0.000105" quat="0.497014 0.499245 0.505462 0.498237"
|
||||
mass="0.678"
|
||||
diaginertia="0.00088403 0.000596003 0.000479967" />
|
||||
<joint name="FL_hip_joint" class="abduction" />
|
||||
<geom mesh="hip_0" material="metal" class="visual" />
|
||||
<geom mesh="hip_1" material="gray" class="visual" />
|
||||
<geom size="0.046 0.02" pos="0 0.08 0" quat="1 1 0 0" type="cylinder" class="collision" />
|
||||
<body name="FL_thigh" pos="0 0.0955 0">
|
||||
<inertial pos="-0.00374 -0.0223 -0.0327" quat="0.829533 0.0847635 -0.0200632 0.551623"
|
||||
mass="1.152"
|
||||
diaginertia="0.00594973 0.00584149 0.000878787" />
|
||||
<joint name="FL_thigh_joint" class="front_hip" />
|
||||
<geom mesh="thigh_0" material="metal" class="visual" />
|
||||
<geom mesh="thigh_1" material="gray" class="visual" />
|
||||
<geom size="0.1065 0.01225 0.017" pos="0 0 -0.1065" quat="0.707107 0 0.707107 0"
|
||||
type="box" class="collision" />
|
||||
<body name="FL_calf" pos="0 0 -0.213">
|
||||
<inertial pos="0.00629595 -0.000622121 -0.141417"
|
||||
quat="0.710672 0.00154099 -0.00450087 0.703508"
|
||||
mass="0.241352" diaginertia="0.0014901 0.00146356 5.31397e-05" />
|
||||
<joint name="FL_calf_joint" class="knee" />
|
||||
<geom mesh="calf_0" material="gray" class="visual" />
|
||||
<geom mesh="calf_1" material="black" class="visual" />
|
||||
<geom size="0.012 0.06" pos="0.008 0 -0.06" quat="0.994493 0 -0.104807 0"
|
||||
type="cylinder" class="collision" />
|
||||
<geom size="0.011 0.0325" pos="0.02 0 -0.148" quat="0.999688 0 0.0249974 0"
|
||||
type="cylinder" class="collision" />
|
||||
<geom pos="0 0 -0.213" mesh="foot" class="visual" material="black" />
|
||||
<geom name="FL" class="foot" />
|
||||
<body name="FL_foot" pos="0 0 -0.213" />
|
||||
</body>
|
||||
</body>
|
||||
</body>
|
||||
<body name="FR_hip" pos="0.1934 -0.0465 0">
|
||||
<inertial pos="-0.0054 -0.00194 -0.000105" quat="0.498237 0.505462 0.499245 0.497014"
|
||||
mass="0.678"
|
||||
diaginertia="0.00088403 0.000596003 0.000479967" />
|
||||
<joint name="FR_hip_joint" class="abduction" />
|
||||
<geom mesh="hip_0" material="metal" class="visual" quat="4.63268e-05 1 0 0" />
|
||||
<geom mesh="hip_1" material="gray" class="visual" quat="4.63268e-05 1 0 0" />
|
||||
<geom size="0.046 0.02" pos="0 -0.08 0" quat="0.707107 0.707107 0 0" type="cylinder"
|
||||
class="collision" />
|
||||
<body name="FR_thigh" pos="0 -0.0955 0">
|
||||
<inertial pos="-0.00374 0.0223 -0.0327" quat="0.551623 -0.0200632 0.0847635 0.829533"
|
||||
mass="1.152"
|
||||
diaginertia="0.00594973 0.00584149 0.000878787" />
|
||||
<joint name="FR_thigh_joint" class="front_hip" />
|
||||
<geom mesh="thigh_mirror_0" material="metal" class="visual" />
|
||||
<geom mesh="thigh_mirror_1" material="gray" class="visual" />
|
||||
<geom size="0.1065 0.01225 0.017" pos="0 0 -0.1065" quat="0.707107 0 0.707107 0"
|
||||
type="box" class="collision" />
|
||||
<body name="FR_calf" pos="0 0 -0.213">
|
||||
<inertial pos="0.00629595 0.000622121 -0.141417"
|
||||
quat="0.703508 -0.00450087 0.00154099 0.710672"
|
||||
mass="0.241352" diaginertia="0.0014901 0.00146356 5.31397e-05" />
|
||||
<joint name="FR_calf_joint" class="knee" />
|
||||
<geom mesh="calf_mirror_0" material="gray" class="visual" />
|
||||
<geom mesh="calf_mirror_1" material="black" class="visual" />
|
||||
<geom size="0.013 0.06" pos="0.01 0 -0.06" quat="0.995004 0 -0.0998334 0"
|
||||
type="cylinder" class="collision" />
|
||||
<geom size="0.011 0.0325" pos="0.02 0 -0.148" quat="0.999688 0 0.0249974 0"
|
||||
type="cylinder" class="collision" />
|
||||
<geom pos="0 0 -0.213" mesh="foot" class="visual" material="black" />
|
||||
<geom name="FR" class="foot" />
|
||||
<body name="FR_foot" pos="0 0 -0.213" />
|
||||
</body>
|
||||
</body>
|
||||
</body>
|
||||
<body name="RL_hip" pos="-0.1934 0.0465 0">
|
||||
<inertial pos="0.0054 0.00194 -0.000105" quat="0.505462 0.498237 0.497014 0.499245"
|
||||
mass="0.678"
|
||||
diaginertia="0.00088403 0.000596003 0.000479967" />
|
||||
<joint name="RL_hip_joint" class="abduction" />
|
||||
<geom mesh="hip_0" material="metal" class="visual" quat="4.63268e-05 0 1 0" />
|
||||
<geom mesh="hip_1" material="gray" class="visual" quat="4.63268e-05 0 1 0" />
|
||||
<geom size="0.046 0.02" pos="0 0.08 0" quat="0.707107 0.707107 0 0" type="cylinder"
|
||||
class="collision" />
|
||||
<body name="RL_thigh" pos="0 0.0955 0">
|
||||
<inertial pos="-0.00374 -0.0223 -0.0327" quat="0.829533 0.0847635 -0.0200632 0.551623"
|
||||
mass="1.152"
|
||||
diaginertia="0.00594973 0.00584149 0.000878787" />
|
||||
<joint name="RL_thigh_joint" class="back_hip" />
|
||||
<geom mesh="thigh_0" material="metal" class="visual" />
|
||||
<geom mesh="thigh_1" material="gray" class="visual" />
|
||||
<geom size="0.1065 0.01225 0.017" pos="0 0 -0.1065" quat="0.707107 0 0.707107 0"
|
||||
type="box" class="collision" />
|
||||
<body name="RL_calf" pos="0 0 -0.213">
|
||||
<inertial pos="0.00629595 -0.000622121 -0.141417"
|
||||
quat="0.710672 0.00154099 -0.00450087 0.703508"
|
||||
mass="0.241352" diaginertia="0.0014901 0.00146356 5.31397e-05" />
|
||||
<joint name="RL_calf_joint" class="knee" />
|
||||
<geom mesh="calf_0" material="gray" class="visual" />
|
||||
<geom mesh="calf_1" material="black" class="visual" />
|
||||
<geom size="0.013 0.06" pos="0.01 0 -0.06" quat="0.995004 0 -0.0998334 0"
|
||||
type="cylinder" class="collision" />
|
||||
<geom size="0.011 0.0325" pos="0.02 0 -0.148" quat="0.999688 0 0.0249974 0"
|
||||
type="cylinder" class="collision" />
|
||||
<geom pos="0 0 -0.213" mesh="foot" class="visual" material="black" />
|
||||
<geom name="RL" class="foot" />
|
||||
<body name="RL_foot" pos="0 0 -0.213" />
|
||||
</body>
|
||||
</body>
|
||||
</body>
|
||||
<body name="RR_hip" pos="-0.1934 -0.0465 0">
|
||||
<inertial pos="0.0054 -0.00194 -0.000105" quat="0.499245 0.497014 0.498237 0.505462"
|
||||
mass="0.678"
|
||||
diaginertia="0.00088403 0.000596003 0.000479967" />
|
||||
<joint name="RR_hip_joint" class="abduction" />
|
||||
<geom mesh="hip_0" material="metal" class="visual"
|
||||
quat="2.14617e-09 4.63268e-05 4.63268e-05 -1" />
|
||||
<geom mesh="hip_1" material="gray" class="visual"
|
||||
quat="2.14617e-09 4.63268e-05 4.63268e-05 -1" />
|
||||
<geom size="0.046 0.02" pos="0 -0.08 0" quat="0.707107 0.707107 0 0" type="cylinder"
|
||||
class="collision" />
|
||||
<body name="RR_thigh" pos="0 -0.0955 0">
|
||||
<inertial pos="-0.00374 0.0223 -0.0327" quat="0.551623 -0.0200632 0.0847635 0.829533"
|
||||
mass="1.152"
|
||||
diaginertia="0.00594973 0.00584149 0.000878787" />
|
||||
<joint name="RR_thigh_joint" class="back_hip" />
|
||||
<geom mesh="thigh_mirror_0" material="metal" class="visual" />
|
||||
<geom mesh="thigh_mirror_1" material="gray" class="visual" />
|
||||
<geom size="0.1065 0.01225 0.017" pos="0 0 -0.1065" quat="0.707107 0 0.707107 0"
|
||||
type="box" class="collision" />
|
||||
<body name="RR_calf" pos="0 0 -0.213">
|
||||
<inertial pos="0.00629595 0.000622121 -0.141417"
|
||||
quat="0.703508 -0.00450087 0.00154099 0.710672"
|
||||
mass="0.241352" diaginertia="0.0014901 0.00146356 5.31397e-05" />
|
||||
<joint name="RR_calf_joint" class="knee" />
|
||||
<geom mesh="calf_mirror_0" material="gray" class="visual" />
|
||||
<geom mesh="calf_mirror_1" material="black" class="visual" />
|
||||
<geom size="0.013 0.06" pos="0.01 0 -0.06" quat="0.995004 0 -0.0998334 0"
|
||||
type="cylinder" class="collision" />
|
||||
<geom size="0.011 0.0325" pos="0.02 0 -0.148" quat="0.999688 0 0.0249974 0"
|
||||
type="cylinder" class="collision" />
|
||||
<geom pos="0 0 -0.213" mesh="foot" class="visual" material="black" />
|
||||
<geom name="RR" class="foot" />
|
||||
<body name="RR_foot" pos="0 0 -0.213" />
|
||||
</body>
|
||||
</body>
|
||||
</body>
|
||||
</body>
|
||||
</worldbody>
|
||||
|
||||
<actuator>
|
||||
<motor class="abduction" name="FR_hip" joint="FR_hip_joint" />
|
||||
<motor class="hip" name="FR_thigh" joint="FR_thigh_joint" />
|
||||
<motor class="knee" name="FR_calf" joint="FR_calf_joint" />
|
||||
<motor class="abduction" name="FL_hip" joint="FL_hip_joint" />
|
||||
<motor class="hip" name="FL_thigh" joint="FL_thigh_joint" />
|
||||
<motor class="knee" name="FL_calf" joint="FL_calf_joint" />
|
||||
<motor class="abduction" name="RR_hip" joint="RR_hip_joint" />
|
||||
<motor class="hip" name="RR_thigh" joint="RR_thigh_joint" />
|
||||
<motor class="knee" name="RR_calf" joint="RR_calf_joint" />
|
||||
<motor class="abduction" name="RL_hip" joint="RL_hip_joint" />
|
||||
<motor class="hip" name="RL_thigh" joint="RL_thigh_joint" />
|
||||
<motor class="knee" name="RL_calf" joint="RL_calf_joint" />
|
||||
</actuator>
|
||||
|
||||
<sensor>
|
||||
<jointpos name="FR_hip_pos" joint="FR_hip_joint" />
|
||||
<jointpos name="FR_thigh_pos" joint="FR_thigh_joint" />
|
||||
<jointpos name="FR_calf_pos" joint="FR_calf_joint" />
|
||||
<jointpos name="FL_hip_pos" joint="FL_hip_joint" />
|
||||
<jointpos name="FL_thigh_pos" joint="FL_thigh_joint" />
|
||||
<jointpos name="FL_calf_pos" joint="FL_calf_joint" />
|
||||
<jointpos name="RR_hip_pos" joint="RR_hip_joint" />
|
||||
<jointpos name="RR_thigh_pos" joint="RR_thigh_joint" />
|
||||
<jointpos name="RR_calf_pos" joint="RR_calf_joint" />
|
||||
<jointpos name="RL_hip_pos" joint="RL_hip_joint" />
|
||||
<jointpos name="RL_thigh_pos" joint="RL_thigh_joint" />
|
||||
<jointpos name="RL_calf_pos" joint="RL_calf_joint" />
|
||||
|
||||
<jointvel name="FR_hip_vel" joint="FR_hip_joint" />
|
||||
<jointvel name="FR_thigh_vel" joint="FR_thigh_joint" />
|
||||
<jointvel name="FR_calf_vel" joint="FR_calf_joint" />
|
||||
<jointvel name="FL_hip_vel" joint="FL_hip_joint" />
|
||||
<jointvel name="FL_thigh_vel" joint="FL_thigh_joint" />
|
||||
<jointvel name="FL_calf_vel" joint="FL_calf_joint" />
|
||||
<jointvel name="RR_hip_vel" joint="RR_hip_joint" />
|
||||
<jointvel name="RR_thigh_vel" joint="RR_thigh_joint" />
|
||||
<jointvel name="RR_calf_vel" joint="RR_calf_joint" />
|
||||
<jointvel name="RL_hip_vel" joint="RL_hip_joint" />
|
||||
<jointvel name="RL_thigh_vel" joint="RL_thigh_joint" />
|
||||
<jointvel name="RL_calf_vel" joint="RL_calf_joint" />
|
||||
|
||||
<jointactuatorfrc name="FR_hip_torque" joint="FR_hip_joint" noise="0.01" />
|
||||
<jointactuatorfrc name="FR_thigh_torque" joint="FR_thigh_joint" noise="0.01" />
|
||||
<jointactuatorfrc name="FR_calf_torque" joint="FR_calf_joint" noise="0.01" />
|
||||
<jointactuatorfrc name="FL_hip_torque" joint="FL_hip_joint" noise="0.01" />
|
||||
<jointactuatorfrc name="FL_thigh_torque" joint="FL_thigh_joint" noise="0.01" />
|
||||
<jointactuatorfrc name="FL_calf_torque" joint="FL_calf_joint" noise="0.01" />
|
||||
<jointactuatorfrc name="RR_hip_torque" joint="RR_hip_joint" noise="0.01" />
|
||||
<jointactuatorfrc name="RR_thigh_torque" joint="RR_thigh_joint" noise="0.01" />
|
||||
<jointactuatorfrc name="RR_calf_torque" joint="RR_calf_joint" noise="0.01" />
|
||||
<jointactuatorfrc name="RL_hip_torque" joint="RL_hip_joint" noise="0.01" />
|
||||
<jointactuatorfrc name="RL_thigh_torque" joint="RL_thigh_joint" noise="0.01" />
|
||||
<jointactuatorfrc name="RL_calf_torque" joint="RL_calf_joint" noise="0.01" />
|
||||
|
||||
<framequat name="imu_quat" objtype="site" objname="imu" />
|
||||
<gyro name="imu_gyro" site="imu" />
|
||||
<accelerometer name="imu_acc" site="imu" />
|
||||
|
||||
<framepos name="frame_pos" objtype="site" objname="imu" />
|
||||
<framelinvel name="frame_vel" objtype="site" objname="imu" />
|
||||
</sensor>
|
||||
|
||||
<!-- <keyframe>
|
||||
<key name="home" qpos="0 0 0.27 1 0 0 0 0 0.9 -1.8 0 0.9 -1.8 0 0.9 -1.8 0 0.9 -1.8"
|
||||
ctrl="0 0.9 -1.8 0 0.9 -1.8 0 0.9 -1.8 0 0.9 -1.8" />
|
||||
</keyframe> -->
|
||||
</mujoco>
|
||||
22
robogauge/resources/terrains/flat.xml
Normal file
22
robogauge/resources/terrains/flat.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<mujoco model="flat">
|
||||
<statistic center="0 0 0.1" extent="0.8"/>
|
||||
|
||||
<visual>
|
||||
<headlight diffuse="0.6 0.6 0.6" ambient="0.3 0.3 0.3" specular="0 0 0"/>
|
||||
<rgba haze="0.15 0.25 0.35 1"/>
|
||||
<global azimuth="-130" elevation="-20"/>
|
||||
</visual>
|
||||
|
||||
<asset>
|
||||
<texture type="skybox" builtin="gradient" rgb1="0.3 0.5 0.7" rgb2="0 0 0" width="512" height="3072"/>
|
||||
<texture type="2d" name="groundplane" builtin="checker" mark="edge" rgb1="0.2 0.3 0.4" rgb2="0.1 0.2 0.3"
|
||||
markrgb="0.8 0.8 0.8" width="300" height="300"/>
|
||||
<material name="groundplane" texture="groundplane" texuniform="true" texrepeat="5 5" reflectance="0.2"/>
|
||||
<material name="sponge_mat" rgba="0.9 0.9 0.2 1" reflectance="0.1"/>
|
||||
</asset>
|
||||
|
||||
<worldbody>
|
||||
<light pos="0 0 1.5" dir="0 0 -1" directional="true"/>
|
||||
<geom name="floor" size="0 0 0.05" type="plane" material="groundplane"/>
|
||||
</worldbody>
|
||||
</mujoco>
|
||||
19
robogauge/scripts/run.py
Normal file
19
robogauge/scripts/run.py
Normal file
@@ -0,0 +1,19 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
@File : run.py
|
||||
@Time : 2025/11/27 15:54:47
|
||||
@Author : wty-yy
|
||||
@Version : 1.0
|
||||
@Blog : https://wty-yy.github.io/
|
||||
@Desc : Run Robogauge Pipeline
|
||||
'''
|
||||
from robogauge.tasks import *
|
||||
from robogauge.utils.task_register import task_register
|
||||
from robogauge.utils.helpers import parse_args
|
||||
from robogauge.utils.logger import logger
|
||||
|
||||
if __name__ == '__main__':
|
||||
args = parse_args()
|
||||
logger.create(args.experiment_name)
|
||||
pipeline: BasePipeline = task_register.make_pipeline(args.task_name, args=args)
|
||||
pipeline.run()
|
||||
7
robogauge/tasks/__init__.py
Normal file
7
robogauge/tasks/__init__.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from robogauge.utils.task_register import task_register
|
||||
from robogauge.tasks.simulator.mujoco_config import MujocoConfig
|
||||
from robogauge.tasks.robots import RobotConfig
|
||||
from robogauge.tasks.pipeline import BasePipeline
|
||||
from robogauge.tasks.gauge import BaseGaugeConfig
|
||||
|
||||
task_register.register('base', BasePipeline, MujocoConfig, BaseGaugeConfig, RobotConfig)
|
||||
4
robogauge/tasks/gauge/__init__.py
Normal file
4
robogauge/tasks/gauge/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
||||
from .base_gauge import BaseGauge
|
||||
from .base_gauge_config import BaseGaugeConfig
|
||||
from .flat.flat_gauge import FlatGauge
|
||||
from .flat.flat_gauge_config import FlatGaugeConfig
|
||||
29
robogauge/tasks/gauge/base_gauge.py
Normal file
29
robogauge/tasks/gauge/base_gauge.py
Normal file
@@ -0,0 +1,29 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
@File : base_gauge.py
|
||||
@Time : 2025/11/27 15:55:19
|
||||
@Author : wty-yy
|
||||
@Version : 1.0
|
||||
@Blog : https://wty-yy.github.io/
|
||||
@Desc : Base Gauge for Robogauge
|
||||
'''
|
||||
from robogauge.tasks.robots.base_robot_config import RobotConfig
|
||||
from robogauge.tasks.gauge.base_gauge_config import BaseGaugeConfig
|
||||
|
||||
class BaseGauge:
|
||||
def __init__(self, cfg: BaseGaugeConfig):
|
||||
self.cfg = cfg
|
||||
|
||||
def is_reset(self) -> bool:
|
||||
return False
|
||||
|
||||
def is_done(self) -> bool:
|
||||
return False
|
||||
|
||||
def get_goal(self) -> dict:
|
||||
goal = {}
|
||||
return goal
|
||||
|
||||
def update_metrics(self, sim_info: dict):
|
||||
...
|
||||
|
||||
26
robogauge/tasks/gauge/base_gauge_config.py
Normal file
26
robogauge/tasks/gauge/base_gauge_config.py
Normal file
@@ -0,0 +1,26 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
@File : base_gauge_config.py
|
||||
@Time : 2025/11/27 15:55:11
|
||||
@Author : wty-yy
|
||||
@Version : 1.0
|
||||
@Blog : https://wty-yy.github.io/
|
||||
@Desc : Base Gauge Configuration
|
||||
'''
|
||||
from robogauge.utils.config import Config
|
||||
|
||||
class BaseGaugeConfig(Config):
|
||||
gauge_class = 'BaseGauge'
|
||||
|
||||
class assets:
|
||||
terrain_xml = '{ROBOGAUGE_ROOT_DIR}/resources/terrains/flat.xml'
|
||||
terrain_spawn_xy = [0, 0] # x y [m]
|
||||
|
||||
class metrics:
|
||||
dof_limits = True
|
||||
|
||||
class commands:
|
||||
stance = True
|
||||
max_lin_vel = True
|
||||
diagonal_lin_vel = True
|
||||
|
||||
13
robogauge/tasks/gauge/flat/flat_gauge.py
Normal file
13
robogauge/tasks/gauge/flat/flat_gauge.py
Normal file
@@ -0,0 +1,13 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
@File : flat_gauge.py
|
||||
@Time : 2025/11/27 16:03:11
|
||||
@Author : wty-yy
|
||||
@Version : 1.0
|
||||
@Blog : https://wty-yy.github.io/
|
||||
@Desc : Flat Gauge Implementation
|
||||
'''
|
||||
from robogauge.tasks.gauge.base_gauge import BaseGauge
|
||||
|
||||
class FlatGauge(BaseGauge):
|
||||
...
|
||||
13
robogauge/tasks/gauge/flat/flat_gauge_config.py
Normal file
13
robogauge/tasks/gauge/flat/flat_gauge_config.py
Normal file
@@ -0,0 +1,13 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
@File : flat_gauge_config.py
|
||||
@Time : 2025/11/27 16:03:02
|
||||
@Author : wty-yy
|
||||
@Version : 1.0
|
||||
@Blog : https://wty-yy.github.io/
|
||||
@Desc : Flat Gauge Configuration
|
||||
'''
|
||||
from robogauge.tasks.gauge.base_gauge_config import BaseGaugeConfig
|
||||
|
||||
class FlatGaugeConfig(BaseGaugeConfig):
|
||||
...
|
||||
1
robogauge/tasks/pipeline/__init__.py
Normal file
1
robogauge/tasks/pipeline/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from .base_pipeline import BasePipeline
|
||||
58
robogauge/tasks/pipeline/base_pipeline.py
Normal file
58
robogauge/tasks/pipeline/base_pipeline.py
Normal file
@@ -0,0 +1,58 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
@File : base_pipeline.py
|
||||
@Time : 2025/11/27 15:53:26
|
||||
@Author : wty-yy
|
||||
@Version : 1.0
|
||||
@Blog : https://wty-yy.github.io/
|
||||
@Desc : Base Pipeline for Robogauge
|
||||
'''
|
||||
import traceback
|
||||
from robogauge.utils.logger import logger
|
||||
from robogauge.tasks.simulator import MujocoSimulator, MujocoConfig
|
||||
from robogauge.tasks.robots import BaseRobot, RobotConfig
|
||||
from robogauge.tasks.gauge import BaseGauge, BaseGaugeConfig
|
||||
|
||||
class BasePipeline:
|
||||
def __init__(self,
|
||||
simulator_cfg: MujocoConfig,
|
||||
robot_cfg: RobotConfig,
|
||||
gauge_cfg: BaseGaugeConfig
|
||||
):
|
||||
self.simulator_cfg = simulator_cfg
|
||||
self.robot_cfg = robot_cfg
|
||||
self.gauge_cfg = gauge_cfg
|
||||
|
||||
self.sim: MujocoSimulator = eval(simulator_cfg.simulator_class)(simulator_cfg)
|
||||
self.robot: BaseRobot = eval(robot_cfg.robot_class)(robot_cfg)
|
||||
self.gauge: BaseGauge = eval(gauge_cfg.gauge_class)(gauge_cfg)
|
||||
|
||||
def load(self):
|
||||
self.sim.load(
|
||||
self.gauge_cfg.assets.terrain_xml,
|
||||
self.robot_cfg.assets.robot_xml,
|
||||
self.gauge_cfg.assets.terrain_spawn_xy,
|
||||
self.robot_cfg.assets.robot_spawn_height
|
||||
)
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
self.load()
|
||||
info = self.sim.step()
|
||||
frame_skip = int(self.robot_cfg.control.control_dt / self.simulator_cfg.physics.simulation_dt)
|
||||
logger.info(f"Sim FPS: {1.0 / self.simulator_cfg.physics.simulation_dt:.2f}, Control FPS: {1.0 / self.robot_cfg.control.control_dt:.2f}, Frame Skip: {frame_skip:d}")
|
||||
logger.info("Starting pipeline...")
|
||||
while not self.gauge.is_done():
|
||||
goal = self.gauge.get_goal()
|
||||
obs = self.robot.build_observation(info, goal)
|
||||
action = self.robot.get_action(obs)
|
||||
for _ in range(frame_skip):
|
||||
self.sim.apply_action(action)
|
||||
info = self.sim.step()
|
||||
self.gauge.update_metrics(info)
|
||||
if self.gauge.is_reset():
|
||||
self.sim.reset()
|
||||
info = self.sim.step()
|
||||
finally:
|
||||
self.sim.close_viewer()
|
||||
logger.info("Pipeline execution finished.")
|
||||
4
robogauge/tasks/robots/__init__.py
Normal file
4
robogauge/tasks/robots/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
||||
from .base_robot_config import RobotConfig
|
||||
from .base_robot import BaseRobot
|
||||
# from .go2.go2_config import Go2Config
|
||||
# from .go2.go2_controller import Go2Controller
|
||||
29
robogauge/tasks/robots/base_robot.py
Normal file
29
robogauge/tasks/robots/base_robot.py
Normal file
@@ -0,0 +1,29 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
@File : base_robot.py
|
||||
@Time : 2025/11/27 15:53:57
|
||||
@Author : wty-yy
|
||||
@Version : 1.0
|
||||
@Blog : https://wty-yy.github.io/
|
||||
@Desc : Base Robot Class
|
||||
'''
|
||||
import torch
|
||||
import numpy as np
|
||||
from robogauge.tasks.robots.base_robot_config import RobotConfig
|
||||
|
||||
class BaseRobot:
|
||||
def __init__(self, cfg: RobotConfig):
|
||||
self.num_act = cfg.mdp.num_actions
|
||||
self.num_obs = cfg.mdp.num_observations
|
||||
self.model = None
|
||||
|
||||
def load_model(self):
|
||||
...
|
||||
|
||||
def build_observation(self, sim_info: dict, goal_info: dict) -> np.ndarray:
|
||||
obs = np.zeros(self.num_obs)
|
||||
return obs
|
||||
|
||||
def get_action(self, obs) -> np.ndarray:
|
||||
action = np.zeros_like(self.num_act)
|
||||
return action
|
||||
34
robogauge/tasks/robots/base_robot_config.py
Normal file
34
robogauge/tasks/robots/base_robot_config.py
Normal file
@@ -0,0 +1,34 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
@File : base_robot_config.py
|
||||
@Time : 2025/11/27 15:53:47
|
||||
@Author : wty-yy
|
||||
@Version : 1.0
|
||||
@Blog : https://wty-yy.github.io/
|
||||
@Desc : Base Robot Configuration
|
||||
'''
|
||||
from robogauge.utils.config import Config
|
||||
|
||||
class RobotConfig(Config):
|
||||
robot_class = 'BaseRobot'
|
||||
|
||||
class assets:
|
||||
robot_xml = "{ROBOGAUGE_ROOT_DIR}/resources/robots/go2/go2.xml"
|
||||
robot_spawn_height = 0.1 # z [m]
|
||||
|
||||
class control:
|
||||
torch_script_model_path = "{ROBOGAUGE_ROOT_DIR}/resources/models/go2/go2_cts_61500.pt"
|
||||
control_dt = 0.02 # 50 Hz
|
||||
action_scale = 0.25 # target pos = action_scale * action * default_pos
|
||||
stiffness = 20.0 # [N*m/rad]
|
||||
damping = 0.5 # [N*m*s/rad]
|
||||
|
||||
class mdp:
|
||||
num_observations = 46
|
||||
num_actions = 12
|
||||
|
||||
class commands:
|
||||
lin_vel_x = [-1, 1] # min max [m/s]
|
||||
lin_vel_y = [-1, 1] # min max [m/s]
|
||||
ang_vel_yaw = [-1, 1] # min max [rad/s]
|
||||
|
||||
0
robogauge/tasks/robots/go2/go2.py
Normal file
0
robogauge/tasks/robots/go2/go2.py
Normal file
20
robogauge/tasks/robots/go2/go2_config.py
Normal file
20
robogauge/tasks/robots/go2/go2_config.py
Normal file
@@ -0,0 +1,20 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
@File : go2_config.py
|
||||
@Time : 2025/11/27 16:03:27
|
||||
@Author : wty-yy
|
||||
@Version : 1.0
|
||||
@Blog : https://wty-yy.github.io/
|
||||
@Desc : Go2 Robot Configuration
|
||||
'''
|
||||
from robogauge.tasks.robots import RobotConfig
|
||||
|
||||
class Go2Config(RobotConfig):
|
||||
class assets:
|
||||
robot_xml = "{ROBOGAUGE_ROOT_DIR}/resources/robots/go2/go2.xml"
|
||||
robot_spawn_height = 0.1 # z [m]
|
||||
|
||||
class control:
|
||||
control_dt = 0.02 # 50 Hz
|
||||
action_scale = 0.25 # scale for normalized actions
|
||||
|
||||
2
robogauge/tasks/simulator/__init__.py
Normal file
2
robogauge/tasks/simulator/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
from .mujoco_simulator import MujocoSimulator
|
||||
from .mujoco_config import MujocoConfig
|
||||
25
robogauge/tasks/simulator/mujoco_config.py
Normal file
25
robogauge/tasks/simulator/mujoco_config.py
Normal file
@@ -0,0 +1,25 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
@File : mujoco_config.py
|
||||
@Time : 2025/11/27 15:55:34
|
||||
@Author : wty-yy
|
||||
@Version : 1.0
|
||||
@Blog : https://wty-yy.github.io/
|
||||
@Desc : Mujoco Simulator Configuration
|
||||
'''
|
||||
from robogauge.utils.config import Config
|
||||
|
||||
class MujocoConfig(Config):
|
||||
simulator_class = 'MujocoSimulator'
|
||||
|
||||
class physics:
|
||||
simulation_dt = 0.005 # 200 Hz
|
||||
|
||||
class viewer:
|
||||
headless = False
|
||||
block_rendering = True # Whether to block rendering in the viewer loop.
|
||||
|
||||
class render:
|
||||
save_video = False
|
||||
height = 480
|
||||
width = 640
|
||||
143
robogauge/tasks/simulator/mujoco_simulator.py
Normal file
143
robogauge/tasks/simulator/mujoco_simulator.py
Normal file
@@ -0,0 +1,143 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
@File : mujoco_simulator.py
|
||||
@Time : 2025/11/27 15:54:20
|
||||
@Author : wty-yy
|
||||
@Version : 1.0
|
||||
@Blog : https://wty-yy.github.io/
|
||||
@Desc : None
|
||||
'''
|
||||
import mujoco
|
||||
import mujoco.viewer
|
||||
from dm_control import mjcf
|
||||
|
||||
import time
|
||||
import imageio
|
||||
import numpy as np
|
||||
|
||||
from robogauge.utils.logger import logger
|
||||
from robogauge.utils.helpers import pares_path
|
||||
from robogauge.tasks.simulator.mujoco_config import MujocoConfig
|
||||
|
||||
class MujocoSimulator:
|
||||
def __init__(self, sim_cfg: MujocoConfig):
|
||||
self.cfg = sim_cfg
|
||||
self.terrain_xml = None
|
||||
self.robot_xml = None
|
||||
self.terrain_spawn_xy = None
|
||||
self.robot_spawn_height = None
|
||||
self.viewer = None
|
||||
self.renderer = None
|
||||
self.vid_writer = None
|
||||
self.vid_count = 0
|
||||
self._pause = True
|
||||
|
||||
def load(
|
||||
self,
|
||||
terrain_xml: str = None,
|
||||
robot_xml: str = None,
|
||||
terrain_spawn_xy: list = None,
|
||||
robot_spawn_height: float = None,
|
||||
):
|
||||
""" Load terrain and robot into the simulator, support re-loading. """
|
||||
if terrain_xml is not None:
|
||||
self.terrain_xml = pares_path(terrain_xml)
|
||||
if robot_xml is not None:
|
||||
self.robot_xml = pares_path(robot_xml)
|
||||
if terrain_spawn_xy is not None:
|
||||
self.terrain_spawn_xy = terrain_spawn_xy
|
||||
if robot_spawn_height is not None:
|
||||
self.robot_spawn_height = robot_spawn_height
|
||||
|
||||
terrain_xml = self.terrain_xml
|
||||
robot_xml = self.robot_xml
|
||||
terrain_spawn_xy = self.terrain_spawn_xy
|
||||
robot_spawn_height = self.robot_spawn_height
|
||||
if terrain_xml is None or robot_xml is None:
|
||||
raise ValueError("Terrain and robot XML paths must be provided.")
|
||||
|
||||
robot_mjcf = mjcf.from_path(robot_xml)
|
||||
terrain_mjcf = mjcf.from_path(terrain_xml)
|
||||
for j in robot_mjcf.find_all('joint'):
|
||||
if j.tag == 'freejoint':
|
||||
j.remove()
|
||||
attachment_frame = terrain_mjcf.attach(robot_mjcf)
|
||||
attachment_frame.add('freejoint')
|
||||
attachment_frame.pos = [*terrain_spawn_xy, robot_spawn_height]
|
||||
|
||||
if self.viewer is not None:
|
||||
self.close_viewer()
|
||||
self.mj_physics = mjcf.Physics.from_mjcf_model(terrain_mjcf)
|
||||
self.mj_model = self.mj_physics.model.ptr
|
||||
self.mj_data = self.mj_physics.data.ptr
|
||||
self.mj_model.opt.timestep = self.cfg.physics.simulation_dt
|
||||
self.headless = self.cfg.viewer.headless
|
||||
if self.cfg.render.save_video and self.headless:
|
||||
logger.warning("Cannot save video in headless mode, disabling video saving.")
|
||||
self.cfg.render.save_video = False
|
||||
if not self.headless:
|
||||
self.viewer = mujoco.viewer.launch_passive(self.mj_model, self.mj_data, key_callback=self.key_callback)
|
||||
self.last_render_time = time.time()
|
||||
if self.cfg.render.save_video:
|
||||
self.renderer = mujoco.Renderer(self.mj_model, height=self.cfg.render.height, width=self.cfg.render.width)
|
||||
|
||||
vid_dir = logger.log_dir / "videos"
|
||||
vid_dir.mkdir(parents=True, exist_ok=True)
|
||||
vid_path = str(vid_dir / f"sim_video_{self.vid_count:03d}.mp4")
|
||||
self.vid_writer = imageio.get_writer(
|
||||
vid_path,
|
||||
fps=int(1 / self.cfg.physics.simulation_dt),
|
||||
)
|
||||
logger.info(f"Saving simulation video to: {vid_path}")
|
||||
self.vid_count += 1
|
||||
self._pause = False
|
||||
|
||||
def key_callback(self, keycode):
|
||||
if keycode == 32:
|
||||
self._pause = not self._pause
|
||||
logger.info(f"Pause toggled: {self._pause}")
|
||||
|
||||
def step(self) -> dict:
|
||||
""" Simulation step, pause will block thread. """
|
||||
while self._pause:
|
||||
time.sleep(0.1)
|
||||
self.mj_physics.step()
|
||||
if self.viewer is not None:
|
||||
if self.viewer.is_running():
|
||||
time_untile_next_render = self.cfg.physics.simulation_dt - (time.time() - self.last_render_time)
|
||||
if time_untile_next_render > 0:
|
||||
time.sleep(time_untile_next_render)
|
||||
self.viewer.sync()
|
||||
if self.vid_writer is not None:
|
||||
self.renderer.update_scene(self.mj_data, camera=self.viewer.cam)
|
||||
frame = self.renderer.render()
|
||||
self.vid_writer.append_data(frame)
|
||||
self.last_render_time = time.time()
|
||||
else:
|
||||
logger.warning("Viewer closed by user, stop video recording.")
|
||||
self.close_viewer()
|
||||
|
||||
info = {}
|
||||
return info
|
||||
|
||||
def reset(self):
|
||||
""" Reset the simulator to initial state. """
|
||||
self.mj_physics.reset()
|
||||
if self.viewer is not None:
|
||||
self.viewer.sync()
|
||||
|
||||
def apply_action(self, action: np.ndarray):
|
||||
""" Apply action to the simulator. """
|
||||
self.mj_data.ctrl[:] = action
|
||||
|
||||
def close_viewer(self):
|
||||
""" Close the viewer and video writer. """
|
||||
if self.viewer is not None:
|
||||
self.viewer.close()
|
||||
self.viewer = None
|
||||
logger.info("Closing viewer.")
|
||||
if self.vid_writer is not None:
|
||||
self.vid_writer.close()
|
||||
self.vid_writer = None
|
||||
self.renderer = None
|
||||
logger.info("Closing video writer.")
|
||||
18
robogauge/utils/config.py
Normal file
18
robogauge/utils/config.py
Normal file
@@ -0,0 +1,18 @@
|
||||
import inspect
|
||||
|
||||
class Config:
|
||||
# From https://github.com/leggedrobotics/legged_gym/blob/master/legged_gym/envs/base/base_config.py
|
||||
def __init__(self) -> None:
|
||||
""" Initializes all member classes recursively. Ignores all namse starting with '__' (buit-in methods)."""
|
||||
self.init_member_classes(self)
|
||||
|
||||
@staticmethod
|
||||
def init_member_classes(obj):
|
||||
for key in dir(obj):
|
||||
if key == "__class__":
|
||||
continue
|
||||
var = getattr(obj, key)
|
||||
if inspect.isclass(var):
|
||||
i_var = var()
|
||||
setattr(obj, key, i_var)
|
||||
Config.init_member_classes(i_var)
|
||||
56
robogauge/utils/helpers.py
Normal file
56
robogauge/utils/helpers.py
Normal file
@@ -0,0 +1,56 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
@File : helpers.py
|
||||
@Time : 2025/11/27 15:26:37
|
||||
@Author : wty-yy
|
||||
@Version : 1.0
|
||||
@Blog : https://wty-yy.github.io/
|
||||
@Desc : Helpers for robogauge, include:
|
||||
- Argument parsing
|
||||
- Class to dict conversion
|
||||
- Path parsing
|
||||
'''
|
||||
from argparse import ArgumentParser
|
||||
from pathlib import Path
|
||||
from robogauge import ROBOGAUGE_ROOT_DIR
|
||||
|
||||
def pares_path(path):
|
||||
if "{ROBOGAUGE_ROOT_DIR}" in str(path):
|
||||
path = str(path).replace("{ROBOGAUGE_ROOT_DIR}", ROBOGAUGE_ROOT_DIR)
|
||||
return path
|
||||
|
||||
def class_to_dict(obj) -> dict:
|
||||
if not hasattr(obj, "__dict__"):
|
||||
return obj
|
||||
result = {}
|
||||
for key in dir(obj):
|
||||
if key.startswith("_"):
|
||||
continue
|
||||
element = []
|
||||
val = getattr(obj, key)
|
||||
if isinstance(val, list):
|
||||
for item in val:
|
||||
element.append(class_to_dict(item))
|
||||
else:
|
||||
element = class_to_dict(val)
|
||||
result[key] = element
|
||||
return result
|
||||
|
||||
def str2bool(v):
|
||||
if v.lower() in ('yes', 'true', 't', 'y', '1'):
|
||||
return True
|
||||
elif v.lower() in ('no', 'false', 'f', 'n', '0'):
|
||||
return False
|
||||
raise TypeError('Boolean value expected.')
|
||||
|
||||
def parse_args():
|
||||
parser = ArgumentParser()
|
||||
parameters = [
|
||||
{"name": "--task-name", "type": str, "default": "base", "help": "Name of the task to run."},
|
||||
{"name": "--experiment-name", "type": str, "default": "base", "help": "Name of the experiment to run."},
|
||||
{"name": "--headless", "action": "store_true", "default": False, "help": "Run in headless mode."},
|
||||
{"name": "--save-video", "action": "store_true", "default": False, "help": "Save video output."},
|
||||
]
|
||||
for param in parameters:
|
||||
parser.add_argument(param['name'], **{k: v for k, v in param.items() if k != 'name'})
|
||||
return parser.parse_args()
|
||||
133
robogauge/utils/logger.py
Normal file
133
robogauge/utils/logger.py
Normal file
@@ -0,0 +1,133 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
@File : my_logger.py
|
||||
@Time : 2025/02/26 21:43:47
|
||||
@Author : wty-yy
|
||||
@Version : 1.0
|
||||
@Blog : https://wty-yy.github.io/
|
||||
@Desc : A customed logger, support:
|
||||
1. Color level name
|
||||
2. Output to console and save log to file
|
||||
3. Support vscode file location jump (ctrl+left key)
|
||||
'''
|
||||
import time
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from robogauge import ROBOGAUGE_ROOT_DIR
|
||||
|
||||
class LogColor:
|
||||
""" ANSI color codes """
|
||||
RESET = '\033[0m'
|
||||
RED = '\033[31m'
|
||||
GREEN = '\033[32m'
|
||||
YELLOW = '\033[33m'
|
||||
BLUE = '\033[34m'
|
||||
MAGENTA = '\033[35m'
|
||||
CYAN = '\033[36m'
|
||||
WHITE = '\033[37m'
|
||||
BOLD = '\033[1m'
|
||||
UNDERLINE = '\033[4m'
|
||||
|
||||
LOG_COLORS = {
|
||||
""" Match level name to color """
|
||||
'DEBUG': LogColor.CYAN,
|
||||
'INFO': LogColor.GREEN,
|
||||
'WARNING': LogColor.YELLOW,
|
||||
'ERROR': LogColor.RED,
|
||||
'CRITICAL': LogColor.RED + LogColor.BOLD,
|
||||
}
|
||||
|
||||
class ColorFormatter(logging.Formatter):
|
||||
"""Color Formatter for color_level"""
|
||||
def __init__(self, fmt, datefmt=None, use_color=True):
|
||||
self.formatter = logging.Formatter(fmt, datefmt)
|
||||
self.use_color = use_color
|
||||
|
||||
def format(self, record):
|
||||
record.color_level = f"{LOG_COLORS.get(record.levelname, LogColor.RESET)}{record.levelname}{LogColor.RESET}" if self.use_color else record.levelname
|
||||
return self.formatter.format(record)
|
||||
|
||||
|
||||
class Logger:
|
||||
def create(self,
|
||||
experiment_name,
|
||||
console_output=True, color_output=True,
|
||||
log_level=logging.DEBUG, save_file_mode='a'
|
||||
):
|
||||
"""
|
||||
Create customed Logger
|
||||
|
||||
Args:
|
||||
logger_name (str): Logger name
|
||||
console_output (bool, optional): Whether output to console. Defaults to True.
|
||||
color_output (bool, optional): Whether use color output. Defaults to True.
|
||||
log_level (int, optional): Defaults to logging.DEBUG.
|
||||
save_file_mode (str, optional): The mode of saving to path_log_file
|
||||
|
||||
Returns:
|
||||
logging.Logger: logger
|
||||
"""
|
||||
self.logger = logging.getLogger(experiment_name + "_logger")
|
||||
self.logger.setLevel(log_level)
|
||||
|
||||
console_formatter = ColorFormatter( # console output format
|
||||
fmt="%(asctime)s - %(color_level)s - %(filename)s:%(lineno)d - %(message)s",
|
||||
datefmt="%Y-%m-%d %H:%M:%S",
|
||||
use_color=color_output
|
||||
)
|
||||
file_formatter = logging.Formatter( # file output format
|
||||
fmt="%(asctime)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s",
|
||||
datefmt="%Y-%m-%d %H:%M:%S",
|
||||
)
|
||||
|
||||
if console_output:
|
||||
sh = logging.StreamHandler()
|
||||
sh.setFormatter(console_formatter)
|
||||
self.logger.addHandler(sh)
|
||||
|
||||
self.log_dir = Path(ROBOGAUGE_ROOT_DIR) / "logs" / experiment_name / time.strftime("%Y%m%d-%H-%M-%S")
|
||||
self.log_dir.mkdir(parents=True, exist_ok=True)
|
||||
path_log_file = self.log_dir / "stdout.log"
|
||||
if path_log_file:
|
||||
fh = logging.FileHandler(path_log_file, mode=save_file_mode, encoding='utf-8')
|
||||
fh.setFormatter(file_formatter)
|
||||
self.logger.addHandler(fh)
|
||||
|
||||
def debug(self, msg, *args, **kwargs):
|
||||
self.logger.debug(msg, *args, **kwargs)
|
||||
|
||||
def info(self, msg, *args, **kwargs):
|
||||
self.logger.info(msg, *args, **kwargs)
|
||||
|
||||
def warning(self, msg, *args, **kwargs):
|
||||
self.logger.warning(msg, *args, **kwargs)
|
||||
|
||||
def error(self, msg, *args, **kwargs):
|
||||
self.logger.error(msg, *args, **kwargs)
|
||||
|
||||
def critical(self, msg, *args, **kwargs):
|
||||
self.logger.critical(msg, *args, **kwargs)
|
||||
|
||||
logger = Logger()
|
||||
|
||||
if __name__ == '__main__':
|
||||
from pathlib import Path
|
||||
path_parent = Path(__file__).parents[0]
|
||||
path_log = path_parent / "app.log"
|
||||
|
||||
logger = Logger()
|
||||
logger.create("my_logger")
|
||||
|
||||
logger.debug("This is a debug message")
|
||||
logger.info("This is an info message")
|
||||
logger.warning("This is a warning message")
|
||||
logger.error("This is an error message")
|
||||
logger.critical("This is a critical message")
|
||||
|
||||
# logger_no_color = Logger()
|
||||
# logger_no_color.create("no_color_logger", console_output=True, color_output=False)
|
||||
# logger_no_color.info("This is a info message without color")
|
||||
|
||||
# logger_file_only = Logger()
|
||||
# logger_file_only.create("file_only_logger", console_output=False) # save to file only
|
||||
# logger_file_only.error("This is an error message only in file")
|
||||
0
robogauge/utils/mujoco_tools.py
Normal file
0
robogauge/utils/mujoco_tools.py
Normal file
55
robogauge/utils/task_register.py
Normal file
55
robogauge/utils/task_register.py
Normal file
@@ -0,0 +1,55 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
@File : task_register.py
|
||||
@Time : 2025/11/27 15:59:03
|
||||
@Author : wty-yy
|
||||
@Version : 1.0
|
||||
@Blog : https://wty-yy.github.io/
|
||||
@Desc : Task Registration Utility
|
||||
'''
|
||||
class TaskRegister():
|
||||
def __init__(self):
|
||||
self.pipeline_classes = {}
|
||||
self.sim_cfgs = {}
|
||||
self.gauger_cfgs = {}
|
||||
self.robot_cfgs = {}
|
||||
|
||||
def register(self, name: str, pipeline_class, sim_cfg, gauger_cfg, robot_cfg):
|
||||
self.pipeline_classes[name] = pipeline_class
|
||||
self.sim_cfgs[name] = sim_cfg
|
||||
self.gauger_cfgs[name] = gauger_cfg
|
||||
self.robot_cfgs[name] = robot_cfg
|
||||
|
||||
def get_pipeline_class(self, name: str):
|
||||
if name not in self.pipeline_classes:
|
||||
raise ValueError(f"Task '{name}' is not registered.")
|
||||
return self.pipeline_classes[name]
|
||||
|
||||
def get_cfgs(self, name):
|
||||
if name not in self.sim_cfgs:
|
||||
raise ValueError(f"Task '{name}' is not registered.")
|
||||
sim_cfg = self.sim_cfgs[name]
|
||||
gauger_cfg = self.gauger_cfgs[name]
|
||||
robot_cfg = self.robot_cfgs[name]
|
||||
return sim_cfg, gauger_cfg, robot_cfg
|
||||
|
||||
def make_pipeline(self, name, args=None, sim_cfg=None, gauger_cfg=None, robot_cfg=None):
|
||||
default_cfgs = self.get_cfgs(name)
|
||||
if sim_cfg is None:
|
||||
sim_cfg = default_cfgs[0]
|
||||
if gauger_cfg is None:
|
||||
gauger_cfg = default_cfgs[1]
|
||||
if robot_cfg is None:
|
||||
robot_cfg = default_cfgs[2]
|
||||
if args is not None:
|
||||
self.update_args_to_cfg(sim_cfg, gauger_cfg, robot_cfg, args)
|
||||
pipeline_class = self.get_pipeline_class(name)
|
||||
return pipeline_class(sim_cfg, robot_cfg, gauger_cfg)
|
||||
|
||||
def update_args_to_cfg(self, sim_cfg, gauger_cfg, robot_cfg, args):
|
||||
if args.headless is not None:
|
||||
sim_cfg.viewer.headless = args.headless
|
||||
if args.save_video is not None:
|
||||
sim_cfg.render.save_video = args.save_video
|
||||
|
||||
task_register = TaskRegister()
|
||||
29
setup.py
Normal file
29
setup.py
Normal file
@@ -0,0 +1,29 @@
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
setup(
|
||||
name="robogauge", # 包名
|
||||
version="0.1.0", # 版本号
|
||||
author="wty-yy", # 你的名字
|
||||
author_email="993660140@qq.com",
|
||||
description="A generic robot RL model evaluation library based on MuJoCo",
|
||||
url="https://github.com/wty-yy/robot_gauge", # 如果有仓库地址
|
||||
packages=find_packages(),
|
||||
install_requires=[
|
||||
"numpy>=1.20.0",
|
||||
"mujoco>=3.0.0", # 必须依赖
|
||||
"dm_control>=1.0.14", # 之前推荐用来组合地形的库
|
||||
"scipy", # 计算信号处理、统计指标常用
|
||||
"matplotlib", # 用于画出评估图表
|
||||
"pandas", # 用于生成评估报告表格
|
||||
"tqdm", # 显示评估进度条
|
||||
# "gymnasium", # 如果你的接口兼容 gym
|
||||
# "torch", # 如果你需要加载 pytorch 模型
|
||||
],
|
||||
python_requires=">=3.8",
|
||||
|
||||
classifiers=[
|
||||
"Programming Language :: Python :: 3",
|
||||
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
||||
"Topic :: Scientific/Engineering :: Robotics",
|
||||
],
|
||||
)
|
||||
Reference in New Issue
Block a user