- particles.py: 网格幸存集滤波,Voronoi 裕量保证真源保留(缺席证明/网格清除证书/跳测规则)
- planner/bayes_loop.py: 统一任务循环(站点/定位/清除滚动 2-opt 排序)、期望后验半径选点
- planner/seven.py: 七点覆盖策略族提升为库代码
- cli: mcm26b run --strategy {grid,seven,bayes}
- BayesConfig 参数组;tests +24 项(真源保留/证书/端到端),129 全绿
146 lines
8.6 KiB
Python
146 lines
8.6 KiB
Python
"""全项目参数中心:题面常量、协议限制、会话策略与搜索规划参数。
|
||
|
||
任何魔法数字只允许在这里出现一次。各模块通过 :class:`ProblemConfig` 读取;
|
||
命令行需要覆盖时用 ``dataclasses.replace`` 派生新配置,不散落字面量。
|
||
|
||
来源标注约定:
|
||
- ``[题面]`` 出自 issue/B题.pdf、附件1《模拟器使用说明》、附件2《通信接口说明》原文;
|
||
- ``[工程]`` 本队工程参数,可依演练实测调整,不代表官方规定。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from dataclasses import dataclass
|
||
|
||
Vec2 = tuple[float, float]
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class FieldConfig:
|
||
"""题目物理世界与协议硬限制。[题面]"""
|
||
|
||
omega_radius_m: float = 1800.0 # 目标区域半径,圆心为原点
|
||
channel_min: int = 1 # 有效频道范围
|
||
channel_max: int = 20
|
||
source_count_min: int = 10 # 一局源总数范围(接口不直接告知)
|
||
source_count_max: int = 16
|
||
r_eff_min_m: float = 1000.0 # 有效接收半径下界:no_signal 只能排除该圆盘
|
||
r_eff_max_m: float = 1500.0 # 有效接收半径上界:direction 隐含距离上界
|
||
near_radius_m: float = 5.0 # "距离过近"阈值,不返回示向度
|
||
clear_radius_m: float = 20.0 # 清除半径;与定向朝向无关
|
||
bearing_error_half_deg: float = 1.0 # 示向度误差幅度(题设模型)
|
||
bearing_decimals: int = 2 # 返回示向度小数位
|
||
coord_abs_limit_m: float = 2_000_000.0 # 提交坐标的绝对值上限(超出返回 HTTP 400)
|
||
directional_half_angle_deg: float = 90.0 # 定向源朝向两侧各90度覆盖(含边界)
|
||
max_virtual_duration_s: float = 360_000.0 # 虚拟世界限时(100小时)
|
||
max_real_duration_s: float = 1200.0 # /enter 成功后程序运行上限(20分钟)
|
||
test_window_s: float = 1500.0 # 确认开始后的测试窗口(25分钟)
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class CostConfig:
|
||
"""虚拟世界动作耗时模型。[题面] 附件1表1、附件2第4节。"""
|
||
|
||
speed_mps: float = 5.0 # 移动速度;耗时=直线距离/speed
|
||
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 SessionConfig:
|
||
"""会话与网络策略。arena/robot/request 规则为 [题面],其余为 [工程]。"""
|
||
|
||
arena_id: str = "default" # [题面] 固定值
|
||
base_url: str = "http://127.0.0.1:2026" # [题面] 模拟器默认接口地址
|
||
robot_id_max_bytes: int = 64 # [题面] UTF-8 长度 1..64
|
||
request_id_max_bytes: int = 128 # [题面] UTF-8 长度 1..128
|
||
request_body_max_bytes: int = 65_536 # [题面] 超出返回 HTTP 413
|
||
connect_timeout_s: float = 5.0 # [工程] 连接超时
|
||
read_timeout_s: float = 15.0 # [工程] 读超时;覆盖"服务器已处理但响应丢失"
|
||
max_attempts: int = 3 # [工程] 单动作最多尝试次数(含首次)
|
||
retry_backoff_s: float = 0.25 # [工程] 重试退避基数,按 2^n 放大
|
||
reserve_exit_s: float = 60.0 # [工程] 预留退出与故障恢复的现实秒数
|
||
real_rtt_p95_s: float = 1.0 # [工程] 现实单请求高分位估计,预算核算用
|
||
# [工程] 自检/演练实机预算:题面正式测试程序运行上限 1200s 不可延长;
|
||
# 自检经 --real-budget-s 覆盖 LocalSimulator 返回的 remaining_real_duration_s,
|
||
# 默认 1800s(30 分钟实机),用于验证"限时内持续运行直到找全"的行为。
|
||
self_test_real_budget_s: float = 1800.0
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class SearchConfig:
|
||
"""搜索与覆盖规划参数。[工程] 推导依据见 docs/B题建模分析与实施计划.md。"""
|
||
|
||
grid_step_m: float = 700.0 # P4 覆盖网格步长;单元对角 700*sqrt(2)≈989.95<=1000
|
||
grid_half_cells: int = 3 # i,j ∈ [-3,3],49 点覆盖 [-2100,2100]^2 ⊇ Ω
|
||
fallback_clear_grid_m: float = 20.0 # P5 后备清除网格;10*sqrt(2)<=20
|
||
# 舍入裕量:服务器对示向度保留两位小数,引入 <=0.005° 偏差 [题面];
|
||
# 不加裕量时合法真源可能被角域裁掉,故库默认与 CLI run 默认一致取 0.005 [工程]。
|
||
bearing_rounding_margin_deg: float = 0.005
|
||
# ---- P3/P4 在线定位与闭环参数 [工程] ----
|
||
polygon_sides: int = 192 # 圆盘外切多边形边数;Ω 近似误差 1800(1-cos(π/192))≈0.24m
|
||
witness_spacing_m: float = 60.0 # 最坏后验评估的边界采样间距;选定后再按 1/4 加密复核
|
||
candidate_rings: tuple = (0.0, 0.4, 0.75, 1.0) # 安全接收盘内候选点的半径比例
|
||
candidate_angles: int = 12 # 每环候选方向数
|
||
clear_margin_m: float = 0.5 # 清除证书的额外工程余量(多边形近似误差之外)
|
||
max_localize_steps: int = 8 # 单频道定位阶段允许的追加测量上限
|
||
max_anomalies_per_channel: int = 3 # 单频道异常记录上限,防止异常刷屏
|
||
# ---- P5 定向扩展参数 [工程] ----
|
||
max_blocked_probes: int = 4 # 定位单轮内被朝向遮挡的探点预算
|
||
# (首次选点+对跖复核+2 次环探;达限转后备清除)
|
||
fallback_max_cells: int = 400 # 后备清除格数硬顶;超过则不盲扫(宽候选集继续定位)
|
||
# ---- 持续搜索 [工程,2026-09-11 用户决策] ----
|
||
# 全部频道"已清除或证明不存在"后不提前退出:只复扫未清除频道,
|
||
# 直到清除数达 16、实机预算耗尽或虚拟安全余量不足(扫到限时为止)。
|
||
persistent_search: bool = True
|
||
virtual_reserve_s: float = 10_000.0 # 虚拟时长安全余量:剩余不足时停止发起动作,
|
||
# 避免触及 100 小时上限被模拟器强制结束
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class BayesConfig:
|
||
"""P4b 贝叶斯网格滤波策略参数(问题3新策略 bayes)。[工程]
|
||
|
||
网格粒子滤波的严格性依赖 Voronoi 裕量:真实源所在格心与源的距离
|
||
<= sqrt(2)/2 * particle_spacing_m,所有 kill 判定都内缩该裕量,
|
||
因此幸存集永远包含真源(定理见 docs/algo-3-bayes.md §2)。
|
||
"""
|
||
|
||
particle_spacing_m: float = 25.0 # 网格间距;Voronoi 裕量 ≈ 17.68m;
|
||
# 全域约 1.6 万格/频道,20 频道内存与耗时可控
|
||
# ---- 信息增益评分 [工程] ----
|
||
score_mode: str = "radius" # radius=期望后验半径(直指证书)|
|
||
# entropy=期望 log 幸存占比(信息增益)
|
||
score_subsample: int = 800 # 期望幸存熵估计的粒子子采样规模
|
||
mc_representatives: int = 6 # 假设真值代表点数(均匀抽取幸存格)
|
||
lambda_move_s: float = 0.1 # 选点评分中移动时间的折算系数(分值/秒);
|
||
# 与后验半径同量纲,量级须小否则终局拒走(欠收敛)
|
||
wide_rings: tuple = (1.0, 1.5, 2.0) # 无保证接收盘时的探索环(× PF MEC 半径)
|
||
# ---- 站点选择 [工程] ----
|
||
station_ring_radius_m: float = 1200.0 # 七点环半径;覆盖半径 968.9m 的解析证明
|
||
inner_mass_weight: float = 0.5 # 站点评分中 1000m 内幸存质量的权重
|
||
resolve_bonus: float = 10.0 # "就地可证毕"(no_signal 即缺席/有信号即收敛)
|
||
# 频道的站点评分加成
|
||
station_move_penalty_s: float = 40.0 # 站点评分中移动时间(秒)的折算分母
|
||
# ---- 任务排序 [工程] ----
|
||
two_opt_enabled: bool = True # 开放路径 2-opt 改进(最近邻打底)
|
||
two_opt_passes: int = 3 # 2-opt 最大改进轮数
|
||
# ---- PF 清除证书 [工程] ----
|
||
grid_cert_enabled: bool = True # 允许用幸存集 MEC 颁发清除证书(凸证书之外)
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class ProblemConfig:
|
||
"""聚合配置。各模块函数的第一个默认参数,便于单测替换。"""
|
||
|
||
field: FieldConfig = FieldConfig()
|
||
cost: CostConfig = CostConfig()
|
||
session: SessionConfig = SessionConfig()
|
||
search: SearchConfig = SearchConfig()
|
||
bayes: BayesConfig = BayesConfig()
|
||
|
||
|
||
DEFAULT_CONFIG = ProblemConfig()
|