Two Mass Drivetrain — Robotics/Actuators And Drivetrain
Robotics/Actuators_And_Drivetrain/Two_Mass_Drivetrain · 2 input / 3 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.
Two Mass Drivetrain
Robotics / Actuators And Drivetrain
A motor inertia and a load inertia coupled by a shaft that is not rigid – the flexible-joint plant, and the reason a position loop that is perfectly stable on the bench oscillates once the load is bolted on.
Four states, x = [θm, ωm, θl, ωl]:
- Jmωm' = τm − k(θm−θl) − c(ωm−ωl) − bmωm
- Jlωl' = k(θm−θl) + c(ωm−ωl) − blωl − τload
The resonance sits at ωn = √(k(1/Jm + 1/Jl)) with damping ratio ζ = (c/2)· √((1/Jm + 1/Jl)/k) – a frequency neither inertia carries on its own, which is exactly why two separate integrators cannot stand in for this block.
Ports
- tau_m – the motor torque applied to the motor inertia (N·m), a scalar [1,1]. This is one drivetrain, so use one block per axis.
- tau_load – the external load torque (N·m), a scalar. It opposes motion: a positive value decelerates the load, matching the sign convention DC Motor uses for its own load port.
- w_m – motor speed (rad/s). This is what the motor encoder reads, and what a naive loop closes on.
- w_l – load speed (rad/s). This is what the machine actually does.
- tau_shaft – the torque the coupling transmits, k(θm−θl) + c(ωm−ωl) (N·m). Exposed as a port because it is the quantity that breaks couplings, and because it is the one output that reads all four states.
Parameters
- Motor Inertia (kg.m^2) – Jm, positive. Rotor plus everything rigidly attached to it.
- Load Inertia (kg.m^2) – Jl, positive.
- Shaft Stiffness (N.m/rad) – k, zero or more. At exactly zero the two inertias are uncoupled and each simply integrates its own torque – a legal degenerate case, and a useful sanity check.
- Shaft Damping (N.m.s/rad) – c, zero or more. This is the internal damping of the coupling, acting on the speed difference; it is what limits the resonant peak.
- Motor Damping (N.m.s) – bm, zero or more, acting on motor speed against ground (bearings, windage).
- Load Damping (N.m.s) – bl, zero or more, the same against the load.
- Initial Shaft Twist (rad) – the initial θm−θl, i.e. a pre-wound shaft. Nonzero here is what makes the plant ring from the first sample.
- Initial Motor Speed (rad/s) / Initial Load Speed (rad/s) – the two speed states at t = 0.
- 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 block exports the discretized state space, by whichever method the model selects (ZOH, FOH, Tustin, Matched, either Euler) – the coefficients are baked in at export time and are not tunable on the generated core, so re-export after changing a drivetrain.
Every row is emitted as a multiply-accumulate over the four states and two inputs, including the entries that are zero, so all ten backends sum the terms in the same order. Floating-point addition is not associative, and that uniformity is what keeps the software targets bit-identical to the C++ reference.
The three HDL targets are synthesizable Q16.16 at ordinary drivetrain parameters – no division and nothing transcendental survives into the body. That is a property of the discretized coefficients rather than of the block: a very small inertia drives 1/J, and with it Bd, up by orders of magnitude, and a shaft stiff enough to put the resonance near the sample rate does the same to Ad. Check the numbers if either applies.
Simulink bridge
No equivalent (Support::None). Drivetrain blocks ship in
Simscape Driveline, which is not installed on this machine
(measured – isfolder([matlabroot '/toolbox/simscape']) is
false), and a physical-network block would not map onto a signal-flow port list
in any case. A model that must cross can carry the same plant as a
State-Space block: A, B, C, D exactly as printed at the top of this
block's source, with states
(θm, ωm, θl,
ωl).
Notes
- Linear and stateful, and it carries a proper continuous state space – so model reduction can absorb it into a neighbouring plant in series, parallel or feedback, and the discretization follows the model's own method rather than a hardcoded Euler step.
- The plant is marginally stable, deliberately. Only the shaft difference enters the equations, so the rigid-body mode (both shafts turning together) has no restoring term and the two absolute positions random-walk under a zero-mean torque. That is the machine, not a defect – an unloaded drivetrain does drift. It is also why the initial condition is a twist rather than two absolute angles: only the difference is observable.
- Damping is split three ways on purpose. c acts on the speed difference (inside the coupling) while bm and bl act against ground. They are not interchangeable: raising c damps the resonance, raising bm or bl mostly just wastes torque.
- No backlash, no friction, no torque limit. Put Backlash or Stribeck Friction on the shaft and Saturation on the torque if the mechanism needs them – keeping them out is what leaves this block linear, and with it everything in the first note.
- Pairs with DC Motor upstream: its w output is the motor side, its tau_load port takes the reaction. Together they are the plant a cascade loop is actually tuned against.
Code facts#
| Fact | Value |
|---|---|
| registered type | Robotics/Actuators_And_Drivetrain/Two_Mass_Drivetrain |
| family | Robotics/Actuators_And_Drivetrain |
| solver environment class | ICoreBlock_0_Robotics_1_Actuators_And_Drivetrain_2_Two_Mass_Drivetrain |
| source | src/ICoreSDK/ICoreBlockLibrary/Blocks/Robotics/Actuators_And_Drivetrain/Two_Mass_Drivetrain/ICoreBlock_0_Robotics_1_Actuators_And_Drivetrain_2_Two_Mass_Drivetrain.cpp |
| header | src/ICoreSDK/ICoreBlockLibrary/Blocks/Robotics/Actuators_And_Drivetrain/Two_Mass_Drivetrain/ICoreBlock_0_Robotics_1_Actuators_And_Drivetrain_2_Two_Mass_Drivetrain.h |
| default size on canvas | 148 × 100 px |
| ports at insert | 2 in, 3 out |
| code generators implemented | Python, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog, PLC Structured Text |
Ports#
| # | Direction | Signal type | Description label |
|---|---|---|---|
| 1 | in | ICoreDouble | tau_m |
| 2 | in | ICoreDouble | tau_load |
| 3 | out | ICoreDouble | w_m |
| 4 | out | ICoreDouble | w_l |
| 5 | out | ICoreDouble | tau_shaft |
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 variable | Default | Simulink parameter |
|---|---|---|
Motor Inertia (kg.m^2) | 0.01 | — |
Load Inertia (kg.m^2) | 0.02 | — |
Shaft Stiffness (N.m/rad) | 100 | — |
Shaft Damping (N.m.s/rad) | 0.05 | — |
Motor Damping (N.m.s) | 0.005 | — |
Load Damping (N.m.s) | 0.005 | — |
Initial Shaft Twist (rad) | 0 | — |
Initial Motor Speed (rad/s) | 0 | — |
Initial Load Speed (rad/s) | 0 | — |
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.
Simulink bridge#
| support | Support::None |
| Simulink path | — |
| port-count rule | PortsParam::None |
SampleTime parameter | yes |
Caveat (shown to the user): no Simulink equivalent available: drivetrain blocks ship in Simscape Driveline, which is not installed on this machine (measured), and a physical-network block carries no signal-flow port list to map onto. A model that must cross can carry the same plant as a State-Space block with states (theta_m, w_m, theta_l, w_l), inputs (tau_m, tau_load) and outputs (w_m, w_l, tau_shaft) - the matrices are printed at the top of this block's source
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).
Two Mass Drivetrain — the flexible joint, and why position loops oscillate theta_m' = w_m Jm w_m' = tau_m - k(theta_m - theta_l) - c(w_m - w_l) - bm w_m theta_l' = w_l Jl w_l' = k(theta_m - theta_l) + c(w_m - w_l) - bl w_l - tau_load
x = [theta_m; w_m; theta_l; w_l], u = [tau_m; tau_load], y = [w_m; w_l; tau_shaft], with tau_shaft = k(theta_m - theta_l) + c(w_m - w_l) -- what the coupling actually transmits.
⚠ SAME STATE-SPACE-FIRST LIFECYCLE AS DC_Motor, inherited rather than re-derived: seed the continuous set in the constructor from the block's own defaults, re-derive it in loadBlockConfig(), take both compute pairs FROM it, and let discretize() decide the export. That block's row was left free for this one to reuse, which is the only reason this file is short enough to read.
⚠ THE RESONANCE IS THE POINT. w_n = sqrt(k(1/Jm + 1/Jl)) with damping ratio zeta = (c/2)*sqrt((1/Jm + 1/Jl)/k). Both are properties of the CONFIG, and both matter to export: a stiff shaft on small inertias puts w_n above the sample rate, at which the discretized set stops describing the plant and no backend can rescue it. The description says so, and the block reports it rather than pretending.
⚠ THE PLANT IS MARGINALLY STABLE BY CONSTRUCTION and that is physics: only theta_m - theta_l enters the equations, so the rigid-body mode is a free integrator and the two positions random-walk under zero-mean torque. It is also why the seed is a TWIST rather than two angles.
Sample results#
The same rig also ran:
| Stimulus | What it is | Output range |
|---|---|---|
impulse | Impulse: one sample of 1 at k = 5, 0 elsewhere (Repeating Sequence Stair) | -1.78e113 … 8.625e157 |
ramp | Ramp: slope 1 from t = 0 | -7.569e168 … 3.296e159 |
sine | Sine Wave: amplitude 1, 2 rad/s, no phase, no bias | -1.504e169 … 6.547e159 |
table | Repeating Sequence Stair: [-2 -1 -0.5 0 0.5 1 2 3], one entry per sample | -3.138e174 … 2.302e138 |
Plotted: step — Step: 0 -> 1 at t = 1 s
Category dynamic · sample time 0.1 · 60 steps · commit ccf005c8 · produced by docsSample --out <folder> --steps 60 · data docs/generated/samples/Robotics__Actuators_And_Drivetrain__Two_Mass_Drivetrain.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).