Quaternion Multiply — Robotics/Orientation 3D
Robotics/Orientation_3D/Quaternion_Multiply · 2 input / 1 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.
Quaternion Multiply
Robotics / Orientation 3D
The Hamilton product of two unit-or-not quaternions, q = q1 ⊗ q2, both stored scalar-first as [w x y z]T:
- w = w1·w2 − x1·x2 − y1·y2 − z1·z2
- x = w1·x2 + x1·w2 + y1·z2 − z1·y2
- y = w1·y2 − x1·z2 + y1·w2 + z1·x2
- z = w1·z2 + x1·y2 − y1·x2 + z1·w2
Composing two rotations is what this is for: if q1 and q2 are unit quaternions, q1 ⊗ q2 is the rotation that applies q2 first and then q1. The product is not commutative – q2 ⊗ q1 is a different rotation – so the port order is part of the answer.
Ports
- q1 – the left factor, a [4,1] column [w1 x1 y1 z1]T. Applied second when the two are rotations.
- q2 – the right factor, also [4,1]. Applied first.
- q – the product q1 ⊗ q2, [4,1]. Its size is fixed, not inherited: a quaternion is four numbers.
Parameters
- 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. There is no tunable parameter, because the block has no parameter at all – both operands arrive on ports.
The three HDL targets are genuinely synthesizable Q16.16, not simulation-only: the product is sixteen multiplies and twelve adds, with no division, square root or trigonometry anywhere.
Where they differ is the accumulator, and it is worth knowing which one you
are exporting to. Verilog and SystemVerilog sum each output
component's four products in a double-width register
(2*ICORE_WIDTH) and shift back to Q16.16 once, so the
intermediate sum keeps its precision. VHDL accumulates in the shared
Fx process variable, which is Q16.16, so every product is
resized to Q16.16 as it is added. The three therefore agree to within the low
bits rather than bit-exactly, and VHDL is the one that rounds soonest.
The term order is identical in all ten backends, so what is left is this
rounding and nothing else.
Simulink bridge
None, and the reason is measured rather than assumed: every Simulink
quaternion block lives in the Aerospace Blockset or the Robotics
System Toolbox, and neither is installed on this machine (aero,
aeroblks, robotics, nav and
fusion are all absent from matlabroot/toolbox). A model
carrying this block exports with the block reported rather than silently
dropped.
Its companion Quaternion Conjugate needs no block of its own: a Gain with the fixed matrix diag(1, −1, −1, −1) computes it, and Normalizer in L2 mode normalizes one.
Notes
- Algebraic and stateless: the output depends only on the current inputs, so the block cannot break an algebraic loop.
- Scalar-first (w, x, y, z), Hamilton convention – the PyTorch/Eigen/textbook order and sign, not the JPL convention, which stores the scalar last and flips the sign of the cross term. Nothing in a signal says which convention it carries, so a mismatch here produces plausible numbers that are wrong: check the source of any quaternion wired in.
- Bilinear, so deliberately no state space – it is linear in either operand with the other held fixed, and nonlinear in the pair. A fabricated linear form would let model reduction merge matrices that do not describe this block.
- Normalization is not performed. The product of two unit quaternions is a unit quaternion in exact arithmetic; over a long chain, rounding drifts the norm. Follow the chain with Normalizer (L2) when that matters – and note the formula above is the contract for non-unit input too, which is what makes the block usable for general quaternion algebra.
Code facts#
| Fact | Value |
|---|---|
| registered type | Robotics/Orientation_3D/Quaternion_Multiply |
| family | Robotics/Orientation_3D |
| solver environment class | ICoreBlock_0_Robotics_1_Orientation_3D_2_Quaternion_Multiply |
| source | src/ICoreSDK/ICoreBlockLibrary/Blocks/Robotics/Orientation_3D/Quaternion_Multiply/ICoreBlock_0_Robotics_1_Orientation_3D_2_Quaternion_Multiply.cpp |
| header | src/ICoreSDK/ICoreBlockLibrary/Blocks/Robotics/Orientation_3D/Quaternion_Multiply/ICoreBlock_0_Robotics_1_Orientation_3D_2_Quaternion_Multiply.h |
| default size on canvas | 126 × 84 px |
| ports at insert | 2 in, 1 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 | q1 |
| 2 | in | ICoreDouble | q2 |
| 3 | out | ICoreDouble | q |
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#
No config variable beyond the Sampling Time (s) every block carries.
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: every quaternion block ships in the Aerospace Blockset or the Robotics System Toolbox, neither of which is installed. Rebuild the product on the Simulink side from Product and Sum blocks, keeping the scalar-first (w, x, y, z) Hamilton convention this block uses
Catalog contract: src/ICoreSDK/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:
B0every stimulus in the sample errored — cross-checks skipped
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).
Quaternion Multiply — the Hamilton product q = q1 (x) q2, scalar-first [4,1] w = w1*w2 - x1*x2 - y1*y2 - z1*z2 x = w1*x2 + x1*w2 + y1*z2 - z1*y2 y = w1*y2 - x1*z2 + y1*w2 + z1*x2 z = w1*z2 + x1*y2 - y1*x2 + z1*w2
Robotics/Orientation_3D's first block, and the one the rest of the family composes: rotating a vector is this product twice, and integrating a gyro is this product plus a state and a norm. Conventions (scalar-first, Hamilton) come from the ledger in BLOCKS_TO_ADD.md -- see the header for why they are the whole block.
⚠ THE TERM ORDER BELOW IS THE CONTRACT, and every one of the ten backends spells each component in exactly this order, left to right. Floating-point addition is not associative, so a backend that grouped the four terms differently would disagree with this reference in the last ulp on most samples -- a row that moves for a reason having nothing to do with the export. It costs nothing to keep them identical, so they are kept identical.
No config of any kind, so no loadBlockConfig() call is needed anywhere: the generators bake in no constant and read no config map (§3's rule bites only blocks whose generators do).
Sample results#
No stimulus produced a sampled output in this rig — Invalid input size at Quaternion Multiply block: ICore Blocks/Home/Quaternion Multiply. That is a fact about the single-block rig, not a verdict on the block: an offline batch fit, a block whose output only appears at onSolverFinish, or one that needs a driven environment cannot be exercised alone.
Category unsampled · sample time 0.1 · 60 steps · commit ccf005c8 · produced by docsSample --out <folder> --steps 60
Sample data: docs/generated/samples/Robotics__Orientation_3D__Quaternion_Multiply.json