第8章 接入真实传感器并发布v0.2
v0.1已经证明设备事件能够到达服务器,并能呈现给用户。本章把按键输入替换为选定的真实传感器。项目将建立“采样—时间戳—数据窗口—上传—存储”链路,并发布v0.2。
真实传感器
→ 连续采样
→ 数据质量检查
→ 固定数据窗口
→ 受控上传
→ 服务器存储
→ 数据回放验证
本章关注传感数据怎样成为可追溯的工程构建物。传感器电气连接、总线驱动和采样原理在《感知与异构计算系统原型》中深入学习;本章负责接口、模式、版本、变更评审、故障证据和数据合规。
早期嵌入式程序常在主循环中直接读取传感器。设备改变后,采样、处理和上传代码往往需要同时修改。稳定的采样接口把硬件差异限制在适配器中,但需要额外维护时间、状态和错误信息。
完成本章后,应当能够:
- 通过Git接收并评审硬件课程交付的传感器适配器;
- 记录传感器型号、配置、硬件版本和固件提交;
- 区分采样时刻、数据窗口和上传批次;
- 定义并验证传感数据模式;
- 检查采样率、时间单调性、样本数量和丢失情况;
- 将数据采集与网络发送解耦;
- 对断网、缓冲区溢出和重传建立明确行为;
- 使用已记录数据重复验证上层链路;
- 控制AI只在已核实的硬件接口范围内修改;
- 通过跨课程合并请求形成
v0.2版本。
8.1 从硬件课程接收传感器适配器
8.1.1 交付物必须形成可评审变更
硬件课程完成传感器连接与底层读取后,应把代码提交到任务分支。压缩包或聊天软件不能保留完整的版本关系。合并请求至少包含:
hardware/BOM.md
hardware/pin-map.md
hardware/sensor-configuration.md
firmware/include/sensor_source.h
firmware/src/<sensor_adapter>.cpp
firmware/tests/<sensor_adapter_test>.cpp
docs/verification/board-test-<日期>.md
其中:
- 物料清单记录实际器件与硬件修订;
- 引脚表记录总线、地址、中断和电源;
- 配置说明记录量程、输出数据率、滤波和先入先出(First In First Out,FIFO)缓冲设置;
- 适配器实现统一
SensorSource接口; - 板级记录保存设备身份、固件提交和实际观察。
没有数据手册来源、硬件版本和板级证据的驱动文件,不能直接作为主项目基线。
8.1.2 评审硬件交付接口
本课程评审以下内容:
[ ] 文件位于约定目录
[ ] 构建命令可从仓库根目录执行
[ ] 引脚与物料清单一致
[ ] 采样单位和轴顺序明确
[ ] 初始化失败可被上层识别
[ ] 读取函数不会隐式执行网络操作
[ ] 传感器配置能够进入运行日志
[ ] 没有凭据、个人路径和临时构建文件
[ ] 板级验证记录对应当前提交
寄存器设置是否正确由硬件课程负责解释,但合并请求必须能够追溯到数据手册位置和实际板卡测试。
8.2 定义稳定的采样接口
8.2.1 样本包含数值和身份
以三轴输入为例:
struct SensorSample {
std::uint32_t timestamp_us;
float x;
float y;
float z;
std::uint32_t sequence;
std::uint8_t status;
};
字段含义:
timestamp_us:设备单调时钟下的采样时刻;x/y/z:按配置文件约定单位换算后的数值;sequence:本次采集会话中的样本序号;status:数据有效、传感器错误或溢出等状态。
真实实现可以使用定点数以节省资源,但对外契约必须说明缩放比例。变量名中的_us用于明确时间单位。
8.2.2 初始化和读取失败要可见
接口示意:
enum class SensorResult {
kOk,
kNotReady,
kBusError,
kDataOverrun,
kInvalidConfiguration
};
class SensorSource {
public:
virtual SensorResult begin() = 0;
virtual SensorResult read(SensorSample& sample) = 0;
virtual const SensorConfiguration& configuration() const = 0;
virtual ~SensorSource() = default;
};
上层不能把所有失败都处理为“没有新数据”。总线错误、配置错误和采样过载对应不同修复措施,也会影响数据是否可用于训练和评价。
8.3 建立采样时间和数据窗口
8.3.1 采样率必须通过时间记录验证
若目标采样率为f,理想采样间隔为:
Δt = 1 / f
实际系统会有调度和总线抖动。验证时计算:
- 实际样本数;
- 相邻时间戳差值;
- 窗口实际持续时间;
- 最大与最小间隔;
- 丢失和重复序号;
- 传感器FIFO溢出次数。
采样率目标来自数据和模型需求,真实结果来自设备记录。不要只检查配置常量是否写成目标值。
8.3.2 窗口是数据与模型的共同接口
连续数据按固定规则组成窗口:
样本流:s0 s1 s2 s3 s4 s5 s6 s7,后续样本按序延续
窗口1: [s0 s1 s2 s3]
窗口2: [s2 s3 s4 s5]
窗口3: [s4 s5 s6 s7]
窗口长度决定一次判断使用多长时间信息,步长决定相邻窗口重叠程度。这两个参数由《人工智能数据科学》课程确定并交付,本仓库用配置和数据清单保存。
firmware/config/data_window.json示例:
{
"schema_version": "1.0",
"sample_rate_hz": 50,
"window_samples": 100,
"stride_samples": 50,
"channels": ["x", "y", "z"],
"units": "m/s^2"
}
示例数值必须替换为项目实际批准参数。修改任何一项都可能使已有数据和模型不兼容,必须建立议题并通知数据科学课程。
8.3.3 采集与发送使用不同任务
基本结构为:
传感器/FIFO
→ 采集任务
→ 有界环形缓冲区
→ 窗口任务
→ 有界上传队列
→ 通信任务
网络延迟不能阻塞固定节奏采样。两个队列都必须有容量上限和溢出策略:
- 丢弃最旧还是最新数据;
- 溢出计数怎样记录;
- 哪些窗口不再可用于训练;
- 用户是否需要看到采集降级状态。
AI可能生成无限增长的动态容器,在资源受限设备上会产生不可预测行为。评审时把缓冲容量与第6章RAM预算对照。
8.3.4 缓冲区隔离不同处理节奏
采集程序若直接等待网络发送,网络阻塞就会改变采样间隔。早期实现也常用轮询反复检查状态,造成处理器时间浪费。缓冲区把生产数据与消费数据分开,使二者按各自节奏运行。
缓冲区不能消除资源限制。容量过小会丢失数据,容量过大又会占用内存。采用有界队列后,系统还必须明确溢出、重试和删除规则。这些现象都可以通过计数器和时间记录验证。
8.4 定义传感数据模式
8.4.1 数据窗口要携带配置
contracts/examples/sensor-window.example.json:
{
"schema_version": "1.0",
"session_id": "session-example-001",
"window_id": "window-example-0001",
"device_id": "course-device-001",
"hardware_revision": "rev-a",
"firmware_version": "0.2.0-dev",
"sensor": {
"kind": "motion-3axis",
"sample_rate_hz": 50,
"range": "project-defined",
"units": "m/s^2",
"channels": ["x", "y", "z"]
},
"window": {
"first_sequence": 1000,
"sample_count": 2,
"start_timestamp_us": 20000000,
"samples": [
[0.02, -0.01, 9.80],
[0.03, -0.01, 9.79]
]
},
"quality": {
"dropped_samples": 0,
"sensor_errors": 0,
"buffer_overruns": 0
}
}
这是一条结构示例,不代表真实采样结果。真实上传必须使用项目批准的采样率、量程、单位和窗口长度。
8.4.2 模式检查结构,不判断信号是否正确
contracts/sensor-window.schema.json至少检查:
- 必填字段;
- 标识符和版本类型;
- 通道数组;
- 样本数组结构;
- 样本数为正;
- 质量计数不为负;
- 不接受未声明字段。
JSON模式可以发现字段缺失和类型错误。它不能判断传感器方向、量程和数据饱和情况。这些问题需要板级测试和数据分析。
执行:
python -m json.tool contracts/sensor-window.schema.json
python -m json.tool contracts/examples/sensor-window.example.json
python tools/project.py contract
8.5 设计采集会话和数据清单
8.5.1 一次采集要有明确身份
每次用于分析或训练的数据采集建立session_id,记录:
# 采集会话记录
- session_id:
- 采集目的:
- 操作人员编号:
- 日期与场地:
- 授权状态:
- 硬件修订:
- 传感器配置:
- 固件提交:
- 数据窗口配置:
- 开始/结束时间:
- 异常:
- 数据保存位置:
- 散列值:
“相同动作”在不同安装位置、量程或固件下可能产生不同数据。缺少这些元数据的数据难以用于可复现分析。
8.5.2 原始数据与公开仓库分离
数据文件可能体积较大,也可能包含个人或场景信息。项目仓库默认只保存:
- 数据模式;
- 脱敏的小型测试样例;
- 数据集清单;
- 授权和用途说明;
- 存储位置标识与文件散列;
- 生成数据的代码版本。
完整原始数据放在课程批准的受控存储中。采集会话清单示例如下:
artifact_id,session_id,path_or_uri,sha256,bytes,access,license_or_consent,created_at
data-0001,session-example-001,<受控位置>,<实际散列>,<实际大小>,restricted,<记录编号>,<时间>
路径和统一资源标识符(Uniform Resource Identifier,URI)不得包含个人令牌。Git提交不能自动扩大数据访问权限。
8.6 接入服务器采集接口
传感数据和产品事件承担不同作用:
| 对象 | 用途 | 默认上传策略 |
|---|---|---|
| EVENT-001 | 产品已经确认的事件 | 正常运行可上传 |
| SENSOR-WINDOW-001 | 原始或预处理传感窗口 | 只在授权采集模式上传 |
不要为了调试长期上传全部原始数据。设备应有明确的采集模式:
NORMAL:只发送产品事件和必要运行状态
COLLECT:在授权会话中发送传感窗口
DIAGNOSTIC:在现场调试时输出受限诊断信息
模式变化进入日志,并由配置控制。服务器采集接口必须验证:
- 设备和采集会话是否获得授权;
- 模式是否正确;
- 窗口是否重复;
- 数据大小是否在上限内;
- 保存结果和散列是否可追溯。
8.7 处理断网、缓存与重传
8.7.1 先定义失败状态
上传队列中的窗口至少区分:
READY
→ SENDING
→ ACKNOWLEDGED
→ RETRYABLE_FAILED
→ PERMANENT_FAILED
→ DROPPED
网络超时通常可以重试;模式错误和未授权请求不能通过重复发送解决。AI生成“所有失败都无限重试”的实现会占满队列和网络。
8.7.2 使用有界缓存
缓存设计要回答:
- 最多保存多少窗口或事件;
- RAM和持久化存储分别保存什么;
- 设备重启后是否保留;
- 队列满时丢弃策略;
- 同一
window_id重传是否幂等; - 成功确认后何时删除;
- 缓存中是否包含敏感原始数据。
本章至少实现有界队列、重试次数和丢弃计数。是否使用非易失存储由产品需求、电源风险和硬件条件决定,并用技术决策记录保存。
8.7.3 退避避免故障放大
重试间隔应逐步增加并设置上限:
第1次失败 → 短等待
第2次失败 → 延长等待
后续失败 → 逐步延长等待
达到上限 → 保持受控频率或停止并报告
具体参数需要结合网络、功耗和数据时效确定。重试不得阻塞采集任务。
8.8 建立数据回放适配器
8.8.1 回放用于重复验证
真实传感器输入会随动作和环境变化。将一次合格采集保存为脱敏测试数据后,可通过ReplaySensorSource按时间顺序读入相同样本:
受控测试数据
→ ReplaySensorSource
→ 数据窗口
→ 数据模式编码
→ 上传与服务器
回放使用与真实传感器相同的SensorSource和窗口接口,因此可以重复验证上层代码。它不能证明传感器驱动、电气连接和现场信号质量正确,真实设备测试仍然必须保留。
8.8.2 固定回放数据的身份
回放记录应包含:
- 数据文件散列;
- 样本数量和通道;
- 原始采集会话;
- 允许用途;
- 预期窗口数;
- 预期丢失和错误计数;
- 适用模式版本。
测试不能只写“使用sample.csv”。文件内容变化后,散列值能够表明测试基线已经改变。
8.9 约束AI处理硬件和数据任务
8.9.1 不让AI猜测硬件接口
实施请求:
任务:把已合并的SensorSource适配器接入数据窗口。
必须读取:
- hardware/sensor-configuration.md
- hardware/pin-map.md
- firmware/include/sensor_source.h
- firmware/config/data_window.json
- contracts/sensor-window.schema.json
只允许修改:
- firmware/include/data_window.h
- firmware/src/data_window.cpp
- firmware/src/main.cpp
- 对应测试
要求:
1. 使用文档中已经核实的单位、通道顺序和采样率;
2. 缓冲区容量必须是编译期可见的有界值;
3. 记录丢失样本、传感器错误和缓冲溢出;
4. 不修改驱动寄存器、物料清单、数据模式和模型;
5. 不把网络发送放进采样函数;
6. 不烧录、不提交、不推送。
若AI给出数据手册中没有的寄存器或引脚,拒绝该变化并回到硬件交付文件核对。
8.9.2 检查AI是否改变数据含义
差异审查重点:
[ ] x/y/z顺序没有改变
[ ] 单位换算只有一个权威位置
[ ] 时间单位没有混用
[ ] 样本序号连续规则明确
[ ] 窗口长度和步长来自版本化配置
[ ] 错误样本没有被静默当作正常值
[ ] 缓冲上限与RAM预算一致
[ ] 采集模式和上传权限没有被绕过
代码能够运行不代表数据含义正确。数据含义变化必须同步更新JSON模式、数据清单和已有模型的适用范围。
8.10 组织跨课程合并
建议合并顺序:
硬件适配器MR
→ 数据JSON模式合并请求
→ 窗口与缓冲MR
→ 服务器采集MR
→ 数据回放MR
→ v0.2集成MR
每个合并请求更新到最新main后再验证。硬件适配器和数据窗口同时修改main.cpp时,先合并较底层接口,另一分支再同步并根据已合并接口调整。
解决冲突时核对:
- 两个议题的目标;
- 当前主分支的接口;
- 时间和单位约束;
- 缓冲与通信调用顺序;
- 受影响的板级和回放测试。
不要让AI只根据冲突标记选择一侧;它必须读取两个议题和当前规格,人工再决定最终语义。
8.11 验证真实数据链路
8.11.1 设备侧检查
在指定硬件上执行:
python tools/project.py doctor
python tools/project.py build-firmware
python tools/project.py flash
python tools/project.py monitor
完成:
| 编号 | 条件 | 检查 |
|---|---|---|
| S1 | 设备静止 | 传感器持续输出,无总线错误 |
| S2 | 沿一个方向缓慢改变姿态 | 对应轴变化,单位和方向符合文档 |
| S3 | 连续运行指定时间 | 序号、时间戳、溢出计数可解释 |
| S4 | 进入COLLECT模式 | 产生正确数量的数据窗口 |
| S5 | 退出COLLECT模式 | 停止上传原始窗口 |
不要求学生在本章评价动作分类准确率。
8.11.2 端云与故障检查
| 编号 | 条件 | 检查 |
|---|---|---|
| D1 | 正常网络 | 窗口被接受并写入采集会话清单 |
| D2 | 重复window_id | 服务器不重复保存 |
| D3 | 暂停服务器 | 采样继续,上传进入可重试状态 |
| D4 | 恢复服务器 | 在策略允许范围内重传 |
| D5 | 队列达到上限 | 按已记录策略丢弃并增加计数 |
| D6 | 无授权采集会话 | 原始数据被拒绝 |
记录实际窗口标识、设备提交、服务器提交、模式版本、硬件修订和错误计数。
8.11.3 回放检查
python tools/project.py replay-data --manifest <测试清单>
核对预期窗口数、散列和服务器记录。回放通过与实机通过分别记录,不能合并为一句“数据测试通过”。
8.12 发布v0.2
更新:
README.md的真实传感器运行步骤;- 物料清单和引脚表;
- SENSOR-WINDOW-001字段说明;
- 采集授权与数据保存说明;
- 预算中的实际闪存、RAM或采样结果;
- 模型接口中确认的窗口参数;
CHANGELOG.md和已知限制;- 需求追踪表。
发布检查:
[ ] 硬件交付MR已评审
[ ] 真实传感器数据可读取
[ ] 时间戳、序号、单位和窗口可解释
[ ] 数据模式与服务器一致
[ ] 采集模式受控
[ ] 断网和队列上限行为已验证
[ ] 数据回放可重复
[ ] 原始数据未误入公开仓库
[ ] 硬件、固件、数据和数据模式版本可追溯
创建版本:
git switch main
git pull --ff-only
git tag -a v0.2.0 -m "v0.2.0 sensor data pipeline"
git push origin v0.2.0
发布说明如实列出尚未接入正式模型、当前采样场景和数据使用限制。
8.13 本章小结
本章把真实传感器接入了v0.1端云系统。系统形成可追溯的数据窗口、受控上传、有界缓存和数据回放链路。硬件课程提供经过板级验证的适配器。数据科学课程确认窗口和数据含义。本课程用Git契约、数据清单、合并请求和版本标签连接各项成果。
第9章将接收正式数据集和基线模型。重点是验证模型包是否完整,以及它是否符合v0.2数据契约。项目还要建立模型、数据、代码和评价结果之间的版本关系。
8.14 综合实践
- 为本组真实传感器定义样本、时间和错误状态。形成接口说明,并由另一组只依据说明编写一个替代数据源。
- 比较两种采样配置。参数应注明数据手册来源、实际测量条件,以及配置变化对数据和模型的影响。
- 根据内存和网络约束设计数据窗口、缓存上限和溢出策略。用受控实验说明为什么选择该方案。
- 选择一段经过授权的真实数据,建立回放适配器。比较回放与实机结果,并列出回放不能证明的硬件性质。
- 通过合并请求接收硬件适配器和数据模式,形成
v0.2.0的物料清单、固件、数据清单和验证记录。