feat: SensorStream 支持文件完整模式、源时间与 field 输出
T08b 补齐文件/debug 完整处理与背压、源帧号和时间基、同帧 field 快照,输入结束时未完成归零明确报错。独立验收见 docs/workflow/sdk-sensorstream-implementation-plan.zh.md 第 44 轮。 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
# 新力侧会话与单路入口(T06a/T06b、T07 输出滤波、T08a)
|
||||
# 新力侧会话与单路入口(T06a/T06b、T07 输出滤波、T08a/T08b)
|
||||
|
||||
本文件是单路 SensorStream 计划 T06a/T06b/T07 的 SDK 合同:新的力侧会话直接消费 T05 的
|
||||
`ImageMeasurement`(T03 `MeasurementFrame` 头 + 同帧 raw/field),参考版本取帧上的
|
||||
@@ -73,7 +73,7 @@ assert session.output_settings.median_window == 3, "switch waits for a frame bou
|
||||
assert session.design_sample_rate_hz == 30.0
|
||||
```
|
||||
|
||||
## SensorStream 入口(T08a)
|
||||
## SensorStream 入口(T08a/T08b)
|
||||
|
||||
`SensorStream` 把 T04–T07 串成一个运行中的单路入口:
|
||||
|
||||
@@ -81,23 +81,35 @@ assert session.design_sample_rate_hz == 30.0
|
||||
已打开的 Sensor 或外部喂帧时只借用,`stop()` 不关闭借用源。运行中只有一个采集所有者;
|
||||
流在 `start()` 启用 measurement session,`stop()`/启动失败时恢复借用 Sensor 原来的模式
|
||||
(原本已启用的保持启用)。
|
||||
- **实时 latest。** `push_frame(...)` 的未领取候选帧会被最新输入覆盖并计入
|
||||
- **实时 latest(T08a)。** `push_frame(...)` 的未领取候选帧会被最新输入覆盖并计入
|
||||
`overwritten_frames`;已经交给流水线的帧照常完成。`wait_output()` 是 latest-wins 结果流,
|
||||
`drain_events()` 取会话事件。
|
||||
- **文件/debug 完整(T08b)。** `realtime=False` 时逐帧处理每个被接受的输入:外部帧进入
|
||||
有上限的 FIFO(`depth`),计算跟不上时 `push_frame` 阻塞并计入 `backpressure_waits`,不丢帧;
|
||||
`end_input()` 结束序列,`wait_outputs(n)`/`drain_outputs()` 按发布顺序返回结果。内置文件源由
|
||||
`SensorStream.open(path, realtime=False, nominal_fps=...)` 打开,同样逐帧处理。文件模式下,
|
||||
只有在**所有已接受帧都已通过力阶段**(`published >= accepted`)且零点仍未完成时,
|
||||
`reset_and_zero` 才以 `SensorStreamError`(“input ended before unloaded zero completed”)
|
||||
结束;生产者刚读到结束标记、管线中还有已接受帧时不会提前判失败,足够的已接受帧仍会正常完成零点。
|
||||
- **线程/版本。** 复用 `StreamPipeline` 的线程与原子 `request_reset()` 版本;力侧只在 HHD
|
||||
线程的 `on_frame` 中调用 `ForceStreamSession`,因此 T07 的 `drain_events` 线程约束由门面满足,
|
||||
没有第二套版本或复位记账。
|
||||
- **组合归零。** `reset_and_zero(reference_id=...)` 执行“图像复位确认 → 同版本 30 帧无载基线 →
|
||||
力 ready”;用户不管理版本号。在途旧版本帧不会进入新基线(T06a 的规则)。每次调用只等待
|
||||
本条命令的结果:力线程把该命令的 `ZeroProgress`/错误写回命令对象并通知,调用方线程不读取
|
||||
`session.zero`,因此连续两次调用时第二次不会复用第一次的零点。
|
||||
- **T08a 边界。** 实时模式、无标定/标量会话、逐帧结果最新视图已实现;field/contact 的
|
||||
`include`、文件/debug 完整逐帧、有界背压、时间来源与 PTS 标注、`source_dt_s`/长间隔诊断和
|
||||
完整生命周期诊断属于 **T08b**。
|
||||
- **时间基准(T08b 实现约定)。** 实时相机 `capture_monotonic`;Orisys 自己录像(文件内保存
|
||||
信号时间)`recorded_signal`;外部调用方提供可证明的容器 PTS 才 `video_pts`;内置 OpenCV
|
||||
读取普通视频文件用 `estimated_source`,`capture_time_ns` 按 `source_frame_id ÷ 名义帧率` 推算,
|
||||
POS_MSEC 只进 diagnostics。T08a 的外部帧默认 `capture_monotonic`。
|
||||
`session.zero`,因此连续两次调用时第二次不会复用第一次的零点。**超时后命令仍在力线程进行**
|
||||
(仍可能稍后 ready),调用方不应假设它被取消;在下一条命令完成前再次调用会被拒绝。
|
||||
- **时间来源(T08b)。** 实时相机 `capture_monotonic`,内置采集用 `CapturedFrame.host_mono_ns`
|
||||
和 `sequence`;Orisys 自己录像(文件内保存信号时间)`recorded_signal`;外部调用方提供可证明
|
||||
的容器 PTS 才 `video_pts`(`source_pts` 原值保留);内置 OpenCV 普通视频用
|
||||
`estimated_source`,`capture_time_ns = source_frame_id ÷ nominal_fps`,`CAP_PROP_POS_MSEC`
|
||||
只作为 `diagnostics` 的 `pos_msec`,不填 `source_pts`。没有时间的输入按帧号推算并标
|
||||
`estimated_source`。同一 PTS 的不同源帧是两帧;入口按源身份忽略重复/迟到输入并计数。
|
||||
- **诊断与 field(T08b)。** 结果行的 `diagnostics` 含覆盖/重复/迟到计数、处理延迟、
|
||||
`long_gap`、`source_dt_s`、`source_time_estimated` 和 `pos_msec`(有则);`source_dt_s` 取同路
|
||||
上一条实际处理帧的源时间差。`include=("field",)` 让 `StreamPipeline` 在 HHD 线程 `_build`
|
||||
中复制同帧 field;门面在标定模型需要 field 时自动启用。默认快照不含 field,已发布场不会被
|
||||
后续帧改写。
|
||||
|
||||
```python
|
||||
import numpy as np
|
||||
@@ -144,8 +156,8 @@ assert row.zero.phase in ("resetting", "collecting")
|
||||
## 后续
|
||||
|
||||
T06b 已交付标量/field/FN-only 三类标定与旧 ForceSession 的完整数值等价;T07 已交付固定
|
||||
设计帧率、显式切换事件与历史/直通合同;T08a 已交付 SensorStream 实时入口、所有权与组合归零。
|
||||
设计帧率、显式切换事件与历史/直通合同;T08a 已交付 SensorStream 实时入口、所有权与组合归零;
|
||||
T08b 已交付文件/debug 完整逐帧、有界背压、时间来源/PTS 标注、`include="field"` 与诊断。
|
||||
spatial_gain/接触映射模型不在 T06/T07 验收范围,需要时另立任务。
|
||||
T08b 补齐文件/debug 完整逐帧、有界背压、时间来源/PTS 标注与完整诊断后再进入 T09/T10(App 迁移)。
|
||||
另一项后续任务:删除旧 `ForceSession` 之前,先把零点、标定、读数引擎从旧类中抽出来,
|
||||
避免新会话依赖待删除的类型(T09 之前)。
|
||||
下一步进入 T09/T10(App 迁移);删除旧 `ForceSession` 前先把零点、标定、读数引擎从旧类中
|
||||
抽出来,避免新会话依赖待删除的类型。
|
||||
|
||||
Reference in New Issue
Block a user