删除未被读取的 expected 配置,避免误认为拓扑已受保护。配置检查、manifest 测试及文档检查通过。
8.7 KiB
Measured geometry, camera and runtime facts (sensors/finger/r01)
Reference measurements of the finger r01 sensing surface, the sensing camera, and the runtime
unroll domain. These are properties of the physical part, not of any one design, so they live
here rather than in a decision record and are reused by the multi-view ownership work, the probe
bench and the optics model.
Every row states how it was obtained. Anything not re-derivable is marked as such.
1. Method
Read-only, never saved. Blender 5.0.1, invoked as blender -b -P <script> against
sensors/finger/r01/source/geometry.blend. The probe scripts lived in
orisys-sensor-design/.scratch/multiview-arch/, which is scratch and has been removed — so the
numbers below are not re-derivable by re-running a stored script. Anything you need to trust
today must be re-measured from the mesh, from the runtime asset named in each row, or from the
recorded blend hash.
Recorded blend hash for the 2026-09-27 measurement set: 61093B1A….
2. Sensing surface
| Fact | Value | Note |
|---|---|---|
| Topology | 1032 verts / 1974 polys, all triangles, 5922 loops, no modifiers | The mesh is pre-triangulated, which is what makes the loop-triangle index a valid stable key |
| Dimensions | 15.81 × 15.0 × 7.5 mm bbox | |
| Surface area | 355.77 mm² | 1.5× the bbox footprint |
| Area distribution | 38.1 % near-vertical wall, 0 % flat top | The cap has a 15 mm-deep circular end, so almost half the surface faces sideways |
| Position | x ∈ [−15.81, −1.77] mm; area-weighted centroid ≈ (−8.13, 0.00, 3.14) mm | The surface is not centred on the origin. Any view aimed at the origin misses it |
| Face normals | 94.5 % inward-facing (normal.z < 0), normal.z ∈ [−0.9999, +0.6755] |
Winding is not trustworthy on this mesh; see §5 |
3. UV layouts
| Fact | Value |
|---|---|
| Maps | exactly two: produce and compute, 5922 loops each; compute is active |
compute extent |
u ∈ [0.00009, 1.0], v ∈ [0.1425, 0.85742] — the empty v-band is the shipped map's domain shape, not a defect |
| Texel density uniformity | per-polygon (UV area / world area): compute min 2.5e-4, p05 1.1e-3, median 1.391e-3, p95 1.872e-3, max 4.061e-3 → spread 2.92× |
produce density |
min 0.0 — degenerate zero-area UV faces exist, median 2.131e-3, max 4.216e-3 |
The unwrap's non-uniformity across the surface (2.92×) is larger than the axis-aligned
anisotropy docs/xynova-texture-investigation.md measured (1.17×) — that earlier figure was taken
at the centre point, which is the good case.
compute is the runtime contract: the shipped unroll_map_uv_s0.4.npz was built from this
mesh and this UV layout, so changing compute invalidates that asset and is a release event, not
a build side effect.
4. Sensing camera (cam_sensor)
| Fact | Value | Note |
|---|---|---|
| Type and lens | PERSP, effective lens 3.5 mm | |
| Sensor width | sensor_width 1.794 × object scale 6.009 = 10.78 mm |
The 6.009 object scale must be divided out by anything that derives a view from this camera, or the pitch is wrong by 6× |
| Position | origin 4.62 mm above the surface | |
| H-FOV | ≈ 114° | Extremely wide and very close: foreshortening is unavoidable |
| Coverage | 1887/1974 triangles inside the frustum (95.6 %), 0 occluded, 0 triangles beyond 75° incidence | The camera is not edge-on-limited |
| Incidence over the surface | p50 42.1° | Intrinsic to a 355.8 mm² cap with 38 % near-vertical wall |
5. Printed cell pitch (measured on the surface)
| Fact | Value | Note |
|---|---|---|
| Adjacent camera-cell pitch | median 0.516 mm horizontal × 0.714 mm vertical; horizontal spread 0.449–0.568 mm | The cell pitch is finer vertically than horizontally on the surface |
design.yaml nominal cell |
vector.width_mm/nx = 0.205, vector.height_mm/ny = 0.3556 |
Wrong as a physical cell size. The vector page (16.4 × 16.0 mm) was sized to the bbox, not to the camera's 80×45 grid |
| Consumers of the wrong value | cli.py:521 (cmd_inspect's nominal_cell_mm), cli.py:751 (pitch_mm = vector_width_mm / nx) |
pitch_mm feeds sample_stack, and therefore the thickness/contact numbers in the optics model |
| Grid vs surface | 80 cells × 0.516 mm = 41 mm of surface pitch across a 14.0 mm-wide surface | A cell spans ~13 surface fragments; the patch colour table is indexed by camera cell, so colour varies much faster than the cell pitch suggests |
| Page scale | 3280/3200 = 1.025 = 16.4/16.0, exactly 200 px/mm both axes |
Any change to nx/ny or the page must preserve this, or _warn_if_page_aspect_differs fires and analyse_map returns inconclusive |
The optics model currently derives the printed cell from the page arithmetic above. Replacing it with the measured 0.516 mm changes the reported thickness/contact numbers, so the two must move together.
6. Runtime unroll domain
From orisys-sdk/sensors/finger/assets/30fps-0703/unroll_map_uv_s0.4.npz:
| Fact | Value |
|---|---|
| Grid | 280 × 300 |
| Valid ratio | 0.408 — 34270 valid pixels |
| Keys | includes world_xyz, projection_K, source_scale, edge_band, edge_expand, method |
| Surface coverage | ≈ 268.8 mm² = 75.6 % of the 355.77 mm² surface (nearest-centroid association, max 1.0 mm error) |
Incidence over the runtime region (the part that actually matters, not the whole surface):
| Measure | Front ortho | Best of 2 views |
|---|---|---|
| p50 | 55.6° | 55.6° |
| p90 | 89.5° | 83.2° |
| > 60° | 44.97 % | — |
| > 75° | 30.4 % | — |
Two findings govern the whole multi-view design:
- Adding views improves the tail, not the median. p90 moves; p50 does not move at all.
- Azimuthal rotation about the cap axis is a measurable no-op — identical statistics at az = 0/±26/±45°. The cap is a surface of revolution, so rotating about its axis changes nothing.
7. Merge-mode agreement (2026-09-27, blend sha256 61093B1A…)
Measured on this revision, and the reason merge.incidence_limit_deg: 62.0 rather than 60:
| Quantity | Value |
|---|---|
| Authored seam | the y = 0 plane |
authored vs computed agreement |
within 0.0014 of coverage at a 60° incidence limit; identical from 62° up |
| Hole fraction at 60° | 0.0218 — fails the max_hole_fraction: 0.02 gate |
| Hole fraction at 62° | 0.0000 |
So neither mode is a compromise at this revision, and the 62° default is a measured threshold, not a preference.
This window is camera-specific and expires when a camera moves. The y = 0 coincidence holds because the two region cameras are mirrored about XZ at azimuth 308.26° / 231.74°, elevation −24.46°, distance 32.62 mm.
incidence_limit_degdescribes a camera-pose and surface combination, not a property of the sensor model, so moving a region camera invalidates it and the scan must be re-run.It has already happened once: on the Xynova surface, 62° produces 4.8 % hole, which is why that revision sets
incidence_limit_deg: 70.0explicitly and the YAML carries a warning not to delete it. The in-code defaults (texture_views.MERGE_DEFAULTSandtexture_merge.py) stay at 62.0 with a test requiring the two to agree, so removing the override silently restores a value that is wrong for that surface.A previous revision of
design.yamlalso recorded "hole 0.100 mm² / fraction 0.0002 at 62°" and "4960/5007 triangles owned, 20454 + 21483 fragments". Those figures were valid before the cameras moved and are retracted; they are recorded here only to show why camera-dependent numbers must state their re-measurement trigger.
A trap worth keeping: assigning a component by count of visible triangles does not work here — both views see ≈100 % of each component's triangles, so the count ties and a naive rule silently hands both components to the first view. Component assignment uses qualifying footprint, which folds in both the incidence limit and the footprint floor.
8. What these numbers imply for any view set
- The sensing surface is offset from the origin, so a frame centred on the origin misses it; derived frames must be built from the surface's own extent.
- The incidence p50 over the runtime region is intrinsic to the cap's shape. No finite view set removes it; it can only shorten the tail.
- The dominant print-distortion term is the UV unwrap's density spread (2.92×), not projection.
- 94.5 % inward normals means anything that trusts winding — a back-face cull, a renderer, a
"which side is the surface" heuristic — will get this mesh backwards. Either fix the winding in
the authored blend, or keep taking
abs()and record why.