2026-09-18 11:25:09 +08:00
2026-09-16 21:04:28 +08:00
2026-09-16 21:04:28 +08:00
2026-09-16 21:04:28 +08:00
2026-09-16 21:04:28 +08:00
2026-09-16 21:04:28 +08:00

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 # LFS: 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+
  • Git LFS on every client (git lfs install)
  • Blender version pinned in blender.lock.json (install separately)

Finding Blender on Windows

The CLI looks for Blender in this order:

  1. Env ORISYS_BLENDER or BLENDER_EXE (absolute path to blender.exe)
  2. blender / blender.exe on PATH
  3. Auto-scan %ProgramFiles%\Blender Foundation\Blender *\blender.exe (prefers blender.lock.json version when installed, otherwise newest)
  4. Optional "executable" in blender.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 one colored patch texture per UV map on the sensing surface. 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   # from the `produce` UV map (tracked, Git LFS)
└── patch_texture_compute.png   # from the `compute` UV map (tracked, Git LFS)

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 PNGs are committed so a revision's textures can be synced to hardware production; .gitattributes already routes *.png through Git LFS (git lfs install / git lfs pull as usual). Only _texture_job.json is ignored, because it records absolute machine paths and is rewritten every run.

Defaults come from the texture_patch block in design.yaml — mesh, camera, the uv_outputs map (UV map → PNG name), texture size, patch grid, colour separation, edge sampling, reference cross, and an optional seed. CLI flags override the shared 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):

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 1024

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_sensor relative to origin_sensor. Writes mesh.npz, camera_intrinsics.npz, world_to_cam.npy (here “world” means the sensor frame). Studio and the wheel build consume this tree via orisys-sdk → assets_dir.
  • studio — documents how to compile a .oryscene from the compute export plus the measured unroll map in SDK. Observation cameras in Studio are derived from mesh bounds; no Blender cam_viz is required.

Promotion (SDK / App)

After export:

  1. Tag this repo, e.g. finger-r01-assets-v1.0.0.
  2. Copy or attach the build/export/finger/r01/compute/ folder to the SDK asset path (or publish as a release artifact).
  3. Keep build/export/finger/r01/manifest.json beside the payload, and update optional design-lock.json in orisys-sdk with commit + bundle SHA256.
  4. Run orisys-app compile_scene.py using 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.001 in design.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.npy for SDK compatibility (sensor frame → camera).
  • Git LFS: *.blend, large textures, exported *.npz in build/export/.

Git LFS on Gitea (NAS)

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)

  1. Enable LFS in Gitea — edit app.ini (or Docker env):

    [server]
    LFS_START_SERVER = true
    
    [lfs]
    PATH = /data/git/lfs
    

    Docker Compose equivalent:

    environment:
      - GITEA__server__LFS_START_SERVER=true
    

    Point PATH at persistent NAS storage (same volume as /data is fine).

  2. Restart Gitea after changing config.

  3. Optional: set LFS_JWT_SECRET in Docker/env for stable LFS auth across restarts (generate once, keep in your NAS secrets).

  4. 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.

  5. 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 already tracks *.blend and large binaries. After git lfs track, always commit .gitattributes.

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 + LFS + r01 + headless compute export
B Lock file in SDK/App + CI reproduces export hashes
C (later) Markers, camera metrics, simulation datasets
S
Description
No description provided
Readme 90 MiB
Languages
Python 99.9%