/udom_utils/udom_geometric_transformation/src/udom_geometric_transformation/mesh_measurer_node.py

https://gitlab.com/jsanch/udom · Python · 234 lines · 83 code · 24 blank · 127 comment · 12 complexity · 14adca5788808b48cd16abf50e666b6b MD5 · raw file

  1. #!/usr/bin/env python
  2. # -*- encoding: utf-8 -*-
  3. """
  4. This node computes the distance between two meshes using the Frobenius norm.
  5. **Input(s):**
  6. * `event_in`: The desired event for the node:
  7. `e_start`: starts the component.
  8. `e_stop`: stops the component.
  9. - *type:* `std_msgs/String`
  10. * `mesh_reference`: Mesh to measure the distance to the target mesh.
  11. - *type:* `udom_modeling_msgs/Mesh`
  12. * `mesh_target`: Mesh to measure the distance to the reference mesh.
  13. - *type:* `udom_modeling_msgs/Mesh`
  14. **Output(s):**
  15. * `distance`: The distance between the two meshes.
  16. - *type:* `std_msgs/Float32`
  17. * `event_out`: The current event of the node.
  18. `e_running`: when the component is running.
  19. `e_stopped`: when the component is stopped.
  20. - *type:* `std_msgs/String`
  21. **Parameter(s):**
  22. * `loop_rate`: Node cycle rate (in Hz).
  23. * `weight_procrustes`: Weight for the Procrustes distance.
  24. * `weight_frobenius`: Weight for the Frobenius distance.
  25. """
  26. import numpy as np
  27. import rospy
  28. from dynamic_reconfigure.server import Server
  29. import std_msgs.msg
  30. import udom_modeling_msgs.msg
  31. import udom_geometric_transformation.mesh_measurer_utils as utils
  32. from udom_geometric_transformation.cfg import MeshDisparityConfig as MeshDisparity
  33. class MeshMeasurerNode(object):
  34. """
  35. Subscribes to two udom_modeling_msgs.msg.Mesh topics, computes their distance
  36. and publishes it as a std_msgs/Float32 message.
  37. """
  38. def __init__(self):
  39. """
  40. Instantiates a mesh measurer node.
  41. :return: Distance between two meshes.
  42. :rtype: MeshMeasurerNode
  43. """
  44. # Params
  45. self.event = None
  46. self.mesh_reference = None
  47. self.mesh_target = None
  48. self.distance = std_msgs.msg.Float32()
  49. # Weight for the Procrustes distance.
  50. self.weight_procrustes = rospy.get_param('~weight_procrustes', 10.0)
  51. # Weight for the Frobenius distance.
  52. self.weight_frobenius = rospy.get_param('~weight_frobenius', 10.0)
  53. # Node cycle rate (in Hz).
  54. self.loop_rate = rospy.Rate(rospy.get_param('~loop_rate', 100))
  55. # Publishers
  56. self.event_out = rospy.Publisher("~event_out", std_msgs.msg.String, queue_size=10)
  57. self.distance_publisher = rospy.Publisher(
  58. "~distance", std_msgs.msg.Float32, queue_size=1, tcp_nodelay=True)
  59. # Subscribers
  60. rospy.Subscriber("~event_in", std_msgs.msg.String, self.event_in_cb)
  61. rospy.Subscriber(
  62. '~mesh_reference', udom_modeling_msgs.msg.Mesh, self.mesh_reference_cb)
  63. rospy.Subscriber('~mesh_target', udom_modeling_msgs.msg.Mesh, self.mesh_target_cb)
  64. # Dynamic Server Reconfiguration
  65. dynamic_reconfig_srv = Server(MeshDisparity, self.dynamic_reconfig_cb)
  66. def dynamic_reconfig_cb(self, config, level):
  67. """
  68. Reconfigures the weights for the disparity measure.
  69. """
  70. self.weight_procrustes = config.weight_procrustes
  71. self.weight_frobenius = config.weight_frobenius
  72. return config
  73. def event_in_cb(self, msg):
  74. """
  75. Obtains the event for the node (e.g. start, stop).
  76. :param msg: Event message for the node.
  77. :type msg: std_msgs.msg.String
  78. """
  79. self.event = msg.data
  80. def mesh_reference_cb(self, msg):
  81. """
  82. Obtains the reference mesh.
  83. :param msg: Reference mesh
  84. :type msg: udom_modeling_msgs.msg.Mesh
  85. """
  86. self.mesh_reference = msg
  87. def mesh_target_cb(self, msg):
  88. """
  89. Obtains the target mesh.
  90. :param msg: Target mesh
  91. :type msg: udom_modeling_msgs.msg.Mesh
  92. """
  93. self.mesh_target = msg
  94. def start(self):
  95. """
  96. Starts the node.
  97. """
  98. rospy.loginfo("Ready to start...")
  99. state = 'INIT'
  100. while not rospy.is_shutdown():
  101. if state == 'INIT':
  102. state = self.init_state()
  103. elif state == 'IDLE':
  104. state = self.idle_state()
  105. elif state == 'RUNNING':
  106. state = self.running_state()
  107. rospy.logdebug("State: {0}".format(state))
  108. self.loop_rate.sleep()
  109. def init_state(self):
  110. """
  111. Executes the INIT state of the state machine.
  112. :return: The updated state.
  113. :rtype: str
  114. """
  115. if self.event == 'e_start':
  116. return 'IDLE'
  117. else:
  118. return 'INIT'
  119. def idle_state(self):
  120. """
  121. Executes the IDLE state of the state machine.
  122. :return: The updated state.
  123. :rtype: str
  124. """
  125. if self.event == 'e_stop':
  126. self.event_out.publish('e_stopped')
  127. self.reset_component_data()
  128. return 'INIT'
  129. elif (self.mesh_reference is not None) and (self.mesh_target is not None):
  130. return 'RUNNING'
  131. else:
  132. return 'IDLE'
  133. def running_state(self):
  134. """
  135. Executes the RUNNING state of the state machine.
  136. :return: The updated state.
  137. :rtype: str
  138. """
  139. if self.event == 'e_stop':
  140. self.event_out.publish('e_stopped')
  141. self.reset_component_data()
  142. return 'INIT'
  143. else:
  144. self.distance.data = self.mesh_distance()
  145. self.event_out.publish('e_running')
  146. self.distance_publisher.publish(self.distance)
  147. self.reset_component_data()
  148. return 'IDLE'
  149. def mesh_distance(self):
  150. """
  151. Computes the distance between the reference and target meshes using the
  152. Frobenius norm.
  153. :return: The distance between the meshes.
  154. :rtype: Float32
  155. """
  156. x = utils.mesh_to_points(self.mesh_reference)
  157. y = utils.mesh_to_points(self.mesh_target)
  158. return np.linalg.norm(x - y)
  159. def reset_component_data(self):
  160. """
  161. Clears the data of the component.
  162. """
  163. self.mesh_reference = None
  164. self.mesh_target = None
  165. self.event = None
  166. def main():
  167. rospy.init_node("mesh_measurer_node", anonymous=True)
  168. mesh_measurer_node = MeshMeasurerNode()
  169. mesh_measurer_node.start()