@holoscript/robotics-plugin 2.0.1 → 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +9 -9
- package/src/__tests__/isaac-lab-interop.test.ts +13 -10
- package/src/ast.ts +6 -1
- package/src/index.ts +32 -22
- package/src/parser.ts +19 -7
- package/src/traits/ROS2HardwareLoopTrait.ts +29 -14
- package/src/usd-codegen.ts +43 -14
- package/LICENSE +0 -21
- package/dist/ast.d.ts +0 -69
- package/dist/ast.js +0 -9
- package/dist/index.d.ts +0 -127
- package/dist/index.js +0 -349
- package/dist/lexer.d.ts +0 -52
- package/dist/lexer.js +0 -251
- package/dist/parser.d.ts +0 -30
- package/dist/parser.js +0 -376
- package/dist/traits/ROS2HardwareLoopTrait.d.ts +0 -15
- package/dist/traits/ROS2HardwareLoopTrait.js +0 -49
- package/dist/traits/types.d.ts +0 -4
- package/dist/traits/types.js +0 -1
- package/dist/usd-codegen.d.ts +0 -45
- package/dist/usd-codegen.js +0 -504
package/package.json
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@holoscript/robotics-plugin",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.2",
|
|
4
4
|
"description": "HoloScript plugin for robotics: compile-time USD/URDF codegen + runtime ROS2/Gazebo integration. VR robot programming and digital twins.",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "tsc",
|
|
8
|
+
"test": "vitest run",
|
|
9
|
+
"clean": "rimraf dist",
|
|
10
|
+
"test:coverage": "vitest run --coverage"
|
|
11
|
+
},
|
|
6
12
|
"keywords": [
|
|
7
13
|
"holoscript",
|
|
8
14
|
"plugin",
|
|
@@ -17,15 +23,9 @@
|
|
|
17
23
|
"author": "HoloScript Contributors",
|
|
18
24
|
"license": "MIT",
|
|
19
25
|
"peerDependencies": {
|
|
20
|
-
"@holoscript/core": "8.0.
|
|
26
|
+
"@holoscript/core": ">=8.0.0"
|
|
21
27
|
},
|
|
22
28
|
"engines": {
|
|
23
29
|
"node": ">=20.0.0"
|
|
24
|
-
},
|
|
25
|
-
"scripts": {
|
|
26
|
-
"build": "tsc",
|
|
27
|
-
"test": "vitest run",
|
|
28
|
-
"clean": "rimraf dist",
|
|
29
|
-
"test:coverage": "vitest run --coverage"
|
|
30
30
|
}
|
|
31
|
-
}
|
|
31
|
+
}
|
|
@@ -92,12 +92,7 @@ describe('Isaac Lab Interop (Path A)', () => {
|
|
|
92
92
|
expect(ast.domainRandomization?.actuator?.kdNoise).toBe(0.05);
|
|
93
93
|
expect(ast.domainRandomization?.initialState?.jointPosRange?.joint1).toEqual([-0.1, 0.1]);
|
|
94
94
|
expect(ast.domainRandomization?.initialState?.rootPoseRange).toEqual([
|
|
95
|
-
-0.5,
|
|
96
|
-
0.5,
|
|
97
|
-
-0.5,
|
|
98
|
-
0.5,
|
|
99
|
-
0,
|
|
100
|
-
1,
|
|
95
|
+
-0.5, 0.5, -0.5, 0.5, 0, 1,
|
|
101
96
|
]);
|
|
102
97
|
});
|
|
103
98
|
|
|
@@ -133,8 +128,12 @@ describe('Isaac Lab Interop (Path A)', () => {
|
|
|
133
128
|
expect(usd).toContain('# Generated for Isaac Lab 2.3');
|
|
134
129
|
expect(usd).toContain('metersPerUnit = 1.0');
|
|
135
130
|
expect(usd).toContain('kilogramsPerMass = 1.0');
|
|
136
|
-
expect(usd).toContain(
|
|
137
|
-
|
|
131
|
+
expect(usd).toContain(
|
|
132
|
+
'# Units: meters, kilograms, seconds; HoloScript angular inputs are radians.'
|
|
133
|
+
);
|
|
134
|
+
expect(usd).toContain(
|
|
135
|
+
'prepend apiSchemas = ["PhysicsArticulationRootAPI", "PhysxArticulationAPI"]'
|
|
136
|
+
);
|
|
138
137
|
expect(usd).toContain('bool physxArticulation:articulationEnabled = true');
|
|
139
138
|
expect(usd).not.toContain('physxArticulation:jointFriction');
|
|
140
139
|
});
|
|
@@ -142,7 +141,9 @@ describe('Isaac Lab Interop (Path A)', () => {
|
|
|
142
141
|
it('emits applied DriveAPI and PhysxJointAxisAPI schemas on actuated joints', () => {
|
|
143
142
|
const usd = new USDCodeGen().generate(parseHoloScript());
|
|
144
143
|
|
|
145
|
-
expect(usd).toContain(
|
|
144
|
+
expect(usd).toContain(
|
|
145
|
+
'prepend apiSchemas = ["PhysicsDriveAPI:angular", "PhysxJointAxisAPI:angular"]'
|
|
146
|
+
);
|
|
146
147
|
expect(usd).toContain('float drive:angular:physics:stiffness = 100');
|
|
147
148
|
expect(usd).toContain('float drive:angular:physics:damping = 10');
|
|
148
149
|
expect(usd).toContain('float drive:angular:physics:maxForce = 50');
|
|
@@ -173,7 +174,9 @@ describe('Isaac Lab Interop (Path A)', () => {
|
|
|
173
174
|
expect(usd).toContain('# kpNoise: 0.1');
|
|
174
175
|
expect(usd).toContain('# kdNoise: 0.05');
|
|
175
176
|
expect(usd).toContain('custom float holoscript:isaacLab:actuatorLatencySeconds = 0.005');
|
|
176
|
-
expect(usd).toContain(
|
|
177
|
+
expect(usd).toContain(
|
|
178
|
+
'# arm_group: type=DelayedPDActuator joints=[joint1, joint2, joint3]'
|
|
179
|
+
);
|
|
177
180
|
});
|
|
178
181
|
|
|
179
182
|
it('disables DriveAPI and PhysX joint-axis output when configured off', () => {
|
package/src/ast.ts
CHANGED
|
@@ -43,7 +43,12 @@ export interface DomainRandomizationConfig {
|
|
|
43
43
|
|
|
44
44
|
export interface ActuatorGroupConfig {
|
|
45
45
|
name: string;
|
|
46
|
-
type:
|
|
46
|
+
type:
|
|
47
|
+
| 'IdealPDActuator'
|
|
48
|
+
| 'DCMotorActuator'
|
|
49
|
+
| 'DelayedPDActuator'
|
|
50
|
+
| 'RemotizedPDActuator'
|
|
51
|
+
| 'ImplicitActuator';
|
|
47
52
|
jointNames: string[];
|
|
48
53
|
stiffness?: number;
|
|
49
54
|
damping?: number;
|
package/src/index.ts
CHANGED
|
@@ -148,15 +148,16 @@ function asString(value: unknown, fallback: string): string {
|
|
|
148
148
|
|
|
149
149
|
function asVec3(value: unknown, fallback: [number, number, number]): [number, number, number] {
|
|
150
150
|
if (Array.isArray(value) && value.length >= 3) {
|
|
151
|
-
return [
|
|
151
|
+
return [
|
|
152
|
+
asNumber(value[0], fallback[0]),
|
|
153
|
+
asNumber(value[1], fallback[1]),
|
|
154
|
+
asNumber(value[2], fallback[2]),
|
|
155
|
+
];
|
|
152
156
|
}
|
|
153
157
|
return fallback;
|
|
154
158
|
}
|
|
155
159
|
|
|
156
|
-
function asJointLimits(
|
|
157
|
-
value: unknown,
|
|
158
|
-
fallback: RobotJoint['limits']
|
|
159
|
-
): RobotJoint['limits'] {
|
|
160
|
+
function asJointLimits(value: unknown, fallback: RobotJoint['limits']): RobotJoint['limits'] {
|
|
160
161
|
if (Array.isArray(value) && value.length >= 4) {
|
|
161
162
|
return {
|
|
162
163
|
lower: asNumber(value[0], fallback?.lower ?? -1.57),
|
|
@@ -179,7 +180,10 @@ function asJointLimits(
|
|
|
179
180
|
return fallback;
|
|
180
181
|
}
|
|
181
182
|
|
|
182
|
-
function findTrait(
|
|
183
|
+
function findTrait(
|
|
184
|
+
node: HoloCompositionNodeLike,
|
|
185
|
+
predicate: (normalizedName: string) => boolean
|
|
186
|
+
): TraitMatch | null {
|
|
183
187
|
for (const trait of node.traits ?? []) {
|
|
184
188
|
const normalizedName = normalizeTraitName(trait.name);
|
|
185
189
|
if (predicate(normalizedName)) {
|
|
@@ -193,11 +197,7 @@ function findTrait(node: HoloCompositionNodeLike, predicate: (normalizedName: st
|
|
|
193
197
|
function getJointTrait(node: HoloCompositionNodeLike): TraitMatch | null {
|
|
194
198
|
return findTrait(
|
|
195
199
|
node,
|
|
196
|
-
(name) =>
|
|
197
|
-
name === 'joint' ||
|
|
198
|
-
name === 'hinge' ||
|
|
199
|
-
name === 'slider' ||
|
|
200
|
-
name.startsWith('joint_')
|
|
200
|
+
(name) => name === 'joint' || name === 'hinge' || name === 'slider' || name.startsWith('joint_')
|
|
201
201
|
);
|
|
202
202
|
}
|
|
203
203
|
|
|
@@ -213,7 +213,9 @@ function jointTypeFromTrait(
|
|
|
213
213
|
nodeProps: Record<string, unknown>,
|
|
214
214
|
defaultJointType: RobotJoint['type']
|
|
215
215
|
): RobotJoint['type'] {
|
|
216
|
-
const explicitType = normalizeTraitName(
|
|
216
|
+
const explicitType = normalizeTraitName(
|
|
217
|
+
asString(nodeProps.joint_type ?? nodeProps.type, defaultJointType)
|
|
218
|
+
);
|
|
217
219
|
|
|
218
220
|
switch (explicitType) {
|
|
219
221
|
case 'revolute':
|
|
@@ -246,7 +248,9 @@ function jointTypeFromTrait(
|
|
|
246
248
|
function hardwareInterfaceFromMotor(
|
|
247
249
|
motorProps: Record<string, unknown>
|
|
248
250
|
): RobotTransmission['hardwareInterface'] {
|
|
249
|
-
const commandMode = normalizeTraitName(
|
|
251
|
+
const commandMode = normalizeTraitName(
|
|
252
|
+
asString(motorProps.commandMode ?? motorProps.mode, 'position')
|
|
253
|
+
);
|
|
250
254
|
|
|
251
255
|
if (commandMode === 'velocity') return 'velocity';
|
|
252
256
|
if (commandMode === 'effort' || commandMode === 'torque') return 'effort';
|
|
@@ -291,8 +295,14 @@ function createTransmission(
|
|
|
291
295
|
return {
|
|
292
296
|
name: sanitizeRobotName(asString(motorProps.name, `${jointName}_transmission`)),
|
|
293
297
|
joint: jointName,
|
|
294
|
-
actuator: sanitizeRobotName(
|
|
295
|
-
|
|
298
|
+
actuator: sanitizeRobotName(
|
|
299
|
+
asString(motorProps.actuator ?? motorProps.name, `${jointName}_motor`)
|
|
300
|
+
),
|
|
301
|
+
mechanicalReduction: Number(
|
|
302
|
+
(asNumber(motorProps.mechanicalReduction ?? motorProps.gearRatio, 1) * hardwareScale).toFixed(
|
|
303
|
+
6
|
|
304
|
+
)
|
|
305
|
+
),
|
|
296
306
|
hardwareInterface: hardwareInterfaceFromMotor(motorProps),
|
|
297
307
|
};
|
|
298
308
|
}
|
|
@@ -321,7 +331,9 @@ function buildLinkXml(link: RobotLink): string {
|
|
|
321
331
|
|
|
322
332
|
if (link.collision) {
|
|
323
333
|
lines.push(' <collision>');
|
|
324
|
-
lines.push(
|
|
334
|
+
lines.push(
|
|
335
|
+
` <geometry><mesh filename="${xmlEscape(link.collision.geometry)}"/></geometry>`
|
|
336
|
+
);
|
|
325
337
|
lines.push(' </collision>');
|
|
326
338
|
}
|
|
327
339
|
|
|
@@ -383,10 +395,7 @@ export function extractURDFFromHoloComposition(
|
|
|
383
395
|
const joints: RobotJoint[] = [];
|
|
384
396
|
const transmissions: RobotTransmission[] = [];
|
|
385
397
|
|
|
386
|
-
const visitNode = (
|
|
387
|
-
node: HoloCompositionNodeLike,
|
|
388
|
-
parentLinkName: string | null
|
|
389
|
-
) => {
|
|
398
|
+
const visitNode = (node: HoloCompositionNodeLike, parentLinkName: string | null) => {
|
|
390
399
|
const props = asRecord(node.properties);
|
|
391
400
|
const link = mapNodeToRobotLink(node, hardwareScale);
|
|
392
401
|
links.push(link);
|
|
@@ -529,7 +538,7 @@ export function generateIsaacLabFeed(
|
|
|
529
538
|
};
|
|
530
539
|
|
|
531
540
|
// Simple content hashes for the receipt (evidence loop)
|
|
532
|
-
const sourceHoloHash = `holo:${ast.name}:${
|
|
541
|
+
const sourceHoloHash = `holo:${ast.name}:${ast.objects?.length || 0}`;
|
|
533
542
|
const usdHash = `usd:${usd.length}`;
|
|
534
543
|
const configHash = `cfg:${JSON.stringify(taskConfig).length}`;
|
|
535
544
|
|
|
@@ -540,7 +549,8 @@ export function generateIsaacLabFeed(
|
|
|
540
549
|
generatedAt: new Date().toISOString(),
|
|
541
550
|
isaacLabVersion: version,
|
|
542
551
|
readyForTraining: true,
|
|
543
|
-
notes:
|
|
552
|
+
notes:
|
|
553
|
+
'Narrow feed bundle — assets + randomization + actuator config. Policy training & real-robot eval are downstream.',
|
|
544
554
|
};
|
|
545
555
|
|
|
546
556
|
return {
|
package/src/parser.ts
CHANGED
|
@@ -6,7 +6,13 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { Token, TokenType } from './lexer';
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
CompositionNode,
|
|
11
|
+
ObjectNode,
|
|
12
|
+
PropertyValue,
|
|
13
|
+
DomainRandomizationConfig,
|
|
14
|
+
ActuatorGroupConfig,
|
|
15
|
+
} from './ast';
|
|
10
16
|
|
|
11
17
|
export class Parser {
|
|
12
18
|
private tokens: Token[];
|
|
@@ -74,8 +80,10 @@ export class Parser {
|
|
|
74
80
|
this.currentToken().type !== TokenType.EOF
|
|
75
81
|
) {
|
|
76
82
|
// Check for domain_randomization block at composition level
|
|
77
|
-
if (
|
|
78
|
-
|
|
83
|
+
if (
|
|
84
|
+
this.currentToken().type === TokenType.IDENTIFIER &&
|
|
85
|
+
this.currentToken().value === 'domain_randomization'
|
|
86
|
+
) {
|
|
79
87
|
this.advance();
|
|
80
88
|
this.expect(TokenType.COLON);
|
|
81
89
|
domainRandomization = this.parseDomainRandomizationBlock();
|
|
@@ -125,15 +133,19 @@ export class Parser {
|
|
|
125
133
|
}
|
|
126
134
|
}
|
|
127
135
|
// Check for domain_randomization block
|
|
128
|
-
else if (
|
|
129
|
-
|
|
136
|
+
else if (
|
|
137
|
+
this.currentToken().type === TokenType.IDENTIFIER &&
|
|
138
|
+
this.currentToken().value === 'domain_randomization'
|
|
139
|
+
) {
|
|
130
140
|
this.advance();
|
|
131
141
|
this.expect(TokenType.COLON);
|
|
132
142
|
domainRandomization = this.parseDomainRandomizationBlock();
|
|
133
143
|
}
|
|
134
144
|
// Check for actuator_group block
|
|
135
|
-
else if (
|
|
136
|
-
|
|
145
|
+
else if (
|
|
146
|
+
this.currentToken().type === TokenType.IDENTIFIER &&
|
|
147
|
+
this.currentToken().value === 'actuator_group'
|
|
148
|
+
) {
|
|
137
149
|
this.advance();
|
|
138
150
|
this.expect(TokenType.COLON);
|
|
139
151
|
const group = this.parseActuatorGroupBlock();
|
|
@@ -18,9 +18,19 @@ import type { TraitHandler, HSPlusNode, TraitContext, TraitEvent } from './types
|
|
|
18
18
|
export function createROS2HardwareLoopHandler(): TraitHandler<ROS2Config> {
|
|
19
19
|
return {
|
|
20
20
|
name: 'ros2_hardware_loop',
|
|
21
|
-
defaultConfig: {
|
|
21
|
+
defaultConfig: {
|
|
22
|
+
nodeName: 'holo_rig_01',
|
|
23
|
+
topicPrefix: '/holo',
|
|
24
|
+
updateFrequencyHz: 60,
|
|
25
|
+
bidirectional: true,
|
|
26
|
+
},
|
|
22
27
|
onAttach(n: unknown, c: ROS2Config, ctx: unknown) {
|
|
23
|
-
(n as any).__ros2State = {
|
|
28
|
+
(n as any).__ros2State = {
|
|
29
|
+
connected: false,
|
|
30
|
+
lastPingMs: 0,
|
|
31
|
+
activeTopics: [],
|
|
32
|
+
hardwareSyncDriftMs: 0,
|
|
33
|
+
};
|
|
24
34
|
(ctx as any).emit?.('ros2:init', { node: c.nodeName });
|
|
25
35
|
},
|
|
26
36
|
onDetach(n: unknown, _c: ROS2Config, ctx: unknown) {
|
|
@@ -32,38 +42,43 @@ export function createROS2HardwareLoopHandler(): TraitHandler<ROS2Config> {
|
|
|
32
42
|
const s = (n as any).__ros2State as ROS2State;
|
|
33
43
|
if (!s) return;
|
|
34
44
|
const evt = e as any;
|
|
35
|
-
|
|
45
|
+
|
|
36
46
|
switch (evt.type) {
|
|
37
47
|
case 'ros2:connect':
|
|
38
48
|
s.connected = true;
|
|
39
49
|
// In real usage, initializes rclnodejs bridge for pub/sub physics
|
|
40
50
|
(ctx as any).emit?.('ros2:connected', { latencyMs: 12 });
|
|
41
51
|
break;
|
|
42
|
-
|
|
52
|
+
|
|
43
53
|
case 'scene:transform_change':
|
|
44
54
|
if (c.bidirectional && s.connected) {
|
|
45
55
|
// Push virtual set transform to physical robot joint
|
|
46
|
-
(ctx as any).emit?.('ros2:publish', {
|
|
56
|
+
(ctx as any).emit?.('ros2:publish', {
|
|
57
|
+
topic: `${c.topicPrefix}/joint_cmd`,
|
|
58
|
+
payload: evt.payload,
|
|
59
|
+
});
|
|
47
60
|
}
|
|
48
|
-
|
|
49
|
-
|
|
61
|
+
break;
|
|
62
|
+
|
|
50
63
|
case 'ros2:telemetry':
|
|
51
64
|
// Receive physical robot telemetry to drive virtual set
|
|
52
65
|
s.hardwareSyncDriftMs = Math.abs(Date.now() - (evt.payload.timestamp || Date.now()));
|
|
53
|
-
(n as any).transform = evt.payload.transform;
|
|
66
|
+
(n as any).transform = evt.payload.transform;
|
|
54
67
|
break;
|
|
55
|
-
|
|
68
|
+
|
|
56
69
|
case 'ttu:manifested':
|
|
57
70
|
// Dynamically bind to Text-To-Universe AST streams
|
|
58
71
|
const crdtRoot = evt.payload?.crdtRoot;
|
|
59
72
|
if (crdtRoot && s.connected) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
73
|
+
const dynamicTopic = `${c.topicPrefix}/ttu_sync/${crdtRoot}`;
|
|
74
|
+
s.activeTopics.push(dynamicTopic);
|
|
75
|
+
(ctx as any).emit?.('ros2:subscribe', { topic: dynamicTopic });
|
|
76
|
+
(ctx as any).emit?.('ros2:log', {
|
|
77
|
+
message: `Bridged digital twin for TTU root: ${crdtRoot}`,
|
|
78
|
+
});
|
|
64
79
|
}
|
|
65
80
|
break;
|
|
66
81
|
}
|
|
67
|
-
}
|
|
82
|
+
},
|
|
68
83
|
};
|
|
69
84
|
}
|
package/src/usd-codegen.ts
CHANGED
|
@@ -61,7 +61,9 @@ export class USDCodeGen {
|
|
|
61
61
|
// Isaac Lab header comment
|
|
62
62
|
this.emit(`# Generated for Isaac Lab ${this.config.isaacLabVersion}`);
|
|
63
63
|
this.emit('# Units: meters, kilograms, seconds; HoloScript angular inputs are radians.');
|
|
64
|
-
this.emit(
|
|
64
|
+
this.emit(
|
|
65
|
+
'# USD angular joint limits and velocities are exported in degrees per OpenUSD/PhysX.'
|
|
66
|
+
);
|
|
65
67
|
|
|
66
68
|
// Emit domain randomization config as USD comments (for Isaac Lab Python codegen)
|
|
67
69
|
if (ast.domainRandomization) {
|
|
@@ -316,7 +318,9 @@ export class USDCodeGen {
|
|
|
316
318
|
const schemas = this.getJointApiSchemas(joint, axisToken);
|
|
317
319
|
|
|
318
320
|
this.emit('');
|
|
319
|
-
this.emit(
|
|
321
|
+
this.emit(
|
|
322
|
+
`def ${joint.kind === 'prismatic' ? 'PhysicsPrismaticJoint' : 'PhysicsRevoluteJoint'} "${joint.name}"`
|
|
323
|
+
);
|
|
320
324
|
if (schemas.length > 0) {
|
|
321
325
|
this.emit('(');
|
|
322
326
|
this.indentLevel++;
|
|
@@ -361,8 +365,12 @@ export class USDCodeGen {
|
|
|
361
365
|
const limits = joint.props.joint_limits || joint.props.limits;
|
|
362
366
|
if (limits) {
|
|
363
367
|
const limitsVec = limits as number[];
|
|
364
|
-
this.emit(
|
|
365
|
-
|
|
368
|
+
this.emit(
|
|
369
|
+
`float physics:lowerLimit = ${this.formatNumber(this.convertJointScalar(limitsVec[0], joint.kind))}`
|
|
370
|
+
);
|
|
371
|
+
this.emit(
|
|
372
|
+
`float physics:upperLimit = ${this.formatNumber(this.convertJointScalar(limitsVec[1], joint.kind))}`
|
|
373
|
+
);
|
|
366
374
|
}
|
|
367
375
|
this.emit('');
|
|
368
376
|
|
|
@@ -374,7 +382,9 @@ export class USDCodeGen {
|
|
|
374
382
|
|
|
375
383
|
const velocity = this.numberProp(joint.props, 'max_velocity');
|
|
376
384
|
if (velocity !== undefined) {
|
|
377
|
-
this.emit(
|
|
385
|
+
this.emit(
|
|
386
|
+
`float physics:maxVelocity = ${this.formatNumber(this.convertJointScalar(velocity, joint.kind))}`
|
|
387
|
+
);
|
|
378
388
|
}
|
|
379
389
|
|
|
380
390
|
// OpenUSD PhysicsDriveAPI attributes for PD actuator control.
|
|
@@ -406,7 +416,9 @@ export class USDCodeGen {
|
|
|
406
416
|
const latency = this.numberProp(joint.props, 'actuator_latency', 'latency');
|
|
407
417
|
if (latency !== undefined) {
|
|
408
418
|
this.emit('');
|
|
409
|
-
this.emit(
|
|
419
|
+
this.emit(
|
|
420
|
+
'# Isaac Lab delayed actuator hint; convert seconds to delay steps in task config.'
|
|
421
|
+
);
|
|
410
422
|
this.emit(`custom float holoscript:isaacLab:actuatorLatencySeconds = ${latency}`);
|
|
411
423
|
}
|
|
412
424
|
|
|
@@ -468,7 +480,10 @@ export class USDCodeGen {
|
|
|
468
480
|
}
|
|
469
481
|
|
|
470
482
|
private hasDriveProperties(props: Record<string, PropertyValue>): boolean {
|
|
471
|
-
return
|
|
483
|
+
return (
|
|
484
|
+
this.numberProp(props, 'kp', 'stiffness', 'kd', 'damping', 'joint_effort', 'max_effort') !==
|
|
485
|
+
undefined
|
|
486
|
+
);
|
|
472
487
|
}
|
|
473
488
|
|
|
474
489
|
private hasPhysxJointAxisProperties(props: Record<string, PropertyValue>): boolean {
|
|
@@ -505,13 +520,17 @@ export class USDCodeGen {
|
|
|
505
520
|
this.emit(`float physxJointAxis:${axisToken}:dynamicFrictionEffort = ${dynamicFriction}`);
|
|
506
521
|
}
|
|
507
522
|
if (viscousFriction !== undefined) {
|
|
508
|
-
this.emit(
|
|
523
|
+
this.emit(
|
|
524
|
+
`float physxJointAxis:${axisToken}:viscousFrictionCoefficient = ${viscousFriction}`
|
|
525
|
+
);
|
|
509
526
|
}
|
|
510
527
|
if (armature !== undefined) {
|
|
511
528
|
this.emit(`float physxJointAxis:${axisToken}:armature = ${armature}`);
|
|
512
529
|
}
|
|
513
530
|
if (velocity !== undefined) {
|
|
514
|
-
this.emit(
|
|
531
|
+
this.emit(
|
|
532
|
+
`float physxJointAxis:${axisToken}:maxJointVelocity = ${this.formatNumber(this.convertJointScalar(velocity, kind))}`
|
|
533
|
+
);
|
|
515
534
|
}
|
|
516
535
|
}
|
|
517
536
|
|
|
@@ -570,13 +589,19 @@ export class USDCodeGen {
|
|
|
570
589
|
this.emit(`# massScale: [${dr.physics.massScale[0]}, ${dr.physics.massScale[1]}]`);
|
|
571
590
|
}
|
|
572
591
|
if (dr.physics.frictionRange) {
|
|
573
|
-
this.emit(
|
|
592
|
+
this.emit(
|
|
593
|
+
`# frictionRange: [${dr.physics.frictionRange[0]}, ${dr.physics.frictionRange[1]}]`
|
|
594
|
+
);
|
|
574
595
|
}
|
|
575
596
|
if (dr.physics.dampingRange) {
|
|
576
|
-
this.emit(
|
|
597
|
+
this.emit(
|
|
598
|
+
`# dampingRange: [${dr.physics.dampingRange[0]}, ${dr.physics.dampingRange[1]}]`
|
|
599
|
+
);
|
|
577
600
|
}
|
|
578
601
|
if (dr.physics.armatureRange) {
|
|
579
|
-
this.emit(
|
|
602
|
+
this.emit(
|
|
603
|
+
`# armatureRange: [${dr.physics.armatureRange[0]}, ${dr.physics.armatureRange[1]}]`
|
|
604
|
+
);
|
|
580
605
|
}
|
|
581
606
|
}
|
|
582
607
|
if (dr.actuator) {
|
|
@@ -612,10 +637,14 @@ export class USDCodeGen {
|
|
|
612
637
|
if (dr.disturbance) {
|
|
613
638
|
this.emit('# disturbance:');
|
|
614
639
|
if (dr.disturbance.forceRange) {
|
|
615
|
-
this.emit(
|
|
640
|
+
this.emit(
|
|
641
|
+
`# forceRange: [${dr.disturbance.forceRange[0]}, ${dr.disturbance.forceRange[1]}]`
|
|
642
|
+
);
|
|
616
643
|
}
|
|
617
644
|
if (dr.disturbance.intervalRange) {
|
|
618
|
-
this.emit(
|
|
645
|
+
this.emit(
|
|
646
|
+
`# intervalRange: [${dr.disturbance.intervalRange[0]}, ${dr.disturbance.intervalRange[1]}]`
|
|
647
|
+
);
|
|
619
648
|
}
|
|
620
649
|
}
|
|
621
650
|
}
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2025-2026 HoloScript Contributors
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
package/dist/ast.d.ts
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Abstract Syntax Tree (AST) node types for HoloScript robotics
|
|
3
|
-
*
|
|
4
|
-
* Extended for Isaac Lab sim-to-real interop (Path A):
|
|
5
|
-
* - Domain randomization blocks
|
|
6
|
-
* - Actuator group configurations
|
|
7
|
-
* - Delayed actuator export hints
|
|
8
|
-
*/
|
|
9
|
-
export type PropertyValue = string | number | boolean | number[] | PropertyValue[];
|
|
10
|
-
export interface DomainRandomizationConfig {
|
|
11
|
-
/** Physics randomization: mass, friction, damping */
|
|
12
|
-
physics?: {
|
|
13
|
-
massScale?: [number, number];
|
|
14
|
-
frictionRange?: [number, number];
|
|
15
|
-
dampingRange?: [number, number];
|
|
16
|
-
armatureRange?: [number, number];
|
|
17
|
-
};
|
|
18
|
-
/** Actuator randomization */
|
|
19
|
-
actuator?: {
|
|
20
|
-
kpNoise?: number;
|
|
21
|
-
kdNoise?: number;
|
|
22
|
-
latencyNoise?: number;
|
|
23
|
-
};
|
|
24
|
-
/** Observation noise */
|
|
25
|
-
observation?: {
|
|
26
|
-
jointPosNoise?: number;
|
|
27
|
-
jointVelNoise?: number;
|
|
28
|
-
imuNoise?: number;
|
|
29
|
-
};
|
|
30
|
-
/** Initial state randomization */
|
|
31
|
-
initialState?: {
|
|
32
|
-
jointPosRange?: Record<string, [number, number]>;
|
|
33
|
-
rootPoseRange?: [number, number, number, number, number, number];
|
|
34
|
-
};
|
|
35
|
-
/** Disturbance forces */
|
|
36
|
-
disturbance?: {
|
|
37
|
-
forceRange?: [number, number];
|
|
38
|
-
intervalRange?: [number, number];
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
export interface ActuatorGroupConfig {
|
|
42
|
-
name: string;
|
|
43
|
-
type: 'IdealPDActuator' | 'DCMotorActuator' | 'DelayedPDActuator' | 'RemotizedPDActuator' | 'ImplicitActuator';
|
|
44
|
-
jointNames: string[];
|
|
45
|
-
stiffness?: number;
|
|
46
|
-
damping?: number;
|
|
47
|
-
friction?: number;
|
|
48
|
-
latency?: number;
|
|
49
|
-
}
|
|
50
|
-
export interface ObjectNode {
|
|
51
|
-
type: 'object';
|
|
52
|
-
name: string;
|
|
53
|
-
traits: string[];
|
|
54
|
-
properties: Record<string, PropertyValue>;
|
|
55
|
-
domainRandomization?: DomainRandomizationConfig;
|
|
56
|
-
actuatorGroups?: ActuatorGroupConfig[];
|
|
57
|
-
template?: string;
|
|
58
|
-
line?: number;
|
|
59
|
-
column?: number;
|
|
60
|
-
}
|
|
61
|
-
export interface CompositionNode {
|
|
62
|
-
type: 'composition';
|
|
63
|
-
name: string;
|
|
64
|
-
objects: ObjectNode[];
|
|
65
|
-
domainRandomization?: DomainRandomizationConfig;
|
|
66
|
-
line?: number;
|
|
67
|
-
column?: number;
|
|
68
|
-
}
|
|
69
|
-
export type ASTNode = CompositionNode | ObjectNode;
|
package/dist/ast.js
DELETED