Planning application development
EGO Planner is a local trajectory planner for quadrotors. In unknown or partially known environments, it uses a local map built from onboard perception to generate smooth, flyab…
6 min read · English documentationEGO-Planner is a local trajectory planner for quadrotors. In unknown or partially known environments, it uses a local map built from onboard perception to generate smooth, flyable, collision-free trajectories in real time and adapts to environmental changes through receding-horizon replanning.
Before modifying the code, if you cannot confirm that the changes will not have a significant impact on the navigation module, complete testing in a simulation environment before conducting tests on the actual aircraft. In this project, we have completed:
- ROS 2 porting
- AirSim simulation integration (LiDAR/depth point clouds)
- Octomap / depth point cloud mapping
- Prometheus flight controller interface
1: What problems does EGO-Planner solve?
| Problem | Description |
|---|---|
| One-shot global planning | It can easily fail when the map is incomplete or the environment changes |
| Pure path search | The path is not smooth enough and is difficult for the flight controller to track directly |
| High computational load | It is difficult to run online at a high frequency |
| Dependence on an ESDF map or a safe corridor | It is difficult to run online at a high frequency |
EGO-Planner's approach
- It does not depend on a complete global map and instead uses a local perception map + a receding local target
- Generate quickly first, then refine: polynomial initial trajectory → A* obstacle avoidance → gradient optimization
- Replan at a high frequency and continuously update the trajectory during flight
2: Module breakdown
This module is located in the Modules/ego_planner_swarm directory of the Prometheus project. swarm in the name indicates that the framework supports multi-UAV planning tasks. Run the following command to enter the directory:
cd /home/amov/Prometheus/Modules/ego_planner_swarm
2.1 Module breakdown
The code structure is as follows:
| Module | Directory name | Function |
|---|---|---|
| Main planning node | ego_planner | Obtains information from other modules to manage the flight process (FSM state machine), planning scheduling, and other operations |
| Planning management | planner_manager | Manages global/local trajectories and the optimization process |
| Trajectory optimization | traj_opt | Control point selection, A* segmentation, and gradient optimization |
| Path search | path_searching | Specific implementation of the A* algorithm |
| Environment map | plan_env | Occupancy grid map GridMap |
| Visualization | traj_utils | RViz display and trajectory messages |
| Perception filtering | filter | Depth/Octomap point cloud filtering |
| Control interface | traj_server_for_prometheus | Converts trajectories into Prometheus commands |
2.2 Runtime logic
EGO uses a finite-state machine to drive the entire planning process. The corresponding code is in theplanner_manager folder in ego_replan_fsm.cpp:
A 10 Hz timer executes the execFSMCallback function. The main execution logic is shown in the following figure.
The following table describes each part in detail.
| State | Trigger condition | Behavior |
|---|---|---|
| INIT | Startup | Wait for odometry |
| WAIT_TARGET | Odom is available | Wait for a target point (and trigger) |
| SEQUENTIAL_START | A target is available | Perform the initial planning; in multi-UAV operation, also wait to receive the preceding UAV's trajectory |
| EXEC_TRAJ | Planning succeeds | Execute the current trajectory and monitor whether replanning is required |
| REPLAN_TRAJ | During execution | Perform local replanning from the current trajectory state |
| GEN_NEW_TRAJ | Local planning fails multiple times | Perform global replanning from odom |
| EMERGENCY_STOP | Collision/depth loss | Generate an emergency-stop trajectory |
Each time execFSMCallback() runs:
- It publishes a heartbeat (used by traj_server to check whether the planner is alive)
- It executes the corresponding logic for the current FSM state
- It publishes debugging data
Replanning decisions in EXEC_TRAJ:
- Trajectory execution time >
fsm.thresh_replan_time(default: 1s) → enterREPLAN_TRAJ - Distance to the end of the current trajectory <
fsm.emergency_timeand the final goal has not been reached → replan in advance - The final goal is reached (the trajectory ends or the odom distance <
goal_reach_dist) →WAIT_TARGET
2.3 Main planning flow
The main planning function calls are shown below. After the /uav1/prometheus/motion_planning/goalmotion/plan topic is received, the process runs in the following order.
-
waypointCallback: Topic callback function that records information -
planNextWaypoint: Uses the current odometry as the start point and the specified target point as the end point, generates a global polynomial trajectory using MINCO, allocates the duration of each segment, and writes it totraj_.global_trajfor subsequent local planning and tracking. This stage does not query the map or perform obstacle avoidance.
-
planFromGlobalTraj(used for the initial planning): Usesodom_pos_ / odom_vel_as the start state and repeatedly callscallReboundReplanto generate the first flyable local trajectory. -
callReboundReplan: Entry point for local planning that links the following two steps:-
getLocalTarget: Truncates a local target pointlocal_target_pt_fromtraj_.global_trajaccording toplanning_horizon_(it still tracks the global reference path and does not itself perform obstacle avoidance). -
reboundReplan: The function that actually performs obstacle avoidance. It first initializes a trajectory using MINCO, and thenPolyTrajOptimizerperforms rebound optimization usinggrid_map_. The result is written totraj_.local_traj. -
Publish trajectory:
polyTraj2ROSMsg→ publishplanning/trajectory. -
FSM →
EXEC_TRAJ: Execute the local trajectory; enterREPLAN_TRAJ/GEN_NEW_TRAJas required during execution.
-
Summary: After receiving a target point, the system first uses MINCO to generate a global reference path without obstacle avoidance (planNextWaypoint), then the FSM triggers local rebound optimization to generate a flyable trajectory (callReboundReplan), and finally publishes the trajectory and enters EXEC_TRAJ for execution. Replanning is performed as required during execution.
2.4 Core planning method
The core function, reboundReplan, implements the core obstacle-avoidance logic. After receiving a straight-line MINCO trajectory, it performs collision detection, optimization, and other operations. It incorporates many techniques, including initialization from the previous time step, polynomial initialization, random initialization, and decomposition of the path into multiple polynomial segments.
reboundReplan
│
├─ STEP 1: INIT (initial trajectory generation)
│ computeInitState() → MINCO initial trajectory
│ finelyCheckAndSetConstraintPoints() → checks for obstacles; if a collision is found, performs A* segmentation
│
├─ STEP 2: OPTIMIZE (gradient optimization)
│ optimizeTrajectory() → smoothing + obstacle avoidance + dynamic constraints
│
└─ STEP 3: STORE (storage)
setLocalTrajFromOpt() → writes to traj_.local_traj for use
-
STEP1: Initial trajectory generation
- Determines the segment length according to
polyTraj_piece_length / max_veland generates the MINCO initial trajectory. -
finelyCheckAndSetConstraintPointssamples along the initial trajectory and usesgrid_map_->getInflateOccupancy()to check for obstacles. - No collision: Directly use the control points of the initial trajectory for optimization.
-
Collision:
PolyTrajOptimizerinternally invokes A* (path_searching) to segment the path around obstacles and resets the control points.
- Determines the segment length according to
-
STEP2: Gradient optimization
-
optimizeTrajectory(headState, tailState, innerPts, durations, cost)iterates over the control points. - The cost generally includes smoothness (jerk), obstacle distance, and dynamic feasibility (velocity/acceleration limits).
- Success →
best_MJO; failure → visualize the failed list and returnfalse.
-
3 Data flow and ROS interfaces
3.1 Input topics
| Topic | Type | Purpose | Callback function |
|---|---|---|---|
odom_world |
nav_msgs/Odometry |
Current pose/velocity | odometryCallback |
/traj_start_trigger |
geometry_msgs/PoseStamped |
Start a preset waypoint mission (flight_type=2) |
triggerCallback |
| Depth/Octomap point cloud | Point cloud message | Build a local occupancy map | Subscribed to internally by GridMap
|
mandatory_stop |
std_msgs/Empty |
Force a stop | mandatoryStopCallback |
3.2 Output topics
| Topic | Type | Purpose |
|---|---|---|
planning/trajectory |
PolyTraj |
Current local flyable trajectory |
planning/heartbeat |
std_msgs/Empty |
Planner heartbeat (published with the FSM at 100 Hz) |
planning/data_display |
DataDisp |
Debugging data |
4 Common planning application development scenarios
| Requirement | Where to make the change | Note |
|---|---|---|
| Change the replanning frequency | fsm.thresh_replan_time |
Check CPU load and trajectory oscillation first |
| Change flight velocity/acceleration |
manager.max_vel / max_acc
|
Both global and local planning will be affected |
| Change the local planning range | fsm.planning_horizon |
Match it to the map range |
| Change the arrival criterion | fsm.goal_reach_dist |
Affects when planning stops |
| Change how conservatively obstacles are avoided | Inflation radius in plan_env and weights in traj_opt
|
May result in long detours or trajectories too close to obstacles |
| Change the target input method |
waypointCallback or flight_type
|
Keep it consistent with the FSM flags |
| Add a custom cost term | traj_opt/PolyTrajOptimizer |
First check convergence in simulation |
| Change the FSM state logic |
ego_replan_fsm.cpp → execFSMCallback
|
Has a broad impact; test thoroughly |
| Change the map update method | plan_env.cpp |
Test offline whether the map is as intended |
Recommendations
- Tune parameters before modifying the algorithm: Most cases of poor flight performance are caused by the horizon, velocity, inflation, or replanning period.
- Identify the failure stage: For INIT failures, check the initial trajectory/A*; for OPT failures, check the weights and constraints.
- Preserve warm starts: During execution, do not change replanning back to a straight-line initial trajectory by default.
- Random initialization is a fallback: Use it to retry after failures, not as the default strategy.
-
Add new constraints to the optimizer: Do not pile them onto the
reboundReplanentry point. - Change only one layer at a time: Test parameters / optimizer / initial trajectory / FSM separately.
- Run simulation stress tests before testing on the actual aircraft: This is especially important when changing A*, random initialization, or EMERGENCY-related logic.
- Local path planning has poor global optimality: It is not suitable for narrow scenarios with only one corridor; it often fails to find a path and resorts to haphazard A* searching.
- Terminal output is very important when a problem occurs: Record it, and then investigate it yourself, use AI, or ask our technical staff.
