Generated reference › Ackermann Steering Model — Robotics/Planar Kinematics
kind: generated#block#robotics-planar-kinematics

Ackermann Steering Model — Robotics/Planar Kinematics

Robotics/Planar_Kinematics/Ackermann_Steering_Model · 2 input / 2 output port(s) at insert · exports to Python, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog, PLC Structured Text

Description#

The block's own DESCRIPTION_HTML, rendered verbatim — the same text the config dialog's info panel and the library navigator show. Fix a wrong sentence in the block's .cpp (R-D9), never here.

Ackermann Steering Model

Robotics / Planar Kinematics

The kinematic bicycle model: a steering angle and a speed become a path curvature and a yaw rate. κ = tan(δ) / L and ω = v · κ, with δ first clamped to the vehicle's mechanical steering limit.

A real Ackermann linkage steers its two road wheels through different angles; this model collapses them onto one virtual wheel at the centre of the front axle, which is the form every path tracker and every vehicle-dynamics text is written against. It is the source of the yaw rate that Unicycle Odometry integrates into a pose.

Ports

  • v – the longitudinal speed of the rear axle (m/s), any size [m,n]. Negative is reverse, and is meaningful: the curvature is unchanged and the yaw rate reverses with it.
  • delta – the virtual front-wheel steering angle (rad), positive to the left (counter-clockwise, per the family's convention). It must be the same size as v; the two are paired entry by entry as one vehicle's command, and neither is broadcast over the other.
  • omega – the yaw rate v·tan(δ)/L (rad/s), the same size as the inputs.
  • kappa – the path curvature tan(δ)/L (1/m), the same size as the inputs. It is independent of speed, which is what makes it the quantity a pursuit controller compares against: the geometry of the path the vehicle is currently on, whatever speed it is travelling at.

Parameters

  • Wheelbase (m)L, the distance from rear axle to front axle, a positive scalar. Default 2.7, an ordinary passenger car.
  • Max Steer Angle (rad) – the mechanical stop, a scalar strictly inside (0, π/2). delta is clamped to ±this before the tangent is taken. Default 0.6 (about 34°), which is a typical car's lock. This is not a safety net bolted on: see the note below on why the clamp is part of the model.
  • Sampling Time (s) – zero or less inherits the solver's rate; a positive value runs the block at that period.

Code export

All ten targets: Python, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog and PLC Structured Text. The wheelbase reaches the core as the folded constant 1/L and the steering limit as a literal; neither is retunable on the generated core, so re-export to change a vehicle.

The three HDL targets are simulation-only: they carry the arithmetic in real and quantize only at the port boundary. A tangent has no Q16.16 form to call, so offering them as synthesizable would be a claim the generated core could not keep.

No target carries a domain guard, and none needs one – the clamp happens before the tangent in all eleven implementations, so the argument is always inside the range the configuration pinned.

Simulink bridge

No equivalent (Support::None). Bicycle-model blocks ship in the Automated Driving Toolbox and the Robotics System Toolbox, and neither is installed on this machine (measured – isfolder([matlabroot '/toolbox/driving']) and the robotics probe both come back false). To rebuild it there: a Saturation on the angle, a Trigonometric Function set to tan, a Gain of 1/L, and a Product against the speed – in that order, since saturating anywhere else is a different curve.

Notes

  • Algebraic and stateless: both outputs depend only on the current inputs, so the block cannot break an algebraic loop. It carries no tyre model, no slip and no lateral dynamics – it is the kinematic bicycle, and it stops being accurate where the tyres start sliding.
  • The clamp is part of the model, not a guard. tan is unbounded as |δ| → π/2, and a single sample near the asymptote does not produce a large error but a different kind of object: a curvature of 1015, which saturates the fixed-point targets and moves a downstream pose by kilometres in one step. The mechanical stop is where a real linkage ends, so clamping to it is what the vehicle does.
  • The clamp is on the ANGLE, never on the curvature. tan is nonlinear, so clamping the output afterwards would leave a different and steeper curve between the two limits – the same endpoints, a different vehicle.
  • Nonlinear, and deliberately carries no state space – nonlinear in delta and bilinear in (v, delta). A fabricated linear form would let model reduction merge matrices that do not describe this block.
  • Straight ahead is exact. At δ = 0 the curvature is exactly zero in every target, so a vehicle commanded straight does not drift.
  • Pairs with Unicycle Odometry: feed v and this block's omega into it to dead-reckon the pose, and keep Angle Wrap out of the path unless you want the heading wrapped – that block is the only one that wraps.

Code facts#

FactValue
registered typeRobotics/Planar_Kinematics/Ackermann_Steering_Model
familyRobotics/Planar_Kinematics
solver environment classICoreBlock_0_Robotics_1_Planar_Kinematics_2_Ackermann_Steering_Model
sourcesrc/ICoreSDK/ICoreBlockLibrary/Blocks/Robotics/Planar_Kinematics/Ackermann_Steering_Model/ICoreBlock_0_Robotics_1_Planar_Kinematics_2_Ackermann_Steering_Model.cpp
headersrc/ICoreSDK/ICoreBlockLibrary/Blocks/Robotics/Planar_Kinematics/Ackermann_Steering_Model/ICoreBlock_0_Robotics_1_Planar_Kinematics_2_Ackermann_Steering_Model.h
default size on canvas150 × 84 px
ports at insert2 in, 2 out
code generators implementedPython, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog, PLC Structured Text

Ports#

#DirectionSignal typeDescription label
1inICoreDoublev
2inICoreDoubledelta
3outICoreDoubleomega
4outICoreDoublekappa

Ports the constructor creates. A block whose port list changes with its configuration adds or removes ports at load time; the count above is the one a freshly inserted block has.

Configuration variables#

Config variableDefaultSimulink parameter
Wheelbase (m)2.7
Max Steer Angle (rad)0.6

Every block also carries Sampling Time (s) from ICoreBlockSolverEnvironment: zero or less inherits the solver's rate, a positive value runs the block at that period.

supportSupport::None
Simulink path
port-count rulePortsParam::None
SampleTime parameteryes

Caveat (shown to the user): no Simulink equivalent available: bicycle-model blocks ship in the Automated Driving Toolbox and the Robotics System Toolbox, and neither is installed on this machine (measured - the same probe that found the aerospace and fusion toolboxes absent). To rebuild it there: a Saturation on the angle, a Trigonometric Function set to tan, a Gain of 1/L, and a Product against the speed - in that order, since saturating anywhere else is a different curve

Catalog contract: src/ICoreSDK/ICoreCoder/ICoreCommandSystem/SimulinkBridge/ICoreSimulinkBlockCatalog.h

Description vs code#

The lists agree. check_block_descriptions.py finds no disagreement between the description's Ports, Parameters, Code export and Simulink bridge lists and the code's.

The verdict above is tools/docs/check_block_descriptions.py (P7.1), which compares LISTS. It cannot read a sentence: "stateless" on a block with a state, an initial-value semantic the recursion does not implement, a "not synthesizable" caveat the HDL banner contradicts. That is the agent audit (P7.3) on BLOCK_DESCRIPTION_AUDIT.md, and this tool's green is not a substitute for one.

File banner (developer view)#

The top comment of the block's .cpp — the maths, the realization and the export strategy, addressed to whoever changes it. It must not contradict the description above (P7.5).

Ackermann Steering Model — the kinematic bicycle d = clamp(delta, -Max Steer Angle, +Max Steer Angle) kappa = tan(d) / L omega = v * kappa

The model every path tracker is written against: two road wheels collapsed onto one virtual wheel at the axle centre. It is what turns a steering command into the yaw rate Unicycle_Odometry integrates, and the curvature output is what a pursuit controller compares against its own.

Three decisions, each of which survives into the generated code:

  • THE CLAMP IS ON THE ANGLE AND IS PART OF THE SPEC. tan diverges at pi/2 and no linkage

reaches it; clamping the INPUT to the mechanical stop is what the vehicle does, and it is what makes the block total -- no backend needs a domain guard, because tan is never handed anything outside a range the config validation pins inside (-pi/2, pi/2). Clamping the CURVATURE instead would be a different (steeper) curve between the limits, since tan is nonlinear.

  • 1/L IS FOLDED ONCE AT CONFIG LOAD. It is the block's only division; the C++ reference

multiplies by the same constant the generators bake in, so the block cannot end up a rounding away from its own export.

  • THE HDL TARGETS ARE SIMULATION-ONLY real. A tangent has no Q16.16 form to call. That

is the one thing this block does NOT inherit from Gear_Train, which stayed genuinely synthesizable -- the difference is the trigonometry, not the family.

Sample results#

Ackermann Steering Model — Repeating Sequence Stair: [-2 -1 -0.5 0 0.5 1 2 3], one entry per sampleAckermann Steering Model — Repeating Sequence Stair: [-2 -1 -0.5 0 0.5 1 2 3], one entry per sample-202012345t (s)in ICoreDouble-Out-0in ICoreDouble-Out-0out ICoreDouble-Out-0out ICoreDouble-Out-1
tin ICoreDouble-Out-0in ICoreDouble-Out-0out ICoreDouble-Out-0out ICoreDouble-Out-1
0-2-20.5068-0.2534
0.40.50.50.10120.2023
0.8-2-20.5068-0.2534
1.20.50.50.10120.2023
1.6-2-20.5068-0.2534
20.50.50.10120.2023
2.4-2-20.5068-0.2534
2.80.50.50.10120.2023
3.2-2-20.5068-0.2534
3.60.50.50.10120.2023
4-2-20.5068-0.2534
4.40.50.50.10120.2023
4.8-2-20.5068-0.2534
5.20.50.50.10120.2023

Every 4th of 60 samples, from the table stimulus.

The same rig also ran:

StimulusWhat it isOutput range
impulseImpulse: one sample of 1 at k = 5, 0 elsewhere (Repeating Sequence Stair)0 … 0.2534
rampRamp: slope 1 from t = 00 … 1.47
sineSine Wave: amplitude 1, 2 rad/s, no phase, no bias0 … 0.2534
stepStep: 0 -> 1 at t = 1 s0 … 0.2534

Plotted: table — Repeating Sequence Stair: [-2 -1 -0.5 0 0.5 1 2 3], one entry per sample

Category static · sample time 0.1 · 60 steps · commit ccf005c8 · produced by docsSample --out <folder> --steps 60 · data docs/generated/samples/Robotics__Planar_Kinematics__Ackermann_Steering_Model.json · the SVG is generated from those numbers by tools/docs/plot_svg.py, so it is a run and not a drawing (R-D10).