Files
orisys-sensor-design/README.md
T
2026-09-18 11:25:09 +08:00

287 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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
```text
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](https://git-lfs.com/) 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)
```powershell
$env:ORISYS_BLENDER = "C:\Program Files\Blender Foundation\Blender 5.0\blender.exe"
```
Convenience launcher (auto-finds Blender for this process, then exports):
```powershell
.\tools\export-compute.bat
```
## Commands
Install the CLI (editable):
```powershell
cd D:\orisys_ws\orisys-sensor-design
python -m pip install -e .
```
Four commands for v1:
```powershell
# 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`:
```powershell
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:
```powershell
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:
```text
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.
```powershell
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:
```text
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:
```powershell
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:
```powershell
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`):
```powershell
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:
```powershell
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):
```ini
[server]
LFS_START_SERVER = true
[lfs]
PATH = /data/git/lfs
```
Docker Compose equivalent:
```yaml
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)
```powershell
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 |