15 from __future__
import print_function
20 from .waypoint
import Waypoint
21 from uuv_control_msgs.msg
import WaypointSet
as WaypointSetMessage
22 from visualization_msgs.msg
import Marker, MarkerArray
23 from nav_msgs.msg
import Path
24 from geometry_msgs.msg
import PoseStamped
32 * `FINAL_WAYPOINT_COLOR` (*type:* list of `float`, *value:* `[1.0, 0.5737, 0.0]`): RGB color for marker of the final waypoint in RViz 33 * `OK_WAYPOINT` (*type:* list of `float`, *value:* `[0.1216, 0.4157, 0.8863]`): RGB color for marker of a successful waypoint in RViz 34 * `FAILED_WAYPOINT` (*type:* list of `float`, *value:* `[1.0, 0.0, 0.0]`): RGB color for marker of a failed waypoint in RViz 38 * `scale` (*type:* `float`, *default:* `0.1`): Scale of the spherical marker for waypoints 39 * `inertial_frame_id` (*type:* `str`, *default:* `'world'`): Name of the inertial reference frame, options are `world` and `world_ned` for `ENU` and `NED` inertial reference frames, respectively 40 * `max_surge_speed` (*type:* `float`, *default:* `None`): Max. surge speed in m/s associated with each waypoint 43 FINAL_WAYPOINT_COLOR = [1.0, 0.5737, 0.0]
44 OK_WAYPOINT = [31. / 255, 106. / 255, 226. / 255]
45 FAILED_WAYPOINT = [1.0, 0.0, 0.0]
47 def __init__(self, scale=0.1, inertial_frame_id='world', max_surge_speed=None):
48 assert inertial_frame_id
in [
'world',
'world_ned']
57 msg =
'================================\n' 58 msg +=
'List of waypoints\n' 59 msg +=
'================================\n' 64 msg +=
'Number of valid waypoints = %d\n' % self.
num_waypoints 68 return 'Waypoint set is empty' 72 """`int`: Number of waypoints""" 77 """`list`: List of the X-coordinates of all waypoints""" 82 """`list`: List of the Y-coordinates of all waypoints""" 87 """`list`: List of the Z-coordinates of all waypoints""" 92 """`bool`: True if the list of waypoints is empty""" 97 """`str`: Name of inertial reference frame""" 100 @inertial_frame_id.setter
102 assert frame_id
in [
'world',
'world_ned'], \
103 'Inertial reference frame can only be either world or world_ned' 107 """Clear the list of waypoints""" 111 """Set the flag violates_constraint to a waypoint 115 * `index` (*type:* `int`): Index of the waypoints 116 * `flag` (*type:* `bool`): True, if waypoint violates a constraint 120 `True` if successful, and `False` if the waypoint `index` is outsite of the list's range. 122 if index < 0
or index >= len(self.
_waypoints):
124 self.
_waypoints[index].violates_constraint = flag
132 * `index` (*type:* `int`): Index of the waypoint 136 Return a waypoint as `uuv_waypoints.Waypoint` or `None` if `index` is outside of range. 138 if index < 0
or index >= len(self.
_waypoints):
143 """Add a waypoint to the set 147 * `waypoint` (*type:* `uuv_waypoints.Waypoint`): Waypoint object 148 * `add_to_beginning` (*type:* `bool`, *default:* `False`): If `True`, add the waypoint to the beginning of the list. 152 `True` if waypoint was added to the set. `False` if a repeated waypoint is already found in the set. 156 if not add_to_beginning:
157 self._waypoints.append(waypoint)
161 print(
'Cannot add repeated waypoint')
164 if not add_to_beginning:
165 self._waypoints.append(waypoint)
171 """Add waypoint from ROS `uuv_control_msgs/Waypoint` message 175 * `msg` (*type:* `uuv_control_msgs/Waypoint`): Waypoint message 179 `True`, if waypoint could be added to set, `False`, otherwise. 181 waypoint = Waypoint()
182 waypoint.from_message(msg)
186 """Return the starting waypoint 190 A `uuv_waypoints.Waypoint` object or None, if the list of waypoints is empty. 198 """Return the final waypoint 202 A `uuv_waypoints.Waypoint` object or None, if the list of waypoints is empty. 209 """Remove waypoint from set. 213 * `waypoint` (*type:* `uuv_waypoints.Waypoint`): Waypoint object 215 new_waypoints = list()
217 if point == waypoint:
219 new_waypoints.append(point)
223 """Read waypoint set from a YAML file. 227 * `filename` (*type:* `str`): Filename of the waypoint set 231 `True` if waypoint set file could be parsed, `False`, otherwise. 233 if not os.path.isfile(filename):
234 print(
'Invalid waypoint filename, filename={}'.format(filename))
238 with open(filename,
'r') as wp_file: 239 wps = yaml.load(wp_file) 240 if isinstance(wps, list):
243 x=wp_data[
'point'][0],
244 y=wp_data[
'point'][1],
245 z=wp_data[
'point'][2],
246 max_forward_speed=wp_data[
'max_forward_speed'],
247 heading_offset=wp_data[
'heading'],
248 use_fixed_heading=wp_data[
'use_fixed_heading'],
249 inertial_frame_id=
'world')
253 assert 'inertial_frame_id' in wps,
'Waypoint input has no inertial_frame_id key' 254 assert 'waypoints' in wps
255 assert wps[
'inertial_frame_id']
in [
'world',
'world_ned']
257 for wp_data
in wps[
'waypoints']:
259 x=wp_data[
'point'][0],
260 y=wp_data[
'point'][1],
261 z=wp_data[
'point'][2],
262 max_forward_speed=wp_data[
'max_forward_speed'],
263 heading_offset=wp_data[
'heading'],
264 use_fixed_heading=wp_data[
'use_fixed_heading'],
265 inertial_frame_id=wps[
'inertial_frame_id'])
267 except Exception
as e:
268 print(
'Error while loading the file, message={}'.format(e))
273 """Export waypoint set to YAML file. 277 * `path` (*type:* `str`): Path to the folder containing the file 278 * `filename` (*type:* `str`): Name of the YAML file. 282 `True` is waypoints could be exported to file. `False`, otherwise. 288 wp_elem = dict(point=[float(wp.x), float(wp.y), float(wp.z)],
289 max_forward_speed=float(wp._max_forward_speed),
290 heading=float(wp._heading_offset
if wp._heading_offset
is not None else 0.0),
291 use_fixed_heading=bool(wp._use_fixed_heading))
292 output[
'waypoints'].append(wp_elem)
293 with open(os.path.join(path, filename),
'w')
as wp_file:
294 yaml.dump(output, wp_file, default_flow_style=
False)
296 except Exception
as e:
297 print(
'Error occured while exporting waypoint file, message={}'.format(e))
301 """Convert waypoints set to message `uuv_control_msgs/WaypointSet` 305 `uuv_control_msgs/WaypointSet` message object 307 msg = WaypointSetMessage()
308 msg.header.stamp = rospy.Time().now()
310 msg.waypoints = list()
312 wp_msg = wp.to_message()
314 msg.waypoints.append(wp_msg)
318 """Convert `uuv_control_msgs/WaypointSet` message into `uuv_waypoints.WaypointSet` 322 * `msg` (*type:* `uuv_control_msgs/WaypointSet` object): Waypoint set message 326 for pnt
in msg.waypoints:
330 """Compute the distance of a waypoint in the set to point 334 * `pos` (*type:* list of `float`): 3D point as a list of coordinates 335 * `index` (*type:* `int`, *default:* `0`): Waypoint index in set 339 Distance between `pos` and the waypoint in `index`. `None` if waypoint set is empty. 347 """Set the radius of acceptance around each waypoint 348 inside which a vehicle is considered to have reached 353 * `index` (*type:* `int`): Index of the waypoint 354 * `radius` (*type:* `float`): Radius of the sphere representing the volume of acceptance 356 if index >= 0
and index < len(self.
_waypoints):
357 self.
_waypoints[index].radius_of_acceptance = radius
360 """Return the radius of acceptance for a waypoint 364 * `index` (*type:* `int`): Index of the waypoint 368 Radius of acceptance for the waypoint in position 369 given by `index` as a `float`. `None` if waypoint 372 if index >= 0
and index < len(self.
_waypoints):
373 return self.
_waypoints[index].radius_of_acceptance
378 """Return a `nav_msgs/Path` message with all waypoints in the set 382 * `clear` (*type:* `bool`, *default:* `False`): Return an empty `nav_msgs/Path` message. 386 `nav_msgs/Path` message 390 path.header.stamp = t
396 pose.header.stamp = rospy.Time(i)
398 pose.pose.position.x = wp.x
399 pose.pose.position.y = wp.y
400 pose.pose.position.z = wp.z
401 path.poses.append(pose)
405 """Return waypoint set as a markers list message of type `visualization_msgs/MarkerArray` 409 * `clear` (*type:* `bool`, *default:* `False`): Return an empty marker array message 413 `visualization_msgs/MarkerArray` message 415 list_waypoints = MarkerArray()
419 marker.header.stamp = t
422 marker.type = Marker.SPHERE
424 list_waypoints.markers.append(marker)
429 marker.header.stamp = t
432 marker.type = Marker.SPHERE
433 marker.action = Marker.ADD
434 marker.pose.position.x = wp.x
435 marker.pose.position.y = wp.y
436 marker.pose.position.z = wp.z
437 marker.scale.x = self.
_scale 438 marker.scale.y = self.
_scale 439 marker.scale.z = self.
_scale 442 color = wp.get_final_color()
444 color = wp.get_color()
445 marker.color.r = color[0]
446 marker.color.g = color[1]
447 marker.color.b = color[2]
448 list_waypoints.markers.append(marker)
449 return list_waypoints
451 def generate_circle(self, radius, center, num_points, max_forward_speed,
452 theta_offset=0.0, heading_offset=0.0, append=
False):
453 """Generate a set of waypoints describing a circle 457 * `radius` (*type:* `float`): Radius of the circle in meters 458 * `num_points` (*type:* `int`): Number of waypoints to be generated 459 * `max_forward_speed` (*type:* `float`): Max. forward speed set to each waypoint in m/s 460 * `theta_offset` (*type:* `float`, *default:* `0`): Angle offset to start generating the waypoints in radians 461 * `heading_offset` (*type:* `float`, *default:* `0`): Heading offset set to the reference heading of the vehicle in radians 462 * `append` (*type:* `bool`, *default:* `False`): If `True`, append the generated waypoints to the existent waypoints in the set 466 `True` if the circle was successfully generated, `False`, otherwise 469 print(
'Invalid radius, value={}'.format(radius))
473 print(
'Invalid number of samples, value={}'.format(num_points))
476 if max_forward_speed <= 0:
477 print(
'Invalid absolute maximum velocity, value={}'.format(max_forward_speed))
484 step_theta = 2 * np.pi / num_points
485 for i
in range(num_points):
486 angle = i * step_theta + theta_offset
487 x = np.cos(angle) * radius + center.x
488 y = np.sin(angle) * radius + center.y
490 wp = Waypoint(x, y, z, max_forward_speed,
495 def generate_helix(self, radius, center, num_points, max_forward_speed, delta_z,
496 num_turns, theta_offset=0.0, heading_offset=0.0,
498 """Generate a set of waypoints describing a helix 502 * `radius` (*type:* `float`): Radius of the circle in meters 503 * `num_points` (*type:* `int`): Number of waypoints to be generated 504 * `max_forward_speed` (*type:* `float`): Max. forward speed set to each waypoint in m/s 505 * `delta_z` (*type:* `float`): Step in the Z direction for each lap of the helix in meters 506 * `theta_offset` (*type:* `float`, *default:* `0`): Angle offset to start generating the waypoints in radians 507 * `heading_offset` (*type:* `float`, *default:* `0`): Heading offset set to the reference heading of the vehicle in radians 508 * `append` (*type:* `bool`, *default:* `False`): If `True`, append the generated waypoints to the existent waypoints in the set 512 `True` if the circle was successfully generated, `False`, otherwise 515 print(
'Invalid radius, value={}'.format(radius))
519 print(
'Invalid number of samples, value={}'.format(num_points))
523 print(
'Invalid number of turns, value={}'.format(num_points))
526 if max_forward_speed <= 0:
527 print(
'Invalid absolute maximum velocity, value={}'.format(max_forward_speed))
534 total_angle = 2 * np.pi * num_turns
535 step_angle = total_angle / num_points
536 step_z = float(delta_z) / num_points
537 for i
in range(num_points):
538 angle = theta_offset + i * step_angle
539 x = radius * np.cos(angle) + center.x
540 y = radius * np.sin(angle) + center.y
541 z = step_z * i + center.z
543 wp = Waypoint(x, y, z, max_forward_speed,
def to_marker_list(self, clear=False)
def read_from_file(self, filename)
def dist_to_waypoint(self, pos, index=0)
def clear_waypoints(self)
def get_start_waypoint(self)
def to_path_marker(self, clear=False)
def generate_helix(self, radius, center, num_points, max_forward_speed, delta_z, num_turns, theta_offset=0.0, heading_offset=0.0, append=False)
def get_waypoint(self, index)
def add_waypoint_from_msg(self, msg)
def get_last_waypoint(self)
def set_constraint_status(self, index, flag)
def export_to_file(self, path, filename)
def generate_circle(self, radius, center, num_points, max_forward_speed, theta_offset=0.0, heading_offset=0.0, append=False)
def remove_waypoint(self, waypoint)
def __init__(self, scale=0.1, inertial_frame_id='world', max_surge_speed=None)
def from_message(self, msg)
def add_waypoint(self, waypoint, add_to_beginning=False)
def set_radius_of_acceptance(self, index, radius)
def get_radius_of_acceptance(self, index)