Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/saquer916/odyssey/llms.txt

Use this file to discover all available pages before exploring further.

A naive autonomous program that runs the robot at full throttle until it nears the end of the path will overshoot: the drivetrain cannot decelerate fast enough to stop at the target. A trapezoidal velocity profile solves this by pre-computing a speed schedule — accelerate from rest, cruise at maximum speed when possible, then brake smoothly to a stop — all while respecting how fast the robot can accelerate and decelerate given its drivetrain characteristics. Odyssey’s VelocityProfile extends the classic trapezoid with a third constraint: centripetal acceleration. On tight curves, even a modest speed can demand more lateral force than the wheels can generate, causing the robot to slide off the path. The profile caps speed at every point where curvature is high enough to matter, then reapplies the forward and backward passes so the trapezoid remains physically achievable around that speed dip.

Three-pass algorithm

VelocityProfile discretizes the path into samples separated by step millimetres and populates a velocities[] array in three sequential passes.
1

Pass 1 — Curvature cap

For every sample index i, query the path curvature κ at distances[i] and compute the maximum speed that keeps centripetal acceleration within maxCentripetalAccel:
curveLimit = sqrt(maxCentripetalAccel / κ)
velocities[i] = min(maxVelocity, curveLimit)
When κ is below 1e-4 (effectively a straight line), curveLimit is set to maxVelocity so the centripetal constraint has no effect on near-straight segments.
2

Pass 2 — Forward acceleration propagation

Starting from velocities[0] = 0 (robot starts from rest), sweep forward and ensure no sample is faster than the robot could reach from the previous sample under maxAcceleration:
velocities[i] = min(velocities[i],  sqrt(velocities[i-1]² + 2·maxAcceleration·ds))
3

Pass 3 — Backward braking propagation

Starting from velocities[last] = 0 (robot stops at the end), sweep backward and ensure no sample is faster than the robot could bleed off to reach the next sample under maxBrake:
velocities[i] = min(velocities[i],  sqrt(velocities[i+1]² + 2·maxBrake·ds))
The result is a velocity array where every value simultaneously satisfies the acceleration budget (forward pass), the braking budget (backward pass), and the centripetal budget (curvature pass).

Constructor

VelocityProfile profile = new VelocityProfile(
    path,
    1200,   // maxVelocity        mm/s  — absolute speed ceiling
    800,    // maxAcceleration    mm/s² — peak forward acceleration
    800,    // maxBrake           mm/s² — peak deceleration
    600,    // maxCentripetalAccel mm/s² — lateral acceleration limit
    5.0     // step               mm    — spatial resolution of the profile
);
ParameterTypeDescription
pathPathThe path to profile — its total length and curvature are queried during construction
maxVelocitydoubleHard speed ceiling in mm/s
maxAccelerationdoubleMaximum forward acceleration in mm/s²
maxBrakedoubleMaximum braking deceleration in mm/s²
maxCentripetalAcceldoubleMaximum centripetal (lateral) acceleration in mm/s²
stepdoubleDistance between profile samples in mm
The step parameter controls a resolution–performance trade-off. A smaller step (e.g. 1.0 mm) samples curvature more densely, catching narrow tight bends that a coarser grid might underestimate — but it increases both the construction time and the size of the velocities[] and distances[] arrays. A value of 5.0 mm works well for most FTC paths. Only go below 2.0 mm if your path has very sharp inflection points that noticeably affect robot tracking.

Querying the profile at runtime

getTargetVelocity(double distance)

Returns the target speed in mm/s at the given arc-length distance from path start. Rather than returning the raw sampled value, Odyssey performs v²-interpolation (quadratic interpolation on squared velocity) between the two surrounding samples for a smooth output:
res = v1² + (distance − d1) · (v2² − v1²) / (d2 − d1)
targetVelocity = sqrt(res)
This preserves the kinematic consistency of the trapezoidal profile — interpolating directly on v would underestimate speed in the middle of an acceleration phase, while interpolating on matches the physics exactly.
// Query the profile at 1 500 mm into the path
double targetSpeed = profile.getTargetVelocity(1500); // mm/s

getTargetTangentialAcceleration(double distance)

Returns the tangential (along-path) acceleration in mm/s² at the given distance, computed directly from the stored sample pair surrounding that distance:
tangentialAccel = (vf² − v0²) / (2 · ds)
This value is positive during the acceleration phase, negative during braking, and near zero during cruise. Feed it to your feedforward controller to reduce tracking error during speed changes:
double ffAccel = profile.getTargetTangentialAcceleration(distanceAlongPath);

Build docs developers (and LLMs) love