Go2Py_SIM/Go2Py/sim/isaacsim/go2.py

273 lines
9.5 KiB
Python

from collections import deque
from typing import Optional
import numpy as np
from omni.isaac.core.articulations import Articulation
from omni.isaac.core.utils.prims import define_prim
from omni.isaac.sensor import ContactSensor, IMUSensor
import Go2Py
from Go2Py.sim.isaacsim.lcm_types.unitree_lowlevel import UnitreeLowState
class UnitreeGo2(Articulation):
def __init__(
self,
prim_path: str,
name: str = "go2_quadruped",
physics_dt: Optional[float] = 1 / 400.0,
position: Optional[np.ndarray] = None,
orientation: Optional[np.ndarray] = None,
usd_path: Optional[str] = None,
) -> None:
"""
[Summary]
initialize robot, set up sensors and interfaces
Args:
prim_path {str} -- prim path of the robot on the stage
name {str} -- name of the quadruped
physics_dt {float} -- physics downtime of the controller
position {np.ndarray} -- position of the robot
orientation {np.ndarray} -- orientation of the robot
"""
self._prim_path = prim_path
prim = define_prim(self._prim_path, "Xform")
prim.GetReferences().AddReference(Go2Py.GO2_USD_PATH)
self.usd_path = usd_path
if self.usd_path is None:
prim.GetReferences().AddReference(Go2Py.GO2_USD_PATH)
else:
prim.GetReferences().AddReference(self.usd_path)
super().__init__(
prim_path=self._prim_path,
name=name,
position=position,
orientation=orientation,
)
# contact sensor setup
self.feet_order = ["FL", "RL", "FR", "RR"]
self.feet_path = [
self._prim_path + "/FL_foot",
self._prim_path + "/FR_foot",
self._prim_path + "/RL_foot",
self._prim_path + "/RR_foot",
]
self.color = [(1, 0, 0, 1), (0, 1, 0, 1), (0, 0, 1, 1), (1, 1, 0, 1)]
self._contact_sensors = [None] * 4
for i in range(4):
self._contact_sensors[i] = ContactSensor(
prim_path=self.feet_path[i] + "/sensor",
min_threshold=0,
max_threshold=1000000,
radius=0.043,
dt=physics_dt,
)
self.foot_force = np.zeros(4)
self.enable_foot_filter = True
self._FILTER_WINDOW_SIZE = 20
self._foot_filters = [deque(), deque(), deque(), deque()]
# imu sensor setup
self.imu_path = self._prim_path + "/imu_link"
self._imu_sensor = IMUSensor(
prim_path=self.imu_path + "/imu_sensor",
name="imu",
dt=physics_dt,
translation=np.array([0, 0, 0]),
orientation=np.array([1, 0, 0, 0]),
)
self.accel = np.zeros((3,))
self.gyro = np.zeros((3,))
# Translation maps between Isaac and Bullet
# self.bullet_joint_order = ['FL_hip_joint', 'FL_thigh_joint', 'FL_calf_joint',
# 'RL_hip_joint', 'RL_thigh_joint', 'RL_calf_joint',
# 'FR_hip_joint', 'FR_thigh_joint', 'FR_calf_joint',
# 'RR_hip_joint', 'RR_thigh_joint', 'RR_calf_joint']
self.bullet_joint_order = [
"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",
]
self.isaac_joint_order = [
"FL_hip_joint",
"FR_hip_joint",
"RL_hip_joint",
"RR_hip_joint",
"FL_thigh_joint",
"FR_thigh_joint",
"RL_thigh_joint",
"RR_thigh_joint",
"FL_calf_joint",
"FR_calf_joint",
"RL_calf_joint",
"RR_calf_joint",
]
self.isaac_name_2_index = {s: i for i, s in enumerate(self.isaac_joint_order)}
self.bullet_name_2_index = {s: i for i, s in enumerate(self.bullet_joint_order)}
self.to_bullet_index = np.array(
[self.isaac_name_2_index[id] for id in self.bullet_joint_order]
)
self.to_isaac_index = np.array(
[self.bullet_name_2_index[id] for id in self.isaac_joint_order]
)
self.tau_est = np.zeros((12,))
self.state = UnitreeLowState()
self.init_pos = np.array([0.0, 0.0, 0.6])
self.init_quat = np.array([0.0, 0.0, 0.0, 1.0])
self.init_joint_pos = np.array(
[0.2, 0.8, -1.5, -0.2, 0.8, -1.5, 0.2, 1.0, -1.6, -0.2, 1.0, -1.6]
)
return
def toIsaacOrder(self, x):
return x[self.to_isaac_index, ...]
def toBulletOrder(self, x):
return x[self.to_bullet_index, ...]
def setState(self, pos, quat, q) -> None:
"""[Summary]
Set the kinematic state of the robot.
Args:
pos {ndarray} -- The position of the robot (x, y, z)
quat {ndarray} -- The orientation of the robot (qx, qy, qz, qw)
q {ndarray} -- Joint angles of the robot in standard Pinocchio order
Raises:
RuntimeError: When the DC Toolbox interface has not been configured.
"""
self.set_world_pose(position=pos, orientation=quat[[3, 0, 1, 2]])
self.set_linear_velocity(np.zeros((3,)))
self.set_angular_velocity(np.zeros((3,)))
self.set_joint_positions(
positions=np.asarray(self.toIsaacOrder(q), dtype=np.float32)
)
self.set_joint_velocities(
velocities=np.asarray(self.toIsaacOrder(np.zeros((12,))), dtype=np.float32)
)
self.set_joint_efforts(np.zeros_like(q))
return
def initialize(self, physics_sim_view=None) -> None:
"""[summary]
initialize dc interface, set up drive mode and initial robot state
"""
super().initialize(physics_sim_view=physics_sim_view)
self.get_articulation_controller().set_effort_modes("force")
self.get_articulation_controller().switch_control_mode("effort")
self.setState(self.init_pos, self.init_quat, self.init_joint_pos)
for i in range(4):
self._contact_sensors[i].initialize()
return
def readContactSensor(self) -> None:
"""[summary]
Updates processed contact sensor data from the robot feet, store them in member variable foot_force
"""
# Order: FL, RL, FR, RR
for i in range(len(self.feet_path)):
frame = self._contact_sensors[i].get_current_frame()
if "force" in frame:
if self.enable_foot_filter:
self._foot_filters[i].append(frame["force"])
if len(self._foot_filters[i]) > self._FILTER_WINDOW_SIZE:
self._foot_filters[i].popleft()
self.foot_force[i] = np.mean(self._foot_filters[i])
else:
self.foot_force[i] = frame["force"]
return self.foot_force
def readIMUSensor(self) -> None:
"""[summary]
Updates processed imu sensor data from the robot body, store them in member variable base_lin and ang_vel
"""
frame = self._imu_sensor.get_current_frame()
self.accel = frame["lin_acc"]
self.gyro = frame["ang_vel"]
return self.accel, self.gyro
def readStates(self):
contact_forces = self.readContactSensor()
accel, gyro = self.readIMUSensor()
# joint pos and vel from the DC interface
joint_state = super().get_joints_state()
joint_pos = self.toBulletOrder(joint_state.positions)
joint_vel = self.toBulletOrder(joint_state.velocities)
# base frame
base_pose = self.get_world_pose()
base_pos = base_pose[0]
base_quat = base_pose[1][[1, 2, 3, 0]]
base_lin_vel = self.get_linear_velocity()
base_ang_vel = self.get_angular_velocity()
# assign to state objects
self.state.accel = accel.tolist()
self.state.gyro = gyro.tolist()
self.state.q = joint_pos.tolist()
self.state.dq = joint_vel.tolist()
self.state.tau_est = self.tau_est
self.state.quaternion = base_quat.tolist()
self.state.gt_pos = base_pos.tolist()
self.state.gt_quat = base_quat.tolist()
self.state.gt_lin_vel = base_lin_vel.tolist()
self.state.gt_ang_vel = base_ang_vel.tolist()
self.state.contact_state = contact_forces.tolist()
# projected_gravity = pp.SO3(base_quat).matrix().T @ np.array(
# [0.0, 0.0, -1.0]
# ).reshape(3, 1)
# self.state.gravity = (
# projected_gravity.squeeze().numpy().astype(np.float32).tolist()
# )
return self.state
def setCommands(self, cmd):
"""[summary]
sets the joint torques
Argument:
action {np.ndarray} -- Joint torque command
"""
q = np.array(self.state.q)
dq = np.array(self.state.dq)
kp = np.array(cmd.kp)
kd = np.array(cmd.kd)
tau_ff = np.array(cmd.tau_ff)
q_des = np.array(cmd.q_des)
dq_des = np.array(cmd.dq_des)
tau = (q_des - q) * kp + (dq_des - dq) * kd + tau_ff
self.set_joint_efforts(np.asarray(self.toIsaacOrder(tau), dtype=np.float32))
self.tau_est = tau
return
def step(self, cmd):
self.readStates()
self.setCommands(cmd)
return self.state