24 Commits
Author SHA1 Message Date
Horldsence d975309a81 holy fuking shit 2026-09-12 01:56:34 +08:00
Horldsence 36f354736c 1919810 2026-09-11 23:07:42 +08:00
Horldsence 5c96699b70 1 2026-09-11 21:07:21 +08:00
Horldsence 1725cd0891 114514 2026-09-11 21:00:39 +08:00
sloray 2a3ead5d3d Merge branch 'q3-bayes' of git.simba-robotics.com:HelixCopex/mcm-26-b into q3-bayes 2026-09-11 17:56:08 +08:00
sloray 4d224e586a 强化学习第二版 2026-09-11 17:56:03 +08:00
Horldsence 7026277a8d Merge branch 'q3-bayes' of https://git.simba-robotics.com/HelixCopex/mcm-26-b into q3-bayes 2026-09-11 17:25:45 +08:00
Horldsence b15db59ac3 14 2026-09-11 17:24:29 +08:00
sloray 30df22e8b2 13 2026-09-11 17:24:07 +08:00
sloray 4699b0d45f 强化学习第一版 2026-09-11 16:56:39 +08:00
Horldsence 2bc4c7ed82 Merge branch 'q3-bayes' of https://git.simba-robotics.com/HelixCopex/mcm-26-b into q3-bayes 2026-09-11 11:37:22 +08:00
Horldsence a18a139a1d 6 2026-09-11 11:36:08 +08:00
Horldsence 2d2a9a4e6a feat: 5 2026-09-11 11:35:47 +08:00
sloray fd48064000 第四次运行 2026-09-11 11:28:36 +08:00
sloray 7dedcd0629 4000s上限优化 2026-09-11 11:16:23 +08:00
sloray 9cf94281a9 论文/3batch 2026-09-11 09:52:05 +08:00
Horldsence f3dfa9c2a1 feat: 修复大粪扫描频率 2026-09-11 09:50:56 +08:00
Horldsence bd479b6d9d Merge remote-tracking branch 'origin/gpt' into q3-bayes 2026-09-11 09:12:24 +08:00
Horldsence 9c9ab451e5 feat(q3): 消融基准(14场景-19.5%)+Optuna调参脚本+文档;run 默认策略切 bayes 2026-09-11 09:11:21 +08:00
Horldsence e0665c73fb feat(q3): 贝叶斯网格粒子滤波闭环(PF负观测+信息增益选点+2-opt调度)
- particles.py: 网格幸存集滤波,Voronoi 裕量保证真源保留(缺席证明/网格清除证书/跳测规则)
- planner/bayes_loop.py: 统一任务循环(站点/定位/清除滚动 2-opt 排序)、期望后验半径选点
- planner/seven.py: 七点覆盖策略族提升为库代码
- cli: mcm26b run --strategy {grid,seven,bayes}
- BayesConfig 参数组;tests +24 项(真源保留/证书/端到端),129 全绿
2026-09-11 09:02:28 +08:00
sloray 52c69b1276 模拟器细节特征对齐 2026-09-11 08:29:56 +08:00
Horldsence 7930eb65bf feat: 3 2026-09-11 03:33:36 +08:00
Horldsence fc70d6edfd feat: 3 2026-09-11 03:32:05 +08:00
sloray 78d52ff81c 第三问的垃圾算法 2026-09-11 03:26:20 +08:00
211 changed files with 54386 additions and 2636 deletions
Vendored
BIN
View File
Binary file not shown.
+3
View File
@@ -24,3 +24,6 @@ docs/paper/*.synctex.gz
# pytest # pytest
.pytest_cache/ .pytest_cache/
# 1
output/
@@ -0,0 +1,87 @@
# 分层强化学习(HRL)实施计划 — 2026 高教社杯 B 题第三、四问
## 0. 现状与复用决策
仓库已有生产级基建,**全部复用、不重写**:
| 现有组件 | 位置 | 用途 |
|---|---|---|
| `SimulatorClient`(HTTP+JSON、幂等重试、截止时钟、JSONL 日志) | `src/mcm26b/protocol/` | 模拟器对接(prompt 第 1 步已完成) |
| 本地评测机(内存回环,一局秒级) | `referee/` + `harness.LocalSimTransport` | 本地简化环境内核 |
| 网格幸存集滤波(真源保留定理已证明) | `src/mcm26b/particles.py` | 每频道信念后验 |
| 凸候选集 + 五状态机 | `src/mcm26b/belief/` | 频道状态观测 |
| 信息增益选点、TSP 2-opt、六边形覆盖、清除证书、后备清除 | `src/mcm26b/planner/` | 候选生成器 + 证书审批兜底 + 启发式基线 |
| 单步 MaskablePPO 全套(特征/掩码/BC/训练/审计) | `src/mcm26b/rl/` + `scripts/` | 代码模式模板 + 端到端对照基线 |
**改进方向**(来自 v2 审计文档已知局限):单步 RL 只做选点、学不到全场调度;Q4 后备清除偏多、尾部退化(13778s→11797s,距 4000s 目标远)。HRL 高层正是补**跨频道全场调度**决策。
**已确认的三个决策**:① 代码集成到 `src/mcm26b/hrl/`(README 给出与 prompt 目录树的映射表);② 低层为字面 8 方向 PPO + 直线控制器对照(诚实预期:部署时只提交终点,低层对虚拟时间增益≈0,价值在架构完整性与消融验证);③ 本次跑通全流程 + 短程训练(1–2h),完整课程训练交付脚本由用户自行启动。
## 1. 新建文件
```
src/mcm26b/hrl/
├── __init__.py
├── constants.py # HRL 接口常量:N_CANDIDATES=96、观测维度,feature_version=3
├── candidates.py # 高层候选生成:待清除源/信息增益探索点/覆盖验证点/Q4 扇区探测点 + 动作掩码
├── features.py # 高层观测(20 频道×14 统计 + 全局标量 + 96×6 候选特征 ≈864 维)、低层观测
├── policy.py # HighLevelPolicy/LowLevelPolicy 加载器(沿用 PointSelectionPolicy 元数据校验模式)
├── low_level_env.py # 低层训练环境:随机起点→子目标,Discrete(8),逐步计费,奖励=−Δd−转向+到达
├── hierarchical_env.py # 高层整场环境:10–16 源,动作=子目标槽位,内嵌低层执行器,域随机化+课程参数
└── executor.py # 子目标执行器:低层 PPO 步进(本地计费)或直线控制器(可切换),终点提交模拟器
scripts/
├── train_hrl_low.py # 低层:BC 克隆直线控制器热启动 → MaskablePPO 微调;直线对照评估
├── train_hrl_high.py # 高层:4 阶段课程(--stage --resume --timesteps),SubprocVecEnv 并行
├── pretrain_hrl_expert.py # 可选:从 BayesLoop 调度器克隆 (obs, 子目标) 对热启动高层
├── tune_hrl.py # Optuna TPE(prompt §10 搜索空间,预算 15–20 trials,初值复用 bayes 已调参数)
├── benchmark_hrl.py # 配对消融:HRL vs bayes/directional/单步 PPO,dev(700-702)/val(91000-91005)/stress 固定场景
└── ablate_hrl.py # 四项消融:无信息增益奖励/无动作掩码/无课程/无粒子滤波
src/mcm26b/planner/hrl_loop.py # HRLLoop 运行时闭环(Q3/Q4),保留证书审批与异常留痕
src/mcm26b/cli.py # 新增 --strategy hrl / --q4-strategy hrl / --hrl-model
tests/test_hrl.py # 特征布局、掩码、环境形状、奖励恒等式、训练-部署观测一致性
docs/hrl.md # 设计文档 + prompt 目录树映射表
docs/paper/ 增补 # 算法流程图、伪代码、超参表、训练曲线
```
## 2. 核心设计
**高层动作空间** Discrete(96) + 掩码:槽位按固定顺序填充(已定位待清除源 ≤16 → PF 驱动探索点 → 覆盖网格未验证点 → Q4 扇区探测点),空槽/已清除频道/接收盘外候选掩码 −∞。
**高层观测**(≈864 维,参照 v2 的 401 维布局模式):20 频道 × 14 特征(六状态 one-hot、MEC μx/μy、σx/σy/ρ、log 幸存格数、期望信息增益、距离、频道号)+ 全局 8 维(位置、时间比例、已清除数、最近检测结果编码/示向度/距离)+ 96×6 候选特征。
**step 语义**:高层选子目标 → executor 步进(低层 8 方向,每步 ≤100m,本地按 Δd/5 计费;部署时仅终点调模拟器)→ 按子目标类型执行 /measure 或 /clear → belief+PF 更新 → 奖励。
**奖励**:`r = −Δ虚拟耗时/100 + 100·清除 + 5·新检测 + w_info·ΔH − 冗余惩罚`,主口径与目标函数(虚拟时间)一致;w_info 可消融。
**课程 4 阶段**(prompt §7.1):1 源/5 频道/无噪 → 3 源/10 频道/±0.5° → 8 源/16 频道/±1° → 10–16 源/20 频道/±1°;每阶段迁移上阶段权重,Q4 从 Q3 权重微调(定向比例递增),域随机化贯穿(源位置/频道/r_eff/朝向/噪声种子)。
**运行时闭环 HRLLoop**:初始覆盖相位(快速发现信号)→ 主循环高层决策(证书达标自动 /clear,与 BayesLoop 同语义)→ 全清即 /exit。不触碰候选集更新与清除判定的守恒性(真源保留定理依赖),异常留痕兜底保留。
## 3. 实施顺序(对应 prompt §12)
1. `constants.py` + `candidates.py` + `features.py`(候选/掩码/观测)+ 单元测试
2. `low_level_env.py` + `train_hrl_low.py`:低层短程训练,验证收敛到近直线(报告与直线最优的差距)
3. `hierarchical_env.py` + `executor.py` + `hierarchical_env` 单元测试(奖励恒等式:回合回报 = f(虚拟时间、清除数))
4. `train_hrl_high.py` + 课程 + 域随机化 + SubprocVecEnv;`pretrain_hrl_expert.py` BC 热启动
5. 短程课程训练(每阶段 2–5 万步,总计 1–2h),产出训练曲线
6. `hrl_loop.py` + CLI 接入,`--local-judge` 本地演练验证全清正确性
7. `benchmark_hrl.py` 配对对比(沿用 benchmark_rl.py 的 all_valid 有效口径)+ `ablate_hrl.py` 四项消融
8. `tune_hrl.py` 受限 Optuna;产出最佳超参表
9. 本地 referee 演练 ×3/题,表 1 格式结果(案例编码/清除数/平均定位清除时间/程序运行时间),日志 JSONL 原样归档 `output/hrl/`
10. `docs/hrl.md`、README 更新、论文素材(流程图/伪代码/超参表/对比分析,含"为什么分层 PPO、为什么非端到端、为什么非多智能体"论证)
## 4. 验收标准
- 全部 pytest 通过(现有 243 项 + 新增 HRL 测试)
- `benchmark_hrl.py` 输出 all_valid=true(真值全清、缺席无误判、零异常、正常 exit、计时复算一致)
- 短程训练下 Q4 HRL 相对 directional 有可测改进方向(对标单步 RL 的 −14.38%);Q3 如实报告与 bayes 的差距(v2 审计口径:Q3 RL 无稳定增益)
- 所有实验固定种子、命令可复现;消融表含均值±方差(≥5 种子)
- 完整训练只需用户执行 `scripts/train_hrl_high.py --full` 级别的命令
## 5. 风险与对策
- **高层整场回合过长(100–200 决策步)不收敛** → γ=1 + 密集 Δcost 奖励 + BC 从 bayes 调度器热启动
- **低层增益≈0** → 如实消融报告(直线对照差异≈0),论文定位为架构必要性验证而非性能来源
- **Q3 可能无增益** → 与 v2 口径一致如实报告,主打 Q4 调度收益;两种策略可共存(HRL 不改默认策略,仅在胜出后由用户决定切换默认)
@@ -0,0 +1,80 @@
# 目标
在新分支 **`q3-bayes`**(基于当前 `gpt`)实现第三问新算法:**网格化贝叶斯粒子滤波(含负观测更新)+ 信息增益选点 + 开放路径 2-opt 任务排序 + 七点覆盖兜底 + Optuna 离线调参**。评分口径 = 虚拟总时间(A2 基线:官方演练 12 源 5232s、109 次检测);硬性要求全清率 100%、缺席证明正确、零异常。
# 关键设计
## 1. 粒子滤波 `src/mcm26b/particles.py`(新建)
与用户方案的差异点:本项目要求清除证书/缺席证明**严格**(A2 靠保守几何证书做到 100% 清除率),软核概率 PF 有删掉真源的风险。因此采用**网格幸存集滤波**(= 硬似然 PF + Voronoi 裕量,与用户"|θ̂−θ|>1° 权重置零"的设定一致但更严格):
- 每频道一组 Ω 上间距 h≈30m 的网格粒子(约 1.1 万格,alive 用整数索引集合;空间哈希加速邻域查询)
- 保守 kill 判定(margin = h·√2/2,保证真源所在格必活):
- `no_signal`@s:kill dist(g,s) ≤ 1000 − margin(题面 r_eff ≥ 1000 ⟹ 可严格排除该盘)
- `direction`@s 读数 θ:kill 角向偏差超出 δ_eff=1.005°(含余量)或 dist > 1500 + margin 的格
- `near`@s:kill dist(g,s) > 5 + margin
- 证书全部由此推导:清除证书 = MEC(alive).R + margin ≤ 20 − clear_margin;**缺席证明 = alive 为空集**(无需额外像素覆盖检查);跳测规则 = min dist(alive, s) > 1500 + margin ⟹ 必 no_signal,跳过省 6s/次
- 信息增益:对候选点用子采样(≤500 粒子)× K 个代表假设做 MC 期望熵减估计;复用 `smallest_enclosing_circle`(Welzl) 求 MEC
## 2. 新策略 `BayesLoop(OmnidirectionalLoop)`(`src/mcm26b/planner/bayes_loop.py`)
复用基类 run() 骨架、异常收尾、summary、anomaly、预算机制,重写阶段逻辑:
- **发现阶段**:原点起扫(频道排序省换频)→ 站点 = 七点环 ∪ PF 高价值点,按"期望发现数 − λ·移动时间"贪心选站;每站批量扫频时跳过"PF 判定必 no_signal"的 (站,频道) 对与已证缺席频道;每站扫完即服务已发现频道(SevenBatch 风格)
- **定位服务**:选点 = 保证接收盘内候选环采样 + 信息增益评分(ΔH − λ·移动 − μ·检测),有证书即清除;步数上限沿用 `max_localize_steps`
- **清除调度**:待服务任务(定位+清除 ≤20 节点)开放路径最近邻 + 2-opt,纯标准库,每个决策点滚动重排
- **收尾**:全部 CLEARED/ABSENT → all_resolved;清满 16 提前停;persistent_search 沿用现有配置语义(实现时对照 `run_q3_seven_practice.py` 保持官方演练停止行为一致)
## 3. 配置 `config.py`
新增 `BayesConfig` frozen dataclass([工程]):particle_spacing_m、λ/μ 权重、MC 代表数/子采样、skip_margin、站点环半径、2-opt 开关/迭代数等;挂到 `ProblemConfig.bayes`。默认值不改变现有策略行为。
## 4. 七点覆盖提升为库代码 `src/mcm26b/planner/seven.py`
把 `scripts/benchmark_q3_search.py` 的 `seven_points()/SevenImmediate/SevenBatch` 移入 src(旧脚本改 import,消融结果可复现),供 CLI 与新基准复用。
## 5. CLI `cli.py`
`mcm26b run --strategy {grid,seven,bayes}`,默认 grid;**若基准通过则默认改为 bayes**(用户已确认)。
## 6. 基准 `scripts/benchmark_q3_bayes.py`
配对消融 {seven_batch, bayes} ×(seeds 0..5 × 10/16 源 + 边界源 + 0 源纯缺席场景),复用现有 run_case/LocalSimulator 模式;输出 `output/q3/bayes-ablation/`(benchmark.json + report.md):全清率、correct_absence、异常、accounted/total 虚拟时间、配对降幅、实机 compute_wall_s。
## 7. Optuna 调参 `scripts/tune_q3_bayes.py`
- `uv add --group dev optuna`(仅调参脚本用,生产零新增依赖)
- TPE,目标 = 场景子集平均虚拟时间 + 10⁶×失败惩罚;场景与 error_seed 多样化防过拟合
- 搜索空间:h、λ/μ、skip margin、候选环、2-opt 开关等
- optuna 不可用时自动退化为随机搜索;最优参数写 JSON,复跑基准确认后合入 config.py 默认值
## 8. 测试
- `tests/test_particles.py`:真源保留性(随机场景 × LocalSimulator 误差场,任意更新序列后真源邻域格必活);证书正确性(alive=∅ ⟺ 真缺席;MEC 证书清除必成功);跳测规则永不漏检
- `tests/test_bayes_loop_q3.py`:端到端 LocalSimulator(10/16/0 源、边界源、预算耗尽、故障注入):全清 + 缺席正确 + 无异常 + 正常退出
- 现有 105 项测试保持绿
## 9. 文档
`docs/algo-3-bayes.md`(Voronoi 裕量严格性论证、信息增益与调度设计、与 A2 对比数据);README 策略表加行;`docs/algo-3.md` 加交叉引用。
# 执行顺序
1. `git checkout -b q3-bayes`
2. BayesConfig → particles.py(含单测)→ seven.py 提升
3. bayes_loop.py + CLI 接入 → pytest 全绿
4. 跑配对消融基准
5. Optuna 调参 → 回填参数复跑基准
6. 若达标(全清 100% 且配对均值更优):`mcm26b run` 默认切 bayes + 文档更新
7. 分支上分阶段 commit(不 push)
# 验收标准
- 基准全部场景:all_cleared 100%、correct_absence 100%、anomalies 空、normal_exit、单局实机计算 < 60s
- 配对平均虚拟时间优于 seven_batch(目标降幅 ≥5%;参考量级 A2=5232s/12 源)
- `uv run pytest` 全绿;`mcm26b run --self-test --problem 3` 通过
# 风险与对策
- PF 实机计算耗时 → 空间哈希 + MC 子采样,基准记录 compute_wall_s,超预算先降 MC 规模(实机预算 1200s,A2 仅用 3.4s,余量巨大)
- LocalSimulator 误差场确定性 → 调参与基准用多 error_seed
- 负观测删点的正确性 → Voronoi 裕量定理 + 随机化真源保留性测试兜底
+108 -13
View File
@@ -6,37 +6,101 @@
- [四问建模分析与 P0–P7 实施计划](docs/B题建模分析与实施计划.md) - [四问建模分析与 P0–P7 实施计划](docs/B题建模分析与实施计划.md)
- [协议规则表、字段表与状态机(P0 交付)](docs/协议规则表与状态机.md) - [协议规则表、字段表与状态机(P0 交付)](docs/协议规则表与状态机.md)
- [问题一完整解答](docs/问题一解答.md) - [问题一完整解答](docs/问题一解答.md)
- [问题三搜索与总耗时改进方案(含七点覆盖证明和自建消融)](docs/algo-3.md)
- [问题3/4 强化学习选点(PPO + CUDA)](docs/algo-rl.md)
- [强化学习审核、修复与独立验证(2026-09-11)](docs/强化学习适用性审核与改进-20260911.md)
- [HRL 优化方案与 Sol 执行指南(含当前缺口、命令和验收门槛)](docs/hrl-sol-execution.md)
- [HRL P0+P1 执行报告(本轮实际命令、smoke 与多轮本地测试)](docs/hrl-p1-execution-report.md)
- [论文](docs/paper/main.tex) - [论文](docs/paper/main.tex)
## 项目结构 ## 项目结构
``` ```
referee/ # 独立评测机包(mcm26b-referee):模拟器引擎+机器狗接口+WebUI,
# 零第三方依赖、不依赖本队代码;题意符合性对照见 referee/README.md
src/mcm26b/ src/mcm26b/
├── config.py # 参数中心:题面常量[题面] + 工程参数[工程],唯一字面量来源 ├── config.py # 参数中心:题面常量[题面] + 工程参数[工程],唯一字面量来源
├── protocol/ # P0/P1 字段表、计时模型、状态机、串行幂等客户端(已完工) ├── protocol/ # P0/P1 字段表、计时模型、状态机、串行幂等客户端(已完工)
├── geometry/ # P2 角域交会、直径、MEC、问题二安全候选盘证书(已完工) ├── geometry/ # P2 角域交会、直径、MEC、问题二安全候选盘证书(已完工)
├── sim/ # 自建模拟器、故障注入、演练日志回放(测试专用,已完工)
├── belief/ # P3 逐频道候选集合 K_c:保守外包多边形 + 半平面裁剪(已完工) ├── belief/ # P3 逐频道候选集合 K_c:保守外包多边形 + 半平面裁剪(已完工)
├── coverage/ # P4 49点覆盖账本(网格生成 + 逐频道记录) ├── coverage/ # P4 49点覆盖账本(网格生成 + 逐频道记录)
├── planner/ # P3 保证接收盘选点器 / P4 全向闭环 / P5 混合源定向闭环(已完工) ├── planner/ # P3 保证接收盘选点器 / P4 全向闭环 / P5 混合源定向闭环(已完工)
│ # P6b RL 接入:planner/rl_loop.py(PPO 选点,惰性加载 torch)
├── rl/ # P6b 强化学习扩展:训练环境、观测/候选特征、PPO 模型加载(可选依赖)
├── evaluation/ # 日志复算与统计(已完工) ├── evaluation/ # 日志复算与统计(已完工)
└── sim/scenes.py # 自检场景生成器:全向与混合源(测试端专用) └── harness/ # 测试侧适配:评测机<->Transport 内存回环、故障注入、回放、场景
tests/ # pytest(兼容 unittest 风格),105 项 tests/ # pytest(含解耦守卫:运行时零导入 referee + 规则表不漂移)
scripts/ # 采集、基准、可视化与校核脚本(含问题四批量仿真 benchmark_q4.py) scripts/ # 采集、基准、可视化与校核脚本(含问题四批量仿真 benchmark_q4.py、
# RL 训练 train_rl.py 与 RL 配对消融 benchmark_rl.py)
output/ examples/ # 演练证据、算例与生成产物 output/ examples/ # 演练证据、算例与生成产物
``` ```
## 快速开始 ## 独立评测机与 WebUI(referee/)
Python ≥ 3.10,依赖仅 `requests`(测试另需 `pytest`)。 评测机已抽成独立包:`referee/`(uv workspace 成员,`uv sync` 自动挂载为 dev 依赖)。
参赛队运行时代码**零导入** referee,双方仅通过附件2 的 HTTP 协议耦合——
评测机可独立服务任何客户端,参赛队代码对接官方模拟器无需任何改动。
```bash ```bash
uv sync # 安装(可编辑挂载本包) uv run python -m referee # WebUI http://127.0.0.1:8026,接口 :2026
uv run pytest # 全部测试 uv run python -m referee --port 2026 --ui-port 8026 --data-dir referee-data
uv run mcm26b run --robot-id 2026001 --local-judge # 一键:起评测机→开演练局→闭环→汇总
```
- **WebUI 完全按附件1 §4 复现官方界面语义**:四个测试模块(演练不限次/正式各3次、
启动即占用、正式前再次确认)、确认开始→数据准备→5秒倒计时→25分钟窗口开放接口、
"指令与反馈"默认最新1000条(100..5000 可调)、中止测试两次确认、结束原因显示、
案例编码、演练结束显示 总数/全向/定向(正式任何界面不显示真值)、行为日志导出
(2MB 上限告警)、空闲时可改端口、只监听本机回环。
- **回放器**("回放"Tab):以本局行为日志 JSONL 驱动,在 Ω 圆上动画复现运动与
扫描过程——移动按 5m/s 展开、检测以雷达扫描动画呈现(direction 附实测示向度
射线)、清除先光学定位再清除;支持播放/暂停、逐步±、1×~300× 倍速快进、
滚轮缩放/拖拽平移、带动作刻度的进度条任意定位;兼容 CSV 式逐事件信息面板。
**支持手动选定 JSONL 回放**:点击"选择 JSONL 文件"或直接拖拽导入任意行为日志
(评测机日志含真值;参赛队客户端会话日志取 accepted 事件,无真值),纯前端解析。
- 引擎与协议:`referee.simulator`(观测判定/幂等/并发409/微秒计时/截止断连)、
`referee.interface`(附件2 §5 完整语法边界,HTTP 服务器与内存直连共用),
一致性由 `referee/tests` 90 项测试与真实 HTTP 测试固化。
- 自建差异(题面未规定)均在 referee/README.md 声明:联网登录/官方校时/17:30
截止/加密上传属官方服务器侧策略,本地不实现;示向度误差场与场景分布是测试假设。
## 快速开始
Python ≥ 3.10,运行时依赖仅 `requests`;测试/自检另需 `pytest` 与独立评测机
包(`uv sync` 一并安装)。
```bash
uv sync # 安装(可编辑挂载本包与 referee)
uv sync --group rl --group dev # 追加 RL 依赖:torch(CUDA)+stable-baselines3+gymnasium
uv run pytest # 全部测试(含 referee 90 项 + 解耦守卫)
uv run python -m referee # 起本地评测机(可选,配合 WebUI 回放)
uv run mcm26b sim-check # P0 自检:复现附件计时示例 105/111/194/199 uv run mcm26b sim-check # P0 自检:复现附件计时示例 105/111/194/199
uv run mcm26b run --robot-id 2026001 --self-test # 问题3自检:随机全向场景闭环(不连官方) uv run mcm26b run --robot-id 2026001 --self-test # 问题3自检:随机全向场景闭环(不连官方)
uv run mcm26b run --robot-id 2026001 --self-test --problem 4 # 问题4自检:混合定向源场景 uv run mcm26b run --robot-id 2026001 --self-test --problem 4 # 问题4自检:混合定向源场景
# RL 策略(需先安装 rl 依赖组并训练模型;详见 docs/algo-rl.md):
uv run python scripts/train_rl.py --problem 3 --timesteps 120000 # PPO + CUDA 训练 Q3 选点
uv run python scripts/pretrain_rl_expert.py --problem 4 --episodes 3000 # 自建模拟器专家行为克隆
uv run python scripts/train_rl.py --problem 4 --timesteps 40000 --init output/rl/q4_ppo_bc_init.zip
uv run mcm26b run --robot-id 2026001 --self-test --strategy rl # 问题3 RL 闭环
uv run mcm26b run --robot-id 2026001 --self-test --problem 4 --q4-strategy rl # 问题4 RL 闭环
uv run python scripts/benchmark_rl.py --seeds 5 # RL vs 手写策略配对消融
uv run python scripts/verify_rl_practice.py output/rl/official-practice/q4/20260911-rl-bc-01
uv run python scripts/benchmark_hrl.py --variants hrl_heuristic,bayes,directional \
--manifest output/hrl/manifests/dev.json --output output/hrl/p1-heuristic
uv run python scripts/train_hrl_high.py --problem 3 --stage 1 --timesteps 2048 \
--envs 1 --vec-env dummy --device mps --config-profile production \
--reward-profile time --model output/hrl/p1-smoke/q3.zip --eval-episodes 2
# 训练前端(本地回环 Web UI):实时进程/进度/参数收敛曲线,详见 docs/training-ui.md
uv run python -m mcm26b.training_ui --ui-port 8036 # http://127.0.0.1:8036
uv run mcm26b run --robot-id 2026001 --self-test --strategy hrl \
--hrl-model output/hrl/p1-smoke/q3.zip --hrl-device mps
# 自检默认:源数按题面 10~16 随机;实机预算 30 分钟(--real-budget-s 可改,仅自检生效);
# 找全后持续复扫未清除频道直到限时(--config 关闭 persistent_search 可回到"找全即退")
uv run mcm26b run --robot-id <队号> --log output/run/session.jsonl # 对接模拟器正式/演练 uv run mcm26b run --robot-id <队号> --log output/run/session.jsonl # 对接模拟器正式/演练
uv run mcm26b run --robot-id <队号> --base-url http://127.0.0.1:2026 # 对接本地 referee
uv run mcm26b run --robot-id 2026001 --local-judge # 一键本地评测:自动起 referee+开演练局
# 端口被占时主动询问换端口(非交互自动改空闲端口);WebUI 可实时观战并在"回放"Tab
# 逐步回看;结束自动汇总结束原因/真值统计/全清校验(--judge-no-ui 可关闭界面)
uv run mcm26b q1 examples/q1/normal.json # 问题一求解(等价 python -m mcm26b.geometry ...) uv run mcm26b q1 examples/q1/normal.json # 问题一求解(等价 python -m mcm26b.geometry ...)
uv run python main.py examples/q1/triangle.json --rounding-margin-deg 0.005 uv run python main.py examples/q1/triangle.json --rounding-margin-deg 0.005
``` ```
@@ -64,9 +128,17 @@ uv run python scripts/benchmark_q1.py # MEC 基准与舍入敏感性
`/clear` 不切频道,虚拟时刻以 accepted 响应为准; `/clear` 不切频道,虚拟时刻以 accepted 响应为准;
- `protocol.client.SimulatorClient`:串行发送、幂等重试(同 ID 同内容原样重发)、 - `protocol.client.SimulatorClient`:串行发送、幂等重试(同 ID 同内容原样重发)、
连接/读超时、基于 `remaining_real_duration_s` 的本地截止时钟与退出预留、JSONL 会话日志; 连接/读超时、基于 `remaining_real_duration_s` 的本地截止时钟与退出预留、JSONL 会话日志;
- `sim.LocalSimulator`:按附件语义复现模拟器(观测门限、定向覆盖角、幂等回放、 - 独立包 `referee`:按附件语义复现评测机(观测门限、定向覆盖角、幂等回放、
400/accepted=false 语义),真值只对测试端开放;`ScriptedFaultTransport` 注入断连/超时/500, 整数微秒计时、现实/虚拟截止、并发409和请求校验),真值只对评测机/测试端开放;
`PlaybackTransport` 回放官方演练日志。 `harness.LocalSimTransport` 内存回环挂载(与 referee HTTP 服务器共用同一边界),
`harness.ScriptedFaultTransport` 注入断连/超时/500,`harness.PlaybackTransport`
回放官方演练日志。
本地模拟器默认执行至多1200秒程序预算、1500秒接口开放窗口,截止后通过
`SimulatorClosed` 模拟连接关闭,已登记动作可以完成。可注入 `clock`、`wall_clock`
和 `window_started_at` 测试迟到与超时。CLI `--self-test` 显式使用 `stress_mode=True`,
保留默认1800秒压力预算并扩展窗口;这不是官方演练时限。
内存传输不实现TCP连接限流、UI或日志上传;场景均匀分布及逐点哈希误差仍是测试假设。
```python ```python
from mcm26b.protocol import SimulatorClient from mcm26b.protocol import SimulatorClient
@@ -84,12 +156,35 @@ client.exit()
| P1 协议基线(客户端 + 自建回放/故障注入测试) | ✅ 完成 | | P1 协议基线(客户端 + 自建回放/故障注入测试) | ✅ 完成 |
| P2 几何内核(角域交、直径、MEC、证书) | ✅ 完成(迁入 `mcm26b.geometry`) | | P2 几何内核(角域交、直径、MEC、证书) | ✅ 完成(迁入 `mcm26b.geometry`) |
| P3 问题二选点(K1、候选区域 Q、采样评估) | ✅ 完成(belief + 选点器,测试固化) | | P3 问题二选点(K1、候选区域 Q、采样评估) | ✅ 完成(belief + 选点器,测试固化) |
| P4 问题3全向闭环(49点覆盖、定位队列、清除、停止) | ✅ 自建完成(官方演练待执行) | | P4 问题3全向闭环(49点覆盖、定位队列、清除、停止) | ✅ 官方问题3演练单案例通过(10/10全清,[验证报告](output/q3/practice/20260911-port2026/report.md)) |
| P5 问题4定向扩展(联合约束、后备清除、预算) | ✅ 自建完成(官方演练待执行) | | P5 问题4定向扩展(联合约束、后备清除、预算) | ✅ 自建完成(官方演练待执行) |
| P6 加速与消融 | 🦴 未开始 | | P6 加速与消融 | ✅ 七点批量(`--strategy seven`,官方演练 12/12)+ 贝叶斯闭环(`--strategy bayes` 默认,自建消融 −30.0%,[算法说明](docs/algo-3-bayes.md)) |
| P6b RL 选点 | ✅ PPO+CUDA 训练环境/闭环接入/官方+自建演练验证/专家 BC 初始化(Q3 `--strategy rl`,Q4 `--q4-strategy rl`,[算法说明](docs/algo-rl.md)) |
## 采集与正式测试约束 ## 采集与正式测试约束
改进版 A2(七点覆盖+本站批量扫频)已通过官方案例 `J4ER-Y9EP-Q379-UPFA`:
12/12源全清,109次检测,虚拟耗时5232.132109秒,现实耗时3.406秒。
[本局报告与运行方法](output/q3/practice/20260911-seven-batch-01/report.md)。
独立入口为 `scripts/run_q3_seven_practice.py`。
贝叶斯闭环 `bayes`(网格粒子滤波+负观测更新+信息增益选点+滚动 2-opt
调度,参数经 Optuna 回填)在自建模拟器配对消融中相对 SevenBatch 平均
虚拟时间再降 30.0%(14/14 场景全清零异常),算法与数据见
[algo-3-bayes.md](docs/algo-3-bayes.md),已是 `mcm26b run` 默认策略。
虚拟时间即成绩口径:找全证明后立即 `/exit`(`--persistent` 可显式开启
"扫到限时"的复扫保险模式)。
2026-09-11 已通过本机 2026 端口完成一次官方问题3演练:案例
`DHFK-PHD2-SR2P-FXK9`,10/10 源全清,540 次检测、10 次清除,现实耗时
7.629 秒。原始请求响应、官方产物及离线审计见上述验证报告。原计时复算的
541 处微秒精度误报也已记录;按官方整数微秒累计规则独立复算后,552 个
响应时刻全部吻合。生产计时复算函数保持原样。
```bash
uv run python scripts/verify_q3_practice.py output/q3/practice/20260911-port2026
```
本项目只使用官方**演练**采集观测,从未使用正式测试。`scripts/capture_practice.py` 本项目只使用官方**演练**采集观测,从未使用正式测试。`scripts/capture_practice.py`
不会启动测试:必须先在界面确认“问题3演练”、接口已就绪,再提供 不会启动测试:必须先在界面确认“问题3演练”、接口已就绪,再提供
`--practice-confirmed` 与本局案例编码。正式测试由团队按计划另行安排; `--practice-confirmed` 与本局案例编码。正式测试由团队按计划另行安排;
+1
View File
@@ -194,6 +194,7 @@ AI 的适合用法是把这些模块逐一变成可测试程序、生成边界
| P4 全向闭环 | 49点覆盖记录、定位队列、清除、停止、日志统计 | 10/16源、边界源和错误响应案例;官方演练全清并正常退出 | 自建闭环已完成:`planner.OmnidirectionalLoop`(10/16/0 源、近边界源、预算耗尽、故障注入测试通过);官方演练全清待团队执行 | | P4 全向闭环 | 49点覆盖记录、定位队列、清除、停止、日志统计 | 10/16源、边界源和错误响应案例;官方演练全清并正常退出 | 自建闭环已完成:`planner.OmnidirectionalLoop`(10/16/0 源、近边界源、预算耗尽、故障注入测试通过);官方演练全清待团队执行 |
| P5 定向扩展 | no_signal联合约束、覆盖发现、有限清除后备与预算 | 朝外边界源不漏;真源不误删;格数与退出预算检查 | 已实现:`planner.DirectionalExtension`(49点覆盖对定向源成立、保证接收盘内 no_signal 记为观测到定向、对跖+环探梯子、`fallback_clear_cells` 有限圆盘覆盖后备清除带格数/预算双硬顶);`tests/test_loop_q4.py` 全过;官方混合源演练待执行 | | P5 定向扩展 | no_signal联合约束、覆盖发现、有限清除后备与预算 | 朝外边界源不漏;真源不误删;格数与退出预算检查 | 已实现:`planner.DirectionalExtension`(49点覆盖对定向源成立、保证接收盘内 no_signal 记为观测到定向、对跖+环探梯子、`fallback_clear_cells` 有限圆盘覆盖后备清除带格数/预算双硬顶);`tests/test_loop_q4.py` 全过;官方混合源演练待执行 |
| P6 加速消融 | 共享测点、自适应覆盖、目标排序、提前清除 | 配对案例全清不退化,时间/请求数改善可复算 | 可删减;闭环基线数据已可用于消融对照 | | P6 加速消融 | 共享测点、自适应覆盖、目标排序、提前清除 | 配对案例全清不退化,时间/请求数改善可复算 | 可删减;闭环基线数据已可用于消融对照 |
| P6b RL 选点 | 问题3/4 测量点选择的 PPO+CUDA 训练环境与闭环接入 | 自建全清;与手写策略配对消融有可复算报告 | 已完成:`scripts/train_rl.py`/`benchmark_rl.py`,模型 `output/rl/q{3,4}_ppo.zip`,说明 `docs/algo-rl.md` |
| P7 正式与论文 | 固定版本、论文表1、日志原名、上传核查 | 问题3/4各3份正式记录与程序版本对应 | 团队后续安排;本任务不执行正式测试 | | P7 正式与论文 | 固定版本、论文表1、日志原名、上传核查 | 问题3/4各3份正式记录与程序版本对应 | 团队后续安排;本任务不执行正式测试 |
按2026年9月10日起的剩余时间,建议10日补齐P1并衔接P3,11日完成P4自建边界案例与全向演练,12日完成P5及混合源演练;未达验收就删减P6并优先修正闭环。演练不限3次,正式机会为问题3和4各3次。团队在版本稳定后自行安排正式记录,留出失败恢复和论文整理时间。附件建议9月13日15:30前完成正式测试,17:30后不能启动新的演练或正式测试;已进入上传队列的日志仍会继续上传。此处引用所给附件安排,运行前核对模拟器界面公告;这份排期不改变当前禁止使用正式测试的限制。 按2026年9月10日起的剩余时间,建议10日补齐P1并衔接P3,11日完成P4自建边界案例与全向演练,12日完成P5及混合源演练;未达验收就删减P6并优先修正闭环。演练不限3次,正式机会为问题3和4各3次。团队在版本稳定后自行安排正式记录,留出失败恢复和论文整理时间。附件建议9月13日15:30前完成正式测试,17:30后不能启动新的演练或正式测试;已进入上传队列的日志仍会继续上传。此处引用所给附件安排,运行前核对模拟器界面公告;这份排期不改变当前禁止使用正式测试的限制。
+138
View File
@@ -0,0 +1,138 @@
# 算法说明:问题3 贝叶斯闭环(`bayes` 策略)
对应实现:`src/mcm26b/particles.py`(感知层)、`src/mcm26b/planner/bayes_loop.py`
(决策层)、`scripts/benchmark_q3_bayes.py`(消融)、`scripts/tune_q3_bayes.py`
(Optuna 调参)。基线对比:`docs/algo-3.md` 的七点批量(`seven`,A2)。
## 1. 建模定位
问题3是部分可观察的在线决策问题:频道数未知(10–16/20)、位置未知、有效
接收半径未知(1000–1500m)、测向误差 ±1°(服务器保留两位小数)。策略层
只能拿到 `/measure` 与 `/clear` 的响应,目标是最小化"发现—定位—清除"的
虚拟总时间。`bayes` 策略把它分解为三层滚动时域闭环:
1. **感知层**:每频道一个贝叶斯后验(网格幸存集),融合全部正/负观测;
2. **决策层**:站点扫频、定位测量、清除三类任务统一建表,开放路径
最近邻 + 2-opt 排序,每个动作后滚动重排(动态 TSP);
3. **选点层**:默认按期望log幸存占比和“到检测点、再接近源”的移动代价评分。
已停靠位置可逐频道补交会观测,真实清除始终由完整角域证书批准。
2026-09-11 当前修订的审核与配对验收见 [三四问算法审核与优化](算法审核与优化-20260911.md)。
本文件第5节是早期版本的历史结果,不应当作当前默认参数的复算结果。
## 2. 网格幸存集滤波与真源保留定理
每频道维护 Ω 上间距 h 的网格(h=14.65m,约 4.7 万格),`alive` 为幸存格
集合。Voronoi 裕量 m = √2/2·h 是格心到格内任一点的最大距离;所有删除
判定都内缩 m,即**仅当整格与可行集不相交时才删格**:
| 观测 | 题面语义 | 删除判定 |
|---|---|---|
| `no_signal`@s | 距离 > r_eff ≥ 1000 | 删格心距 s ≤ 1000 − m 的格 |
| `direction`@s | 距离 ≤ 1500,\|读数−真值\| ≤ δ_eff | 删角向超出 δ_eff + asin(m/(d−m)) 或距 s > 1500 + m 的格 |
| `near`@s | 距离 ≤ 5 | 删格心距 s > 5 + m 的格 |
**定理(真源保留)**:真源 P 满足全部题面约束;P 所在格的格心 g 满足
|g−P| ≤ m,故"整格不相交"判定永不命中该格。因此任意观测序列后 P ∈
幸存格的并集,且:
- **缺席证明**:幸存集为空 ⟹ 可行集为空 ⟹ 该频道无源(严格);
- **清除证书**:MEC(幸存).R + m ≤ 20 − 工程余量 ⟹ 真源距清除点 ≤ 20;
- **跳测规则**:幸存格到测点最小距离 > 1500 + m ⟹ 该测量必 no_signal
且无新排除,可跳过。
性质测试:`tests/test_particles.py`(随机源 × 随机测点 × 随机合法误差,
真源格永不被删;`tests/test_bayes_loop_q3.py` 端到端零异常)。
与 `belief.CandidateRegion`(保守凸外包)的分工:凸表示负责**清除证书**
(精确角域裁剪、收敛后触发),网格负责凸表示做不到的三件事——非凸负
观测挖洞(no_signal 排除盘)、缺席提前证毕、跳测判定。两者同读一套
响应流(`_record_measure`),任何一方证毕即收尾。
## 3. 信息增益选点
候选点集(二选一,按保证接收盘是否非空):
- **保证接收盘** B(MEC_poly 圆心, 1000 − ρ_poly)(盘内必收 direction/near,
几何同 `docs/algo-2.md` 定理1):环形采样(`search.candidate_rings`×12 方向);
- **宽候选集**(ρ_poly > 1000 时):PF MEC 的 1.0/1.5/2.0 倍探索环。
评分(`BayesConfig.score_mode`):
- `radius`(可选):期望后验半径 —— 对 K 个代表假设(幸存格均匀抽样)
各自模拟一次测量的幸存分支,分支半径用幸存子采样的质心最大距离近似,
r_eff~U(1000,1500) 的不确定性解析积分(p_no_signal = (d−1000)/500);
总分 = E[半径] + λ·移动秒数(λ=0.1);
- `entropy`(当前默认,λ=0.0105):期望log幸存占比,加上λ乘以
当前点→候选检测点→候选源中心的移动秒数。来向对称候选补充固定全局方向网格。
这里的均匀先验、零误差代表读数和均匀接收半径是选点评分的工程假设,
不属于题面给定的概率模型;只有完整±1.005°角域与距离界参与安全证明。
λ 与半径同量纲,量级必须小:λ 过大时终局阶段"走 300m"的代价压过
"半径 35m→25m"的收益,选点器原地重测、收敛停滞(消融实测 λ≥0.5 时
单源定位步数翻倍)。
## 4. 统一任务循环(动态 TSP)
每个决策点构建任务表并排序执行首个任务,随后滚动重排:
- **站点任务**:七站(原点 + 当前1159.7m环六点)上的UNKNOWN频道;
构造时验证全向覆盖解析界小于1000m,跳测规则与覆盖账本过滤;
- **定位任务**:DETECTED 且无证书的频道,位置由信息增益选点产生;
- **清除任务**:有证书(凸或网格)的频道,可沿当前位置方向移动证书圆心,
但平移量与原半径之和不得超过清除证书阈值,节省最后一段接近路程。
排序 = 开放路径最近邻打底 + 2-opt 改进(`two_opt_passes` 轮,成本含
移动 + 换频 + 动作耗时;换频按路径顺序逐步传递当前频道,clear保持频道;
终点自由不回起点)。这是 A2"站内最近邻服务
队列"的推广:消融显示距离缩短约 24% 是主要收益来源。
停止条件与基线一致:清满 16(数量上界)或全部频道 CLEARED/ABSENT,
`stop_reason=all_resolved` 即停并 `/exit`——**虚拟时间即评测成绩口径**
[用户决策修订 2026-09-11]。复扫保险模式(`mcm26b run --persistent`,
原默认行为):找全后继续只复扫未清除频道直到清除数达 16、实机预算
耗尽或虚拟安全余量(`search.virtual_reserve_s=10000s`)不足,供需要
"防缺席证明假设偏差"保险的场景显式开启。
## 5. 消融结果(自建模拟器,persistent 关,对齐找全口径)
12 随机场景(6 种子 × 10/16 源)+ 2 边界场景,与 SevenBatch 配对;
bayes 参数为 Optuna 回填后的库默认值:
| 指标 | SevenBatch | bayes |
|---|---|---|
| 有效运行(全清+缺席正确+零异常+正常退出) | 14/14 | **14/14** |
| 平均虚拟时间 | 5352.7s | **3726.2s(−30.0%)** |
| 配对降幅(最差 / 最好) | — | +7% / −41% |
| 平均移动距离 | 23413m | **15050m(−36%)** |
| 平均检测次数 | 102.4 | 110.0 |
| 单局实机计算墙钟 | 1.3s | 1.9s |
复现:`uv run python scripts/benchmark_q3_bayes.py --seeds 0,1,2,3,4,5`
(逐场景数据与场景 JSON 见 `output/q3/bayes-ablation/`)。
## 6. Optuna 调参
`scripts/tune_q3_bayes.py`:TPE(dev 依赖 `optuna`,缺失时自动退化为
随机搜索),目标 = 场景子集平均虚拟时间 + 10⁶×失败惩罚。搜索空间:
λ、代表点数、子采样规模、网格间距、站点环半径、2-opt 轮数、评分模式。
最优组合(entropy 评分、λ=0.0105、h=14.65m、代表点 7、2-opt 4 轮)
已按验收流程回填为 `config.BayesConfig` 默认值:全量基准 3726s/−30.0%
优于回填前的 4277s/−19.5%。两处人工修订:
- `station_ring_radius_m` 保留 1200(Optuna 建议 1159.7 虽再快约 60s,
但七站排除盘对最外圈格心的覆盖差 3.3m,PF 无法独立证明缺席,
只能靠覆盖账本兜底——设计性质优先于 1.7% 的均值差异);
- 评分模式在 entropy(信息增益)与 radius(期望后验半径)之间由调参
数据决定,两者实现均保留(`BayesConfig.score_mode`)。
调参记录见 `output/q3/bayes-tuning/best_params.json`。
## 7. 已知边界
- 检测次数略多于 A2(109.6 vs 102.4):定位收敛多用 1-2 次测量换距离
大幅缩短,净收益为正;跳测规则在 UNKNOWN 阶段(先验全域)暂无收益
面,收益集中在定位与复扫阶段;
- 网格清除证书(h=25m 时阈值 ≤1.8m)极少触发,作为凸证书的补位保险;
- 调参与消融基于自建模拟器(逐点固定 ±1° 误差场),不代表官方误差
分布;真源保留定理只依赖题面界(±1°、r_eff∈[1000,1500]),不依赖
误差分布假设。
+414
View File
@@ -0,0 +1,414 @@
# 问题三:具有覆盖证明的多源搜索与总耗时优化
## 摘要
针对源数为10至16、频道互异、位置未知的全向无线电干扰源,建立“发现—定位—清除—完整性证明”的在线决策模型,以全部清除为约束、以移动、检测、换频道和清除的虚拟总耗时为优化目标。沿用问题一的角域交与最小包围圆、问题二的安全接收区域,将改进重点放在全域覆盖规模与多源任务调度。首先从官方演练日志识别移动与无源频道验证的主要开销;随后证明中心加六个环形检测点即可覆盖题设圆域,并提出同站批量扫频、发现任务入队、就近定位清除的可执行基线。进一步规划利用无信号观测维护剩余区域、复用顺路检测点、优化安全清除位置及滚动比较剩余任务耗时的方法。给出停止条件、几何保守性、算法复杂度和分阶段验收方案。12个自建场景的配对消融表明,七点批量策略全部清除成功,平均逐局虚拟时间降幅为58.55%;该结果用于选择下一步实现方向,不作为官方成绩或全局最优性证明。
**关键词:** 全向源搜索;覆盖证书;集合定位;任务调度;虚拟总耗时;滚动规划。
> **延伸实现(2026-09-11)**:本文规划的无信号负观测更新、开放路径 2-opt
> 任务排序等已落地为贝叶斯闭环策略 `bayes`(网格幸存集滤波 + 信息增益
> 选点 + 滚动任务调度,参数经 Optuna 回填),自建消融相对本文七点批量
> 策略平均虚拟时间再降 30.0%(均值 3726s)。设计、严格性证明与消融数据
> 见 [algo-3-bayes.md](algo-3-bayes.md)。
## 1. 问题界定与思路来源
第三问要求在不知道干扰源位置和具体数量的条件下,自动发现并清除全部全向源,尽量缩短总时间。每个频道最多一个源,因此本题的数据关联由频道确定;机器狗无需解决无法区分多个信号身份的问题。源位于半径1800米的圆域内,最小有效接收半径为1000米。这两个尺度之间的关系使稀疏、有证明的覆盖成为可能。[题面](C:/Users/HelixCopex/code/MATH/issue/B题.pdf)、[接口规则](C:/Users/HelixCopex/code/MATH/docs/协议规则表与状态机.md)
本文与前两问分工如下:问题一回答“当前观测允许源在哪里、能否一次清除”;问题二回答“下一次去哪里测才安全且有效”;问题三回答“先搜哪些位置、扫哪些频道、何时中断搜索去清除,以及何时能够确信任务结束”。全局调度会调用前两问的几何内核,但不能只把第二问的局部精度目标逐个重复就视为总时间优化。
参考本项目[问题3—4论文列表](C:/Users/HelixCopex/code/MATH/docs/B题问题3-4相关论文列表.md),选取并核对以下相关思想。
| 来源 | 本文借鉴的内容 | 与本题的区别 |
| --- | --- | --- |
| Bayram、Vander Hook、Isler,2016,列表第10项 | 联合规划测向位置和访问顺序,同时计入移动时间与测量时间 | 原文以GDOP描述不确定度;其近似比不能转移为本文有界角域和清除任务的近似保证 |
| Olofsson等,2020,列表第22项 | 根据已观测区域分配后续搜索资源,在有限预算下规划路径 | 原文使用PHD和期望发现数;本文用确定性的逐频道剩余区域,不用概率很小替代无源证明 |
| Perera等,2023,列表第4项 | 将稀疏覆盖路线与无线电源定位结合,并分别评价路径与采样代价 | 原文依赖RSSI;本题接口没有RSS/RSSI数值,不能直接使用其估计器 |
上述内容分别见[大学研究记录及论文摘要](https://experts.umn.edu/en/publications/gathering-bearing-data-for-target-localization-6/)、[作者公开论文](https://torarnj.folk.ntnu.no/AURO2020a.pdf)、[arXiv原文入口](https://arxiv.org/abs/2312.03493)。这里只迁移任务组织方法,以下七点几何证明和题目化调度公式在本文独立推导。
论文列表中建议的RSS梯度逼近不能直接用于本题:`/measure` 只返回无信号、近距离或示向度,没有连续场强值。Atanasov等的源搜索也依赖场信号、随机梯度或测量模型,其收敛结论不证明本题仅测向闭环收敛。[原论文摘要](https://arxiv.org/abs/1402.0051) 本文也不假定源位置均匀分布、每次误差独立重抽样或某一交会角在所有场景下最优。
## 2. 现有方案的实测瓶颈
官方问题3演练 `DHFK-PHD2-SR2P-FXK9` 已证明现有实现能在该局全清10个源,完整数据见[演练验证报告](C:/Users/HelixCopex/code/MATH/output/q3/practice/20260911-port2026/report.md)。该局在策略运行结束后才读取官方源数,源真值没有进入决策。
| 分项 | 本局数值 | 对改进的含义 |
| --- | ---: | --- |
| 总虚拟时间 | 16217.454814秒 | 作为官方基线,不与自建场景混算 |
| 移动距离 / 耗时 | 64737.274米 / 12947.454814秒 | 移动约占79.84%,应优先减少站点和往返 |
| 检测次数 / 耗时 | 540次 / 2700秒 | 无源频道各扫49点,共490次,占全部检测90.74% |
| 换频道耗时 | 520秒 | 可以优化,但节省一次换频仅相当于少走5米 |
| 清除耗时 | 50秒,10次全部成功 | 清除可靠性已得到单局支持,无需先追求更小定位半径 |
| 最后一个源清除时刻 | 8454.332399秒 | 其后7763.122415秒用于剩余任务,约占总时间47.87% |
现有 `OmnidirectionalLoop._coverage_phase` 的结构还引入额外往返:在一个覆盖站按频道扫描,遇到 `direction` 就立即定位清除,再回原覆盖站继续下一频道。由原始动作日志统计,从非网格点返回网格检测点共10次,累计约9996.714米、1999.343秒。这是实际行程分类,不能全部当成可直接删除的收益,因为改序后仍需访问下一任务;但它明确说明同站任务应先批量处理。
当前49点网格同时服务第三问和第四问。它的定向覆盖证明要求更强的几何条件;第三问无需为未知发射朝向付出同样的采样代价。改进第三问时,应显式区分全向策略和第四问策略,不能全局替换公用网格。
## 3. 状态、信息与优化目标
### 3.1 符号与可用信息
| 符号 | 含义 |
| --- | --- |
| $\Omega=B(0,R)$,$R=1800$ | 源位置区域,单位米 |
| $R_-=1000$,$R_+=1500$ | 有效接收半径下界、上界 |
| $p,f,t$ | 当前机器人位置、测向频道、官方虚拟时刻 |
| $K_c$ | 已发现频道 $c$ 的保守位置候选集 |
| $E_c$ | 尚未发现频道 $c$ 仍可能存在源的位置集合 |
| $\mathcal U,\mathcal D,\mathcal L,\mathcal C,\mathcal A$ | 未发现、待定位、可保证清除、已清除、已证明无源频道集 |
| $\mathcal P$ | 曾获得有效存在证据的不同频道集,包含已清除频道 |
| $\delta=1.005^\circ$ | 题设1°误差加两位小数舍入裕量 |
| $S$ | 具有全域覆盖证明的检测站集合 |
| $L,N_m,N_s,N_c^+,N_c^-$ | 移动长度、检测数、换频数、成功与失败清除数 |
源静止、同位置同频道的测向误差固定。只有该频道实际收到的观测才能更新其状态;经过一个位置不代表在那里扫过所有频道。不同频道的无信号证据不能互相替代。
### 3.2 统一的时间口径
忽略微秒量化时,整局虚拟耗时为
$$T=\frac{L}{5}+5N_m+N_s+5N_c^++3N_c^-.$$
`/clear` 的目标频道不改变测向机频道,因而不引入换频代价;`/exit` 不要求回到原点,也不增加虚拟时间。因此访问顺序是从当前位置出发、终点自由的**开放路径**问题,不能默认使用需要返回起点的旅行商回路。
官方内部按微秒累计,验收时按每次移动耗时量化到整数微秒,再累加检测、换频和清除的整数秒。连续实数公式用于规划,逐动作微秒公式用于复算。原演练汇总的541处偏差已查明为累计浮点与微秒精度口径不同,详见[独立审计](C:/Users/HelixCopex/code/MATH/output/q3/practice/20260911-port2026/verification.json)。
优化目标按优先级定义为:保持真源候选和有效完成条件;在现实1200秒、虚拟360000秒上限内全清;在可完成策略中降低 $T$。现实计算和网络耗时单独约束,不加进虚拟时间公式。由于源场景和误差场未知,理想模型可写为对允许场景集合 $\Xi$ 的 $\inf_\pi\sup_{\xi\in\Xi}T(\pi,\xi)$,但本文不声称解出了这个连续在线极小极大问题,也不声称已证明任意合法场景都能在两个预算内完成。
## 4. 逐频道证据与安全停止
### 4.1 观测更新
首次 `direction` 在 $s$ 返回 $\theta$ 时,建立
$$K_c=\Omega\cap B(s,R_+)\cap W(s,\theta,\delta).$$
以后每次有效测向再交入相应距离上界和角域。`near` 时交入 $B(s,5)$,当前检测点本身就满足20米清除条件。角域的半平面表达、保守圆盘外包和最小包围圆计算沿用[问题一](C:/Users/HelixCopex/code/MATH/docs/algo-1.md)、[问题二](C:/Users/HelixCopex/code/MATH/docs/algo-2.md)。
全向源在 $s$ 返回 `no_signal`,意味着若该频道仍存在未清除源,则其距离严格大于实际接收半径,进而大于1000米。因此对未发现频道可维护
$$E_c=\Omega\setminus\bigcup_{s\in\mathcal N_c}B(s,R_-),$$
其中 $\mathcal N_c$ 只含该频道实际测得无信号的位置。有限精度实现可只排除 $B(s,R_- -\eta)$,$\eta>0$ 是有依据的数值裕量;少删不会漏源。该负观测更新仅用于第三问,不能直接用于第四问的定向源。
圆盘差通常非凸。不能把扣除圆盘后的集合强行表示成单个凸多边形,更不能丢弃狭窄或小面积分量。第一阶段保留当前凸定位内核,仅在未知频道的覆盖账本中使用负观测;后续如把无信号用于已发现源的精定位,应使用多片区域或再取保守凸外包。
### 4.2 完成与计数推理
安全停止条件是 $\mathcal C\cup\mathcal A=\{1,\ldots,20\}$,且本局 `/exit` 已被接受。无源证明可来自完整覆盖、剩余集合的可靠判空,或下述数量上界。
由于每频道至多一个源且总数不超过16,当 $|\mathcal P|=16$ 时,所有未出现过有效信号的频道即可由数量上界判为无源,**不必等待16个都清除才停止扫未知频道**。随后仍须清除 $\mathcal P\setminus\mathcal C$,不能直接退出。成功清除16个则立即满足全清条件。
只发现或清除10个不够,因为10是下界。反过来,若无源证据已排除10个频道,则其余10个频道都必须有源,但仍需获取其定位观测;这种计数推理不能替代位置搜索。若已知存在与无源证据互相矛盾,或可存在频道数不足10,应报告协议、模型或数值不一致,不能把矛盾解释为成功。
## 5. 用七个检测站替代49点网格
### 5.1 布点
取原点及半径 $a$ 圆周上的六个等角点:
$$S(a)=\{(0,0)\}\cup\{a(\cos(k\pi/3),\sin(k\pi/3)):k=0,\ldots,5\}.$$
推荐首个工程版本固定 $a=1200$ 米。站点坐标为 $(0,0)$、$(1200,0)$、$(600,\pm600\sqrt3)$、$(-600,\pm600\sqrt3)$、$(-1200,0)$。这些点全部位于源圆域内;允许域外检测这一规则仍然保留,但本方案不需要依赖它。
### 5.2 连续覆盖证明
**定理1:** 若 $0<a/\sqrt3\le R$,则 $S(a)$ 对 $B(0,R)$ 的覆盖距离满足
$$\max_{g\in\Omega}\min_{s\in S(a)}\|g-s\|\le D(a):=\max\left\{\frac a{\sqrt3},\sqrt{R^2+a^2-\sqrt3Ra}\right\}.$$
**证明:** 任意 $g$ 写成极坐标 $(r,\theta)$。它与最近环上站点的夹角 $\varphi$ 不超过 $\pi/6$。令 $r_0=a/\sqrt3$。若 $r\le r_0$,选择原点,距离至多 $r_0$。若 $r\in[r_0,R]$,选择最近环站,有
$$\|g-s\|^2=r^2+a^2-2ar\cos\varphi\le r^2+a^2-\sqrt3ar.$$
右侧关于 $r$ 为凸二次函数,在闭区间最大值出现在端点。代入 $r_0$ 得 $a^2/3$,代入 $R$ 得第二项平方,从而得到结论。证毕。
代入 $R=1800$、$a=1200$,得到 $r_0=692.820323$ 米,$D(a)=968.901572$ 米,距离接收下界有31.098428米余量。因此每个合法全向源至少可在七站之一被检测到,不依赖位置概率、源数或源频道。
这是连续圆域的证明,不是“抽样没看到空洞”。此外用130501个极坐标采样点复算,最大最近站距为968.901572米,与解析值一致;该数值检查仅辅助发现公式或实现错误,见[覆盖校核数据](C:/Users/HelixCopex/code/MATH/output/q3/search-ablation/benchmark.json)。
**推论:** 对某频道七站全部测得无信号,就能证明该频道不存在源。若真实有源,覆盖定理给出一个距其小于1000米的站,在该站不可能返回无信号,矛盾。对已发现或已清除频道可以停止剩余覆盖检测,必须在逐频道账本中保存其原因。
### 5.3 环半径与时间的权衡
在本七点族内,若要求覆盖距离至多 $d\le1000$,外边界约束为
$$a^2-\sqrt3Ra+R^2-d^2\le0,$$
同时要求 $a/\sqrt3\le d$。当 $d\ge R/2$ 时,环半径的外边界允许区间为
$$R\cos(\pi/6)-\sqrt{d^2-R^2/4}\le a\le R\cos(\pi/6)+\sqrt{d^2-R^2/4}.$$
只按1000米边界、零裕量计算,最小 $a$ 约为1122.955832米;保留1米覆盖裕量时为1125.254906米。1200米不是最短路线的解析最优参数,而是在站距与覆盖裕量之间采用的透明工程选择。较小 $a$ 缩短站间路线,却改变后续发现时的交会几何和定位路程;应在独立场景上比较,不能仅由扫描路长选全局最优值。
从原点先到任一环站,再依次访问相邻五站,不返回原点,纯站间路长为 $6a=7200$ 米。每站最坏扫描20个频道,发现阶段最多140次检测,顺序扫描的换频次数至多139。因此这一**不含定位清除插入**的纯覆盖路线有上界
$$T_{\rm cover}\le7200/5+140\times5+139=2279\text{秒}.$$
不能把2279秒当成全清总耗时上界;定位插入还会改变站间行程。对本次官方案例的10个无源频道,完整覆盖所需检测可从490次降为70次,差420次、2100秒检测时间。这是覆盖账本的解析差额,不是已在相同官方案例实测的新算法收益。
七点既未被证明是最少站数,也未被证明在全部可行布点中路线最短。第四问边界朝外的定向源可看不到这些内侧站点,所以该定理仅进入第三问专用策略。
## 6. 同站批量扫频与定位任务队列
### 6.1 先完成本站,再离站
推荐的第一阶段策略如下:到达一个覆盖站,先对尚需在该站搜索的全部未知频道各测一次。若当前测向频道在待测集合中,将其排第一以节省一次换频,其余频道按固定顺序扫描。`direction` 只更新候选集并加入定位队列;`near` 可以在当前站立即清除,因为不需增加移动。完成本站的频道批次后,再决定离站执行哪些定位任务。
这保留每次请求的串行语义,也避免“查出一个频道—离站—返回—再查下一个频道”的结构性往返。全局频道分组,即先把某频道扫遍全域再换下一频道,虽然省换频,却可能把路径重复十余遍;每次检测移动只有超过5米,其耗时就已超过一次换频,应以空间批处理优先。
### 6.2 队列中的基本排序
第一版对待定位队列按当前点到候选集最小包围圆中心的距离选择最近频道,完成该频道的安全测向和清除后重新排序,然后访问仍有未知频道待测的最近覆盖站。圆心距离只是一种便宜的排序代理,不是实际完成该频道的精确剩余成本。
相比按频道编号处理,重新排序能考虑清除后的新位置;相比扫完七站才定位,同站发现后的批量服务能避免把所有局部信息拖到远处再处理。第12节的消融支持先采用这一方案,但最近优先没有全局最优或对所有场景更快的保证。
同一轮每个待定位频道最多调用一次既有定位子循环;该子循环每轮最多追加8次检测。未解决的频道保留在队列,不能忙等或重复清除同一份失败证书。覆盖阶段结束后再处理未解决任务;预算不足时明确记为未完成。
### 6.3 后续将队列代价改成“插入耗时”
设当前点为 $p$,下一计划覆盖站为 $b$。若某待清除目标允许的位置集合为 $\mathcal C_c$,其插入路径代价可写为
$$\Delta L_c=\min_{q\in\mathcal C_c}\bigl(\|p-q\|+\|q-b\|\bigr)-\|p-b\|.$$
再加5秒清除时间,即可与直接前往下一覆盖站比较。待定位目标用一次或两次安全测向的短程预演估计对应代价。存在多个目标时,采用最近插入、开放路径2-opt或限宽束搜索重排;测量产生新候选集后只执行当前最佳方案的第一步,再重规划。缺少未来目标真值时,这些都是显式的在线启发式。
“把所有新发现目标都做完再去下一站”是第一版规则,后续可允许代价过高的目标暂存,但要设置可审计的最大延后次数,并在搜索终止后清空队列,防止某个难目标长期得不到处理。
## 7. 无信号区域复用与自适应跳站
七点是固定的完成保证,负观测区域允许进一步减少实际检测。在定位、清除行程的已到达位置,可以评估是否顺路测量少量未知频道;每个额外频道仍需5秒,不能把“已到达”解释为“免费探测”。
### 7.1 具有保守性的区域表示
对每个未知频道以方形单元并集 $\widetilde E_c$ 外包 $E_c$。初始保留与 $\Omega$ 可能相交的单元。设单元中心为 $z$、半对角线为 $h$,只有满足 $\|z\|-h>1800$ 的单元才可直接判为域外;边界相交不确定时继续保留或细分。
一次该频道无信号观测位于 $s$,若整个单元满足
$$\|z-s\|+h\le1000-\eta,$$
则单元全部落入保证可收盘,可以安全删除。不得只检查单元中心,也不得用近似覆盖面积达到100%作为判空证据。若单元仍与真实残余相交,就保留它,直至可给出整单元删除证书。
**命题2:** 以上规则维持 $E_c\subseteq\widetilde E_c$,故 $\widetilde E_c=\varnothing$ 足以证明无源。反过来,$\widetilde E_c\ne\varnothing$ 可能只是离散保守性,不能据此断言有源。
七点有31.098米几何余量;可使最终方格边长不超过20米,其直径不超过28.285米。对任意与 $\Omega$ 相交的单元,取其中一个域内点,该点距某站不超过968.902米;单元全部点距该站不超过997.186米。因此在工程裕量小于约2.81米且正确处理边界相交的情况下,七站无信号足以最终删除这些单元。这里用整单元的最大距离证书;若只用 $\|z-s\|+h$ 的较松证书仍未通过,继续细分或直接使用七点账本证明,不错误地停在粗格残片上。
### 7.2 候选点与频道选择
新增候选点来自:未完成的七个锚点、当前位置、已规划的定位点/清除点、残余区域边界附近点。对 $q$ 和频道集合 $F$,定义
$$G(q,F)=\sum_{c\in F}\operatorname{area}\bigl(\widetilde E_c\cap B(q,1000-\eta)\bigr),$$
并以 $G(q,F)/(\|p-q\|/5+5|F|+N_{\rm switch}(F))$ 作为探索排序指标。面积是搜索潜力代理,不是发现概率或保证的信息增益;面积计算可近似,但只影响候选排序。无源判定仍由整区域覆盖证书给出。对极小、线状或退化残余,还要保留单元数、最远未覆盖距离及锚点完成优先级,不能因面积接近零而永久忽略。
在当前位置按每个候选频道的 $G(q,\{c\})$ 与5秒检测、可能1秒换频排序,只有优于当前预演方案时才添加。未来站点若要删除,必须重新证明该频道的剩余 $\widetilde E_c$ 被保留站点的保证接收盘覆盖;一次扫描所得“没有发现”只删该次盘内区域,不能整片按经验跳过。
### 7.3 完备后备
七点中未完成的“站点—频道”记录始终保留。自适应探索若连续不产生新的可靠覆盖、计算超时或动态证书不确定,则恢复固定七点访问。设置有限的额外探测额度或要求每次替换都完成至少一个新的覆盖记录,使启发式不能无限拖延覆盖完成。
这给出确定性的搜索后备:自适应部分只允许节省已证明冗余的动作,不允许删除尚未证明冗余的覆盖责任。该设计尚未进入当前生产实现,也未包含在第12节的七点消融中。
## 8. 定位和清除也应优化剩余时间
### 8.1 定位到“够清除”就停止
设保守多边形 $\widetilde K_c$ 的最小包围圆为 $B(m_c,\rho_c)$。清除阈值使用 $r_{\rm safe}=20-\varepsilon$,其中 $\varepsilon$ 覆盖实际计算和坐标提交误差。若 $\rho_c\le r_{\rm safe}$,就具有一次清除证书;没有必要为了获得更小半径继续增加检测或横移。
外包多边形本身已经包含真集,它的多余面积使决策更保守;不能把“外包多边形与原圆盘的径向差”未经传播分析当作浮点误差保证。现有实现还扣除了 `slack_m` 和工程清除余量,本次消融完整保留其行为;后续统一外包与数值误差口径时另行验收。
### 8.2 在所有保证清除点中选择短路径位置
若 $V_c$ 为 $\widetilde K_c$ 的顶点,则
$$\mathcal C_c=\bigcap_{v\in V_c}B(v,r_{\rm safe})$$
是凸集,非空当且仅当 $\rho(\widetilde K_c)\le r_{\rm safe}$。圆盘凸性保证覆盖全部顶点等价于覆盖整个多边形,故任意 $q\in\mathcal C_c$ 都能清除真源。
无后续任务时,选择 $\arg\min_{q\in\mathcal C_c}\|q-p\|$;已计划下一站 $b$ 时选择 $\arg\min_{q\in\mathcal C_c}(\|p-q\|+\|q-b\|)$。两者都是凸优化问题,可用二阶锥形式表达,也可在圆盘交的候选边界上求解后逐顶点复核。
轻量实现可先使用内部安全圆盘 $B(m_c,r_{\rm safe}-\rho_c)\subseteq\mathcal C_c$:将 $p$ 投影到该圆盘即可得到一个不远于前往 $m_c$ 的保证清除点。这只是对该充分圆盘的最近点,并非对整个 $\mathcal C_c$ 最优。若圆盘半径为0就退化到 $m_c$。
### 8.3 安全测点的目标由半径改为任务耗时
问题二提供的安全区域为 $\mathcal Q(K_c)=\bigcap_{g\in K_c}B(g,1000)$,可用解析盘或保守多边形构造充分区域。选点仍必须先满足接收保证,不能为走近一个猜测坐标而接受未经评估的失联风险。
在可安全测量的候选点中,使用以秒计量的短程代价
$$J_H(q)=\frac{\|p-q\|}{5}+5+\mathbf1_{f\ne c}+\max_{z\in\mathcal Z(q)}\widehat V_{H-1}(K_c\mid q,z;q,c),$$
其中 $z$ 包含 `near` 及允许的示向度,$\widehat V$ 估计完成该源及返回后续任务的时间。若某分支已能保证清除,其终端代价包括移动至 $\mathcal C_c$ 的时间和5秒清除;若未能清除,可继续一层安全测向,或赋予经过标定的剩余定位代价。未来测点、清除位置与访问顺序共同影响 $\widehat V$。
这个形式解释了为什么“后验半径更小”不一定“完成更快”:走远很久将半径降到5米,可能劣于近处测量后已经达到19米。所有实际比较项统一为秒,避免随意相加米、面积和时间。有限候选和有限视野只给出预演策略的启发式排序。
若要把 $\max_z$ 报告为严格上界,必须采用问题二第10节覆盖全部读数的角区间扩张,并对区域误差与终端代价也作保守上界。当前“边界位置采样×误差端点”并无该保证,不能仅因取了样本最大值就声称最坏总时间已受控。推荐先保留现有局部选点,验证搜索调度收益,再单独启用这一成本模型。
## 9. 保守性、有限结束与预算边界
**命题3(不漏发现):** 固定七点对每个仍未知频道完成扫描,或由可靠的残余覆盖/数量证书替代相应扫描,必能发现所有合法全向源。
**证明:** 固定点由定理1保证;动态替代保持该频道覆盖域包含所有仍可能位置;数量证书只在已有16个不同源的存在证据时排除其他频道。因此真源不可能被当作无源跳过。证毕。
**命题4(证书清除):** 若维护的外包集合包含真源,实际提交点满足对该外包的20米清除约束,则在题设规则下 `/clear` 必成功。几何证书通过而官方失败时必须记录异常并检查模型、数值及协议状态,不能在同一证书上循环重试。
上述命题不证明任意安全测点序列都会有限步收缩到20米。现有每轮8次测量是工程限额,不能作为收敛定理。拟在后续增加有限后备:对已发现频道的有界外包区域,用边长20米方格的中心覆盖所有相交格,任意格内点至格心不超过 $10\sqrt2<20$ 米;遍历该有限清除候选即可在模型成立时清除源。单元相交计算必须向保留更多格的方向保守。
后备仅在定位停滞时使用,并须先检查实际枚举格数、路径和预算。设当前到首格及格间的计划路长为 $L_f$,共 $M_f$ 格,最迟第 $M_f$ 格成功的虚拟耗时上界为
$$T_f\le L_f/5+3(M_f-1)+5.$$
使用相交格数硬顶、现实高分位单请求估计 $M_f\,t_{\rm RTT}^{95}$ 加退出预留,以及虚拟剩余预算共同筛选。现有第四问中的后备格算法可供复用,但不能跳过第三问的验证。硬顶或预算不允许时,保留“未完成”,不将耗尽预算说成证明无源。
因此本文区分三层结论:覆盖与证书的数学安全性;预算足够且正确遍历有限后备时的有限完成性;现实/虚拟双预算内的性能,目前由实验评价,尚无所有合法场景的统一完成上界。
## 10. 推荐算法与伪代码
**算法3A:七点覆盖+同站批量扫频+就近定位清除(优先落地版本)。**
```text
读取第三问参数,生成七点 S(1200),初始化逐频道观测和覆盖账本
调用 /enter,记录官方时间预算
while 尚有未知频道的站点记录未完成:
若预算不足:标记未完成,转退出
选择仍有未知频道待测的最近站点 s
在 s 批量测量待查频道(当前测向频道若在其中,优先测)
no_signal:记录该频道在 s 的覆盖证据
direction:更新该频道 K,加入待定位队列
near:在 s 清除并依据响应更新状态
对本站结束后的待定位队列:
按当前位置到候选圆心距离选最近频道
调用问题二安全选点与问题一清除证书完成其定位子循环
清除成功后重新排序;未完成者保留,单轮不无限重试
若已清除16个:结束任务
根据逐频道七点账本标记无源;继续处理尚未清除的已发现频道
仅在全频道均已清除/有无源证明时标记完成
调用 /exit;退出响应失败单独记录,不能宣称正常结束
```
**算法3B:在3A之上逐项增加的完整改进规划。** 每次 `direction/near` 更新存在集合 $\mathcal P$,达到16时提前结束未知频道搜索;在各实际测量点更新负观测区域并进行有证书的站点替代;用插入耗时比较“先搜下一站”与“先做某个源”;对候选路线重排;以预计剩余时间选择安全测点,在可清除区域中选择省路位置;定位停滞时按预算调用有限后备。每执行一个动作就用真实响应更新状态,禁止整段盲执行预测的后续动作。
```mermaid
flowchart TD
A[进入并读取预算] --> B[更新逐频道证据与完成条件]
B -->|已全清且无遗漏证明完整| X[退出并保存日志]
B -->|预算不足| Y[按未完成退出]
B -->|仍有任务| C[候选覆盖站与定位清除任务]
C --> D[比较短程总耗时并保持覆盖责任]
D --> E[同站批量扫描或执行一次定位清除动作]
E --> F[接收官方响应并验证候选及证书]
F --> B
```
第一版和后续规划分别命名,防止把3B尚未实现的负观测跳站、时间型选点和后备算法算进3A的测试成绩。
## 11. 计算量与工程实现
七点固定账本最多 $7\times20=140$ 个布尔记录。最近站选择检查7个点,每次按至多20个频道统计待测项;16个源的贪心任务队列重排规模很小。固定站路线的开放路径最优排序如需精确解,可使用子集动态规划,规模为 $O(k^2 2^k)$、$k\le7$;但加入观测依赖的定位任务后不再是同一个静态问题。
定位计算仍主要消耗在多个候选点、候选源见证点、读数分支的几何裁剪和最小圆计算。假设候选测点 $Q$ 个、读数区间 $B$ 个、外包顶点 $V$ 个,单层保守评估包含 $QB$ 次裁剪和最小圆计算,可表示为 $O(QB[V+T_{\rm MEC}(V)])$;两层分支不能简单称为线性。复用候选集缓存、先粗筛少量入围点,再完整区间验证,能够控制现实计算预算。
残余区域若使用 $H$ 个单元,20频道掩码空间为 $O(20H)$,一次频道负观测朴素检查 $O(H)$;可以预计算七锚点的单元覆盖掩码,并通过空间索引限制动态点的更新范围。分辨率只控制证书的保守程度,不得因内存不足删除尚未证实的残余格。
| 模块 | 当前事实 | 建议改动 |
| --- | --- | --- |
| `coverage` | 共用49点与逐频道账本 | 新增第三问七点构造;保留第四问网格及其独立证明 |
| `planner.OmnidirectionalLoop` | 发现后立即离站定位 | 把“本站扫频”与“队列服务”拆开,引入第三问策略选项 |
| `belief` | 无信号不裁剪候选 | 增加第三问独立残余覆盖层,避免破坏第四问朝向语义 |
| `geometry/planning`、`planner` | 有安全选点与最小圆证书 | 后续增加最近安全清除点和时间型候选评分 |
| `evaluation` | 浮点累计、固定微秒容差 | 单独兼容官方逐步微秒时钟和自建连续时钟,保留两种原始口径 |
| `protocol` | 串行、幂等、预算退出、JSONL日志 | 延用;增加的动作仍走同一客户端,不能并发测多个频道 |
实际路径分别位于 [coverage](C:/Users/HelixCopex/code/MATH/src/mcm26b/coverage/__init__.py)、[planner](C:/Users/HelixCopex/code/MATH/src/mcm26b/planner/__init__.py)、[belief](C:/Users/HelixCopex/code/MATH/src/mcm26b/belief/__init__.py)、[evaluation](C:/Users/HelixCopex/code/MATH/src/mcm26b/evaluation/__init__.py)。前两问文档中部分旧路径仍写为根目录 `geometry`,复用时以当前 `src/mcm26b/geometry` 为准。
## 12. 已完成的自建配对消融
### 12.1 实验口径
为验证优先级,新增独立脚本 [benchmark_q3_search.py](C:/Users/HelixCopex/code/MATH/scripts/benchmark_q3_search.py),只连接进程内 `LocalSimulator`,不连接官方端口、不修改生产策略。数据保存在 [search-ablation](C:/Users/HelixCopex/code/MATH/output/q3/search-ablation/benchmark.json),同时逐局保存源场景、固定误差场种子、接受动作日志与哈希。
共12个场景:源数10和16各一组,每组包含 `random_omni_scene` 种子0、1、2、3、4的5个场景,以及1个源位于1800米边界、接收半径均为1000米的场景。随机生成器径向采样不是圆域面积均匀分布,并特意含近边界源,不将其称为真实场景分布。各变体使用同一局源坐标、接收半径、频道和固定空间误差场;不同位置的合成误差机制不代表官方误差场。边界例包含扇区接缝附近的源。
比较四种版本,均沿用现有安全选点与清除内核、舍入裕量0.005°和8次追加检测限额:
| 版本 | 唯一涉及的搜索与调度变化 |
| --- | --- |
| A0:49点即时定位 | 当前生产基线 |
| A1:七点即时定位 | 只把覆盖点换成七点,仍发现即离站 |
| A2:七点本站批量 | 本站扫频完成后,按最近候选圆心服务队列;当前频道优先 |
| A3:七点全域先扫 | 扫完所有必要覆盖站后,再按最近候选圆心定位;near仍原地清除 |
A2/A3都包含当前频道优先这一小优化,因此A1与A2之差不是“纯粹只改变一个排序变量”的严格单因素实验。后续应再拆分消融换频顺序。3B的动态残余区域、16个已发现源提前停止搜索、时间型选点、最近安全清除点及后备均未在这组实验启用。
### 12.2 全部12个场景结果
| 版本 | 全清且正常完成 | 平均虚拟时间 / 秒 | 平均检测数 | 平均移动距离 / 米 | 平均逐局时间降幅 |
| --- | ---: | ---: | ---: | ---: | ---: |
| A0 | 12/12 | 13788.04 | 418.00 | 56183.95 | — |
| A1 | 12/12 | 7476.09 | 100.42 | 34146.70 | 43.25% |
| A2 | 12/12 | 5496.20 | 100.92 | 24177.66 | **58.55%** |
| A3 | 12/12 | 6094.73 | 101.67 | 27164.04 | 53.90% |
每个版本共清除156个源,48局合计624个源全部清除,无失败清除、无错误缺席判定、无定位异常。A2相对A0的逐局降幅范围为37.50%至70.84%。降幅按 $\frac1n\sum_i(1-T_{A2,i}/T_{A0,i})$ 计算,不能与两个均值之比混称同一个指标。
另以保存的自建场景真值独立复核48局动作日志,逐步检查正观测后的候选保留真源、清除证书及实际清除距离、全清集合、计时、检测数、退出响应与日志哈希,全部通过,见[证据复核结果](C:/Users/HelixCopex/code/MATH/output/q3/search-ablation/evidence-audit.json)。这些真值只用于离线验收,不进入策略。
| 源数(每组6场景) | A0平均时间 / 秒 | A1平均时间 / 秒 | A2平均时间 / 秒 | A3平均时间 / 秒 |
| --- | ---: | ---: | ---: | ---: |
| 10 | 15516.58 | 6313.47 | **4836.11** | 5467.38 |
| 16 | 12059.50 | 8638.71 | **6156.28** | 6722.08 |
10源时,未知频道的无源证明开销较大,所以七点替换收益明显;16源时,可通过清除数量上界提前停止,原网格未必扫完,但批量调度仍显著减少移动。A1与A2检测数相近,A2平均移动距离进一步下降,表明减少往返比单纯少测一次更有价值。这组自建证据支持优先实现A2,而非先投入复杂概率图、强化学习或全局非线性优化。
四版本平均本地计算墙钟约1.7至1.8秒/局,记录在数据中;它不含官方网络、真实程序截止或官方UI开销,不能当作新方案官方现实耗时。原官方动作日志只能验证旧路径上的响应,不能用它推断新测点的读数。只有在官方新演练执行新策略后,才能报告官方提速。
### 12.3 复现
```powershell
.venv/Scripts/python.exe scripts/benchmark_q3_search.py --seeds 0,1,2,3,4
```
也可使用 `uv run python` 调用同一脚本。输出包含原始逐局数据,失败不会从汇总中剔除。当前实验是小规模方案筛选,不能证明所有合法场景、所有固定误差场或不同环半径下的性能。
## 13. 分阶段改进与验收计划
| 阶段 | 交付 | 必须通过的验收 |
| --- | --- | --- |
| I:七点+本站批量 | 将A2接入第三问CLI选项,保留A0可切换;原始日志格式兼容 | 七点连续覆盖证明与坐标误差预算;10/16源及边界例全清;第四问覆盖策略未被改动 |
| II:存在计数与扫频次序 | $|\mathcal P|=16$ 提前停止未知频道扫描;同站频道优先独立消融 | 已发现数、已清除数不混淆;发现10个不能停;clear不改变测向频道 |
| III:残余覆盖与顺路观测 | 保守单元账本、负观测复用、有证书跳站 | 边界及单元接缝不漏;未知频道各有独立覆盖责任;启发式停滞自动恢复七点 |
| IV:时间型局部动作 | 最近安全清除点、短程任务预演、插入排序 | 实际提交点满足全部外包顶点约束;near和跨0°读数正确;采样评分不冒充严格上界 |
| V:定位停滞与预算后备 | 有界区域有限清除覆盖,明确上限与退出 | 清除格子覆盖全部候选;格数/两种时间预算不足均记未完成;故障与失败可回放 |
每个阶段都在相同场景和同一固定误差场上进行配对比较。建议将种子0—4保留为本次开发集;最终验证另生成独立种子100—199,并分别覆盖10至16个源、接收半径1000和1500米、边界源、团簇源、同站near、接近共线读数、误差恰在±1°及空间相关误差场。所有超时、矛盾和未全清样例进入结果表,不允许只报成功局。
主要指标为全清且正常退出比例、平均/中位/P95/最大虚拟总时间、移动长度、检测数、换频数、清除失败数、证明无源的成本;同时报告最后清除时刻、现实计算时间与总请求数。小样本不确定性用逐局配对差分及其范围表达,增加样本后可在声明抽样假设的前提下给出区间估计;不从当前12例推出总体成功率为100%。
官方验收继续使用 socket 接口,逐条保存请求响应,最后保存官方结果及加密日志;优先使用官方支持的同案例复现功能进行配对,如果无法复现同一案例,就分别报告案例差异,不将两次随机局的时间差当成纯算法收益。本轮只完成方案研究与自建实验,没有再次启动官方演练。
## 14. 结论与实现边界
第三问首先应把覆盖责任从49点缩减为已证明足够的七点,并将覆盖站内扫频和离站定位拆开。该改进直接针对官方日志中的主要开销,在保持几何定位方法不变时,已经得到12个自建场景的配对支持。推荐先落地A2,再独立验证存在数量上界、残余覆盖和时间型测点/清除点优化。
初稿形成时,七点与三种调度变体存在于离线消融脚本,官方仅验证A0;随后A2已通过下述一次官方演练。生产 `src/mcm26b` 默认策略仍未修改,A2由独立运行脚本调用。3B给出了可实现的进一步规划,但没有将其预期收益写成已测结果。固定覆盖、观测集合和清除证书负责可靠性;候选评分、任务排序和半径调参负责效率,二者的保证范围分别陈述。
## 15. 补充:A2官方演练已通过
在方案研究之后,用户启动新的问题3演练,于2026-09-11通过2026端口实际执行A2。案例 `J4ER-Y9EP-Q379-UPFA` 的官方源数为12,12个全部清除,109次检测、12次清除无失败,现实耗时3.406秒,总虚拟耗时5232.132109秒(1小时27分12秒),移动22645.661米。其余8个频道各有完整七点无信号记录,最终正常退出。
123个请求响应已严格离线回放,清除证书和官方微秒计时全部通过复核。原始数据、官方产物、运行代码哈希及复现入口见[本局验证报告](C:/Users/HelixCopex/code/MATH/output/q3/practice/20260911-seven-batch-01/report.md)。因此A2现已有自建配对实验及一个官方案例的有效性证据。
相较之前A0官方10源案例的16217.454814秒,本局虚拟时间少67.74%,但案例和源数不同,不能把该差异称为严格算法提速率;第12节的58.55%仍仅指12个自建场景的配对平均结果。3B的进一步改进尚未启用。
## 参考文献
1. 全国大学生数学建模竞赛组委会. 2026年高教社杯全国大学生数学建模竞赛B题:无线电干扰源的快速自动定位与清除[Z]. 2026. [题面](C:/Users/HelixCopex/code/MATH/issue/B题.pdf);模拟器配套材料《附件2:模拟器通信接口说明及编程指南》[本地文件](C:/Users/HelixCopex/code/MATH/issue/附件/附件2.docx)。
2. 本项目. 问题一:有界测向误差下定位区域的直径与圆覆盖;问题二:具有接收保证的第二检测点选择[Z]. 2026. [algo-1](C:/Users/HelixCopex/code/MATH/docs/algo-1.md)、[algo-2](C:/Users/HelixCopex/code/MATH/docs/algo-2.md)。
3. BAYRAM H, VANDER HOOK J, ISLER V. Gathering Bearing Data for Target Localization[J]. IEEE Robotics and Automation Letters, 2016, 1(1): 369–374. DOI: 10.1109/LRA.2016.2521387. [作者所在大学的研究记录](https://experts.umn.edu/en/publications/gathering-bearing-data-for-target-localization-6/)。核对题目、作者、页码及移动与测量联合代价;未套用其GDOP近似比。
4. OLOFSSON J, HENDEBY G, LAUKNES T R, JOHANSEN T A. Multi-Agent Informed Path Planning Using the Probability Hypothesis Density[J]. Autonomous Robots, 2020. [作者公开稿](https://torarnj.folk.ntnu.no/AURO2020a.pdf),第1—2节。用于未观测区域与预算路径的建模对照。
5. PERERA A, TRAN V P, ANAVATTI S, KASMARIK K, GARRATT M. Radio Source Localization using Sparse Signal Measurements from Uncrewed Ground Vehicles[EB/OL]. arXiv:2312.03493, 2023. [原文入口](https://arxiv.org/abs/2312.03493)。用于稀疏覆盖与实验设计的对照,RSSI估计器未迁移。
6. ATANASOV N A, LE NY J, PAPPAS G J. Distributed Algorithms for Stochastic Source Seeking with Mobile Robot Networks: Technical Report[EB/OL]. arXiv:1402.0051v2, 2014. [原文入口](https://arxiv.org/abs/1402.0051)。用于说明场强梯度和测量模型假设的适用边界,不引用其结论证明本算法收敛。
在线来源核对日期:2026-09-11。本文的七点构造、覆盖界和具体调度规划来自对题设参数的推导与本项目实验,不宣称一般几何和路径规划思想在文献中首次出现。
+167
View File
@@ -0,0 +1,167 @@
# 问题3/4 强化学习选点(PPO + CUDA)
## 0. 摘要
**2026-09-11审核更新:**本文件下方的训练成绩与BC准确率是第一版历史记录。
复核发现训练/部署特征错位、BC激活函数不一致以及评测只检查completed等问题。
当前修复、Maskable PPO新模型和独立配对验证见
[强化学习适用性审核与改进](强化学习适用性审核与改进-20260911.md)。
新模型未替换原默认检查点;通过`--rl-model`显式加载。
本项目在保持 P0–P5 保守性与可解释证明不变的前提下,为问题3(全向源)与
问题4(定向源)增加了一个可选的**强化学习测量点选择策略**:
- 把“下一检测点去哪”建模为离散动作的局部决策问题;401维特征是压缩观测,
不能认定为完整马尔可夫状态,尤其第四问仍有未知朝向和被省略的历史约束;
- 状态由逐频道信念的几何特征 + 60 个候选检测点的几何特征组成(401 维);
- 动作为 60 选 1(保证接收盘环候选、对称交会候选、定向遮挡环探候选);
- 使用 Stable-Baselines3 的 PPO 训练,MLP 策略与价值网络在 **CUDA GPU**
(本机 RTX 4060 Laptop, CUDA 12.8 / torch 2.11.0+cu128)上更新;
- 训练环境基于自建评测机 `referee`(内存回环,与真实 HTTP 协议同边界);
- 运行闭环仅替换既有选点排序器,覆盖证明、清除证书、定向后备清除全部保留。
历史结果(第一版自建配对消融,5 个种子):Q3 用 12 万步随机初始化 PPO(与 `bayes`
基本持平,+1.18%);Q4 用**专家行为克隆初始化 + PPO 微调**,混合源平均
虚拟时间相对 `directional` 基线降低 **7.16%**(14250.1s → 13230.2s)。
Q3 的 BC 初始化在消融中反而退化,因此保留随机初始化模型;Q4 的 BC 初始化
是当时五场比较中均值最好的版本。官方演练验证见 §5:两次 Q4 演练(3 定向混合源、10 定向全定向
场景)与一次 Q3 演练均全清,且通过离线审计。
## 1. 环境搭建(CUDA)
```powershell
# 安装可选依赖组 rl(PyTorch CUDA 12.8 + stable-baselines3 + gymnasium)
uv sync --group rl --group dev
# 验证 CUDA
uv run python -c "import torch; print(torch.cuda.is_available(), torch.cuda.get_device_name(0))"
```
`pyproject.toml` 中 `[tool.uv.sources] torch = { index = "pytorch-cu128" }`
与 `[[tool.uv.index]]` 指向 PyTorch 官方 CUDA 12.8 轮子;下载约 2.6 GB,
首次同步建议 `$env:UV_HTTP_TIMEOUT='300'`。默认 PyPI 的 Windows torch 轮子
为 `+cpu`,可以使用CPU训练;本轮修订模型即采用CPU,CUDA并非训练前提。
## 2. 第一版局部决策环境
训练环境:`src/mcm26b/rl/env.py::LocalizationEnv`。
| 项 | 定义 |
| --- | --- |
| 初始状态 | 自建模拟器随机布设一个源(Q3 全向;Q4 定向比例可调,训练默认 1.0),机器狗完成一次保证得到 `direction` 的首测 |
| 观测 | 401 维:21 个全局特征 + 20 维频道 one-hot + 60×6 维候选点特征(见 `features.py`) |
| 动作 | `Discrete(60)`:0=候选集 MEC 圆心,1..36=保证接收盘 3 环×12 方向,37..51=对称交会 15 点,52..59=定向遮挡环探 8 点 |
| 转移 | 按动作位置调用 `/measure`,用 `BeliefState` 保守更新候选集;Q4 保证接收盘内 `no_signal` 记为朝向遮挡并进入 `blocked` 序列;证书达标后自动 `/clear`;遮挡梯子用尽后模拟 20m 网格后备清除 |
| 奖励 | `-Δvirtual_time/100`(移动+检测+换频+清除真实耗时)、候选半径收缩加成、首次遮挡证据 +2、重复测点 -0.75、清除成功 +10、失败/截断负奖励 |
约束说明:候选点全部来自既有几何生成器(`planner.candidate_points`、
`planner.safe_disk_radius`、定向环探梯子),RL 不生成新的空间候选;它只做
排序。清除仍须通过几何证书;但“保证收到方向”只适用于全向源且实际点位于
距离保证盘内。第一版的52—59号环探点可能在盘外,第四问的定向遮挡也不受
距离盘保证。v2对第三问加入盘外动作屏蔽,第四问保留探索分支。
## 3. 训练与自建模拟器逻辑利用
```powershell
# 1) 用自建模拟器上的手写专家生成 (观测, 动作) 并行为克隆
uv run python scripts/pretrain_rl_expert.py --problem 3 --episodes 3000
uv run python scripts/pretrain_rl_expert.py --problem 4 --episodes 3000
# 2) 从当前v2 BC初始化继续普通PPO(算法类型需显式匹配)
uv run python scripts/train_rl.py --problem 4 --algorithm ppo --timesteps 40000 \
--init output/rl/q4_ppo_bc_init.zip
# Q3 保留随机初始化长训练版本:
uv run python scripts/train_rl.py --problem 3 --timesteps 120000
```
专家逻辑与自建模拟器同源:Q3 用 worst-posterior 选点器,Q4 用对跖/环探
遮挡梯子;`pretrain_rl_expert.py` 在 `LocalizationEnv` 上记录每个状态的
专家动作,训练与 PPO policy_net/action_net 同构的 MLP,再把权重拷入 PPO
作为初始化。产物位于 `output/rl/bc/` 与 `output/rl/q{3,4}_ppo_bc_init.zip`。
实测:Q3 专家数据 5141 条、BC 验证准确率 38.9%,微调后自建消融退化
(保留 12 万步随机初始化模型);Q4 专家数据 14181 条、BC 验证准确率
65.3%,微调后自建消融从 −3.84% 提升到 **−7.16%**。但审核确认BC与PPO的
激活函数不一致,这些数字不能证明成功迁移了专家策略,也不足以判断全向分支已最优。
输出:`output/rl/q3_ppo.zip`、`output/rl/q4_ppo.zip` 及同名 JSON 报告;
训练脚本先打印 `train_start`(含 device/gpu 字段),再执行 PPO 并在独立
种子场景上确定性评估成功率、平均步数与平均回报。Q4 4 万步微调约 236s;
Q3 12 万步约 625s(RTX 4060 Laptop,2 个 CPU 环境)。
## 4. 运行闭环接入
- Q3:`uv run mcm26b run --robot-id 2026001 --self-test --strategy rl`
`RLBayesLoop` 继承 `BayesLoop`(七点覆盖 + 网格粒子滤波 + 滚动 2-opt),
仅 `_select_point` 改由 PPO 输出候选编号。
- Q4:`uv run mcm26b run --robot-id 2026001 --self-test --problem 4 --q4-strategy rl`
`RLDirectionalLoop` 继承 `DirectionalExtension`(49 点覆盖 + 遮挡梯子 +
20m 有限圆盘后备清除),仅 `_next_probe` 改由 PPO 输出。
- 模型加载时校验观测 401 维 / 动作 60 维,失配直接拒绝运行。
- 默认运行路径不导入 torch/referee;只有显式选择 `rl` 才惰性导入,项目
运行时依赖仍保持 `requests` 一项。
## 5. 消融基准
```powershell
uv run python scripts/benchmark_rl.py --seeds 5
```
同种子同场景配对比较,结果写入 `output/rl/benchmark.json`。以下为第一版历史结果:
| 场景 | 基线 | 平均虚拟时间(基线 / RL) | 相对差 | 全清 |
| --- | --- | --- | --- | --- |
| Q3(10 源全向 ×5 种子,scratch PPO) | `bayes` | 3088.3s / 3124.8s | +1.18% | 5/5 |
| Q4(10 源、3 定向 ×5 种子,BC+PPO) | `directional` | 14250.1s / 13230.2s | −7.16% | 5/5 |
训练局评估:Q3 单源 40 局成功率 100%、平均 3.55 步;Q4 定向单源 40 局
成功率 90%、平均 4.45 步;其余单源训练局未完成,不能由此宣称已兜底成功。随机策略对照:
Q3 平均回报 13.28 vs PPO 17.71;Q4 平均回报 8.72 vs BC+PPO 12.62。
### 5.1 官方模拟器演练验证(仅演练,未触碰正式测试)
使用目录内官方 `jammers-simulator-full` 的**演练测试**(不限次数),通过
官方 HTTP 接口运行同一闭环;官方产物按原名归档并离线审计:
| 问题 | 案例 | 场景真值(演练结束后可见) | 结果 | 虚拟时间 | 请求数 | 审计 |
| --- | --- | --- | --- | --- | --- | --- |
| Q3 | `F38Y-9Q9J-B82V-CB9Z` | 13 全向 | 13/13 全清 | 3538.8s | 124 | ✅ |
| Q4 | `X43X-VMRF-8GRZ-3SYA` | 7 全向 + 3 定向 | 10/10 全清 | 14003.1s | 619 | ✅ |
| Q4(BC) | `BXSA-TFQ3-2DJ6-DHH7` | **10 定向** | 10/10 全清 | 14562.8s | 691 | ✅ |
前两次为随机初始化模型,第三次为 BC 初始化微调模型。两个 Q4 官方案例的
源构成不同(3 定向 vs 10 定向),**虚拟时间不可直接相减比较**;可确认的
是 RL 在官方混合源与全定向源演练中都能完成全部清除,且
`all_timestamps_match_microsecond_recomputation`、官方 `.jlog` 的
SHA-256 校验全部通过。归档目录:`output/rl/official-practice/`。
### 5.2 自建 referee 演练验证
`uv run mcm26b run --local-judge` 走自建 referee 的 `q3_practice` /
`q4_practice` 模块(与官方相同的演练生命周期,真值在结束后可见):
| 问题 | 案例 | 场景 | 结果 | 虚拟时间 | 归档 |
| --- | --- | --- | --- | --- | --- |
| Q3 | `FKUY-K8KJ-RXRL-FMBM` | 10 全向 | 10/10 全清 | 3317.6s | `output/rl/local-practice/q3/20260911-rl-01` |
| Q4 | `3J8T-L9GY-HRGL-LYVZ` | 7 全向 + 3 定向 | 10/10 全清 | 11942.8s | `output/rl/local-practice/q4/20260911-rl-01` |
| Q4(BC) | `VM2G-UVC6-4N7R-TVGW` | 7 全向 + 3 定向 | 10/10 全清 | 12520.8s | `output/rl/local-practice/q4/20260911-rl-bc-01` |
不同案例仍不可直接比较;成对可比的提升证据是 §5 的 5 种子同场景消融。
## 6. 结论与边界
以下是第一版结论;BC权重拷贝缺陷使“同构初始化”的解释不成立,性能判断
应以新审核报告的同场景验证及不确定性为准,而不能由BC分类准确率推出。
- RL 已接入问题3/4 完整闭环,并在自建 referee 演练与官方模拟器演练中
全清验证;这是方法验证,不是对正式成绩的承诺。
- 第一版Q4的BC初始化后均值变化为−3.84% → −7.16%,但初始化存在激活函数
缺陷,样本数也不足以支持“明确收益”的归因;当前判断见新审核报告。
- 训练环境使用自建场景假设(源均匀分布、逐点固定误差场、定向源朝向随机);
官方隐藏分布未必相同。正式测试仍应默认使用经过演练验证的手写策略;
`--strategy rl` / `--q4-strategy rl` 适合消融、方法对比与后续调参。
- 官方演练仅使用“演练测试”按钮与开放接口,从未点击“正式测试”;官方
`.jlog/.result.json` 保持原样,未解密。
- 保守性不变式:RL 只改变选点排序,不改变覆盖账本、候选集更新、清除证书
与后备清除的数学证明;所有策略均以 `/exit` 与完整日志收尾。
+117
View File
@@ -0,0 +1,117 @@
# HRL P0+P1 执行报告(本地自建评测机)
日期:2026-09-11(本机工作区)
范围:按用户确认,仅完成 P0 正确性回归 + P1 最小可训练/可评测闭环;
P2 的 BC、完整课程长训、Optuna、正式消融与正式测试表留待下一阶段。
## 1. 本轮代码交付
| 文件 | 内容 |
| --- | --- |
| `scripts/benchmark_hrl.py` | 同一 manifest 真值场景复用;`hrl`/`hrl_heuristic`/`bayes`/`directional` 配对评测;写 `results.json`、`aggregate.json/csv`、每局 `summary.json` 与 `session.jsonl` |
| `scripts/train_hrl_high.py` | 高层 MaskablePPO 训练;`--problem/--stage/--envs/--vec-env/--device/--config-profile/--reward-profile/--model/--resume/--init/--eval-manifest`;模型元数据固化 feature/action/reward 版本;墙钟硬上限 |
| `src/mcm26b/hrl/constants.py` | 新增 `ACTION_LAYOUT_VERSION=1`、`REWARD_VERSION=1` |
| `src/mcm26b/hrl/policy.py` | 高层模型严格校验元数据;低层模型保持独立基础校验 |
| `src/mcm26b/cli.py` | 新增 `--strategy hrl`、`--q4-strategy hrl`、`--hrl-model`、`--hrl-low-model`、`--hrl-device`、`--trace-low` |
| `src/mcm26b/hrl/hierarchical_env.py` | 新增 `HRLReward.time_only()`:P0.4 建议的纯时间目标,去掉逐源清除/发现/熵减塑形 |
| `project/` | 严格目录树薄兼容层,转发真实实现,不复制算法 |
| `tests/test_hrl_p1.py` | manifest 往返、真值隔离、纯时间奖励、project re-export 回归 |
## 2. 全量回归
```bash
.venv/bin/python -m pytest -q --tb=short
# 275 passed in 124.57s
```
## 3. P1 smoke 训练(本机 Apple MPS)
```bash
.venv/bin/python scripts/train_hrl_high.py \
--problem 3 --stage 1 --timesteps 2048 --seed 320001 \
--envs 1 --vec-env dummy --device mps \
--config-profile production --reward-profile time \
--model output/hrl/p1-smoke-time/q3.zip \
--eval-episodes 2 --rollout-steps 128 --batch-size 128 \
--n-epochs 3 --max-wall-s 800 --verbose 0
```
结果:
- `actual_timesteps=2048`,墙钟 `260.069 s`;
- 保存后确定性评估 2/2 成功,`accounted=20`,无异常;
- 模型元数据含 `feature_version=4`、`action_layout_version=1`、
`reward_version=1`、`reward_profile=time`、`model_sha256`。
模型重载已由以下两条路径验证;`--resume` 的 `--timesteps` 是增量步数
(SB3 `reset_num_timesteps=False` 语义),元数据写 `timesteps_semantics="additional"`:
```bash
.venv/bin/python -m mcm26b.cli run \
--robot-id 2026001 --self-test --strategy hrl \
--hrl-model output/hrl/p1-smoke-time/q3.zip --hrl-device mps \
--sources 3 --seed 1 --summary-steps 5
python scripts/benchmark_hrl.py --variants hrl \
--hrl-model output/hrl/p1-smoke-time/q3.zip --hrl-device mps \
--problem 3 --scenes 1 --sources 3 --output output/hrl/p1-bench-hrl
```
## 4. 多轮本地配对测试
### Q3:3 个独立场景 × 3 个变体
```bash
python scripts/benchmark_hrl.py --variants hrl,hrl_heuristic,bayes \
--hrl-model output/hrl/p1-smoke-time/q3.zip --hrl-device mps \
--problem 3 --scenes 3 --sources 3 --start-seed 91000 \
--output output/hrl/p1-multiround-q3
```
| variant | episodes | valid_rate | cleared_mean | virtual_time_mean_s | virtual_time_std_s | program_run_time_mean_s |
| --- | ---: | ---: | ---: | ---: | ---: | ---: |
| bayes | 3 | 1.0 | 3.0 | 2518.371 | 74.183 | 0.748 |
| hrl_heuristic | 3 | 1.0 | 3.0 | 2518.371 | 74.183 | 0.883 |
| hrl | 3 | 1.0 | 3.0 | 3731.380 | 120.536 | 1.853 |
结论:P1 模型只在 stage 1 单源课程上训练 2048 步,可直接重载并在 3 源场景保持 3/3 有效全清;
但用时明显未优于 bayes/heuristic,符合本轮不做正式性能声明的边界。
### Q4:3 个独立混合源场景 × 2 个变体
```bash
python scripts/benchmark_hrl.py --variants hrl_heuristic,directional \
--problem 4 --scenes 3 --sources 3 --directional 1 --start-seed 92000 \
--output output/hrl/p1-multiround-q4 --force
```
| variant | episodes | valid_rate | cleared_mean | virtual_time_mean_s | virtual_time_std_s | program_run_time_mean_s |
| --- | ---: | ---: | ---: | ---: | ---: | ---: |
| directional | 3 | 1.0 | 3.0 | 13442.997 | 263.902 | 1.063 |
| hrl_heuristic | 3 | 1.0 | 3.0 | 12292.198 | 344.827 | 6.284 |
说明:Q4 高层 PPO 尚未训练;本轮只验证 Q4 的 `hrl_heuristic` 与 `directional` 闭环、真值隔离与计时审计。
## 5. P0 状态
- `--resume` 重载:16 步起步模型续训 32 增量步后 `actual_timesteps=48`,
校验 `feature_version/action_layout_version/reward_version` 后保存;
- 槽位唯一性:`test_hrl.py` 覆盖 channel 17–20 与 1–4 同时 clear/locate 不丢;
- 清除事件一次且仅一次:fallback 成功写 `_step_clears` 已有回归;
- 候选快照:`test_opportunity_measure_precedes_candidate_snapshot` 已覆盖;
- 奖励对齐:新增 `HRLReward.time_only()`,训练脚本默认 `--reward-profile time`;
- 终止/安全网:现有 `HierarchicalEnv` 回合测试继续通过;
- 训练/部署配置:`--config-profile production` 使用生产配置,`coarse-pretrain`
显式映射到 fast 评分配置并写入模型元数据;
- 依赖守卫:全量测试 275 项在 dev+rl 环境通过。
## 6. 未完成 / P2 待办
- `scripts/pretrain_hrl_expert.py` 已建可 `--help` 的接口占位,专家轨迹 + BC 功能待实现;
- `scripts/ablate_hrl.py` 已建接口占位,无信息增益/无掩码/无课程/无粒子统计消融待实现;
- `scripts/tune_hrl.py` 已建接口占位,Optuna TPE 搜索待实现;
- Q3→Q4 迁移训练与配对实验;
- 完整 10–16 源课程训练、多个训练种子、冻结测试清单;
- 官方三次正式测试表:必须有真实官方案例码与导出日志后才能填写;
本地 `referee` 结果只标 `local_self_test`。
+267
View File
@@ -0,0 +1,267 @@
# HRL 续作执行说明:交给 Sol 的工程任务书
适用日期:2026-09-11。工作目录:本仓库根目录。先检查当前代码与本轮测试结果,再按 P0 → P1 → P2 → P3 顺序执行。
目标是交付可运行、可审计的整场调度学习系统;训练收益由完整场景验证决定,不把训练脚本存在或局部奖励上升视为完成。
本说明更新了原粘贴 Prompt 与 `.zcode/plans/plan-sess_cf390556-4ef3-4ce6-b91e-782aee1718f9.md` 中不符合现有协议或证据的部分。
## 1. 现在有什么,缺什么
| 组件 | 当前状态 | Sol 的操作 |
| --- | --- | --- |
| `protocol/`、`referee/`、`harness.py` | 已有客户端、独立评测机、内存回环与计时审计 | 复用,不另建模拟器客户端 |
| `belief/`、`particles.py`、`planner/` | 已有保守候选、网格幸存集、覆盖证明、证书清除与后备清除 | 保持证明前提;改调度时复用 |
| `rl/`、`train_rl.py`、现有 RL 检查点 | 已有单源选点 PPO/MaskablePPO 与完整对照 | 作为局部策略和对照,勿命名为端到端 PPO |
| `hrl/constants.py`、`scheduler.py`、`planner/hrl_loop.py` | 已有高层候选、特征、执行与闭环骨架;存在本轮需修复的 P0 项 | 先完成第 4 节门槛 |
| `hrl/hierarchical_env.py`、`curriculum.py` | 已有训练环境和课程;奖励、终止、训练配置仍需审核 | 在长训前修正,覆盖独立环境/部署一致性 |
| `hrl/low_level_env.py`、`executor.py`、`train_hrl_low.py` | 有八方向到达实验;训练入口尚有掩码等缺陷 | 作为可选离线实验,不能卡住主线 |
| `tests/test_hrl.py` | 有接口和启发式 episode 测试 | 增补行为验证,不能只检验公式与实现一致 |
| `train_hrl_high.py`、`pretrain_hrl_expert.py` | 尚未实现 | P1 编写最小可训练闭环,P2 再加入 BC |
| `benchmark_hrl.py`、`ablate_hrl.py`、`tune_hrl.py` | 尚未实现 | 先评测,再训练,再做消融,最后受限调参 |
| CLI 的 `hrl` 策略和 `--hrl-model` | 当前入口尚未接入 | P1 接入并检验 `--help` 与离线闭环 |
| `output/hrl/` 的高层模型、曲线、评测表 | 本次审阅未发现这些产物 | 不声称已有 HRL 模型或训练成绩 |
现有证据:修订单步 RL 的 Q3 独立验证均值比 `bayes` 低 0.69%,区间跨 0;Q4 比 `directional` 低 14.38%,比旧 PPO 低 2.26%,后一比较区间跨 0。
这些来自固定模型、本地 18 场/题的验证;新 Q4 的后备清除次数更多,边界压力性能有退化。详情见 `docs/强化学习适用性审核与改进-20260911.md`。
没有证据证明 HRL 已优于上述基线,没有证据支持“Q4 迁移节约 80% 训练成本”或“全部合法场景小于 4000 秒”。
## 2. 必须纠正的原 Prompt 假设
1. `/measure` 与 `/clear` 提交终点,移动按两次实际动作位置间的直线距离除以 5 计费;没有朝向、转弯或八方向移动协议。
2. 八方向策略仅在本地推演再强制补上终点,无法影响模拟器实际路径;不能把这段推演称为比赛收益来源。
3. `/clear` 不切换接收机频道,目标频道与当前接收机频道应分别记录;`near` 是测量结果,清除仍需独立请求。
4. 接收半径逐源未知,动作掩码只能使用观测与保守界,不能读真实半径、源位置、类型或朝向。
5. Q3 的 `no_signal` 只能按已证明的保守半径规则删格;Q4 的 `no_signal` 可能由方向遮挡造成,不能直接删空间候选。
6. Q4 联合位置/半径/朝向粒子是后续研究选项;普通二维粒子加“角度门控”并不会自动得到正确联合后验。
7. 固定地点误差固定;同地重测不是新独立样本。保留读数舍入裕量,不收紧真实的 ±1° 误差界来追求表面收敛。
8. 源数最多 16 不等于只有 16 个频道;所有按频道编号索引的动作必须覆盖 1–20。
9. 连续无信号次数不能作为全场结束条件;覆盖参数不能随意调到破坏已证明的发现覆盖。
10. “无信息增益”“无动作掩码”“无粒子滤波”必须精确定义;不能通过取消清除证书或真源保留约束制造消融。
11. 原 Prompt 的 6 个状态与实现里的状态枚举不一致;以 `ChannelStatus` 为准,额外 TRACKING/LOCATED 只能定义为派生特征。
12. 协议文档 §6 仍写持续搜索默认开启;当前 `config.py` 为 `persistent_search=False`,CLI 用 `--persistent` 显式开启。先同步文档,不恢复过时默认值。
## 3. 推荐架构与工程边界
采用“高层学习调度 + 低层局部选点 + 协议执行”的分层架构。高层学习先处理哪个频道、继续覆盖还是定位、何时使用后备清除。
低层默认调用成熟几何选点;可切换现有 `PointSelectionPolicy` 学习局部检测点,形成第二个学习层。直线移动与设备动作继续由协议客户端执行。
Q3 首版低层用 `bayes` 几何选点;Q4 先保留 `directional` 的遮挡处理,待接线一致性通过后对比旧/新局部 PPO。
```text
SimulatorClient.state + belief + particles + coverage ledger
↓ 同一份候选快照和动作掩码
高层 MaskablePPO:选择频道/覆盖站/清除任务
↓
局部选点:geometry | 已有 v2 PointSelectionPolicy
↓
证书/预算/覆盖约束 + 实际 measure/clear 宏动作
↓
accepted 请求与响应 → 时间、信念、清除与覆盖账本
```
八方向 PPO 保留为 `low_level_env.py` 的离线附录实验;生产默认无需加载。若论文要求字面双 PPO,用高层调度 PPO + 局部选点 PPO 对应真正可影响成绩的两个层次。
现有高层一个动作可以包含整站逐频道扫描或多次后备清除,因此是变长宏动作。记录每个宏动作的实际持续时间、请求数和中断原因。
先采用 `gamma=1` 与实际时间成本,避免把“每宏动作折扣一次”误当成“每秒折扣一次”;只有明确改变目标才引入按持续时间折扣。
第一版保留 120 槽位 MLP,不同时重写注意力网络或连续动作;需要多候选/频道时另升动作版本并独立比较。
建议新增的共享接口如下;这些是待实现契约,避免训练和 CLI 分别拼接策略。
```python
@dataclass(frozen=True)
class HRLPolicyConfig:
local_selector: str = "geometry" # geometry | rl
local_model_path: str | None = None
def make_hrl_loop(client, config, *, problem: int,
high_policy=None, policy_config=HRLPolicyConfig()) -> HRLLoop: ...
def validate_episode(summary: dict, events: list[dict],
truth_channels: set[int]) -> dict: ... # 仅评测模块
def run_episode(scene: dict, variant: dict, *, output_dir: Path) -> dict: ...
```
## 4. P0:修复正确性,未过门槛不得长训
以下表中的状态以本轮提交和测试报告为准;Sol 接手时先查看 diff,已完成的项只做回归,不重复实现。
| 项目 | 精确入口与问题 | 目标与验收 |
| --- | --- | --- |
| P0.1 槽位冲突 | `constants.py`;`scheduler.build_candidates/heuristic_action`:clear 频道 17–20 与 locate 频道 1–4 重叠 | 本轮优先修复;每类按 20 频道分配,构造同时就绪冲突场景验证任务均保留 |
| P0.2 清除事件遗漏 | `scheduler._hrl_execute_fallback` 成功未写 `_step_clears` | 本轮优先修复;证书/后备/宏动作内部清除均由相同成功事件口径计数,一次且仅一次 |
| P0.3 候选过期 | `HRLLoop._coverage_phase` 在构建候选后 `_measure_at_stop` 可改变信念 | 本轮优先修复;选动作前候选/特征/掩码属于同一个状态快照 |
| P0.4 奖励对齐 | `HierarchicalEnv.step/_reward_from/HRLReward` | 改默认奖励,成功/异常/安全网/截断均计入已经发生的真实时间 |
| P0.5 回合终止 | `HierarchicalEnv.step/reset/close` | 实际完成、失败终止与采样截断分开;收尾不丢 /exit,不把零观测用于有效截断 bootstrap |
| P0.6 训练部署配置 | `curriculum.training_config` 默认网格 29.3m,部署 14.65m | 首个可比较模型使用生产几何配置;粗网格只能显式快速预训练,后接完整保真微调与评测 |
| P0.7 无进展循环 | `scheduler` 的 locate/probe/fallback 掩码及预算检查 | 去重、已清除/无效证书、后备预算不足在动作前处理;合法动作不能无限零成本自循环 |
| P0.8 测试与依赖 | `tests/test_hrl.py` 导入守卫、`policy.py/scheduler.py` 顶层 numpy | 不安装 RL 组时基础 CLI 可导入;完整 RL 环境运行测试时不得意外全部 skip |
建议槽位布局为 clear 0–19、locate 20–39、station 40–88、fallback 89–108、保留 109–119;所有槽位互斥且小于 120。
槽位含义改变必须升 `FEATURE_VERSION`,即使仍是 1248 维/120 动作,也必须拒绝旧语义检查点。
模型元数据应要求 `level/problem/algorithm/feature_version/action_layout_version/reward_version`,并记录模型 SHA、配置、依赖版本、代码版本。
缺字段不能默认为兼容;Q3 权重初始化 Q4 必须走显式训练迁移参数,部署加载仍严格检查题号。
默认奖励先用:`r = -实际本步虚拟时间增量 / 100`;证明完成时加固定完成奖,未完成的真实终止加失败罚。
失败惩罚按冻结的 episode 最大成本界设置并记录,不随意用不足以覆盖数千秒的 `-100`。
相同全清场景里逐源常数奖可能只是常数偏移,但不同终止路径、事件漏记和失败场景会改变激励;故首版去掉新发现/熵减/每源清除附加奖。
如需塑形,另做势函数差 `gamma * Phi(next_state) - Phi(state)`,真正终止态统一势函数值,并检验总和望远镜消去。
如果优化题目平均时间,用整场时间作为训练目标时明确这是所选代理;不同源数的跨场景均值与 `T/N` 均值不等价,不能暗称完全一致。
人工步数上限若仅为采样截断,返回可用的终末观测并允许 bootstrap;若定义为任务失败期限,应作为真实终止计罚。按该语义设计测试。
P0 验收至少覆盖以下行为,不只验证 reward 按代码相加:
- 20 个频道的各类槽位唯一;频道 17–20 与 1–4 同时有 clear/locate 不丢任务。
- 同一观测轨迹分别构建独立训练环境与部署闭环,比较候选、mask、特征、下一实际请求及状态。
- 固定动作序列跨成功、失败、后备与安全网路径:reward 的时间分项总和等于 accepted 日志复算时间。
- Q4 保证盘内/盘外无信号不误删真源;正读数重置遮挡序列;无效证书不能执行清除。
- 10/16 源、频道含 20、边界源与预算中断均检查真实源集合、缺席集合、正常退出、异常与计时。
- 评测端真值仅进 `validate_episode`,策略构建、候选、mask、观测与模型选择均不得读取真值。
## 5. P1:最小完整闭环,先评测再短训
先实现 `scripts/benchmark_hrl.py` 与共享审计函数,使 `HRLLoop(high_policy=None)` 可以与原 `bayes/directional` 同场景对比。
这个启发式 HRL 骨架是重要对照:若差距来自任务候选、机会测量、Q4 保底或预算语义变化,先修或如实量化,不归因给神经网络。
每次运行将 scene、误差种子、配置、summary、完整 JSONL/accepted 事件、时间复算和模型 SHA 写入新目录;目录已存在则拒绝覆盖。
单场异常也写结果;聚合存在任一无效局时 `all_valid=false` 且脚本非零退出,不从平均值表中静默删除失败场景。
随后实现 `scripts/train_hrl_high.py`,第一版提供以下参数并通过 `--help` 验证:
```text
--problem {3,4} --stage {1,2,3,4} --timesteps INT --seed INT
--envs INT --vec-env {dummy,subproc} --device {cpu,cuda,mps,auto}
--config-profile {production,coarse-pretrain}
--model PATH --resume PATH --eval-manifest PATH --verbose INT
--local-selector {geometry,rl} --local-model PATH
```
阶段 1 即保持 20 频道与固定观测维度,只改变布景复杂度;早期课程可减少源数,但完整训练必须覆盖 10/13/16 源与实际 ±1° 固定地点误差。
Q4 完整课程同时覆盖少量/半数/大量定向源及近边界向外朝向;Q3→Q4 迁移与 Q4 随机初始化作为配对实验。
禁止让策略知道课程场景真实源数或隐藏的占用频道列表;初始位置保持官方 `(0,0)`,除非任务被明确标为局部交接课程。
先测 1/2/4 个环境的采样吞吐、内存和每次决策耗时,再选择 Dummy/Subproc;小 MLP 不假定 GPU 更快。
Subproc 环境类内部提供 `action_masks()`;Windows/macOS 使用入口守卫及可序列化工厂。
随机种子覆盖 Python/NumPy/Torch、PPO、每个环境、场景误差;每个 worker 使用独立 Monitor 文件。
保存实际 `model.num_timesteps` 与请求步数,恢复时说明增量还是累计;续训加载正确算法/特征版本且不覆盖原模型。
使用掩码评估接口,如 `MaskableEvalCallback`;同时在确定性推理中传入同源 mask。
CLI 加入 `--strategy hrl`、`--q4-strategy hrl`、`--hrl-model` 与局部策略选项,经 `make_hrl_loop` 统一构建;默认策略保持现状。
显式指定缺失模型应报可操作错误,不能把启发式降级结果记成 HRL 模型成绩;启发式骨架单独命名 `hrl_heuristic`。
P1 门槛:启发式骨架全场审计通过;512–2048 步训练冒烟可保存、重载、确定性推理;CLI 内存回环至少 Q3/Q4 各一场通过。
短训先用单个种子验证流程,不用这次模型的成功率作论文结论;完成后记录吞吐再定时间预算,不预设 1–2 小时足够。
## 6. P2:完整保真训练、独立评测与消融
实现 `pretrain_hrl_expert.py`:运行 `hrl_heuristic`,保存 `(episode_id, state, candidates, mask, action)`;按 episode 划分训练/验证。
BC 与 PPO 的网络层、激活、归一化、mask 处理保持一致;复制后在真实样本上逐位比较 logits,critic 单独初始化。
记录专家动作无法映射到候选的比率与原因;若大量回退到最近候选,先改专家映射,不用假专家标签继续训练。
至少比较 BC+PPO 与随机初始化 PPO;BC 准确率单独报告,不能代替独立闭环用时与有效全清率。
冻结数据集分工:
| 集合 | 用途与约束 |
| --- | --- |
| 已有 700–702 开发场景 | 查实现错误、探索配置与性能分解 |
| 已有 91000–91005 场景 | 历史回归;结果已经被审阅,不再宣称是本次全新盲测 |
| 新开发/选择清单 | 固定 JSON manifest,记录 seed、源数、定向组成、error_seed;用于训练选择和调参 |
| 新冻结最终测试清单 | 与训练、BC、调参及选择清单不重叠;模型冻结后运行,失败后修改则新开一轮测试 |
| 压力场景 | 圆周边界、1000m 半径、向外/切向定向、高频道、固定误差与预算边界;单独汇总 |
同场景每个变体用相同位置、频道、半径、朝向和地点误差函数。每个模型至少跑 30 个完整场景,再按预算扩大;“30”是工程起点,不是统计充分性证明。
最终用于稳定收益结论的主方法至少 5 个独立训练种子;场景种子与训练种子分开报告,不能用 5 个场景替代 5 次训练。
Q3、Q4 分开统计,并按源数/定向占比分层;所有基线与候选使用相同停止、预算和计时口径。
| 指标 | 固定定义 |
| --- | --- |
| 有效全清 | 成功清除频道集合等于评测真值;缺席集合与真值不交;completed、正常 exit、无异常、计时一致 |
| `virtual_time_s` | 实际退出前的完整虚拟时间;主竞赛口径显式 `persistent_search=False` |
| `avg_locate_clear_time_s` | 每场 `T / 实际清除数`;有效全清主表再做逐场平均,另报源数分组 |
| `accounted_virtual_time_s` | 首次完成证明的时间;与最终时间并列,不悄悄用它替换正式计分 |
| 运行时间 | 每场实际墙钟、模型推理耗时分位数;本地内存回环时间不冒充官方程序运行时间 |
| 成本分解 | 移动秒、测量秒、换频秒、成功/失败 clear 秒、后备尝试、重复测量、总请求数 |
| 尾部与失败 | 中位数/P90/P95/最大值、有效全清率、失败明细;小样本尾分位注明估计局限 |
平均 `T/N` 不等于 `平均 T / 平均 N`;若另用总体总时间/总源数,明确标为按源数加权口径。
配对效应以同场景时间差为基础,报告均值相对变化、胜出场数、95% 配对 bootstrap 区间;相同场景家族共享种子时按家族重采样。
多训练种子结果报告种子间分散程度;必要时对训练种子和场景做分层重采样,不能用单一场景区间代表训练稳定性。
最小比较矩阵:`bayes/directional`、旧局部 PPO、修订局部 MaskablePPO、`hrl_heuristic`、高层 PPO+几何、后续高层 PPO+局部 PPO。
此矩阵先回答“改调度是否有用”和“增加局部学习是否有用”;既有单步 RL 不能命名为无几何的端到端基线。
若仍要端到端 PPO,必须单独定义输入、实际动作、安全执行边界并训练,标记预算;它不是 P1 完成条件。
每项消融使用相同训练步数/场景预算/种子和评测集,单次只改变一个因素:
- 调度:学习高层 vs `hrl_heuristic`,保持候选生成和局部选点一致。
- 局部策略:同一高层与执行接口下,geometry vs 现有局部 PPO;报告是冻结替换还是联合重训。
- 塑形:纯时间目标 vs 经过恒等式核验的势函数塑形;无信息增益奖励就是系数为 0。
- 课程:相同总交互步数的课程训练 vs 从完整场景开始训练;Q4 迁移另列,不预设节约百分比。
- 掩码:只取消重复/低效候选等策略性屏蔽;协议合法性、证书和预算硬守卫始终保留,标明实际取消范围。
- 粒子特征:保留几何守恒内核,先将粒子统计输入和评分消融;若真的移除粒子模块,另实现保守几何替代并重新验证。
- 八方向:只报告离线到达率/路径效率/推演耗时,以及实际协议终点相同时成绩相同的事实。
P2 推广门槛:所有最终与压力场景有效,Q4 相对最强已冻结基线的时间差区间上界小于 0;尾部容忍度先写入 manifest。
建议初始尾部门槛为 P95 不退化超过 5%,这是可调整的工程标准,不是题设要求;任何调整都在重跑最终测试前完成。
Q3 若未显示稳定收益,保留 `bayes` 默认并交付负结果。仅比手写基线快、却未优于现有 PPO,不能宣布替换默认模型。
## 7. P3:受限调参、论文与官方验证
只有 P2 主方法具备竞争力才实现并运行 `tune_hrl.py`。先搜索学习率、熵系数、网络宽度、GAE、合法候选排序参数。
不搜索真实误差上限、光学清除半径、无信号停止次数或破坏覆盖证明的网格间距;确定性网格没有粒子重采样阈值可调。
Optuna 只用开发/选择清单,失败配置先按有效性淘汰;预算与失败/剪枝逻辑写入结果。最终模型重新用固定配置多种子训练。
文档、流程图与论文表明确:学习负责哪些决策、证明由哪些模块提供、本地生成分布未知是否对应官方分布。
训练曲线、实际步数、配置、全部对照与失败明细均归档;不要只挑最好种子或去掉不利的边界场景。
官方三次测试需要对应演练已经启动、真实案例码与官方导出日志;本地 referee 的三场不能填写成“三次正式测试”。
不覆盖旧官方归档;官方结果与本地结果分表,不对不同官方案例直接计算改进百分比。
## 8. 命令:已经存在与待实现的严格区分
以下入口当前已经存在;依赖需先安装,命令失败要修复或如实报告,不能跳过错误声称通过:
```bash
uv sync --group dev --group rl
uv run python -m pytest -q --tb=short
uv run python -m pytest -q tests/test_hrl.py --tb=short
uv run mcm26b run --help
uv run mcm26b run --robot-id 2026001 --self-test --strategy bayes
uv run mcm26b run --robot-id 2026001 --self-test --problem 4 --q4-strategy directional
uv run python scripts/train_rl.py --help
uv run python scripts/train_hrl_low.py --help
```
`train_hrl_low.py` 目前仅 `--help` 可用于检查入口,不把完整训练列为已可用:
其 `MaskablePPO.learn()` 需要环境掩码,当前 `LowLevelEnv` 没有 `action_masks()`;改普通 PPO 或提供全 True 掩码后再试。
还需修 BC 按 episode 划分、统一随机种子、每 worker 独立 Monitor 文件、评估 seed 生效、实际路径长度与成功率分开统计。
低层到达失败不能靠 executor 最后强制补终点伪装成策略到达;环境裁剪后的实际移动长度也不能简单记为 `steps*100m`。
下面是 P1/P2 实现后必须支持并实测的命令契约,当前尚不能直接运行:
```bash
# P1:先验证不加载模型的共享调度闭环
uv run python scripts/benchmark_hrl.py --variants hrl_heuristic,bayes,directional --manifest output/hrl/manifests/dev.json --output output/hrl/p1-heuristic
# P1:完整保真冒烟;模型目录必须是新目录
uv run python scripts/train_hrl_high.py --problem 3 --stage 1 --timesteps 2048 --seed 320001 --envs 1 --vec-env dummy --device cpu --config-profile production --model output/hrl/p1-smoke/q3.zip --verbose 0
# P1:CLI 明确加载高层模型
uv run mcm26b run --robot-id 2026001 --self-test --strategy hrl --hrl-model output/hrl/p1-smoke/q3.zip
# P2:正式阶段训练;步数先依据采样吞吐与学习曲线再调整
uv run python scripts/train_hrl_high.py --problem 4 --stage 4 --timesteps 60000 --seed 420001 --envs 2 --vec-env dummy --device cpu --config-profile production --model output/hrl/p2/q4-seed420001.zip --eval-manifest output/hrl/manifests/select.json --verbose 0
# P2:冻结模型完整对照;variants 的模型位置来自同目录的配置清单
uv run python scripts/benchmark_hrl.py --variants hrl,hrl_heuristic,legacy,revised,baseline --manifest output/hrl/manifests/final.json --output output/hrl/p2-final
```
命令使用的 manifests 也要由脚本生成、校验并归档;尚不存在的模型不能当作初始化输入。
添加 `--full` 一键课程入口时,它应展开并记录每阶段命令/配置/检查点链,失败即停,可继续,不是忽略错误的 shell 包装。
参考现有依赖配置:基础运行依赖仅 requests;RL 为可选组。固定环境版本并记录硬件,勿假定当前执行机就是旧报告中的 RTX 4060。
## 9. 可以直接粘贴给 Sol 的续作 Prompt
```text
继续当前仓库的 HRL 工作。先读 docs/hrl-sol-execution.md、最新测试结果与 git diff,保留现有修改。
已有客户端/referee/几何/PPO 都要复用;不要重写骨架,不把缺失脚本或 CLI 说成已完成。
先完成 P0 尚未关闭的奖励、终止、完整保真配置、掩码和依赖问题;本轮已修的槽位/清除事件/候选过期只回归。
采用高层 MaskablePPO 调度 + 几何/已有局部 PPO 选点 + 协议直线执行;八方向只作为可选离线实验。
按 P1 先实现 benchmark_hrl 与真值隔离审计,再实现 train_hrl_high、模型元数据和 CLI,跑可重载的短训冒烟。
P0/P1 没通过就修实现,不用加训练步数掩盖问题。通过后再开展 BC、完整保真课程、多训练种子、冻结场景配对评测。
保持 Q3 bayes/Q4 现有默认策略;完整实验胜出前不覆盖默认模型。主指标包含有效全清、T、逐场 T/N、请求分解和尾部。
每阶段交付具体文件、实际执行命令、测试结果、模型/日志路径与未完成项;不预设 Q4 80% 节约或 4000 秒保证。
官方测试只有真实演练开始并获得案例码/导出日志才记正式结果,本地实验单独标注。
```
接口参考:[MaskablePPO 官方文档](https://sb3-contrib.readthedocs.io/en/master/modules/ppo_mask.html),[Gymnasium 终止与时间限制](https://gymnasium.farama.org/tutorials/gymnasium_basics/handling_time_limits/)。
Binary file not shown.
+1 -1
View File
@@ -49,7 +49,7 @@
{\heiti\zihao{4} 摘\quad 要} {\heiti\zihao{4} 摘\quad 要}
\end{center} \end{center}
针对无线电干扰源的快速自动定位与清除问题,本文以测向观测的三值反馈 无线电干扰源的测向与规划问题在生产和军事上的应用十分广泛。
\texttt{no\_signal}、\texttt{near}、\texttt{direction} 为唯一信息来源,建立 \texttt{no\_signal}、\texttt{near}、\texttt{direction} 为唯一信息来源,建立
\textbf{有界误差交会定位—主动选点—覆盖证明搜索—动作耗时调度}的统一框架:四个问题 \textbf{有界误差交会定位—主动选点—覆盖证明搜索—动作耗时调度}的统一框架:四个问题
复用同一几何内核,以覆盖证明与严格停止条件保证“清除所有干扰源”的硬约束,再优化 复用同一几何内核,以覆盖证明与严格停止条件保证“清除所有干扰源”的硬约束,再优化
+261
View File
@@ -0,0 +1,261 @@
# 训练前端 Windows 兼容性与「暂停」审计(结论 + 修复方案)
> 审计对象:`src/mcm26b/training_ui/`、`src/mcm26b/process_control.py`、
> `scripts/train_hrl_high.py`。
> 触发问题:「无法暂停」「频繁出现 os 无 killpg 等」。
> 结论一句话:**这两个现象同源——训练前端的停止/暂停路径是纯 POSIX 设计;
> Windows 上 `os.killpg` 根本不存在,异常又没被兜住,所以点了停止等于没点。**
---
## 0. 结论摘要
| # | 现象 | 根因 | 严重度 |
| --- | --- | --- | --- |
| 1 | 点「停止训练」返回 500 / 无效果,训练照跑,于是「无法暂停」 | `TrainingJob.stop()` 直接调用 `os.killpg(os.getpgid(...))`;这两个符号**只存在于 POSIX**,Windows 上抛 `AttributeError`,而旧 `except` 只接 `ProcessLookupError/PermissionError` | 阻断 |
| 2 | 控制台/日志反复出现 `module 'os' has no attribute 'killpg'` | 同上:错误最终落到 `_UIHandler.do_POST` 的兜底 `except Exception`,返回 500 并带出 `AttributeError` 文本;每点一次复现一次 | 阻断 |
| 3 | 即使把 killpg 换成 `terminate()`,停止后模型/元数据仍然丢失(Windows 上) | Windows 的 `os.kill(pid, SIGTERM)` 语义是 `TerminateProcess` **硬杀**,训练脚本里"收到信号后在 step 边界保存"的处理器不会执行 | 高 |
| 4 | 界面根本没有「暂停」能力(POSIX 也没有) | 原设计只有 start/stop;且 `SIGSTOP/SIGCONT` 在 Windows 上不存在,**原生暂停在 Windows 上无法实现**,必须改成协作式 | 高 |
| 5 | 子进程进程组隔离在 Windows 上失效 | `Popen(start_new_session=True)` 在 Windows 被**静默忽略**(CPython 的 Windows `_execute_child` 形参名就是 `unused_start_new_session`) | 中 |
| 6 | subproc worker 在 Windows 下可能抢在主进程收尾前退出 | worker 只忽略 `SIGTERM/SIGINT`,没处理 Windows 的 `SIGBREAK`(`CTRL_BREAK_EVENT` 的落点) | 中 |
**修复原则**:把"暂停/继续/停止"从**信号语义**改成**控制文件语义**。
控制文件在 POSIX 与 Windows 上都只是普通文件 IO,行为完全一致;信号与强杀退居
兜底。这样 Windows 上暂停、继续、优雅停止、落盘保证全部成立。
---
## 1. 失败链复现(旧代码)
```
用户点「停止训练」
└─ POST /api/train/stop
└─ TrainingUI.stop() → TrainingJob.stop()
└─ os.killpg(os.getpgid(process.pid), signal.SIGTERM)
├─ POSIX :成功,进程组收到 SIGTERM,脚本优雅收尾
└─ Windows:AttributeError: module 'os' has no attribute 'killpg'
↑ 只 except (ProcessLookupError, PermissionError),接不住
└─ _UIHandler.do_POST 兜底 except Exception → HTTP 500
└─ 训练子进程完全没收到任何指令,继续跑 → 「无法暂停 / 停止没反应」
```
关键代码(修复前,`src/mcm26b/training_ui/server.py`):
```python
def stop(self) -> bool:
with self._lock:
process = self.process
if process is None or process.poll() is not None:
return False
try:
os.killpg(os.getpgid(process.pid), signal.SIGTERM)
except (ProcessLookupError, PermissionError):
process.terminate()
self.status = "stopping"
return True
```
---
## 2. 根因逐条(含证据)
### 2.1 `os.killpg` / `os.getpgid` 是 POSIX-only
CPython 只在 `posix` 构建里注册这两个函数;Windows 的 `os` 模块没有它们。
访问会得到 `AttributeError`(不是 `OSError` 子类),所以旧 `except` 接不住。
### 2.2 `start_new_session=True` 在 Windows 被静默忽略
`subprocess.Popen` 接受该参数,但 Windows 的 `_execute_child` 形参名是
`unused_start_new_session`,也就是**根本不读**。要隔离进程组,Windows 必须传
`creationflags=CREATE_NEW_PROCESS_GROUP`。
### 2.3 Windows 的 `SIGTERM` 是硬杀
`os.kill(pid, sig)` 在 Windows 上只有 `CTRL_C_EVENT` / `CTRL_BREAK_EVENT` 是
"软"信号;**任何其它值**(含 `SIGTERM`)都会走 `TerminateProcess`。因此
`scripts/train_hrl_high.py` 里的:
```python
for _signum in (signal.SIGTERM, signal.SIGINT): # 旧代码
signal.signal(_signum, _on_stop_signal)
```
在 Windows 上形同虚设——处理器注册了,但没人会用它。这也解释了为什么"停止后
模型没保存"。
### 2.4 Windows 没有 `SIGSTOP`,暂停无法用信号实现
暂停(挂起/继续)在 POSIX 上可以用 `SIGSTOP/SIGCONT`,但 Windows 既无这两个
信号,也无等价的"无侵入挂起"用户态 API(真正的挂起要 `SuspendThread`/调试 API)。
所以**任何跨平台暂停都必须是协作式的**:由训练进程自己在 step 边界检查标志并
让出执行。本项目原先没有这条通道,界面也没有暂停按钮。
### 2.5 `SIGBREAK` 未处理
Windows 的 `CTRL_BREAK_EVENT`(发给进程组的软信号)在 Python 里落到
`signal.SIGBREAK`。旧的 handler 安装与 worker 屏蔽列表都只含
`SIGTERM/SIGINT`,Windows 下等于裸奔。
### 2.6 异常类型没兜住
`except (ProcessLookupError, PermissionError)` 漏掉 `AttributeError`、以及
更一般的 `OSError`;`_hard_kill` 里 `signal.SIGKILL` 在 Windows 上也不存在
(`getattr(signal, "SIGKILL", None) is None`),同样会 `AttributeError`。
---
## 3. 修复方案(已实施)
### 3.1 新增 `src/mcm26b/process_control.py`(零第三方依赖)
| 能力 | 实现 | 作用 |
| --- | --- | --- |
| 子进程隔离 | `spawn_kwargs()`:POSIX `start_new_session=True`;Windows `creationflags=CREATE_NEW_PROCESS_GROUP` | 前端 Ctrl+C 不误伤训练;Windows 也能组杀进程树 |
| 控制协议读写 | `write_control()` / `read_control()`:JSON + 同目录临时文件 + `os.replace` 原子替换;损坏/缺失一律视为"无指令" | 两端统一的暂停/停止通道 |
| 强杀兜底 | `terminate_process_tree(process, grace_s)`:先等宽限期;超时后 POSIX 走 `killpg(SIGKILL)`、Windows 走 `taskkill /F /T /PID`;所有 POSIX 符号用 `getattr` 探测 | 彻底移除 `os.killpg` 无条件调用;返回"是否动用了强杀" |
`IS_WINDOWS` 是运行期常量而非导入期分支,便于测试直接 monkeypatch 模拟 Windows。
### 3.2 控制文件协议
路径由前端生成并随命令下发:`--control-file <abs>`(默认落在
`output/hrl/ui/control/<job_id>.control.json`,`output/` 已 gitignore)。
```json
{"paused": false, "stop": false, "seq": 1712345678901234567, "ts": 1712345678.9}
```
- 前端**只写**:`pause()` → `paused=true`;`resume()` → `paused=false`;
`stop()` → `stop=true`(同时清零 paused,保证阻塞中的暂停循环能被唤醒)。
- 训练脚本**只读**:`_ControlPoll` 每 32 个 step 抽样读一次盘(避免每 step 一次
文件 IO);暂停时在 `_on_step` 内阻塞轮询(`poll_s=0.2`),进程存活、显存仍占用,
但不消耗算力。
- 墙钟上限 `--max-wall-s` **扣除累计暂停时长**,避免"挂起一小时回来直接判超时"。
### 3.3 训练脚本 `scripts/train_hrl_high.py`
- 新增 `--control-file`;`_ControlPoll`(纯标准库、可单测)+ 薄薄的
`_ControlChannel(BaseCallback)` 接到 SB3 回调链**最前面**。
- 收尾信号集合扩为 `_stop_signals()`:POSIX `SIGTERM/SIGINT`;Windows 追加
`SIGBREAK`。worker 屏蔽同一集合,保证主进程 `vec_env.close()` 之前 worker 不先退。
- 落盘保证不变:停止后仍写最终模型 + 元数据,`stopped_by_signal=true`,
`stop_signal` 记 `control_file` 或信号名;异常仍落 `*_emergency.zip`。
### 3.4 训练前端 `server.py` / `static/`
- 新增 `POST /api/train/pause`、`POST /api/train/resume`;`stop` 语义改为
"写控制文件 → 宽限期内自行退出;超时强杀",强杀在**独立看门狗线程**里做,
HTTP 请求不会被 15s 等待卡住。
- 状态机新增 `paused`;`_sync_control_events()` 用训练脚本回显的
`paused/resumed/stop_requested` 事件校正状态。
- 快照新增 `forced_kill`,前端横幅区分"优雅停止"与"超时强杀"。
- 前端新增「暂停训练 / 继续训练」按钮、暂停横幅;停止确认文案更新。
### 3.5 停止/暂停时序
```
pause: UI 写 {paused:true} ──► 脚本 _on_step 读到 ──► 回显 paused,阻塞轮询
resume: UI 写 {paused:false} ─► 暂停循环退出 ────────► 回显 resumed,继续训练
stop: UI 写 {stop:true} ────► 脚本 step 边界收尾落盘 ──► 退出码 0
│
└─(宽限 15s 未退)─► terminate_process_tree 强杀;forced_kill=true
```
### 3.6 急停(强杀)也必须可恢复
宽限期一到就强杀,训练脚本**没有任何 Python 收尾机会**——不写最终模型、不写
元数据、不写 `training_failure.json`。如果磁盘上只有 `--checkpoint-every` 的稀疏
历史 checkpoint,急停就可能丢掉上万步,或者在"保存途中被杀"时留下一个打不开的
半截 zip。为此加了四条保证:
| 保证 | 实现 | 效果 |
| --- | --- | --- |
| 起步基线 | `recovery_every > 0` 时,`learn()` 之前先写一份 `<stem>_latest.zip` | 训练刚起就急停,也有可加载、可 `--resume` 的产物 |
| 滚动恢复点 | 每 `--recovery-every`(默认 2000)环境步覆盖写同一份 `<stem>_latest.zip` | 急停最多丢 `recovery_every` 步,而不是 `checkpoint-every` 步 |
| 原子落盘 | 模型 zip / 旁挂 json / `resume.json` 一律「同目录 `.tmp-` + `os.replace`」 | 保存途中被强杀,目标路径仍是上一份**完整**快照;不会出现半截文件 |
| 权威恢复指针 | 每次落盘刷新 `<run_dir>/resume.json`(model / timesteps / 增量语义 / 生效 PPO 超参);前端 `/api/train/state` 暴露为 `recovery` | 无论怎么停,都有一份明确的"从哪继续";界面一键「用最近检查点续训」 |
前端在强杀后还会补写 `<run_dir>/forced_stop.json`(`job_id`、`pid`、`reason`、
`recovery.model`),因为此时训练脚本已经无法记录任何事情。
---
## 4. 平台兼容性矩阵(修复后)
| 能力 | POSIX | Windows |
| --- | --- | --- |
| 启动子进程并隔离 | `start_new_session=True`(setsid) | `CREATE_NEW_PROCESS_GROUP` |
| 暂停 / 继续 | 控制文件(协作式) | 控制文件(协作式) |
| 优雅停止 + 落盘 | 控制文件;`SIGTERM` 亦可 | 控制文件;`CTRL_BREAK_EVENT`→`SIGBREAK` 亦可 |
| 超时强杀进程树 | `killpg(SIGKILL)` | `taskkill /F /T /PID` |
| `os.killpg` 依赖 | 有 | **无(不再调用)** |
| 暂停期间是否算墙钟 | 否(扣除) | 否(扣除) |
---
## 5. 验证
- 新增/更新回归测试:
- `tests/test_training_ui.py`:控制文件协议往返与损坏容错、`spawn_kwargs`
平台分支、Windows 强杀走 `taskkill`、**无 `killpg` 环境下强杀不抛异常**
(直接复现旧 bug)、暂停/继续/停止全链路、控制文件被忽略时强杀并写
`forced_stop.json` 恢复指引、`discover_recovery` 的
`resume.json`/扫描/悬空指针三分支、HTTP `pause/resume` 端到端。
- `tests/test_hrl_checkpoint.py`:控制文件状态机单测;原子保存失败不破坏上一份
快照;JSON 原子替换;历史清理不误删滚动恢复点;起步基线可加载;
**强杀急停后 `resume.json` + 滚动恢复点仍可 `--resume`**;控制文件停止后
模型/元数据落盘(`stop_signal=control_file`)与暂停→继续;POSIX 信号路径
单列并加平台跳过。以上集成用例都用**真实训练脚本**跑 subproc。
- 手工验证:`--recovery-every 32` 短程实跑后强杀,`resume.json` 指向
`q3_ckpt_latest.zip`;以它为 `--resume` 目标再跑 32 步,元数据
`actual_timesteps` 从 64 变 96、`timesteps_semantics=additional`。
- 全量测试:`pytest tests referee/tests` 全绿。
- 全仓库 `pylint src scripts project tests referee main.py` 达到 10.00/10
(口径见 §7)。
---
## 6. 残余风险与后续建议
1. **暂停粒度** = `poll_every` 个 env step(默认 32,约 0.1–3s 量级),不是即时;
若要更快可调小 `_ControlChannel(poll_every=...)`,代价是更频繁的文件 stat。
2. **宽限期固定 15s**(`server.STOP_GRACE_S`)。大模型保存可能更久;但因为落盘是
原子的,即使宽限期到点被强杀,也只是"新的最终模型没写成",**上一份滚动恢复点
与历史 checkpoint 完好**,不会出现半截文件。需要更长可调大该常量。
3. **急停的损失上界 = `--recovery-every`**(默认 2000 环境步)。要更小就调小它;
调到 0 表示放弃滚动恢复点(此时只剩 `--checkpoint-every` 的稀疏历史
checkpoint 与起步基线)。
4. **Windows 强杀依赖 `taskkill`**(系统自带)。若被安全策略禁用,会退回
`Popen.kill()`(只杀主进程,spawn 的 worker 可能残留)。
5. 更严格的 Windows 方案是 **Job Object**(子进程随 job 句柄一起销毁),但需要
`ctypes`/`pywin32`,本仓库基础依赖刻意保持轻量,未引入。
6. **前端进程重启后**,内存中的任务对象没了,「用最近检查点续训」按钮不会自动
出现;但恢复信息都落在磁盘上(`<run_dir>/resume.json`、`forced_stop.json`、
`checkpoints/<stem>_latest.zip`),在表单里填同一个模型输出路径并勾选
`--resume` 即可继续。
7. 界面 `--device mps` 选项在 Windows 上无意义;`auto` 会回退 cuda/cpu,属既有
行为,未改。
8. `_ControlPoll` 的暂停循环会占用一个 Python 线程的调度(每 0.2s 一次读盘),
长暂停期间 CPU 占用可忽略。
---
## 7. pylint 口径说明
配置写在 `pyproject.toml` 的 `[tool.pylint.*]`,目标是
`pylint src scripts project tests referee main.py` = 10.00/10。
**改代码修掉的真实问题**(不是 disable):未使用的 import/变量/参数、重复字典键
(`reward_version`)、`value != value` 判 NaN、`__dataclass_fields__` 私有访问、
`raise` 非异常类型、`pytest.importorskip` 之外的赋值不确定、过长行、无谓括号、
`len(x)` 判空、循环闭包变量、`subprocess.run` 未显式 `check`、`os.path` 循环导入等。
**有意 disable 的类别**(每条在 pyproject 里写了理由):项目刻意的懒加载
(`import-outside-toplevel`)、防御式宽异常(`broad-exception-caught`)、测试访问
内部状态(`protected-access`)、pytest fixture 同名(`redefined-outer-name`)、
无头环境不实现 `gym.Env.render`(`abstract-method`)、以及静态分析的已知误报
(`possibly-used-before-assignment`)。
**注意**:`good-names` 不覆盖 `redefined-builtin`,stdlib 回调签名里的
`log_message(self, format, ...)` 只能在对应 `def` 上行内 disable。
+160
View File
@@ -0,0 +1,160 @@
# 模型训练前端(training-ui)
本地回环 Web UI,用于**实时观察一次 HRL 高层 PPO 训练**:子进程状态、步进进度、
参数收敛曲线、逐回合记录与完整 stdout 日志。设计与评测前端(`referee/` WebUI)
同构——同一套暗色面板语言、同一个「只监听 127.0.0.1 + 静态白名单 + JSON 轮询
API」模式——差别在于本页托管的是一个**训练子进程**而不是评测会话。
## 启动
```bash
uv run python -m mcm26b.training_ui --ui-port 8036 # 打开 http://127.0.0.1:8036
uv run python scripts/train_hrl_ui.py --ui-port 8036 # 等价薄入口(支撑材料命令)
uv run python -m mcm26b.training_ui --print-schema # 只看配置契约,不起服务
uv run python -m mcm26b.training_ui --print-preview # 只看离线示例快照
```
`--no-browser` 阻止自动打开浏览器;Ctrl+C 退出时会先停止训练子进程。
无训练进程时,界面顶栏「离线预览」可渲染三组合成快照(上升 / 平台期 / KL 过猛),
用于检查曲线、指标卡、时间线与日志着色——预览不启动任何进程、不写任何文件。
## 界面结构
| 区块 | 内容 |
| --- | --- |
| 顶栏 | 任务摘要、进程/进度/墙钟/设备/连接五个状态 chip、刷新·预览·暂停/继续·停止 |
| 执行进度与进程 | 步进进度条、剩余时间与预计结束、PID/吞吐/退出码、PPO 阶段时间线、完整命令行 |
| 训练配置 | 由 `/api/train/schema` 生成的五组表单 + 四个预设,运行中锁定 |
| 参数收敛状态 | 判读 chip + 判据说明,每个指标一张卡片(最新值、增量、迷你趋势线、异常高亮) |
| 曲线 | 主指标(平均回合奖励)+ 可切换诊断量,悬停十字线读数、KL=0.05 等参考线 |
| 实时运行日志 | 关键字过滤、错误/进度 JSON 开关、自动跟随、最近 4000 行 |
| 逐回合记录 | Monitor CSV 的每回合奖励/长度/时刻 |
## 数据来源与契约
三个端点都在 `src/mcm26b/training_ui/server.py`:
| 端点 | 说明 |
| --- | --- |
| `GET /api/train/schema` | 字段/分组/预设/指标字典,**与 `TrainConfig` 同源**(`schema.py`) |
| `GET /api/train/state` | 快照;支持 `log_tail`、`metric_limit`、`after_metric` 游标与 `preview=1` |
| `POST /api/train/start` | 允许只提交部分字段(其余取默认值),校验失败返回 409 |
| `POST /api/train/pause` | 写控制文件请求在 step 边界挂起(跨平台) |
| `POST /api/train/resume` | 解除挂起,从暂停处继续 |
| `POST /api/train/stop` | 请求优雅停止;宽限期(`STOP_GRACE_S`,默认 15s)后强杀进程树 |
暂停/停止走**控制文件**而不是信号,原因与平台差异见
[`training-ui-windows-audit.md`](training-ui-windows-audit.md):Windows 没有
`SIGSTOP`,且 `os.kill(pid, SIGTERM)` 会退化成 `TerminateProcess` 硬杀,训练脚本
"保存模型再退出"的收尾分支不会执行。控制文件在 POSIX 与 Windows 上都只是普通
文件 IO,语义完全一致;信号(POSIX `SIGTERM/SIGINT`、Windows `SIGBREAK`)只作兜底。
进度数据的两个来源,服务端都已解析:
1. **progress JSON**(主来源):`train_hrl_high.py --progress-json` 每若干环境步打印
一行 `{"event": "progress", "timesteps": ..., "ep_rew_mean": ...}`,字段最全;
热身阶段间隔会自动缩小,短程 smoke 也能看到完整曲线。
2. **SB3 verbose 表格**(补充来源):解析 `| key | value |` 表格,用于补
`clip_fraction`/`learning_rate` 等诊断量。表格按段分组缓存、等本表的
`total_timesteps` 到位后统一打横轴刻度;与 JSON 同一步数的表格版本会被跳过,
避免重复点。
`timesteps` 是整个前端的横轴:服务端不伪造刻度(缺刻度的表宁可丢弃,截断的表
按末尾已知刻度兜底并在收尾提交),前端不再自行编号。
## 收敛判读口径
`summarize_convergence()` 只做**可解释的启发式判读**,不替代正式学习曲线分析:
| 状态 | 触发条件 |
| --- | --- |
| 等待首个 rollout | 尚无任何指标 |
| 预热阶段 | 进度点 < 3,无法判断趋势 |
| KL 偏大,策略更新可能过猛 | 最近 `approx_kl` > 0.05(判定优先级最高) |
| 价值函数尚未拟合 | 最近 `explained_variance` < 0 |
| 奖励未形成上升趋势 | 最近窗口奖励斜率为负 |
| 已趋于平稳(疑似收敛) | 窗口对半位移 ≤ 逐点噪声、变异系数 ≤ 0.15、极差 ≤ 15% 量级 |
| 收敛趋势良好 | 其余(斜率正向) |
平台判据特意用「对半窗口位移 / 逐点噪声」而不是单看斜率绝对值:慢速但持续的
提升(斜率很小)不会被误判成收敛。所有判据数字都会写进 `notes`,便于人工复核。
## 安全边界
- 只监听 `127.0.0.1`;静态资源是固定白名单,不提供目录遍历。
- 命令由服务端以**参数列表**构造,不经过 shell;字符串参数只拒绝控制字符。
- 同一时刻只允许一个训练任务;暂停中的任务仍算「在跑」,必须先停止才能开新一局。
- 暂停/停止走控制文件(`--control-file`,默认 `output/hrl/ui/control/<job_id>.control.json`),
进程组隔离按平台选择(POSIX `setsid` / Windows `CREATE_NEW_PROCESS_GROUP`),
超时强杀按平台选择(POSIX `killpg(SIGKILL)` / Windows `taskkill /F /T`)。
- 产物路径限定在仓库根下解析,模型输出沿用训练脚本自身的 `--force`/`--resume`
语义(两者互斥,由后端校验)。
## 暂停、停止与落盘保证
「暂停」是**协作式**的:前端原子写控制文件,`train_hrl_high.py` 的
`_ControlPoll` 每 32 个环境步抽样读一次;暂停时训练阻塞在 `_on_step` 的轮询里,
进程与显存保留、不消耗算力,点「继续」从同一个 step 边界恢复。暂停时长不计入
`--max-wall-s`(否则"挂起一小时回来直接判超时")。
「停止」同样是控制文件请求(**跨平台**)。`train_hrl_high.py` 有多层兜底,
点一下「停止」不再等于这一局白跑;而**急停**(强杀,例如 `SIGKILL` /
`taskkill /F`,进程没有任何收尾机会)也保证可恢复:
| 机制 | 行为 |
| --- | --- |
| 滚动恢复点 | `--recovery-every`(默认 2000 环境步)把 `<checkpoints>/<stem>_latest.zip` **原子覆盖写**一次;训练开始前先写一份**起步基线**。因此急停后最多只丢 `--recovery-every` 个环境步,且磁盘上始终有一份完整、可加载、可 `--resume` 的产物 |
| 原子落盘 | 模型 zip、旁挂 json、`resume.json` 都是「同目录临时文件 + `os.replace`」:保存途中被强杀不会留下半截文件,也不会破坏上一份完整快照 |
| 恢复指针 | `<run_dir>/resume.json` 记录当前最佳恢复目标 + 步数(增量语义)+ 生效过的 PPO 超参;前端顶部的「用最近检查点续训」按钮直接读它 |
| 定期 checkpoint | `--checkpoint-every`(默认 20000 环境步)另留 `<stem>_<步数>.zip`,`--checkpoint-keep` 只保留最近 K 个(默认 3),供返祖对比与多档恢复;滚动恢复点与起步基线不受清理影响 |
| 优雅停止 | 控制文件 `stop=true`(或 POSIX 信号 / Windows `SIGBREAK`)后,主进程在 step 边界收尾,保存最终模型与元数据(`stopped_by_signal=true`、`stop_signal`、`checkpoint_files`、`recovery_model`)并正常关闭环境 |
| 超时强杀 | 宽限期(`STOP_GRACE_S`,默认 15s)内没退出则强杀整棵进程树,快照 `forced_kill=true`,并在 `<run_dir>/forced_stop.json` 写明 `recovery.model`(可直接续训的目标) |
| 异常兜底 | `learn()` 抛出任何异常时先落 `*_emergency.zip` + 元数据 + `resume.json`,再写 `training_failure.json` 并抛出 |
急停后恢复的操作方式:界面点「用最近检查点续训」;命令行则
`--resume --model <resume.json 里的 model>`,`--timesteps` 按**追加**步数理解。
checkpoint 与最终模型共用同一份 json 契约,因此 `--resume <checkpoint.zip>` 直接
续训(`--timesteps` 为增量步数)。注意 `--resume`/`--init` 会沿用 checkpoint 内的
PPO 超参(`n_steps`/`batch_size`/`n_epochs`):命令行这三项被忽略,脚本会打一条
`hyperparams_from_checkpoint` 事件说明差异,元数据 `ppo_hyperparams` 记录**实际生效**值。
回归测试见 `tests/test_hrl_checkpoint.py`,其中端到端用例真起 subproc 子进程,覆盖
控制文件停止、POSIX 信号停止、**强杀后仍可续训**与暂停→继续(信号路径在 Windows
上自动跳过)。
## 测试
```bash
.venv/bin/python -m pytest tests/test_training_ui.py tests/test_training_ui_js.py tests/test_hrl_checkpoint.py -q
```
- `tests/test_training_ui.py`:配置契约同源、预设可直接提交、参数校验、SB3 表格
刻度打点、游标分页、`describe_process` 进度/ETA、示例快照与真实快照同键;
用**桩训练脚本真起子进程**覆盖启动/轮询/暂停/继续/停止/退出码全链路;并覆盖
跨平台进程控制——控制文件往返与损坏容错、`spawn_kwargs` 平台分支、Windows 走
`taskkill`、**无 `killpg` 环境下强杀不抛异常**(旧 bug 的直接回归)、控制文件被
忽略时强杀并写 `forced_stop.json` 恢复指引、`discover_recovery` 的
`resume.json`/扫描/悬空指针三分支、HTTP `pause/resume`。
- `tests/js/training_ui_harness.cjs`(经 `tests/test_training_ui_js.py` 调起):
DOM 桩加载发布的 `charts.js`/`app.js` 原文,用服务端生成的真实快照驱动渲染,
断言表单/指标卡/曲线/日志/逐回合表都拿到数据,覆盖增量游标三条分支与
`classList`、HTML 转义、极端数值等边界。
- `tests/test_hrl_checkpoint.py`:落盘与恢复的端到端回归——原子保存失败不破坏
上一份快照、JSON 原子替换、历史清理不误删滚动恢复点、起步基线可加载、
**强杀急停后 `resume.json` 与滚动恢复点仍可继续训练**、控制文件停止落盘、
暂停→继续、POSIX 信号停止。
## 已知取舍
- 进度点保留上限 5000(服务端 deque 与前端本地缓存同值),超长训练只保留最近一段。
- 表格解析依赖 SB3 的段名(`rollout/`/`time/`/`train/`/`system/`);SB3 若改表格
格式,退化路径是「只显示 progress JSON」,不会导致界面崩溃。
- 收敛判读是启发式;正式结论仍需用 `scripts/benchmark_hrl.py` 的配对评测与
学习曲线人工复核。
- 暂停粒度是 `poll_every` 个环境步(默认 32),不是即时挂起;停止宽限期默认 15s。
- Windows 强杀依赖系统自带的 `taskkill`;若被策略禁用会退回 `Popen.kill()`
(只杀主进程)。更严格的 Job Object 方案需要额外依赖,未引入。
Windows 兼容性问题的完整根因与修复记录见
[`training-ui-windows-audit.md`](training-ui-windows-audit.md)。
+15 -2
View File
@@ -141,9 +141,22 @@ remaining_real_duration_s 为准,不得假定恒为 1200);测试窗口 25
- **清除证书**:候选集外包多边形半径(含近似误差与工程余量) - **清除证书**:候选集外包多边形半径(含近似误差与工程余量)
≤ 20m 才发起 `/clear`;证书有效但清除失败视为异常,先补测再重试, ≤ 20m 才发起 `/clear`;证书有效但清除失败视为异常,先补测再重试,
不在同一证书上重复消耗 3 秒/次的失败清除; 不在同一证书上重复消耗 3 秒/次的失败清除;
- **停止条件**(全部显式实现):清除数达 16(源数上界证明)、 - **停止条件与完成判定**(2026-09-11 用户决策后):找全证明达成
每频道均"已清除或覆盖证明不存在"、现实预算耗尽。发现若干源、 (每频道均"已清除或覆盖证明不存在",或清除数达 16 数量上界)后
**不提前退出**:按 `search.persistent_search`(默认开)只复扫未清除
频道,直到清除数达 16、实机预算耗尽或虚拟安全余量
(`virtual_reserve_s`,防触及附件2 §4.5 的 100 小时上限;
本地模拟器对达限后的动作以 accepted=false 兜底)不足——"扫到限时为止"。
summary 中 `completed` 表示任务证明完成(无中断且结束时每频道
CLEARED/ABSENT,或 16 清除);`accounted_virtual_time_s` 记录首次
证明找全的虚拟时刻(计算"平均定位清除时间"建议用它,而非复扫
耗尽后的最终时刻);`stop_reason` 记录循环结束原因。发现若干源、
连续 no_signal、候选采样为空都不是停止条件; 连续 no_signal、候选采样为空都不是停止条件;
- **每步计时留痕**(同上决策):客户端每个被接受动作写一条 `step`
事件(实机墙钟耗时 `real_elapsed_s`/`real_since_enter_s` + 比赛虚拟
耗时分项 `virtual_cost`:移动=直线距离/5、切频道 1s、检测 5s、
清除成功 5s/未发现 3s),summary 以 `steps` 数组与 `step_totals`
汇总,并给出题面口径的 `avg_locate_clear_time_s`;
- **验收**:`tests/test_loop_q3.py` 在自建模拟器上覆盖 10/16/0 源、 - **验收**:`tests/test_loop_q3.py` 在自建模拟器上覆盖 10/16/0 源、
近边界源、预算耗尽与故障注入;`uv run mcm26b run --robot-id <队号> 近边界源、预算耗尽与故障注入;`uv run mcm26b run --robot-id <队号>
--self-test` 可离线复跑随机场景,正式模式去掉 `--self-test` 即对接 --self-test` 可离线复跑随机场景,正式模式去掉 `--self-test` 即对接
@@ -0,0 +1,165 @@
# 第三、四问强化学习适用性审核与改进
本轮审核第一版强化学习代码、专家数据、模型及官方演练归档,修复训练与部署不一致的问题,训练两个新的 Maskable PPO 模型,并用独立实现的本地 `referee` 完成同场景对照。所有新性能数字均为本地结果,没有启动新的官方演练。
结论:强化学习适合在现有几何算法内尝试“下一检测点”的排序,第四问比第三问更有收益空间。当前证据支持第四问的混合方案优于手写 `directional` 基线;尚不能证明新模型稳定优于旧 PPO。第三问没有稳定增益,应保留 `bayes` 默认策略。新模型作为实验版本显式加载,没有覆盖旧默认模型。
## 1. 题设和方法适用范围
附录2(3)的交会必须使用带误差的角域,不能把两条示向线的交点当作真值;同地重测不会提供新的独立误差样本。附录2(4)的单接收机逐频道检测,结合动作规则,须计入移动、5秒检测及实际1秒换频。第三问的全向假设允许用无信号排除接收圆盘;第四问的无信号还可能来自方向遮挡,不能沿用这一排除结论。题设及几何算法的完整审核见[算法审核与优化](算法审核与优化-20260911.md)。
当前 RL 实际只替换 `RLBayesLoop._select_point` 和 `RLDirectionalLoop._next_probe`。七站/49站覆盖、频道发现、全局任务调度、保守候选集、清除证书和第四问后备清除仍由原算法负责。因此,完整场景全清验证的是混合系统,不是神经网络独立获得了完备性证明。
401维输入概括几何、历史读数和60个候选点,但省略完整历史、候选多边形细节及位置—朝向联合约束,不能直接称为完整马尔可夫状态。局部单源训练也没有学习全场覆盖路线与多源调度。PPO可以作为这种压缩观测下的近似策略尝试,但其目标并不自动等于全场最短时间;评估必须使用独立完整场景。[SB3官方训练与评估建议](https://stable-baselines3.readthedocs.io/en/master/guide/rl_tips.html)也强调独立评估、多随机种子和状态信息完整性。
## 2. 确认的实现缺陷及修复
| 缺陷 | 证据和影响 | 已实施修复 |
| --- | --- | --- |
| 训练传入整个 `BeliefState`,部署传入 `ChannelBelief` | 同一状态的特征索引7、14、15、17、18不同;训练丢失测量次数、末次结果和方位信息 | `env._observe` 使用目标频道信念;测试逐位比较部署输入,并覆盖跨频道交接 |
| BC使用ReLU,接收权重的PPO使用Tanh | 仅复制线性权重无法保留函数。原始专家输入探针中,Q3的90.96%、Q4的67.42%出现不同argmax | BC改为Tanh;复制时检查完整前向输出,重新初始化critic;旧ReLU/旧特征检查点拒绝直接迁移 |
| BC按观测行随机拆分 | 同一episode的相关状态可能同时出现在训练和验证中,分类准确率不足以说明独立场景泛化 | 保存episode编号,按episode划分训练/验证 |
| 第四问训练遮挡状态与运行时不一致 | 训练只记录距离保证盘内的无信号,且获得新方向后未正确重置连续遮挡;后备清除触发轨迹偏离部署 | 所有连续无信号记录为探测序列,仅盘内无信号用于确认定向;正观测清空序列,统一4次遮挡/8次定位阈值 |
| 奖励鼓励首次遮挡等中间事件 | “获得遮挡证据”额外+2、半径收缩等奖励会改变实际时间目标;较高回报不一定更快 | 主要奖励改为实际虚拟耗时的负值,成功清除统一+20,失败/截断重罚;取消这些中间奖励,采用gamma=1 |
| 未屏蔽同地重测、重合候选和第三问盘外梯子点 | 浪费固定误差条件下的检测时间;旧文档“所有候选保证接收”不成立 | 训练/推理共用动作屏蔽,排除已测0.5m格位置、重复候选、越界点;第三问存在距离保证盘时排除盘外点 |
| 保证盘半径恰为0时动作数量可能改变 | 依赖去重后的候选生成器会破坏固定60动作接口 | 即使位置重合也保留编号,交由动作屏蔽处理 |
| 只校验401/60维度不能识别语义变化 | 接收机频道被目标频道替代;旧模型与新模型同形但输入含义不同 | 加入特征版本与算法元数据,v2使用实际接收机频道,旧版保留原输入语义;拒绝问题编号或屏蔽模型元数据不匹配 |
| 基准将 `completed` 当作全清 | 内部状态自洽不代表全部真实源已清除 | 用评测端真实频道集合对照成功清除集合,并检查真实频道没有被误判缺席、正常退出、异常及逐步计时;旧基准新增 `all_valid` 和失败退出码 |
| 官方审计没有严格对应问题与策略名称 | 第三问/第四问摘要可能交叉通过名称检查 | `verify_rl_practice.py` 分别要求 `rl` / `rl_q4` |
BC探针是对旧网络和“拷贝后换激活”的网络进行比较,并不是新模型错误率,也不是重新测得的BC验证准确率。旧报告38.9%/65.3%的分类准确率不能直接解释为已迁移PPO的模仿准确率。本轮训练的新模型均从随机初始化开始,未使用BC;BC修复通过前向一致性测试,未据此声称重新训练BC已经带来收益。
动作屏蔽采用官方 `MaskablePPO` 的 `action_masks` 接口,训练和确定性推理都传入相同规则。[SB3-Contrib官方文档](https://sb3-contrib.readthedocs.io/en/master/modules/ppo_mask.html)说明了这两处的用法。第四问仍允许探索盘外候选,距离保证不等于方向可见。退化到所有动作均被屏蔽时保留中心动作,由步数/预算边界处理,因此不能把本次零重复观测扩展为所有可能状态的严格保证。
正常成功episode的累计奖励为 `20 - 局部实际虚拟时间/100`;环境仍保留异常与失败处理,包括证书清除失败的额外惩罚。局部时间从首测与可选交接完成后开始,不能用训练报告中的局部均值替代整场成绩。第三问训练自动清除仍使用证书圆心,而完整 `BayesLoop` 可在证书范围内平移清除点;全局调度、预算与局部环境也有差异,完整闭环验证因此不可省略。
## 3. 训练和数据
第一版Q3为120000步随机初始化PPO;第一版Q4为BC初始化后40000步PPO。原专家数据分别为5141、14181条,原5场完整测试均值分别是Q3:3088.33/3124.76秒、Q4:14250.15/13230.20秒(手写/RL)。这些历史数字受旧环境和特征影响,不与本轮新课程的单源成功率直接比较。
修订课程保留首次有效测向,并加入35%的跨频道、跨位置交接,20%的区域边缘且最小接收半径场景。定向首测覆盖朝向±89.9°,更接近半平面边界。源位置、朝向、半径和逐点误差的抽样仍是自建分布,不是已知的官方生成分布;真值仅用于布设和评测,不进入策略观测或动作屏蔽。
两模型使用 `[128,128]` Tanh MLP、2个内存环境、`n_steps=512`、`batch_size=128`、6轮更新、学习率0.0003。实际均完成60416步,来自完整rollout批次向上取整;元数据中的60000是请求步数。安装版本为SB3 2.9.0、SB3-Contrib 2.9.0、Torch 2.11.0+cu128。本次选择CPU、单Torch线程,机器CUDA可用。
| 项目 | Q3新模型 | Q4新模型 |
| --- | ---: | ---: |
| 训练种子 | 310001 | 410001 |
| 定向概率 | 0 | 0.75 |
| 训练墙钟时间 | 276.6s | 250.6s |
| 独立单源评估成功 | 59/60 | 60/60 |
| 单源平均局部时间 | 270.04s | 349.51s |
| 平均动作步数 | 2.42 | 3.13 |
Q3有一次局部训练任务未完成,不能声称单源成功率100%。完整闭环仍有自身调度与后备路径。两问题各只有一个新模型训练种子,未覆盖训练随机性。
## 4. 完整场景同条件对照
比较三种固定策略:当前手写基线(Q3 `bayes`、Q4 `directional`)、旧默认PPO、新Maskable PPO。相同场景使用相同源位置、频道、半径、朝向及逐点固定误差种子。`referee` 为单独实现,策略不读取模拟器真值。每场保存场景、完整请求/响应、summary、模型哈希和计时核验。
开发集为种子700—702、10/16源,各问题6场。随后固定新模型,不再据验证成绩调参;验证集为种子91000—91005、10/13/16源,各问题18场。第四问定向数覆盖1、约半数、仅一个全向三种组成。另加两种圆周边界压力场景。开发、验证、压力共52个不同场景,三策略共156次完整运行,全部满足真实源全清、缺席无误判、正常exit、零异常、逐步耗时复算一致。
### 开发集平均整场时间
| 问题 | 手写基线 | 旧PPO | 新模型 |
| --- | ---: | ---: | ---: |
| Q3,6场 | 3294.62s | 3206.20s | 3264.62s |
| Q4,6场 | 14289.76s | 12443.43s | 11758.72s |
### 独立验证集
| 问题/指标 | 手写基线 | 旧PPO | 新模型 |
| --- | ---: | ---: | ---: |
| Q3有效全清 | 18/18 | 18/18 | 18/18 |
| Q3平均时间 | 3330.72s | 3254.61s | 3307.65s |
| Q3中位时间 | 3309.16s | 3262.64s | 3222.50s |
| Q3最大时间 | 3690.11s | 3786.98s | 3776.55s |
| Q4有效全清 | 18/18 | 18/18 | 18/18 |
| Q4平均时间 | 13778.82s | 12070.53s | 11797.57s |
| Q4中位时间 | 14587.80s | 12891.83s | 12488.23s |
| Q4最大时间 | 17273.98s | 15365.76s | 15894.44s |
| Q4累计未成功clear尝试 | 616 | 719 | 1380 |
| Q4累计同频道同点重测 | 12 | 28 | 0 |
Q3三组都没有失败clear或同点重测。Q4新模型减少了重测,但更多使用后备光学清除尝试,平均耗时降低的同时尾部表现有所退化。失败clear本身可来自允许的后备搜索,不能仅凭该计数认定证书错误;其移动和3秒动作耗时全部纳入结果。
对独立验证场景做10000次配对bootstrap,负值表示新模型更快:
| 对比 | 均值相对变化 | 95%场景重采样区间 | 新模型更快场数 |
| --- | ---: | ---: | ---: |
| Q3新模型对手写 | -0.69% | [-4.50%, +2.95%] | 9/18 |
| Q3新模型对旧PPO | +1.63% | [-1.64%, +5.19%] | 8/18 |
| Q4新模型对手写 | -14.38% | [-17.93%, -11.48%] | 18/18 |
| Q4新模型对旧PPO | -2.26% | [-6.08%, +1.71%] | 13/18 |
新旧PPO的区间均跨0,不能据小幅平均变化宣称稳定改进。区间只反映固定模型在这些场景上的差异,不覆盖训练种子、未知官方分布或所有合法边界。此外本轮同时修改多项训练因素,这不是能归因到单个修复的消融实验。
### 边界压力与4000秒目标
源全部放在半径1800m圆周、接收半径1000m;第四问隔一个源采用向外发射方向。10/16源各一场,各策略都完成全清。
| 问题 | 手写平均 | 旧PPO平均 | 新模型平均 |
| --- | ---: | ---: | ---: |
| Q3 | 4472.58s | 4429.04s | 4648.26s |
| Q4 | 18248.87s | 14404.01s | 15210.08s |
新Q3的两场分别为4659.06s、4637.46s,仍超过4000s;新Q4为14573.62s、15846.54s,且比旧PPO退化。此前几何优化在另一独立Q3验证组达到平均3315.89s、最大3981.74s,本轮新Q3验证组最大3776.55s;二者只能说明各自验证组达标,不能承诺全部题设场景小于4000s。第四问尚未达到4000s。
## 5. 官方证据与测试
重新审计已有官方归档,三次均通过题号/策略、官方包SHA、源数、完整清除、正常退出、请求时窗及逐步计时检查,未改写原归档:
| 问题 | 既有官方案例 | 全清 | 整场时间 |
| --- | --- | ---: | ---: |
| Q3 | F38Y-9Q9J-B82V-CB9Z | 13/13 | 3538.81s |
| Q4,3个定向源 | X43X-VMRF-8GRZ-3SYA | 10/10 | 14003.09s |
| Q4,10个定向源 | BXSA-TFQ3-2DJ6-DHH7 | 10/10 | 14562.82s |
这些是旧模型的官方可运行证据,不是新模型成绩;不同案例也不能直接计算优化百分比。新模型目前通过本地验证,未在官方新演练中运行。
全量回归:`python -m pytest -q --tb=short`,243项通过。初次沙箱运行有24个临时目录权限导致的fixture错误,获得权限后重跑全部通过。新增5项测试覆盖训练/部署观测、动作屏蔽、方向读数重置遮挡、实际成本奖励和BC完整前向一致性。之后对基准/官方审计入口的小修补分别用4次完整场景冒烟运行及三份官方归档复核通过。
## 6. 产物与复现
主目录为 `output/rl/review-20260911/`:
- `q3_masked.zip` / `.json`、`q4_masked.zip` / `.json`:新模型与训练元数据,必须成对保存。
- `evaluation/development/{controls,revised}/results.json`:开发对照。
- `evaluation/validation/final/results.json`:独立验证与每场完整日志。
- `evaluation/stress/final/results.json`:边界压力结果与日志。
- `review_evidence.json`:配对区间、BC/特征错位探针、旧官方重审结果和代码哈希。
- `benchmark-smoke.json`:旧基准有效性口径修复后的冒烟结果。
新Q3模型SHA-256:`b8b3a2bb103588348d8d7791bde9a2b0635c04778f0ac64f91c6f5c7cf87ca6e`。
新Q4模型SHA-256:`6865d541ff9bdee81886d327345f2660d5f7d72f4b39810a58ae4b867ab37e42`。
从项目根目录复现训练时,使用新目录保留本轮检查点:
```powershell
.venv/Scripts/python.exe scripts/train_rl.py --problem 3 --algorithm maskable_ppo --timesteps 60000 --seed 310001 --device cpu --model output/rl/reproduction/q3_masked.zip --eval-episodes 60 --verbose 0
.venv/Scripts/python.exe scripts/train_rl.py --problem 4 --algorithm maskable_ppo --timesteps 60000 --seed 410001 --directional-probability 0.75 --device cpu --model output/rl/reproduction/q4_masked.zip --eval-episodes 60 --verbose 0
```
复核本轮冻结模型的完整对照(label须使用尚不存在的名称):
```powershell
.venv/Scripts/python.exe scripts/evaluate_rl_revision.py --split validation --label rerun-validation
.venv/Scripts/python.exe scripts/evaluate_rl_revision.py --split stress --label rerun-stress
.venv/Scripts/python.exe scripts/summarize_rl_revision.py
```
本地内存自检可显式加载对应检查点;`--self-test`不会连接官方模拟器:
```powershell
.venv/Scripts/mcm26b.exe run --robot-id 2026001 --self-test --strategy rl --rl-model output/rl/review-20260911/q3_masked.zip
.venv/Scripts/mcm26b.exe run --robot-id 2026001 --self-test --problem 4 --q4-strategy rl --rl-model output/rl/review-20260911/q4_masked.zip
```
在用户另行启动相应问题的官方演练后,使用实际登录队号和`--base-url`连接。例如第三问(路径使用本轮尚未创建的官方演练目录):
```powershell
.venv/Scripts/mcm26b.exe run --robot-id 2026001 --base-url http://127.0.0.1:2026 --problem 3 --strategy rl --rl-model output/rl/review-20260911/q3_masked.zip --log output/rl/review-20260911/practice-q3/session.jsonl
```
该命令保存请求/响应日志,汇总输出到终端;官方结果文件与汇总还需按原归档流程保存。第四问改用`--problem 4 --q4-strategy rl`及对应模型。本轮未执行上述官方连接命令。
进一步研究应先缩小单源训练与完整调度的差异,并处理Q4后备清除和边界退化,再做多个训练种子及新的冻结验证集。当前结果不足以将新模型自动替换为默认策略,也不足以把训练步数增加当作达到4000秒的依据。
+102
View File
@@ -0,0 +1,102 @@
# 第三、四问算法审核与优化
依据:原始 `issue/B题.pdf` 第1—3页,重点为附录2(3)(4),并结合(1)(2)(5)(6)(8)与第三、四问要求。
审核起点为 `9cf9428` 的程序;最后一次官方第三问演练为 `3BW5-9R54-NZNA-MYB9`,
13/13源成功清除、虚拟总时间4058.709750秒。本轮改进验证使用本地模拟器,没有启动或消耗官方测试。
## 1. 题设约束及审查结论
附录2(3)中的交会是有宽度的角域交集。不能把两条示向线的交点当作精确真值,
也不能利用同地重测求平均缩小±1°误差。程序已保留完整角域及0.005°舍入裕量,
本轮继续使用该证据颁发清除证书。统计评分仅用于选点,不能替代保守几何证书。
附录2(4)要求逐频道检测,换频1秒;结合(5),每次停靠检测5秒且不能在移动中检测。
已有HTTP客户端是串行的,没有把20频道视为一次免费测量。缺陷在于规划成本和站点利用:
| 审核发现 | 影响 | 当前改进 |
| --- | --- | --- |
| 已停靠点只测UNKNOWN频道,已发现频道容易错失交会机会 | 之后为第二条方位线另走一段路 | 对DETECTED频道预测收缩;当前位置到整个候选集均小于1000m、未在此处测过且预测半径减少35%时,逐频道补测;每次仍支付5秒和实际换频成本 |
| 定位选点只计到测点的移动 | 测完后到清除区域的路程没有进入选点目标 | 加入候选检测点→候选源中心的接近代价,保留后续根据真实响应重新规划 |
| 候选方向固定在全局角度网格 | 与当前交会几何不对齐,可能产生多余绕行 | 增加以当前位置到候选中心为轴的对称候选;均仍位于距离保证盘内 |
| 路线代价中的service_s按初始频道一次估计 | 2-opt比较不同顺序时没有正确传递频道状态 | 路径成本逐任务传递频道,站内当前频道优先,clear保持频道;两个任务也比较完整开放路径成本 |
| 清除默认走到候选集包围圆心 | 未利用20米光学定位/清除范围 | 在证书允许范围内向当前位置平移目标;平移距离+原包围半径不超过阈值,保留三角不等式证明 |
| 七站环半径可调但没有执行覆盖校验 | 不合法调参可能把“都没测到”误作不存在 | 构造时检查解析覆盖界;不满足条件直接拒绝运行 |
| 第四问盘外探点无信号也可能记为定向遮挡 | 把接收距离不足误诊为已确认定向 | 仅当实际探点位于距离保证盘内才据无信号确认定向;盘外无信号保留不确定性 |
| 第四问发现一个源就离站定位,后续频道再返回原站 | 同站频道间产生长距离往返 | 保留49站定向覆盖,先逐频道扫完本站,再按当前位置就近处理已发现源 |
| 后备清除按初始位置距离静态排序 | 狭长候选集可能反复折返,且循环内无虚拟预算检查 | 每一步从当前位置选最近未处理格,并逐次检查虚拟预算 |
第三问所有源均为全向,才能用no_signal排除1000米圆盘。第四问源类型与朝向未知,
no_signal还可能是半平面遮挡,不能用上述排除规则,也不能直接沿用七站缺席证明。
本轮没有把BayesLoop套到第四问;第四问继续采用49站覆盖和独立的定向定位/后备清除。
回归用例包含(1800,0)向外发射的源:七站全无信号,49站中有站可检出。
七站覆盖界为 `max(r/√3, sqrt(R²+r²−√3Rr))`,前提是`0<r≤√3R`。
当前R=1800m、r=1159.7m,通过小于1000m的校验。网格滤波额外带Voronoi裕量,
故无源时仍可能留下边界格;此时完整七站覆盖可以证明缺席,不要求PF一定为空。
## 2. 验收口径
4000秒目标按整场虚拟总时间(包含移动、换频、全部检测、清除和缺席确认)比较,
没有在4000秒强行停止,也没有以只清除部分源或最后一次成功清除的时刻代替总时间。
题面表格的平均定位清除时间仍等于总时间除以实际清除源数。
第三问优化开发集固定为种子0—5、10/16源,加两个最小接收半径的圆周边界场景(14场)。
选择实现后停止调整,再运行种子100—105、10/13/16源的独立验证集(18场)。
同场景前后使用相同位置、频道、有效半径和逐点固定误差种子。
真值只传给本地模拟器和执行后的校验,不提供给算法;全部请求和响应保存。
有效运行要求:全部真实源清除、没有误判真实频道缺席、正常exit、无算法异常、逐步耗时复算一致。
| 第三问指标 | 旧版 | 改进后 |
| --- | ---: | ---: |
| 开发集全清且有效 | 14/14 | 14/14 |
| 开发集平均总时间 | 3812.97s | 3390.88s |
| 开发集最大总时间 | 5535.13s | 4624.97s |
| 开发集中小于4000s | 10/14 | 12/14 |
| 独立验证集全清且有效 | 18/18 | 18/18 |
| 独立验证集平均总时间 | 3799.72s | **3315.89s(减少12.73%)** |
| 独立验证集最大总时间 | 4569.47s | **3981.74s** |
| 独立验证集中小于4000s | 12/18 | **18/18** |
两个人工圆周边界场景改进后仍为4145.19s、4624.97s左右。因此本轮实现达成的是
独立验证组每场小于4000秒,**不是对全部合法题目场景的最坏时间保证**。
旧官方案例已经结束,无法对新路线查询其未知响应;本地结果不冒充官方同案例重跑。
更新后的官方成绩仍需新演练验证,不能由前后不同案例的数字直接得出提升比例。
## 3. 第四问结果
固定四场:10源/3定向,种子0、1;16源/8定向,种子2、3。覆盖朝向未知场景,
每场源分布与误差种子前后相同。旧版、改进版均4/4全清、正常退出、零异常和零计时偏差。
| 场景 | 旧版总时间 | 改进后总时间 |
| --- | ---: | ---: |
| 10源,种子0 | 18599.317s | 13652.140s |
| 10源,种子1 | 16709.225s | 14918.134s |
| 16源,种子2 | 16388.604s | 11635.398s |
| 16源,种子3 | 16983.270s | 12453.274s |
| 平均 | 17170.104s | **13164.737s(减少23.33%)** |
第四问尚未降到4000秒。保留域外49站是为未知朝向源提供发现证据;本轮优化减少重复移动,
没有用第三问假设缩小第四问覆盖范围。其后备清除仍是有预算上限的算法,不能据四场结果声称所有混合场景必定在预算内完成。
## 4. 数据及复现
- 第三问数据根目录:`output/q3/optimization-20260911/`,含冻结旧版代码、配置、逐场景真值、动作日志、summary及汇总。
- 最终开发集:`development/improved/results.json`;独立验证:`validation/verified/results.json`;对应旧版在各自`baseline/results.json`。
- 第四问:`output/q4/optimization-20260911/{baseline,improved}-{10,16}.json`。
- 中间开发试验也保留:同点补测、移动前瞻、路线终点、来向候选、路径计费及自适应发现。最终未采用均值变差的路线终点估计和未知频道自适应补扫。
- 回归测试:`pytest -q`,**166项全部通过**。新增用例验证串行6秒补测、不重复同地读数、频道状态成本、清除圆平移证书、无信号诊断及七站/49站的适用边界。
在项目根目录运行(label使用未存在的名称以保护已有证据):
```powershell
.venv/Scripts/python.exe scripts/benchmark_q3_revision.py --label rerun-development
.venv/Scripts/python.exe scripts/benchmark_q3_revision.py --label rerun-validation --suite validation
.venv/Scripts/python.exe scripts/benchmark_q3_revision.py --label rerun-baseline --suite validation --baseline
.venv/Scripts/python.exe scripts/benchmark_q4.py --seeds 0,1 --sources 10 --directional 3 --out output/q4/rerun-10.json
.venv/Scripts/python.exe scripts/benchmark_q4.py --seeds 2,3 --sources 16 --directional 8 --out output/q4/rerun-16.json
```
概率限制:网格幸存集是保守可行集,不是已知官方概率模型的精确贝叶斯后验。
均匀源分布、接收半径分布、代表读数和哈希误差均属测试或启发式假设。
完备覆盖与清除证书依赖题面给出的确定性边界,性能数字则依赖具体测试分布。
+4
View File
@@ -0,0 +1,4 @@
# logs/
正式测试/演练导出的 JSONL 与汇总日志放置目录。本地 P1 训练/评测日志实际写入
`output/hrl/...`;本目录用于最终支撑材料归档,不修改文件名。
+4
View File
@@ -0,0 +1,4 @@
# models/
最终冻结模型归档目录。训练脚本默认把模型写到 `--model` 指定路径;
P1 smoke 产物见 `output/hrl/p1-smoke-time/`。正式提交前把冻结模型复制到本目录并保留同名 `.json` 元数据。
-815
View File
@@ -1,815 +0,0 @@
[
{
"id": "normal",
"name": "三点交会",
"source": "synthetic",
"target": [
80,
90
],
"measurements": [
{
"x": -600,
"y": -250,
"bearing_deg": 27.21505117707799,
"half_angle_deg": 1.0
},
{
"x": 650,
"y": -100,
"bearing_deg": 161.015051177078,
"half_angle_deg": 1.0
},
{
"x": 100,
"y": 700,
"bearing_deg": 268.4721225527146,
"half_angle_deg": 1.0
}
],
"frames": [
{
"status": "unbounded",
"vertices": [
[
-600.0,
-250.0
]
],
"tolerance_m": 1e-09,
"diameter_infinite": true,
"diameter_m": null,
"recession_direction": [
0.8971423583981722,
0.44174154068614097
],
"message": "存在非零衰退方向,区域无界,直径为无穷大"
},
{
"status": "polygon",
"vertices": [
[
45.3908396669742,
96.27384560337686
],
[
80.26352724282751,
84.95314961314784
],
[
106.24825816469405,
97.74770218809789
],
[
71.61376404345356,
110.34332460542697
]
],
"tolerance_m": 6.294839156007086e-07,
"diameter_infinite": false,
"diameter_m": 60.87526295170263,
"diameter_pair": [
[
45.3908396669742,
96.27384560337686
],
[
106.24825816469405,
97.74770218809789
]
],
"area_m2": 778.9645078415749,
"diameter_circle": {
"center": [
75.81954891583413,
97.01077389573737
],
"radius_m": 30.437631475851315,
"max_vertex_distance_m": 30.43763147585132,
"covers": true,
"coverage_tolerance_m": 2.5179356624028345e-06,
"max_excess_m": 4.367922104569409e-15,
"outside_vertex_indices": [],
"witness_vertex": null
},
"minimum_enclosing_circle": {
"center": [
75.81954891583412,
97.01077389573737
],
"radius_m": 30.437631475851326
},
"guaranteed_clearable_at_mec_center": false,
"clearance_certificate": {
"model": "angular_intersection_only",
"clear_radius_m": 20.0,
"numerical_margin_m": 2.5179356624028345e-06,
"radius_with_margin_m": 30.437633993786985,
"remaining_margin_m": -10.437633993786985,
"center": [
75.81954891583412,
97.01077389573737
]
}
},
{
"status": "polygon",
"vertices": [
[
72.95144059951443,
87.32686714676511
],
[
80.26352724282755,
84.95314961314779
],
[
94.39740997503758,
91.91249455495989
],
[
94.49056873706193,
102.02365367196415
],
[
73.93038642999645,
109.5008321248877
]
],
"tolerance_m": 7.462871059079371e-07,
"diameter_infinite": false,
"diameter_m": 26.986082918182134,
"diameter_pair": [
[
94.39740997503758,
91.91249455495989
],
[
73.93038642999645,
109.5008321248877
]
],
"area_m2": 382.03705547378974,
"diameter_circle": {
"center": [
84.163898202517,
100.7066633399238
],
"radius_m": 13.493041459091067,
"max_vertex_distance_m": 17.45675089097608,
"covers": false,
"coverage_tolerance_m": 2.9851484236317485e-06,
"max_excess_m": 3.96370943188501,
"outside_vertex_indices": [
0,
1
],
"witness_vertex": [
72.95144059951443,
87.32686714676511
]
},
"minimum_enclosing_circle": {
"center": [
81.87363979249537,
98.04155797588491
],
"radius_m": 13.943107186092131
},
"guaranteed_clearable_at_mec_center": true,
"clearance_certificate": {
"model": "angular_intersection_only",
"clear_radius_m": 20.0,
"numerical_margin_m": 2.9851484236317485e-06,
"radius_with_margin_m": 13.943110171240557,
"remaining_margin_m": 6.056889828759443,
"center": [
81.87363979249537,
98.04155797588491
]
}
}
]
},
{
"id": "triangle",
"name": "直径圆反例",
"source": "synthetic",
"target": [
20,
11.547005383792515
],
"measurements": [
{
"x": -1000,
"y": 0,
"bearing_deg": 1,
"half_angle_deg": 1.0
},
{
"x": 540,
"y": -866.0254037844386,
"bearing_deg": 121,
"half_angle_deg": 1.0
},
{
"x": 520,
"y": 900.6664199358162,
"bearing_deg": 241,
"half_angle_deg": 1.0
}
],
"frames": [
{
"status": "unbounded",
"vertices": [
[
-1000.0,
0.0
]
],
"tolerance_m": 1e-09,
"diameter_infinite": true,
"diameter_m": null,
"recession_direction": [
1.0,
0.0
],
"message": "存在非零衰退方向,区域无界,直径为无穷大"
},
{
"status": "polygon",
"vertices": [
[
-22.483050583603557,
34.1356440648463
],
[
-1.1527327997955013,
0.0
],
[
40.000000000000284,
0.0
],
[
19.44641513032613,
35.59985327195466
]
],
"tolerance_m": 8.83402513014311e-07,
"diameter_infinite": false,
"diameter_m": 71.19953515266143,
"diameter_pair": [
[
-22.483050583603557,
34.1356440648463
],
[
40.000000000000284,
0.0
]
],
"area_m2": 1463.776307271084,
"diameter_circle": {
"center": [
8.75847470819835,
17.06782203242318
],
"radius_m": 35.59976757633071,
"max_vertex_distance_m": 35.59976757633074,
"covers": true,
"coverage_tolerance_m": 3.533610052057244e-06,
"max_excess_m": 2.4519345249005315e-14,
"outside_vertex_indices": [],
"witness_vertex": null
},
"minimum_enclosing_circle": {
"center": [
8.75847470819835,
17.06782203242318
],
"radius_m": 35.59976757633074
},
"guaranteed_clearable_at_mec_center": false,
"clearance_certificate": {
"model": "angular_intersection_only",
"clear_radius_m": 20.0,
"numerical_margin_m": 3.533610052057244e-06,
"radius_with_margin_m": 35.59977110994079,
"remaining_margin_m": -15.59977110994079,
"center": [
8.75847470819835,
17.06782203242318
]
}
},
{
"status": "polygon",
"vertices": [
[
-5.755396159656812e-13,
0.0
],
[
40.000000000000234,
0.0
],
[
19.999999999999847,
34.64101615137823
]
],
"tolerance_m": 1.0200653573832087e-06,
"diameter_infinite": false,
"diameter_m": 40.00000000000081,
"diameter_pair": [
[
-5.755396159656812e-13,
0.0
],
[
40.000000000000234,
0.0
]
],
"area_m2": 692.8203230275786,
"diameter_circle": {
"center": [
19.99999999999983,
0.0
],
"radius_m": 20.000000000000405,
"max_vertex_distance_m": 34.641016151378224,
"covers": false,
"coverage_tolerance_m": 4.080261429532835e-06,
"max_excess_m": 14.641016151377823,
"outside_vertex_indices": [
2
],
"witness_vertex": [
19.999999999999847,
34.64101615137823
]
},
"minimum_enclosing_circle": {
"center": [
19.99999999999983,
11.547005383792735
],
"radius_m": 23.094010767585495
},
"guaranteed_clearable_at_mec_center": false,
"clearance_certificate": {
"model": "angular_intersection_only",
"clear_radius_m": 20.0,
"numerical_margin_m": 4.080261429532835e-06,
"radius_with_margin_m": 23.094014847846925,
"remaining_margin_m": -3.0940148478469247,
"center": [
19.99999999999983,
11.547005383792735
]
}
}
]
},
{
"id": "poor",
"name": "近共线检测点",
"source": "synthetic",
"target": [
900,
50
],
"measurements": [
{
"x": 0,
"y": 0,
"bearing_deg": 3.1798301198642345,
"half_angle_deg": 1.0
},
{
"x": 100,
"y": 0,
"bearing_deg": 3.576334374997351,
"half_angle_deg": 1.0
},
{
"x": 200,
"y": 0,
"bearing_deg": 4.085616779974877,
"half_angle_deg": 1.0
}
],
"frames": [
{
"status": "unbounded",
"vertices": [
[
0.0,
0.0
]
],
"tolerance_m": 1e-09,
"diameter_infinite": true,
"diameter_m": null,
"recession_direction": [
0.9992763681850043,
0.03803603537420482
],
"message": "存在非零衰退方向,区域无界,直径为无穷大"
},
{
"status": "unbounded",
"vertices": [
[
190.67332308426802,
7.257709171010532
],
[
649.0783141257873,
24.706243940862326
],
[
1149.8860918066787,
84.03538833511422
]
],
"tolerance_m": 5.0000000000000004e-08,
"diameter_infinite": true,
"diameter_m": null,
"recession_direction": [
0.9973401975299315,
0.07288710716551386
],
"message": "存在非零衰退方向,区域无界,直径为无穷大"
},
{
"status": "unbounded",
"vertices": [
[
349.47112173238565,
13.302121787008087
],
[
649.0783141257873,
24.706243940862326
],
[
704.9784261478786,
27.221516841665565
],
[
1149.886091806679,
84.03538833511422
],
[
1094.1078143807658,
79.57076189541714
]
],
"tolerance_m": 1.0000000000000001e-07,
"diameter_infinite": true,
"diameter_m": null,
"recession_direction": [
0.9973401975299315,
0.07288710716551386
],
"message": "存在非零衰退方向,区域无界,直径为无穷大"
}
]
},
{
"id": "empty",
"name": "不相容观测",
"source": "synthetic",
"target": null,
"measurements": [
{
"x": -100,
"y": 0,
"bearing_deg": 180,
"half_angle_deg": 1.0
},
{
"x": 100,
"y": 0,
"bearing_deg": 0,
"half_angle_deg": 1.0
}
],
"frames": [
{
"status": "unbounded",
"vertices": [
[
-100.0,
0.0
]
],
"tolerance_m": 1e-09,
"diameter_infinite": true,
"diameter_m": null,
"recession_direction": [
-0.9998476951563913,
0.01745240643728344
],
"message": "存在非零衰退方向,区域无界,直径为无穷大"
},
{
"status": "empty",
"vertices": [],
"diameter_m": null,
"diameter_infinite": false,
"tolerance_m": 1.0000000000000001e-07,
"message": "测向角域没有公共点;检查频道、数据及误差界"
}
]
},
{
"id": "segment",
"name": "零误差退化线段",
"source": "synthetic",
"target": [
40,
0
],
"measurements": [
{
"x": 0,
"y": 0,
"bearing_deg": 0,
"half_angle_deg": 0
},
{
"x": 100,
"y": 0,
"bearing_deg": 180,
"half_angle_deg": 0
}
],
"frames": [
{
"status": "unbounded",
"vertices": [
[
0.0,
0.0
]
],
"tolerance_m": 1e-09,
"diameter_infinite": true,
"diameter_m": null,
"recession_direction": [
1.0,
0.0
],
"message": "存在非零衰退方向,区域无界,直径为无穷大"
},
{
"status": "segment",
"vertices": [
[
0.0,
0.0
],
[
100.0,
0.0
]
],
"tolerance_m": 5.0000000000000004e-08,
"diameter_infinite": false,
"diameter_m": 100.0,
"diameter_pair": [
[
0.0,
0.0
],
[
100.0,
0.0
]
],
"area_m2": 0.0,
"diameter_circle": {
"center": [
50.0,
0.0
],
"radius_m": 50.0,
"max_vertex_distance_m": 50.0,
"covers": true,
"coverage_tolerance_m": 2.0000000000000002e-07,
"max_excess_m": 0.0,
"outside_vertex_indices": [],
"witness_vertex": null
},
"minimum_enclosing_circle": {
"center": [
50.0,
0.0
],
"radius_m": 50.0
},
"guaranteed_clearable_at_mec_center": false,
"clearance_certificate": {
"model": "angular_intersection_only",
"clear_radius_m": 20.0,
"numerical_margin_m": 2.0000000000000002e-07,
"radius_with_margin_m": 50.0000002,
"remaining_margin_m": -30.000000200000002,
"center": [
50.0,
0.0
]
}
}
]
},
{
"id": "practice-U5ZW-P9KN-TCAG-R7ST",
"name": "官方演练 U5ZW-P9KN-TCAG-R7ST",
"source": "official_practice",
"target": null,
"measurements": [
{
"x": 0,
"y": 0,
"bearing_deg": 131.52,
"half_angle_deg": 1.0
},
{
"x": -613.7618853216438,
"y": 391.53077545248124,
"bearing_deg": 34.92,
"half_angle_deg": 1.0
},
{
"x": -314.2721344680094,
"y": 656.6833525352392,
"bearing_deg": 227.19,
"half_angle_deg": 1.0
}
],
"frames": [
{
"status": "unbounded",
"vertices": [
[
0.0,
0.0
]
],
"tolerance_m": 1e-09,
"diameter_infinite": true,
"diameter_m": null,
"recession_direction": [
-0.6497134405131868,
0.7601792191427741
],
"message": "存在非零衰退方向,区域无界,直径为无穷大"
},
{
"status": "polygon",
"vertices": [
[
-460.69830582516533,
502.4117755118043
],
[
-456.18958970820563,
497.4948221803977
],
[
-436.511353451526,
510.7280211898303
],
[
-441.37035594940073,
516.4131625064848
]
],
"tolerance_m": 3.6400549446402593e-07,
"diameter_infinite": false,
"diameter_m": 25.57672002626576,
"diameter_pair": [
[
-460.69830582516533,
502.4117755118043
],
[
-436.511353451526,
510.7280211898303
]
],
"area_m2": 167.16830387852585,
"diameter_circle": {
"center": [
-448.6048296383457,
506.5698983508173
],
"radius_m": 12.78836001313288,
"max_vertex_distance_m": 12.788360013132888,
"covers": true,
"coverage_tolerance_m": 1.4560219778561037e-06,
"max_excess_m": 7.577386519575496e-15,
"outside_vertex_indices": [],
"witness_vertex": null
},
"minimum_enclosing_circle": {
"center": [
-448.6048296383457,
506.5698983508173
],
"radius_m": 12.788360013132888
},
"guaranteed_clearable_at_mec_center": true,
"clearance_certificate": {
"model": "angular_intersection_only",
"clear_radius_m": 20.0,
"numerical_margin_m": 1.4560219778561037e-06,
"radius_with_margin_m": 12.788361469154864,
"remaining_margin_m": 7.211638530845136,
"center": [
-448.6048296383457,
506.5698983508173
]
}
},
{
"status": "polygon",
"vertices": [
[
-460.69830582516533,
502.41177551180425
],
[
-456.4242356873799,
497.75071394627366
],
[
-440.56989343525083,
515.4766035082919
],
[
-441.3703559494006,
516.4131625064849
]
],
"tolerance_m": 4.666666666666667e-07,
"diameter_infinite": false,
"diameter_m": 23.996723024535473,
"diameter_pair": [
[
-460.69830582516533,
502.41177551180425
],
[
-440.56989343525083,
515.4766035082919
]
],
"area_m2": 89.48455568449387,
"diameter_circle": {
"center": [
-450.6340996302081,
508.9441895100481
],
"radius_m": 11.998361512267737,
"max_vertex_distance_m": 12.60236369723464,
"covers": false,
"coverage_tolerance_m": 1.8666666666666669e-06,
"max_excess_m": 0.604002184966904,
"outside_vertex_indices": [
1
],
"witness_vertex": [
-456.4242356873799,
497.75071394627366
]
},
"minimum_enclosing_circle": {
"center": [
-449.97695530423994,
507.9528348831342
],
"radius_m": 12.068583013309823
},
"guaranteed_clearable_at_mec_center": true,
"clearance_certificate": {
"model": "angular_intersection_only",
"clear_radius_m": 20.0,
"numerical_margin_m": 1.8666666666666669e-06,
"radius_with_margin_m": 12.068584879976491,
"remaining_margin_m": 7.931415120023509,
"center": [
-449.97695530423994,
507.9528348831342
]
}
}
]
}
]
-8
View File
@@ -1,8 +0,0 @@
{
"status": "empty",
"vertices": [],
"diameter_m": null,
"diameter_infinite": false,
"tolerance_m": 1.0000000000000001e-07,
"message": "测向角域没有公共点;检查频道、数据及误差界"
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 5.5 KiB

-77
View File
@@ -1,77 +0,0 @@
{
"status": "polygon",
"vertices": [
[
72.95144059951443,
87.32686714676511
],
[
80.26352724282755,
84.95314961314779
],
[
94.39740997503758,
91.91249455495989
],
[
94.49056873706193,
102.02365367196415
],
[
73.93038642999645,
109.5008321248877
]
],
"tolerance_m": 7.462871059079371e-07,
"diameter_infinite": false,
"diameter_m": 26.986082918182134,
"diameter_pair": [
[
94.39740997503758,
91.91249455495989
],
[
73.93038642999645,
109.5008321248877
]
],
"area_m2": 382.03705547378974,
"diameter_circle": {
"center": [
84.163898202517,
100.7066633399238
],
"radius_m": 13.493041459091067,
"max_vertex_distance_m": 17.45675089097608,
"covers": false,
"coverage_tolerance_m": 2.9851484236317485e-06,
"max_excess_m": 3.96370943188501,
"outside_vertex_indices": [
0,
1
],
"witness_vertex": [
72.95144059951443,
87.32686714676511
]
},
"minimum_enclosing_circle": {
"center": [
81.87363979249537,
98.04155797588491
],
"radius_m": 13.943107186092131
},
"guaranteed_clearable_at_mec_center": true,
"clearance_certificate": {
"model": "angular_intersection_only",
"clear_radius_m": 20.0,
"numerical_margin_m": 2.9851484236317485e-06,
"radius_with_margin_m": 13.943110171240557,
"remaining_margin_m": 6.056889828759443,
"center": [
81.87363979249537,
98.04155797588491
]
}
}
-67
View File
@@ -1,67 +0,0 @@
{
"python": "3.12.14",
"timing_method": "median perf_counter; warm local process",
"timing_scope": "MEC only; not whole solver speedup or simulator/network performance",
"circle_benchmarks": [
{
"vertices": 4,
"reference_ms": 0.021899999410379678,
"incremental_ms": 0.028999995265621692,
"speedup": 0.7551725167466228,
"radius_difference_m": 0.0
},
{
"vertices": 20,
"reference_ms": 4.815899999812245,
"incremental_ms": 0.055900003644637764,
"speedup": 86.15205162467306,
"radius_difference_m": 3.552713678800501e-15
},
{
"vertices": 60,
"reference_ms": 306.5234000023338,
"incremental_ms": 0.09600000339560211,
"speedup": 3192.9519704202016,
"radius_difference_m": 0.0
}
],
"practice_sensitivity": [
{
"additional_half_angle_deg": 0,
"solve_ms": 0.09890000364976004,
"diameter_m": 23.996723024535473,
"mec_radius_m": 12.068583013309823,
"diameter_circle_excess_m": 0.604002184966904,
"clearance_margin_m": 7.931415120023509
},
{
"additional_half_angle_deg": 0.005,
"solve_ms": 0.0962999984039925,
"diameter_m": 24.119088149560866,
"mec_radius_m": 12.130376957888465,
"diameter_circle_excess_m": 0.608486475900504,
"clearance_margin_m": 7.869621175444868
}
],
"q2_first_bearing_certificate": {
"source_model": "first_bearing_sector_with_range_upper_bound",
"sector_enclosing_circle": {
"center": [
750.1153914962542,
0.0
],
"radius_m": 750.1153914962542
},
"sufficient_candidate_disk": {
"center": [
750.1153914962542,
0.0
],
"radius_m": 249.88460850374577
},
"candidate_nonempty_certified": true,
"distance_only": true,
"max_range_m": 1500,
"guaranteed_range_m": 1000
}
}
-33
View File
@@ -1,33 +0,0 @@
{
"status": "unbounded",
"vertices": [
[
349.47112173238565,
13.302121787008087
],
[
649.0783141257873,
24.706243940862326
],
[
704.9784261478786,
27.221516841665565
],
[
1149.886091806679,
84.03538833511422
],
[
1094.1078143807658,
79.57076189541714
]
],
"tolerance_m": 1.0000000000000001e-07,
"diameter_infinite": true,
"diameter_m": null,
"recession_direction": [
0.9973401975299315,
0.07288710716551386
],
"message": "存在非零衰退方向,区域无界,直径为无穷大"
}
@@ -1,72 +0,0 @@
{
"status": "polygon",
"vertices": [
[
-460.69830582516533,
502.41177551180425
],
[
-456.4242356873799,
497.75071394627366
],
[
-440.56989343525083,
515.4766035082919
],
[
-441.3703559494006,
516.4131625064849
]
],
"tolerance_m": 4.666666666666667e-07,
"diameter_infinite": false,
"diameter_m": 23.996723024535473,
"diameter_pair": [
[
-460.69830582516533,
502.41177551180425
],
[
-440.56989343525083,
515.4766035082919
]
],
"area_m2": 89.48455568449387,
"diameter_circle": {
"center": [
-450.6340996302081,
508.9441895100481
],
"radius_m": 11.998361512267737,
"max_vertex_distance_m": 12.60236369723464,
"covers": false,
"coverage_tolerance_m": 1.8666666666666669e-06,
"max_excess_m": 0.604002184966904,
"outside_vertex_indices": [
1
],
"witness_vertex": [
-456.4242356873799,
497.75071394627366
]
},
"minimum_enclosing_circle": {
"center": [
-449.97695530423994,
507.9528348831342
],
"radius_m": 12.068583013309823
},
"guaranteed_clearable_at_mec_center": true,
"clearance_certificate": {
"model": "angular_intersection_only",
"clear_radius_m": 20.0,
"numerical_margin_m": 1.8666666666666669e-06,
"radius_with_margin_m": 12.068584879976491,
"remaining_margin_m": 7.931415120023509,
"center": [
-449.97695530423994,
507.9528348831342
]
}
}
@@ -1,278 +0,0 @@
[
{
"path": "/enter",
"request": {
"arena_id": "default",
"robot_id": "202608023016",
"request_id": "q1-536f4d679c4f-1"
},
"response": {
"accepted": true,
"real_timestamp_ms": 1789041227659,
"virtual_time_s": 0,
"max_virtual_duration_s": 360000,
"max_real_duration_s": 1200,
"remaining_real_duration_s": 1200
},
"retries": 0
},
{
"path": "/measure",
"request": {
"arena_id": "default",
"robot_id": "202608023016",
"request_id": "q1-536f4d679c4f-2",
"position": {
"x": 0,
"y": 0
},
"channel": 1
},
"response": {
"accepted": true,
"real_timestamp_ms": 1789041227678,
"virtual_time_s": 5,
"measure_result": "no_signal"
},
"retries": 0
},
{
"path": "/measure",
"request": {
"arena_id": "default",
"robot_id": "202608023016",
"request_id": "q1-536f4d679c4f-3",
"position": {
"x": 0,
"y": 0
},
"channel": 2
},
"response": {
"accepted": true,
"real_timestamp_ms": 1789041227694,
"virtual_time_s": 11,
"measure_result": "no_signal"
},
"retries": 0
},
{
"path": "/measure",
"request": {
"arena_id": "default",
"robot_id": "202608023016",
"request_id": "q1-536f4d679c4f-4",
"position": {
"x": 0,
"y": 0
},
"channel": 3
},
"response": {
"accepted": true,
"real_timestamp_ms": 1789041227696,
"virtual_time_s": 17,
"measure_result": "no_signal"
},
"retries": 0
},
{
"path": "/measure",
"request": {
"arena_id": "default",
"robot_id": "202608023016",
"request_id": "q1-536f4d679c4f-5",
"position": {
"x": 0,
"y": 0
},
"channel": 4
},
"response": {
"accepted": true,
"real_timestamp_ms": 1789041227697,
"virtual_time_s": 23,
"measure_result": "no_signal"
},
"retries": 0
},
{
"path": "/measure",
"request": {
"arena_id": "default",
"robot_id": "202608023016",
"request_id": "q1-536f4d679c4f-6",
"position": {
"x": 0,
"y": 0
},
"channel": 5
},
"response": {
"accepted": true,
"real_timestamp_ms": 1789041227709,
"virtual_time_s": 29,
"measure_result": "no_signal"
},
"retries": 0
},
{
"path": "/measure",
"request": {
"arena_id": "default",
"robot_id": "202608023016",
"request_id": "q1-536f4d679c4f-7",
"position": {
"x": 0,
"y": 0
},
"channel": 6
},
"response": {
"accepted": true,
"real_timestamp_ms": 1789041227725,
"virtual_time_s": 35,
"measure_result": "no_signal"
},
"retries": 0
},
{
"path": "/measure",
"request": {
"arena_id": "default",
"robot_id": "202608023016",
"request_id": "q1-536f4d679c4f-8",
"position": {
"x": 0,
"y": 0
},
"channel": 7
},
"response": {
"accepted": true,
"real_timestamp_ms": 1789041227727,
"virtual_time_s": 41,
"measure_result": "no_signal"
},
"retries": 0
},
{
"path": "/measure",
"request": {
"arena_id": "default",
"robot_id": "202608023016",
"request_id": "q1-536f4d679c4f-9",
"position": {
"x": 0,
"y": 0
},
"channel": 8
},
"response": {
"accepted": true,
"real_timestamp_ms": 1789041227729,
"virtual_time_s": 47,
"measure_result": "no_signal"
},
"retries": 0
},
{
"path": "/measure",
"request": {
"arena_id": "default",
"robot_id": "202608023016",
"request_id": "q1-536f4d679c4f-10",
"position": {
"x": 0,
"y": 0
},
"channel": 9
},
"response": {
"accepted": true,
"real_timestamp_ms": 1789041227740,
"virtual_time_s": 53,
"measure_result": "direction",
"svd_deg": 131.52
},
"retries": 0
},
{
"path": "/measure",
"request": {
"arena_id": "default",
"robot_id": "202608023016",
"request_id": "q1-536f4d679c4f-11",
"position": {
"x": 0,
"y": 0
},
"channel": 9
},
"response": {
"accepted": true,
"real_timestamp_ms": 1789041227755,
"virtual_time_s": 58,
"measure_result": "direction",
"svd_deg": 131.52
},
"retries": 0
},
{
"path": "/measure",
"request": {
"arena_id": "default",
"robot_id": "202608023016",
"request_id": "q1-536f4d679c4f-12",
"position": {
"x": -613.7618853216438,
"y": 391.53077545248124
},
"channel": 9
},
"response": {
"accepted": true,
"real_timestamp_ms": 1789041227770,
"virtual_time_s": 208.602198,
"measure_result": "direction",
"svd_deg": 34.92
},
"retries": 0
},
{
"path": "/measure",
"request": {
"arena_id": "default",
"robot_id": "202608023016",
"request_id": "q1-536f4d679c4f-13",
"position": {
"x": -314.2721344680094,
"y": 656.6833525352392
},
"channel": 9
},
"response": {
"accepted": true,
"real_timestamp_ms": 1789041227772,
"virtual_time_s": 293.602198,
"measure_result": "direction",
"svd_deg": 227.19
},
"retries": 0
},
{
"path": "/exit",
"request": {
"arena_id": "default",
"robot_id": "202608023016",
"request_id": "q1-536f4d679c4f-14"
},
"response": {
"accepted": true,
"real_timestamp_ms": 1789041227775,
"virtual_time_s": 293.602198,
"exit_reason": "user_exit"
},
"retries": 0
}
]
@@ -1,87 +0,0 @@
{
"name": "官方问题3演练观测",
"source": "official_practice",
"case_code": "U5ZW-P9KN-TCAG-R7ST",
"channel": 9,
"ground_truth": null,
"measurements": [
{
"x": 0,
"y": 0,
"bearing_deg": 131.52,
"half_angle_deg": 1.0
},
{
"x": -613.7618853216438,
"y": 391.53077545248124,
"bearing_deg": 34.92,
"half_angle_deg": 1.0
},
{
"x": -314.2721344680094,
"y": 656.6833525352392,
"bearing_deg": 227.19,
"half_angle_deg": 1.0
}
],
"repeated_station_response": {
"accepted": true,
"real_timestamp_ms": 1789041227755,
"virtual_time_s": 58,
"measure_result": "direction",
"svd_deg": 131.52
},
"result": {
"status": "polygon",
"vertices": [
[
-460.69830582516533,
502.41177551180425
],
[
-456.4242356873799,
497.75071394627366
],
[
-440.56989343525083,
515.4766035082919
],
[
-441.3703559494006,
516.4131625064849
]
],
"tolerance_m": 4.666666666666667e-07,
"diameter_infinite": false,
"diameter_m": 23.996723024535473,
"diameter_pair": [
[
-460.69830582516533,
502.41177551180425
],
[
-440.56989343525083,
515.4766035082919
]
],
"area_m2": 89.48455568449387,
"diameter_circle": {
"center": [
-450.6340996302081,
508.9441895100481
],
"radius_m": 11.998361512267737,
"max_vertex_distance_m": 12.60236369723464,
"covers": false
},
"minimum_enclosing_circle": {
"center": [
-449.97695530423994,
507.9528348831342
],
"radius_m": 12.068583013309823
},
"guaranteed_clearable_at_mec_center": true
},
"scope": "仅采集问题一观测,不执行问题三全清除任务;没有调用clear"
}
File diff suppressed because one or more lines are too long
-59
View File
@@ -1,59 +0,0 @@
{
"status": "segment",
"vertices": [
[
0.0,
0.0
],
[
100.0,
0.0
]
],
"tolerance_m": 5.0000000000000004e-08,
"diameter_infinite": false,
"diameter_m": 100.0,
"diameter_pair": [
[
0.0,
0.0
],
[
100.0,
0.0
]
],
"area_m2": 0.0,
"diameter_circle": {
"center": [
50.0,
0.0
],
"radius_m": 50.0,
"max_vertex_distance_m": 50.0,
"covers": true,
"coverage_tolerance_m": 2.0000000000000002e-07,
"max_excess_m": 0.0,
"outside_vertex_indices": [],
"witness_vertex": null
},
"minimum_enclosing_circle": {
"center": [
50.0,
0.0
],
"radius_m": 50.0
},
"guaranteed_clearable_at_mec_center": false,
"clearance_certificate": {
"model": "angular_intersection_only",
"clear_radius_m": 20.0,
"numerical_margin_m": 2.0000000000000002e-07,
"radius_with_margin_m": 50.0000002,
"remaining_margin_m": -30.000000200000002,
"center": [
50.0,
0.0
]
}
}
-48
View File
@@ -1,48 +0,0 @@
{
"status": "polygon",
"vertices": [
[
-5.755396159656812e-13,
0.0
],
[
40.000000000000234,
0.0
],
[
19.999999999999847,
34.64101615137823
]
],
"tolerance_m": 1.0200653573832087e-06,
"diameter_infinite": false,
"diameter_m": 40.00000000000081,
"diameter_pair": [
[
-5.755396159656812e-13,
0.0
],
[
40.000000000000234,
0.0
]
],
"area_m2": 692.8203230275786,
"diameter_circle": {
"center": [
19.99999999999983,
0.0
],
"radius_m": 20.000000000000405,
"max_vertex_distance_m": 34.641016151378224,
"covers": false
},
"minimum_enclosing_circle": {
"center": [
19.99999999999983,
11.547005383792735
],
"radius_m": 23.094010767585495
},
"guaranteed_clearable_at_mec_center": false
}
-68
View File
@@ -1,68 +0,0 @@
{
"status": "polygon",
"vertices": [
[
-5.755396159656812e-13,
0.0
],
[
40.000000000000234,
0.0
],
[
19.999999999999847,
34.64101615137823
]
],
"tolerance_m": 1.0200653573832087e-06,
"diameter_infinite": false,
"diameter_m": 40.00000000000081,
"diameter_pair": [
[
-5.755396159656812e-13,
0.0
],
[
40.000000000000234,
0.0
]
],
"area_m2": 692.8203230275786,
"diameter_circle": {
"center": [
19.99999999999983,
0.0
],
"radius_m": 20.000000000000405,
"max_vertex_distance_m": 34.641016151378224,
"covers": false,
"coverage_tolerance_m": 4.080261429532835e-06,
"max_excess_m": 14.641016151377823,
"outside_vertex_indices": [
2
],
"witness_vertex": [
19.999999999999847,
34.64101615137823
]
},
"minimum_enclosing_circle": {
"center": [
19.99999999999983,
11.547005383792735
],
"radius_m": 23.094010767585495
},
"guaranteed_clearable_at_mec_center": false,
"clearance_certificate": {
"model": "angular_intersection_only",
"clear_radius_m": 20.0,
"numerical_margin_m": 4.080261429532835e-06,
"radius_with_margin_m": 23.094014847846925,
"remaining_margin_m": -3.0940148478469247,
"center": [
19.99999999999983,
11.547005383792735
]
}
}
-7
View File
@@ -1,7 +0,0 @@
{
"interaction_states_checked": 32,
"missing_selectors": 0,
"nonfinite_svg_values": 0,
"browser_layout_verified": false,
"note": "Browser local-file navigation blocked by browser URL policy. Event handlers checked in minimal DOM; SVG statically inspected separately."
}
-288
View File
@@ -1,288 +0,0 @@
{
"summary": {
"config": {
"sources": 10,
"directional": 3,
"bearing_margin_deg": 0.005
},
"games": 16,
"completed": 16,
"all_cleared": 16,
"measures_total": 9453,
"clears_total": 471,
"total_requests_total": 9924,
"fallback_attempts_total": 316,
"fallback_attempts_max": 165,
"virtual_time_s_max": 18756.107,
"mismatch_total": 0,
"anomaly_total": 0
},
"games": [
{
"seed": 0,
"sources": 10,
"directional": 3,
"completed": true,
"stop_reason": "all_resolved",
"all_cleared": true,
"measures": 623,
"clears": 10,
"total_requests": 633,
"fallback_cells": [],
"fallback_attempts": 0,
"virtual_time_s": 17147.133,
"virtual_recompute_mismatches": 0,
"anomaly_count": 0
},
{
"seed": 1,
"sources": 10,
"directional": 3,
"completed": true,
"stop_reason": "all_resolved",
"all_cleared": true,
"measures": 591,
"clears": 10,
"total_requests": 601,
"fallback_cells": [],
"fallback_attempts": 0,
"virtual_time_s": 16383.06,
"virtual_recompute_mismatches": 0,
"anomaly_count": 0
},
{
"seed": 2,
"sources": 10,
"directional": 3,
"completed": true,
"stop_reason": "all_resolved",
"all_cleared": true,
"measures": 580,
"clears": 10,
"total_requests": 590,
"fallback_cells": [],
"fallback_attempts": 0,
"virtual_time_s": 16380.242,
"virtual_recompute_mismatches": 0,
"anomaly_count": 0
},
{
"seed": 3,
"sources": 10,
"directional": 3,
"completed": true,
"stop_reason": "all_resolved",
"all_cleared": true,
"measures": 559,
"clears": 10,
"total_requests": 569,
"fallback_cells": [],
"fallback_attempts": 0,
"virtual_time_s": 14575.032,
"virtual_recompute_mismatches": 0,
"anomaly_count": 0
},
{
"seed": 4,
"sources": 10,
"directional": 3,
"completed": true,
"stop_reason": "all_resolved",
"all_cleared": true,
"measures": 576,
"clears": 10,
"total_requests": 586,
"fallback_cells": [],
"fallback_attempts": 0,
"virtual_time_s": 16128.325,
"virtual_recompute_mismatches": 0,
"anomaly_count": 0
},
{
"seed": 5,
"sources": 10,
"directional": 3,
"completed": true,
"stop_reason": "all_resolved",
"all_cleared": true,
"measures": 609,
"clears": 10,
"total_requests": 619,
"fallback_cells": [],
"fallback_attempts": 0,
"virtual_time_s": 15963.724,
"virtual_recompute_mismatches": 0,
"anomaly_count": 0
},
{
"seed": 6,
"sources": 10,
"directional": 3,
"completed": true,
"stop_reason": "all_resolved",
"all_cleared": true,
"measures": 624,
"clears": 10,
"total_requests": 634,
"fallback_cells": [],
"fallback_attempts": 0,
"virtual_time_s": 16364.634,
"virtual_recompute_mismatches": 0,
"anomaly_count": 0
},
{
"seed": 7,
"sources": 10,
"directional": 3,
"completed": true,
"stop_reason": "all_resolved",
"all_cleared": true,
"measures": 569,
"clears": 10,
"total_requests": 579,
"fallback_cells": [],
"fallback_attempts": 0,
"virtual_time_s": 17010.511,
"virtual_recompute_mismatches": 0,
"anomaly_count": 0
},
{
"seed": 8,
"sources": 10,
"directional": 3,
"completed": true,
"stop_reason": "all_resolved",
"all_cleared": true,
"measures": 599,
"clears": 12,
"total_requests": 611,
"fallback_cells": [
5
],
"fallback_attempts": 3,
"virtual_time_s": 18756.107,
"virtual_recompute_mismatches": 0,
"anomaly_count": 0
},
{
"seed": 9,
"sources": 10,
"directional": 3,
"completed": true,
"stop_reason": "all_resolved",
"all_cleared": true,
"measures": 671,
"clears": 13,
"total_requests": 684,
"fallback_cells": [
5
],
"fallback_attempts": 4,
"virtual_time_s": 17391.766,
"virtual_recompute_mismatches": 0,
"anomaly_count": 0
},
{
"seed": 10,
"sources": 10,
"directional": 3,
"completed": true,
"stop_reason": "all_resolved",
"all_cleared": true,
"measures": 613,
"clears": 10,
"total_requests": 623,
"fallback_cells": [],
"fallback_attempts": 0,
"virtual_time_s": 17981.333,
"virtual_recompute_mismatches": 0,
"anomaly_count": 0
},
{
"seed": 11,
"sources": 10,
"directional": 3,
"completed": true,
"stop_reason": "all_resolved",
"all_cleared": true,
"measures": 552,
"clears": 149,
"total_requests": 701,
"fallback_cells": [
142
],
"fallback_attempts": 140,
"virtual_time_s": 17099.436,
"virtual_recompute_mismatches": 0,
"anomaly_count": 0
},
{
"seed": 12,
"sources": 10,
"directional": 3,
"completed": true,
"stop_reason": "all_resolved",
"all_cleared": true,
"measures": 543,
"clears": 174,
"total_requests": 717,
"fallback_cells": [
168
],
"fallback_attempts": 165,
"virtual_time_s": 17802.514,
"virtual_recompute_mismatches": 0,
"anomaly_count": 0
},
{
"seed": 13,
"sources": 10,
"directional": 3,
"completed": true,
"stop_reason": "all_resolved",
"all_cleared": true,
"measures": 579,
"clears": 10,
"total_requests": 589,
"fallback_cells": [],
"fallback_attempts": 0,
"virtual_time_s": 16392.501,
"virtual_recompute_mismatches": 0,
"anomaly_count": 0
},
{
"seed": 14,
"sources": 10,
"directional": 3,
"completed": true,
"stop_reason": "all_resolved",
"all_cleared": true,
"measures": 587,
"clears": 13,
"total_requests": 600,
"fallback_cells": [
5
],
"fallback_attempts": 4,
"virtual_time_s": 16565.569,
"virtual_recompute_mismatches": 0,
"anomaly_count": 0
},
{
"seed": 15,
"sources": 10,
"directional": 3,
"completed": true,
"stop_reason": "all_resolved",
"all_cleared": true,
"measures": 578,
"clears": 10,
"total_requests": 588,
"fallback_cells": [],
"fallback_attempts": 0,
"virtual_time_s": 15288.608,
"virtual_recompute_mismatches": 0,
"anomaly_count": 0
}
]
}
-195
View File
@@ -1,195 +0,0 @@
{
"summary": {
"config": {
"sources": 16,
"directional": 6,
"bearing_margin_deg": 0.005
},
"games": 10,
"completed": 10,
"all_cleared": 10,
"measures_total": 3222,
"clears_total": 1024,
"total_requests_total": 4246,
"fallback_attempts_total": 872,
"fallback_attempts_max": 199,
"virtual_time_s_max": 18879.215,
"mismatch_total": 0,
"anomaly_total": 0
},
"games": [
{
"seed": 0,
"sources": 16,
"directional": 6,
"completed": true,
"stop_reason": "sixteen_cleared",
"all_cleared": true,
"measures": 375,
"clears": 16,
"total_requests": 391,
"fallback_cells": [],
"fallback_attempts": 0,
"virtual_time_s": 17651.199,
"virtual_recompute_mismatches": 0,
"anomaly_count": 0
},
{
"seed": 1,
"sources": 16,
"directional": 6,
"completed": true,
"stop_reason": "sixteen_cleared",
"all_cleared": true,
"measures": 356,
"clears": 16,
"total_requests": 372,
"fallback_cells": [],
"fallback_attempts": 0,
"virtual_time_s": 17003.576,
"virtual_recompute_mismatches": 0,
"anomaly_count": 0
},
{
"seed": 2,
"sources": 16,
"directional": 6,
"completed": true,
"stop_reason": "sixteen_cleared",
"all_cleared": true,
"measures": 297,
"clears": 214,
"total_requests": 511,
"fallback_cells": [
204
],
"fallback_attempts": 199,
"virtual_time_s": 18137.964,
"virtual_recompute_mismatches": 0,
"anomaly_count": 0
},
{
"seed": 3,
"sources": 16,
"directional": 6,
"completed": true,
"stop_reason": "sixteen_cleared",
"all_cleared": true,
"measures": 346,
"clears": 16,
"total_requests": 362,
"fallback_cells": [],
"fallback_attempts": 0,
"virtual_time_s": 16885.059,
"virtual_recompute_mismatches": 0,
"anomaly_count": 0
},
{
"seed": 4,
"sources": 16,
"directional": 6,
"completed": true,
"stop_reason": "sixteen_cleared",
"all_cleared": true,
"measures": 372,
"clears": 177,
"total_requests": 549,
"fallback_cells": [
201,
172
],
"fallback_attempts": 163,
"virtual_time_s": 18525.209,
"virtual_recompute_mismatches": 0,
"anomaly_count": 0
},
{
"seed": 5,
"sources": 16,
"directional": 6,
"completed": true,
"stop_reason": "sixteen_cleared",
"all_cleared": true,
"measures": 330,
"clears": 145,
"total_requests": 475,
"fallback_cells": [
130,
6
],
"fallback_attempts": 131,
"virtual_time_s": 18089.132,
"virtual_recompute_mismatches": 0,
"anomaly_count": 0
},
{
"seed": 6,
"sources": 16,
"directional": 6,
"completed": true,
"stop_reason": "sixteen_cleared",
"all_cleared": true,
"measures": 187,
"clears": 209,
"total_requests": 396,
"fallback_cells": [
207
],
"fallback_attempts": 194,
"virtual_time_s": 15131.949,
"virtual_recompute_mismatches": 0,
"anomaly_count": 0
},
{
"seed": 7,
"sources": 16,
"directional": 6,
"completed": true,
"stop_reason": "sixteen_cleared",
"all_cleared": true,
"measures": 258,
"clears": 199,
"total_requests": 457,
"fallback_cells": [
188,
6
],
"fallback_attempts": 185,
"virtual_time_s": 18879.215,
"virtual_recompute_mismatches": 0,
"anomaly_count": 0
},
{
"seed": 8,
"sources": 16,
"directional": 6,
"completed": true,
"stop_reason": "sixteen_cleared",
"all_cleared": true,
"measures": 325,
"clears": 16,
"total_requests": 341,
"fallback_cells": [],
"fallback_attempts": 0,
"virtual_time_s": 16301.93,
"virtual_recompute_mismatches": 0,
"anomaly_count": 0
},
{
"seed": 9,
"sources": 16,
"directional": 6,
"completed": true,
"stop_reason": "sixteen_cleared",
"all_cleared": true,
"measures": 376,
"clears": 16,
"total_requests": 392,
"fallback_cells": [],
"fallback_attempts": 0,
"virtual_time_s": 15287.399,
"virtual_recompute_mismatches": 0,
"anomaly_count": 0
}
]
}
+29
View File
@@ -0,0 +1,29 @@
# project/ 薄兼容层
题目 Prompt 要求 `project/` 目录树;本仓库的真实算法保持唯一一份,
位于 `src/mcm26b`、`scripts/`。本目录只做 re-export/转发,不复制实现。
| Prompt 路径 | 真实实现 |
|---|---|
| `env/simulator_client.py` | `src/mcm26b/protocol/client.py` |
| `env/local_env.py` | `src/mcm26b/hrl/hierarchical_env.py`, `low_level_env.py` |
| `env/particle_filter.py` | `src/mcm26b/particles.py` |
| `policies/high_level_ppo.py` | `src/mcm26b/hrl/policy.py` |
| `policies/low_level_ppo.py` | `src/mcm26b/hrl/policy.py`, `low_level_env.py` |
| `train/curriculum.py` | `src/mcm26b/hrl/curriculum.py` |
| `train/domain_randomization.py` | `src/mcm26b/hrl/curriculum.py` + 配置包装 |
| `eval/evaluate.py` | `scripts/benchmark_hrl.py` |
| `eval/ablation.py` | `scripts/ablate_hrl.py`(P2) |
| `baselines/heuristic.py` | `src/mcm26b/planner/hrl_loop.py` 启发式降级 |
| `baselines/end_to_end_ppo.py` | 显式未实现;单源局部 PPO 不冒充 E2E |
使用示例:
```python
from project.env.local_env import make_local_env
env = make_local_env(problem=3, stage=1, seed=7)
obs, info = env.reset(seed=7)
```
P1 当前交付:`benchmark_hrl.py`、`train_hrl_high.py`、CLI `--strategy hrl`、
`--q4-strategy hrl`。P2 的 BC/Optuna/消融在 smoke 通过后另开。
+4
View File
@@ -0,0 +1,4 @@
"""题目 Prompt 所需目录树的薄兼容层。
真实实现仍位于 ``src/mcm26b``;本包只做 re-export / CLI 转发,避免两份算法逻辑。
"""
+3
View File
@@ -0,0 +1,3 @@
from .heuristic import HeuristicHRL, run_heuristic
__all__ = ["HeuristicHRL", "run_heuristic"]
+16
View File
@@ -0,0 +1,16 @@
"""端到端 PPO 基线占位。
按本轮确认:当前只交付局部 PPO 基线;raw-observation/raw-action 的真正
端到端 PPO 不在 P0+P1 范围内,因此显式标记未实现,避免把单源局部选点
PPO 误称为端到端 baseline。
"""
from __future__ import annotations
def train_end_to_end_ppo(*args, **kwargs):
raise NotImplementedError(
"本轮只交付局部 PPO 基线;真正的端到端 PPO 基线标记为未实现,"
"需单独立项定义原始观测、动作与安全执行边界。")
__all__ = ["train_end_to_end_ppo"]
+16
View File
@@ -0,0 +1,16 @@
"""兼容入口:信息增益/TSP/覆盖规则 + 高层启发式调度。"""
from mcm26b.planner.bayes_loop import BayesLoop
from mcm26b.planner.hrl_loop import HRLLoop
def HeuristicHRL(*args, **kwargs): # pylint: disable=invalid-name # 兼容入口保留题面公开名
"""返回无模型的高层 HRL 闭环(heuristic_action 降级路径)。"""
kwargs.setdefault("high_policy", None)
return HRLLoop(*args, **kwargs)
def run_heuristic(*args, **kwargs):
return HeuristicHRL(*args, **kwargs).run()
__all__ = ["HeuristicHRL", "run_heuristic", "BayesLoop", "HRLLoop"]
+6
View File
@@ -0,0 +1,6 @@
from .simulator_client import SimulatorClient
from .local_env import HierarchicalEnv, LowLevelEnv, HRLReward, make_local_env
from .particle_filter import ChannelParticles
__all__ = ["SimulatorClient", "HierarchicalEnv", "LowLevelEnv", "HRLReward",
"make_local_env", "ChannelParticles"]
+22
View File
@@ -0,0 +1,22 @@
"""兼容入口:本地简化训练环境。
HierarchicalEnv 是高层宏动作环境;LowLevelEnv 是独立八方向导航环境。
本文件不复制实现,只转发 mcm26b.hrl 下的真实类。
"""
from mcm26b.hrl.hierarchical_env import HRLReward, HierarchicalEnv
from mcm26b.hrl.low_level_env import LowLevelEnv
__all__ = ["HierarchicalEnv", "LowLevelEnv", "HRLReward", "make_local_env"]
def make_local_env(*, problem: int = 3, stage: int = 4, seed: int | None = None,
fidelity: str = "production"):
"""按题目 Prompt 的四阶段编号创建训练环境。"""
from mcm26b.hrl.curriculum import STAGES
if not 1 <= int(stage) <= len(STAGES):
raise ValueError(f"stage 必须在 1..{len(STAGES)}")
env = HierarchicalEnv(problem=problem, stage=STAGES[int(stage) - 1],
fidelity=fidelity)
if seed is not None:
env.reset(seed=seed)
return env
+4
View File
@@ -0,0 +1,4 @@
"""兼容入口:逐频道网格粒子/幸存集。"""
from mcm26b.particles import ChannelParticles, angular_gap
__all__ = ["ChannelParticles", "angular_gap"]
+8
View File
@@ -0,0 +1,8 @@
"""兼容入口:真实类见 mcm26b.protocol.client。"""
from mcm26b.protocol.client import (ActionRejected, ClearOutcome, DeadlineExceeded,
ExitInfo, MeasureOutcome, ProtocolError,
ProtocolViolation, SimulatorClient)
__all__ = ["SimulatorClient", "MeasureOutcome", "ClearOutcome", "ExitInfo",
"ProtocolError", "ActionRejected", "ProtocolViolation",
"DeadlineExceeded"]
+4
View File
@@ -0,0 +1,4 @@
from .evaluate import run_benchmark
from .ablation import run_ablation
__all__ = ["run_benchmark", "run_ablation"]
+20
View File
@@ -0,0 +1,20 @@
"""兼容入口:调用 scripts/ablate_hrl.py(P2 后启用)。"""
from __future__ import annotations
import importlib.util
from pathlib import Path
def run_ablation(argv=None) -> int:
path = Path(__file__).resolve().parents[2] / "scripts" / "ablate_hrl.py"
if not path.exists():
raise NotImplementedError(
"scripts/ablate_hrl.py 尚未实现;P1 阶段只保证 benchmark/training/CLI。")
spec = importlib.util.spec_from_file_location("ablate_hrl_compat", path)
module = importlib.util.module_from_spec(spec)
assert spec.loader is not None
spec.loader.exec_module(module)
return int(module.main(argv))
__all__ = ["run_ablation"]
+17
View File
@@ -0,0 +1,17 @@
"""兼容入口:调用 scripts/benchmark_hrl.py。"""
from __future__ import annotations
import importlib.util
from pathlib import Path
def run_benchmark(argv=None) -> int:
path = Path(__file__).resolve().parents[2] / "scripts" / "benchmark_hrl.py"
spec = importlib.util.spec_from_file_location("benchmark_hrl_compat", path)
module = importlib.util.module_from_spec(spec)
assert spec.loader is not None
spec.loader.exec_module(module)
return int(module.main(argv))
__all__ = ["run_benchmark"]
+5
View File
@@ -0,0 +1,5 @@
from .high_level_ppo import HighLevelPolicy, load_high_policy
from .low_level_ppo import LowLevelPolicy, load_low_policy
__all__ = ["HighLevelPolicy", "load_high_policy",
"LowLevelPolicy", "load_low_policy"]
+10
View File
@@ -0,0 +1,10 @@
"""兼容入口:高层 MaskablePPO 推理策略。"""
from mcm26b.hrl.policy import HighLevelPolicy
def load_high_policy(model_path: str, *, device: str = "auto",
problem: int | None = None) -> HighLevelPolicy:
return HighLevelPolicy(model_path, device=device, problem=problem)
__all__ = ["HighLevelPolicy", "load_high_policy"]
+9
View File
@@ -0,0 +1,9 @@
"""兼容入口:低层八方向到达策略(离线导航实验)。"""
from mcm26b.hrl.policy import LowLevelPolicy
def load_low_policy(model_path: str, *, device: str = "auto") -> LowLevelPolicy:
return LowLevelPolicy(model_path, device=device)
__all__ = ["LowLevelPolicy", "load_low_policy"]
+5
View File
@@ -0,0 +1,5 @@
from .curriculum import STAGES, Stage, make_scene, training_config
from .domain_randomization import randomize_problem_config
__all__ = ["STAGES", "Stage", "make_scene", "training_config",
"randomize_problem_config"]
+6
View File
@@ -0,0 +1,6 @@
"""兼容入口:课程学习与域随机化。"""
from mcm26b.hrl.curriculum import (STAGES, Stage, make_scene, stage_with_noise,
training_config, training_spec)
__all__ = ["STAGES", "Stage", "make_scene", "stage_with_noise",
"training_config", "training_spec"]
+31
View File
@@ -0,0 +1,31 @@
"""兼容入口:本地域随机化参数包装。
当前真实随机化在 mcm26b.hrl.curriculum.make_scene 内完成;这里只暴露
ProblemConfig 的显式替换助手,便于 Optuna/消融在实验配置中记录随机化范围。
"""
from __future__ import annotations
import dataclasses
from random import Random
from mcm26b.config import ProblemConfig
def randomize_problem_config(base: ProblemConfig, *, seed: int,
bearing_error_half_deg: float | None = None,
r_eff_range: tuple[float, float] | None = None
) -> ProblemConfig:
"""返回带域随机化标量的新配置;不修改全局默认配置。"""
_rng = Random(seed) # 固定种子留作兼容:真实随机化在 make_scene 内完成
field = base.field
if bearing_error_half_deg is None:
bearing_error_half_deg = field.bearing_error_half_deg
if r_eff_range is not None:
lo, hi = r_eff_range
if not 0 <= lo <= hi:
raise ValueError("r_eff_range 必须为递增非负区间")
return dataclasses.replace(base, field=dataclasses.replace(
field, bearing_error_half_deg=float(bearing_error_half_deg)))
__all__ = ["randomize_problem_config"]
+20
View File
@@ -0,0 +1,20 @@
"""兼容入口:调用 scripts/tune_hrl.py(P2 后启用)。"""
from __future__ import annotations
import importlib.util
from pathlib import Path
def run_tuning(argv=None) -> int:
path = Path(__file__).resolve().parents[2] / "scripts" / "tune_hrl.py"
if not path.exists():
raise NotImplementedError(
"scripts/tune_hrl.py 尚未实现;P1 阶段先冻结超参并留待 P2。")
spec = importlib.util.spec_from_file_location("tune_hrl_compat", path)
module = importlib.util.module_from_spec(spec)
assert spec.loader is not None
spec.loader.exec_module(module)
return int(module.main(argv))
__all__ = ["run_tuning"]
+93 -1
View File
@@ -10,7 +10,34 @@ dependencies = [
[dependency-groups] [dependency-groups]
dev = [ dev = [
"optuna>=5.0.0",
"pytest>=8", "pytest>=8",
# 独立评测机(自建模拟器):仅测试侧依赖,运行时代码零导入(懒加载 + 守卫测试)
"mcm26b-referee",
]
rl = [
"gymnasium>=1.1",
"sb3-contrib==2.9.0",
"stable-baselines3>=2.6",
"torch==2.11.0",
]
# uv 工作区:referee/ 是独立包(零第三方依赖、不依赖 mcm26b),双方仅通过 HTTP 协议耦合
[tool.uv.workspace]
members = ["referee"]
# RL 训练使用 CUDA 12.8 版 PyTorch(本机 RTX 4060;运行时依赖保持轻量)。
[[tool.uv.index]]
name = "pytorch-cu128"
url = "https://download.pytorch.org/whl/cu128"
explicit = true
[tool.uv.sources]
mcm26b-referee = { workspace = true }
# CUDA 12.8 版 PyTorch 仅限 Windows/Linux(RTX 4060 训练机);
# macOS (Apple Silicon, MPS) 回落到 PyPI 官方轮子。
torch = [
{ index = "pytorch-cu128", marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
] ]
[build-system] [build-system]
@@ -24,4 +51,69 @@ packages = ["src/mcm26b"]
mcm26b = "mcm26b.cli:main" mcm26b = "mcm26b.cli:main"
[tool.pytest.ini_options] [tool.pytest.ini_options]
testpaths = ["tests"] testpaths = ["tests", "referee/tests"]
# ---------------------------------------------------------------------------
# pylint:全仓库静态检查口径
#
# 目标:`python -m pylint src scripts project tests referee main.py` 拿 10.00/10。
# 原则:真实的逻辑 / 资源 / 命名 / 死代码问题一律改代码;下面 disable 的每一条
# 都对应项目刻意的设计选择或已确认的静态分析误报,并写明理由,不做"一键静音"。
# ---------------------------------------------------------------------------
[tool.pylint.main]
py-version = "3.10"
jobs = 0
# 不写 ~/Library/Caches/pylint:CI 与受限沙箱里没有该目录,会刷警告
persistent = false
recursive = true
ignore = [".venv", ".uv-cache", "output"]
[tool.pylint.format]
max-line-length = 100
[tool.pylint.design]
# 阈值按"实际最长函数 + 余量"设定,而不是关闭检查:超过即说明该拆了
max-args = 14
max-positional-arguments = 10
max-locals = 45
max-branches = 26
max-statements = 180
max-attributes = 32
max-nested-blocks = 6
max-returns = 12
max-module-lines = 4000
min-public-methods = 0
[tool.pylint.basic]
# 这些是既有代码里的短名/别名(importorskip 后的模块句柄、几何单字母常量、
# 坐标系里的 x/y/z),让命名检查接受它们。
# 注意:good-names 不覆盖 redefined-builtin——stdlib 回调签名里的 `format`
# 之类只能在对应 def 上写行内 disable(见 server.py / referee 的 log_message)。
good-names = [
"i", "j", "k", "n", "m", "x", "y", "z", "_", "ex",
"id", "pytest", "numpy", "S", "ROOT", "FONT", "ET",
]
# http.server 要求 do_GET/do_POST 这类方法名,不能改成 snake_case
method-rgx = "[a-z_][a-z0-9_]{2,49}$|do_(GET|POST|PUT|DELETE|HEAD|PATCH|OPTIONS)$"
[tool.pylint."messages control"]
disable = [
# —— 项目刻意风格 ——
"missing-module-docstring", # 内部小模块与 __main__ 不强制
"missing-class-docstring",
"missing-function-docstring",
"import-outside-toplevel", # torch/SB3/referee 等重依赖与可选依赖懒加载
"wrong-import-position", # 同上:importorskip 守卫后的延迟导入
"ungrouped-imports", # 同上:延迟导入无法与顶部导入分组
"broad-exception-caught", # 子进程 / IO / HTTP 边界防御式兜底,逐处注释
"protected-access", # 测试与 mixin 有意检查内部状态
"duplicate-code", # 脚本与测试夹具存在结构性相似,非复制粘贴缺陷
"redefined-outer-name", # pytest 要求测试参数与 fixture 同名
"attribute-defined-outside-init", # 轻量对象在 helper 里补字段
"multiple-statements", # 单行守卫 (if x: return) 是可读性选择
"use-dict-literal", # dict(...) 与 {...} 混用是既有风格
"abstract-method", # 无头训练环境不需要实现 gym.Env.render
# —— 已确认的静态分析误报 ——
"possibly-used-before-assignment", # pytest.importorskip 成功即赋值,skip 会抛异常
]
@@ -0,0 +1,126 @@
{"event": "session_start", "case_code": "6X58-XQ7U-CJ24-JQQR", "module": "q4_practice", "robot_id": "1", "seed": 477239471, "countdown_s": 5.0, "window_s": 1500.0, "max_real_duration_s": 1200.0, "truth": [{"channel": 17, "x": -1178.1831977517415, "y": -1351.4242739803465, "r_eff_m": 1058.2857083120255, "directional": true, "heading_deg": 228.91780452670704}, {"channel": 1, "x": 616.704399833097, "y": 1665.0757168990908, "r_eff_m": 1324.912278063795, "directional": true, "heading_deg": 69.67656996032791}, {"channel": 7, "x": 875.9714308961434, "y": -356.413074975542, "r_eff_m": 1417.6410943244593, "directional": true, "heading_deg": 226.02323139790843}, {"channel": 15, "x": 77.58288789485773, "y": -1312.1259181145524, "r_eff_m": 1099.6028939269304, "directional": false, "heading_deg": 0.0}, {"channel": 10, "x": 569.7512600859908, "y": -287.53357300110514, "r_eff_m": 1163.1765321843473, "directional": false, "heading_deg": 0.0}, {"channel": 20, "x": -669.5662948265503, "y": -665.215319006332, "r_eff_m": 1376.2325668493522, "directional": false, "heading_deg": 0.0}, {"channel": 5, "x": -508.03938834052923, "y": 606.335297392372, "r_eff_m": 1447.9949377114212, "directional": false, "heading_deg": 0.0}, {"channel": 13, "x": 306.16535401564636, "y": 115.13724934212215, "r_eff_m": 1379.981981356019, "directional": false, "heading_deg": 0.0}, {"channel": 19, "x": 611.4980531732139, "y": 346.74044433389514, "r_eff_m": 1363.037307441268, "directional": false, "heading_deg": 0.0}, {"channel": 8, "x": -1328.739848763958, "y": 356.8075133866415, "r_eff_m": 1184.9420447581683, "directional": false, "heading_deg": 0.0}, {"channel": 16, "x": 1381.669496042516, "y": -1070.127602503145, "r_eff_m": 1413.4108109277565, "directional": false, "heading_deg": 0.0}, {"channel": 12, "x": -417.0933355563403, "y": -928.2011450309352, "r_eff_m": 1356.0447298689226, "directional": false, "heading_deg": 0.0}, {"channel": 2, "x": -1498.1076807839925, "y": -382.17536931742296, "r_eff_m": 1189.186960250608, "directional": false, "heading_deg": 0.0}]}
{"event": "action", "seq": 1, "wall_ts_ms": 1789097890374, "method": "POST", "path": "/enter", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0001"}, "response": {"accepted": true, "real_timestamp_ms": 1789097890374, "virtual_time_s": 0.0, "max_virtual_duration_s": 360000.0, "max_real_duration_s": 1200, "remaining_real_duration_s": 1200}}
{"event": "action", "seq": 2, "wall_ts_ms": 1789097890402, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0002", "position": {"x": 0.0, "y": 0.0}, "channel": 1}, "response": {"accepted": true, "real_timestamp_ms": 1789097890402, "virtual_time_s": 5.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 3, "wall_ts_ms": 1789097890411, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0003", "position": {"x": 0.0, "y": 0.0}, "channel": 2}, "response": {"accepted": true, "real_timestamp_ms": 1789097890411, "virtual_time_s": 11.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 4, "wall_ts_ms": 1789097890417, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0004", "position": {"x": 0.0, "y": 0.0}, "channel": 3}, "response": {"accepted": true, "real_timestamp_ms": 1789097890417, "virtual_time_s": 17.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 5, "wall_ts_ms": 1789097890424, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0005", "position": {"x": 0.0, "y": 0.0}, "channel": 4}, "response": {"accepted": true, "real_timestamp_ms": 1789097890424, "virtual_time_s": 23.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 6, "wall_ts_ms": 1789097890428, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0006", "position": {"x": 0.0, "y": 0.0}, "channel": 5}, "response": {"accepted": true, "real_timestamp_ms": 1789097890428, "virtual_time_s": 29.0, "measure_result": "direction", "svd_deg": 129.52}}
{"event": "action", "seq": 7, "wall_ts_ms": 1789097890457, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0007", "position": {"x": 0.0, "y": 0.0}, "channel": 6}, "response": {"accepted": true, "real_timestamp_ms": 1789097890457, "virtual_time_s": 35.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 8, "wall_ts_ms": 1789097890461, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0008", "position": {"x": 0.0, "y": 0.0}, "channel": 7}, "response": {"accepted": true, "real_timestamp_ms": 1789097890461, "virtual_time_s": 41.0, "measure_result": "direction", "svd_deg": 338.47}}
{"event": "action", "seq": 9, "wall_ts_ms": 1789097890490, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0009", "position": {"x": 0.0, "y": 0.0}, "channel": 8}, "response": {"accepted": true, "real_timestamp_ms": 1789097890490, "virtual_time_s": 47.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 10, "wall_ts_ms": 1789097890495, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0010", "position": {"x": 0.0, "y": 0.0}, "channel": 9}, "response": {"accepted": true, "real_timestamp_ms": 1789097890495, "virtual_time_s": 53.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 11, "wall_ts_ms": 1789097890499, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0011", "position": {"x": 0.0, "y": 0.0}, "channel": 10}, "response": {"accepted": true, "real_timestamp_ms": 1789097890499, "virtual_time_s": 59.0, "measure_result": "direction", "svd_deg": 333.15}}
{"event": "action", "seq": 12, "wall_ts_ms": 1789097890528, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0012", "position": {"x": 0.0, "y": 0.0}, "channel": 11}, "response": {"accepted": true, "real_timestamp_ms": 1789097890528, "virtual_time_s": 65.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 13, "wall_ts_ms": 1789097890536, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0013", "position": {"x": 0.0, "y": 0.0}, "channel": 12}, "response": {"accepted": true, "real_timestamp_ms": 1789097890535, "virtual_time_s": 71.0, "measure_result": "direction", "svd_deg": 246.6}}
{"event": "action", "seq": 14, "wall_ts_ms": 1789097890576, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0014", "position": {"x": 0.0, "y": 0.0}, "channel": 13}, "response": {"accepted": true, "real_timestamp_ms": 1789097890576, "virtual_time_s": 77.0, "measure_result": "direction", "svd_deg": 21.6}}
{"event": "action", "seq": 15, "wall_ts_ms": 1789097890619, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0015", "position": {"x": 0.0, "y": 0.0}, "channel": 14}, "response": {"accepted": true, "real_timestamp_ms": 1789097890619, "virtual_time_s": 83.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 16, "wall_ts_ms": 1789097890626, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0016", "position": {"x": 0.0, "y": 0.0}, "channel": 15}, "response": {"accepted": true, "real_timestamp_ms": 1789097890626, "virtual_time_s": 89.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 17, "wall_ts_ms": 1789097890632, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0017", "position": {"x": 0.0, "y": 0.0}, "channel": 16}, "response": {"accepted": true, "real_timestamp_ms": 1789097890632, "virtual_time_s": 95.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 18, "wall_ts_ms": 1789097890638, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0018", "position": {"x": 0.0, "y": 0.0}, "channel": 17}, "response": {"accepted": true, "real_timestamp_ms": 1789097890638, "virtual_time_s": 101.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 19, "wall_ts_ms": 1789097890644, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0019", "position": {"x": 0.0, "y": 0.0}, "channel": 18}, "response": {"accepted": true, "real_timestamp_ms": 1789097890644, "virtual_time_s": 107.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 20, "wall_ts_ms": 1789097890649, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0020", "position": {"x": 0.0, "y": 0.0}, "channel": 19}, "response": {"accepted": true, "real_timestamp_ms": 1789097890649, "virtual_time_s": 113.0, "measure_result": "direction", "svd_deg": 30.39}}
{"event": "action", "seq": 21, "wall_ts_ms": 1789097890682, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0021", "position": {"x": 0.0, "y": 0.0}, "channel": 20}, "response": {"accepted": true, "real_timestamp_ms": 1789097890682, "virtual_time_s": 119.0, "measure_result": "direction", "svd_deg": 224.38}}
{"event": "action", "seq": 22, "wall_ts_ms": 1789097890911, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0022", "position": {"x": -56.50838450880073, "y": -623.7611294008199}, "channel": 12}, "response": {"accepted": true, "real_timestamp_ms": 1789097890911, "virtual_time_s": 250.263106, "measure_result": "direction", "svd_deg": 220.85}}
{"event": "action", "seq": 23, "wall_ts_ms": 1789097891082, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0023", "position": {"x": -395.918006797198, "y": -649.411475208361}, "channel": 20}, "response": {"accepted": true, "real_timestamp_ms": 1789097891082, "virtual_time_s": 324.338603, "measure_result": "direction", "svd_deg": 182.47}}
{"event": "action", "seq": 24, "wall_ts_ms": 1789097891085, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0024", "position": {"x": -395.918006797198, "y": -649.411475208361}, "channel": 12}, "response": {"accepted": true, "real_timestamp_ms": 1789097891085, "virtual_time_s": 330.338603, "measure_result": "direction", "svd_deg": 266.06}}
{"event": "action", "seq": 25, "wall_ts_ms": 1789097891235, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0025", "position": {"x": -677.2147882619608, "y": -662.0789583420399}, "channel": 20}, "response": {"accepted": true, "real_timestamp_ms": 1789097891235, "virtual_time_s": 392.654975, "measure_result": "direction", "svd_deg": 338.23}}
{"event": "action", "seq": 26, "wall_ts_ms": 1789097891236, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0026", "position": {"x": -677.2147882619608, "y": -662.0789583420399}, "channel": 20}, "response": {"accepted": true, "real_timestamp_ms": 1789097891236, "virtual_time_s": 397.654975, "clear_result": "success"}}
{"event": "action", "seq": 27, "wall_ts_ms": 1789097891371, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0027", "position": {"x": -416.69419258062, "y": -934.0015882185094}, "channel": 12}, "response": {"accepted": true, "real_timestamp_ms": 1789097891371, "virtual_time_s": 477.971081, "clear_result": "success"}}
{"event": "action", "seq": 28, "wall_ts_ms": 1789097891504, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0028", "position": {"x": -579.8500000000006, "y": -1004.3296607688133}, "channel": 1}, "response": {"accepted": true, "real_timestamp_ms": 1789097891504, "virtual_time_s": 519.504646, "measure_result": "no_signal"}}
{"event": "action", "seq": 29, "wall_ts_ms": 1789097891508, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0029", "position": {"x": -579.8500000000006, "y": -1004.3296607688133}, "channel": 2}, "response": {"accepted": true, "real_timestamp_ms": 1789097891508, "virtual_time_s": 525.504646, "measure_result": "direction", "svd_deg": 145.0}}
{"event": "action", "seq": 30, "wall_ts_ms": 1789097891531, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0030", "position": {"x": -579.8500000000006, "y": -1004.3296607688133}, "channel": 3}, "response": {"accepted": true, "real_timestamp_ms": 1789097891531, "virtual_time_s": 531.504646, "measure_result": "no_signal"}}
{"event": "action", "seq": 31, "wall_ts_ms": 1789097891536, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0031", "position": {"x": -579.8500000000006, "y": -1004.3296607688133}, "channel": 4}, "response": {"accepted": true, "real_timestamp_ms": 1789097891536, "virtual_time_s": 537.504646, "measure_result": "no_signal"}}
{"event": "action", "seq": 32, "wall_ts_ms": 1789097891544, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0032", "position": {"x": -579.8500000000006, "y": -1004.3296607688133}, "channel": 6}, "response": {"accepted": true, "real_timestamp_ms": 1789097891544, "virtual_time_s": 543.504646, "measure_result": "no_signal"}}
{"event": "action", "seq": 33, "wall_ts_ms": 1789097891547, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0033", "position": {"x": -579.8500000000006, "y": -1004.3296607688133}, "channel": 8}, "response": {"accepted": true, "real_timestamp_ms": 1789097891547, "virtual_time_s": 549.504646, "measure_result": "no_signal"}}
{"event": "action", "seq": 34, "wall_ts_ms": 1789097891551, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0034", "position": {"x": -579.8500000000006, "y": -1004.3296607688133}, "channel": 9}, "response": {"accepted": true, "real_timestamp_ms": 1789097891551, "virtual_time_s": 555.504646, "measure_result": "no_signal"}}
{"event": "action", "seq": 35, "wall_ts_ms": 1789097891556, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0035", "position": {"x": -579.8500000000006, "y": -1004.3296607688133}, "channel": 11}, "response": {"accepted": true, "real_timestamp_ms": 1789097891556, "virtual_time_s": 561.504646, "measure_result": "no_signal"}}
{"event": "action", "seq": 36, "wall_ts_ms": 1789097891567, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0036", "position": {"x": -579.8500000000006, "y": -1004.3296607688133}, "channel": 14}, "response": {"accepted": true, "real_timestamp_ms": 1789097891567, "virtual_time_s": 567.504646, "measure_result": "no_signal"}}
{"event": "action", "seq": 37, "wall_ts_ms": 1789097891579, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0037", "position": {"x": -579.8500000000006, "y": -1004.3296607688133}, "channel": 15}, "response": {"accepted": true, "real_timestamp_ms": 1789097891579, "virtual_time_s": 573.504646, "measure_result": "direction", "svd_deg": 334.49}}
{"event": "action", "seq": 38, "wall_ts_ms": 1789097891600, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0038", "position": {"x": -579.8500000000006, "y": -1004.3296607688133}, "channel": 16}, "response": {"accepted": true, "real_timestamp_ms": 1789097891600, "virtual_time_s": 579.504646, "measure_result": "no_signal"}}
{"event": "action", "seq": 39, "wall_ts_ms": 1789097891608, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0039", "position": {"x": -579.8500000000006, "y": -1004.3296607688133}, "channel": 17}, "response": {"accepted": true, "real_timestamp_ms": 1789097891608, "virtual_time_s": 585.504646, "measure_result": "no_signal"}}
{"event": "action", "seq": 40, "wall_ts_ms": 1789097891611, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0040", "position": {"x": -579.8500000000006, "y": -1004.3296607688133}, "channel": 18}, "response": {"accepted": true, "real_timestamp_ms": 1789097891611, "virtual_time_s": 591.504646, "measure_result": "no_signal"}}
{"event": "action", "seq": 41, "wall_ts_ms": 1789097891814, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0041", "position": {"x": -1015.3031768622058, "y": -487.18523982583633}, "channel": 2}, "response": {"accepted": true, "real_timestamp_ms": 1789097891814, "virtual_time_s": 732.716749, "measure_result": "direction", "svd_deg": 168.65}}
{"event": "action", "seq": 42, "wall_ts_ms": 1789097891986, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0042", "position": {"x": -1445.1368680915793, "y": -399.765559583966}, "channel": 2}, "response": {"accepted": true, "real_timestamp_ms": 1789097891986, "virtual_time_s": 825.443416, "measure_result": "direction", "svd_deg": 160.85}}
{"event": "action", "seq": 43, "wall_ts_ms": 1789097892159, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0043", "position": {"x": -1475.621893761271, "y": -389.72231937134376}, "channel": 2}, "response": {"accepted": true, "real_timestamp_ms": 1789097892159, "virtual_time_s": 836.862773, "measure_result": "direction", "svd_deg": 161.74}}
{"event": "action", "seq": 44, "wall_ts_ms": 1789097892162, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0044", "position": {"x": -1488.5778163464163, "y": -385.4540125603667}, "channel": 2}, "response": {"accepted": true, "real_timestamp_ms": 1789097892162, "virtual_time_s": 844.590955, "clear_result": "success"}}
{"event": "action", "seq": 45, "wall_ts_ms": 1789097892328, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0045", "position": {"x": -1159.7, "y": 1.4202228929711856e-13}, "channel": 1}, "response": {"accepted": true, "real_timestamp_ms": 1789097892328, "virtual_time_s": 951.929085, "measure_result": "no_signal"}}
{"event": "action", "seq": 46, "wall_ts_ms": 1789097892331, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0046", "position": {"x": -1159.7, "y": 1.4202228929711856e-13}, "channel": 3}, "response": {"accepted": true, "real_timestamp_ms": 1789097892331, "virtual_time_s": 957.929085, "measure_result": "no_signal"}}
{"event": "action", "seq": 47, "wall_ts_ms": 1789097892333, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0047", "position": {"x": -1159.7, "y": 1.4202228929711856e-13}, "channel": 4}, "response": {"accepted": true, "real_timestamp_ms": 1789097892333, "virtual_time_s": 963.929085, "measure_result": "no_signal"}}
{"event": "action", "seq": 48, "wall_ts_ms": 1789097892336, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0048", "position": {"x": -1159.7, "y": 1.4202228929711856e-13}, "channel": 6}, "response": {"accepted": true, "real_timestamp_ms": 1789097892336, "virtual_time_s": 969.929085, "measure_result": "no_signal"}}
{"event": "action", "seq": 49, "wall_ts_ms": 1789097892338, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0049", "position": {"x": -1159.7, "y": 1.4202228929711856e-13}, "channel": 8}, "response": {"accepted": true, "real_timestamp_ms": 1789097892338, "virtual_time_s": 975.929085, "measure_result": "direction", "svd_deg": 114.49}}
{"event": "action", "seq": 50, "wall_ts_ms": 1789097892355, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0050", "position": {"x": -1159.7, "y": 1.4202228929711856e-13}, "channel": 9}, "response": {"accepted": true, "real_timestamp_ms": 1789097892355, "virtual_time_s": 981.929085, "measure_result": "no_signal"}}
{"event": "action", "seq": 51, "wall_ts_ms": 1789097892362, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0051", "position": {"x": -1159.7, "y": 1.4202228929711856e-13}, "channel": 11}, "response": {"accepted": true, "real_timestamp_ms": 1789097892362, "virtual_time_s": 987.929085, "measure_result": "no_signal"}}
{"event": "action", "seq": 52, "wall_ts_ms": 1789097892365, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0052", "position": {"x": -1159.7, "y": 1.4202228929711856e-13}, "channel": 14}, "response": {"accepted": true, "real_timestamp_ms": 1789097892365, "virtual_time_s": 993.929085, "measure_result": "no_signal"}}
{"event": "action", "seq": 53, "wall_ts_ms": 1789097892369, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0053", "position": {"x": -1159.7, "y": 1.4202228929711856e-13}, "channel": 16}, "response": {"accepted": true, "real_timestamp_ms": 1789097892369, "virtual_time_s": 999.929085, "measure_result": "no_signal"}}
{"event": "action", "seq": 54, "wall_ts_ms": 1789097892372, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0054", "position": {"x": -1159.7, "y": 1.4202228929711856e-13}, "channel": 17}, "response": {"accepted": true, "real_timestamp_ms": 1789097892372, "virtual_time_s": 1005.929085, "measure_result": "no_signal"}}
{"event": "action", "seq": 55, "wall_ts_ms": 1789097892374, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0055", "position": {"x": -1159.7, "y": 1.4202228929711856e-13}, "channel": 18}, "response": {"accepted": true, "real_timestamp_ms": 1789097892374, "virtual_time_s": 1011.929085, "measure_result": "no_signal"}}
{"event": "action", "seq": 56, "wall_ts_ms": 1789097892550, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0056", "position": {"x": -1156.2699064822978, "y": 431.08058643700474}, "channel": 8}, "response": {"accepted": true, "real_timestamp_ms": 1789097892550, "virtual_time_s": 1104.147932, "measure_result": "direction", "svd_deg": 204.2}}
{"event": "action", "seq": 57, "wall_ts_ms": 1789097892554, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0057", "position": {"x": -1311.9381820719905, "y": 361.24482040167044}, "channel": 8}, "response": {"accepted": true, "real_timestamp_ms": 1789097892554, "virtual_time_s": 1143.27103, "clear_result": "success"}}
{"event": "action", "seq": 58, "wall_ts_ms": 1789097892723, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0058", "position": {"x": -718.1413851639411, "y": 643.7959617866113}, "channel": 5}, "response": {"accepted": true, "real_timestamp_ms": 1789097892723, "virtual_time_s": 1280.789817, "measure_result": "direction", "svd_deg": 350.4}}
{"event": "action", "seq": 59, "wall_ts_ms": 1789097892862, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0059", "position": {"x": -500.89208942120797, "y": 606.5963774288548}, "channel": 5}, "response": {"accepted": true, "real_timestamp_ms": 1789097892862, "virtual_time_s": 1329.872043, "measure_result": "direction", "svd_deg": 182.0}}
{"event": "action", "seq": 60, "wall_ts_ms": 1789097892864, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0060", "position": {"x": -500.89208942120797, "y": 606.5963774288548}, "channel": 5}, "response": {"accepted": true, "real_timestamp_ms": 1789097892864, "virtual_time_s": 1334.872043, "clear_result": "success"}}
{"event": "action", "seq": 61, "wall_ts_ms": 1789097893000, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0061", "position": {"x": -579.8499999999998, "y": 1004.3296607688136}, "channel": 1}, "response": {"accepted": true, "real_timestamp_ms": 1789097893000, "virtual_time_s": 1421.971024, "measure_result": "no_signal"}}
{"event": "action", "seq": 62, "wall_ts_ms": 1789097893003, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0062", "position": {"x": -579.8499999999998, "y": 1004.3296607688136}, "channel": 3}, "response": {"accepted": true, "real_timestamp_ms": 1789097893003, "virtual_time_s": 1427.971024, "measure_result": "no_signal"}}
{"event": "action", "seq": 63, "wall_ts_ms": 1789097893006, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0063", "position": {"x": -579.8499999999998, "y": 1004.3296607688136}, "channel": 4}, "response": {"accepted": true, "real_timestamp_ms": 1789097893006, "virtual_time_s": 1433.971024, "measure_result": "no_signal"}}
{"event": "action", "seq": 64, "wall_ts_ms": 1789097893009, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0064", "position": {"x": -579.8499999999998, "y": 1004.3296607688136}, "channel": 6}, "response": {"accepted": true, "real_timestamp_ms": 1789097893009, "virtual_time_s": 1439.971024, "measure_result": "no_signal"}}
{"event": "action", "seq": 65, "wall_ts_ms": 1789097893012, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0065", "position": {"x": -579.8499999999998, "y": 1004.3296607688136}, "channel": 9}, "response": {"accepted": true, "real_timestamp_ms": 1789097893012, "virtual_time_s": 1445.971024, "measure_result": "no_signal"}}
{"event": "action", "seq": 66, "wall_ts_ms": 1789097893015, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0066", "position": {"x": -579.8499999999998, "y": 1004.3296607688136}, "channel": 11}, "response": {"accepted": true, "real_timestamp_ms": 1789097893015, "virtual_time_s": 1451.971024, "measure_result": "no_signal"}}
{"event": "action", "seq": 67, "wall_ts_ms": 1789097893019, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0067", "position": {"x": -579.8499999999998, "y": 1004.3296607688136}, "channel": 14}, "response": {"accepted": true, "real_timestamp_ms": 1789097893019, "virtual_time_s": 1457.971024, "measure_result": "no_signal"}}
{"event": "action", "seq": 68, "wall_ts_ms": 1789097893022, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0068", "position": {"x": -579.8499999999998, "y": 1004.3296607688136}, "channel": 16}, "response": {"accepted": true, "real_timestamp_ms": 1789097893022, "virtual_time_s": 1463.971024, "measure_result": "no_signal"}}
{"event": "action", "seq": 69, "wall_ts_ms": 1789097893027, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0069", "position": {"x": -579.8499999999998, "y": 1004.3296607688136}, "channel": 17}, "response": {"accepted": true, "real_timestamp_ms": 1789097893027, "virtual_time_s": 1469.971024, "measure_result": "no_signal"}}
{"event": "action", "seq": 70, "wall_ts_ms": 1789097893029, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0070", "position": {"x": -579.8499999999998, "y": 1004.3296607688136}, "channel": 18}, "response": {"accepted": true, "real_timestamp_ms": 1789097893029, "virtual_time_s": 1475.971024, "measure_result": "no_signal"}}
{"event": "action", "seq": 71, "wall_ts_ms": 1789097893175, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0071", "position": {"x": 579.8500000000001, "y": 1004.3296607688135}, "channel": 18}, "response": {"accepted": true, "real_timestamp_ms": 1789097893175, "virtual_time_s": 1712.911024, "measure_result": "no_signal"}}
{"event": "action", "seq": 72, "wall_ts_ms": 1789097893178, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0072", "position": {"x": 579.8500000000001, "y": 1004.3296607688135}, "channel": 1}, "response": {"accepted": true, "real_timestamp_ms": 1789097893178, "virtual_time_s": 1718.911024, "measure_result": "no_signal"}}
{"event": "action", "seq": 73, "wall_ts_ms": 1789097893181, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0073", "position": {"x": 579.8500000000001, "y": 1004.3296607688135}, "channel": 3}, "response": {"accepted": true, "real_timestamp_ms": 1789097893181, "virtual_time_s": 1724.911024, "measure_result": "no_signal"}}
{"event": "action", "seq": 74, "wall_ts_ms": 1789097893184, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0074", "position": {"x": 579.8500000000001, "y": 1004.3296607688135}, "channel": 4}, "response": {"accepted": true, "real_timestamp_ms": 1789097893184, "virtual_time_s": 1730.911024, "measure_result": "no_signal"}}
{"event": "action", "seq": 75, "wall_ts_ms": 1789097893187, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0075", "position": {"x": 579.8500000000001, "y": 1004.3296607688135}, "channel": 6}, "response": {"accepted": true, "real_timestamp_ms": 1789097893187, "virtual_time_s": 1736.911024, "measure_result": "no_signal"}}
{"event": "action", "seq": 76, "wall_ts_ms": 1789097893189, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0076", "position": {"x": 579.8500000000001, "y": 1004.3296607688135}, "channel": 9}, "response": {"accepted": true, "real_timestamp_ms": 1789097893189, "virtual_time_s": 1742.911024, "measure_result": "no_signal"}}
{"event": "action", "seq": 77, "wall_ts_ms": 1789097893192, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0077", "position": {"x": 579.8500000000001, "y": 1004.3296607688135}, "channel": 11}, "response": {"accepted": true, "real_timestamp_ms": 1789097893192, "virtual_time_s": 1748.911024, "measure_result": "no_signal"}}
{"event": "action", "seq": 78, "wall_ts_ms": 1789097893194, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0078", "position": {"x": 579.8500000000001, "y": 1004.3296607688135}, "channel": 14}, "response": {"accepted": true, "real_timestamp_ms": 1789097893194, "virtual_time_s": 1754.911024, "measure_result": "no_signal"}}
{"event": "action", "seq": 79, "wall_ts_ms": 1789097893197, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0079", "position": {"x": 579.8500000000001, "y": 1004.3296607688135}, "channel": 16}, "response": {"accepted": true, "real_timestamp_ms": 1789097893197, "virtual_time_s": 1760.911024, "measure_result": "no_signal"}}
{"event": "action", "seq": 80, "wall_ts_ms": 1789097893200, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0080", "position": {"x": 579.8500000000001, "y": 1004.3296607688135}, "channel": 17}, "response": {"accepted": true, "real_timestamp_ms": 1789097893200, "virtual_time_s": 1766.911024, "measure_result": "no_signal"}}
{"event": "action", "seq": 81, "wall_ts_ms": 1789097893337, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0081", "position": {"x": 620.5177715246222, "y": 627.4795573759427}, "channel": 19}, "response": {"accepted": true, "real_timestamp_ms": 1789097893337, "virtual_time_s": 1848.718641, "measure_result": "direction", "svd_deg": 267.9}}
{"event": "action", "seq": 82, "wall_ts_ms": 1789097893338, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0082", "position": {"x": 610.3522309687227, "y": 358.7211633093649}, "channel": 19}, "response": {"accepted": true, "real_timestamp_ms": 1789097893338, "virtual_time_s": 1907.508756, "clear_result": "success"}}
{"event": "action", "seq": 83, "wall_ts_ms": 1789097893340, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0083", "position": {"x": 610.3522309687227, "y": 358.7211633093649}, "channel": 13}, "response": {"accepted": true, "real_timestamp_ms": 1789097893340, "virtual_time_s": 1913.508756, "measure_result": "direction", "svd_deg": 218.89}}
{"event": "action", "seq": 84, "wall_ts_ms": 1789097893428, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0084", "position": {"x": 324.9336720474606, "y": 129.42995154285606}, "channel": 13}, "response": {"accepted": true, "real_timestamp_ms": 1789097893428, "virtual_time_s": 1991.731215, "measure_result": "direction", "svd_deg": 217.51}}
{"event": "action", "seq": 85, "wall_ts_ms": 1789097893429, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0085", "position": {"x": 319.81645135573456, "y": 125.64271630078247}, "channel": 13}, "response": {"accepted": true, "real_timestamp_ms": 1789097893429, "virtual_time_s": 1998.004464, "clear_result": "success"}}
{"event": "action", "seq": 86, "wall_ts_ms": 1789097893508, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0086", "position": {"x": 633.1131080089233, "y": -33.88884703594749}, "channel": 7}, "response": {"accepted": true, "real_timestamp_ms": 1789097893508, "virtual_time_s": 2074.319502, "measure_result": "no_signal"}}
{"event": "action", "seq": 87, "wall_ts_ms": 1789097893511, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0087", "position": {"x": 633.1131080089233, "y": -33.88884703594749}, "channel": 10}, "response": {"accepted": true, "real_timestamp_ms": 1789097893511, "virtual_time_s": 2080.319502, "measure_result": "direction", "svd_deg": 256.29}}
{"event": "action", "seq": 88, "wall_ts_ms": 1789097893538, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0088", "position": {"x": 1159.7, "y": 0.0}, "channel": 1}, "response": {"accepted": true, "real_timestamp_ms": 1789097893538, "virtual_time_s": 2191.854749, "measure_result": "no_signal"}}
{"event": "action", "seq": 89, "wall_ts_ms": 1789097893542, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0089", "position": {"x": 1159.7, "y": 0.0}, "channel": 3}, "response": {"accepted": true, "real_timestamp_ms": 1789097893542, "virtual_time_s": 2197.854749, "measure_result": "no_signal"}}
{"event": "action", "seq": 90, "wall_ts_ms": 1789097893545, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0090", "position": {"x": 1159.7, "y": 0.0}, "channel": 4}, "response": {"accepted": true, "real_timestamp_ms": 1789097893545, "virtual_time_s": 2203.854749, "measure_result": "no_signal"}}
{"event": "action", "seq": 91, "wall_ts_ms": 1789097893548, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0091", "position": {"x": 1159.7, "y": 0.0}, "channel": 6}, "response": {"accepted": true, "real_timestamp_ms": 1789097893548, "virtual_time_s": 2209.854749, "measure_result": "no_signal"}}
{"event": "action", "seq": 92, "wall_ts_ms": 1789097893550, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0092", "position": {"x": 1159.7, "y": 0.0}, "channel": 9}, "response": {"accepted": true, "real_timestamp_ms": 1789097893550, "virtual_time_s": 2215.854749, "measure_result": "no_signal"}}
{"event": "action", "seq": 93, "wall_ts_ms": 1789097893552, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0093", "position": {"x": 1159.7, "y": 0.0}, "channel": 11}, "response": {"accepted": true, "real_timestamp_ms": 1789097893552, "virtual_time_s": 2221.854749, "measure_result": "no_signal"}}
{"event": "action", "seq": 94, "wall_ts_ms": 1789097893554, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0094", "position": {"x": 1159.7, "y": 0.0}, "channel": 14}, "response": {"accepted": true, "real_timestamp_ms": 1789097893554, "virtual_time_s": 2227.854749, "measure_result": "no_signal"}}
{"event": "action", "seq": 95, "wall_ts_ms": 1789097893557, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0095", "position": {"x": 1159.7, "y": 0.0}, "channel": 16}, "response": {"accepted": true, "real_timestamp_ms": 1789097893557, "virtual_time_s": 2233.854749, "measure_result": "direction", "svd_deg": 282.51}}
{"event": "action", "seq": 96, "wall_ts_ms": 1789097893567, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0096", "position": {"x": 1159.7, "y": 0.0}, "channel": 17}, "response": {"accepted": true, "real_timestamp_ms": 1789097893567, "virtual_time_s": 2239.854749, "measure_result": "no_signal"}}
{"event": "action", "seq": 97, "wall_ts_ms": 1789097893569, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0097", "position": {"x": 1159.7, "y": 0.0}, "channel": 18}, "response": {"accepted": true, "real_timestamp_ms": 1789097893569, "virtual_time_s": 2245.854749, "measure_result": "no_signal"}}
{"event": "action", "seq": 98, "wall_ts_ms": 1789097893620, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0098", "position": {"x": 1400.966492396071, "y": -459.8888959615904}, "channel": 16}, "response": {"accepted": true, "real_timestamp_ms": 1789097893620, "virtual_time_s": 2355.721455, "measure_result": "direction", "svd_deg": 268.11}}
{"event": "action", "seq": 99, "wall_ts_ms": 1789097893652, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0099", "position": {"x": 1384.3965578790708, "y": -1021.7034041040436}, "channel": 16}, "response": {"accepted": true, "real_timestamp_ms": 1789097893652, "virtual_time_s": 2473.133217, "measure_result": "direction", "svd_deg": 266.90999999999997}}
{"event": "action", "seq": 100, "wall_ts_ms": 1789097893684, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0100", "position": {"x": 1383.126942952607, "y": -1056.5770079707515}, "channel": 16}, "response": {"accepted": true, "real_timestamp_ms": 1789097893684, "virtual_time_s": 2485.112558, "measure_result": "direction", "svd_deg": 264.22}}
{"event": "action", "seq": 101, "wall_ts_ms": 1789097893685, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0101", "position": {"x": 1382.2283880591076, "y": -1067.33389922316}, "channel": 16}, "response": {"accepted": true, "real_timestamp_ms": 1789097893685, "virtual_time_s": 2492.271429, "clear_result": "success"}}
{"event": "action", "seq": 102, "wall_ts_ms": 1789097893713, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0102", "position": {"x": 348.3821957575218, "y": -1265.9247435227858}, "channel": 15}, "response": {"accepted": true, "real_timestamp_ms": 1789097893712, "virtual_time_s": 2708.820832, "measure_result": "direction", "svd_deg": 190.35}}
{"event": "action", "seq": 103, "wall_ts_ms": 1789097893717, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0103", "position": {"x": 73.53358401969189, "y": -1315.6136497521597}, "channel": 15}, "response": {"accepted": true, "real_timestamp_ms": 1789097893717, "virtual_time_s": 2769.68164, "measure_result": "direction", "svd_deg": 40.45}}
{"event": "action", "seq": 104, "wall_ts_ms": 1789097893719, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0104", "position": {"x": 73.53358401969189, "y": -1315.6136497521597}, "channel": 15}, "response": {"accepted": true, "real_timestamp_ms": 1789097893719, "virtual_time_s": 2774.68164, "clear_result": "success"}}
{"event": "action", "seq": 105, "wall_ts_ms": 1789097893721, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0105", "position": {"x": 579.8500000000001, "y": -1004.3296607688135}, "channel": 1}, "response": {"accepted": true, "real_timestamp_ms": 1789097893721, "virtual_time_s": 2899.551995, "measure_result": "no_signal"}}
{"event": "action", "seq": 106, "wall_ts_ms": 1789097893724, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0106", "position": {"x": 579.8500000000001, "y": -1004.3296607688135}, "channel": 3}, "response": {"accepted": true, "real_timestamp_ms": 1789097893724, "virtual_time_s": 2905.551995, "measure_result": "no_signal"}}
{"event": "action", "seq": 107, "wall_ts_ms": 1789097893727, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0107", "position": {"x": 579.8500000000001, "y": -1004.3296607688135}, "channel": 4}, "response": {"accepted": true, "real_timestamp_ms": 1789097893727, "virtual_time_s": 2911.551995, "measure_result": "no_signal"}}
{"event": "action", "seq": 108, "wall_ts_ms": 1789097893730, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0108", "position": {"x": 579.8500000000001, "y": -1004.3296607688135}, "channel": 6}, "response": {"accepted": true, "real_timestamp_ms": 1789097893730, "virtual_time_s": 2917.551995, "measure_result": "no_signal"}}
{"event": "action", "seq": 109, "wall_ts_ms": 1789097893732, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0109", "position": {"x": 579.8500000000001, "y": -1004.3296607688135}, "channel": 9}, "response": {"accepted": true, "real_timestamp_ms": 1789097893732, "virtual_time_s": 2923.551995, "measure_result": "no_signal"}}
{"event": "action", "seq": 110, "wall_ts_ms": 1789097893734, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0110", "position": {"x": 579.8500000000001, "y": -1004.3296607688135}, "channel": 11}, "response": {"accepted": true, "real_timestamp_ms": 1789097893734, "virtual_time_s": 2929.551995, "measure_result": "no_signal"}}
{"event": "action", "seq": 111, "wall_ts_ms": 1789097893736, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0111", "position": {"x": 579.8500000000001, "y": -1004.3296607688135}, "channel": 14}, "response": {"accepted": true, "real_timestamp_ms": 1789097893736, "virtual_time_s": 2935.551995, "measure_result": "no_signal"}}
{"event": "action", "seq": 112, "wall_ts_ms": 1789097893739, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0112", "position": {"x": 579.8500000000001, "y": -1004.3296607688135}, "channel": 17}, "response": {"accepted": true, "real_timestamp_ms": 1789097893739, "virtual_time_s": 2941.551995, "measure_result": "no_signal"}}
{"event": "action", "seq": 113, "wall_ts_ms": 1789097893741, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0113", "position": {"x": 579.8500000000001, "y": -1004.3296607688135}, "channel": 18}, "response": {"accepted": true, "real_timestamp_ms": 1789097893741, "virtual_time_s": 2947.551995, "measure_result": "no_signal"}}
{"event": "action", "seq": 114, "wall_ts_ms": 1789097893743, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0114", "position": {"x": 571.1154888652, "y": -294.661369661795}, "channel": 10}, "response": {"accepted": true, "real_timestamp_ms": 1789097893743, "virtual_time_s": 3094.496403, "clear_result": "success"}}
{"event": "action", "seq": 115, "wall_ts_ms": 1789097893744, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0115", "position": {"x": 571.1154888652, "y": -294.661369661795}, "channel": 7}, "response": {"accepted": true, "real_timestamp_ms": 1789097893744, "virtual_time_s": 3100.496403, "measure_result": "direction", "svd_deg": 348.0}}
{"event": "action", "seq": 116, "wall_ts_ms": 1789097893775, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0116", "position": {"x": 1071.0447634419545, "y": -59.153833413874054}, "channel": 7}, "response": {"accepted": true, "real_timestamp_ms": 1789097893775, "virtual_time_s": 3216.021166, "measure_result": "no_signal"}}
{"event": "action", "seq": 117, "wall_ts_ms": 1789097893806, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0117", "position": {"x": 1071.0447634419545, "y": -59.153833413874054}, "channel": 7}, "response": {"accepted": true, "real_timestamp_ms": 1789097893806, "virtual_time_s": 3221.021166, "measure_result": "no_signal"}}
{"event": "action", "seq": 118, "wall_ts_ms": 1789097893838, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0118", "position": {"x": 1071.0447634419545, "y": -59.153833413874054}, "channel": 7}, "response": {"accepted": true, "real_timestamp_ms": 1789097893838, "virtual_time_s": 3226.021166, "measure_result": "no_signal"}}
{"event": "action", "seq": 119, "wall_ts_ms": 1789097893870, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0119", "position": {"x": 1071.0447634419545, "y": -59.153833413874054}, "channel": 7}, "response": {"accepted": true, "real_timestamp_ms": 1789097893870, "virtual_time_s": 3231.021166, "measure_result": "no_signal"}}
{"event": "action", "seq": 120, "wall_ts_ms": 1789097893903, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0120", "position": {"x": 1071.0447634419545, "y": -59.153833413874054}, "channel": 7}, "response": {"accepted": true, "real_timestamp_ms": 1789097893903, "virtual_time_s": 3236.021166, "measure_result": "no_signal"}}
{"event": "action", "seq": 121, "wall_ts_ms": 1789097893937, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0121", "position": {"x": 1071.0447634419545, "y": -59.153833413874054}, "channel": 7}, "response": {"accepted": true, "real_timestamp_ms": 1789097893937, "virtual_time_s": 3241.021166, "measure_result": "no_signal"}}
{"event": "action", "seq": 122, "wall_ts_ms": 1789097893971, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0122", "position": {"x": 1071.0447634419545, "y": -59.153833413874054}, "channel": 7}, "response": {"accepted": true, "real_timestamp_ms": 1789097893971, "virtual_time_s": 3246.021166, "measure_result": "no_signal"}}
{"event": "action", "seq": 123, "wall_ts_ms": 1789097894009, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0123", "position": {"x": 1071.0447634419545, "y": -59.153833413874054}, "channel": 7}, "response": {"accepted": true, "real_timestamp_ms": 1789097894009, "virtual_time_s": 3251.021166, "measure_result": "no_signal"}}
{"event": "action", "seq": 124, "wall_ts_ms": 1789097894011, "method": "POST", "path": "/exit", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "8a2d7fbadd15-0124"}, "response": {"accepted": true, "real_timestamp_ms": 1789097894011, "virtual_time_s": 3251.021166, "exit_reason": "user_exit"}}
{"event": "session_end", "reason": "user_exit", "virtual_time_s": 3251.021166, "real_elapsed_s": 10.123456, "request_count": 124, "cleared_count": 10}
@@ -0,0 +1,112 @@
{
"case_code": "6X58-XQ7U-CJ24-JQQR",
"module": "q4_practice",
"robot_id": "1",
"seed": 477239471,
"truth": [
{
"channel": 17,
"x": -1178.1831977517415,
"y": -1351.4242739803465,
"r_eff_m": 1058.2857083120255,
"directional": true,
"heading_deg": 228.91780452670704
},
{
"channel": 1,
"x": 616.704399833097,
"y": 1665.0757168990908,
"r_eff_m": 1324.912278063795,
"directional": true,
"heading_deg": 69.67656996032791
},
{
"channel": 7,
"x": 875.9714308961434,
"y": -356.413074975542,
"r_eff_m": 1417.6410943244593,
"directional": true,
"heading_deg": 226.02323139790843
},
{
"channel": 15,
"x": 77.58288789485773,
"y": -1312.1259181145524,
"r_eff_m": 1099.6028939269304,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 10,
"x": 569.7512600859908,
"y": -287.53357300110514,
"r_eff_m": 1163.1765321843473,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 20,
"x": -669.5662948265503,
"y": -665.215319006332,
"r_eff_m": 1376.2325668493522,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 5,
"x": -508.03938834052923,
"y": 606.335297392372,
"r_eff_m": 1447.9949377114212,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 13,
"x": 306.16535401564636,
"y": 115.13724934212215,
"r_eff_m": 1379.981981356019,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 19,
"x": 611.4980531732139,
"y": 346.74044433389514,
"r_eff_m": 1363.037307441268,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 8,
"x": -1328.739848763958,
"y": 356.8075133866415,
"r_eff_m": 1184.9420447581683,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 16,
"x": 1381.669496042516,
"y": -1070.127602503145,
"r_eff_m": 1413.4108109277565,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 12,
"x": -417.0933355563403,
"y": -928.2011450309352,
"r_eff_m": 1356.0447298689226,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 2,
"x": -1498.1076807839925,
"y": -382.17536931742296,
"r_eff_m": 1189.186960250608,
"directional": false,
"heading_deg": 0.0
}
]
}
@@ -0,0 +1,133 @@
{"event": "session_start", "case_code": "BAU8-WBA6-KTVC-ZEXJ", "module": "q4_practice", "robot_id": "1", "seed": 1724280692, "countdown_s": 5.0, "window_s": 1500.0, "max_real_duration_s": 1200.0, "truth": [{"channel": 11, "x": -651.4491911816699, "y": -861.9941841983957, "r_eff_m": 1158.6608380331968, "directional": true, "heading_deg": 183.92591871574925}, {"channel": 12, "x": 1585.5153767709246, "y": -598.9783966903034, "r_eff_m": 1422.1912155936766, "directional": true, "heading_deg": 181.61389868883072}, {"channel": 6, "x": 31.50984974130298, "y": -1431.359436573822, "r_eff_m": 1485.36130365001, "directional": true, "heading_deg": 92.85184332916663}, {"channel": 1, "x": -108.01732725078004, "y": -1695.4363576323935, "r_eff_m": 1493.4753791235612, "directional": false, "heading_deg": 0.0}, {"channel": 13, "x": 1201.552514046707, "y": 190.10520305773161, "r_eff_m": 1303.4011108874158, "directional": false, "heading_deg": 0.0}, {"channel": 16, "x": 979.2068152143753, "y": 146.43846094844704, "r_eff_m": 1232.2441710447958, "directional": false, "heading_deg": 0.0}, {"channel": 8, "x": 567.6868428443917, "y": -32.68947299569081, "r_eff_m": 1242.1787489349902, "directional": false, "heading_deg": 0.0}, {"channel": 3, "x": -307.0094050788321, "y": 931.898187331384, "r_eff_m": 1339.2730119404985, "directional": false, "heading_deg": 0.0}, {"channel": 2, "x": 1385.5262555901513, "y": 595.9468914421283, "r_eff_m": 1050.5024603796926, "directional": false, "heading_deg": 0.0}, {"channel": 10, "x": 1365.446283577057, "y": -619.1808438513183, "r_eff_m": 1495.4685578869562, "directional": false, "heading_deg": 0.0}]}
{"event": "action", "seq": 1, "wall_ts_ms": 1789098286512, "method": "POST", "path": "/enter", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0001"}, "response": {"accepted": true, "real_timestamp_ms": 1789098286512, "virtual_time_s": 0.0, "max_virtual_duration_s": 360000.0, "max_real_duration_s": 1200, "remaining_real_duration_s": 1200}}
{"event": "action", "seq": 2, "wall_ts_ms": 1789098286526, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0002", "position": {"x": 0.0, "y": 0.0}, "channel": 1}, "response": {"accepted": true, "real_timestamp_ms": 1789098286526, "virtual_time_s": 5.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 3, "wall_ts_ms": 1789098286530, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0003", "position": {"x": 0.0, "y": 0.0}, "channel": 2}, "response": {"accepted": true, "real_timestamp_ms": 1789098286530, "virtual_time_s": 11.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 4, "wall_ts_ms": 1789098286534, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0004", "position": {"x": 0.0, "y": 0.0}, "channel": 3}, "response": {"accepted": true, "real_timestamp_ms": 1789098286534, "virtual_time_s": 17.0, "measure_result": "direction", "svd_deg": 107.63}}
{"event": "action", "seq": 5, "wall_ts_ms": 1789098286562, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0005", "position": {"x": 0.0, "y": 0.0}, "channel": 4}, "response": {"accepted": true, "real_timestamp_ms": 1789098286562, "virtual_time_s": 23.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 6, "wall_ts_ms": 1789098286566, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0006", "position": {"x": 0.0, "y": 0.0}, "channel": 5}, "response": {"accepted": true, "real_timestamp_ms": 1789098286566, "virtual_time_s": 29.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 7, "wall_ts_ms": 1789098286570, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0007", "position": {"x": 0.0, "y": 0.0}, "channel": 6}, "response": {"accepted": true, "real_timestamp_ms": 1789098286570, "virtual_time_s": 35.0, "measure_result": "direction", "svd_deg": 272.12}}
{"event": "action", "seq": 8, "wall_ts_ms": 1789098286597, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0008", "position": {"x": 0.0, "y": 0.0}, "channel": 7}, "response": {"accepted": true, "real_timestamp_ms": 1789098286597, "virtual_time_s": 41.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 9, "wall_ts_ms": 1789098286602, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0009", "position": {"x": 0.0, "y": 0.0}, "channel": 8}, "response": {"accepted": true, "real_timestamp_ms": 1789098286602, "virtual_time_s": 47.0, "measure_result": "direction", "svd_deg": 356.22}}
{"event": "action", "seq": 10, "wall_ts_ms": 1789098286633, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0010", "position": {"x": 0.0, "y": 0.0}, "channel": 9}, "response": {"accepted": true, "real_timestamp_ms": 1789098286633, "virtual_time_s": 53.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 11, "wall_ts_ms": 1789098286638, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0011", "position": {"x": 0.0, "y": 0.0}, "channel": 10}, "response": {"accepted": true, "real_timestamp_ms": 1789098286638, "virtual_time_s": 59.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 12, "wall_ts_ms": 1789098286645, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0012", "position": {"x": 0.0, "y": 0.0}, "channel": 11}, "response": {"accepted": true, "real_timestamp_ms": 1789098286645, "virtual_time_s": 65.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 13, "wall_ts_ms": 1789098286659, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0013", "position": {"x": 0.0, "y": 0.0}, "channel": 12}, "response": {"accepted": true, "real_timestamp_ms": 1789098286659, "virtual_time_s": 71.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 14, "wall_ts_ms": 1789098286669, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0014", "position": {"x": 0.0, "y": 0.0}, "channel": 13}, "response": {"accepted": true, "real_timestamp_ms": 1789098286669, "virtual_time_s": 77.0, "measure_result": "direction", "svd_deg": 9.8}}
{"event": "action", "seq": 15, "wall_ts_ms": 1789098286741, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0015", "position": {"x": 0.0, "y": 0.0}, "channel": 14}, "response": {"accepted": true, "real_timestamp_ms": 1789098286741, "virtual_time_s": 83.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 16, "wall_ts_ms": 1789098286753, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0016", "position": {"x": 0.0, "y": 0.0}, "channel": 15}, "response": {"accepted": true, "real_timestamp_ms": 1789098286753, "virtual_time_s": 89.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 17, "wall_ts_ms": 1789098286762, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0017", "position": {"x": 0.0, "y": 0.0}, "channel": 16}, "response": {"accepted": true, "real_timestamp_ms": 1789098286762, "virtual_time_s": 95.0, "measure_result": "direction", "svd_deg": 8.45}}
{"event": "action", "seq": 18, "wall_ts_ms": 1789098286799, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0018", "position": {"x": 0.0, "y": 0.0}, "channel": 17}, "response": {"accepted": true, "real_timestamp_ms": 1789098286799, "virtual_time_s": 101.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 19, "wall_ts_ms": 1789098286810, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0019", "position": {"x": 0.0, "y": 0.0}, "channel": 18}, "response": {"accepted": true, "real_timestamp_ms": 1789098286810, "virtual_time_s": 107.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 20, "wall_ts_ms": 1789098286822, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0020", "position": {"x": 0.0, "y": 0.0}, "channel": 19}, "response": {"accepted": true, "real_timestamp_ms": 1789098286822, "virtual_time_s": 113.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 21, "wall_ts_ms": 1789098286828, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0021", "position": {"x": 0.0, "y": 0.0}, "channel": 20}, "response": {"accepted": true, "real_timestamp_ms": 1789098286828, "virtual_time_s": 119.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 22, "wall_ts_ms": 1789098286992, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0022", "position": {"x": 609.4509025457847, "y": 242.75812328833265}, "channel": 16}, "response": {"accepted": true, "real_timestamp_ms": 1789098286991, "virtual_time_s": 256.203949, "measure_result": "direction", "svd_deg": 344.42}}
{"event": "action", "seq": 23, "wall_ts_ms": 1789098286994, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0023", "position": {"x": 609.4509025457847, "y": 242.75812328833265}, "channel": 8}, "response": {"accepted": true, "real_timestamp_ms": 1789098286994, "virtual_time_s": 262.203949, "measure_result": "direction", "svd_deg": 261.55}}
{"event": "action", "seq": 24, "wall_ts_ms": 1789098286996, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0024", "position": {"x": 609.4509025457847, "y": 242.75812328833265}, "channel": 13}, "response": {"accepted": true, "real_timestamp_ms": 1789098286996, "virtual_time_s": 268.203949, "measure_result": "direction", "svd_deg": 354.36}}
{"event": "action", "seq": 25, "wall_ts_ms": 1789098287066, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0025", "position": {"x": 1129.9723230767108, "y": 193.27058928343394}, "channel": 13}, "response": {"accepted": true, "real_timestamp_ms": 1789098287066, "virtual_time_s": 377.777668, "measure_result": "direction", "svd_deg": 356.72}}
{"event": "action", "seq": 26, "wall_ts_ms": 1789098287068, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0026", "position": {"x": 1129.9723230767108, "y": 193.27058928343394}, "channel": 16}, "response": {"accepted": true, "real_timestamp_ms": 1789098287068, "virtual_time_s": 383.777668, "measure_result": "direction", "svd_deg": 196.38}}
{"event": "action", "seq": 27, "wall_ts_ms": 1789098287131, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0027", "position": {"x": 1177.3957914158104, "y": 191.3865914463714}, "channel": 13}, "response": {"accepted": true, "real_timestamp_ms": 1789098287131, "virtual_time_s": 399.269843, "measure_result": "direction", "svd_deg": 357.33}}
{"event": "action", "seq": 28, "wall_ts_ms": 1789098287188, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0028", "position": {"x": 1201.1075255853598, "y": 190.44459252784014}, "channel": 13}, "response": {"accepted": true, "real_timestamp_ms": 1789098287188, "virtual_time_s": 409.015931, "measure_result": "near"}}
{"event": "action", "seq": 29, "wall_ts_ms": 1789098287190, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0029", "position": {"x": 1201.1075255853598, "y": 190.44459252784014}, "channel": 13}, "response": {"accepted": true, "real_timestamp_ms": 1789098287190, "virtual_time_s": 414.015931, "clear_result": "success"}}
{"event": "action", "seq": 30, "wall_ts_ms": 1789098287261, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0030", "position": {"x": 1159.7, "y": 0.0}, "channel": 1}, "response": {"accepted": true, "real_timestamp_ms": 1789098287261, "virtual_time_s": 458.994759, "measure_result": "no_signal"}}
{"event": "action", "seq": 31, "wall_ts_ms": 1789098287265, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0031", "position": {"x": 1159.7, "y": 0.0}, "channel": 2}, "response": {"accepted": true, "real_timestamp_ms": 1789098287265, "virtual_time_s": 464.994759, "measure_result": "direction", "svd_deg": 69.82}}
{"event": "action", "seq": 32, "wall_ts_ms": 1789098287291, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0032", "position": {"x": 1159.7, "y": 0.0}, "channel": 4}, "response": {"accepted": true, "real_timestamp_ms": 1789098287291, "virtual_time_s": 470.994759, "measure_result": "no_signal"}}
{"event": "action", "seq": 33, "wall_ts_ms": 1789098287296, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0033", "position": {"x": 1159.7, "y": 0.0}, "channel": 5}, "response": {"accepted": true, "real_timestamp_ms": 1789098287296, "virtual_time_s": 476.994759, "measure_result": "no_signal"}}
{"event": "action", "seq": 34, "wall_ts_ms": 1789098287300, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0034", "position": {"x": 1159.7, "y": 0.0}, "channel": 7}, "response": {"accepted": true, "real_timestamp_ms": 1789098287300, "virtual_time_s": 482.994759, "measure_result": "no_signal"}}
{"event": "action", "seq": 35, "wall_ts_ms": 1789098287304, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0035", "position": {"x": 1159.7, "y": 0.0}, "channel": 9}, "response": {"accepted": true, "real_timestamp_ms": 1789098287304, "virtual_time_s": 488.994759, "measure_result": "no_signal"}}
{"event": "action", "seq": 36, "wall_ts_ms": 1789098287308, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0036", "position": {"x": 1159.7, "y": 0.0}, "channel": 10}, "response": {"accepted": true, "real_timestamp_ms": 1789098287308, "virtual_time_s": 494.994759, "measure_result": "direction", "svd_deg": 287.57}}
{"event": "action", "seq": 37, "wall_ts_ms": 1789098287329, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0037", "position": {"x": 1159.7, "y": 0.0}, "channel": 11}, "response": {"accepted": true, "real_timestamp_ms": 1789098287329, "virtual_time_s": 500.994759, "measure_result": "no_signal"}}
{"event": "action", "seq": 38, "wall_ts_ms": 1789098287333, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0038", "position": {"x": 1159.7, "y": 0.0}, "channel": 12}, "response": {"accepted": true, "real_timestamp_ms": 1789098287333, "virtual_time_s": 506.994759, "measure_result": "direction", "svd_deg": 304.84000000000003}}
{"event": "action", "seq": 39, "wall_ts_ms": 1789098287362, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0039", "position": {"x": 1159.7, "y": 0.0}, "channel": 14}, "response": {"accepted": true, "real_timestamp_ms": 1789098287362, "virtual_time_s": 512.994759, "measure_result": "no_signal"}}
{"event": "action", "seq": 40, "wall_ts_ms": 1789098287366, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0040", "position": {"x": 1159.7, "y": 0.0}, "channel": 15}, "response": {"accepted": true, "real_timestamp_ms": 1789098287366, "virtual_time_s": 518.994759, "measure_result": "no_signal"}}
{"event": "action", "seq": 41, "wall_ts_ms": 1789098287369, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0041", "position": {"x": 1159.7, "y": 0.0}, "channel": 17}, "response": {"accepted": true, "real_timestamp_ms": 1789098287369, "virtual_time_s": 524.994759, "measure_result": "no_signal"}}
{"event": "action", "seq": 42, "wall_ts_ms": 1789098287373, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0042", "position": {"x": 1159.7, "y": 0.0}, "channel": 18}, "response": {"accepted": true, "real_timestamp_ms": 1789098287373, "virtual_time_s": 530.994759, "measure_result": "no_signal"}}
{"event": "action", "seq": 43, "wall_ts_ms": 1789098287377, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0043", "position": {"x": 1159.7, "y": 0.0}, "channel": 19}, "response": {"accepted": true, "real_timestamp_ms": 1789098287377, "virtual_time_s": 536.994759, "measure_result": "no_signal"}}
{"event": "action", "seq": 44, "wall_ts_ms": 1789098287380, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0044", "position": {"x": 1159.7, "y": 0.0}, "channel": 20}, "response": {"accepted": true, "real_timestamp_ms": 1789098287380, "virtual_time_s": 542.994759, "measure_result": "no_signal"}}
{"event": "action", "seq": 45, "wall_ts_ms": 1789098287513, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0045", "position": {"x": 964.0195882337356, "y": 143.6973976268055}, "channel": 16}, "response": {"accepted": true, "real_timestamp_ms": 1789098287513, "virtual_time_s": 596.549786, "clear_result": "success"}}
{"event": "action", "seq": 46, "wall_ts_ms": 1789098287624, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0046", "position": {"x": 1147.7195456894221, "y": 445.38724453588145}, "channel": 2}, "response": {"accepted": true, "real_timestamp_ms": 1789098287624, "virtual_time_s": 673.19324, "measure_result": "direction", "svd_deg": 32.39}}
{"event": "action", "seq": 47, "wall_ts_ms": 1789098287718, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0047", "position": {"x": 1377.593698088216, "y": 591.7213795881403}, "channel": 2}, "response": {"accepted": true, "real_timestamp_ms": 1789098287718, "virtual_time_s": 732.693077, "measure_result": "direction", "svd_deg": 28.81}}
{"event": "action", "seq": 48, "wall_ts_ms": 1789098287722, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0048", "position": {"x": 1377.593698088216, "y": 591.7213795881403}, "channel": 2}, "response": {"accepted": true, "real_timestamp_ms": 1789098287722, "virtual_time_s": 737.693077, "clear_result": "success"}}
{"event": "action", "seq": 49, "wall_ts_ms": 1789098287821, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0049", "position": {"x": 1510.5079041685437, "y": -165.55211232315685}, "channel": 12}, "response": {"accepted": true, "real_timestamp_ms": 1789098287821, "virtual_time_s": 897.462948, "measure_result": "direction", "svd_deg": 280.71}}
{"event": "action", "seq": 50, "wall_ts_ms": 1789098287824, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0050", "position": {"x": 1510.5079041685437, "y": -165.55211232315685}, "channel": 10}, "response": {"accepted": true, "real_timestamp_ms": 1789098287824, "virtual_time_s": 903.462948, "measure_result": "direction", "svd_deg": 252.35}}
{"event": "action", "seq": 51, "wall_ts_ms": 1789098287901, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0051", "position": {"x": 1600.0757430229646, "y": -634.2319383410297}, "channel": 12}, "response": {"accepted": true, "real_timestamp_ms": 1789098287901, "virtual_time_s": 1004.895265, "measure_result": "no_signal"}}
{"event": "action", "seq": 52, "wall_ts_ms": 1789098287905, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0052", "position": {"x": 1600.0757430229646, "y": -634.2319383410297}, "channel": 10}, "response": {"accepted": true, "real_timestamp_ms": 1789098287905, "virtual_time_s": 1010.895265, "measure_result": "direction", "svd_deg": 176.19}}
{"event": "action", "seq": 53, "wall_ts_ms": 1789098287970, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0053", "position": {"x": 1374.4959772224145, "y": -620.5386205711816}, "channel": 10}, "response": {"accepted": true, "real_timestamp_ms": 1789098287970, "virtual_time_s": 1061.094264, "clear_result": "success"}}
{"event": "action", "seq": 54, "wall_ts_ms": 1789098287971, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0054", "position": {"x": 1374.4959772224145, "y": -620.5386205711816}, "channel": 12}, "response": {"accepted": true, "real_timestamp_ms": 1789098287971, "virtual_time_s": 1067.094264, "measure_result": "direction", "svd_deg": 4.98}}
{"event": "action", "seq": 55, "wall_ts_ms": 1789098288029, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0055", "position": {"x": 1579.038663733305, "y": -602.8124850846476}, "channel": 12}, "response": {"accepted": true, "real_timestamp_ms": 1789098288029, "virtual_time_s": 1113.156133, "clear_result": "success"}}
{"event": "action", "seq": 56, "wall_ts_ms": 1789098288091, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0056", "position": {"x": 579.8500000000001, "y": -1004.3296607688135}, "channel": 1}, "response": {"accepted": true, "real_timestamp_ms": 1789098288091, "virtual_time_s": 1334.525028, "measure_result": "direction", "svd_deg": 224.86}}
{"event": "action", "seq": 57, "wall_ts_ms": 1789098288107, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0057", "position": {"x": 579.8500000000001, "y": -1004.3296607688135}, "channel": 4}, "response": {"accepted": true, "real_timestamp_ms": 1789098288107, "virtual_time_s": 1340.525028, "measure_result": "no_signal"}}
{"event": "action", "seq": 58, "wall_ts_ms": 1789098288110, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0058", "position": {"x": 579.8500000000001, "y": -1004.3296607688135}, "channel": 5}, "response": {"accepted": true, "real_timestamp_ms": 1789098288110, "virtual_time_s": 1346.525028, "measure_result": "no_signal"}}
{"event": "action", "seq": 59, "wall_ts_ms": 1789098288113, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0059", "position": {"x": 579.8500000000001, "y": -1004.3296607688135}, "channel": 7}, "response": {"accepted": true, "real_timestamp_ms": 1789098288113, "virtual_time_s": 1352.525028, "measure_result": "no_signal"}}
{"event": "action", "seq": 60, "wall_ts_ms": 1789098288117, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0060", "position": {"x": 579.8500000000001, "y": -1004.3296607688135}, "channel": 9}, "response": {"accepted": true, "real_timestamp_ms": 1789098288117, "virtual_time_s": 1358.525028, "measure_result": "no_signal"}}
{"event": "action", "seq": 61, "wall_ts_ms": 1789098288120, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0061", "position": {"x": 579.8500000000001, "y": -1004.3296607688135}, "channel": 11}, "response": {"accepted": true, "real_timestamp_ms": 1789098288120, "virtual_time_s": 1364.525028, "measure_result": "no_signal"}}
{"event": "action", "seq": 62, "wall_ts_ms": 1789098288123, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0062", "position": {"x": 579.8500000000001, "y": -1004.3296607688135}, "channel": 14}, "response": {"accepted": true, "real_timestamp_ms": 1789098288123, "virtual_time_s": 1370.525028, "measure_result": "no_signal"}}
{"event": "action", "seq": 63, "wall_ts_ms": 1789098288129, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0063", "position": {"x": 579.8500000000001, "y": -1004.3296607688135}, "channel": 15}, "response": {"accepted": true, "real_timestamp_ms": 1789098288129, "virtual_time_s": 1376.525028, "measure_result": "no_signal"}}
{"event": "action", "seq": 64, "wall_ts_ms": 1789098288132, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0064", "position": {"x": 579.8500000000001, "y": -1004.3296607688135}, "channel": 17}, "response": {"accepted": true, "real_timestamp_ms": 1789098288132, "virtual_time_s": 1382.525028, "measure_result": "no_signal"}}
{"event": "action", "seq": 65, "wall_ts_ms": 1789098288135, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0065", "position": {"x": 579.8500000000001, "y": -1004.3296607688135}, "channel": 18}, "response": {"accepted": true, "real_timestamp_ms": 1789098288135, "virtual_time_s": 1388.525028, "measure_result": "no_signal"}}
{"event": "action", "seq": 66, "wall_ts_ms": 1789098288138, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0066", "position": {"x": 579.8500000000001, "y": -1004.3296607688135}, "channel": 19}, "response": {"accepted": true, "real_timestamp_ms": 1789098288138, "virtual_time_s": 1394.525028, "measure_result": "no_signal"}}
{"event": "action", "seq": 67, "wall_ts_ms": 1789098288141, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0067", "position": {"x": 579.8500000000001, "y": -1004.3296607688135}, "channel": 20}, "response": {"accepted": true, "real_timestamp_ms": 1789098288141, "virtual_time_s": 1400.525028, "measure_result": "no_signal"}}
{"event": "action", "seq": 68, "wall_ts_ms": 1789098288226, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0068", "position": {"x": 254.04553841852473, "y": -854.2212845947172}, "channel": 6}, "response": {"accepted": true, "real_timestamp_ms": 1789098288226, "virtual_time_s": 1478.269316, "measure_result": "direction", "svd_deg": 248.48000000000002}}
{"event": "action", "seq": 69, "wall_ts_ms": 1789098288278, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0069", "position": {"x": 109.51724182376685, "y": -1232.1472944851855}, "channel": 1}, "response": {"accepted": true, "real_timestamp_ms": 1789098288278, "virtual_time_s": 1565.193107, "measure_result": "direction", "svd_deg": 244.24}}
{"event": "action", "seq": 70, "wall_ts_ms": 1789098288282, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0070", "position": {"x": 109.51724182376685, "y": -1232.1472944851855}, "channel": 6}, "response": {"accepted": true, "real_timestamp_ms": 1789098288282, "virtual_time_s": 1571.193107, "measure_result": "direction", "svd_deg": 248.97}}
{"event": "action", "seq": 71, "wall_ts_ms": 1789098288319, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0071", "position": {"x": 50.30435176463236, "y": -1389.531228036628}, "channel": 6}, "response": {"accepted": true, "real_timestamp_ms": 1789098288319, "virtual_time_s": 1609.823967, "measure_result": "direction", "svd_deg": 246.48000000000002}}
{"event": "action", "seq": 72, "wall_ts_ms": 1789098288353, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0072", "position": {"x": 39.195887560088515, "y": -1416.3295871617352}, "channel": 6}, "response": {"accepted": true, "real_timestamp_ms": 1789098288353, "virtual_time_s": 1620.625863, "measure_result": "direction", "svd_deg": 243.70999999999998}}
{"event": "action", "seq": 73, "wall_ts_ms": 1789098288355, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0073", "position": {"x": 35.823200123353644, "y": -1423.469399884071}, "channel": 6}, "response": {"accepted": true, "real_timestamp_ms": 1789098288355, "virtual_time_s": 1627.205128, "clear_result": "success"}}
{"event": "action", "seq": 74, "wall_ts_ms": 1789098288386, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0074", "position": {"x": -118.1861771959669, "y": -1700.8617020167337}, "channel": 1}, "response": {"accepted": true, "real_timestamp_ms": 1789098288386, "virtual_time_s": 1696.660743, "measure_result": "direction", "svd_deg": 28.76}}
{"event": "action", "seq": 75, "wall_ts_ms": 1789098288387, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0075", "position": {"x": -118.1861771959669, "y": -1700.8617020167337}, "channel": 1}, "response": {"accepted": true, "real_timestamp_ms": 1789098288387, "virtual_time_s": 1701.660743, "clear_result": "success"}}
{"event": "action", "seq": 76, "wall_ts_ms": 1789098288416, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0076", "position": {"x": -579.8500000000006, "y": -1004.3296607688133}, "channel": 4}, "response": {"accepted": true, "real_timestamp_ms": 1789098288416, "virtual_time_s": 1874.788283, "measure_result": "no_signal"}}
{"event": "action", "seq": 77, "wall_ts_ms": 1789098288420, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0077", "position": {"x": -579.8500000000006, "y": -1004.3296607688133}, "channel": 5}, "response": {"accepted": true, "real_timestamp_ms": 1789098288420, "virtual_time_s": 1880.788283, "measure_result": "no_signal"}}
{"event": "action", "seq": 78, "wall_ts_ms": 1789098288423, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0078", "position": {"x": -579.8500000000006, "y": -1004.3296607688133}, "channel": 7}, "response": {"accepted": true, "real_timestamp_ms": 1789098288422, "virtual_time_s": 1886.788283, "measure_result": "no_signal"}}
{"event": "action", "seq": 79, "wall_ts_ms": 1789098288425, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0079", "position": {"x": -579.8500000000006, "y": -1004.3296607688133}, "channel": 9}, "response": {"accepted": true, "real_timestamp_ms": 1789098288425, "virtual_time_s": 1892.788283, "measure_result": "no_signal"}}
{"event": "action", "seq": 80, "wall_ts_ms": 1789098288428, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0080", "position": {"x": -579.8500000000006, "y": -1004.3296607688133}, "channel": 11}, "response": {"accepted": true, "real_timestamp_ms": 1789098288428, "virtual_time_s": 1898.788283, "measure_result": "no_signal"}}
{"event": "action", "seq": 81, "wall_ts_ms": 1789098288430, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0081", "position": {"x": -579.8500000000006, "y": -1004.3296607688133}, "channel": 14}, "response": {"accepted": true, "real_timestamp_ms": 1789098288430, "virtual_time_s": 1904.788283, "measure_result": "no_signal"}}
{"event": "action", "seq": 82, "wall_ts_ms": 1789098288433, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0082", "position": {"x": -579.8500000000006, "y": -1004.3296607688133}, "channel": 15}, "response": {"accepted": true, "real_timestamp_ms": 1789098288433, "virtual_time_s": 1910.788283, "measure_result": "no_signal"}}
{"event": "action", "seq": 83, "wall_ts_ms": 1789098288435, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0083", "position": {"x": -579.8500000000006, "y": -1004.3296607688133}, "channel": 17}, "response": {"accepted": true, "real_timestamp_ms": 1789098288435, "virtual_time_s": 1916.788283, "measure_result": "no_signal"}}
{"event": "action", "seq": 84, "wall_ts_ms": 1789098288439, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0084", "position": {"x": -579.8500000000006, "y": -1004.3296607688133}, "channel": 18}, "response": {"accepted": true, "real_timestamp_ms": 1789098288439, "virtual_time_s": 1922.788283, "measure_result": "no_signal"}}
{"event": "action", "seq": 85, "wall_ts_ms": 1789098288443, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0085", "position": {"x": -579.8500000000006, "y": -1004.3296607688133}, "channel": 19}, "response": {"accepted": true, "real_timestamp_ms": 1789098288443, "virtual_time_s": 1928.788283, "measure_result": "no_signal"}}
{"event": "action", "seq": 86, "wall_ts_ms": 1789098288448, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0086", "position": {"x": -579.8500000000006, "y": -1004.3296607688133}, "channel": 20}, "response": {"accepted": true, "real_timestamp_ms": 1789098288448, "virtual_time_s": 1934.788283, "measure_result": "no_signal"}}
{"event": "action", "seq": 87, "wall_ts_ms": 1789098288479, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0087", "position": {"x": -1159.7, "y": 1.4202228929711856e-13}, "channel": 20}, "response": {"accepted": true, "real_timestamp_ms": 1789098288479, "virtual_time_s": 2171.728283, "measure_result": "no_signal"}}
{"event": "action", "seq": 88, "wall_ts_ms": 1789098288481, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0088", "position": {"x": -1159.7, "y": 1.4202228929711856e-13}, "channel": 4}, "response": {"accepted": true, "real_timestamp_ms": 1789098288481, "virtual_time_s": 2177.728283, "measure_result": "no_signal"}}
{"event": "action", "seq": 89, "wall_ts_ms": 1789098288484, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0089", "position": {"x": -1159.7, "y": 1.4202228929711856e-13}, "channel": 5}, "response": {"accepted": true, "real_timestamp_ms": 1789098288484, "virtual_time_s": 2183.728283, "measure_result": "no_signal"}}
{"event": "action", "seq": 90, "wall_ts_ms": 1789098288486, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0090", "position": {"x": -1159.7, "y": 1.4202228929711856e-13}, "channel": 7}, "response": {"accepted": true, "real_timestamp_ms": 1789098288486, "virtual_time_s": 2189.728283, "measure_result": "no_signal"}}
{"event": "action", "seq": 91, "wall_ts_ms": 1789098288489, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0091", "position": {"x": -1159.7, "y": 1.4202228929711856e-13}, "channel": 9}, "response": {"accepted": true, "real_timestamp_ms": 1789098288489, "virtual_time_s": 2195.728283, "measure_result": "no_signal"}}
{"event": "action", "seq": 92, "wall_ts_ms": 1789098288491, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0092", "position": {"x": -1159.7, "y": 1.4202228929711856e-13}, "channel": 11}, "response": {"accepted": true, "real_timestamp_ms": 1789098288491, "virtual_time_s": 2201.728283, "measure_result": "direction", "svd_deg": 301.47}}
{"event": "action", "seq": 93, "wall_ts_ms": 1789098288503, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0093", "position": {"x": -1159.7, "y": 1.4202228929711856e-13}, "channel": 14}, "response": {"accepted": true, "real_timestamp_ms": 1789098288503, "virtual_time_s": 2207.728283, "measure_result": "no_signal"}}
{"event": "action", "seq": 94, "wall_ts_ms": 1789098288507, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0094", "position": {"x": -1159.7, "y": 1.4202228929711856e-13}, "channel": 15}, "response": {"accepted": true, "real_timestamp_ms": 1789098288507, "virtual_time_s": 2213.728283, "measure_result": "no_signal"}}
{"event": "action", "seq": 95, "wall_ts_ms": 1789098288510, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0095", "position": {"x": -1159.7, "y": 1.4202228929711856e-13}, "channel": 17}, "response": {"accepted": true, "real_timestamp_ms": 1789098288510, "virtual_time_s": 2219.728283, "measure_result": "no_signal"}}
{"event": "action", "seq": 96, "wall_ts_ms": 1789098288516, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0096", "position": {"x": -1159.7, "y": 1.4202228929711856e-13}, "channel": 18}, "response": {"accepted": true, "real_timestamp_ms": 1789098288516, "virtual_time_s": 2225.728283, "measure_result": "no_signal"}}
{"event": "action", "seq": 97, "wall_ts_ms": 1789098288518, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0097", "position": {"x": -1159.7, "y": 1.4202228929711856e-13}, "channel": 19}, "response": {"accepted": true, "real_timestamp_ms": 1789098288518, "virtual_time_s": 2231.728283, "measure_result": "no_signal"}}
{"event": "action", "seq": 98, "wall_ts_ms": 1789098288550, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0098", "position": {"x": -774.2496286053656, "y": -389.8030122282429}, "channel": 11}, "response": {"accepted": true, "real_timestamp_ms": 1789098288550, "virtual_time_s": 2347.367396, "measure_result": "direction", "svd_deg": 283.74}}
{"event": "action", "seq": 99, "wall_ts_ms": 1789098288579, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0099", "position": {"x": -435.04585346585066, "y": 576.4769379998097}, "channel": 3}, "response": {"accepted": true, "real_timestamp_ms": 1789098288579, "virtual_time_s": 2558.184987, "measure_result": "direction", "svd_deg": 70.1}}
{"event": "action", "seq": 100, "wall_ts_ms": 1789098288582, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0100", "position": {"x": -579.8499999999998, "y": 1004.3296607688136}, "channel": 4}, "response": {"accepted": true, "real_timestamp_ms": 1789098288582, "virtual_time_s": 2654.523504, "measure_result": "no_signal"}}
{"event": "action", "seq": 101, "wall_ts_ms": 1789098288585, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0101", "position": {"x": -579.8499999999998, "y": 1004.3296607688136}, "channel": 5}, "response": {"accepted": true, "real_timestamp_ms": 1789098288585, "virtual_time_s": 2660.523504, "measure_result": "no_signal"}}
{"event": "action", "seq": 102, "wall_ts_ms": 1789098288588, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0102", "position": {"x": -579.8499999999998, "y": 1004.3296607688136}, "channel": 7}, "response": {"accepted": true, "real_timestamp_ms": 1789098288588, "virtual_time_s": 2666.523504, "measure_result": "no_signal"}}
{"event": "action", "seq": 103, "wall_ts_ms": 1789098288591, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0103", "position": {"x": -579.8499999999998, "y": 1004.3296607688136}, "channel": 9}, "response": {"accepted": true, "real_timestamp_ms": 1789098288591, "virtual_time_s": 2672.523504, "measure_result": "no_signal"}}
{"event": "action", "seq": 104, "wall_ts_ms": 1789098288594, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0104", "position": {"x": -579.8499999999998, "y": 1004.3296607688136}, "channel": 14}, "response": {"accepted": true, "real_timestamp_ms": 1789098288594, "virtual_time_s": 2678.523504, "measure_result": "no_signal"}}
{"event": "action", "seq": 105, "wall_ts_ms": 1789098288598, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0105", "position": {"x": -579.8499999999998, "y": 1004.3296607688136}, "channel": 15}, "response": {"accepted": true, "real_timestamp_ms": 1789098288598, "virtual_time_s": 2684.523504, "measure_result": "no_signal"}}
{"event": "action", "seq": 106, "wall_ts_ms": 1789098288602, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0106", "position": {"x": -579.8499999999998, "y": 1004.3296607688136}, "channel": 17}, "response": {"accepted": true, "real_timestamp_ms": 1789098288602, "virtual_time_s": 2690.523504, "measure_result": "no_signal"}}
{"event": "action", "seq": 107, "wall_ts_ms": 1789098288606, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0107", "position": {"x": -579.8499999999998, "y": 1004.3296607688136}, "channel": 18}, "response": {"accepted": true, "real_timestamp_ms": 1789098288606, "virtual_time_s": 2696.523504, "measure_result": "no_signal"}}
{"event": "action", "seq": 108, "wall_ts_ms": 1789098288610, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0108", "position": {"x": -579.8499999999998, "y": 1004.3296607688136}, "channel": 19}, "response": {"accepted": true, "real_timestamp_ms": 1789098288610, "virtual_time_s": 2702.523504, "measure_result": "no_signal"}}
{"event": "action", "seq": 109, "wall_ts_ms": 1789098288613, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0109", "position": {"x": -579.8499999999998, "y": 1004.3296607688136}, "channel": 20}, "response": {"accepted": true, "real_timestamp_ms": 1789098288613, "virtual_time_s": 2708.523504, "measure_result": "no_signal"}}
{"event": "action", "seq": 110, "wall_ts_ms": 1789098288618, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0110", "position": {"x": -579.8499999999998, "y": 1004.3296607688136}, "channel": 3}, "response": {"accepted": true, "real_timestamp_ms": 1789098288618, "virtual_time_s": 2714.523504, "measure_result": "direction", "svd_deg": 346.0}}
{"event": "action", "seq": 111, "wall_ts_ms": 1789098288620, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0111", "position": {"x": -314.40286948037965, "y": 938.2728681520762}, "channel": 3}, "response": {"accepted": true, "real_timestamp_ms": 1789098288620, "virtual_time_s": 2774.23207, "clear_result": "success"}}
{"event": "action", "seq": 112, "wall_ts_ms": 1789098288621, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0112", "position": {"x": 579.8500000000001, "y": 1004.3296607688135}, "channel": 4}, "response": {"accepted": true, "real_timestamp_ms": 1789098288621, "virtual_time_s": 2959.569929, "measure_result": "no_signal"}}
{"event": "action", "seq": 113, "wall_ts_ms": 1789098288624, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0113", "position": {"x": 579.8500000000001, "y": 1004.3296607688135}, "channel": 5}, "response": {"accepted": true, "real_timestamp_ms": 1789098288624, "virtual_time_s": 2965.569929, "measure_result": "no_signal"}}
{"event": "action", "seq": 114, "wall_ts_ms": 1789098288626, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0114", "position": {"x": 579.8500000000001, "y": 1004.3296607688135}, "channel": 7}, "response": {"accepted": true, "real_timestamp_ms": 1789098288626, "virtual_time_s": 2971.569929, "measure_result": "no_signal"}}
{"event": "action", "seq": 115, "wall_ts_ms": 1789098288628, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0115", "position": {"x": 579.8500000000001, "y": 1004.3296607688135}, "channel": 9}, "response": {"accepted": true, "real_timestamp_ms": 1789098288628, "virtual_time_s": 2977.569929, "measure_result": "no_signal"}}
{"event": "action", "seq": 116, "wall_ts_ms": 1789098288631, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0116", "position": {"x": 579.8500000000001, "y": 1004.3296607688135}, "channel": 14}, "response": {"accepted": true, "real_timestamp_ms": 1789098288631, "virtual_time_s": 2983.569929, "measure_result": "no_signal"}}
{"event": "action", "seq": 117, "wall_ts_ms": 1789098288633, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0117", "position": {"x": 579.8500000000001, "y": 1004.3296607688135}, "channel": 15}, "response": {"accepted": true, "real_timestamp_ms": 1789098288633, "virtual_time_s": 2989.569929, "measure_result": "no_signal"}}
{"event": "action", "seq": 118, "wall_ts_ms": 1789098288636, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0118", "position": {"x": 579.8500000000001, "y": 1004.3296607688135}, "channel": 17}, "response": {"accepted": true, "real_timestamp_ms": 1789098288636, "virtual_time_s": 2995.569929, "measure_result": "no_signal"}}
{"event": "action", "seq": 119, "wall_ts_ms": 1789098288642, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0119", "position": {"x": 579.8500000000001, "y": 1004.3296607688135}, "channel": 18}, "response": {"accepted": true, "real_timestamp_ms": 1789098288642, "virtual_time_s": 3001.569929, "measure_result": "no_signal"}}
{"event": "action", "seq": 120, "wall_ts_ms": 1789098288646, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0120", "position": {"x": 579.8500000000001, "y": 1004.3296607688135}, "channel": 19}, "response": {"accepted": true, "real_timestamp_ms": 1789098288646, "virtual_time_s": 3007.569929, "measure_result": "no_signal"}}
{"event": "action", "seq": 121, "wall_ts_ms": 1789098288648, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0121", "position": {"x": 579.8500000000001, "y": 1004.3296607688135}, "channel": 20}, "response": {"accepted": true, "real_timestamp_ms": 1789098288648, "virtual_time_s": 3013.569929, "measure_result": "no_signal"}}
{"event": "action", "seq": 122, "wall_ts_ms": 1789098288653, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0122", "position": {"x": 567.7315195519344, "y": -30.08795183085696}, "channel": 8}, "response": {"accepted": true, "real_timestamp_ms": 1789098288653, "virtual_time_s": 3225.467648, "clear_result": "success"}}
{"event": "action", "seq": 123, "wall_ts_ms": 1789098288675, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0123", "position": {"x": -316.6685045965652, "y": -701.8080492945575}, "channel": 11}, "response": {"accepted": true, "real_timestamp_ms": 1789098288675, "virtual_time_s": 3453.582149, "measure_result": "no_signal"}}
{"event": "action", "seq": 124, "wall_ts_ms": 1789098288694, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0124", "position": {"x": -316.6685045965652, "y": -701.8080492945575}, "channel": 11}, "response": {"accepted": true, "real_timestamp_ms": 1789098288694, "virtual_time_s": 3458.582149, "measure_result": "no_signal"}}
{"event": "action", "seq": 125, "wall_ts_ms": 1789098288717, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0125", "position": {"x": -316.6685045965652, "y": -701.8080492945575}, "channel": 11}, "response": {"accepted": true, "real_timestamp_ms": 1789098288717, "virtual_time_s": 3463.582149, "measure_result": "no_signal"}}
{"event": "action", "seq": 126, "wall_ts_ms": 1789098288745, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0126", "position": {"x": -316.6685045965652, "y": -701.8080492945575}, "channel": 11}, "response": {"accepted": true, "real_timestamp_ms": 1789098288745, "virtual_time_s": 3468.582149, "measure_result": "no_signal"}}
{"event": "action", "seq": 127, "wall_ts_ms": 1789098288767, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0127", "position": {"x": -316.6685045965652, "y": -701.8080492945575}, "channel": 11}, "response": {"accepted": true, "real_timestamp_ms": 1789098288767, "virtual_time_s": 3473.582149, "measure_result": "no_signal"}}
{"event": "action", "seq": 128, "wall_ts_ms": 1789098288786, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0128", "position": {"x": -316.6685045965652, "y": -701.8080492945575}, "channel": 11}, "response": {"accepted": true, "real_timestamp_ms": 1789098288786, "virtual_time_s": 3478.582149, "measure_result": "no_signal"}}
{"event": "action", "seq": 129, "wall_ts_ms": 1789098288809, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0129", "position": {"x": -316.6685045965652, "y": -701.8080492945575}, "channel": 11}, "response": {"accepted": true, "real_timestamp_ms": 1789098288809, "virtual_time_s": 3483.582149, "measure_result": "no_signal"}}
{"event": "action", "seq": 130, "wall_ts_ms": 1789098288832, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0130", "position": {"x": -316.6685045965652, "y": -701.8080492945575}, "channel": 11}, "response": {"accepted": true, "real_timestamp_ms": 1789098288832, "virtual_time_s": 3488.582149, "measure_result": "no_signal"}}
{"event": "action", "seq": 131, "wall_ts_ms": 1789098288834, "method": "POST", "path": "/exit", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "40d2ecd3a4e3-0131"}, "response": {"accepted": true, "real_timestamp_ms": 1789098288834, "virtual_time_s": 3488.582149, "exit_reason": "user_exit"}}
{"event": "session_end", "reason": "user_exit", "virtual_time_s": 3488.582149, "real_elapsed_s": 6.480214, "request_count": 131, "cleared_count": 9}
@@ -0,0 +1,88 @@
{
"case_code": "BAU8-WBA6-KTVC-ZEXJ",
"module": "q4_practice",
"robot_id": "1",
"seed": 1724280692,
"truth": [
{
"channel": 11,
"x": -651.4491911816699,
"y": -861.9941841983957,
"r_eff_m": 1158.6608380331968,
"directional": true,
"heading_deg": 183.92591871574925
},
{
"channel": 12,
"x": 1585.5153767709246,
"y": -598.9783966903034,
"r_eff_m": 1422.1912155936766,
"directional": true,
"heading_deg": 181.61389868883072
},
{
"channel": 6,
"x": 31.50984974130298,
"y": -1431.359436573822,
"r_eff_m": 1485.36130365001,
"directional": true,
"heading_deg": 92.85184332916663
},
{
"channel": 1,
"x": -108.01732725078004,
"y": -1695.4363576323935,
"r_eff_m": 1493.4753791235612,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 13,
"x": 1201.552514046707,
"y": 190.10520305773161,
"r_eff_m": 1303.4011108874158,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 16,
"x": 979.2068152143753,
"y": 146.43846094844704,
"r_eff_m": 1232.2441710447958,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 8,
"x": 567.6868428443917,
"y": -32.68947299569081,
"r_eff_m": 1242.1787489349902,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 3,
"x": -307.0094050788321,
"y": 931.898187331384,
"r_eff_m": 1339.2730119404985,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 2,
"x": 1385.5262555901513,
"y": 595.9468914421283,
"r_eff_m": 1050.5024603796926,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 10,
"x": 1365.446283577057,
"y": -619.1808438513183,
"r_eff_m": 1495.4685578869562,
"directional": false,
"heading_deg": 0.0
}
]
}
@@ -0,0 +1,133 @@
{"event": "session_start", "case_code": "BBM9-HZG2-77N5-NKKC", "module": "q3_practice", "robot_id": "1", "seed": 214175614, "countdown_s": 5.0, "window_s": 1500.0, "max_real_duration_s": 1200.0, "truth": [{"channel": 13, "x": -667.5434273375823, "y": 1201.9279733147766, "r_eff_m": 1068.7667479727882, "directional": false, "heading_deg": 0.0}, {"channel": 18, "x": 936.4803225514449, "y": -449.0798658945765, "r_eff_m": 1100.2220789348723, "directional": false, "heading_deg": 0.0}, {"channel": 7, "x": 1168.5586737701378, "y": -665.9024303254582, "r_eff_m": 1380.180019258212, "directional": false, "heading_deg": 0.0}, {"channel": 16, "x": -704.338944452677, "y": -1500.6801256821625, "r_eff_m": 1260.765213581664, "directional": false, "heading_deg": 0.0}, {"channel": 9, "x": 301.3279949940345, "y": -1195.568730289507, "r_eff_m": 1268.9333402487596, "directional": false, "heading_deg": 0.0}, {"channel": 19, "x": -1016.5047516327342, "y": -1362.2054377699205, "r_eff_m": 1029.8927902123228, "directional": false, "heading_deg": 0.0}, {"channel": 17, "x": -1128.3611120627002, "y": -751.6668239293164, "r_eff_m": 1073.440585013539, "directional": false, "heading_deg": 0.0}, {"channel": 2, "x": 1267.339479448259, "y": 687.5619158006604, "r_eff_m": 1380.3211633911274, "directional": false, "heading_deg": 0.0}, {"channel": 14, "x": -431.7778870393432, "y": 921.0181764839424, "r_eff_m": 1418.973099517202, "directional": false, "heading_deg": 0.0}, {"channel": 11, "x": 437.0112473060851, "y": -112.52557536387796, "r_eff_m": 1181.7035745633132, "directional": false, "heading_deg": 0.0}]}
{"event": "action", "seq": 1, "wall_ts_ms": 1789097576646, "method": "POST", "path": "/enter", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0001"}, "response": {"accepted": true, "real_timestamp_ms": 1789097576646, "virtual_time_s": 0.0, "max_virtual_duration_s": 360000.0, "max_real_duration_s": 1200, "remaining_real_duration_s": 1200}}
{"event": "action", "seq": 2, "wall_ts_ms": 1789097576662, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0002", "position": {"x": 0.0, "y": 0.0}, "channel": 1}, "response": {"accepted": true, "real_timestamp_ms": 1789097576662, "virtual_time_s": 5.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 3, "wall_ts_ms": 1789097576667, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0003", "position": {"x": 0.0, "y": 0.0}, "channel": 2}, "response": {"accepted": true, "real_timestamp_ms": 1789097576667, "virtual_time_s": 11.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 4, "wall_ts_ms": 1789097576672, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0004", "position": {"x": 0.0, "y": 0.0}, "channel": 3}, "response": {"accepted": true, "real_timestamp_ms": 1789097576672, "virtual_time_s": 17.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 5, "wall_ts_ms": 1789097576677, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0005", "position": {"x": 0.0, "y": 0.0}, "channel": 4}, "response": {"accepted": true, "real_timestamp_ms": 1789097576677, "virtual_time_s": 23.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 6, "wall_ts_ms": 1789097576689, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0006", "position": {"x": 0.0, "y": 0.0}, "channel": 5}, "response": {"accepted": true, "real_timestamp_ms": 1789097576688, "virtual_time_s": 29.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 7, "wall_ts_ms": 1789097576716, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0007", "position": {"x": 0.0, "y": 0.0}, "channel": 6}, "response": {"accepted": true, "real_timestamp_ms": 1789097576716, "virtual_time_s": 35.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 8, "wall_ts_ms": 1789097576727, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0008", "position": {"x": 0.0, "y": 0.0}, "channel": 7}, "response": {"accepted": true, "real_timestamp_ms": 1789097576727, "virtual_time_s": 41.0, "measure_result": "direction", "svd_deg": 329.53}}
{"event": "action", "seq": 9, "wall_ts_ms": 1789097576757, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0009", "position": {"x": 0.0, "y": 0.0}, "channel": 8}, "response": {"accepted": true, "real_timestamp_ms": 1789097576757, "virtual_time_s": 47.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 10, "wall_ts_ms": 1789097576762, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0010", "position": {"x": 0.0, "y": 0.0}, "channel": 9}, "response": {"accepted": true, "real_timestamp_ms": 1789097576762, "virtual_time_s": 53.0, "measure_result": "direction", "svd_deg": 284.61}}
{"event": "action", "seq": 11, "wall_ts_ms": 1789097576791, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0011", "position": {"x": 0.0, "y": 0.0}, "channel": 10}, "response": {"accepted": true, "real_timestamp_ms": 1789097576791, "virtual_time_s": 59.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 12, "wall_ts_ms": 1789097576796, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0012", "position": {"x": 0.0, "y": 0.0}, "channel": 11}, "response": {"accepted": true, "real_timestamp_ms": 1789097576796, "virtual_time_s": 65.0, "measure_result": "direction", "svd_deg": 345.95}}
{"event": "action", "seq": 13, "wall_ts_ms": 1789097576825, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0013", "position": {"x": 0.0, "y": 0.0}, "channel": 12}, "response": {"accepted": true, "real_timestamp_ms": 1789097576825, "virtual_time_s": 71.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 14, "wall_ts_ms": 1789097576830, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0014", "position": {"x": 0.0, "y": 0.0}, "channel": 13}, "response": {"accepted": true, "real_timestamp_ms": 1789097576830, "virtual_time_s": 77.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 15, "wall_ts_ms": 1789097576835, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0015", "position": {"x": 0.0, "y": 0.0}, "channel": 14}, "response": {"accepted": true, "real_timestamp_ms": 1789097576835, "virtual_time_s": 83.0, "measure_result": "direction", "svd_deg": 115.07}}
{"event": "action", "seq": 16, "wall_ts_ms": 1789097576864, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0016", "position": {"x": 0.0, "y": 0.0}, "channel": 15}, "response": {"accepted": true, "real_timestamp_ms": 1789097576864, "virtual_time_s": 89.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 17, "wall_ts_ms": 1789097576868, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0017", "position": {"x": 0.0, "y": 0.0}, "channel": 16}, "response": {"accepted": true, "real_timestamp_ms": 1789097576868, "virtual_time_s": 95.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 18, "wall_ts_ms": 1789097576873, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0018", "position": {"x": 0.0, "y": 0.0}, "channel": 17}, "response": {"accepted": true, "real_timestamp_ms": 1789097576873, "virtual_time_s": 101.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 19, "wall_ts_ms": 1789097576878, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0019", "position": {"x": 0.0, "y": 0.0}, "channel": 18}, "response": {"accepted": true, "real_timestamp_ms": 1789097576878, "virtual_time_s": 107.0, "measure_result": "direction", "svd_deg": 334.09}}
{"event": "action", "seq": 20, "wall_ts_ms": 1789097576907, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0020", "position": {"x": 0.0, "y": 0.0}, "channel": 19}, "response": {"accepted": true, "real_timestamp_ms": 1789097576907, "virtual_time_s": 113.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 21, "wall_ts_ms": 1789097576913, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0021", "position": {"x": 0.0, "y": 0.0}, "channel": 20}, "response": {"accepted": true, "real_timestamp_ms": 1789097576913, "virtual_time_s": 119.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 22, "wall_ts_ms": 1789097577020, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0022", "position": {"x": -382.32549659893994, "y": 438.1825018611922}, "channel": 14}, "response": {"accepted": true, "real_timestamp_ms": 1789097577020, "virtual_time_s": 241.305923, "measure_result": "direction", "svd_deg": 95.7}}
{"event": "action", "seq": 23, "wall_ts_ms": 1789097577105, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0023", "position": {"x": -599.9999999999998, "y": 1039.2304845413264}, "channel": 1}, "response": {"accepted": true, "real_timestamp_ms": 1789097577105, "virtual_time_s": 375.155971, "measure_result": "no_signal"}}
{"event": "action", "seq": 24, "wall_ts_ms": 1789097577108, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0024", "position": {"x": -599.9999999999998, "y": 1039.2304845413264}, "channel": 2}, "response": {"accepted": true, "real_timestamp_ms": 1789097577108, "virtual_time_s": 381.155971, "measure_result": "no_signal"}}
{"event": "action", "seq": 25, "wall_ts_ms": 1789097577111, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0025", "position": {"x": -599.9999999999998, "y": 1039.2304845413264}, "channel": 3}, "response": {"accepted": true, "real_timestamp_ms": 1789097577111, "virtual_time_s": 387.155971, "measure_result": "no_signal"}}
{"event": "action", "seq": 26, "wall_ts_ms": 1789097577114, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0026", "position": {"x": -599.9999999999998, "y": 1039.2304845413264}, "channel": 4}, "response": {"accepted": true, "real_timestamp_ms": 1789097577114, "virtual_time_s": 393.155971, "measure_result": "no_signal"}}
{"event": "action", "seq": 27, "wall_ts_ms": 1789097577117, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0027", "position": {"x": -599.9999999999998, "y": 1039.2304845413264}, "channel": 5}, "response": {"accepted": true, "real_timestamp_ms": 1789097577117, "virtual_time_s": 399.155971, "measure_result": "no_signal"}}
{"event": "action", "seq": 28, "wall_ts_ms": 1789097577121, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0028", "position": {"x": -599.9999999999998, "y": 1039.2304845413264}, "channel": 6}, "response": {"accepted": true, "real_timestamp_ms": 1789097577121, "virtual_time_s": 405.155971, "measure_result": "no_signal"}}
{"event": "action", "seq": 29, "wall_ts_ms": 1789097577124, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0029", "position": {"x": -599.9999999999998, "y": 1039.2304845413264}, "channel": 8}, "response": {"accepted": true, "real_timestamp_ms": 1789097577124, "virtual_time_s": 411.155971, "measure_result": "no_signal"}}
{"event": "action", "seq": 30, "wall_ts_ms": 1789097577128, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0030", "position": {"x": -599.9999999999998, "y": 1039.2304845413264}, "channel": 10}, "response": {"accepted": true, "real_timestamp_ms": 1789097577128, "virtual_time_s": 417.155971, "measure_result": "no_signal"}}
{"event": "action", "seq": 31, "wall_ts_ms": 1789097577132, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0031", "position": {"x": -599.9999999999998, "y": 1039.2304845413264}, "channel": 12}, "response": {"accepted": true, "real_timestamp_ms": 1789097577132, "virtual_time_s": 423.155971, "measure_result": "no_signal"}}
{"event": "action", "seq": 32, "wall_ts_ms": 1789097577137, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0032", "position": {"x": -599.9999999999998, "y": 1039.2304845413264}, "channel": 13}, "response": {"accepted": true, "real_timestamp_ms": 1789097577136, "virtual_time_s": 429.155971, "measure_result": "direction", "svd_deg": 112.67}}
{"event": "action", "seq": 33, "wall_ts_ms": 1789097577155, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0033", "position": {"x": -599.9999999999998, "y": 1039.2304845413264}, "channel": 15}, "response": {"accepted": true, "real_timestamp_ms": 1789097577155, "virtual_time_s": 435.155971, "measure_result": "no_signal"}}
{"event": "action", "seq": 34, "wall_ts_ms": 1789097577158, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0034", "position": {"x": -599.9999999999998, "y": 1039.2304845413264}, "channel": 16}, "response": {"accepted": true, "real_timestamp_ms": 1789097577158, "virtual_time_s": 441.155971, "measure_result": "no_signal"}}
{"event": "action", "seq": 35, "wall_ts_ms": 1789097577161, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0035", "position": {"x": -599.9999999999998, "y": 1039.2304845413264}, "channel": 17}, "response": {"accepted": true, "real_timestamp_ms": 1789097577161, "virtual_time_s": 447.155971, "measure_result": "no_signal"}}
{"event": "action", "seq": 36, "wall_ts_ms": 1789097577165, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0036", "position": {"x": -599.9999999999998, "y": 1039.2304845413264}, "channel": 19}, "response": {"accepted": true, "real_timestamp_ms": 1789097577165, "virtual_time_s": 453.155971, "measure_result": "no_signal"}}
{"event": "action", "seq": 37, "wall_ts_ms": 1789097577169, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0037", "position": {"x": -599.9999999999998, "y": 1039.2304845413264}, "channel": 20}, "response": {"accepted": true, "real_timestamp_ms": 1789097577169, "virtual_time_s": 459.155971, "measure_result": "no_signal"}}
{"event": "action", "seq": 38, "wall_ts_ms": 1789097577275, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0038", "position": {"x": -432.59886137022454, "y": 927.952956866304}, "channel": 14}, "response": {"accepted": true, "real_timestamp_ms": 1789097577275, "virtual_time_s": 505.358374, "measure_result": "direction", "svd_deg": 276.22}}
{"event": "action", "seq": 39, "wall_ts_ms": 1789097577364, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0039", "position": {"x": -428.8404761449007, "y": 898.3063116196065}, "channel": 14}, "response": {"accepted": true, "real_timestamp_ms": 1789097577364, "virtual_time_s": 516.335159, "measure_result": "direction", "svd_deg": 96.95}}
{"event": "action", "seq": 40, "wall_ts_ms": 1789097577365, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0040", "position": {"x": -430.7196687575625, "y": 913.129634242954}, "channel": 14}, "response": {"accepted": true, "real_timestamp_ms": 1789097577365, "virtual_time_s": 524.323552, "clear_result": "success"}}
{"event": "action", "seq": 41, "wall_ts_ms": 1789097577455, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0041", "position": {"x": -441.9875302830439, "y": 1247.8826866081681}, "channel": 13}, "response": {"accepted": true, "real_timestamp_ms": 1789097577455, "virtual_time_s": 597.31208, "measure_result": "direction", "svd_deg": 191.28}}
{"event": "action", "seq": 42, "wall_ts_ms": 1789097577456, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0042", "position": {"x": -668.4093907720109, "y": 1202.7926660772903}, "channel": 13}, "response": {"accepted": true, "real_timestamp_ms": 1789097577456, "virtual_time_s": 648.485652, "clear_result": "success"}}
{"event": "action", "seq": 43, "wall_ts_ms": 1789097577536, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0043", "position": {"x": 600.0000000000001, "y": 1039.2304845413264}, "channel": 1}, "response": {"accepted": true, "real_timestamp_ms": 1789097577536, "virtual_time_s": 910.267979, "measure_result": "no_signal"}}
{"event": "action", "seq": 44, "wall_ts_ms": 1789097577539, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0044", "position": {"x": 600.0000000000001, "y": 1039.2304845413264}, "channel": 2}, "response": {"accepted": true, "real_timestamp_ms": 1789097577539, "virtual_time_s": 916.267979, "measure_result": "direction", "svd_deg": 332.39}}
{"event": "action", "seq": 45, "wall_ts_ms": 1789097577554, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0045", "position": {"x": 600.0000000000001, "y": 1039.2304845413264}, "channel": 3}, "response": {"accepted": true, "real_timestamp_ms": 1789097577554, "virtual_time_s": 922.267979, "measure_result": "no_signal"}}
{"event": "action", "seq": 46, "wall_ts_ms": 1789097577557, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0046", "position": {"x": 600.0000000000001, "y": 1039.2304845413264}, "channel": 4}, "response": {"accepted": true, "real_timestamp_ms": 1789097577557, "virtual_time_s": 928.267979, "measure_result": "no_signal"}}
{"event": "action", "seq": 47, "wall_ts_ms": 1789097577560, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0047", "position": {"x": 600.0000000000001, "y": 1039.2304845413264}, "channel": 5}, "response": {"accepted": true, "real_timestamp_ms": 1789097577560, "virtual_time_s": 934.267979, "measure_result": "no_signal"}}
{"event": "action", "seq": 48, "wall_ts_ms": 1789097577564, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0048", "position": {"x": 600.0000000000001, "y": 1039.2304845413264}, "channel": 6}, "response": {"accepted": true, "real_timestamp_ms": 1789097577564, "virtual_time_s": 940.267979, "measure_result": "no_signal"}}
{"event": "action", "seq": 49, "wall_ts_ms": 1789097577567, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0049", "position": {"x": 600.0000000000001, "y": 1039.2304845413264}, "channel": 8}, "response": {"accepted": true, "real_timestamp_ms": 1789097577567, "virtual_time_s": 946.267979, "measure_result": "no_signal"}}
{"event": "action", "seq": 50, "wall_ts_ms": 1789097577571, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0050", "position": {"x": 600.0000000000001, "y": 1039.2304845413264}, "channel": 10}, "response": {"accepted": true, "real_timestamp_ms": 1789097577571, "virtual_time_s": 952.267979, "measure_result": "no_signal"}}
{"event": "action", "seq": 51, "wall_ts_ms": 1789097577574, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0051", "position": {"x": 600.0000000000001, "y": 1039.2304845413264}, "channel": 12}, "response": {"accepted": true, "real_timestamp_ms": 1789097577574, "virtual_time_s": 958.267979, "measure_result": "no_signal"}}
{"event": "action", "seq": 52, "wall_ts_ms": 1789097577577, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0052", "position": {"x": 600.0000000000001, "y": 1039.2304845413264}, "channel": 15}, "response": {"accepted": true, "real_timestamp_ms": 1789097577577, "virtual_time_s": 964.267979, "measure_result": "no_signal"}}
{"event": "action", "seq": 53, "wall_ts_ms": 1789097577581, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0053", "position": {"x": 600.0000000000001, "y": 1039.2304845413264}, "channel": 16}, "response": {"accepted": true, "real_timestamp_ms": 1789097577581, "virtual_time_s": 970.267979, "measure_result": "no_signal"}}
{"event": "action", "seq": 54, "wall_ts_ms": 1789097577583, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0054", "position": {"x": 600.0000000000001, "y": 1039.2304845413264}, "channel": 17}, "response": {"accepted": true, "real_timestamp_ms": 1789097577583, "virtual_time_s": 976.267979, "measure_result": "no_signal"}}
{"event": "action", "seq": 55, "wall_ts_ms": 1789097577586, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0055", "position": {"x": 600.0000000000001, "y": 1039.2304845413264}, "channel": 19}, "response": {"accepted": true, "real_timestamp_ms": 1789097577586, "virtual_time_s": 982.267979, "measure_result": "no_signal"}}
{"event": "action", "seq": 56, "wall_ts_ms": 1789097577588, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0056", "position": {"x": 600.0000000000001, "y": 1039.2304845413264}, "channel": 20}, "response": {"accepted": true, "real_timestamp_ms": 1789097577588, "virtual_time_s": 988.267979, "measure_result": "no_signal"}}
{"event": "action", "seq": 57, "wall_ts_ms": 1789097577687, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0057", "position": {"x": 994.1110687766269, "y": 542.6492729278544}, "channel": 2}, "response": {"accepted": true, "real_timestamp_ms": 1789097577687, "virtual_time_s": 1121.061738, "measure_result": "direction", "svd_deg": 28.25}}
{"event": "action", "seq": 58, "wall_ts_ms": 1789097577767, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0058", "position": {"x": 1268.7031752387938, "y": 689.8006776789011}, "channel": 2}, "response": {"accepted": true, "real_timestamp_ms": 1789097577767, "virtual_time_s": 1188.368836, "measure_result": "near"}}
{"event": "action", "seq": 59, "wall_ts_ms": 1789097577773, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0059", "position": {"x": 1268.7031752387938, "y": 689.8006776789011}, "channel": 2}, "response": {"accepted": true, "real_timestamp_ms": 1789097577773, "virtual_time_s": 1193.368836, "clear_result": "success"}}
{"event": "action", "seq": 60, "wall_ts_ms": 1789097577859, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0060", "position": {"x": 1200.0, "y": 0.0}, "channel": 1}, "response": {"accepted": true, "real_timestamp_ms": 1789097577859, "virtual_time_s": 1338.011557, "measure_result": "no_signal"}}
{"event": "action", "seq": 61, "wall_ts_ms": 1789097577861, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0061", "position": {"x": 1200.0, "y": 0.0}, "channel": 3}, "response": {"accepted": true, "real_timestamp_ms": 1789097577861, "virtual_time_s": 1344.011557, "measure_result": "no_signal"}}
{"event": "action", "seq": 62, "wall_ts_ms": 1789097577864, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0062", "position": {"x": 1200.0, "y": 0.0}, "channel": 4}, "response": {"accepted": true, "real_timestamp_ms": 1789097577864, "virtual_time_s": 1350.011557, "measure_result": "no_signal"}}
{"event": "action", "seq": 63, "wall_ts_ms": 1789097577866, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0063", "position": {"x": 1200.0, "y": 0.0}, "channel": 5}, "response": {"accepted": true, "real_timestamp_ms": 1789097577866, "virtual_time_s": 1356.011557, "measure_result": "no_signal"}}
{"event": "action", "seq": 64, "wall_ts_ms": 1789097577869, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0064", "position": {"x": 1200.0, "y": 0.0}, "channel": 6}, "response": {"accepted": true, "real_timestamp_ms": 1789097577869, "virtual_time_s": 1362.011557, "measure_result": "no_signal"}}
{"event": "action", "seq": 65, "wall_ts_ms": 1789097577871, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0065", "position": {"x": 1200.0, "y": 0.0}, "channel": 8}, "response": {"accepted": true, "real_timestamp_ms": 1789097577871, "virtual_time_s": 1368.011557, "measure_result": "no_signal"}}
{"event": "action", "seq": 66, "wall_ts_ms": 1789097577873, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0066", "position": {"x": 1200.0, "y": 0.0}, "channel": 10}, "response": {"accepted": true, "real_timestamp_ms": 1789097577873, "virtual_time_s": 1374.011557, "measure_result": "no_signal"}}
{"event": "action", "seq": 67, "wall_ts_ms": 1789097577876, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0067", "position": {"x": 1200.0, "y": 0.0}, "channel": 12}, "response": {"accepted": true, "real_timestamp_ms": 1789097577875, "virtual_time_s": 1380.011557, "measure_result": "no_signal"}}
{"event": "action", "seq": 68, "wall_ts_ms": 1789097577878, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0068", "position": {"x": 1200.0, "y": 0.0}, "channel": 15}, "response": {"accepted": true, "real_timestamp_ms": 1789097577878, "virtual_time_s": 1386.011557, "measure_result": "no_signal"}}
{"event": "action", "seq": 69, "wall_ts_ms": 1789097577881, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0069", "position": {"x": 1200.0, "y": 0.0}, "channel": 16}, "response": {"accepted": true, "real_timestamp_ms": 1789097577881, "virtual_time_s": 1392.011557, "measure_result": "no_signal"}}
{"event": "action", "seq": 70, "wall_ts_ms": 1789097577884, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0070", "position": {"x": 1200.0, "y": 0.0}, "channel": 17}, "response": {"accepted": true, "real_timestamp_ms": 1789097577884, "virtual_time_s": 1398.011557, "measure_result": "no_signal"}}
{"event": "action", "seq": 71, "wall_ts_ms": 1789097577888, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0071", "position": {"x": 1200.0, "y": 0.0}, "channel": 19}, "response": {"accepted": true, "real_timestamp_ms": 1789097577888, "virtual_time_s": 1404.011557, "measure_result": "no_signal"}}
{"event": "action", "seq": 72, "wall_ts_ms": 1789097577890, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0072", "position": {"x": 1200.0, "y": 0.0}, "channel": 20}, "response": {"accepted": true, "real_timestamp_ms": 1789097577890, "virtual_time_s": 1410.011557, "measure_result": "no_signal"}}
{"event": "action", "seq": 73, "wall_ts_ms": 1789097577973, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0073", "position": {"x": 904.3723232997338, "y": -5.399878906793646}, "channel": 11}, "response": {"accepted": true, "real_timestamp_ms": 1789097577973, "virtual_time_s": 1475.146955, "measure_result": "direction", "svd_deg": 193.85}}
{"event": "action", "seq": 74, "wall_ts_ms": 1789097578037, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0074", "position": {"x": 732.9363400589687, "y": 158.81239802766925}, "channel": 11}, "response": {"accepted": true, "real_timestamp_ms": 1789097578037, "virtual_time_s": 1527.625779, "measure_result": "direction", "svd_deg": 223.37}}
{"event": "action", "seq": 75, "wall_ts_ms": 1789097578039, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0075", "position": {"x": 446.4480711916748, "y": -111.48435372746496}, "channel": 11}, "response": {"accepted": true, "real_timestamp_ms": 1789097578039, "virtual_time_s": 1611.400359, "clear_result": "success"}}
{"event": "action", "seq": 76, "wall_ts_ms": 1789097578098, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0076", "position": {"x": 609.8586286156244, "y": -86.93118226598972}, "channel": 18}, "response": {"accepted": true, "real_timestamp_ms": 1789097578098, "virtual_time_s": 1650.449334, "measure_result": "direction", "svd_deg": 312.82}}
{"event": "action", "seq": 77, "wall_ts_ms": 1789097578145, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0077", "position": {"x": 711.4189708756367, "y": -138.68820908196022}, "channel": 7}, "response": {"accepted": true, "real_timestamp_ms": 1789097578145, "virtual_time_s": 1679.246959, "measure_result": "direction", "svd_deg": 311.79}}
{"event": "action", "seq": 78, "wall_ts_ms": 1789097578171, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0078", "position": {"x": 968.3799882543295, "y": -471.87178417317915}, "channel": 18}, "response": {"accepted": true, "real_timestamp_ms": 1789097578171, "virtual_time_s": 1769.399264, "measure_result": "direction", "svd_deg": 145.41}}
{"event": "action", "seq": 79, "wall_ts_ms": 1789097578198, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0079", "position": {"x": 948.6903772101683, "y": -457.77800595341034}, "channel": 18}, "response": {"accepted": true, "real_timestamp_ms": 1789097578198, "virtual_time_s": 1779.242054, "measure_result": "direction", "svd_deg": 144.5}}
{"event": "action", "seq": 80, "wall_ts_ms": 1789097578200, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0080", "position": {"x": 938.8455716880909, "y": -450.7311168435281}, "channel": 18}, "response": {"accepted": true, "real_timestamp_ms": 1789097578200, "virtual_time_s": 1786.663449, "clear_result": "success"}}
{"event": "action", "seq": 81, "wall_ts_ms": 1789097578222, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0081", "position": {"x": 1216.8995655319154, "y": -717.7217593627017}, "channel": 7}, "response": {"accepted": true, "real_timestamp_ms": 1789097578222, "virtual_time_s": 1869.760278, "measure_result": "direction", "svd_deg": 132.81}}
{"event": "action", "seq": 82, "wall_ts_ms": 1789097578247, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0082", "position": {"x": 1185.682710431516, "y": -684.9782355200915}, "channel": 7}, "response": {"accepted": true, "real_timestamp_ms": 1789097578247, "virtual_time_s": 1883.808218, "measure_result": "direction", "svd_deg": 132.18}}
{"event": "action", "seq": 83, "wall_ts_ms": 1789097578269, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0083", "position": {"x": 1170.5902788598225, "y": -668.8979941203445}, "channel": 7}, "response": {"accepted": true, "real_timestamp_ms": 1789097578269, "virtual_time_s": 1893.218913, "measure_result": "near"}}
{"event": "action", "seq": 84, "wall_ts_ms": 1789097578272, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0084", "position": {"x": 1170.6182266765854, "y": -668.8718181334558}, "channel": 7}, "response": {"accepted": true, "real_timestamp_ms": 1789097578272, "virtual_time_s": 1898.226571, "clear_result": "success"}}
{"event": "action", "seq": 85, "wall_ts_ms": 1789097578293, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0085", "position": {"x": 600.0000000000001, "y": -1039.2304845413264}, "channel": 1}, "response": {"accepted": true, "real_timestamp_ms": 1789097578293, "virtual_time_s": 2040.281075, "measure_result": "no_signal"}}
{"event": "action", "seq": 86, "wall_ts_ms": 1789097578296, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0086", "position": {"x": 600.0000000000001, "y": -1039.2304845413264}, "channel": 3}, "response": {"accepted": true, "real_timestamp_ms": 1789097578296, "virtual_time_s": 2046.281075, "measure_result": "no_signal"}}
{"event": "action", "seq": 87, "wall_ts_ms": 1789097578299, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0087", "position": {"x": 600.0000000000001, "y": -1039.2304845413264}, "channel": 4}, "response": {"accepted": true, "real_timestamp_ms": 1789097578299, "virtual_time_s": 2052.281075, "measure_result": "no_signal"}}
{"event": "action", "seq": 88, "wall_ts_ms": 1789097578301, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0088", "position": {"x": 600.0000000000001, "y": -1039.2304845413264}, "channel": 5}, "response": {"accepted": true, "real_timestamp_ms": 1789097578301, "virtual_time_s": 2058.281075, "measure_result": "no_signal"}}
{"event": "action", "seq": 89, "wall_ts_ms": 1789097578304, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0089", "position": {"x": 600.0000000000001, "y": -1039.2304845413264}, "channel": 6}, "response": {"accepted": true, "real_timestamp_ms": 1789097578304, "virtual_time_s": 2064.281075, "measure_result": "no_signal"}}
{"event": "action", "seq": 90, "wall_ts_ms": 1789097578307, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0090", "position": {"x": 600.0000000000001, "y": -1039.2304845413264}, "channel": 8}, "response": {"accepted": true, "real_timestamp_ms": 1789097578307, "virtual_time_s": 2070.281075, "measure_result": "no_signal"}}
{"event": "action", "seq": 91, "wall_ts_ms": 1789097578311, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0091", "position": {"x": 600.0000000000001, "y": -1039.2304845413264}, "channel": 10}, "response": {"accepted": true, "real_timestamp_ms": 1789097578311, "virtual_time_s": 2076.281075, "measure_result": "no_signal"}}
{"event": "action", "seq": 92, "wall_ts_ms": 1789097578314, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0092", "position": {"x": 600.0000000000001, "y": -1039.2304845413264}, "channel": 12}, "response": {"accepted": true, "real_timestamp_ms": 1789097578314, "virtual_time_s": 2082.281075, "measure_result": "no_signal"}}
{"event": "action", "seq": 93, "wall_ts_ms": 1789097578318, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0093", "position": {"x": 600.0000000000001, "y": -1039.2304845413264}, "channel": 15}, "response": {"accepted": true, "real_timestamp_ms": 1789097578318, "virtual_time_s": 2088.281075, "measure_result": "no_signal"}}
{"event": "action", "seq": 94, "wall_ts_ms": 1789097578322, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0094", "position": {"x": 600.0000000000001, "y": -1039.2304845413264}, "channel": 16}, "response": {"accepted": true, "real_timestamp_ms": 1789097578322, "virtual_time_s": 2094.281075, "measure_result": "no_signal"}}
{"event": "action", "seq": 95, "wall_ts_ms": 1789097578325, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0095", "position": {"x": 600.0000000000001, "y": -1039.2304845413264}, "channel": 17}, "response": {"accepted": true, "real_timestamp_ms": 1789097578325, "virtual_time_s": 2100.281075, "measure_result": "no_signal"}}
{"event": "action", "seq": 96, "wall_ts_ms": 1789097578328, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0096", "position": {"x": 600.0000000000001, "y": -1039.2304845413264}, "channel": 19}, "response": {"accepted": true, "real_timestamp_ms": 1789097578328, "virtual_time_s": 2106.281075, "measure_result": "no_signal"}}
{"event": "action", "seq": 97, "wall_ts_ms": 1789097578332, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0097", "position": {"x": 600.0000000000001, "y": -1039.2304845413264}, "channel": 20}, "response": {"accepted": true, "real_timestamp_ms": 1789097578332, "virtual_time_s": 2112.281075, "measure_result": "no_signal"}}
{"event": "action", "seq": 98, "wall_ts_ms": 1789097578355, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0098", "position": {"x": 431.0177074456876, "y": -790.4383437162506}, "channel": 9}, "response": {"accepted": true, "real_timestamp_ms": 1789097578355, "virtual_time_s": 2178.431734, "measure_result": "direction", "svd_deg": 252.8}}
{"event": "action", "seq": 99, "wall_ts_ms": 1789097578358, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0099", "position": {"x": 308.8495661381867, "y": -1188.2218722331802}, "channel": 9}, "response": {"accepted": true, "real_timestamp_ms": 1789097578358, "virtual_time_s": 2266.655959, "measure_result": "direction", "svd_deg": 225.19}}
{"event": "action", "seq": 100, "wall_ts_ms": 1789097578359, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0100", "position": {"x": 302.26396092359636, "y": -1195.0880783213174}, "channel": 9}, "response": {"accepted": true, "real_timestamp_ms": 1789097578359, "virtual_time_s": 2273.558746, "clear_result": "success"}}
{"event": "action", "seq": 101, "wall_ts_ms": 1789097578360, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0101", "position": {"x": -600.0000000000006, "y": -1039.230484541326}, "channel": 1}, "response": {"accepted": true, "real_timestamp_ms": 1789097578360, "virtual_time_s": 2462.684041, "measure_result": "no_signal"}}
{"event": "action", "seq": 102, "wall_ts_ms": 1789097578363, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0102", "position": {"x": -600.0000000000006, "y": -1039.230484541326}, "channel": 3}, "response": {"accepted": true, "real_timestamp_ms": 1789097578363, "virtual_time_s": 2468.684041, "measure_result": "no_signal"}}
{"event": "action", "seq": 103, "wall_ts_ms": 1789097578366, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0103", "position": {"x": -600.0000000000006, "y": -1039.230484541326}, "channel": 4}, "response": {"accepted": true, "real_timestamp_ms": 1789097578366, "virtual_time_s": 2474.684041, "measure_result": "no_signal"}}
{"event": "action", "seq": 104, "wall_ts_ms": 1789097578371, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0104", "position": {"x": -600.0000000000006, "y": -1039.230484541326}, "channel": 5}, "response": {"accepted": true, "real_timestamp_ms": 1789097578371, "virtual_time_s": 2480.684041, "measure_result": "no_signal"}}
{"event": "action", "seq": 105, "wall_ts_ms": 1789097578374, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0105", "position": {"x": -600.0000000000006, "y": -1039.230484541326}, "channel": 6}, "response": {"accepted": true, "real_timestamp_ms": 1789097578374, "virtual_time_s": 2486.684041, "measure_result": "no_signal"}}
{"event": "action", "seq": 106, "wall_ts_ms": 1789097578377, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0106", "position": {"x": -600.0000000000006, "y": -1039.230484541326}, "channel": 8}, "response": {"accepted": true, "real_timestamp_ms": 1789097578377, "virtual_time_s": 2492.684041, "measure_result": "no_signal"}}
{"event": "action", "seq": 107, "wall_ts_ms": 1789097578380, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0107", "position": {"x": -600.0000000000006, "y": -1039.230484541326}, "channel": 10}, "response": {"accepted": true, "real_timestamp_ms": 1789097578380, "virtual_time_s": 2498.684041, "measure_result": "no_signal"}}
{"event": "action", "seq": 108, "wall_ts_ms": 1789097578384, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0108", "position": {"x": -600.0000000000006, "y": -1039.230484541326}, "channel": 12}, "response": {"accepted": true, "real_timestamp_ms": 1789097578383, "virtual_time_s": 2504.684041, "measure_result": "no_signal"}}
{"event": "action", "seq": 109, "wall_ts_ms": 1789097578386, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0109", "position": {"x": -600.0000000000006, "y": -1039.230484541326}, "channel": 15}, "response": {"accepted": true, "real_timestamp_ms": 1789097578386, "virtual_time_s": 2510.684041, "measure_result": "no_signal"}}
{"event": "action", "seq": 110, "wall_ts_ms": 1789097578389, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0110", "position": {"x": -600.0000000000006, "y": -1039.230484541326}, "channel": 16}, "response": {"accepted": true, "real_timestamp_ms": 1789097578389, "virtual_time_s": 2516.684041, "measure_result": "direction", "svd_deg": 257.75}}
{"event": "action", "seq": 111, "wall_ts_ms": 1789097578399, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0111", "position": {"x": -600.0000000000006, "y": -1039.230484541326}, "channel": 17}, "response": {"accepted": true, "real_timestamp_ms": 1789097578399, "virtual_time_s": 2522.684041, "measure_result": "direction", "svd_deg": 150.83}}
{"event": "action", "seq": 112, "wall_ts_ms": 1789097578409, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0112", "position": {"x": -600.0000000000006, "y": -1039.230484541326}, "channel": 19}, "response": {"accepted": true, "real_timestamp_ms": 1789097578409, "virtual_time_s": 2528.684041, "measure_result": "direction", "svd_deg": 217.09}}
{"event": "action", "seq": 113, "wall_ts_ms": 1789097578419, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0113", "position": {"x": -600.0000000000006, "y": -1039.230484541326}, "channel": 20}, "response": {"accepted": true, "real_timestamp_ms": 1789097578419, "virtual_time_s": 2534.684041, "measure_result": "no_signal"}}
{"event": "action", "seq": 114, "wall_ts_ms": 1789097578449, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0114", "position": {"x": -855.523050877763, "y": -1149.4070702441772}, "channel": 16}, "response": {"accepted": true, "real_timestamp_ms": 1789097578449, "virtual_time_s": 2596.336861, "measure_result": "direction", "svd_deg": 293.06}}
{"event": "action", "seq": 115, "wall_ts_ms": 1789097578473, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0115", "position": {"x": -702.0198196486191, "y": -1511.0796905664733}, "channel": 16}, "response": {"accepted": true, "real_timestamp_ms": 1789097578473, "virtual_time_s": 2679.91684, "measure_result": "direction", "svd_deg": 101.78}}
{"event": "action", "seq": 116, "wall_ts_ms": 1789097578475, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0116", "position": {"x": -704.0596428309664, "y": -1500.3611560598777}, "channel": 16}, "response": {"accepted": true, "real_timestamp_ms": 1789097578475, "virtual_time_s": 2687.099021, "clear_result": "success"}}
{"event": "action", "seq": 117, "wall_ts_ms": 1789097578495, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0117", "position": {"x": -786.4741505952657, "y": -1490.051846445478}, "channel": 19}, "response": {"accepted": true, "real_timestamp_ms": 1789097578495, "virtual_time_s": 2709.710382, "measure_result": "direction", "svd_deg": 151.04}}
{"event": "action", "seq": 118, "wall_ts_ms": 1789097578497, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0118", "position": {"x": -1023.4461023835017, "y": -1359.1588736906476}, "channel": 19}, "response": {"accepted": true, "real_timestamp_ms": 1789097578497, "virtual_time_s": 2768.854145, "clear_result": "success"}}
{"event": "action", "seq": 119, "wall_ts_ms": 1789097578511, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0119", "position": {"x": -1266.2459250683066, "y": -1017.2747950835054}, "channel": 17}, "response": {"accepted": true, "real_timestamp_ms": 1789097578511, "virtual_time_s": 2858.719866, "measure_result": "direction", "svd_deg": 62.8}}
{"event": "action", "seq": 120, "wall_ts_ms": 1789097578513, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0120", "position": {"x": -1126.6121592386512, "y": -745.1634840678316}, "channel": 17}, "response": {"accepted": true, "real_timestamp_ms": 1789097578513, "virtual_time_s": 2924.88919, "clear_result": "success"}}
{"event": "action", "seq": 121, "wall_ts_ms": 1789097578515, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0121", "position": {"x": -1200.0, "y": 1.4695761589768238e-13}, "channel": 1}, "response": {"accepted": true, "real_timestamp_ms": 1789097578515, "virtual_time_s": 3080.642907, "measure_result": "no_signal"}}
{"event": "action", "seq": 122, "wall_ts_ms": 1789097578518, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0122", "position": {"x": -1200.0, "y": 1.4695761589768238e-13}, "channel": 3}, "response": {"accepted": true, "real_timestamp_ms": 1789097578518, "virtual_time_s": 3086.642907, "measure_result": "no_signal"}}
{"event": "action", "seq": 123, "wall_ts_ms": 1789097578520, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0123", "position": {"x": -1200.0, "y": 1.4695761589768238e-13}, "channel": 4}, "response": {"accepted": true, "real_timestamp_ms": 1789097578520, "virtual_time_s": 3092.642907, "measure_result": "no_signal"}}
{"event": "action", "seq": 124, "wall_ts_ms": 1789097578522, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0124", "position": {"x": -1200.0, "y": 1.4695761589768238e-13}, "channel": 5}, "response": {"accepted": true, "real_timestamp_ms": 1789097578522, "virtual_time_s": 3098.642907, "measure_result": "no_signal"}}
{"event": "action", "seq": 125, "wall_ts_ms": 1789097578524, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0125", "position": {"x": -1200.0, "y": 1.4695761589768238e-13}, "channel": 6}, "response": {"accepted": true, "real_timestamp_ms": 1789097578524, "virtual_time_s": 3104.642907, "measure_result": "no_signal"}}
{"event": "action", "seq": 126, "wall_ts_ms": 1789097578526, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0126", "position": {"x": -1200.0, "y": 1.4695761589768238e-13}, "channel": 8}, "response": {"accepted": true, "real_timestamp_ms": 1789097578526, "virtual_time_s": 3110.642907, "measure_result": "no_signal"}}
{"event": "action", "seq": 127, "wall_ts_ms": 1789097578527, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0127", "position": {"x": -1200.0, "y": 1.4695761589768238e-13}, "channel": 10}, "response": {"accepted": true, "real_timestamp_ms": 1789097578527, "virtual_time_s": 3116.642907, "measure_result": "no_signal"}}
{"event": "action", "seq": 128, "wall_ts_ms": 1789097578529, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0128", "position": {"x": -1200.0, "y": 1.4695761589768238e-13}, "channel": 12}, "response": {"accepted": true, "real_timestamp_ms": 1789097578529, "virtual_time_s": 3122.642907, "measure_result": "no_signal"}}
{"event": "action", "seq": 129, "wall_ts_ms": 1789097578531, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0129", "position": {"x": -1200.0, "y": 1.4695761589768238e-13}, "channel": 15}, "response": {"accepted": true, "real_timestamp_ms": 1789097578531, "virtual_time_s": 3128.642907, "measure_result": "no_signal"}}
{"event": "action", "seq": 130, "wall_ts_ms": 1789097578533, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0130", "position": {"x": -1200.0, "y": 1.4695761589768238e-13}, "channel": 20}, "response": {"accepted": true, "real_timestamp_ms": 1789097578533, "virtual_time_s": 3134.642907, "measure_result": "no_signal"}}
{"event": "action", "seq": 131, "wall_ts_ms": 1789097578535, "method": "POST", "path": "/exit", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "4721c649063d-0131"}, "response": {"accepted": true, "real_timestamp_ms": 1789097578535, "virtual_time_s": 3134.642907, "exit_reason": "user_exit"}}
{"event": "session_end", "reason": "user_exit", "virtual_time_s": 3134.642907, "real_elapsed_s": 3.209263, "request_count": 131, "cleared_count": 10}
@@ -0,0 +1,88 @@
{
"case_code": "BBM9-HZG2-77N5-NKKC",
"module": "q3_practice",
"robot_id": "1",
"seed": 214175614,
"truth": [
{
"channel": 13,
"x": -667.5434273375823,
"y": 1201.9279733147766,
"r_eff_m": 1068.7667479727882,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 18,
"x": 936.4803225514449,
"y": -449.0798658945765,
"r_eff_m": 1100.2220789348723,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 7,
"x": 1168.5586737701378,
"y": -665.9024303254582,
"r_eff_m": 1380.180019258212,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 16,
"x": -704.338944452677,
"y": -1500.6801256821625,
"r_eff_m": 1260.765213581664,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 9,
"x": 301.3279949940345,
"y": -1195.568730289507,
"r_eff_m": 1268.9333402487596,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 19,
"x": -1016.5047516327342,
"y": -1362.2054377699205,
"r_eff_m": 1029.8927902123228,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 17,
"x": -1128.3611120627002,
"y": -751.6668239293164,
"r_eff_m": 1073.440585013539,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 2,
"x": 1267.339479448259,
"y": 687.5619158006604,
"r_eff_m": 1380.3211633911274,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 14,
"x": -431.7778870393432,
"y": 921.0181764839424,
"r_eff_m": 1418.973099517202,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 11,
"x": 437.0112473060851,
"y": -112.52557536387796,
"r_eff_m": 1181.7035745633132,
"directional": false,
"heading_deg": 0.0
}
]
}
@@ -0,0 +1 @@
{"event": "session_start", "case_code": "GTS5-XVTM-V9YN-Y4BJ", "module": "q3_practice", "robot_id": "1", "seed": 2128480631, "countdown_s": 5.0, "window_s": 1500.0, "max_real_duration_s": 1200.0, "truth": [{"channel": 14, "x": 869.7117494788679, "y": 1325.898501771252, "r_eff_m": 1167.1710168530835, "directional": false, "heading_deg": 0.0}, {"channel": 7, "x": -1665.7822886877482, "y": 66.52241734048596, "r_eff_m": 1247.496847519186, "directional": false, "heading_deg": 0.0}, {"channel": 17, "x": -267.2501504987328, "y": -1556.6283220268592, "r_eff_m": 1491.0954004659015, "directional": false, "heading_deg": 0.0}, {"channel": 1, "x": -636.3395610110787, "y": -1598.1328560008155, "r_eff_m": 1208.9713764899795, "directional": false, "heading_deg": 0.0}, {"channel": 4, "x": 558.9076992395989, "y": 1032.2007939243106, "r_eff_m": 1211.461745342395, "directional": false, "heading_deg": 0.0}, {"channel": 16, "x": -1532.9905318641804, "y": -709.688503369543, "r_eff_m": 1067.0517959760218, "directional": false, "heading_deg": 0.0}, {"channel": 13, "x": -915.4908507030872, "y": -586.2791352960422, "r_eff_m": 1095.6771524182404, "directional": false, "heading_deg": 0.0}, {"channel": 11, "x": -1153.6749206930394, "y": 519.9519609239459, "r_eff_m": 1400.3919177962928, "directional": false, "heading_deg": 0.0}, {"channel": 18, "x": 1445.4267699899235, "y": 497.35501424117336, "r_eff_m": 1119.6707058479788, "directional": false, "heading_deg": 0.0}, {"channel": 15, "x": -329.7113112604381, "y": -157.01421172012004, "r_eff_m": 1432.5378503183756, "directional": false, "heading_deg": 0.0}, {"channel": 6, "x": 840.8717095338735, "y": -679.5717339286426, "r_eff_m": 1174.5610978768314, "directional": false, "heading_deg": 0.0}, {"channel": 5, "x": 1254.97217446553, "y": -347.277464286233, "r_eff_m": 1170.296779068818, "directional": false, "heading_deg": 0.0}, {"channel": 8, "x": -232.91757962061797, "y": 1588.9705512017863, "r_eff_m": 1102.2856329340332, "directional": false, "heading_deg": 0.0}, {"channel": 10, "x": 760.4387707310572, "y": -568.2401844640306, "r_eff_m": 1040.0816807146166, "directional": false, "heading_deg": 0.0}, {"channel": 12, "x": -685.4862181378795, "y": -1250.2632246388744, "r_eff_m": 1185.3345488548437, "directional": false, "heading_deg": 0.0}]}
@@ -0,0 +1,128 @@
{
"case_code": "GTS5-XVTM-V9YN-Y4BJ",
"module": "q3_practice",
"robot_id": "1",
"seed": 2128480631,
"truth": [
{
"channel": 14,
"x": 869.7117494788679,
"y": 1325.898501771252,
"r_eff_m": 1167.1710168530835,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 7,
"x": -1665.7822886877482,
"y": 66.52241734048596,
"r_eff_m": 1247.496847519186,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 17,
"x": -267.2501504987328,
"y": -1556.6283220268592,
"r_eff_m": 1491.0954004659015,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 1,
"x": -636.3395610110787,
"y": -1598.1328560008155,
"r_eff_m": 1208.9713764899795,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 4,
"x": 558.9076992395989,
"y": 1032.2007939243106,
"r_eff_m": 1211.461745342395,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 16,
"x": -1532.9905318641804,
"y": -709.688503369543,
"r_eff_m": 1067.0517959760218,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 13,
"x": -915.4908507030872,
"y": -586.2791352960422,
"r_eff_m": 1095.6771524182404,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 11,
"x": -1153.6749206930394,
"y": 519.9519609239459,
"r_eff_m": 1400.3919177962928,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 18,
"x": 1445.4267699899235,
"y": 497.35501424117336,
"r_eff_m": 1119.6707058479788,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 15,
"x": -329.7113112604381,
"y": -157.01421172012004,
"r_eff_m": 1432.5378503183756,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 6,
"x": 840.8717095338735,
"y": -679.5717339286426,
"r_eff_m": 1174.5610978768314,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 5,
"x": 1254.97217446553,
"y": -347.277464286233,
"r_eff_m": 1170.296779068818,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 8,
"x": -232.91757962061797,
"y": 1588.9705512017863,
"r_eff_m": 1102.2856329340332,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 10,
"x": 760.4387707310572,
"y": -568.2401844640306,
"r_eff_m": 1040.0816807146166,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 12,
"x": -685.4862181378795,
"y": -1250.2632246388744,
"r_eff_m": 1185.3345488548437,
"directional": false,
"heading_deg": 0.0
}
]
}
@@ -0,0 +1,121 @@
{"event": "session_start", "case_code": "RM7Y-JRYA-9S7E-6J2T", "module": "q3_practice", "robot_id": "1", "seed": 1539645273, "countdown_s": 5.0, "window_s": 1500.0, "max_real_duration_s": 1200.0, "truth": [{"channel": 18, "x": 1514.5988909830828, "y": 578.5437460895635, "r_eff_m": 1242.5532543305198, "directional": false, "heading_deg": 0.0}, {"channel": 15, "x": -813.7264918366334, "y": 184.45394380231278, "r_eff_m": 1281.7579743845968, "directional": false, "heading_deg": 0.0}, {"channel": 14, "x": -483.3588607307586, "y": 1609.042825940461, "r_eff_m": 1261.397442029016, "directional": false, "heading_deg": 0.0}, {"channel": 19, "x": 484.25883869219865, "y": 412.79231501032433, "r_eff_m": 1403.135275873889, "directional": false, "heading_deg": 0.0}, {"channel": 10, "x": -1185.3432835076858, "y": -882.5936176534458, "r_eff_m": 1051.898596297638, "directional": false, "heading_deg": 0.0}, {"channel": 7, "x": 27.489354230125446, "y": 597.1806051296936, "r_eff_m": 1016.5786767252073, "directional": false, "heading_deg": 0.0}, {"channel": 20, "x": -786.2186502489933, "y": -1030.2391402202422, "r_eff_m": 1337.9526501887033, "directional": false, "heading_deg": 0.0}, {"channel": 13, "x": -528.7812163217789, "y": 1415.4507846756549, "r_eff_m": 1342.4148601109948, "directional": false, "heading_deg": 0.0}, {"channel": 12, "x": 1618.6469617181342, "y": -780.4844715210944, "r_eff_m": 1183.5031703982477, "directional": false, "heading_deg": 0.0}, {"channel": 9, "x": -730.8436887494869, "y": 1629.1773460674606, "r_eff_m": 1268.3689368990042, "directional": false, "heading_deg": 0.0}, {"channel": 2, "x": -1498.0608735557626, "y": 84.22312468223669, "r_eff_m": 1169.058568141951, "directional": false, "heading_deg": 0.0}, {"channel": 3, "x": 1278.1226595751832, "y": 1262.0779642908842, "r_eff_m": 1096.052626308773, "directional": false, "heading_deg": 0.0}, {"channel": 8, "x": 16.436633792518094, "y": -864.8969376336956, "r_eff_m": 1051.1924624749086, "directional": false, "heading_deg": 0.0}, {"channel": 4, "x": 86.92957139289182, "y": 837.7240456106593, "r_eff_m": 1472.5243578771838, "directional": false, "heading_deg": 0.0}, {"channel": 5, "x": 264.7412123726052, "y": -576.0112311236278, "r_eff_m": 1472.6387608455834, "directional": false, "heading_deg": 0.0}, {"channel": 1, "x": -157.93200084817084, "y": -1476.5943058247763, "r_eff_m": 1381.0058509120777, "directional": false, "heading_deg": 0.0}]}
{"event": "action", "seq": 1, "wall_ts_ms": 1789097557703, "method": "POST", "path": "/enter", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0001"}, "response": {"accepted": true, "real_timestamp_ms": 1789097557703, "virtual_time_s": 0.0, "max_virtual_duration_s": 360000.0, "max_real_duration_s": 1200, "remaining_real_duration_s": 1200}}
{"event": "action", "seq": 2, "wall_ts_ms": 1789097557718, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0002", "position": {"x": 0.0, "y": 0.0}, "channel": 1}, "response": {"accepted": true, "real_timestamp_ms": 1789097557718, "virtual_time_s": 5.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 3, "wall_ts_ms": 1789097557723, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0003", "position": {"x": 0.0, "y": 0.0}, "channel": 2}, "response": {"accepted": true, "real_timestamp_ms": 1789097557723, "virtual_time_s": 11.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 4, "wall_ts_ms": 1789097557727, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0004", "position": {"x": 0.0, "y": 0.0}, "channel": 3}, "response": {"accepted": true, "real_timestamp_ms": 1789097557727, "virtual_time_s": 17.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 5, "wall_ts_ms": 1789097557731, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0005", "position": {"x": 0.0, "y": 0.0}, "channel": 4}, "response": {"accepted": true, "real_timestamp_ms": 1789097557731, "virtual_time_s": 23.0, "measure_result": "direction", "svd_deg": 83.92}}
{"event": "action", "seq": 6, "wall_ts_ms": 1789097557758, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0006", "position": {"x": 0.0, "y": 0.0}, "channel": 5}, "response": {"accepted": true, "real_timestamp_ms": 1789097557758, "virtual_time_s": 29.0, "measure_result": "direction", "svd_deg": 294.12}}
{"event": "action", "seq": 7, "wall_ts_ms": 1789097557788, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0007", "position": {"x": 0.0, "y": 0.0}, "channel": 6}, "response": {"accepted": true, "real_timestamp_ms": 1789097557788, "virtual_time_s": 35.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 8, "wall_ts_ms": 1789097557793, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0008", "position": {"x": 0.0, "y": 0.0}, "channel": 7}, "response": {"accepted": true, "real_timestamp_ms": 1789097557793, "virtual_time_s": 41.0, "measure_result": "direction", "svd_deg": 87.89}}
{"event": "action", "seq": 9, "wall_ts_ms": 1789097557821, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0009", "position": {"x": 0.0, "y": 0.0}, "channel": 8}, "response": {"accepted": true, "real_timestamp_ms": 1789097557821, "virtual_time_s": 47.0, "measure_result": "direction", "svd_deg": 270.40999999999997}}
{"event": "action", "seq": 10, "wall_ts_ms": 1789097557852, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0010", "position": {"x": 0.0, "y": 0.0}, "channel": 9}, "response": {"accepted": true, "real_timestamp_ms": 1789097557852, "virtual_time_s": 53.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 11, "wall_ts_ms": 1789097557856, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0011", "position": {"x": 0.0, "y": 0.0}, "channel": 10}, "response": {"accepted": true, "real_timestamp_ms": 1789097557856, "virtual_time_s": 59.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 12, "wall_ts_ms": 1789097557861, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0012", "position": {"x": 0.0, "y": 0.0}, "channel": 11}, "response": {"accepted": true, "real_timestamp_ms": 1789097557861, "virtual_time_s": 65.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 13, "wall_ts_ms": 1789097557866, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0013", "position": {"x": 0.0, "y": 0.0}, "channel": 12}, "response": {"accepted": true, "real_timestamp_ms": 1789097557866, "virtual_time_s": 71.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 14, "wall_ts_ms": 1789097557871, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0014", "position": {"x": 0.0, "y": 0.0}, "channel": 13}, "response": {"accepted": true, "real_timestamp_ms": 1789097557871, "virtual_time_s": 77.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 15, "wall_ts_ms": 1789097557875, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0015", "position": {"x": 0.0, "y": 0.0}, "channel": 14}, "response": {"accepted": true, "real_timestamp_ms": 1789097557875, "virtual_time_s": 83.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 16, "wall_ts_ms": 1789097557880, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0016", "position": {"x": 0.0, "y": 0.0}, "channel": 15}, "response": {"accepted": true, "real_timestamp_ms": 1789097557880, "virtual_time_s": 89.0, "measure_result": "direction", "svd_deg": 166.24}}
{"event": "action", "seq": 17, "wall_ts_ms": 1789097557910, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0017", "position": {"x": 0.0, "y": 0.0}, "channel": 16}, "response": {"accepted": true, "real_timestamp_ms": 1789097557910, "virtual_time_s": 95.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 18, "wall_ts_ms": 1789097557915, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0018", "position": {"x": 0.0, "y": 0.0}, "channel": 17}, "response": {"accepted": true, "real_timestamp_ms": 1789097557915, "virtual_time_s": 101.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 19, "wall_ts_ms": 1789097557921, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0019", "position": {"x": 0.0, "y": 0.0}, "channel": 18}, "response": {"accepted": true, "real_timestamp_ms": 1789097557921, "virtual_time_s": 107.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 20, "wall_ts_ms": 1789097557928, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0020", "position": {"x": 0.0, "y": 0.0}, "channel": 19}, "response": {"accepted": true, "real_timestamp_ms": 1789097557928, "virtual_time_s": 113.0, "measure_result": "direction", "svd_deg": 40.84}}
{"event": "action", "seq": 21, "wall_ts_ms": 1789097558022, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0021", "position": {"x": 0.0, "y": 0.0}, "channel": 20}, "response": {"accepted": true, "real_timestamp_ms": 1789097558022, "virtual_time_s": 119.0, "measure_result": "direction", "svd_deg": 231.86}}
{"event": "action", "seq": 22, "wall_ts_ms": 1789097558210, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0022", "position": {"x": -149.65135118390128, "y": 573.0324376220874}, "channel": 7}, "response": {"accepted": true, "real_timestamp_ms": 1789097558210, "virtual_time_s": 243.450277, "measure_result": "direction", "svd_deg": 8.69}}
{"event": "action", "seq": 23, "wall_ts_ms": 1789097558213, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0023", "position": {"x": 22.17702290799913, "y": 599.4956299989444}, "channel": 7}, "response": {"accepted": true, "real_timestamp_ms": 1789097558213, "virtual_time_s": 283.221122, "clear_result": "success"}}
{"event": "action", "seq": 24, "wall_ts_ms": 1789097558335, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0024", "position": {"x": -101.9760778275257, "y": 697.4671868713128}, "channel": 4}, "response": {"accepted": true, "real_timestamp_ms": 1789097558335, "virtual_time_s": 320.851752, "measure_result": "direction", "svd_deg": 37.35}}
{"event": "action", "seq": 25, "wall_ts_ms": 1789097558442, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0025", "position": {"x": 90.33435176041925, "y": 844.7755213644774}, "channel": 4}, "response": {"accepted": true, "real_timestamp_ms": 1789097558442, "virtual_time_s": 374.30092, "measure_result": "direction", "svd_deg": 245.20999999999998}}
{"event": "action", "seq": 26, "wall_ts_ms": 1789097558444, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0026", "position": {"x": 88.15565839928949, "y": 840.2676738025095}, "channel": 4}, "response": {"accepted": true, "real_timestamp_ms": 1789097558444, "virtual_time_s": 380.302267, "clear_result": "success"}}
{"event": "action", "seq": 27, "wall_ts_ms": 1789097558553, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0027", "position": {"x": 390.54100585774984, "y": 667.5806304250419}, "channel": 19}, "response": {"accepted": true, "real_timestamp_ms": 1789097558553, "virtual_time_s": 455.946423, "measure_result": "direction", "svd_deg": 290.48}}
{"event": "action", "seq": 28, "wall_ts_ms": 1789097558555, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0028", "position": {"x": 483.4581452322053, "y": 418.111211730111}, "channel": 19}, "response": {"accepted": true, "real_timestamp_ms": 1789097558555, "virtual_time_s": 514.188732, "clear_result": "success"}}
{"event": "action", "seq": 29, "wall_ts_ms": 1789097558643, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0029", "position": {"x": 600.0000000000001, "y": 1039.2304845413264}, "channel": 1}, "response": {"accepted": true, "real_timestamp_ms": 1789097558643, "virtual_time_s": 646.58037, "measure_result": "no_signal"}}
{"event": "action", "seq": 30, "wall_ts_ms": 1789097558648, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0030", "position": {"x": 600.0000000000001, "y": 1039.2304845413264}, "channel": 2}, "response": {"accepted": true, "real_timestamp_ms": 1789097558648, "virtual_time_s": 652.58037, "measure_result": "no_signal"}}
{"event": "action", "seq": 31, "wall_ts_ms": 1789097558651, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0031", "position": {"x": 600.0000000000001, "y": 1039.2304845413264}, "channel": 3}, "response": {"accepted": true, "real_timestamp_ms": 1789097558651, "virtual_time_s": 658.58037, "measure_result": "direction", "svd_deg": 18.6}}
{"event": "action", "seq": 32, "wall_ts_ms": 1789097558671, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0032", "position": {"x": 600.0000000000001, "y": 1039.2304845413264}, "channel": 6}, "response": {"accepted": true, "real_timestamp_ms": 1789097558671, "virtual_time_s": 664.58037, "measure_result": "no_signal"}}
{"event": "action", "seq": 33, "wall_ts_ms": 1789097558675, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0033", "position": {"x": 600.0000000000001, "y": 1039.2304845413264}, "channel": 9}, "response": {"accepted": true, "real_timestamp_ms": 1789097558675, "virtual_time_s": 670.58037, "measure_result": "no_signal"}}
{"event": "action", "seq": 34, "wall_ts_ms": 1789097558678, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0034", "position": {"x": 600.0000000000001, "y": 1039.2304845413264}, "channel": 10}, "response": {"accepted": true, "real_timestamp_ms": 1789097558678, "virtual_time_s": 676.58037, "measure_result": "no_signal"}}
{"event": "action", "seq": 35, "wall_ts_ms": 1789097558681, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0035", "position": {"x": 600.0000000000001, "y": 1039.2304845413264}, "channel": 11}, "response": {"accepted": true, "real_timestamp_ms": 1789097558681, "virtual_time_s": 682.58037, "measure_result": "no_signal"}}
{"event": "action", "seq": 36, "wall_ts_ms": 1789097558685, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0036", "position": {"x": 600.0000000000001, "y": 1039.2304845413264}, "channel": 12}, "response": {"accepted": true, "real_timestamp_ms": 1789097558685, "virtual_time_s": 688.58037, "measure_result": "no_signal"}}
{"event": "action", "seq": 37, "wall_ts_ms": 1789097558689, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0037", "position": {"x": 600.0000000000001, "y": 1039.2304845413264}, "channel": 13}, "response": {"accepted": true, "real_timestamp_ms": 1789097558689, "virtual_time_s": 694.58037, "measure_result": "direction", "svd_deg": 161.62}}
{"event": "action", "seq": 38, "wall_ts_ms": 1789097558712, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0038", "position": {"x": 600.0000000000001, "y": 1039.2304845413264}, "channel": 14}, "response": {"accepted": true, "real_timestamp_ms": 1789097558712, "virtual_time_s": 700.58037, "measure_result": "direction", "svd_deg": 151.34}}
{"event": "action", "seq": 39, "wall_ts_ms": 1789097558731, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0039", "position": {"x": 600.0000000000001, "y": 1039.2304845413264}, "channel": 16}, "response": {"accepted": true, "real_timestamp_ms": 1789097558731, "virtual_time_s": 706.58037, "measure_result": "no_signal"}}
{"event": "action", "seq": 40, "wall_ts_ms": 1789097558734, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0040", "position": {"x": 600.0000000000001, "y": 1039.2304845413264}, "channel": 17}, "response": {"accepted": true, "real_timestamp_ms": 1789097558734, "virtual_time_s": 712.58037, "measure_result": "no_signal"}}
{"event": "action", "seq": 41, "wall_ts_ms": 1789097558739, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0041", "position": {"x": 600.0000000000001, "y": 1039.2304845413264}, "channel": 18}, "response": {"accepted": true, "real_timestamp_ms": 1789097558739, "virtual_time_s": 718.58037, "measure_result": "direction", "svd_deg": 333.05}}
{"event": "action", "seq": 42, "wall_ts_ms": 1789097558912, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0042", "position": {"x": 763.3161794058348, "y": 1329.0912690006119}, "channel": 3}, "response": {"accepted": true, "real_timestamp_ms": 1789097558912, "virtual_time_s": 791.121019, "measure_result": "direction", "svd_deg": 352.69}}
{"event": "action", "seq": 43, "wall_ts_ms": 1789097559125, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0043", "position": {"x": 1083.3861227014502, "y": 1072.8276376774256}, "channel": 18}, "response": {"accepted": true, "real_timestamp_ms": 1789097559125, "virtual_time_s": 879.124877, "measure_result": "direction", "svd_deg": 310.85}}
{"event": "action", "seq": 44, "wall_ts_ms": 1789097559280, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0044", "position": {"x": 982.5080403720083, "y": 985.7374435518959}, "channel": 3}, "response": {"accepted": true, "real_timestamp_ms": 1789097559280, "virtual_time_s": 911.779024, "measure_result": "direction", "svd_deg": 42.69}}
{"event": "action", "seq": 45, "wall_ts_ms": 1789097559281, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0045", "position": {"x": 1276.505194434143, "y": 1256.7197197867613}, "channel": 3}, "response": {"accepted": true, "real_timestamp_ms": 1789097559281, "virtual_time_s": 996.745447, "clear_result": "success"}}
{"event": "action", "seq": 46, "wall_ts_ms": 1789097559407, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0046", "position": {"x": 1519.930091635603, "y": 570.0314765599868}, "channel": 18}, "response": {"accepted": true, "real_timestamp_ms": 1789097559407, "virtual_time_s": 1148.457001, "measure_result": "direction", "svd_deg": 122.2}}
{"event": "action", "seq": 47, "wall_ts_ms": 1789097559530, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0047", "position": {"x": 1509.5530416186468, "y": 585.8862512524679}, "channel": 18}, "response": {"accepted": true, "real_timestamp_ms": 1789097559530, "virtual_time_s": 1157.246761, "measure_result": "direction", "svd_deg": 304.71}}
{"event": "action", "seq": 48, "wall_ts_ms": 1789097559532, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0048", "position": {"x": 1513.76371887733, "y": 579.5738004890592}, "channel": 18}, "response": {"accepted": true, "real_timestamp_ms": 1789097559532, "virtual_time_s": 1163.764349, "clear_result": "success"}}
{"event": "action", "seq": 49, "wall_ts_ms": 1789097559651, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0049", "position": {"x": 1200.0, "y": 0.0}, "channel": 1}, "response": {"accepted": true, "real_timestamp_ms": 1789097559651, "virtual_time_s": 1301.575344, "measure_result": "no_signal"}}
{"event": "action", "seq": 50, "wall_ts_ms": 1789097559654, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0050", "position": {"x": 1200.0, "y": 0.0}, "channel": 2}, "response": {"accepted": true, "real_timestamp_ms": 1789097559654, "virtual_time_s": 1307.575344, "measure_result": "no_signal"}}
{"event": "action", "seq": 51, "wall_ts_ms": 1789097559656, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0051", "position": {"x": 1200.0, "y": 0.0}, "channel": 6}, "response": {"accepted": true, "real_timestamp_ms": 1789097559656, "virtual_time_s": 1313.575344, "measure_result": "no_signal"}}
{"event": "action", "seq": 52, "wall_ts_ms": 1789097559659, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0052", "position": {"x": 1200.0, "y": 0.0}, "channel": 9}, "response": {"accepted": true, "real_timestamp_ms": 1789097559659, "virtual_time_s": 1319.575344, "measure_result": "no_signal"}}
{"event": "action", "seq": 53, "wall_ts_ms": 1789097559661, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0053", "position": {"x": 1200.0, "y": 0.0}, "channel": 10}, "response": {"accepted": true, "real_timestamp_ms": 1789097559661, "virtual_time_s": 1325.575344, "measure_result": "no_signal"}}
{"event": "action", "seq": 54, "wall_ts_ms": 1789097559664, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0054", "position": {"x": 1200.0, "y": 0.0}, "channel": 11}, "response": {"accepted": true, "real_timestamp_ms": 1789097559664, "virtual_time_s": 1331.575344, "measure_result": "no_signal"}}
{"event": "action", "seq": 55, "wall_ts_ms": 1789097559667, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0055", "position": {"x": 1200.0, "y": 0.0}, "channel": 12}, "response": {"accepted": true, "real_timestamp_ms": 1789097559667, "virtual_time_s": 1337.575344, "measure_result": "direction", "svd_deg": 297.54}}
{"event": "action", "seq": 56, "wall_ts_ms": 1789097559682, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0056", "position": {"x": 1200.0, "y": 0.0}, "channel": 16}, "response": {"accepted": true, "real_timestamp_ms": 1789097559682, "virtual_time_s": 1343.575344, "measure_result": "no_signal"}}
{"event": "action", "seq": 57, "wall_ts_ms": 1789097559687, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0057", "position": {"x": 1200.0, "y": 0.0}, "channel": 17}, "response": {"accepted": true, "real_timestamp_ms": 1789097559687, "virtual_time_s": 1349.575344, "measure_result": "no_signal"}}
{"event": "action", "seq": 58, "wall_ts_ms": 1789097559827, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0058", "position": {"x": 1192.2027278108449, "y": -350.0560673308244}, "channel": 12}, "response": {"accepted": true, "real_timestamp_ms": 1789097559827, "virtual_time_s": 1425.603923, "measure_result": "direction", "svd_deg": 315.51}}
{"event": "action", "seq": 59, "wall_ts_ms": 1789097559949, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0059", "position": {"x": 1579.4443151483792, "y": -732.0861531283488}, "channel": 12}, "response": {"accepted": true, "real_timestamp_ms": 1789097559949, "virtual_time_s": 1539.397863, "measure_result": "direction", "svd_deg": 308.75}}
{"event": "action", "seq": 60, "wall_ts_ms": 1789097560079, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0060", "position": {"x": 1599.756957132486, "y": -758.3250236728769}, "channel": 12}, "response": {"accepted": true, "real_timestamp_ms": 1789097560079, "virtual_time_s": 1551.034373, "measure_result": "direction", "svd_deg": 309.97}}
{"event": "action", "seq": 61, "wall_ts_ms": 1789097560080, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0061", "position": {"x": 1610.0853690933254, "y": -771.0954929815541}, "channel": 12}, "response": {"accepted": true, "real_timestamp_ms": 1789097560080, "virtual_time_s": 1559.319253, "clear_result": "success"}}
{"event": "action", "seq": 62, "wall_ts_ms": 1789097560208, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0062", "position": {"x": 600.0000000000001, "y": -1039.2304845413264}, "channel": 1}, "response": {"accepted": true, "real_timestamp_ms": 1789097560208, "virtual_time_s": 1774.333016, "measure_result": "direction", "svd_deg": 210.85}}
{"event": "action", "seq": 63, "wall_ts_ms": 1789097560222, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0063", "position": {"x": 600.0000000000001, "y": -1039.2304845413264}, "channel": 2}, "response": {"accepted": true, "real_timestamp_ms": 1789097560222, "virtual_time_s": 1780.333016, "measure_result": "no_signal"}}
{"event": "action", "seq": 64, "wall_ts_ms": 1789097560226, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0064", "position": {"x": 600.0000000000001, "y": -1039.2304845413264}, "channel": 6}, "response": {"accepted": true, "real_timestamp_ms": 1789097560226, "virtual_time_s": 1786.333016, "measure_result": "no_signal"}}
{"event": "action", "seq": 65, "wall_ts_ms": 1789097560230, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0065", "position": {"x": 600.0000000000001, "y": -1039.2304845413264}, "channel": 9}, "response": {"accepted": true, "real_timestamp_ms": 1789097560230, "virtual_time_s": 1792.333016, "measure_result": "no_signal"}}
{"event": "action", "seq": 66, "wall_ts_ms": 1789097560233, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0066", "position": {"x": 600.0000000000001, "y": -1039.2304845413264}, "channel": 10}, "response": {"accepted": true, "real_timestamp_ms": 1789097560233, "virtual_time_s": 1798.333016, "measure_result": "no_signal"}}
{"event": "action", "seq": 67, "wall_ts_ms": 1789097560236, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0067", "position": {"x": 600.0000000000001, "y": -1039.2304845413264}, "channel": 11}, "response": {"accepted": true, "real_timestamp_ms": 1789097560236, "virtual_time_s": 1804.333016, "measure_result": "no_signal"}}
{"event": "action", "seq": 68, "wall_ts_ms": 1789097560240, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0068", "position": {"x": 600.0000000000001, "y": -1039.2304845413264}, "channel": 16}, "response": {"accepted": true, "real_timestamp_ms": 1789097560240, "virtual_time_s": 1810.333016, "measure_result": "no_signal"}}
{"event": "action", "seq": 69, "wall_ts_ms": 1789097560242, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0069", "position": {"x": 600.0000000000001, "y": -1039.2304845413264}, "channel": 17}, "response": {"accepted": true, "real_timestamp_ms": 1789097560242, "virtual_time_s": 1816.333016, "measure_result": "no_signal"}}
{"event": "action", "seq": 70, "wall_ts_ms": 1789097560389, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0070", "position": {"x": 548.4205541744728, "y": -749.0884755472262}, "channel": 5}, "response": {"accepted": true, "real_timestamp_ms": 1789097560389, "virtual_time_s": 1881.271229, "measure_result": "direction", "svd_deg": 147.73}}
{"event": "action", "seq": 71, "wall_ts_ms": 1789097560511, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0071", "position": {"x": 626.7987000553718, "y": -661.6714058193344}, "channel": 5}, "response": {"accepted": true, "real_timestamp_ms": 1789097560511, "virtual_time_s": 1909.753033, "measure_result": "direction", "svd_deg": 166.92}}
{"event": "action", "seq": 72, "wall_ts_ms": 1789097560513, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0072", "position": {"x": 260.33825092933466, "y": -574.1726350109003}, "channel": 5}, "response": {"accepted": true, "real_timestamp_ms": 1789097560513, "virtual_time_s": 1990.105351, "clear_result": "success"}}
{"event": "action", "seq": 73, "wall_ts_ms": 1789097560632, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0073", "position": {"x": 246.24176870568039, "y": -685.4655800184461}, "channel": 8}, "response": {"accepted": true, "real_timestamp_ms": 1789097560632, "virtual_time_s": 2018.541777, "measure_result": "direction", "svd_deg": 217.69}}
{"event": "action", "seq": 74, "wall_ts_ms": 1789097560728, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0074", "position": {"x": 101.4809670295322, "y": -1057.528116723252}, "channel": 1}, "response": {"accepted": true, "real_timestamp_ms": 1789097560728, "virtual_time_s": 2104.388185, "measure_result": "direction", "svd_deg": 237.95}}
{"event": "action", "seq": 75, "wall_ts_ms": 1789097560809, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0075", "position": {"x": -180.8231606416641, "y": -1506.673415466817}, "channel": 1}, "response": {"accepted": true, "real_timestamp_ms": 1789097560809, "virtual_time_s": 2215.487595, "measure_result": "direction", "svd_deg": 53.15}}
{"event": "action", "seq": 76, "wall_ts_ms": 1789097560891, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0076", "position": {"x": -167.18396538240404, "y": -1489.1246807747386}, "channel": 1}, "response": {"accepted": true, "real_timestamp_ms": 1789097560891, "virtual_time_s": 2224.932753, "measure_result": "direction", "svd_deg": 54.04}}
{"event": "action", "seq": 77, "wall_ts_ms": 1789097560893, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0077", "position": {"x": -160.74458223819045, "y": -1480.568461060665}, "channel": 1}, "response": {"accepted": true, "real_timestamp_ms": 1789097560893, "virtual_time_s": 2232.074477, "clear_result": "success"}}
{"event": "action", "seq": 78, "wall_ts_ms": 1789097560972, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0078", "position": {"x": 5.9157845288639805, "y": -871.6596394811392}, "channel": 8}, "response": {"accepted": true, "real_timestamp_ms": 1789097560972, "virtual_time_s": 2364.335418, "measure_result": "direction", "svd_deg": 33.33}}
{"event": "action", "seq": 79, "wall_ts_ms": 1789097560974, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0079", "position": {"x": 13.593963995537074, "y": -866.4150648005227}, "channel": 8}, "response": {"accepted": true, "real_timestamp_ms": 1789097560974, "virtual_time_s": 2371.195095, "clear_result": "success"}}
{"event": "action", "seq": 80, "wall_ts_ms": 1789097561053, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0080", "position": {"x": -286.37408911397563, "y": -766.8255500189505}, "channel": 20}, "response": {"accepted": true, "real_timestamp_ms": 1789097561053, "virtual_time_s": 2440.408671, "measure_result": "direction", "svd_deg": 207.25}}
{"event": "action", "seq": 81, "wall_ts_ms": 1789097561113, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0081", "position": {"x": -600.0000000000006, "y": -1039.230484541326}, "channel": 2}, "response": {"accepted": true, "real_timestamp_ms": 1789097561113, "virtual_time_s": 2529.490717, "measure_result": "no_signal"}}
{"event": "action", "seq": 82, "wall_ts_ms": 1789097561117, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0082", "position": {"x": -600.0000000000006, "y": -1039.230484541326}, "channel": 6}, "response": {"accepted": true, "real_timestamp_ms": 1789097561117, "virtual_time_s": 2535.490717, "measure_result": "no_signal"}}
{"event": "action", "seq": 83, "wall_ts_ms": 1789097561121, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0083", "position": {"x": -600.0000000000006, "y": -1039.230484541326}, "channel": 9}, "response": {"accepted": true, "real_timestamp_ms": 1789097561121, "virtual_time_s": 2541.490717, "measure_result": "no_signal"}}
{"event": "action", "seq": 84, "wall_ts_ms": 1789097561124, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0084", "position": {"x": -600.0000000000006, "y": -1039.230484541326}, "channel": 10}, "response": {"accepted": true, "real_timestamp_ms": 1789097561124, "virtual_time_s": 2547.490717, "measure_result": "direction", "svd_deg": 165.82}}
{"event": "action", "seq": 85, "wall_ts_ms": 1789097561138, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0085", "position": {"x": -600.0000000000006, "y": -1039.230484541326}, "channel": 11}, "response": {"accepted": true, "real_timestamp_ms": 1789097561138, "virtual_time_s": 2553.490717, "measure_result": "no_signal"}}
{"event": "action", "seq": 86, "wall_ts_ms": 1789097561141, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0086", "position": {"x": -600.0000000000006, "y": -1039.230484541326}, "channel": 16}, "response": {"accepted": true, "real_timestamp_ms": 1789097561141, "virtual_time_s": 2559.490717, "measure_result": "no_signal"}}
{"event": "action", "seq": 87, "wall_ts_ms": 1789097561144, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0087", "position": {"x": -600.0000000000006, "y": -1039.230484541326}, "channel": 17}, "response": {"accepted": true, "real_timestamp_ms": 1789097561144, "virtual_time_s": 2565.490717, "measure_result": "no_signal"}}
{"event": "action", "seq": 88, "wall_ts_ms": 1789097561223, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0088", "position": {"x": -821.4557486194155, "y": -1043.9702928641734}, "channel": 20}, "response": {"accepted": true, "real_timestamp_ms": 1789097561223, "virtual_time_s": 2615.79201, "measure_result": "direction", "svd_deg": 21.12}}
{"event": "action", "seq": 89, "wall_ts_ms": 1789097561290, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0089", "position": {"x": -799.1301367753487, "y": -1034.893452384792}, "channel": 20}, "response": {"accepted": true, "real_timestamp_ms": 1789097561290, "virtual_time_s": 2625.61206, "measure_result": "direction", "svd_deg": 20.37}}
{"event": "action", "seq": 90, "wall_ts_ms": 1789097561292, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0090", "position": {"x": -788.1496255408595, "y": -1030.5957631304852}, "channel": 20}, "response": {"accepted": true, "real_timestamp_ms": 1789097561292, "virtual_time_s": 2632.970379, "clear_result": "success"}}
{"event": "action", "seq": 91, "wall_ts_ms": 1789097561362, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0091", "position": {"x": -1067.9438619396667, "y": -719.4050934512866}, "channel": 10}, "response": {"accepted": true, "real_timestamp_ms": 1789097561362, "virtual_time_s": 2722.666125, "measure_result": "direction", "svd_deg": 233.54000000000002}}
{"event": "action", "seq": 92, "wall_ts_ms": 1789097561363, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0092", "position": {"x": -1193.79864138515, "y": -889.3490379535914}, "channel": 10}, "response": {"accepted": true, "real_timestamp_ms": 1789097561363, "virtual_time_s": 2769.960508, "clear_result": "success"}}
{"event": "action", "seq": 93, "wall_ts_ms": 1789097561422, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0093", "position": {"x": -1200.0, "y": 1.4695761589768238e-13}, "channel": 2}, "response": {"accepted": true, "real_timestamp_ms": 1789097561422, "virtual_time_s": 2953.83464, "measure_result": "direction", "svd_deg": 163.42}}
{"event": "action", "seq": 94, "wall_ts_ms": 1789097561432, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0094", "position": {"x": -1200.0, "y": 1.4695761589768238e-13}, "channel": 6}, "response": {"accepted": true, "real_timestamp_ms": 1789097561432, "virtual_time_s": 2959.83464, "measure_result": "no_signal"}}
{"event": "action", "seq": 95, "wall_ts_ms": 1789097561439, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0095", "position": {"x": -1200.0, "y": 1.4695761589768238e-13}, "channel": 9}, "response": {"accepted": true, "real_timestamp_ms": 1789097561439, "virtual_time_s": 2965.83464, "measure_result": "no_signal"}}
{"event": "action", "seq": 96, "wall_ts_ms": 1789097561442, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0096", "position": {"x": -1200.0, "y": 1.4695761589768238e-13}, "channel": 11}, "response": {"accepted": true, "real_timestamp_ms": 1789097561442, "virtual_time_s": 2971.83464, "measure_result": "no_signal"}}
{"event": "action", "seq": 97, "wall_ts_ms": 1789097561445, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0097", "position": {"x": -1200.0, "y": 1.4695761589768238e-13}, "channel": 16}, "response": {"accepted": true, "real_timestamp_ms": 1789097561445, "virtual_time_s": 2977.83464, "measure_result": "no_signal"}}
{"event": "action", "seq": 98, "wall_ts_ms": 1789097561449, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0098", "position": {"x": -1200.0, "y": 1.4695761589768238e-13}, "channel": 17}, "response": {"accepted": true, "real_timestamp_ms": 1789097561449, "virtual_time_s": 2983.83464, "measure_result": "no_signal"}}
{"event": "action", "seq": 99, "wall_ts_ms": 1789097561517, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0099", "position": {"x": -1423.667553533217, "y": -173.2063353897658}, "channel": 2}, "response": {"accepted": true, "real_timestamp_ms": 1789097561517, "virtual_time_s": 3046.412943, "measure_result": "direction", "svd_deg": 106.43}}
{"event": "action", "seq": 100, "wall_ts_ms": 1789097561519, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0100", "position": {"x": -1501.4366031905558, "y": 89.91826644321941}, "channel": 2}, "response": {"accepted": true, "real_timestamp_ms": 1789097561519, "virtual_time_s": 3106.288286, "clear_result": "success"}}
{"event": "action", "seq": 101, "wall_ts_ms": 1789097561576, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0101", "position": {"x": -905.3480310884979, "y": 1.476683864108395}, "channel": 15}, "response": {"accepted": true, "real_timestamp_ms": 1789097561576, "virtual_time_s": 3232.811064, "measure_result": "direction", "svd_deg": 63.06}}
{"event": "action", "seq": 102, "wall_ts_ms": 1789097561578, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0102", "position": {"x": -805.596590259142, "y": 197.1442590697214}, "channel": 15}, "response": {"accepted": true, "real_timestamp_ms": 1789097561578, "virtual_time_s": 3281.736524, "clear_result": "success"}}
{"event": "action", "seq": 103, "wall_ts_ms": 1789097561616, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0103", "position": {"x": -599.9999999999998, "y": 1039.2304845413264}, "channel": 6}, "response": {"accepted": true, "real_timestamp_ms": 1789097561616, "virtual_time_s": 3461.100784, "measure_result": "no_signal"}}
{"event": "action", "seq": 104, "wall_ts_ms": 1789097561619, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0104", "position": {"x": -599.9999999999998, "y": 1039.2304845413264}, "channel": 9}, "response": {"accepted": true, "real_timestamp_ms": 1789097561619, "virtual_time_s": 3467.100784, "measure_result": "direction", "svd_deg": 102.88}}
{"event": "action", "seq": 105, "wall_ts_ms": 1789097561626, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0105", "position": {"x": -599.9999999999998, "y": 1039.2304845413264}, "channel": 11}, "response": {"accepted": true, "real_timestamp_ms": 1789097561626, "virtual_time_s": 3473.100784, "measure_result": "no_signal"}}
{"event": "action", "seq": 106, "wall_ts_ms": 1789097561629, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0106", "position": {"x": -599.9999999999998, "y": 1039.2304845413264}, "channel": 16}, "response": {"accepted": true, "real_timestamp_ms": 1789097561629, "virtual_time_s": 3479.100784, "measure_result": "no_signal"}}
{"event": "action", "seq": 107, "wall_ts_ms": 1789097561631, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0107", "position": {"x": -599.9999999999998, "y": 1039.2304845413264}, "channel": 17}, "response": {"accepted": true, "real_timestamp_ms": 1789097561631, "virtual_time_s": 3485.100784, "measure_result": "no_signal"}}
{"event": "action", "seq": 108, "wall_ts_ms": 1789097561691, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0108", "position": {"x": -468.74856219927324, "y": 1147.741683519248}, "channel": 9}, "response": {"accepted": true, "real_timestamp_ms": 1789097561691, "virtual_time_s": 3525.160508, "measure_result": "direction", "svd_deg": 119.34}}
{"event": "action", "seq": 109, "wall_ts_ms": 1789097561729, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0109", "position": {"x": -288.39506636259784, "y": 1099.5726385930388}, "channel": 13}, "response": {"accepted": true, "real_timestamp_ms": 1789097561729, "virtual_time_s": 3568.495553, "measure_result": "direction", "svd_deg": 126.77}}
{"event": "action", "seq": 110, "wall_ts_ms": 1789097561753, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0110", "position": {"x": -231.41543619323764, "y": 1147.1056551785941}, "channel": 14}, "response": {"accepted": true, "real_timestamp_ms": 1789097561753, "virtual_time_s": 3589.336127, "measure_result": "direction", "svd_deg": 118.26}}
{"event": "action", "seq": 111, "wall_ts_ms": 1789097561759, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0111", "position": {"x": -523.9735913338733, "y": 1413.4654346550624}, "channel": 13}, "response": {"accepted": true, "real_timestamp_ms": 1789097561759, "virtual_time_s": 3674.465845, "measure_result": "direction", "svd_deg": 156.58}}
{"event": "action", "seq": 112, "wall_ts_ms": 1789097561761, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0112", "position": {"x": -529.9009483775656, "y": 1416.1573221581389}, "channel": 13}, "response": {"accepted": true, "real_timestamp_ms": 1789097561761, "virtual_time_s": 3680.767841, "clear_result": "success"}}
{"event": "action", "seq": 113, "wall_ts_ms": 1789097561765, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0113", "position": {"x": -497.5575661714986, "y": 1640.1638651043113}, "channel": 14}, "response": {"accepted": true, "real_timestamp_ms": 1789097561765, "virtual_time_s": 3732.033734, "measure_result": "direction", "svd_deg": 294.96}}
{"event": "action", "seq": 114, "wall_ts_ms": 1789097561768, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0114", "position": {"x": -489.1226703393842, "y": 1622.842919408121}, "channel": 14}, "response": {"accepted": true, "real_timestamp_ms": 1789097561768, "virtual_time_s": 3740.88685, "measure_result": "direction", "svd_deg": 293.48}}
{"event": "action", "seq": 115, "wall_ts_ms": 1789097561770, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0115", "position": {"x": -485.2642914202578, "y": 1614.3706024009493}, "channel": 14}, "response": {"accepted": true, "real_timestamp_ms": 1789097561770, "virtual_time_s": 3747.748755, "clear_result": "success"}}
{"event": "action", "seq": 116, "wall_ts_ms": 1789097561773, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0116", "position": {"x": -723.7252173218731, "y": 1600.2232732795474}, "channel": 9}, "response": {"accepted": true, "real_timestamp_ms": 1789097561773, "virtual_time_s": 3801.524799, "measure_result": "direction", "svd_deg": 102.92}}
{"event": "action", "seq": 117, "wall_ts_ms": 1789097561777, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0117", "position": {"x": -728.399681602855, "y": 1619.0766264002268}, "channel": 9}, "response": {"accepted": true, "real_timestamp_ms": 1789097561777, "virtual_time_s": 3810.409639, "measure_result": "direction", "svd_deg": 102.9}}
{"event": "action", "seq": 118, "wall_ts_ms": 1789097561779, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0118", "position": {"x": -730.7304567672215, "y": 1628.491328583946}, "channel": 9}, "response": {"accepted": true, "real_timestamp_ms": 1789097561779, "virtual_time_s": 3817.349424, "clear_result": "success"}}
{"event": "action", "seq": 119, "wall_ts_ms": 1789097561780, "method": "POST", "path": "/exit", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "b635af372db3-0119"}, "response": {"accepted": true, "real_timestamp_ms": 1789097561780, "virtual_time_s": 3817.349424, "exit_reason": "user_exit"}}
{"event": "session_end", "reason": "user_exit", "virtual_time_s": 3817.349424, "real_elapsed_s": 7.603792, "request_count": 119, "cleared_count": 16}
@@ -0,0 +1,136 @@
{
"case_code": "RM7Y-JRYA-9S7E-6J2T",
"module": "q3_practice",
"robot_id": "1",
"seed": 1539645273,
"truth": [
{
"channel": 18,
"x": 1514.5988909830828,
"y": 578.5437460895635,
"r_eff_m": 1242.5532543305198,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 15,
"x": -813.7264918366334,
"y": 184.45394380231278,
"r_eff_m": 1281.7579743845968,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 14,
"x": -483.3588607307586,
"y": 1609.042825940461,
"r_eff_m": 1261.397442029016,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 19,
"x": 484.25883869219865,
"y": 412.79231501032433,
"r_eff_m": 1403.135275873889,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 10,
"x": -1185.3432835076858,
"y": -882.5936176534458,
"r_eff_m": 1051.898596297638,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 7,
"x": 27.489354230125446,
"y": 597.1806051296936,
"r_eff_m": 1016.5786767252073,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 20,
"x": -786.2186502489933,
"y": -1030.2391402202422,
"r_eff_m": 1337.9526501887033,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 13,
"x": -528.7812163217789,
"y": 1415.4507846756549,
"r_eff_m": 1342.4148601109948,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 12,
"x": 1618.6469617181342,
"y": -780.4844715210944,
"r_eff_m": 1183.5031703982477,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 9,
"x": -730.8436887494869,
"y": 1629.1773460674606,
"r_eff_m": 1268.3689368990042,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 2,
"x": -1498.0608735557626,
"y": 84.22312468223669,
"r_eff_m": 1169.058568141951,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 3,
"x": 1278.1226595751832,
"y": 1262.0779642908842,
"r_eff_m": 1096.052626308773,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 8,
"x": 16.436633792518094,
"y": -864.8969376336956,
"r_eff_m": 1051.1924624749086,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 4,
"x": 86.92957139289182,
"y": 837.7240456106593,
"r_eff_m": 1472.5243578771838,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 5,
"x": 264.7412123726052,
"y": -576.0112311236278,
"r_eff_m": 1472.6387608455834,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 1,
"x": -157.93200084817084,
"y": -1476.5943058247763,
"r_eff_m": 1381.0058509120777,
"directional": false,
"heading_deg": 0.0
}
]
}
@@ -0,0 +1,121 @@
{"event": "session_start", "case_code": "VQKB-DHWP-JGTM-98EW", "module": "q3_practice", "robot_id": "1", "seed": 1368779781, "countdown_s": 5.0, "window_s": 1500.0, "max_real_duration_s": 1200.0, "truth": [{"channel": 13, "x": 554.8588259633532, "y": 1244.659365990903, "r_eff_m": 1362.874296044368, "directional": false, "heading_deg": 0.0}, {"channel": 7, "x": 1065.3524749007952, "y": 217.7585180332171, "r_eff_m": 1071.7166972389825, "directional": false, "heading_deg": 0.0}, {"channel": 10, "x": -402.4394670146116, "y": -1031.4211824187898, "r_eff_m": 1296.8377765558223, "directional": false, "heading_deg": 0.0}, {"channel": 3, "x": -622.4073708003119, "y": 1647.9468895217547, "r_eff_m": 1075.9709994154707, "directional": false, "heading_deg": 0.0}, {"channel": 12, "x": 944.568444060052, "y": -334.4610463406964, "r_eff_m": 1376.0615386573004, "directional": false, "heading_deg": 0.0}, {"channel": 8, "x": 1274.840248994663, "y": 170.93445645012275, "r_eff_m": 1399.6338852090485, "directional": false, "heading_deg": 0.0}, {"channel": 1, "x": 45.45947329669638, "y": 1380.9782532154657, "r_eff_m": 1318.9836429255063, "directional": false, "heading_deg": 0.0}, {"channel": 18, "x": -546.5934213344553, "y": -762.9975795799155, "r_eff_m": 1368.904295278126, "directional": false, "heading_deg": 0.0}, {"channel": 20, "x": 952.4459677190133, "y": 1327.9097138456584, "r_eff_m": 1107.7244397116335, "directional": false, "heading_deg": 0.0}, {"channel": 2, "x": -938.3564523770655, "y": -1335.504363831014, "r_eff_m": 1496.4037785889939, "directional": false, "heading_deg": 0.0}, {"channel": 4, "x": -1169.8069503281326, "y": -81.42522149758867, "r_eff_m": 1234.2519658982385, "directional": false, "heading_deg": 0.0}, {"channel": 17, "x": 383.9916268825851, "y": -913.4442984993188, "r_eff_m": 1163.1974374312167, "directional": false, "heading_deg": 0.0}, {"channel": 15, "x": 529.9690595799416, "y": 886.5384827951485, "r_eff_m": 1246.3397672927551, "directional": false, "heading_deg": 0.0}, {"channel": 11, "x": 245.1461688066881, "y": 1454.481757671101, "r_eff_m": 1296.0699292612499, "directional": false, "heading_deg": 0.0}, {"channel": 5, "x": -1219.6790461495623, "y": -1175.2294002972626, "r_eff_m": 1392.0228530017737, "directional": false, "heading_deg": 0.0}]}
{"event": "action", "seq": 1, "wall_ts_ms": 1789097723031, "method": "POST", "path": "/enter", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0001"}, "response": {"accepted": true, "real_timestamp_ms": 1789097723031, "virtual_time_s": 0.0, "max_virtual_duration_s": 360000.0, "max_real_duration_s": 1200, "remaining_real_duration_s": 1200}}
{"event": "action", "seq": 2, "wall_ts_ms": 1789097723052, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0002", "position": {"x": 0.0, "y": 0.0}, "channel": 1}, "response": {"accepted": true, "real_timestamp_ms": 1789097723052, "virtual_time_s": 5.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 3, "wall_ts_ms": 1789097723058, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0003", "position": {"x": 0.0, "y": 0.0}, "channel": 2}, "response": {"accepted": true, "real_timestamp_ms": 1789097723058, "virtual_time_s": 11.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 4, "wall_ts_ms": 1789097723069, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0004", "position": {"x": 0.0, "y": 0.0}, "channel": 3}, "response": {"accepted": true, "real_timestamp_ms": 1789097723069, "virtual_time_s": 17.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 5, "wall_ts_ms": 1789097723078, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0005", "position": {"x": 0.0, "y": 0.0}, "channel": 4}, "response": {"accepted": true, "real_timestamp_ms": 1789097723078, "virtual_time_s": 23.0, "measure_result": "direction", "svd_deg": 183.77}}
{"event": "action", "seq": 6, "wall_ts_ms": 1789097723114, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0006", "position": {"x": 0.0, "y": 0.0}, "channel": 5}, "response": {"accepted": true, "real_timestamp_ms": 1789097723114, "virtual_time_s": 29.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 7, "wall_ts_ms": 1789097723118, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0007", "position": {"x": 0.0, "y": 0.0}, "channel": 6}, "response": {"accepted": true, "real_timestamp_ms": 1789097723118, "virtual_time_s": 35.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 8, "wall_ts_ms": 1789097723123, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0008", "position": {"x": 0.0, "y": 0.0}, "channel": 7}, "response": {"accepted": true, "real_timestamp_ms": 1789097723123, "virtual_time_s": 41.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 9, "wall_ts_ms": 1789097723127, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0009", "position": {"x": 0.0, "y": 0.0}, "channel": 8}, "response": {"accepted": true, "real_timestamp_ms": 1789097723127, "virtual_time_s": 47.0, "measure_result": "direction", "svd_deg": 6.82}}
{"event": "action", "seq": 10, "wall_ts_ms": 1789097723156, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0010", "position": {"x": 0.0, "y": 0.0}, "channel": 9}, "response": {"accepted": true, "real_timestamp_ms": 1789097723156, "virtual_time_s": 53.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 11, "wall_ts_ms": 1789097723161, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0011", "position": {"x": 0.0, "y": 0.0}, "channel": 10}, "response": {"accepted": true, "real_timestamp_ms": 1789097723161, "virtual_time_s": 59.0, "measure_result": "direction", "svd_deg": 248.32}}
{"event": "action", "seq": 12, "wall_ts_ms": 1789097723189, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0012", "position": {"x": 0.0, "y": 0.0}, "channel": 11}, "response": {"accepted": true, "real_timestamp_ms": 1789097723189, "virtual_time_s": 65.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 13, "wall_ts_ms": 1789097723194, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0013", "position": {"x": 0.0, "y": 0.0}, "channel": 12}, "response": {"accepted": true, "real_timestamp_ms": 1789097723194, "virtual_time_s": 71.0, "measure_result": "direction", "svd_deg": 341.12}}
{"event": "action", "seq": 14, "wall_ts_ms": 1789097723221, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0014", "position": {"x": 0.0, "y": 0.0}, "channel": 13}, "response": {"accepted": true, "real_timestamp_ms": 1789097723221, "virtual_time_s": 77.0, "measure_result": "direction", "svd_deg": 66.19}}
{"event": "action", "seq": 15, "wall_ts_ms": 1789097723249, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0015", "position": {"x": 0.0, "y": 0.0}, "channel": 14}, "response": {"accepted": true, "real_timestamp_ms": 1789097723249, "virtual_time_s": 83.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 16, "wall_ts_ms": 1789097723257, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0016", "position": {"x": 0.0, "y": 0.0}, "channel": 15}, "response": {"accepted": true, "real_timestamp_ms": 1789097723257, "virtual_time_s": 89.0, "measure_result": "direction", "svd_deg": 59.3}}
{"event": "action", "seq": 17, "wall_ts_ms": 1789097723284, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0017", "position": {"x": 0.0, "y": 0.0}, "channel": 16}, "response": {"accepted": true, "real_timestamp_ms": 1789097723284, "virtual_time_s": 95.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 18, "wall_ts_ms": 1789097723289, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0018", "position": {"x": 0.0, "y": 0.0}, "channel": 17}, "response": {"accepted": true, "real_timestamp_ms": 1789097723289, "virtual_time_s": 101.0, "measure_result": "direction", "svd_deg": 292.42}}
{"event": "action", "seq": 19, "wall_ts_ms": 1789097723317, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0019", "position": {"x": 0.0, "y": 0.0}, "channel": 18}, "response": {"accepted": true, "real_timestamp_ms": 1789097723317, "virtual_time_s": 107.0, "measure_result": "direction", "svd_deg": 234.95999999999998}}
{"event": "action", "seq": 20, "wall_ts_ms": 1789097723347, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0020", "position": {"x": 0.0, "y": 0.0}, "channel": 19}, "response": {"accepted": true, "real_timestamp_ms": 1789097723347, "virtual_time_s": 113.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 21, "wall_ts_ms": 1789097723351, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0021", "position": {"x": 0.0, "y": 0.0}, "channel": 20}, "response": {"accepted": true, "real_timestamp_ms": 1789097723351, "virtual_time_s": 119.0, "measure_result": "no_signal"}}
{"event": "action", "seq": 22, "wall_ts_ms": 1789097723521, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0022", "position": {"x": 45.5311293088603, "y": -628.5542772885196}, "channel": 17}, "response": {"accepted": true, "real_timestamp_ms": 1789097723521, "virtual_time_s": 251.040242, "measure_result": "direction", "svd_deg": 319.18}}
{"event": "action", "seq": 23, "wall_ts_ms": 1789097723673, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0023", "position": {"x": -35.83921138147937, "y": -632.3431072483985}, "channel": 10}, "response": {"accepted": true, "real_timestamp_ms": 1789097723673, "virtual_time_s": 273.331942, "measure_result": "direction", "svd_deg": 227.8}}
{"event": "action", "seq": 24, "wall_ts_ms": 1789097723831, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0024", "position": {"x": -189.58483778122178, "y": -678.6629482886165}, "channel": 18}, "response": {"accepted": true, "real_timestamp_ms": 1789097723831, "virtual_time_s": 311.446266, "measure_result": "direction", "svd_deg": 193.49}}
{"event": "action", "seq": 25, "wall_ts_ms": 1789097723940, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0025", "position": {"x": -424.00310375885584, "y": -1062.6328269504545}, "channel": 10}, "response": {"accepted": true, "real_timestamp_ms": 1789097723940, "virtual_time_s": 407.42066, "measure_result": "direction", "svd_deg": 55.76}}
{"event": "action", "seq": 26, "wall_ts_ms": 1789097724048, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0026", "position": {"x": -304.7753115379185, "y": -1102.2525422490633}, "channel": 17}, "response": {"accepted": true, "real_timestamp_ms": 1789097724048, "virtual_time_s": 438.548325, "measure_result": "direction", "svd_deg": 15.6}}
{"event": "action", "seq": 27, "wall_ts_ms": 1789097724153, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0027", "position": {"x": -600.0000000000006, "y": -1039.230484541326}, "channel": 1}, "response": {"accepted": true, "real_timestamp_ms": 1789097724153, "virtual_time_s": 504.923617, "measure_result": "no_signal"}}
{"event": "action", "seq": 28, "wall_ts_ms": 1789097724156, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0028", "position": {"x": -600.0000000000006, "y": -1039.230484541326}, "channel": 2}, "response": {"accepted": true, "real_timestamp_ms": 1789097724156, "virtual_time_s": 510.923617, "measure_result": "direction", "svd_deg": 221.53}}
{"event": "action", "seq": 29, "wall_ts_ms": 1789097724176, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0029", "position": {"x": -600.0000000000006, "y": -1039.230484541326}, "channel": 3}, "response": {"accepted": true, "real_timestamp_ms": 1789097724176, "virtual_time_s": 516.923617, "measure_result": "no_signal"}}
{"event": "action", "seq": 30, "wall_ts_ms": 1789097724180, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0030", "position": {"x": -600.0000000000006, "y": -1039.230484541326}, "channel": 5}, "response": {"accepted": true, "real_timestamp_ms": 1789097724180, "virtual_time_s": 522.923617, "measure_result": "direction", "svd_deg": 192.51}}
{"event": "action", "seq": 31, "wall_ts_ms": 1789097724200, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0031", "position": {"x": -600.0000000000006, "y": -1039.230484541326}, "channel": 6}, "response": {"accepted": true, "real_timestamp_ms": 1789097724200, "virtual_time_s": 528.923617, "measure_result": "no_signal"}}
{"event": "action", "seq": 32, "wall_ts_ms": 1789097724203, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0032", "position": {"x": -600.0000000000006, "y": -1039.230484541326}, "channel": 7}, "response": {"accepted": true, "real_timestamp_ms": 1789097724203, "virtual_time_s": 534.923617, "measure_result": "no_signal"}}
{"event": "action", "seq": 33, "wall_ts_ms": 1789097724207, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0033", "position": {"x": -600.0000000000006, "y": -1039.230484541326}, "channel": 9}, "response": {"accepted": true, "real_timestamp_ms": 1789097724207, "virtual_time_s": 540.923617, "measure_result": "no_signal"}}
{"event": "action", "seq": 34, "wall_ts_ms": 1789097724212, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0034", "position": {"x": -600.0000000000006, "y": -1039.230484541326}, "channel": 11}, "response": {"accepted": true, "real_timestamp_ms": 1789097724212, "virtual_time_s": 546.923617, "measure_result": "no_signal"}}
{"event": "action", "seq": 35, "wall_ts_ms": 1789097724217, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0035", "position": {"x": -600.0000000000006, "y": -1039.230484541326}, "channel": 14}, "response": {"accepted": true, "real_timestamp_ms": 1789097724217, "virtual_time_s": 552.923617, "measure_result": "no_signal"}}
{"event": "action", "seq": 36, "wall_ts_ms": 1789097724220, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0036", "position": {"x": -600.0000000000006, "y": -1039.230484541326}, "channel": 16}, "response": {"accepted": true, "real_timestamp_ms": 1789097724220, "virtual_time_s": 558.923617, "measure_result": "no_signal"}}
{"event": "action", "seq": 37, "wall_ts_ms": 1789097724225, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0037", "position": {"x": -600.0000000000006, "y": -1039.230484541326}, "channel": 19}, "response": {"accepted": true, "real_timestamp_ms": 1789097724225, "virtual_time_s": 564.923617, "measure_result": "no_signal"}}
{"event": "action", "seq": 38, "wall_ts_ms": 1789097724231, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0038", "position": {"x": -600.0000000000006, "y": -1039.230484541326}, "channel": 20}, "response": {"accepted": true, "real_timestamp_ms": 1789097724231, "virtual_time_s": 570.923617, "measure_result": "no_signal"}}
{"event": "action", "seq": 39, "wall_ts_ms": 1789097724356, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0039", "position": {"x": -570.2217432790651, "y": -1166.041238500334}, "channel": 17}, "response": {"accepted": true, "real_timestamp_ms": 1789097724356, "virtual_time_s": 602.975651, "measure_result": "direction", "svd_deg": 14.06}}
{"event": "action", "seq": 40, "wall_ts_ms": 1789097724357, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0040", "position": {"x": 380.0185292494899, "y": -917.1715545195534}, "channel": 17}, "response": {"accepted": true, "real_timestamp_ms": 1789097724357, "virtual_time_s": 804.433554, "clear_result": "success"}}
{"event": "action", "seq": 41, "wall_ts_ms": 1789097724468, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0041", "position": {"x": 399.67804914246256, "y": -1012.5554428208678}, "channel": 18}, "response": {"accepted": true, "real_timestamp_ms": 1789097724468, "virtual_time_s": 829.911319, "measure_result": "direction", "svd_deg": 164.48}}
{"event": "action", "seq": 42, "wall_ts_ms": 1789097724581, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0042", "position": {"x": 408.8763329318663, "y": -1008.680409937976}, "channel": 18}, "response": {"accepted": true, "real_timestamp_ms": 1789097724581, "virtual_time_s": 836.907559, "measure_result": "direction", "svd_deg": 166.46}}
{"event": "action", "seq": 43, "wall_ts_ms": 1789097724583, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0043", "position": {"x": -541.8152713560978, "y": -765.4160556969817}, "channel": 18}, "response": {"accepted": true, "real_timestamp_ms": 1789097724583, "virtual_time_s": 1038.171878, "clear_result": "success"}}
{"event": "action", "seq": 44, "wall_ts_ms": 1789097724696, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0044", "position": {"x": -670.6906035776568, "y": -757.3448720731842}, "channel": 10}, "response": {"accepted": true, "real_timestamp_ms": 1789097724696, "virtual_time_s": 1069.997443, "measure_result": "direction", "svd_deg": 315.33}}
{"event": "action", "seq": 45, "wall_ts_ms": 1789097724698, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0045", "position": {"x": -399.11939627359095, "y": -1025.855640658727}, "channel": 10}, "response": {"accepted": true, "real_timestamp_ms": 1789097724698, "virtual_time_s": 1151.377795, "clear_result": "success"}}
{"event": "action", "seq": 46, "wall_ts_ms": 1789097724807, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0046", "position": {"x": -571.2452955285204, "y": -1313.1455393717845}, "channel": 2}, "response": {"accepted": true, "real_timestamp_ms": 1789097724807, "virtual_time_s": 1224.359229, "measure_result": "direction", "svd_deg": 183.54}}
{"event": "action", "seq": 47, "wall_ts_ms": 1789097724922, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0047", "position": {"x": -646.882802561242, "y": -1443.0990706454477}, "channel": 5}, "response": {"accepted": true, "real_timestamp_ms": 1789097724922, "virtual_time_s": 1260.431776, "measure_result": "direction", "svd_deg": 155.79}}
{"event": "action", "seq": 48, "wall_ts_ms": 1789097725026, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0048", "position": {"x": -962.0760943012335, "y": -1452.0959091366963}, "channel": 5}, "response": {"accepted": true, "real_timestamp_ms": 1789097725026, "virtual_time_s": 1328.49611, "measure_result": "direction", "svd_deg": 132.87}}
{"event": "action", "seq": 49, "wall_ts_ms": 1789097725028, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0049", "position": {"x": -1218.5659146282205, "y": -1176.2219268777521}, "channel": 5}, "response": {"accepted": true, "real_timestamp_ms": 1789097725028, "virtual_time_s": 1408.833612, "clear_result": "success"}}
{"event": "action", "seq": 50, "wall_ts_ms": 1789097725128, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0050", "position": {"x": -935.6927627448108, "y": -1336.060116189502}, "channel": 2}, "response": {"accepted": true, "real_timestamp_ms": 1789097725128, "virtual_time_s": 1479.815292, "measure_result": "near"}}
{"event": "action", "seq": 51, "wall_ts_ms": 1789097725133, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0051", "position": {"x": -935.6927627448109, "y": -1336.060116189502}, "channel": 2}, "response": {"accepted": true, "real_timestamp_ms": 1789097725133, "virtual_time_s": 1484.815292, "clear_result": "success"}}
{"event": "action", "seq": 52, "wall_ts_ms": 1789097725249, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0052", "position": {"x": -813.2456457709504, "y": -290.3860795658868}, "channel": 4}, "response": {"accepted": true, "real_timestamp_ms": 1789097725249, "virtual_time_s": 1701.379058, "measure_result": "direction", "svd_deg": 150.53}}
{"event": "action", "seq": 53, "wall_ts_ms": 1789097725330, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0053", "position": {"x": -1191.4025609460489, "y": -77.70039209077237}, "channel": 4}, "response": {"accepted": true, "real_timestamp_ms": 1789097725330, "virtual_time_s": 1793.151831, "measure_result": "direction", "svd_deg": 350.41}}
{"event": "action", "seq": 54, "wall_ts_ms": 1789097725332, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0054", "position": {"x": -1179.1411102893894, "y": -79.99395273914965}, "channel": 4}, "response": {"accepted": true, "real_timestamp_ms": 1789097725332, "virtual_time_s": 1800.646654, "clear_result": "success"}}
{"event": "action", "seq": 55, "wall_ts_ms": 1789097725414, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0055", "position": {"x": -1200.0, "y": 1.4695761589768238e-13}, "channel": 1}, "response": {"accepted": true, "real_timestamp_ms": 1789097725414, "virtual_time_s": 1823.180408, "measure_result": "no_signal"}}
{"event": "action", "seq": 56, "wall_ts_ms": 1789097725417, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0056", "position": {"x": -1200.0, "y": 1.4695761589768238e-13}, "channel": 3}, "response": {"accepted": true, "real_timestamp_ms": 1789097725417, "virtual_time_s": 1829.180408, "measure_result": "no_signal"}}
{"event": "action", "seq": 57, "wall_ts_ms": 1789097725419, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0057", "position": {"x": -1200.0, "y": 1.4695761589768238e-13}, "channel": 6}, "response": {"accepted": true, "real_timestamp_ms": 1789097725419, "virtual_time_s": 1835.180408, "measure_result": "no_signal"}}
{"event": "action", "seq": 58, "wall_ts_ms": 1789097725422, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0058", "position": {"x": -1200.0, "y": 1.4695761589768238e-13}, "channel": 7}, "response": {"accepted": true, "real_timestamp_ms": 1789097725422, "virtual_time_s": 1841.180408, "measure_result": "no_signal"}}
{"event": "action", "seq": 59, "wall_ts_ms": 1789097725424, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0059", "position": {"x": -1200.0, "y": 1.4695761589768238e-13}, "channel": 9}, "response": {"accepted": true, "real_timestamp_ms": 1789097725424, "virtual_time_s": 1847.180408, "measure_result": "no_signal"}}
{"event": "action", "seq": 60, "wall_ts_ms": 1789097725427, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0060", "position": {"x": -1200.0, "y": 1.4695761589768238e-13}, "channel": 11}, "response": {"accepted": true, "real_timestamp_ms": 1789097725427, "virtual_time_s": 1853.180408, "measure_result": "no_signal"}}
{"event": "action", "seq": 61, "wall_ts_ms": 1789097725429, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0061", "position": {"x": -1200.0, "y": 1.4695761589768238e-13}, "channel": 14}, "response": {"accepted": true, "real_timestamp_ms": 1789097725429, "virtual_time_s": 1859.180408, "measure_result": "no_signal"}}
{"event": "action", "seq": 62, "wall_ts_ms": 1789097725431, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0062", "position": {"x": -1200.0, "y": 1.4695761589768238e-13}, "channel": 16}, "response": {"accepted": true, "real_timestamp_ms": 1789097725431, "virtual_time_s": 1865.180408, "measure_result": "no_signal"}}
{"event": "action", "seq": 63, "wall_ts_ms": 1789097725433, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0063", "position": {"x": -1200.0, "y": 1.4695761589768238e-13}, "channel": 19}, "response": {"accepted": true, "real_timestamp_ms": 1789097725433, "virtual_time_s": 1871.180408, "measure_result": "no_signal"}}
{"event": "action", "seq": 64, "wall_ts_ms": 1789097725435, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0064", "position": {"x": -1200.0, "y": 1.4695761589768238e-13}, "channel": 20}, "response": {"accepted": true, "real_timestamp_ms": 1789097725435, "virtual_time_s": 1877.180408, "measure_result": "no_signal"}}
{"event": "action", "seq": 65, "wall_ts_ms": 1789097725517, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0065", "position": {"x": -599.9999999999998, "y": 1039.2304845413264}, "channel": 20}, "response": {"accepted": true, "real_timestamp_ms": 1789097725517, "virtual_time_s": 2122.180408, "measure_result": "no_signal"}}
{"event": "action", "seq": 66, "wall_ts_ms": 1789097725523, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0066", "position": {"x": -599.9999999999998, "y": 1039.2304845413264}, "channel": 1}, "response": {"accepted": true, "real_timestamp_ms": 1789097725523, "virtual_time_s": 2128.180408, "measure_result": "direction", "svd_deg": 27.02}}
{"event": "action", "seq": 67, "wall_ts_ms": 1789097725536, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0067", "position": {"x": -599.9999999999998, "y": 1039.2304845413264}, "channel": 3}, "response": {"accepted": true, "real_timestamp_ms": 1789097725536, "virtual_time_s": 2134.180408, "measure_result": "direction", "svd_deg": 91.23}}
{"event": "action", "seq": 68, "wall_ts_ms": 1789097725549, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0068", "position": {"x": -599.9999999999998, "y": 1039.2304845413264}, "channel": 6}, "response": {"accepted": true, "real_timestamp_ms": 1789097725549, "virtual_time_s": 2140.180408, "measure_result": "no_signal"}}
{"event": "action", "seq": 69, "wall_ts_ms": 1789097725551, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0069", "position": {"x": -599.9999999999998, "y": 1039.2304845413264}, "channel": 7}, "response": {"accepted": true, "real_timestamp_ms": 1789097725551, "virtual_time_s": 2146.180408, "measure_result": "no_signal"}}
{"event": "action", "seq": 70, "wall_ts_ms": 1789097725554, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0070", "position": {"x": -599.9999999999998, "y": 1039.2304845413264}, "channel": 9}, "response": {"accepted": true, "real_timestamp_ms": 1789097725554, "virtual_time_s": 2152.180408, "measure_result": "no_signal"}}
{"event": "action", "seq": 71, "wall_ts_ms": 1789097725556, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0071", "position": {"x": -599.9999999999998, "y": 1039.2304845413264}, "channel": 11}, "response": {"accepted": true, "real_timestamp_ms": 1789097725556, "virtual_time_s": 2158.180408, "measure_result": "direction", "svd_deg": 25.75}}
{"event": "action", "seq": 72, "wall_ts_ms": 1789097725570, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0072", "position": {"x": -599.9999999999998, "y": 1039.2304845413264}, "channel": 14}, "response": {"accepted": true, "real_timestamp_ms": 1789097725570, "virtual_time_s": 2164.180408, "measure_result": "no_signal"}}
{"event": "action", "seq": 73, "wall_ts_ms": 1789097725573, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0073", "position": {"x": -599.9999999999998, "y": 1039.2304845413264}, "channel": 16}, "response": {"accepted": true, "real_timestamp_ms": 1789097725573, "virtual_time_s": 2170.180408, "measure_result": "no_signal"}}
{"event": "action", "seq": 74, "wall_ts_ms": 1789097725578, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0074", "position": {"x": -599.9999999999998, "y": 1039.2304845413264}, "channel": 19}, "response": {"accepted": true, "real_timestamp_ms": 1789097725578, "virtual_time_s": 2176.180408, "measure_result": "no_signal"}}
{"event": "action", "seq": 75, "wall_ts_ms": 1789097725701, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0075", "position": {"x": -341.8633127668281, "y": 1298.2674759087915}, "channel": 3}, "response": {"accepted": true, "real_timestamp_ms": 1789097725701, "virtual_time_s": 2255.319922, "measure_result": "direction", "svd_deg": 129.38}}
{"event": "action", "seq": 76, "wall_ts_ms": 1789097725825, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0076", "position": {"x": -613.1173997480632, "y": 1629.4606258634378}, "channel": 3}, "response": {"accepted": true, "real_timestamp_ms": 1789097725825, "virtual_time_s": 2345.939472, "measure_result": "direction", "svd_deg": 116.88}}
{"event": "action", "seq": 77, "wall_ts_ms": 1789097725827, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0077", "position": {"x": -618.510363302433, "y": 1640.5793402529719}, "channel": 3}, "response": {"accepted": true, "real_timestamp_ms": 1789097725827, "virtual_time_s": 2353.410989, "clear_result": "success"}}
{"event": "action", "seq": 78, "wall_ts_ms": 1789097725948, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0078", "position": {"x": -160.86440481749315, "y": 1554.6757280823801}, "channel": 1}, "response": {"accepted": true, "real_timestamp_ms": 1789097725948, "virtual_time_s": 2452.538698, "measure_result": "direction", "svd_deg": 320.78}}
{"event": "action", "seq": 79, "wall_ts_ms": 1789097725950, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0079", "position": {"x": 59.39508758620733, "y": 1375.2792811115912}, "channel": 1}, "response": {"accepted": true, "real_timestamp_ms": 1789097725950, "virtual_time_s": 2514.353249, "clear_result": "success"}}
{"event": "action", "seq": 80, "wall_ts_ms": 1789097726058, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0080", "position": {"x": 205.44691039168774, "y": 1199.815390575682}, "channel": 11}, "response": {"accepted": true, "real_timestamp_ms": 1789097726058, "virtual_time_s": 2566.012295, "measure_result": "direction", "svd_deg": 81.79}}
{"event": "action", "seq": 81, "wall_ts_ms": 1789097726140, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0081", "position": {"x": 241.27184119723879, "y": 1445.3290109381398}, "channel": 11}, "response": {"accepted": true, "real_timestamp_ms": 1789097726140, "virtual_time_s": 2620.635017, "measure_result": "direction", "svd_deg": 66.49}}
{"event": "action", "seq": 82, "wall_ts_ms": 1789097726142, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0082", "position": {"x": 244.5635577315176, "y": 1453.2739565420536}, "channel": 11}, "response": {"accepted": true, "real_timestamp_ms": 1789097726142, "virtual_time_s": 2627.354989, "clear_result": "success"}}
{"event": "action", "seq": 83, "wall_ts_ms": 1789097726222, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0083", "position": {"x": 238.46119571907008, "y": 927.5083908883764}, "channel": 13}, "response": {"accepted": true, "real_timestamp_ms": 1789097726222, "virtual_time_s": 2738.515185, "measure_result": "direction", "svd_deg": 45.66}}
{"event": "action", "seq": 84, "wall_ts_ms": 1789097726285, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0084", "position": {"x": 318.1172768672118, "y": 886.4632982829636}, "channel": 15}, "response": {"accepted": true, "real_timestamp_ms": 1789097726285, "virtual_time_s": 2762.437005, "measure_result": "direction", "svd_deg": 359.39}}
{"event": "action", "seq": 85, "wall_ts_ms": 1789097726337, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0085", "position": {"x": 554.7573623332934, "y": 1253.4316727035684}, "channel": 13}, "response": {"accepted": true, "real_timestamp_ms": 1789097726337, "virtual_time_s": 2855.767256, "measure_result": "direction", "svd_deg": 270.78}}
{"event": "action", "seq": 86, "wall_ts_ms": 1789097726339, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0086", "position": {"x": 554.7309223739858, "y": 1246.6988269631088}, "channel": 13}, "response": {"accepted": true, "real_timestamp_ms": 1789097726339, "virtual_time_s": 2862.113836, "clear_result": "success"}}
{"event": "action", "seq": 87, "wall_ts_ms": 1789097726380, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0087", "position": {"x": 600.0000000000001, "y": 1039.2304845413264}, "channel": 6}, "response": {"accepted": true, "real_timestamp_ms": 1789097726380, "virtual_time_s": 2910.583779, "measure_result": "no_signal"}}
{"event": "action", "seq": 88, "wall_ts_ms": 1789097726383, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0088", "position": {"x": 600.0000000000001, "y": 1039.2304845413264}, "channel": 7}, "response": {"accepted": true, "real_timestamp_ms": 1789097726383, "virtual_time_s": 2916.583779, "measure_result": "direction", "svd_deg": 299.27}}
{"event": "action", "seq": 89, "wall_ts_ms": 1789097726395, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0089", "position": {"x": 600.0000000000001, "y": 1039.2304845413264}, "channel": 9}, "response": {"accepted": true, "real_timestamp_ms": 1789097726395, "virtual_time_s": 2922.583779, "measure_result": "no_signal"}}
{"event": "action", "seq": 90, "wall_ts_ms": 1789097726398, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0090", "position": {"x": 600.0000000000001, "y": 1039.2304845413264}, "channel": 14}, "response": {"accepted": true, "real_timestamp_ms": 1789097726398, "virtual_time_s": 2928.583779, "measure_result": "no_signal"}}
{"event": "action", "seq": 91, "wall_ts_ms": 1789097726400, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0091", "position": {"x": 600.0000000000001, "y": 1039.2304845413264}, "channel": 16}, "response": {"accepted": true, "real_timestamp_ms": 1789097726400, "virtual_time_s": 2934.583779, "measure_result": "no_signal"}}
{"event": "action", "seq": 92, "wall_ts_ms": 1789097726403, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0092", "position": {"x": 600.0000000000001, "y": 1039.2304845413264}, "channel": 19}, "response": {"accepted": true, "real_timestamp_ms": 1789097726403, "virtual_time_s": 2940.583779, "measure_result": "no_signal"}}
{"event": "action", "seq": 93, "wall_ts_ms": 1789097726406, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0093", "position": {"x": 600.0000000000001, "y": 1039.2304845413264}, "channel": 20}, "response": {"accepted": true, "real_timestamp_ms": 1789097726406, "virtual_time_s": 2946.583779, "measure_result": "direction", "svd_deg": 38.53}}
{"event": "action", "seq": 94, "wall_ts_ms": 1789097726483, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0094", "position": {"x": 586.1737858863822, "y": 1302.4505299064706}, "channel": 20}, "response": {"accepted": true, "real_timestamp_ms": 1789097726483, "virtual_time_s": 3004.300363, "measure_result": "direction", "svd_deg": 3.94}}
{"event": "action", "seq": 95, "wall_ts_ms": 1789097726546, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0095", "position": {"x": 626.6901072371054, "y": 1262.04134792287}, "channel": 15}, "response": {"accepted": true, "real_timestamp_ms": 1789097726546, "virtual_time_s": 3021.744967, "measure_result": "direction", "svd_deg": 255.32999999999998}}
{"event": "action", "seq": 96, "wall_ts_ms": 1789097726548, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0096", "position": {"x": 527.7218100252474, "y": 884.3716769891525}, "channel": 15}, "response": {"accepted": true, "real_timestamp_ms": 1789097726548, "virtual_time_s": 3104.829307, "clear_result": "success"}}
{"event": "action", "seq": 97, "wall_ts_ms": 1789097726606, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0097", "position": {"x": 863.5266281306339, "y": 952.1934215754386}, "channel": 20}, "response": {"accepted": true, "real_timestamp_ms": 1789097726606, "virtual_time_s": 3179.34636, "measure_result": "direction", "svd_deg": 76.37}}
{"event": "action", "seq": 98, "wall_ts_ms": 1789097726608, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0098", "position": {"x": 954.8140462629618, "y": 1328.001693570206}, "channel": 20}, "response": {"accepted": true, "real_timestamp_ms": 1789097726608, "virtual_time_s": 3261.693694, "clear_result": "success"}}
{"event": "action", "seq": 99, "wall_ts_ms": 1789097726672, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0099", "position": {"x": 1143.6030848754813, "y": 561.6607676258121}, "channel": 7}, "response": {"accepted": true, "real_timestamp_ms": 1789097726672, "virtual_time_s": 3425.54422, "measure_result": "direction", "svd_deg": 257.15}}
{"event": "action", "seq": 100, "wall_ts_ms": 1789097726716, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0100", "position": {"x": 809.4613485062162, "y": 330.6635723771227}, "channel": 8}, "response": {"accepted": true, "real_timestamp_ms": 1789097726716, "virtual_time_s": 3512.787165, "measure_result": "direction", "svd_deg": 340.99}}
{"event": "action", "seq": 101, "wall_ts_ms": 1789097726743, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0101", "position": {"x": 1064.0311792299826, "y": 210.32188555906123}, "channel": 7}, "response": {"accepted": true, "real_timestamp_ms": 1789097726743, "virtual_time_s": 3575.103387, "measure_result": "direction", "svd_deg": 79.29}}
{"event": "action", "seq": 102, "wall_ts_ms": 1789097726745, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0102", "position": {"x": 1066.2810199514943, "y": 223.47708343730568}, "channel": 7}, "response": {"accepted": true, "real_timestamp_ms": 1789097726745, "virtual_time_s": 3582.772627, "clear_result": "success"}}
{"event": "action", "seq": 103, "wall_ts_ms": 1789097726766, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0103", "position": {"x": 1318.903431506997, "y": 156.5033273362243}, "channel": 8}, "response": {"accepted": true, "real_timestamp_ms": 1789097726766, "virtual_time_s": 3641.042529, "measure_result": "direction", "svd_deg": 162.78}}
{"event": "action", "seq": 104, "wall_ts_ms": 1789097726788, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0104", "position": {"x": 1290.0056375606528, "y": 164.90711719307632}, "channel": 8}, "response": {"accepted": true, "real_timestamp_ms": 1789097726788, "virtual_time_s": 3652.06152, "measure_result": "direction", "svd_deg": 159.25}}
{"event": "action", "seq": 105, "wall_ts_ms": 1789097726790, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0105", "position": {"x": 1277.5398868926416, "y": 169.38155097430587}, "channel": 8}, "response": {"accepted": true, "real_timestamp_ms": 1789097726790, "virtual_time_s": 3659.71041, "clear_result": "success"}}
{"event": "action", "seq": 106, "wall_ts_ms": 1789097726814, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0106", "position": {"x": 1200.0, "y": 0.0}, "channel": 6}, "response": {"accepted": true, "real_timestamp_ms": 1789097726814, "virtual_time_s": 3702.967648, "measure_result": "no_signal"}}
{"event": "action", "seq": 107, "wall_ts_ms": 1789097726817, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0107", "position": {"x": 1200.0, "y": 0.0}, "channel": 9}, "response": {"accepted": true, "real_timestamp_ms": 1789097726817, "virtual_time_s": 3708.967648, "measure_result": "no_signal"}}
{"event": "action", "seq": 108, "wall_ts_ms": 1789097726820, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0108", "position": {"x": 1200.0, "y": 0.0}, "channel": 14}, "response": {"accepted": true, "real_timestamp_ms": 1789097726820, "virtual_time_s": 3714.967648, "measure_result": "no_signal"}}
{"event": "action", "seq": 109, "wall_ts_ms": 1789097726822, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0109", "position": {"x": 1200.0, "y": 0.0}, "channel": 16}, "response": {"accepted": true, "real_timestamp_ms": 1789097726822, "virtual_time_s": 3720.967648, "measure_result": "no_signal"}}
{"event": "action", "seq": 110, "wall_ts_ms": 1789097726825, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0110", "position": {"x": 1200.0, "y": 0.0}, "channel": 19}, "response": {"accepted": true, "real_timestamp_ms": 1789097726825, "virtual_time_s": 3726.967648, "measure_result": "no_signal"}}
{"event": "action", "seq": 111, "wall_ts_ms": 1789097726850, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0111", "position": {"x": 886.6998305999583, "y": -65.43898742544147}, "channel": 12}, "response": {"accepted": true, "real_timestamp_ms": 1789097726850, "virtual_time_s": 3796.979915, "measure_result": "direction", "svd_deg": 282.4}}
{"event": "action", "seq": 112, "wall_ts_ms": 1789097726854, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0112", "position": {"x": 943.7575366372944, "y": -323.04992770003867}, "channel": 12}, "response": {"accepted": true, "real_timestamp_ms": 1789097726854, "virtual_time_s": 3854.750732, "measure_result": "direction", "svd_deg": 273.61}}
{"event": "action", "seq": 113, "wall_ts_ms": 1789097726855, "method": "POST", "path": "/clear", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0113", "position": {"x": 944.5186065002501, "y": -332.4782587002512}, "channel": 12}, "response": {"accepted": true, "real_timestamp_ms": 1789097726855, "virtual_time_s": 3861.642532, "clear_result": "success"}}
{"event": "action", "seq": 114, "wall_ts_ms": 1789097726856, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0114", "position": {"x": 600.0000000000001, "y": -1039.2304845413264}, "channel": 6}, "response": {"accepted": true, "real_timestamp_ms": 1789097726856, "virtual_time_s": 4024.892877, "measure_result": "no_signal"}}
{"event": "action", "seq": 115, "wall_ts_ms": 1789097726860, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0115", "position": {"x": 600.0000000000001, "y": -1039.2304845413264}, "channel": 9}, "response": {"accepted": true, "real_timestamp_ms": 1789097726860, "virtual_time_s": 4030.892877, "measure_result": "no_signal"}}
{"event": "action", "seq": 116, "wall_ts_ms": 1789097726862, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0116", "position": {"x": 600.0000000000001, "y": -1039.2304845413264}, "channel": 14}, "response": {"accepted": true, "real_timestamp_ms": 1789097726862, "virtual_time_s": 4036.892877, "measure_result": "no_signal"}}
{"event": "action", "seq": 117, "wall_ts_ms": 1789097726864, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0117", "position": {"x": 600.0000000000001, "y": -1039.2304845413264}, "channel": 16}, "response": {"accepted": true, "real_timestamp_ms": 1789097726864, "virtual_time_s": 4042.892877, "measure_result": "no_signal"}}
{"event": "action", "seq": 118, "wall_ts_ms": 1789097726867, "method": "POST", "path": "/measure", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0118", "position": {"x": 600.0000000000001, "y": -1039.2304845413264}, "channel": 19}, "response": {"accepted": true, "real_timestamp_ms": 1789097726867, "virtual_time_s": 4048.892877, "measure_result": "no_signal"}}
{"event": "action", "seq": 119, "wall_ts_ms": 1789097726869, "method": "POST", "path": "/exit", "status": 200, "request": {"arena_id": "default", "robot_id": "1", "request_id": "eb3f25500072-0119"}, "response": {"accepted": true, "real_timestamp_ms": 1789097726869, "virtual_time_s": 4048.892877, "exit_reason": "user_exit"}}
{"event": "session_end", "reason": "user_exit", "virtual_time_s": 4048.892877, "real_elapsed_s": 11.64477, "request_count": 119, "cleared_count": 15}
@@ -0,0 +1,128 @@
{
"case_code": "VQKB-DHWP-JGTM-98EW",
"module": "q3_practice",
"robot_id": "1",
"seed": 1368779781,
"truth": [
{
"channel": 13,
"x": 554.8588259633532,
"y": 1244.659365990903,
"r_eff_m": 1362.874296044368,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 7,
"x": 1065.3524749007952,
"y": 217.7585180332171,
"r_eff_m": 1071.7166972389825,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 10,
"x": -402.4394670146116,
"y": -1031.4211824187898,
"r_eff_m": 1296.8377765558223,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 3,
"x": -622.4073708003119,
"y": 1647.9468895217547,
"r_eff_m": 1075.9709994154707,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 12,
"x": 944.568444060052,
"y": -334.4610463406964,
"r_eff_m": 1376.0615386573004,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 8,
"x": 1274.840248994663,
"y": 170.93445645012275,
"r_eff_m": 1399.6338852090485,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 1,
"x": 45.45947329669638,
"y": 1380.9782532154657,
"r_eff_m": 1318.9836429255063,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 18,
"x": -546.5934213344553,
"y": -762.9975795799155,
"r_eff_m": 1368.904295278126,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 20,
"x": 952.4459677190133,
"y": 1327.9097138456584,
"r_eff_m": 1107.7244397116335,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 2,
"x": -938.3564523770655,
"y": -1335.504363831014,
"r_eff_m": 1496.4037785889939,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 4,
"x": -1169.8069503281326,
"y": -81.42522149758867,
"r_eff_m": 1234.2519658982385,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 17,
"x": 383.9916268825851,
"y": -913.4442984993188,
"r_eff_m": 1163.1974374312167,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 15,
"x": 529.9690595799416,
"y": 886.5384827951485,
"r_eff_m": 1246.3397672927551,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 11,
"x": 245.1461688066881,
"y": 1454.481757671101,
"r_eff_m": 1296.0699292612499,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 5,
"x": -1219.6790461495623,
"y": -1175.2294002972626,
"r_eff_m": 1392.0228530017737,
"directional": false,
"heading_deg": 0.0
}
]
}
+65
View File
@@ -0,0 +1,65 @@
# mcm26b-referee — CUMCM 2026 B 题独立评测机
自建的官方模拟器复现:**模拟器引擎 + 机器狗 HTTP 接口 + 官方流程 WebUI**,
作为一个**独立包**发布(本目录),零第三方依赖,且**不导入任何参赛队代码**。
与参赛队实现(仓库根的 `mcm26b` 包)完全解耦:双方只通过附件2 定义的
HTTP 协议耦合——评测机换任何客户端都成立,参赛队代码对接官方模拟器也无需改动。
```bash
uv sync # 根工作区会同时挂载本包(dev 依赖)
uv run python -m referee # WebUI http://127.0.0.1:8026,接口 :2026
uv run pytest referee/tests # 本包测试
```
- 浏览器打开 `http://127.0.0.1:8026`:选择测试模块 → 确认开始 → 5 秒倒计时 →
接口开放,地图/状态/指令与反馈实时可见;
- **回放器**("回放"Tab):按案例加载存档回放,也可**手动选定 JSONL 文件**(点击
选择或拖拽导入,纯前端解析;兼容评测机行为日志与参赛队客户端会话日志两种格式),
支持播放/暂停、逐步±、倍速快进、缩放/平移、进度条定位;
- 机器狗程序照常对接 `http://127.0.0.1:2026`(如 `uv run mcm26b run --robot-id 2026001 --base-url http://127.0.0.1:2026`,
或一键 `uv run mcm26b run --robot-id 2026001 --local-judge`)。
## 包结构
```
referee/src/referee/
├── spec.py # 题面常量:目标区域/频道/半径/限时/耗时模型([题面] 逐条标注)
├── rules.py # 服务端字段表、结果码与 HTTP 语义(附件2 §5-9 的可执行表格)
├── timing.py # 纯计时模型:移动+换频道+动作,整数微秒累计(105/111/194/199 锚点)
├── simulator.py # 评测引擎:观测判定、状态机、幂等、并发 409、截止与断连
├── jsonio.py # 请求严格解析(BOM/重复键/NaN/深度)+ 响应规范化序列化
├── interface.py # 机器狗接口完整边界(与传输无关;HTTP 服务器与内存直连共用)
├── server.py # 接口 HTTP 承载:仅回环、仅窗口期监听、关闭即断连
├── session.py # 官方流程状态机:四模块/次数/倒计时/窗口/中止/结束原因/日志
├── scenes.py # 一局真值的数据准备(q3 全向 / q4 混合,可固定种子)
├── webui.py # 界面服务器与 /ui/api/*
└── static/ # 单页前端(原生 HTML/CSS/JS,离线可用)
```
## 题意符合性对照(附件1《模拟器使用说明》/附件2《通信接口说明》)
| 题意条款 | 实现 | 固化测试 |
| --- | --- | --- |
| 表1 动作与耗时(移动=距离/5、换频道 1s、检测 5s、清除 5s/3s,逐段整数微秒累计) | `timing.py` + `simulator.py` | `test_timing_examples.py`(105/111/194/199) |
| 观测判定:no_signal/near(≤5m)/direction(±1° 误差、两位小数、[0,360) 归一化);定向 ±90° 含边界;清除 ≤20m 与朝向无关、仅一次 | `simulator.py` | `test_simulator_conformance.py` |
| 协议边界:路径精确/仅 POST/415/413/严格 JSON/未知字段 accepted=false/幂等回放/409/并发 409 | `interface.py` + `simulator.py` | `test_interface_boundary.py`、`test_http_server.py`(真实 HTTP) |
| 时限:5s 倒计时、25min 窗口、/enter 后 20min(取早)、虚拟 100h;截止后接口关闭、已登记动作允许完成 | `session.py` 看门狗 + `simulator.py` | `test_session_flow.py` |
| 四个测试模块;演练不限次;正式各 3 次、启动成功即占用、中止仍占用;正式开始前再次确认 | `session.py` + WebUI | `test_session_flow.py` |
| 指令与反馈:默认最新 1000 条、可调 100..5000、不限请求总数、完整行为入日志 | `session.py` + WebUI 设置 | `test_session_flow.py` |
| 中止测试需两次确认;结束后不能复位或继续,只能开始新一局;结束原因显示 | WebUI + `session.py` | `test_session_flow.py` |
| 测试案例编码;演练结束显示 总数/全向/定向;正式任何界面不显示真值 | `session.py` 真值策略 + WebUI | `test_session_flow.py` |
| 接口地址默认 127.0.0.1:2026、只监听回环、空闲可改端口;非开放期连接失败属正常 | `server.py` + WebUI 设置 | `test_http_server.py` |
| robot_id/request_id 格式(1..64/128 字节、无控制/格式字符);arena_id="default" | `simulator.py` + `session.py` | `test_simulator_conformance.py` |
| 响应 virtual_time_s 微秒累计、最多 6 位小数去尾零;accepted=false 时恒为 0 | `jsonio.py` + `simulator.py` | `test_jsonio.py` |
| 行为日志可导出;加密上传上限 2MB 提示 | WebUI 日志导出 + 大小告警 | `test_session_flow.py` |
### 自建差异声明(题面未规定,均为 [工程] 假设,不代表官方实现)
- **联网登录/官方校时/17:30 启动截止/加密上传队列**:属官方服务器侧策略,本地评测机
不实现;robot_id 由界面填写并照常参与逐字节校验。
- **示向度误差场**:同一(频道,位置)误差由种子确定性决定(重复测量不变,
题面仅要求"同一地点误差不变"),不同位置独立白噪声;分布不代表官方。
- **场景分布**:源位置在目标圆域内均匀、r_eff 在 [1000,1500] 均匀、频道无重复抽样;
q4 定向源数量默认 3;均可用种子复现。
- **429 流量保护/幂等记录上限**:正常串行合法请求无速率上限 [题面],本地不设限。
- **案例编码格式**:4×4 大写字母数字(去易混字符),格式自拟。
+16
View File
@@ -0,0 +1,16 @@
[project]
name = "mcm26b-referee"
version = "1.0.0"
description = "CUMCM 2026 B题 独立评测机:自建模拟器引擎 + 机器狗HTTP接口 + 官方流程WebUI(零第三方依赖,不依赖任何参赛队代码)"
readme = "README.md"
requires-python = ">=3.10"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel]
packages = ["src/referee"]
[project.scripts]
referee = "referee.__main__:main"
@@ -0,0 +1,2 @@
{"event": "session_start", "case_code": "UVUZ-3SFE-RGJY-FXA9", "module": "q3_practice", "robot_id": "1", "seed": 1964351469, "countdown_s": 5.0, "window_s": 1500.0, "max_real_duration_s": 1200.0, "truth": [{"channel": 10, "x": 6.922639626707193, "y": -54.21785074493296, "r_eff_m": 1208.9714793043695, "directional": false, "heading_deg": 0.0}, {"channel": 19, "x": 155.98145671114492, "y": -36.754669349087465, "r_eff_m": 1426.0272326630359, "directional": false, "heading_deg": 0.0}, {"channel": 2, "x": 194.61427765031897, "y": -938.8739424947762, "r_eff_m": 1415.8506691978328, "directional": false, "heading_deg": 0.0}, {"channel": 17, "x": -17.404952811392654, "y": -452.2344629569482, "r_eff_m": 1364.0125860666094, "directional": false, "heading_deg": 0.0}, {"channel": 5, "x": 953.096497657794, "y": 795.5438323769687, "r_eff_m": 1155.5298061192445, "directional": false, "heading_deg": 0.0}, {"channel": 7, "x": -1032.982820237914, "y": -917.7537803665613, "r_eff_m": 1264.9186065215185, "directional": false, "heading_deg": 0.0}, {"channel": 1, "x": -312.94440343408445, "y": -1541.6524882495355, "r_eff_m": 1066.7005705743811, "directional": false, "heading_deg": 0.0}, {"channel": 8, "x": 1422.296786062693, "y": 409.19844666284126, "r_eff_m": 1242.207191999178, "directional": false, "heading_deg": 0.0}, {"channel": 11, "x": 474.78629925075734, "y": 429.1289982059512, "r_eff_m": 1085.4674303896347, "directional": false, "heading_deg": 0.0}, {"channel": 4, "x": -984.8373429071806, "y": 382.0719140671826, "r_eff_m": 1172.483404991756, "directional": false, "heading_deg": 0.0}]}
{"event": "session_end", "reason": "manual_abort", "virtual_time_s": 0.0, "real_elapsed_s": 1.608163, "request_count": 0, "cleared_count": 0}
@@ -0,0 +1,88 @@
{
"case_code": "UVUZ-3SFE-RGJY-FXA9",
"module": "q3_practice",
"robot_id": "1",
"seed": 1964351469,
"truth": [
{
"channel": 10,
"x": 6.922639626707193,
"y": -54.21785074493296,
"r_eff_m": 1208.9714793043695,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 19,
"x": 155.98145671114492,
"y": -36.754669349087465,
"r_eff_m": 1426.0272326630359,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 2,
"x": 194.61427765031897,
"y": -938.8739424947762,
"r_eff_m": 1415.8506691978328,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 17,
"x": -17.404952811392654,
"y": -452.2344629569482,
"r_eff_m": 1364.0125860666094,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 5,
"x": 953.096497657794,
"y": 795.5438323769687,
"r_eff_m": 1155.5298061192445,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 7,
"x": -1032.982820237914,
"y": -917.7537803665613,
"r_eff_m": 1264.9186065215185,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 1,
"x": -312.94440343408445,
"y": -1541.6524882495355,
"r_eff_m": 1066.7005705743811,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 8,
"x": 1422.296786062693,
"y": 409.19844666284126,
"r_eff_m": 1242.207191999178,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 11,
"x": 474.78629925075734,
"y": 429.1289982059512,
"r_eff_m": 1085.4674303896347,
"directional": false,
"heading_deg": 0.0
},
{
"channel": 4,
"x": -984.8373429071806,
"y": 382.0719140671826,
"r_eff_m": 1172.483404991756,
"directional": false,
"heading_deg": 0.0
}
]
}
+27
View File
@@ -0,0 +1,27 @@
"""mcm26b-referee:CUMCM 2026 B 题独立评测机(自建模拟器 + 机器狗接口 + WebUI)。
完全按附件1《模拟器使用说明》与附件2《模拟器通信接口说明及编程指南》复现
官方模拟器的可观测行为;与任何参赛队代码零依赖,双方只通过附件2 定义的
HTTP 协议耦合。启动入口::
python -m referee --ui-port 8026 --port 2026 --data-dir referee-data
"""
from .scenes import mixed_scene, random_omni_scene
from .session import (DISPLAY_LIMIT_DEFAULT, DISPLAY_LIMIT_MAX, DISPLAY_LIMIT_MIN,
FORMAL_ATTEMPTS, MODULES, Referee, RefereeError)
from .simulator import LocalSimulator, SimulatorClosed, Source
from .spec import (DEFAULT_ACTION_PORT, DEFAULT_SPEC, DEFAULT_UI_PORT, CostSpec,
FieldSpec, Spec)
from .timing import CostBreakdown, clear_cost, measure_cost, move_time
__version__ = "1.0.0"
__all__ = [
"CostBreakdown", "CostSpec", "DEFAULT_ACTION_PORT", "DEFAULT_SPEC",
"DEFAULT_UI_PORT", "DISPLAY_LIMIT_DEFAULT", "DISPLAY_LIMIT_MAX",
"DISPLAY_LIMIT_MIN", "FieldSpec", "FORMAL_ATTEMPTS", "LocalSimulator",
"MODULES", "Referee", "RefereeError", "SimulatorClosed", "Source", "Spec",
"clear_cost", "measure_cost", "mixed_scene", "move_time",
"random_omni_scene",
]
+65
View File
@@ -0,0 +1,65 @@
"""评测机启动入口:python -m referee [选项]。
同时启动两个仅回环的服务 [题面 附件2 1.5]:
- WebUI(默认 8026):浏览器打开 http://127.0.0.1:8026 操作测试流程;
- 机器狗接口(默认 2026):仅测试窗口期开放,其余时间连接失败属正常现象。
"""
from __future__ import annotations
import argparse
import socket
import sys
from pathlib import Path
from .server import ActionInterfaceServer
from .session import Referee
from .spec import DEFAULT_ACTION_PORT, DEFAULT_UI_PORT
from .webui import WebUIServer
def _probe_port_free(port: int) -> bool:
"""启动时预检端口占用 [题面 附件1 4.4]:模拟器启动时会检测并保留端口。"""
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as probe:
probe.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
try:
probe.bind(("127.0.0.1", port))
except OSError:
return False
return True
def main(argv=None) -> int:
parser = argparse.ArgumentParser(
prog="referee", description="CUMCM 2026 B 自建评测机(模拟器 + 机器狗接口 + WebUI)")
parser.add_argument("--ui-port", type=int, default=DEFAULT_UI_PORT,
help=f"WebUI 端口(默认 {DEFAULT_UI_PORT},仅监听回环)")
parser.add_argument("--port", type=int, default=DEFAULT_ACTION_PORT,
help=f"机器狗接口端口(默认 {DEFAULT_ACTION_PORT},仅窗口期开放)")
parser.add_argument("--data-dir", type=Path, default=Path("referee-data"),
help="会话日志与真值存档目录(默认 ./referee-data)")
args = parser.parse_args(argv)
for name, port in (("WebUI", args.ui_port), ("机器狗接口", args.port)):
if not _probe_port_free(port):
print(f"[referee] {name}端口 {port} 已被占用;请更换端口后重试 "
f"(接口端口也可在 WebUI 空闲时修改)", file=sys.stderr)
return 2
referee = Referee(action_port=args.port, data_dir=args.data_dir)
ActionInterfaceServer(referee) # 注入接口开关回调
ui = WebUIServer(referee, args.ui_port)
print(f"[referee] WebUI http://127.0.0.1:{args.ui_port}")
print(f"[referee] 机器狗接口 http://127.0.0.1:{args.port}"
f"(测试窗口期开放,数据目录 {args.data_dir})")
try:
ui.serve_forever()
except KeyboardInterrupt:
print("\n[referee] 收到退出信号,关闭评测机")
finally:
ui.shutdown()
return 0
if __name__ == "__main__":
raise SystemExit(main())
+94
View File
@@ -0,0 +1,94 @@
"""机器狗接口的完整边界:HTTP 语法层到业务层的全部规则,与传输方式无关。
依据附件2 第5节:路径精确匹配(404)、仅 POST(405)、Content-Type/
Content-Encoding 校验(415)、请求体上限 65536 字节(413)、严格 JSON
(400),其余交给模拟器状态机(200/400/409)。
同一实现同时服务真实 HTTP 服务器(server.py)与内存直连(参赛队测试
传输),保证两条路径行为一致。
非测试期间、倒计时期间与测试结束后,接口不开放:由会话层控制监听开关;
此处若模拟器已关闭则抛 SimulatorClosed,调用方按"连接直接关闭、无
JSON 体"处理 [题面 附件2 5.3]。
"""
from __future__ import annotations
from dataclasses import dataclass
from .jsonio import RequestFormatError, dumps, parse_request_object
from .rules import ACTION_FIELDS
from .simulator import LocalSimulator
MAX_BODY_BYTES = 65_536 # [题面 附件2 5.1]
MAX_SAFE_READ_BYTES = 1_048_576 # 超限请求最多读 1MB 即断,防内存滥用 [工程]
@dataclass(frozen=True)
class InterfaceResult:
"""一次接口调用的完整结果。response 是解析后的业务响应体。"""
status: int
body: bytes
response: dict | None
def handle_raw(simulator: LocalSimulator, method: str, target: str,
headers, body: bytes, *,
on_request=None) -> InterfaceResult:
"""处理一次原始 HTTP 请求。
simulator: 当前开放会话的模拟器;
target: 原始请求目标,如 "/measure" 或 "/enter?x=1";
headers: (名字, 值) 有序重复列表,未归一化;
body: 原始请求体字节;
on_request: 可选回调 on_request(path, payload, result)。所有形成了
HTTP 响应的请求都会回调(含语法层拒绝;payload 解析失败
时为 None),对应官方界面"指令与反馈"[题面 附件1 4.5]。
模拟器已关闭时抛 SimulatorClosed(连接层关闭,无响应体,无记录)。
"""
simulator.ensure_open()
path = target.split("?", 1)[0].split("#", 1)[0]
def reject(status: int, payload=None) -> InterfaceResult:
response = simulator.error_body(path)
raw = dumps(response).encode("utf-8")
result = InterfaceResult(status, raw, response)
if on_request is not None:
on_request(path, payload, result)
return result
# 1) 路径必须精确,不接受尾随斜线与查询参数 [题面 附件2 5.1]
if path not in ACTION_FIELDS or "?" in target or "#" in target:
return reject(404)
# 2) 已知路径仅接受 POST [题面 附件2 5.3]
if method != "POST":
return reject(405)
# 3) 头校验:Content-Type application/json(只允许 charset=utf-8 参数)、
# Content-Encoding 缺省或 identity;同名重复头直接 415
normalized: dict[str, str] = {}
for name, value in headers:
key = name.lower()
if key in normalized:
return reject(415)
normalized[key] = value.strip().lower()
media = [part.strip() for part in normalized.get("content-type", "").split(";")]
if (media[0] != "application/json" or len(media) > 2
or (len(media) == 2 and media[1].replace(" ", "")
not in ("charset=utf-8", 'charset="utf-8"'))
or normalized.get("content-encoding", "identity") != "identity"):
return reject(415)
# 4) 请求体上限 [题面 附件2 5.1]
if len(body) > MAX_BODY_BYTES:
return reject(413)
# 5) 严格 JSON 解析
try:
payload = parse_request_object(body)
except RequestFormatError:
return reject(400)
status, response = simulator.handle(path, payload)
raw = dumps(response).encode("utf-8")
result = InterfaceResult(status, raw, response)
if on_request is not None:
on_request(path, payload, result)
return result
+110
View File
@@ -0,0 +1,110 @@
"""请求 JSON 严格解析与业务响应规范化序列化。
解析规则 [题面 附件2 5.1]:无 BOM 的 UTF-8 JSON 对象、不能有重复键、
嵌套不超过 16 层;NaN/Infinity 等非法常量拒绝。
序列化规则 [题面 附件2 4.1]:virtual_time_s 按微秒累计后"最多保留 6 位小数,
并删除无意义的末尾零"——整数时刻输出不带小数点(如 ``105``),带小数时刻
输出最短表示(如 ``5282.843``)。字符串输出不转义非 ASCII(响应为 UTF-8)。
"""
from __future__ import annotations
import json
from math import isfinite
class RequestFormatError(ValueError):
"""请求体不是题面要求的合法 JSON 对象(对应 HTTP 400)。"""
def _object_pairs(pairs):
result = {}
for key, value in pairs:
if key in result:
raise RequestFormatError("重复键")
result[key] = value
return result
def _invalid_constant(value):
raise RequestFormatError(f"非法 JSON 常量 {value}")
def _depth(value) -> int:
if isinstance(value, dict):
return 1 + max((_depth(v) for v in value.values()), default=0)
if isinstance(value, list):
return 1 + max((_depth(v) for v in value), default=0)
return 0
def parse_request_object(body: bytes, *, max_depth: int = 16) -> dict:
"""按题面 5.1 解析请求体;任何违规抛 RequestFormatError(HTTP 400)。"""
try:
text = body.decode("utf-8") # UTF-8 失败或带 BOM(\\ufeff)都在此暴露
except UnicodeDecodeError as exc:
raise RequestFormatError("请求体不是无 BOM 的 UTF-8") from exc
try:
payload = json.loads(text, object_pairs_hook=_object_pairs,
parse_constant=_invalid_constant)
except RequestFormatError:
raise
except (ValueError, RecursionError) as exc:
raise RequestFormatError("JSON 语法错误") from exc
if not isinstance(payload, dict):
raise RequestFormatError("请求体必须是 JSON 对象")
if _depth(payload) > max_depth:
raise RequestFormatError("JSON 嵌套超过 16 层")
return payload
def format_number(value) -> str:
"""JSON number 的规范文本:整数不带小数点,浮点取最短往返表示。"""
if isinstance(value, int):
return str(value)
value = float(value)
if not isfinite(value):
raise ValueError(f"无法序列化非有限数 {value!r}")
if value.is_integer() and abs(value) < 2 ** 53:
return str(int(value))
return repr(value)
def dumps(obj) -> str:
"""业务响应序列化:紧凑、非 ASCII 不转义、数字按 format_number 规范化。"""
out: list[str] = []
def write(value) -> None:
if value is True:
out.append("true")
elif value is False:
out.append("false")
elif value is None:
out.append("null")
elif isinstance(value, str):
out.append(json.dumps(value, ensure_ascii=False))
elif isinstance(value, int):
out.append(str(value))
elif isinstance(value, float):
out.append(format_number(value))
elif isinstance(value, dict):
out.append("{")
for index, (key, item) in enumerate(value.items()):
if index:
out.append(",")
out.append(json.dumps(str(key), ensure_ascii=False))
out.append(":")
write(item)
out.append("}")
elif isinstance(value, (list, tuple)):
out.append("[")
for index, item in enumerate(value):
if index:
out.append(",")
write(item)
out.append("]")
else:
raise TypeError(f"不可序列化类型 {type(value).__name__}")
write(obj)
return "".join(out)
+97
View File
@@ -0,0 +1,97 @@
"""评测机服务端规则表:四条指令的字段表、结果码与 HTTP 语义。
依据附件2《模拟器通信接口说明及编程指南》第 5-9 节,全部落成可执行数据;
人类可读版本见 docs/协议规则表与状态机.md(参赛队仓库)。
本表是评测机的权威校验依据,与参赛队客户端侧的字段表是两份独立拷贝,
由参赛队仓库的解耦守卫测试保证不漂移。
"""
from __future__ import annotations
from dataclasses import dataclass
# ---- 结果码 [题面 附件1 2.2/2.3、附件2 7/8/9] --------------------------------
NO_SIGNAL = "no_signal" # 无未清除源 / 超有效接收半径 / 定向覆盖角外
NEAR = "near" # 距离 <=5m 且在覆盖角内;不返回 svd_deg
DIRECTION = "direction" # 有信号;附 svd_deg(含 ±1° 误差,保留两位小数)
MEASURE_RESULTS = (NO_SIGNAL, NEAR, DIRECTION)
CLEAR_SUCCESS = "success" # 20m 内发现并清除;耗时 5s
CLEAR_NO_TARGET = "no_target_in_range" # 未发现(无源/已清除/超20m);耗时 3s
CLEAR_RESULTS = (CLEAR_SUCCESS, CLEAR_NO_TARGET)
EXIT_USER = "user_exit" # /exit 成功响应中的固定退出原因 [题面 附件2 9.2]
# accepted=false 的响应只含这 3 个字段;virtual_time_s 恒为 0 [题面 附件2 5.2]
BASE_RESPONSE_FIELDS = ("accepted", "real_timestamp_ms", "virtual_time_s")
# ---- 请求字段表 [题面 附件2 5.1、6.1、7.1、8.1、9.1] -------------------------
@dataclass(frozen=True)
class FieldSpec:
"""单个请求字段的约束。数值字段由模拟器按整型/浮点细则单独校验。"""
kind: type # str / dict / float(JSON number)
required: bool = True
max_bytes: int | None = None # 字符串字段的 UTF-8 长度上限
COMMON_FIELDS: dict[str, FieldSpec] = {
"arena_id": FieldSpec(str), # 必须逐字节为 "default"
"robot_id": FieldSpec(str, max_bytes=64), # 逐字节等于登录参赛队号
"request_id": FieldSpec(str, max_bytes=128), # 会话内幂等键,新动作用新 ID
}
POSITION_FIELDS: dict[str, FieldSpec] = {
"x": FieldSpec(float), # 有限 JSON number,|x| <= 2_000_000
"y": FieldSpec(float),
}
# 路径 -> 除公共字段外的动作字段;/enter 与 /exit 无附加字段
ACTION_FIELDS: dict[str, dict[str, FieldSpec]] = {
"/enter": {},
"/measure": {"position": FieldSpec(dict), "channel": FieldSpec(float)},
"/clear": {"position": FieldSpec(dict), "channel": FieldSpec(float)},
"/exit": {},
}
ACTION_PATHS = tuple(ACTION_FIELDS)
# channel 写成 JSON number 1.0 且恰为整数也接受;1.5 返回 HTTP 400 [题面 附件2 5.1]
# position 下的未知字段同样 -> HTTP 200 accepted=false [题面 附件2 7.4]
# ---- 响应字段表(accepted=true 时的附加字段)[题面 附件2 6.2/7.2/8.3/9.2] ----
RESPONSE_FIELDS: dict[str, tuple[str, ...]] = {
"/enter": ("max_virtual_duration_s", "max_real_duration_s", "remaining_real_duration_s"),
"/measure": ("measure_result",), # direction 时另有 svd_deg
"/clear": ("clear_result",),
"/exit": ("exit_reason",),
}
# ---- HTTP 状态语义 [题面 附件2 5.3] ------------------------------------------
STATUS_OK = 200
STATUS_BAD_REQUEST = 400 # JSON 语法/重复键/缺字段/类型/频道或坐标范围
STATUS_CONFLICT = 409 # 同 request_id 对应不同动作,或并发不同动作
STATUS_RETRYABLE = (429, 500) # 流量保护/幂等记录上限、服务器内部错误
def http_status_meaning(status: int) -> str:
table = {
200: "动作已执行,或 JSON 合法但被业务拒绝(看 accepted)",
400: "请求结构错误;不占用 request_id,修正后可复用该 ID",
404: "路径未知或不精确(不接受尾随斜线与查询参数)",
405: "已知路径使用了非 POST 方法",
409: "request_id 幂等冲突或并发发送不同动作",
413: "请求体超过 65536 字节",
415: "Content-Type 或 Content-Encoding 不受支持",
429: "连接/流量保护触发,或本局幂等记录达到上限",
500: "模拟器内部错误",
}
return table.get(status, "未知状态码")
+65
View File
@@ -0,0 +1,65 @@
"""评测机场景生成:一局真值(干扰源布设)的数据准备。
q3 模块用全向场景,q4 模块用全向+定向混合场景 [题面 附件1 4.3]。
题面只规定源总数 10..16、频道 1..20、位置在目标圆域内、有效接收半径
[1000,1500] [题面 附件2 1.1/1.3/2.1],未规定概率分布——均匀分布是本
评测机的自建假设 [工程];定向源数量默认 3 也是 [工程] 约定。
支持固定种子复现,供演练复盘与参赛队回归测试。
"""
from __future__ import annotations
import math
from random import Random
from .simulator import Source
from .spec import Spec, DEFAULT_SPEC
def random_omni_scene(seed: int = 20260910, count: int | None = None,
spec: Spec = DEFAULT_SPEC) -> list[Source]:
"""全向源场景 [题面 问题3]:count=None 时源总数在 10..16 均匀随机。
位置在半径 omega_radius_m 圆域内按面积均匀(r = R*sqrt(u)),
有效接收半径在 [r_eff_min, r_eff_max] 均匀,频道从 1..20 无重复抽样。
"""
field = spec.field
rng = Random(seed)
if count is None:
count = rng.randint(field.source_count_min, field.source_count_max)
if not field.channel_min <= count <= field.channel_max:
raise ValueError(f"源数量须在 {field.channel_min}..{field.channel_max}")
channels = rng.sample(range(field.channel_min, field.channel_max + 1), count)
sources = []
for channel in channels:
radius = field.omega_radius_m * math.sqrt(rng.random())
angle = rng.uniform(0.0, 2 * math.pi)
r_eff = rng.uniform(field.r_eff_min_m, field.r_eff_max_m)
sources.append(Source(channel=channel,
x=radius * math.cos(angle),
y=radius * math.sin(angle),
r_eff_m=r_eff))
return sources
def mixed_scene(seed: int = 20260910, count: int | None = None,
directional: int = 3, spec: Spec = DEFAULT_SPEC) -> list[Source]:
"""问题4混合场景:count 个源中 directional 个为定向源 [题面 问题4]。
改造 random_omni_scene 的前 directional 个源:近圆域边界的前两个定向源
朝向取"背离圆心"(发射半平面基本朝向域外,覆盖网格仍保证可发现,
即"朝外边界源不漏"验收情形),其余定向源朝向随机。
"""
sources = list(random_omni_scene(seed=seed, count=count, spec=spec))
directional = min(directional, len(sources))
rng = Random(seed + 1)
for index in range(directional):
source = sources[index]
if index < 2 and len(sources) > 1 and math.hypot(source.x, source.y) > 1700.0:
heading = math.degrees(math.atan2(source.y, source.x)) % 360.0
else:
heading = rng.uniform(0.0, 360.0)
sources[index] = Source(channel=source.channel, x=source.x,
y=source.y, r_eff_m=source.r_eff_m,
directional=True, heading_deg=heading)
return sources
+131
View File
@@ -0,0 +1,131 @@
"""机器狗接口的 HTTP 承载:仅监听本机回环 [题面 附件2 1.5],
仅测试窗口期开放 [题面 附件1 4.4]——非测试期间、5 秒倒计时期间和测试
结束后,连接直接失败属于正常现象 [题面 附件1 4.4]。
业务规则全部在 interface.handle_raw(与传输无关),本模块只负责:
- 套接字生命周期:open(session) 时绑定端口,finish 时关闭;
- 请求体按 Content-Length 读取,超限安全截断;
- 模拟器已关闭时直接关闭连接、不发送任何字节 [题面 附件2 5.3]。
"""
from __future__ import annotations
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from .interface import MAX_BODY_BYTES, MAX_SAFE_READ_BYTES, handle_raw
from .session import LiveSession, Referee
from .simulator import SimulatorClosed
class _ActionHandler(BaseHTTPRequestHandler):
protocol_version = "HTTP/1.1"
server_version = "MCM26B-Referee/1.0"
# ---- 日志静默(界面"指令与反馈"由会话层记录) ----------------------------
def log_message(self, format, *args): # pylint: disable=redefined-builtin(标准库签名)
pass
# ---- 各方法统一走同一边界;未知方法由 do_* 缺失天然 501,这里显式覆盖常用
def do_POST(self):
self._serve("POST")
def do_GET(self):
self._serve("GET")
def do_PUT(self):
self._serve("PUT")
def do_DELETE(self):
self._serve("DELETE")
# ---- 实现
def _read_body(self) -> bytes:
try:
length = int(self.headers.get("Content-Length") or 0)
except ValueError:
length = -1
if length < 0:
self.close_connection = True
return b""
if length > MAX_BODY_BYTES:
# 超限:安全读取上限后即返回(handle_raw 会判 413),不再保持连接
self.close_connection = True
remaining = min(length, MAX_SAFE_READ_BYTES)
while remaining > 0:
chunk = self.rfile.read(min(remaining, 65536))
if not chunk:
break
remaining -= len(chunk)
return b"\0" * (MAX_BODY_BYTES + 1)
body = b""
while len(body) < length:
chunk = self.rfile.read(length - len(body))
if not chunk:
break
body += chunk
return body
def _serve(self, method: str) -> None:
session: LiveSession = self.server.session # type: ignore[attr-defined]
referee: Referee = self.server.referee # type: ignore[attr-defined]
body = self._read_body()
try:
result = handle_raw(
session.simulator, method, self.path,
list(self.headers.items()), body,
on_request=lambda path, payload, res:
referee.record_action(session, method, path, payload, res))
except SimulatorClosed:
# 接口已关闭:不形成 HTTP 响应,直接断开 [题面 附件2 5.3]
self.close_connection = True
return
except Exception: # noqa: BLE001
# 兜底:任何未预期异常按"模拟器内部错误"返回 [题面 附件2 5.3]
error = session.simulator.error_body(self.path)
from .jsonio import dumps
result_body = dumps(error).encode("utf-8")
self._send(500, result_body)
return
self._send(result.status, result.body)
def _send(self, status: int, body: bytes) -> None:
self.send_response(status)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(body)))
self.send_header("Cache-Control", "no-store")
self.end_headers()
self.wfile.write(body)
class ActionInterfaceServer:
"""机器狗接口监听容器。open/close 由会话看门狗在倒计时结束/结束时调用。"""
def __init__(self, referee: Referee):
self.referee = referee
self._httpd: ThreadingHTTPServer | None = None
self._session: LiveSession | None = None
referee.set_interface_callbacks(self.open, self.close)
@property
def is_open(self) -> bool:
return self._httpd is not None
def open(self, session: LiveSession) -> None:
"""开放接口:绑定 127.0.0.1:port 并开始服务 [题面 附件2 1.5 只监听回环]。"""
httpd = ThreadingHTTPServer(("127.0.0.1", self.referee.action_port), _ActionHandler)
httpd.daemon_threads = True
httpd.session = session
httpd.referee = self.referee
self._httpd = httpd
self._session = session
import threading
threading.Thread(target=httpd.serve_forever,
name=f"referee-action-{session.case_code}",
daemon=True).start()
def close(self, _session: LiveSession) -> None:
httpd, self._httpd, self._session = self._httpd, None, None
if httpd is None:
return
httpd.shutdown()
httpd.server_close()
+445
View File
@@ -0,0 +1,445 @@
"""评测机会话状态机:按附件1 §2.5/§4 复现官方测试流程与界面语义。
一局的完整生命周期 [题面 附件1 2.5/4.4/4.5]:
确认开始 -> 数据准备 -> 5秒倒计时 -> 开启 25 分钟测试窗口并开放机器狗接口
-> (机器狗 /enter 后另计 20 分钟程序运行上限,两者取早)
-> 结束(/exit、手工中止、窗口超时、程序超时、虚拟时间超时、技术异常)
-> 关闭接口,显示结束原因;结束后不能复位或继续原测试,只能开始新的一局。
模块语义 [题面 附件1 4.3]:四个测试模块(问题3/4 × 演练/正式);演练不限次数;
正式各 3 次,启动成功即占用机会,正式测试中"中止测试"仍占用机会 [题面 4.5];
正式测试在任何界面和完成提示中都不显示案例真值 [题面 4.6],演练结束后显示
本局干扰源总数、全向数量、定向数量 [题面 4.6]。
指令与反馈区域默认仅显示最新 1000 条,可在 100..5000 调整 [题面 4.5];
界面显示数量不限制请求总数,完整行为仍写入日志 [题面 4.5]。
"""
from __future__ import annotations
import json
import random
import string
import threading
from dataclasses import dataclass, field as dc_field
from pathlib import Path
from time import monotonic, time
from typing import Callable
from . import scenes
from .rules import CLEAR_SUCCESS
from .simulator import LocalSimulator, Source
from .spec import (DEFAULT_SPEC, DEFAULT_ACTION_PORT, Spec)
FORMAL_ATTEMPTS = 3 # [题面 附件1 4.3] 正式测试各 3 次机会
DISPLAY_LIMIT_DEFAULT = 1000 # [题面 附件1 4.5] 指令与反馈默认显示最新 1000 条
DISPLAY_LIMIT_MIN = 100 # [题面 附件1 4.5]
DISPLAY_LIMIT_MAX = 5000 # [题面 附件1 4.5]
FORMAL_LOG_SOFT_LIMIT_BYTES = 2 * 1024 * 1024 # [题面 附件1 4.6] 加密日志上传上限 2MB
CASE_ALPHABET = string.ascii_uppercase.replace("I", "").replace("O", "") \
+ string.digits.replace("0", "").replace("1", "") # [工程] 去易混字符
@dataclass(frozen=True)
class ModuleInfo:
key: str
title: str
formal: bool
problem: int
MODULES: dict[str, ModuleInfo] = {
"q3_practice": ModuleInfo("q3_practice", "问题3演练测试", False, 3),
"q4_practice": ModuleInfo("q4_practice", "问题4演练测试", False, 4),
"q3_formal": ModuleInfo("q3_formal", "问题3正式测试", True, 3),
"q4_formal": ModuleInfo("q4_formal", "问题4正式测试", True, 4),
}
END_REASONS: dict[str, str] = {
"user_exit": "机器狗主动调用 /exit 结束测试",
"manual_abort": "手工中止测试",
"window_timeout": "测试窗口超时(25 分钟)",
"program_timeout": "程序运行超时(/enter 后 20 分钟)",
"virtual_timeout": "虚拟时间超时(100 小时)",
"technical_error": "评测机技术异常",
}
class RefereeError(RuntimeError):
"""界面/API 操作与当前评测机状态冲突或参数非法。"""
def new_case_code(rng: random.Random | None = None) -> str:
"""测试案例编码 [工程]:4×4 大写字母数字(去易混字符),形如官方 XXXX-XXXX-XXXX-XXXX。"""
rng = rng or random.SystemRandom()
return "-".join("".join(rng.choices(CASE_ALPHABET, k=4)) for _ in range(4))
def validate_robot_id(robot_id: str, max_bytes: int = 64) -> str:
"""robot_id 必须 1..64 字节且不含控制/不可见格式字符 [题面 附件2 5.1]。"""
from unicodedata import category
if not isinstance(robot_id, str) or not robot_id:
raise RefereeError("参赛队号必须是非空字符串")
encoded = robot_id.encode("utf-8")
if len(encoded) > max_bytes:
raise RefereeError("参赛队号 UTF-8 长度不得超过 64 字节")
if any(category(c) in ("Cc", "Cf", "Cs") for c in robot_id):
raise RefereeError("参赛队号不能包含控制字符或不可见格式字符")
return robot_id
@dataclass
class LiveSession:
"""一局测试的全部评测机侧状态(真值只存在于此层与日志)。"""
module: ModuleInfo
robot_id: str
case_code: str
seed: int
sources: list[Source]
spec: Spec
countdown_s: float
window_s: float
created_wall_ms: float
phase: str = "countdown" # countdown -> open -> finished
simulator: LocalSimulator | None = None
countdown_end: float = 0.0 # clock 时域
opened_at: float = 0.0
window_deadline: float = 0.0
ended_at: float | None = None
end_reason: str | None = None
feed: list[dict] = dc_field(default_factory=list) # 指令与反馈(上限 FEED 硬顶)
trail: list[dict] = dc_field(default_factory=list) # 已接受动作的轨迹点(全量)
request_count: int = 0
measure_count: int = 0
clear_count: int = 0
clear_success_count: int = 0
log_path: Path | None = None
watchdog: threading.Thread | None = None
class Referee:
"""评测机总控:模块次数、当前会话、接口开关回调、快照与行为日志。"""
def __init__(self, *, action_port: int = DEFAULT_ACTION_PORT,
data_dir: Path | str = "referee-data", spec: Spec = DEFAULT_SPEC,
clock=monotonic, wall_clock=time):
self.action_port = int(action_port)
self.data_dir = Path(data_dir)
self.spec = spec
self._clock = clock
self._wall_clock = wall_clock
self._lock = threading.RLock()
self._attempts = {key: (FORMAL_ATTEMPTS if module.formal else None)
for key, module in MODULES.items()}
self.session: LiveSession | None = None
self.display_limit = DISPLAY_LIMIT_DEFAULT
self._open_interface: Callable[[LiveSession], None] | None = None
self._close_interface: Callable[[LiveSession], None] | None = None
# ---------------------------------------------------------- 服务器接线
def set_interface_callbacks(self, open_fn, close_fn) -> None:
"""由 server.py 注入:open_fn 在倒计时结束开放接口,close_fn 在结束关闭。"""
self._open_interface, self._close_interface = open_fn, close_fn
# ---------------------------------------------------------- 会话控制
def start(self, module_key: str, robot_id: str, *, seed: int | None = None,
source_count: int | None = None, directional_count: int = 3,
countdown_s: float | None = None,
window_s: float | None = None) -> LiveSession:
"""确认开始一局:数据准备 -> 进入倒计时;正式启动成功即占用机会 [题面 4.3]。"""
with self._lock:
current = self.session
if current is not None and current.phase != "finished":
raise RefereeError("已有测试进行中;请先中止或等待结束")
module = MODULES.get(module_key)
if module is None:
raise RefereeError(f"未知测试模块 {module_key!r}")
if module.formal and self._attempts[module_key] <= 0:
raise RefereeError("该模块正式测试机会已用完")
validate_robot_id(robot_id)
field = self.spec.field
countdown_s = field.countdown_s if countdown_s is None else float(countdown_s)
window_s = field.test_window_s if window_s is None else float(window_s)
if countdown_s < 0 or window_s <= 0:
raise RefereeError("倒计时/窗口时长非法")
spec = self.spec
seed = random.SystemRandom().randrange(2 ** 31) if seed is None else int(seed)
if module.problem == 4:
sources = scenes.mixed_scene(seed, source_count, directional_count, spec)
else:
sources = scenes.random_omni_scene(seed, source_count, spec)
session = LiveSession(
module=module, robot_id=robot_id, case_code=new_case_code(),
seed=seed, sources=sources, spec=spec,
countdown_s=countdown_s, window_s=window_s,
created_wall_ms=self._wall_clock() * 1000)
session.countdown_end = self._clock() + countdown_s
if module.formal:
self._attempts[module_key] -= 1 # 启动成功即占用 [题面 4.3]
self._prepare_log_dir(session)
self._log(session, {"event": "session_start",
"case_code": session.case_code,
"module": module.key, "robot_id": robot_id,
"seed": seed, "countdown_s": countdown_s,
"window_s": window_s,
"max_real_duration_s": field.max_real_duration_s,
"truth": self._sources_payload(sources)})
self.session = session
session.watchdog = threading.Thread(
target=self._watchdog_loop, args=(session,),
name=f"referee-watchdog-{session.case_code}", daemon=True)
session.watchdog.start()
return session
def abort(self) -> bool:
"""中止当前测试 [题面 4.5](界面上需两次确认,此处为最终执行)。"""
with self._lock:
session = self.session
if session is None or session.phase not in ("countdown", "open"):
return False
self._finish(session, "manual_abort")
return True
def _watchdog_loop(self, session: LiveSession) -> None:
"""倒计时开启接口;窗口/程序/虚拟超时与 /exit 后自动结束并关闭接口。"""
ticker = threading.Event()
while True:
close_now = False
with self._lock:
if session.phase == "finished":
return
now = self._clock()
if session.phase == "countdown":
if now >= session.countdown_end:
self._open(session)
close_now = session.phase == "finished"
elif session.phase == "open":
simulator = session.simulator
if simulator.exited:
self._finish_locked(session, "user_exit")
close_now = True
elif simulator.virtual_time_s >= simulator.field.max_virtual_duration_s:
self._finish_locked(session, "virtual_timeout")
close_now = True
elif now >= session.window_deadline:
if not simulator.has_pending_action:
self._finish_locked(session, "window_timeout")
close_now = True
# 已登记动作允许完成 [题面 附件2 4.5];完成后再结束
elif (simulator.entered and now >= simulator.real_deadline_s
and not simulator.has_pending_action):
self._finish_locked(session, "program_timeout")
close_now = True
if close_now:
close_fn = self._close_interface
if close_fn is not None:
close_fn(session)
ticker.wait(0.02)
def _open(self, session: LiveSession) -> None:
"""倒计时结束:开放机器狗接口并开启测试窗口 [题面 附件1 2.5/4.4]。
先绑定端口再进入 open:成功前界面与客户端都看不到"接口已开放"。
"""
now = self._clock()
session.simulator = LocalSimulator(
session.sources, robot_id=session.robot_id, spec=session.spec,
error_seed=session.seed, clock=self._clock, wall_clock=self._wall_clock)
session.opened_at = now
session.window_deadline = now + session.window_s
open_fn = self._open_interface
if open_fn is not None:
try:
open_fn(session)
except OSError as exc:
self._finish_locked(session, "technical_error")
self._log(session, {"event": "technical_error", "detail": str(exc)})
return
session.phase = "open"
def _finish_locked(self, session: LiveSession, reason: str) -> None:
"""持锁结束:写终态;接口关闭在锁外执行(回调线程会再取锁)。"""
if session.phase == "finished":
return
session.phase = "finished"
session.end_reason = reason
session.ended_at = self._clock()
self._log(session, {
"event": "session_end", "reason": reason,
"virtual_time_s": (session.simulator.virtual_time_s
if session.simulator else 0.0),
"real_elapsed_s": round(session.ended_at - session.opened_at, 6)
if session.opened_at else 0.0,
"request_count": session.request_count,
"cleared_count": len(session.simulator.cleared) if session.simulator else 0,
})
def _finish(self, session: LiveSession, reason: str) -> None:
with self._lock:
self._finish_locked(session, reason)
close_fn = self._close_interface
if close_fn is not None:
close_fn(session)
# ---------------------------------------------------------- 接口记录
def record_action(self, session: LiveSession, method: str, path: str,
payload: dict, result) -> None:
"""指令与反馈记录 + 全量轨迹 + 行为日志 [题面 附件1 4.5/4.6]。"""
with self._lock:
session.request_count += 1
entry = {"seq": session.request_count,
"wall_ts_ms": int(self._wall_clock() * 1000),
"method": method, "path": path,
"status": result.status,
"request": payload, "response": result.response}
session.feed.append(entry)
if len(session.feed) > DISPLAY_LIMIT_MAX:
del session.feed[:-DISPLAY_LIMIT_MAX]
response = result.response or {}
accepted = response.get("accepted") is True and result.status == 200
if accepted and path in ("/measure", "/clear"):
position = payload.get("position", {})
kind = "measure" if path == "/measure" else "clear"
outcome = response.get("measure_result") or response.get("clear_result")
session.trail.append({
"seq": entry["seq"], "kind": kind,
"x": position.get("x"), "y": position.get("y"),
"channel": payload.get("channel"), "result": outcome})
if kind == "measure":
session.measure_count += 1
else:
session.clear_count += 1
if outcome == CLEAR_SUCCESS:
session.clear_success_count += 1
self._log(session, {"event": "action", **entry})
# ---------------------------------------------------------- 界面快照
def snapshot(self, *, feed_limit: int | None = None,
examiner: bool = False) -> dict:
"""UI 轮询快照。examiner 仅对演练模块生效的"考官视图"(非官方功能):
运行中显示真值位置;正式模块任何情况下都不显示真值 [题面 4.6]。"""
with self._lock:
session = self.session
attempts = dict(self._attempts)
display_limit = self.display_limit
action_port = self.action_port
now = self._clock()
wall_now_ms = int(self._wall_clock() * 1000)
base = {"phase": "idle", "action_port": action_port,
"attempts": attempts,
"display_limit": display_limit,
"wall_ts_ms": wall_now_ms,
"spec": {"omega_radius_m": self.spec.field.omega_radius_m,
"max_virtual_duration_s": self.spec.field.max_virtual_duration_s,
"max_real_duration_s": self.spec.field.max_real_duration_s,
"test_window_s": self.spec.field.test_window_s,
"countdown_s": self.spec.field.countdown_s},
"modules": {key: {"title": m.title, "formal": m.formal,
"attempts_left": attempts[key]}
for key, m in MODULES.items()}}
if session is None:
return base
base.update({
"phase": session.phase, "module": session.module.key,
"module_title": session.module.title,
"formal": session.module.formal, "problem": session.module.problem,
"case_code": session.case_code, "robot_id": session.robot_id,
"seed": session.seed, "request_count": session.request_count,
"measure_count": session.measure_count,
"clear_count": session.clear_count,
"clear_success_count": session.clear_success_count,
"trail": session.trail,
})
if session.phase == "countdown":
base["countdown_remaining_s"] = max(0.0, session.countdown_end - now)
if session.phase == "open" and session.simulator is not None:
simulator = session.simulator
base.update({
"virtual_time_s": simulator.virtual_time_s,
"position": list(simulator.position),
"channel": simulator.channel,
"cleared_channels": sorted(simulator.cleared),
"window_remaining_s": max(0.0, session.window_deadline - now),
"program_remaining_s": (max(0.0, simulator.real_deadline_s - now)
if simulator.entered else None),
})
if session.phase == "finished":
base["end_reason"] = session.end_reason
base["end_reason_text"] = END_REASONS.get(session.end_reason, "未知")
base["virtual_time_s"] = (session.simulator.virtual_time_s
if session.simulator else 0.0)
base["cleared_channels"] = (sorted(session.simulator.cleared)
if session.simulator else [])
base["truth"] = self._truth_view(session, examiner)
limit = display_limit if feed_limit is None else max(0, int(feed_limit))
base["feed"] = session.feed[-limit:]
if session.log_path is not None:
try:
size = session.log_path.stat().st_size
except OSError:
size = 0
base["log"] = {"path": str(session.log_path), "size_bytes": size,
"over_upload_limit": size > FORMAL_LOG_SOFT_LIMIT_BYTES}
return base
def _truth_view(self, session: LiveSession, examiner: bool) -> dict:
"""真值可见性策略 [题面 附件1 4.6]:正式测试任何界面不显示;
演练结束后显示总数/全向/定向;演练运行中仅考官视图可见。"""
if session.module.formal:
return {"revealed": False}
revealed = session.phase == "finished" or examiner
view = {"revealed": revealed}
if revealed:
view.update(self._truth_summary(session.sources))
view["sources"] = self._sources_payload(session.sources)
return view
@staticmethod
def _truth_summary(sources: list[Source]) -> dict:
directional = sum(1 for s in sources if s.directional)
return {"source_count": len(sources),
"omni_count": len(sources) - directional,
"directional_count": directional}
@staticmethod
def _sources_payload(sources: list[Source]) -> list[dict]:
return [{"channel": s.channel, "x": s.x, "y": s.y, "r_eff_m": s.r_eff_m,
"directional": s.directional, "heading_deg": s.heading_deg}
for s in sources]
# ---------------------------------------------------------- 设置与日志
def set_action_port(self, port: int) -> None:
"""修改机器狗接口端口:仅空闲时可改 [题面 附件1 4.4]。"""
with self._lock:
current = self.session
if current is not None and current.phase in ("countdown", "open"):
raise RefereeError("测试进行中不能修改端口;请等待结束或先中止")
if not isinstance(port, int) or not 1024 <= port <= 65535:
raise RefereeError("端口须为 1024..65535 的整数")
self.action_port = port
def set_display_limit(self, limit: int) -> None:
"""指令与反馈显示条数 100..5000 [题面 附件1 4.5];不影响日志完整性。"""
with self._lock:
if not isinstance(limit, int) or not DISPLAY_LIMIT_MIN <= limit <= DISPLAY_LIMIT_MAX:
raise RefereeError(f"显示条数须为 {DISPLAY_LIMIT_MIN}..{DISPLAY_LIMIT_MAX}")
self.display_limit = limit
def _prepare_log_dir(self, session: LiveSession) -> None:
directory = self.data_dir / "sessions" / session.case_code
directory.mkdir(parents=True, exist_ok=True)
session.log_path = directory / "log.jsonl"
meta = {"case_code": session.case_code, "module": session.module.key,
"robot_id": session.robot_id, "seed": session.seed,
"truth": self._sources_payload(session.sources)}
(directory / "meta.json").write_text(
json.dumps(meta, ensure_ascii=False, indent=2), encoding="utf-8")
def _log(self, session: LiveSession, event: dict) -> None:
if session.log_path is None:
return
line = json.dumps(event, ensure_ascii=False, default=str)
with session.log_path.open("a", encoding="utf-8") as handle:
handle.write(line + "\n")
@@ -1,12 +1,11 @@
"""自建本地模拟器:按附件1/2语义复现官方模拟器的可观测行为。 """自建评测机引擎:按附件1/2 语义复现官方模拟器的可观测行为。
用途(P1 验收"自建回放测试"):不依赖官方模拟器即可测试客户端、 与官方实现的边界:复现公开规定的响应字段、耗时、截止、幂等与拒绝语义,
故障恢复与后续策略。真值 Source 只对测试端开放,策略端只能拿到响应。 不复现 TCP 限流、登录与上传生命周期。示向度误差场是测试假设:
同一(源频道,位置)的示向度误差由种子确定性决定,重复测量读数不变
[题面:同一地点误差不变];不同位置误差独立(白噪声假设),不代表官方实现。
与官方实现的边界:本类只承诺"协议可观测行为一致"(响应字段、 真值 Source 只对评测机侧(本包与测试端)开放,参赛队策略端只能拿到响应。
耗时推进、幂等与拒绝语义);示向度误差分布是测试假设,不代表官方实现。
误差场为逐点固定:同一(源频道,位置)的示向度误差由种子确定性决定,
重复测量读数不变 [题面/官方演练实测];不同位置误差独立(白噪声假设)。
""" """
from __future__ import annotations from __future__ import annotations
@@ -15,19 +14,25 @@ import hashlib
import json import json
from dataclasses import dataclass from dataclasses import dataclass
from math import atan2, degrees, hypot, isfinite from math import atan2, degrees, hypot, isfinite
from time import time from time import monotonic, time
from threading import Event, Lock
from unicodedata import category
from copy import deepcopy
from ..config import ProblemConfig from .rules import (ACTION_FIELDS, CLEAR_NO_TARGET, CLEAR_SUCCESS,
from ..protocol.rules import (ACTION_FIELDS, CLEAR_NO_TARGET, CLEAR_RESULTS, COMMON_FIELDS, DIRECTION, EXIT_USER, NEAR,
CLEAR_SUCCESS, COMMON_FIELDS, DIRECTION, NO_SIGNAL, POSITION_FIELDS)
EXIT_USER, MEASURE_RESULTS, NEAR, NO_SIGNAL, from .spec import Spec, DEFAULT_SPEC
POSITION_FIELDS) from .timing import clear_cost, measure_cost
from ..protocol.timing import clear_cost, measure_cost
class SimulatorClosed(Exception):
"""测试结束,机器狗接口此时关闭,不再返回业务响应(连接层直接关闭)。"""
@dataclass(frozen=True) @dataclass(frozen=True)
class Source: class Source:
"""一局真值。每个频道最多一个源 [题面];构造时强制检查。""" """一局真值。每个频道最多一个源 [题面 附件2 1.3];构造时强制检查。"""
channel: int channel: int
x: float x: float
@@ -38,76 +43,158 @@ class Source:
class LocalSimulator: class LocalSimulator:
"""handle(path, payload) -> (http_status, body_dict)。""" """handle 返回状态码及 JSON;测试结束后抛 SimulatorClosed。
默认执行官方时限;stress_mode 允许配置更长预算和窗口 [工程,自检用]。
clock 为单调秒时钟,wall_clock 为 Unix 秒时钟;窗口起点默认为接口开放时刻。
"""
def __init__(self, sources: list[Source], *, robot_id: str = "2026001", def __init__(self, sources: list[Source], *, robot_id: str = "2026001",
config: ProblemConfig = ProblemConfig(), spec: Spec = DEFAULT_SPEC,
error_half_deg: float | None = None, error_seed: int = 20260910, error_half_deg: float | None = None, error_seed: int = 20260910,
rtt_ms: float = 40.0): clock=monotonic, wall_clock=time,
window_started_at: float | None = None, stress_mode: bool = False):
channels = [s.channel for s in sources] channels = [s.channel for s in sources]
if len(channels) != len(set(channels)): if len(channels) != len(set(channels)):
raise ValueError("每个频道最多对应一个干扰源") raise ValueError("每个频道最多对应一个干扰源")
for s in sources: for s in sources:
if not (config.field.channel_min <= s.channel <= config.field.channel_max): if not spec.field.channel_min <= s.channel <= spec.field.channel_max:
raise ValueError("频道超出 1..20") raise ValueError("频道超出 1..20")
if not s.directional and s.heading_deg != 0.0: if not s.directional and s.heading_deg != 0.0:
raise ValueError("全向源不应有朝向") raise ValueError("全向源不应有朝向")
self.sources = list(sources) self.sources = list(sources)
self.robot_id = robot_id self.robot_id = robot_id
self.config = config self.spec = spec
self.field = config.field self.field = spec.field
self.cost = config.cost self.cost = spec.cost
self.error_half_deg = (self.field.bearing_error_half_deg self.error_half_deg = (self.field.bearing_error_half_deg
if error_half_deg is None else error_half_deg) if error_half_deg is None else error_half_deg)
self.error_seed = error_seed self.error_seed = error_seed
self._rtt_ms = rtt_ms self._clock = clock
self._wall_clock = wall_clock
self.stress_mode = stress_mode
self._real_limit = (self.field.max_real_duration_s if stress_mode
else min(1200, self.field.max_real_duration_s))
start = clock() if window_started_at is None else window_started_at
self._deadline = start + (max(self.field.test_window_s, self._real_limit)
if stress_mode else min(1500, self.field.test_window_s))
self._lock = Lock()
self._pending = None
self.closed_reason = None
self.entered = False self.entered = False
self.exited = False self.exited = False
self.position = (0.0, 0.0) self.position = (0.0, 0.0)
self.channel = self.field.channel_min self.channel = self.field.channel_min
self.virtual_time_s = 0.0 self._virtual_us = 0
self.cleared: set[int] = set() self.cleared: set[int] = set()
self._idempotency: dict[str, tuple[str, int, dict]] = {} # id -> (指纹, 状态码, 响应) self._idempotency: dict[str, tuple[str, int, dict]] = {} # id -> (指纹, 状态码, 响应)
# ------------------------------------------------------------------ 主入口 # ------------------------------------------------------------------ 主入口
def handle(self, path: str, payload) -> tuple[int, dict]: def handle(self, path: str, payload) -> tuple[int, dict]:
# 只在登记/发布时持锁;动作执行期间不同请求必须立即得到409。
fingerprint = self._fingerprint(path, payload)
request_id = payload.get("request_id") if isinstance(payload, dict) else None
with self._lock:
self.ensure_open()
if self._pending is not None:
pending = self._pending
if pending["id"] != request_id or pending["fingerprint"] != fingerprint:
return 409, self.error_body(path)
owner = False
else:
pending = {"id": request_id, "fingerprint": fingerprint,
"done": Event(), "result": None, "error": None}
self._pending = pending
self._registered_at = self._clock()
owner = True
if not owner:
pending["done"].wait()
failure: BaseException | None = pending["error"]
if failure is not None:
raise failure
return deepcopy(pending["result"])
try:
result = self._handle(path, payload)
pending["result"] = deepcopy(result)
return result
except BaseException as exc:
pending["error"] = exc
raise
finally:
with self._lock:
try:
self.ensure_open()
except SimulatorClosed:
pass # 已登记动作仍可取得完整响应。
self._pending = None
pending["done"].set()
def ensure_open(self):
if self.closed_reason is None:
if self.exited:
self.closed_reason = "user_exit"
elif self.virtual_time_s >= self.field.max_virtual_duration_s:
self.closed_reason = "virtual_time_limit"
elif self._clock() >= self._deadline:
self.closed_reason = "real_time_limit"
if self.closed_reason is not None:
raise SimulatorClosed(self.closed_reason)
# ------------------------------------------------------------------ 观测用
@property
def real_deadline_s(self) -> float:
"""本局现实截止(clock 时域):min(窗口截止, /enter 成功后程序运行截止)。"""
return self._deadline
@property
def has_pending_action(self) -> bool:
"""是否有已登记、尚未完成的动作(截止前完整到达的动作允许完成)。"""
with self._lock:
return self._pending is not None
def error_body(self, path: str) -> dict:
"""无法形成业务响应时的 JSON 错误体(至少含 3 个基础字段)[题面 附件2 5.3]。"""
return {"accepted": False, "real_timestamp_ms": int(self._wall_clock() * 1000),
"virtual_time_s": 0, "detail": f"{path} 请求被拒绝"}
def _handle(self, path: str, payload) -> tuple[int, dict]:
if path not in ACTION_FIELDS: if path not in ACTION_FIELDS:
return 404, self._reject(path) return 404, self.error_body(path)
if not isinstance(payload, dict): if not isinstance(payload, dict):
return 400, self._reject(path) return 400, self.error_body(path)
# 1) 公共字段存在性与类型(缺失/类型错 -> 400 [题面]) # 1) 公共字段存在性与类型(缺失/类型错 -> 400 [题面 附件2 5.1])
for name, spec in COMMON_FIELDS.items(): for name, field_spec in COMMON_FIELDS.items():
value = payload.get(name) value = payload.get(name)
if not isinstance(value, str) or (spec.max_bytes if (not isinstance(value, str) or not value
and len(value.encode("utf-8")) > spec.max_bytes): or any(category(c) in ("Cc", "Cf", "Cs") for c in value)
return 400, self._reject(path) or (field_spec.max_bytes
if not value: and len(value.encode("utf-8")) > field_spec.max_bytes)):
return 400, self._reject(path) return 400, self.error_body(path)
# 2) 动作字段存在性 -> 400 # 2) 动作字段存在性 -> 400
spec = ACTION_FIELDS[path] action_spec = ACTION_FIELDS[path]
for name in spec: for name in action_spec:
if name not in payload: if name not in payload:
return 400, self._reject(path) return 400, self.error_body(path)
# 3) 未知字段 -> 200 accepted=false(不占用 request_id [题面]) # 3) 未知字段 -> 200 accepted=false(不占用 request_id [题面 附件2 5.1])
known = set(COMMON_FIELDS) | set(spec) known = set(COMMON_FIELDS) | set(action_spec)
if set(payload) != known: if set(payload) != known:
return 200, self._not_accepted() return 200, self._not_accepted()
# 4) arena / robot 不匹配 -> 200 accepted=false(不占用 request_id [题面]) # 4) arena / robot 不匹配 -> 200 accepted=false(不占用 request_id [题面])
if payload["arena_id"] != "default" or payload["robot_id"] != self.robot_id: if payload["arena_id"] != "default" or payload["robot_id"] != self.robot_id:
return 200, self._not_accepted() return 200, self._not_accepted()
# 4.5) position 下的未知字段同样 -> 200 accepted=false [题面 7.4] # 4.5) position 下的未知字段同样 -> 200 accepted=false [题面 附件2 7.4]
if path in ("/measure", "/clear"): if path in ("/measure", "/clear"):
position = payload.get("position") position = payload.get("position")
if isinstance(position, dict) and set(position) - set(POSITION_FIELDS): if isinstance(position, dict) and set(position) - set(POSITION_FIELDS):
return 200, self._not_accepted() return 200, self._not_accepted()
# 5) 幂等:同一 ID 同一内容回放首次响应;不同内容 409 [题面] # 5) 幂等:同一 ID 同一内容回放首次响应;不同内容 409 [题面 附件2 5.3]
request_id = payload["request_id"] request_id = payload["request_id"]
fingerprint = self._fingerprint(path, payload) fingerprint = self._fingerprint(path, payload)
if request_id in self._idempotency: if request_id in self._idempotency:
seen_fp, seen_status, seen_body = self._idempotency[request_id] seen_fp, seen_status, seen_body = self._idempotency[request_id]
if seen_fp == fingerprint: if seen_fp == fingerprint:
return seen_status, dict(seen_body) return seen_status, dict(seen_body)
return 409, self._reject(path) return 409, self.error_body(path)
# 6) 业务状态机 # 6) 业务状态机
status, body = self._execute(path, payload) status, body = self._execute(path, payload)
if status == 200 and body.get("accepted") is True: if status == 200 and body.get("accepted") is True:
@@ -120,30 +207,31 @@ class LocalSimulator:
if self.entered or self.exited: if self.entered or self.exited:
return 200, self._not_accepted() return 200, self._not_accepted()
self.entered = True self.entered = True
self._deadline = min(self._deadline, self._registered_at + self._real_limit)
return 200, self._ok({ return 200, self._ok({
"max_virtual_duration_s": self.field.max_virtual_duration_s, "max_virtual_duration_s": self.field.max_virtual_duration_s,
"max_real_duration_s": self.field.max_real_duration_s, "max_real_duration_s": self._real_limit,
"remaining_real_duration_s": self.field.max_real_duration_s, "remaining_real_duration_s": int(self._deadline - self._registered_at),
}) })
if not self.entered or self.exited: if not self.entered or self.exited:
return 200, self._not_accepted() return 200, self._not_accepted()
if path == "/exit": if path == "/exit":
self.exited = True self.exited = True
return 200, self._ok({"exit_reason": EXIT_USER}) return 200, self._ok({"exit_reason": EXIT_USER})
# /measure 与 /clear 共同的字段校验 -> 400 [题面] # /measure 与 /clear 共同的字段校验 -> 400 [题面 附件2 7.4]
position = payload.get("position") position = payload.get("position")
if not isinstance(position, dict) or not set(POSITION_FIELDS) <= set(position): if not isinstance(position, dict) or not set(POSITION_FIELDS) <= set(position):
return 400, self._reject(path) return 400, self.error_body(path)
for key in ("x", "y"): for key in ("x", "y"):
value = position[key] value = position[key]
if isinstance(value, bool) or not isinstance(value, (int, float)) \ if isinstance(value, bool) or not isinstance(value, (int, float)) \
or not isfinite(value) or abs(value) > self.field.coord_abs_limit_m: or abs(value) > self.field.coord_abs_limit_m or not isfinite(value):
return 400, self._reject(path) return 400, self.error_body(path)
channel = payload.get("channel") channel = payload.get("channel")
if isinstance(channel, bool) or not isinstance(channel, (int, float)) \ if isinstance(channel, bool) or not isinstance(channel, (int, float)) \
or not float(channel).is_integer() \ or not self.field.channel_min <= channel <= self.field.channel_max \
or not self.field.channel_min <= channel <= self.field.channel_max: or not float(channel).is_integer():
return 400, self._reject(path) return 400, self.error_body(path)
channel = int(channel) channel = int(channel)
pos = (float(position["x"]), float(position["y"])) pos = (float(position["x"]), float(position["y"]))
@@ -155,8 +243,8 @@ class LocalSimulator:
breakdown = measure_cost(self.position, pos, self.channel, channel, self.cost) breakdown = measure_cost(self.position, pos, self.channel, channel, self.cost)
result, svd = self._observe(pos, channel) result, svd = self._observe(pos, channel)
self.position = pos self.position = pos
self.channel = channel # 只有合法 /measure 更新频道 [题面] self.channel = channel # 只有合法 /measure 更新频道 [题面 附件2 4.3]
self._advance(breakdown.total_s) self._advance(breakdown.total_us)
body = {"measure_result": result} body = {"measure_result": result}
if result == DIRECTION: if result == DIRECTION:
body["svd_deg"] = svd body["svd_deg"] = svd
@@ -168,10 +256,10 @@ class LocalSimulator:
and hypot(pos[0] - source.x, pos[1] - source.y) and hypot(pos[0] - source.x, pos[1] - source.y)
<= self.field.clear_radius_m) <= self.field.clear_radius_m)
breakdown = clear_cost(self.position, pos, success, self.cost) breakdown = clear_cost(self.position, pos, success, self.cost)
self.position = pos # /clear 不改变频道 [题面] self.position = pos # /clear 不改变频道 [题面 附件1 2.3]
if success: if success:
self.cleared.add(channel) self.cleared.add(channel)
self._advance(breakdown.total_s) self._advance(breakdown.total_us)
result = CLEAR_SUCCESS if success else CLEAR_NO_TARGET result = CLEAR_SUCCESS if success else CLEAR_NO_TARGET
return 200, self._ok({"clear_result": result}) return 200, self._ok({"clear_result": result})
@@ -201,7 +289,8 @@ class LocalSimulator:
坐标按 1e-6 m 量化后与种子、频道一起哈希,映射到 坐标按 1e-6 m 量化后与种子、频道一起哈希,映射到
[-error_half_deg, +error_half_deg);不同位置近似独立(白噪声假设)。 [-error_half_deg, +error_half_deg);不同位置近似独立(白噪声假设)。
""" """
key = f"{self.error_seed}|{channel}|{round(pos[0], 6)}|{round(pos[1], 6)}" x, y = (round(float(value), 6) or 0.0 for value in pos)
key = f"{self.error_seed}|{channel}|{x}|{y}"
digest = hashlib.sha256(key.encode("utf-8")).digest() digest = hashlib.sha256(key.encode("utf-8")).digest()
unit = int.from_bytes(digest[:8], "big") / 2.0 ** 64 unit = int.from_bytes(digest[:8], "big") / 2.0 ** 64
return (2.0 * unit - 1.0) * self.error_half_deg return (2.0 * unit - 1.0) * self.error_half_deg
@@ -217,27 +306,30 @@ class LocalSimulator:
return abs((a - b + 180.0) % 360.0 - 180.0) return abs((a - b + 180.0) % 360.0 - 180.0)
# ------------------------------------------------------------------ 计时与响应 # ------------------------------------------------------------------ 计时与响应
def _advance(self, seconds: float) -> None: def _advance(self, virtual_us: int) -> None:
self.virtual_time_s += seconds self._virtual_us += virtual_us # 微秒整数累计 [题面 附件2 4.1]
@property
def virtual_time_s(self):
return self._virtual_us / 1_000_000
@virtual_time_s.setter
def virtual_time_s(self, seconds):
self._virtual_us = round(seconds * 1_000_000)
def _ok(self, extra: dict) -> dict: def _ok(self, extra: dict) -> dict:
self._rtt_ms += 40.0
body = {"accepted": True, body = {"accepted": True,
"real_timestamp_ms": 1_760_000_000_000.0 + self._rtt_ms, "real_timestamp_ms": int(self._wall_clock() * 1000),
"virtual_time_s": self.virtual_time_s, **extra} "virtual_time_s": self.virtual_time_s, **extra}
return body return body
def _not_accepted(self) -> dict: def _not_accepted(self) -> dict:
return {"accepted": False, "real_timestamp_ms": 1_760_000_000_000.0, return {"accepted": False, "real_timestamp_ms": int(self._wall_clock() * 1000),
"virtual_time_s": 0} # accepted=false 时恒为 0 [题面] "virtual_time_s": 0} # accepted=false 时恒为 0 [题面 附件2 4.1]
def _reject(self, path: str) -> dict:
return {"accepted": False, "real_timestamp_ms": 1_760_000_000_000.0,
"virtual_time_s": 0, "detail": f"{path} 请求被拒绝"}
@staticmethod @staticmethod
def _fingerprint(path: str, payload) -> str: def _fingerprint(path: str, payload) -> str:
canonical = json.dumps({"path": path, "payload": payload}, canonical = json.dumps({"path": path, "payload": payload},
sort_keys=True, ensure_ascii=False, sort_keys=True, ensure_ascii=False,
separators=(",", ":")) separators=(",", ":"))
return hashlib.sha256(canonical.encode("utf-8")).hexdigest() return hashlib.sha256(canonical.encode("utf-8", errors="surrogatepass")).hexdigest()
+64
View File
@@ -0,0 +1,64 @@
"""评测机题面常量:物理世界、协议硬限制与虚拟耗时模型。
依据 issue/B题.pdf、附件1《模拟器使用说明》、附件2《模拟器通信接口说明及编程指南》。
``[题面]`` 标注出自题面原文;未标注或注明 ``[工程]`` 的为本评测机的自建实现约定
(题面未规定、不代表官方实现),全部差异在包 README 的"符合性对照"中列明。
本包与参赛队代码(mcm26b)完全解耦:这里不允许出现任何 mcm26b 导入;
两侧只通过附件2 定义的 HTTP 协议耦合。
"""
from __future__ import annotations
from dataclasses import dataclass
Vec2 = tuple[float, float]
ARENA_ID = "default" # [题面] 附件2 5.1:arena_id 必须是 "default"
DEFAULT_ACTION_PORT = 2026 # [题面] 附件2 1.5:默认 http://127.0.0.1:2026
DEFAULT_UI_PORT = 8026 # [工程] 评测机 WebUI 端口(与机器狗接口分离)
@dataclass(frozen=True)
class FieldSpec:
"""目标区域、干扰源与限时的物理/协议硬限制。[题面]"""
omega_radius_m: float = 1800.0 # 目标区域半径,圆心为原点
channel_min: int = 1 # 有效频道范围 [题面 附件2 1.3]
channel_max: int = 20
source_count_min: int = 10 # 一局源总数范围 [题面 附件2 1.3]
source_count_max: int = 16
r_eff_min_m: float = 1000.0 # 有效接收半径范围 [题面 附件2 2.1]
r_eff_max_m: float = 1500.0
near_radius_m: float = 5.0 # 距离过近阈值,不返回示向度 [题面 附件2 2.4]
clear_radius_m: float = 20.0 # 清除半径,与朝向无关 [题面 附件2 2.4]
bearing_error_half_deg: float = 1.0 # 示向度误差幅度(题设模型)
bearing_decimals: int = 2 # svd_deg 保留两位小数 [题面 附件2 2.3]
coord_abs_limit_m: float = 2_000_000.0 # 坐标绝对值上限,超出 HTTP 400 [题面 附件2 1.1]
directional_half_angle_deg: float = 90.0 # 定向源朝向两侧各 90°,含边界 [题面 附件2 2.2]
max_virtual_duration_s: float = 360_000.0 # 虚拟世界限时 100 小时 [题面 附件1 2.5]
max_real_duration_s: float = 1200.0 # /enter 成功后程序运行上限 20 分钟 [题面 附件1 2.5]
test_window_s: float = 1500.0 # 倒计时结束后的测试窗口 25 分钟 [题面 附件1 2.5]
countdown_s: float = 5.0 # 数据就绪后的倒计时 [题面 附件1 2.5/4.4]
@dataclass(frozen=True)
class CostSpec:
"""虚拟世界动作耗时模型。[题面] 附件1 表1、附件2 第4节。"""
speed_mps: float = 5.0 # 移动速度;耗时=直线距离/速度 [题面]
measure_s: float = 5.0 # 每次检测耗时(无论结果)[题面]
switch_s: float = 1.0 # 任意两频道间切换耗时,仅由合法 /measure 触发 [题面]
clear_success_s: float = 5.0 # 清除成功 = 光学定位3s + 激光清除2s [题面]
clear_fail_s: float = 3.0 # 未发现 = 仅光学定位3s [题面]
@dataclass(frozen=True)
class Spec:
"""评测机配置聚合。模拟器/场景/会话的第一个可选参数,便于测试替换。"""
field: FieldSpec = FieldSpec()
cost: CostSpec = CostSpec()
DEFAULT_SPEC = Spec()
+308
View File
@@ -0,0 +1,308 @@
/* 评测机 WebUI 前端:纯原生 JS,无外部依赖(离线可用)。
界面语义对齐附件1 §4:四模块、5秒倒计时、指令与反馈、中止两次确认、
结束原因、案例编码、演练真值统计、日志导出、空闲可改端口。 */
"use strict";
const $ = (id) => document.getElementById(id);
let state = null; // 最近一次快照
let selectedModule = null; // 待机页选中的模块
let dismissedResult = false; // 结束页点了"开始新一局"后暂回待机视图
let lastCase = null;
// ---------------------------------------------------------------- 轮询
async function poll() {
try {
const examiner = $("examiner").checked ? 1 : 0;
const limit = state ? state.display_limit : 1000;
const res = await fetch(`/ui/api/state?feed_limit=${limit}&examiner=${examiner}`);
state = await res.json();
render();
} catch (err) { /* 界面服务器未就绪时静默重试 */ }
}
setInterval(poll, 500);
poll();
// ---------------------------------------------------------------- 渲染分发
function showView(id) {
for (const v of ["setup", "countdown", "running", "finished"]) {
$(`view-${v}`).classList.toggle("hidden", v !== id);
}
}
function render() {
if (!state) return;
$("action-port").textContent = state.action_port;
$("ui-port").textContent = location.port;
$("iface-state").textContent = state.phase === "open" ? "接口已开放" : "接口未开放";
$("iface-state").className = `chip ${state.phase === "open" ? "ok" : "gray"}`;
if (state.phase === "idle") { renderSetup(); return; }
if (state.phase === "countdown") { renderCountdown(); return; }
if (state.phase === "open") { renderRunning(); return; }
// finished:用户点过"开始新一局"则暂回待机视图
if (dismissedResult && lastCase === state.case_code) { renderSetup(); return; }
renderFinished();
}
// ---------------------------------------------------------------- 待机
function renderSetup() {
showView("setup");
$("port-input").value = state.action_port;
$("display-limit").value = state.display_limit;
const wrap = $("modules");
wrap.innerHTML = "";
for (const [key, mod] of Object.entries(state.modules)) {
const card = document.createElement("div");
card.className = "module" + (selectedModule === key ? " selected" : "");
const quota = mod.formal
? (mod.attempts_left > 0 ? `正式测试 · 剩余 ${mod.attempts_left} 次机会`
: "正式测试 · 机会已用完")
: "演练测试 · 不限次数";
card.innerHTML = `<div class="name">${mod.title}</div><div class="quota ${mod.formal && mod.attempts_left <= 0 ? "exhausted" : ""}">${quota}</div>`;
card.onclick = () => { selectedModule = key; renderSetup(); };
wrap.appendChild(card);
}
if (selectedModule && !state.modules[selectedModule]) selectedModule = null;
}
$("start-form").addEventListener("submit", async (ev) => {
ev.preventDefault();
const err = $("setup-error");
err.textContent = "";
if (!selectedModule) { err.textContent = "请先选择一个测试模块"; return; }
const mod = state.modules[selectedModule];
if (mod.formal && mod.attempts_left <= 0) { err.textContent = "该模块正式测试机会已用完"; return; }
const robotId = $("robot-id").value.trim();
if (!robotId) { err.textContent = "请填写参赛队号(机器狗接口将校验 robot_id)"; return; }
// [题面 4.3] 正式测试开始前会再次确认;启动成功即占用一次机会
if (mod.formal && !confirm(`即将开始【${mod.title}】。\n启动成功后将占用对应问题的一次正式测试机会(剩余 ${mod.attempts_left} 次)。确认开始?`)) return;
if (mod.formal && !confirm("再次确认:确定现在开始正式测试?")) return;
const body = { module: selectedModule, robot_id: robotId };
const seed = $("seed").value.trim(), count = $("source-count").value.trim();
if (seed !== "") body.seed = Number(seed);
if (count !== "") body.source_count = Number(count);
const res = await fetch("/ui/api/start", {
method: "POST", headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
});
if (!res.ok) { err.textContent = (await res.json()).error || "启动失败"; return; }
dismissedResult = false;
poll();
});
$("btn-config").addEventListener("click", async () => {
const body = {};
if (Number($("display-limit").value) !== state.display_limit) body.display_limit = Number($("display-limit").value);
if (Number($("port-input").value) !== state.action_port) body.action_port = Number($("port-input").value);
if (!Object.keys(body).length) return;
const res = await fetch("/ui/api/config", {
method: "POST", headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
});
if (!res.ok) alert((await res.json()).error || "设置失败");
poll();
});
// ---------------------------------------------------------------- 倒计时
function renderCountdown() {
showView("countdown");
$("count-num").textContent = Math.ceil(state.countdown_remaining_s || 0);
}
// ---------------------------------------------------------------- 进行中
function renderRunning() {
showView("running");
drawChips();
drawMap();
drawFeed();
}
function drawChips() {
const chips = [
["模块", state.module_title],
["案例编码", state.case_code],
["参赛队号", state.robot_id],
];
if (state.virtual_time_s !== undefined) chips.push(["虚拟时刻", fmtV(state.virtual_time_s) + " s"]);
if (state.position) chips.push(["位置", `(${state.position[0]}, ${state.position[1]})`]);
if (state.channel !== undefined) chips.push(["频道", state.channel]);
chips.push(["检测", state.measure_count]);
chips.push(["清除成功", state.clear_success_count]);
if (state.cleared_channels) chips.push(["已清除频道", state.cleared_channels.join(", ") || "—"]);
if (state.window_remaining_s !== undefined) chips.push(["窗口剩余", fmtClock(state.window_remaining_s), "warn"]);
if (state.program_remaining_s !== null && state.program_remaining_s !== undefined)
chips.push(["程序剩余", fmtClock(state.program_remaining_s), "warn"]);
chips.push(["请求数", state.request_count]);
$("status-chips").innerHTML = chips.map(([k, v, cls = ""]) =>
`<span class="chip ${cls}">${k}<b>${escapeHtml(String(v))}</b></span>`).join("");
}
// ---------------------------------------------------------------- 地图
function drawMap() {
const canvas = $("map"), ctx = canvas.getContext("2d");
const size = canvas.width, k = size / 4000, cx = size / 2, cy = size / 2;
const X = (x) => cx + x * k, Y = (y) => cy - y * k;
ctx.clearRect(0, 0, size, size);
// 目标区域 Ω:半径 1800m 圆 [题面]
ctx.strokeStyle = "#33415c"; ctx.lineWidth = 1.5;
ctx.beginPath(); ctx.arc(cx, cy, 1800 * k, 0, Math.PI * 2); ctx.stroke();
ctx.fillStyle = "#33415c"; ctx.font = "11px sans-serif";
ctx.fillText("东 E →", size - 52, cy + 14);
ctx.fillText("北 N ↑", cx + 6, 14);
// 真值(考官视图 / 演练结束)
const truth = state.truth || {};
if (truth.revealed && truth.sources) {
for (const s of truth.sources) {
// 有效接收半径
ctx.strokeStyle = "rgba(244,63,94,.15)";
ctx.beginPath(); ctx.arc(X(s.x), Y(s.y), s.r_eff_m * k, 0, Math.PI * 2); ctx.stroke();
// 定向源覆盖扇区(±90°,含边界)
if (s.directional) {
const a = (s.heading_deg) * Math.PI / 180;
ctx.fillStyle = "rgba(244,63,94,.10)";
ctx.beginPath(); ctx.moveTo(X(s.x), Y(s.y));
ctx.arc(X(s.x), Y(s.y), s.r_eff_m * k, -(a + Math.PI / 2), -(a - Math.PI / 2));
ctx.closePath(); ctx.fill();
}
ctx.fillStyle = "#f43f5e";
ctx.beginPath(); ctx.arc(X(s.x), Y(s.y), 4, 0, Math.PI * 2); ctx.fill();
ctx.fillText(String(s.channel), X(s.x) + 6, Y(s.y) - 6);
}
}
// 轨迹
ctx.strokeStyle = "rgba(230,236,248,.5)"; ctx.lineWidth = 1;
ctx.beginPath();
state.trail.forEach((p, i) => i ? ctx.lineTo(X(p.x), Y(p.y)) : ctx.moveTo(X(p.x), Y(p.y)));
ctx.stroke();
// 动作标记
for (const p of state.trail) {
if (p.kind === "measure") {
ctx.fillStyle = p.result === "no_signal" ? "#6b7280" : p.result === "near" ? "#f59e0b" : "#38bdf8";
ctx.beginPath(); ctx.arc(X(p.x), Y(p.y), 3, 0, Math.PI * 2); ctx.fill();
} else {
ctx.fillStyle = p.result === "success" ? "#34d399" : "#f87171";
ctx.fillRect(X(p.x) - 3.5, Y(p.y) - 3.5, 7, 7);
ctx.strokeStyle = "#0b0f1a"; ctx.strokeRect(X(p.x) - 3.5, Y(p.y) - 3.5, 7, 7);
}
}
// 机器狗当前位置
if (state.position) {
ctx.fillStyle = "#fff";
ctx.beginPath(); ctx.arc(X(state.position[0]), Y(state.position[1]), 4, 0, Math.PI * 2); ctx.fill();
ctx.strokeStyle = "rgba(255,255,255,.5)";
ctx.beginPath(); ctx.arc(X(state.position[0]), Y(state.position[1]), 7, 0, Math.PI * 2); ctx.stroke();
}
// 图例
$("legend").innerHTML = [
["#6b7280", "no_signal"], ["#f59e0b", "near"], ["#38bdf8", "direction"],
["#34d399", "清除 success"], ["#f87171", "清除 no_target"], ["#f43f5e", "干扰源真值(可见时)"],
].map(([c, t]) => `<span><span class="sw" style="background:${c}"></span>${t}</span>`).join("");
}
// ---------------------------------------------------------------- 指令与反馈
function drawFeed() {
const tbody = $("feed").querySelector("tbody");
const rows = (state.feed || []).slice(-300).reverse(); // 显示最新在前;DOM 上限 300 行
tbody.innerHTML = rows.map((e) => {
const cls = e.status >= 500 ? "s5xx" : e.status >= 400 ? "s4xx" : "s200";
return `<tr class="${cls}"><td>${e.seq}</td><td>${fmtTs(e.wall_ts_ms)}</td>` +
`<td>${e.method} ${escapeHtml(e.path)}</td><td>${e.status}</td>` +
`<td class="code" title="${escapeHtml(JSON.stringify(e.request))}">${escapeHtml(sumReq(e))}</td>` +
`<td class="code" title="${escapeHtml(JSON.stringify(e.response))}">${escapeHtml(sumRes(e))}</td></tr>`;
}).join("");
}
function sumReq(e) {
const r = e.request || {};
if (r.position) return `pos=(${r.position.x}, ${r.position.y}) ch=${r.channel} id=${r.request_id}`;
if (r.request_id) return `id=${r.request_id}`;
return "(请求体未通过解析)";
}
function sumRes(e) {
const r = e.response || {};
if (r.accepted !== true) return `accepted=false${r.detail ? " · " + r.detail : ""}`;
let extra = "";
if (r.measure_result !== undefined) extra = r.measure_result + (r.svd_deg !== undefined ? ` svd=${r.svd_deg}°` : "");
else if (r.clear_result !== undefined) extra = r.clear_result;
else if (r.exit_reason !== undefined) extra = r.exit_reason;
else if (r.remaining_real_duration_s !== undefined) extra = `剩余现实 ${r.remaining_real_duration_s}s`;
return `accepted=true v=${fmtV(r.virtual_time_s)}s ${extra}`;
}
// ---------------------------------------------------------------- 结束
function renderFinished() {
showView("finished");
lastCase = state.case_code;
dismissedResult = false;
const truth = state.truth || {};
let stats = "";
if (truth.revealed) {
// [题面 4.6] 演练测试结束后显示:总数 / 全向 / 定向
stats = `<span class="chip ok">干扰源总数<b>${truth.source_count}</b></span>` +
`<span class="chip">全向<b>${truth.omni_count}</b></span>` +
`<span class="chip">定向<b>${truth.directional_count}</b></span>` +
`<span class="chip">已清除<b>${(state.cleared_channels || []).length}</b></span>` +
`<span class="chip warn">虚拟耗时<b>${fmtV(state.virtual_time_s)} s</b></span>`;
} else {
// 正式测试不显示任何案例真值 [题面 4.6]
stats = `<span class="chip gray">正式测试不显示案例真值</span>` +
`<span class="chip">清除成功<b>${state.clear_success_count}</b></span>` +
`<span class="chip warn">虚拟耗时<b>${fmtV(state.virtual_time_s)} s</b></span>`;
}
$("end-banner").innerHTML = `<div class="reason">测试结束:${escapeHtml(state.end_reason_text || "")}</div>` +
`<div class="case">案例编码 ${state.case_code} · 模块 ${state.module_title} · 参赛队号 ${escapeHtml(state.robot_id)}</div>`;
$("end-stats").innerHTML = stats;
const over = state.log && state.log.over_upload_limit;
$("log-size").textContent = state.log
? `日志 ${formatBytes(state.log.size_bytes)}` + (over ? "(超过 2MB 上限,正式测试无法上传)" : "")
: "";
$("log-size").style.color = over ? "var(--bad)" : "";
}
$("btn-new").addEventListener("click", () => {
dismissedResult = true;
renderSetup();
});
// ---------------------------------------------------------------- 中止(两次确认)
$("btn-abort").addEventListener("click", () => $("abort-confirm").classList.remove("hidden"));
$("btn-abort-no").addEventListener("click", () => $("abort-confirm").classList.add("hidden"));
$("btn-abort-yes").addEventListener("click", async () => {
$("abort-confirm").classList.add("hidden");
await fetch("/ui/api/abort", { method: "POST", headers: { "Content-Type": "application/json" }, body: "{}" });
poll();
});
// 倒计时页复用同一中止控件
const countdownAbort = document.createElement("button");
countdownAbort.textContent = "中止测试";
countdownAbort.className = "danger";
countdownAbort.onclick = () => $("btn-abort").click();
$("view-countdown").appendChild(countdownAbort);
countdownAbort.addEventListener("click", () => {
// 从倒计时视图打开确认条:临时切换到 running 视图逻辑会复杂,直接用 confirm 两次确认
$("abort-confirm").classList.add("hidden");
if (confirm("确认中止本次测试?") && confirm("再次确认:确定中止测试?")) {
fetch("/ui/api/abort", { method: "POST", headers: { "Content-Type": "application/json" }, body: "{}" }).then(poll);
}
});
$("examiner").addEventListener("change", poll);
// ---------------------------------------------------------------- 工具
function fmtV(v) {
const s = Number(v).toFixed(6).replace(/0+$/, "").replace(/\.$/, "");
return s === "" ? "0" : s;
}
function fmtClock(sec) {
const s = Math.max(0, Math.ceil(sec));
return `${String(Math.floor(s / 60)).padStart(2, "0")}:${String(s % 60).padStart(2, "0")}`;
}
function fmtTs(ms) {
const d = new Date(ms);
return d.toTimeString().slice(0, 8);
}
function formatBytes(n) {
return n >= 1024 * 1024 ? `${(n / 1024 / 1024).toFixed(2)} MB` : `${(n / 1024).toFixed(1)} KB`;
}
function escapeHtml(s) {
return s.replace(/[&<>"']/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" }[c]));
}
+155
View File
@@ -0,0 +1,155 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>评测机 · CUMCM 2026 B(自建)</title>
<link rel="stylesheet" href="/style.css">
</head>
<body>
<header>
<h1>评测机 <span class="sub">CUMCM 2026 B · 自建复现</span></h1>
<div class="conn">机器狗接口 <code id="action-port">—</code> · 界面 <code id="ui-port">—</code> <span id="iface-state" class="chip gray">接口未开放</span></div>
</header>
<nav id="tabs">
<button id="tab-live" class="tab active">实时</button>
<button id="tab-replay" class="tab">回放</button>
</nav>
<main>
<div id="page-live">
<!-- 待机:选择模块 -->
<section id="view-setup" class="view hidden">
<h2>选择测试模块</h2>
<div class="modules" id="modules"></div>
<form id="start-form" autocomplete="off">
<label>参赛队号 <input id="robot-id" required maxlength="64" placeholder="当前登录参赛队号,如 2026001"></label>
<label>场景种子 <input id="seed" type="number" placeholder="留空 = 随机"></label>
<label>源数量 <input id="source-count" type="number" min="1" max="20" placeholder="留空 = 10~16 随机"></label>
<button id="btn-start" type="submit" class="primary">开始测试</button>
</form>
<details class="settings">
<summary>设置</summary>
<div class="settings-row">
<label>指令与反馈显示条数(100~5000,不影响日志完整性)
<input id="display-limit" type="number" min="100" max="5000" value="1000"></label>
<label>机器狗接口端口(默认 2026,仅空闲时可改,只监听本机回环)
<input id="port-input" type="number" min="1024" max="65535"></label>
<button id="btn-config" type="button">保存设置</button>
</div>
</details>
<p class="hint" id="setup-error"></p>
</section>
<!-- 倒计时 -->
<section id="view-countdown" class="view hidden">
<div class="countdown">
<div id="count-num">5</div>
<p>数据准备完成,正在倒计时……倒计时结束后开放机器狗接口并开启测试窗口</p>
</div>
</section>
<!-- 进行中 -->
<section id="view-running" class="view hidden">
<div id="status-chips" class="chips"></div>
<div class="toolbar">
<label class="examiner"><input type="checkbox" id="examiner"> 考官视图·显示真值(非官方功能,仅演练模块)</label>
<button id="btn-abort" class="danger">中止测试</button>
</div>
<div id="abort-confirm" class="confirm hidden">
<span>确认中止本次测试?正式测试中止仍会占用一次机会。</span>
<button id="btn-abort-yes" class="danger">再次确认中止</button>
<button id="btn-abort-no">取消</button>
</div>
<div class="map-wrap">
<canvas id="map" width="560" height="560"></canvas>
<div id="legend"></div>
</div>
<h2>指令与反馈</h2>
<div class="feed-wrap">
<table id="feed">
<thead><tr><th>#</th><th>现实时刻</th><th>指令</th><th>HTTP</th><th>请求</th><th>响应</th></tr></thead>
<tbody></tbody>
</table>
</div>
</section>
<!-- 结束 -->
<section id="view-finished" class="view hidden">
<div id="end-banner" class="end-banner"></div>
<div id="end-stats"></div>
<div class="toolbar">
<a id="btn-log" class="button" href="/ui/api/log">导出行为日志</a>
<span id="log-size" class="hint"></span>
<button id="btn-new" class="primary">开始新一局</button>
</div>
</section>
</div><!-- /page-live -->
<!-- 回放:以 JSONL 行为日志驱动,复现运动与扫描过程 -->
<div id="page-replay" class="hidden">
<section id="view-replay" class="view">
<div class="toolbar">
<select id="rp-session" title="选择要回放的测试案例"></select>
<button id="rp-load" type="button">加载回放</button>
<span class="toolbar-sep"></span>
<button id="rp-open-file" type="button" title="选择本机 JSONL 行为日志回放(也可直接拖到下方画布)">
选择 JSONL 文件</button>
<input type="file" id="rp-file" accept=".jsonl,.json,.log,application/jsonl,application/json,text/plain" class="hidden">
<span id="rp-info" class="hint"></span>
</div>
<div class="replay-stage" id="rp-stage">
<div class="rp-canvas-wrap">
<canvas id="rp-map" width="640" height="640"></canvas>
<div class="rp-zoom">
<button id="rp-zoom-in" title="放大">+</button>
<button id="rp-zoom-out" title="缩小">-</button>
<button id="rp-zoom-reset" title="复位视图">复位</button>
<span id="rp-zoom-label" class="hint">100%</span>
</div>
</div>
<div class="rp-side">
<div id="rp-status" class="chips"></div>
<div id="rp-event" class="rp-event hint">加载一局后开始回放</div>
<div id="rp-legend"></div>
</div>
</div>
<div class="rp-controls">
<button id="rp-start" title="跳到开头">⏮ 开头</button>
<button id="rp-prev" title="上一步(逐步回退)">⏪ 逐步−</button>
<button id="rp-play" title="播放/暂停(空格)">▶ 播放</button>
<button id="rp-next" title="下一步(逐步前进)">逐步+ ⏩</button>
<button id="rp-end" title="跳到结尾">结尾 ⏭</button>
<label>倍速
<select id="rp-speed">
<option value="1">1×</option>
<option value="2">2×</option>
<option value="5">5×</option>
<option value="10" selected>10×</option>
<option value="30">30×</option>
<option value="100">100×</option>
<option value="300">300×</option>
</select>
</label>
<label class="examiner"><input type="checkbox" id="rp-truth" checked> 显示真值</label>
</div>
<div class="rp-progress">
<input id="rp-slider" type="range" min="0" max="1000" value="0">
<div id="rp-ticks"></div>
<div class="rp-times"><span id="rp-t-cur">0 s</span><span id="rp-t-total">0 s</span></div>
</div>
<p class="hint">回放数据来自 JSONL 行为日志(虚拟时刻驱动):可用上方选择器加载本机存档,
或点击/拖拽导入任意 JSONL——同时兼容评测机行为日志(含真值)与参赛队客户端
会话日志(取 accepted 事件,无真值)。移动按直线距离/5m/s 展开,检测以扫描动画
呈现(direction 附实测示向度射线),清除先光学定位再清除;被拒绝的请求不移动
机器人。拖动进度条可任意定位,滚轮缩放,按住拖拽平移。</p>
</section>
</div><!-- /page-replay -->
</main>
<footer>自建评测机:按附件1《模拟器使用说明》与附件2《通信接口说明》复现;与参赛队代码完全解耦,仅通过 HTTP 协议交互。</footer>
<script src="/app.js"></script>
<script src="/replay.js"></script>
</body>
</html>

Some files were not shown because too many files have changed in this diff Show More