3ad29356c9
集成通用机器人数值接口、本机控制桥、LeRobot 插件和统一键盘遥操作。采用离线 CoACD 全臂碰撞配方 revision 4、局部装配区切分与结构自接触,限制直接关节位姿写入并保留安全看门狗。同步版本号、变更记录、来源许可证和兼容性验证。
92 lines
4.4 KiB
TypeScript
92 lines
4.4 KiB
TypeScript
import { expect, test } from '@playwright/test';
|
|
import { resolve } from 'node:path';
|
|
import { writeFile } from 'node:fs/promises';
|
|
import type {} from '../physics/runner';
|
|
|
|
test('真实 WASM:相邻 Mirror/Square 连杆主体阻挡、反向脱离,机器人禁止 qpos 瞬移', async ({
|
|
page,
|
|
}, info) => {
|
|
const errors: string[] = [];
|
|
page.on('pageerror', (e) => errors.push(e.message));
|
|
await page.goto('/physics/runner.html');
|
|
await page.waitForFunction(() => Boolean(window.lekiwiPhysics));
|
|
await page.evaluate((path) => window.lekiwiPhysics.boot(path), `/@fs${resolve('build/lekiwi')}`);
|
|
try {
|
|
const result = await page.evaluate(() => window.lekiwiPhysics.adjacentArmChecks());
|
|
console.log('adjacent arm', JSON.stringify(result));
|
|
await writeFile(info.outputPath('adjacent-arm-contacts.json'), JSON.stringify(result, null, 2));
|
|
expect(result.teleportAccepted).toBe(false);
|
|
expect(result.jointResetError).toMatch(/profile.*瞬移/);
|
|
expect(result.editable).toBe(false);
|
|
expect(result.before).toEqual(result.after);
|
|
for (const sample of result.samples) {
|
|
expect(sample.neutral.contacts).toEqual([]);
|
|
expect(Math.abs(sample.neutral.panActual - sample.pan)).toBeLessThan(0.01);
|
|
expect(Math.abs(sample.neutral.actual)).toBeLessThan(0.015);
|
|
expect(sample.blocked.maxContacts).toBeGreaterThan(0);
|
|
expect(sample.blocked.contacts.length).toBeGreaterThan(0);
|
|
expect(sample.blocked.actual).toBeGreaterThan(sample.blocked.target + 0.5);
|
|
expect(sample.blocked.minContactRadius).toBeGreaterThan(0.04); // structural contact, NOT the bearing core
|
|
expect(sample.blocked.maxPenetration).toBeLessThan(0.002);
|
|
expect(sample.blocked.finite).toBe(true);
|
|
for (const c of sample.blocked.contacts) expect(c.distance).toBeGreaterThan(-0.0005);
|
|
expect(Math.abs(sample.released.actual - 0.3)).toBeLessThan(0.015);
|
|
expect(sample.released.contacts).toEqual([]);
|
|
}
|
|
expect(errors).toEqual([]);
|
|
} finally {
|
|
await page.evaluate(() => window.lekiwiPhysics.dispose());
|
|
}
|
|
});
|
|
|
|
test('真实 WASM:六关节双向有界目标扫掠,无整体穿入或数值重置', async ({ page }, info) => {
|
|
const errors: string[] = [];
|
|
page.on('pageerror', (e) => errors.push(e.message));
|
|
await page.goto('/physics/runner.html');
|
|
await page.waitForFunction(() => Boolean(window.lekiwiPhysics));
|
|
await page.evaluate((path) => window.lekiwiPhysics.boot(path), `/@fs${resolve('build/lekiwi')}`);
|
|
try {
|
|
const result = await page.evaluate(() => window.lekiwiPhysics.jointSweepChecks());
|
|
await writeFile(info.outputPath('joint-sweeps.json'), JSON.stringify(result, null, 2));
|
|
console.log('joint sweeps', JSON.stringify(result));
|
|
expect(result).toHaveLength(12);
|
|
for (const sample of result) {
|
|
expect(sample.finite, sample.joint).toBe(true);
|
|
expect(sample.elapsed).toBeCloseTo(sample.expectedTime, 6);
|
|
expect(sample.maxPenetration, sample.joint).toBeLessThan(0.002);
|
|
if (sample.joint === 'arm_shoulder_pan') {
|
|
expect(Math.abs(sample.actual - sample.target)).toBeLessThan(0.01);
|
|
expect(sample.maxContacts).toBe(0); // core/structural straddlers must not lock the bearing
|
|
}
|
|
if (Math.abs(sample.actual - sample.target) > 0.05)
|
|
expect(sample.maxContacts, sample.joint).toBeGreaterThan(0);
|
|
}
|
|
expect(errors).toEqual([]);
|
|
} finally {
|
|
await page.evaluate(() => window.lekiwiPhysics.dispose());
|
|
}
|
|
});
|
|
|
|
test('真实 WASM:从原始 STL 独立取六向极值,整臂 18 个视觉网格均有外部物体接触覆盖', async ({
|
|
page,
|
|
}, info) => {
|
|
const errors: string[] = [];
|
|
page.on('pageerror', (e) => errors.push(e.message));
|
|
await page.goto('/physics/runner.html');
|
|
await page.waitForFunction(() => Boolean(window.lekiwiPhysics));
|
|
await page.evaluate((path) => window.lekiwiPhysics.boot(path), `/@fs${resolve('build/lekiwi')}`);
|
|
try {
|
|
const result = await page.evaluate(() => window.lekiwiPhysics.visualCollisionCoverage());
|
|
await writeFile(info.outputPath('arm-visual-coverage.json'), JSON.stringify(result, null, 2));
|
|
expect(result).toHaveLength(18);
|
|
for (const sample of result) {
|
|
expect(sample.probes).toHaveLength(6);
|
|
for (const probe of sample.probes)
|
|
expect(probe.contacts.length, `${sample.visual}: ${probe.point}`).toBeGreaterThan(0);
|
|
}
|
|
expect(errors).toEqual([]);
|
|
} finally {
|
|
await page.evaluate(() => window.lekiwiPhysics.dispose());
|
|
}
|
|
});
|