1313import pyompl
1414from custom_interfaces .msg import HelperLatLon
1515from rclpy .impl .rcutils_logger import RcutilsLogger
16+ from shapely .geometry import MultiPolygon , Point , Polygon , box
1617
1718import local_pathfinding .coord_systems as cs
19+ from local_pathfinding .coord_systems import XY
1820from local_pathfinding .objectives import get_sailing_objective
1921
2022if TYPE_CHECKING :
2426# ou.setLogLevel(ou.LOG_WARN)
2527
2628
27- class OMPLPathState :
28- def __init__ (self , local_path_state : LocalPathState , logger : RcutilsLogger ):
29- # TODO: derive OMPLPathState attributes from local_path_state
30- self .heading_direction = 45.0
31- self .wind_direction = 10.0
32- self .wind_speed = 1.0
33-
34- self .state_domain = (- 1 , 1 )
35- self .state_range = (- 1 , 1 )
36- self .start_state = (0.5 , 0.4 )
37- self .goal_state = (0.5 , - 0.4 )
38-
39- self .reference_latlon = (
40- local_path_state .global_path [- 1 ]
41- if local_path_state .global_path and len (local_path_state .global_path ) > 0
42- else HelperLatLon (latitude = 0.0 , longitude = 0.0 )
43- )
44-
45- if local_path_state :
46- self .planner = None
47- # self.planner = pyompl.RRTstar()
48-
49-
5029class OMPLPath :
5130 """Represents the general OMPL Path.
5231
5332 Attributes
5433 _logger (RcutilsLogger): ROS logger of this class.
5534 _simple_setup (og.SimpleSetup): OMPL SimpleSetup object.
35+ _box_buffer (float): buffer around the sailbot position and the goal position in km
5636 solved (bool): True if the path is a solution to the OMPL query, else false.
5737 """
5838
@@ -69,6 +49,7 @@ def __init__(
6949 max_runtime (float): Maximum amount of time in seconds to look for a solution path.
7050 local_path_state (LocalPathState): State of Sailbot.
7151 """
52+ self ._box_buffer = 1
7253 self ._logger = parent_logger .get_child (name = "ompl_path" )
7354
7455 self ._simple_setup = self ._init_simple_setup (local_path_state ) # this needs state
@@ -114,6 +95,13 @@ def get_waypoints(self) -> List[HelperLatLon]:
11495
11596 return waypoints
11697
98+ def create_buffer_around_position (self : OMPLPath , position : XY ) -> Polygon :
99+ """Create a space around the given position. Position is the center of the space and
100+ is a tuple of x and y.
101+ """
102+ space = Point (position .x , position .y ).buffer (self ._box_buffer , cap_style = 3 , join_style = 2 )
103+ return space
104+
117105 def update_objectives (self ):
118106 """Update the objectives on the basis of which the path is optimized.
119107 Raises:
@@ -122,21 +110,33 @@ def update_objectives(self):
122110 raise NotImplementedError
123111
124112 def _init_simple_setup (self , local_path_state ) -> pyompl .SimpleSetup :
125- """Initialize and configure the OMPL SimpleSetup object.
126-
127- Returns:
128- og.SimpleSetup: Encapsulates the various objects necessary to solve a geometric or
129- control query in OMPL.
130- """
131- self .state = OMPLPathState (local_path_state , self ._logger )
113+ self .state = local_path_state
114+
115+ # Create buffered spaces and extract their centers
116+ start_position_in_xy = cs .latlon_to_xy (self .state .reference_latlon , self .state .position )
117+ start_box = self .create_buffer_around_position (start_position_in_xy )
118+ start_x = start_position_in_xy .x
119+ start_y = start_position_in_xy .y
120+
121+ if not self .state .global_path :
122+ goal_polygon = self .create_buffer_around_position (cs .XY (0 , 0 ))
123+ goal_x , goal_y = (0.0 , 0.0 )
124+ else :
125+ goal_position = self .state .global_path [- 1 ]
126+ goal_position_in_xy = cs .latlon_to_xy (self .state .reference_latlon , goal_position )
127+ goal_polygon = self .create_buffer_around_position (goal_position_in_xy )
128+ goal_x , goal_y = goal_position_in_xy
132129
133130 # create an SE2 state space: rotation and translation in a plane
134131 space = pyompl .SE2StateSpace ()
135132
136133 # set the bounds of the state space
137134 bounds = pyompl .RealVectorBounds (dim = 2 )
138- x_min , x_max = self .state .state_domain
139- y_min , y_max = self .state .state_range
135+ state_space = box (* MultiPolygon ([start_box , goal_polygon ]).bounds )
136+ x_min , y_min , x_max , y_max = state_space .bounds
137+
138+ if x_max <= x_min or y_max <= y_min :
139+ raise ValueError (f"Invalid bounds: x=[{ x_min } , { x_max } ], y=[{ y_min } , { y_max } ]" )
140140 bounds .setLow (0 , x_min )
141141 bounds .setLow (1 , y_min )
142142 bounds .setHigh (0 , x_max )
@@ -153,11 +153,8 @@ def _init_simple_setup(self, local_path_state) -> pyompl.SimpleSetup:
153153 simple_setup = pyompl .SimpleSetup (space )
154154 simple_setup .setStateValidityChecker (is_state_valid )
155155
156- # set the goal and start states of the simple setup object
157156 start = pyompl .ScopedState (space )
158157 goal = pyompl .ScopedState (space )
159- start_x , start_y = self .state .start_state
160- goal_x , goal_y = self .state .goal_state
161158 start .setXY (start_x , start_y )
162159 goal .setXY (goal_x , goal_y )
163160 """self._logger.debug(
@@ -170,6 +167,7 @@ def _init_simple_setup(self, local_path_state) -> pyompl.SimpleSetup:
170167 # Constructs a space information instance for this simple setup
171168 space_information = simple_setup .getSpaceInformation ()
172169
170+ # figure this out
173171 self .state .planner = pyompl .RRTstar (space_information )
174172
175173 # set the optimization objective of the simple setup object
@@ -178,7 +176,8 @@ def _init_simple_setup(self, local_path_state) -> pyompl.SimpleSetup:
178176 objective = get_sailing_objective (
179177 space_information ,
180178 simple_setup ,
181- self .state .heading_direction ,
179+ # This too
180+ self .state .heading ,
182181 self .state .wind_direction ,
183182 self .state .wind_speed ,
184183 )
0 commit comments