exp: 端点进度选择——commit2 到达 52.1%/time_ok 35.2%(n=512),ratio2s 0.343→0.285;附带 50Hz 饥饿实测
- select='endpoint'(候选块末态离目标最近)优于 consist:n=512 到达 +1.3pp、time_ok +6.3pp、ratio −0.058 - 负结论:safety_try 1/2(41.4/44.9%)、K=4(47.3%)、DLS 兜底(52.3%/碰撞+1.1pp)、增益调度(47.3%) - JS Runner:endpoint 选择落地、冷启动跳前瞻;starveTicks 指标;commit2 平均空转 0.8–1.3 tick,commit3 可覆盖 - route40 endpoint:12/20 ok、13/20 到达、刹车 40.4%
This commit is contained in:
@@ -477,6 +477,40 @@
|
|||||||
- **结论**:实时配置定为 `dag2 + 解析 J + gain2.2 + commit2 + safety_partial + replan=empty`:n=512 到达 **50.8%**、碰撞 **1.4%**。浏览器端从「0 到达 + 47% 刹车」恢复到 nc 集可达,plan P50 33.5/37.6 ms;P99 仍 61–66 ms,commit=2 的 40 ms 队列会偶发饥饿,需继续压 P99(K/安全候选数/JS 特征)。
|
- **结论**:实时配置定为 `dag2 + 解析 J + gain2.2 + commit2 + safety_partial + replan=empty`:n=512 到达 **50.8%**、碰撞 **1.4%**。浏览器端从「0 到达 + 47% 刹车」恢复到 nc 集可达,plan P50 33.5/37.6 ms;P99 仍 61–66 ms,commit=2 的 40 ms 队列会偶发饥饿,需继续压 P99(K/安全候选数/JS 特征)。
|
||||||
- **下一步**:① Python 测 `safety_try=1`(只认证首选候选)对准确率的影响,若无损则 JS 同步(省多次 collideFree);② K=4/6 的准确率-时延权衡;③ JS `gapMatrix` 同步只算 8 组特征;④ commit=2 异步部署的饥饿率实测。
|
- **下一步**:① Python 测 `safety_try=1`(只认证首选候选)对准确率的影响,若无损则 JS 同步(省多次 collideFree);② K=4/6 的准确率-时延权衡;③ JS `gapMatrix` 同步只算 8 组特征;④ commit=2 异步部署的饥饿率实测。
|
||||||
|
|
||||||
|
## 轮次 28:端点进度选择——时间效率大幅提升(exp: `exp/dagger-deploy`,新历史最佳)
|
||||||
|
|
||||||
|
- **假设**:一致性选择奖励"与上一条计划一致",但失败主因是超时(轮次 13:失败 57% 是超时)。改成直接按候选块末态离目标的距离选(`select='endpoint'`),应提升时间效率与精度。
|
||||||
|
- **判据**:time_ok_1p3 显著上升、到达不降、碰撞 ≤3%。
|
||||||
|
- **配方**:dag2 + 解析 J,gain2.2,commit=2,replan=empty,safety_partial;n=256 → n=512。选择器用 `goal_err(fut[-1])`(只做一次 K 端点 FK,不额外推理)。
|
||||||
|
- **指标(n=256,commit=2)**:
|
||||||
|
|
||||||
|
| 选择器 | 到达 | time_ok_1p3 | 碰撞 | ratio2s | 刹停/题 |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| consist(轮次 27) | 52.0% | 29.7% | 1.6% | 0.329 | 35.3 |
|
||||||
|
| **endpoint** | **52.7%** | **37.5%** | 2.3% | **0.210** | 34.6 |
|
||||||
|
| mix(consist − 0.1·端点mm) | 50.0% | 30.5% | 2.3% | 0.300 | 35.1 |
|
||||||
|
|
||||||
|
- **n=512 复核(endpoint,commit=2)**:**到达 52.1% / time_ok 35.2% / 碰撞 2.0% / ratio2s 0.285 / 刹停 35.7**(consist n=512:50.8% / 28.9% / 1.4% / 0.343 / 35.9)。
|
||||||
|
- **节奏曲线(endpoint,n=256)**:commit=1 → **59.4% / 42.6% / 3.9% / 0.133**;commit=2 → 52.7% / 37.5% / 2.3% / 0.210;commit=3 → 47.7% / 27.0% / 1.2% / 0.245。
|
||||||
|
- **负结论**:
|
||||||
|
- `safety_try=1/2`(只认证前 1/2 条候选):41.4% / 44.9%(全 K 52.0%)——候选兜底对到达贡献 ~10 pp,不能省。
|
||||||
|
- K=4:47.3%(K=8 52.0%)——少采样同样损失到达。
|
||||||
|
- DLS 兜底半步(全拒时朝目标走一步并认证):52.3%(基线 52.0%),碰撞 1.6→2.7%,无收益;重复多步版本碰撞 45.7%(第二步未认证),已回退为单步且默认关闭。
|
||||||
|
- 增益调度(远 2.8/近 2.2):47.3% / time_ok 32.8%,等同 gain2.8 常数;高增益在远处就造成过冲,按距离衰减无效。
|
||||||
|
- **浏览器同步**:`select='endpoint'` 进 Runner/bench(`--select endpoint`),冷启动跳过 8 份前瞻(省 ~80 ms P99,冷启动价值评分被端点排序替代)。nc10 bench(--seg 0 --select endpoint):pos ok 4/5 reached、pose ok 2/4,刹车率 32.1%/25.5%(consist 48.1%/31.6%),planP50 31.9/36.5 ms、P99 63.5/67.5 ms。
|
||||||
|
- **50 Hz 部署口径实测(Runner 增加 `starveTicks`:ceil(planMs/20) − 规划时队列步数)**:
|
||||||
|
|
||||||
|
| 集合(endpoint, commit=2, --seg 0) | 到达 ok/reached | 刹车率 | planP50/P99 | 饥饿 tick/次规划 |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| near_column_pos (10) | 4/5 | 32.7% | 32.0/62.1 ms | 0.87 |
|
||||||
|
| near_column_pose (10) | 2/3 | 25.6% | 36.8/68.0 ms | 0.78 |
|
||||||
|
| route40 (20) | 12/13 | 40.4% | 44.3/68.1 ms | 1.26 |
|
||||||
|
|
||||||
|
- commit=2 的队列只有 40 ms,route 集 planP50 44 ms 已经超过 → 每次规划平均空转 ~1.3 tick(50 Hz 下机械臂会"走两步停一下")。
|
||||||
|
- **commit=3(60 ms 队列)可覆盖 P50 44 ms 与多数 P99**:endpoint n=256 → 47.7% / 27.0% / 1.2% / 0.245;gain2.5 反而降到 45.7% / 24.6%。**连续 50 Hz 口径选 commit=3(47.7%),极限准确率选 commit=2(52.1%,偶发空转)**。
|
||||||
|
- **结论**:**新历史最佳(50 Hz 节奏,n=512)= 端点选择 + 解析 J + gain2.2 + commit=2 + safety_partial:到达 52.1% / time_ok 35.2% / 碰撞 2.0% / ratio2s 0.285**。相对本会话开始的历史最佳(42.2% / 29.1% / 2.9% / 0.270)到达 +9.9 pp、时间效率 +6.1 pp。commit=1 上限 59.4%(碰撞 3.9%,超 3% 线,且规划频率不可实时)。
|
||||||
|
- **下一步**:① 碰撞 2.0% 的来源分解(端点选择使候选更贴障碍?);② 把 `select=endpoint` 写进查看器默认(Runner 默认仍 consist);③ route 集 safety P50 30 ms 是 planP50 的主项,继续压认证成本(候选去重/按需投影已做,剩余在 collideFree 本身)。
|
||||||
|
|
||||||
## 分支清理(2026-10-20)
|
## 分支清理(2026-10-20)
|
||||||
|
|
||||||
- `feat/fb-mix`(8e5431c):反馈混合方案的中间分支,**已验证是 `exp/dagger-deploy` 的祖先**(所有提交都在后者历史里),本地/远端分支删除,内容保留在 exp/dagger-deploy。
|
- `feat/fb-mix`(8e5431c):反馈混合方案的中间分支,**已验证是 `exp/dagger-deploy` 的祖先**(所有提交都在后者历史里),本地/远端分支删除,内容保留在 exp/dagger-deploy。
|
||||||
|
|||||||
+7
-2
@@ -40,9 +40,14 @@ def main():
|
|||||||
ap.add_argument('--commit', type=int, default=3)
|
ap.add_argument('--commit', type=int, default=3)
|
||||||
ap.add_argument('--fb', type=float, default=1.8)
|
ap.add_argument('--fb', type=float, default=1.8)
|
||||||
ap.add_argument('--ki', type=float, default=0.0)
|
ap.add_argument('--ki', type=float, default=0.0)
|
||||||
|
ap.add_argument('--fb_scale', type=float, default=0.0)
|
||||||
|
ap.add_argument('--fb_near', type=float, default=0.2)
|
||||||
|
ap.add_argument('--fb_fallback', type=float, default=0.0)
|
||||||
ap.add_argument('--kdecay', type=float, default=0.9)
|
ap.add_argument('--kdecay', type=float, default=0.9)
|
||||||
ap.add_argument('--kimax', type=float, default=0.5)
|
ap.add_argument('--kimax', type=float, default=0.5)
|
||||||
ap.add_argument('--fb_every', type=int, default=1)
|
ap.add_argument('--fb_every', type=int, default=1)
|
||||||
|
ap.add_argument('--safety_try', type=int, default=0)
|
||||||
|
ap.add_argument('--sel_w', type=float, default=0.1)
|
||||||
ap.add_argument('--select', default='consist')
|
ap.add_argument('--select', default='consist')
|
||||||
ap.add_argument('--replan', default='r3')
|
ap.add_argument('--replan', default='r3')
|
||||||
ap.add_argument('--safety', type=int, default=1, help='1=安全层+部分前缀;0=关')
|
ap.add_argument('--safety', type=int, default=1, help='1=安全层+部分前缀;0=关')
|
||||||
@@ -63,8 +68,8 @@ def main():
|
|||||||
t0 = time.perf_counter()
|
t0 = time.perf_counter()
|
||||||
r = run(model, kin, tasks, a.device, K=a.K, safety=bool(a.safety), seed=a.seed, max_sec=a.max_sec,
|
r = run(model, kin, tasks, a.device, K=a.K, safety=bool(a.safety), seed=a.seed, max_sec=a.max_sec,
|
||||||
safety_partial=(a.commit if (a.safety and a.partial) else 0), commit_n=a.commit,
|
safety_partial=(a.commit if (a.safety and a.partial) else 0), commit_n=a.commit,
|
||||||
select=a.select, replan=a.replan, fb_gain=a.fb, fb_ki=a.ki, fb_decay=a.kdecay, fb_imax=a.kimax,
|
select=a.select, replan=a.replan, fb_gain=a.fb, fb_scale=a.fb_scale, fb_near=a.fb_near, fb_fallback=a.fb_fallback, fb_ki=a.ki, fb_decay=a.kdecay, fb_imax=a.kimax,
|
||||||
fb_every=a.fb_every)
|
fb_every=a.fb_every, safety_try=a.safety_try, sel_w=a.sel_w)
|
||||||
wall = time.perf_counter() - t0
|
wall = time.perf_counter() - t0
|
||||||
print('ckpt=%s n=%d K=%d commit=%d fb=%.2f ki=%.2f select=%s replan=%s safety=%d' %
|
print('ckpt=%s n=%d K=%d commit=%d fb=%.2f ki=%.2f select=%s replan=%s safety=%d' %
|
||||||
(a.ckpt, r['n'], a.K, a.commit, a.fb, a.ki, a.select, a.replan, a.safety))
|
(a.ckpt, r['n'], a.K, a.commit, a.fb, a.ki, a.select, a.replan, a.safety))
|
||||||
|
|||||||
+20
-3
@@ -153,7 +153,7 @@ class TeacherOracle:
|
|||||||
def rollout(model, kin, tasks, device, K=8, safety=False, seed=0, max_sec=None, return_traj=False, use_sub=True, sub_override=None,
|
def rollout(model, kin, tasks, device, K=8, safety=False, seed=0, max_sec=None, return_traj=False, use_sub=True, sub_override=None,
|
||||||
qp0=None, steps=1, z_scale=1.0, select='consist', oracle=None, replan='r3', sub_rule='cross',
|
qp0=None, steps=1, z_scale=1.0, select='consist', oracle=None, replan='r3', sub_rule='cross',
|
||||||
fb_gain=0.0, fb_scale=0.0, fb_near=0.2, fb_fallback=0.0, safety_partial=0, commit_n=3, collect=None,
|
fb_gain=0.0, fb_scale=0.0, fb_near=0.2, fb_fallback=0.0, safety_partial=0, commit_n=3, collect=None,
|
||||||
fb_exec=0.0, exec_safety=0, fb_ki=0.0, fb_decay=0.9, fb_imax=0.5, fb_every=1):
|
fb_exec=0.0, exec_safety=0, fb_ki=0.0, fb_decay=0.9, fb_imax=0.5, fb_every=1, safety_try=0, sel_w=0.1):
|
||||||
"""use_sub=False:不用子目标(sf=0);sub_override=(subs [B,M,6] 绝对关节角, n [B] 个数 或 need [B] bool):冷启动时用给定途经点代替子目标头;
|
"""use_sub=False:不用子目标(sf=0);sub_override=(subs [B,M,6] 绝对关节角, n [B] 个数 或 need [B] bool):冷启动时用给定途经点代替子目标头;
|
||||||
qp0 [B,6]:起点的上一帧(默认 = q0,即静止起步)。steps:MeanFlow 采样段数;z_scale:噪声缩放;
|
qp0 [B,6]:起点的上一帧(默认 = q0,即静止起步)。steps:MeanFlow 采样段数;z_scale:噪声缩放;
|
||||||
select:consist(部署口径:冷启动价值前瞻、之后与上一条已提交计划的一致性)/ value(每次都价值前瞻)/ first(不选,取第 0 条)。
|
select:consist(部署口径:冷启动价值前瞻、之后与上一条已提交计划的一致性)/ value(每次都价值前瞻)/ first(不选,取第 0 条)。
|
||||||
@@ -237,6 +237,10 @@ def rollout(model, kin, tasks, device, K=8, safety=False, seed=0, max_sec=None,
|
|||||||
fut = qe[:, None, None] + torch.cumsum(inc[:, :, r:], 2) # [B,K,H-r,6]
|
fut = qe[:, None, None] + torch.cumsum(inc[:, :, r:], 2) # [B,K,H-r,6]
|
||||||
if select == 'first':
|
if select == 'first':
|
||||||
score = torch.zeros(B, K, device=device); score[:, 0] = 1
|
score = torch.zeros(B, K, device=device); score[:, 0] = 1
|
||||||
|
elif select == 'endpoint':
|
||||||
|
qa = fut[:, :, -1].reshape(B * K, 6)
|
||||||
|
e_end, _ = goal_err(kin, qa, T.repeat_interleave(K, 0), R.repeat_interleave(K, 0))
|
||||||
|
score = -e_end.view(B, K)
|
||||||
elif cold or select == 'value':
|
elif cold or select == 'value':
|
||||||
# 短前瞻:候选末态的价值
|
# 短前瞻:候选末态的价值
|
||||||
qa = fut[:, :, -1].reshape(B * K, 6); qb = fut[:, :, -2].reshape(B * K, 6)
|
qa = fut[:, :, -1].reshape(B * K, 6); qb = fut[:, :, -2].reshape(B * K, 6)
|
||||||
@@ -253,6 +257,10 @@ def rollout(model, kin, tasks, device, K=8, safety=False, seed=0, max_sec=None,
|
|||||||
# 2026-10-19(审查 review_closedloop F5):上一次是刹停或 DLS 接管的行没有「已提交计划」,
|
# 2026-10-19(审查 review_closedloop F5):上一次是刹停或 DLS 接管的行没有「已提交计划」,
|
||||||
# 与 JS(lastPlan = null → 一致性项缺席、各候选同分 → 取第 0 条)一致:同分,稳定排序取第 0 条
|
# 与 JS(lastPlan = null → 一致性项缺席、各候选同分 → 取第 0 条)一致:同分,稳定排序取第 0 条
|
||||||
score = torch.where(last_ok[:, None], -(d2 * w).sum(-1), torch.zeros_like(d2[..., 0]))
|
score = torch.where(last_ok[:, None], -(d2 * w).sum(-1), torch.zeros_like(d2[..., 0]))
|
||||||
|
if select == 'mix': # 一致性 + 端点进度(w=sel_w/mm)
|
||||||
|
qa = fut[:, :, -1].reshape(B * K, 6)
|
||||||
|
e_end, _ = goal_err(kin, qa, T.repeat_interleave(K, 0), R.repeat_interleave(K, 0))
|
||||||
|
score = score - sel_w * e_end.view(B, K)
|
||||||
order = torch.sort(score, dim=-1, descending=True, stable=True).indices
|
order = torch.sort(score, dim=-1, descending=True, stable=True).indices
|
||||||
pick = order[:, 0]
|
pick = order[:, 0]
|
||||||
if select == 'average': # 2026-10-20:候选平均(平滑误差)——所有候选置为 K 条动作的均值
|
if select == 'average': # 2026-10-20:候选平均(平滑误差)——所有候选置为 K 条动作的均值
|
||||||
@@ -267,7 +275,8 @@ def rollout(model, kin, tasks, device, K=8, safety=False, seed=0, max_sec=None,
|
|||||||
if safety and safety_partial: # 2026-10-20:允许「部分安全前缀」——执行到第一个不安全步为止,而不是全拒刹停
|
if safety and safety_partial: # 2026-10-20:允许「部分安全前缀」——执行到第一个不安全步为止,而不是全拒刹停
|
||||||
maxlen = torch.zeros(B, dtype=torch.long, device=device)
|
maxlen = torch.zeros(B, dtype=torch.long, device=device)
|
||||||
chosen = torch.zeros(B, commit_n, 6, device=device); chosen_k = pick.clone()
|
chosen = torch.zeros(B, commit_n, 6, device=device); chosen_k = pick.clone()
|
||||||
for kk in range(K_eff):
|
ntry = min(K_eff, safety_try) if safety_try > 0 else K_eff
|
||||||
|
for kk in range(ntry):
|
||||||
ck = inc[ar, order[:, kk], r:r + commit_n]
|
ck = inc[ar, order[:, kk], r:r + commit_n]
|
||||||
pts = qe[:, None] + torch.cumsum(ck, 1)
|
pts = qe[:, None] + torch.cumsum(ck, 1)
|
||||||
mids = torch.cat([qe[:, None], pts[:, :-1]], 1) + 0.5 * ck
|
mids = torch.cat([qe[:, None], pts[:, :-1]], 1) + 0.5 * ck
|
||||||
@@ -281,12 +290,20 @@ def rollout(model, kin, tasks, device, K=8, safety=False, seed=0, max_sec=None,
|
|||||||
if B == 1 and int(maxlen[0]) >= commit_n: break # 实时单题:首个全安全候选即停(安全层 16 点→2-4 点)
|
if B == 1 and int(maxlen[0]) >= commit_n: break # 实时单题:首个全安全候选即停(安全层 16 点→2-4 点)
|
||||||
msk = (torch.arange(commit_n, device=device)[None] < maxlen[:, None])
|
msk = (torch.arange(commit_n, device=device)[None] < maxlen[:, None])
|
||||||
commit = torch.where(msk[..., None], chosen, torch.zeros_like(chosen))
|
commit = torch.where(msk[..., None], chosen, torch.zeros_like(chosen))
|
||||||
|
if fb_gain > 0 and fb_fallback > 0: # 全拒时兜底:半步朝目标(须通过间隙认证),避免原地停满整个周期
|
||||||
|
fb1 = dls_steps(kin, qe, T, R, n=1) * fb_fallback
|
||||||
|
ok1 = kin.gaps(qe + fb1[:, 0], 0.5)['gm'].min(-1).values > 0
|
||||||
|
fbcom = torch.zeros(B, commit_n, 6, device=device); fbcom[:, 0] = fb1[:, 0] # 只走一步(重复会被第二步未经认证的位移带进碰撞)
|
||||||
|
mskf = (maxlen == 0) & ok1
|
||||||
|
commit = torch.where(mskf[:, None, None], fbcom, commit)
|
||||||
|
msk = msk | mskf[:, None]
|
||||||
brakes += (maxlen == 0).float()
|
brakes += (maxlen == 0).float()
|
||||||
pick = chosen_k; committed = maxlen > 0
|
pick = chosen_k; committed = maxlen > 0
|
||||||
elif safety:
|
elif safety:
|
||||||
ok_any = torch.zeros(B, dtype=torch.bool, device=device)
|
ok_any = torch.zeros(B, dtype=torch.bool, device=device)
|
||||||
chosen = torch.zeros(B, commit_n, 6, device=device); chosen_k = pick.clone()
|
chosen = torch.zeros(B, commit_n, 6, device=device); chosen_k = pick.clone()
|
||||||
for kk in range(K_eff):
|
ntry = min(K_eff, safety_try) if safety_try > 0 else K_eff
|
||||||
|
for kk in range(ntry):
|
||||||
ck = inc[ar, order[:, kk], r:r + commit_n]
|
ck = inc[ar, order[:, kk], r:r + commit_n]
|
||||||
pts = qe[:, None] + torch.cumsum(ck, 1)
|
pts = qe[:, None] + torch.cumsum(ck, 1)
|
||||||
mids = torch.cat([qe[:, None], pts[:, :-1]], 1) + 0.5 * ck
|
mids = torch.cat([qe[:, None], pts[:, :-1]], 1) + 0.5 * ck
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ const fs = require('fs'), path = require('path');
|
|||||||
const argv = process.argv.slice(2);
|
const argv = process.argv.slice(2);
|
||||||
const flag = k => argv.includes(k);
|
const flag = k => argv.includes(k);
|
||||||
const opt = k => { const i = argv.indexOf(k); return i >= 0 ? argv[i + 1] : null; };
|
const opt = k => { const i = argv.indexOf(k); return i >= 0 ? argv[i + 1] : null; };
|
||||||
const VALOPT = new Set(['--out', '--nc-n', '--route-n', '--r8-n', '--fb', '--commit', '--seg']);
|
const VALOPT = new Set(['--out', '--nc-n', '--route-n', '--r8-n', '--fb', '--commit', '--seg', '--proj', '--select']);
|
||||||
const pos = argv.filter((a, i) => !a.startsWith('--') && !(i > 0 && VALOPT.has(argv[i - 1])));
|
const pos = argv.filter((a, i) => !a.startsWith('--') && !(i > 0 && VALOPT.has(argv[i - 1])));
|
||||||
const MODEL = pos[0] || path.join(__dirname, '../webapp/models/traj_policy.onnx');
|
const MODEL = pos[0] || path.join(__dirname, '../webapp/models/traj_policy.onnx');
|
||||||
const SETS = (pos[1] || 'nc,route').split(',').concat(flag('--nc1000') ? ['nc1000'] : []);
|
const SETS = (pos[1] || 'nc,route').split(',').concat(flag('--nc1000') ? ['nc1000'] : []);
|
||||||
@@ -66,12 +66,12 @@ const pct = (a, p) => { if (!a.length) return null; const s = a.slice().sort((x,
|
|||||||
const cert = POL.makeCert(S, {});
|
const cert = POL.makeCert(S, {});
|
||||||
const out = { model: MODEL, meta: { fp: meta.fp, eval: meta.eval } };
|
const out = { model: MODEL, meta: { fp: meta.fp, eval: meta.eval } };
|
||||||
const runSet = async (name, cases, mode) => {
|
const runSet = async (name, cases, mode) => {
|
||||||
let ok = 0, reached = 0, colTasks = 0, colFrames = 0, uncert = 0, brakes = 0, locks = 0, plans = 0, recovered = 0, lockTasks = 0, livelock = 0;
|
let ok = 0, reached = 0, colTasks = 0, colFrames = 0, uncert = 0, brakes = 0, locks = 0, plans = 0, recovered = 0, lockTasks = 0, livelock = 0, starve = 0;
|
||||||
const inf = [], saf = [], look = [], planT = [], planW = [], cq = [], times = [], fails = []; let hits = 0; const why = {};
|
const inf = [], saf = [], look = [], planT = [], planW = [], cq = [], times = [], fails = []; let hits = 0; const why = {};
|
||||||
for (let ci = 0; ci < cases.length; ci++) {
|
for (let ci = 0; ci < cases.length; ci++) {
|
||||||
const c = cases[ci];
|
const c = cases[ci];
|
||||||
const R = mode === 'pose' ? c.R : null;
|
const R = mode === 'pose' ? c.R : null;
|
||||||
const r = new POL.Runner(S, infer, { seed: 1000 + ci, actScale: meta.act_scale || 1, fbGain: +opt('--fb') || 0, commit: Math.max(1, +opt('--commit') || 3), segCert: opt('--seg') !== '0' }); // 2026-10-19:动作尺度随模型
|
const r = new POL.Runner(S, infer, { seed: 1000 + ci, actScale: meta.act_scale || 1, fbGain: +opt('--fb') || 0, commit: Math.max(1, +opt('--commit') || 3), segCert: opt('--seg') !== '0', projMax: opt('--proj') === '0' ? 0 : 0.5, select: opt('--select') || 'consist' }); // 2026-10-19:动作尺度随模型
|
||||||
r.reset(c.q0); r.setGoal(c.T, R);
|
r.reset(c.q0); r.setGoal(c.T, R);
|
||||||
let prev = c.q0.slice(), cf = 0, tLock = null;
|
let prev = c.q0.slice(), cf = 0, tLock = null;
|
||||||
for (let k = 0; k < 500 && !r.done; k++) {
|
for (let k = 0; k < 500 && !r.done; k++) {
|
||||||
@@ -86,14 +86,14 @@ const pct = (a, p) => { if (!a.length) return null; const s = a.slice().sort((x,
|
|||||||
const { e, oe } = r.err(r.q);
|
const { e, oe } = r.err(r.q);
|
||||||
const rc = r.done && e < 1 && oe < 0.5;
|
const rc = r.done && e < 1 && oe < 0.5;
|
||||||
if (rc) reached++; if (cf) { colTasks++; colFrames += cf; } if (rc && !cf) { ok++; times.push(r.t); } else fails.push({ ci, err: +e.toFixed(1), oerr: +oe.toFixed(1), colFrames: cf, brakes: r.stats.brakes });
|
if (rc) reached++; if (cf) { colTasks++; colFrames += cf; } if (rc && !cf) { ok++; times.push(r.t); } else fails.push({ ci, err: +e.toFixed(1), oerr: +oe.toFixed(1), colFrames: cf, brakes: r.stats.brakes });
|
||||||
inf.push(...r.stats.inferMs); saf.push(...r.stats.safetyMs); look.push(...r.stats.lookaheadMs); planT.push(...r.stats.planMs); planW.push(...r.stats.planWarmMs); cq.push(...r.stats.certQ); hits += r.stats.certHit;
|
inf.push(...r.stats.inferMs); saf.push(...r.stats.safetyMs); look.push(...r.stats.lookaheadMs); planT.push(...r.stats.planMs); planW.push(...r.stats.planWarmMs); starve += r.stats.starveTicks || 0; cq.push(...r.stats.certQ); hits += r.stats.certHit;
|
||||||
for (const k in r.stats.certWhy) why[k] = (why[k] || 0) + r.stats.certWhy[k]; brakes += r.stats.brakes; locks += r.stats.locks; plans += r.stats.plans;
|
for (const k in r.stats.certWhy) why[k] = (why[k] || 0) + r.stats.certWhy[k]; brakes += r.stats.brakes; locks += r.stats.locks; plans += r.stats.plans;
|
||||||
if (tLock != null) { lockTasks++; if (rc && !cf && r.t - tLock <= 3.0) recovered++; } else if (rc && !cf) recovered++;
|
if (tLock != null) { lockTasks++; if (rc && !cf && r.t - tLock <= 3.0) recovered++; } else if (rc && !cf) recovered++;
|
||||||
if (!rc && r.stats.locks >= 2) livelock++;
|
if (!rc && r.stats.locks >= 2) livelock++;
|
||||||
if ((ci + 1) % 50 === 0) process.stderr.write(`${name} ${ci + 1}/${cases.length}\n`);
|
if ((ci + 1) % 50 === 0) process.stderr.write(`${name} ${ci + 1}/${cases.length}\n`);
|
||||||
}
|
}
|
||||||
out[name] = { n: cases.length, ok, reached, collisionTasks: colTasks, collisionFrames: colFrames, uncertifiedSegs: uncert, brakes, locks,
|
out[name] = { n: cases.length, ok, reached, collisionTasks: colTasks, collisionFrames: colFrames, uncertifiedSegs: uncert, brakes, locks,
|
||||||
plans, brakeRate: plans ? +(brakes / plans).toFixed(4) : null, lockTasks, recovered3s: recovered, recoveryRate: cases.length ? +(recovered / cases.length).toFixed(4) : null, livelock,
|
plans, brakeRate: plans ? +(brakes / plans).toFixed(4) : null, starveTicks: starve, starvePerPlan: plans ? +(starve / plans).toFixed(3) : null, lockTasks, recovered3s: recovered, recoveryRate: cases.length ? +(recovered / cases.length).toFixed(4) : null, livelock,
|
||||||
timeP50: pct(times, 0.5), inferMsP50: pct(inf, 0.5), inferMsP99: pct(inf, 0.99), safetyMsP50: pct(saf, 0.5), safetyMsP99: pct(saf, 0.99),
|
timeP50: pct(times, 0.5), inferMsP50: pct(inf, 0.5), inferMsP99: pct(inf, 0.99), safetyMsP50: pct(saf, 0.5), safetyMsP99: pct(saf, 0.99),
|
||||||
// 2026-10-18 第六轮(审查 B-1/B-2):前瞻、整次规划耗时,每次规划的距离查询数与记忆命中率,认证失败原因——与查看器 autotest 同口径对照
|
// 2026-10-18 第六轮(审查 B-1/B-2):前瞻、整次规划耗时,每次规划的距离查询数与记忆命中率,认证失败原因——与查看器 autotest 同口径对照
|
||||||
lookaheadMsP99: pct(look, 0.99), planMsP50: pct(planT, 0.5), planMsP99: pct(planT, 0.99), planWarmMsP99: pct(planW, 0.99), certQueriesP50: pct(cq, 0.5), certQueriesP99: pct(cq, 0.99),
|
lookaheadMsP99: pct(look, 0.99), planMsP50: pct(planT, 0.5), planMsP99: pct(planT, 0.99), planWarmMsP99: pct(planW, 0.99), certQueriesP50: pct(cq, 0.5), certQueriesP99: pct(cq, 0.99),
|
||||||
|
|||||||
+26
-17
@@ -191,14 +191,14 @@ class Runner {
|
|||||||
this.o = Object.assign({ K: 8, commit: 3, margin: 5, /* 2026-10-18:运行时投影余量保持 5 mm(真机护栏,不迁就数据分布);数据侧另行过滤认证间隙 < 3 mm 的老师轨迹,APG 训练余量 3 mm */
|
this.o = Object.assign({ K: 8, commit: 3, margin: 5, /* 2026-10-18:运行时投影余量保持 5 mm(真机护栏,不迁就数据分布);数据侧另行过滤认证间隙 < 3 mm 的老师轨迹,APG 训练余量 3 mm */
|
||||||
projMax: 0.5, refineMm: 5, arriveMm: 1, arriveDeg: 0.5,
|
projMax: 0.5, refineMm: 5, arriveMm: 1, arriveDeg: 0.5,
|
||||||
stuckPlans: 5, lockSec: 1.0, subReachDeg: 5, subTimeout: 1.5, subRule: 'cross', subLeadSec: 0.02, seed: 12345, lookahead: true,
|
stuckPlans: 5, lockSec: 1.0, subReachDeg: 5, subTimeout: 1.5, subRule: 'cross', subLeadSec: 0.02, seed: 12345, lookahead: true,
|
||||||
actScale: 1, certBudget: 40, safetyMs: 30, firstMs: 45, fbGain: 0, segCert: true }, opts || {});
|
actScale: 1, certBudget: 40, safetyMs: 30, firstMs: 45, fbGain: 0, segCert: true, select: 'consist' }, opts || {});
|
||||||
/* 2026-10-19:actScale = 模型 meta.act_scale(动作标签按 act_scale·STEP 归一化,旧模型 1);增量 = clamp(act, ±1)·actScale·STEP,
|
/* 2026-10-19:actScale = 模型 meta.act_scale(动作标签按 act_scale·STEP 归一化,旧模型 1);增量 = clamp(act, ±1)·actScale·STEP,
|
||||||
与 ml/sim/closed_loop.py 同口径。subRule:'cross'(默认,越过顶点即切,与训练标签同口径;见 _subAdvance)/ 'timeout'(旧:< subReachDeg 或 subTimeout 超时)。 */ // 每条候选最多 40 次距离查询;安全层总预算 30 ms,第 1 条候选可用到 45 ms(超出按刹停处理)
|
与 ml/sim/closed_loop.py 同口径。subRule:'cross'(默认,越过顶点即切,与训练标签同口径;见 _subAdvance)/ 'timeout'(旧:< subReachDeg 或 subTimeout 超时)。 */ // 每条候选最多 40 次距离查询;安全层总预算 30 ms,第 1 条候选可用到 45 ms(超出按刹停处理)
|
||||||
this.cert = makeCert(S, { leverTable: this.o.leverTable, cap: 20 });
|
this.cert = makeCert(S, { leverTable: this.o.leverTable, cap: 20 });
|
||||||
this.rng = makeRng(this.o.seed);
|
this.rng = makeRng(this.o.seed);
|
||||||
/* 2026-10-18 第六轮(审查 B-1/B-2):耗时拆开记——inferMs 主推理、lookaheadMs 前瞻(第二次推理 + 8 份特征)、safetyMs 只含认证、
|
/* 2026-10-18 第六轮(审查 B-1/B-2):耗时拆开记——inferMs 主推理、lookaheadMs 前瞻(第二次推理 + 8 份特征)、safetyMs 只含认证、
|
||||||
planMs 整次规划(R9 口径:必须 < 60 ms 周期);certQ 每次规划新增的距离查询数,certHit 记忆命中数,certWhy 认证失败原因计数 */
|
planMs 整次规划(R9 口径:必须 < 60 ms 周期);certQ 每次规划新增的距离查询数,certHit 记忆命中数,certWhy 认证失败原因计数 */
|
||||||
this.stats = { plans: 0, inferMs: [], lookaheadMs: [], safetyMs: [], planMs: [], planWarmMs: [], planColdMs: [], certQ: [], certHit: 0, certWhy: {}, brakes: 0, refine: 0, lookahead: 0, locks: 0, projected: 0, partial: 0 };
|
this.stats = { plans: 0, inferMs: [], lookaheadMs: [], safetyMs: [], planMs: [], planWarmMs: [], planColdMs: [], certQ: [], certHit: 0, certWhy: {}, brakes: 0, refine: 0, lookahead: 0, locks: 0, projected: 0, partial: 0, starveTicks: 0 };
|
||||||
this.reset(null);
|
this.reset(null);
|
||||||
}
|
}
|
||||||
reset(q0) {
|
reset(q0) {
|
||||||
@@ -302,11 +302,14 @@ class Runner {
|
|||||||
async plan() {
|
async plan() {
|
||||||
if (!this.needPlan()) return;
|
if (!this.needPlan()) return;
|
||||||
this.planning = true;
|
this.planning = true;
|
||||||
|
const qlen0 = this.queue.length; // 规划开始时队列里可执行步数
|
||||||
const t = now(), c0 = this.cert.counters(), wasCold = this.cold; // 第八轮:冷启动(含前瞻)与稳态分开记,R9 判稳态
|
const t = now(), c0 = this.cert.counters(), wasCold = this.cold; // 第八轮:冷启动(含前瞻)与稳态分开记,R9 判稳态
|
||||||
try { await this._plan(); } finally {
|
try { await this._plan(); } finally {
|
||||||
this.planning = false; this._until = undefined;
|
this.planning = false; this._until = undefined;
|
||||||
const c1 = this.cert.counters();
|
const c1 = this.cert.counters();
|
||||||
const dt = now() - t; this.stats.planMs.push(dt); (wasCold ? this.stats.planColdMs : this.stats.planWarmMs).push(dt); this.stats.certQ.push(c1.queries - c0.queries); this.stats.certHit += c1.hits - c0.hits;
|
const dt = now() - t; this.stats.planMs.push(dt); (wasCold ? this.stats.planColdMs : this.stats.planWarmMs).push(dt); this.stats.certQ.push(c1.queries - c0.queries); this.stats.certHit += c1.hits - c0.hits;
|
||||||
|
/* 50 Hz 饥饿估计:规划耗时 dt 期间每 20 ms 消耗一步;超过 qlen0 的步数即队列空转 tick */
|
||||||
|
this.stats.starveTicks += Math.max(0, Math.ceil(dt / (DT * 1000)) - qlen0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async _plan() {
|
async _plan() {
|
||||||
@@ -344,27 +347,21 @@ class Runner {
|
|||||||
if (sb[12] > 0.5) { this.subs = [0, 1].map(v => this.q.map((x, j) => x + sb[v * 6 + j] * 180)); this.subPrev = this.q.slice(); }
|
if (sb[12] > 0.5) { this.subs = [0, 1].map(v => this.q.map((x, j) => x + sb[v * 6 + j] * 180)); this.subPrev = this.q.slice(); }
|
||||||
}
|
}
|
||||||
/* 候选:去掉前缀后的提交步,投影,打分 */
|
/* 候选:去掉前缀后的提交步,投影,打分 */
|
||||||
const cands = [], near = S.criticalPairs(qe, o.margin + 10).slice(0, 64);
|
const cands = [], near = o.projMax > 0 ? S.criticalPairs(qe, o.margin + 10).slice(0, 64) : [];
|
||||||
// 2026-10-20:全程目标反馈(与 ml/sim/closed_loop.py 的 fb_gain 一致)——一次 DLS,加到所有候选所有步
|
// 2026-10-20:全程目标反馈(与 ml/sim/closed_loop.py 的 fb_gain 一致)——一次 DLS,加到所有候选所有步
|
||||||
const fbStep = (o.fbGain > 0 && this.goal) ? refineStep(S, qe, this.goal.T, this.goal.R, 0.3).dq : null;
|
const fbStep = (o.fbGain > 0 && this.goal) ? refineStep(S, qe, this.goal.T, this.goal.R, 0.3).dq : null;
|
||||||
for (let k = 0; k < K; k++) {
|
for (let k = 0; k < K; k++) {
|
||||||
const inc = this._cand(out.act, k, r);
|
const inc = this._cand(out.act, k, r);
|
||||||
if (fbStep) for (let s = 0; s < H; s++) for (let j = 0; j < 6; j++) inc[s][j] += o.fbGain * fbStep[j];
|
if (fbStep) for (let s = 0; s < H; s++) for (let j = 0; j < 6; j++) inc[s][j] += o.fbGain * fbStep[j];
|
||||||
let steps = inc.slice(r, r + o.commit), q = qe.slice();
|
const steps = inc.slice(r, r + o.commit); // 先不投影;只对进入认证的候选投影(2026-10-20:投影 8 份候选占安全层 ~13 ms)
|
||||||
const adj = [];
|
|
||||||
for (const d of steps) {
|
|
||||||
let qn = q.map((v, j) => v + d[j]);
|
|
||||||
let qp = project(S, qn, o.margin, o.projMax, near);
|
|
||||||
/* 限位轴先夹到 LIM(与执行 clamp_box 同口径),否则候选会被认证的 inBox 整段拒绝 → 永远刹停 */
|
|
||||||
qp = qp.map((v, j) => { const L = S.LIM[S.ORDER[j]]; return L.cont ? v : Math.max(L.lo, Math.min(L.hi, v)); });
|
|
||||||
if (qp !== qn) this.stats.projected++;
|
|
||||||
adj.push(qp.map((v, j) => v - q[j])); q = qp;
|
|
||||||
}
|
|
||||||
steps = adj;
|
|
||||||
const fut = []; let qq = qe.slice(); for (let s = r; s < H; s++) { qq = qq.map((v, j) => v + inc[s][j]); fut.push(qq); }
|
const fut = []; let qq = qe.slice(); for (let s = r; s < H; s++) { qq = qq.map((v, j) => v + inc[s][j]); fut.push(qq); }
|
||||||
let j1 = 0; for (let s = 0; s < H; s++) j1 += inc[s][0];
|
let j1 = 0; for (let s = 0; s < H; s++) j1 += inc[s][0];
|
||||||
let score = -2.0 * out.value[k];
|
let score = -2.0 * out.value[k];
|
||||||
if (this.lastPlan) { // 与上一条已选计划的未来重叠步对齐:lastPlan[i] = 旧 qe 走 i 步,新 qe = lastPlan[commit],fut[s] 对应 lastPlan[s+commit+1]
|
if (o.select === 'endpoint') {
|
||||||
|
/* 2026-10-20:端点进度选择(与 ml/sim/closed_loop.py select='endpoint' 同口径)——n=512 时间效率 29→35%、ratio2s 0.343→0.285 */
|
||||||
|
const qa = fut[fut.length - 1], pe = S.fk(qa, 0);
|
||||||
|
score = -Math.hypot(pe[0] - this.goal.T[0], pe[1] - this.goal.T[1], pe[2] - this.goal.T[2]);
|
||||||
|
} else if (this.lastPlan) { // 与上一条已选计划的未来重叠步对齐:lastPlan[i] = 旧 qe 走 i 步,新 qe = lastPlan[commit],fut[s] 对应 lastPlan[s+commit+1]
|
||||||
// 2026-10-19 修错位(原对齐 lastPlan[s+commit],奖励比上一计划慢一步的候选;Python closed_loop.py 同步修正)
|
// 2026-10-19 修错位(原对齐 lastPlan[s+commit],奖励比上一计划慢一步的候选;Python closed_loop.py 同步修正)
|
||||||
let c = 0, w = 1;
|
let c = 0, w = 1;
|
||||||
for (let s = 0; s < fut.length && s + o.commit + 1 < this.lastPlan.length; s++, w *= 0.85) {
|
for (let s = 0; s < fut.length && s + o.commit + 1 < this.lastPlan.length; s++, w *= 0.85) {
|
||||||
@@ -387,7 +384,7 @@ class Runner {
|
|||||||
/* 短前瞻:冷启动(新目标 / 目标跳变)时,各候选块末态再推一块,用其价值打分。
|
/* 短前瞻:冷启动(新目标 / 目标跳变)时,各候选块末态再推一块,用其价值打分。
|
||||||
2026-10-18 第六轮:去掉"卡死时也前瞻"——与仿真 ml/sim/closed_loop.py(只在首块前瞻)对齐;
|
2026-10-18 第六轮:去掉"卡死时也前瞻"——与仿真 ml/sim/closed_loop.py(只在首块前瞻)对齐;
|
||||||
前瞻要算 8 份特征(8 次 gapMatrix cap40,沙箱 Node 实测 P99 126 ms),卡死时再做会让整次规划连续超周期。卡死由换向锁处理。 */
|
前瞻要算 8 份特征(8 次 gapMatrix cap40,沙箱 Node 实测 P99 126 ms),卡死时再做会让整次规划连续超周期。卡死由换向锁处理。 */
|
||||||
if (o.lookahead && this.cold) {
|
if (o.lookahead && this.cold && o.select !== 'endpoint') {
|
||||||
this.stats.lookahead++;
|
this.stats.lookahead++;
|
||||||
const feat2 = new Float32Array(pool.length * F_DIM), z2 = new Float32Array(pool.length * H * 6);
|
const feat2 = new Float32Array(pool.length * F_DIM), z2 = new Float32Array(pool.length * H * 6);
|
||||||
pool.forEach((c, i) => {
|
pool.forEach((c, i) => {
|
||||||
@@ -410,7 +407,19 @@ class Runner {
|
|||||||
for (const c of pool) {
|
for (const c of pool) {
|
||||||
if (c !== pool[0] && now() - t1 > o.safetyMs) break;
|
if (c !== pool[0] && now() - t1 > o.safetyMs) break;
|
||||||
this._until = t1 + (c === pool[0] ? Math.max(o.firstMs, o.safetyMs) : o.safetyMs);
|
this._until = t1 + (c === pool[0] ? Math.max(o.firstMs, o.safetyMs) : o.safetyMs);
|
||||||
const v = this._certCommit(qe, c.steps, true);
|
let steps = c.steps;
|
||||||
|
if (o.projMax > 0) { // 投影只做给要认证的候选(按需),不再对全部 K 条预算
|
||||||
|
const adj = []; let q = qe.slice();
|
||||||
|
for (const d of c.steps) {
|
||||||
|
const qn = q.map((v, j) => v + d[j]);
|
||||||
|
let qp = project(S, qn, o.margin, o.projMax, near);
|
||||||
|
qp = qp.map((v, j) => { const L = S.LIM[S.ORDER[j]]; return L.cont ? v : Math.max(L.lo, Math.min(L.hi, v)); });
|
||||||
|
if (qp !== qn) this.stats.projected++;
|
||||||
|
adj.push(qp.map((v, j) => v - q[j])); q = qp;
|
||||||
|
}
|
||||||
|
steps = adj;
|
||||||
|
}
|
||||||
|
const v = this._certCommit(qe, steps, true);
|
||||||
if (v.ok) { picked = c; pickedSteps = v.steps; this.brakeTail = v.tail; break; }
|
if (v.ok) { picked = c; pickedSteps = v.steps; this.brakeTail = v.tail; break; }
|
||||||
}
|
}
|
||||||
this._until = undefined;
|
this._until = undefined;
|
||||||
|
|||||||
Reference in New Issue
Block a user