第一问建模完成
This commit is contained in:
+249
@@ -0,0 +1,249 @@
|
||||
# B题问题一 交会定位区域的直径与圆覆盖
|
||||
|
||||
问题一可以转化为二维半平面交问题:每次带误差的示向度给出一个前向角域,多次测量的交集就是定位区域。对于非空有界定位多边形,直径等于最远顶点对的距离。**以这个直径为直径的圆不一定覆盖定位区域。**判断是否能覆盖,应检验最远顶点对中点到所有顶点的距离,或直接计算最小包围圆。
|
||||
|
||||
代码、合成算例与官方演练观测均已实现和保存。本解答只解决问题一;在官方问题3演练中采集同一频道的三个检测点,用于检验交会计算,没有执行全域搜索清除任务,未使用正式测试。
|
||||
|
||||
**一 题目假设和模型边界**
|
||||
|
||||
检测点为 \(S_i=(s_{ix},s_{iy})\),测得示向度为 \(\theta_i\),误差半角为 \(\delta=1^\circ\)。角度从正东开始逆时针计算。同一频道对应同一干扰源;位置不变时重复检测不会消除误差。
|
||||
|
||||
本问求附录2交会法所定义的多边形,因此主模型只取前向角域之交,不额外加入1800米圆域或1500米接收上界。这些先验可用于后续搜索定位,但加入圆盘后边界可能包含圆弧,不能仍按原问题的纯多边形处理。本程序也不以图像视窗作为真实边界。
|
||||
|
||||
模拟器示向度保留两位小数,正文规定误差在±1°内。本次主计算使用±1°;如需把独立舍入误差也纳入保守预算,可用 `--rounding-margin-deg 0.005` 显式追加半角,输出会记录实际误差半角。这是额外的工程裕量,不是题面规定。
|
||||
|
||||
**二 从测向角域得到线性约束**
|
||||
|
||||
设二维叉积 \([a,b]=a_xb_y-a_yb_x\),令
|
||||
|
||||
\[
|
||||
u_i^-=(\cos(\theta_i-\delta),\sin(\theta_i-\delta)),\qquad
|
||||
u_i^+=(\cos(\theta_i+\delta),\sin(\theta_i+\delta)).
|
||||
\]
|
||||
|
||||
未知源位置 \(X=(x,y)\) 必须满足
|
||||
|
||||
\[
|
||||
[u_i^-,X-S_i]\ge0,\qquad [u_i^+,X-S_i]\le0.
|
||||
\]
|
||||
|
||||
这两条不等式选择两条边界射线之间的前向角域,而不是无限延伸的两条直线。写为标准形式 \(a_jx+b_jy\le c_j\):
|
||||
|
||||
\[
|
||||
\begin{aligned}
|
||||
\sin(\theta_i-\delta)x-\cos(\theta_i-\delta)y
|
||||
&\le \sin(\theta_i-\delta)s_{ix}-\cos(\theta_i-\delta)s_{iy},\\
|
||||
-\sin(\theta_i+\delta)x+\cos(\theta_i+\delta)y
|
||||
&\le-\sin(\theta_i+\delta)s_{ix}+\cos(\theta_i+\delta)s_{iy}.
|
||||
\end{aligned}
|
||||
\]
|
||||
|
||||
程序另外保留 \(u_i\cdot(X-S_i)\ge0\) 的前向约束。当 \(0<\delta<90^\circ\) 时它是冗余的,但可让 \(\delta=0\) 的扩展情形正确表示射线,而不是整条直线。实现支持0≤δ<90°,题目算例始终使用1°。
|
||||
|
||||
定位区域为
|
||||
|
||||
\[
|
||||
P=\bigcap_{i=1}^{n}W_i=\{X:AX\le b\}.
|
||||
\]
|
||||
|
||||
通过向量而不是简单的角度大小比较处理约束,可自然处理359°与0°跨界问题。
|
||||
|
||||
**三 可复现的算法**
|
||||
|
||||
1. 验证输入为有限数值,将检测点平移至其均值附近并按尺度归一化,减小大坐标带来的抵消误差。
|
||||
2. 构造所有半平面;枚举每对不平行边界直线的交点。
|
||||
3. 只保留满足全部不等式的交点;去重后用单调链算法求凸包,并保留点、线段等退化结果。
|
||||
4. 判断有无非零方向 \(d\) 满足 \(Ad\le0\)。若存在,任一可行点沿该方向无限前进仍可行,区域无界,直径为无穷大。
|
||||
5. 对有界非空区域,枚举凸包顶点对,得到直径及一对最远顶点。
|
||||
6. 用任意一组最远顶点对中点与半径D/2检验圆覆盖;失败时返回圆外顶点及最大超出距离。用Welzl随机增量法求最小包围圆,输出20米清除半径余量,以区分“直径圆覆盖”和“保证清除”。
|
||||
|
||||
非空前向角域的交集不会包含一条双向无限直线,即它是尖的多面集。这保证非空区域存在极点,所以在本模型中可以通过是否存在可行边界交点来判空。无观测时需单独返回整个平面无界,不能依据“没有顶点”误判为空集。
|
||||
|
||||
二维非平凡衰退锥若存在,至少有一条边界方向与某个约束边界平行。因此检查每条边界的两个切向方向是否满足 \(Ad\le0\),即可判定无界性。无界情形中输出的有限顶点仅是区域的极点,不构成完整有限多边形。
|
||||
|
||||
伪代码如下:
|
||||
|
||||
```text
|
||||
输入同一频道的测量列表 M
|
||||
若 M 为空:返回无界
|
||||
H ← 将每次测量转换为前向角域的半平面约束
|
||||
V ← 所有边界对交点中满足 H 的点
|
||||
若 V 为空:返回空集
|
||||
若存在非零 d 满足 H 的齐次约束 A d ≤ 0:返回无界
|
||||
V ← 去重并排序为凸包
|
||||
(D, A, B) ← V 中的最远顶点对
|
||||
O ← (A+B)/2
|
||||
覆盖成立 ← max_{v∈V} ||v−O|| ≤ D/2
|
||||
返回 V、D、A、B、O、覆盖判断和最小包围圆
|
||||
```
|
||||
|
||||
设半平面数为k(本实现k=3n),最终顶点数为m。枚举交点并检验全部约束需要O(k³),直径计算O(m²),无界性检查O(k²)。可行交点排序的代价上界为O(k² log k),临时交点存储上界O(k²)。新增直径圆证据只需O(m)遍历。
|
||||
|
||||
最小包围圆改为Welzl随机增量算法:先打乱点顺序;已有圆包含新点则保留,否则强制新点成为边界点并解更小子问题;二维最多需要3个支撑点。3个强制边界点必须用外接圆,不能误用这3点的无约束最小圆。实现以显式栈代替递归,防止大量点超过Python递归深度。固定随机种子使输出可复现,理论期望线性指随机排列下的期望,不是该固定种子的最坏复杂度保证。对外函数内部也做坐标归一化,最后重新计算所有点到输出圆心的最大距离,保持数值包含性。旧O(m⁴)支撑点枚举保留为独立测试基准。
|
||||
|
||||
半平面交与直径仍采用容易审查的小规模算法,未为少量测点引入更复杂的旋转卡壳。大规模在线数据可后续替换半平面交,不能把MEC局部提速等同于整个求解器线性复杂度。
|
||||
|
||||
**四 为什么只枚举顶点对就能得到直径**
|
||||
|
||||
有界凸多边形内任意两点可表示为顶点的凸组合:
|
||||
|
||||
\[
|
||||
x=\sum_i\lambda_i v_i,\quad y=\sum_j\mu_jv_j,\quad
|
||||
\lambda_i,\mu_j\ge0,\quad\sum_i\lambda_i=\sum_j\mu_j=1.
|
||||
\]
|
||||
|
||||
由三角不等式,
|
||||
|
||||
\[
|
||||
\|x-y\|=\left\|\sum_{i,j}\lambda_i\mu_j(v_i-v_j)\right\|
|
||||
\le\sum_{i,j}\lambda_i\mu_j\|v_i-v_j\|
|
||||
\le\max_{i,j}\|v_i-v_j\|.
|
||||
\]
|
||||
|
||||
最远顶点对本身又属于区域,因此上界可以达到:
|
||||
|
||||
\[
|
||||
\boxed{D(P)=\max_{i,j}\|v_i-v_j\|.}
|
||||
\]
|
||||
|
||||
点的直径为0,线段的直径为端点距离。对空集,本程序约定直径未定义,输出null;对无界区域,输出 `diameter_infinite=true`,不把Infinity写入不兼容标准的JSON数值。
|
||||
|
||||
**五 直径圆能否覆盖的充分必要条件**
|
||||
|
||||
取任意最远顶点对A、B,距离为D,令O=(A+B)/2。如果半径D/2的某个圆能够同时包含A和B,则
|
||||
|
||||
\[
|
||||
D=\|A-B\|\le\|A-O'\|+\|O'-B\|\le D.
|
||||
\]
|
||||
|
||||
两个不等式只能同时取等号,因此其圆心只能是O。又因为圆盘是凸集,包含全部顶点等价于包含整个多边形。所以
|
||||
|
||||
\[
|
||||
\boxed{\text{存在直径为D的覆盖圆}\iff
|
||||
\max_{v\in V}\|v-(A+B)/2\|\le D/2.}
|
||||
\]
|
||||
|
||||
不必尝试移动直径圆,也不必尝试其它最远点对:若这个唯一可能的圆心不满足覆盖,就不存在直径为D的覆盖圆。最小包围圆半径ρ满足ρ≥D/2,当且仅当ρ=D/2时存在这样的覆盖圆。
|
||||
|
||||
代码的 `diameter_circle.max_excess_m` 为maxᵥ‖v−O‖−D/2;超出数值容差时返回 `witness_vertex` 和从0开始的 `outside_vertex_indices`。因此输出既有判断,也有可独立复核的反例。判定中使用 `coverage_tolerance_m`,边界附近的布尔结果属于浮点容差下结论。
|
||||
|
||||
保证清除使用MEC半径加数值余量εₘ,再比较20米。`clearance_certificate.remaining_margin_m=20−(ρ+εₘ)`;非负时支持当前圆心清除,负数时不作此保证。这里的证书以观测误差界正确、真源属于P和浮点几何计算可靠为前提,不是实际清除成功记录。数学上所有可行清除中心构成∩ᵥB(v,20),MEC圆心是充分选择;后续可在此集合中另选距离机器狗最近的点。
|
||||
|
||||
**六 满足题设±1°的具体反例**
|
||||
|
||||
取三个检测点及示向度:
|
||||
|
||||
| 检测点 | x(米) | y(米) | 示向度 |
|
||||
| --- | ---: | ---: | ---: |
|
||||
| S1 | −1000 | 0 | 1° |
|
||||
| S2 | 540 | −500√3 | 121° |
|
||||
| S3 | 520 | 520√3 | 241° |
|
||||
|
||||
三个角域的交集恰为等边三角形,顶点是(0,0)、(40,0)、(20,20√3)。每个角域的一条边界对应三角形的一条边,另一条边界在三角形外侧,不切去三角形内部。可将真实源设在三角形中心(20,20√3/3),其真实方位与上述读数的偏差均小于1°,且三个检测距离都小于1500米。因此这不是脱离测向模型的任意图形反例。
|
||||
|
||||
它的直径D=40米;直径圆半径20米。以底边两端为最远点对,圆心为(20,0),到第三个顶点的距离是20√3≈34.6410米,因此不能覆盖。其最小包围圆为
|
||||
|
||||
\[
|
||||
O_*=(20,20\sqrt3/3),\qquad \rho_*=40/\sqrt3\approx23.0940\text{米}>20\text{米}.
|
||||
\]
|
||||
|
||||

|
||||
|
||||
图中蓝色为定位区域,红色虚线为直径圆,绿色实线为最小包围圆。黑色加号只在合成场景中表示已知真源。
|
||||
|
||||
**七 官方演练数据验证**
|
||||
|
||||
2026年9月10日,界面明确为“问题3 演练 测试”后,通过公开HTTP接口采集。案例编码为 `U5ZW-P9KN-TCAG-R7ST`,选取频道9。
|
||||
|
||||
| 检测点 | x(米) | y(米) | 示向度 |
|
||||
| --- | ---: | ---: | ---: |
|
||||
| S1 | 0 | 0 | 131.52° |
|
||||
| S2 | −613.761885 | 391.530775 | 34.92° |
|
||||
| S3 | −314.272134 | 656.683353 | 227.19° |
|
||||
|
||||
重复在S1测量一次仍返回131.52°。这次重复观测只用于核查原地误差固定,没有作为新的独立测点加入模型。
|
||||
|
||||
使用±1°得到四边形,其顶点按逆时针顺序为:
|
||||
|
||||
| 顶点 | x(米) | y(米) |
|
||||
| --- | ---: | ---: |
|
||||
| V1 | −460.698306 | 502.411776 |
|
||||
| V2 | −456.424236 | 497.750714 |
|
||||
| V3 | −440.569893 | 515.476604 |
|
||||
| V4 | −441.370356 | 516.413163 |
|
||||
|
||||
计算结果:D=23.996723米,最远点对为V1和V3;直径圆中心为(−450.634100,508.944190),半径11.998362米,但最远顶点到这个圆心的距离为12.602364米,所以该圆不能覆盖区域。最小包围圆中心为(−449.976955,507.952835),半径12.068583米。
|
||||
|
||||

|
||||
|
||||
共发送14个被接受的请求:1次进入、12次检测、1次退出,最终虚拟时间293.602198秒。日志末尾为 `/exit`、`accepted=true`、`exit_reason=user_exit`;界面显示测试正常结束。没有调用 `/clear`,所以这里不能将演练结束解读为“清除了所有源”。官方没有公开该源的坐标真值,本例验证的是观测交会计算、接口适配和几何自洽性,不声称验证了真实位置误差。
|
||||
|
||||
原始请求响应保存在 `output/q1/practice/*_actions.json`,保留了复算所需的请求ID、位置、频道与时间;三点观测及其计算结果保存在 `*_observations.json`。没有读取或解密模拟器隐藏场景数据。
|
||||
|
||||
**八 运行方式和代码入口**
|
||||
|
||||
核心算法仅用Python标准库,支持Python3.10及以上;本次在Python3.12.14中验证。在项目根目录运行:
|
||||
|
||||
```powershell
|
||||
python -m geometry examples/q1/triangle.json -o output/q1/triangle_recomputed.json
|
||||
python main.py examples/q1/normal.json
|
||||
python -m geometry examples/q1/normal.json --rounding-margin-deg 0.005
|
||||
python -m unittest discover -s tests -v
|
||||
python scripts/build_q1_demo.py
|
||||
python scripts/benchmark_q1.py
|
||||
```
|
||||
|
||||
输入JSON的最小格式:
|
||||
|
||||
```json
|
||||
{
|
||||
"measurements": [
|
||||
{"x": 0, "y": 0, "bearing_deg": 131.52, "half_angle_deg": 1},
|
||||
{"x": -613.7618853216438, "y": 391.53077545248124, "bearing_deg": 34.92},
|
||||
{"x": -314.2721344680094, "y": 656.6833525352392, "bearing_deg": 227.19}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
省略 `half_angle_deg` 时默认为1。输出包含区域状态、顶点、直径、最远点对、直径圆覆盖判断、反例证据、最小包围圆和清除余量。CLI的 `error_policy` 记录追加裕量与每条观测最终半角;默认追加0,不改变输入口径。`tolerance_m` 是归一化容差换算回米的尺度;核心算法不接收真源坐标,样例中的 `target` 只用于合成数据的可视化与测试。空集和无界区域不输出有限圆及清除证书。
|
||||
|
||||
| 文件 | 职责 |
|
||||
| --- | --- |
|
||||
| `geometry/localization.py` | 测向角域、半平面、区域分类、直径和包围圆 |
|
||||
| `geometry/planning.py` | 问题二首次测向的距离保证候选圆盘;独立于问题一模型 |
|
||||
| `geometry/__main__.py`、`main.py` | JSON命令行输入输出 |
|
||||
| `tests/test_geometry.py` | 解析反例、随机几何、独立裁剪对照与退化测试 |
|
||||
| `tests/test_practice_replay.py` | 离线复算演练计时与观测结果,不连接模拟器 |
|
||||
| `tests/test_geometry_certificates.py` | 增量圆与穷举法对照、覆盖证据、清除阈值、舍入选项及首次选点证书 |
|
||||
| `scripts/benchmark_q1.py` | 局部性能对比与官方演练误差界敏感性复算 |
|
||||
| `scripts/capture_practice.py` | 在已经启动且已核验的演练中采集少量测向 |
|
||||
| `scripts/build_q1_demo.py` | 用Python算法生成案例、结果和交互图数据 |
|
||||
| `visualization/q1.template.html` | 可视化源模板;不另写一套求解算法 |
|
||||
| `output/q1/question1.html` | 可直接打开的离线交互式模拟,所有数据已内嵌 |
|
||||
|
||||
如需再次采集,先在模拟器明确启动“问题3演练测试”,等待接口就绪,读取案例编码,然后运行:
|
||||
|
||||
```powershell
|
||||
python scripts/capture_practice.py --robot-id 你的参赛队号 --practice-confirmed --case-code 界面中的演练案例编码
|
||||
```
|
||||
|
||||
接口协议没有返回演练/正式模式的字段,`--practice-confirmed` 是操作者确认已核验界面的显式参数,不能替代界面核查。采集脚本没有启动测试的功能,没有正式模式选项,只会调用 `/enter`、`/measure`、`/exit`。禁止在正式测试会话中运行。
|
||||
|
||||
**九 验证范围和数值限制**
|
||||
|
||||
20项自动化测试通过,包括100组随机有界案例、60组与独立矩形裁剪实现的对照、250组增量圆与穷举圆对照、1500个共圆点及重复点/共线点、平移旋转缩放不变性,以及已保存演练日志的逐动作计时复算。新增测试还覆盖清除距离20米临界值、显式舍入选项、首次安全候选圆盘边界和零半径情形。已测试一组有限但直径超过10亿米的近乎平行测向,确认不会因为显示范围而被截断成小区域。
|
||||
|
||||
本次离线优化报告保存在 `output/q1/optimization_report.json`。同一演练观测的敏感性结果如下,原始记录不改写:
|
||||
|
||||
| 误差半角 | 直径 / 米 | MEC半径 / 米 | 直径圆最大超出 / 米 | 含数值裕量的清除余量 / 米 |
|
||||
| --- | ---: | ---: | ---: | ---: |
|
||||
| 1°(默认题设) | 23.996723 | 12.068583 | 0.604002 | 7.931415 |
|
||||
| 1.005°(额外舍入) | 24.119088 | 12.130377 | 0.608486 | 7.869621 |
|
||||
|
||||
在本机60个共圆点的局部基准中,旧MEC枚举中位约306.5毫秒,新算法约0.096毫秒;4点时旧法约0.022毫秒、新法约0.029毫秒,小规模没有提速优势。这些是特定输入的局部实测,不是普遍倍率保证,也不代表网络、全清闭环或官方成绩。三点演练的完整问题一求解中位约0.099毫秒。运行基准脚本可以重新生成当前机器的报告。
|
||||
|
||||
浮点计算不是精确算术。实现使用归一化位置容差10⁻⁹、平行行列式阈值10⁻¹²和较严格的衰退方向角度容差。极端近乎平行、差异接近机器精度或接近退化的输入可能需要高精度几何谓词;结果接近临界阈值时应做容差敏感性检查,而不把最后若干小数位当作严格证明。正常题设坐标和测向精度下,本次案例远离这些数值极限。
|
||||
|
||||
交互图数据全部由同一Python求解器生成。已在最小DOM测试环境中检查32个“案例/测点数量/视图”组合,未发现缺失控件或NaN等非法图形坐标;并对360与736像素、明暗两种配色的SVG作静态图形检查。浏览器工具对本地文件URL的访问被策略拦截,因此没有声称完成真实浏览器的HTML/CSS布局验证。离线页面和对话内可视化均已生成;静态PNG可作为直接查看的补充。
|
||||
|
||||
几何算法参考:[CGAL最小包围圆定义与Welzl增量方法](https://doc.cgal.org/latest/Bounding_volumes/classCGAL_1_1Min__circle__2.html);[McGill旋转卡壳说明](https://www-cgrl.cs.mcgill.ca/~godfried/research/calipers.html)。本次代码仅用Python标准库,不依赖CGAL。
|
||||
Reference in New Issue
Block a user