Isaac Gym
Qmini Robot Training Series - Tune URDF
Author
Tony Wang
Date Published
1# This file is used to tune PID without RL learning, run it to see how well it will behave2# Parameters 'p' & 'd' can be changed in line 118 & 1193# Data will be saved in foldr 'excel/tune_PID'4import time5import math6import numpy as np7from isaacgym import gymapi, gymutil, gymtorch8import os9from math import pi10import matplotlib.pyplot as plt11import pandas as pd12import warnings13from os.path import join14from collections import deque15from env.legged_robot import get_euler_xyz, to_torch16import torch1718warnings.filterwarnings('ignore')192021def _from_2pi_to_pi(rpy):22 return rpy.cpu() - 2 * pi * np.floor((rpy.cpu() + pi) / (2 * pi))232425def get_euler_from_quat(quat):26 base_rpy = get_euler_xyz(quat)27 r = to_torch(_from_2pi_to_pi(base_rpy[0]))28 p = to_torch(_from_2pi_to_pi(base_rpy[1]))29 y = to_torch(_from_2pi_to_pi(base_rpy[2]))30 return torch.t(torch.vstack((r, p, y)))313233# Z-up axis in this file, (x,y,z)3435def clamp(x, min_value, max_value):36 return max(min(x, max_value), min_value)373839def exp_filter(history, present, weight):40 """41 exponential filter42 result = history * weight + present * (1. - weight)43 """44 return history * weight + present * (1. - weight)454647# initialize gym48gym = gymapi.acquire_gym()4950# configure sim51sim_params = gymapi.SimParams()52sim_params.dt = 0.001 # dt*action_repeat=0.0153sim_params.up_axis = gymapi.UP_AXIS_Z54sim_params.gravity = gymapi.Vec3(0.0, 0.0, -9.81) # -9.8155# sim_params.gravity = gymapi.Vec3(0.0, 0.0, -0) # -9.815657sim_params.substeps = 158sim_params.use_gpu_pipeline = False59print("WARNING: Forcing CPU pipeline.")6061# physx parameters62sim_params.physx.solver_type = 163sim_params.physx.num_position_iterations = 464sim_params.physx.num_velocity_iterations = 065sim_params.physx.num_threads = 1066sim_params.physx.contact_offset = 0.0167sim_params.physx.rest_offset = 0.068sim_params.physx.bounce_threshold_velocity = 0.569sim_params.physx.max_depenetration_velocity = 1.070sim_params.physx.max_gpu_contact_pairs = 2 ** 2371sim_params.physx.default_buffer_size_multiplier = 572sim_params.physx.use_gpu = True7374asset_options = gymapi.AssetOptions()75asset_options.angular_damping = 0.76asset_options.collapse_fixed_joints = True77asset_options.default_dof_drive_mode = 378asset_options.density = 0.00179asset_options.max_angular_velocity = 100.80asset_options.replace_cylinder_with_capsule = True81asset_options.thickness = 0.0182asset_options.flip_visual_attachments = False8384render = True85asset_options.fix_base_link = True # todo "on rack if True"8687asset_options.use_mesh_materials = False # color!!! False have color88sim = gym.create_sim(0, 0, gymapi.SIM_PHYSX, sim_params)89if sim is None:90 print("*** Failed to create sim")91 quit()9293plane_params = gymapi.PlaneParams()94plane_params.normal = gymapi.Vec3(0, 0, 1)95plane_params.restitution = 096gym.add_ground(sim, plane_params)9798# create viewer99if render:100 viewer = gym.create_viewer(sim, gymapi.CameraProperties())101 if viewer is None:102 print("*** Failed to create viewer")103 quit()104105# load asset106asset_path = "assets/q1/urdf/q1.urdf"107108asset_root = os.path.dirname(asset_path)109asset_file = os.path.basename(asset_path)110111asset = gym.load_asset(sim, asset_root, asset_file, asset_options)112113# some parameters from 'rhloc_v1/tune_PID'114action_repeat = 15115dt = action_repeat * sim_params.dt116action, motor_position, motor_torque, motor_velocity = [], [], [], []117118pose = gymapi.Transform()119pose.p = gymapi.Vec3(-0., 0., 0.6) # todo base init position120121pose.r = gymapi.Quat(0, -0, -0, 1) # actor init orientation122123# set up the env grid124num_envs = 1125num_per_row = 1126spacing = 3.127env_lower = gymapi.Vec3(-spacing, -spacing, 0.0)128env_upper = gymapi.Vec3(spacing, spacing, spacing)129if render:130 cam_pos = gymapi.Vec3(1, -1., 0.55)131 cam_target = gymapi.Vec3(0, 0, 0.55)132 gym.viewer_camera_look_at(viewer, None, cam_pos, cam_target)133134# get array of DOF properties135init_dof_props = gym.get_asset_dof_properties(asset)136137init_dof_props['driveMode'].fill(gymapi.DOF_MODE_EFFORT)138139joint_pos_lower_limit = init_dof_props['lower']140joint_pos_upper_limit = init_dof_props['upper']141torque_lower_limits = -init_dof_props["effort"]142torque_upper_limits = init_dof_props["effort"]143144# create an array of DOF states that will be used to update the actors145num_dofs = gym.get_asset_dof_count(asset)146names_dofs = gym.get_asset_dof_names(asset)147148kp = torch.zeros(num_dofs, dtype=torch.float, requires_grad=False)149kd = torch.zeros(num_dofs, dtype=torch.float, requires_grad=False)150kp_real = torch.zeros(num_dofs, dtype=torch.float, requires_grad=False)151kd_real = torch.zeros(num_dofs, dtype=torch.float, requires_grad=False)152153#154stiffness = {'hip_yaw': 50., 'hip_roll': 100., 'hip_pitch': 70., 'knee': 40., 'ankle': 25.} # [N*m/rad]155damping = {'hip_yaw': 0.3, 'hip_roll': 2.5, 'hip_pitch': 0.3, 'knee': 0.5, 'ankle': 0.25} # [N*m*s/rad]156157plt_id = list(range(0, 10))158159joint_tor_offset = torch.tensor([0.6, 1, 0., 0.7, 0.] + [-0.6, -1, -0., -0.7, 0.], dtype=torch.float)160joint_vel_sign = torch.tensor([0., 1, 0., 0., 0.] * 2, dtype=torch.float)161162s_nsteps_ago = -30163enable_d_ctrl, a_nsteps_agp = 0., -1164165for i in range(num_dofs):166 for gain_name in stiffness:167 if gain_name in names_dofs[i]:168 kp[i] = stiffness[gain_name]169 kd[i] = damping[gain_name]170 kp_real[i] = stiffness[gain_name]171 kd_real[i] = damping[gain_name]172debug_dir = join('experiments', 'tune_urdf') # sin_test birl10_on_rack173ref_act = torch.tensor([0.4, 0.02, -2.3, -0.1, -1.4,174 -0.4, -0.02, 2.3, 0.1, 1.4], dtype=torch.float)175176os.makedirs(debug_dir, exist_ok=True)177torque_limits = torch.zeros(num_dofs, dtype=torch.float, requires_grad=False)178for i in range(num_dofs):179 torque_limits[i] = init_dof_props["effort"][i].item()180181env = gym.create_env(sim, env_lower, env_upper, num_per_row)182actor_handle = gym.create_actor(env, asset, pose, "actor", 0, 1)183# reload parameters184gym.set_actor_dof_properties(env, actor_handle, init_dof_props)185dof_props = gym.get_actor_dof_properties(env, actor_handle)186gym.enable_actor_dof_force_sensors(env, actor_handle)187188189def compute_torques(actions, joint_pos, joint_vel, joint_pos_inc):190 error = actions - joint_pos191 torques = kp * error + kd * (joint_pos_inc * enable_d_ctrl - joint_vel) - 3.5 * torch.sign(joint_vel) * joint_vel_sign + joint_tor_offset192 return torch.clip(torques, torch.tensor(torque_lower_limits), torch.tensor(torque_upper_limits))193194195reset_act = ref_act.clone() # torch.tensor([0.] * num_dofs, dtype=torch.float)196init_dof_states = np.array(reset_act, dtype=gymapi.DofState.dtype)197init_dof_vel = np.array([0] * num_dofs, dtype=gymapi.DofState.dtype)198199gym.set_actor_dof_states(env, actor_handle, init_dof_states, gymapi.STATE_POS)200# set init dof velocity201gym.set_actor_dof_states(env, actor_handle, init_dof_vel, gymapi.STATE_VEL)202203dof_state_tensor = gym.acquire_dof_state_tensor(sim)204dof_states = gymtorch.wrap_tensor(dof_state_tensor)205206actor_root_state = gym.acquire_actor_root_state_tensor(sim)207root_states = gymtorch.wrap_tensor(actor_root_state)208209gym.simulate(sim)210gym.fetch_results(sim, True)211gym.refresh_dof_force_tensor(sim)212gym.refresh_dof_state_tensor(sim)213214joint_pos = dof_states.view(num_dofs, 2)[..., 0]215joint_vel = dof_states.view(num_dofs, 2)[..., 1]216base_quat = root_states[:, 3:7]217218noisy_joint_pos = joint_pos.clone()219noisy_joint_vel = joint_vel.clone()220joint_pos_his, joint_vel_his, joint_tau_his, joint_act_his = deque(maxlen=150), deque(maxlen=150), deque(maxlen=150), deque(maxlen=150)221torques = compute_torques(torch.tensor(reset_act), joint_pos, joint_vel, joint_pos * 0.)222223start = time.time()224act = reset_act225226act = reset_act.clone()227act_inc = torch.tensor([0.] * num_dofs, dtype=torch.float)228229for _ in range(joint_pos_his.maxlen):230 joint_pos_his.append(joint_pos.clone())231 joint_vel_his.append(joint_vel.clone())232 joint_tau_his.append(torques.clone())233 joint_act_his.append(joint_pos.clone())234joint_pos_noise = (2. * torch.rand_like(joint_pos) - 1.) * 0.1235joint_vel_noise = (2. * torch.rand_like(joint_pos) - 1.) * 1236joint_tau_noise = (2. * torch.rand_like(joint_pos) - 1.) * 1237while True:238 act = ref_act239 joint_act_his.append(act.clone())240 for i in range(action_repeat):241 joint_pos_noise = (2. * torch.rand_like(joint_pos) - 1.) * 0.242 joint_vel_noise = (2. * torch.rand_like(joint_pos) - 1.) * 0243 joint_tau_noise = (2. * torch.rand_like(joint_pos) - 1.) * 0244 selected_actions = joint_act_his[a_nsteps_agp]245246 torques = compute_torques(selected_actions,247 joint_pos_his[a_nsteps_agp].clone(),248 joint_vel_his[a_nsteps_agp].clone(),249 act_inc.clone())250 gym.set_dof_actuation_force_tensor(sim, gymtorch.unwrap_tensor(torques))251 gym.simulate(sim)252 gym.fetch_results(sim, True)253 gym.refresh_dof_force_tensor(sim)254 gym.refresh_dof_state_tensor(sim)255 gym.refresh_actor_root_state_tensor(sim)256257 mt = gym.get_actor_dof_forces(env, actor_handle)258 joint_pos_filtered = exp_filter(joint_pos_his[-1], joint_pos + joint_pos_noise, 0.)259 joint_vel_filtered = exp_filter(joint_vel_his[-1], joint_vel + joint_vel_noise, 0.)260 joint_tau_filtered = exp_filter(joint_tau_his[-1], torques + joint_tau_noise, 0.)261262 joint_pos_his.append(joint_pos_filtered.clone())263 joint_vel_his.append(joint_vel_filtered.clone())264 joint_tau_his.append(joint_tau_filtered.clone())265 base_quat = root_states[:, 3:7]266 base_rpy = get_euler_from_quat(base_quat)267 # print("base_quat", base_quat)268 # print("base_rpy:", base_rpy)269270 noisy_joint_pos = joint_pos_his[s_nsteps_ago]271 noisy_joint_vel = joint_vel_his[s_nsteps_ago]272 noisy_joint_tau = joint_tau_his[s_nsteps_ago]273 action.append(act.tolist().copy())274 mp = noisy_joint_pos.tolist().copy()275 motor_position.append(mp)276 mv = noisy_joint_vel.tolist().copy()277 motor_velocity.append(mv)278 motor_torque.append(noisy_joint_tau.tolist().copy())279 if render:280 # update the viewer281 gym.step_graphics(sim)282 gym.draw_viewer(viewer, sim, True)283 gym.sync_frame_time(sim)284285end = time.time()286print(end - start)287print("Done")288289# convert them from 1 dimension to 2 dimension290joint_action = np.stack(action.copy())291joint_position = np.stack(motor_position.copy())292joint_velocity = np.stack(motor_velocity.copy())293joint_torque = np.stack(motor_torque.copy())294295path = join(debug_dir, f'tune_urdf.xlsx')296with pd.ExcelWriter(path) as f:297 pd.DataFrame(np.hstack([joint_action]), columns=names_dofs).to_excel(f, 'joint_act', index=False)298 pd.DataFrame(np.hstack([joint_position]), columns=names_dofs).to_excel(f, 'joint_pos', index=False)299 pd.DataFrame(np.hstack([joint_velocity]), columns=names_dofs).to_excel(f, 'joint_vel', index=False)300 pd.DataFrame(np.hstack([joint_torque]), columns=names_dofs).to_excel(f, 'joint_tau', index=False)301print(f"Debug data has been saved to {path}.")302303if render:304 gym.destroy_viewer(viewer)305gym.destroy_sim(sim)306