@@ -236,30 +236,7 @@ void shape_vel_accel_xy(const Vector2f& vel_desired, const Vector2f& accel_desir
236236 // acceleration to correct velocity
237237 Vector2f accel_target = sqrt_controller (vel_error, KPa, jerk_max, dt);
238238
239- // limit correction acceleration to accel_max
240- if (vel_desired.is_zero ()) {
241- accel_target.limit_length (accel_max);
242- } else {
243- // calculate acceleration in the direction of and perpendicular to the velocity input
244- const Vector2f vel_desired_unit = vel_desired.normalized ();
245- float accel_dir = vel_desired_unit * accel_target;
246- Vector2f accel_cross = accel_target - (vel_desired_unit * accel_dir);
247-
248- // Ensure sufficient acceleration is reserved for cross-track correction.
249- // If forward component dominates, limit lateral component to stay within total magnitude.
250- if (sq (accel_dir) <= accel_cross.length_squared ()) {
251- // accel_target can be simply limited in magnitude
252- accel_target.limit_length (accel_max);
253- } else {
254- // limiting the length of the vector will reduce the lateral acceleration below 1/sqrt(2)
255- // limit the lateral acceleration to 1/sqrt(2) and retain as much of the remaining
256- // acceleration as possible.
257- accel_cross.limit_length (CORNER_ACCELERATION_RATIO * accel_max);
258- float accel_max_dir = safe_sqrt (sq (accel_max) - accel_cross.length_squared ());
259- accel_dir = constrain_float (accel_dir, -accel_max_dir, accel_max_dir);
260- accel_target = accel_cross + vel_desired_unit * accel_dir;
261- }
262- }
239+ limit_accel_corner_xy (vel_desired, accel_target, accel_max);
263240
264241 accel_target += accel_desired;
265242
@@ -460,6 +437,46 @@ bool limit_accel_xy(const Vector2f& vel, Vector2f& accel, float accel_max)
460437 return false ;
461438}
462439
440+ // Limits acceleration magnitude while preserving cross-track authority during turns.
441+ // - Splits acceleration into components parallel and perpendicular to velocity.
442+ // - Ensures sufficient lateral acceleration is reserved for path curvature.
443+ // - If forward acceleration dominates, lateral acceleration is limited to
444+ // CORNER_ACCELERATION_RATIO * accel_max.
445+ // Returns true if limiting was applied. (including simple magnitude limiting when vel is zero).
446+ bool limit_accel_corner_xy (const Vector2f& vel, Vector2f& accel, float accel_max)
447+ {
448+ // check accel_max is defined
449+ if (!is_positive (accel_max)) {
450+ return false ;
451+ }
452+ if (vel.is_zero ()) {
453+ // No along/cross decomposition possible; apply a simple magnitude limit.
454+ return accel.limit_length (accel_max);
455+ }
456+
457+ // Unit velocity direction defines the along-track axis.
458+ const Vector2f vel_unit = vel.normalized ();
459+
460+ // Decompose accel into along-track and cross-track components.
461+ float accel_dir = vel_unit * accel;
462+ Vector2f accel_cross = accel - (vel_unit * accel_dir);
463+
464+ // If cross-track magnitude is at least as large as along-track, a simple vector limit
465+ // preserves at least 1/sqrt(2) of accel_max for cross-track correction.
466+ if (sq (accel_dir) <= accel_cross.length_squared ()) {
467+ // accel can be simply limited in magnitude
468+ return accel.limit_length (accel_max);
469+ }
470+
471+ // Along-track dominates: preserve cross-track authority by limiting cross-track first,
472+ // then allocate remaining magnitude to along-track.
473+ accel_cross.limit_length (CORNER_ACCELERATION_RATIO * accel_max);
474+ float accel_max_dir = safe_sqrt (sq (accel_max) - accel_cross.length_squared ());
475+ accel_dir = constrain_float (accel_dir, -accel_max_dir, accel_max_dir);
476+ accel = accel_cross + vel_unit * accel_dir;
477+ return true ;
478+ }
479+
463480// Piecewise square-root + linear controller that limits second-order response (acceleration).
464481// - Behaves like a P controller near the setpoint.
465482// - Switches to sqrt(2·a·Δx) shaping beyond a threshold to limit acceleration.
0 commit comments