Skip to content

Add DualDriveDeltaMotor and DualDriveDeltaArm modules#210

Draft
JensOgorek wants to merge 2 commits into
mainfrom
dual_drive_delta_arm
Draft

Add DualDriveDeltaMotor and DualDriveDeltaArm modules#210
JensOgorek wants to merge 2 commits into
mainfrom
dual_drive_delta_arm

Conversation

@JensOgorek
Copy link
Copy Markdown
Collaborator

Motivation

Add Lizard support for the CAN-based dual-drive delta arm (two-motor positioning arm with hall feedback and per-side endstops) so it can be used through the standard Lizard module interface.

This PR is the second slice of #194 and stacks on top of #209: it ships the delta-arm parts (delta motor + arm kinematics/calibration). The shared DualDriveMotorBase is duplicated here against main so the branch can be reviewed independently — once #209 merges, merging main back into this branch resolves the base class to a no-op.

Implementation

Three new modules under main/modules/:

  • DualDriveMotorBase — shared base, identical to the one in Add DualDriveMotor and DualDriveWheels modules #209. Handles common state (voltage, temperature, error codes, switch state, firmware version), enable/disable, and the Configure (0x0B) / SwitchState (0x0A) / stop CAN commands.

  • DualDriveDeltaMotor — delta-mode specialization. Auto-switches the controller into the operating mode for the configured motor variant (Configure 0x02) on construction. Variants currently supported:

    • windmeile — first delta arm, 300 ticks/rev, mode 0xB5B5
    • g350 — drive motor used as delta, 600 ticks/rev, mode 0xD5D5
    • g250r-t — new delta arm 14.2:1 gear, 678 ticks/rev, mode 0xB5B5 (later 0xC5C5)

    Exposes:

    • motor.rel_angle(angle, vel_limit?, acc_limit?, jerk_limit_exp?) — relative angle move via RelAngleCmd 0x02
    • motor.delta_angle(motor_select, pos1, spd1?, pos2?, spd2?) — per-motor or combined position command via AngleCmd 0x03 (0x10=left, 0x20=right, 0x30=both)
    • motor.reference_drive_start(motor, clockwise?) / motor.reference_drive_stop(motor) — trigger and brake the firmware's reference drive via SingleMotorControl 0x0C
    • motor.single_motor_control(cmd_motor1, cmd_motor2) — raw 0x0C for combined frames
    • properties: reversed, per-motor angle_m{1,2} (hall ticks), per-motor current_m{1,2} (A), ref_result_m{1,2}
  • DualDriveDeltaArm — combines a DualDriveDeltaMotor with two Input endstops. Owns the calibration state machine and the high-level positioning interface:

    • arm.position(left_deg, right_deg, speed_left?, speed_right?) — move both arms in degrees, blocked unless calibrated and target inside deg_limit
    • arm.reference("left" | "right" | "both") — start a reference drive; if the relevant endstop is already active the arm first nudges the motor away in BACKOFF_STEP_TICKS increments, then issues the reference command. For "both" the calibration is sent as a single combined 0x0C frame because some firmware revisions only honor "both" that way.
    • arm.stop() — abort calibration and brake both motors; arm.stop(1|2) brakes a single motor
    • arm.on() / arm.off() / arm.enable() / arm.disable() — power and module-level enable
    • properties: enabled, calibrating, calibrated_left, calibrated_right, active, stalled, angle_left, angle_right, deg_limit, cal_timeout, stall_current

    Detection logic in step():

    • Target reached: active clears once both arms stay within POSITION_TOL_DEG (1.2°) for STABLE_MS (100 ms).
    • Stall guard: while active, overcurrent (|i| > stall_current) AND no position change beyond STALL_POS_TOL_DEG (2.4°) within STALL_MS (200 ms) marks the arm stalled, invalidates calibration, and disables the motor.
    • Calibration timeout: cal_timeout (default 10 s) brakes both motors and clears calibration if the firmware never reports a ReferenceFeedback.

Notes on the firmware contract:

  • Reference completion is driven by the firmware's own ReferenceFeedback 0x14 (REF_OK / REF_OVERCURRENT / REF_END); the endstop only triggers the brake, not the "done" transition.
  • 0x14 arrives per-motor in separate frames (each only setting its own nibble), so the arm latches the highest non-zero value seen per motor across the calibration window.
  • No runtime endstop brake during normal motion — endstop safety is enforced at move-start by can_move() and physically by the motor firmware.

Module registration follows the same pattern as DualDriveMotor / DualDriveWheels.

Progress

  • The implementation is complete.
  • Tested on hardware.
  • Documentation has been updated (or is not necessary).

@JensOgorek JensOgorek self-assigned this May 5, 2026
@JensOgorek JensOgorek added enhancement New feature or request labels May 5, 2026
@JensOgorek JensOgorek modified the milestone: 0.12.0 May 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants