autocal layout first design

This commit is contained in:
ykcheungab
2026-09-20 21:15:39 +08:00
parent 07d7c3bad6
commit e276b5de0f
92 changed files with 18071 additions and 64 deletions
+335
View File
@@ -0,0 +1,335 @@
# AutoCal workflow — platform bring-up to calibration data
This is the operating workflow the application is built around. It is written to
be argued with: each phase names what it needs, what it produces, why it exists,
and what must be true before the next one starts.
A **gate** is a checkpoint. The app's job at a gate is to make the operator's
answer trustworthy, not to ask "are you sure?".
```text
P0 Session & safety baseline [gate]
P1 ECAT setup [gate]
P2 Homing + test motion [gate]
P3 Sensor & probe setup [gate]
P4 Coordinate setup: sensor <-> platform [gate]
P5 Sequence design [gate]
P6 Simulate & dry run [gate]
P7 Run + acquire [gate]
P8 Store raw
P9 Post-process [gate]
P10 Calibration export
P11 Verify [gate]
```
---
## P0 — Session & safety baseline
**Why:** the machine can move from P2 onward. Everything that limits that motion
should be decided while nothing is moving.
**Inputs:** operator identity, machine/bench identifier, units per axis,
`units_per_mm`.
**Safety limits, set here, enforced from P2:**
| Limit | Meaning |
|---|---|
| maximum travel per axis | never commanded past this, in user units |
| maximum Z descent per step | a single approach may not exceed this |
| press-depth ceiling | the press depth a sequence profile is allowed to request |
| no-contact abort | if contact is not detected within the descent budget, stop the run |
| following-error bound | how far a drive may lag before the move counts as stalled |
**Outputs:** a session record — who, when, which machine, and the limits in
force. Stamped onto everything the session produces.
**Gate:** every limit has a value; no limit is "unset" (an unset limit is an
unlimited machine).
**Currently missing from the workflow:** this phase did not exist. Without it,
step 1 went from bus setup straight to jogging five axes.
---
## P1 — ECAT setup
**Why:** nothing else can be trusted until the controller has actually found its
slaves. A missing drive discovered at P7 wastes the whole run.
**Inputs:** controller address, the matching `.bas` program
(`ECAT.bas` / `RTEX.bas`).
**Steps:** connect → download and run the BAS program → watch
`Bus_InitStatus` to completion or timeout → read `BUS_TYPE`,
`Bus_TotalAxisnum`, and the node count.
**Outputs:** bus type, node count, axis count, init status.
**Gate:** init status is success, **and** the node/axis counts match what this
bench is expected to have. A mismatch is a finding, not a warning.
**Existing code:** `MotionService.download_bas` / `poll` already implement the
state machine and the 25 s timeout.
---
## P2 — Homing + test motion
**Why:** a drive that homes and jogs cleanly at low speed is the precondition for
letting a sequence drive it.
**Inputs:** per-axis homing mode, speeds, offset; a test-motion profile.
**Steps:** enable → home each axis → low-speed jog each axis in both directions
→ confirm movement and return.
**Outputs:** per-axis pass/fail with the evidence (distance moved, following
error, alarm state).
**Gate — the test motion needs a criterion.** This was missing. Proposed:
- the axis moved at least a configured minimum distance;
- it returned within a tolerance of its start position;
- following error stayed under the bound from P0;
- no alarm before, during or after.
**Existing code:** the axis panel does the jog/homing; the criterion is new.
---
## P3 — Sensor & probe setup
**Why:** which sensor is under test, what touches it, and how far the touching
point is from the axis reference. Contact depth is meaningless without the tool.
**Inputs:**
- sensor type and config reference (the same `config/sensors/<type>/sensor.json`
the studio uses);
- the sensor's 3D scene package and its `geometry_id`;
- the **probe tip** (tool): which axis presses, and the tip offset from the
platform's reference point;
- the mounting pose: how the sensor is fixtured on the platform.
**Outputs:** a saved setup block.
**Gate:**
- the calibration's `geometry_id` matches the scene's, or the app says which is
which rather than colouring the wrong geometry;
- the probe tip offset is set (a zero offset that was never entered is not the
same as a probe at the origin).
---
## P4 — Coordinate setup: sensor ↔ platform
**Why this is the load-bearing phase.** "Which points on the sensor should be
tested" is a question in the *sensor's* frame. The platform moves in *platform*
coordinates. Without the transform between them:
- a generated grid cannot be checked against the sensor's surface;
- a measured point cannot be placed on that surface for the sensitivity map;
- a calibration cannot be re-applied to a re-fixtured sensor.
**Inputs:** the probe tip from P3, and a set of **at least four non-collinear
known features** on the sensor or its fixture, probed in platform coordinates.
**Steps:** probe each feature → solve for the rigid transform (rotation and
translation; scale if the units are not yet trusted) → report the residual.
**Outputs:** a 4×4 transform from sensor frame to platform frame, with its
residual and the feature set that produced it.
**Gate:** residual under a configured tolerance, and at least four non-collinear
points (four collinear points determine a rotation about that line, not a
transform — the app should refuse that set rather than return a number).
**Not built in this step:** the solver. Only the workflow step, the stored
transform, and the place its residual is displayed.
---
## P5 — Sequence design
**Why:** this is the decision the whole run exists to carry out.
**Inputs (the profile, persisted and reusable):**
| Parameter | Meaning |
|---|---|
| mode | flat surface or curved (a cylindrical/spherical sensor area) |
| grid pitch | spacing between points, in mm |
| margin / radius | how far in from the active edge the grid stays; radius for curved |
| clear height | standoff above the surface to travel at |
| approach speed | descent speed before contact |
| press speed | descent speed after contact |
| press depth | how far past contact to press |
| dwell | how long to hold the press |
| settle window | how long to wait before the measurement is considered settled |
| repeats | how many times each point is measured |
| point order | row-wise, serpentine, or by distance |
**Steps:** generate the grid → validate **every point** three ways:
1. **on the sensor** — its sensor-frame position falls on the modelled surface,
not off the edge and not inside a hole;
2. **within travel** — the platform can reach it inside the P0 limits;
3. **confirmable** — the sensor's sensing area is under the point, so a contact
there can actually be seen by the sensor.
**Outputs:** a point list with a sensor-frame position and a platform-frame
target for each, plus a validation verdict per point.
**Gate:** no point may be unusable without being marked as such. A run may
proceed with skipped points if the operator says so explicitly; it may not
proceed with unvalidated ones.
**The interface:** three zones in the Sequence tab — profile parameters, a 3D
preview of the surface with the points and their visiting order, and the
validation list. See `apps/autocal/README.md`.
---
## P6 — Simulate & dry run
**Why:** a wrong Z or a wrong grid is much cheaper to find now.
**Simulate:** play the full toolpath in the 3D view — a moving marker with a
phase label (standoff → descend → press → dwell → retract), the estimated
duration, and per-point reachability. No motion.
**Dry run:** execute the real XY path, descending only to the clear height,
never pressing. This proves the geometry, the ordering, and the timing.
**Outputs:** estimated duration, per-point reachability, and (dry run) the
measured time per point.
**Gate:** the operator has seen the path and a dry run has completed without a
guard violation.
---
## P7 — Run + acquire
**Why:** the only phase that touches the sensor.
**Per step, in order:**
1. move to the point's XY at clear height;
2. descend at approach speed;
3. **detect contact by position** — the axis stalls / following error rises
while descending;
4. **confirm with the sensor under test** — its reading must change when the
probe presses. This is the second opinion: position says "I stopped moving",
the sensor says "something real is happening";
5. press to the configured depth at press speed;
6. dwell, then acquire the settle window;
7. retract, move to the next point.
**Failure semantics (per point, because resume needs them):**
| Outcome | Run behaviour |
|---|---|
| contact confirmed | record the point as measured |
| contact detected, sensor shows no change | mark **suspect**, continue; a suspect point is visible, not silently averaged in |
| no contact within the descent budget | **abort** the run (P0 limit) and leave the point unfinished |
| guard violation | abort the run, name the step and the limit |
**Writes are incremental**: each point is written as it completes, with its
status, so an interrupted run resumes at the first unfinished point and partial
data stays valid.
**Existing code:** the motion layer has the guards and the poller; the step
machine, the contact detection and the acquisition binding are new.
---
## P8 — Store raw
**Why:** post-processing will change. The raw record must not.
**Contents:**
- the sequence profile and the instantiated point list (positions and order);
- per point: the position trace, the raw sensor series, the settle window used,
timings, the contact position, and the status;
- the setup: sensor identity, geometry id, probe tip, the P4 transform and its
residual;
- the session record and app/version stamps from P0.
Nothing derived is authoritative. The requirement that makes this reproducible:
**a dataset can be regenerated from the profile, the setup and the raw series**,
with no dependency on a previously computed result.
---
## P9 — Post-process
**Why:** turning samples into a number is a decision, and it has to be visible.
**Steps:** choose the settle window per point → stability check (is the point
actually settled, or drifting?) → sensitivity per point per axis → outlier
detection → residual per point → fit the surface across points.
**Outputs:** per-point sensitivity with a quality verdict, and a surface field
over the sensor's geometry ready for the 3D colour map.
**Gate:** every point is either valid or explicitly excluded, with a reason.
A point with too few samples or a drifting window is not a coefficient.
---
## P10 — Calibration export
**Per-point results and the fitted surface, explicitly linked.** They are one
calibration; exporting them as unrelated artefacts is how a map ends up in a
report that does not match the table beside it.
---
## P11 — Verify
**Why this exists:** P9 and P10 produce numbers. Nothing yet checks they are
right. "Post process → calibration data" had no acceptance test.
**Steps:** apply the calibration → re-measure a small subset of the points →
compare the measured sensitivity against what the calibration predicts →
report the discrepancy per point.
**Gate:** the discrepancy is within tolerance, or the verification is recorded
as failed. A calibration that was never verified is labelled as such wherever it
is used.
---
## Gaps this document closes
| # | Gap | Phase |
|---|---|---|
| 1 | no limits before motion | P0 |
| 2 | no session identity (who/when/which machine) | P0 |
| 3 | the sensor↔platform transform was absent | P4 |
| 4 | the probe tip (tool) was never defined | P3 |
| 5 | the test motion had no pass/fail criterion | P2 |
| 6 | grid validity was not checked per point | P5 |
| 7 | the settle window and stability criterion were undefined | P9 |
| 8 | a point's failure had no defined behaviour | P7 |
| 9 | the sequence was transient, not a saved, versioned artefact | P8 |
| 10 | no verification pass | P11 |
## Data flow
```text
P0 limits ─┬───────────────────────────────> enforced at P2, P6, P7
P1 bus ────┴─> axis availability ─────────> P2
P3 sensor setup ─┬─ geometry id ──────────> P5 validation, P10 surface
└─ probe tip ────────────> P4, P7 press depth
P4 transform ─────> sensor <-> platform ──> P5 point generation, P9 map
(residual) P11 re-fixture check
P5 profile + points ──> P6 simulate/dry run ──> P7 run
P7 raw series ─────> P8 dataset ──> P9 post-process ──> P10 export
└─> P11 verify
```