Skip to content

Model URDF <transmission> blocks in the BB DSL #108

@jimsynz

Description

@jimsynz

Problem

URDF's <transmission> element couples a joint to one or more actuators, capturing things like mechanical reduction (gear ratio), polarity, and the hardware interface kind (position/velocity/effort). The URDF importer (#101, PR #107) currently surfaces a <transmission> block as a parser warning and emits nothing for it, leaving the user to wire up the actuator manually.

This is the right call for v1 of the importer because BB doesn't have a transmission abstraction yet — but the absence of one bites in a few places:

  • Drivers reimplement the same knobs. BB.Servo.Feetech and BB.Servo.Robotis both already carry reverse? and an implicit center_angle offset (search radians_to_joint_angle). Every new driver re-derives the same conversion. None of them has a place for mechanical reduction yet.
  • Importing real URDFs loses information. UR5, Panda, etc. all ship transmissions that name the actuator and the reduction. Right now that data is dropped on import.
  • mix bb.to_urdf can't round-trip transmissions — the exporter has nowhere to read them from.

Things URDF transmissions carry

URDF concept What it does Maps to (today)
<type>SimpleTransmission</type> 1 joint ↔ 1 actuator nothing
mechanicalReduction gear ratio between actuator and joint nothing
offset encoder offset (zero-point calibration) per-driver center_angle (Feetech/Robotis)
polarity / sign flip direction reversal per-driver reverse?
<hardwareInterface> position vs velocity vs effort interface implicit in actuator's command messages
DifferentialTransmission 2 joints ↔ 2 actuators with cross-coupling partial: BB.Sensor.Mimic covers one direction
FourBarLinkageTransmission mechanically-linked joint pair nothing
PR2-style custom transmissions vendor-specific nothing

Sketches of possible abstractions

I haven't picked one. Each has tradeoffs worth talking through:

A. Transmission as a DSL entity inside joint

joint :shoulder do
  type :revolute
  transmission do
    reduction 50
    offset ~u(45 degree)
    reversed? true
  end
  actuator :motor, {BB.Servo.Feetech, servo_id: 1, controller: :bus}
end

Drivers stop carrying reverse? / center_angle and instead read the transmission off the joint. Fits BB's spatial-tree shape. Doesn't naturally handle coupled transmissions.

B. Transmission as actuator decoration / wrapper

joint :shoulder do
  actuator :motor, {BB.Transmission.Simple,
    reduction: 50,
    offset: ~u(45 degree),
    actuator: {BB.Servo.Feetech, servo_id: 1}
  }
end

Composable; each transmission type is a separate module implementing the BB.Actuator behaviour. Drivers stay narrow. Reads a bit deeper because of nesting, and the wrapper has to forward safety/disarm correctly.

C. Top-level transmissions section mirroring URDF

topology do
  joint :shoulder doend
end

transmissions do
  transmission :shoulder_trans do
    type :simple
    joint :shoulder
    actuator :shoulder_motor
    reduction 50
  end
end

URDF-faithful and the only one that naturally handles N-joint ↔ M-actuator coupled cases. Breaks BB's locality principle — supervision tree no longer reflects which actuator drives which joint.

D. Just add knobs to existing drivers

Add reduction:, offset:, reversed?: to every driver's option schema. Cheapest. Pushes the duplication further but doesn't introduce a new concept.

My weak preference is A + a thin version of B for coupled cases — give the simple per-joint case a DSL entity, and provide BB.Transmission.Differential etc. as actuator wrappers for the multi-joint cases — but I'd want to talk it through.

Open questions

  • Does this subsume BB.Sensor.Mimic? Mimic does soft coupling via the message bus (state mirroring after the fact). A transmission models hard mechanical coupling at command time. They might be the same abstraction at the right level, or they might be two separate concepts that occasionally do similar things. URDF treats <mimic> and <transmission> as orthogonal.
  • Where does the math live? Inside the actuator (driver applies reduction/offset before writing to hardware) or in a pipeline stage that transforms Command messages before they reach the actuator? The pipeline approach makes it easier to log "the user asked for X, the motor was commanded Y."
  • Backwards compatibility for reverse? / center_angle. If we lift these into a transmission entity, do existing driver options get deprecated, kept as aliases, or both?
  • Round-trip with mix bb.to_urdf. Should the exporter emit <transmission> blocks when a transmission entity is present? Probably yes, at least for the simple case.
  • Importer behaviour after this lands. mix bb.from_urdf should emit transmissions instead of warning-and-skipping. The importer change is small; the question is what shape it's emitting into.
  • Simulation. BB.Sim.Actuator should presumably respect the transmission so simulated motion matches what the real hardware would do.

Related code

  • Importer warning: lib/bb/urdf/parser.ex (skipping <transmission> …)
  • Per-driver knobs that overlap: bb_servo_feetech/lib/bb/servo/feetech/actuator.ex, bb_servo_robotis/lib/bb/servo/robotis/actuator.ex (both have reverse?; both derive center_angle in their controller)
  • BB.Sensor.Mimic (lib/bb/sensor/mimic.ex) — the closest existing thing to a transmission concept
  • DSL reference for joints/actuators: documentation/dsls/DSL-BB.md
  • Importer PR for context on the importer side: feat: add mix bb.from_urdf URDF importer #107

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions