36 __author__ =
'Antons Rebguns' 37 __copyright__ =
'Copyright (c) 2010-2011 Antons Rebguns' 38 __credits__ =
'Cody Jorgensen, Cara Slutter' 41 __maintainer__ =
'Antons Rebguns' 42 __email__ =
'anton@email.arizona.edu' 48 from collections
import deque
49 from threading
import Thread
50 from collections
import defaultdict
53 roslib.load_manifest(
'dynamixel_driver')
59 from diagnostic_msgs.msg
import DiagnosticArray
60 from diagnostic_msgs.msg
import DiagnosticStatus
61 from diagnostic_msgs.msg
import KeyValue
63 from dynamixel_msgs.msg
import MotorState
64 from dynamixel_msgs.msg
import MotorStateList
68 port_name=
'/dev/ttyUSB0',
69 port_namespace=
'ttyUSB0',
95 self.
diagnostics_pub = rospy.Publisher(
'/diagnostics', DiagnosticArray, queue_size=1)
102 rospy.logfatal(e.message)
114 Stores some extra information about each motor on the parameter server. 115 Some of these paramters are used in joint controller implementation. 117 angles = self.dxl_io.get_angle_limits(motor_id)
118 voltage = self.dxl_io.get_voltage(motor_id)
119 voltages = self.dxl_io.get_voltage_limits(motor_id)
121 rospy.set_param(
'dynamixel/%s/%d/model_number' %(self.
port_namespace, motor_id), model_number)
122 rospy.set_param(
'dynamixel/%s/%d/model_name' %(self.
port_namespace, motor_id), DXL_MODEL_TO_PARAMS[model_number][
'name'])
123 rospy.set_param(
'dynamixel/%s/%d/min_angle' %(self.
port_namespace, motor_id), angles[
'min'])
124 rospy.set_param(
'dynamixel/%s/%d/max_angle' %(self.
port_namespace, motor_id), angles[
'max'])
126 torque_per_volt = DXL_MODEL_TO_PARAMS[model_number][
'torque_per_volt']
127 rospy.set_param(
'dynamixel/%s/%d/torque_per_volt' %(self.
port_namespace, motor_id), torque_per_volt)
128 rospy.set_param(
'dynamixel/%s/%d/max_torque' %(self.
port_namespace, motor_id), torque_per_volt * voltage)
130 velocity_per_volt = DXL_MODEL_TO_PARAMS[model_number][
'velocity_per_volt']
131 rpm_per_tick = DXL_MODEL_TO_PARAMS[model_number][
'rpm_per_tick']
132 rospy.set_param(
'dynamixel/%s/%d/velocity_per_volt' %(self.
port_namespace, motor_id), velocity_per_volt)
133 rospy.set_param(
'dynamixel/%s/%d/max_velocity' %(self.
port_namespace, motor_id), velocity_per_volt * voltage)
134 rospy.set_param(
'dynamixel/%s/%d/radians_second_per_encoder_tick' %(self.
port_namespace, motor_id), rpm_per_tick * RPM_TO_RADSEC)
136 encoder_resolution = DXL_MODEL_TO_PARAMS[model_number][
'encoder_resolution']
137 range_degrees = DXL_MODEL_TO_PARAMS[model_number][
'range_degrees']
138 range_radians = math.radians(range_degrees)
139 rospy.set_param(
'dynamixel/%s/%d/encoder_resolution' %(self.
port_namespace, motor_id), encoder_resolution)
140 rospy.set_param(
'dynamixel/%s/%d/range_degrees' %(self.
port_namespace, motor_id), range_degrees)
141 rospy.set_param(
'dynamixel/%s/%d/range_radians' %(self.
port_namespace, motor_id), range_radians)
142 rospy.set_param(
'dynamixel/%s/%d/encoder_ticks_per_degree' %(self.
port_namespace, motor_id), encoder_resolution / range_degrees)
143 rospy.set_param(
'dynamixel/%s/%d/encoder_ticks_per_radian' %(self.
port_namespace, motor_id), encoder_resolution / range_radians)
144 rospy.set_param(
'dynamixel/%s/%d/degrees_per_encoder_tick' %(self.
port_namespace, motor_id), range_degrees / encoder_resolution)
145 rospy.set_param(
'dynamixel/%s/%d/radians_per_encoder_tick' %(self.
port_namespace, motor_id), range_radians / encoder_resolution)
149 self.
motor_static_info[motor_id][
'model'] = DXL_MODEL_TO_PARAMS[model_number][
'name']
150 self.
motor_static_info[motor_id][
'firmware'] = self.dxl_io.get_firmware_version(motor_id)
151 self.
motor_static_info[motor_id][
'delay'] = self.dxl_io.get_return_delay_time(motor_id)
165 result = self.dxl_io.ping(motor_id)
166 except Exception
as ex:
167 rospy.logerr(
'Exception thrown while pinging motor %d - %s' % (motor_id, ex))
171 self.motors.append(motor_id)
178 counts = defaultdict(int)
180 to_delete_if_error = []
181 for motor_id
in self.
motors:
184 model_number = self.dxl_io.get_model_number(motor_id)
186 except Exception
as ex:
187 rospy.logerr(
'Exception thrown while getting attributes for motor %d - %s' % (motor_id, ex))
191 counts[model_number] += 1
194 for motor_id
in to_delete_if_error:
195 self.motors.remove(motor_id)
200 for model_number,count
in counts.items():
202 model_name = DXL_MODEL_TO_PARAMS[model_number][
'name']
203 status_str +=
'%d %s [' % (count, model_name)
205 for motor_id
in self.
motors:
207 status_str +=
'%d, ' % motor_id
209 status_str = status_str[:-2] +
'], ' 211 rospy.loginfo(
'%s, initialization complete.' % status_str[:-2])
215 rates = deque([float(self.
update_rate)]*num_events, maxlen=num_events)
216 last_time = rospy.Time.now()
219 while not rospy.is_shutdown()
and self.
running:
222 for motor_id
in self.
motors:
224 state = self.dxl_io.get_feedback(motor_id)
226 motor_states.append(MotorState(**state))
227 if dynamixel_io.exception:
raise dynamixel_io.exception
232 rospy.logdebug(nfece)
238 rospy.logdebug(dpe.message)
240 if ose.errno != errno.EAGAIN:
241 rospy.logfatal(errno.errorcode[ose.errno])
242 rospy.signal_shutdown(errno.errorcode[ose.errno])
245 msl = MotorStateList()
246 msl.motor_states = motor_states
247 self.motor_states_pub.publish(msl)
252 current_time = rospy.Time.now()
253 rates.append(1.0 / (current_time - last_time).to_sec())
255 last_time = current_time
260 diag_msg = DiagnosticArray()
263 while not rospy.is_shutdown()
and self.
running:
265 diag_msg.header.stamp = rospy.Time.now()
267 status = DiagnosticStatus()
270 status.hardware_id =
'Dynamixel Serial Bus on port %s' % self.
port_name 271 status.values.append(KeyValue(
'Baud Rate', str(self.
baud_rate)))
272 status.values.append(KeyValue(
'Min Motor ID', str(self.
min_motor_id)))
273 status.values.append(KeyValue(
'Max Motor ID', str(self.
max_motor_id)))
274 status.values.append(KeyValue(
'Desired Update Rate', str(self.
update_rate)))
275 status.values.append(KeyValue(
'Actual Update Rate', str(self.
actual_rate)))
276 status.values.append(KeyValue(
'# Non Fatal Errors', str(self.
error_counts[
'non_fatal'])))
277 status.values.append(KeyValue(
'# Checksum Errors', str(self.
error_counts[
'checksum'])))
278 status.values.append(KeyValue(
'# Dropped Packet Errors', str(self.
error_counts[
'dropped'])))
279 status.level = DiagnosticStatus.OK
280 status.message =
'OK' 283 status.level = DiagnosticStatus.WARN
284 status.message =
'Actual update rate is lower than desired' 286 diag_msg.status.append(status)
288 for motor_state
in self.current_state.motor_states:
291 status = DiagnosticStatus()
293 status.name =
'Robotis Dynamixel Motor %d on port %s' % (mid, self.
port_namespace)
294 status.hardware_id =
'DXL-%d@%s' % (motor_state.id, self.
port_namespace)
295 status.values.append(KeyValue(
'Model Name', str(self.
motor_static_info[mid][
'model'])))
296 status.values.append(KeyValue(
'Firmware Version', str(self.
motor_static_info[mid][
'firmware'])))
297 status.values.append(KeyValue(
'Return Delay Time', str(self.
motor_static_info[mid][
'delay'])))
298 status.values.append(KeyValue(
'Minimum Voltage', str(self.
motor_static_info[mid][
'min_voltage'])))
299 status.values.append(KeyValue(
'Maximum Voltage', str(self.
motor_static_info[mid][
'max_voltage'])))
300 status.values.append(KeyValue(
'Minimum Position (CW)', str(self.
motor_static_info[mid][
'min_angle'])))
301 status.values.append(KeyValue(
'Maximum Position (CCW)', str(self.
motor_static_info[mid][
'max_angle'])))
303 status.values.append(KeyValue(
'Goal', str(motor_state.goal)))
304 status.values.append(KeyValue(
'Position', str(motor_state.position)))
305 status.values.append(KeyValue(
'Error', str(motor_state.error)))
306 status.values.append(KeyValue(
'Velocity', str(motor_state.speed)))
307 status.values.append(KeyValue(
'Load', str(motor_state.load)))
308 status.values.append(KeyValue(
'Voltage', str(motor_state.voltage)))
309 status.values.append(KeyValue(
'Temperature', str(motor_state.temperature)))
310 status.values.append(KeyValue(
'Moving', str(motor_state.moving)))
313 status.level = DiagnosticStatus.ERROR
314 status.message =
'OVERHEATING' 316 status.level = DiagnosticStatus.WARN
317 status.message =
'VERY HOT' 319 status.level = DiagnosticStatus.OK
320 status.message =
'OK' 322 diag_msg.status.append(status)
324 self.diagnostics_pub.publish(diag_msg)
327 if __name__ ==
'__main__':
330 serial_proxy.connect()
332 serial_proxy.disconnect()
333 except rospy.ROSInterruptException:
pass
def __fill_motor_parameters(self, motor_id, model_number)
def __publish_diagnostic_information(self)
def __init__(self, port_name='/dev/ttyUSB0', port_namespace='ttyUSB0', baud_rate='1000000', min_motor_id=1, max_motor_id=25, update_rate=5, diagnostics_rate=1, error_level_temp=75, warn_level_temp=70, readback_echo=False)
def __update_motor_states(self)