Odyssey represents every path segment as a cubic Bézier curve — a smooth, C1-continuous polynomial that connects two field positions while letting you shape the curve through a pair of interior control-point handles. Because the curve is defined by a compact closed-form expression, evaluating a point on it is fast enough for the tight control loops FTC robots require, yet expressive enough to describe sweeping arcs, tight turns, and gentle S-bends with a single object.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.
The four control points
A cubic Bézier is fully described by fourVector2d control points:
| Point | Role |
|---|---|
p0 | Start of the curve — the robot’s position when it enters this segment |
p1 | First handle — pulls the curve away from p0 in a specific direction |
p2 | Second handle — pulls the curve into p3 from a specific direction |
p3 | End of the curve — the robot’s position when it leaves this segment |
p1 away from p0 along some direction sets the exit tangent of the curve; moving p2 away from p3 along some direction sets the entry tangent into the endpoint. That symmetry is what makes it straightforward to create smooth seams between consecutive segments.
The cubic formula evaluated by getPoint(double t) is:
Constructing a BezierCurve
Pass the four control points and aHeadingInterpolator (which governs robot orientation — see Heading Interpolation):
All coordinates are in millimeters to match the FTC field coordinate system (the full field is approximately 3 658 mm × 3 658 mm). Using millimeters keeps curvature and velocity units consistent throughout the library.
Why arc-length parameterization matters
The raw parametert does not correspond to equal distances along the curve. Near tight bends, equal steps in t produce small spatial steps; near straight sections the same t step covers much more distance. Driving the robot at constant increments of t would therefore cause uneven, jerky motion.
Odyssey solves this in two stages:
Compute arc length with Gauss–Legendre integration
getLength(double b) integrates the magnitude of the tangent vector from 0 to b using Apache Commons Math’s IterativeLegendreGaussIntegrator (5-point rule, absolute and relative tolerances of 1e-9, up to 1 000 evaluations):Invert with a Brent solver
getTAtDistance(double dist) answers the inverse question: which t value corresponds to a given arc-length distance? It sets up the root-finding problem getLength(b) − dist = 0 and solves it with a BrentSolver (absolute and relative tolerances of 1e-9, up to 1 000 iterations):t directly.Closest-T projection
The Follower needs to know where on the curve the robot currently is.getClosestT(Vector2d pose) finds the parameter t whose point on the curve is nearest to the robot’s measured position.
It runs a two-phase search:
Coarse scan (step = 0.01)
Sweeps
t from 0 to 1 in steps of 0.01, evaluating the squared Euclidean distance at each step and recording the best t.getClosestT(Vector2d pose, double coarseStep, double fineStep) exposes both step sizes if you need to tune the trade-off between speed and accuracy.
Curvature for centripetal limiting
getCurvature(double t) computes the signed curvature κ at parameter t using the standard cross-product formula:
B′ is the first derivative (tangent vector, from getTangentVector) and B″ is the second derivative (from getSecondDerivative). The VelocityProfile queries this value at every discretized distance step to cap robot speed at the centripetal limit sqrt(maxCentripetalAccel / κ), preventing the robot from sliding outward on tight bends.
