Autonomy Software C++ 24.5.1
Welcome to the Autonomy Software repository of the Mars Rover Design Team (MRDT) at Missouri University of Science and Technology (Missouri S&T)! API reference contains the source code and other resources for the development of the autonomy software for our Mars rover. The Autonomy Software project aims to compete in the University Rover Challenge (URC) by demonstrating advanced autonomous capabilities and robust navigation algorithms.
Loading...
Searching...
No Matches
pathplanners::AStar Class Reference

Implements the A* (ASTAR) algorithm with the ability to plan paths around obstacles and provide path bias points that the algorithm will try to adhere to. More...

#include <AStar.h>

Collaboration diagram for pathplanners::AStar:

Public Member Functions

 AStar ()
 Construct a new AStar::AStar object.
 
 ~AStar ()
 Destroy the AStar::AStar object.
 
std::vector< geoops::WaypointPlanAvoidancePath (const geoops::Waypoint &stStartCoordinate, const geoops::Waypoint &stGoalCoordinate)
 Called in the obstacle avoidance state to plan a path around obstacles.
 
std::vector< geoops::WaypointPlanAvoidancePath (const geoops::UTMCoordinate &stStartCoordinate, const geoops::UTMCoordinate &stGoalCoordinate)
 Called in the obstacle avoidance state to plan a path around obstacles blocking our path.
 
std::vector< geoops::WaypointPlanAvoidancePath (const geoops::GPSCoordinate &stStartCoordinate, const geoops::GPSCoordinate &stGoalCoordinate)
 Called in the obstacle avoidance state to plan a path around obstacles.
 
void CancelPathGeneration ()
 Cancels the path generation process.
 
void UpsertObstacleData (const geoops::Waypoint &stObstacle)
 Adds new obstacle data to the class member variable m_vObstacles.
 
void UpsertObstacleData (const geoops::UTMCoordinate &stObstacle)
 Adds new obstacle data to the class member variable m_vObstacles.
 
void UpsertObstacleData (const geoops::GPSCoordinate &stObstacle)
 Adds new obstacle data to the class member variable m_vObstacles.
 
void UpsertObstacleData (const std::vector< geoops::Waypoint > &vObstacles)
 Adds new obstacle data to the class member variable m_vObstacles. Also checks if the obstacle is already in the vector and skips it if it is.
 
void UpsertObstacleData (const std::vector< geoops::UTMCoordinate > &vObstacles)
 Adds new obstacle data to the class member variable m_vObstacles.
 
void UpsertObstacleData (const std::vector< geoops::GPSCoordinate > &vObstacles)
 Adds new obstacle data to the class member variable m_vObstacles.
 
void ClearObstacleData ()
 Helper function to destroy objects from m_vObstacles.
 
std::vector< geoops::WaypointGetPath () const
 Getter for the path calculated by ASTAR.
 
std::vector< geoops::WaypointGetObstacleData () const
 Getter for the current obstacle data.
 

Private Member Functions

geoops::Waypoint FindNearestGoalPoint (const geoops::UTMCoordinate &stGoalCoordinate)
 Helper function for the PlanAvoidancePath method. This method takes in a UTMCoordinate reference and uses class member variables to mutate the m_stGoalNode object's coordinates to represent the nearest boundary point.
 
geoops::Waypoint FindNearestStartPoint (const geoops::UTMCoordinate &stStartCoordinate)
 Helper function to round a UTMCoordinate to align with the grid.
 
void RoundUTMCoordinate (geoops::UTMCoordinate &stCoordinateToRound)
 Helper function used to round UTMCoordinates to the nearest constants::ASTAR_NODE_SIZE to avoid rounding errors when trying to determine if two nodes have the same location.
 
void ConstructPath (const nodes::AStarNode &stFinalNode)
 Called when a goal node has been reached. Recursively builds a vector of UTMCoordinates by tracing the parent pointers of AStarNodes. This function then saves that vector to m_vPathNodes.
 
std::string UTMCoordinateToString (const geoops::UTMCoordinate &stToTranslate)
 Helper function used to translate a UTMCoordinate's dEasting and dNorthing values into a string that can be hashed for the unordered_map data structure for O(1) lookup of nodes at a particular location.
 
bool ValidCoordinate (const double dEasting, const double dNorthing)
 Helper function used to determine if a potential UTMCoordinate is valid. Returns False if there is an obstacle is blocking the node or the node is outside the max boundary. Returns True otherwise, representing the node is a valid path to consider. To save memory and compute time, we only evaluate the doubles representing the coordinate.
 

Private Attributes

nodes::AStarNode m_stStartNode
 
nodes::AStarNode m_stGoalNode
 
std::vector< geoops::Waypointm_vPathCoordinates
 
std::vector< geoops::Waypointm_vObstacles
 
std::chrono::steady_clock::time_point m_tmStartTime
 
std::atomic_bool m_bPathGenerationCancelled
 

Detailed Description

Implements the A* (ASTAR) algorithm with the ability to plan paths around obstacles and provide path bias points that the algorithm will try to adhere to.

Author
Kai Shafe (kasq5.nosp@m.m@um.nosp@m.syste.nosp@m.m.ed.nosp@m.u)
Date
2024-02-01

Constructor & Destructor Documentation

◆ AStar()

pathplanners::AStar::AStar ( )

Construct a new AStar::AStar object.

Author
Sam Nolte (samno.nosp@m.lte0.nosp@m.302@g.nosp@m.mail.nosp@m..com)
Date
2024-11-18
39 {
40 // Nothing to do yet.
41 }

◆ ~AStar()

pathplanners::AStar::~AStar ( )

Destroy the AStar::AStar object.

Author
clayjay3 (clayt.nosp@m.onra.nosp@m.ycowe.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2024-02-01
51 {
52 // Nothing to destroy yet.
53 }

Member Function Documentation

◆ PlanAvoidancePath() [1/3]

std::vector< geoops::Waypoint > pathplanners::AStar::PlanAvoidancePath ( const geoops::Waypoint stStartCoordinate,
const geoops::Waypoint stGoalCoordinate 
)

Called in the obstacle avoidance state to plan a path around obstacles.

Parameters
stStartCoordinate- A Waypoint reference that represents the start location.
stGoalCoordinate- A Waypoint reference that represents the goal location.
Returns
std::vector<geoops::Waypoint> - A vector of Waypoints representing the path calculated by ASTAR.
Author
clayjay3 (clayt.nosp@m.onra.nosp@m.ycowe.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2025-02-21
66 {
67 // Call the UTMCoordinate version of PlanAvoidancePath.
68 return PlanAvoidancePath(stStartCoordinate.GetUTMCoordinate(), stGoalCoordinate.GetUTMCoordinate());
69 }
std::vector< geoops::Waypoint > PlanAvoidancePath(const geoops::Waypoint &stStartCoordinate, const geoops::Waypoint &stGoalCoordinate)
Called in the obstacle avoidance state to plan a path around obstacles.
Definition AStar.cpp:65
const geoops::UTMCoordinate & GetUTMCoordinate() const
Accessor for the geoops::UTMCoordinate member variable.
Definition GeospatialOperations.hpp:477
Here is the call graph for this function:
Here is the caller graph for this function:

◆ PlanAvoidancePath() [2/3]

std::vector< geoops::Waypoint > pathplanners::AStar::PlanAvoidancePath ( const geoops::UTMCoordinate stStartCoordinate,
const geoops::UTMCoordinate stGoalCoordinate 
)

Called in the obstacle avoidance state to plan a path around obstacles blocking our path.

Parameters
stStartCoordinate- A UTMCoordinate reference that represents the start location.
stGoalCoordinate- A UTMCoordinate reference that represents the goal location.
Returns
- A vector of Waypoints representing the path calculated by ASTAR.
Author
Kai Shafe (kasq5.nosp@m.m@um.nosp@m.syste.nosp@m.m.ed.nosp@m.u)
Date
2024-02-02
83 {
84 // Clear previous path data.
85 m_vPathCoordinates.clear();
86 // Reset path generation cancellation flag.
87 m_bPathGenerationCancelled = false;
88
89 // Submit log message.
90 LOG_NOTICE(logging::g_qSharedLogger,
91 "ASTAR has started planning a path up to {} meters long with a node spacing of {} meters. Please wait...",
92 constants::ASTAR_MAX_SEARCH_GRID,
93 constants::ASTAR_NODE_SIZE);
94 // Update path plan start time.
95 m_tmStartTime = std::chrono::steady_clock::now();
96
97 // Map the start location to a nearby point that isn't overlapping with an obstacle.
98 geoops::Waypoint stRoundedStart(FindNearestStartPoint(stStartCoordinate));
99 // Create start node.
100 m_stStartNode = nodes::AStarNode(nullptr, stRoundedStart.GetUTMCoordinate());
101 // Map the goalLocation to an edge node based on maximum search size.
102 geoops::Waypoint stRoundedGoal(FindNearestGoalPoint(stGoalCoordinate));
103 // Create goal node.
104 m_stGoalNode = nodes::AStarNode(nullptr, stRoundedGoal.GetUTMCoordinate());
105
106 // -------------------A* algorithm-------------------
107 // Create Open and Closed Lists.
108 // Using an additional unordered map is memory inefficient but allows for O(1)
109 // lookup of nodes based on their position rather than iterating over the heap.
110 // Carefully manage nodes between 'lists' to ensure data is consistent.
111
112 // Open list implemented as a min-heap queue for O(1) retrieval of the node with min dKf value.
113 // C++ utilizes the '*_heap' family of functions which operate on vectors.
114 std::vector<nodes::AStarNode> vOpenList;
115 std::make_heap(vOpenList.begin(), vOpenList.end(), std::greater<nodes::AStarNode>());
116 // Unordered map of coordinates for open list for O(1) lookup.
117 std::unordered_map<std::string, double> umOpenListLookup;
118
119 // Vector containing pointers to nodes on the closed list.
120 // This vector also contains the nodes that will be copied to m_vPathCoordinates.
121 std::vector<std::shared_ptr<nodes::AStarNode>> vClosedList;
122
123 // Unordered map of coordinates for closed list for O(1) lookup.
124 std::unordered_map<std::string, double> umClosedList;
125
126 // Place Starting node on open list.
127 vOpenList.push_back(m_stStartNode);
128 // Translate start node to string and add location on open list lookup map.
129 std::string szLocationString = UTMCoordinateToString(m_stStartNode.stNodeLocation);
130 umOpenListLookup.emplace(std::make_pair(szLocationString, 0.0));
131
132 // While open list is not empty:
133 while (!vOpenList.empty())
134 {
135 // Check if path generation has been cancelled.
136 if (m_bPathGenerationCancelled)
137 {
138 // Submit log message.
139 LOG_WARNING(logging::g_qSharedLogger, "ASTAR path generation has been cancelled.");
140 // Clear path coordinates.
141 m_vPathCoordinates.clear();
142 // Return empty path.
143 return m_vPathCoordinates;
144 }
145
146 // Check if we have exceeded the maximum search time.
147 std::chrono::steady_clock::time_point tmCurrentTime = std::chrono::steady_clock::now();
148 std::chrono::duration<double> dElapsedTime = std::chrono::duration_cast<std::chrono::duration<double>>(tmCurrentTime - m_tmStartTime);
149 if (dElapsedTime.count() > constants::ASTAR_MAX_SEARCH_TIME)
150 {
151 // Submit log message.
152 LOG_WARNING(logging::g_qSharedLogger,
153 "ASTAR has exceeded the maximum search time of {} seconds. Path planning has been aborted.",
154 constants::ASTAR_MAX_SEARCH_TIME);
155 // Return empty path.
156 return m_vPathCoordinates;
157 }
158
159 // Retrieve node with the minimum dKf on open list (Q).
160 std::pop_heap(vOpenList.begin(), vOpenList.end(), std::greater<nodes::AStarNode>());
161 nodes::AStarNode stNextParent = vOpenList.back();
162 // Pop Q off open list.
163 vOpenList.pop_back();
164 // Put Q on closed list to allocate parent pointers of successors.
165 // Note: make_shared creates a copy of stNextParent on the heap, and points to that copy.
166 vClosedList.push_back(std::make_shared<nodes::AStarNode>(stNextParent));
167
168 // Generate Q's 8 successors (neighbors), setting parent to Q.
169 std::vector<nodes::AStarNode> vSuccessors;
170
171 // Counter for avoiding parent duplication.
172 for (int nEastingDirection = -1; nEastingDirection <= 1; nEastingDirection += 1)
173 {
174 for (int nNorthingDirection = -1; nNorthingDirection <= 1; nNorthingDirection += 1)
175 {
176 // Skip parent node.
177 if (nEastingDirection == 0 && nNorthingDirection == 0)
178 {
179 continue;
180 }
181
182 // Calculate successor coordinates.
183 double dSuccessorEasting = stNextParent.stNodeLocation.dEasting + (nEastingDirection * constants::ASTAR_NODE_SIZE);
184 double dSuccessorNorthing = stNextParent.stNodeLocation.dNorthing + (nNorthingDirection * constants::ASTAR_NODE_SIZE);
185 // Check for valid coordinate (check for boundary and obstacles).
186 if (!ValidCoordinate(dSuccessorEasting, dSuccessorNorthing))
187 {
188 continue;
189 }
190
191 // Copy data from parent coordinate.
192 geoops::UTMCoordinate stSuccessorCoordinate = stNextParent.stNodeLocation;
193
194 // Adjust Easting and Northing offsets to create new coordinate.
195 stSuccessorCoordinate.dEasting = dSuccessorEasting;
196 stSuccessorCoordinate.dNorthing = dSuccessorNorthing;
197 RoundUTMCoordinate(stSuccessorCoordinate);
198 // Create successor node, initialize values to 0 (done by constructor).
199 nodes::AStarNode stNextSuccessor(vClosedList.back(), stSuccessorCoordinate);
200 // Copy successor node to vector.
201 vSuccessors.emplace_back(stNextSuccessor);
202 }
203 }
204
205 // For each successor:
206 for (size_t i = 0; i < vSuccessors.size(); i++)
207 {
208 // Vars for distance evaluation.
209 bool bAtGoal = false;
210
211 // If successor distance to goal is less than the node size, stop search.
212 double dDeltaGoalEasting = std::abs(vSuccessors[i].stNodeLocation.dEasting - m_stGoalNode.stNodeLocation.dEasting);
213 double dDeltaGoalNorthing = std::abs(vSuccessors[i].stNodeLocation.dNorthing - m_stGoalNode.stNodeLocation.dNorthing);
214 bAtGoal = dDeltaGoalEasting <= 0.1 && dDeltaGoalNorthing <= 0.1;
215
216 // Construct and return path if we have reached the goal.
217 if (bAtGoal)
218 {
219 // Construct path from goal node.
220 ConstructPath(vSuccessors[i]);
221 // Calculate elapsed time.
222 std::chrono::steady_clock::time_point tmEndTime = std::chrono::steady_clock::now();
223 std::chrono::duration<double> dElapsedTime = std::chrono::duration_cast<std::chrono::duration<double>>(tmEndTime - m_tmStartTime);
224 // Submit log message.
225 LOG_NOTICE(logging::g_qSharedLogger,
226 "ASTAR has successfully planned a path from UTM point ({}, {}) to UTM point ({}, {}) in {} seconds.",
227 m_stStartNode.stNodeLocation.dEasting,
228 m_stStartNode.stNodeLocation.dNorthing,
229 m_stGoalNode.stNodeLocation.dEasting,
230 m_stGoalNode.stNodeLocation.dNorthing,
231 dElapsedTime.count());
232 // Return path.
233 return m_vPathCoordinates;
234 }
235
236 // Create and format lookup string.
237 std::string szSuccessorLookup = UTMCoordinateToString(vSuccessors[i].stNodeLocation);
238
240 // Compute dKg, dKh, and dKf for successor.
242
243 // Calculate the cost of the path from parent node to the successor node.
244 double dDeltaParentEasting = vSuccessors[i].stNodeLocation.dEasting - stNextParent.stNodeLocation.dEasting;
245 double dDeltaParentNorthing = vSuccessors[i].stNodeLocation.dNorthing - stNextParent.stNodeLocation.dNorthing;
246 vSuccessors[i].dKg = stNextParent.dKg + std::sqrt(std::pow(dDeltaParentEasting, 2) + std::pow(dDeltaParentNorthing, 2));
247
248 // Calculate heuristic value using the Euclidean distance to the goal node.
249 vSuccessors[i].dKh = std::sqrt(std::pow(dDeltaGoalEasting, 2) + std::pow(dDeltaGoalNorthing, 2));
250
251 // f = g + h
252 vSuccessors[i].dKf = vSuccessors[i].dKg + vSuccessors[i].dKh;
253
254 // Check if this node is already in the closed list.
255 if (umClosedList.count(szSuccessorLookup))
256 {
257 // Standard A* approach: Skip nodes in closed list entirely.
258 continue;
259 }
260
261 // Check if this node is already in the open list.
262 if (umOpenListLookup.count(szSuccessorLookup))
263 {
264 // If we found a better path to this node, update it.
265 if (vSuccessors[i].dKf < umOpenListLookup[szSuccessorLookup])
266 {
267 // Update the open list lookup with the new, better cost.
268 umOpenListLookup[szSuccessorLookup] = vSuccessors[i].dKf;
269
270 // Note: The node in the heap will be updated when it's eventually processed.
271 // This avoids the expensive operation of finding and updating the heap mid-execution.
272 }
273
274 // Skip adding this node to open list, as it's already there.
275 continue;
276 }
277
278 // Add the successor to the open list
279 umOpenListLookup.emplace(std::make_pair(szSuccessorLookup, vSuccessors[i].dKf));
280 vOpenList.push_back(vSuccessors[i]);
281 std::push_heap(vOpenList.begin(), vOpenList.end(), std::greater<nodes::AStarNode>());
282 }
283
284 // Create and format lookup string.
285 std::string szParentLookup = UTMCoordinateToString(stNextParent.stNodeLocation);
286 // Push lookup string and dKf value to lookup map.
287 umClosedList.emplace(std::make_pair(szParentLookup, stNextParent.dKf));
288 } // End While(!vOpenList.empty).
289
290 // Calculate elapsed time.
291 std::chrono::steady_clock::time_point tmEndTime = std::chrono::steady_clock::now();
292 std::chrono::duration<double> dElapsedTime = std::chrono::duration_cast<std::chrono::duration<double>>(tmEndTime - m_tmStartTime);
293
294 // Function has failed to find a valid path.
295 LOG_ERROR(logging::g_qSharedLogger,
296 "After {} seconds, ASTAR Failed to find a path from UTM point ({}, {}) to UTM point ({}, {})",
297 dElapsedTime.count(),
298 m_stStartNode.stNodeLocation.dEasting,
299 m_stStartNode.stNodeLocation.dNorthing,
300 m_stGoalNode.stNodeLocation.dEasting,
301 m_stGoalNode.stNodeLocation.dNorthing);
302 // Return empty vector and handle outside of class.
303 return m_vPathCoordinates;
304 }
geoops::Waypoint FindNearestStartPoint(const geoops::UTMCoordinate &stStartCoordinate)
Helper function to round a UTMCoordinate to align with the grid.
Definition AStar.cpp:626
std::string UTMCoordinateToString(const geoops::UTMCoordinate &stToTranslate)
Helper function used to translate a UTMCoordinate's dEasting and dNorthing values into a string that ...
Definition AStar.cpp:717
geoops::Waypoint FindNearestGoalPoint(const geoops::UTMCoordinate &stGoalCoordinate)
Helper function for the PlanAvoidancePath method. This method takes in a UTMCoordinate reference and ...
Definition AStar.cpp:508
void ConstructPath(const nodes::AStarNode &stFinalNode)
Called when a goal node has been reached. Recursively builds a vector of UTMCoordinates by tracing th...
Definition AStar.cpp:794
bool ValidCoordinate(const double dEasting, const double dNorthing)
Helper function used to determine if a potential UTMCoordinate is valid. Returns False if there is an...
Definition AStar.cpp:737
void RoundUTMCoordinate(geoops::UTMCoordinate &stCoordinateToRound)
Helper function used to round UTMCoordinates to the nearest constants::ASTAR_NODE_SIZE to avoid round...
Definition AStar.cpp:779
This struct stores/contains information about a UTM coordinate.
Definition GeospatialOperations.hpp:195
This struct is used by the WaypointHandler class to store location, size, and type information about ...
Definition GeospatialOperations.hpp:392
Here is the call graph for this function:

◆ PlanAvoidancePath() [3/3]

std::vector< geoops::Waypoint > pathplanners::AStar::PlanAvoidancePath ( const geoops::GPSCoordinate stStartCoordinate,
const geoops::GPSCoordinate stGoalCoordinate 
)

Called in the obstacle avoidance state to plan a path around obstacles.

Parameters
stStartCoordinate- A GPSCoordinate reference that represents the start location.
stGoalCoordinate- A GPSCoordinate reference that represents the goal location.
Returns
std::vector<geoops::Waypoint> - A vector of Waypoints representing the path calculated by ASTAR.
Author
clayjay3 (clayt.nosp@m.onra.nosp@m.ycowe.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2025-02-21
317 {
318 // Convert the GPS coordinates to UTM coordinates.
319 geoops::UTMCoordinate stStartUTM = geoops::ConvertGPSToUTM(stStartCoordinate);
320 geoops::UTMCoordinate stGoalUTM = geoops::ConvertGPSToUTM(stGoalCoordinate);
321
322 // Call the UTMCoordinate version of PlanAvoidancePath.
323 return PlanAvoidancePath(stStartUTM, stGoalUTM);
324 }
UTMCoordinate ConvertGPSToUTM(const GPSCoordinate &stGPSCoord)
Given a GPS coordinate, convert to UTM and create a new UTMCoordinate object.
Definition GeospatialOperations.hpp:302
Here is the call graph for this function:

◆ CancelPathGeneration()

void pathplanners::AStar::CancelPathGeneration ( )

Cancels the path generation process.

Author
clayjay3 (clayt.nosp@m.onra.nosp@m.ycowe.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2025-02-21
334 {
335 m_bPathGenerationCancelled = true;
336 }

◆ UpsertObstacleData() [1/6]

void pathplanners::AStar::UpsertObstacleData ( const geoops::Waypoint stObstacle)

Adds new obstacle data to the class member variable m_vObstacles.

Parameters
stObstacle- A Waypoint representing the obstacle to add to the path.
Author
clayjay3 (clayt.nosp@m.onra.nosp@m.ycowe.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2025-02-23
347 {
348 // Only add obstacles if they are not already in the vector.
349 std::vector<geoops::Waypoint>::iterator stdIter =
350 std::find_if(m_vObstacles.begin(),
351 m_vObstacles.end(),
352 [&stObstacle](const geoops::Waypoint& stExistingObstacle) { return stExistingObstacle == stObstacle; });
353
354 if (stdIter == m_vObstacles.end())
355 {
356 m_vObstacles.push_back(stObstacle);
357 }
358 }
Here is the caller graph for this function:

◆ UpsertObstacleData() [2/6]

void pathplanners::AStar::UpsertObstacleData ( const geoops::UTMCoordinate stObstacle)

Adds new obstacle data to the class member variable m_vObstacles.

Parameters
stObstacle- A UTMCoordinate representing the obstacle to add to the path.
Author
clayjay3 (clayt.nosp@m.onra.nosp@m.ycowe.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2025-02-23
369 {
370 // Only add obstacles if they are not already in the vector.
371 std::vector<geoops::Waypoint>::iterator stdIter =
372 std::find_if(m_vObstacles.begin(),
373 m_vObstacles.end(),
374 [&stObstacle](const geoops::Waypoint& stExistingObstacle) { return stExistingObstacle.GetUTMCoordinate() == stObstacle; });
375
376 if (stdIter == m_vObstacles.end())
377 {
378 m_vObstacles.push_back(geoops::Waypoint(stObstacle));
379 }
380 }

◆ UpsertObstacleData() [3/6]

void pathplanners::AStar::UpsertObstacleData ( const geoops::GPSCoordinate stObstacle)

Adds new obstacle data to the class member variable m_vObstacles.

Parameters
stObstacle- A GPSCoordinate representing the obstacle to add to the path.
Author
clayjay3 (clayt.nosp@m.onra.nosp@m.ycowe.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2025-02-23
391 {
392 // Convert the GPS coordinate to a UTM coordinate.
393 geoops::UTMCoordinate stUTMObstacle = geoops::ConvertGPSToUTM(stObstacle);
394 // Only add obstacles if they are not already in the vector.
395 std::vector<geoops::Waypoint>::iterator stdIter =
396 std::find_if(m_vObstacles.begin(),
397 m_vObstacles.end(),
398 [&stUTMObstacle](const geoops::Waypoint& stExistingObstacle) { return stExistingObstacle.GetUTMCoordinate() == stUTMObstacle; });
399
400 if (stdIter == m_vObstacles.end())
401 {
402 m_vObstacles.push_back(geoops::Waypoint(stUTMObstacle));
403 }
404 }
Here is the call graph for this function:

◆ UpsertObstacleData() [4/6]

void pathplanners::AStar::UpsertObstacleData ( const std::vector< geoops::Waypoint > &  vObstacles)

Adds new obstacle data to the class member variable m_vObstacles. Also checks if the obstacle is already in the vector and skips it if it is.

Parameters
vObstacles- A vector of Waypoints representing the obstacles to add to the path.
Author
clayjay3 (clayt.nosp@m.onra.nosp@m.ycowe.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2025-02-21
416 {
417 // Only add obstacles if they are not already in the vector.
418 for (const geoops::Waypoint& stObstacle : vObstacles)
419 {
420 this->UpsertObstacleData(stObstacle);
421 }
422 }
void UpsertObstacleData(const geoops::Waypoint &stObstacle)
Adds new obstacle data to the class member variable m_vObstacles.
Definition AStar.cpp:346
Here is the call graph for this function:

◆ UpsertObstacleData() [5/6]

void pathplanners::AStar::UpsertObstacleData ( const std::vector< geoops::UTMCoordinate > &  vObstacles)

Adds new obstacle data to the class member variable m_vObstacles.

Parameters
vObstacles- A vector of UTMCoordinates representing the obstacles to add to the path.
Author
clayjay3 (clayt.nosp@m.onra.nosp@m.ycowe.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2025-02-21
433 {
434 // Only add obstacles if they are not already in the vector.
435 for (const geoops::UTMCoordinate& stObstacle : vObstacles)
436 {
437 this->UpsertObstacleData(stObstacle);
438 }
439 }
Here is the call graph for this function:

◆ UpsertObstacleData() [6/6]

void pathplanners::AStar::UpsertObstacleData ( const std::vector< geoops::GPSCoordinate > &  vObstacles)

Adds new obstacle data to the class member variable m_vObstacles.

Parameters
vObstacles- A vector of GPSCoordinates representing the obstacles to add to the path.
Author
clayjay3 (clayt.nosp@m.onra.nosp@m.ycowe.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2025-02-21
450 {
451 // Only add obstacles if they are not already in the vector.
452 for (const geoops::GPSCoordinate& stObstacle : vObstacles)
453 {
454 this->UpsertObstacleData(stObstacle);
455 }
456 }
This struct stores/contains information about a GPS data.
Definition GeospatialOperations.hpp:99
Here is the call graph for this function:

◆ ClearObstacleData()

void pathplanners::AStar::ClearObstacleData ( )

Helper function to destroy objects from m_vObstacles.

Author
Kai Shafe (kasq5.nosp@m.m@um.nosp@m.syste.nosp@m.m.ed.nosp@m.u)
Date
2024-02-02
465 {
466 m_vObstacles.clear();
467 }
Here is the caller graph for this function:

◆ GetPath()

std::vector< geoops::Waypoint > pathplanners::AStar::GetPath ( ) const

Getter for the path calculated by ASTAR.

Returns
std::vector<geoops::UTMCoordinate> - A vector of UTMCoordinates representing the path calculated by ASTAR.
Author
clayjay3 (clayt.nosp@m.onra.nosp@m.ycowe.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2025-01-09
478 {
479 return m_vPathCoordinates;
480 }

◆ GetObstacleData()

std::vector< geoops::Waypoint > pathplanners::AStar::GetObstacleData ( ) const

Getter for the current obstacle data.

Returns
std::vector<AStar::Obstacle> - A vector of Obstacle structs representing the obstacles in the path.
Author
clayjay3 (clayt.nosp@m.onra.nosp@m.ycowe.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2025-01-09
491 {
492 return m_vObstacles;
493 }

◆ FindNearestGoalPoint()

geoops::Waypoint pathplanners::AStar::FindNearestGoalPoint ( const geoops::UTMCoordinate stGoalCoordinate)
private

Helper function for the PlanAvoidancePath method. This method takes in a UTMCoordinate reference and uses class member variables to mutate the m_stGoalNode object's coordinates to represent the nearest boundary point.

Precondition
- m_stStartNode has been initialized with a UTMCoordinate representing the rover's current location.
Parameters
stGoalCoordinate- UTMCoordinate reference representing the current rover destination.
Author
Kai Shafe (kasq5.nosp@m.m@um.nosp@m.syste.nosp@m.m.ed.nosp@m.u)
Date
2024-02-15
509 {
510 // Round the goal coordinate to align with the grid.
511 geoops::UTMCoordinate stRoundedGoal = stGoalCoordinate;
512 RoundUTMCoordinate(stRoundedGoal);
513
514 // Create return value.
515 geoops::UTMCoordinate stBoundaryCoordinate = stRoundedGoal;
516 // Determine components of the distance vector formed by the current location and goal.
517 const double dDeltaX = stRoundedGoal.dEasting - m_stStartNode.stNodeLocation.dEasting;
518 const double dDeltaY = stRoundedGoal.dNorthing - m_stStartNode.stNodeLocation.dNorthing;
519
520 // Only calculate the boundary point if the goal is not within the search grid.
521 if (std::fabs(dDeltaX) > constants::ASTAR_MAX_SEARCH_GRID || std::fabs(dDeltaY) > constants::ASTAR_MAX_SEARCH_GRID)
522 {
523 // Calculate the slope of the line formed by the goal and the current location.
524 const double dSlope = std::fabs(dDeltaY / dDeltaX);
525 // Calculate the angle of the line formed by the goal and the current location.
526 const double dAngle = std::atan(dSlope);
527 // Calculate the boundary point's X and Y components.
528 const double dBoundaryX = m_stStartNode.stNodeLocation.dEasting + (constants::ASTAR_MAX_SEARCH_GRID * std::cos(dAngle)) * (dDeltaX < 0 ? -1 : 1);
529 const double dBoundaryY = m_stStartNode.stNodeLocation.dNorthing + (constants::ASTAR_MAX_SEARCH_GRID * std::sin(dAngle)) * (dDeltaY < 0 ? -1 : 1);
530 // Set the boundary point's coordinates.
531 stBoundaryCoordinate.dEasting = dBoundaryX;
532 stBoundaryCoordinate.dNorthing = dBoundaryY;
533
534 // Submit log message.
535 geoops::GeoMeasurement stMeasurement = geoops::CalculateGeoMeasurement(m_stStartNode.stNodeLocation, stBoundaryCoordinate);
536 LOG_WARNING(logging::g_qSharedLogger,
537 "The goal node was adjusted from UTM point ({}, {}) to UTM point ({}, {}) to stay within the search grid of {} meters. The distance between the "
538 "original goal and the boundary point is {} meters.",
539 stRoundedGoal.dEasting,
540 stRoundedGoal.dNorthing,
541 stBoundaryCoordinate.dEasting,
542 stBoundaryCoordinate.dNorthing,
543 constants::ASTAR_MAX_SEARCH_GRID,
544 stMeasurement.dDistanceMeters);
545 }
546
547 // Handle edge case of an obstacle blocking the goal coordinate.
548 bool bGoalBlocked = true;
549 /*
550 * This loop will check if the goal node is within the avoidance radius of any obstacle.
551 * If it is, the goal node will be shifted along the X and Y axes to avoid the obstacle.
552 * Then the loop will recheck all obstacles to ensure the new goal node is not blocked.
553 */
554 while (bGoalBlocked)
555 {
556 bGoalBlocked = false;
557 // For each obstacle:
558 for (size_t i = 0; i < m_vObstacles.size(); i++)
559 {
560 // Multiplier for avoidance radius.
561 double dAvoidanceRadius = constants::ASTAR_AVOIDANCE_MULTIPLIER * m_vObstacles[i].dRadius;
562 // Create obstacle borders.
563 double dEastObstacleBorder = m_vObstacles[i].GetUTMCoordinate().dEasting + dAvoidanceRadius;
564 double dWestObstacleBorder = m_vObstacles[i].GetUTMCoordinate().dEasting - dAvoidanceRadius;
565 double dNorthObstacleBorder = m_vObstacles[i].GetUTMCoordinate().dNorthing + dAvoidanceRadius;
566 double dSouthObstacleBorder = m_vObstacles[i].GetUTMCoordinate().dNorthing - dAvoidanceRadius;
567
568 // If goal node coordinate is within obstacle borders.
569 if (dWestObstacleBorder < stBoundaryCoordinate.dEasting && stBoundaryCoordinate.dEasting < dEastObstacleBorder &&
570 dSouthObstacleBorder < stBoundaryCoordinate.dNorthing && stBoundaryCoordinate.dNorthing < dNorthObstacleBorder)
571 {
572 bGoalBlocked = true;
573 // Shift goal coordinate along X axis to avoid obstacle.
574 if (stBoundaryCoordinate.dEasting > m_vObstacles[i].GetUTMCoordinate().dEasting)
575 {
576 stBoundaryCoordinate.dEasting = dEastObstacleBorder + constants::ASTAR_NODE_SIZE * 2;
577 }
578 else
579 {
580 stBoundaryCoordinate.dEasting = dWestObstacleBorder - constants::ASTAR_NODE_SIZE * 2;
581 }
582 // Shift goal coordinate along Y axis to avoid obstacle.
583 if (stBoundaryCoordinate.dNorthing > m_vObstacles[i].GetUTMCoordinate().dNorthing)
584 {
585 stBoundaryCoordinate.dNorthing = dNorthObstacleBorder + constants::ASTAR_NODE_SIZE * 2;
586 }
587 else
588 {
589 stBoundaryCoordinate.dNorthing = dSouthObstacleBorder - constants::ASTAR_NODE_SIZE * 2;
590 }
591 RoundUTMCoordinate(stBoundaryCoordinate);
592 // Recheck all obstacles after adjusting the coordinate.
593 break;
594 }
595 }
596 }
597
598 // Check if the goal node doesn't equal the original goal node.
599 if (stBoundaryCoordinate != stRoundedGoal)
600 {
601 // Submit log message.
602 geoops::GeoMeasurement stMeasurement = geoops::CalculateGeoMeasurement(stRoundedGoal, stBoundaryCoordinate);
603 LOG_WARNING(logging::g_qSharedLogger,
604 "The goal node was adjusted from UTM point ({}, {}) to UTM point ({}, {}) to avoid obstacles. The distance between the original goal and the "
605 "adjusted goal is {} meters.",
606 stRoundedGoal.dEasting,
607 stRoundedGoal.dNorthing,
608 stBoundaryCoordinate.dEasting,
609 stBoundaryCoordinate.dNorthing,
610 stMeasurement.dDistanceMeters);
611 }
612
613 // Return rounded coordinate.
614 return stBoundaryCoordinate;
615 }
GeoMeasurement CalculateGeoMeasurement(const GPSCoordinate &stCoord1, const GPSCoordinate &stCoord2)
The shortest path between two points on an ellipsoid at (lat1, lon1) and (lat2, lon2) is called the g...
Definition GeospatialOperations.hpp:522
This struct is used to store the distance, arc length, and relative bearing for a calculated geodesic...
Definition GeospatialOperations.hpp:82
Here is the call graph for this function:
Here is the caller graph for this function:

◆ FindNearestStartPoint()

geoops::Waypoint pathplanners::AStar::FindNearestStartPoint ( const geoops::UTMCoordinate stStartCoordinate)
private

Helper function to round a UTMCoordinate to align with the grid.

Parameters
stStartCoordinate- A UTMCoordinate reference that represents the coordinate to round.
Returns
geoops::Waypoint - A Waypoint struct containing the rounded coordinate.
Author
clayjay3 (clayt.nosp@m.onra.nosp@m.ycowe.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2025-02-21
627 {
628 // Create instance variables.
629 geoops::UTMCoordinate stAdjustedStartCoordinate = stStartCoordinate;
630 // Round the start coordinate to align with the grid.
631 RoundUTMCoordinate(stAdjustedStartCoordinate);
632 // Make a copy of the rounded start coordinate so we can compare it to the original start coordinate later.
633 geoops::UTMCoordinate stRoundedStart = stAdjustedStartCoordinate;
634
635 // Continue shifting until the adjusted start coordinate no longer overlaps any obstacle.
636 bool bStartBlocked = true;
637 while (bStartBlocked)
638 {
639 bStartBlocked = false;
640
641 // Check each obstacle.
642 for (size_t i = 0; i < m_vObstacles.size(); i++)
643 {
644 // Calculate the avoidance radius and obstacle borders.
645 double dAvoidanceRadius = constants::ASTAR_AVOIDANCE_MULTIPLIER * m_vObstacles[i].dRadius;
646 double dEastObstacleBorder = m_vObstacles[i].GetUTMCoordinate().dEasting + dAvoidanceRadius;
647 double dWestObstacleBorder = m_vObstacles[i].GetUTMCoordinate().dEasting - dAvoidanceRadius;
648 double dNorthObstacleBorder = m_vObstacles[i].GetUTMCoordinate().dNorthing + dAvoidanceRadius;
649 double dSouthObstacleBorder = m_vObstacles[i].GetUTMCoordinate().dNorthing - dAvoidanceRadius;
650
651 // If the start coordinate is inside the obstacle's borders...
652 if (dWestObstacleBorder < stAdjustedStartCoordinate.dEasting && stAdjustedStartCoordinate.dEasting < dEastObstacleBorder &&
653 dSouthObstacleBorder < stAdjustedStartCoordinate.dNorthing && stAdjustedStartCoordinate.dNorthing < dNorthObstacleBorder)
654 {
655 bStartBlocked = true;
656
657 // Shift along the X axis: move to just outside the obstacle.
658 if (stAdjustedStartCoordinate.dEasting > m_vObstacles[i].GetUTMCoordinate().dEasting)
659 {
660 stAdjustedStartCoordinate.dEasting = dEastObstacleBorder + constants::ASTAR_NODE_SIZE * 2;
661 }
662 else
663 {
664 stAdjustedStartCoordinate.dEasting = dWestObstacleBorder - constants::ASTAR_NODE_SIZE * 2;
665 }
666
667 // Shift along the Y axis: move to just outside the obstacle.
668 if (stAdjustedStartCoordinate.dNorthing > m_vObstacles[i].GetUTMCoordinate().dNorthing)
669 {
670 stAdjustedStartCoordinate.dNorthing = dNorthObstacleBorder + constants::ASTAR_NODE_SIZE * 2;
671 }
672 else
673 {
674 stAdjustedStartCoordinate.dNorthing = dSouthObstacleBorder - constants::ASTAR_NODE_SIZE * 2;
675 }
676
677 // Round the coordinate to align with the grid.
678 RoundUTMCoordinate(stAdjustedStartCoordinate);
679
680 // Break out of the obstacle loop to recheck all obstacles with the new coordinate.
681 break;
682 }
683 }
684 }
685
686 // Check if the adjusted start coordinate doesn't equal the original start coordinate.
687 if (stAdjustedStartCoordinate != stRoundedStart)
688 {
689 // Submit log message.
690 geoops::GeoMeasurement stMeasurement = geoops::CalculateGeoMeasurement(stRoundedStart, stAdjustedStartCoordinate);
691 LOG_WARNING(logging::g_qSharedLogger,
692 "The start node was adjusted from UTM point ({}, {}) to UTM point ({}, {}) to avoid obstacles. The distance between the original start and the "
693 "adjusted start is {} meters.",
694 stRoundedStart.dEasting,
695 stRoundedStart.dNorthing,
696 stAdjustedStartCoordinate.dEasting,
697 stAdjustedStartCoordinate.dNorthing,
698 stMeasurement.dDistanceMeters);
699 }
700
701 // Return the adjusted start point that no longer overlaps any obstacle.
702 return stAdjustedStartCoordinate;
703 }
Here is the call graph for this function:
Here is the caller graph for this function:

◆ RoundUTMCoordinate()

void pathplanners::AStar::RoundUTMCoordinate ( geoops::UTMCoordinate stCoordinateToRound)
private

Helper function used to round UTMCoordinates to the nearest constants::ASTAR_NODE_SIZE to avoid rounding errors when trying to determine if two nodes have the same location.

Parameters
stCoordinateToRound- A UTMCoordinate reference that will have its dNorthing and dEasting values mutated to round them to the nearest constants::ASTAR_NODE_SIZE.
Author
Kai Shafe (kasq5.nosp@m.m@um.nosp@m.syste.nosp@m.m.ed.nosp@m.u)
Date
2024-02-12
780 {
781 stCoordinateToRound.dEasting = std::round(stCoordinateToRound.dEasting / constants::ASTAR_NODE_SIZE) * constants::ASTAR_NODE_SIZE;
782 stCoordinateToRound.dNorthing = std::round(stCoordinateToRound.dNorthing / constants::ASTAR_NODE_SIZE) * constants::ASTAR_NODE_SIZE;
783 }
Here is the caller graph for this function:

◆ ConstructPath()

void pathplanners::AStar::ConstructPath ( const nodes::AStarNode stEndNode)
private

Called when a goal node has been reached. Recursively builds a vector of UTMCoordinates by tracing the parent pointers of AStarNodes. This function then saves that vector to m_vPathNodes.

Author
Kai Shafe (kasq5.nosp@m.m@um.nosp@m.syste.nosp@m.m.ed.nosp@m.u)
Date
2024-02-13
795 {
796 // Base case: Check for origin node.
797 if (stEndNode.pParentNode == nullptr)
798 {
799 // Start copying
800 m_vPathCoordinates.emplace_back(stEndNode.stNodeLocation);
801 return;
802 }
803 // Recursive case: call ConstructPath on parent pointer.
804 ConstructPath(*stEndNode.pParentNode);
805 // Copy node UTMCoordinate data to m_vPathNodes.
806 m_vPathCoordinates.emplace_back(stEndNode.stNodeLocation);
807 return;
808 }
Here is the call graph for this function:
Here is the caller graph for this function:

◆ UTMCoordinateToString()

std::string pathplanners::AStar::UTMCoordinateToString ( const geoops::UTMCoordinate stToTranslate)
private

Helper function used to translate a UTMCoordinate's dEasting and dNorthing values into a string that can be hashed for the unordered_map data structure for O(1) lookup of nodes at a particular location.

Parameters
stToTranslate- A UTMCoordinate struct reference containing the data to translate.
Returns
- A string containing the translated coordinate.
Author
Kai Shafe (kasq5.nosp@m.m@um.nosp@m.syste.nosp@m.m.ed.nosp@m.u)
Date
2024-02-05
718 {
719 std::string szTranslation = std::to_string(stToTranslate.dEasting);
720 szTranslation.append(std::to_string(stToTranslate.dNorthing));
721 return szTranslation;
722 }
Here is the caller graph for this function:

◆ ValidCoordinate()

bool pathplanners::AStar::ValidCoordinate ( const double  dEasting,
const double  dNorthing 
)
private

Helper function used to determine if a potential UTMCoordinate is valid. Returns False if there is an obstacle is blocking the node or the node is outside the max boundary. Returns True otherwise, representing the node is a valid path to consider. To save memory and compute time, we only evaluate the doubles representing the coordinate.

Parameters
dEasting- A const double reference representing a dEasting to evaluate.
dNorthing- A const double reference representing a dNorthing to evaluate.
Author
Kai Shafe (kasq5.nosp@m.m@um.nosp@m.syste.nosp@m.m.ed.nosp@m.u)
Date
2024-02-06
738 {
739 // For each obstacle.
740 for (size_t i = 0; i < m_vObstacles.size(); i++)
741 {
742 // Multiplier for avoidance radius.
743 double dAvoidanceRadius = constants::ASTAR_AVOIDANCE_MULTIPLIER * m_vObstacles[i].dRadius;
744 // Create obstacle borders.
745 double dEastObstacleBorder = m_vObstacles[i].GetUTMCoordinate().dEasting + dAvoidanceRadius;
746 double dWestObstacleBorder = m_vObstacles[i].GetUTMCoordinate().dEasting - dAvoidanceRadius;
747 double dNorthObstacleBorder = m_vObstacles[i].GetUTMCoordinate().dNorthing + dAvoidanceRadius;
748 double dSouthObstacleBorder = m_vObstacles[i].GetUTMCoordinate().dNorthing - dAvoidanceRadius;
749
750 // Return false if node is within obstacle borders.
751 if (dWestObstacleBorder < dEasting && dEasting < dEastObstacleBorder && dNorthObstacleBorder > dNorthing && dNorthing > dSouthObstacleBorder)
752 {
753 return false;
754 }
755 }
756
757 // Boundary check (Returns true if params indicate a coordinate inside of the search grid).
758 if (dEasting >= (m_stStartNode.stNodeLocation.dEasting - constants::ASTAR_MAX_SEARCH_GRID - constants::ASTAR_NODE_SIZE) &&
759 dEasting <= (m_stStartNode.stNodeLocation.dEasting + constants::ASTAR_MAX_SEARCH_GRID + constants::ASTAR_NODE_SIZE) &&
760 dNorthing >= (m_stStartNode.stNodeLocation.dNorthing - constants::ASTAR_MAX_SEARCH_GRID - constants::ASTAR_NODE_SIZE) &&
761 dNorthing <= (m_stStartNode.stNodeLocation.dNorthing + constants::ASTAR_MAX_SEARCH_GRID + constants::ASTAR_NODE_SIZE))
762 {
763 return true;
764 }
765 // Return false if boundary check failed.
766 return false;
767 }
Here is the caller graph for this function:

The documentation for this class was generated from the following files: