route_manipulation.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. #!/usr/bin/env python
  2. # Copyright (c) 2018-2019 Intel Labs.
  3. # authors: German Ros (german.ros@intel.com), Felipe Codevilla (felipe.alcm@gmail.com)
  4. #
  5. # This work is licensed under the terms of the MIT license.
  6. # For a copy, see <https://opensource.org/licenses/MIT>.
  7. """
  8. Module to manipulate the routes, by making then more or less dense (Up to a certain parameter).
  9. It also contains functions to convert the CARLA world location do GPS coordinates.
  10. """
  11. import math
  12. import xml.etree.ElementTree as ET
  13. from agents.navigation.global_route_planner import GlobalRoutePlanner
  14. from agents.navigation.local_planner import RoadOption
  15. def _location_to_gps(lat_ref, lon_ref, location):
  16. """
  17. Convert from world coordinates to GPS coordinates
  18. :param lat_ref: latitude reference for the current map
  19. :param lon_ref: longitude reference for the current map
  20. :param location: location to translate
  21. :return: dictionary with lat, lon and height
  22. """
  23. EARTH_RADIUS_EQUA = 6378137.0 # pylint: disable=invalid-name
  24. scale = math.cos(lat_ref * math.pi / 180.0)
  25. mx = scale * lon_ref * math.pi * EARTH_RADIUS_EQUA / 180.0
  26. my = scale * EARTH_RADIUS_EQUA * math.log(math.tan((90.0 + lat_ref) * math.pi / 360.0))
  27. mx += location.x
  28. my -= location.y
  29. lon = mx * 180.0 / (math.pi * EARTH_RADIUS_EQUA * scale)
  30. lat = 360.0 * math.atan(math.exp(my / (EARTH_RADIUS_EQUA * scale))) / math.pi - 90.0
  31. z = location.z
  32. return {'lat': lat, 'lon': lon, 'z': z}
  33. def location_route_to_gps(route, lat_ref, lon_ref):
  34. """
  35. Locate each waypoint of the route into gps, (lat long ) representations.
  36. :param route:
  37. :param lat_ref:
  38. :param lon_ref:
  39. :return:
  40. """
  41. gps_route = []
  42. for transform, connection in route:
  43. gps_point = _location_to_gps(lat_ref, lon_ref, transform.location)
  44. gps_route.append((gps_point, connection))
  45. return gps_route
  46. def _get_latlon_ref(world):
  47. """
  48. Convert from waypoints world coordinates to CARLA GPS coordinates
  49. :return: tuple with lat and lon coordinates
  50. """
  51. xodr = world.get_map().to_opendrive()
  52. tree = ET.ElementTree(ET.fromstring(xodr))
  53. # default reference
  54. lat_ref = 42.0
  55. lon_ref = 2.0
  56. for opendrive in tree.iter("OpenDRIVE"):
  57. for header in opendrive.iter("header"):
  58. for georef in header.iter("geoReference"):
  59. if georef.text:
  60. str_list = georef.text.split(' ')
  61. for item in str_list:
  62. if '+lat_0' in item:
  63. lat_ref = float(item.split('=')[1])
  64. if '+lon_0' in item:
  65. lon_ref = float(item.split('=')[1])
  66. return lat_ref, lon_ref
  67. def downsample_route(route, sample_factor):
  68. """
  69. Downsample the route by some factor.
  70. :param route: the trajectory , has to contain the waypoints and the road options
  71. :param sample_factor: Maximum distance between samples
  72. :return: returns the ids of the final route that can
  73. """
  74. ids_to_sample = []
  75. prev_option = None
  76. dist = 0
  77. for i, point in enumerate(route):
  78. curr_option = point[1]
  79. # Lane changing
  80. if curr_option in (RoadOption.CHANGELANELEFT, RoadOption.CHANGELANERIGHT):
  81. ids_to_sample.append(i)
  82. dist = 0
  83. # When road option changes
  84. elif prev_option != curr_option and prev_option not in (RoadOption.CHANGELANELEFT, RoadOption.CHANGELANERIGHT):
  85. ids_to_sample.append(i)
  86. dist = 0
  87. # After a certain max distance
  88. elif dist > sample_factor:
  89. ids_to_sample.append(i)
  90. dist = 0
  91. # At the end
  92. elif i == len(route) - 1:
  93. ids_to_sample.append(i)
  94. dist = 0
  95. # Compute the distance traveled
  96. else:
  97. curr_location = point[0].location
  98. prev_location = route[i - 1][0].location
  99. dist += curr_location.distance(prev_location)
  100. prev_option = curr_option
  101. return ids_to_sample
  102. def interpolate_trajectory(world, waypoints_trajectory, hop_resolution=1.0):
  103. """
  104. Given some raw keypoints interpolate a full dense trajectory to be used by the user.
  105. :param world: an reference to the CARLA world so we can use the planner
  106. :param waypoints_trajectory: the current coarse trajectory
  107. :param hop_resolution: is the resolution, how dense is the provided trajectory going to be made
  108. :return: the full interpolated route both in GPS coordinates and also in its original form.
  109. """
  110. grp = GlobalRoutePlanner(world.get_map(), hop_resolution)
  111. # Obtain route plan
  112. route = []
  113. for i in range(len(waypoints_trajectory) - 1): # Goes until the one before the last.
  114. waypoint = waypoints_trajectory[i]
  115. waypoint_next = waypoints_trajectory[i + 1]
  116. interpolated_trace = grp.trace_route(waypoint, waypoint_next)
  117. for wp_tuple in interpolated_trace:
  118. route.append((wp_tuple[0].transform, wp_tuple[1]))
  119. # Increase the route position to avoid fails
  120. lat_ref, lon_ref = _get_latlon_ref(world)
  121. return location_route_to_gps(route, lat_ref, lon_ref), route