Files
mcm-26-b/src/mcm26b/config.py
T
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

146 lines
8.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""全项目参数中心:题面常量、协议限制、会话策略与搜索规划参数。
任何魔法数字只允许在这里出现一次。各模块通过 :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()