added motion trajectory simulation

This commit is contained in:
ykcheungab
2026-09-23 09:58:00 +08:00
parent f1bec62ca4
commit 3347d17a20
25 changed files with 5198 additions and 1762 deletions
+22 -19
View File
@@ -41,28 +41,23 @@ Because the atlas is a single island, a click maps to exactly one 3D point.
An earlier revision of this note reported that the **yaw +-90 deg limit made a
quarter of the patch unalignable**, worst residual 65.1 deg, only 47 of 61
sampled points reachable. **That was a solver bug, not a property of the
machine.** The corrected solver solves the real surface as:
machine.**
| Envelope | Worst residual | Median | Within 0.01 deg | Within 0.5 deg |
|---|---|---|---|---|
| pitch 360 deg, **yaw +-90 deg** (the machine) | **0.21 deg** | 0.00 deg | **98.4%** | **100%** |
| pitch 360 deg, yaw +-180 deg | 0.24 deg | 0.00 deg | — | 100% |
A later revision then reported the residual as 0.21 deg worst with the yaw limit
"not the constraint". **That was also wrong**, in the opposite direction: the
limit itself was wrong. The bench confirmed the yaw stage turns a **full half-turn
either way (+-180 deg)**, not +-90.
So **every** vertex of the sensing patch is reachable, and widening the yaw range
buys essentially nothing (0.21 deg → 0.24 deg worst). The 0.21 deg worst case is
a genuinely clamped vertex: the sweep's own minimum at that vertex is 0.211 deg
at the yaw clamp, so the number is physical, not lost precision.
With the corrected envelope, solved by closed form
(see `inverse-kinematics.md`):
Why the yaw limit is not the constraint, which the earlier note had backwards:
| Envelope | Worst residual | Within 0.5 deg | In range |
|---|---|---|---|
| pitch 360 deg, **yaw +-180 deg** (the machine) | **1.2e-06 deg** | 100% | **1032 / 1032** |
- The pitch stage turns a vector around a circle in the plane **perpendicular to
the pitch axis**.
- The yaw stage can reach a given great circle as long as the yawed normal is not
parallel to the pitch axis. A normal parallel to the pitch axis after yawing
would have to be perpendicular to the tool axis already, and the finger patch
normals — which point along sensor +Y, mapping to platform **+Z**, the tool
direction — are nothing of the sort.
- Pitch travel is therefore what does the work, and the 360 deg stage provides it.
Every vertex is reachable, and the residual is at the floating-point floor rather
than at any clamp. The "0.21 deg worst, genuinely clamped vertex" claim above was
an artefact of the wrong limit and no longer holds.
**No mounting change is required for reachability.** The three options the earlier
note listed (re-mount, state a tolerance, index the plate) are all unnecessary on
@@ -70,6 +65,12 @@ the reachability evidence. Whether they help *repeatability* — a shorter pitch
move, a better-conditioned contact — is a separate bench question, not a
kinematic one.
One thing the corrected solve did surface: **51 of 1032 vertices end inverted**,
whichever closed-form branch is chosen, because the tool direction is
antiparallel to the sensor's own up axis. That is the tool-polarity question, it
needs the bench, and it is *not* visible in the residual. See
`inverse-kinematics.md` → *Still open*.
## What was wrong before, and why it is worth recording
**Four** solver versions preceded this one, each wrong in a way that produced
@@ -104,9 +105,11 @@ as a number rather than as a plausible pose.
| `sensor/uv_flat.py` | flat image + pixel→3D table; gap pixels are `None` |
| `motion/kinematics.py` | constrained search + measured residual; stage limits are parameters; singular case reported, not guessed |
| `ui/sensor_panel.py` | picker, readouts, point table; refuses gap pixels with a reason; picked rows kept at full precision, not re-read from the 3-decimal display text |
| `ui/pose_preview.py` | 3D preview coloured by residual, tool axis drawn; the legend is a sibling label, because text composited over the GL surface is unreadable once it overflows |
| `ui/pose_preview.py` | 3D preview coloured by residual, tool axis drawn; the legend is a sibling label, because text composited over the GL surface is unreadable once it overflows. The scene, the palette and the framing rule are described in [finger-preview.md](finger-preview.md) |
| `motion/kinematics.py` | closed-form solve, exact to 1.2e-06°; the sweep is kept only as the reference the tests check it against — see [inverse-kinematics.md](inverse-kinematics.md) |
| workspace `finger` tab | wired, checked at 1440×900 and 1920×1080 in both themes |
| startup | the remembered bundle loads automatically, so the tab comes up working instead of showing a filled-in path over two empty views |
| `tools/preview_snapshot.py` | photographs the 3D pane with a real GL context, at rest or mid-playback, pane-only or composited into the page |
Not yet built: capture of picked points into the sequence, and the live-sensor
counterpart of the simulated pose.