This reading develops the cornering and braking models introduced in Chapter 3. Start with the pictures and the interpretations. The calculus section is optional; you can use its result without following every derivation step.
We are solving a model, not finding the universally shortest braking distance in TORCS. Assume straight-line braking on a level surface, constant effective grip and mass over the manoeuvre, and aerodynamic forces proportional to speed squared. The tyres must retain positive normal load; with lift, require \(mg+k_Lv^2>0\) over the interval. Real tyre behaviour, load transfer, banking, and actuator response still matter when driving.
Drag acts against motion and directly helps remove kinetic energy. Downforce adds normal load at the tyres; in our simple friction model that increases the force they can transmit. It is not another backward force.
Wings can produce a force toward the ground. Flow beneath the car can also produce downforce, often called ground effect. Ride height changes that underbody flow, so the same car's aerodynamic behaviour can change as its suspension moves.
We collect the aerodynamic properties into two coefficients:
\[ F_D=k_Dv^2,\qquad F_L=k_Lv^2. \]
Positive \(k_L\) means downforce; negative \(k_L\) means lift.
| Symbol | Meaning | Unit |
|---|---|---|
| \(k_D\) | Drag-force coefficient including density and area | kg/m |
| \(k_L\) | Downforce coefficient including the chosen aerodynamic state | kg/m |
| \(m\) | Current mass used by the model | kg |
| \(\mu\) | Effective tyre/surface grip estimate | Dimensionless |
These \(k\) values are not the dimensionless textbook coefficients \(C_D\) and \(C_L\). For the familiar body-drag model, \(k_D=\tfrac12\rho C_D A\): dynamic pressure \(\tfrac12\rho v^2\), area, and shape together determine the force. Here \(\rho\) is air density in kg/m³, \(A\) is reference area in m², and \(C_D\) is the dimensionless drag coefficient.
The normal load is now approximated by \(mg+k_Lv^2\). Repeating the inward-force balance from Chapter 3 gives:
\[\begin{aligned} \frac{mv^2}{R} &\leq \mu(mg+k_Lv^2),\\ v^2\left(1-\frac{\mu k_LR}{m}\right) &\leq \mu gR. \end{aligned} \]
For a positive denominator:
\[ v_{\mathrm{corner}}= \sqrt{\frac{\mu gR}{1-\mu k_LR/m}}. \]
Unlike the no-aerodynamics case, mass no longer cancels out.
With downforce, the estimate increases. With lift, it decreases. If \(k_L>0\) and \(R\geq m/(\mu k_L)\), the denominator is zero or negative. The force inequality then supplies no finite speed bound under this model. It does not prove a real car can take the bend at any speed.
The implementation returns the gearing/redline search bound in that case, rather than dividing by zero or taking a square root of a negative number. For negative \(k_L\), it also keeps the cap below the model's loss-of-normal-load speed \(\sqrt{mg/(-k_L)}\). The ordinary corner margin still applies.
The tyres can provide a braking force of approximately \(\mu(mg+k_Lv^2)\), and drag contributes \(k_Dv^2\). The total opposing force is:
\[ F_{\mathrm{brake}}(v)=\mu mg+(\mu k_L+k_D)v^2. \]
The energy difference is equal to the work over braking distance \(d\):
\[\frac{m}{2}(v_{\mathrm{in}}^2-v_{\mathrm{out}}^2) =\mu mgd+(\mu k_L+k_D)\int_0^d v(s)^2\,ds. \]
The integral is necessary because speed changes along the braking path. Using the starting speed throughout would overestimate the aerodynamic contribution.
For compact notation, define:
\[ a_0=\mu g,\qquad b=\frac{\mu k_L+k_D}{m}. \]
Then the deceleration magnitude is \(a(v)=a_0+bv^2\). The units are m/s² for \(a_0\) and 1/m for \(b\). We assume \(a_0>0\) and require positive deceleration throughout the speed interval. The usual downforce-and-drag case has \(b\geq0\).
For \(b\geq0\), deceleration is weakest at the final speed. Pretend that this weaker value applies throughout the braking manoeuvre:
\[ d_{\mathrm{bound}}= \frac{v_{\mathrm{in}}^2-v_{\mathrm{out}}^2} {2(a_0+bv_{\mathrm{out}}^2)}. \]
In the energy picture, this approximation replaces the area under \(v(s)^2\) by the smaller rectangle \(v_{\mathrm{out}}^2d\):
Underestimating the aerodynamic braking work produces a longer estimated distance. It is an upper bound within this model, not a guarantee about the complete simulator. If a signed coefficient gives \(b<0\), the weaker endpoint is instead the initial speed; the code uses the weaker of both endpoints.
Select BrakingModel::AeroBound in speed.cpp to try this model.
A single rectangle can be crude over a large speed change. Divide the interval into smaller pieces and add their braking distances.
The code uses \(u=v^2\), with equally spaced boundaries \(u_0,\ldots,u_N\) from \(v_{\mathrm{out}}^2\) to \(v_{\mathrm{in}}^2\). Each piece contributes:
\[\Delta d_i= \frac{u_{i+1}-u_i}{2\min(a_0+bu_i,\ a_0+bu_{i+1})}, \qquad d_N=\sum_{i=0}^{N-1}\Delta d_i. \]
This keeps the conservative endpoint idea, but follows the changing deceleration more closely. With zero aerodynamic contribution, every subdivision gives the same constant-deceleration result.
Select BrakingModel::AeroNumerical, then try NUMERICAL_INTERVALS values of 1, 4, 8, and 32 in braking.cpp. More intervals reduce approximation error but cost more computation. No single interval count is equally accurate for every speed range and coefficient choice. Simpson or Romberg integration is another possible numerical approach; it does not automatically retain this upper-bound property.
This is the complete analytical route. Only this section needs differential and integral calculus. Our initial condition is \(v(0)=v_{\mathrm{in}}\).
From the force balance:
\[ \frac{dv}{dt}=-(a_0+bv^2). \]
Since \(ds/dt=v\), the chain rule gives:
\[ v\frac{dv}{ds}=-(a_0+bv^2). \]
Equivalently, differentiating the work-energy equation gives \(d(\tfrac12mv^2)/ds=-m(a_0+bv^2)\). These are the same model.
Let \(u=v^2\). Then \(du/ds=2v\,dv/ds\), so:
\[ \frac{du}{ds}=-2(a_0+bu). \]
For \(b\ne0\), separate the variables:
\[\begin{aligned} \int_{v_{\mathrm{in}}^2}^{u(s)}\frac{du}{a_0+bu}&=-2\int_0^s ds,\\ \frac{1}{b}\ln\left(\frac{a_0+bu(s)}{a_0+bv_{\mathrm{in}}^2}\right)&=-2s. \end{aligned} \]
Rearranging gives the squared-speed profile:
\[ v(s)^2= \left(v_{\mathrm{in}}^2+\frac{a_0}{b}\right)e^{-2bs}-\frac{a_0}{b}. \]
Use the nonnegative square root for forward speed. This profile describes the braking motion only until the car stops; extending it beyond that point would eventually produce a negative squared speed.
Set \(v(d)=v_{\mathrm{out}}\) and solve for \(d\):
\[ \boxed{ d=\frac{1}{2b}\ln\left( \frac{a_0+bv_{\mathrm{in}}^2}{a_0+bv_{\mathrm{out}}^2} \right)}. \]
For \(v_{\mathrm{in}}\geq v_{\mathrm{out}}\geq0\) and positive deceleration throughout, the distance is nonnegative. The expression also works for a negative \(b\) while that domain condition holds. The code refuses a finite distance if deceleration reaches zero or becomes negative in the interval. The logarithm's argument is dimensionless, and \(1/(2b)\) supplies metres.
When \(b=0\), the differential equation becomes \(du/ds=-2a_0\). Therefore:
\[ v(s)^2=v_{\mathrm{in}}^2-2a_0s, \qquad d=\frac{v_{\mathrm{in}}^2-v_{\mathrm{out}}^2}{2a_0}. \]
This is exactly the main chapter's constant-deceleration model. The logarithmic formula approaches this result as \(b\to0\).
Directly subtracting nearly equal squared speeds and then taking a logarithm of a number nearly equal to one loses precision. The implementation in braking.cpp uses the equivalent expressions:
\[\begin{aligned} \Delta u &=(v_{\mathrm{in}}-v_{\mathrm{out}})(v_{\mathrm{in}}+v_{\mathrm{out}}),\\ z&=\frac{b\Delta u}{a_0+bv_{\mathrm{out}}^2},\\ d&=\frac{\operatorname{log1p}(z)}{2b}. \end{aligned} \]
std::log1p(z) computes \(\ln(1+z)\) accurately for small \(z\). The code handles \(b=0\) explicitly and uses the small- \(z\) expansion \(\ln(1+z)/z=1-z/2+O(z^2)\) for very small terms. Equal speeds return zero.
Select BrakingModel::AeroAnalytical to use this implementation. For all optional models, the planner searches for the largest allowed approach speed whose calculated braking distance fits the available distance. That bounded search also works with the numerical approximation.
For \(b\geq0\), the no-aero estimate is longest, the one-interval aero estimate is shorter, and the analytical distance is shorter still. Subdivision approaches the analytical curve. At high speeds the opposing aerodynamic force grows with \(v^2\) too, so the distance no longer follows the simple quadratic growth of the no-aero model. This explains why the aerodynamic braking curves in the graph grow more slowly at high speeds.
The graph uses the printed illustrative coefficients. It is not a measurement of a particular TORCS car, and an exact model calculation does not promise a better lap time on every run.
For the same illustrative coefficients, slowing from 30 m/s to 15 m/s gives:
| Calculation | Distance |
|---|---|
| No aerodynamic contribution | 56.25 m |
| One-interval aerodynamic bound | 52.33 m |
| Eight conservative intervals | 48.08 m |
| Analytical aerodynamic solution | 47.51 m |
This connects the worked example in the main chapter with the successive improvements. The difference between the last two rows is approximation error within the same model, not a difference between two measured laps.
The full distance depends on both speeds. The contour map below shows that two-variable dependence on a flat, labelled grid:
Pick an initial speed on the horizontal axis and a final speed on the vertical axis. The labelled contours show the model distance in metres. At or above the equal-speed diagonal, slowing down is unnecessary; that does not mean changing to a higher speed takes no time or distance.
SpeedDriver::configure reads the current car parameter handle once per session. The helper follows the present force-model conventions, using both wings:
\[\begin{aligned} H&=1.5\sum_{i=1}^{4}h_i, & f_h&=2e^{-3H^4},\\ q_j&=1.23 A_j\sin\alpha_j,\\ k_L&=f_h(c_{\mathrm{front}}+c_{\mathrm{rear}})+4\sum_j q_j,\\ k_D&=0.645 C_x A_{\mathrm{front}}+\sum_j q_j. \end{aligned} \]
We freeze ride height and wing state at the configured values. During driving, pitch, suspension motion, airflow direction, damage, and other cars can change the actual forces. The current mass is updated as dryMass + car->_fuel, matching the mass term used by the simulator rather than introducing a second fuel-density conversion. As fuel is used, the same aerodynamic force acts on less mass, changing the cornering and braking estimates.
The driver additionally uses planning reserves. Its effective grip combines the lowest configured tyre coefficient with the relevant surface coefficient and GRIP_RESERVE. For braking, BRAKE_USAGE scales both \(a_0\) and \(b\); for cornering, CORNER_MARGIN reduces the model speed. The distance margins remain active even with the analytical model.
Aerodynamic cornering is enabled in the supplied speed.cpp. Set AERO_CORNERS = false to compare the gravity-only model, then restore true. Compare one change at a time, then restore the defaults if the simple steering cannot follow the more ambitious targets.
The derivation does not fully describe front/rear load transfer, brake balance, load-sensitive tyre grip, tyre temperature and wear, changing ride height, wheel rotational energy, road gradient, or combined braking and cornering. Some forces also depend on airflow and vehicle orientation rather than forward speed alone. Those are reasons to retain margins and observe the car.
The analytical result is exact within its assumptions. Numerical agreement checks the mathematics; driving checks whether the approximation is useful.
Return to Chapter 3, or visit Questions and feedback.