Generate Polynomial Trajectory Behavior
The generate_polynomial_trajectory_behavior is the core component of the
as2_behaviors_trajectory_generation package in Aerostack2.
This behavior turns a list of waypoints into a smooth time-parameterised
trajectory and feeds it to the controller through the
motion_reference/trajectory_setpoints topic. It exposes a plugin-based
architecture so the actual trajectory-generation algorithm can be selected
at runtime via the plugin_name launch argument, while the action server,
TF handling, yaw policy, sampling and debug topics are shared by every
plugin.
Working Principle
The behavior is split in two layers:
Wrapper (
GeneratePolynomialTrajectoryBehavior): anas2_behavior::BehaviorServerthat owns the action lifecycle (on_activate/on_modify/on_deactivate/on_pause/on_resume/on_run), converts the incoming waypoints to the configureddesired_frame_idvia TF, samples the trajectory at the control-cycle rate and forwards the setpoints through theas2::motionReferenceHandlers::TrajectoryMotionhandler.Plugin (one of the available trajectory generators, see below): solves the actual generation problem (polynomial fitting, S-curve simulation, GCOPTER optimisation, …) behind a stable interface (
GeneratePolynomialTrajectoryBase).
Pipeline per goal:
The wrapper validates the incoming waypoints, converts them to
desired_frame_idand stores them in the pending mission queue.The plugin is asked to generate (or update) the trajectory through its contract (
generateTrajectory/updateWaypoints).On every control cycle (
on_run), the wrapper evaluates the plugin attrajectory_time_forsampling_nsamples spaced bysampling_dtand publishes aTrajectorySetpointsmessage.When the plugin reports
isFinished(), the wrapper hands over to the hover handler and closes the action with success.
Yaw Policy
The yaw goal mode is taken from the action yaw field
(as2_msgs/YawMode). The wrapper supports the following modes:
KEEP_YAW— hold the current yaw measured at goal acceptance.PATH_FACING— point the nose along the trajectory tangent.FIXED_YAW— track the yaw angle provided in the goal.YAW_FROM_TOPIC— track yaw from themotion_reference/yawtopic.FACE_REFERENCE— point the nose at a reference position; rate-limited byyaw_speed_threshold.
The thresholds yaw_threshold (m) and yaw_speed_threshold (rad/s)
gate the yaw recomputation to avoid jitter when the drone is stationary or
very close to the reference.
Pause-after-Generate (start_on_paused)
When a goal is sent with start_on_paused: true the wrapper builds the
trajectory in on_activate but does not emit setpoints. The first
on_run tick reports PAUSED and the behavior stays in that state
until the client sends resume. After resume the trajectory is
sampled from trajectory_time_ = 0 exactly, so the planned mission is
preserved without realignment.
Partial-Trajectory Feed (path_length)
For long missions the wrapper can deliver waypoints to the plugin in a
sliding window of path_length waypoints instead of feeding the full
list up front. This trades a CPU spike at start for a steady drip of
smaller re-plans while the drone flies.
path_length: 0(default) — feeds the full mission to the plugin in one shot (legacy behavior).path_length: N— keeps at mostNwaypoints active in the plugin; consumed waypoints are replaced by the next pending one throughplugin->updateWaypoints(). Plugins that overrideupdateWaypointsfor smooth stitching keep their internal time axis untouched; plugins that don’t override it regenerate using the livevehicle_pose/vehicle_twistas boundary conditions.
In both cases feedback.remaining_waypoints reflects the full mission
queue, not the active window.
Frame-Drift Watchdog
When frequency_update_frame > 0 the wrapper checks the map↔odom
transform at that rate and triggers a regeneration when the translation
drift exceeds transform_threshold (m). This keeps the trajectory
consistent with the active state-estimation frame after re-localisations.
Degenerate-Hold
When the active waypoint list collapses to a single waypoint and that
waypoint sits closer than kDegenerateDistanceM (hard-coded to
0.05 m) to the current vehicle pose, the wrapper short-circuits the
plugin: no trajectory is generated, no setpoints are emitted, and the
action reports SUCCESS immediately. This avoids degenerate generations
(zero-length trajectories) when a client re-sends the current pose as a
goal, while still letting the controller hold the drone via its own
station-keeping.
Available Plugins
The package ships four plugins that implement the
GeneratePolynomialTrajectoryBase contract. Choose one at launch time
through the plugin_name argument.
dynamic_mav_trajectory_generator
Wrapper around the dynamic_trajectory_generator library (modifiable polynomial generator on top of ETH-ASL mav_trajectory_generation). Supports smooth online stitching:
updateWaypointsre-plans the pending segment without breaking the internal time axis, so the boundary velocity is taken from the current trajectory and there is no visible discontinuity. Regeneration is asynchronous (the new trajectory is computed in a background thread and swapped lazily). Recommended for long missions with frequenton_modifyrequests.mav_trajectory_generator
Wrapper around the mav_trajectory_generation_library (Aerostack2 fork of ETH-ASL mav_trajectory_generation, static polynomial generator with linear or NLopt-based nonlinear solver). Uses the regenerate semantics for
updateWaypoints: every modify replans from scratch usingvehicle_pose_/vehicle_twist_as boundary state, withWaypoint::velocityinjecting the current twist for C1 continuity.jerk_limited_trajectory_generator
Wrapper around the jerk_limited_trajectory_generator_lib S-curve simulator (based on the PX4-Autopilot jerk-limited trajectory profile). Enforces per-axis jerk, plus 3D-norm acceleration and speed bounds. Optional L1 corner-cut smoothing through
acceptance_radiuslets the drone fly through intermediate waypoints without coming to a full stop. Uses regenerate semantics onupdateWaypoints.gcopter_trajectory_generator
Wrapper around the gcopter_trajectory_generator_lib optimiser (Aerostack2 fork of ZJU-FAST-Lab GCOPTER, with safe-flight-corridor support). Optimises a polynomial trajectory under hard dynamic limits (max body rate, tilt, thrust, velocity) and an AABB corridor anchored on each user waypoint. Uses regenerate semantics on
updateWaypointsand honours the livevehicle_twist_as a hard initial-velocity boundary condition.
Action Interface
The behavior exposes the action as2_msgs/action/GeneratePolynomialTrajectory
on <namespace>/TrajectoryGeneratorBehavior.
Field |
Description |
|---|---|
stamp ( |
Request timestamp. |
yaw ( |
Yaw goal mode (see Yaw Policy above). |
path ( |
List of waypoints with id and stamped pose. Empty ids are auto-filled
as |
max_speed ( |
Maximum desired translation speed along the path (m/s). |
start_on_paused ( |
If |
Field |
Description |
|---|---|
trajectory_generator_success ( |
|
Field |
Description |
|---|---|
next_waypoint_id ( |
Id of the next pending waypoint in the mission queue. |
remaining_waypoints ( |
Number of waypoints in the mission queue that have not yet been
consumed (counts the full mission, not the active window when
|
Configuration Parameters
The configuration is split in two layers: the wrapper YAML with shared parameters and one plugin YAML per backend with algorithm-specific keys.
Wrapper Parameters
Defaults in
generate_polynomial_trajectory_behavior/config/config_default.yaml.
Parameter |
Default |
Description |
|---|---|---|
desired_frame_id |
|
TF frame in which the trajectory is generated and evaluated. Incoming waypoints are converted to this frame before being fed to the plugin. |
sampling_n |
|
Number of trajectory samples emitted per control cycle. Must be ≥ 1. |
sampling_dt |
|
Time step between consecutive samples (s). |
path_length |
|
Active waypoint window fed to the plugin. |
yaw_threshold |
|
Minimum planar distance (m) before recomputing yaw to avoid jitter at low displacement. |
yaw_speed_threshold |
|
Maximum yaw rate (rad/s) for the |
frequency_update_frame |
|
Map↔odom drift watchdog rate (Hz). |
transform_threshold |
|
Translation drift (m) on the watched transform that triggers a
regeneration. |
debug.path_topic |
|
Topic where the sampled generated trajectory is republished as
|
debug.reference_setpoint |
|
Topic for the current reference point as |
debug.reference_end_waypoint |
|
Topic for the end-of-horizon point as |
debug.reference_waypoints |
|
Topic for the full waypoint queue as
|
debug.generation_time_topic |
|
Topic where the wall-clock time (s) spent in the last plugin call to
|
mav_trajectory_generator Parameters
Defaults in
plugins/mav_trajectory_generator/config/mav_trajectory_generator.yaml,
under the namespace mav_trajectory_generator.optimization.*.
Parameter |
Default |
Description |
|---|---|---|
derivative_to_optimize |
|
Derivative order minimised by the cost: |
solver |
|
|
a_max |
|
Maximum acceleration norm enforced by the nonlinear solver (m/s2). |
nl_max_iterations |
|
NLopt iteration cap. |
nl_f_rel |
|
NLopt relative cost tolerance. |
nl_x_rel |
|
NLopt relative parameter tolerance. |
nl_time_penalty |
|
Penalty weight on total trajectory time. |
nl_initial_stepsize_rel |
|
Initial NLopt step size (relative). |
nl_inequality_constraint_tolerance |
|
Tolerance on the inequality (acceleration) constraint. |
dynamic_mav_trajectory_generator Parameters
The dynamic plugin currently exposes no per-instance ROS parameters; the
underlying dynamic_trajectory_generator library is configured with its
own internal defaults. The plugin honours all the wrapper parameters
described above.
jerk_limited_trajectory_generator Parameters
Defaults in
plugins/jerk_limited_trajectory_generator/config/jerk_limited_trajectory_generator.yaml,
under the namespace jerk_limited_trajectory_generator.{limits,simulator}.*.
The action goal’s max_speed transiently overrides limits.max_speed
for a single call.
Parameter |
Default |
Description |
|---|---|---|
limits.max_speed |
|
Maximum 3D speed norm (m/s). Set ≤ 0 to disable. |
limits.max_acceleration |
|
Maximum 3D acceleration norm (m/s2). Set ≤ 0 to disable. |
limits.max_jerk |
|
Maximum per-axis jerk (m/s3). Set ≤ 0 to disable. At least
one of |
limits.acceptance_radius |
|
Radius (m) for the L1 corner-cut at intermediate waypoints. |
limits.final_acceptance_radius |
|
Position tolerance at the last waypoint (m). ≤ 0 reuses
|
limits.trajectory_gain |
|
Scales acceleration when computing the corner-speed cap. |
simulator.step_dt |
|
Internal integration step (s). |
simulator.max_simulation_time |
|
Hard horizon (s) for the offline simulation; trajectories that exceed it fail to generate. |
simulator.settle_velocity |
|
Velocity norm (m/s) below which the drone is considered at rest. |
simulator.advance_radius_min |
|
Floor (m) for the per-waypoint advance threshold. The effective
threshold is |
simulator.polynomial_order |
|
Number of polynomial coefficients per axis and segment in the resulting spline. |
gcopter_trajectory_generator Parameters
Defaults in
plugins/gcopter_trajectory_generator/config/gcopter_trajectory_generator.yaml,
under the namespace
gcopter_trajectory_generator.{drone,limits,optimization,waypoints}.*.
The action goal’s max_speed transiently overrides limits.max_velocity
for a single call.
Parameter |
Default |
Description |
|---|---|---|
drone.mass |
|
Vehicle mass (kg) used by the flatness model. |
drone.gravity |
|
Gravity acceleration (m/s2). |
drone.horizontal_drag |
|
Horizontal drag coefficient. |
drone.vertical_drag |
|
Vertical drag coefficient. |
drone.parasitic_drag |
|
Parasitic drag coefficient. |
drone.speed_smooth_factor |
|
Smoothing factor on the speed term in the flatness model. |
limits.max_velocity |
|
Maximum velocity norm (m/s). |
limits.max_body_rate |
|
Maximum body rate (rad/s). |
limits.max_tilt_angle |
|
Maximum tilt angle (rad). |
limits.min_thrust |
|
Minimum collective thrust (N). |
limits.max_thrust |
|
Maximum collective thrust (N). |
optimization.time_weight |
|
Weight on the total trajectory time term. |
optimization.position_weight |
|
Weight on position constraint violations. |
optimization.velocity_weight |
|
Weight on velocity constraint violations. |
optimization.body_rate_weight |
|
Weight on body-rate constraint violations. |
optimization.tilt_weight |
|
Weight on tilt-angle constraint violations. |
optimization.thrust_weight |
|
Weight on thrust constraint violations. |
optimization.smoothing_eps |
|
Smoothing epsilon for the soft-constraint penalty. |
optimization.integral_resolution |
|
Number of integration samples per segment for the constraint penalty. |
optimization.rel_cost_tol |
|
L-BFGS relative cost tolerance. |
optimization.corridor_margin |
|
AABB half-margin (m) used between anchors and on segments without intermediate waypoints. |
optimization.vertical_perturbation |
|
Initial-state in-plane offset (m) along +x applied as a workaround
for the L-BFGS rank-deficiency on world-Z aligned segments (every
waypoint of the active segment sits inside the vertical-alignment
band defined below). |
optimization.vertical_alignment_threshold |
|
Planar distance band (m) under which all waypoints in the active segment are considered vertically aligned with the vehicle, which triggers the perturbation above. |
waypoints.waypoint_margin |
|
AABB half-margin (m) on the two pinch segments around each intermediate waypoint. |
waypoints.waypoint_anchor_radius |
|
Offset (m) of each anchor along the path. ≤ 0 disables the anchoring and lets the solver cut corners. |
Launching the Behavior
The plugin to load is selected through the plugin_name launch
argument. The set of valid choices is auto-populated from the registered
plugins via pluginlib, so an invalid value is rejected at launch time.
Standalone node
ros2 launch as2_behaviors_trajectory_generation generate_polynomial_trajectory_behavior_launch.py \
namespace:=<drone_namespace> \
plugin_name:=dynamic_mav_trajectory_generator
Launch arguments:
namespace— drone namespace (defaults to theAEROSTACK2_SIMULATION_DRONE_IDenvironment variable).plugin_name— one ofdynamic_mav_trajectory_generator,mav_trajectory_generator,jerk_limited_trajectory_generator,gcopter_trajectory_generator. If left empty, the launch tries to read it fromconfig_file; if it is also missing there, the launch fails.config_file— path to the wrapper YAML (defaults toconfig_default.yaml). The plugin YAML is auto-located inplugins/<plugin_name>/config/<plugin_name>.yaml.log_level— node log level (defaultinfo).use_sim_time— use the simulation clock iftrue(defaultfalse).
Composable node
ros2 launch as2_behaviors_trajectory_generation composable_generate_polynomial_trajectory_behavior.launch.py \
namespace:=<drone_namespace> \
plugin_name:=dynamic_mav_trajectory_generator \
container:=<existing_container_name>
If container is empty a new container is created; otherwise the
behavior is loaded into the existing one as a
rclcpp_components composable node.
Sending a goal manually
ros2 action send_goal /<namespace>/TrajectoryGeneratorBehavior \
as2_msgs/action/GeneratePolynomialTrajectory \
"{
yaw: {mode: 0, angle: 0.0},
path: [
{id: 'wp_0', pose: {header: {frame_id: 'earth'},
pose: {position: {x: 1.0, y: 0.0, z: 1.0}}}},
{id: 'wp_1', pose: {header: {frame_id: 'earth'},
pose: {position: {x: 2.0, y: 1.0, z: 1.5}}}}
],
max_speed: 1.0,
start_on_paused: false
}"
Set start_on_paused: true to generate the trajectory and keep the
behavior in PAUSED until the action client sends resume.
Debugging Topics
When the corresponding debug.* parameter is non-empty the wrapper
publishes:
<namespace>/debug/behaviors/trajectory_generation/trajectory/generated(nav_msgs/Path) — sampled trajectory currently held by the plugin. Refreshed on every regeneration and, for asynchronous plugins, on the cycle where the new backend trajectory is swapped in.<namespace>/debug/behaviors/trajectory_generation/reference/waypoint(visualization_msgs/Marker) — current reference point being commanded.<namespace>/debug/behaviors/trajectory_generation/reference/end_waypoint(visualization_msgs/Marker) — end-of-horizon point of the active sampling window.<namespace>/debug/behaviors/trajectory_generation/trajectory/waypoints(visualization_msgs/MarkerArray) — pending mission waypoints.<namespace>/debug/behaviors/trajectory_generation/generation_time(std_msgs/Float64) — wall-clock time (s) spent in the last plugin call togenerateTrajectory/updateWaypoints.
See also
Writing a New Trajectory Generation Plugin — step-by-step guide to writing a new trajectory-generation plugin against the
GeneratePolynomialTrajectoryBasecontract.