37 A node to check the TF tree 
   39 A big chunk of the code was copied from tfwtf, the wtf plugin for tf. 
   42 import roslib; roslib.load_manifest(
'diagnostic_common_diagnostics')
 
   44 import diagnostic_updater 
as DIAG
 
   59     for m, stamp, callerid 
in _msgs:
 
   60         for t 
in m.transforms:
 
   61             d = t.header.stamp - stamp
 
   64                 if callerid 
in deltas:
 
   65                     if abs(secs) > abs(deltas[callerid]):
 
   66                         deltas[callerid] = secs
 
   68                     deltas[callerid]  = secs
 
   71     for k, v 
in deltas.items():
 
   72         errors.append(
"receiving transform from [%s] that differed from ROS time by %ss"%(k, v))
 
   78     for m, stamp, callerid 
in _msgs:
 
   79         for t 
in m.transforms:
 
   80             frame_id = t.child_frame_id
 
   81             parent_id = t.header.frame_id
 
   82             if frame_id 
in parent_id_map 
and parent_id_map[frame_id] != parent_id:
 
   83                 msg = 
"reparenting of [%s] to [%s] by [%s]"%(frame_id, parent_id, callerid)
 
   84                 parent_id_map[frame_id] = parent_id
 
   88                 parent_id_map[frame_id] = parent_id
 
   95     for m, stamp, callerid 
in _msgs:
 
   96         for t 
in m.transforms:
 
   97             frame_id = t.child_frame_id
 
   98             parent_id = t.header.frame_id
 
   99             parent_id_map[frame_id] = parent_id
 
  101     for frame 
in parent_id_map:
 
  103         current_frame = frame
 
  105         while count < max_depth + 2:
 
  107             frame_list.append(current_frame)
 
  109                 current_frame = parent_id_map[current_frame]
 
  112             if current_frame == frame:
 
  113                 errors.append(
"Frame %s is in a loop. It's loop has elements:\n%s"% (frame, 
" -> ".join(frame_list)))
 
  122     for m, stamp, callerid 
in _msgs:
 
  123         for t 
in m.transforms:
 
  124             frame_id = t.child_frame_id
 
  125             parent_id = t.header.frame_id
 
  126             if frame_id 
in authority_map 
and authority_map[frame_id] != callerid:
 
  127                 msg = 
"node [%s] publishing transform [%s] with parent [%s] already published by node [%s]"%(callerid, frame_id, parent_id, authority_map[frame_id])
 
  128                 authority_map[frame_id] = callerid
 
  129                 if msg 
not in errors:
 
  132                 authority_map[frame_id] = callerid
 
  140     _msgs.append((msg, rospy.get_rostime(), msg._connection_header[
'callerid']))
 
  148     '''A diagnostic function generator''' 
  151         stat.summary(0, okmsg)
 
  154         if isinstance(res, bool):
 
  156                 stat.summary(errlvl, errmsg)
 
  157         elif isinstance(res, list):
 
  159                 stat.summary(errlvl, errmsg)
 
  160                 for i,r 
in enumerate(res):
 
  161                     stat.add(
"Error %d" % (i+1), r)
 
  168 rospy.init_node(
'tf_monitor')
 
  171 diag_updater = DIAG.Updater()
 
  172 diag_updater.setHardwareID(
'none')
 
  173 diag_updater.add(
'Messaging status', 
make_diag_fn(no_msgs, DIAG.WARN, 
'No tf messages received') )
 
  174 diag_updater.add(
'Time status', 
make_diag_fn(rostime_delta, DIAG.WARN, 
'Received out-of-date/future transforms') )
 
  175 diag_updater.add(
'Reparenting status', 
make_diag_fn(reparenting, DIAG.ERROR, 
'TF re-parenting contention') )
 
  176 diag_updater.add(
'Cycle status', 
make_diag_fn(cycle_detection, DIAG.ERROR, 
'TF cycle detection') )
 
  177 diag_updater.add(
'Multiple authority status', 
make_diag_fn(multiple_authority, DIAG.ERROR, 
'TF multiple authority contention') )
 
  180 while not rospy.is_shutdown():
 
  182     sub1 = rospy.Subscriber(
'/tf', tf.msg.tfMessage, _tf_handle)
 
  185     diag_updater.update()