00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043 __author__ = 'Antons Rebguns, Cody Jorgensen, Cara Slutter'
00044 __copyright__ = 'Copyright (c) 2010-2011 Antons Rebguns, Cody Jorgensen, Cara Slutter'
00045
00046 __license__ = 'BSD'
00047 __maintainer__ = 'Antons Rebguns'
00048 __email__ = 'anton@email.arizona.edu'
00049
00050
00051 from threading import Thread, Lock
00052
00053 import sys
00054 from optparse import OptionParser
00055
00056 import rospy
00057
00058
00059
00060
00061 from threading import Thread, Lock
00062
00063 from dynamixel_driver.dynamixel_serial_proxy import SerialProxy
00064
00065 import rospy
00066
00067 class DummyDynamixelIO(object):
00068 """ Provides low level IO with the Dynamixel servos through pyserial. Has the
00069 ability to write instruction packets, request and read register value
00070 packets, send and receive a response to a ping packet, and send a SYNC WRITE
00071 multi-servo instruction packet.
00072 """
00073
00074 def __init__(self, port, baudrate, max_motor_id, readback_echo=False):
00075 """ Constructor takes serial port and baudrate as arguments. """
00076 self.serial_mutex = Lock()
00077 self.port_name = port
00078 self.readback_echo = readback_echo
00079
00080 self.goal = [2048]*(max_motor_id+1)
00081 self.position = [2048]*(max_motor_id+1)
00082 self.speed = [0]*(max_motor_id+1)
00083
00084 def ping(self, servo_id):
00085 self.goal[servo_id] = 2048
00086 self.position[servo_id] = 2048
00087 self.speed[servo_id] = 0
00088 return True;
00089
00090 def get_model_number(self, servo_id):
00091 """ Reads the servo's model number (e.g. 12 for AX-12+). """
00092 return 12
00093
00094 def get_angle_limits(self, servo_id):
00095 cwLimit = 0
00096 ccwLimit = 4096
00097 return {'min':cwLimit, 'max':ccwLimit}
00098
00099 def get_voltage(self, servo_id):
00100 return 12
00101
00102 def get_voltage_limits(self, servo_id):
00103
00104 min_voltage = 8
00105 max_voltage = 12
00106 return {'min':min_voltage, 'max':max_voltage}
00107
00108 def get_position(self, servo_id):
00109 return self.position[servo_id]
00110
00111 def get_speed(self, servo_id):
00112 return self.speed[servo_id]
00113
00114 def get_current(self, servo_id):
00115 return 0
00116
00117 def get_firmware_version(self, servo_id):
00118 return None
00119
00120 def get_return_delay_time(self, servo_id):
00121 """ Reads the servo's return delay time. """
00122 return 5
00123
00124 def get_feedback(self, servo_id):
00125 """
00126 Returns the id, goal, position, error, speed, load, voltage, temperature
00127 and moving values from the specified servo.
00128 """
00129
00130 error = 0
00131 if self.speed[servo_id] != 0 :
00132 speed = self.speed[servo_id]
00133 self.goal[servo_id] += speed
00134 else:
00135 speed = (self.goal[servo_id] - self.position[servo_id])*0.1
00136 self.position[servo_id] += speed
00137 return { 'timestamp': rospy.Time.now().to_sec(),
00138 'id': servo_id,
00139 'goal': self.goal[servo_id],
00140 'position': self.position[servo_id] % 4096,
00141 'error': error,
00142 'speed': speed,
00143 'load': 0}
00144
00145 def set_multi_speed(self, valueTuples):
00146 for servo_id,val in valueTuples:
00147 self.speed[servo_id] = val
00148 return True
00149
00150 def set_multi_position(self, valueTuples):
00151 for servo_id,val in valueTuples:
00152 self.goal[servo_id] = val
00153 self.speed[servo_id] = 0
00154 return True
00155
00156
00157 class DummyProxy(SerialProxy):
00158 def connect(self):
00159 self.dxl_io = DummyDynamixelIO(self.port_name, self.baud_rate, self.max_motor_id)
00160 self._SerialProxy__find_motors()
00161 self.running = True
00162 if self.update_rate > 0: Thread(target=self._SerialProxy__update_motor_states).start()
00163 if self.diagnostics_rate > 0: Thread(target=self._SerialProxy__publish_diagnostic_information).start()
00164
00165
00166
00167 from diagnostic_msgs.msg import DiagnosticArray
00168 from diagnostic_msgs.msg import DiagnosticStatus
00169 from diagnostic_msgs.msg import KeyValue
00170
00171 from dynamixel_controllers.srv import StartController
00172 from dynamixel_controllers.srv import StartControllerResponse
00173 from dynamixel_controllers.srv import StopController
00174 from dynamixel_controllers.srv import StopControllerResponse
00175 from dynamixel_controllers.srv import RestartController
00176 from dynamixel_controllers.srv import RestartControllerResponse
00177
00178 class ControllerManager:
00179 def __init__(self):
00180 rospy.init_node('dynamixel_controller_manager', anonymous=True)
00181 rospy.on_shutdown(self.on_shutdown)
00182
00183 self.waiting_meta_controllers = []
00184 self.controllers = {}
00185 self.serial_proxies = {}
00186 self.diagnostics_rate = rospy.get_param('~diagnostics_rate', 1)
00187
00188 self.start_controller_lock = Lock()
00189 self.stop_controller_lock = Lock()
00190
00191 manager_namespace = rospy.get_param('~namespace')
00192 serial_ports = rospy.get_param('~serial_ports')
00193
00194 for port_namespace,port_config in serial_ports.items():
00195 port_name = port_config['port_name']
00196 baud_rate = port_config['baud_rate']
00197 readback_echo = port_config['readback_echo'] if 'readback_echo' in port_config else False
00198 min_motor_id = port_config['min_motor_id'] if 'min_motor_id' in port_config else 0
00199 max_motor_id = port_config['max_motor_id'] if 'max_motor_id' in port_config else 253
00200 update_rate = port_config['update_rate'] if 'update_rate' in port_config else 5
00201 error_level_temp = 75
00202 warn_level_temp = 70
00203
00204 if 'diagnostics' in port_config:
00205 if 'error_level_temp' in port_config['diagnostics']:
00206 error_level_temp = port_config['diagnostics']['error_level_temp']
00207 if 'warn_level_temp' in port_config['diagnostics']:
00208 warn_level_temp = port_config['diagnostics']['warn_level_temp']
00209
00210 serial_proxy = SerialProxy(port_name=port_name,
00211 port_namespace=port_namespace,
00212 baud_rate=baud_rate,
00213 min_motor_id=min_motor_id,
00214 max_motor_id=max_motor_id,
00215 update_rate=update_rate,
00216 diagnostics_rate=self.diagnostics_rate,
00217 error_level_temp=error_level_temp,
00218 warn_level_temp=warn_level_temp)
00219
00220 serial_proxy.connect()
00221
00222
00223
00224
00225
00226
00227 rospy.Service('%s/%s/start_controller' % (manager_namespace, port_namespace), StartController, self.start_controller)
00228 rospy.Service('%s/%s/stop_controller' % (manager_namespace, port_namespace), StopController, self.stop_controller)
00229 rospy.Service('%s/%s/restart_controller' % (manager_namespace, port_namespace), RestartController, self.restart_controller)
00230
00231 self.serial_proxies[port_namespace] = serial_proxy
00232
00233
00234
00235
00236
00237
00238
00239
00240 rospy.Service('%s/meta/start_controller' % manager_namespace, StartController, self.start_controller)
00241 rospy.Service('%s/meta/stop_controller' % manager_namespace, StopController, self.stop_controller)
00242 rospy.Service('%s/meta/restart_controller' % manager_namespace, RestartController, self.restart_controller)
00243
00244 self.diagnostics_pub = rospy.Publisher('/diagnostics', DiagnosticArray)
00245 if self.diagnostics_rate > 0: Thread(target=self.diagnostics_processor).start()
00246
00247 def on_shutdown(self):
00248 for serial_proxy in self.serial_proxies.values():
00249 serial_proxy.disconnect()
00250
00251 def diagnostics_processor(self):
00252 diag_msg = DiagnosticArray()
00253
00254 rate = rospy.Rate(self.diagnostics_rate)
00255 while not rospy.is_shutdown():
00256 diag_msg.status = []
00257 diag_msg.header.stamp = rospy.Time.now()
00258
00259 for controller in self.controllers.values():
00260 try:
00261 joint_state = controller.joint_state
00262 temps = joint_state.motor_temps
00263 max_temp = max(temps)
00264
00265 status = DiagnosticStatus()
00266 status.name = 'Joint Controller (%s)' % controller.joint_name
00267 status.hardware_id = 'Robotis Dynamixel %s on port %s' % (str(joint_state.motor_ids), controller.port_namespace)
00268 status.values.append(KeyValue('Goal', str(joint_state.goal_pos)))
00269 status.values.append(KeyValue('Position', str(joint_state.current_pos)))
00270 status.values.append(KeyValue('Error', str(joint_state.error)))
00271 status.values.append(KeyValue('Velocity', str(joint_state.velocity)))
00272 status.values.append(KeyValue('Load', str(joint_state.load)))
00273 status.values.append(KeyValue('Moving', str(joint_state.is_moving)))
00274 status.values.append(KeyValue('Temperature', str(max_temp)))
00275 status.level = DiagnosticStatus.OK
00276 status.message = 'OK'
00277
00278 diag_msg.status.append(status)
00279 except:
00280 pass
00281
00282 self.diagnostics_pub.publish(diag_msg)
00283 rate.sleep()
00284
00285 def check_deps(self):
00286 controllers_still_waiting = []
00287
00288 for i,(controller_name,deps,kls) in enumerate(self.waiting_meta_controllers):
00289 if not set(deps).issubset(self.controllers.keys()):
00290 controllers_still_waiting.append(self.waiting_meta_controllers[i])
00291 rospy.logwarn('[%s] not all dependencies started, still waiting for %s...' % (controller_name, str(list(set(deps).difference(self.controllers.keys())))))
00292 else:
00293 dependencies = [self.controllers[dep_name] for dep_name in deps]
00294 controller = kls(controller_name, dependencies)
00295
00296 if controller.initialize():
00297 controller.start()
00298 self.controllers[controller_name] = controller
00299
00300 self.waiting_meta_controllers = controllers_still_waiting[:]
00301
00302 def start_controller(self, req):
00303 port_name = req.port_name
00304 package_path = req.package_path
00305 module_name = req.module_name
00306 class_name = req.class_name
00307 controller_name = req.controller_name
00308
00309 self.start_controller_lock.acquire()
00310
00311 if controller_name in self.controllers:
00312 self.start_controller_lock.release()
00313 return StartControllerResponse(False, 'Controller [%s] already started. If you want to restart it, call restart.' % controller_name)
00314
00315 try:
00316 if module_name not in sys.modules:
00317
00318 package_module = __import__(package_path, globals(), locals(), [module_name], -1)
00319 else:
00320
00321 package_module = reload(sys.modules[package_path])
00322 controller_module = getattr(package_module, module_name)
00323 except ImportError, ie:
00324 self.start_controller_lock.release()
00325 return StartControllerResponse(False, 'Cannot find controller module. Unable to start controller %s\n%s' % (module_name, str(ie)))
00326 except SyntaxError, se:
00327 self.start_controller_lock.release()
00328 return StartControllerResponse(False, 'Syntax error in controller module. Unable to start controller %s\n%s' % (module_name, str(se)))
00329 except Exception, e:
00330 self.start_controller_lock.release()
00331 return StartControllerResponse(False, 'Unknown error has occured. Unable to start controller %s\n%s' % (module_name, str(e)))
00332
00333 kls = getattr(controller_module, class_name)
00334
00335 if port_name == 'meta':
00336 self.waiting_meta_controllers.append((controller_name,req.dependencies,kls))
00337 self.check_deps()
00338 self.start_controller_lock.release()
00339 return StartControllerResponse(True, '')
00340
00341 if port_name != 'meta' and (port_name not in self.serial_proxies):
00342 self.start_controller_lock.release()
00343 return StartControllerResponse(False, 'Specified port [%s] not found, available ports are %s. Unable to start controller %s' % (port_name, str(self.serial_proxies.keys()), controller_name))
00344
00345 controller = kls(self.serial_proxies[port_name].dxl_io, controller_name, port_name)
00346
00347 if controller.initialize():
00348 controller.start()
00349 self.controllers[controller_name] = controller
00350
00351 self.check_deps()
00352 self.start_controller_lock.release()
00353
00354 return StartControllerResponse(True, 'Controller %s successfully started.' % controller_name)
00355 else:
00356 self.start_controller_lock.release()
00357 return StartControllerResponse(False, 'Initialization failed. Unable to start controller %s' % controller_name)
00358
00359 def stop_controller(self, req):
00360 controller_name = req.controller_name
00361 self.stop_controller_lock.acquire()
00362
00363 if controller_name in self.controllers:
00364 self.controllers[controller_name].stop()
00365 del self.controllers[controller_name]
00366 self.stop_controller_lock.release()
00367 return StopControllerResponse(True, 'controller %s successfully stopped.' % controller_name)
00368 else:
00369 self.self.stop_controller_lock.release()
00370 return StopControllerResponse(False, 'controller %s was not running.' % controller_name)
00371
00372 def restart_controller(self, req):
00373 response1 = self.stop_controller(StopController(req.controller_name))
00374 response2 = self.start_controller(req)
00375 return RestartControllerResponse(response1.success and response2.success, '%s\n%s' % (response1.reason, response2.reason))
00376
00377 if __name__ == '__main__':
00378 try:
00379 usage_msg = 'Usage: %prog [options]'
00380 parser = OptionParser(usage=usage_msg)
00381 parser.add_option('-d', '--dummy', default=False,
00382 help='running manager in dummy mode, which did not connect to actual haradware device')
00383 (options, args) = parser.parse_args(rospy.myargv()[1:])
00384 if options.dummy:
00385 SerialProxy = DummyProxy
00386 manager = ControllerManager()
00387 rospy.spin()
00388 except rospy.ROSInterruptException: pass
00389