update data structures and URDF parser

This commit is contained in:
Brian Delhaisse
2019-10-11 03:31:30 +02:00
parent 58937d4bfc
commit 041b5e4716
2 changed files with 950 additions and 21 deletions
@@ -4,6 +4,7 @@
"""
import copy
from enum import Enum
import numpy as np
import trimesh
from collections import OrderedDict, Iterable
@@ -870,8 +871,10 @@ class MultiBody(object):
self.materials = {}
self.frame = Frame(position=position, orientation=orientation, dtype='world')
# sensors and actuators (with their transmissions)
self.sensors = {}
self.actuators = {}
self.transmissions = {}
@property
def name(self):
@@ -1068,6 +1071,13 @@ class MultiBody(object):
"""
return len(self.actuators) > 0
def has_transmissions(self):
"""
Return True if the multi-body data structure has some transmissions (transmission between joint and motor
actuator).
"""
return len(self.transmissions) > 0
def add_sensor(self, sensor):
"""
Add a sensor to the multi-body data structure.
@@ -1092,6 +1102,18 @@ class MultiBody(object):
"{}".format(type(actuator)))
self.actuators[actuator.id] = actuator
def add_transmission(self, transmission):
"""
Add a transmission element (i.e. link between joint and motor actuator).
Args:
transmission (Transmission): transmission data structure.
"""
if not isinstance(transmission, Transmission):
raise TypeError("Expecting the given 'transmission' to be an instance of `Transmission`, but got instead: "
"{}".format(type(transmission)))
self.transmissions[transmission.name] = transmission
# alias
Tree = MultiBody
@@ -2879,10 +2901,41 @@ class Noise(object):
class GaussianNoise(Noise):
"""Gaussian noise distribution used in sensors and actuators."""
def __init__(self, mean, stddev):
def __init__(self, mean=None, stddev=None):
"""
Initialize the Gaussian noise.
Args:
mean (float): mean value.
stddev (float): standard deviation value.
"""
self.mean = mean
self.stddev = stddev
@property
def mean(self):
"""Return the Gaussian noise mean value."""
return self._mean
@mean.setter
def mean(self, mean):
"""Set the Gaussian noise mean value."""
if mean is not None:
mean = float(mean)
self._mean = mean
@property
def stddev(self):
"""Return the Gaussian noise standard deviation value."""
return self._stddev
@stddev.setter
def stddev(self, stddev):
"""Set the Gaussian noise standard deviation value."""
if stddev is not None:
stddev = float(stddev)
self._stddev = stddev
class Sensor(object):
r"""Sensor (abstract) class.
@@ -2918,6 +2971,18 @@ class Sensor(object):
raise TypeError("Expecting the given 'name' to be a string, instead got: {}".format(type(name)))
self._name = name
@property
def update_rate(self):
"""Return the update rate."""
return self._rate
@update_rate.setter
def update_rate(self, rate):
"""Set the update rate."""
if rate is not None:
rate = float(rate)
self._rate = rate
@property
def noise(self):
"""Return the sensor noise."""
@@ -3005,6 +3070,59 @@ class LinkSensor(Sensor):
self._link = link
class Image(object):
r"""Image data structure."""
def __init__(self, width=None, height=None, format=None):
"""
Initialize the image data structure.
Args:
width (int, None): width of the image in pixels.
height (int, None): height of the image in pixels.
format (str, None): format of the image. Ex: 'R8G8B8'.
"""
self.width = width
self.height = height
self.format = format
@property
def width(self):
"""Return the width of the image."""
return self._width
@width.setter
def width(self, width):
"""Set the width of the image."""
if width is not None:
width = int(width)
self._width = width
@property
def height(self):
"""Return the height of the image."""
return self._height
@height.setter
def height(self, height):
"""Set the height of the image."""
if height is not None:
height = int(height)
self._height = height
@property
def format(self):
"""Return the image format."""
return self._format
@format.setter
def format(self, format):
"""Set the image format."""
if format is not None and not isinstance(format, str):
raise TypeError("Expecting the given 'format' to be a string, but instead got: {}".format(type(format)))
self._format = format
class CameraSensor(LinkSensor):
"""Camera sensor
@@ -3028,10 +3146,8 @@ class CameraSensor(LinkSensor):
self.visualize = False
# intrinsic properties of camera
self.image = Image(width=None, height=None, format=None)
self.horizontal_fov = None
self.width = None
self.height = None
self.format = None # R8G8B8
self.near = None
self.far = None
@@ -3041,12 +3157,171 @@ class CameraSensor(LinkSensor):
self.image_topic = None # added to the camera_base_topic
self.camera_info_topic = None # added to the camera_base_topic
self.frame_name = None # check 'name' attribute in Frame class
self.hack_baseline = None
self.distortion_k1 = None
self.distortion_k2 = None
self.distortion_k3 = None
self.distortion_t1 = None
self.distortion_t2 = None
self.focal_length = None
self.cx_prime = None
self.cx = None
self.cy = None
@property
def visualize(self):
"""Return if we should visualize in the simulator the sensor data."""
return self._visualize
@visualize.setter
def visualize(self, visualize):
"""Set if we should visualize in the simulator the sensor data."""
if visualize is None:
visualize = False
self._visualize = bool(visualize)
@property
def image(self):
"""Return the image data structure."""
return self._image
@image.setter
def image(self, image):
if image is not None and not isinstance(image, Image):
raise TypeError("Expecting the given 'image' to be an instance of `Image` but got instead: "
"{}".format(type(image)))
self._image = image
@property
def horizontal_fov(self):
return self._horizontal_fov
@horizontal_fov.setter
def horizontal_fov(self, fov):
if fov is not None:
fov = float(fov)
self._horizontal_fov = fov
@property
def near(self):
return self._near
@near.setter
def near(self, near):
if near is not None:
near = float(near)
self._near = near
@property
def far(self):
return self._far
@far.setter
def far(self, far):
if far is not None:
far = float(far)
self._far = far
@property
def hack_baseline(self):
return self._hack_baseline
@hack_baseline.setter
def hack_baseline(self, baseline):
if baseline is not None:
baseline = float(baseline)
self._hack_baseline = baseline
@property
def distortion_k1(self):
return self._distortion_k1
@distortion_k1.setter
def distortion_k1(self, distortion_k1):
if distortion_k1 is not None:
distortion_k1 = float(distortion_k1)
self._distortion_k1 = distortion_k1
@property
def distortion_k2(self):
return self._distortion_k2
@distortion_k2.setter
def distortion_k2(self, distortion_k2):
if distortion_k2 is not None:
distortion_k2 = float(distortion_k2)
self._distortion_k2 = distortion_k2
@property
def distortion_k3(self):
return self._distortion_k3
@distortion_k3.setter
def distortion_k3(self, distortion_k3):
if distortion_k3 is not None:
distortion_k3 = float(distortion_k3)
self._distortion_k3 = distortion_k3
@property
def distortion_t1(self):
return self._distortion_t1
@distortion_t1.setter
def distortion_t1(self, distortion_t1):
if distortion_t1 is not None:
distortion_t1 = float(distortion_t1)
self._distortion_t1 = distortion_t1
@property
def distortion_t2(self):
return self._distortion_t2
@distortion_t2.setter
def distortion_t2(self, distortion_t2):
if distortion_t2 is not None:
distortion_t2 = float(distortion_t2)
self._distortion_t2 = distortion_t2
@property
def focal_length(self):
return self._focal_length
@focal_length.setter
def focal_length(self, focal_length):
if focal_length is not None:
focal_length = float(focal_length)
self._focal_length = focal_length
@property
def cx_prime(self):
return self._cx_prime
@cx_prime.setter
def cx_prime(self, cx_prime):
if cx_prime is not None:
cx_prime = float(cx_prime)
self._cx_prime = cx_prime
@property
def cx(self):
return self._cx
@cx.setter
def cx(self, cx):
if cx is not None:
cx = float(cx)
self._cx = cx
@property
def cy(self):
return self._cy
@cy.setter
def cy(self, cy):
if cy is not None:
cy = float(cy)
self._cy = cy
class DepthCameraSensor(LinkSensor):
@@ -3090,7 +3365,7 @@ class GPURay(LinkSensor):
"""
super(GPURay, self).__init__(sensor_id, link, name, update_rate, noise)
self.frame = Frame()
self.frame = Frame() # pose
self.visualize = False
# <scan>
@@ -3101,14 +3376,159 @@ class GPURay(LinkSensor):
# <range>
self.range = None # <min> and <max>
self.range_resolution = 0.01
self.range_resolution = None
# plugin
self.plugin_filename = None
self.plugin_name = None
self.topic = None
self.topic_name = None
self.frame_name = None # Check 'name' attribute in Frame class
@property
def pose(self):
pose = self.frame.pose
if pose is not None:
pos, rpy = pose
if pos is None:
pos = np.zeros(3)
if rpy is None:
rpy = np.zeros(3)
return np.concatenate((pos, rpy))
@pose.setter
def pose(self, pose):
self.frame.pose = pose
@property
def visualize(self):
"""Return if we should visualize in the simulator the sensor data."""
return self._visualize
@visualize.setter
def visualize(self, visualize):
"""Set if we should visualize in the simulator the sensor data."""
if visualize is None:
visualize = False
elif isinstance(visualize, str):
visualize = visualize.lower()
if visualize == 'false':
visualize = False
elif visualize == 'true':
visualize = True
self._visualize = bool(visualize)
@property
def samples(self):
return self._samples
@samples.setter
def samples(self, samples):
if samples is not None:
samples = int(samples)
self._samples = samples
@property
def scan_resolution(self):
return self._scan_resolution
@scan_resolution.setter
def scan_resolution(self, resolution):
if resolution is not None:
resolution = float(resolution)
self._scan_resolution = resolution
@property
def range_angle(self):
return self._range_angle
@range_angle.setter
def range_angle(self, range_angle):
if range_angle is not None:
if not isinstance(range_angle, (tuple, list, np.ndarray)):
raise TypeError("Expecting the given 'range_angle' to be a tuple/list/np.array of 2 float, but got "
"instead: {}".format(type(range_angle)))
if len(range_angle) != 2:
raise ValueError("Expecting the given 'range_angle' to be of length 2 but got instead a length of: "
"{}".format(len(range_angle)))
self._range_angle = range_angle
@property
def range(self):
return self._range
@range.setter
def range(self, range_distance):
if range_distance is not None:
if not isinstance(range_distance, (tuple, list, np.ndarray)):
raise TypeError("Expecting the given 'range' to be a tuple/list/np.array of 2 float, but got "
"instead: {}".format(type(range_distance)))
if len(range_distance) != 2:
raise ValueError("Expecting the given 'range' to be of length 2 but got instead a length of: "
"{}".format(len(range_distance)))
self._range = range_distance
@property
def min_angle(self):
if self.range_angle is not None:
return self.range_angle[0]
@min_angle.setter
def min_angle(self, angle):
if angle is not None:
angle = float(angle)
if self.range_angle is None:
self.range_angle = (None, None)
self.range_angle[0] = angle
@property
def max_angle(self):
if self.range_angle is not None:
return self.range_angle[1]
@max_angle.setter
def max_angle(self, angle):
if angle is not None:
angle = float(angle)
if self.range_angle is None:
self.range_angle = (None, None)
self.range_angle[1] = angle
@property
def min_range(self):
if self.range is not None:
return self.range[0]
@min_range.setter
def min_range(self, dist):
if dist is not None:
dist = float(dist)
if self.range is None:
self.range = (None, None)
self.range[0] = dist
@property
def max_range(self):
if self.range is not None:
return self.range[1]
@max_range.setter
def max_range(self, dist):
if dist is not None:
dist = float(dist)
if self.range is None:
self.range = (None, None)
self.range[1] = dist
@property
def range_resolution(self):
return self._range_resolution
@range_resolution.setter
def range_resolution(self, resolution):
if resolution is not None:
resolution = float(resolution)
self._range_resolution = resolution
class IMUSensor(LinkSensor):
"""IMU sensor.
@@ -3130,6 +3550,18 @@ class IMUSensor(LinkSensor):
"""
super(IMUSensor, self).__init__(sensor_id, link, name, update_rate, noise)
self.frame = Frame() # pose
self.visualize = False
self.gravity = False
# plugin
self.plugin_filename = None
self.plugin_name = None
self.topic_name = None
self.body_name = None # Check 'name' of link
self.gaussian_noise = None
self.frame_name = None # Check 'name' attribute in Frame class
class ForceTorqueSensor(JointSensor):
"""Force torque sensor.
@@ -3164,7 +3596,7 @@ class Actuator(object): # Motor
Args:
actuator_id (int): actuator unique id.
name (str): name of the actuator/motor.
actuators (list[Sensor]): inner list of actuators.
actuators (list[Actuator]): inner list of actuators.
"""
self.id = actuator_id
self.name = name
@@ -3220,7 +3652,7 @@ class JointActuator(Actuator):
class MotorJointActuator(JointActuator):
"""Motor joint actuator."""
def __init__(self, actuator_id, joint, name=None):
def __init__(self, actuator_id, joint, name=None, hardware_interface=None, mechanical_reduction=None):
"""
Initialize the joint actuator.
@@ -3228,12 +3660,38 @@ class MotorJointActuator(JointActuator):
actuator_id (int): unique actuator id.
joint (Joint): joint to which the actuator is attached.
name (str): name of the actuator.
hardware_interface (str): hardware interface used in ROS control.
mechanical_reduction (float): mechanical reduction factor.
"""
super(MotorJointActuator, self).__init__(actuator_id, joint, name)
self.transmission_type = None
self.hardware_interface = None # EffortJointInterface
self.mechanical_reduction = None
self.hardware_interface = hardware_interface # EffortJointInterface
self.mechanical_reduction = mechanical_reduction
@property
def hardware_interface(self):
"""Return the hardware interface."""
return self._hardware_interface
@hardware_interface.setter
def hardware_interface(self, interface):
"""Set the hardware interface."""
if interface is not None and not isinstance(interface, str):
raise TypeError("Expecting the given 'hardware_interface' to be a string but got instead: "
"{}".format(type(interface)))
self._hardware_interface = interface
@property
def mechanical_reduction(self):
"""Get the mechanical reduction factor."""
return self._mechanical_reduction
@mechanical_reduction.setter
def mechanical_reduction(self, reduction):
"""Set the mechanical reduction factor."""
if reduction is not None:
reduction = float(reduction)
self._mechanical_reduction = reduction
class PositionJointActuator(MotorJointActuator):
@@ -3270,6 +3728,13 @@ class Constraint(object):
pass
class TransmissionType(Enum):
"""Transmission type: simple, four-bar linkage, differential."""
SIMPLE = 1
FOUR_BAR_LINKAGE = 2
DIFFERENTIAL = 3
class Transmission(object):
r"""Transmission interface.
@@ -3294,20 +3759,250 @@ class Transmission(object):
- [3] URDF Transmissions: https://wiki.ros.org/urdf/XML/Transmission
"""
def __init__(self, name, joint, transmission_type=None, actuator_name=None, hardware_interface=None):
def __init__(self, name, transmission_type=None, joint=None, actuator=None):
"""
Initialize the transmission.
Args:
name (str): name of the transmission.
joint (Joint): joint to which is attached the transmission.
transmission_type (str): transmission type; select between {'simple', 'four-bar linkage', 'differential'}.
actuator_name (str): name of the actuator.
hardware_interface (str): hardware interface.
joint (Joint): joint to which the transmission is related to.
actuator (MotorJointActuator): actuator to which the transmission is connected to.
"""
self.name = name
self.type = transmission_type
self.actuator = actuator_name
self.mechanical_reduction = None
self.joint = joint
self.hardware_interface = hardware_interface
self.actuator = actuator
@property
def name(self):
"""Return the transmission unique name."""
return self._name
@name.setter
def name(self, name):
"""Set the transmission unique name."""
if not isinstance(name, str):
raise TypeError("Expecting the given 'name' to be a string, instead got: {}".format(type(name)))
self._name = name
@property
def type(self):
"""Return the transmission type."""
return self._type
@type.setter
def type(self, dtype):
"""Set the transmission type."""
# if dtype is not None:
# if isinstance(dtype, str):
# dtype = dtype.lower()
# for t in TransmissionType:
# if dtype == t.name.lower():
# dtype = t
# break
# elif not isinstance(dtype, TransmissionType):
# raise TypeError("Expecting the given 'transmission_type' to be an instance of a string or an "
# "instance of `TransmissionType`, but got instead: {}".format(type(dtype)))
if dtype is not None and not isinstance(dtype, str):
raise TypeError("Expecting the given 'transmission_type' to be a string, instead got: "
"{}".format(type(dtype)))
self._type = dtype
@property
def joint(self):
"""Return the joint data structure to which the transmission is related to."""
return self._joint
@joint.setter
def joint(self, joint):
"""Set the joint data structure to which the transmission is attached."""
if joint is not None and not isinstance(joint, Joint):
raise TypeError("Expecting the given 'joint' to be an instance of `Joint`, but got instead: "
"{}".format(type(joint)))
self._joint = joint
@property
def actuator(self):
"""Return the actuator data structure to which the transmission is connected to."""
return self._actuator
@actuator.setter
def actuator(self, actuator):
"""Set the actuator data structure to which the transmission is connected to."""
if actuator is not None and not isinstance(actuator, MotorJointActuator):
raise TypeError("Expecting the given 'actuator' to be an instance of `MotorJointActuator`, but got "
"instead: {}".format(type(actuator)))
self._actuator = actuator
class ControlMode(Enum):
"""Control mode: position, velocity, effort."""
NULL = 0
POSITION = 1
VELOCITY = 2
EFFORT = 3
class PID(object):
"""PID control"""
def __init__(self, p=None, i=None, d=None, pid=None):
"""
Initialize PID.
Args:
p (float, int, str): p coefficient value.
i (float, int, str): i coefficient value.
d (float, int, str): d coefficient value.
pid (list/tuple/np.array[float[3]], str): pid values.
"""
if pid is not None:
self.pid = pid
else:
self.p = p
self.i = i
self.d = d
@property
def p(self):
return self._p
@p.setter
def p(self, p):
if p is not None:
if not isinstance(p, (float, str, int)):
raise TypeError("Expecting the p coefficient to be an int, float, or str, but got instead: "
"{}".format(type(p)))
p = float(p)
self._p = p
@property
def i(self):
return self._i
@i.setter
def i(self, i):
if i is not None:
if not isinstance(i, (float, str, int)):
raise TypeError("Expecting the i coefficient to be an int, float, or str, but got instead: "
"{}".format(type(i)))
i = float(i)
self._i = i
@property
def d(self):
return self._d
@d.setter
def d(self, d):
if d is not None:
if not isinstance(d, (float, str, int)):
raise TypeError("Expecting the d coefficient to be an int, float, or str, but got instead: "
"{}".format(type(d)))
d = float(d)
self._d = d
@property
def pid(self):
pid = [self._p, self._i, self._d]
for i in range(len(pid)):
if pid[i] is None:
pid[i] = 0.
return np.array(pid)
@pid.setter
def pid(self, pid):
if pid is not None:
if isinstance(pid, str):
pid = [float(y) for x in pid.split(',') for y in x.split()]
elif isinstance(pid, (list, tuple, np.ndarray)):
if len(pid) != 3:
raise ValueError("Expecting the given pid list to be of length 3, but got a length of: "
"{}".format(len(pid)))
else:
raise TypeError("Expecting the given pid to be a list/tuple/np.array of 3 floats, or a string, but "
"instead got: {}".format(type(pid)))
self.p = pid[0]
self.i = pid[1]
self.d = pid[2]
else:
self.p = None
self.i = None
self.d = None
class Control(object):
r"""Control
"""
def __init__(self, mode=None, pid=None):
"""
Initialize the control.
Args:
mode (ControlMode, None): control mode.
pid (PID, None): PID values.
"""
self.mode = mode
self.pid = pid
@property
def mode(self):
return self._mode
@mode.setter
def mode(self, mode):
if mode is not None:
if isinstance(mode, str):
mode = mode.lower()
for m in ControlMode:
if mode == m.name.lower():
mode = m
break
elif not isinstance(mode, ControlMode):
raise TypeError("Expecting the given control mode to be an instance of `ControlMode` or a string, but "
"got instead: {}".format(type(mode)))
self._mode = mode
@property
def pid(self):
return self._pid
@pid.setter
def pid(self, pid):
if pid is None:
pid = PID()
elif isinstance(pid, (list, tuple, np.ndarray, str)):
pid = PID(pid=pid)
elif not isinstance(pid, PID):
raise TypeError("Expecting the given PID values to be a list/tuple/np.array of 3 float, an instance of "
"`PID`, or a string, but got instead: {}".format(type(pid)))
self._pid = pid
class ROSControl(object):
"""ROS control data structure as specified in URDFs."""
def __init__(self, namespace=None, control_period=None, robot_parameters=None, robot_sim_interface=None,
control_config_path=None):
"""
Initialize the ROS control data structure.
Args:
namespace (str, None): ROS namespace, default to robot name in URDF.
control_period (int, None): period of the controller update (in seconds). If not specified, it will be
the default one.
robot_parameters (str, None): location of the robot_description (URDF) on the parameter server, default to
`/robot_description`.
robot_sim_interface (str, None): name of custum robot simulator interface to be used. Default to
`DefaultRobotHWSim`.
control_config_path (str, None): control configuration file path (usually it is a YAML file).
"""
self.namespace = namespace
self.control_period = control_period
self.robot_parameters = robot_parameters
self.robot_sim_interface = robot_sim_interface
self.config_path = control_config_path
+236 -2
View File
@@ -97,7 +97,24 @@ class URDFParser(RobotParser):
child_body = joint.child
child_body.parent_joints[joint.name] = joint
# TODO: check sensor, plugins, transmission, etc
# check transmissions (for control)
for i, transmission_tag in enumerate(root.findall('transmission')):
# get transmission instance
transmission = self._parse_transmission(transmission_tag, idx=i, tree=tree)
# add transmission
tree.add_transmission(transmission)
# check sensors
for i, sensor_tag in enumerate(root.findall('gazebo')):
# get sensor instance
sensor = self._parse_sensor(sensor_tag, idx=i, tree=tree)
# add sensor in tree
if sensor is not None:
tree.add_sensor(sensor)
# TODO: check plugins
# set the root element
key = next(iter(tree.bodies))
@@ -247,7 +264,7 @@ class URDFParser(RobotParser):
Args:
joint_tag (ET.Element): joint XML element.
idx (int): joint index.
tree (Tree): tree data structure.
tree (Tree): MultiBody data structure.
Returns:
Joint: joint data structure.
@@ -295,6 +312,186 @@ class URDFParser(RobotParser):
return joint
@staticmethod
def _parse_transmission(transmission_tag, idx, tree):
"""
Return the transmission data structure instance from a <transmission> tag. See `ros_control` for more info.
Args:
transmission_tag (ET.Element): transmission XML element.
idx (int): joint/motor index.
tree (Tree): tree data structure.
Returns:
Transmission: transmission data structure.
"""
transmission = Transmission(name=transmission_tag.attrib.get('name'))
# get type tag
type_tag = transmission_tag.find('type')
if type_tag is not None:
transmission.type = type_tag.text
# get joint
joint_tag = transmission_tag.find('joint')
if joint_tag is not None:
transmission.joint = tree.joints[joint_tag.attrib.get('name')]
# get actuator
actuator_tag = transmission_tag.find('actuator')
if actuator_tag is not None:
hardware_interface = actuator_tag.find('hardwareInterface').text
mechanical_reduction = actuator_tag.find('mechanicalReduction').text
actuator = MotorJointActuator(actuator_id=idx, joint=transmission.joint,
name=actuator_tag.attrib.get('name'), hardware_interface=hardware_interface,
mechanical_reduction=mechanical_reduction)
transmission.actuator = actuator
tree.add_actuator(actuator)
return transmission
@staticmethod
def _parse_sensor(gazebo_tag, idx, tree):
"""
Return the sensor data structure instance from a <gazebo> tag.
Args:
gazebo_tag (ET.Element): gazebo sensor XML element.
idx (int): joint/motor index.
tree (Tree): tree data structure.
Returns:
Sensor: sensor data structure.
"""
reference = gazebo_tag.attrib.get('reference')
# get sensor
sensor_tag = gazebo_tag.find('sensor')
if sensor_tag is None:
return None
sensor_type = sensor_tag.attrib.get('type')
# define set_attribute text function
def set_attribute(obj, attribute_name, parent_tag, tag=None):
"""Set XML tag text to object's attribute."""
if tag is None:
tag = attribute_name
tag = parent_tag.find(tag)
if tag is not None:
setattr(obj, attribute_name, tag.text)
# define check_noise tag function
def check_noise(tag):
"""Check the <noise> tag and returns the Noise instance or None if no noise tags were found."""
noise_tag = tag.find('noise')
noise = None
if noise_tag is not None:
type_tag = noise_tag.find('type')
if type_tag is not None:
noise_type = type_tag.text
if noise_type == 'gaussian':
noise = GaussianNoise()
for t in ['mean', 'stddev']:
set_attribute(noise, t, noise_tag)
return noise
# check sensor type #
sensor = None
if sensor_type == 'camera': # CAMERA sensor
sensor = CameraSensor(sensor_id=idx, link=tree.bodies[reference], name=sensor_tag.attrib.get('name'))
# check <update_rate> tag
set_attribute(sensor, 'update_rate', sensor_tag)
# check <camera> tag
camera_tag = sensor_tag.find('camera')
if camera_tag is not None:
# check <image> tag
image_tag = camera_tag.find('image')
if image_tag is not None:
image = Image()
for tag in ['width', 'height', 'format']:
set_attribute(image, tag, image_tag)
sensor.image = image
# check <clip> tag
clip_tag = camera_tag.find('clip')
if clip_tag is not None:
for tag in ['near', 'far']:
set_attribute(sensor, tag, clip_tag)
# check <noise> tag
sensor.noise = check_noise(camera_tag)
# check <plugin> tag
plugin_tag = sensor_tag.find('plugin')
if plugin_tag is not None:
sensor.plugin_filename = plugin_tag.attrib.get('filename')
sensor.plugin_name = plugin_tag.attrib.get('name')
attributes = ['camera_base_topic', 'image_topic', 'camera_info_topic', 'frame_name', 'hack_baseline',
'distortion_k1', 'distortion_k2', 'distortion_k3', 'distortion_t1', 'distortion_t2',
'focal_length', 'cx_prime', 'cx', 'cy']
tags = ['cameraName', 'imageTopicName', 'cameraInfoTopicName', 'frameName', 'hackBaseline',
'distortionK1', 'distortionK2', 'distortionK3', 'distortionT1', 'distortionT2', 'focalLength',
'CxPrime', 'Cx', 'Cy']
for attrib, tag in zip(attributes, tags):
set_attribute(sensor, attrib, plugin_tag, tag)
elif sensor_type == 'gpu_ray': # GPU RAY sensor
sensor = GPURay(sensor_id=idx, link=tree.bodies[reference], name=sensor_tag.attrib.get('name'))
set_attribute(sensor, 'pose', sensor_tag)
set_attribute(sensor, 'visualize', sensor_tag)
set_attribute(sensor, 'update_rate', sensor_tag)
# check <ray> tag
ray_tag = sensor_tag.find('ray')
if ray_tag is not None:
# check <scan> tag
scan_tag = ray_tag.find('scan')
if scan_tag is not None:
# check <horizontal> tag
horizontal_tag = scan_tag.find('horizontal')
if horizontal_tag is not None:
attributes = ['samples', 'min_angle', 'max_angle']
for attrib in attributes:
set_attribute(sensor, attrib, horizontal_tag)
set_attribute(sensor, 'scan_resolution', horizontal_tag, 'resolution')
# check <range> tag
range_tag = ray_tag.find('range')
if range_tag is not None:
attributes = ['min_range', 'max_range', 'range_resolution']
tags = ['min', 'max', 'resolution']
for attrib, tag in zip(attributes, tags):
set_attribute(sensor, attrib, range_tag, tag)
# check <noise> tag
sensor.noise = check_noise(ray_tag)
# check <plugin> tag
plugin_tag = sensor_tag.find('plugin')
if plugin_tag is not None:
sensor.plugin_filename = plugin_tag.attrib.get('filename')
sensor.plugin_name = plugin_tag.attrib.get('name')
attributes = ['topic_name', 'frame_name']
tags = ['topicName', 'frameName']
for attrib, tag in zip(attributes, tags):
set_attribute(sensor, attrib, plugin_tag, tag)
# elif sensor_type == 'imu': # imu sensor
else:
print("WARNING: THE SENSOR {} WAS NOT PARSED. PLEASE IMPLEMENT THE METHOD.")
return sensor
def generate(self, tree=None):
"""
Generate the XML tree from the `Tree` data structure.
@@ -472,5 +669,42 @@ class URDFParser(RobotParser):
kwargs['upper'] = str(joint.limits[1])
ET.SubElement(joint_tag, 'limit', attrib=kwargs)
# generate transmission
for transmission in tree.transmissions.values():
# set transmission name
transmission_tag = ET.SubElement(root, 'transmission', attrib={'name': transmission.name})
# set transmission type
type_tag = ET.SubElement(transmission_tag, 'type')
type_tag.text = transmission.type
# set transmission joint
joint_tag = ET.SubElement(transmission_tag, 'joint', attrib={'name': transmission.joint.name})
joint_hw_tag = ET.SubElement(joint_tag, 'hardwareInterface')
joint_hw_tag.text = transmission.actuator.hardware_interface
# set transmission actuator
actuator_tag = ET.SubElement(transmission_tag, 'actuator', attrib={'name': transmission.actuator.name})
hw_tag = ET.SubElement(actuator_tag, 'hardwareInterface')
hw_tag.text = transmission.actuator.hardware_interface
mechanical_reduction_tag = ET.SubElement(actuator_tag, 'mechanicalReduction')
mechanical_reduction_tag.text = str(transmission.actuator.mechanical_reduction)
# generate sensors
# for sensor in tree.sensors.values():
# # set sensor reference link/joint
# if isinstance(sensor, JointSensor):
# attrib = {'reference': sensor.joint.name}
# elif isinstance(sensor, LinkSensor):
# attrib = {'reference': sensor.link.name}
# else:
# raise TypeError("Expecting the sensor to be a joint sensor or link sensor...")
# gazebo_tag = ET.SubElement(root, 'gazebo', attrib=attrib)
#
# # set sensor name and type
# sensor_tag = set_name_and_type(gazebo_tag, 'sensor', sensor)
#
# # depending on the sensor generate the necessary tags
# return root XML element
return root