Skip to content

feat(firmware)!: adopt DCMotorServo 2.0's speed loop and SET_SPEED_FF - #116

Merged
CameronBrooks11 merged 3 commits into
mainfrom
feat/dcmotorservo-2
Nov 6, 2026
Merged

CameronBrooks11 merged 3 commits into
mainfrom
feat/dcmotorservo-2

Conversation

@CameronBrooks11

Copy link
Copy Markdown
Member

Adopts DCMotorServo ^2.0.0 (feastorg/dcmotorservo#26) and bread-crumbs-contracts ^0.13.0 (feastorg/bread-crumbs-contracts#75). The speed loop now sets PWM as feedforward plus a PI trim on the measured speed, instead of a PID moving the position target. Fixes #105, and the encoder default moves to the measured 848 counts per turn (#103).

  • Speed gains: SET_PID speed gains (and PIDSPEED=) are Kp in PWM per RPM and Ki in PWM per RPM-second. kd is stored and unused. Defaults are 0.6, 3.0 and 0.
  • SET_SPEED_FF: new, behind DCMT_CAP_SPEED_FEEDFORWARD (bit 13), which is advertised only where the speed loop is built. It sets each motor's static PWM, RPM per PWM step and trim bound.
    • The loop applies them as setFeedforward() with kv = rpm_per_pwm × CPR / 60 counts/s per PWM, plus setTrimLimit(). Until one is sent, the loop is a plain PI.
    • A payload that isn't 8 bytes is refused MALFORMED, and a build without the speed loop refuses it NOT_BUILT. It is taken during a hold, as SET_PID is.
  • Serial: SPEEDFF=static1,rpp1,trim1,static2,rpp2,trim2, with rpp in RPM per PWM step.
  • Removed: setMaxLead(), DCMT_TACHO_MAX_LEAD and the feat(firmware): latch a stall fault, freeze the speed integral, Ki 0.2 #97 Ki freeze while the lead cap clamped. The library's conditional integration replaces them. Stall detection is unchanged.
  • Encoder default: MOTOR1_CPR and MOTOR2_CPR are 848, and the README and CHANGELOG say so.

Checks

Not checked

Hardware. The bench step is next:

  1. Calibrate each motor's feedforward with an open-loop sweep (dcmt_bench.py ffsweep).
  2. Validate 10–150 RPM, grips, entry from reverse, and that position mode is unchanged.

BREAKING CHANGE: GET_VERSION reports module major 3, and SET_PID's speed gains are read in PWM units. Speed gains tuned for major-2 firmware don't carry over.

The E-S Motor 36PG-3429-19-EN measured 848 counts per output turn on
the bench (#103), not 798, so speeds read about 6 % high. Held back:
at 848 test_speed_entry_62 reverse fails on a one-sample -18 PWM from
the position loop at speed entry, which also occurs at 798 for other
entry timings (#105).

Refs #103
The README still listed 798, and the changelog had no entry for the
change to 848 (#103).
Adopt DCMotorServo ^2.0.0 and bread-crumbs-contracts ^0.13.0. The
speed loop now sets PWM as feedforward plus a PI trim on the measured
speed, instead of a PID moving the position target.

- SET_PID speed gains (and PIDSPEED=) are Kp in PWM per RPM and Ki in
  PWM per RPM-second; kd is stored and unused. Defaults 0.6, 3.0, 0.
- New SET_SPEED_FF (DCMT_CAP_SPEED_FEEDFORWARD, bit 13, advertised only
  where the speed loop is built) sets each motor's static PWM, RPM per
  PWM step and trim bound. The loop applies them as setFeedforward()
  with kv = rpm_per_pwm * CPR / 60 counts/s per PWM, and
  setTrimLimit(). Until one is sent the loop is a plain PI. A payload
  that isn't 8 bytes is refused MALFORMED, a build without the speed
  loop refuses it NOT_BUILT; it is taken during a hold, as SET_PID is.
- Serial SPEEDFF=static1,rpp1,trim1,static2,rpp2,trim2, rpp in RPM per
  PWM step.
- Remove setMaxLead(), DCMT_TACHO_MAX_LEAD and the Ki freeze while the
  lead cap clamped; the library's conditional integration replaces
  them. Stall detection is unchanged.

Host tests: test_speed_ff covers the new frame, serial command, caps
and module major 3 on every env; test_max_lead and test_speed_freeze go
with the code they tested. The motor tests now configure the model
motor's feedforward (static 10, 1.719 RPM per PWM, trim 20) and Kp 0.6
Ki 3.0, and test_speed_entry_62 adds a sweep of the reverse entry over
550 to 750 ms in 2 ms steps: 0 of 101 timings drive backwards, where
2fda4c3 with DCMotorServo 1.2.1 gave 15 of 101.

BREAKING CHANGE: GET_VERSION reports module major 3, and SET_PID's
speed gains are read in PWM units. Speed gains tuned for major 2
firmware don't carry over.
@CameronBrooks11
CameronBrooks11 merged commit c276d88 into main Nov 6, 2026
20 checks passed
@CameronBrooks11
CameronBrooks11 deleted the feat/dcmotorservo-2 branch November 6, 2026 20:49
CameronBrooks11 added a commit that referenced this pull request Nov 8, 2026
docs(bench): add the 2026-11-07 speed and position record

Adds the 2026-11-07 bench record for #116 on a gen2 Nano Every with
both motors and 848-count encoders, from main at c276d88.

Passed:

- Open-loop sweeps gave M1 static 10, 2.024 RPM per PWM and M2 static
  7, 2.005, sent with SET_SPEED_FF (trim 20). A 4-byte payload was
  refused.
- Closed-loop speed at +10, -60, +120 and -150 RPM on each motor:
  within 5 % at 0.22-0.23 s, and the last-5 s means within 0.4 RPM of
  the target.
- A 0.5 s grip at 60 RPM recovered in 0.2 s with no fault; entry from
  -255 into +60 RPM never drove backwards (PWM 39-41, #62).
- Both motors at 120 RPM for 5 minutes with GET_STATE every 100 ms: no
  lockup and no lease trip (#109).

Findings:

- A -7282 -> 0 position move stopped at +48 with no drive for 60 s.
  DCMotorServo 2.0.1 (feastorg/dcmotorservo#30) fixes it; with the fix
  moves of 7282, 16000 and 20725 counts ended within 6 counts.
- 32 of 2968 polls (1.1 %) failed CRC with both motors running.
- A steady hand load on a held position latched M1_STALL within about
  a second (#102).

Not checked: the saturated-hold case of the fix, M2 grip and entry,
#102's small step, and the analog builds on this board.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

In the motor model, closed-loop speed above ~90 RPM climbs past the target and collapses every ~30 s

1 participant