Estimate Center Of Gravity — Robotics/Mass Properties
Robotics/Mass_Properties/Estimate_Center_Of_Gravity · 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.
Estimate Center Of Gravity
Robotics / Mass Properties
Interpolates the centre of gravity between its empty and full values against the current mass, and reports its rate of change from the mass rate. With λ the clamped position of the mass between the two:
- λ = clamp((m − mempty) / (mfull − mempty), 0, 1)
- CG = (1 − λ)·cg_empty + λ·cg_full, entry by entry
- CG_dot = ((cg_full − cg_empty) / (mfull − mempty))·ṁ
Ports
- Mass – the current mass m, a scalar. It is the only thing λ is computed from.
- M_dot – the mass rate ṁ, a scalar, in mass units per second.
- CG – the interpolated centre of gravity, the same size as the two tables ([3,1] by default).
- CG_dot – its rate of change, the same size again.
Parameters
- Empty Mass – mempty, a scalar. Defaults to 1.
- Full Mass – mfull, a scalar. Defaults to 2, and it must differ from the empty mass: the two are the ends of a division.
- Empty Center Of Gravity – the centre of gravity at the empty mass, any
size [m,n]. Defaults to
[0.5; 0.5; 0.5]. - Full Center Of Gravity – the centre of gravity at the full mass, and it
must be the same size as the entry above. Defaults to
[1; 1; 1]. - 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.
Both tables and both masses are baked into the emitted arithmetic at export time rather than exposed as tunables, and so is the per-entry slope derived from them: they describe which vehicle this is, which is structure and not a knob, and deriving the slope once is also what keeps the ten backends from differing by an association order. All are printed at 17 significant digits.
The three hardware targets are simulation-only. The clamp and the interpolation are multiplies and adds, which a Q16.16 datapath has, but λ needs a division by a difference of two masses – and replacing that division by a pre-computed reciprocal is exactly the change that stops this block reproducing its Simulink counterpart to the last bit. Values convert at the port boundary and the arithmetic runs in floating point.
Simulink bridge
Import and export, mapped to Aerospace Blockset's
aerolibbdyn/Estimate Center of Gravity. All four configuration values cross
straight through: Empty Mass → emass,
Full Mass → fmass,
Empty Center Of Gravity → ecg and
Full Center Of Gravity → fcg.
The Simulink block defines no SampleTime parameter,
measured, so the rate stays on the ICore side.
Notes
- Algebraic and stateless: both outputs depend on this sample alone.
- Not linear – the clamp is what makes it so – so the block carries no state space and model reduction correctly reports it as unmergeable.
- ⚠ The two outputs do not agree about the ends. CG clamps outside the mass range; CG_dot does not, and keeps reporting a rate of change at masses where the value it belongs to has stopped changing. That is what the Aerospace Blockset block does, measured, and not an oversight here.
- Nothing checks that the mass rate is the derivative of the mass. They arrive on separate ports and the block trusts both.
- Verified against R2026a over two tables, seven masses and seven mass rates: all 63 interpolated values and all 48 rates agree to the last bit.
Code facts#
| Fact | Value |
|---|---|
| registered type | Robotics/Mass_Properties/Estimate_Center_Of_Gravity |
| family | Robotics/Mass_Properties |
| solver environment class | ICoreBlock_0_Robotics_1_Mass_Properties_2_Estimate_Center_Of_Gravity |
| source | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Robotics/Mass_Properties/Estimate_Center_Of_Gravity/ICoreBlock_0_Robotics_1_Mass_Properties_2_Estimate_Center_Of_Gravity.cpp |
| header | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Robotics/Mass_Properties/Estimate_Center_Of_Gravity/ICoreBlock_0_Robotics_1_Mass_Properties_2_Estimate_Center_Of_Gravity.h |
| default size on canvas | 170 × 86 px |
| ports at insert | 2 in, 2 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 | Mass |
| 2 | in | ICoreDouble | M_dot |
| 3 | out | ICoreDouble | CG |
| 4 | out | ICoreDouble | CG_dot |
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 |
|---|---|---|
Empty Mass | DEFAULT_EMPTY_MASS | — |
Full Mass | DEFAULT_FULL_MASS | — |
Empty Center Of Gravity | DEFAULT_EMPTY | — |
Full Center Of Gravity | DEFAULT_FULL | — |
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::Both |
| Simulink path | aerolibbdyn/Estimate Center of Gravity |
| port-count rule | PortsParam::None |
SampleTime parameter | no — the counterpart defines none; the rate stays on the ICore side |
| ICore config | Simulink parameter | Value translation |
|---|---|---|
CONFIG_EMPTY_MASS.c_str() | emass | passes through |
CONFIG_FULL_MASS.c_str() | fmass | passes through |
CONFIG_EMPTY.c_str() | ecg | passes through |
CONFIG_FULL.c_str() | fcg | passes through |
Caveat (shown to the user): the mass and the mass rate are scalars on separate ports, and the two outputs are the size of the two tables. ⚠ The interpolated value CLAMPS outside the mass range and the rate does NOT -- that is measured, not an oversight, so a user reading a rate at a clamped mass is reading what Simulink reports too. The Simulink block has no SampleTime, so the rate stays on the ICore side
Catalog contract: src/ICoreBlocks/ICoreCoder/ICoreCommandSystem/SimulinkBridge/ICoreSimulinkBlockCatalog.h
Description vs code#
The checker has a blind spot here — it could not resolve something (a grouped port bullet, a computed config name), which is reported and never counted as a pass. A reader has to settle it:
B04 Simulink params rule(s) this tool cannot resolve
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).
Estimate Center Of Gravity -- the centre of gravity interpolated between an empty and a full mass lambda = clamp((m - m_empty) / (m_full - m_empty), 0, 1) CG = (1 - lambda) * cg_empty + lambda * cg_full CG_dot = ((cg_full - cg_empty) / (m_full - m_empty)) * m_dot
⚠ THE TWO OUTPUTS DO NOT AGREE ABOUT THE ENDS, AND THAT IS THE BLOCK. CG clamps -- below the empty mass it holds cg_empty, above the full mass it holds cg_full -- and CG_dot does not, because it is a constant slope times the mass rate and never sees the mass at all. Measured in R2026a by looking inside the masked subsystem AND by driving it: the clamp is a Prelookup with
ExtrapMethod = Clip, the rate is a Constant into an element-wise Product, and over a mass sweep from 0.4 to 3 against a range of [1.1, 2.3] the rate is the same at both ends as it is in the middle.⚠ THE SPELLING IS THE MEASURED ONE, AND THE OBVIOUS ONE IS NOT. Over 63 interpolated values and 48 rates -- two tables, seven masses, seven mass rates --
(1 - lambda) * E + lambda * Freproduces the block BIT FOR BIT.E + lambda * (F - E), which is algebraically the same thing, moves 14 of the 63 by one unit in the last place, and replacing the division by a pre-computed reciprocal moves 4 more. Every backend therefore writes it this way round.Elementwise over whatever [m,n] the two tables are, which is what lets one arrangement serve a [3,1] table here and a differently shaped one in its sibling. ALGEBRAIC and STATELESS, and the clamp makes it nonlinear, so no state space.
Sample results#
| t | in ICoreDouble-Out-0 | in ICoreDouble-Out-0 | out ICoreDouble-Out-0 [3x1] entry 0 | out ICoreDouble-Out-1 [3x1] entry 0 |
|---|---|---|---|---|
| 0 | -2 | -2 | [0.5, 0.5, 0.5] | [-1, -1, -1] |
| 0.4 | 0.5 | 0.5 | [0.5, 0.5, 0.5] | [0.25, 0.25, 0.25] |
| 0.8 | -2 | -2 | [0.5, 0.5, 0.5] | [-1, -1, -1] |
| 1.2 | 0.5 | 0.5 | [0.5, 0.5, 0.5] | [0.25, 0.25, 0.25] |
| 1.6 | -2 | -2 | [0.5, 0.5, 0.5] | [-1, -1, -1] |
| 2 | 0.5 | 0.5 | [0.5, 0.5, 0.5] | [0.25, 0.25, 0.25] |
| 2.4 | -2 | -2 | [0.5, 0.5, 0.5] | [-1, -1, -1] |
| 2.8 | 0.5 | 0.5 | [0.5, 0.5, 0.5] | [0.25, 0.25, 0.25] |
| 3.2 | -2 | -2 | [0.5, 0.5, 0.5] | [-1, -1, -1] |
| 3.6 | 0.5 | 0.5 | [0.5, 0.5, 0.5] | [0.25, 0.25, 0.25] |
| 4 | -2 | -2 | [0.5, 0.5, 0.5] | [-1, -1, -1] |
| 4.4 | 0.5 | 0.5 | [0.5, 0.5, 0.5] | [0.25, 0.25, 0.25] |
| 4.8 | -2 | -2 | [0.5, 0.5, 0.5] | [-1, -1, -1] |
| 5.2 | 0.5 | 0.5 | [0.5, 0.5, 0.5] | [0.25, 0.25, 0.25] |
Every 4th of 60 samples, from the table stimulus.
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) | 0.5 … 0.5 |
ramp | Ramp: slope 1 from t = 0 | 0.5 … 1 |
sine | Sine Wave: amplitude 1, 2 rad/s, no phase, no bias | 0.5 … 0.5 |
step | Step: 0 -> 1 at t = 1 s | 0.5 … 0.5 |
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 d36e1255b059647c8b7bd9d16f0bc9f5849272eb · produced by docsSample --out <folder> --blocks Estimate_Center_Of_Gravity Estimate_Inertia_Tensor --steps 60 · data docs/generated/samples/Robotics__Mass_Properties__Estimate_Center_Of_Gravity.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).