Skip to content

Commit b7b7ba1

Browse files
feat: expose rotation axis info in UrdfJoint (#985)
* feat: expose rotation axis info in UrdfJoint * Fail hard on invalid axis --------- Co-authored-by: Ezra Brooks <ezra@brooks.cx>
1 parent 4677e79 commit b7b7ba1

File tree

3 files changed

+91
-1
lines changed

3 files changed

+91
-1
lines changed

src/urdf/UrdfJoint.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*/
55

66
import { UrdfAttrs, type UrdfDefaultOptions } from "./UrdfTypes.js";
7-
import { Pose } from "../math/index.js";
7+
import { Pose, Vector3 } from "../math/index.js";
88
import { parseUrdfOrigin } from "./UrdfUtils.js";
99
import type { Nullable } from "../types/interface-types.js";
1010

@@ -19,6 +19,11 @@ export default class UrdfJoint {
1919
minval = NaN;
2020
maxval = NaN;
2121
origin: Pose = new Pose();
22+
axis: Vector3 = new Vector3({
23+
x: 1,
24+
y: 0,
25+
z: 0,
26+
});
2227

2328
constructor({ xml }: UrdfDefaultOptions) {
2429
this.name = xml.getAttribute(UrdfAttrs.Name) ?? "unknown_name";
@@ -49,5 +54,21 @@ export default class UrdfJoint {
4954
if (origins.length > 0) {
5055
this.origin = parseUrdfOrigin(origins[0]);
5156
}
57+
58+
const axis = xml.getElementsByTagName(UrdfAttrs.Axis);
59+
if (axis.length > 0) {
60+
const xyzValue = axis[0].getAttribute(UrdfAttrs.Xyz)?.split(" ");
61+
if (!xyzValue || xyzValue.length !== 3) {
62+
throw new Error(
63+
"If specified, axis must have an xyz value composed of three numbers",
64+
);
65+
}
66+
const [x, y, z] = xyzValue.map(parseFloat);
67+
this.axis = new Vector3({
68+
x,
69+
y,
70+
z,
71+
});
72+
}
5273
}
5374
}

src/urdf/UrdfTypes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export enum UrdfAttrs {
3030
Geometry = "geometry",
3131
Material = "material",
3232
Scale = "scale",
33+
Axis = "axis",
3334
}
3435

3536
export interface UrdfDefaultOptions {

test/urdfjoint.test.js

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { describe, it, expect } from "vitest";
2+
import { DOMParser } from "@xmldom/xmldom";
3+
import UrdfJoint from "../src/urdf/UrdfJoint";
4+
5+
describe("UrdfJoint", () => {
6+
it("should parse axis correctly from URDF", () => {
7+
const jointWithAxisUrdf = `
8+
<joint name="test_joint" type="revolute">
9+
<parent link="link1"/>
10+
<child link="link2"/>
11+
<axis xyz="0 1 0"/>
12+
</joint>`;
13+
const parser = new DOMParser();
14+
const xml = parser.parseFromString(
15+
jointWithAxisUrdf,
16+
"text/xml",
17+
).documentElement;
18+
if (!xml) {
19+
throw new Error("Failed to parse XML");
20+
}
21+
const joint = new UrdfJoint({ xml });
22+
expect(joint.axis.x).toBe(0);
23+
expect(joint.axis.y).toBe(1);
24+
expect(joint.axis.z).toBe(0);
25+
});
26+
27+
it("should default axis to (1,0,0) if not present", () => {
28+
const jointNoAxisUrdf = `
29+
<joint name="test_joint" type="revolute">
30+
<parent link="link1"/>
31+
<child link="link2"/>
32+
</joint>
33+
`;
34+
const parser = new DOMParser();
35+
const xml = parser.parseFromString(
36+
jointNoAxisUrdf,
37+
"text/xml",
38+
).documentElement;
39+
if (!xml) {
40+
throw new Error("Failed to parse XML");
41+
}
42+
const joint = new UrdfJoint({ xml });
43+
expect(joint.axis.x).toBe(1);
44+
expect(joint.axis.y).toBe(0);
45+
expect(joint.axis.z).toBe(0);
46+
});
47+
48+
it("should throw if axis xyz is malformed", () => {
49+
const jointMalformedAxisUrdf = `
50+
<joint name="test_joint" type="revolute">
51+
<parent link="link1"/>
52+
<child link="link2"/>
53+
<axis xyz="malformed data"/>
54+
</joint>
55+
`;
56+
const parser = new DOMParser();
57+
const xml = parser.parseFromString(
58+
jointMalformedAxisUrdf,
59+
"text/xml",
60+
).documentElement;
61+
if (!xml) {
62+
throw new Error("Failed to parse XML");
63+
}
64+
expect(() => new UrdfJoint({ xml })).toThrowError(
65+
"If specified, axis must have an xyz value composed of three numbers",
66+
);
67+
});
68+
});

0 commit comments

Comments
 (0)