Skip to content

Commit 8f49699

Browse files
SPDonaghyjamenkayeFireBoyAJ24
authored
Remove any magic numbers and replace with raised ValueErrors (#545)
* updated lpath_data_callback docstr * Fix typing in LocalPath.py Change LocalPath.waypoints to List[HelperLatLon]; change LocalPathState.global_path to Path * fixed None global path handling in LocalPathState * validated new and updated msg types * ensure reference_latlon initialized correctly * converted publishing local path data to a function call in desired heading callback * Fix if statement syntax? * Try to fix index out of bounds error * Try to fix index out of bounds again? * pass obstacles up to naviagte node * all but one ompl path test pass * fix docstring of get waypoints and set global path in tests * all unit tests pass * fixed coord sys * fixed up global path * few more global path refactors * local path import changes * updated local path * mgp and mais * refactors done * fixed little arror with lpath data call back * changed get_waypoints to get_path * publish lpathdata * added runtime warning for inactive subs * parsing obstacles into ros msgs * all data publishes * comment out state validity logging * type checking for invalid state log * fixed bad merge errors * fixed some more duplicate code from merge * added note about runtime warn form get desired * replaced default values with valueerrors * configure ompl path to use real globalpath * plotted output path * rm nb * Minor Syntax changes --------- Co-authored-by: Jamen Kaye <jamen.kaye@gmail.com> Co-authored-by: Jamen Kaye <71156197+jamenkaye@users.noreply.github.com> Co-authored-by: Akshanjay Kompelli <63443017+FireBoyAJ24@users.noreply.github.com>
1 parent 1c022c2 commit 8f49699

File tree

4 files changed

+118
-42
lines changed

4 files changed

+118
-42
lines changed

src/local_pathfinding/local_pathfinding/local_path.py

Lines changed: 21 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -36,36 +36,28 @@ def __init__(
3636
filtered_wind_sensor: ci.WindSensor,
3737
planner: str,
3838
):
39-
if gps: # TODO: remove when mock can be run
40-
self.position = gps.lat_lon
41-
self.speed = gps.speed.speed
42-
self.heading = gps.heading.heading
43-
else:
44-
# this position has been verified to be close enough to land that
45-
# land obstacles should be generated
46-
self.position = ci.HelperLatLon(latitude=49.29, longitude=-126.32)
47-
self.speed = 0.0
48-
self.heading = 0.0
49-
50-
if ais_ships: # TODO: remove when mock can be run
51-
self.ais_ships = [ship for ship in ais_ships.ships]
52-
else:
53-
self.ais_ships = [] # ensures this attribute is always set, to avoid AtributeError
54-
55-
if filtered_wind_sensor: # TODO: remove when mock can be run
56-
self.wind_speed = filtered_wind_sensor.speed.speed
57-
self.wind_direction = filtered_wind_sensor.direction
58-
else:
59-
self.wind_speed = 0.0
60-
self.wind_direction = 0
61-
62-
self.global_path = global_path
63-
64-
if self.global_path and self.global_path.waypoints:
65-
self.reference_latlon = self.global_path.waypoints[-1]
66-
else:
39+
if not gps:
40+
raise ValueError("gps must not be None")
41+
self.position = gps.lat_lon
42+
self.speed = gps.speed.speed
43+
self.heading = gps.heading.heading
44+
45+
if not ais_ships:
46+
raise ValueError("ais_ships must not be None")
47+
self.ais_ships = [ship for ship in ais_ships.ships]
48+
49+
if not filtered_wind_sensor:
50+
raise ValueError("filtered_wind_sensor must not be None")
51+
self.wind_speed = filtered_wind_sensor.speed.speed
52+
self.wind_direction = filtered_wind_sensor.direction
53+
54+
if not (global_path and global_path.waypoints):
6755
raise ValueError("Cannot create a LocalPathState with an empty global_path")
56+
self.global_path = global_path
57+
self.reference_latlon = self.global_path.waypoints[-1]
6858

59+
if not planner:
60+
raise ValueError("planner must not be None")
6961
self.planner = planner
7062

7163
# obstacles are initialized by OMPLPath right before solving
@@ -106,6 +98,7 @@ def update_if_needed(
10698
global_path (ci.Path): Path to the destination.
10799
filtered_wind_sensor (ci.WindSensor): Wind data.
108100
"""
101+
# this raises ValueError if any of the parameters are not properly initialized
109102
state = LocalPathState(gps, ais_ships, global_path, filtered_wind_sensor, planner)
110103
self.state = state
111104
ompl_path = OMPLPath(

src/local_pathfinding/local_pathfinding/node_navigate.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,6 @@ def __init__(self):
115115
self.get_logger().debug(f"Got parameter: {self.planner=}")
116116

117117
# subscriber callbacks
118-
119118
def ais_ships_callback(self, msg: ci.AISShips):
120119
self.get_logger().debug(f"Received data from {self.ais_ships_sub.topic}: {msg}")
121120
self.ais_ships = msg
@@ -135,7 +134,6 @@ def filtered_wind_sensor_callback(self, msg: ci.WindSensor):
135134
self.filtered_wind_sensor = msg
136135

137136
# publisher callbacks
138-
139137
def desired_heading_callback(self):
140138
"""Get and publish the desired heading."""
141139

src/local_pathfinding/local_pathfinding/ompl_path.py

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
# OMPL logging: only log warnings and above
2929
ou.setLogLevel(ou.LOG_WARN)
3030

31+
BOX_BUFFER_SIZE = 1.0 # km
32+
3133

3234
class OMPLPath:
3335
"""Represents the general OMPL Path.
@@ -61,7 +63,7 @@ def __init__(
6163
max_runtime (float): Maximum amount of time in seconds to look for a solution path.
6264
local_path_state (LocalPathState): State of Sailbot.
6365
"""
64-
self._box_buffer = 1
66+
self._box_buffer = BOX_BUFFER_SIZE
6567
self._logger = parent_logger.get_child(name="ompl_path")
6668
self._simple_setup = self._init_simple_setup(local_path_state) # this needs state
6769

@@ -194,15 +196,10 @@ def _init_simple_setup(self, local_path_state) -> og.SimpleSetup:
194196
start_x = start_position_in_xy.x
195197
start_y = start_position_in_xy.y
196198

197-
# TODO this needs to be removed when mocks are ready
198-
if not self.state.global_path:
199-
goal_polygon = self.create_buffer_around_position(cs.XY(0, 0))
200-
goal_x, goal_y = (0.0, 0.0)
201-
else:
202-
goal_position = self.state.global_path.waypoints[-1]
203-
goal_position_in_xy = cs.latlon_to_xy(self.state.reference_latlon, goal_position)
204-
goal_polygon = self.create_buffer_around_position(goal_position_in_xy)
205-
goal_x, goal_y = goal_position_in_xy
199+
goal_position = self.state.global_path.waypoints[-1]
200+
goal_position_in_xy = cs.latlon_to_xy(self.state.reference_latlon, goal_position)
201+
goal_polygon = self.create_buffer_around_position(goal_position_in_xy)
202+
goal_x, goal_y = goal_position_in_xy
206203

207204
# create an SE2 state space: rotation and translation in a plane
208205
space = base.SE2StateSpace()

src/local_pathfinding/test/test_local_path.py

Lines changed: 90 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1+
import pytest
12
from custom_interfaces.msg import GPS, AISShips, HelperLatLon, Path, WindSensor
23
from rclpy.impl.rcutils_logger import RcutilsLogger
34

4-
import local_pathfinding.local_path as local_path
5+
import local_pathfinding.local_path as lp
56

6-
PATH = local_path.LocalPath(parent_logger=RcutilsLogger())
7+
PATH = lp.LocalPath(parent_logger=RcutilsLogger())
78

89

910
def test_LocalPath_update_if_needed():
@@ -22,3 +23,90 @@ def test_LocalPath_update_if_needed():
2223
)
2324
assert PATH.path is not None, "waypoints is not initialized"
2425
assert len(PATH.path.waypoints) > 1, "waypoints length <= 1"
26+
27+
28+
def test_LocalPathState_parameter_checking():
29+
with pytest.raises(ValueError):
30+
lps = (
31+
lp.LocalPathState(
32+
gps=None,
33+
ais_ships=AISShips(),
34+
global_path=Path(
35+
waypoints=[
36+
HelperLatLon(latitude=0.0, longitude=0.0),
37+
HelperLatLon(latitude=1.0, longitude=1.0),
38+
]
39+
),
40+
filtered_wind_sensor=WindSensor(),
41+
planner="rrtstar",
42+
),
43+
)
44+
45+
with pytest.raises(ValueError):
46+
lps = (
47+
lp.LocalPathState(
48+
gps=GPS(),
49+
ais_ships=None,
50+
global_path=Path(
51+
waypoints=[
52+
HelperLatLon(latitude=0.0, longitude=0.0),
53+
HelperLatLon(latitude=1.0, longitude=1.0),
54+
]
55+
),
56+
filtered_wind_sensor=WindSensor(),
57+
planner="rrtstar",
58+
),
59+
)
60+
61+
with pytest.raises(ValueError):
62+
lps = (
63+
lp.LocalPathState(
64+
gps=GPS(),
65+
ais_ships=AISShips(),
66+
global_path=Path(waypoints=[]),
67+
filtered_wind_sensor=WindSensor(),
68+
planner="rrtstar",
69+
),
70+
)
71+
72+
with pytest.raises(ValueError):
73+
lps = (
74+
lp.LocalPathState(
75+
gps=GPS(),
76+
ais_ships=AISShips(),
77+
global_path=None,
78+
filtered_wind_sensor=WindSensor(),
79+
planner="rrtstar",
80+
),
81+
)
82+
83+
with pytest.raises(ValueError):
84+
lps = (
85+
lp.LocalPathState(
86+
gps=GPS(),
87+
ais_ships=AISShips(),
88+
global_path=Path(
89+
waypoints=[
90+
HelperLatLon(latitude=0.0, longitude=0.0),
91+
HelperLatLon(latitude=1.0, longitude=1.0),
92+
]
93+
),
94+
filtered_wind_sensor=None,
95+
planner="rrtstar",
96+
),
97+
)
98+
with pytest.raises(ValueError):
99+
lps = ( # noqa
100+
lp.LocalPathState(
101+
gps=GPS(),
102+
ais_ships=AISShips(),
103+
global_path=Path(
104+
waypoints=[
105+
HelperLatLon(latitude=0.0, longitude=0.0),
106+
HelperLatLon(latitude=1.0, longitude=1.0),
107+
]
108+
),
109+
filtered_wind_sensor=WindSensor(),
110+
planner=None,
111+
),
112+
)

0 commit comments

Comments
 (0)