Files
orisys-app/apps/autocal/docs/workflow.md
T
2026-09-20 21:15:39 +08:00

336 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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
```