feat(abs-target): 方案 B 初版——Y 改 16 步绝对目标角(/180),训练/评估/rollout/ckpt 适配;修复前缀噪声增广对绝对目标的标签污染(eps 直接加到绝对位置=18°);实测一步生成误差 31°、闭环 0%,此配方不成立
This commit is contained in:
@@ -25,7 +25,10 @@ const DATA_VER = 2;
|
||||
const MIN_GAP_MM = +(process.env.MIN_GAP_MM || 3);
|
||||
const AUG_P = +(process.env.AUG_P || 0.4), AUG_SIGMA = +(process.env.AUG_SIGMA || 3), AUG_BACK = +(process.env.AUG_BACK || 12);
|
||||
const ACT_SCALE = +(process.env.ACT_SCALE || 1.25);
|
||||
const SIG = ['v' + DATA_VER, 's' + ACT_SCALE, 'p' + AUG_P, 'sg' + AUG_SIGMA, 'b' + AUG_BACK, 'g' + MIN_GAP_MM].join('|');
|
||||
/* 2026-10-19(方案 B):ABS_TARGET=1 时标签改为 16 步绝对目标关节角(/180 归一化),执行端每步朝目标走;
|
||||
prefix 仍存增量(度),与特征/部署的历史动作语义一致。未设时行为与原来完全一致。 */
|
||||
const ABS_TARGET = +(process.env.ABS_TARGET || 0);
|
||||
const SIG = ['v' + DATA_VER, 's' + ACT_SCALE, 'a' + ABS_TARGET, 'p' + AUG_P, 'sg' + AUG_SIGMA, 'b' + AUG_BACK, 'g' + MIN_GAP_MM].join('|');
|
||||
if (process.argv[2] === '--sig') { console.log(SIG); process.exit(0); }
|
||||
const IN = process.argv[2], OUTDIR = process.argv[3] || path.join(__dirname, '../data/samples');
|
||||
if (!IN) { console.error('usage: build_dataset.cjs <shard.jsonl> [outDir] | --sig'); process.exit(1); }
|
||||
@@ -74,14 +77,30 @@ for (let li = 0; li < NT; li++) {
|
||||
}
|
||||
const pos = k => { const i = Math.min(N, t + k); const w = aug ? Math.max(0, 1 - k / AUG_BACK) : 0; return traj[i].map((v, j) => v + off[j] * w); };
|
||||
const y = new Float32Array(H * 6);
|
||||
/* prevP = 按标签实际会走到的位置:被 ±1 截掉的亏空留在 prevP 与目标之差里,下一步接着补(2026-10-19,C2) */
|
||||
let prevP = q.slice();
|
||||
for (let k = 1; k <= H; k++) {
|
||||
const pk = pos(k);
|
||||
for (let j = 0; j < 6; j++) { const v = Math.max(-1, Math.min(1, (pk[j] - prevP[j]) / ASTEP[j])); y[(k - 1) * 6 + j] = v; prevP[j] += v * ASTEP[j]; }
|
||||
if (ABS_TARGET) {
|
||||
for (let k = 1; k <= H; k++) {
|
||||
const pk = pos(k);
|
||||
for (let j = 0; j < 6; j++) y[(k - 1) * 6 + j] = Math.max(-1, Math.min(1, pk[j] / 180));
|
||||
}
|
||||
} else {
|
||||
/* prevP = 按标签实际会走到的位置:被 ±1 截掉的亏空留在 prevP 与目标之差里,下一步接着补(2026-10-19,C2) */
|
||||
let prevP = q.slice();
|
||||
for (let k = 1; k <= H; k++) {
|
||||
const pk = pos(k);
|
||||
for (let j = 0; j < 6; j++) { const v = Math.max(-1, Math.min(1, (pk[j] - prevP[j]) / ASTEP[j])); y[(k - 1) * 6 + j] = v; prevP[j] += v * ASTEP[j]; }
|
||||
}
|
||||
}
|
||||
const rr = Math.floor(rnd() * 4), prefix = [];
|
||||
for (let k = 0; k < rr; k++) { const row = []; for (let j = 0; j < 6; j++) row.push(y[k * 6 + j] * ASTEP[j]); prefix.push(row); }
|
||||
for (let k = 0; k < rr; k++) {
|
||||
const row = [];
|
||||
for (let j = 0; j < 6; j++) {
|
||||
if (ABS_TARGET) { // 前缀仍存「已提交的增量(度)」,特征语义不变
|
||||
const cur = y[k * 6 + j] * 180, prev = k ? y[(k - 1) * 6 + j] * 180 : q[j];
|
||||
row.push(cur - prev);
|
||||
} else row.push(y[k * 6 + j] * ASTEP[j]);
|
||||
}
|
||||
prefix.push(row);
|
||||
}
|
||||
const remain = inter.filter(v => v.tau > taus[t] + 0.02);
|
||||
const sub = remain.length && rnd() < 0.8 ? remain[0].p : null;
|
||||
const gm = S.gapMatrix(q, POL.GAP_CAP);
|
||||
@@ -102,7 +121,7 @@ writeNpy(path.join(OUTDIR, base + '.V.npy'), Float32Array.from(V), [n]);
|
||||
writeNpy(path.join(OUTDIR, base + '.SG.npy'), cat(SG, 13), [n, 13]);
|
||||
writeNpy(path.join(OUTDIR, base + '.TID.npy'), TID, [n], 'int32');
|
||||
/* meta 在 tasks.jsonl 之前写:tasks.jsonl 仍是「本 shard 打包完成」的标记 */
|
||||
fs.writeFileSync(path.join(OUTDIR, base + '.meta.json'), JSON.stringify({ data_ver: DATA_VER, act_scale: ACT_SCALE, aug_p: AUG_P, aug_sigma: AUG_SIGMA,
|
||||
fs.writeFileSync(path.join(OUTDIR, base + '.meta.json'), JSON.stringify({ data_ver: DATA_VER, act_scale: ACT_SCALE, abs_target: ABS_TARGET, aug_p: AUG_P, aug_sigma: AUG_SIGMA,
|
||||
aug_back: AUG_BACK, min_gap_mm: MIN_GAP_MM, aug_carry: true, sig: SIG }));
|
||||
// 2026-10-18 断点续跑:tasks.jsonl 最后写、先写 .tmp 再改名 —— 它存在就代表这个 shard 的 5 个 npy 都已写完,run_prep.sh 据此跳过
|
||||
const tf = path.join(OUTDIR, base + '.tasks.jsonl');
|
||||
|
||||
Reference in New Issue
Block a user