Files
orisys-app/apps/autocal/docs/sequence-format.md
T
ykcheungab ff5f089dc1 autocal: 序列页阵列预览工具、2D 图点可见度,标签顺序与送到标定页
- 标签顺序:几何与安装 放到 测试序列 之前
- 序列点增加可选 uv_px(向后兼容),点表新增 U/V 列;加入所选点、删除所选、
  送到标定页(按 U/V 经同一套求解加入标定队列并切换页面)
- 已选点结果改显示在选点区状态行,不再写入全局顶部状态条
- 序列页左上区改紧凑:名称与备注同一行,分“阵列”“运动参数”两页;
  运动参数每行三组,输入框改用 compactField
- 新增阵列工具(array_plan.py、ui/array_tool.py):UV 规则网格(可蛇形遍历)与
  阶段预设,逐点检查是否在曲面内、是否留有边距,候选点实时预览在 2D 图上,
  只有可用点才能加入,已在序列里的点不重复加入
- 2D 平面图:标记改为缩放后再绘制并加深色描边,修复缩小后点不清;
  底图可选 原图/灰度/变暗/仅轮廓,点色可选 黄/白/青/橙/灰(默认灰度+黄,写入偏好);
  序列点带编号并连线显示遍历顺序,选中行高亮
- 新增测试 test_sequence_uv、test_sequence_to_capture、test_array_plan、test_array_tool
2026-10-19 11:39:09 +08:00

7.2 KiB

Calibration sequence format (.oryseq)

A calibration sequence is a small JSON document: the profile that generated the points, plus the points themselves. It is written by 导出序列… in the Sequence tab and read by 导入序列….

The Finger simulation page separately exports an operator CSV list with centred UV pixels, sensor/platform millimetres and mapping identity. It uses the manual calibration coordinate convention; that list is not an .oryseq and is not imported into the acquisition page.

Why JSON and not a binary container: a sequence is at most a few hundred points of text. It is read by a human, diffed in review, and emailed to whoever runs the bench. Binary would buy nothing and cost all three. (The acquired data of a run is a different problem — per-point time series are large and will need a sidecar; that decision belongs to workflow P8, not here.)

Conventions

The format follows this repository's existing documents rather than inventing a convention. Where it matches session.json and force_calibration.json, that is deliberate:

Convention Value
Version field "format_version": "1.0" — a dotted string, compared as a tuple
Identity "kind": "autocal.sequence" — checked on import
Extension .oryseq by convention; import accepts any .json
Encoding UTF-8, indent=2, ensure_ascii=False, trailing newline
Write temp file then Path.replace — a half-written sequence that still parses looks like a design

The document

{
  "format_version": "1.0",
  "kind": "autocal.sequence",
  "created_at": "2026-09-20T10:31:00+00:00",
  "name": "5 点灵敏度扫描",
  "note": "",
  "versions": { "app": "0.1.0", "shared": "0.1.0", "shared_api": "0.1" },
  "device": {
    "sensor_type": "box",
    "config_ref": "…\\config\\sensors\\box\\sensor.json",
    "geometry_id": "finger-1.0.0",
    "probe_tip_set": true
  },
  "frame": { "targets": "platform", "length_unit": "mm", "units_per_mm": 10.0 },
  "profile": {
    "mode": "flat", "pitch_mm": 2.0, "margin_mm": 1.0, "radius_mm": 0.0,
    "clear_height_mm": 5.0, "approach_speed_mm_s": 2.0, "press_speed_mm_s": 0.5,
    "press_depth_mm": 0.2, "dwell_s": 0.5, "settle_window_s": 0.5,
    "repeats": 3, "order": "serpentine"
  },
  "points": [
    {
      "id": 1,
      "target_mm": { "x": 0.0, "y": 0.0, "z": 12.4 },
      "sensor_mm": { "x": 0.0, "y": 0.0, "z": 0.0 },
      "clearance_mm": 5.0,
      "enabled": true,
      "note": ""
    }
  ],
  "summary": {
    "count": 12, "enabled": 12,
    "min_mm": { "x": 0.0, "y": 0.0, "z": 12.4 },
    "max_mm": { "x": 10.0, "y": 4.0, "z": 12.4 },
    "span_mm": { "x": 10.0, "y": 4.0, "z": 0.0 },
    "pitch_mm": 2.0,
    "estimated_duration_s": 380.0
  }
}

Fields

frame — required

Field Meaning
targets the frame target_mm is expressed in; today always "platform"
length_unit "mm"; anything else is refused
units_per_mm the pulse equivalent the points were built with

A sequence whose numbers do not say whether they are millimetres or user units is a sequence that will eventually be read wrong. units_per_mm is recorded so a file from a differently-configured axis is detectable rather than silently believed.

profile — required

The twelve fields the Sequence tab edits, and nothing else. A field absent from the file is kept at its current value and reported, never silently defaulted to another bench's number.

points — required, may be empty

Field Meaning
id 1-based, stable within the file; used for run progress and resume
target_mm where the axes go — required, with all three of x, y, z
sensor_mm where that point lands on the sensor; optional but written
uv_px {u, v}: the point's place on the flat map, in pixels from the map origin; optional (added without a version change, older files simply lack it). The capture page takes its points by U/V, so a point with uv_px can be sent there directly
clearance_mm the standoff for this point
enabled false = designed but deliberately skipped
note free text

Both positions are stored on purpose. The platform target is what the run uses; the sensor position is what makes the point checkable against the surface. Deriving one from the other on every load would silently change what the points mean the moment a sensor is re-fixtured — and a re-fixture is exactly when a stored transform should be noticed, not recomputed from.

summary — derived, not authoritative

Present so a human reading the file sees the point count and travel span without counting rows, and so a diff is legible. It is recomputed on load; a mismatch is reported as a warning and the recomputed values win. That is how a hand-edited document announces itself instead of quietly disagreeing with its own contents.

estimated_duration_s assumes a constant traverse rate (sequence_io.ASSUMED_TRAVERSE_MM_S) and ignores acceleration. It is a planning figure and is labelled an estimate wherever it is shown.

device — a reference, not a requirement

Records which sensor the sequence was designed for. Importing a sequence for another sensor is allowed and warned about, because re-fixturing deliberately is a normal thing to do.

Import rules

Every rejection names the specific problem — invalid file tells an operator nothing.

Condition Behaviour
not JSON refused, with line and column
top level not an object refused
kind missing or different refused, naming what was found
format_version missing or not a string refused
format_version newer than this build refused, naming both versions
frame missing, or length_unit not mm refused
profile not an object refused
a profile key absent imported; the current value is kept and the key is reported
points absent imported as profile-only, and reported
a point without target_mm.x/y/z refused, naming the point and the axis
summary disagreeing with points imported; recomputed, and reported
current design is unsaved the operator is asked before it is replaced

Version strings are compared as tuples, never as strings: "1.10" is newer than "1.9", and a string comparison would make a newer file look older and load it as if it were supported.

Compatibility rules

  • Adding an optional key does not change format_version.
  • Adding a required key, or changing a field's meaning or unit, does.
  • A reader must refuse a document whose version it does not support rather than interpreting the parts it recognises — a sequence half-applied is worse than one rejected.
  • 1.x readers are expected to accept 1.y for y <= x on the same major version and report anything they did not use.

Where the pieces live

Concern Module
The document, validation, the derived summary apps/autocal/src/orisys_autocal/sequence_io.py
The design screen and its controls apps/autocal/src/orisys_autocal/ui/sequence_panel.py
The file dialogs and the unsaved-work guard apps/autocal/src/orisys_autocal/ui/window.py
Why a sequence is an artifact (P8) apps/autocal/docs/workflow.md