This repository has been archived by the owner on Mar 23, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Akshanjay/optimization objective skeleton code (#37)
* Skeleton code for the path_objective file * Added the skeleton code for ompl_path file * Added the base test cases * Reduced the number of inputs to the updated objective functions * Linted the path_objective * Fixed a linting error in ompl_path and test_path_objective * Add file docstring * Rename path_objective to objectives * Fix docstring * Cleanup * Remove unnecessary global variables --------- Co-authored-by: Patrick Creighton <[email protected]>
- Loading branch information
1 parent
f66766d
commit 110a103
Showing
4 changed files
with
167 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
"""Our custom OMPL optimization objectives.""" | ||
|
||
from ompl import base as ob | ||
|
||
|
||
class Objective(ob.StateCostIntegralObjective): | ||
"""All of our optimization objectives inherit from this class. | ||
Notes: | ||
- This class inherits from the OMPL class StateCostIntegralObjective: | ||
https://ompl.kavrakilab.org/classompl_1_1base_1_1StateCostIntegralObjective.html | ||
- Camelcase is used for functions that override OMPL functions, as that is their convention. | ||
Attributes: | ||
space_information: https://ompl.kavrakilab.org/classompl_1_1base_1_1SpaceInformation.html | ||
""" | ||
|
||
def __init__(self, space_information): | ||
super().__init__(si=space_information, enableMotionCostInterpolation=True) | ||
self.space_information = space_information | ||
|
||
def motionCost(self, s1: ob.SE2StateSpace, s2: ob.SE2StateSpace) -> ob.Cost: | ||
raise NotImplementedError | ||
|
||
|
||
class DistanceObjective(Objective): | ||
def __init__(self, space_information): | ||
super().__init__(space_information) | ||
|
||
def motionCost(self, s1: ob.SE2StateSpace, s2: ob.SE2StateSpace) -> ob.Cost: | ||
return ob.Cost() | ||
|
||
def _get_path_length_objective(self): | ||
raise NotImplementedError | ||
|
||
def _get_euclidean_path_length_objective(self, s1, s2): | ||
raise NotImplementedError | ||
|
||
def _get_latlon_path_length_objective(self, s1, s2): | ||
raise NotImplementedError | ||
|
||
|
||
class MinimumTurningObjective(Objective): | ||
def __init__(self, space_information, simple_setup, heading_degrees: float): | ||
super().__init__(space_information) | ||
self.simple_setup = simple_setup | ||
self.heading_degrees = heading_degrees | ||
|
||
def motionCost(self, s1: ob.SE2StateSpace, s2: ob.SE2StateSpace) -> ob.Cost: | ||
return ob.Cost() | ||
|
||
def _goal_path_turn_cost(self, s1, s2): | ||
raise NotImplementedError | ||
|
||
def _goal_heading_turn_cost(self, s1): | ||
raise NotImplementedError | ||
|
||
def _heading_path_turn_cost(self, s1, s2): | ||
raise NotImplementedError | ||
|
||
|
||
class WindObjective(Objective): | ||
def __init__(self, space_information, wind_direction_degrees: float): | ||
super().__init__(space_information) | ||
self.wind_direction_degrees = wind_direction_degrees | ||
|
||
# This objective function punishes the boat for going up/downwind | ||
def motionCost(self, s1: ob.SE2StateSpace, s2: ob.SE2StateSpace) -> ob.Cost: | ||
return ob.Cost() | ||
|
||
@staticmethod | ||
def _is_upwind(wind_direction_radians: float, boat_direction_radians: float) -> bool: | ||
raise NotImplementedError | ||
|
||
@staticmethod | ||
def _is_downwind(wind_direction_radians: float, boat_direction_radians: float) -> bool: | ||
raise NotImplementedError | ||
|
||
|
||
def get_sailing_objective( | ||
space_information, simple_setup, heading_degrees: float, wind_direction_degrees: float | ||
) -> ob.OptimizationObjective: | ||
objective = ob.MultiOptimizationObjective(si=space_information) | ||
objective.addObjective(objective=DistanceObjective(space_information), weight=1.0) | ||
objective.addObjective( | ||
objective=MinimumTurningObjective(space_information, simple_setup, heading_degrees), | ||
weight=100.0, | ||
) | ||
objective.addObjective( | ||
objective=WindObjective(space_information, wind_direction_degrees), weight=1.0 | ||
) | ||
|
||
return objective |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import pytest | ||
from rclpy.impl.rcutils_logger import RcutilsLogger | ||
|
||
import local_pathfinding.objectives as objectives | ||
import local_pathfinding.ompl_path as ompl_path | ||
|
||
PATH = ompl_path.OMPLPath( | ||
parent_logger=RcutilsLogger(), | ||
max_runtime=1, | ||
local_path_state=None, # type: ignore[arg-type] # None is placeholder | ||
) | ||
|
||
|
||
def test_distance_objective(): | ||
distance_objective = objectives.DistanceObjective(PATH._simple_setup.getSpaceInformation()) | ||
assert distance_objective is not None | ||
|
||
|
||
def test_get_path_length_objective(): | ||
with pytest.raises(NotImplementedError): | ||
raise NotImplementedError | ||
|
||
|
||
def test_get_euclidean_path_length_objective(): | ||
with pytest.raises(NotImplementedError): | ||
raise NotImplementedError | ||
|
||
|
||
def test_get_latlon_path_length_objective(): | ||
with pytest.raises(NotImplementedError): | ||
raise NotImplementedError | ||
|
||
|
||
def test_minimum_turning_objective(): | ||
minimum_turning_objective = objectives.MinimumTurningObjective( | ||
PATH._simple_setup.getSpaceInformation(), None, 0 | ||
) | ||
assert minimum_turning_objective is not None | ||
|
||
|
||
def test_goalPathTurnCost(): | ||
with pytest.raises(NotImplementedError): | ||
raise NotImplementedError | ||
|
||
|
||
def test_goalHeadingTurnCost(): | ||
with pytest.raises(NotImplementedError): | ||
raise NotImplementedError | ||
|
||
|
||
def test_headingPathTurnCost(): | ||
with pytest.raises(NotImplementedError): | ||
raise NotImplementedError | ||
|
||
|
||
def test_wind_objective(): | ||
wind_objective = objectives.WindObjective(PATH._simple_setup.getSpaceInformation(), 0) | ||
assert wind_objective is not None |