Skip to content
LIBRARY
Examples.ACADOSMPCRaceCarSpatialExample.md

Examples.ACADOSMPCRaceCarSpatialExample ​

The lap of ACADOSMPCRaceCarExample with the arc length as the independent variable throughout: RaceCarSpatialModel is both the plant and the prediction model, the simulation runs over the distance along the centerline, the controller ticks every ds metres and its shooting intervals are ds long, and the elapsed time tlap is a state, as in the dymos formulation of the problem. The tyre constraint is the friction ellipse of dymos itself rather than the octagon inscribed in it. The receding-horizon loop is used here as a way of solving the lap-time problem, not as a real-time controller: nothing is applied at a fixed rate in time, so nothing needs the time domain.

Loop ​

The independent variable of the simulation is the arc length, so the solution is a function of s and the lap time is tlap at  . The applied control (the three rates) is held over exactly the first shooting interval, since the clock ticks in space with the same ds, and the plant integrates the same model the controller predicts with: the loop has no discretization mismatch, and the constraints hold to numerical precision at the nodes. In the time-domain example the control is held for a clock period while the prediction assumes a shooting interval, and the tyre constraints are exceeded by a fraction of a percent at the ticks.

Cost ​

The lap time is the elapsed time at the end of the horizon, so the cost is the plain least-squares term Missing open brace for subscript on a state that starts at zero at every tick: no unreachable reference is needed, since minimizing the square of a nonnegative quantity minimizes the quantity. The same small weight q_n on the lateral offset as in the time-domain example resolves the flat direction on the straights, and the control rates are weighted by Q2.

Constraints ​

The friction ellipse Missing open brace for subscript of each tyre and the normalized power are given in the convex-over-nonlinear form of ACADOSMPC (nl_convex_inner, the normalized forces sx_fl, sy_fl, ... and power_norm; nl_convex_outer, the squared norm of each pair and the identity on the power). acados adds the curvature of the outer function to the Hessian of the QP, so the linearization of the ellipse, which carries no information about the lateral force at small slip angles and lets a plain nonlinear constraint be exceeded by a factor of 3 to 4, is completed by its second derivative and the tyres stay within a few percent of the limit. The constraints are hard from the first predicted state on, as in the time-domain example; the track and the actuator bounds are soft state bounds.

Horizon ​

The horizon is Np intervals of ds metres, a fixed distance ahead of the car whatever its speed, whereas the time-domain horizon shortens in the corners. With the control held over a whole interval, intervals of 10 m (or 7.5 m) are too coarse: the loop leaves the track at the entry of the first corner. At 5 m the lap is 22.49 s and at 2.5 m 22.48 s, so the grid is converged at 5 m, which the example uses with 50 intervals, a horizon of 250 m. A horizon of 200 m costs 0.16 s of lap time and one of 150 m 0.65 s: the horizon has to see around the next corner.

Solver ​

Dividing every rate by    makes the model singular as the speed approaches zero and strongly nonlinear at low speed, which the time-domain model is not: the throttle, brake and steering change per interval grow with 1/V, and a step of the sequential quadratic programming that brakes hard in the prediction lands where the linearization is poor. With the octagon as the tyre constraint the iterations diverged unless the speed had a floor of 25 m/s and the damping was raised to 10; with the ellipse in the convex form the curvature of the constraint stabilizes the steps, and the floor and the damping of the time-domain example suffice. The tolerance is 0.1, since the gradients of the lap-time cost are smaller than those of the progress-rate cost. Three iterations per tick give the same lap as thirty: 22.49 s from 65 m/s against 22.9 s for the time-domain example with the octagon (the ellipse gives the tyres the full combined adherence) and 22.27 s for the periodic dymos lap. The 1.1 laps take about 1.2 s of solver time over 273 ticks, a third of it in the QP.

Usage ​

MPCComponents.Examples.ACADOSMPCRaceCarSpatialExample(tol=0.1, levenberg_marquardt=1.0)

Parameters: ​

NameDescriptionUnitsDefault value
NpPrediction horizon (number of shooting intervals); 250 m with the default interval, around the next corner–70
dsLength of each shooting interval and of the clock period, in metres–4.5
V0Speed at the start line–65.0
n_maxHalf width of the track, the bound on the lateral offset–4.0
V_minLower bound of the speed (the model is singular at zero speed)–5.0
V_maxUpper bound of the speed–200.0
delta_maxBound of the steering angle–0.15
thrust_maxBound of the throttle and of the brake–3.0
delta_rate_maxBound of the steering rate–1.0
thrust_rate_maxBound of the throttle and brake rates–10.0
c_maxShare of the adherence of each tyre the controller may use (one for the full limit)–1.0
soft_weightQuadratic penalty weight on the slack variables of the soft constraints–1e6
output_trajectoriesRecord the predicted trajectories and solver residuals of the controller (for mpc_gui)–true
time_stepsLengths of the shooting intervals in metres (derived)–linear_time...s, Np, 500)
ellipse_innerInner signals of the friction-ellipse constraints: the normalized longitudinal and lateral force of each tyre and the normalized power (derived)–race_car_ellipse_inner()
ellipse_outerOuter function of the friction-ellipse constraints: the squared norm of the two forces of each tyre and the power (derived)–race_car_ellipse_outer()
ellipse_maxUpper bounds of the friction-ellipse constraints (derived from c_max)–race_car_el..._max(c_max)
nsegNumber of segments in the track tables (derived)–racetrack_segment_count()
q_timeWeight on the elapsed time at each node–20.0
q_nWeight on the lateral offset, resolving the flat direction on the straights–0.5
r_deltaWeight on the steering rate–100.0
r_thrustWeight on the throttle and brake rates–0.1
max_iterIteration cap of the SQP at each tick–3
tolConvergence tolerance of the SQP, an absolute residual on the scale of the cost gradients–0.1
levenberg_marquardtLevenberg-Marquardt regularization of the SQP steps–1.0

Variables ​

NameDescriptionUnits
sArc length along the centerline–
nLateral offset from the centerline–
alphaHeading relative to the centerline–
VSpeed–
lambdaBody slip angle–
omegaYaw rate–
deltaSteering angle–
throttleThrottle–
brakeBrake–
thrustThrottle minus brake, the signed thrust of dymos–
sdotRate of progress along the centerline–
power_normTractive power relative to the engine limit–
c_flFriction ellipse of the front-left tyre (one at the limit)–
c_frFriction ellipse of the front-right tyre–
c_rlFriction ellipse of the rear-left tyre–
c_rrFriction ellipse of the rear-right tyre–
flagacados status–
tlapElapsed time; the lap time is its value at s = L–

Behavior ​

Behavior of this component cannot be rendered because it includes path variables.

Source ​

dyad
"""
The lap of `ACADOSMPCRaceCarExample` with the arc length as the independent variable throughout:
`RaceCarSpatialModel` is both the plant and the prediction model, the simulation runs over the
distance along the centerline, the controller ticks every `ds` metres and its shooting intervals are
`ds` long, and the elapsed time `tlap` is a state, as in the dymos formulation of the problem. The
tyre constraint is the friction ellipse of dymos itself rather than the octagon inscribed in it. The
receding-horizon loop is used here as a way of solving the lap-time problem, not as a real-time
controller: nothing is applied at a fixed rate in time, so nothing needs the time domain.

# Loop
The independent variable of the simulation is the arc length, so the solution is a function of `s`
and the lap time is `tlap` at ``s = L``. The applied control (the three rates) is held over exactly the
first shooting interval, since the clock ticks in space with the same `ds`, and the plant integrates
the same model the controller predicts with: the loop has no discretization mismatch, and the
constraints hold to numerical precision at the nodes. In the time-domain example the control is held
for a clock period while the prediction assumes a shooting interval, and the tyre constraints are
exceeded by a fraction of a percent at the ticks.

# Cost
The lap time is the elapsed time at the end of the horizon, so the cost is the plain least-squares term
``q_\\mathrm{time} \\, t_{\\mathrm{lap},k}^2`` on a state that starts at zero at every tick: no
unreachable reference is needed, since minimizing the square of a nonnegative quantity minimizes the
quantity. The same small weight `q_n` on the lateral offset as in the time-domain example resolves the
flat direction on the straights, and the control rates are weighted by `Q2`.

# Constraints
The friction ellipse ``(S / \\mu_x N)^2 + (F / \\mu_y N)^2 \\le c_\\mathrm{max}`` of each tyre and the
normalized power are given in the convex-over-nonlinear form of `ACADOSMPC` (`nl_convex_inner`, the
normalized forces `sx_fl`, `sy_fl`, ... and `power_norm`; `nl_convex_outer`, the squared norm of each
pair and the identity on the power). acados adds the curvature of the outer function to the Hessian of
the QP, so the linearization of the ellipse, which carries no information about the lateral force at
small slip angles and lets a plain nonlinear constraint be exceeded by a factor of 3 to 4, is
completed by its second derivative and the tyres stay within a few percent of the limit. The
constraints are hard from the first predicted state on, as in the time-domain example; the track and
the actuator bounds are soft state bounds.

# Horizon
The horizon is `Np` intervals of `ds` metres, a fixed distance ahead of the car whatever its speed,
whereas the time-domain horizon shortens in the corners. With the control held over a whole interval,
intervals of 10 m (or 7.5 m) are too coarse: the loop leaves the track at the entry of the first
corner. At 5 m the lap is 22.49 s and at 2.5 m 22.48 s, so the grid is converged at 5 m, which the
example uses with 50 intervals, a horizon of 250 m. A horizon of 200 m costs 0.16 s of lap time and
one of 150 m 0.65 s: the horizon has to see around the next corner.

# Solver
Dividing every rate by ``\\dot s`` makes the model singular as the speed approaches zero and strongly
nonlinear at low speed, which the time-domain model is not: the throttle, brake and steering change per
interval grow with 1/V, and a step of the sequential quadratic programming that brakes hard in the
prediction lands where the linearization is poor. With the octagon as the tyre constraint the
iterations diverged unless the speed had a floor of 25 m/s and the damping was raised to 10; with the
ellipse in the convex form the curvature of the constraint stabilizes the steps, and the floor and
the damping of the time-domain example suffice. The tolerance is 0.1, since the gradients of the
lap-time cost are smaller than those of the progress-rate cost. Three iterations per tick give the
same lap as thirty: 22.49 s from 65 m/s against 22.9 s for the time-domain example with the octagon
(the ellipse gives the tyres the full combined adherence) and 22.27 s for the periodic dymos lap. The
1.1 laps take about 1.2 s of solver time over 273 ticks, a third of it in the QP.
"""
example component ACADOSMPCRaceCarSpatialExample
  "The clock ticks in the arc length, once per shooting interval"
  periodicclock = DiscreteComponents.PeriodicClock(final dt = ds) {
    "Dyad": {"placement": {"diagram": {"x1": 300, "y1": 140, "x2": 400, "y2": 240}}}
  }
  plant = MPCComponents.Examples.RaceCarSpatialModel(V0 = V0) {
    "Dyad": {"placement": {"diagram": {"x1": 900, "y1": 300, "x2": 1000, "y2": 400}}}
  }
  "Samplers of the eleven state variables of the car"
  sx = [DiscreteComponents.Sampler() for i in 1:11] {
    "Dyad": {
      "placement": {"diagram": {"x1": 700, "y1": 600, "x2": 800, "y2": 700, "rot": 180}}
    }
  }
  "Holds of the three control rates"
  zoh = [DiscreteComponents.ZeroOrderHold() for i in 1:3] {
    "Dyad": {"placement": {"diagram": {"x1": 700, "y1": 300, "x2": 800, "y2": 400}}}
  }
  "The two track tables of the plant, joined into the parameter input of the controller"
  trackmux = MPCComponents.Examples.Mux(final n1 = nseg, final n2 = nseg) {
    "Dyad": {
      "placement": {"diagram": {"x1": 700, "y1": 450, "x2": 800, "y2": 550, "rot": 180}}
    }
  }
  mpc = MPCComponents.ACADOSMPC(dynamics = plant, inputs = ["delta_rate", "throttle_rate", "brake_rate"], tunable_parameters = ["seg_curvature", "seg_length"], parameter_input = true, state_variables = ["s", "tlap", "n", "alpha", "V", "lambda", "omega", "ax", "ay", "delta", "throttle", "brake"], outputs = ["tlap", "n"], final Ts = ds, final Np = Np, final time_steps = time_steps, Q1 = diagonal([q_time, q_n]), Q2 = diagonal([r_delta, r_thrust, r_thrust]), final umin = [-delta_rate_max, -thrust_rate_max, -thrust_rate_max], final umax = [delta_rate_max, thrust_rate_max, thrust_rate_max], constrained = ["n", "V", "delta", "throttle", "brake"], final constrained_min = [-n_max, V_min, -delta_max, 0.0, 0.0], final constrained_max = [n_max, V_max, delta_max, thrust_max, thrust_max], final nl_convex_inner = ellipse_inner, final nl_convex_outer = ellipse_outer, final nl_convex_max = ellipse_max, nl_constraints_soft = false, nl_constraints_from_stage = 1, final soft_weight = soft_weight, final x_guess = [0.0, 0.0, 0.0, 0.0, V0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], x_scale = [1000.0, 1.0, 4.0, 0.15, 40.0, 0.01, 0.3, 8.0, 8.0, 0.04, 1.0, 1.0], u_scale = [1.0, 10.0, 10.0], nlp_solver = MPCComponents.ACADOSSolver.SQP(), max_iter = max_iter, tol = tol, levenberg_marquardt = levenberg_marquardt, integrator = MPCComponents.ACADOSIntegrator.IRK(), collocation = MPCComponents.ACADOSCollocation.RadauIIA(), integrator_stages = 2, integrator_steps = 2, qp_cond_N = 5, qp_warm_start = 2, final output_trajectories = output_trajectories) {
    "Dyad": {"placement": {"diagram": {"x1": 500, "y1": 300, "x2": 600, "y2": 400}}}
  }
  "Prediction horizon (number of shooting intervals); 250 m with the default interval, around the next corner"
  structural parameter Np::Integer = 70
  "Length of each shooting interval and of the clock period, in metres"
  structural parameter ds::Real = 4.5
  "Speed at the start line"
  structural parameter V0::Real = 65.0
  "Half width of the track, the bound on the lateral offset"
  structural parameter n_max::Real = 4.0
  "Lower bound of the speed (the model is singular at zero speed)"
  structural parameter V_min::Real = 5.0
  "Upper bound of the speed"
  structural parameter V_max::Real = 200.0
  "Bound of the steering angle"
  structural parameter delta_max::Real = 0.15
  "Bound of the throttle and of the brake"
  structural parameter thrust_max::Real = 3.0
  "Bound of the steering rate"
  structural parameter delta_rate_max::Real = 1.0
  "Bound of the throttle and brake rates"
  structural parameter thrust_rate_max::Real = 10.0
  "Share of the adherence of each tyre the controller may use (one for the full limit)"
  structural parameter c_max::Real = 1.0
  "Quadratic penalty weight on the slack variables of the soft constraints"
  structural parameter soft_weight::Real = 1e6
  "Record the predicted trajectories and solver residuals of the controller (for mpc_gui)"
  structural parameter output_trajectories::Boolean = true
  "Lengths of the shooting intervals in metres (derived)"
  final structural parameter time_steps::Real[:] = linear_time_steps(ds, Np, 500)
  "Inner signals of the friction-ellipse constraints: the normalized longitudinal and lateral force of each tyre and the normalized power (derived)"
  final structural parameter ellipse_inner::String[:] = race_car_ellipse_inner()
  "Outer function of the friction-ellipse constraints: the squared norm of the two forces of each tyre and the power (derived)"
  final structural parameter ellipse_outer::MPCComponents.ConvexOuter = race_car_ellipse_outer()
  "Upper bounds of the friction-ellipse constraints (derived from c_max)"
  final structural parameter ellipse_max::Real[:] = race_car_ellipse_max(c_max)
  "Number of segments in the track tables (derived)"
  final structural parameter nseg::Integer = racetrack_segment_count()
  "Weight on the elapsed time at each node"
  structural parameter q_time::Real = 20.0
  "Weight on the lateral offset, resolving the flat direction on the straights"
  structural parameter q_n::Real = 0.5
  "Weight on the steering rate"
  structural parameter r_delta::Real = 100.0
  "Weight on the throttle and brake rates"
  structural parameter r_thrust::Real = 0.1
  "Iteration cap of the SQP at each tick"
  structural parameter max_iter::Integer = 3
  "Convergence tolerance of the SQP, an absolute residual on the scale of the cost gradients"
  parameter tol::Real = 0.1
  "Levenberg-Marquardt regularization of the SQP steps"
  parameter levenberg_marquardt::Real = 1.0
  "Arc length along the centerline"
  variable s::Real
  "Lateral offset from the centerline"
  variable n::Real
  "Heading relative to the centerline"
  variable alpha::Real
  "Speed"
  variable V::Real
  "Body slip angle"
  variable lambda::Real
  "Yaw rate"
  variable omega::Real
  "Steering angle"
  variable delta::Real
  "Throttle"
  variable throttle::Real
  "Brake"
  variable brake::Real
  "Throttle minus brake, the signed thrust of dymos"
  variable thrust::Real
  "Rate of progress along the centerline"
  variable sdot::Real
  "Tractive power relative to the engine limit"
  variable power_norm::Real
  "Friction ellipse of the front-left tyre (one at the limit)"
  variable c_fl::Real
  "Friction ellipse of the front-right tyre"
  variable c_fr::Real
  "Friction ellipse of the rear-left tyre"
  variable c_rl::Real
  "Friction ellipse of the rear-right tyre"
  variable c_rr::Real
  "acados status"
  variable flag::Real
  "Elapsed time; the lap time is its value at s = L"
  variable tlap::Real
relations
  s = plant.s
  tlap = plant.tlap
  n = plant.n
  alpha = plant.alpha
  V = plant.V
  lambda = plant.lambda
  omega = plant.omega
  delta = plant.delta
  throttle = plant.throttle
  brake = plant.brake
  thrust = plant.thrust
  sdot = plant.sdot
  power_norm = plant.power_norm
  c_fl = plant.c_fl
  c_fr = plant.c_fr
  c_rl = plant.c_rl
  c_rr = plant.c_rr
  flag = mpc.exitflag
  connect(plant.s_out, sx[1].u) {}
  connect(plant.n_out, sx[2].u) {}
  connect(plant.alpha_out, sx[3].u) {}
  connect(plant.V_out, sx[4].u) {}
  connect(plant.lambda_out, sx[5].u) {}
  connect(plant.omega_out, sx[6].u) {}
  connect(plant.ax_out, sx[7].u) {}
  connect(plant.ay_out, sx[8].u) {}
  connect(plant.delta_out, sx[9].u) {}
  connect(plant.throttle_out, sx[10].u) {}
  connect(plant.brake_out, sx[11].u) {}
  # The elapsed time of the prediction restarts at zero at every tick, so its state input is a constant
  connect(sx[1].y, mpc.x[1], periodicclock.y) {}
  mpc.x[2] = 0
  for i in 2:11
    connect(sx[i].y, mpc.x[i + 1])
  end
  mpc.r[1] = 0
  mpc.r[2] = 0
  for i in 1:3
    connect(mpc.u[i], zoh[i].u)
  end
  connect(plant.seg_curvature_out, trackmux.u1) {}
  connect(plant.seg_length_out, trackmux.u2) {}
  connect(trackmux.y, mpc.p) {}
  connect(zoh[1].y, plant.delta_rate) {}
  connect(zoh[2].y, plant.throttle_rate) {}
  connect(zoh[3].y, plant.brake_rate) {}
metadata {"Dyad": {"doc": {"behavior": false}}}
end
Flattened Source
dyad
"""
The lap of `ACADOSMPCRaceCarExample` with the arc length as the independent variable throughout:
`RaceCarSpatialModel` is both the plant and the prediction model, the simulation runs over the
distance along the centerline, the controller ticks every `ds` metres and its shooting intervals are
`ds` long, and the elapsed time `tlap` is a state, as in the dymos formulation of the problem. The
tyre constraint is the friction ellipse of dymos itself rather than the octagon inscribed in it. The
receding-horizon loop is used here as a way of solving the lap-time problem, not as a real-time
controller: nothing is applied at a fixed rate in time, so nothing needs the time domain.

# Loop
The independent variable of the simulation is the arc length, so the solution is a function of `s`
and the lap time is `tlap` at ``s = L``. The applied control (the three rates) is held over exactly the
first shooting interval, since the clock ticks in space with the same `ds`, and the plant integrates
the same model the controller predicts with: the loop has no discretization mismatch, and the
constraints hold to numerical precision at the nodes. In the time-domain example the control is held
for a clock period while the prediction assumes a shooting interval, and the tyre constraints are
exceeded by a fraction of a percent at the ticks.

# Cost
The lap time is the elapsed time at the end of the horizon, so the cost is the plain least-squares term
``q_\\mathrm{time} \\, t_{\\mathrm{lap},k}^2`` on a state that starts at zero at every tick: no
unreachable reference is needed, since minimizing the square of a nonnegative quantity minimizes the
quantity. The same small weight `q_n` on the lateral offset as in the time-domain example resolves the
flat direction on the straights, and the control rates are weighted by `Q2`.

# Constraints
The friction ellipse ``(S / \\mu_x N)^2 + (F / \\mu_y N)^2 \\le c_\\mathrm{max}`` of each tyre and the
normalized power are given in the convex-over-nonlinear form of `ACADOSMPC` (`nl_convex_inner`, the
normalized forces `sx_fl`, `sy_fl`, ... and `power_norm`; `nl_convex_outer`, the squared norm of each
pair and the identity on the power). acados adds the curvature of the outer function to the Hessian of
the QP, so the linearization of the ellipse, which carries no information about the lateral force at
small slip angles and lets a plain nonlinear constraint be exceeded by a factor of 3 to 4, is
completed by its second derivative and the tyres stay within a few percent of the limit. The
constraints are hard from the first predicted state on, as in the time-domain example; the track and
the actuator bounds are soft state bounds.

# Horizon
The horizon is `Np` intervals of `ds` metres, a fixed distance ahead of the car whatever its speed,
whereas the time-domain horizon shortens in the corners. With the control held over a whole interval,
intervals of 10 m (or 7.5 m) are too coarse: the loop leaves the track at the entry of the first
corner. At 5 m the lap is 22.49 s and at 2.5 m 22.48 s, so the grid is converged at 5 m, which the
example uses with 50 intervals, a horizon of 250 m. A horizon of 200 m costs 0.16 s of lap time and
one of 150 m 0.65 s: the horizon has to see around the next corner.

# Solver
Dividing every rate by ``\\dot s`` makes the model singular as the speed approaches zero and strongly
nonlinear at low speed, which the time-domain model is not: the throttle, brake and steering change per
interval grow with 1/V, and a step of the sequential quadratic programming that brakes hard in the
prediction lands where the linearization is poor. With the octagon as the tyre constraint the
iterations diverged unless the speed had a floor of 25 m/s and the damping was raised to 10; with the
ellipse in the convex form the curvature of the constraint stabilizes the steps, and the floor and
the damping of the time-domain example suffice. The tolerance is 0.1, since the gradients of the
lap-time cost are smaller than those of the progress-rate cost. Three iterations per tick give the
same lap as thirty: 22.49 s from 65 m/s against 22.9 s for the time-domain example with the octagon
(the ellipse gives the tyres the full combined adherence) and 22.27 s for the periodic dymos lap. The
1.1 laps take about 1.2 s of solver time over 273 ticks, a third of it in the QP.
"""
example component ACADOSMPCRaceCarSpatialExample
  "The clock ticks in the arc length, once per shooting interval"
  periodicclock = DiscreteComponents.PeriodicClock(final dt = ds) {
    "Dyad": {"placement": {"diagram": {"x1": 300, "y1": 140, "x2": 400, "y2": 240}}}
  }
  plant = MPCComponents.Examples.RaceCarSpatialModel(V0 = V0) {
    "Dyad": {"placement": {"diagram": {"x1": 900, "y1": 300, "x2": 1000, "y2": 400}}}
  }
  "Samplers of the eleven state variables of the car"
  sx = [DiscreteComponents.Sampler() for i in 1:11] {
    "Dyad": {
      "placement": {"diagram": {"x1": 700, "y1": 600, "x2": 800, "y2": 700, "rot": 180}}
    }
  }
  "Holds of the three control rates"
  zoh = [DiscreteComponents.ZeroOrderHold() for i in 1:3] {
    "Dyad": {"placement": {"diagram": {"x1": 700, "y1": 300, "x2": 800, "y2": 400}}}
  }
  "The two track tables of the plant, joined into the parameter input of the controller"
  trackmux = MPCComponents.Examples.Mux(final n1 = nseg, final n2 = nseg) {
    "Dyad": {
      "placement": {"diagram": {"x1": 700, "y1": 450, "x2": 800, "y2": 550, "rot": 180}}
    }
  }
  mpc = MPCComponents.ACADOSMPC(dynamics = plant, inputs = ["delta_rate", "throttle_rate", "brake_rate"], tunable_parameters = ["seg_curvature", "seg_length"], parameter_input = true, state_variables = ["s", "tlap", "n", "alpha", "V", "lambda", "omega", "ax", "ay", "delta", "throttle", "brake"], outputs = ["tlap", "n"], final Ts = ds, final Np = Np, final time_steps = time_steps, Q1 = diagonal([q_time, q_n]), Q2 = diagonal([r_delta, r_thrust, r_thrust]), final umin = [-delta_rate_max, -thrust_rate_max, -thrust_rate_max], final umax = [delta_rate_max, thrust_rate_max, thrust_rate_max], constrained = ["n", "V", "delta", "throttle", "brake"], final constrained_min = [-n_max, V_min, -delta_max, 0.0, 0.0], final constrained_max = [n_max, V_max, delta_max, thrust_max, thrust_max], final nl_convex_inner = ellipse_inner, final nl_convex_outer = ellipse_outer, final nl_convex_max = ellipse_max, nl_constraints_soft = false, nl_constraints_from_stage = 1, final soft_weight = soft_weight, final x_guess = [0.0, 0.0, 0.0, 0.0, V0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], x_scale = [1000.0, 1.0, 4.0, 0.15, 40.0, 0.01, 0.3, 8.0, 8.0, 0.04, 1.0, 1.0], u_scale = [1.0, 10.0, 10.0], nlp_solver = MPCComponents.ACADOSSolver.SQP(), max_iter = max_iter, tol = tol, levenberg_marquardt = levenberg_marquardt, integrator = MPCComponents.ACADOSIntegrator.IRK(), collocation = MPCComponents.ACADOSCollocation.RadauIIA(), integrator_stages = 2, integrator_steps = 2, qp_cond_N = 5, qp_warm_start = 2, final output_trajectories = output_trajectories) {
    "Dyad": {"placement": {"diagram": {"x1": 500, "y1": 300, "x2": 600, "y2": 400}}}
  }
  "Prediction horizon (number of shooting intervals); 250 m with the default interval, around the next corner"
  structural parameter Np::Integer = 70
  "Length of each shooting interval and of the clock period, in metres"
  structural parameter ds::Real = 4.5
  "Speed at the start line"
  structural parameter V0::Real = 65.0
  "Half width of the track, the bound on the lateral offset"
  structural parameter n_max::Real = 4.0
  "Lower bound of the speed (the model is singular at zero speed)"
  structural parameter V_min::Real = 5.0
  "Upper bound of the speed"
  structural parameter V_max::Real = 200.0
  "Bound of the steering angle"
  structural parameter delta_max::Real = 0.15
  "Bound of the throttle and of the brake"
  structural parameter thrust_max::Real = 3.0
  "Bound of the steering rate"
  structural parameter delta_rate_max::Real = 1.0
  "Bound of the throttle and brake rates"
  structural parameter thrust_rate_max::Real = 10.0
  "Share of the adherence of each tyre the controller may use (one for the full limit)"
  structural parameter c_max::Real = 1.0
  "Quadratic penalty weight on the slack variables of the soft constraints"
  structural parameter soft_weight::Real = 1e6
  "Record the predicted trajectories and solver residuals of the controller (for mpc_gui)"
  structural parameter output_trajectories::Boolean = true
  "Lengths of the shooting intervals in metres (derived)"
  final structural parameter time_steps::Real[:] = linear_time_steps(ds, Np, 500)
  "Inner signals of the friction-ellipse constraints: the normalized longitudinal and lateral force of each tyre and the normalized power (derived)"
  final structural parameter ellipse_inner::String[:] = race_car_ellipse_inner()
  "Outer function of the friction-ellipse constraints: the squared norm of the two forces of each tyre and the power (derived)"
  final structural parameter ellipse_outer::MPCComponents.ConvexOuter = race_car_ellipse_outer()
  "Upper bounds of the friction-ellipse constraints (derived from c_max)"
  final structural parameter ellipse_max::Real[:] = race_car_ellipse_max(c_max)
  "Number of segments in the track tables (derived)"
  final structural parameter nseg::Integer = racetrack_segment_count()
  "Weight on the elapsed time at each node"
  structural parameter q_time::Real = 20.0
  "Weight on the lateral offset, resolving the flat direction on the straights"
  structural parameter q_n::Real = 0.5
  "Weight on the steering rate"
  structural parameter r_delta::Real = 100.0
  "Weight on the throttle and brake rates"
  structural parameter r_thrust::Real = 0.1
  "Iteration cap of the SQP at each tick"
  structural parameter max_iter::Integer = 3
  "Convergence tolerance of the SQP, an absolute residual on the scale of the cost gradients"
  parameter tol::Real = 0.1
  "Levenberg-Marquardt regularization of the SQP steps"
  parameter levenberg_marquardt::Real = 1.0
  "Arc length along the centerline"
  variable s::Real
  "Lateral offset from the centerline"
  variable n::Real
  "Heading relative to the centerline"
  variable alpha::Real
  "Speed"
  variable V::Real
  "Body slip angle"
  variable lambda::Real
  "Yaw rate"
  variable omega::Real
  "Steering angle"
  variable delta::Real
  "Throttle"
  variable throttle::Real
  "Brake"
  variable brake::Real
  "Throttle minus brake, the signed thrust of dymos"
  variable thrust::Real
  "Rate of progress along the centerline"
  variable sdot::Real
  "Tractive power relative to the engine limit"
  variable power_norm::Real
  "Friction ellipse of the front-left tyre (one at the limit)"
  variable c_fl::Real
  "Friction ellipse of the front-right tyre"
  variable c_fr::Real
  "Friction ellipse of the rear-left tyre"
  variable c_rl::Real
  "Friction ellipse of the rear-right tyre"
  variable c_rr::Real
  "acados status"
  variable flag::Real
  "Elapsed time; the lap time is its value at s = L"
  variable tlap::Real
relations
  s = plant.s
  tlap = plant.tlap
  n = plant.n
  alpha = plant.alpha
  V = plant.V
  lambda = plant.lambda
  omega = plant.omega
  delta = plant.delta
  throttle = plant.throttle
  brake = plant.brake
  thrust = plant.thrust
  sdot = plant.sdot
  power_norm = plant.power_norm
  c_fl = plant.c_fl
  c_fr = plant.c_fr
  c_rl = plant.c_rl
  c_rr = plant.c_rr
  flag = mpc.exitflag
  connect(plant.s_out, sx[1].u) {}
  connect(plant.n_out, sx[2].u) {}
  connect(plant.alpha_out, sx[3].u) {}
  connect(plant.V_out, sx[4].u) {}
  connect(plant.lambda_out, sx[5].u) {}
  connect(plant.omega_out, sx[6].u) {}
  connect(plant.ax_out, sx[7].u) {}
  connect(plant.ay_out, sx[8].u) {}
  connect(plant.delta_out, sx[9].u) {}
  connect(plant.throttle_out, sx[10].u) {}
  connect(plant.brake_out, sx[11].u) {}
  # The elapsed time of the prediction restarts at zero at every tick, so its state input is a constant
  connect(sx[1].y, mpc.x[1], periodicclock.y) {}
  mpc.x[2] = 0
  for i in 2:11
    connect(sx[i].y, mpc.x[i + 1])
  end
  mpc.r[1] = 0
  mpc.r[2] = 0
  for i in 1:3
    connect(mpc.u[i], zoh[i].u)
  end
  connect(plant.seg_curvature_out, trackmux.u1) {}
  connect(plant.seg_length_out, trackmux.u2) {}
  connect(trackmux.y, mpc.p) {}
  connect(zoh[1].y, plant.delta_rate) {}
  connect(zoh[2].y, plant.throttle_rate) {}
  connect(zoh[3].y, plant.brake_rate) {}
metadata {"Dyad": {"doc": {"behavior": false}}}
end


Test Cases ​

No test cases defined.

  • Examples

  • Experiments

  • Analyses