Trajectory Planner APIs
Trajectory Planner
The TrajectoryPlanner component converts normalized joystick velocity commands
(v_cmd, w_cmd) into smooth, dynamically feasible chassis motion commands
(v_ref, w_ref). It is drive-system independent – the downstream kinematics
layer is responsible for converting chassis commands into individual motor commands.
The jerk-limited mode uses a discrete optimal-control approach: at each step the planner computes the minimum velocity-change distance needed to decelerate the current acceleration to zero, then decides whether to accelerate, maintain, or decelerate – landing exactly on the target without overshoot, equivalent to a time-optimal S-curve under joint jerk and acceleration constraints.
Code examples are provided in the trajectory_planner example folder.
API Reference
Header File
Classes
-
class TrajectoryPlanner : public espp::BaseComponent
Converts normalized joystick velocity commands into smooth, dynamically feasible chassis motion commands (v, w).
The planner is drive-system independent — it does not know about wheel geometry or kinematics. It only enforces velocity, acceleration, and jerk limits on chassis-level commands. The downstream kinematics layer converts (v_ref, w_ref) into individual motor commands.
### Algorithm The jerk-limited mode uses a discrete optimal-control approach: at each step the planner computes the minimum velocity-change distance needed to decelerate the current acceleration to zero, then decides whether to accelerate, maintain, or decelerate to land exactly on the target without overshoot — equivalent to a time-optimal S-curve under jerk and acceleration constraints.
### Profiles Motion limits are grouped into two MotionProfile objects inside Config:
**driving_profile** — used whenever the target is non-zero.
**stopping_profile** — used when the target is (0, 0). Setting jerk to 0 gives a trapezoidal stop; higher acceleration gives faster, firmer braking.
A MotionProfile selects its mode automatically:
**Trapezoidal**: `max_linear_jerk == 0 && max_angular_jerk == 0`
**S-curve**: either jerk field is non-zero
### Timing Two independent timers run internally:
**planning timer** — calls `update()` at `planning_period` (default 20 ms / 50 Hz). Recommended range: 5-200 ms on microcontrollers.
**callback task** — fires `output_callback` on every `update()` that produces a new output value (CV-notified by the planning timer); no separate period needed.
This class is thread-safe: set_target(), get_target(), output(), stop(), and reset() may be called from different threads concurrently.
Quick-Start: Full Public API
// 1. Construct with a Config - task starts automatically. espp::TrajectoryPlanner planner({ .max_linear_velocity = 1.0f, // m/s .max_angular_velocity = 3.14159f, // rad/s .driving_profile = {.max_linear_acceleration = 2.0f, .max_angular_acceleration = 6.28f, .max_linear_jerk = 2.0f, .max_angular_jerk = 20.0f}, // Trapezoidal stop (no jerk) - fast, clean, no overshoot .stopping_profile = {.max_linear_acceleration = 5.0f, .max_angular_acceleration = 10.0f}, .enforce_motion_envelope = true, // keep (v/vmax)²+(ω/ωmax)²≤1 .max_centripetal_acceleration = 0.4f, // m/s² .output_callback = [&logger](const espp::TrajectoryPlanner::MotionCommand &cmd) { logger.debug("callback: {}", cmd); }, .planning_period = planning_period, .planning_task_config = {.name = "tp_qs", .stack_size_bytes = 4096}, }); // 2. is_running() - confirm the task started. logger.info("Task running: {}", planner.is_running()); // 3. get_config() - inspect active configuration. auto cfg = planner.get_config(); logger.info("Config: {}", cfg); // 4. set_target(linear, angular) - normalized [-1, +1] joystick inputs. // +1.0 linear = max_linear_velocity forward. planner.set_target(1.0f, 0.0f); std::this_thread::sleep_for(600ms); // 5. output() - poll the latest smoothed command at any time. auto cmd = planner.output(); logger.info("Polled output: {}", cmd); // 6. set_target with combined motion - forward + right turn. planner.set_target(0.6f, -0.5f); std::this_thread::sleep_for(600ms); // 7. set_config() - change parameters at runtime; resets state by default. espp::TrajectoryPlanner::Config new_cfg = planner.get_config(); new_cfg.max_linear_velocity = 0.5f; // half speed cap planner.set_config(new_cfg, /*reset_state=*/false); logger.info("Updated max_linear_velocity to 0.5 m/s"); planner.set_target(1.0f, 0.0f); // still clamped to new 0.5 m/s std::this_thread::sleep_for(600ms); // 8. stop() - ramp down to zero respecting deceleration limits. logger.info("Commanding stop (ramp-down)"); planner.stop(); std::this_thread::sleep_for(400ms); // 9. reset() - zero state immediately (e.g. after e-stop). logger.info("Emergency reset"); planner.reset(); logger.info("Output after reset: {}", planner.output()); // 10. Destructor stops the task automatically when planner leaves scope.
S-Curve Driving / Trapezoidal Stop
std::atomic<int> tick{0}; espp::TrajectoryPlanner planner({ .max_linear_velocity = 1.0f, // m/s .max_angular_velocity = 3.14159f, // rad/s // S-curve driving: smooth ramp with jerk limiting .driving_profile = {.max_linear_acceleration = 2.0f, .max_angular_acceleration = 6.28f, .max_linear_jerk = 2.0f, .max_angular_jerk = 25.0f}, // Trapezoidal stop: no jerk = immediate deceleration, no overshoot .stopping_profile = {.max_linear_acceleration = 6.0f, .max_angular_acceleration = 12.0f}, .enforce_motion_envelope = true, .max_centripetal_acceleration = 0.5f, // m/s² .output_callback = [&logger, &tick](const espp::TrajectoryPlanner::MotionCommand &cmd) { logger.info("[{:3d}] {}", tick.load(), cmd); tick++; }, .planning_period = planning_period, .planning_task_config = {.name = "tp_ex1", .stack_size_bytes = 10240, .priority = 5, .core_id = -1}, }); // Full forward (normalized: 1.0 = max_linear_velocity) logger.info("Commanding full forward"); planner.set_target(1.0f, 0.0f); std::this_thread::sleep_for(3s); // Gentle right curve at half speed logger.info("Commanding half-speed right curve"); planner.set_target(0.5f, -0.4f); std::this_thread::sleep_for(3s); // Controlled stop - uses braking deceleration limits logger.info("Stopping"); planner.stop(); std::this_thread::sleep_for(500ms); // task stops automatically when planner goes out of scope
High-Speed S-Curve with Centripetal Limiting
std::atomic<int> tick{0}; espp::TrajectoryPlanner planner({ .max_linear_velocity = 4.47f, // 10 mph .max_angular_velocity = 3.14159f / 2.0f, // 90 deg/s // S-curve driving: smooth acceleration with jerk limits .driving_profile = {.max_linear_acceleration = 2.0f, .max_angular_acceleration = 6.28f, .max_linear_jerk = 10.0f, .max_angular_jerk = 30.0f}, // Trapezoidal stop: no jerk limit = clean stop, no overshoot .stopping_profile = {.max_linear_acceleration = 4.0f, .max_angular_acceleration = 8.0f}, .enforce_motion_envelope = true, .max_centripetal_acceleration = 0.3f, .output_callback = [&logger, &tick](const espp::TrajectoryPlanner::MotionCommand &cmd) { logger.info("[{:3d}] {}", tick.load(), cmd); tick++; }, .planning_period = planning_period, .planning_task_config = {.name = "tp_ex2", .stack_size_bytes = 10240, .priority = 5, .core_id = -1}, }); // Forward + left turn (0.8 = 80% max linear, 0.5 = 50% max angular) logger.info("Commanding forward + left turn"); planner.set_target(0.8f, 0.5f); std::this_thread::sleep_for(1s); // Reverse direction logger.info("Reversing direction"); planner.set_target(-0.5f, -0.5f); std::this_thread::sleep_for(1s); // Controlled stop logger.info("Stopping"); planner.stop(); std::this_thread::sleep_for(500ms); // task stops automatically when planner goes out of scope
Constraint Validation
using Cfg = espp::TrajectoryPlanner::Config; using Cmd = espp::TrajectoryPlanner::MotionCommand; // ---- Validator struct ------------------------------------------------ struct Validator { // Immutable limits float max_v, max_w; float a_drv_v, a_drv_w, j_drv_v, j_drv_w; // driving profile float a_stp_v, a_stp_w, j_stp_v, j_stp_w; // stopping profile float max_cp, dt; // Callback-thread history (only written from callback) float pv{0}, pw{0}, pa_v{0}, pa_w{0}; bool inited{false}; int skip{2}; // skip first N ticks for derivative settling std::chrono::steady_clock::time_point prev_time{}; bool time_inited{false}; // Shared results - protected by mtx mutable std::mutex mtx; espp::Logger *log{nullptr}; // set after construction bool stopping{false}; int viol_drv{0}; // violations during driving phase int viol_stp{0}; // violations during stopping phase struct PhasePeaks { float v{0}, w{0}, a_v{0}, a_w{0}, j_v{0}, j_w{0}, cp{0}; void reset() { v = w = a_v = a_w = j_v = j_w = cp = 0; } }; PhasePeaks pk_drv, pk_stp; void mark_stopping() { std::lock_guard<std::mutex> lk(mtx); stopping = true; skip = 3; } // Call before resuming a non-zero target after a stop. void mark_driving() { std::lock_guard<std::mutex> lk(mtx); stopping = false; skip = 3; } // Call before any abrupt target change (direction reversal, etc.). void mark_transition() { std::lock_guard<std::mutex> lk(mtx); skip = 3; } void reset() { // Always called before a new planner starts - no concurrent access std::lock_guard<std::mutex> lk(mtx); stopping = false; viol_drv = 0; viol_stp = 0; pk_drv.reset(); pk_stp.reset(); pv = pw = pa_v = pa_w = 0; inited = false; skip = 2; time_inited = false; } void operator()(const Cmd &cmd) { std::lock_guard<std::mutex> lk(mtx); const float tol = 1.08f; // 8% margin for scheduler jitter const float ma_v = stopping ? a_stp_v : a_drv_v; const float ma_w = stopping ? a_stp_w : a_drv_w; const float mj_v = stopping ? j_stp_v : j_drv_v; const float mj_w = stopping ? j_stp_w : j_drv_w; // Speed limits PhasePeaks &pk = stopping ? pk_stp : pk_drv; pk.v = std::max(pk.v, std::abs(cmd.linear_velocity)); pk.w = std::max(pk.w, std::abs(cmd.angular_velocity)); if (std::abs(cmd.linear_velocity) > max_v * tol) { if (log) log->warn("VIOL speed_v: {:.3f} > {:.3f} ({})", cmd.linear_velocity, max_v * tol, stopping ? "stop" : "drv"); stopping ? viol_stp++ : viol_drv++; } if (std::abs(cmd.angular_velocity) > max_w * tol) { if (log) log->warn("VIOL speed_w: {:.3f} > {:.3f} ({})", cmd.angular_velocity, max_w * tol, stopping ? "stop" : "drv"); stopping ? viol_stp++ : viol_drv++; } // Centripetal limit float cp = std::abs(cmd.linear_velocity * cmd.angular_velocity); pk.cp = std::max(pk.cp, cp); if (inited) { // Use actual elapsed time to avoid systematic error from FreeRTOS // scheduling jitter (actual tick ~30 ms vs. nominal 20 ms). auto now = std::chrono::steady_clock::now(); float adt = time_inited ? std::chrono::duration<float>(now - prev_time).count() : dt; // nominal for first step prev_time = now; time_inited = true; float a_v = (cmd.linear_velocity - pv) / adt; float a_w = (cmd.angular_velocity - pw) / adt; pk.a_v = std::max(pk.a_v, std::abs(a_v)); pk.a_w = std::max(pk.a_w, std::abs(a_w)); float j_v = (a_v - pa_v) / adt; float j_w = (a_w - pa_w) / adt; if (skip > 0) { --skip; } else { pk.j_v = std::max(pk.j_v, std::abs(j_v)); pk.j_w = std::max(pk.j_w, std::abs(j_w)); if (std::abs(a_v) > ma_v * tol) { if (log) log->warn("VIOL accel_v: {:.3f} > {:.3f} ({})", a_v, ma_v * tol, stopping ? "stop" : "drv"); stopping ? viol_stp++ : viol_drv++; } if (std::abs(a_w) > ma_w * tol) { if (log) log->warn("VIOL accel_w: {:.3f} > {:.3f} ({})", a_w, ma_w * tol, stopping ? "stop" : "drv"); stopping ? viol_stp++ : viol_drv++; } if (mj_v > 0.0f && std::abs(j_v) > mj_v * tol) { if (log) log->warn("VIOL jerk_v: {:.3f} > {:.3f} ({})", j_v, mj_v * tol, stopping ? "stop" : "drv"); stopping ? viol_stp++ : viol_drv++; } if (mj_w > 0.0f && std::abs(j_w) > mj_w * tol) { if (log) log->warn("VIOL jerk_w: {:.3f} > {:.3f} ({})", j_w, mj_w * tol, stopping ? "stop" : "drv"); stopping ? viol_stp++ : viol_drv++; } } pa_v = a_v; pa_w = a_w; } else { // First tick: start the clock prev_time = std::chrono::steady_clock::now(); time_inited = true; } pv = cmd.linear_velocity; pw = cmd.angular_velocity; inited = true; } bool report(espp::Logger &log, std::string_view name) { std::lock_guard<std::mutex> lk(mtx); log.info("--- {} --- drv_viol={} stp_viol={}", name, viol_drv, viol_stp); log.info(" [driving]"); log.info(" v={:.3f}/{:.1f} w={:.3f}/{:.1f}", pk_drv.v, max_v, pk_drv.w, max_w); log.info(" a_v={:.3f}/{:.1f} a_w={:.3f}/{:.1f}", pk_drv.a_v, a_drv_v, pk_drv.a_w, a_drv_w); log.info(" j_v={:.3f}/{:.1f} j_w={:.3f}/{:.1f}", pk_drv.j_v, j_drv_v, pk_drv.j_w, j_drv_w); log.info(" cp={:.3f}/{:.2f}", pk_drv.cp, max_cp); log.info(" [stopping]"); log.info(" v={:.3f}/{:.1f} w={:.3f}/{:.1f}", pk_stp.v, max_v, pk_stp.w, max_w); log.info(" a_v={:.3f}/{:.1f} a_w={:.3f}/{:.1f}", pk_stp.a_v, a_stp_v, pk_stp.a_w, a_stp_w); log.info(" j_v={:.3f}/{:.1f} j_w={:.3f}/{:.1f}", pk_stp.j_v, j_stp_v, pk_stp.j_w, j_stp_w); log.info(" cp={:.3f}/{:.2f}", pk_stp.cp, max_cp); log.info((viol_drv == 0 && viol_stp == 0) ? " [PASS]" : " [FAIL]"); return viol_drv == 0 && viol_stp == 0; } }; // ---- Shared limits --------------------------------------------------- constexpr float MAX_V = 1.0f, MAX_W = 3.14159f; constexpr float A_DRV_V = 2.0f, A_DRV_W = 6.28f; constexpr float J_DRV_V = 10.0f, J_DRV_W = 25.0f; constexpr float A_STP_V = 5.0f, A_STP_W = 10.0f; constexpr float MAX_CP = 0.4f, DT = 0.020f; Validator val; val.max_v = MAX_V; val.max_w = MAX_W; val.a_drv_v = A_DRV_V; val.a_drv_w = A_DRV_W; val.j_drv_v = J_DRV_V; val.j_drv_w = J_DRV_W; val.a_stp_v = A_STP_V; val.a_stp_w = A_STP_W; val.j_stp_v = 0.0f; val.j_stp_w = 0.0f; // tests use trapez stop; set non-zero if needed val.max_cp = MAX_CP; val.dt = DT; val.log = &logger; const Cfg cfg{ .max_linear_velocity = MAX_V, .max_angular_velocity = MAX_W, .driving_profile = {.max_linear_acceleration = A_DRV_V, .max_angular_acceleration = A_DRV_W, .max_linear_jerk = J_DRV_V, .max_angular_jerk = J_DRV_W}, .stopping_profile = {.max_linear_acceleration = A_STP_V, .max_angular_acceleration = A_STP_W}, .enforce_motion_envelope = true, .max_centripetal_acceleration = MAX_CP, .output_callback = [&val](const Cmd &cmd) { val(cmd); }, .planning_period = std::chrono::milliseconds(50), .planning_task_config = {.name = "tp_val", .stack_size_bytes = 1024 * 10, .priority = 5}, .callback_task_config = {.name = "tp_val_cb", .stack_size_bytes = 8192, .priority = 5}, }; // ---- Test A: pure linear ramp ---------------------------------------- { logger.info("[A] Pure linear ramp target=(+1, 0)"); val.reset(); espp::TrajectoryPlanner planner(cfg); planner.set_target(1.0f, 0.0f); std::this_thread::sleep_for(800ms); val.report(logger, "A: Pure linear ramp"); } // ---- Test B: pure angular ramp ---------------------------------------- { logger.info("[B] Pure angular ramp target=(0, +1)"); val.reset(); espp::TrajectoryPlanner planner(cfg); planner.set_target(0.0f, 1.0f); std::this_thread::sleep_for(800ms); val.report(logger, "B: Pure angular ramp"); } // ---- Test C: combined motion - tests envelope + centripetal ---------- { logger.info("[C] Combined target=(0.9, 0.9) envelope + centripetal"); val.reset(); espp::TrajectoryPlanner planner(cfg); planner.set_target(0.9f, 0.9f); std::this_thread::sleep_for(1s); val.report(logger, "C: Combined (envelope + centripetal)"); } // ---- Test D: drive → controlled stop (stopping profile) --------------- { logger.info("[D] Drive then stop - tests stopping profile switch"); val.reset(); espp::TrajectoryPlanner planner(cfg); planner.set_target(1.0f, 0.0f); std::this_thread::sleep_for(600ms); val.mark_stopping(); planner.stop(); std::this_thread::sleep_for(400ms); val.report(logger, "D: Drive then stop"); } // ---- Test E: direction reversal --------------------------------------- { logger.info("[E] Direction reversal (+1, 0) → (-1, 0)"); val.reset(); espp::TrajectoryPlanner planner(cfg); planner.set_target(1.0f, 0.0f); std::this_thread::sleep_for(1000ms); planner.set_target(-1.0f, 0.0f); std::this_thread::sleep_for(2000ms); val.report(logger, "E: Direction reversal"); } // ---- Test F: multi-step sweep - various (linear, angular) pairs ------- { logger.info("[F] Multi-step sweep - varied inputs"); val.reset(); espp::TrajectoryPlanner planner(cfg); bool was_stopping = false; for (float lin : {0.3f, 0.6f, 1.0f, -0.5f, 0.0f}) { for (float ang : {0.0f, 0.5f, -0.5f}) { bool next_stopping = (lin == 0.0f && ang == 0.0f); if (next_stopping && !was_stopping) val.mark_stopping(); else if (!next_stopping && was_stopping) val.mark_driving(); was_stopping = next_stopping; planner.set_target(lin, ang); std::this_thread::sleep_for(250ms); } } val.mark_stopping(); planner.stop(); std::this_thread::sleep_for(300ms); val.report(logger, "F: Multi-step sweep"); }
Public Types
-
typedef std::function<void(const MotionCommand&)> output_callback_t
Callback invoked at the end of every update() call with the latest smoothed MotionCommand. Use to feed the output directly into a kinematics layer without polling output().
Note
The callback is called without holding the internal mutex, so it is safe to call set_target(), stop(), or output() from within it.
Public Functions
-
explicit TrajectoryPlanner(const Config &config)
Construct the planner with the given configuration. The periodic update task is started automatically.
- Parameters:
config – Configuration parameters.
-
~TrajectoryPlanner()
Destructor. Stops the periodic update task before destruction.
-
bool set_config(const Config &config, bool reset_state = true)
Update the planner configuration.
- Parameters:
config – New configuration parameters.
reset_state – If true (default), resets velocity/acceleration state to zero.
- Returns:
true if the configuration was successfully applied.
-
const Config &get_config() const
Get the current configuration.
- Returns:
Const reference to the active Config.
-
void set_target(float linear, float angular)
Set the desired chassis velocity target using normalized joystick inputs.
Both inputs are in the range [-1, +1] and are scaled internally:
Values outside [-1, +1] are clamped before scaling.v_target = linear * max_linear_velocity (linear >= 0) v_target = linear * max_reverse_linear_velocity (linear < 0; = max_linear_velocity when 0) w_target = angular * max_angular_velocity
- Parameters:
linear – Normalized linear velocity command [-1, +1]. +1 = full forward, -1 = full reverse.
angular – Normalized angular velocity command [-1, +1]. +1 = full left turn, -1 = full right turn.
-
std::pair<float, float> get_target() const
Get the current normalized velocity target.
Note
If enforce_motion_envelope is enabled the stored target may differ from the value passed to set_target() (it is projected onto the unit circle). A negative linear target is normalized by the reverse limit, so -1 is full reverse stick whatever the two limits are.
- Returns:
Pair of {linear, angular} in [-1, +1].
-
espp::TrajectoryPlanner::MotionCommand output() const
Get the current smoothed motion command.
- Returns:
MotionCommand containing the trajectory-limited (v_ref, w_ref).
-
void stop()
Command the planner to decelerate to a full stop.
Equivalent to set_target(0, 0). The planner ramps down respecting all configured limits rather than cutting output immediately.
-
void reset()
Reset velocity, acceleration state, and target to zero immediately.
The next call to output() will return (0, 0). Use after an emergency stop or before re-initialising with a new configuration.
-
bool is_running() const
Check whether the periodic update task is currently running.
- Returns:
true if the task is running.
-
bool start_task()
Start the periodic planning and callback timers.
The planning timer calls update() at Config::planning_period. The callback task fires output_callback whenever update() produces new output. Has no effect if the timers are already running.
- Returns:
true if both timers were started successfully.
-
bool stop_task()
Stop the periodic planning and callback timers (joins their tasks).
The planner can be started again with start_task(); the destructor stops them itself.
- Returns:
true if the task was stopped, false if it was not running.
-
inline const std::string &get_name() const
Get the name of the component
Note
This is the tag of the logger
- Returns:
A const reference to the name of the component
-
inline void set_log_tag(const std::string_view &tag)
Set the tag for the logger
- Parameters:
tag – The tag to use for the logger
-
inline espp::Logger::Verbosity get_log_level() const
Get the log level for the logger
See also
See also
- Returns:
The verbosity level of the logger
-
inline void set_log_level(espp::Logger::Verbosity level)
Set the log level for the logger
See also
See also
- Parameters:
level – The verbosity level to use for the logger
-
inline void set_log_verbosity(espp::Logger::Verbosity level)
Set the log verbosity for the logger
See also
See also
See also
Note
This is a convenience method that calls set_log_level
- Parameters:
level – The verbosity level to use for the logger
-
inline espp::Logger::Verbosity get_log_verbosity() const
Get the log verbosity for the logger
See also
See also
See also
Note
This is a convenience method that calls get_log_level
- Returns:
The verbosity level of the logger
-
inline void set_log_rate_limit(std::chrono::duration<float> rate_limit)
Set the rate limit for the logger
See also
Note
Only calls to the logger that have _rate_limit suffix will be rate limited
- Parameters:
rate_limit – The rate limit to use for the logger
-
struct Config
Configuration for the TrajectoryPlanner.
Public Members
-
float max_linear_velocity = 1.0f
Maximum forward linear velocity (m/s): the speed at full forward stick.
-
float max_reverse_linear_velocity = 0.0f
Maximum reverse linear velocity (m/s): the speed at full reverse stick. 0 = the same as max_linear_velocity (symmetric).
-
float max_angular_velocity = 3.14159f
Maximum angular velocity magnitude (rad/s).
-
espp::TrajectoryPlanner::MotionProfile driving_profile
Accel/jerk limits used when target != (0, 0).
-
espp::TrajectoryPlanner::MotionProfile stopping_profile
Accel/jerk limits used when target == (0, 0). Set jerk to 0 here for a clean trapezoidal stop with no overshoot. Higher acceleration than the driving profile gives faster, firmer braking.
-
bool enforce_motion_envelope = false
When true, enforces (v/vmax)^2+(w/wmax)^2<=1 on output to prevent infeasible combined commands.
-
float max_centripetal_acceleration = 0.1f
Maximum centripetal acceleration |v*w| (m/s^2). 0 disables the limit. Both v and w are scaled proportionally when the limit is exceeded.
-
espp::TrajectoryPlanner::output_callback_t output_callback = nullptr
Optional callback invoked after each update() with the latest MotionCommand output. Leave as nullptr to disable.
-
std::chrono::duration<float> planning_period = std::chrono::milliseconds(20)
Planner update rate (default 50 Hz). Recommended: 5-200 ms on microcontrollers.
- espp::Task::BaseConfig planning_task_config = {.name = "TP_planning",.stack_size_bytes = 4096,.priority = 0,.core_id = -1}
Underlying task config for the timer.
- espp::Task::BaseConfig callback_task_config = {.name = "TP_cb",.stack_size_bytes = 8192,.priority = 0,.core_id = -1}
Underlying task config for the callback timer.
-
float max_linear_velocity = 1.0f
-
struct MotionCommand
Chassis motion command produced by the planner.
-
struct MotionProfile
Acceleration and jerk limits for one phase of motion.
Set max_linear_jerk / max_angular_jerk to 0 for a trapezoidal (ramp) profile, or to a positive value for an S-curve profile. The choice is per axis: a profile may be jerk-limited on one axis and trapezoidal on the other.
Note
Using a trapezoidal stopping profile (jerk = 0) is recommended to avoid S-curve overshoot past zero when the planner decelerates from a jerk-limited driving phase. The hand-off between the profiles is continuous: the next step always resumes from the acceleration actually applied on the previous one, and an acceleration beyond the new profile’s limit is wound back at that profile’s jerk limit.
Public Members
-
float max_linear_acceleration = 0.0f
Linear acceleration limit (m/s^2).
-
float max_angular_acceleration = 0.0f
Angular acceleration limit (rad/s^2).
-
float max_linear_jerk = 0.0f
Linear jerk limit (m/s^3). 0 = trapezoidal.
-
float max_angular_jerk = 0.0f
Angular jerk limit (rad/s^3). 0 = trapezoidal.
-
float max_linear_acceleration = 0.0f