feat(lekiwi): release V0.10.1 初步集成 LeKiwi,优化碰撞模型
集成通用机器人数值接口、本机控制桥、LeRobot 插件和统一键盘遥操作。采用离线 CoACD 全臂碰撞配方 revision 4、局部装配区切分与结构自接触,限制直接关节位姿写入并保留安全看门狗。同步版本号、变更记录、来源许可证和兼容性验证。
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
import { resolve } from 'node:path';
|
||||
import { startBridge } from './fixtures/controlBridge';
|
||||
import type {} from '../physics/runner';
|
||||
|
||||
test('LeKiwi:真实通用 Python SDK 双向控制,3秒30Hz,无 LeRobot 依赖', async ({ page }) => {
|
||||
const bridge = await startBridge();
|
||||
try {
|
||||
await page.goto('/physics/runner.html');
|
||||
await page.waitForFunction(() => Boolean(window.lekiwiPhysics));
|
||||
await page.evaluate(
|
||||
(path) => window.lekiwiPhysics.boot(path),
|
||||
`/@fs${resolve(process.cwd(), 'build/lekiwi')}`,
|
||||
);
|
||||
await page.evaluate(
|
||||
({ endpoint, token }) => window.lekiwiPhysics.connectBridge(endpoint, token),
|
||||
{ endpoint: bridge.endpoint, token: bridge.token },
|
||||
);
|
||||
const run = await bridge.python(`
|
||||
import json, os, time
|
||||
from mujoco_control_bridge import SimRobotClient
|
||||
with SimRobotClient(os.environ['MUJOCO_CONTROL_ENDPOINT']) as robot:
|
||||
values={c['id']:0.0 for c in robot.describe()['actionChannels']}
|
||||
values.update({'arm_shoulder_pan.pos':.1,'arm_gripper.pos':.5})
|
||||
before=robot.get_observation()['values']
|
||||
start=time.monotonic(); latency=[]
|
||||
for i in range(90):
|
||||
values.update({'x.vel':.08 if i<30 else 0,'y.vel':.08 if 30<=i<60 else 0,'theta.vel':.3 if i>=60 else 0})
|
||||
tick=time.monotonic(); robot.send_action(values); obs=robot.get_observation()
|
||||
latency.append((time.monotonic()-tick)*1000)
|
||||
time.sleep(max(0,start+(i+1)/30-time.monotonic()))
|
||||
print(json.dumps({'before':before,'after':obs['values'],'rttMeanMs':sum(latency)/len(latency),'rttMaxMs':max(latency),'seq':obs['appliedActionSeq']}))
|
||||
`);
|
||||
const result = JSON.parse(run.stdout.trim());
|
||||
expect(result.after['base.x'] - result.before['base.x']).toBeGreaterThan(0.04);
|
||||
expect(result.after['base.y'] - result.before['base.y']).toBeGreaterThan(0.04);
|
||||
expect(result.after['base.yaw']).toBeGreaterThan(0.2);
|
||||
expect(result.after['arm_gripper.pos']).toBeCloseTo(0.5, 1);
|
||||
expect(result.seq).toBeGreaterThan(85);
|
||||
console.log('SDK bridge timing', { meanMs: result.rttMeanMs, maxMs: result.rttMaxMs });
|
||||
await expect
|
||||
.poll(
|
||||
async () =>
|
||||
(await page.evaluate(() => window.lekiwiPhysics.externalState())).observation?.paused,
|
||||
)
|
||||
.toBe(true);
|
||||
expect(bridge.errors()).toBe('');
|
||||
} finally {
|
||||
await page.evaluate(() => window.lekiwiPhysics?.dispose()).catch(() => {});
|
||||
await bridge.stop();
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user