Spaces:
Running
Running
File size: 2,537 Bytes
bac98f7 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | // Frame sampling over a REAL emotion from the library (committed fixture)
// — proves the recorded-move JSON → puppet trajectory pipeline on
// production data.
import { describe, expect, it } from "vitest";
import { sampleFrames } from "../../src/emotions/loader";
import type { EmotionMotion } from "../../src/robot/adapter";
import cheerful1 from "../fixtures/cheerful1.json";
const motion = cheerful1 as unknown as EmotionMotion;
describe("sampleFrames on cheerful1", () => {
const frames = sampleFrames(motion);
it("keeps every recorded frame with monotonic timestamps", () => {
expect(frames).toHaveLength(motion.time.length);
expect(frames[0]!.t).toBeCloseTo(0, 1);
for (let i = 1; i < frames.length; i++) {
expect(frames[i]!.t).toBeGreaterThanOrEqual(frames[i - 1]!.t);
}
// A real move lasts a few seconds.
expect(frames[frames.length - 1]!.t).toBeGreaterThan(1);
expect(frames[frames.length - 1]!.t).toBeLessThan(30);
});
it("produces plausible logical degrees", () => {
for (const f of frames) {
expect(Math.abs(f.headRoll)).toBeLessThanOrEqual(90);
expect(Math.abs(f.headPitch)).toBeLessThanOrEqual(90);
expect(Math.abs(f.headYaw)).toBeLessThanOrEqual(120);
expect(Math.abs(f.bodyYaw)).toBeLessThanOrEqual(180);
expect(Math.abs(f.antennaRight)).toBeLessThanOrEqual(180);
expect(Math.abs(f.antennaLeft)).toBeLessThanOrEqual(180);
for (const v of Object.values(f)) expect(Number.isFinite(v)).toBe(true);
}
});
it("actually moves (not a degenerate constant trajectory)", () => {
const pitches = frames.map((f) => f.headPitch);
const span = Math.max(...pitches) - Math.min(...pitches);
const antSpan =
Math.max(...frames.map((f) => f.antennaLeft)) - Math.min(...frames.map((f) => f.antennaLeft));
expect(span + antSpan).toBeGreaterThan(3);
});
it("holds previous values when a channel is missing", () => {
const sparse: EmotionMotion = {
time: [0, 0.1, 0.2],
set_target_data: [
{ head: motion.set_target_data[0]!.head, antennas: [0.1, -0.1], body_yaw: 0.2 },
{}, // nothing recorded this tick
{ antennas: [0.3, -0.3] },
],
};
const out = sampleFrames(sparse);
expect(out[1]).toMatchObject({
headRoll: out[0]!.headRoll,
antennaRight: out[0]!.antennaRight,
bodyYaw: out[0]!.bodyYaw,
});
expect(out[2]!.antennaRight).not.toBe(out[1]!.antennaRight);
expect(out[2]!.headPitch).toBe(out[1]!.headPitch);
});
});
|