Many modifs.

This commit is contained in:
jogima-cyber 2024-11-04 22:27:32 +00:00
parent 2d6ca96367
commit 56c62ae370
13 changed files with 59292 additions and 364 deletions

View File

@ -0,0 +1,239 @@
<mujoco model="go2">
<compiler angle="radian" meshdir="assets" autolimits="true"/>
<option cone="elliptic" impratio="100"/>
<default>
<default class="go2">
<geom friction="0.6" margin="0.001" condim="1"/>
<joint axis="0 1 0" damping="0" 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" solimp="0.015 1 0.031" condim="6"
friction="0.8 0.02 0.01"/>
</default>
</default>
</default>
</default>
<asset>
<hfield name="terrain" nrow="300" ncol="300" size="5 5 1. 0.1"/>
<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="gray" rgba="0.671705 0.692426 0.774270 1"/>
<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"/>
<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>
<geom name="heightfield" pos="0.0 0.0 -0.1" type="hfield" hfield="terrain"/>
<geom name="floor" size="0 0 0.05" type="plane" material="groundplane"/>
<body name="base" pos="0 0 0.445" childclass="go2">
<light pos="0.0 0.0 2.5" />
<camera name="front_camera" mode="fixed" pos="0.321618 0.033305 0.081622" euler="1.57079632679 0. 0." resolution="640 480" fovy="45."/>
<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="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"/>
<site name="FR_foot" pos="0 0 -0.213" size="0.02"/>
</body>
</body>
</body>
<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"/>
<site name="FL_foot" pos="0 0 -0.213" size="0.02"/>
</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"/>
<site name="RR_foot" pos="0 0 -0.213" size="0.02"/>
</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"/>
<site name="RL_foot" pos="0 0 -0.213" size="0.02"/>
</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>
<!-- IMU -->
<accelerometer name="imu_accel" site="imu"/>
<gyro name="imu_gyro" site="imu"/>
<!-- Contact sensors for the feet -->
<touch name="FR_foot_contact" site="FR_foot"/>
<touch name="FL_foot_contact" site="RL_foot"/>
<touch name="RR_foot_contact" site="RR_foot"/>
<touch name="RL_foot_contact" site="RL_foot"/>
</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>

View File

@ -666,71 +666,6 @@ Stephen Brawner (brawner@gmail.com)
<axis xyz="0 0 0"/>
</joint>
<joint name="vicon_joint" type="fixed">
<parent link="base_link"/>
<child link="vicon_link"/>
<origin rpy="-0.037935 -0.066371 -2.668897" xyz="0.022944 -0.000007 0.058458"/>
</joint>
<link name="vicon_link">
<visual>
<origin rpy="0 0 0" xyz="0 0 0"/>
<geometry>
<box size="0.001 0.001 0.001"/>
</geometry>
<!-- <material name="red"/> -->
</visual>
</link>
<joint name="infra1_joint" type="fixed">
<parent link="base_link"/>
<child link="infra1_link"/>
<origin rpy="-1.641669 -0.014332 -1.617155" xyz="0.321618 0.033305 0.081622"/>
</joint>
<link name="infra1_link">
<visual>
<origin rpy="0 0 0" xyz="0 0 0"/>
<geometry>
<box size="0.001 0.001 0.001"/>
</geometry>
<!-- <material name="red"/> -->
</visual>
</link>
<joint name="infra2_joint" type="fixed">
<parent link="base_link"/>
<child link="infra1_link"/>
<origin rpy="-1.640637 -0.014093 -1.611177" xyz="0.317969 -0.061919 0.082773"/>
</joint>
<link name="infra2_link">
<visual>
<origin rpy="0 0 0" xyz="0 0 0"/>
<geometry>
<box size="0.001 0.001 0.001"/>
</geometry>
<!-- <material name="red"/> -->
</visual>
</link>
<joint name="color_joint" type="fixed">
<parent link="base_link"/>
<child link="color_link"/>
<origin rpy="-1.640491 -0.011125 -1.610364" xyz="0.319415 -0.025790 0.082311"/>
</joint>
<link name="color_link">
<visual>
<origin rpy="0 0 0" xyz="0 0 0"/>
<geometry>
<box size="0.001 0.001 0.001"/>
</geometry>
<!-- <material name="red"/> -->
</visual>
</link>
<link name="utlidar">
<inertial>
<origin rpy="0 0 0" xyz="0 0 0"/>

View File

@ -235,6 +235,7 @@ class CaTAgent:
self.gravity_vector = np.zeros(3)
self.dof_pos = np.zeros(12)
self.dof_vel = np.zeros(12)
self.pos = np.zeros(3)
self.body_linear_vel = np.zeros(3)
self.body_angular_vel = np.zeros(3)
self.joint_pos_target = np.zeros(12)
@ -263,9 +264,11 @@ class CaTAgent:
self.prev_dof_vel = self.dof_vel.copy()
self.dof_vel = np.array(joint_state['dq'])[self.unitree_to_policy_map[:, 1]]
self.body_angular_vel = self.robot.getIMU()["gyro"]
self.body_linear_vel = self.robot.getLinVel()
try:
self.foot_contact_forces_mag = self.robot.getFootContact()
self.body_linear_vel = self.robot.getLinVel()
self.pos, _ = self.robot.getPose()
except:
pass
@ -327,7 +330,7 @@ class CaTAgent:
# if self.timestep % 100 == 0:
# print(f"frq: {1 / (time.time() - self.time)} Hz")
self.time = time.time()
obs = self.get_obs()
#obs = self.get_obs()
joint_acc = np.abs(self.prev_dof_vel - self.dof_vel) / self.dt
if self.prev_joint_acc is None:
@ -345,6 +348,7 @@ class CaTAgent:
"joint_vel_target": self.joint_vel_target[np.newaxis, :],
"body_linear_vel": self.body_linear_vel[np.newaxis, :],
"body_angular_vel": self.body_angular_vel[np.newaxis, :],
"body_pos": self.pos[np.newaxis, :].copy(),
"contact_state": self.contact_state[np.newaxis, :],
"body_linear_vel_cmd": self.commands[np.newaxis, 0:2],
"body_angular_vel_cmd": self.commands[np.newaxis, 2:],
@ -356,4 +360,4 @@ class CaTAgent:
}
self.timestep += 1
return obs, None, None, infos
return None, None, None, infos

364
Go2Py/control/cat_parkour.py Executable file
View File

@ -0,0 +1,364 @@
import os
import time
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
import onnxruntime
class RunningMeanStd(nn.Module):
def __init__(self, shape = (), epsilon=1e-08):
super(RunningMeanStd, self).__init__()
self.register_buffer("running_mean", torch.zeros(shape))
self.register_buffer("running_var", torch.ones(shape))
self.register_buffer("count", torch.ones(()))
self.epsilon = epsilon
def forward(self, obs, update = True):
if update:
self.update(obs)
return (obs - self.running_mean) / torch.sqrt(self.running_var + self.epsilon)
def update(self, x):
"""Updates the mean, var and count from a batch of samples."""
batch_mean = torch.mean(x, dim=0)
batch_var = torch.var(x, correction=0, dim=0)
batch_count = x.shape[0]
self.update_from_moments(batch_mean, batch_var, batch_count)
def update_from_moments(self, batch_mean, batch_var, batch_count):
"""Updates from batch mean, variance and count moments."""
self.running_mean, self.running_var, self.count = update_mean_var_count_from_moments(
self.running_mean, self.running_var, self.count, batch_mean, batch_var, batch_count
)
def update_mean_var_count_from_moments(
mean, var, count, batch_mean, batch_var, batch_count
):
"""Updates the mean, var and count using the previous mean, var, count and batch values."""
delta = batch_mean - mean
tot_count = count + batch_count
new_mean = mean + delta * batch_count / tot_count
m_a = var * count
m_b = batch_var * batch_count
M2 = m_a + m_b + torch.square(delta) * count * batch_count / tot_count
new_var = M2 / tot_count
new_count = tot_count
return new_mean, new_var, new_count
def layer_init(layer, std=np.sqrt(2), bias_const=0.0):
torch.nn.init.orthogonal_(layer.weight, std)
torch.nn.init.constant_(layer.bias, bias_const)
return layer
class Agent(nn.Module):
def __init__(self):
super().__init__()
self.critic = nn.Sequential(
layer_init(nn.Linear(45 + 144, 512)),
nn.ELU(),
layer_init(nn.Linear(512, 256)),
nn.ELU(),
layer_init(nn.Linear(256, 128)),
nn.ELU(),
layer_init(nn.Linear(128, 1), std=1.0),
)
self.actor_mean = nn.Sequential(
layer_init(nn.Linear(45 + 144, 512)),
nn.ELU(),
layer_init(nn.Linear(512, 256)),
nn.ELU(),
layer_init(nn.Linear(256, 128)),
nn.ELU(),
layer_init(nn.Linear(128, 12), std=0.01),
)
self.actor_logstd = nn.Parameter(torch.zeros(1, 12))
self.obs_rms = RunningMeanStd(shape = (45 + 144,))
self.value_rms = RunningMeanStd(shape = ())
def get_value(self, x):
return self.critic(x)
def get_action(self, x):
action_mean = self.actor_mean(x)
return action_mean
def forward(self, x):
action_mean = self.actor_mean(self.obs_rms(x, update = False))
return action_mean
class Policy:
def __init__(self, checkpoint_path):
self.agent = Agent()
actor_sd = torch.load(checkpoint_path, map_location="cpu")
self.agent.load_state_dict(actor_sd)
onnx_file_name = checkpoint_path.replace(".pt", ".onnx")
dummy_input = torch.randn(1, 45 + 144)
with torch.no_grad():
torch_out = self.agent(dummy_input)
torch.onnx.export(
self.agent, # The model being converted
dummy_input, # An example input for the model
onnx_file_name, # Output file name
export_params=True, # Store trained parameter weights inside the model file
opset_version=11, # ONNX version (opset) to export to, adjust as needed
do_constant_folding=True, # Whether to perform constant folding optimization
input_names=['input'], # Name of the input in the ONNX graph (can be customized)
output_names=['action'], # Name of the output (assuming get_action and get_value are key outputs)
)
self.ort_session = onnxruntime.InferenceSession(onnx_file_name, providers=["CPUExecutionProvider"])
ort_inputs = {'input': dummy_input.numpy()}
ort_outs = self.ort_session.run(None, ort_inputs)
np.testing.assert_allclose(torch_out.numpy(), ort_outs[0], rtol=1e-03, atol=1e-05)
print("Exported model has been tested with ONNXRuntime, and the result looks good!")
def __call__(self, obs, info):
#with torch.no_grad():
# action = self.agent.get_action(self.agent.obs_rms(obs.unsqueeze(0), update = False))
ort_inputs = {'input': obs[np.newaxis].astype(np.float32)}
ort_outs = self.ort_session.run(None, ort_inputs)
return ort_outs[0]
class CommandInterface:
def __init__(self, limits=None):
self.limits = limits
self.x_vel_cmd, self.y_vel_cmd, self.yaw_vel_cmd = 0.0, 0.0, 0.0
def get_command(self):
command = np.zeros((3,))
command[0] = self.x_vel_cmd
command[1] = self.y_vel_cmd
command[2] = self.yaw_vel_cmd
return command, False
class CaTAgent:
def __init__(self, command_profile, robot):
self.robot = robot
self.command_profile = command_profile
# self.lcm_bridge = LCMBridgeClient(robot_name=self.robot_name)
self.sim_dt = 0.001
self.decimation = 20
self.dt = self.sim_dt * self.decimation
self.timestep = 0
self.device = "cpu"
joint_names = [
"FL_hip_joint",
"FL_thigh_joint",
"FL_calf_joint",
"FR_hip_joint",
"FR_thigh_joint",
"FR_calf_joint",
"RL_hip_joint",
"RL_thigh_joint",
"RL_calf_joint",
"RR_hip_joint",
"RR_thigh_joint",
"RR_calf_joint",
]
policy_joint_names = joint_names
unitree_joint_names = [
"FR_hip_joint",
"FR_thigh_joint",
"FR_calf_joint",
"FL_hip_joint",
"FL_thigh_joint",
"FL_calf_joint",
"RR_hip_joint",
"RR_thigh_joint",
"RR_calf_joint",
"RL_hip_joint",
"RL_thigh_joint",
"RL_calf_joint",
]
policy_to_unitree_map = []
unitree_to_policy_map = []
for i, policy_joint_name in enumerate(policy_joint_names):
id = np.where([name == policy_joint_name for name in unitree_joint_names])[0][0]
policy_to_unitree_map.append((i, id))
self.policy_to_unitree_map = np.array(policy_to_unitree_map).astype(np.uint32)
for i, unitree_joint_name in enumerate(unitree_joint_names):
id = np.where([name == unitree_joint_name for name in policy_joint_names])[0][0]
unitree_to_policy_map.append((i, id))
self.unitree_to_policy_map = np.array(unitree_to_policy_map).astype(np.uint32)
default_joint_angles = {
"FL_hip_joint": 0.1,
"RL_hip_joint": 0.1,
"FR_hip_joint": -0.1,
"RR_hip_joint": -0.1,
"FL_thigh_joint": 0.8,
"RL_thigh_joint": 1.0,
"FR_thigh_joint": 0.8,
"RR_thigh_joint": 1.0,
"FL_calf_joint": -1.5,
"RL_calf_joint": -1.5,
"FR_calf_joint": -1.5,
"RR_calf_joint": -1.5
}
self.default_dof_pos = np.array(
[
default_joint_angles[name]
for name in joint_names
]
)
self.default_dof_pos = self.default_dof_pos
self.p_gains = np.zeros(12)
self.d_gains = np.zeros(12)
for i in range(12):
self.p_gains[i] = 20.0
self.d_gains[i] = 0.5
print(f"p_gains: {self.p_gains}")
self.commands = np.zeros(3)
self.actions = np.zeros((1, 12))
self.last_actions = np.zeros((1,12))
self.gravity_vector = np.zeros(3)
self.dof_pos = np.zeros(12)
self.dof_vel = np.zeros(12)
self.pos = np.zeros(3)
self.body_linear_vel = np.zeros(3)
self.body_angular_vel = np.zeros(3)
self.joint_pos_target = np.zeros(12)
self.joint_vel_target = np.zeros(12)
self.prev_joint_acc = None
self.torques = np.zeros(12)
self.contact_state = np.ones(4)
self.foot_contact_forces_mag = np.zeros(4)
self.prev_foot_contact_forces_mag = np.zeros(4)
self.test = 0
def wait_for_state(self):
# return self.lcm_bridge.getStates(timeout=2)
pass
def get_obs(self):
cmds, reset_timer = self.command_profile.get_command()
self.commands[:] = cmds
# self.state = self.wait_for_state()
joint_state = self.robot.getJointStates()
if joint_state is not None:
self.gravity_vector = self.robot.getGravityInBody()
self.prev_dof_pos = self.dof_pos.copy()
self.dof_pos = np.array(joint_state['q'])[self.unitree_to_policy_map[:, 1]]
self.prev_dof_vel = self.dof_vel.copy()
self.dof_vel = np.array(joint_state['dq'])[self.unitree_to_policy_map[:, 1]]
self.body_angular_vel = self.robot.getIMU()["gyro"]
try:
self.foot_contact_forces_mag = self.robot.getFootContact()
self.body_linear_vel = self.robot.getLinVel()
self.pos, _ = self.robot.getPose()
except:
pass
ob = np.concatenate(
(
self.body_angular_vel * 0.25,
self.commands * np.array([2.0, 2.0, 0.25]),
self.gravity_vector[:, 0],
self.dof_pos * 1.0,
#((self.dof_pos - self.prev_dof_pos) / self.dt) * 0.05,
self.dof_vel * 0.05,
self.last_actions[0],
np.zeros(144)
),
axis=0,
)
#return torch.tensor(ob, device=self.device).float()
return ob
def publish_action(self, action, hard_reset=False):
# command_for_robot = UnitreeLowCommand()
#self.joint_pos_target = (
# action[0, :12].detach().cpu().numpy() * 0.25
#).flatten()
self.joint_pos_target = (
action[0, :12] * 0.25
).flatten()
self.joint_pos_target += self.default_dof_pos
self.joint_vel_target = np.zeros(12)
# command_for_robot.q_des = self.joint_pos_target
# command_for_robot.dq_des = self.joint_vel_target
# command_for_robot.kp = self.p_gains
# command_for_robot.kd = self.d_gains
# command_for_robot.tau_ff = np.zeros(12)
if hard_reset:
command_for_robot.id = -1
self.torques = (self.joint_pos_target - self.dof_pos) * self.p_gains + (
self.joint_vel_target - self.dof_vel
) * self.d_gains
# self.lcm_bridge.sendCommands(command_for_robot)
self.robot.setCommands(self.joint_pos_target[self.policy_to_unitree_map[:, 1]],
self.joint_vel_target[self.policy_to_unitree_map[:, 1]],
self.p_gains[self.policy_to_unitree_map[:, 1]],
self.d_gains[self.policy_to_unitree_map[:, 1]],
np.zeros(12))
def reset(self):
self.actions = torch.zeros((1, 12))
self.time = time.time()
self.timestep = 0
return self.get_obs()
def step(self, actions, hard_reset=False):
self.last_actions = self.actions[:]
self.actions = actions
self.publish_action(self.actions, hard_reset=hard_reset)
# time.sleep(max(self.dt - (time.time() - self.time), 0))
# if self.timestep % 100 == 0:
# print(f"frq: {1 / (time.time() - self.time)} Hz")
self.time = time.time()
#obs = self.get_obs()
joint_acc = np.abs(self.prev_dof_vel - self.dof_vel) / self.dt
if self.prev_joint_acc is None:
self.prev_joint_acc = np.zeros_like(joint_acc)
joint_jerk = np.abs(self.prev_joint_acc - joint_acc) / self.dt
self.prev_joint_acc = joint_acc.copy()
foot_contact_rate = np.abs(self.foot_contact_forces_mag - self.prev_foot_contact_forces_mag)
self.prev_foot_contact_forces_mag = self.foot_contact_forces_mag.copy()
infos = {
"joint_pos": self.dof_pos[np.newaxis, :],
"joint_vel": self.dof_vel[np.newaxis, :],
"joint_pos_target": self.joint_pos_target[np.newaxis, :],
"joint_vel_target": self.joint_vel_target[np.newaxis, :],
"body_linear_vel": self.body_linear_vel[np.newaxis, :],
"body_angular_vel": self.body_angular_vel[np.newaxis, :],
"body_pos": self.pos[np.newaxis, :].copy(),
"contact_state": self.contact_state[np.newaxis, :],
"body_linear_vel_cmd": self.commands[np.newaxis, 0:2],
"body_angular_vel_cmd": self.commands[np.newaxis, 2:],
"torques": self.torques,
"foot_contact_forces_mag": self.foot_contact_forces_mag.copy(),
"joint_acc": joint_acc[np.newaxis, :],
"joint_jerk": joint_jerk[np.newaxis, :],
"foot_contact_rate": foot_contact_rate[np.newaxis, :],
}
self.timestep += 1
return None, None, None, infos

361
Go2Py/control/cat_rnn.py Executable file
View File

@ -0,0 +1,361 @@
import os
import time
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
import onnxruntime
class RunningMeanStd(nn.Module):
def __init__(self, shape = (), epsilon=1e-08):
super(RunningMeanStd, self).__init__()
self.register_buffer("running_mean", torch.zeros(shape))
self.register_buffer("running_var", torch.ones(shape))
self.register_buffer("count", torch.ones(()))
self.epsilon = epsilon
def forward(self, obs, update = True):
if update:
self.update(obs)
return (obs - self.running_mean) / torch.sqrt(self.running_var + self.epsilon)
def update(self, x):
"""Updates the mean, var and count from a batch of samples."""
batch_mean = torch.mean(x, dim=0)
batch_var = torch.var(x, correction=0, dim=0)
batch_count = x.shape[0]
self.update_from_moments(batch_mean, batch_var, batch_count)
def update_from_moments(self, batch_mean, batch_var, batch_count):
"""Updates from batch mean, variance and count moments."""
self.running_mean, self.running_var, self.count = update_mean_var_count_from_moments(
self.running_mean, self.running_var, self.count, batch_mean, batch_var, batch_count
)
def update_mean_var_count_from_moments(
mean, var, count, batch_mean, batch_var, batch_count
):
"""Updates the mean, var and count using the previous mean, var, count and batch values."""
delta = batch_mean - mean
tot_count = count + batch_count
new_mean = mean + delta * batch_count / tot_count
m_a = var * count
m_b = batch_var * batch_count
M2 = m_a + m_b + torch.square(delta) * count * batch_count / tot_count
new_var = M2 / tot_count
new_count = tot_count
return new_mean, new_var, new_count
def layer_init(layer, std=np.sqrt(2), bias_const=0.0):
torch.nn.init.orthogonal_(layer.weight, std)
torch.nn.init.constant_(layer.bias, bias_const)
return layer
class Agent(nn.Module):
def __init__(self):
super().__init__()
self.actor_memory = nn.GRU(45, 256, batch_first=True)
self.critic_memory = nn.GRU(45, 256, batch_first=True)
self.critic = nn.Sequential(
layer_init(nn.Linear(45 + 256, 512)),
nn.ELU(),
layer_init(nn.Linear(512, 256)),
nn.ELU(),
layer_init(nn.Linear(256, 128)),
nn.ELU(),
layer_init(nn.Linear(128, 1), std=1.0),
)
self.actor_mean = nn.Sequential(
layer_init(nn.Linear(45 + 256, 512)),
nn.ELU(),
layer_init(nn.Linear(512, 256)),
nn.ELU(),
layer_init(nn.Linear(256, 128)),
nn.ELU(),
layer_init(nn.Linear(128, 12), std=0.01),
)
self.actor_logstd = nn.Parameter(torch.zeros(1, 12))
self.obs_rms = RunningMeanStd(shape = (45,))
self.value_rms = RunningMeanStd(shape = ())
def forward(self, x, ac_hidden_in):
x = self.obs_rms(x, update = False)
ac_out, ac_hidden_out = self.actor_memory(x, ac_hidden_in)
h = torch.cat([x, ac_out], dim = -1)
action_mean = self.actor_mean(h)
return action_mean, ac_hidden_out
class Policy:
def __init__(self, checkpoint_path):
self.agent = Agent()
actor_sd = torch.load(checkpoint_path, map_location="cpu")
self.agent.load_state_dict(actor_sd)
onnx_file_name = checkpoint_path.replace(".pt", ".onnx")
dummy_input = torch.randn(1, 1, 45)
dummy_hidden = torch.randn(1, 1, 256)
with torch.no_grad():
torch_out, torch_hidden_out = self.agent(dummy_input, dummy_hidden)
torch.onnx.export(
self.agent, # The model being converted
(dummy_input, dummy_hidden), # An example input for the model
onnx_file_name, # Output file name
export_params=True, # Store trained parameter weights inside the model file
opset_version=11, # ONNX version (opset) to export to, adjust as needed
do_constant_folding=True, # Whether to perform constant folding optimization
input_names=['input', 'hidden_in'], # Name of the input in the ONNX graph (can be customized)
output_names=['action', 'hidden_out'], # Name of the output (assuming get_action and get_value are key outputs)
)
self.ort_session = onnxruntime.InferenceSession(onnx_file_name, providers=["CPUExecutionProvider"])
ort_inputs = {'input': dummy_input.numpy(), 'hidden_in': dummy_hidden.numpy()}
ort_outs = self.ort_session.run(None, ort_inputs)
action, hidden_out = ort_outs[0], ort_outs[1]
np.testing.assert_allclose(torch_out.numpy(), action, rtol=1e-03, atol=1e-05)
np.testing.assert_allclose(torch_hidden_out.numpy(), hidden_out, rtol=1e-03, atol=1e-05)
print("Exported model has been tested with ONNXRuntime, and the result looks good!")
def __call__(self, obs, info):
#with torch.no_grad():
# action = self.agent.get_action(self.agent.obs_rms(obs.unsqueeze(0), update = False))
ort_inputs = {'input': obs[np.newaxis].astype(np.float32)}
ort_outs = self.ort_session.run(None, ort_inputs)
return ort_outs[0]
class CommandInterface:
def __init__(self, limits=None):
self.limits = limits
self.x_vel_cmd, self.y_vel_cmd, self.yaw_vel_cmd = 0.0, 0.0, 0.0
def get_command(self):
command = np.zeros((3,))
command[0] = self.x_vel_cmd
command[1] = self.y_vel_cmd
command[2] = self.yaw_vel_cmd
return command, False
class CaTAgent:
def __init__(self, command_profile, robot):
self.robot = robot
self.command_profile = command_profile
# self.lcm_bridge = LCMBridgeClient(robot_name=self.robot_name)
self.sim_dt = 0.001
self.decimation = 20
self.dt = self.sim_dt * self.decimation
self.timestep = 0
self.device = "cpu"
joint_names = [
"FL_hip_joint",
"FL_thigh_joint",
"FL_calf_joint",
"FR_hip_joint",
"FR_thigh_joint",
"FR_calf_joint",
"RL_hip_joint",
"RL_thigh_joint",
"RL_calf_joint",
"RR_hip_joint",
"RR_thigh_joint",
"RR_calf_joint",
]
policy_joint_names = joint_names
unitree_joint_names = [
"FR_hip_joint",
"FR_thigh_joint",
"FR_calf_joint",
"FL_hip_joint",
"FL_thigh_joint",
"FL_calf_joint",
"RR_hip_joint",
"RR_thigh_joint",
"RR_calf_joint",
"RL_hip_joint",
"RL_thigh_joint",
"RL_calf_joint",
]
policy_to_unitree_map = []
unitree_to_policy_map = []
for i, policy_joint_name in enumerate(policy_joint_names):
id = np.where([name == policy_joint_name for name in unitree_joint_names])[0][0]
policy_to_unitree_map.append((i, id))
self.policy_to_unitree_map = np.array(policy_to_unitree_map).astype(np.uint32)
for i, unitree_joint_name in enumerate(unitree_joint_names):
id = np.where([name == unitree_joint_name for name in policy_joint_names])[0][0]
unitree_to_policy_map.append((i, id))
self.unitree_to_policy_map = np.array(unitree_to_policy_map).astype(np.uint32)
default_joint_angles = {
"FL_hip_joint": 0.1,
"RL_hip_joint": 0.1,
"FR_hip_joint": -0.1,
"RR_hip_joint": -0.1,
"FL_thigh_joint": 0.8,
"RL_thigh_joint": 1.0,
"FR_thigh_joint": 0.8,
"RR_thigh_joint": 1.0,
"FL_calf_joint": -1.5,
"RL_calf_joint": -1.5,
"FR_calf_joint": -1.5,
"RR_calf_joint": -1.5
}
self.default_dof_pos = np.array(
[
default_joint_angles[name]
for name in joint_names
]
)
self.default_dof_pos = self.default_dof_pos
self.p_gains = np.zeros(12)
self.d_gains = np.zeros(12)
for i in range(12):
self.p_gains[i] = 40. # 20.0
self.d_gains[i] = 1. # 0.5
print(f"p_gains: {self.p_gains}")
self.commands = np.zeros(3)
self.actions = np.zeros((1, 12))
self.last_actions = np.zeros((1,12))
self.gravity_vector = np.zeros(3)
self.dof_pos = np.zeros(12)
self.dof_vel = np.zeros(12)
self.body_linear_vel = np.zeros(3)
self.body_angular_vel = np.zeros(3)
self.joint_pos_target = np.zeros(12)
self.joint_vel_target = np.zeros(12)
self.prev_joint_acc = None
self.torques = np.zeros(12)
self.contact_state = np.ones(4)
self.foot_contact_forces_mag = np.zeros(4)
self.prev_foot_contact_forces_mag = np.zeros(4)
self.test = 0
def wait_for_state(self):
# return self.lcm_bridge.getStates(timeout=2)
pass
def get_obs(self):
cmds, reset_timer = self.command_profile.get_command()
self.commands[:] = cmds
# self.state = self.wait_for_state()
joint_state = self.robot.getJointStates()
if joint_state is not None:
self.gravity_vector = self.robot.getGravityInBody()
self.prev_dof_pos = self.dof_pos.copy()
self.dof_pos = np.array(joint_state['q'])[self.unitree_to_policy_map[:, 1]]
self.prev_dof_vel = self.dof_vel.copy()
self.dof_vel = np.array(joint_state['dq'])[self.unitree_to_policy_map[:, 1]]
self.body_angular_vel = self.robot.getIMU()["gyro"]
try:
self.foot_contact_forces_mag = self.robot.getFootContact()
self.body_linear_vel = self.robot.getLinVel()
except:
pass
ob = np.concatenate(
(
self.body_angular_vel * 0.25,
self.commands * np.array([2.0, 2.0, 0.25]),
self.gravity_vector[:, 0],
self.dof_pos * 1.0,
#((self.dof_pos - self.prev_dof_pos) / self.dt) * 0.05,
self.dof_vel * 0.05,
self.last_actions[0]
),
axis=0,
)
#return torch.tensor(ob, device=self.device).float()
return ob
def publish_action(self, action, hard_reset=False):
# command_for_robot = UnitreeLowCommand()
#self.joint_pos_target = (
# action[0, :12].detach().cpu().numpy() * 0.25
#).flatten()
self.joint_pos_target = (
action[0, :12] * 0.25
).flatten()
self.joint_pos_target += self.default_dof_pos
self.joint_vel_target = np.zeros(12)
# command_for_robot.q_des = self.joint_pos_target
# command_for_robot.dq_des = self.joint_vel_target
# command_for_robot.kp = self.p_gains
# command_for_robot.kd = self.d_gains
# command_for_robot.tau_ff = np.zeros(12)
if hard_reset:
command_for_robot.id = -1
self.torques = (self.joint_pos_target - self.dof_pos) * self.p_gains + (
self.joint_vel_target - self.dof_vel
) * self.d_gains
# self.lcm_bridge.sendCommands(command_for_robot)
self.robot.setCommands(self.joint_pos_target[self.policy_to_unitree_map[:, 1]],
self.joint_vel_target[self.policy_to_unitree_map[:, 1]],
self.p_gains[self.policy_to_unitree_map[:, 1]],
self.d_gains[self.policy_to_unitree_map[:, 1]],
np.zeros(12))
def reset(self):
self.actions = torch.zeros((1, 12))
self.time = time.time()
self.timestep = 0
return self.get_obs()
def step(self, actions, hard_reset=False):
self.last_actions = self.actions[:]
self.actions = actions
self.publish_action(self.actions, hard_reset=hard_reset)
# time.sleep(max(self.dt - (time.time() - self.time), 0))
# if self.timestep % 100 == 0:
# print(f"frq: {1 / (time.time() - self.time)} Hz")
self.time = time.time()
#obs = self.get_obs()
joint_acc = np.abs(self.prev_dof_vel - self.dof_vel) / self.dt
if self.prev_joint_acc is None:
self.prev_joint_acc = np.zeros_like(joint_acc)
joint_jerk = np.abs(self.prev_joint_acc - joint_acc) / self.dt
self.prev_joint_acc = joint_acc.copy()
foot_contact_rate = np.abs(self.foot_contact_forces_mag - self.prev_foot_contact_forces_mag)
self.prev_foot_contact_forces_mag = self.foot_contact_forces_mag.copy()
infos = {
"joint_pos": self.dof_pos[np.newaxis, :],
"joint_vel": self.dof_vel[np.newaxis, :],
"joint_pos_target": self.joint_pos_target[np.newaxis, :],
"joint_vel_target": self.joint_vel_target[np.newaxis, :],
"body_linear_vel": self.body_linear_vel[np.newaxis, :],
"body_angular_vel": self.body_angular_vel[np.newaxis, :],
"contact_state": self.contact_state[np.newaxis, :],
"body_linear_vel_cmd": self.commands[np.newaxis, 0:2],
"body_angular_vel_cmd": self.commands[np.newaxis, 2:],
"torques": self.torques,
"foot_contact_forces_mag": self.foot_contact_forces_mag.copy(),
"joint_acc": joint_acc[np.newaxis, :],
"joint_jerk": joint_jerk[np.newaxis, :],
"foot_contact_rate": foot_contact_rate[np.newaxis, :],
}
self.timestep += 1
return None, None, None, infos

View File

@ -158,13 +158,24 @@ class Policy:
self.adaptation_module = torch.jit.load(
os.path.join(checkpoint_path, "checkpoints/adaptation_module_latest.jit")
)
self.step_counter = 0
self.perturbation = None
def __call__(self, obs, info):
latent = self.adaptation_module.forward(obs["obs_history"].to("cpu"))
action = self.body.forward(
torch.cat((obs["obs_history"].to("cpu"), latent), dim=-1)
)
info["latent"] = latent
with torch.no_grad():
if self.perturbation is None:
latent = self.adaptation_module.forward(obs["obs_history"].to("cpu"))
self.perturbation = torch.rand_like(latent)*0.9
else:
latent = self.adaptation_module.forward(obs["obs_history"].to("cpu"))+self.perturbation
if self.step_counter%50==0:
self.perturbation = torch.rand_like(latent)*0.9
self.step_counter +=1
print(latent, self.perturbation)
info["latent"] = latent
action = self.body.forward(
torch.cat((obs["obs_history"].to("cpu"), latent), dim=-1)
)
return action

View File

@ -20,7 +20,6 @@ class FrictionModel:
def softSign(self, u, temperature=0.1):
return np.tanh(u/temperature)
class Go2Model:
"""

View File

@ -1,11 +1,27 @@
import time
import numpy as np
class SafetyHypervisor():
def __init__(self, robot):
def __init__(self, robot, max_temperature=70, body_stability_idx=0.7):
self.robot = robot
self.max_temperature = max_temperature
self.normalize = lambda v: v/np.linalg.norm(v)
self.compute_body_stability_idx = lambda v: (self.normalize(v).T@np.array([0,0,-1]))
self.body_stability_idx = body_stability_idx
def unsafe(self):
if self.robot.simulated:
return False
else:
state = self.robot.getJointStates()
temperature = state['temperature']
if np.any(np.array(temperature)>self.max_temperature):
print(f'One of the actuators reached max temperature of {self.max_temperature}')
return True
if self.compute_body_stability_idx(self.robot.getGravityInBody()) < self.body_stability_idx:
print('Body is tilted too much! deactivating the controller')
return True
return False
def controlTimeout(self):

View File

@ -425,6 +425,11 @@ class Go2Sim:
body_v = world_R_body.T@self.data.qvel[0:3].reshape(3,1)
return body_v
def getFootVelInWorld(self, site_name):
Jp, Jr = self.getSiteJacobian(site_name)
foot_vel = Jp@self.data.qvel.reshape(-1,1)
return foot_vel
def stepHighlevel(self, vx, vy, omega_z, body_z_offset=0, step_height = 0.08, kp=[2, 0.5, 0.5], ki=[0.02, 0.01, 0.01]):
policy_info = {}
if self.step_counter % (self.control_dt // self.dt) == 0:

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long