越野跑赛道解析与比赛策略
把一份轨迹文件转化为可执行的完赛方案。核心主张:赛道数据必须服务于跑者的具体目标,而非反过来。同一份赛道,对 ITRA 900 和 ITRA 600 的结论完全相反。
强制前置:必须拿到轨迹文件
绝不臆测赛道。 未拿到用户上传的轨迹文件时,直接告知需要文件并停下,询问 GPX 或 KML 的获取方式(跑步 App 导出、表盘网站、赛事官网)。不要基于赛道名称猜测地形。
支持 .gpx .kml .kmz .tcx。用户说"路线/轨迹/赛道文件"即指这些。
第二步:必须问清两件事
用 AskUserQuestion 一次性询问,不要分多轮:
- 目标完赛时间 —— 例如
5:10、5小时10分、310分钟 - ITRA 表现分 —— 例如
760
分支处理
| 用户回答 | 做法 |
|---|---|
| 两者都有 | 单档定制,--target + --itra |
| 只有 ITRA | --itra,脚本按分数推断档位与配速 |
| 只有完赛时间 | --target,档位按时间长度推断 |
| 都没有 | --tiers,输出高中低三档方案对比 |
用户明确表示"知难度仍要冲某目标"时,照做,但必须在报告开头明确写出代价与达成条件——用户有知情权,不可只报喜。
执行流程
SCRIPTS=~/.workbuddy/skills/trail-race-strategy/scripts
PY=/Users/kalochen/.workbuddy/binaries/python/versions/3.13.12/bin/python3
1. 解析赛道
$PY $SCRIPTS/trail_parse.py <轨迹文件> <输出目录>
产出 profile.json。必读终端摘要,确认:距离、爬升、最高点、陡坡占比、数据性质(规划轨迹 or 实测)、切分方式。
若报错"有效轨迹点过少"或格式不支持,把错误原文告知用户并请其换文件。
2. 生成策略
# 单档
$PY $SCRIPTS/race_strategy.py profile.json --target 5:10 --itra 760
# 三档
$PY $SCRIPTS/race_strategy.py profile.json --tiers
# 仅按 ITRA
$PY $SCRIPTS/race_strategy.py profile.json --itra 760
3. 生成报告
$PY $SCRIPTS/make_report.py --profile profile.json --strategy strategy.json \
--title "<赛事名>|<档位>执行方案" --out report.html
# 三档时改用 --tiers strategy_tiers.json
4. 交付
调用 present_files 展示 HTML。正文回复必须包含:总距离/爬升/等效平地距离、目标完赛时间与所需平地配速、地形系数 K、分段打卡表、补给总量、最长无补给区间、以及最关键的 1–2 个风险点。
核心模型
地形系数 K(等效平地位数)
逐 20m 积分坡度→配速时间倍数,K = Σ(0.02km × 倍数)。含义:这条赛道相当于多少公里平地。
三档倍数表(scripts/race_strategy.py 内 GRADE_FACTORS):
| 档位 | >25% | 15–25% | 7–15% | 平地 | −7~−15% | −15~−25% | <−25% |
|---|---|---|---|---|---|---|---|
| elite | 2.8 | 1.9 | 1.4 | 1.0 | 1.15 | 1.5 | 2.0 |
| advanced | 3.2 | 2.0 | 1.45 | 1.0 | 1.25 | 1.7 | 2.4 |
| recreational | 3.4 | 2.2 | 1.5 | 1.0 | 1.35 | 1.8 | 2.6 |
反向用法(更易暴露可行性):用户给目标时间时,平地配速 = 目标分钟 / K。若算出 4.3 min/km 以下,直接告知目标处于极限。
三档定位
| 档位 | 平地配速 | 碳水 | 水 | 钠 | 咖啡因 | CP 停留 | 适用 ITRA |
|---|---|---|---|---|---|---|---|
| elite | 5.0 | 90 g/h | 800 ml/h | 700 mg/h | 300 mg | 2 min | ≥720 |
| advanced | 5.6 | 75 g/h | 650 ml/h | 550 mg/h | 150 mg | 3 min | 600–719 |
| recreational | 6.4 | 60 g/h | 550 ml/h | 450 mg/h | 0 | 4 min | <600 |
三档的总时长通常呈倍数关系(精英 : 进阶 : 完赛 ≈ 1 : 1.3 : 1.8)。时间短的档位补给总量反而更小,但摄入速率更高——不要凭直觉判断。
能耗系数(Minetti 2002)
cost = 155.4g⁵ − 30.4g⁴ − 43.3g³ + 46.3g² + 19.5g + 3.6,g 为小数坡度。平地 = 3.6 J/kg/m,mult = cost / 3.6。>1.30 为高消耗段。
必须写进报告的判断
数据性质分级(不可省略)
读 profile.json 的 meta.nature 与 actuals:
- 实测记录 → 实测配速是最强证据,优先于模型
- 规划轨迹(多次绘制拼接) → 必须在报告中声明;地形几何可靠,但配速结论仅供参考
- 若
actuals非空,在报告中单列"文件内嵌实测片段"表格。这是校准配速预期最可靠的依据,优先于任何理论模型。 注意提醒用户部分片段可能混入驾车或补给间隙造成的异常值。
风险区段识别
- 持续爬升:坡度 >2% 且净爬升 >40m 且长度 >0.3km
- 陡降:坡度 <−10% 且净下降 >40m
对最陡爬升段与最长陡降段分别给出专项策略。三条铁律:
- 陡爬段严禁固体补给 —— 高坡度下躯干前倾,吞咽固体必然引发恶心
- 陡降段严禁固体补给 —— 胃肠震荡,补固体等于补呕吐
- 疲劳状态下的陡降是最主要受伤来源 —— 宁可超时不可受伤
补给站分布决定携带量
算出最长无补给区间(分钟数),该区间决定必须自带的碳水与水量。若该区间超过 100 分钟且内含陡坡,必须在报告中显著提示。
装备结论随难度升级
- 双登山杖:精英档写"必备"(陡坡维持效率的唯一手段),其他档写"强烈建议"
- 越野鞋:必须大齿深抓地,理由是极限陡降段
- 照明:预估完赛时间接近日落时必须提示
常见陷阱(实战踩过)
| 陷阱 | 后果 | 做法 |
|---|---|---|
| 按数组顺序取补给站 | GPX 的 wpt 顺序与赛道顺序无关,里程全错 |
必须用 aids[i].track_km 与 segments[i].a_km 匹配 |
| 坡度单位混淆 | 全部统计归零或放大千倍 | 坡度一律用百分数(5.0 = 5%);_assert_units 做全局极值校验,不能只看前几个采样点(前段常是平缓区会漏判) |
| 时间单位混用 min/h | 完赛时间差 60 倍 | 所有时长字段名带 _min |
| 按原始点索引分段 | 点位疏密不均导致分段错位 | 里程驱动重采样(默认 20m),不依赖原始索引 |
| 只报"最极端坡度" | 漏掉最长的连续陡降 | 风险段同时按"最极端"和"净下降量最大"筛选 |
| 高程平滑过度 | 短陡坎被抹平 | ±160m 窗口是上限,超过会丢失真实风险 |
| 时间戳非单调 | 算出 100 万分钟的平均配速 | 用 _actuals() 切分单调时间段;多次拼接是规划轨迹的典型特征 |
参考资料
需要更深入的生理学或赛事知识时读:
references/physiology.md—— 碳水/水/钠/咖啡因的摄入速率依据、生理上限、补给时机references/terrain.md—— 坡度分类、可跑性判断、地形类型与对应策略、不同 ITRA 水平的期望时间
报告结构约定
无论单档还是三档,HTML 报告应包含:KPI 卡片区 → 高程剖面(Canvas 双轴:海拔 + 坡度,标注补给站与主峰)→ 分段难度总表 → 关键坡段清单 → 风险提示 → 策略正文 → 内嵌实测片段(若有)→ 数据说明与局限。
三档模式下用对比卡片 + 各自的完整表格呈现,让用户直观看到"选哪个档"。