Post

Velocity Planner: Layering a Speed Profile from Curvature

Velocity Planner: Layering a Speed Profile from Curvature

Once path optimization is done, “where to drive” is decided — but “how fast to drive” at each point is not yet. The Velocity Planner is the algorithm that layers a target speed profile (vx, ax) onto this fixed path (waypoints) based on curvature. Since it leaves the shape of the path untouched and decides only the speed, it is an independent stage separate from path optimization.

This post walks through how speed is derived from curvature, the empirically tuned friction limits (GGV), and the Forward–Backward computation that accounts for both acceleration and braking — covering UNICORN Racing’s velocity planning process.

What Needs to Be Solved

The UNICORN Racing team runs multiple planners together to respond robustly to diverse driving situations, and a common Velocity Planner layers speed onto every planner’s path.

Goal: driving without overshoot or unintended slip

If the target speed is set above a corner’s physical limit, the car leaves the line or the tires lose grip — so the speed profile must be decided within the dynamic constraints the vehicle can actually sustain.

Problem: the dynamic constraints are hard to know precisely

  • Building a precise vehicle dynamics model covering tire–road friction, suspension, and drivetrain response is not realistic.
  • The values also change with road surface and tire condition.

Solution: empirically tuned friction limits

Instead of deriving a precise model, the friction limits the tires can sustain are tuned empirically from real driving data. The observed limits are kept in a table, and given only the curvature, a safe speed is computed by referencing that table.

Core idea — Instead of estimating an accurate dynamics model, the tires’ longitudinal/lateral acceleration limits (GGV) are tuned empirically from driving data. Since speed is determined only by these limits and the path’s curvature, it is not tied to any specific point or segment and applies identically to any path.

How Speed Is Derived from Curvature

The Curvature–Speed Relationship

Speed drops in high-curvature (tight) corners and rises in low-curvature (gentle) segments. The lateral acceleration needed to take a corner must not exceed the tires’ lateral limit, so each point’s curvature $\kappa$ and the lateral limit $a_{y,max}$ define a cornering speed ceiling.

\[v_{max}(i) = \sqrt{\frac{a_{y,max}}{|\kappa(i)|}}\]

This is a theoretical ceiling considering only the cornering limit; refining it into an actually drivable profile by adding acceleration and braking capability comes next.

GGV: Empirically Tuned Friction Limits

GGV diagram of longitudinal/lateral acceleration limits per speed GGV diagram of longitudinal/lateral acceleration limits per speed (an illustrative example, different from the actual settings)

The combination of longitudinal (accel/brake) and lateral (cornering) acceleration a tire can produce is confined within one friction limit. The table organizing this limit per speed is the GGV (ggv.csv); each row holds a speed with its longitudinal limit $a_{x,max}$ and lateral limit $a_{y,max}$. These values are not derived from a precise model — they are tuned empirically, watching driving data and adjusting up to the point where no slip or overshoot appears.

GGV is a table structure that can hold per-speed limits, but the UNICORN Racing team tunes under the assumption that the tires perform identically at all speeds. That is, every row of ggv.csv carries the same $a_{x,max}$/$a_{y,max}$ values (currently 5.0/4.5 m/s²), and tuning adjusts this single limit rather than fitting a per-speed curve.

How much of a combined longitudinal/lateral acceleration is allowed is expressed with the Generalized Friction Circle model.

\[\left(\frac{a_x}{a_{x,max}}\right)^p + \left(\frac{a_y}{a_{y,max}}\right)^p \leq 1\]

From this relationship, when a corner is already consuming lateral acceleration $a_{y,used}=v^2/r$, the remaining longitudinal acceleration shrinks as follows.

\[a_{x,avail} = a_{x,max} \left(1 - \left(\frac{a_{y,used}}{a_{y,max}}\right)^p\right)^{1/p}\]

On straights there is no lateral acceleration, so full longitudinal acceleration is available; as the car turns into a corner and steering grows, the available longitudinal acceleration shrinks. The exponent $p$ (dyn_model_exp) shapes this friction limit — the smaller it is (closer to 1.0), the more conservatively acceleration is limited mid-corner. The UNICORN Racing Stack currently keeps dyn_model_exp: 1.0 as the default and varies it slightly with road conditions.

The Two-Direction Forward–Backward Computation

Forward pass, backward pass, and the final velocity profile Forward pass, backward pass, and the final velocity profile taken as the minimum of the two

The cornering ceiling alone is not enough: the car must brake ahead of the next corner, and after exiting a corner it can only build speed as fast as its acceleration allows. To reflect these accel/brake constraints, the path is swept in two directions.

  • Forward pass: proceeding from the start, it propagates forward the maximum speed achievable with the longitudinal acceleration available at each point.
  • Backward pass: moving backwards from the end, it propagates backward the speed from which the car can safely brake down to the next point’s target.

In both directions the speed update follows the constant-acceleration relation $v_{i+1}=\sqrt{v_i^2 + 2\,a_x\,\Delta s}$, and each point’s final speed is the smaller of the two results.

\[v_{final}(i) = \min(v_{forward}(i),\, v_{backward}(i))\]

After the Forward–Backward pass, the cornering, acceleration, and braking limits are all satisfied within one profile. The available longitudinal acceleration incorporates not only tire friction but also motor/brake capability (ax_max_machines, b_ax_max_machines) — values that, like the GGV, are empirically tuned limits.

Why This Approach Is a Strength

The heart of this pipeline is that speed depends only on the path’s curvature and the tuned friction limits. Several practical benefits follow.

A globally reusable speed setting — Since speed is a function of curvature, the values are not fitted to a specific track or segment: given curvature, the same setting layers onto any path. One tuned GGV delivers uniform speed performance across many paths.

The same limits reapply when the path changes. Whether it is the global raceline optimized for minimum curvature (IQP) or shortest path (SP), or a path generated by another planner in avoidance/overtaking situations — as long as curvature is available, the same Velocity Planner applies as-is. No per-path speed rules need defining, so driving characteristics stay consistent across the stack.

Speeds can be updated quickly at competitions. With scarce practice time, fine-tuning speed per path is impractical — and conditions change constantly: tires get swapped, maps get rebuilt, air-conditioning makes the track floor slippery. In each such situation, adjusting just a few GGV and motor/brake limit values recomputes the speed profile for the entire course at once. In fact, global_velocity_planner.py subscribes to /global_waypoints, recomputes speeds from curvature, and republishes — so limits tuned on-site propagate to the whole stack immediately without rerunning path optimization from scratch. This allows efficient yet consistent responses to varied field conditions.

Where It Is Used

Velocity Planner

Related code: race_utils/f110_utils/libs/vel_planner/vel_planner/vel_planner.py

It takes the path’s curvature (kappa) and element lengths together with the friction limits (GGV), motor limits (ax_max_machines), and brake limits (b_ax_max_machines) as arrays, computes the velocity profile with a forward–backward pass, and returns it.

Default inputs: stack_master/config/<racecar_version>/veh_dyn_info/

CSVContentFormat
ggv.csvfriction limitsper-speed ax_max, ay_max
ax_max_machines.csvmotor (drive) limitsper-speed ax_max_machines
b_ax_max_machines.csvbrake limitsper-speed ax_max_machines

The UNICORN Racing team tunes so the same limit values are used at all speeds.

How It Is Consumed

  • planner/gb_optimizer/
    Calls calc_vel_profile with the default input CSVs and the optimized path’s curvature to assign speed to the raceline, publishing /global_waypoints with vx_mps included.
  • state_machine/state_machine/state_machine_node.py
    Calls calc_vel_profile to assign speed to the planner’s output path from the default input CSVs and curvature, adjusting longitudinal accel/brake margins with a safety factor.
  • stack_master/scripts/global_velocity_planner.py
    Calls calc_vel_profile with the input values tuned inside the node, recomputing and republishing speeds from the curvature of /global_waypoints.

global_velocity_planner.py overrides the limits with hardcoded values inside the node instead of the CSVs, allowing real-time tuning without editing the CSVs directly.

1
2
3
4
5
6
7
# ---- tune here: hardcoded limits override the ini/csv values ----
self.v_max = 12.0
self.ax_max_motor = 7.0
self.ax_max_brake = 8.0
self.dyn_model_exp = 1.0
self.a_y_max = 6.5
self.a_x_max = 9.0

Adjusting these six values — v_max, ax_max_motor, ax_max_brake, dyn_model_exp, a_y_max, a_x_max — makes the node recompute and publish the velocity profile from the curvature of /global_waypoints, so results can be checked immediately without re-optimization. Running with save_csv:=true saves the tuned limits to the three CSVs in veh_dyn_info/, so they also become the defaults other nodes reference afterwards.

Tuning with global_velocity_planner.py while driving

Parameter Tuning

The meaning of each parameter and how to tune it are explained in detail in the document below.

Reference: Velocity Planner (UNICORN Racing docs)

Wrap-up

The Velocity Planner is the stage that layers a speed profile onto a fixed path using only curvature and empirically tuned friction limits (GGV). Setting the cornering ceiling from curvature and folding in the accel/brake limits with a Forward–Backward pass yields a consistent speed setting that applies identically to any path. In the field, global_velocity_planner.py lets you refresh the speeds of the entire course instantly by adjusting just a few limit values — and this tuning is the key work that determines driving performance.

This post is licensed under CC BY 4.0 by the author.