Reference for OctoMY™ Lobes — pluggable controllers that translate high-level commands into actuator control.
Heads up — type names and placement
Lobe types are described by
FixoLobeTyperecords with two notable fields:outputPorts(aQVector<FixoPort>declaring named, semantically-tagged outputs) andplacement(one ofboard-only,native-only,both). The placement field is what decides whether the lobe runs in the AVR firmware ELF, in the node-side.so, or either. Stanza templates use the output-port semantic tags to auto-wire to actuators via Jaccard similarity. The ground-truth references are Fixo Data Model and Stanza Templates; the conceptual model below applies with these extra knobs.
Did You Know?
Lobes are named after brain regions because they encapsulate decision-making that can run either on the OctoMY™ Agent or directly on the controller hardware. When running on-board the Arduino, lobes provide lower latency, reduced power draw, and resilience — your robot can keep walking even if it briefly loses connection to the Agent. The
placementfield onFixoLobeTypeis how each lobe declares which environment it can run in.
Lobes are named after the distinct regions in our brain dedicated to specific tasks. In OctoMY™, Lobes represent real-time processing units that take input from sensors and user controls and generate output to actuators.
A Lobe encapsulates decision-making into a unit that can run in different environments:
Since the lobe API unification, both environments run the same C++
template from src/libs/libfixo/fixo/lobes/ — the host-side .so
and the AVR firmware are compiled from one header, and the parity
harness (test/testLobeParity/) asserts byte-identical actuator
output between them.
When Lobes run on the controller hardware:
Since Lobes are well-defined, they can be tested in simulation environments before deployment to real hardware.
A simple car steering lobe (steering_throttle):
In this basic example, the Lobe simply passes throttle input to the motor and steering angle to the steering servo. The key is that it sets up expectations - this is made to control any car-like robot, allowing a fancy UI with steering wheel and foot pedal to be created.
Adding IMU and GPS enables advanced processing:
Now the Lobe can perform:
This takes a basic toy to a full race-car with just firmware changes.
Lobes provide abstraction between movement intent and actuator control:
These are the shipping lobe templates. The lobeType string is what a
preset stanza names; the header is the single source compiled into
both targets.
| lobeType | Header | Inputs | Outputs |
|---|---|---|---|
identity |
IdentityLobe.hpp |
one per actuator | passthrough to each actuator |
trim |
TrimLobe.hpp |
one per actuator + trim pot sensor | passthrough with wire-space trim offset on one axis |
idle |
IdleLobe.hpp |
none | none (explicit no-op for each channel's idle stanza) |
steering_throttle |
SteeringThrottleLobe.hpp |
forward, yaw | throttle servo, steering servo |
differential_drive |
DifferentialDriveLobe.hpp |
forward, yaw, parking_brake, max_step_rate | two accumulating stepper step targets |
legged_gait |
LeggedGaitLobe.hpp |
phase_offset, velocity, turn, body_pitch, body_roll, step_height | 3 x legCount servo joints (coxa/femur/tibia per leg), trot pattern |
legged_gait_2d |
LeggedGaitLobeIntent2D.hpp |
forward, yaw | same as legged_gait (intent-face wrapper) |
Configuration constants (e.g. legCount, maxStepRate) live on the
generated Config; the Phase-18 parameter-binding machinery decides per
target whether each is compile-time baked or runtime-adjustable.
A lobe is a header-only struct with a nested Instance<Config>
template — no QObject, no virtuals, no allocation:
struct MyLobe {
static constexpr LobeId id = LobeId::IDENTITY;
template<typename Config>
struct Instance {
using Inputs = typename Config::LobeInputs;
using Outputs = typename Config::LobeOutputs;
void init();
void update(const Inputs &in, Outputs &out, uint16_t deltaMs);
// Phase-7 transition hooks (usually forwarded to
// DefaultLobeTransition<Outputs>):
void readCurrentState(const Outputs ¤t);
void interpolate(const Outputs &target, double t, Outputs &out) const;
};
};
Key facts:
Inputs/Outputs are
LobeOutputBuffer instantiations whose slots each carry a
ValueRep (WORD servo pulse, DWORD step count, …). Lobes read and
write through the semantic accessors — semantic<I>(),
setSemantic<I>(v), and the runtime-indexed semanticAt(i) for
uniform buffers — which route every value through the shared
ValueCodec, the single definition of the semantic-to-wire mapping.fixo/LobeMath.hpp. This keeps host and AVR output byte-identical.ParamInfo records in the header — the host UI
reads them via native introspection; there is no duplicate
parameter list anywhere else."rep": "sword" etc.); when every
input of a stanza declares one, codegen builds the lobe's input
buffer from the preset. Otherwise the lobe's contract default
applies.Three steps (see the Custom Lobes tutorial for the full walk-through):
src/libs/libfixo/fixo/lobes/.kUnifiedLobeSpecs
(fixo/controller/FirmwareGenerator.cpp) and a FixoLobeType
record in the registry. An unregistered lobe type in a preset is a
loud build failure — there is no silent fallback.test/testLobeParity/.Lobes are selected per stanza in a preset — the stanza names the
lobeType, maps its outputs onto actuators, and declares the logical
inputs the remote drives. Switching stanzas at runtime switches lobes,
optionally blending outputs over a transition duration. See
Stanza Templates for the preset side and the
Fixo Data Model for the full record shapes.
| Issue | Cause | Solution |
|---|---|---|
| Build fails with "unknown lobe type" | Preset names a type missing from kUnifiedLobeSpecs |
Register the type or fix the preset |
| Robot goes wrong direction | Motor wiring | Set invert flags |
| Robot curves when going straight | Motor speed difference | Calibrate motors |
| Walking robot falls | Wrong gait timing | Tune step timing / step_height |
| Host and board disagree | A lobe bypassing the semantic accessors or using double/libm math | Fix the lobe; testLobeParity pins the contract |