Skip to content
LIBRARY
Examples.ACADOSMPCOmniRobotExample.md

Examples.ACADOSMPCOmniRobotExample ​

Navigation of a mecanum-wheeled mobile robot platform (OmniRobotPlant, carrying the data of a Husarion ROSbot XL) around an obstacle with ACADOSMPC, driven by a previewed position path.

A path planner, which is not part of the example, has produced the path (omni_path_pose): a sequence of straight segments and circular arcs that leaves the corridor by 0.5 m to pass an obstacle of radius 0.22 m, runs past it and returns, 4.74 m long in total and ending at a standstill. The step aside is sharp, two 90-degree arcs of radius 0.25 m, because the obstacle is close; the return is gentle, two 20-degree arcs of radius 2 m, because nothing constrains it. The path is a path and not a trajectory – it carries positions only, no velocities and no heading – and enters the loop as an input signal through OmniPathPreview, which samples it along its arc length at the nominal speed of 0.4 m/s to give one position per shooting node. The obstacle appears neither in the plant nor in the controller; the path already avoids it.

The horizon is 2 s covered by Np = 20 intervals of linearly increasing length (linear_time_steps(Ts, Np, Tf)), the first of which is the 0.02 s clock period. A uniform grid of that resolution would need 100 intervals for the same lookahead, and the lookahead is what lets the controller start each manoeuvre before the bend that calls for it and start braking before the end of the path.

Turning or translating ​

The controlled outputs are the two coordinates, the lateral velocity and the yaw rate   , the references of the last two being zero. Position is the only thing tracked, so the controller chooses the speed and the heading itself, and the two zero-reference terms are what decide how it traverses a bend of radius at the speed :

  • Turning with the platform aligned to the path needs the yaw rate throughout the bend, which costs per unit time. Penalizing the yaw rate is the ordinary thing to do for a robot that works among people and navigates from a lidar and a depth camera, both of which degrade when the base turns quickly.

  • Translating through the bend instead needs a lateral velocity of up to      for a bend of    radians, which costs per unit time, and a lateral acceleration of .

The turning cost grows as and the translating cost does not grow at all as the bend tightens, so there is a radius below which using the rollers is the cheaper way through. With   and   that radius falls between the two radii of the path: the gentle return is turned, the heading following the tangent of the path to within 0.04 rad, while through the sharp step aside the platform turns by only a third of the tangent's 90 degrees and translates the rest, with reaching 0.28 m/s. On the straight segments both terms are zero at the same time and the platform simply drives, apart from the 0.1 m/s of lateral velocity it builds up on either side of the step aside, which is the preview at work. Over the gentle return and the final straight stays below 0.04 m/s, so the controller exploits the omni property where it pays off and nowhere else.

The bounds, in contrast to the weights, are the platform's own: 0.8 m/s in either body direction, 3.14 rad/s of yaw and Missing open brace for subscript per wheel, the last being the documented acceleration limit of the platform spread over its four wheels. Only the force bounds become active. Because that bound is per wheel, the reachable body force is the diamond Missing open brace for subscript: the force available along one body axis shrinks as the other is used. The motors also limit the rolling speed of each wheel to about 1.05 m/s, which restricts translation and rotation jointly; the example leaves that constraint out, as it would be a hard one (Ax, lb, ub), and the loop stays below 0.7 m/s of wheel speed anyway.

Q2 is positive definite, which it has to be: the map from wheel forces to the body wrench has the null direction    (see OmniRobotPlant), and nothing else penalizes it. A penalty on the control increments is not available on a non-uniform grid, so Q2 alone smooths the control. Verified in test/test_acados_mpc.jl; animated by examples/omni_robot.jl.

Usage ​

MPCComponents.Examples.ACADOSMPCOmniRobotExample(obstacle_x=1.5, obstacle_y=0.0, obstacle_radius=0.22)

Parameters: ​

NameDescriptionUnitsDefault value
NpPrediction horizon (number of shooting intervals)–20
TsSample interval, the length of the first shooting interval–0.02
TfPrediction horizon in seconds, spanned by the Np shooting intervals–2.0
output_trajectoriesRecord the predicted trajectories and solver residuals of the controller (for mpc_gui)–false
mMass of the platform–5.38
a_maxAcceleration limit of the platform–1.0
speedSpeed at which the planned path is sampled to give the position of each shooting node–0.4
v_maxSpeed limit of the platform in both body directions–0.8
om_maxYaw-rate limit of the platform–3.14
f_maxForce bound of one wheel, the acceleration limit spread over the four wheels (derived)–m * a_max / 4
time_stepsLengths of the shooting intervals, growing linearly from Ts to cover Tf (derived)–linear_time...Ts, Np, Tf)
nspNumber of preview samplers, one per previewed coordinate (derived)–2 * Np
obstacle_xPosition along the corridor of the obstacle the path was planned around–1.5
obstacle_yPosition across the corridor of the obstacle–0.0
obstacle_radiusRadius of the obstacle–0.22

Variables ​

NameDescriptionUnits
pxPosition along the corridor–
pyPosition across the corridor–
thHeading–
vyLateral velocity in the body frame–
omYaw rate–
rxReference position along the corridor at the first shooting node–
ryReference position across the corridor at the first shooting node–
u1Drive force of the front-left wheel–
flagacados status–

Behavior ​

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

Source ​

dyad
"""
Navigation of a mecanum-wheeled mobile robot platform (`OmniRobotPlant`, carrying the data of a
Husarion ROSbot XL) around an obstacle with `ACADOSMPC`, driven by a previewed position path.

A path planner, which is not part of the example, has produced the path (`omni_path_pose`): a
sequence of straight segments and circular arcs that leaves the corridor by 0.5 m to pass an
obstacle of radius 0.22 m, runs past it and returns, 4.74 m long in total and ending at a
standstill. The step aside is sharp, two 90-degree arcs of radius 0.25 m, because the obstacle is
close; the return is gentle, two 20-degree arcs of radius 2 m, because nothing constrains it. The
path is a path and not a trajectory -- it carries positions only, no velocities and no heading -- and
enters the loop as an input signal through `OmniPathPreview`, which samples it along its arc length
at the nominal speed of 0.4 m/s to give one position per shooting node. The obstacle appears neither
in the plant nor in the controller; the path already avoids it.

The horizon is 2 s covered by `Np = 20` intervals of linearly increasing length
(`linear_time_steps(Ts, Np, Tf)`), the first of which is the 0.02 s clock period. A uniform grid of
that resolution would need 100 intervals for the same lookahead, and the lookahead is what lets the
controller start each manoeuvre before the bend that calls for it and start braking before the end
of the path.

# Turning or translating
The controlled outputs are the two coordinates, the lateral velocity ``v_y`` and the yaw rate
``\\omega``, the references of the last two being zero. Position is the only thing tracked, so the
controller chooses the speed and the heading itself, and the two zero-reference terms are what
decide how it traverses a bend of radius ``R`` at the speed ``v``:

  * Turning with the platform aligned to the path needs the yaw rate ``v/R`` throughout the bend,
    which costs ``Q_1[4,4] (v/R)^2`` per unit time. Penalizing the yaw rate is the ordinary thing to
    do for a robot that works among people and navigates from a lidar and a depth camera, both of
    which degrade when the base turns quickly.
  * Translating through the bend instead needs a lateral velocity of up to ``v \\sin(\\varphi/2)``
    for a bend of ``\\varphi`` radians, which costs ``Q_1[3,3] v_y^2`` per unit time, and a lateral
    acceleration of ``v^2/R``.

The turning cost grows as ``1/R^2`` and the translating cost does not grow at all as the bend
tightens, so there is a radius below which using the rollers is the cheaper way through. With
``Q_1[3,3] = 1`` and ``Q_1[4,4] = 0.1`` that radius falls between the two radii of the path: the
gentle return is turned, the heading following the tangent of the path to within 0.04 rad, while
through the sharp step aside the platform turns by only a third of the tangent's 90 degrees and
translates the rest, with ``v_y`` reaching 0.28 m/s. On the straight segments both terms are zero at
the same time and the platform simply drives, apart from the 0.1 m/s of lateral velocity it builds
up on either side of the step aside, which is the preview at work. Over the gentle return and the
final straight ``|v_y|`` stays below 0.04 m/s, so the controller exploits the omni property where it
pays off and nowhere else.

The bounds, in contrast to the weights, are the platform's own: 0.8 m/s in either body direction,
3.14 rad/s of yaw and ``\\pm f_\\mathrm{max}`` per wheel, the last being the documented acceleration
limit of the platform spread over its four wheels. Only the force bounds become active. Because that
bound is per wheel, the reachable body force is the diamond ``|F_x| + |F_y| \\le 4 f_\\mathrm{max}``:
the force available along one body axis shrinks as the other is used. The motors also limit the
rolling speed of each wheel to about 1.05 m/s, which restricts translation and rotation jointly; the
example leaves that constraint out, as it would be a hard one (`Ax`, `lb`, `ub`), and the loop stays
below 0.7 m/s of wheel speed anyway.

`Q2` is positive definite, which it has to be: the map from wheel forces to the body wrench has the
null direction ``(1, 1, -1, -1)`` (see `OmniRobotPlant`), and nothing else penalizes it. A penalty on
the control increments is not available on a non-uniform grid, so `Q2` alone smooths the control.
Verified in `test/test_acados_mpc.jl`; animated by `examples/omni_robot.jl`.
"""
example component ACADOSMPCOmniRobotExample
  periodicclock = DiscreteComponents.PeriodicClock(final dt = Ts) {
    "Dyad": {"placement": {"diagram": {"x1": 300, "y1": 160, "x2": 400, "y2": 260}}}
  }
  plant = MPCComponents.Examples.OmniRobotPlant(m = m, a_max = a_max) {
    "Dyad": {"placement": {"diagram": {"x1": 700, "y1": 300, "x2": 800, "y2": 400}}}
  }
  "Samplers of the six state variables"
  s = [DiscreteComponents.Sampler() for i in 1:6] {
    "Dyad": {
      "placement": {"diagram": {"x1": 500, "y1": 550, "x2": 600, "y2": 450, "rot": 180}}
    }
  }
  "Holds of the four wheel forces"
  zoh = [DiscreteComponents.ZeroOrderHold() for i in 1:4] {
    "Dyad": {"placement": {"diagram": {"x1": 500, "y1": 300, "x2": 600, "y2": 400}}}
  }
  "The planned path, previewed over the shooting grid"
  preview = MPCComponents.Examples.OmniPathPreview(final Np = Np, final Ts = Ts, final Tf = Tf, final speed = speed) {
    "Dyad": {"placement": {"diagram": {"x1": -100, "y1": 300, "x2": 0, "y2": 400}}}
  }
  "Samplers of the previewed path coordinates, the first Np along the corridor and the rest across"
  sp = [DiscreteComponents.Sampler() for i in 1:nsp] {
    "Dyad": {"placement": {"diagram": {"x1": 100, "y1": 300, "x2": 200, "y2": 400}}}
  }
  mpc = MPCComponents.ACADOSMPC(dynamics = plant, inputs = ["f1", "f2", "f3", "f4"], state_variables = ["px", "py", "th", "vx", "vy", "om"], outputs = ["px", "py", "vy", "om"], final Ts = Ts, final Np = Np, final time_steps = time_steps, reference_preview = true, Q1 = diagonal([200.0, 200.0, 1.0, 0.1]), Q2 = diagonal([0.01, 0.01, 0.01, 0.01]), final umin = [-f_max, -f_max, -f_max, -f_max], final umax = [f_max, f_max, f_max, f_max], constrained = ["vx", "vy", "om"], final constrained_min = [-v_max, -v_max, -om_max], final constrained_max = [v_max, v_max, om_max], integrator_steps = 1, final output_trajectories = output_trajectories, nlp_solver = MPCComponents.ACADOSSolver.SQP_RTI(), qp_cond_N = 5) {
    "Dyad": {"placement": {"diagram": {"x1": 300, "y1": 300, "x2": 400, "y2": 400}}}
  }
  "Prediction horizon (number of shooting intervals)"
  structural parameter Np::Integer = 20
  "Sample interval, the length of the first shooting interval"
  structural parameter Ts::Real = 0.02
  "Prediction horizon in seconds, spanned by the Np shooting intervals"
  structural parameter Tf::Real = 2.0
  "Record the predicted trajectories and solver residuals of the controller (for mpc_gui)"
  structural parameter output_trajectories::Boolean = false
  "Mass of the platform"
  structural parameter m::Real = 5.38
  "Acceleration limit of the platform"
  structural parameter a_max::Real = 1.0
  "Speed at which the planned path is sampled to give the position of each shooting node"
  structural parameter speed::Real = 0.4
  "Speed limit of the platform in both body directions"
  structural parameter v_max::Real = 0.8
  "Yaw-rate limit of the platform"
  structural parameter om_max::Real = 3.14
  "Force bound of one wheel, the acceleration limit spread over the four wheels (derived)"
  final structural parameter f_max::Real = m * a_max / 4
  "Lengths of the shooting intervals, growing linearly from Ts to cover Tf (derived)"
  final structural parameter time_steps::Real[:] = linear_time_steps(Ts, Np, Tf)
  "Number of preview samplers, one per previewed coordinate (derived)"
  final structural parameter nsp::Integer = 2 * Np
  "Position along the corridor of the obstacle the path was planned around"
  parameter obstacle_x::Real = 1.5
  "Position across the corridor of the obstacle"
  parameter obstacle_y::Real = 0.0
  "Radius of the obstacle"
  parameter obstacle_radius::Real = 0.22
  "Position along the corridor"
  variable px::Real
  "Position across the corridor"
  variable py::Real
  "Heading"
  variable th::Real
  "Lateral velocity in the body frame"
  variable vy::Real
  "Yaw rate"
  variable om::Real
  "Reference position along the corridor at the first shooting node"
  variable rx::Real
  "Reference position across the corridor at the first shooting node"
  variable ry::Real
  "Drive force of the front-left wheel"
  variable u1::Real
  "acados status"
  variable flag::Real
relations
  px = plant.px
  py = plant.py
  th = plant.th
  vy = plant.vy
  om = plant.om
  rx = sp[1].y
  ry = sp[Np + 1].y
  u1 = mpc.u[1]
  flag = mpc.exitflag
  connect(plant.px_out, s[1].u) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 870, "y": 310}, {"x": 870, "y": 500}], "E": 2}],
      "path": {}
    }
  }
  connect(plant.py_out, s[2].u) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 860, "y": 325}, {"x": 860, "y": 500}], "E": 2}],
      "path": {}
    }
  }
  connect(plant.th_out, s[3].u) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 850, "y": 340}, {"x": 850, "y": 500}], "E": 2}],
      "path": {}
    }
  }
  connect(plant.vx_out, s[4].u) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 840, "y": 355}, {"x": 840, "y": 500}], "E": 2}],
      "path": {}
    }
  }
  connect(plant.vy_out, s[5].u) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 830, "y": 370}, {"x": 830, "y": 500}], "E": 2}],
      "path": {}
    }
  }
  connect(plant.om_out, s[6].u) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 820, "y": 385}, {"x": 820, "y": 500}], "E": 2}],
      "path": {}
    }
  }
  connect(s[1].y, mpc.x[1], periodicclock.y) {
    "Dyad": {
      "edges": [
        {"S": 1, "M": [{"x": 280, "y": 500}, {"x": 280, "y": 383.5}], "E": 2},
        {"S": 2, "M": [], "E": 3}
      ],
      "path": {}
    }
  }
  for i in 2:6
    connect(s[i].y, mpc.x[i])
  end
  for j in 1:Np
    connect(preview.px[j], sp[j].u)
    connect(preview.py[j], sp[Np + j].u)
    connect(sp[j].y, mpc.r[4 * (j - 1) + 1])
    connect(sp[Np + j].y, mpc.r[4 * (j - 1) + 2])
    mpc.r[4 * (j - 1) + 3] = 0
    mpc.r[4 * (j - 1) + 4] = 0
  end
  for i in 1:4
    connect(mpc.u[i], zoh[i].u)
  end
  connect(zoh[1].y, plant.f1)
  connect(zoh[2].y, plant.f2)
  connect(zoh[3].y, plant.f3)
  connect(zoh[4].y, plant.f4)
metadata {"Dyad": {"doc": {"behavior": false}}}
end
Flattened Source
dyad
"""
Navigation of a mecanum-wheeled mobile robot platform (`OmniRobotPlant`, carrying the data of a
Husarion ROSbot XL) around an obstacle with `ACADOSMPC`, driven by a previewed position path.

A path planner, which is not part of the example, has produced the path (`omni_path_pose`): a
sequence of straight segments and circular arcs that leaves the corridor by 0.5 m to pass an
obstacle of radius 0.22 m, runs past it and returns, 4.74 m long in total and ending at a
standstill. The step aside is sharp, two 90-degree arcs of radius 0.25 m, because the obstacle is
close; the return is gentle, two 20-degree arcs of radius 2 m, because nothing constrains it. The
path is a path and not a trajectory -- it carries positions only, no velocities and no heading -- and
enters the loop as an input signal through `OmniPathPreview`, which samples it along its arc length
at the nominal speed of 0.4 m/s to give one position per shooting node. The obstacle appears neither
in the plant nor in the controller; the path already avoids it.

The horizon is 2 s covered by `Np = 20` intervals of linearly increasing length
(`linear_time_steps(Ts, Np, Tf)`), the first of which is the 0.02 s clock period. A uniform grid of
that resolution would need 100 intervals for the same lookahead, and the lookahead is what lets the
controller start each manoeuvre before the bend that calls for it and start braking before the end
of the path.

# Turning or translating
The controlled outputs are the two coordinates, the lateral velocity ``v_y`` and the yaw rate
``\\omega``, the references of the last two being zero. Position is the only thing tracked, so the
controller chooses the speed and the heading itself, and the two zero-reference terms are what
decide how it traverses a bend of radius ``R`` at the speed ``v``:

  * Turning with the platform aligned to the path needs the yaw rate ``v/R`` throughout the bend,
    which costs ``Q_1[4,4] (v/R)^2`` per unit time. Penalizing the yaw rate is the ordinary thing to
    do for a robot that works among people and navigates from a lidar and a depth camera, both of
    which degrade when the base turns quickly.
  * Translating through the bend instead needs a lateral velocity of up to ``v \\sin(\\varphi/2)``
    for a bend of ``\\varphi`` radians, which costs ``Q_1[3,3] v_y^2`` per unit time, and a lateral
    acceleration of ``v^2/R``.

The turning cost grows as ``1/R^2`` and the translating cost does not grow at all as the bend
tightens, so there is a radius below which using the rollers is the cheaper way through. With
``Q_1[3,3] = 1`` and ``Q_1[4,4] = 0.1`` that radius falls between the two radii of the path: the
gentle return is turned, the heading following the tangent of the path to within 0.04 rad, while
through the sharp step aside the platform turns by only a third of the tangent's 90 degrees and
translates the rest, with ``v_y`` reaching 0.28 m/s. On the straight segments both terms are zero at
the same time and the platform simply drives, apart from the 0.1 m/s of lateral velocity it builds
up on either side of the step aside, which is the preview at work. Over the gentle return and the
final straight ``|v_y|`` stays below 0.04 m/s, so the controller exploits the omni property where it
pays off and nowhere else.

The bounds, in contrast to the weights, are the platform's own: 0.8 m/s in either body direction,
3.14 rad/s of yaw and ``\\pm f_\\mathrm{max}`` per wheel, the last being the documented acceleration
limit of the platform spread over its four wheels. Only the force bounds become active. Because that
bound is per wheel, the reachable body force is the diamond ``|F_x| + |F_y| \\le 4 f_\\mathrm{max}``:
the force available along one body axis shrinks as the other is used. The motors also limit the
rolling speed of each wheel to about 1.05 m/s, which restricts translation and rotation jointly; the
example leaves that constraint out, as it would be a hard one (`Ax`, `lb`, `ub`), and the loop stays
below 0.7 m/s of wheel speed anyway.

`Q2` is positive definite, which it has to be: the map from wheel forces to the body wrench has the
null direction ``(1, 1, -1, -1)`` (see `OmniRobotPlant`), and nothing else penalizes it. A penalty on
the control increments is not available on a non-uniform grid, so `Q2` alone smooths the control.
Verified in `test/test_acados_mpc.jl`; animated by `examples/omni_robot.jl`.
"""
example component ACADOSMPCOmniRobotExample
  periodicclock = DiscreteComponents.PeriodicClock(final dt = Ts) {
    "Dyad": {"placement": {"diagram": {"x1": 300, "y1": 160, "x2": 400, "y2": 260}}}
  }
  plant = MPCComponents.Examples.OmniRobotPlant(m = m, a_max = a_max) {
    "Dyad": {"placement": {"diagram": {"x1": 700, "y1": 300, "x2": 800, "y2": 400}}}
  }
  "Samplers of the six state variables"
  s = [DiscreteComponents.Sampler() for i in 1:6] {
    "Dyad": {
      "placement": {"diagram": {"x1": 500, "y1": 550, "x2": 600, "y2": 450, "rot": 180}}
    }
  }
  "Holds of the four wheel forces"
  zoh = [DiscreteComponents.ZeroOrderHold() for i in 1:4] {
    "Dyad": {"placement": {"diagram": {"x1": 500, "y1": 300, "x2": 600, "y2": 400}}}
  }
  "The planned path, previewed over the shooting grid"
  preview = MPCComponents.Examples.OmniPathPreview(final Np = Np, final Ts = Ts, final Tf = Tf, final speed = speed) {
    "Dyad": {"placement": {"diagram": {"x1": -100, "y1": 300, "x2": 0, "y2": 400}}}
  }
  "Samplers of the previewed path coordinates, the first Np along the corridor and the rest across"
  sp = [DiscreteComponents.Sampler() for i in 1:nsp] {
    "Dyad": {"placement": {"diagram": {"x1": 100, "y1": 300, "x2": 200, "y2": 400}}}
  }
  mpc = MPCComponents.ACADOSMPC(dynamics = plant, inputs = ["f1", "f2", "f3", "f4"], state_variables = ["px", "py", "th", "vx", "vy", "om"], outputs = ["px", "py", "vy", "om"], final Ts = Ts, final Np = Np, final time_steps = time_steps, reference_preview = true, Q1 = diagonal([200.0, 200.0, 1.0, 0.1]), Q2 = diagonal([0.01, 0.01, 0.01, 0.01]), final umin = [-f_max, -f_max, -f_max, -f_max], final umax = [f_max, f_max, f_max, f_max], constrained = ["vx", "vy", "om"], final constrained_min = [-v_max, -v_max, -om_max], final constrained_max = [v_max, v_max, om_max], integrator_steps = 1, final output_trajectories = output_trajectories, nlp_solver = MPCComponents.ACADOSSolver.SQP_RTI(), qp_cond_N = 5) {
    "Dyad": {"placement": {"diagram": {"x1": 300, "y1": 300, "x2": 400, "y2": 400}}}
  }
  "Prediction horizon (number of shooting intervals)"
  structural parameter Np::Integer = 20
  "Sample interval, the length of the first shooting interval"
  structural parameter Ts::Real = 0.02
  "Prediction horizon in seconds, spanned by the Np shooting intervals"
  structural parameter Tf::Real = 2.0
  "Record the predicted trajectories and solver residuals of the controller (for mpc_gui)"
  structural parameter output_trajectories::Boolean = false
  "Mass of the platform"
  structural parameter m::Real = 5.38
  "Acceleration limit of the platform"
  structural parameter a_max::Real = 1.0
  "Speed at which the planned path is sampled to give the position of each shooting node"
  structural parameter speed::Real = 0.4
  "Speed limit of the platform in both body directions"
  structural parameter v_max::Real = 0.8
  "Yaw-rate limit of the platform"
  structural parameter om_max::Real = 3.14
  "Force bound of one wheel, the acceleration limit spread over the four wheels (derived)"
  final structural parameter f_max::Real = m * a_max / 4
  "Lengths of the shooting intervals, growing linearly from Ts to cover Tf (derived)"
  final structural parameter time_steps::Real[:] = linear_time_steps(Ts, Np, Tf)
  "Number of preview samplers, one per previewed coordinate (derived)"
  final structural parameter nsp::Integer = 2 * Np
  "Position along the corridor of the obstacle the path was planned around"
  parameter obstacle_x::Real = 1.5
  "Position across the corridor of the obstacle"
  parameter obstacle_y::Real = 0.0
  "Radius of the obstacle"
  parameter obstacle_radius::Real = 0.22
  "Position along the corridor"
  variable px::Real
  "Position across the corridor"
  variable py::Real
  "Heading"
  variable th::Real
  "Lateral velocity in the body frame"
  variable vy::Real
  "Yaw rate"
  variable om::Real
  "Reference position along the corridor at the first shooting node"
  variable rx::Real
  "Reference position across the corridor at the first shooting node"
  variable ry::Real
  "Drive force of the front-left wheel"
  variable u1::Real
  "acados status"
  variable flag::Real
relations
  px = plant.px
  py = plant.py
  th = plant.th
  vy = plant.vy
  om = plant.om
  rx = sp[1].y
  ry = sp[Np + 1].y
  u1 = mpc.u[1]
  flag = mpc.exitflag
  connect(plant.px_out, s[1].u) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 870, "y": 310}, {"x": 870, "y": 500}], "E": 2}],
      "path": {}
    }
  }
  connect(plant.py_out, s[2].u) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 860, "y": 325}, {"x": 860, "y": 500}], "E": 2}],
      "path": {}
    }
  }
  connect(plant.th_out, s[3].u) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 850, "y": 340}, {"x": 850, "y": 500}], "E": 2}],
      "path": {}
    }
  }
  connect(plant.vx_out, s[4].u) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 840, "y": 355}, {"x": 840, "y": 500}], "E": 2}],
      "path": {}
    }
  }
  connect(plant.vy_out, s[5].u) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 830, "y": 370}, {"x": 830, "y": 500}], "E": 2}],
      "path": {}
    }
  }
  connect(plant.om_out, s[6].u) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 820, "y": 385}, {"x": 820, "y": 500}], "E": 2}],
      "path": {}
    }
  }
  connect(s[1].y, mpc.x[1], periodicclock.y) {
    "Dyad": {
      "edges": [
        {"S": 1, "M": [{"x": 280, "y": 500}, {"x": 280, "y": 383.5}], "E": 2},
        {"S": 2, "M": [], "E": 3}
      ],
      "path": {}
    }
  }
  for i in 2:6
    connect(s[i].y, mpc.x[i])
  end
  for j in 1:Np
    connect(preview.px[j], sp[j].u)
    connect(preview.py[j], sp[Np + j].u)
    connect(sp[j].y, mpc.r[4 * (j - 1) + 1])
    connect(sp[Np + j].y, mpc.r[4 * (j - 1) + 2])
    mpc.r[4 * (j - 1) + 3] = 0
    mpc.r[4 * (j - 1) + 4] = 0
  end
  for i in 1:4
    connect(mpc.u[i], zoh[i].u)
  end
  connect(zoh[1].y, plant.f1)
  connect(zoh[2].y, plant.f2)
  connect(zoh[3].y, plant.f3)
  connect(zoh[4].y, plant.f4)
metadata {"Dyad": {"doc": {"behavior": false}}}
end


Test Cases ​

No test cases defined.

  • Examples

  • Experiments

  • Analyses