新增统一力读数会话 ForceSession
把同帧测量、空载归零、模型推断与共用后处理收进一个 SDK 会话,Studio 显示与记录使用同一样本;旧 Sensor 读数接口保持兼容。以 force session 与受影响 SDK 套件、lint 与文档门禁验证,硬件与真实载荷精度未验证。 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CpCfJF6SvonMKmPZhzKZHb
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
# 力读数工作流(`ForceSession`)
|
||||
|
||||
用途:SDK 显式力读数会话(`studio-force/1`)的公共接口参考。状态:已实现并接入 Studio,2026-10-20;
|
||||
硬件、真实相机与真实载荷精度未验证。需求与验收矩阵见
|
||||
[流程规格](../../docs/workflow/studio-force-workflow.zh.md),实施状态见
|
||||
[重写计划](../../docs/workflow/studio-force-rewrite-plan.zh.md)。
|
||||
|
||||
测量、标定模型、空载基线与输出后处理全部由本会话拥有;调用方只提供源帧与帧身份,
|
||||
发送归零/换模型请求,并分发返回的 `ForceOutput`。这样同一帧只测量、推断、后处理各一次,
|
||||
显示与记录拿到同一个不可变输出。
|
||||
|
||||
## 入口与版本
|
||||
|
||||
```python
|
||||
from orisys import ForceSession, WORKFLOW_VERSION
|
||||
```
|
||||
|
||||
`WORKFLOW_VERSION = "studio-force/1"` 是处理顺序与语义的稳定身份,随每个输出返回,
|
||||
记录、缓存键与诊断引用它;改变顺序或语义时必须提升该版本。
|
||||
|
||||
构造显式启用测量源(`Sensor` 或 `FingerRuntime`)的会话接缝:
|
||||
|
||||
```python
|
||||
session = ForceSession(
|
||||
source=runtime, # 已打开、由调用方负责 close
|
||||
sensor_family="finger", # box | mini | finger | finger-mems
|
||||
device_id="FINGER-001", # 可选,用于标定身份校验
|
||||
context=context, # schema 3 模型需要的实际管线身份
|
||||
calibration=model, # Calibration(schema 1/2)、ModelCalibration(schema 3)或 None
|
||||
settings=output_settings, # 可选;默认 OutputSettings(sample_rate_hz=30.0)
|
||||
)
|
||||
```
|
||||
|
||||
构造失败不会留下“半切换”的会话;旧 `Sensor.read_info` / `ForceReadout.apply` 路径在
|
||||
未构造 `ForceSession` 时保持原语义,本模块不与旧路径并行运行。
|
||||
|
||||
## 主要方法与属性
|
||||
|
||||
| 成员 | 说明 |
|
||||
|---|---|
|
||||
| `process_frame(source_frame, stamp) -> ForceOutput` | 唯一逐帧入口:测量 → 归零状态 → 标定 → 后处理。同一 `(session_id, frame_id)` 重复调用返回同一缓存对象且不推进状态;乱序帧显式失败并清除完成标志 |
|
||||
| `read_sdk_frame(source_frame, stamp) -> SdkReading` | 只做同帧测量(图像准备、光流/HHD、可选接触/场),不推断、不读数滤波 |
|
||||
| `request_zero(*, reference_id, declared_unloaded, replay_window=None) -> ZeroProgress` | 启动唯一空载归零操作;`declared_unloaded` 必须为空载确认;拒绝带载相对置零 |
|
||||
| `update_zero(reading) -> ZeroProgress` | 推进归零状态机并执行本帧一次模型推断;迟到代次的结果被忽略 |
|
||||
| `invalidate(*, reason) -> ZeroProgress` | 断流、取消、换模型或测量配置变化时作废基线/完成状态与滤波历史 |
|
||||
| `apply_calibration(reading, zero) -> CalibratedReading` | 不复用旧结果、不重复推断,给出未做读数滤波的 raw/N 与逐轴状态 |
|
||||
| `apply_filter(measurement) -> ForceOutput` | 唯一后处理:raw 与 N 各自独立地 median → low-pass → 可关闭逐轴阈值 |
|
||||
| `set_calibration(calibration, *, context=None)` | 帧边界原子换模型;校验失败保留原模型、基线与完成状态 |
|
||||
| `zero`、`calibration_fingerprint`、`output_settings`、`last_output` | 只读状态/身份;`output_settings` 是实际生效参数 |
|
||||
|
||||
## 帧身份
|
||||
|
||||
`FrameStamp(session_id, frame_id, source_frame_id, signal_time_s, time_basis)`;后处理低通的
|
||||
采样率来自信号时间,不取离线处理耗时。实时与顺序录像只替换源适配器,其余流程相同。
|
||||
|
||||
## 归零(空载基线)
|
||||
|
||||
1. `request_zero(declared_unloaded=True, reference_id=...)` 只登记请求并递增参考代次;
|
||||
2. 处理线程在下一个帧边界重置 tracker,然后才测量该帧;
|
||||
3. 采集重置后连续 30 个有效处理帧,按模型类型得到基线:标量模型取 raw 中位数,
|
||||
场模型委托 `ModelCalibration` 用同一增益图加权后的 30 帧中位数;
|
||||
4. 第 30 帧仍为基线帧;第 31 帧起 `phase="ready"`、`completed=True` 并输出 N,
|
||||
`effective_from_frame_id` 指向该帧。
|
||||
|
||||
基线未完成、无效帧、窗口不足或已取消时 N 为 `NaN`、状态显式;窗口不足不越界补帧。
|
||||
录像重算用 `replay_window=(start, end)` 在声明区间执行同一操作。
|
||||
|
||||
## 后处理与有效设置
|
||||
|
||||
`OutputSettings(sample_rate_hz, median_window=1, lowpass_hz=0.0, lowpass_order=2,
|
||||
raw_threshold=0.0, newton_threshold=0.0)`。每阶段由自身值关闭(median=1、lowpass=0、
|
||||
threshold=0),全关时对有效值严格直通(NaN 保留);无效轴不进入滤波历史。
|
||||
`ForceOutput.settings` 返回实际生效的参数(例如截止频率达到 Nyquist 时解析为 off)。
|
||||
|
||||
## 离线重滤波
|
||||
|
||||
```python
|
||||
from orisys.force_readout import process_output_sequence
|
||||
|
||||
raw_out, newton_out, effective = process_output_sequence(
|
||||
settings, raw_samples, newton_samples,
|
||||
raw_valid=None, newton_valid=None, signal_times_s=times,
|
||||
)
|
||||
```
|
||||
|
||||
该函数只重放输出级(带完整 warm-up),用于从已存未滤波序列生成新参数下的最终输出;
|
||||
不重新测量、不打开模型链。
|
||||
|
||||
## 状态与诊断
|
||||
|
||||
逐轴状态:`ok`、`out_of_range`、`zero_unavailable`、`no_contact`、`coordinate_unmapped`、
|
||||
`outside_coverage`、`invalid_input`、`uncalibrated`、`measurement_failed`、`frame_order`、
|
||||
`signal_time_order`。仅 `ok`/`out_of_range` 携带可用牛顿值;模型不输出的轴(例如 FN-only
|
||||
模型的 FX/FY)恒为 `NaN` + `uncalibrated`。缺失值与有效零分开。
|
||||
|
||||
`CalibratedReading.diagnostics` 提供 `reference_id`、`baseline_N`、`baseline_raw_fn/fx/fy`、
|
||||
`model_fingerprint`、`mapping`、`extrapolated_fraction`、`coverage_fraction`、`area_scale`、
|
||||
`pending_reference_frames` 等;调用方存储或展示,不据此改写逐轴有效性。
|
||||
|
||||
场模型质量(外推、覆盖)与 downsample 差异属于诊断,不替代有效性判断;
|
||||
`hhd_downsample` 不同是唯一允许的管线身份差异,其余差异仍拒绝并报告。
|
||||
Reference in New Issue
Block a user