Document the drivetrain keys, the derived degree scale and the homing findings

This commit is contained in:
pgdalmeida 2026-09-17 17:58:46 +02:00
parent 0c1d0a6df8
commit 5b3662c529
Signed by: pedro.almeida
GPG Key ID: D4A6C394DF13F1D7
2 changed files with 75 additions and 0 deletions

View File

@ -69,6 +69,10 @@ Parsed and validated by `ConfigLoader` ([src/core/Config.cpp](../src/core/Config
| `Motor` | `yaw_heading_at_center` | float | `0` | True bearing (0 = north) at that centre (**calibrate**) | | `Motor` | `yaw_heading_at_center` | float | `0` | True bearing (0 = north) at that centre (**calibrate**) |
| `Motor` | `pitch_angle_at_center` | float | `0` | Elevation above level at that centre (**calibrate**; not 0 on a real rig) | | `Motor` | `pitch_angle_at_center` | float | `0` | Elevation above level at that centre (**calibrate**; not 0 on a real rig) |
| `Motor` | `yaw_soft_margin_counts` / `pitch_soft_margin_counts` | int | `5000` | Clearance kept from each hard endstop; sent to the firmware, which enforces it | | `Motor` | `yaw_soft_margin_counts` / `pitch_soft_margin_counts` | int | `5000` | Clearance kept from each hard endstop; sent to the firmware, which enforces it |
| `Motor` | `homing_timeout_s` | int | `65` | How long a full two-end homing may take before the host calls it failed. A property of the drivetrain — a high gear ratio searching gently takes minutes |
| `Motor.<axis>` | `r_sense_ohm` | float | — | Sense resistor on that axis's TMC5160 board. **hardware.ini.** Makes the milliamp figures below mean something |
| `Motor.<axis>` | `run_current_ma` / `homing_current_ma` | float | — | Coil current (mA RMS) for normal motion, and while hunting an endstop. **hardware.ini** |
| `Motor.<axis>` | `travel_deg` | float | — | Mechanical angle between the hard endstops. **hardware.ini.** Homing divides the measured travel by it to derive `<axis>_counts_per_deg` |
| `Scan` | `grid_file` | string | — | CSV of `yaw_deg,pitch_deg` waypoints; empty → generate | | `Scan` | `grid_file` | string | — | CSV of `yaw_deg,pitch_deg` waypoints; empty → generate |
| `Scan` | `yaw_intervals` | int | `56` | Generated yaw positions across `[yaw_min_deg, yaw_max_deg]` | | `Scan` | `yaw_intervals` | int | `56` | Generated yaw positions across `[yaw_min_deg, yaw_max_deg]` |
| `Scan` | `yaw_min_deg`/`yaw_max_deg` | float | `-90`/`90` | Generated yaw arc | | `Scan` | `yaw_min_deg`/`yaw_max_deg` | float | `-90`/`90` | Generated yaw arc |
@ -588,10 +592,53 @@ The firmware speaks **full-word, newline-terminated** commands in absolute **enc
| `ENABLE Y` / `ENABLE P` | init | energize the axis coils | | `ENABLE Y` / `ENABLE P` | init | energize the axis coils |
| `HOME` | init | home all axes (firmware runs it non-blocking; watch `ST` state `R→H→A`) | | `HOME` | init | home all axes (firmware runs it non-blocking; watch `ST` state `R→H→A`) |
| `SPEED Y\|P <vel>` | init | set the production move speed (VMAX, counts/s) | | `SPEED Y\|P <vel>` | init | set the production move speed (VMAX, counts/s) |
| `CURRENT Y\|P <scaler> <irun> <irun_home>` | init, before `HOME` | motor current as TMC register fields, converted by the host from `[Motor.<axis>]` milliamps |
| `CENTER Y\|P <count>` | init, before `HOME` | the count homing anchors the travel midpoint to |
| `MARGIN Y\|P <counts>` | init, before `HOME` | clearance kept from each hard endstop |
| `MOVE <yaw>,<pitch>` | each capture point | drive both axes to absolute counts (combined form) | | `MOVE <yaw>,<pitch>` | each capture point | drive both axes to absolute counts (combined form) |
| `MOVE Y\|P <pos>` | — | single-axis absolute move (`set motorctl`) | | `MOVE Y\|P <pos>` | — | single-axis absolute move (`set motorctl`) |
| `STOP Y\|P\|ALL` | — | ramp to a controlled stop | | `STOP Y\|P\|ALL` | — | ramp to a controlled stop |
### Motor current, and why the host computes it
The TMC5160 expresses coil current as two dimensionless registers, and what a given pair
delivers depends on the sense resistor soldered to the board. Only `hardware.ini` knows that
resistor, so the **host** converts milliamps to registers and sends the result; the firmware
keeps compile-time defaults so a bench board still behaves.
`homing_current_ma` exists for one reason: **an endstop the motor can out-push is not an
endstop.** Homing detects a stop by driving into it until the shaft falls behind the
commanded position. On the IOTA tower, whose gear ratio was raised for wind stability, the
motor simply sheared one screw-head stop past the other — it ran 1.8 turns past the stop with
the encoder never falling behind. At 189 mA it homes against the same screw cleanly.
The startup log names what the registers **actually** deliver, which is not always what was
asked for:
```
yaw motor current: run 756 mA (GLOBALSCALER 80, IRUN 31), homing 189 mA (IRUN 7)
-- R_sense 0.075 ohm from hardware.ini [Motor.yaw]
```
Because both registers are write-only on the chip, the firmware echoes them in `DUMP` and the
host checks them **before** homing. If an axis declares a reduced homing current and the
firmware has not adopted it, the host **refuses to home** rather than driving into a stop at
full torque — the fire it is there to prevent.
### Deriving `counts_per_deg` from the homed travel
`travel_deg` is a nameplate fact: the mechanical angle between the stops. Homing measures the
same span in counts, so dividing gives the degree scale. On a successful home the host
compares the two and, when they disagree by more than a factor of two, adopts the derived
value and offers it at the usual `y`/`n` prompt, saving to `config.ini` with a `;prev` line.
Within a factor of two it only logs the comparison. That guard matters: a `gimbal heading
setup` landmark fit is a better measurement than this one, and a routine re-home must not
quietly overwrite it.
**The derived scale is only as good as `travel_deg`**, which is usually a rough figure — a 1 %
error in the angle is a 1 % error in every bearing. `gimbal heading setup` supersedes it.
Capture is a **move → settle → trigger** cycle: the scheduler issues a `MOVE`, waits until both Capture is a **move → settle → trigger** cycle: the scheduler issues a `MOVE`, waits until both
axes report standstill at the target, then triggers the cameras. **ControlCode 0** walks the scan axes report standstill at the target, then triggers the cameras. **ControlCode 0** walks the scan
grid (front to back, repeating); **ControlCode 1** drives yaw to `target_HDG` (pitch held). Telemetry arrives as grid (front to back, repeating); **ControlCode 1** drives yaw to `target_HDG` (pitch held). Telemetry arrives as

View File

@ -297,6 +297,34 @@ commit `aab1925`.
|---|---|---| |---|---|---|
| 0 | Remote-flash question researched — two viable paths, design rule derived (§2) | **done, and now measured on the board: neither works** (§2). First flash needs the MicroPython REPL or BOOTSEL; remote *update* is unsolved pending a `BOOTSEL` command | | 0 | Remote-flash question researched — two viable paths, design rule derived (§2) | **done, and now measured on the board: neither works** (§2). First flash needs the MicroPython REPL or BOOTSEL; remote *update* is unsolved pending a `BOOTSEL` command |
| 2 | **Phase 2 gate: RP2040 + TMC5160-BOB at 3.3 V** | **PASSED** on tower 121, 2026-09-15: `IOIN Y=0x3000001E version=0x30`, encoder reading (`X_ENC`), one axis, `speed=3000` | | 2 | **Phase 2 gate: RP2040 + TMC5160-BOB at 3.3 V** | **PASSED** on tower 121, 2026-09-15: `IOIN Y=0x3000001E version=0x30`, encoder reading (`X_ENC`), one axis, `speed=3000` |
| 3 | Yaw homing on the real drivetrain | **BLOCKED — no endstop found.** See below |
### Phase 3, first attempt — 2026-09-16
Three homing runs at VMAX 120, 1200 and 6000. What came out of them:
| Established | Value |
|---|---|
| Speed model | **~0.73 counts/s per unit of VMAX** (87 c/s at 120, 880 at 1200, 4400 at 6000 — predictions held to within 0.5 %) |
| Geometry | **~3,400 counts/degree**, so ~1.23 M counts per turn |
| `steps_per_rev` | The inherited Delta value of 177,000 is **wrong by ~7x** for this drivetrain. It is not a search limit (`motor.cpp` uses it only as a minimum-travel check once both limits are found), so it slowed nothing — but it made the axis look broken, and the check is meaningless until corrected |
**The axis turned ~2,157,000 counts — about 630 degrees, 1.8 full turns — in one
direction without the encoder deviation ever exceeding ~100 counts** against the
1500 that marks a jam. The motor met no resistance at any speed.
Homing detects an endstop by the shaft falling behind the commanded position, so
it cannot work if nothing stops the shaft. Two candidates, indistinguishable
over the wire because the encoder sits on the motor side:
1. **No hard stop on this yaw** — it rotates continuously, and stall-based homing
is the wrong strategy for this rig.
2. **The output is not following the motor** — a slipping coupling or a loose
grub screw. The encoder would track the motor perfectly either way.
**Resolving it needs a physical observation**: did the gimbal itself rotate ~1.8
turns, or did the motor spin while the platform stayed put? Check any cabling
through that joint before moving the axis further.
| 2 | `src/platform.h` — reset cause and free RAM behind one interface per MCU family | **done** | | 2 | `src/platform.h` — reset cause and free RAM behind one interface per MCU family | **done** |
| 2 | `Serial.begin()` moved ahead of all hardware init, so a wedged image stays flashable | **done** | | 2 | `Serial.begin()` moved ahead of all hardware init, so a wedged image stays flashable | **done** |
| 2 | `[env:iota_rp2040]` PlatformIO target (arduino-pico core, 8 MB flash) | **done and built** on tower 121, 2026-09-15: 49452 B flash (0.6 % of 8 MB), 9068 B RAM. `leonardo` still builds (19254 B) and the 22 native tests still pass | | 2 | `[env:iota_rp2040]` PlatformIO target (arduino-pico core, 8 MB flash) | **done and built** on tower 121, 2026-09-15: 49452 B flash (0.6 % of 8 MB), 9068 B RAM. `leonardo` still builds (19254 B) and the 22 native tests still pass |