Files
ESP32S3_vt/DEVELOPMENT.md
T
sloray cc6b82d7ec
构建客户端 / 检查(类型 + 单元测试) (push) Canceled after 0s
构建客户端 / Linux x64(deb + AppImage) (push) Canceled after 0s
构建客户端 / Windows x64(NSIS 安装包) (push) Canceled after 0s
构建客户端 / macOS 通用(.app + .dmg) (push) Canceled after 0s
feat: 链路控制、延迟校准与长测工具,补全文档
- 固件:新增 link_control / stream_control,覆盖运行中 Wi-Fi 断线恢复、取帧节拍、
  温控与质量/帧率减载;UDP 发送增加有界重试预算与帧期限,PSRAM JPEG 直发减少拷贝。
- 协议:重组器严格校验分片布局,统计未收齐、超时与完全缺失的帧,处理帧号回绕与设备重启。
- 桌面端:视频改 latest-value 通道并前置帧时间标记,拒绝积压和慢解码的过期画面;新增
  延迟标记页与 --latency-report 校准,完善录制、服务端与退出流程。
- 文档与脚本:新增硬件、开发、延迟、功耗、实时性、稳定性文档和改动记录,重写根 README;
  新增构建、性能、延迟与串口长测脚本,以及长测客户端脚本。
- 构建:固定 esp-1.97.0.0 工具链,分区表随构建复制,更新 sdkconfig 与忽略规则。
2026-10-19 00:19:07 +08:00

130 lines
8.9 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.
# 开发与验证
编译入口见 [README.md](README.md),硬件限制见 [HARDWARE.md](HARDWARE.md),历史变化见 [CHANGELOG.md](CHANGELOG.md)。
## 镜像导出
以下使用 espflash 4.4.0 与固定的 `esp-1.97.0.0` 工具链。完成构建后,使用 `esp-idf-sys` 复制到 `release/` 的 Bootloader。Windows PowerShell:
```powershell
$targetDir = if ($env:CARGO_TARGET_DIR) { $env:CARGO_TARGET_DIR } else { 'target' }
New-Item -ItemType Directory -Force target/firmware | Out-Null
espflash save-image --chip esp32s3 --flash-size 16mb --flash-mode dio --flash-freq 40mhz --partition-table partitions.csv --bootloader "$targetDir/xtensa-esp32s3-espidf/release/bootloader.bin" --merge --skip-padding "$targetDir/xtensa-esp32s3-espidf/release/esp32" target/firmware/esp32s3-merged.bin
```
Linux / macOS(Bash):
```bash
target_dir="${CARGO_TARGET_DIR:-target}"
mkdir -p target/firmware
espflash save-image --chip esp32s3 --flash-size 16mb --flash-mode dio --flash-freq 40mhz --partition-table partitions.csv --bootloader "$target_dir/xtensa-esp32s3-espidf/release/bootloader.bin" --merge --skip-padding "$target_dir/xtensa-esp32s3-espidf/release/esp32" target/firmware/esp32s3-merged.bin
```
合并镜像包含 Bootloader、分区表、App 和必要地址填充,`--skip-padding` 避免将文件填满 16 MB。镜像从 `0x0` 写入:
```text
espflash write-bin --chip esp32s3 --port COM5 0x0 target/firmware/esp32s3-merged.bin
```
去掉导出命令的 `--merge --skip-padding` 可生成独立 App,它需写入 `partitions.csv` 声明的 Factory 偏移,并与板上 Bootloader 和分区表匹配。坏板重编可能改变该偏移;新板优先使用 ELF 或合并镜像。
## 主机测试
协议层不依赖 ESP-IDF。进入 `protocol/`,以 Windows MSVC 为例:
```powershell
cargo +stable test --locked --target x86_64-pc-windows-msvc --config 'unstable.build-std=[]'
```
Linux 常见 host 为 `x86_64-unknown-linux-gnu`,Apple Silicon 为 `aarch64-apple-darwin`,可用 `rustc +stable -vV` 查看。`--target` 覆盖仓库里的其他平台配置,空 `build-std` 避免主机测试继承嵌入式标准库重编译。
装甲规则从 `armor/` 执行 `cargo +stable test -p armor-protocol`,桌面核心从 `desktop/core/` 执行 `cargo +stable test`,同样按需指定宿主 `--target` 和空 `build-std`。
限帧、温度回差和 JPEG 压缩回压策略可以独立在主机运行,无需连接板卡。Windows 在根目录执行:
```powershell
New-Item -ItemType Directory -Force target | Out-Null
rustc +stable --edition 2021 --test src/stream_control.rs -o target/stream-control-tests.exe
.\target\stream-control-tests.exe
```
实际持续运行的串口与客户端通道记录方法见 [STABILITY.md](STABILITY.md)。主机策略测试不等于已经触发板上过温减载。
## 清理构建缓存
改动 C 组件或 `sdkconfig.defaults` 后,增量构建若没有反映变化,可从根目录执行:
```text
cargo clean --release --target xtensa-esp32s3-espidf -p esp-idf-sys
```
随后用构建脚本重编,确认 CMake / Ninja 重新编译相应组件。仍有缓存问题时使用 `cargo clean` 完整清理。不要删除 `components/esp32-camera/target/`。
构建脚本设置 `CARGO_WORKSPACE_DIR`,并通过 `ESP_IDF_GLOB_BASE` / `ESP_IDF_GLOB_PARTITIONS` 将分区表复制到生成的 CMake 工程,因此支持外部 `CARGO_TARGET_DIR` 和不同 Cargo 输出层级。Windows 使用短目录,如 `C:\s3vt`;设置 `ESP_IDF_PATH_ISSUES=ignore` 或 `subst` 不能消除底层路径限制。
## 发现与协议
- 图传板每 2 秒广播 Hello;客户端每 800 ms 广播 Subscribe,并向已发现的在线板卡 IP 单播续订,固定绑定 UDP 5600。
- 全局广播可能被 VPN / 虚拟网卡路由到其它接口。收到 Hello 后按设备 IP 定向续订,可以使用通往实际局域网的路由;队伍端仅向绑定的板卡增加单播续订。
- 固件使用订阅包的源地址作为发送目标,6 秒不续订即移除,最多保留 4 个对端。
- 新客户端为每个接收器生成随机会话 ID;同一源 IP 下,同一会话换端口会更新推流目标。旧版公共固定 ID `0x0C110001` 保持按地址区分,避免合并同机旧客户端。
- MAC 派生的 `sender_id` 不随 DHCP 地址变化,适合客户端持久绑定。
- Hello 和视频都是设备在线证据;客户端列表清理与固件订阅使用不同超时。
- 数据报为 27 字节头 + 最多 1400 字节载荷,采用小端编码;字段及拒收条件见 [protocol/README.md](protocol/README.md)。
缺失分片不重传,不完整帧整体丢弃。JPEG 由传感器编码,桌面后端转交原始 JPEG,由 WebView 解码,避免接收线程进行耗时编解码。
## 性能测量
本次在保留 TUN 环境中的对照测试、最终参数与验证边界见 [PERFORMANCE.md](PERFORMANCE.md)。
下表为原 README 保存的**历史单次测试数据**,不是本次构建实测或性能保证。配置为 OV5640、VGA;原始日志、时长和完整网络条件未随这些数字保存。
| 指标 | 早期 HTTP MJPEG / TCP | 后续 UDP |
| --- | --- | --- |
| 传感器产出 | 约 22 fps | 约 22 fps |
| 客户端收到 | 约 1.9 fps | 约 22 fps |
| 吞吐 | 约 0.28 Mbit/s | 约 1.6 Mbit/s |
| 丢帧 | 未记录 | 当次报告 0% |
这些数据不足以单独证明性能差异的根因或无线链路容量。复测时记录板型、固件版本、分辨率、画质、信号、订阅者数量、时长和丢帧数。
FPS 应按**同一时间窗口的帧数增量 / 窗口秒数**计算。累计帧数与窗口时长混用会产生假数值,UI 轮询间隔也不代表相机出帧间隔。探测脚本的重组状态需按设备及帧隔离,避免测量错误。
可重复测量:`python scripts/perf_probe.py <板卡IP> --seconds 30 --json perf.json`。默认使用独立的临时端口和 1 MiB 接收缓冲,每 800 ms 定向续订。脚本只依赖 Python 标准库;`--save-frame frame.jpg` 可保存首张完整 JPEG。公平比较应保持订阅者数量、分辨率、画质与网络条件一致。
`frame_loss_percent` 包含不完整、超时及帧号缺口,不包含开始订阅前或测量结束后的帧。`assembly_p95_ms` 是首末分片到达的时间差,`inter_frame_p95_ms` 是完整帧到达间隔,均使用高精度计时;没有发送时间戳及两端时钟同步,不能称为端到端延迟。
固件日志将采集尝试与完整提交分开计数。`submitted` 只表示全部分片交给了网络,不代表无线确认或客户端显示;`send failures` 可能包含随后重试成功的调用,应结合 `peer frames dropped` 和接收端指标判断。
画面链路测量使用裁判端 `--latency-report <新文件>`,让相机拍摄校准页左侧标记,固定相机和窗口位置。标记经正常采集、网络与绘制路径回到客户端,源与回显共用主机单调时钟;不需要 ESP 时钟同步。测试期间只保留一个原生客户端,额外的本地 WebSocket 观察器不增加 UDP 订阅。方法、刷新估计边界及最新结果见 [LATENCY.md](LATENCY.md)。
按记录文件的半开行号区间复算延迟:
```text
python scripts/analyze_latency.py --report latency-run.ndjson --range fb3-range.json --range fb2-range.json --out latency-result.json
```
区间文件至少包含 `start_row`、`end_row` 和 `fb_count`,行号从 0 开始;报告关闭后再分析,避免读取写入中的半行。分析器拒绝重复源标记、无效时间边界与空样本,输出 P50/P95/P99、rAF 区间宽度和解码完成到绘制的耗时。JPEG 接收间隔仍是独立指标。
恢复策略是独立纯 Rust 模块,可在主机运行:
```text
rustc +stable --test src/link_control.rs -o link-control-tests
./link-control-tests
```
Windows 为输出文件加 `.exe`。实机故障注入仅在诊断构建设置 `WIFI_TEST_DROP_AFTER_S`:主循环运行相应秒数后断开一次并保持 5 秒,正式构建保持 `0`。重连后需观察重新取址、socket 重建和订阅恢复,不能仅凭 connect 调用成功判定恢复视频。
## 板上验证
阻塞与缓冲交接测试:`python scripts/test-latest-frame.py` 使用主机 clang/gcc 编译真实 `cam_take_latest` 函数及队列替身,验证持续生产下有限取帧、旧缓冲仅归还一次、无新帧保留有效旧帧,以及首次超时不泄漏。`rustc +stable --test src/stream_control.rs` 验证发送期限、共享重试预算和零重试模式;`desktop/` 的 `pnpm test:stream` 验证 WebView 积压、解码阻塞及过期帧清理。实机验证范围另见 [REALTIME.md](REALTIME.md)。
1. 相机识别、PSRAM 初始化及稳定采集。
2. Wi-Fi 完成关联和 DHCP,得到有效 IP。
3. 客户端启动后订阅者增加并持续显示画面。
4. `python scripts/udp_probe.py 10` 独立核对协议及帧统计。
5. 分别验证多端订阅、断网重连、不同分辨率与持续运行。
编译、镜像生成与主机测试通过均不等于烧录、板上运行或硬件稳定性已经验证。