12 KiB
orisys-sensor-design
Nominal sensor geometry and Blender authoring for Orisys. This repo is the design
source of truth. Measured calibration, unroll maps, and shipped runtime bundles live in
orisys-sdk and compiled .oryscene packages live in orisys-app.
For the full long-term workflow see sensor-design-workflow.md in the workspace root.
This repository intentionally implements a minimal v1 for a small team.
Two buckets (daily work)
| Bucket | Where | Contents |
|---|---|---|
| Design | this repo | .blend, nominal export, release manifest |
| Runtime | orisys-sdk / orisys-app |
measured cal, unroll map, wheel/App assets, .oryscene |
Never copy runtime NPZ files into this repo to “fix” a mismatch. Promote through a manifest + lock file instead.
Layout
sensors/finger/
├── current.yaml # active revision pointer
└── r01/
├── design.yaml # units, frames, object names
├── source/geometry.blend # named collections + objects
├── profiles/
│ ├── compute.yaml # → SDK bundle layout (Studio runtime)
│ └── studio.yaml # inputs for orisys-app compile_scene
└── golden/ # optional preview + metrics
Prerequisites
- Python 3.10+
- Blender version pinned in
blender.lock.json(install separately)
No Git LFS is required — binaries are committed as ordinary files.
Finding Blender on Windows
The CLI looks for Blender in this order:
- Env
ORISYS_BLENDERorBLENDER_EXE(absolute path toblender.exe) blender/blender.exeonPATH- Auto-scan
%ProgramFiles%\Blender Foundation\Blender *\blender.exe(prefersblender.lock.jsonversion when installed, otherwise newest) - Optional
"executable"inblender.lock.json(local only; do not commit machine paths)
$env:ORISYS_BLENDER = "C:\Program Files\Blender Foundation\Blender 5.0\blender.exe"
Convenience launcher (auto-finds Blender for this process, then exports):
.\tools\export-compute.bat
Commands
Install the CLI (editable):
cd D:\orisys_ws\orisys-sensor-design
python -m pip install -e .
Four commands for v1:
# 1) Check revision metadata and (optionally) Blender scene conventions
orisys-design validate sensors/finger/r01
# 2) Export nominal geometry for a profile (compute → SDK layout)
orisys-design export sensors/finger/r01 --profile compute
# 3) Generate the colored patch texture headlessly
orisys-design texture sensors/finger/r01
# 4) Write release manifest with file hashes
orisys-design manifest sensors/finger/r01
Headless Blender export uses a JSON job file (stdlib json inside Blender — no
PyYAML required). orisys-design export writes it automatically from design.yaml:
orisys-design export sensors/finger/r01 --profile compute
# writes build/export/finger/r01/_export_job.json then runs Blender
Manual Blender invocation with the same job:
blender -b sensors/finger/r01/source/geometry.blend -P blender/scripts/export.py -- `
--job D:\orisys_ws\orisys-sensor-design\build\export\finger\r01\_export_job.json
Optional flag overrides (--output, --mesh, --cam, --origin) win over job values.
Export output is grouped by revision:
build/export/finger/r01/
├── _export_job.json # Blender input, regenerated by export
├── manifest.json # release manifest from `orisys-design manifest`
└── compute/ # clean SDK-shaped payload
├── mesh.npz
├── camera_intrinsics.npz
└── world_to_cam.npy
Texture patches (headless)
orisys-design texture generates a PNG, editable SVG, and native vector PDF for
each UV map on the sensing surface. The SVG/PDF are built directly from the UV
mesh and camera-space patch polygons — they do not embed or trace the PNG.
UV unwrapping is still an in-app Blender step: this command
consumes the UV maps already stored in source_blend and never creates or
unwraps one. Unwrap the mesh in the Blender GUI, save the .blend, then run the
command. A .blend saved in Edit mode is fine — the headless run enters Object
mode in memory and never writes the file.
orisys-design texture sensors/finger/r01
# writes build/textures/finger/r01/_texture_job.json then runs Blender
Output is revision-scoped, so nothing has to be copied by hand:
build/textures/finger/r01/
├── _texture_job.json # Blender input, regenerated by texture (gitignored)
├── patch_texture_produce.png # raster preview/use (tracked)
├── patch_texture_produce.svg # editable grouped vector artwork
├── patch_texture_produce.pdf # vector production/print handoff (tracked)
├── patch_texture_compute.png
├── patch_texture_compute.svg
└── patch_texture_compute.pdf
produce is the hardware-production layout and compute the compute/runtime
layout; they are the two UV maps authored on sensing_surface. Both outputs
share one set of settings and one seed, and both read the same camera-space
patch grid, so a given physical patch carries the same colour in either file —
the images differ only in how the unwrap arranges the patches.
Generated artifacts are committed so a revision's textures can be synced to
hardware production. They are stored as real files, not Git LFS pointers, so a
plain git clone yields usable images and .blend files; .gitattributes only
marks binaries as non-text to protect them from line-ending conversion. Only
_texture_job.json is ignored, because it records absolute machine paths and is
rewritten every run.
SVG imports into CorelDRAW as named groups (background, mesh, patches, and
reference-cross) containing individual editable polygons. PDF contains the
same filled vector paths on a correctly sized page, with no embedded bitmap.
Seam-wrapped duplicate polygons are clipped to the page, so no stray objects sit
outside the artwork.
Defaults come from the texture_patch block in design.yaml — mesh, camera, the
uv_outputs map (UV map → artifact stem), raster dimensions, vector page size in
millimetres, patch grid, colour separation, edge sampling, reference cross, and
an optional seed. The current production page is configurable as:
vector:
width_mm: 164
height_mm: 160
CLI flags override the shared rendering values for every output:
orisys-design texture sensors/finger/r01 `
--width 1024 --height 1024 --nx 16 --ny 12 --seed 7 --no-cross
Manual Blender invocation with the same job:
blender -b sensors/finger/r01/source/geometry.blend -P blender/scripts/random_patch_dd02c.py -- `
--job D:\orisys_ws\orisys-sensor-design\build\textures\finger\r01\_texture_job.json
A single manual target is also possible (this cannot be combined with --job).
--output names the PNG; same-stem SVG/PDF files are generated alongside it.
The vector page defaults to 164×160 mm and can be overridden manually:
blender -b sensors/finger/r01/source/geometry.blend -P blender/scripts/random_patch_dd02c.py -- `
--uv-map produce --output D:\tmp\produce.png --width 1024 --height 999 `
--vector-width-mm 164 --vector-height-mm 160
Only the colored "patches" mode is reachable headlessly; the checker and dot
helpers remain in random_patch_dd02c.py but are not exposed by the CLI. The
default 3280×3200 grid matches the in-app script and is deliberately slow
(pure-Python rasterization) — use smaller --width/--height/--nx/--ny for quick
check runs, and the full default grid for the textures you actually ship.
Collection check:
blender -b sensors/finger/r01/source/geometry.blend -P blender/scripts/validate.py -- sensors/finger/r01
Profiles
compute—sensing_surface+cam_sensorrelative toorigin_sensor. Writesmesh.npz,camera_intrinsics.npz,world_to_cam.npy(here “world” means the sensor frame). Studio and the wheel build consume this tree viaorisys-sdk→assets_dir.studio— documents how to compile a.oryscenefrom the compute export plus the measured unroll map in SDK. Observation cameras in Studio are derived from mesh bounds; no Blendercam_vizis required.
Promotion (SDK / App)
After export:
- Tag this repo, e.g.
finger-r01-assets-v1.0.0. - Copy or attach the
build/export/finger/r01/compute/folder to the SDK asset path (or publish as a release artifact). - Keep
build/export/finger/r01/manifest.jsonbeside the payload, and update optionaldesign-lock.jsoninorisys-sdkwith commit + bundle SHA256. - Run
orisys-appcompile_scene.pyusing the locked compute export + SDK unroll map.
Example lock file: examples/design-lock.json.
Conventions
- Units: metres at export boundaries. Author in mm if you like; set
meters_per_source_unit: 0.001indesign.yaml. - Collections:
FRAMES,EXPORT_COMPUTE,ASSEMBLY,DESIGN_SIM,EXCLUDE. - Objects:
origin_sensor,sensing_surface,cam_sensor. - Matrix names must state direction; exported pose file remains
world_to_cam.npyfor SDK compatibility (sensor frame → camera). - Binary files:
.blend, textures and exported*.npzare committed as ordinary files (no Git LFS)..gitattributesmarks them-textonly.
Git LFS on Gitea (NAS) — not currently used
Status: this repository does not use Git LFS. .gitattributes only marks
binaries as non-text, and real .blend / .png / .pdf bytes are committed
directly. This is deliberate: LFS pointers are unusable without a reachable LFS
server, so a plain clone or git checkout would hand you 131-byte files instead
of images. The notes below are kept as a migration guide for when the NAS server
is ready.
Short answer: not difficult. Gitea has built-in LFS; NAS hosting is common. Plan for extra disk on the NAS volume that holds LFS objects, not just the git bare repos.
Server (one-time, ~5 minutes)
-
Enable LFS in Gitea — edit
app.ini(or Docker env):[server] LFS_START_SERVER = true [lfs] PATH = /data/git/lfsDocker Compose equivalent:
environment: - GITEA__server__LFS_START_SERVER=truePoint
PATHat persistent NAS storage (same volume as/datais fine). -
Restart Gitea after changing config.
-
Optional: set
LFS_JWT_SECRETin Docker/env for stable LFS auth across restarts (generate once, keep in your NAS secrets). -
Reverse proxy: LFS uploads use HTTP(S) batch API. Ensure your NAS proxy allows large request bodies (nginx
client_max_body_size, etc.). SSH git push still works for normal objects; LFS file transfer is usually HTTPS unless you enable pure SSH LFS. -
Backup: include the LFS directory in NAS backup — it is not inside git history.
Requirements: Git ≥ 2.1.2 on the server (Gitea’s embedded git is enough).
Clients (each developer)
git lfs install
git clone git@your-nas:team/orisys-sensor-design.git
cd orisys-sensor-design
git lfs pull
This repo’s .gitattributes does not track anything through LFS today. To turn
LFS on, add the extensions back with a filter (the file contains a ready-made
example line), run git add --renormalize . so real bytes become pointers, and
commit .gitattributes together with the renormalized files.
Typical pitfalls
| Issue | Fix |
|---|---|
Push succeeds but .blend is a tiny pointer file |
Run git lfs install before commit; re-add file |
| LFS upload 413 / timeout | Increase proxy body size and Gitea [server] LFS_HTTP_TIMEOUT if needed |
| Wrong permissions on NAS volume | Match container USER_UID / USER_GID to volume owner |
| Repo already has large blobs in history | Do not rewrite history casually; use git lfs migrate only with team agreement |
Pure SSH LFS (LFS_ALLOW_PURE_SSH) is optional and still has client quirks; HTTPS LFS
behind your NAS reverse proxy is the path of least resistance.
Phase plan
| Phase | Goal |
|---|---|
| A (now) | Repo + r01 + headless compute export |
| B | Lock file in SDK/App + CI reproduces export hashes |
| C (later) | Markers, camera metrics, simulation datasets |