254 lines
12 KiB
YAML
254 lines
12 KiB
YAML
sensor: finger-xynova
|
|
revision: r01
|
|
status: draft
|
|
|
|
# Authoring unit in Blender (mm). Export converts using meters_per_source_unit.
|
|
authoring_unit: mm
|
|
meters_per_source_unit: 0.001
|
|
|
|
source_blend: source/geometry.blend
|
|
|
|
# Expected sensing surface topology for r01 (from shipped 30fps-0703 runtime).
|
|
# First exports from geometry.blend may differ until topology is locked.
|
|
expected:
|
|
sensing_vertices: 2595
|
|
sensing_triangles: 5005
|
|
|
|
frames:
|
|
sensor: origin_sensor
|
|
|
|
objects:
|
|
sensing_surface: sensing_surface
|
|
cam_sensor: cam_sensor
|
|
origin_sensor: origin_sensor
|
|
|
|
# Headless colored-patch texture generation (`orisys-design texture`).
|
|
# UV unwrapping is still done in the Blender GUI: the headless run consumes the
|
|
# UV maps already stored in source_blend and never creates or unwraps one.
|
|
# One PNG/SVG/PDF set is written per UV map below. All maps and formats share
|
|
# the settings and patch colours, so they differ only by UV layout.
|
|
# These are the revision defaults; CLI flags override every value.
|
|
texture_patch:
|
|
mesh: sensing_surface
|
|
camera: cam_sensor
|
|
uv_outputs:
|
|
produce: patch_texture_produce.png # hardware production layout
|
|
compute: patch_texture_compute.png # compute/runtime layout
|
|
width: 3700 # 1850*2
|
|
height: 3280 # 1640*2
|
|
# Camera frame is square (1000x1000), so equal counts make camera-space
|
|
# elements square. The original 80x45 grid was copied from a 16:9 camera.
|
|
nx: 80
|
|
ny: 80
|
|
color_r: 0.05
|
|
edge_sample_grid: 5
|
|
seed: 7 # fixed so PNG/SVG/PDF share one reproducible colour set
|
|
vector:
|
|
width_mm: 18.5 # editable SVG/PDF production page width
|
|
height_mm: 16.4 # editable SVG/PDF production page height
|
|
cross:
|
|
enabled: true
|
|
length_px: 40
|
|
thickness_px: 3
|
|
color: [1.0, 0.0, 0.0, 1.0] # red reference cross
|
|
|
|
# Multi-view production textures (`orisys-design texture-views`).
|
|
#
|
|
# `cam_uv_1` / `cam_uv_2` already existed in this blend; `uv_1` / `uv_2` and the seam were
|
|
# added to `sensing_surface`. Verified: the marked seam is a valid cut — 45 edges splitting
|
|
# the surface into 2 components of 308.314 / 308.311 mm^2 (50.0 / 50.0) — and at the default
|
|
# 62 degree limit the partition leaves 0.100 mm^2 of hole (fraction 0.0002), well inside
|
|
# max_hole_fraction. Coverage: 4960/5007 triangles owned, 20454 + 21483 fragments emitted.
|
|
#
|
|
# NOTE: this revision contains SIX sensing-surface variants (`sensing_surface`, `_0`, `_0918`,
|
|
# `_0928`, `_45`, `_918_s`). `_0928` is the only one carrying the seam and `uv_1`/`uv_2`, so
|
|
# `sensing_surface` is a copy of it. Two consequences to be aware of:
|
|
# 1. `_0928` is a COINCIDENT duplicate of `sensing_surface` at identical coordinates. The
|
|
# `EXCLUDE` collection has `exclude=False`, so it is not actually excluded from the view
|
|
# layer, and the raycast hits the duplicate instead of the target. That rejects every
|
|
# fragment and the region cameras return 0 — `texture-views` now fails loudly and names
|
|
# the intercepting object rather than writing blank textures. `_0928` is hidden.
|
|
# 2. The probe/render baseline (`build/probe/finger-xynova/r01/probe_frames.npz`) was built
|
|
# from the 2508-vertex `sensing_surface_918_s`, while `sensing_surface` has 2584 vertices.
|
|
# `tests/test_render.py::test_end_to_end_render_manifest` therefore fails on the vertex
|
|
# count. That is a fixture/stale-baseline mismatch, not a texture bug: either regenerate
|
|
# the probe frames for the current surface, or author the seam and the extra UV layouts
|
|
# on whichever surface is canonical. `expected.*` below would have caught this and is
|
|
# still read by nothing.
|
|
texture_patch_views:
|
|
enabled: true
|
|
pattern_camera: cam_sensor # the pattern every layout carries, same source as `compute`
|
|
# Per-view page sizing. Measured here: the uv_1/uv_2 layouts are 21.1989 x 23.2003 mm
|
|
# (aspect 0.9137) and 21.2001 x 23.2003 mm (aspect 0.9138) — PORTRAIT. `vector:` above says
|
|
# 18.5 x 16.4 (aspect 1.1280), which is LANDSCAPE, so the artwork was stretched 23% in the
|
|
# wrong direction. `auto` derives the page per view and proves the layout really is a
|
|
# bounds-projection from the named camera before using the result.
|
|
#
|
|
# `px_per_mm: 200` reproduces the existing density exactly (3700/18.5 = 200.0).
|
|
#
|
|
# `fixed` keeps `texture_patch.vector`, which unwrapped layouts (`produce`, `compute`)
|
|
# require — they are not projections.
|
|
#
|
|
# Overlay note: the scene render is 250x250 while these layouts are 0.9138 aspect, so set the
|
|
# render resolution to the emitted raster size and use "Stretch to fill".
|
|
page:
|
|
mode: auto
|
|
px_per_mm: 200
|
|
pairs:
|
|
- id: uv_1
|
|
camera: cam_uv_1 # REGION camera
|
|
uv_map: uv_1
|
|
- id: uv_2
|
|
camera: cam_uv_2
|
|
uv_map: uv_2
|
|
merge:
|
|
mode: authored # honour the 45-edge seam marked on the mesh
|
|
# RE-MEASURED 2026-09-29 after the region cameras moved (cam_uv_1 az 318.9 -> 308.3,
|
|
# elevation -43.3 -> -24.5, distance 37.6 -> 32.6 mm). Sweep at min_triangle_coverage 0.60:
|
|
# 62deg -> hole 0.0480 FAILS max_hole_fraction (a visible bald patch of 29.6 mm^2)
|
|
# 65deg -> hole 0.0130 passes, but barely
|
|
# 70deg -> hole 0.0001 passes with margin <-- current
|
|
# The limit is a property of the camera poses and the surface, so MOVING THE CAMERAS
|
|
# INVALIDATES IT. Re-run the sweep after any re-pose. min_triangle_coverage is not binding
|
|
# here: relaxing it to 0 changes nothing at 62 or 70 degrees.
|
|
incidence_limit_deg: 70.0
|
|
min_footprint_mm2: 2.0e-3
|
|
min_triangle_coverage: 0.60
|
|
max_hole_fraction: 0.02 # enforced: the run fails above this rather than shipping a gap
|
|
component_policy: best_coverage
|
|
seam:
|
|
boundary: use_seam
|
|
pins: []
|
|
# Empty band along the seam, to absorb two-pass registration error. Default total width is
|
|
# 2 x alignment_tolerance_mm = 0.2 mm, half per file; `total_mm` overrides the derivation.
|
|
gap:
|
|
enabled: false # false = hard butt at the seam, no band at all
|
|
apply: post_process # post_process = straight edge | geometry_trim = sawtooth
|
|
treatment: no_print # the band carries nothing
|
|
alignment_tolerance_mm: 0.1 # per-pass placement accuracy, mm
|
|
print:
|
|
base_fill: owned # white ground, clipped by the seam
|
|
|
|
# Post-generation printability inspection (`orisys-design inspect`). Advisory
|
|
# only: it reports a verdict but never blocks generation or changes exit codes.
|
|
# Millimetres are measured on the vector page declared above.
|
|
printability:
|
|
printer_dpi: 600 # target UV printer resolution
|
|
min_dots: 2 # printer dots required across the smallest feature
|
|
min_feature_mm: 0.10 # smallest acceptable feature width
|
|
min_edge_mm: 0.05 # shortest acceptable polygon edge
|
|
max_sliver_ratio: 4.0 # longest edge / minimum width
|
|
min_patch_area_mm2: 1.0e-4 # area above which a polygon is a material feature
|
|
|
|
# Optical stack above the markers, ordered from the camera inward.
|
|
# The markers sit on the sensing surface underneath these layers, so their image
|
|
# is displaced relative to the straight-line pinhole model.
|
|
#
|
|
# ior values are LITERATURE ASSUMPTIONS, not measurements - that is what
|
|
# ior_source records, so an assumed index can never pass for a measured one.
|
|
# Replace them with measured values when available; nothing else needs to change.
|
|
optics:
|
|
marker_surface: sensing_surface
|
|
layers:
|
|
# Solids, not surface pairs: `shell` and `rubber-1` are each one closed mesh,
|
|
# so the ray enters and leaves the same object.
|
|
# Stack: camera > shell > rubber-1 > sensing_surface > rubber-2.
|
|
# rubber-2 is BEHIND the markers, so it does not refract camera light and is
|
|
# deliberately not a layer.
|
|
- name: plastic_shell
|
|
solid: shell
|
|
ior: 1.59
|
|
ior_source: assumed_pc_literature
|
|
- name: clear_rubber
|
|
solid: rubber-1
|
|
ior: 1.43
|
|
ior_source: assumed_silicone_literature
|
|
|
|
# Probe sensitivity bench (`orisys-design deform|render|track|sensitivity|optimise`).
|
|
# Deforms the vertices of `group` through known x/y/z displacements expressed in a
|
|
# local probe-average basis (normal force along z, shear along x/y), renders what
|
|
# `camera` sees, and measures DIS optical flow against the zero-displacement frame.
|
|
#
|
|
# Contract: docs/probe-pipeline.md. The `.blend` is never written by the pipeline.
|
|
probe:
|
|
mesh: sensing_surface
|
|
camera: cam_sensor
|
|
group: probe
|
|
uv_map: compute
|
|
texture: patch_texture_compute.png
|
|
# Proportional falloff radius for the outer rubber. Note this mesh has a median
|
|
# edge of 0.63 mm, so 0.5 mm reaches only the probe's immediate neighbours; the
|
|
# deform report prints how many vertices actually move rather than hiding it.
|
|
radius_mm: 0.5
|
|
falloff: smooth
|
|
step_mm: 0.01
|
|
max_mm: 0.10
|
|
axes: [x, y, z]
|
|
# Frames are rendered at the revision's real camera resolution. Keep this in step with
|
|
# the camera in source/geometry.blend: the pipeline overrides the scene resolution from
|
|
# here, so a blend edited to a different size is silently ignored otherwise.
|
|
#
|
|
# Single scale on purpose. The ROI radius is radius_mm x the probe-local px/mm, so at
|
|
# 125x125 it would be 1.66 px against an 8 px DIS window: the estimator measures its own
|
|
# regulariser, not the displacement. See docs/probe-findings.md section 2.
|
|
resolution: [250, 250]
|
|
process_resolution: [250, 250]
|
|
# Mirrors the SDK sensor tracker keys (orisys-sdk config/finger.json), EXCEPT
|
|
# dis_mode. The estimator window is 8 * 2**dis_mode input px (16 at dis_mode=1, 8 at 0)
|
|
# and the moving region is ~6.6 px across at 250x250, so the shipped dis_mode=1 gives
|
|
# window/moving = 2.42: DIS's smoothness pulls the estimate toward zero and the measured
|
|
# response drops ~3x (1.42 -> 4.30 px/mm at the matched cell). dis_mode=0 gives 1.21,
|
|
# the same ratio as 500x500 with dis_mode=1, which recovers to ~73%.
|
|
# See docs/probe-findings.md section 2.
|
|
# If the deployed tracker is fixed at dis_mode=1, run the bench at 500x500 instead.
|
|
tracker:
|
|
algorithm: dis
|
|
dis_preset: medium
|
|
dis_mode: 0
|
|
# Standard (not AgX) and denoise off: a filmic view transform silently distorts
|
|
# the marker values the tracker is supposed to measure.
|
|
render:
|
|
engine: CYCLES
|
|
samples: 16
|
|
denoise: false
|
|
view_transform: Standard
|
|
interpolate: Closest
|
|
seed: 0
|
|
# Noise calibration and the injected noise model.
|
|
#
|
|
# The estimator's `floor_px` is measured as reference vs a re-render at seed+1, which
|
|
# is Cycles antialiasing jitter, NOT sensor noise: on this data it differs by ~5 grey
|
|
# levels RMS on half the pixels and scales with edge contrast. Ranking patterns by
|
|
# gain/that-floor is unsound - the ordering reverses under additive noise, and it
|
|
# understates the real idle floor measured from the footage below, coarse patterns
|
|
# most. So the render-seed floor is kept only as a reproducibility diagnostic and the
|
|
# score is anchored to the injected model plus this measured calibration.
|
|
noise:
|
|
# Real idle (no-contact) footage, relative to the repo root. Measured: floor
|
|
# ~0.23 px at 1280x720 (short baseline == long baseline, so it is white noise and
|
|
# the clip is genuinely idle), dominant marker period ~25 px, i.e. ~0.0093 floor
|
|
# per period. That is 2-6x the simulated render-seed floor per cell.
|
|
idle_footage: ../data/dd02-0703/190.mp4
|
|
roi_fraction: 0.18
|
|
max_frames: 120
|
|
# Additive white Gaussian in grey levels. Defaults are deliberately small: each
|
|
# sigma costs a full DIS pass, and the process scale is a resample of the render
|
|
# one, so only the render scale is evaluated unless asked.
|
|
sigmas: [4.0, 16.0]
|
|
draws: 1
|
|
seed: 0
|
|
models: [render_seed, injected]
|
|
scales: [render]
|
|
|
|
collections:
|
|
frames: FRAMES
|
|
export_compute: EXPORT_COMPUTE
|
|
assembly: ASSEMBLY
|
|
design_sim: DESIGN_SIM
|
|
exclude: EXCLUDE
|
|
|
|
notes: |
|
|
Canonical source is geometry.blend with named collections and objects.
|
|
Compute export uses sensing_surface + cam_sensor relative to origin_sensor.
|
|
Studio observation cameras are derived from mesh bounds; no cam_viz required.
|