I recommend the MuJoCo simulator.
Its free and open source and they have a great model of a dog.
The python API does not tell you which sensors & muscles are which. Instead it gives you a list of unnamed numbers. For RL-algorithms that is fine, but that’s not helpful for our purposes and it’s clearly not how real animals work either. So to fix this I wrote a snippet of python code to convert the arrays into dictionaries:
dog_body_interface.py
import numpy as np
import math
joint_names = (
'L_1_extend',
'L_1_bend',
'L_2_twist',
'L_3_extend',
'L_3_bend',
'L_4_twist',
'L_5_extend',
'L_5_bend',
'L_6_twist',
'L_7_extend',
'L_7_bend',
'hip_L_supinate',
'hip_L_abduct',
'hip_L_extend',
'knee_L',
'ankle_L',
'toe_L',
'hip_R_supinate',
'hip_R_abduct',
'hip_R_extend',
'knee_R',
'ankle_R',
'toe_R',
'Ca_1_extend',
'Ca_2_bend',
'Ca_3_extend',
'Ca_4_bend',
'Ca_5_extend',
'Ca_6_bend',
'Ca_7_extend',
'Ca_8_bend',
'Ca_9_extend',
'Ca_10_bend',
'Ca_11_extend',
'Ca_12_bend',
'Ca_13_extend',
'Ca_14_bend',
'Ca_15_extend',
'Ca_16_bend',
'Ca_17_extend',
'Ca_18_bend',
'Ca_19_extend',
'Ca_20_bend',
'Ca_21_extend',
'C_7_extend',
'C_7_bend',
'C_6_twist',
'C_5_extend',
'C_5_bend',
'C_4_twist',
'C_3_extend',
'C_3_bend',
'C_2_twist',
'C_1_extend',
'C_1_bend',
'atlas',
'mandible',
'scapula_L_supinate',
'scapula_L_abduct',
'scapula_L_extend',
'shoulder_L_supinate',
'shoulder_L_extend',
'elbow_L',
'wrist_L',
'finger_L',
'scapula_R_supinate',
'scapula_R_abduct',
'scapula_R_extend',
'shoulder_R_supinate',
'shoulder_R_extend',
'elbow_R',
'wrist_R',
'finger_R')
actuator_names = (
'lumbar_extend',
'lumbar_bend',
'lumbar_twist',
'cervical_extend',
'cervical_bend',
'cervical_twist',
'caudal_extend',
'caudal_bend',
'hip_L_supinate',
'hip_L_abduct',
'hip_L_extend',
'knee_L',
'ankle_L',
'toe_L',
'hip_R_supinate',
'hip_R_abduct',
'hip_R_extend',
'knee_R',
'ankle_R',
'toe_R',
'atlas',
'mandible',
'scapula_L_supinate',
'scapula_L_abduct',
'scapula_L_extend',
'shoulder_L_supinate',
'shoulder_L_extend',
'elbow_L',
'wrist_L',
'finger_L',
'scapula_R_supinate',
'scapula_R_abduct',
'scapula_R_extend',
'shoulder_R_supinate',
'shoulder_R_extend',
'elbow_R',
'wrist_R',
'finger_R')
foot_names = ('HL', 'HR', 'FL', 'FR')
touch_sensor_names = ('FL', 'FR', 'HL', 'HR')
def combine_vertebrae(joint_data):
""" Combine all of the sensory measurements from the vertebrae joints. """
joint_data['lumbar_extend'] = (
joint_data['L_1_extend'] +
joint_data['L_3_extend'] +
joint_data['L_5_extend'] +
joint_data['L_7_extend'])
joint_data['lumbar_bend'] = (
joint_data['L_1_bend'] +
joint_data['L_3_bend'] +
joint_data['L_5_bend'] +
joint_data['L_7_bend'])
joint_data['lumbar_twist'] = (
joint_data['L_2_twist'] +
joint_data['L_4_twist'] +
joint_data['L_6_twist'])
joint_data['cervical_extend'] = (
joint_data['C_1_extend'] +
joint_data['C_3_extend'] +
joint_data['C_5_extend'] +
joint_data['C_7_extend'])
joint_data['cervical_bend'] = (
joint_data['C_1_bend'] +
joint_data['C_3_bend'] +
joint_data['C_5_bend'] +
joint_data['C_7_bend'])
joint_data['cervical_twist'] = (
joint_data['C_2_twist'] +
joint_data['C_4_twist'] +
joint_data['C_6_twist'])
joint_data['caudal_extend'] = (
joint_data['Ca_1_extend'] +
joint_data['Ca_3_extend'] +
joint_data['Ca_5_extend'] +
joint_data['Ca_7_extend'] +
joint_data['Ca_9_extend'] +
joint_data['Ca_11_extend'] +
joint_data['Ca_13_extend'] +
joint_data['Ca_15_extend'] +
joint_data['Ca_17_extend'] +
joint_data['Ca_19_extend'] +
joint_data['Ca_21_extend'])
joint_data['caudal_bend'] = (
joint_data['Ca_2_bend'] +
joint_data['Ca_4_bend'] +
joint_data['Ca_6_bend'] +
joint_data['Ca_8_bend'] +
joint_data['Ca_10_bend'] +
joint_data['Ca_12_bend'] +
joint_data['Ca_14_bend'] +
joint_data['Ca_16_bend'] +
joint_data['Ca_18_bend'] +
joint_data['Ca_20_bend'])
class SensoryInput:
def __init__(self, sensory_input): # This argument is the "time_step.observation"
self.joint_pos = dict(zip(joint_names, np.rad2deg(sensory_input['joint_angles'])))
self.joint_vel = dict(zip(joint_names, np.rad2deg(sensory_input['joint_velocites'])))
combine_vertebrae(self.joint_pos)
combine_vertebrae(self.joint_vel)
self.actuator_state = dict(zip(actuator_names, sensory_input['actuator_state']))
self.foot_forces = dict(zip(foot_names, np.array(sensory_input['foot_forces']).reshape(4,-1)))
self.touch_sensors = dict(zip(touch_sensor_names, sensory_input['touch_sensors']))
self.torso_com_velocity = sensory_input['torso_com_velocity'] # Velocity at the Center-Of-Mass
self.torso_height = sensory_input['torso_pelvis_height'][0]
self.pelivis_height = sensory_input['torso_pelvis_height'][1]
self.accelerometer = sensory_input['inertial_sensors'][0:3]
self.velocimeter = sensory_input['inertial_sensors'][3:6]
self.gyro = sensory_input['inertial_sensors'][6:9]
(self.head_yaw, self.head_roll, self.head_pitch,
self.chest_yaw, self.chest_roll, self.chest_pitch,
self.pelvis_yaw, self.pelvis_roll, self.pelvis_pitch) = np.rad2deg(np.arcsin(sensory_input['z_projection']))