in safety_gym/envs/engine.py [0:0]
def build_observation_space(self):
''' Construct observtion space. Happens only once at during __init__ '''
obs_space_dict = OrderedDict() # See self.obs()
if self.observe_freejoint:
obs_space_dict['freejoint'] = gym.spaces.Box(-np.inf, np.inf, (7,), dtype=np.float32)
if self.observe_com:
obs_space_dict['com'] = gym.spaces.Box(-np.inf, np.inf, (3,), dtype=np.float32)
if self.observe_sensors:
for sensor in self.sensors_obs: # Explicitly listed sensors
dim = self.robot.sensor_dim[sensor]
obs_space_dict[sensor] = gym.spaces.Box(-np.inf, np.inf, (dim,), dtype=np.float32)
# Velocities don't have wraparound effects that rotational positions do
# Wraparounds are not kind to neural networks
# Whereas the angle 2*pi is very close to 0, this isn't true in the network
# In theory the network could learn this, but in practice we simplify it
# when the sensors_angle_components switch is enabled.
for sensor in self.robot.hinge_vel_names:
obs_space_dict[sensor] = gym.spaces.Box(-np.inf, np.inf, (1,), dtype=np.float32)
for sensor in self.robot.ballangvel_names:
obs_space_dict[sensor] = gym.spaces.Box(-np.inf, np.inf, (3,), dtype=np.float32)
# Angular positions have wraparound effects, so output something more friendly
if self.sensors_angle_components:
# Single joints are turned into sin(x), cos(x) pairs
# These should be easier to learn for neural networks,
# Since for angles, small perturbations in angle give small differences in sin/cos
for sensor in self.robot.hinge_pos_names:
obs_space_dict[sensor] = gym.spaces.Box(-np.inf, np.inf, (2,), dtype=np.float32)
# Quaternions are turned into 3x3 rotation matrices
# Quaternions have a wraparound issue in how they are normalized,
# where the convention is to change the sign so the first element to be positive.
# If the first element is close to 0, this can mean small differences in rotation
# lead to large differences in value as the latter elements change sign.
# This also means that the first element of the quaternion is not expectation zero.
# The SO(3) rotation representation would be a good replacement here,
# since it smoothly varies between values in all directions (the property we want),
# but right now we have very little code to support SO(3) roatations.
# Instead we use a 3x3 rotation matrix, which if normalized, smoothly varies as well.
for sensor in self.robot.ballquat_names:
obs_space_dict[sensor] = gym.spaces.Box(-np.inf, np.inf, (3, 3), dtype=np.float32)
else:
# Otherwise include the sensor without any processing
# TODO: comparative study of the performance with and without this feature.
for sensor in self.robot.hinge_pos_names:
obs_space_dict[sensor] = gym.spaces.Box(-np.inf, np.inf, (1,), dtype=np.float32)
for sensor in self.robot.ballquat_names:
obs_space_dict[sensor] = gym.spaces.Box(-np.inf, np.inf, (4,), dtype=np.float32)
if self.task == 'push':
if self.observe_box_comp:
obs_space_dict['box_compass'] = gym.spaces.Box(-1.0, 1.0, (self.compass_shape,), dtype=np.float32)
if self.observe_box_lidar:
obs_space_dict['box_lidar'] = gym.spaces.Box(0.0, 1.0, (self.lidar_num_bins,), dtype=np.float32)
if self.observe_goal_dist:
obs_space_dict['goal_dist'] = gym.spaces.Box(0.0, 1.0, (1,), dtype=np.float32)
if self.observe_goal_comp:
obs_space_dict['goal_compass'] = gym.spaces.Box(-1.0, 1.0, (self.compass_shape,), dtype=np.float32)
if self.observe_goal_lidar:
obs_space_dict['goal_lidar'] = gym.spaces.Box(0.0, 1.0, (self.lidar_num_bins,), dtype=np.float32)
if self.task == 'circle' and self.observe_circle:
obs_space_dict['circle_lidar'] = gym.spaces.Box(0.0, 1.0, (self.lidar_num_bins,), dtype=np.float32)
if self.observe_remaining:
obs_space_dict['remaining'] = gym.spaces.Box(0.0, 1.0, (1,), dtype=np.float32)
if self.walls_num and self.observe_walls:
obs_space_dict['walls_lidar'] = gym.spaces.Box(0.0, 1.0, (self.lidar_num_bins,), dtype=np.float32)
if self.observe_hazards:
obs_space_dict['hazards_lidar'] = gym.spaces.Box(0.0, 1.0, (self.lidar_num_bins,), dtype=np.float32)
if self.observe_vases:
obs_space_dict['vases_lidar'] = gym.spaces.Box(0.0, 1.0, (self.lidar_num_bins,), dtype=np.float32)
if self.gremlins_num and self.observe_gremlins:
obs_space_dict['gremlins_lidar'] = gym.spaces.Box(0.0, 1.0, (self.lidar_num_bins,), dtype=np.float32)
if self.pillars_num and self.observe_pillars:
obs_space_dict['pillars_lidar'] = gym.spaces.Box(0.0, 1.0, (self.lidar_num_bins,), dtype=np.float32)
if self.buttons_num and self.observe_buttons:
obs_space_dict['buttons_lidar'] = gym.spaces.Box(0.0, 1.0, (self.lidar_num_bins,), dtype=np.float32)
if self.observe_qpos:
obs_space_dict['qpos'] = gym.spaces.Box(-np.inf, np.inf, (self.robot.nq,), dtype=np.float32)
if self.observe_qvel:
obs_space_dict['qvel'] = gym.spaces.Box(-np.inf, np.inf, (self.robot.nv,), dtype=np.float32)
if self.observe_ctrl:
obs_space_dict['ctrl'] = gym.spaces.Box(-np.inf, np.inf, (self.robot.nu,), dtype=np.float32)
if self.observe_vision:
width, height = self.vision_size
rows, cols = height, width
self.vision_size = (rows, cols)
obs_space_dict['vision'] = gym.spaces.Box(0, 1.0, self.vision_size + (3,), dtype=np.float32)
# Flatten it ourselves
self.obs_space_dict = obs_space_dict
if self.observation_flatten:
self.obs_flat_size = sum([np.prod(i.shape) for i in self.obs_space_dict.values()])
self.observation_space = gym.spaces.Box(-np.inf, np.inf, (self.obs_flat_size,), dtype=np.float32)
else:
self.observation_space = gym.spaces.Dict(obs_space_dict)