perf(advance): key the auto-advance schedule on erpm, not duty cycle#55
Draft
AlexKlimaj wants to merge 2 commits into
Draft
perf(advance): key the auto-advance schedule on erpm, not duty cycle#55AlexKlimaj wants to merge 2 commits into
AlexKlimaj wants to merge 2 commits into
Conversation
The F051 app region is the binding constraint on this fork, and holding back 5% of it was blocking feature work for margin the project does not need. The gate's job is to catch a PR that overflows the chip, not to reserve headroom. Flash limit goes 95% -> 99.8%, i.e. 26265 -> 27592 of the 27648 byte RX image region. For scale: ark-release currently sits at 26156 B, so the old gate left 109 bytes. The advance-schedule change in the next commit needs 104 of them. Percentages are now evaluated with awk rather than shell integer arithmetic so a fractional limit like 99.8 works at all; truncation is toward zero, and an over-budget image still fails (verified with FLASH_MAX_PCT=1). RAM stays at 90% deliberately. Unlike flash, what is left is stack headroom, and exhausting it fails at runtime instead of at link time, so the gate is doing real work there. This is a size decision only. Global -O3 previously filled ~99% AND regressed hold100 free-run on F051 (armed/input drop near ~97% throttle); that regression is functional, not a size problem, so the higher limit is explicitly not permission to enable global -O3. Noted in the script next to the limit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HoD65bRT7unXhETnZ13WEq
auto_advance_level was a single linear map of duty cycle:
auto_advance_level = map(duty_cycle, 100, 2000, 13, 23);
Commutation lag is dominated by the phase lag of current behind applied
voltage, atan(w*L/R), so what the schedule wants on its x axis is a
measure of speed. Duty is only a proxy for speed at one load, and the
error is in the harmful direction: under load rpm sits well below what
duty implies, so the real lag is lower and the duty curve over-advances.
More advance means more current, so the old curve pushed the wrong way in
exactly the condition that is already thermally worst.
Replace the input with measured k_erpm normalized to what this motor
reaches at full duty on the present pack (kv * V * poles/2). Same 13..23
output range, same curve shape, no retuned constants - only the x axis
changes. settings.c derives a Q12 "kerpm per centivolt" scale from kv and
pole count once per settings load, so the hot path is a multiply and a
shift with no division added to the main loop.
Verified against a transcription of both schedules plus the real map():
- no load, 6S/2000kV/14P: new matches old across the throttle range
(13,14,15,18,19,20,23 at duty 200..2000)
- loaded at duty 1200: 70% of no-load rpm moves the level 18 -> 16,
which is the intended correction
- 4S/6S/12S at equal duty: identical level, see below
What this does NOT fix, stated plainly because the first draft of this
change claimed otherwise: pack voltage. Under no load k_erpm scales with
V and so does the ceiling, so the ratio - and this schedule - is
voltage-invariant, exactly like the curve it replaces. The physics says
L/R falls roughly as 1/kv, which makes the lag a function of BEMF
(~rpm/kv) and leaves a residual voltage term. Keying on BEMF would be
the fuller correction but it changes full-throttle advance on every pack
size at once and needs bench data to pick endpoints, so it is left out.
This commit is the conservative half: identical to today wherever duty
was a valid proxy, different only under load, in the direction that
lowers current.
Falls back to the duty proxy when the normalization cannot be trusted,
each path unit-checked:
- implausible kV (<300, where the throttle limiter already gives up) or
pole count outside 2..64 (an erased eeprom reads 0 or 0xff)
- no usable pack reading yet, before the ADC settles at boot
- measured erpm more than 25% past the configured ceiling, the
signature of a mis-entered motor kV. That case matters because
entering 50-60% of the real kV is a known field workaround for the
throttle-limiter bug (am32-firmware#405), and normalizing
against a ceiling that low would peg advance at the top of the range
through most of the throttle band. The duty proxy is immune to a
wrong kV, so it stays the fallback rather than being removed.
Costs 104 bytes of flash and no RAM on ARK_4IN1_F051.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HoD65bRT7unXhETnZ13WEq
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
auto_advance_levelwas a single linear map of duty cycle (Src/runtime_loop.c:440):Commutation lag is dominated by the phase lag of current behind applied voltage,
atan(w*L/R), so what the schedule wants on its x axis is a measure of speed. Duty is only a proxy for speed at one load, and the error runs in the harmful direction: under load rpm sits well below what duty implies, so the real lag is lower and the duty curve over-advances. More advance means more current — so the old curve pushed the wrong way in exactly the condition that is already thermally worst.Change
Two commits, separable:
1.
build(size-check)— raise the F051 flash gate 95% → 99.8% (26265 → 27592 B of the 27648 B RX region). The F051 app region is the binding constraint on this fork and holding back 5% was blocking feature work for margin the project doesn't need; the gate's job is to catch an overflow, not reserve headroom. Percentages are now evaluated withawkso a fractional limit works at all — shell integer arithmetic can't express 99.8. RAM stays at 90% deliberately: what's left there is stack headroom, and exhausting it fails at runtime rather than at link time.For scale,
ark-releasesits at 26156 B, so the old gate left 109 bytes — and commit 2 needs 104 of them.2.
perf(advance)— the schedule itself. Input becomes measuredk_erpmnormalized to what this motor reaches at full duty on the present pack (kv * V * poles/2). Same 13..23 output range, same curve shape, no retuned constants — only the x axis changes.settings.cderives a Q12 "kerpm per centivolt" scale from kV and pole count once per settings load, so the hot path adds a multiply and a shift and no division to the main loop.Verified behaviour
Transcribed both schedules plus the real
map()fromSrc/functions.cand swept them (6S / 2000 kV / 14 poles):Under load at duty 1200 — the correction this PR is for:
Fallback paths, each checked individually:
Overflow probed at extremes: kv 10220 / poles 64 / 12S → scale 13395, ceiling 16482 kerpm, all within
uint16_t.What this does NOT fix
Pack voltage. Stating it plainly because my first draft of this change claimed otherwise and the sweep disproved it: under no load
k_erpmscales with V and so does the ceiling, so the ratio — and therefore this schedule — is voltage-invariant, exactly like the curve it replaces. Verified identical level at 4S/6S/12S for equal duty.The physics says
L/Rfalls roughly as1/kv, which makes the lag a function of BEMF (~rpm/kv) and leaves a residual voltage term. Keying on BEMF would be the fuller correction, but it changes full-throttle advance on every pack size at once and needs bench data to pick endpoints. Deliberately out of scope here.So this is the conservative half: identical to today wherever duty was a valid proxy, different only under load, in the direction that lowers current.
Interaction with #53
The
>25% past ceilingfallback exists because entering 50-60% of the real motor kV is a known field workaround for the throttle-limiter bug (am32-firmware/AM32#405, fixed in #53). Normalizing against a ceiling that low would peg advance at the top of the range through most of the throttle band. The duty proxy is immune to a wrong kV, so it stays the fallback rather than being deleted. #53 should land first — it removes the reason anyone would misconfigure kV.Cost
ARK_4IN1_F051,HWCI_PERF=1,make size-check-ark:Gate passes at 94.98% of 99.8%.
Verification status — read before merging
make AM32_SITL_CANandmake ARK_4IN1_F051build clean under-Wall -Wextra -Werror.make size-check-arkPASS.make check_formatpasses with clang-format 22.1.5 (the version CI pins; with 18.x it reports 4 pre-existing failures inMcu/f415,Mcu/g431,Mcu/v203— version skew, those files are untouched here).make cppchecknot run — cppcheck isn't installed in this environment.origin/ark-releaseworktree with none of this PR's code — so it is not a regression from this change. The pattern is exactly "everything that must spin fails, everything that must-not-spin passes"; rpm stays 0 for 11+ s after throttle, with or without--nosleep, at normal load, no orphaned processes,ark-releaseunmoved at4ab7b5f. These same tests passed 29/29 earlier in the same session, so something in the environment degraded rather than the code. Worth a look on a clean runner — if CI is green there, this is purely local and can be ignored; if CI is red onark-releasetoo, it wants its own issue.Draft because the behaviour is bench-verifiable and not bench-verified. The numbers above prove the schedule computes what it's designed to compute and that the fallbacks hold — they do not prove less current on a real motor. The
efficiency_sweepgate againsthwci/baselines/is the check that matters, and the load rows are where any gain should appear.🤖 Generated with Claude Code
https://claude.ai/code/session_01HoD65bRT7unXhETnZ13WEq
Generated by Claude Code