Skip to content

Commit d765c84

Browse files
Multi Joint Robot Tests [AARD-1971] (#1188)
Co-authored-by: Alexey Dmitriev <157652245+AlexD717@users.noreply.github.com> Co-authored-by: Brandon Pacewic <92102436+BrandonPacewic@users.noreply.github.com>
2 parents ced0972 + f135f16 commit d765c84

3 files changed

Lines changed: 186 additions & 15 deletions

File tree

fission/src/test/MirabufParser.test.ts

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,46 @@ describe("Mirabuf Parser Tests", () => {
1414
const rn = [...t.rigidNodes.values()]
1515

1616
expect(filterNonPhysicsNodes(rn, spikeMira!).length).toBe(7)
17+
18+
// Validate joints
19+
const jointValidation = validateJoints(spikeMira!)
20+
expect(jointValidation.isValid).toBe(true)
21+
expect(jointValidation.jointCount).toBe(6)
22+
expect(jointValidation.wheelJoints).toBe(6)
23+
expect(jointValidation.allJoints).toContain(mirabuf.joint.JointMotion.REVOLUTE) // Wheels are revolute joints
24+
expect(jointValidation.allJoints).not.toContain(mirabuf.joint.JointMotion.SLIDER) // Dozer has no slider joints
25+
})
26+
27+
/*
28+
* Multi-Joint Wheels robot contains
29+
* - 4 wheels (4 revolute joints)
30+
* - 2 additional revolute joints
31+
* - 2 slider joints
32+
* Mira File: https://synthesis.autodesk.com/api/mira/private/Multi-Joint_Wheels_v0.mira
33+
*/
34+
test("Generate Rigid Nodes (Multi-Joint Wheels)", async () => {
35+
const spikeMira = await MirabufCachingService.cacheRemote(
36+
"/api/mira/private/Multi-Joint_Wheels_v0.mira",
37+
MiraType.ROBOT
38+
).then(x => MirabufCachingService.get(x!.id, MiraType.ROBOT))
39+
40+
const t = new MirabufParser(spikeMira!)
41+
const rn = [...t.rigidNodes.values()]
42+
43+
expect(filterNonPhysicsNodes(rn, spikeMira!).length).toBe(9)
44+
45+
// Validate joints
46+
const jointValidation = validateJoints(spikeMira!)
47+
expect(jointValidation.isValid).toBe(true)
48+
expect(jointValidation.jointCount).toBe(8)
49+
50+
// Validate joint type distribution
51+
const revoluteJoints = jointValidation.allJoints.filter(j => j === mirabuf.joint.JointMotion.REVOLUTE)
52+
const sliderJoints = jointValidation.allJoints.filter(j => j === mirabuf.joint.JointMotion.SLIDER)
53+
54+
expect(revoluteJoints.length).toBe(6) // Should have 6 revolute joints (4 wheels + 2 additional)
55+
expect(sliderJoints.length).toBe(2) // Should have 2 slider joints
56+
expect(jointValidation.wheelJoints).toBe(4) // Should have 4 wheel joints
1757
})
1858

1959
test("Generate Rigid Nodes (FRC Field 2018_v13.mira)", async () => {
@@ -40,6 +80,116 @@ function filterNonPhysicsNodes(nodes: RigidNodeReadOnly[], mira: mirabuf.Assembl
4080
})
4181
}
4282

83+
interface JointValidationResult {
84+
isValid: boolean
85+
jointCount: number
86+
allJoints: mirabuf.joint.JointMotion[]
87+
wheelJoints: number
88+
errors: string[]
89+
warnings: string[]
90+
}
91+
92+
function validateJoints(assembly: mirabuf.Assembly): JointValidationResult {
93+
const result: JointValidationResult = {
94+
isValid: true,
95+
jointCount: 0,
96+
allJoints: [],
97+
wheelJoints: 0,
98+
errors: [],
99+
warnings: [],
100+
}
101+
102+
const jointData = assembly.data?.joints
103+
if (!jointData) {
104+
result.errors.push("No joint data found in assembly")
105+
result.isValid = false
106+
return result
107+
}
108+
109+
// Validate joint definitions and instances
110+
const jointDefinitions = jointData.jointDefinitions || {}
111+
const jointInstances = jointData.jointInstances || {}
112+
113+
// Count non-grounded joints
114+
const nonGroundedJoints = Object.entries(jointInstances).filter(([key]) => key !== "grounded")
115+
result.jointCount = nonGroundedJoints.length
116+
117+
// Validate each joint
118+
for (const [jointId, jointInstance] of nonGroundedJoints) {
119+
try {
120+
// Check if joint definition exists
121+
const jointDef = jointDefinitions[jointInstance.jointReference!]
122+
if (!jointDef) {
123+
result.errors.push(
124+
`Joint instance '${jointId}' references missing definition '${jointInstance.jointReference}'`
125+
)
126+
result.isValid = false
127+
continue
128+
}
129+
130+
// Get all joints
131+
if (jointDef.jointMotionType !== null && jointDef.jointMotionType !== undefined)
132+
result.allJoints.push(jointDef.jointMotionType)
133+
134+
// Check for wheel joints
135+
if (
136+
jointDef.userData?.data?.wheel === "true" ||
137+
(jointDef.jointMotionType === mirabuf.joint.JointMotion.REVOLUTE &&
138+
jointDef.userData?.data?.wheelType !== undefined)
139+
) {
140+
result.wheelJoints++
141+
}
142+
143+
// Validate joint motion type specific properties
144+
switch (jointDef.jointMotionType) {
145+
case mirabuf.joint.JointMotion.REVOLUTE:
146+
if (!jointDef.rotational) {
147+
result.errors.push(`Revolute joint '${jointId}' missing rotational definition`)
148+
result.isValid = false
149+
}
150+
break
151+
case mirabuf.joint.JointMotion.SLIDER:
152+
if (!jointDef.prismatic) {
153+
result.errors.push(`Slider joint '${jointId}' missing prismatic definition`)
154+
result.isValid = false
155+
}
156+
break
157+
case mirabuf.joint.JointMotion.BALL:
158+
// Note: Ball joint properties are validated differently in the mirabuf format
159+
if (!jointDef.custom) {
160+
result.warnings.push(`Ball joint '${jointId}' may be missing ball-specific configuration`)
161+
}
162+
break
163+
case mirabuf.joint.JointMotion.CUSTOM:
164+
if (!jointDef.custom) {
165+
result.errors.push(`Custom joint '${jointId}' missing custom definition`)
166+
result.isValid = false
167+
}
168+
break
169+
}
170+
171+
// Validate joint has an origin
172+
if (!jointDef.origin) {
173+
result.warnings.push(`Joint '${jointId}' has no origin defined`)
174+
}
175+
} catch (error) {
176+
result.errors.push(`Error validating joint '${jointId}': ${error}`)
177+
result.isValid = false
178+
}
179+
}
180+
181+
// Validate rigid groups if they exist
182+
if (jointData.rigidGroups) {
183+
for (const rigidGroup of jointData.rigidGroups) {
184+
if (!rigidGroup.occurrences || rigidGroup.occurrences.length < 2) {
185+
result.warnings.push(`Rigid group '${rigidGroup.name}' has fewer than 2 occurrences`)
186+
}
187+
}
188+
}
189+
190+
return result
191+
}
192+
43193
// function printRigidNodeParts(nodes: RigidNodeReadOnly[], mira: mirabuf.Assembly) {
44194
// nodes.forEach(x => {
45195
// console.log(`[ ${x.name} ]:`);

fission/src/test/physics/PhysicsSystem.test.ts

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import { test, expect, describe, assert, beforeEach, afterEach } from "vitest"
22
import PhysicsSystem, { LayerReserve, BodyAssociate } from "../../systems/physics/PhysicsSystem"
3-
import MirabufParser from "@/mirabuf/MirabufParser"
4-
import MirabufCachingService, { MiraType } from "@/mirabuf/MirabufLoader"
53
import * as THREE from "three"
64
import JOLT from "@/util/loading/JoltSyncLoader"
75
import Jolt from "@azaleacolburn/jolt-physics"
@@ -698,16 +696,3 @@ describe("Update Loop", () => {
698696
expect(body.GetPosition().GetY()).toBeLessThanOrEqual(10)
699697
})
700698
})
701-
702-
describe("Mirabuf Physics Loading", () => {
703-
test("Body Loading (Dozer)", async () => {
704-
const assembly = await MirabufCachingService.cacheRemote("/api/mira/robots/Dozer_v9.mira", MiraType.ROBOT).then(
705-
x => MirabufCachingService.get(x!.id, MiraType.ROBOT)
706-
)
707-
const parser = new MirabufParser(assembly!)
708-
const physSystem = new PhysicsSystem()
709-
const mapping = physSystem.createBodiesFromParser(parser, new LayerReserve())
710-
711-
expect(mapping.size).toBe(7)
712-
})
713-
})
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { describe, test, expect } from "vitest"
2+
import MirabufParser from "@/mirabuf/MirabufParser"
3+
import MirabufCachingService, { MiraType } from "@/mirabuf/MirabufLoader"
4+
import PhysicsSystem, { LayerReserve } from "@/systems/physics/PhysicsSystem"
5+
6+
describe("Mirabuf Physics Loading", () => {
7+
test("Body Loading (Dozer)", async () => {
8+
const assembly = await MirabufCachingService.cacheRemote("/api/mira/robots/Dozer_v9.mira", MiraType.ROBOT).then(
9+
x => MirabufCachingService.get(x!.id, MiraType.ROBOT)
10+
)
11+
const parser = new MirabufParser(assembly!)
12+
const physSystem = new PhysicsSystem()
13+
const mapping = physSystem.createBodiesFromParser(parser, new LayerReserve())
14+
15+
expect(mapping.size).toBe(7)
16+
})
17+
18+
/*
19+
* Multi-Joint Wheels robot contains
20+
* - 4 wheels (4 revolute joints)
21+
* - 2 additional revolute joints
22+
* - 2 slider joints
23+
* Mira File: https://synthesis.autodesk.com/api/mira/private/Multi-Joint_Wheels_v0.mira
24+
*/
25+
test("Body Loading (Multi-Joint Wheels)", async () => {
26+
const assembly = await MirabufCachingService.cacheRemote(
27+
"/api/mira/private/Multi-Joint_Wheels_v0.mira",
28+
MiraType.ROBOT
29+
).then(x => MirabufCachingService.get(x!.id, MiraType.ROBOT))
30+
const parser = new MirabufParser(assembly!)
31+
const physSystem = new PhysicsSystem()
32+
const mapping = physSystem.createBodiesFromParser(parser, new LayerReserve())
33+
34+
expect(mapping.size).toBe(9)
35+
})
36+
})

0 commit comments

Comments
 (0)