mir_bridge.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 import rospy
3 
4 import copy
5 import sys
6 from collections import Iterable
7 
8 from mir_driver import rosbridge
9 from rospy_message_converter import message_converter
10 
11 from actionlib import SimpleActionClient
12 import actionlib_msgs.msg
13 import diagnostic_msgs.msg
14 import dynamic_reconfigure.msg
15 import geometry_msgs.msg
16 import mir_actions.msg
17 import mir_msgs.msg
18 import move_base_msgs.msg
19 import nav_msgs.msg
20 import rosgraph_msgs.msg
21 import sdc21x0.msg
22 import sensor_msgs.msg
23 import std_msgs.msg
24 import tf2_msgs.msg
25 import visualization_msgs.msg
26 
27 from collections import OrderedDict
28 
29 tf_prefix = ''
30 static_transforms = OrderedDict()
31 
32 
33 class TopicConfig(object):
34  def __init__(self, topic, topic_type, latch=False, dict_filter=None):
35  self.topic = topic
36  self.topic_type = topic_type
37  self.latch = latch
38  self.dict_filter = dict_filter
39 
40 
41 # remap mir_actions/MirMoveBaseAction => move_base_msgs/MoveBaseAction
43  filtered_msg_dict = copy.deepcopy(msg_dict)
44  filtered_msg_dict['goal']['move_task'] = mir_actions.msg.MirMoveBaseGoal.GLOBAL_MOVE
45  filtered_msg_dict['goal']['goal_dist_threshold'] = 0.25
46  filtered_msg_dict['goal']['clear_costmaps'] = True
47  return filtered_msg_dict
48 
49 
51  # filter out slots from the dict that are not in our message definition
52  # e.g., MiRMoveBaseFeedback has the field "state", which MoveBaseFeedback doesn't
53  filtered_msg_dict = copy.deepcopy(msg_dict)
54  filtered_msg_dict['feedback'] = {
55  key: msg_dict['feedback'][key] for key in move_base_msgs.msg.MoveBaseFeedback.__slots__
56  }
57  return filtered_msg_dict
58 
59 
61  filtered_msg_dict = copy.deepcopy(msg_dict)
62  filtered_msg_dict['result'] = {key: msg_dict['result'][key] for key in move_base_msgs.msg.MoveBaseResult.__slots__}
63  return filtered_msg_dict
64 
65 
66 def _cmd_vel_dict_filter(msg_dict):
67  """
68  Converts a geometry_msgs/Twist message dict (as sent from the ROS side) to
69  a geometry_msgs/TwistStamped message dict (as expected by the MiR on
70  software version >=2.7).
71  """
72  header = std_msgs.msg.Header(frame_id='', stamp=rospy.Time.now())
73  filtered_msg_dict = {
74  'header': message_converter.convert_ros_message_to_dictionary(header),
75  'twist': copy.deepcopy(msg_dict),
76  }
77  return filtered_msg_dict
78 
79 
80 def _tf_dict_filter(msg_dict):
81  filtered_msg_dict = copy.deepcopy(msg_dict)
82  for transform in filtered_msg_dict['transforms']:
83  transform['child_frame_id'] = tf_prefix + '/' + transform['child_frame_id'].strip('/')
84  return filtered_msg_dict
85 
86 
87 def _tf_static_dict_filter(msg_dict):
88  """
89  The tf_static topic needs special handling. Publishers on tf_static are *latched*, which means that the ROS master
90  caches the last message that was sent by each publisher on that topic, and will forward it to new subscribers.
91  However, since the mir_driver node appears to the ROS master as a single node with a single publisher on tf_static,
92  and there are multiple actual publishers hiding behind it on the MiR side, only one of those messages will be
93  cached. Therefore, we need to implement the caching ourselves and make sure that we always publish the full set of
94  transforms as a single message.
95  """
96  global static_transforms
97 
98  # prepend tf_prefix
99  filtered_msg_dict = _tf_dict_filter(msg_dict)
100 
101  # The following code was copied + modified from https://github.com/tradr-project/static_transform_mux .
102 
103  # Process the incoming transforms, merge them with our cache.
104  for transform in filtered_msg_dict['transforms']:
105  key = transform['child_frame_id']
106  rospy.loginfo(
107  "[%s] tf_static: updated transform %s->%s.",
108  rospy.get_name(),
109  transform['header']['frame_id'],
110  transform['child_frame_id'],
111  )
112  static_transforms[key] = transform
113 
114  # Return the cached messages.
115  filtered_msg_dict['transforms'] = static_transforms.values()
116  rospy.loginfo(
117  "[%s] tf_static: sent %i transforms: %s",
118  rospy.get_name(),
119  len(static_transforms),
120  str(static_transforms.keys()),
121  )
122  return filtered_msg_dict
123 
124 
126  # filtered_msg_dict = copy.deepcopy(msg_dict)
127  if not isinstance(msg_dict, dict): # can happen during recursion
128  return
129  for (key, value) in msg_dict.iteritems():
130  if key == 'header':
131  try:
132  # prepend frame_id
133  frame_id = value['frame_id'].strip('/')
134  if frame_id != 'map':
135  # prepend tf_prefix, then remove leading '/' (e.g., when tf_prefix is empty)
136  value['frame_id'] = (tf_prefix + '/' + frame_id).strip('/')
137  else:
138  value['frame_id'] = frame_id
139 
140  except TypeError:
141  pass # value is not a dict
142  except KeyError:
143  pass # value doesn't have key 'frame_id'
144  elif isinstance(value, dict):
146  elif isinstance(value, Iterable): # an Iterable other than dict (e.g., a list)
147  for item in value:
149  return msg_dict
150 
151 
153  # filtered_msg_dict = copy.deepcopy(msg_dict)
154  if not isinstance(msg_dict, dict): # can happen during recursion
155  return
156  for (key, value) in msg_dict.iteritems():
157  if key == 'header':
158  try:
159  # remove frame_id
160  s = value['frame_id'].strip('/')
161  if s.find(tf_prefix) == 0:
162  value['frame_id'] = (s[len(tf_prefix) :]).strip('/') # strip off tf_prefix, then strip leading '/'
163  except TypeError:
164  pass # value is not a dict
165  except KeyError:
166  pass # value doesn't have key 'frame_id'
167  elif isinstance(value, dict):
169  elif isinstance(value, Iterable): # an Iterable other than dict (e.g., a list)
170  for item in value:
172  return msg_dict
173 
174 
175 # topics we want to publish to ROS (and subscribe to from the MiR)
176 PUB_TOPICS = [
177  # TopicConfig('LightCtrl/bms_data', mir_msgs.msg.BMSData),
178  # TopicConfig('LightCtrl/charging_state', mir_msgs.msg.ChargingState),
179  TopicConfig('LightCtrl/us_list', sensor_msgs.msg.Range),
180  # TopicConfig('MC/battery_currents', mir_msgs.msg.BatteryCurrents),
181  # TopicConfig('MC/battery_voltage', std_msgs.msg.Float64),
182  TopicConfig('MC/currents', sdc21x0.msg.MotorCurrents),
183  # TopicConfig('MC/encoders', sdc21x0.msg.StampedEncoders),
184  TopicConfig('MissionController/CheckArea/visualization_marker', visualization_msgs.msg.Marker),
185  # TopicConfig('MissionController/goal_position_guid', std_msgs.msg.String),
186  # TopicConfig('MissionController/prompt_user', mir_msgs.msg.UserPrompt),
187  TopicConfig('SickPLC/parameter_descriptions', dynamic_reconfigure.msg.ConfigDescription),
188  TopicConfig('SickPLC/parameter_updates', dynamic_reconfigure.msg.Config),
189  # TopicConfig('active_mapping_guid', std_msgs.msg.String),
190  TopicConfig('amcl_pose', geometry_msgs.msg.PoseWithCovarianceStamped),
191  TopicConfig('b_raw_scan', sensor_msgs.msg.LaserScan),
192  TopicConfig('b_scan', sensor_msgs.msg.LaserScan),
193  TopicConfig('camera_floor/background', sensor_msgs.msg.PointCloud2),
194  TopicConfig('camera_floor/depth/parameter_descriptions', dynamic_reconfigure.msg.ConfigDescription),
195  TopicConfig('camera_floor/depth/parameter_updates', dynamic_reconfigure.msg.Config),
196  TopicConfig('camera_floor/depth/points', sensor_msgs.msg.PointCloud2),
197  TopicConfig('camera_floor/filter/visualization_marker', visualization_msgs.msg.Marker),
198  TopicConfig('camera_floor/floor', sensor_msgs.msg.PointCloud2),
199  TopicConfig('camera_floor/obstacles', sensor_msgs.msg.PointCloud2),
200  TopicConfig('check_area/polygon', geometry_msgs.msg.PolygonStamped),
201  # TopicConfig('check_pose_area/polygon', geometry_msgs.msg.PolygonStamped),
202  # TopicConfig('data_events/area_events', mir_data_msgs.msg.AreaEventEvent),
203  # TopicConfig('data_events/maps', mir_data_msgs.msg.MapEvent),
204  # TopicConfig('data_events/positions', mir_data_msgs.msg.PositionEvent),
205  # TopicConfig('data_events/registers', mir_data_msgs.msg.PLCRegisterEvent),
206  # TopicConfig('data_events/sounds', mir_data_msgs.msg.SoundEvent),
207  TopicConfig('diagnostics', diagnostic_msgs.msg.DiagnosticArray),
208  TopicConfig('diagnostics_agg', diagnostic_msgs.msg.DiagnosticArray),
209  TopicConfig('diagnostics_toplevel_state', diagnostic_msgs.msg.DiagnosticStatus),
210  TopicConfig('f_raw_scan', sensor_msgs.msg.LaserScan),
211  TopicConfig('f_scan', sensor_msgs.msg.LaserScan),
212  TopicConfig('imu_data', sensor_msgs.msg.Imu),
213  TopicConfig('laser_back/driver/parameter_descriptions', dynamic_reconfigure.msg.ConfigDescription),
214  TopicConfig('laser_back/driver/parameter_updates', dynamic_reconfigure.msg.Config),
215  TopicConfig('laser_front/driver/parameter_descriptions', dynamic_reconfigure.msg.ConfigDescription),
216  TopicConfig('laser_front/driver/parameter_updates', dynamic_reconfigure.msg.Config),
217  # TopicConfig('localization_score', std_msgs.msg.Float64),
218  TopicConfig('/map', nav_msgs.msg.OccupancyGrid, latch=True),
219  TopicConfig('/map_metadata', nav_msgs.msg.MapMetaData),
220  # TopicConfig('marker_tracking_node/feedback', mir_marker_tracking.msg.MarkerTrackingActionFeedback),
221  # TopicConfig(
222  # 'marker_tracking_node/laser_line_extract/parameter_descriptions', dynamic_reconfigure.msg.ConfigDescription
223  # ),
224  # TopicConfig('marker_tracking_node/laser_line_extract/parameter_updates', dynamic_reconfigure.msg.Config),
225  # TopicConfig('marker_tracking_node/laser_line_extract/visualization_marker', visualization_msgs.msg.Marker),
226  # TopicConfig('marker_tracking_node/result', mir_marker_tracking.msg.MarkerTrackingActionResult),
227  # TopicConfig('marker_tracking_node/status', actionlib_msgs.msg.GoalStatusArray),
228  # TopicConfig('mirEventTrigger/events', mir_msgs.msg.Events),
229  TopicConfig('mir_amcl/parameter_descriptions', dynamic_reconfigure.msg.ConfigDescription),
230  TopicConfig('mir_amcl/parameter_updates', dynamic_reconfigure.msg.Config),
231  TopicConfig('mir_amcl/selected_points', sensor_msgs.msg.PointCloud2),
232  TopicConfig('mir_log', rosgraph_msgs.msg.Log),
233  # TopicConfig('mir_sound/sound_event', mir_msgs.msg.SoundEvent),
234  TopicConfig('mir_status_msg', std_msgs.msg.String),
235  # TopicConfig('mirspawn/node_events', mirSpawn.msg.LaunchItem),
236  TopicConfig('mirwebapp/grid_map_metadata', mir_msgs.msg.LocalMapStat),
237  TopicConfig('mirwebapp/laser_map_metadata', mir_msgs.msg.LocalMapStat),
238  # TopicConfig('mirwebapp/web_path', mir_msgs.msg.WebPath),
239  # really mir_actions/MirMoveBaseActionFeedback:
240  TopicConfig(
241  'move_base/feedback', move_base_msgs.msg.MoveBaseActionFeedback, dict_filter=_move_base_feedback_dict_filter
242  ),
243  # really mir_actions/MirMoveBaseActionResult:
244  TopicConfig('move_base/result', move_base_msgs.msg.MoveBaseActionResult, dict_filter=_move_base_result_dict_filter),
245  TopicConfig('move_base/status', actionlib_msgs.msg.GoalStatusArray),
246  # TopicConfig('move_base_node/MIRPlannerROS/cost_cloud', sensor_msgs.msg.PointCloud2),
247  # TopicConfig('move_base_node/MIRPlannerROS/global_plan', nav_msgs.msg.Path),
248  # TopicConfig('move_base_node/MIRPlannerROS/len_to_goal', std_msgs.msg.Float64),
249  TopicConfig('move_base_node/MIRPlannerROS/local_plan', nav_msgs.msg.Path),
250  # TopicConfig('move_base_node/MIRPlannerROS/parameter_descriptions', dynamic_reconfigure.msg.ConfigDescription),
251  # TopicConfig('move_base_node/MIRPlannerROS/parameter_updates', dynamic_reconfigure.msg.Config),
252  TopicConfig('move_base_node/MIRPlannerROS/updated_global_plan', mir_msgs.msg.PlanSegments),
253  # TopicConfig('move_base_node/MIRPlannerROS/visualization_marker', visualization_msgs.msg.MarkerArray),
254  TopicConfig('move_base_node/SBPLLatticePlanner/plan', nav_msgs.msg.Path),
255  # TopicConfig(
256  # 'move_base_node/SBPLLatticePlanner/sbpl_lattice_planner_stats', sbpl_lattice_planner.msg.SBPLLatticePlannerStats
257  # ),
258  # TopicConfig('move_base_node/SBPLLatticePlanner/visualization_marker', visualization_msgs.msg.MarkerArray),
259  TopicConfig('move_base_node/current_goal', geometry_msgs.msg.PoseStamped),
260  # TopicConfig('move_base_node/global_costmap/inflated_obstacles', nav_msgs.msg.GridCells),
261  # TopicConfig('move_base_node/global_costmap/obstacles', nav_msgs.msg.GridCells),
262  # TopicConfig('move_base_node/global_costmap/parameter_descriptions', dynamic_reconfigure.msg.ConfigDescription),
263  # TopicConfig('move_base_node/global_costmap/parameter_updates', dynamic_reconfigure.msg.Config),
264  # TopicConfig('move_base_node/global_costmap/robot_footprint', geometry_msgs.msg.PolygonStamped),
265  # TopicConfig('move_base_node/global_costmap/unknown_space', nav_msgs.msg.GridCells),
266  # TopicConfig('move_base_node/global_plan', nav_msgs.msg.Path),
267  TopicConfig('move_base_node/local_costmap/inflated_obstacles', nav_msgs.msg.GridCells),
268  TopicConfig('move_base_node/local_costmap/obstacles', nav_msgs.msg.GridCells),
269  # TopicConfig('move_base_node/local_costmap/parameter_descriptions', dynamic_reconfigure.msg.ConfigDescription),
270  # TopicConfig('move_base_node/local_costmap/parameter_updates', dynamic_reconfigure.msg.Config),
271  TopicConfig('move_base_node/local_costmap/robot_footprint', geometry_msgs.msg.PolygonStamped),
272  # TopicConfig('move_base_node/local_costmap/safety_zone', geometry_msgs.msg.PolygonStamped),
273  # TopicConfig('move_base_node/local_costmap/unknown_space', nav_msgs.msg.GridCells),
274  # TopicConfig('move_base_node/mir_escape_recovery/visualization_marker', visualization_msgs.msg.Marker),
275  # TopicConfig('move_base_node/parameter_descriptions', dynamic_reconfigure.msg.ConfigDescription),
276  # TopicConfig('move_base_node/parameter_updates', dynamic_reconfigure.msg.Config),
277  TopicConfig('move_base_node/time_to_coll', std_msgs.msg.Float64),
278  TopicConfig('move_base_node/traffic_costmap/inflated_obstacles', nav_msgs.msg.GridCells),
279  TopicConfig('move_base_node/traffic_costmap/obstacles', nav_msgs.msg.GridCells),
280  TopicConfig('move_base_node/traffic_costmap/parameter_descriptions', dynamic_reconfigure.msg.ConfigDescription),
281  TopicConfig('move_base_node/traffic_costmap/parameter_updates', dynamic_reconfigure.msg.Config),
282  TopicConfig('move_base_node/traffic_costmap/robot_footprint', geometry_msgs.msg.PolygonStamped),
283  TopicConfig('move_base_node/traffic_costmap/unknown_space', nav_msgs.msg.GridCells),
284  TopicConfig('move_base_node/visualization_marker', visualization_msgs.msg.Marker),
285  TopicConfig('move_base_simple/visualization_marker', visualization_msgs.msg.Marker),
286  TopicConfig('odom', nav_msgs.msg.Odometry),
287  TopicConfig('odom_enc', nav_msgs.msg.Odometry),
288  # TopicConfig('one_way_map', nav_msgs.msg.OccupancyGrid),
289  # TopicConfig('param_update', std_msgs.msg.String),
290  # TopicConfig('particlevizmarker', visualization_msgs.msg.MarkerArray),
291  # TopicConfig('resource_tracker/needed_resources', mir_msgs.msg.ResourcesState),
292  TopicConfig('robot_mode', mir_msgs.msg.RobotMode),
293  TopicConfig('robot_pose', geometry_msgs.msg.Pose),
294  TopicConfig('robot_state', mir_msgs.msg.RobotState),
295  # TopicConfig('robot_status', mir_msgs.msg.RobotStatus),
296  TopicConfig('/rosout', rosgraph_msgs.msg.Log),
297  TopicConfig('/rosout_agg', rosgraph_msgs.msg.Log),
298  TopicConfig('scan', sensor_msgs.msg.LaserScan),
299  # TopicConfig('scan_filter/parameter_descriptions', dynamic_reconfigure.msg.ConfigDescription),
300  # TopicConfig('scan_filter/parameter_updates', dynamic_reconfigure.msg.Config),
301  TopicConfig('scan_filter/visualization_marker', visualization_msgs.msg.Marker),
302  # TopicConfig('session_importer_node/info', mirSessionImporter.msg.SessionImportInfo),
303  # TopicConfig('set_mc_PID', std_msgs.msg.Float64MultiArray),
304  TopicConfig('/tf', tf2_msgs.msg.TFMessage, dict_filter=_tf_dict_filter),
305  TopicConfig('/tf_static', tf2_msgs.msg.TFMessage, dict_filter=_tf_static_dict_filter, latch=True),
306  # TopicConfig('traffic_map', nav_msgs.msg.OccupancyGrid),
307  # TopicConfig('wifi_diagnostics', diagnostic_msgs.msg.DiagnosticArray),
308  # TopicConfig('wifi_diagnostics/cur_ap', mir_wifi_msgs.msg.APInfo),
309  # TopicConfig('wifi_diagnostics/roam_events', mir_wifi_msgs.msg.WifiRoamEvent),
310  # TopicConfig('wifi_diagnostics/wifi_ap_interface_stats', mir_wifi_msgs.msg.WifiInterfaceStats),
311  # TopicConfig('wifi_diagnostics/wifi_ap_rssi', mir_wifi_msgs.msg.APRssiStats),
312  # TopicConfig('wifi_diagnostics/wifi_ap_time_stats', mir_wifi_msgs.msg.APTimeStats),
313  # TopicConfig('wifi_watchdog/ping', mir_wifi_msgs.msg.APPingStats),
314 ]
315 
316 # topics we want to subscribe to from ROS (and publish to the MiR)
317 SUB_TOPICS = [
318  # really geometry_msgs.msg.TwistStamped:
319  TopicConfig('cmd_vel', geometry_msgs.msg.Twist, dict_filter=_cmd_vel_dict_filter),
320  TopicConfig('initialpose', geometry_msgs.msg.PoseWithCovarianceStamped),
321  TopicConfig('light_cmd', std_msgs.msg.String),
322  TopicConfig('mir_cmd', std_msgs.msg.String),
323  TopicConfig('move_base/cancel', actionlib_msgs.msg.GoalID),
324  # really mir_actions/MirMoveBaseActionGoal:
325  TopicConfig('move_base/goal', move_base_msgs.msg.MoveBaseActionGoal, dict_filter=_move_base_goal_dict_filter),
326 ]
327 
328 
329 class PublisherWrapper(rospy.SubscribeListener):
330  def __init__(self, topic_config, robot):
331  self.topic_config = topic_config
332  self.robot = robot
333  self.connected = False
334  # Use topic_config.topic directly here. If it does not have a leading slash, it will use the private namespace.
335  self.pub = rospy.Publisher(
336  name=topic_config.topic,
337  data_class=topic_config.topic_type,
338  subscriber_listener=self,
339  latch=topic_config.latch,
340  queue_size=10,
341  )
342  rospy.loginfo(
343  "[%s] publishing topic '%s' [%s]", rospy.get_name(), topic_config.topic, topic_config.topic_type._type
344  )
345  # latched topics must be subscribed immediately
346  if topic_config.latch:
347  self.peer_subscribe("", None, None)
348 
349  def peer_subscribe(self, topic_name, topic_publish, peer_publish):
350  if not self.connected:
351  self.connected = True
352  rospy.loginfo("[%s] starting to stream messages on topic '%s'", rospy.get_name(), self.topic_config.topic)
353  absolute_topic = '/' + self.topic_config.topic.lstrip('/') # ensure exactly 1 leading slash for MiR comm
354  self.robot.subscribe(topic=absolute_topic, callback=self.callback)
355 
356  def peer_unsubscribe(self, topic_name, num_peers):
357  pass
358  # doesn't work: once ubsubscribed, robot doesn't let us subscribe again
359  # if self.connected and self.pub.get_num_connections() == 0 and not self.topic_config.latch:
360  # self.connected = False
361  # rospy.loginfo("[%s] stopping to stream messages on topic '%s'", rospy.get_name(), self.topic_config.topic)
362  # absolute_topic = '/' + self.topic_config.topic.lstrip('/') # ensure exactly 1 leading slash for MiR comm
363  # self.robot.unsubscribe(topic=absolute_topic)
364 
365  def callback(self, msg_dict):
366  msg_dict = _prepend_tf_prefix_dict_filter(msg_dict)
367  if self.topic_config.dict_filter is not None:
368  msg_dict = self.topic_config.dict_filter(msg_dict)
369  msg = message_converter.convert_dictionary_to_ros_message(self.topic_config.topic_type._type, msg_dict)
370  self.pub.publish(msg)
371 
372 
373 class SubscriberWrapper(object):
374  def __init__(self, topic_config, robot):
375  self.topic_config = topic_config
376  self.robot = robot
377  # Use topic_config.topic directly here. If it does not have a leading slash, it will use the private namespace.
378  self.sub = rospy.Subscriber(
379  name=topic_config.topic, data_class=topic_config.topic_type, callback=self.callback, queue_size=10
380  )
381  rospy.loginfo(
382  "[%s] subscribing to topic '%s' [%s]", rospy.get_name(), topic_config.topic, topic_config.topic_type._type
383  )
384 
385  def callback(self, msg):
386  msg_dict = message_converter.convert_ros_message_to_dictionary(msg)
387  msg_dict = _remove_tf_prefix_dict_filter(msg_dict)
388  if self.topic_config.dict_filter is not None:
389  msg_dict = self.topic_config.dict_filter(msg_dict)
390  absolute_topic = '/' + self.topic_config.topic.lstrip('/') # ensure exactly 1 leading slash for MiR comm
391  self.robot.publish(absolute_topic, msg_dict)
392 
393 
394 class MiR100Bridge(object):
395  def __init__(self):
396  try:
397  hostname = rospy.get_param('~hostname')
398  except KeyError:
399  rospy.logfatal('[%s] parameter "hostname" is not set!', rospy.get_name())
400  sys.exit(-1)
401  port = rospy.get_param('~port', 9090)
402 
403  global tf_prefix
404  tf_prefix = rospy.get_param('~tf_prefix', '').strip('/')
405 
406  rospy.loginfo('[%s] trying to connect to %s:%i...', rospy.get_name(), hostname, port)
407  self.robot = rosbridge.RosbridgeSetup(hostname, port)
408 
409  r = rospy.Rate(10)
410  i = 1
411  while not self.robot.is_connected():
412  if rospy.is_shutdown():
413  sys.exit(0)
414  if self.robot.is_errored():
415  rospy.logfatal('[%s] connection error to %s:%i, giving up!', rospy.get_name(), hostname, port)
416  sys.exit(-1)
417  if i % 10 == 0:
418  rospy.logwarn('[%s] still waiting for connection to %s:%i...', rospy.get_name(), hostname, port)
419  i += 1
420  r.sleep()
421 
422  rospy.loginfo('[%s] ... connected.', rospy.get_name())
423 
424  topics = self.get_topics()
425  published_topics = [topic_name for (topic_name, _, has_publishers, _) in topics if has_publishers]
426  subscribed_topics = [topic_name for (topic_name, _, _, has_subscribers) in topics if has_subscribers]
427 
428  for pub_topic in PUB_TOPICS:
429  PublisherWrapper(pub_topic, self.robot)
430  absolute_topic = '/' + pub_topic.topic.lstrip('/') # ensure exactly 1 leading slash for MiR comm
431  if absolute_topic not in published_topics:
432  rospy.logwarn("[%s] topic '%s' is not published by the MiR!", rospy.get_name(), pub_topic.topic)
433 
434  for sub_topic in SUB_TOPICS:
435  SubscriberWrapper(sub_topic, self.robot)
436  absolute_topic = '/' + sub_topic.topic.lstrip('/') # ensure exactly 1 leading slash for MiR comm
437  if absolute_topic not in subscribed_topics:
438  rospy.logwarn("[%s] topic '%s' is not yet subscribed to by the MiR!", rospy.get_name(), sub_topic.topic)
439 
440  # At least with software version 2.8 there were issues when forwarding a simple goal to the robot
441  # This workaround converts it into an action. Check https://github.com/dfki-ric/mir_robot/issues/60 for details.
442  self._move_base_client = SimpleActionClient('move_base', move_base_msgs.msg.MoveBaseAction)
443  rospy.Subscriber("move_base_simple/goal", geometry_msgs.msg.PoseStamped, self._move_base_simple_goal_callback)
444 
445  def get_topics(self):
446  srv_response = self.robot.callService('/rosapi/topics', msg={})
447  topic_names = sorted(srv_response['topics'])
448  topics = []
449 
450  for topic_name in topic_names:
451  srv_response = self.robot.callService("/rosapi/topic_type", msg={'topic': topic_name})
452  topic_type = srv_response['type']
453 
454  srv_response = self.robot.callService("/rosapi/publishers", msg={'topic': topic_name})
455  has_publishers = True if len(srv_response['publishers']) > 0 else False
456 
457  srv_response = self.robot.callService("/rosapi/subscribers", msg={'topic': topic_name})
458  has_subscribers = True if len(srv_response['subscribers']) > 0 else False
459 
460  topics.append([topic_name, topic_type, has_publishers, has_subscribers])
461 
462  print 'Publishers:'
463  for (topic_name, topic_type, has_publishers, has_subscribers) in topics:
464  if has_publishers:
465  print ' * %s [%s]' % (topic_name, topic_type)
466 
467  print '\nSubscribers:'
468  for (topic_name, topic_type, has_publishers, has_subscribers) in topics:
469  if has_subscribers:
470  print ' * %s [%s]' % (topic_name, topic_type)
471 
472  return topics
473 
475  if not self._move_base_client.wait_for_server(rospy.Duration(2)):
476  rospy.logwarn("Could not connect to 'move_base' server after two seconds. Dropping goal.")
477  rospy.logwarn("Did you activate 'planner' in the MIR web interface?")
478  return
479  goal = move_base_msgs.msg.MoveBaseGoal()
480  goal.target_pose.header = copy.deepcopy(msg.header)
481  goal.target_pose.pose = copy.deepcopy(msg.pose)
482  self._move_base_client.send_goal(goal)
483 
484 
485 def main():
486  rospy.init_node('mir_bridge')
487  MiR100Bridge()
488  rospy.spin()
489 
490 
491 if __name__ == '__main__':
492  try:
493  main()
494  except rospy.ROSInterruptException:
495  pass
def _tf_static_dict_filter(msg_dict)
Definition: mir_bridge.py:87
def peer_subscribe(self, topic_name, topic_publish, peer_publish)
Definition: mir_bridge.py:349
def _move_base_feedback_dict_filter(msg_dict)
Definition: mir_bridge.py:50
def _move_base_simple_goal_callback(self, msg)
Definition: mir_bridge.py:474
def _cmd_vel_dict_filter(msg_dict)
Definition: mir_bridge.py:66
def __init__(self, topic_config, robot)
Definition: mir_bridge.py:374
def __init__(self, topic_config, robot)
Definition: mir_bridge.py:330
def __init__(self, topic, topic_type, latch=False, dict_filter=None)
Definition: mir_bridge.py:34
def peer_unsubscribe(self, topic_name, num_peers)
Definition: mir_bridge.py:356
def main()
Definition: mir_bridge.py:485
def _prepend_tf_prefix_dict_filter(msg_dict)
Definition: mir_bridge.py:125
def _tf_dict_filter(msg_dict)
Definition: mir_bridge.py:80
def _move_base_result_dict_filter(msg_dict)
Definition: mir_bridge.py:60
def _move_base_goal_dict_filter(msg_dict)
Definition: mir_bridge.py:42
def _remove_tf_prefix_dict_filter(msg_dict)
Definition: mir_bridge.py:152
def callback(self, msg_dict)
Definition: mir_bridge.py:365


mir_driver
Author(s): Martin Günther
autogenerated on Wed Mar 22 2023 02:18:51