controller_manager.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 #
4 # Software License Agreement (BSD License)
5 #
6 # Copyright (c) 2010, Antons Rebguns, Cody Jorgensen, Cara Slutter
7 # 2010-2011, Antons Rebguns
8 # All rights reserved.
9 #
10 # Redistribution and use in source and binary forms, with or without
11 # modification, are permitted provided that the following conditions
12 # are met:
13 #
14 # * Redistributions of source code must retain the above copyright
15 # notice, this list of conditions and the following disclaimer.
16 # * Redistributions in binary form must reproduce the above
17 # copyright notice, this list of conditions and the following
18 # disclaimer in the documentation and/or other materials provided
19 # with the distribution.
20 # * Neither the name of University of Arizona nor the names of its
21 # contributors may be used to endorse or promote products derived
22 # from this software without specific prior written permission.
23 #
24 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
27 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
28 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
30 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
34 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 # POSSIBILITY OF SUCH DAMAGE.
36 
37 
38 __author__ = 'Antons Rebguns, Cody Jorgensen, Cara Slutter'
39 __copyright__ = 'Copyright (c) 2010-2011 Antons Rebguns, Cody Jorgensen, Cara Slutter'
40 
41 __license__ = 'BSD'
42 __maintainer__ = 'Antons Rebguns'
43 __email__ = 'anton@email.arizona.edu'
44 
45 
46 from threading import Thread, Lock
47 
48 import sys
49 
50 import rospy
51 
52 from dynamixel_driver.dynamixel_serial_proxy import SerialProxy
53 
54 from diagnostic_msgs.msg import DiagnosticArray
55 from diagnostic_msgs.msg import DiagnosticStatus
56 from diagnostic_msgs.msg import KeyValue
57 
58 from dynamixel_controllers.srv import StartController
59 from dynamixel_controllers.srv import StartControllerResponse
60 from dynamixel_controllers.srv import StopController
61 from dynamixel_controllers.srv import StopControllerResponse
62 from dynamixel_controllers.srv import RestartController
63 from dynamixel_controllers.srv import RestartControllerResponse
64 
66  def __init__(self):
67  rospy.init_node('dynamixel_controller_manager', anonymous=True)
68  rospy.on_shutdown(self.on_shutdown)
69 
71  self.controllers = {}
72  self.serial_proxies = {}
73  self.diagnostics_rate = rospy.get_param('~diagnostics_rate', 1)
74 
75  self.start_controller_lock = Lock()
76  self.stop_controller_lock = Lock()
77 
78  manager_namespace = rospy.get_param('~namespace')
79  serial_ports = rospy.get_param('~serial_ports')
80 
81  for port_namespace,port_config in serial_ports.items():
82  port_name = port_config['port_name']
83  baud_rate = port_config['baud_rate']
84  readback_echo = port_config['readback_echo'] if 'readback_echo' in port_config else False
85  min_motor_id = port_config['min_motor_id'] if 'min_motor_id' in port_config else 0
86  max_motor_id = port_config['max_motor_id'] if 'max_motor_id' in port_config else 253
87  update_rate = port_config['update_rate'] if 'update_rate' in port_config else 5
88  error_level_temp = 75
89  warn_level_temp = 70
90 
91  if 'diagnostics' in port_config:
92  if 'error_level_temp' in port_config['diagnostics']:
93  error_level_temp = port_config['diagnostics']['error_level_temp']
94  if 'warn_level_temp' in port_config['diagnostics']:
95  warn_level_temp = port_config['diagnostics']['warn_level_temp']
96 
97  serial_proxy = SerialProxy(port_name,
98  port_namespace,
99  baud_rate,
100  min_motor_id,
101  max_motor_id,
102  update_rate,
103  self.diagnostics_rate,
104  error_level_temp,
105  warn_level_temp,
106  readback_echo)
107  serial_proxy.connect()
108 
109  # will create a set of services for each serial port under common manager namesapce
110  # e.g. /dynamixel_manager/robot_arm_port/start_controller
111  # /dynamixel_manager/robot_head_port/start_controller
112  # where 'dynamixel_manager' is manager's namespace
113  # 'robot_arm_port' and 'robot_head_port' are human readable names for serial ports
114  rospy.Service('%s/%s/start_controller' % (manager_namespace, port_namespace), StartController, self.start_controller)
115  rospy.Service('%s/%s/stop_controller' % (manager_namespace, port_namespace), StopController, self.stop_controller)
116  rospy.Service('%s/%s/restart_controller' % (manager_namespace, port_namespace), RestartController, self.restart_controller)
117 
118  self.serial_proxies[port_namespace] = serial_proxy
119 
120  # services for 'meta' controllers, e.g. joint trajectory controller
121  # these controllers don't have their own serial port, instead they rely
122  # on regular controllers for serial connection. The advantage of meta
123  # controller is that it can pack commands for multiple motors on multiple
124  # serial ports.
125  # NOTE: all serial ports that meta controller needs should be managed by
126  # the same controler manager.
127  rospy.Service('%s/meta/start_controller' % manager_namespace, StartController, self.start_controller)
128  rospy.Service('%s/meta/stop_controller' % manager_namespace, StopController, self.stop_controller)
129  rospy.Service('%s/meta/restart_controller' % manager_namespace, RestartController, self.restart_controller)
130 
131  self.diagnostics_pub = rospy.Publisher('/diagnostics', DiagnosticArray, queue_size=1)
132  if self.diagnostics_rate > 0: Thread(target=self.diagnostics_processor).start()
133 
134  def on_shutdown(self):
135  for serial_proxy in self.serial_proxies.values():
136  serial_proxy.disconnect()
137 
139  diag_msg = DiagnosticArray()
140 
141  rate = rospy.Rate(self.diagnostics_rate)
142  while not rospy.is_shutdown():
143  diag_msg.status = []
144  diag_msg.header.stamp = rospy.Time.now()
145 
146  for controller in self.controllers.values():
147  try:
148  joint_state = controller.joint_state
149  temps = joint_state.motor_temps
150  max_temp = max(temps)
151 
152  status = DiagnosticStatus()
153  status.name = 'Joint Controller (%s)' % controller.joint_name
154  status.hardware_id = 'Robotis Dynamixel %s on port %s' % (str(joint_state.motor_ids), controller.port_namespace)
155  status.values.append(KeyValue('Goal', str(joint_state.goal_pos)))
156  status.values.append(KeyValue('Position', str(joint_state.current_pos)))
157  status.values.append(KeyValue('Error', str(joint_state.error)))
158  status.values.append(KeyValue('Velocity', str(joint_state.velocity)))
159  status.values.append(KeyValue('Load', str(joint_state.load)))
160  status.values.append(KeyValue('Moving', str(joint_state.is_moving)))
161  status.values.append(KeyValue('Temperature', str(max_temp)))
162  status.level = DiagnosticStatus.OK
163  status.message = 'OK'
164 
165  diag_msg.status.append(status)
166  except:
167  pass
168 
169  self.diagnostics_pub.publish(diag_msg)
170  rate.sleep()
171 
172  def check_deps(self):
173  controllers_still_waiting = []
174 
175  for i,(controller_name,deps,kls) in enumerate(self.waiting_meta_controllers):
176  if not set(deps).issubset(self.controllers.keys()):
177  controllers_still_waiting.append(self.waiting_meta_controllers[i])
178  rospy.logwarn('[%s] not all dependencies started, still waiting for %s...' % (controller_name, str(list(set(deps).difference(self.controllers.keys())))))
179  else:
180  dependencies = [self.controllers[dep_name] for dep_name in deps]
181  controller = kls(controller_name, dependencies)
182 
183  if controller.initialize():
184  controller.start()
185  self.controllers[controller_name] = controller
186 
187  self.waiting_meta_controllers = controllers_still_waiting[:]
188 
189  def start_controller(self, req):
190  port_name = req.port_name
191  package_path = req.package_path
192  module_name = req.module_name
193  class_name = req.class_name
194  controller_name = req.controller_name
195 
196  self.start_controller_lock.acquire()
197 
198  if controller_name in self.controllers:
199  self.start_controller_lock.release()
200  return StartControllerResponse(False, 'Controller [%s] already started. If you want to restart it, call restart.' % controller_name)
201 
202  try:
203  if module_name not in sys.modules:
204  # import if module not previously imported
205  package_module = __import__(package_path, globals(), locals(), [module_name], -1)
206  else:
207  # reload module if previously imported
208  package_module = reload(sys.modules[package_path])
209  controller_module = getattr(package_module, module_name)
210  except ImportError, ie:
211  self.start_controller_lock.release()
212  return StartControllerResponse(False, 'Cannot find controller module. Unable to start controller %s\n%s' % (module_name, str(ie)))
213  except SyntaxError, se:
214  self.start_controller_lock.release()
215  return StartControllerResponse(False, 'Syntax error in controller module. Unable to start controller %s\n%s' % (module_name, str(se)))
216  except Exception, e:
217  self.start_controller_lock.release()
218  return StartControllerResponse(False, 'Unknown error has occured. Unable to start controller %s\n%s' % (module_name, str(e)))
219 
220  kls = getattr(controller_module, class_name)
221 
222  if port_name == 'meta':
223  self.waiting_meta_controllers.append((controller_name,req.dependencies,kls))
224  self.check_deps()
225  self.start_controller_lock.release()
226  return StartControllerResponse(True, '')
227 
228  if port_name != 'meta' and (port_name not in self.serial_proxies):
229  self.start_controller_lock.release()
230  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))
231 
232  controller = kls(self.serial_proxies[port_name].dxl_io, controller_name, port_name)
233 
234  if controller.initialize():
235  controller.start()
236  self.controllers[controller_name] = controller
237 
238  self.check_deps()
239  self.start_controller_lock.release()
240 
241  return StartControllerResponse(True, 'Controller %s successfully started.' % controller_name)
242  else:
243  self.start_controller_lock.release()
244  return StartControllerResponse(False, 'Initialization failed. Unable to start controller %s' % controller_name)
245 
246  def stop_controller(self, req):
247  controller_name = req.controller_name
248  self.stop_controller_lock.acquire()
249 
250  if controller_name in self.controllers:
251  self.controllers[controller_name].stop()
252  del self.controllers[controller_name]
253  self.stop_controller_lock.release()
254  return StopControllerResponse(True, 'controller %s successfully stopped.' % controller_name)
255  else:
256  self.self.stop_controller_lock.release()
257  return StopControllerResponse(False, 'controller %s was not running.' % controller_name)
258 
259  def restart_controller(self, req):
260  response1 = self.stop_controller(StopController(req.controller_name))
261  response2 = self.start_controller(req)
262  return RestartControllerResponse(response1.success and response2.success, '%s\n%s' % (response1.reason, response2.reason))
263 
264 if __name__ == '__main__':
265  try:
266  manager = ControllerManager()
267  rospy.spin()
268  except rospy.ROSInterruptException: pass
269 


dynamixel_controllers
Author(s): Antons Rebguns, Cody Jorgensen, Cara Slutter
autogenerated on Tue May 12 2020 03:10:59