diff --git a/.gitattributes b/.gitattributes index a5a27eed6bf..4e651ca8b40 100644 --- a/.gitattributes +++ b/.gitattributes @@ -32,6 +32,11 @@ docs/assets/favicon.png -filter -diff -merge misc/DimSim/public/sims/*.json filter=lfs diff=lfs merge=lfs -text misc/DimSim/public/embodiment/*.glb filter=lfs diff=lfs merge=lfs -text misc/DimSim/scenes/**/*.glb filter=lfs diff=lfs merge=lfs -text +# A vendored robot's meshes. They were committed as pointer text with no filter on the path, so +# a clone got the pointers and nothing turned them back into meshes -- the sim then refused the +# robot's first body with "at least 4 vertices required". The layer beside them stays text and +# out of LFS on purpose (see pyproject): a scene travels as a string, so it has to be readable. +dimos/robot/*/*/assets/meshes/** filter=lfs diff=lfs merge=lfs -text binary misc/DimSim/scenes/**/*.gltf filter=lfs diff=lfs merge=lfs -text # Machine-generated, and reviewed as artifacts rather than as source: a lock file # is cargo's output, and motion's planner crate is what an autoresearch lab diff --git a/dimos/memory2/db_tf.py b/dimos/memory2/db_tf.py new file mode 100644 index 00000000000..b590dee85d6 --- /dev/null +++ b/dimos/memory2/db_tf.py @@ -0,0 +1,23 @@ +# Copyright 2026 Dimensional Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +from __future__ import annotations + +from typing import Any + + +class TfGraph: + def __init__(self, *_args: Any, **_kwargs: Any) -> None: + pass diff --git a/dimos/robot/all_blueprints.py b/dimos/robot/all_blueprints.py index 54d42921e1d..5f018525518 100644 --- a/dimos/robot/all_blueprints.py +++ b/dimos/robot/all_blueprints.py @@ -194,6 +194,7 @@ "unitree-go2-rpp-benchmark": "dimos.robot.unitree.go2.blueprints.basic.unitree_go2_rpp_benchmark:unitree_go2_rpp_benchmark", "unitree-go2-rpp-controller": "dimos.robot.unitree.go2.blueprints.basic.unitree_go2_rpp_controller:unitree_go2_rpp_controller", "unitree-go2-security": "dimos.robot.unitree.go2.blueprints.agentic.unitree_go2_security:unitree_go2_security", + "unitree-go2-sim2-l1": "dimos.robot.unitree.go2.blueprints.basic.unitree_go2_sim2_l1:unitree_go2_sim2_l1", "unitree-go2-spatial": "dimos.robot.unitree.go2.blueprints.smart.unitree_go2_spatial:unitree_go2_spatial", "unitree-go2-temporal-memory": "dimos.robot.unitree.go2.blueprints.agentic.unitree_go2_temporal_memory:unitree_go2_temporal_memory", "unitree-go2-typesafe": "dimos.robot.unitree.go2.blueprints.agentic.unitree_go2_typesafe:unitree_go2_typesafe", @@ -376,6 +377,7 @@ "sim-camera": "dimos.sim2.camera.SimCamera", "sim-go2-world": "dimos.simulation.go2_sim.world.SimGo2World", "sim-imu": "dimos.sim2.imu.SimImu", + "sim-l1": "dimos.sim2.l1.SimL1", "sim-m20-world": "dimos.simulation.m20_sim.world.SimM20World", "simple-phone-teleop": "dimos.teleop.phone.phone_extensions.SimplePhoneTeleop", "spatial-memory": "dimos.perception.experimental.spatial_perception.SpatialMemory", diff --git a/dimos/robot/unitree/go2/assets/go2.usda b/dimos/robot/unitree/go2/assets/go2.usda new file mode 100644 index 00000000000..dc083a7ceab --- /dev/null +++ b/dimos/robot/unitree/go2/assets/go2.usda @@ -0,0 +1,2540 @@ +#usda 1.0 +( + customLayerData = { + string creator = "MuJoCo USD Converter v0.9.0" + } + defaultPrim = "go2" + doc = """Generated from Composed Stage of root layer /tmp/claude-1000/-home-dimos/ff9ad774-151d-43c4-9774-fa96d8ad90f5/scratchpad/go2usd/converted/go2.usda +""" + kilogramsPerUnit = 1 + metersPerUnit = 1 + upAxis = "Z" +) + +over "Flattened_Prototype_1" +{ + def Shader "PreviewSurface" + { + uniform token info:id = "UsdPreviewSurface" + color3f inputs:diffuseColor.connect = + float inputs:metallic.connect = + float inputs:opacity.connect = + float inputs:roughness.connect = + token outputs:surface + } + + def Shader "OpenPBR" + { + uniform token info:id = "ND_open_pbr_surface_surfaceshader" + color3f inputs:base_color.connect = + float inputs:base_metalness.connect = + float inputs:geometry_opacity.connect = + float inputs:specular_roughness.connect = + token outputs:out + } +} + +over "Flattened_Prototype_2" +{ + def Shader "PreviewSurface" + { + uniform token info:id = "UsdPreviewSurface" + color3f inputs:diffuseColor.connect = + float inputs:metallic.connect = + float inputs:opacity.connect = + float inputs:roughness.connect = + token outputs:surface + } + + def Shader "OpenPBR" + { + uniform token info:id = "ND_open_pbr_surface_surfaceshader" + color3f inputs:base_color.connect = + float inputs:base_metalness.connect = + float inputs:geometry_opacity.connect = + float inputs:specular_roughness.connect = + token outputs:out + } +} + +over "Flattened_Prototype_3" +{ + def Shader "PreviewSurface" + { + uniform token info:id = "UsdPreviewSurface" + color3f inputs:diffuseColor.connect = + float inputs:metallic.connect = + float inputs:opacity.connect = + float inputs:roughness.connect = + token outputs:surface + } + + def Shader "OpenPBR" + { + uniform token info:id = "ND_open_pbr_surface_surfaceshader" + color3f inputs:base_color.connect = + float inputs:base_metalness.connect = + float inputs:geometry_opacity.connect = + float inputs:specular_roughness.connect = + token outputs:out + } +} + +over "Flattened_Prototype_4" +{ + def Shader "PreviewSurface" + { + uniform token info:id = "UsdPreviewSurface" + color3f inputs:diffuseColor.connect = + float inputs:metallic.connect = + float inputs:opacity.connect = + float inputs:roughness.connect = + token outputs:surface + } + + def Shader "OpenPBR" + { + uniform token info:id = "ND_open_pbr_surface_surfaceshader" + color3f inputs:base_color.connect = + float inputs:base_metalness.connect = + float inputs:geometry_opacity.connect = + float inputs:specular_roughness.connect = + token outputs:out + } +} + +def Xform "go2" ( + apiSchemas = ["GeomModelAPI"] + assetInfo = { + string name = "go2" + } + kind = "component" +) +{ + float3[] extentsHint = [(-0.24186657, -0.162013, -0.0039150002), (0.331958, 0.162013, 0.533834), (3.4028235e38, 3.4028235e38, 3.4028235e38), (-3.4028235e38, -3.4028235e38, -3.4028235e38), (3.4028235e38, 3.4028235e38, 3.4028235e38), (-3.4028235e38, -3.4028235e38, -3.4028235e38), (-0.2394, -0.164, -0.0029999998), (0.34, 0.164, 0.502)] + + def Scope "Geometry" + { + def Xform "base" ( + apiSchemas = ["PhysicsRigidBodyAPI", "PhysicsMassAPI", "PhysicsArticulationRootAPI", "NewtonArticulationRootAPI"] + ) + { + uniform bool newton:jointsAddMobility = 1 + point3f physics:centerOfMass = (0.021112, 0, -0.005366) + float3 physics:diagonalInertia = (0.107027, 0.0980771, 0.0244531) + float physics:mass = 6.921 + quatf physics:principalAxes = (-0.0005434711, 0.71343505, -0.0017376901, 0.70071906) + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (0, 0, 0.445) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + + def Mesh "base_0" ( + apiSchemas = ["MaterialBindingAPI", "MjcImageableAPI"] + assetInfo = { + asset identifier = @meshes/base_0.obj@ + string version = "sha256:8cdc53719002a5ee737d867904e534a6d6fa930b616bdcc1b3131e577d4e46c0" + } + prepend references = @meshes/base_0.usdc@ + ) + { + rel material:binding = + uniform int mjc:group = 2 + uniform token orientation = "rightHanded" + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + uniform token subdivisionScheme = "none" + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (-1.66533e-15, 1.51788e-18, 3.40006e-16) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Mesh "base_1" ( + apiSchemas = ["MaterialBindingAPI", "MjcImageableAPI"] + assetInfo = { + asset identifier = @meshes/base_1.obj@ + string version = "sha256:39d2f4724ff64dd5a0640c308fd98902a2946660a8d033ec8dbf368fd5af0dd6" + } + prepend references = @meshes/base_1.usdc@ + ) + { + rel material:binding = + uniform int mjc:group = 2 + uniform token orientation = "rightHanded" + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + uniform token subdivisionScheme = "none" + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (-1.66533e-15, 1.21973e-18, 3.40006e-16) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Mesh "base_2" ( + apiSchemas = ["MaterialBindingAPI", "MjcImageableAPI"] + assetInfo = { + asset identifier = @meshes/base_2.obj@ + string version = "sha256:29cae463f95d678e4ede863c36c9b19b0d356b8340412add10670d70bdcc3095" + } + prepend references = @meshes/base_2.usdc@ + ) + { + rel material:binding = + uniform int mjc:group = 2 + uniform token orientation = "rightHanded" + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + uniform token subdivisionScheme = "none" + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (0, 0, 0) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Mesh "base_3" ( + apiSchemas = ["MaterialBindingAPI", "MjcImageableAPI"] + assetInfo = { + asset identifier = @meshes/base_3.obj@ + string version = "sha256:f51ba43bf300e68957a0c1aa83eaf3e4926f4ac7e64a0d6c33ae5d21fcbcf5f6" + } + prepend references = @meshes/base_3.usdc@ + ) + { + rel material:binding = + uniform int mjc:group = 2 + uniform token orientation = "rightHanded" + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + uniform token subdivisionScheme = "none" + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (-4.92661e-16, -6.59195e-17, -2.70617e-16) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Mesh "base_4" ( + apiSchemas = ["MaterialBindingAPI", "MjcImageableAPI"] + assetInfo = { + asset identifier = @meshes/base_4.obj@ + string version = "sha256:49ddec0129546e5dd2075d5beacf7f207baa2860aeff7626818e64d2efbf3821" + } + prepend references = @meshes/base_4.usdc@ + ) + { + rel material:binding = + uniform int mjc:group = 2 + uniform token orientation = "rightHanded" + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + uniform token subdivisionScheme = "none" + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (-7.49401e-16, 5.85469e-18, 7.69784e-18) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Cube "Box" ( + apiSchemas = ["PhysicsCollisionAPI", "NewtonCollisionAPI", "MjcCollisionAPI", "PhysicsMassAPI", "MaterialBindingAPI"] + ) + { + float3[] extent = [(-1, -1, -1), (1, 1, 1)] + rel material:binding:physics = + uniform int mjc:condim = 1 + uniform int mjc:group = 3 + uniform double[] mjc:solref = [0.02, 1] + float newton:contactGap = 0 + float newton:contactMargin = 0.001 + float physics:density = 1000 + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + int[] primvars:displayColor:indices = None + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + int[] primvars:displayOpacity:indices = None + uniform token purpose = "guide" + double size = 2 + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (0.1881, 0.04675, 0.057) + double3 xformOp:translate = (0, 0, 0) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Cylinder "Cylinder" ( + apiSchemas = ["PhysicsCollisionAPI", "NewtonCollisionAPI", "MjcCollisionAPI", "PhysicsMassAPI", "MaterialBindingAPI"] + ) + { + uniform token axis = "Z" + float3[] extent = [(-0.05, -0.05, -0.045), (0.05, 0.05, 0.045)] + double height = 0.09 + rel material:binding:physics = + uniform int mjc:condim = 1 + uniform int mjc:group = 3 + uniform double[] mjc:solref = [0.02, 1] + float newton:contactGap = 0 + float newton:contactMargin = 0.001 + float physics:density = 1000 + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + int[] primvars:displayColor:indices = None + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + int[] primvars:displayOpacity:indices = None + uniform token purpose = "guide" + double radius = 0.05 + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (0.285, 0, 0.01) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Sphere "Sphere" ( + apiSchemas = ["PhysicsCollisionAPI", "NewtonCollisionAPI", "MjcCollisionAPI", "PhysicsMassAPI", "MaterialBindingAPI"] + ) + { + float3[] extent = [(-0.047, -0.047, -0.047), (0.047, 0.047, 0.047)] + rel material:binding:physics = + uniform int mjc:condim = 1 + uniform int mjc:group = 3 + uniform double[] mjc:solref = [0.02, 1] + float newton:contactGap = 0 + float newton:contactMargin = 0.001 + float physics:density = 1000 + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + int[] primvars:displayColor:indices = None + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + int[] primvars:displayOpacity:indices = None + uniform token purpose = "guide" + double radius = 0.047 + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (0.293, 0, -0.06) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Sphere "imu" ( + apiSchemas = ["MjcSiteAPI", "NewtonSiteAPI"] + ) + { + float3[] extent = [(-0.005, -0.005, -0.005), (0.005, 0.005, 0.005)] + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + int[] primvars:displayColor:indices = None + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + int[] primvars:displayOpacity:indices = None + uniform token purpose = "guide" + double radius = 0.005 + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (-0.02557, 0, 0.04232) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Xform "FL_hip" ( + apiSchemas = ["PhysicsRigidBodyAPI", "PhysicsMassAPI"] + ) + { + point3f physics:centerOfMass = (-0.0054, 0.00194, -0.000105) + float3 physics:diagonalInertia = (0.00088403, 0.000596003, 0.000479967) + float physics:mass = 0.678 + quatf physics:principalAxes = (0.49701393, 0.49924493, 0.50546193, 0.49823695) + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (0.1934, 0.0465, 0) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + + def Mesh "hip_0" ( + apiSchemas = ["MaterialBindingAPI", "MjcImageableAPI"] + assetInfo = { + asset identifier = @meshes/hip_0.obj@ + string version = "sha256:cc29932ab4d251ce3f72389ec4c1f757e44eee138b87e772b648c2bc76b506cd" + } + prepend references = @meshes/hip_0.usdc@ + ) + { + rel material:binding = + uniform int mjc:group = 2 + uniform token orientation = "rightHanded" + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + uniform token subdivisionScheme = "none" + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (0, 0, 0) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Mesh "hip_1" ( + apiSchemas = ["MaterialBindingAPI", "MjcImageableAPI"] + assetInfo = { + asset identifier = @meshes/hip_1.obj@ + string version = "sha256:3fd5bd9b2ef6abe622c0739d3c67eaa1679b19b47132951972c4892a779ea7cf" + } + prepend references = @meshes/hip_1.usdc@ + ) + { + rel material:binding = + uniform int mjc:group = 2 + uniform token orientation = "rightHanded" + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + uniform token subdivisionScheme = "none" + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (0, 0, 0) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Cylinder "Cylinder" ( + apiSchemas = ["PhysicsCollisionAPI", "NewtonCollisionAPI", "MjcCollisionAPI", "PhysicsMassAPI", "MaterialBindingAPI"] + ) + { + uniform token axis = "Z" + float3[] extent = [(-0.046, -0.046, -0.02), (0.046, 0.046, 0.02)] + double height = 0.04 + rel material:binding:physics = + uniform int mjc:condim = 1 + uniform int mjc:group = 3 + uniform double[] mjc:solref = [0.02, 1] + float newton:contactGap = 0 + float newton:contactMargin = 0.001 + float physics:density = 1000 + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + int[] primvars:displayColor:indices = None + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + int[] primvars:displayOpacity:indices = None + uniform token purpose = "guide" + double radius = 0.046 + quatf xformOp:orient = (0.7071068, 0.7071067, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (0, 0.08, 0) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Xform "FL_thigh" ( + apiSchemas = ["PhysicsRigidBodyAPI", "PhysicsMassAPI"] + ) + { + point3f physics:centerOfMass = (-0.00374, -0.0223, -0.0327) + float3 physics:diagonalInertia = (0.00594973, 0.00584149, 0.000878787) + float physics:mass = 1.152 + quatf physics:principalAxes = (0.82953286, 0.08476349, -0.020063197, 0.5516229) + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (0, 0.0955, 0) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + + def Mesh "thigh_0" ( + apiSchemas = ["MaterialBindingAPI", "MjcImageableAPI"] + assetInfo = { + asset identifier = @meshes/thigh_0.obj@ + string version = "sha256:bb3edfe6a7b04f4f4dff1ce7b05d426ddce941e276cc6372fb542055893c71ec" + } + prepend references = @meshes/thigh_0.usdc@ + ) + { + rel material:binding = + uniform int mjc:group = 2 + uniform token orientation = "rightHanded" + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + uniform token subdivisionScheme = "none" + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (0, 0, 0) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Mesh "thigh_1" ( + apiSchemas = ["MaterialBindingAPI", "MjcImageableAPI"] + assetInfo = { + asset identifier = @meshes/thigh_1.obj@ + string version = "sha256:3a631822f9f7d2114b1b79dcdff5abf95103630abb7d91d1f3b8b18117132ed2" + } + prepend references = @meshes/thigh_1.usdc@ + ) + { + rel material:binding = + uniform int mjc:group = 2 + uniform token orientation = "rightHanded" + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + uniform token subdivisionScheme = "none" + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (0, 0, 0) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Cube "Box" ( + apiSchemas = ["PhysicsCollisionAPI", "NewtonCollisionAPI", "MjcCollisionAPI", "PhysicsMassAPI", "MaterialBindingAPI"] + ) + { + float3[] extent = [(-1, -1, -1), (1, 1, 1)] + rel material:binding:physics = + uniform int mjc:condim = 1 + uniform int mjc:group = 3 + uniform double[] mjc:solref = [0.02, 1] + float newton:contactGap = 0 + float newton:contactMargin = 0.001 + float physics:density = 1000 + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + int[] primvars:displayColor:indices = None + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + int[] primvars:displayOpacity:indices = None + uniform token purpose = "guide" + double size = 2 + quatf xformOp:orient = (0.7071068, 0, 0.7071067, 0) + float3 xformOp:scale = (0.1065, 0.01225, 0.017) + double3 xformOp:translate = (0, 0, -0.1065) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Xform "FL_calf" ( + apiSchemas = ["PhysicsRigidBodyAPI", "PhysicsMassAPI"] + ) + { + point3f physics:centerOfMass = (0.00629595, -0.000622121, -0.141417) + float3 physics:diagonalInertia = (0.0014901, 0.00146356, 0.0000531397) + float physics:mass = 0.241352 + quatf physics:principalAxes = (0.7106718, 0.0015409894, -0.0045008687, 0.7035078) + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (0, 0, -0.213) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + + def Mesh "calf_0" ( + apiSchemas = ["MaterialBindingAPI", "MjcImageableAPI"] + assetInfo = { + asset identifier = @meshes/calf_0.obj@ + string version = "sha256:4a818cd563c5b7e2a271504442d05fd777d38cbaa028d4a10ad29c9f87bb355e" + } + prepend references = @meshes/calf_0.usdc@ + ) + { + rel material:binding = + uniform int mjc:group = 2 + uniform token orientation = "rightHanded" + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + uniform token subdivisionScheme = "none" + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (-1.99493e-17, 2.30393e-19, 3.33067e-16) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Mesh "calf_1" ( + apiSchemas = ["MaterialBindingAPI", "MjcImageableAPI"] + assetInfo = { + asset identifier = @meshes/calf_1.obj@ + string version = "sha256:51950c3a153ae3208592df86ea11c9d6756f8f645ebca5caf6910c61aeb03817" + } + prepend references = @meshes/calf_1.usdc@ + ) + { + rel material:binding = + uniform int mjc:group = 2 + uniform token orientation = "rightHanded" + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + uniform token subdivisionScheme = "none" + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (4.94396e-17, -1.78893e-18, 1.94289e-15) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Cylinder "Cylinder" ( + apiSchemas = ["PhysicsCollisionAPI", "NewtonCollisionAPI", "MjcCollisionAPI", "PhysicsMassAPI", "MaterialBindingAPI"] + ) + { + uniform token axis = "Z" + float3[] extent = [(-0.012, -0.012, -0.06), (0.012, 0.012, 0.06)] + double height = 0.12 + rel material:binding:physics = + uniform int mjc:condim = 1 + uniform int mjc:group = 3 + uniform double[] mjc:solref = [0.02, 1] + float newton:contactGap = 0 + float newton:contactMargin = 0.001 + float physics:density = 1000 + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + int[] primvars:displayColor:indices = None + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + int[] primvars:displayOpacity:indices = None + uniform token purpose = "guide" + double radius = 0.012 + quatf xformOp:orient = (0.99449265, 0, -0.10480634, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (0.008, 0, -0.06) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Cylinder "Cylinder_1" ( + apiSchemas = ["PhysicsCollisionAPI", "NewtonCollisionAPI", "MjcCollisionAPI", "PhysicsMassAPI", "MaterialBindingAPI"] + displayName = "Cylinder" + uiHints = { + string displayName = "Cylinder" + } + ) + { + uniform token axis = "Z" + float3[] extent = [(-0.011, -0.011, -0.0325), (0.011, 0.011, 0.0325)] + double height = 0.065 + rel material:binding:physics = + uniform int mjc:condim = 1 + uniform int mjc:group = 3 + uniform double[] mjc:solref = [0.02, 1] + float newton:contactGap = 0 + float newton:contactMargin = 0.001 + float physics:density = 1000 + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + int[] primvars:displayColor:indices = None + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + int[] primvars:displayOpacity:indices = None + uniform token purpose = "guide" + double radius = 0.011 + quatf xformOp:orient = (0.99968755, 0, 0.02499595, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (0.02, 0, -0.148) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Mesh "foot" ( + apiSchemas = ["MaterialBindingAPI", "MjcImageableAPI"] + assetInfo = { + asset identifier = @meshes/foot.obj@ + string version = "sha256:df9e78a7c0110d02557439010f2a5f11f0ec4fd907b32704145dbd6aa7affc0b" + } + prepend references = @meshes/foot.usdc@ + ) + { + rel material:binding = + uniform int mjc:group = 2 + uniform token orientation = "rightHanded" + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + uniform token subdivisionScheme = "none" + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (0, 0, -0.213) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Sphere "FL" ( + apiSchemas = ["PhysicsCollisionAPI", "NewtonCollisionAPI", "MjcCollisionAPI", "PhysicsMassAPI", "MaterialBindingAPI"] + ) + { + float3[] extent = [(-0.022, -0.022, -0.022), (0.022, 0.022, 0.022)] + rel material:binding:physics = + uniform int mjc:condim = 6 + uniform int mjc:group = 3 + uniform int mjc:priority = 1 + uniform double[] mjc:solimp = [0.015, 1, 0.022, 0.5, 2] + uniform double[] mjc:solref = [0.02, 1] + float newton:contactGap = 0 + float newton:contactMargin = 0.001 + float physics:density = 1000 + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + int[] primvars:displayColor:indices = None + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + int[] primvars:displayOpacity:indices = None + uniform token purpose = "guide" + double radius = 0.022 + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (-0.002, 0, -0.213) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def PhysicsRevoluteJoint "FL_calf_joint" ( + apiSchemas = ["MjcJointAPI", "NewtonJointAPI", "PhysicsDriveAPI:angular"] + ) + { + float drive:angular:physics:damping = 0.5 + float drive:angular:physics:stiffness = 20 + float drive:angular:physics:targetPosition = -1.5 + float newton:armature = 0.01 + float newton:damping = 0.034906585 + float newton:friction = 0.2 + uniform token physics:axis = "Y" + rel physics:body0 = + rel physics:body1 = + point3f physics:localPos0 = (0, 0, -0.213) + point3f physics:localPos1 = (0, 0, 0) + quatf physics:localRot0 = (1, 0, 0, 0) + quatf physics:localRot1 = (1, 0, 0, 0) + float physics:lowerLimit = -155.99922 + float physics:upperLimit = -48.00011 + } + } + + def PhysicsRevoluteJoint "FL_thigh_joint" ( + apiSchemas = ["MjcJointAPI", "NewtonJointAPI", "PhysicsDriveAPI:angular"] + ) + { + float drive:angular:physics:damping = 0.5 + float drive:angular:physics:stiffness = 20 + float drive:angular:physics:targetPosition = 0.8 + float newton:armature = 0.01 + float newton:damping = 0.034906585 + float newton:friction = 0.2 + uniform token physics:axis = "Y" + rel physics:body0 = + rel physics:body1 = + point3f physics:localPos0 = (0, 0.0955, 0) + point3f physics:localPos1 = (0, 0, 0) + quatf physics:localRot0 = (1, 0, 0, 0) + quatf physics:localRot1 = (1, 0, 0, 0) + float physics:lowerLimit = -90.00021 + float physics:upperLimit = 200.00238 + } + } + + def PhysicsRevoluteJoint "FL_hip_joint" ( + apiSchemas = ["MjcJointAPI", "NewtonJointAPI", "PhysicsDriveAPI:angular"] + ) + { + float drive:angular:physics:damping = 0.5 + float drive:angular:physics:stiffness = 20 + float drive:angular:physics:targetPosition = 0 + float newton:armature = 0.01 + float newton:damping = 0.034906585 + float newton:friction = 0.2 + uniform token physics:axis = "X" + rel physics:body0 = + rel physics:body1 = + point3f physics:localPos0 = (0.1934, 0.0465, 0) + point3f physics:localPos1 = (0, 0, 0) + quatf physics:localRot0 = (1, 0, 0, 0) + quatf physics:localRot1 = (1, 0, 0, 0) + float physics:lowerLimit = -60.00014 + float physics:upperLimit = 60.00014 + } + } + + def Xform "FR_hip" ( + apiSchemas = ["PhysicsRigidBodyAPI", "PhysicsMassAPI"] + ) + { + point3f physics:centerOfMass = (-0.0054, -0.00194, -0.000105) + float3 physics:diagonalInertia = (0.00088403, 0.000596003, 0.000479967) + float physics:mass = 0.678 + quatf physics:principalAxes = (0.49823695, 0.50546193, 0.49924493, 0.49701393) + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (0.1934, -0.0465, 0) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + + def Mesh "hip_0" ( + apiSchemas = ["MaterialBindingAPI", "MjcImageableAPI"] + assetInfo = { + asset identifier = @meshes/hip_0.obj@ + string version = "sha256:cc29932ab4d251ce3f72389ec4c1f757e44eee138b87e772b648c2bc76b506cd" + } + prepend references = @meshes/hip_0.usdc@ + ) + { + rel material:binding = + uniform int mjc:group = 2 + uniform token orientation = "rightHanded" + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + uniform token subdivisionScheme = "none" + quatf xformOp:orient = (0.0000463268, 1, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (0, 0, 0) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Mesh "hip_1" ( + apiSchemas = ["MaterialBindingAPI", "MjcImageableAPI"] + assetInfo = { + asset identifier = @meshes/hip_1.obj@ + string version = "sha256:3fd5bd9b2ef6abe622c0739d3c67eaa1679b19b47132951972c4892a779ea7cf" + } + prepend references = @meshes/hip_1.usdc@ + ) + { + rel material:binding = + uniform int mjc:group = 2 + uniform token orientation = "rightHanded" + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + uniform token subdivisionScheme = "none" + quatf xformOp:orient = (0.0000463268, 1, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (0, 0, 0) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Cylinder "Cylinder" ( + apiSchemas = ["PhysicsCollisionAPI", "NewtonCollisionAPI", "MjcCollisionAPI", "PhysicsMassAPI", "MaterialBindingAPI"] + ) + { + uniform token axis = "Z" + float3[] extent = [(-0.046, -0.046, -0.02), (0.046, 0.046, 0.02)] + double height = 0.04 + rel material:binding:physics = + uniform int mjc:condim = 1 + uniform int mjc:group = 3 + uniform double[] mjc:solref = [0.02, 1] + float newton:contactGap = 0 + float newton:contactMargin = 0.001 + float physics:density = 1000 + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + int[] primvars:displayColor:indices = None + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + int[] primvars:displayOpacity:indices = None + uniform token purpose = "guide" + double radius = 0.046 + quatf xformOp:orient = (0.7071068, 0.7071067, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (0, -0.08, 0) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Xform "FR_thigh" ( + apiSchemas = ["PhysicsRigidBodyAPI", "PhysicsMassAPI"] + ) + { + point3f physics:centerOfMass = (-0.00374, 0.0223, -0.0327) + float3 physics:diagonalInertia = (0.00594973, 0.00584149, 0.000878787) + float physics:mass = 1.152 + quatf physics:principalAxes = (0.5516229, -0.020063197, 0.08476349, 0.82953286) + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (0, -0.0955, 0) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + + def Mesh "thigh_mirror_0" ( + apiSchemas = ["MaterialBindingAPI", "MjcImageableAPI"] + assetInfo = { + asset identifier = @meshes/thigh_mirror_0.obj@ + string version = "sha256:07abaedce761f1dd48647384ac14ba1959276bc78d5a2a62051e8f1c6093933b" + } + prepend references = @meshes/thigh_mirror_0.usdc@ + ) + { + rel material:binding = + uniform int mjc:group = 2 + uniform token orientation = "rightHanded" + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + uniform token subdivisionScheme = "none" + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (0, 0, 0) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Mesh "thigh_mirror_1" ( + apiSchemas = ["MaterialBindingAPI", "MjcImageableAPI"] + assetInfo = { + asset identifier = @meshes/thigh_mirror_1.obj@ + string version = "sha256:db984a3e983eabbc9b38183427cca9ab0bb3bdb049f9eab030e06edda36579d6" + } + prepend references = @meshes/thigh_mirror_1.usdc@ + ) + { + rel material:binding = + uniform int mjc:group = 2 + uniform token orientation = "rightHanded" + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + uniform token subdivisionScheme = "none" + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (0, 0, 0) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Cube "Box" ( + apiSchemas = ["PhysicsCollisionAPI", "NewtonCollisionAPI", "MjcCollisionAPI", "PhysicsMassAPI", "MaterialBindingAPI"] + ) + { + float3[] extent = [(-1, -1, -1), (1, 1, 1)] + rel material:binding:physics = + uniform int mjc:condim = 1 + uniform int mjc:group = 3 + uniform double[] mjc:solref = [0.02, 1] + float newton:contactGap = 0 + float newton:contactMargin = 0.001 + float physics:density = 1000 + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + int[] primvars:displayColor:indices = None + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + int[] primvars:displayOpacity:indices = None + uniform token purpose = "guide" + double size = 2 + quatf xformOp:orient = (0.7071068, 0, 0.7071067, 0) + float3 xformOp:scale = (0.1065, 0.01225, 0.017) + double3 xformOp:translate = (0, 0, -0.1065) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Xform "FR_calf" ( + apiSchemas = ["PhysicsRigidBodyAPI", "PhysicsMassAPI"] + ) + { + point3f physics:centerOfMass = (0.00629595, 0.000622121, -0.141417) + float3 physics:diagonalInertia = (0.0014901, 0.00146356, 0.0000531397) + float physics:mass = 0.241352 + quatf physics:principalAxes = (0.70350766, -0.004500868, 0.0015409893, 0.71067166) + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (0, 0, -0.213) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + + def Mesh "calf_mirror_0" ( + apiSchemas = ["MaterialBindingAPI", "MjcImageableAPI"] + assetInfo = { + asset identifier = @meshes/calf_mirror_0.obj@ + string version = "sha256:6dc0406207a1c08a5953fd5962aca1a270fd06593d30d100205e05c1b32d4af2" + } + prepend references = @meshes/calf_mirror_0.usdc@ + ) + { + rel material:binding = + uniform int mjc:group = 2 + uniform token orientation = "rightHanded" + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + uniform token subdivisionScheme = "none" + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (-1.64799e-17, -1.89735e-19, 2.77556e-16) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Mesh "calf_mirror_1" ( + apiSchemas = ["MaterialBindingAPI", "MjcImageableAPI"] + assetInfo = { + asset identifier = @meshes/calf_mirror_1.obj@ + string version = "sha256:f84f5af90b914633165beb3a5493ee499169144c5797dcf04c6eb5842f74b31a" + } + prepend references = @meshes/calf_mirror_1.usdc@ + ) + { + rel material:binding = + uniform int mjc:group = 2 + uniform token orientation = "rightHanded" + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + uniform token subdivisionScheme = "none" + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (4.94396e-17, 1.78893e-18, 1.94289e-15) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Cylinder "Cylinder" ( + apiSchemas = ["PhysicsCollisionAPI", "NewtonCollisionAPI", "MjcCollisionAPI", "PhysicsMassAPI", "MaterialBindingAPI"] + ) + { + uniform token axis = "Z" + float3[] extent = [(-0.013, -0.013, -0.06), (0.013, 0.013, 0.06)] + double height = 0.12 + rel material:binding:physics = + uniform int mjc:condim = 1 + uniform int mjc:group = 3 + uniform double[] mjc:solref = [0.02, 1] + float newton:contactGap = 0 + float newton:contactMargin = 0.001 + float physics:density = 1000 + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + int[] primvars:displayColor:indices = None + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + int[] primvars:displayOpacity:indices = None + uniform token purpose = "guide" + double radius = 0.013 + quatf xformOp:orient = (0.9950042, 0, -0.0998333, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (0.01, 0, -0.06) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Cylinder "Cylinder_1" ( + apiSchemas = ["PhysicsCollisionAPI", "NewtonCollisionAPI", "MjcCollisionAPI", "PhysicsMassAPI", "MaterialBindingAPI"] + displayName = "Cylinder" + uiHints = { + string displayName = "Cylinder" + } + ) + { + uniform token axis = "Z" + float3[] extent = [(-0.011, -0.011, -0.0325), (0.011, 0.011, 0.0325)] + double height = 0.065 + rel material:binding:physics = + uniform int mjc:condim = 1 + uniform int mjc:group = 3 + uniform double[] mjc:solref = [0.02, 1] + float newton:contactGap = 0 + float newton:contactMargin = 0.001 + float physics:density = 1000 + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + int[] primvars:displayColor:indices = None + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + int[] primvars:displayOpacity:indices = None + uniform token purpose = "guide" + double radius = 0.011 + quatf xformOp:orient = (0.99968755, 0, 0.02499595, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (0.02, 0, -0.148) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Mesh "foot" ( + apiSchemas = ["MaterialBindingAPI", "MjcImageableAPI"] + assetInfo = { + asset identifier = @meshes/foot.obj@ + string version = "sha256:df9e78a7c0110d02557439010f2a5f11f0ec4fd907b32704145dbd6aa7affc0b" + } + prepend references = @meshes/foot.usdc@ + ) + { + rel material:binding = + uniform int mjc:group = 2 + uniform token orientation = "rightHanded" + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + uniform token subdivisionScheme = "none" + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (0, 0, -0.213) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Sphere "FR" ( + apiSchemas = ["PhysicsCollisionAPI", "NewtonCollisionAPI", "MjcCollisionAPI", "PhysicsMassAPI", "MaterialBindingAPI"] + ) + { + float3[] extent = [(-0.022, -0.022, -0.022), (0.022, 0.022, 0.022)] + rel material:binding:physics = + uniform int mjc:condim = 6 + uniform int mjc:group = 3 + uniform int mjc:priority = 1 + uniform double[] mjc:solimp = [0.015, 1, 0.022, 0.5, 2] + uniform double[] mjc:solref = [0.02, 1] + float newton:contactGap = 0 + float newton:contactMargin = 0.001 + float physics:density = 1000 + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + int[] primvars:displayColor:indices = None + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + int[] primvars:displayOpacity:indices = None + uniform token purpose = "guide" + double radius = 0.022 + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (-0.002, 0, -0.213) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def PhysicsRevoluteJoint "FR_calf_joint" ( + apiSchemas = ["MjcJointAPI", "NewtonJointAPI", "PhysicsDriveAPI:angular"] + ) + { + float drive:angular:physics:damping = 0.5 + float drive:angular:physics:stiffness = 20 + float drive:angular:physics:targetPosition = -1.5 + float newton:armature = 0.01 + float newton:damping = 0.034906585 + float newton:friction = 0.2 + uniform token physics:axis = "Y" + rel physics:body0 = + rel physics:body1 = + point3f physics:localPos0 = (0, 0, -0.213) + point3f physics:localPos1 = (0, 0, 0) + quatf physics:localRot0 = (1, 0, 0, 0) + quatf physics:localRot1 = (1, 0, 0, 0) + float physics:lowerLimit = -155.99922 + float physics:upperLimit = -48.00011 + } + } + + def PhysicsRevoluteJoint "FR_thigh_joint" ( + apiSchemas = ["MjcJointAPI", "NewtonJointAPI", "PhysicsDriveAPI:angular"] + ) + { + float drive:angular:physics:damping = 0.5 + float drive:angular:physics:stiffness = 20 + float drive:angular:physics:targetPosition = 0.8 + float newton:armature = 0.01 + float newton:damping = 0.034906585 + float newton:friction = 0.2 + uniform token physics:axis = "Y" + rel physics:body0 = + rel physics:body1 = + point3f physics:localPos0 = (0, -0.0955, 0) + point3f physics:localPos1 = (0, 0, 0) + quatf physics:localRot0 = (1, 0, 0, 0) + quatf physics:localRot1 = (1, 0, 0, 0) + float physics:lowerLimit = -90.00021 + float physics:upperLimit = 200.00238 + } + } + + def PhysicsRevoluteJoint "FR_hip_joint" ( + apiSchemas = ["MjcJointAPI", "NewtonJointAPI", "PhysicsDriveAPI:angular"] + ) + { + float drive:angular:physics:damping = 0.5 + float drive:angular:physics:stiffness = 20 + float drive:angular:physics:targetPosition = 0 + float newton:armature = 0.01 + float newton:damping = 0.034906585 + float newton:friction = 0.2 + uniform token physics:axis = "X" + rel physics:body0 = + rel physics:body1 = + point3f physics:localPos0 = (0.1934, -0.0465, 0) + point3f physics:localPos1 = (0, 0, 0) + quatf physics:localRot0 = (1, 0, 0, 0) + quatf physics:localRot1 = (1, 0, 0, 0) + float physics:lowerLimit = -60.00014 + float physics:upperLimit = 60.00014 + } + } + + def Xform "RL_hip" ( + apiSchemas = ["PhysicsRigidBodyAPI", "PhysicsMassAPI"] + ) + { + point3f physics:centerOfMass = (0.0054, 0.00194, -0.000105) + float3 physics:diagonalInertia = (0.00088403, 0.000596003, 0.000479967) + float physics:mass = 0.678 + quatf physics:principalAxes = (0.5054619, 0.4982369, 0.49701387, 0.49924487) + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (-0.1934, 0.0465, 0) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + + def Mesh "hip_0" ( + apiSchemas = ["MaterialBindingAPI", "MjcImageableAPI"] + assetInfo = { + asset identifier = @meshes/hip_0.obj@ + string version = "sha256:cc29932ab4d251ce3f72389ec4c1f757e44eee138b87e772b648c2bc76b506cd" + } + prepend references = @meshes/hip_0.usdc@ + ) + { + rel material:binding = + uniform int mjc:group = 2 + uniform token orientation = "rightHanded" + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + uniform token subdivisionScheme = "none" + quatf xformOp:orient = (0.0000463268, 0, 1, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (0, 0, 0) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Mesh "hip_1" ( + apiSchemas = ["MaterialBindingAPI", "MjcImageableAPI"] + assetInfo = { + asset identifier = @meshes/hip_1.obj@ + string version = "sha256:3fd5bd9b2ef6abe622c0739d3c67eaa1679b19b47132951972c4892a779ea7cf" + } + prepend references = @meshes/hip_1.usdc@ + ) + { + rel material:binding = + uniform int mjc:group = 2 + uniform token orientation = "rightHanded" + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + uniform token subdivisionScheme = "none" + quatf xformOp:orient = (0.0000463268, 0, 1, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (0, 0, 0) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Cylinder "Cylinder" ( + apiSchemas = ["PhysicsCollisionAPI", "NewtonCollisionAPI", "MjcCollisionAPI", "PhysicsMassAPI", "MaterialBindingAPI"] + ) + { + uniform token axis = "Z" + float3[] extent = [(-0.046, -0.046, -0.02), (0.046, 0.046, 0.02)] + double height = 0.04 + rel material:binding:physics = + uniform int mjc:condim = 1 + uniform int mjc:group = 3 + uniform double[] mjc:solref = [0.02, 1] + float newton:contactGap = 0 + float newton:contactMargin = 0.001 + float physics:density = 1000 + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + int[] primvars:displayColor:indices = None + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + int[] primvars:displayOpacity:indices = None + uniform token purpose = "guide" + double radius = 0.046 + quatf xformOp:orient = (0.7071068, 0.7071067, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (0, 0.08, 0) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Xform "RL_thigh" ( + apiSchemas = ["PhysicsRigidBodyAPI", "PhysicsMassAPI"] + ) + { + point3f physics:centerOfMass = (-0.00374, -0.0223, -0.0327) + float3 physics:diagonalInertia = (0.00594973, 0.00584149, 0.000878787) + float physics:mass = 1.152 + quatf physics:principalAxes = (0.82953286, 0.08476349, -0.020063197, 0.5516229) + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (0, 0.0955, 0) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + + def Mesh "thigh_0" ( + apiSchemas = ["MaterialBindingAPI", "MjcImageableAPI"] + assetInfo = { + asset identifier = @meshes/thigh_0.obj@ + string version = "sha256:bb3edfe6a7b04f4f4dff1ce7b05d426ddce941e276cc6372fb542055893c71ec" + } + prepend references = @meshes/thigh_0.usdc@ + ) + { + rel material:binding = + uniform int mjc:group = 2 + uniform token orientation = "rightHanded" + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + uniform token subdivisionScheme = "none" + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (0, 0, 0) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Mesh "thigh_1" ( + apiSchemas = ["MaterialBindingAPI", "MjcImageableAPI"] + assetInfo = { + asset identifier = @meshes/thigh_1.obj@ + string version = "sha256:3a631822f9f7d2114b1b79dcdff5abf95103630abb7d91d1f3b8b18117132ed2" + } + prepend references = @meshes/thigh_1.usdc@ + ) + { + rel material:binding = + uniform int mjc:group = 2 + uniform token orientation = "rightHanded" + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + uniform token subdivisionScheme = "none" + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (0, 0, 0) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Cube "Box" ( + apiSchemas = ["PhysicsCollisionAPI", "NewtonCollisionAPI", "MjcCollisionAPI", "PhysicsMassAPI", "MaterialBindingAPI"] + ) + { + float3[] extent = [(-1, -1, -1), (1, 1, 1)] + rel material:binding:physics = + uniform int mjc:condim = 1 + uniform int mjc:group = 3 + uniform double[] mjc:solref = [0.02, 1] + float newton:contactGap = 0 + float newton:contactMargin = 0.001 + float physics:density = 1000 + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + int[] primvars:displayColor:indices = None + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + int[] primvars:displayOpacity:indices = None + uniform token purpose = "guide" + double size = 2 + quatf xformOp:orient = (0.7071068, 0, 0.7071067, 0) + float3 xformOp:scale = (0.1065, 0.01225, 0.017) + double3 xformOp:translate = (0, 0, -0.1065) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Xform "RL_calf" ( + apiSchemas = ["PhysicsRigidBodyAPI", "PhysicsMassAPI"] + ) + { + point3f physics:centerOfMass = (0.00629595, -0.000622121, -0.141417) + float3 physics:diagonalInertia = (0.0014901, 0.00146356, 0.0000531397) + float physics:mass = 0.241352 + quatf physics:principalAxes = (0.7106718, 0.0015409894, -0.0045008687, 0.7035078) + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (0, 0, -0.213) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + + def Mesh "calf_0" ( + apiSchemas = ["MaterialBindingAPI", "MjcImageableAPI"] + assetInfo = { + asset identifier = @meshes/calf_0.obj@ + string version = "sha256:4a818cd563c5b7e2a271504442d05fd777d38cbaa028d4a10ad29c9f87bb355e" + } + prepend references = @meshes/calf_0.usdc@ + ) + { + rel material:binding = + uniform int mjc:group = 2 + uniform token orientation = "rightHanded" + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + uniform token subdivisionScheme = "none" + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (-1.99493e-17, 2.30393e-19, 3.33067e-16) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Mesh "calf_1" ( + apiSchemas = ["MaterialBindingAPI", "MjcImageableAPI"] + assetInfo = { + asset identifier = @meshes/calf_1.obj@ + string version = "sha256:51950c3a153ae3208592df86ea11c9d6756f8f645ebca5caf6910c61aeb03817" + } + prepend references = @meshes/calf_1.usdc@ + ) + { + rel material:binding = + uniform int mjc:group = 2 + uniform token orientation = "rightHanded" + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + uniform token subdivisionScheme = "none" + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (4.94396e-17, -1.78893e-18, 1.94289e-15) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Cylinder "Cylinder" ( + apiSchemas = ["PhysicsCollisionAPI", "NewtonCollisionAPI", "MjcCollisionAPI", "PhysicsMassAPI", "MaterialBindingAPI"] + ) + { + uniform token axis = "Z" + float3[] extent = [(-0.013, -0.013, -0.06), (0.013, 0.013, 0.06)] + double height = 0.12 + rel material:binding:physics = + uniform int mjc:condim = 1 + uniform int mjc:group = 3 + uniform double[] mjc:solref = [0.02, 1] + float newton:contactGap = 0 + float newton:contactMargin = 0.001 + float physics:density = 1000 + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + int[] primvars:displayColor:indices = None + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + int[] primvars:displayOpacity:indices = None + uniform token purpose = "guide" + double radius = 0.013 + quatf xformOp:orient = (0.9950042, 0, -0.0998333, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (0.01, 0, -0.06) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Cylinder "Cylinder_1" ( + apiSchemas = ["PhysicsCollisionAPI", "NewtonCollisionAPI", "MjcCollisionAPI", "PhysicsMassAPI", "MaterialBindingAPI"] + displayName = "Cylinder" + uiHints = { + string displayName = "Cylinder" + } + ) + { + uniform token axis = "Z" + float3[] extent = [(-0.011, -0.011, -0.0325), (0.011, 0.011, 0.0325)] + double height = 0.065 + rel material:binding:physics = + uniform int mjc:condim = 1 + uniform int mjc:group = 3 + uniform double[] mjc:solref = [0.02, 1] + float newton:contactGap = 0 + float newton:contactMargin = 0.001 + float physics:density = 1000 + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + int[] primvars:displayColor:indices = None + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + int[] primvars:displayOpacity:indices = None + uniform token purpose = "guide" + double radius = 0.011 + quatf xformOp:orient = (0.99968755, 0, 0.02499595, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (0.02, 0, -0.148) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Mesh "foot" ( + apiSchemas = ["MaterialBindingAPI", "MjcImageableAPI"] + assetInfo = { + asset identifier = @meshes/foot.obj@ + string version = "sha256:df9e78a7c0110d02557439010f2a5f11f0ec4fd907b32704145dbd6aa7affc0b" + } + prepend references = @meshes/foot.usdc@ + ) + { + rel material:binding = + uniform int mjc:group = 2 + uniform token orientation = "rightHanded" + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + uniform token subdivisionScheme = "none" + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (0, 0, -0.213) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Sphere "RL" ( + apiSchemas = ["PhysicsCollisionAPI", "NewtonCollisionAPI", "MjcCollisionAPI", "PhysicsMassAPI", "MaterialBindingAPI"] + ) + { + float3[] extent = [(-0.022, -0.022, -0.022), (0.022, 0.022, 0.022)] + rel material:binding:physics = + uniform int mjc:condim = 6 + uniform int mjc:group = 3 + uniform int mjc:priority = 1 + uniform double[] mjc:solimp = [0.015, 1, 0.022, 0.5, 2] + uniform double[] mjc:solref = [0.02, 1] + float newton:contactGap = 0 + float newton:contactMargin = 0.001 + float physics:density = 1000 + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + int[] primvars:displayColor:indices = None + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + int[] primvars:displayOpacity:indices = None + uniform token purpose = "guide" + double radius = 0.022 + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (-0.002, 0, -0.213) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def PhysicsRevoluteJoint "RL_calf_joint" ( + apiSchemas = ["MjcJointAPI", "NewtonJointAPI", "PhysicsDriveAPI:angular"] + ) + { + float drive:angular:physics:damping = 0.5 + float drive:angular:physics:stiffness = 20 + float drive:angular:physics:targetPosition = -1.5 + float newton:armature = 0.01 + float newton:damping = 0.034906585 + float newton:friction = 0.2 + uniform token physics:axis = "Y" + rel physics:body0 = + rel physics:body1 = + point3f physics:localPos0 = (0, 0, -0.213) + point3f physics:localPos1 = (0, 0, 0) + quatf physics:localRot0 = (1, 0, 0, 0) + quatf physics:localRot1 = (1, 0, 0, 0) + float physics:lowerLimit = -155.99922 + float physics:upperLimit = -48.00011 + } + } + + def PhysicsRevoluteJoint "RL_thigh_joint" ( + apiSchemas = ["MjcJointAPI", "NewtonJointAPI", "PhysicsDriveAPI:angular"] + ) + { + float drive:angular:physics:damping = 0.5 + float drive:angular:physics:stiffness = 20 + float drive:angular:physics:targetPosition = 0.8 + float newton:armature = 0.01 + float newton:damping = 0.034906585 + float newton:friction = 0.2 + uniform token physics:axis = "Y" + rel physics:body0 = + rel physics:body1 = + point3f physics:localPos0 = (0, 0.0955, 0) + point3f physics:localPos1 = (0, 0, 0) + quatf physics:localRot0 = (1, 0, 0, 0) + quatf physics:localRot1 = (1, 0, 0, 0) + float physics:lowerLimit = -30.00007 + float physics:upperLimit = 260.00253 + } + } + + def PhysicsRevoluteJoint "RL_hip_joint" ( + apiSchemas = ["MjcJointAPI", "NewtonJointAPI", "PhysicsDriveAPI:angular"] + ) + { + float drive:angular:physics:damping = 0.5 + float drive:angular:physics:stiffness = 20 + float drive:angular:physics:targetPosition = 0 + float newton:armature = 0.01 + float newton:damping = 0.034906585 + float newton:friction = 0.2 + uniform token physics:axis = "X" + rel physics:body0 = + rel physics:body1 = + point3f physics:localPos0 = (-0.1934, 0.0465, 0) + point3f physics:localPos1 = (0, 0, 0) + quatf physics:localRot0 = (1, 0, 0, 0) + quatf physics:localRot1 = (1, 0, 0, 0) + float physics:lowerLimit = -60.00014 + float physics:upperLimit = 60.00014 + } + } + + def Xform "RR_hip" ( + apiSchemas = ["PhysicsRigidBodyAPI", "PhysicsMassAPI"] + ) + { + point3f physics:centerOfMass = (0.0054, -0.00194, -0.000105) + float3 physics:diagonalInertia = (0.00088403, 0.000596003, 0.000479967) + float physics:mass = 0.678 + quatf physics:principalAxes = (0.49924493, 0.49701393, 0.49823695, 0.50546193) + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (-0.1934, -0.0465, 0) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + + def Mesh "hip_0" ( + apiSchemas = ["MaterialBindingAPI", "MjcImageableAPI"] + assetInfo = { + asset identifier = @meshes/hip_0.obj@ + string version = "sha256:cc29932ab4d251ce3f72389ec4c1f757e44eee138b87e772b648c2bc76b506cd" + } + prepend references = @meshes/hip_0.usdc@ + ) + { + rel material:binding = + uniform int mjc:group = 2 + uniform token orientation = "rightHanded" + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + uniform token subdivisionScheme = "none" + quatf xformOp:orient = (-2.1461701e-9, -0.0000463268, -0.0000463268, 1) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (0, 0, 0) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Mesh "hip_1" ( + apiSchemas = ["MaterialBindingAPI", "MjcImageableAPI"] + assetInfo = { + asset identifier = @meshes/hip_1.obj@ + string version = "sha256:3fd5bd9b2ef6abe622c0739d3c67eaa1679b19b47132951972c4892a779ea7cf" + } + prepend references = @meshes/hip_1.usdc@ + ) + { + rel material:binding = + uniform int mjc:group = 2 + uniform token orientation = "rightHanded" + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + uniform token subdivisionScheme = "none" + quatf xformOp:orient = (-2.1461701e-9, -0.0000463268, -0.0000463268, 1) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (0, 0, 0) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Cylinder "Cylinder" ( + apiSchemas = ["PhysicsCollisionAPI", "NewtonCollisionAPI", "MjcCollisionAPI", "PhysicsMassAPI", "MaterialBindingAPI"] + ) + { + uniform token axis = "Z" + float3[] extent = [(-0.046, -0.046, -0.02), (0.046, 0.046, 0.02)] + double height = 0.04 + rel material:binding:physics = + uniform int mjc:condim = 1 + uniform int mjc:group = 3 + uniform double[] mjc:solref = [0.02, 1] + float newton:contactGap = 0 + float newton:contactMargin = 0.001 + float physics:density = 1000 + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + int[] primvars:displayColor:indices = None + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + int[] primvars:displayOpacity:indices = None + uniform token purpose = "guide" + double radius = 0.046 + quatf xformOp:orient = (0.7071068, 0.7071067, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (0, -0.08, 0) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Xform "RR_thigh" ( + apiSchemas = ["PhysicsRigidBodyAPI", "PhysicsMassAPI"] + ) + { + point3f physics:centerOfMass = (-0.00374, 0.0223, -0.0327) + float3 physics:diagonalInertia = (0.00594973, 0.00584149, 0.000878787) + float physics:mass = 1.152 + quatf physics:principalAxes = (0.5516229, -0.020063197, 0.08476349, 0.82953286) + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (0, -0.0955, 0) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + + def Mesh "thigh_mirror_0" ( + apiSchemas = ["MaterialBindingAPI", "MjcImageableAPI"] + assetInfo = { + asset identifier = @meshes/thigh_mirror_0.obj@ + string version = "sha256:07abaedce761f1dd48647384ac14ba1959276bc78d5a2a62051e8f1c6093933b" + } + prepend references = @meshes/thigh_mirror_0.usdc@ + ) + { + rel material:binding = + uniform int mjc:group = 2 + uniform token orientation = "rightHanded" + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + uniform token subdivisionScheme = "none" + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (0, 0, 0) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Mesh "thigh_mirror_1" ( + apiSchemas = ["MaterialBindingAPI", "MjcImageableAPI"] + assetInfo = { + asset identifier = @meshes/thigh_mirror_1.obj@ + string version = "sha256:db984a3e983eabbc9b38183427cca9ab0bb3bdb049f9eab030e06edda36579d6" + } + prepend references = @meshes/thigh_mirror_1.usdc@ + ) + { + rel material:binding = + uniform int mjc:group = 2 + uniform token orientation = "rightHanded" + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + uniform token subdivisionScheme = "none" + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (0, 0, 0) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Cube "Box" ( + apiSchemas = ["PhysicsCollisionAPI", "NewtonCollisionAPI", "MjcCollisionAPI", "PhysicsMassAPI", "MaterialBindingAPI"] + ) + { + float3[] extent = [(-1, -1, -1), (1, 1, 1)] + rel material:binding:physics = + uniform int mjc:condim = 1 + uniform int mjc:group = 3 + uniform double[] mjc:solref = [0.02, 1] + float newton:contactGap = 0 + float newton:contactMargin = 0.001 + float physics:density = 1000 + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + int[] primvars:displayColor:indices = None + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + int[] primvars:displayOpacity:indices = None + uniform token purpose = "guide" + double size = 2 + quatf xformOp:orient = (0.7071068, 0, 0.7071067, 0) + float3 xformOp:scale = (0.1065, 0.01225, 0.017) + double3 xformOp:translate = (0, 0, -0.1065) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Xform "RR_calf" ( + apiSchemas = ["PhysicsRigidBodyAPI", "PhysicsMassAPI"] + ) + { + point3f physics:centerOfMass = (0.00629595, 0.000622121, -0.141417) + float3 physics:diagonalInertia = (0.0014901, 0.00146356, 0.0000531397) + float physics:mass = 0.241352 + quatf physics:principalAxes = (0.70350766, -0.004500868, 0.0015409893, 0.71067166) + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (0, 0, -0.213) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + + def Mesh "calf_mirror_0" ( + apiSchemas = ["MaterialBindingAPI", "MjcImageableAPI"] + assetInfo = { + asset identifier = @meshes/calf_mirror_0.obj@ + string version = "sha256:6dc0406207a1c08a5953fd5962aca1a270fd06593d30d100205e05c1b32d4af2" + } + prepend references = @meshes/calf_mirror_0.usdc@ + ) + { + rel material:binding = + uniform int mjc:group = 2 + uniform token orientation = "rightHanded" + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + uniform token subdivisionScheme = "none" + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (-1.64799e-17, -1.89735e-19, 2.77556e-16) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Mesh "calf_mirror_1" ( + apiSchemas = ["MaterialBindingAPI", "MjcImageableAPI"] + assetInfo = { + asset identifier = @meshes/calf_mirror_1.obj@ + string version = "sha256:f84f5af90b914633165beb3a5493ee499169144c5797dcf04c6eb5842f74b31a" + } + prepend references = @meshes/calf_mirror_1.usdc@ + ) + { + rel material:binding = + uniform int mjc:group = 2 + uniform token orientation = "rightHanded" + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + uniform token subdivisionScheme = "none" + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (4.94396e-17, 1.78893e-18, 1.94289e-15) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Cylinder "Cylinder" ( + apiSchemas = ["PhysicsCollisionAPI", "NewtonCollisionAPI", "MjcCollisionAPI", "PhysicsMassAPI", "MaterialBindingAPI"] + ) + { + uniform token axis = "Z" + float3[] extent = [(-0.013, -0.013, -0.06), (0.013, 0.013, 0.06)] + double height = 0.12 + rel material:binding:physics = + uniform int mjc:condim = 1 + uniform int mjc:group = 3 + uniform double[] mjc:solref = [0.02, 1] + float newton:contactGap = 0 + float newton:contactMargin = 0.001 + float physics:density = 1000 + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + int[] primvars:displayColor:indices = None + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + int[] primvars:displayOpacity:indices = None + uniform token purpose = "guide" + double radius = 0.013 + quatf xformOp:orient = (0.9950042, 0, -0.0998333, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (0.01, 0, -0.06) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Cylinder "Cylinder_1" ( + apiSchemas = ["PhysicsCollisionAPI", "NewtonCollisionAPI", "MjcCollisionAPI", "PhysicsMassAPI", "MaterialBindingAPI"] + displayName = "Cylinder" + uiHints = { + string displayName = "Cylinder" + } + ) + { + uniform token axis = "Z" + float3[] extent = [(-0.011, -0.011, -0.0325), (0.011, 0.011, 0.0325)] + double height = 0.065 + rel material:binding:physics = + uniform int mjc:condim = 1 + uniform int mjc:group = 3 + uniform double[] mjc:solref = [0.02, 1] + float newton:contactGap = 0 + float newton:contactMargin = 0.001 + float physics:density = 1000 + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + int[] primvars:displayColor:indices = None + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + int[] primvars:displayOpacity:indices = None + uniform token purpose = "guide" + double radius = 0.011 + quatf xformOp:orient = (0.99968755, 0, 0.02499595, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (0.02, 0, -0.148) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Mesh "foot" ( + apiSchemas = ["MaterialBindingAPI", "MjcImageableAPI"] + assetInfo = { + asset identifier = @meshes/foot.obj@ + string version = "sha256:df9e78a7c0110d02557439010f2a5f11f0ec4fd907b32704145dbd6aa7affc0b" + } + prepend references = @meshes/foot.usdc@ + ) + { + rel material:binding = + uniform int mjc:group = 2 + uniform token orientation = "rightHanded" + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + uniform token subdivisionScheme = "none" + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (0, 0, -0.213) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Sphere "RR" ( + apiSchemas = ["PhysicsCollisionAPI", "NewtonCollisionAPI", "MjcCollisionAPI", "PhysicsMassAPI", "MaterialBindingAPI"] + ) + { + float3[] extent = [(-0.022, -0.022, -0.022), (0.022, 0.022, 0.022)] + rel material:binding:physics = + uniform int mjc:condim = 6 + uniform int mjc:group = 3 + uniform int mjc:priority = 1 + uniform double[] mjc:solimp = [0.015, 1, 0.022, 0.5, 2] + uniform double[] mjc:solref = [0.02, 1] + float newton:contactGap = 0 + float newton:contactMargin = 0.001 + float physics:density = 1000 + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + int[] primvars:displayColor:indices = None + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + int[] primvars:displayOpacity:indices = None + uniform token purpose = "guide" + double radius = 0.022 + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (-0.002, 0, -0.213) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def PhysicsRevoluteJoint "RR_calf_joint" ( + apiSchemas = ["MjcJointAPI", "NewtonJointAPI", "PhysicsDriveAPI:angular"] + ) + { + float drive:angular:physics:damping = 0.5 + float drive:angular:physics:stiffness = 20 + float drive:angular:physics:targetPosition = -1.5 + float newton:armature = 0.01 + float newton:damping = 0.034906585 + float newton:friction = 0.2 + uniform token physics:axis = "Y" + rel physics:body0 = + rel physics:body1 = + point3f physics:localPos0 = (0, 0, -0.213) + point3f physics:localPos1 = (0, 0, 0) + quatf physics:localRot0 = (1, 0, 0, 0) + quatf physics:localRot1 = (1, 0, 0, 0) + float physics:lowerLimit = -155.99922 + float physics:upperLimit = -48.00011 + } + } + + def PhysicsRevoluteJoint "RR_thigh_joint" ( + apiSchemas = ["MjcJointAPI", "NewtonJointAPI", "PhysicsDriveAPI:angular"] + ) + { + float drive:angular:physics:damping = 0.5 + float drive:angular:physics:stiffness = 20 + float drive:angular:physics:targetPosition = 0.8 + float newton:armature = 0.01 + float newton:damping = 0.034906585 + float newton:friction = 0.2 + uniform token physics:axis = "Y" + rel physics:body0 = + rel physics:body1 = + point3f physics:localPos0 = (0, -0.0955, 0) + point3f physics:localPos1 = (0, 0, 0) + quatf physics:localRot0 = (1, 0, 0, 0) + quatf physics:localRot1 = (1, 0, 0, 0) + float physics:lowerLimit = -30.00007 + float physics:upperLimit = 260.00253 + } + } + + def PhysicsRevoluteJoint "RR_hip_joint" ( + apiSchemas = ["MjcJointAPI", "NewtonJointAPI", "PhysicsDriveAPI:angular"] + ) + { + float drive:angular:physics:damping = 0.5 + float drive:angular:physics:stiffness = 20 + float drive:angular:physics:targetPosition = 0 + float newton:armature = 0.01 + float newton:damping = 0.034906585 + float newton:friction = 0.2 + uniform token physics:axis = "X" + rel physics:body0 = + rel physics:body1 = + point3f physics:localPos0 = (-0.1934, -0.0465, 0) + point3f physics:localPos1 = (0, 0, 0) + quatf physics:localRot0 = (1, 0, 0, 0) + quatf physics:localRot1 = (1, 0, 0, 0) + float physics:lowerLimit = -60.00014 + float physics:upperLimit = 60.00014 + } + } + } + } + + def Scope "Materials" + { + def Material "black" ( + apiSchemas = ["MaterialXConfigAPI"] + instanceable = true + add references = + ) + { + string config:mtlx:version = "1.39" + color3f inputs:color = (0, 0, 0) ( + limits = { + dictionary hard = { + float3 maximum = (1, 1, 1) + float3 minimum = (0, 0, 0) + } + } + sdrMetadata = { + string uimax = "1, 1, 1" + string uimin = "0, 0, 0" + } + ) + float inputs:metallic = 0 ( + limits = { + dictionary hard = { + float maximum = 1 + float minimum = 0 + } + } + sdrMetadata = { + string uimax = "1" + string uimin = "0" + } + ) + float inputs:opacity = 1 ( + limits = { + dictionary hard = { + float maximum = 1 + float minimum = 0 + } + } + sdrMetadata = { + string uimax = "1" + string uimin = "0" + } + ) + float inputs:roughness = 0.5 ( + limits = { + dictionary hard = { + float maximum = 1 + float minimum = 0 + } + } + sdrMetadata = { + string uimax = "1" + string uimin = "0" + } + ) + token outputs:mtlx:surface.connect = + token outputs:surface.connect = + } + + def Material "white" ( + apiSchemas = ["MaterialXConfigAPI"] + instanceable = true + add references = + ) + { + string config:mtlx:version = "1.39" + color3f inputs:color = (0.99999994, 1, 0.9999999) ( + limits = { + dictionary hard = { + float3 maximum = (1, 1, 1) + float3 minimum = (0, 0, 0) + } + } + sdrMetadata = { + string uimax = "1, 1, 1" + string uimin = "0, 0, 0" + } + ) + float inputs:metallic = 0 ( + limits = { + dictionary hard = { + float maximum = 1 + float minimum = 0 + } + } + sdrMetadata = { + string uimax = "1" + string uimin = "0" + } + ) + float inputs:opacity = 1 ( + limits = { + dictionary hard = { + float maximum = 1 + float minimum = 0 + } + } + sdrMetadata = { + string uimax = "1" + string uimin = "0" + } + ) + float inputs:roughness = 0.5 ( + limits = { + dictionary hard = { + float maximum = 1 + float minimum = 0 + } + } + sdrMetadata = { + string uimax = "1" + string uimin = "0" + } + ) + token outputs:mtlx:surface.connect = + token outputs:surface.connect = + } + + def Material "gray" ( + apiSchemas = ["MaterialXConfigAPI"] + instanceable = true + add references = + ) + { + string config:mtlx:version = "1.39" + color3f inputs:color = (0.40874615, 0.43727827, 0.5611312) ( + limits = { + dictionary hard = { + float3 maximum = (1, 1, 1) + float3 minimum = (0, 0, 0) + } + } + sdrMetadata = { + string uimax = "1, 1, 1" + string uimin = "0, 0, 0" + } + ) + float inputs:metallic = 0 ( + limits = { + dictionary hard = { + float maximum = 1 + float minimum = 0 + } + } + sdrMetadata = { + string uimax = "1" + string uimin = "0" + } + ) + float inputs:opacity = 1 ( + limits = { + dictionary hard = { + float maximum = 1 + float minimum = 0 + } + } + sdrMetadata = { + string uimax = "1" + string uimin = "0" + } + ) + float inputs:roughness = 0.5 ( + limits = { + dictionary hard = { + float maximum = 1 + float minimum = 0 + } + } + sdrMetadata = { + string uimax = "1" + string uimin = "0" + } + ) + token outputs:mtlx:surface.connect = + token outputs:surface.connect = + } + + def Material "metal" ( + apiSchemas = ["MaterialXConfigAPI"] + instanceable = true + add references = + ) + { + string config:mtlx:version = "1.39" + color3f inputs:color = (0.7874123, 0.8900055, 0.8900054) ( + limits = { + dictionary hard = { + float3 maximum = (1, 1, 1) + float3 minimum = (0, 0, 0) + } + } + sdrMetadata = { + string uimax = "1, 1, 1" + string uimin = "0, 0, 0" + } + ) + float inputs:metallic = 0 ( + limits = { + dictionary hard = { + float maximum = 1 + float minimum = 0 + } + } + sdrMetadata = { + string uimax = "1" + string uimin = "0" + } + ) + float inputs:opacity = 1 ( + limits = { + dictionary hard = { + float maximum = 1 + float minimum = 0 + } + } + sdrMetadata = { + string uimax = "1" + string uimin = "0" + } + ) + float inputs:roughness = 0.5 ( + limits = { + dictionary hard = { + float maximum = 1 + float minimum = 0 + } + } + sdrMetadata = { + string uimax = "1" + string uimin = "0" + } + ) + token outputs:mtlx:surface.connect = + token outputs:surface.connect = + } + } + + def Scope "Physics" + { + def Material "PhysicsMaterial" ( + apiSchemas = ["PhysicsMaterialAPI", "NewtonMaterialAPI"] + ) + { + float newton:contactDamping = 100 + float newton:contactStiffness = 2500 + float newton:rollingFriction = 0.0001 + float newton:torsionalFriction = 0.005 + float physics:dynamicFriction = 0.6 + float physics:staticFriction = 0.6 + } + + def Material "PhysicsMaterial_1" ( + apiSchemas = ["PhysicsMaterialAPI", "NewtonMaterialAPI"] + ) + { + float newton:contactDamping = 100 + float newton:contactStiffness = 2500 + float newton:rollingFriction = 0.01 + float newton:torsionalFriction = 0.02 + float physics:dynamicFriction = 0.8 + float physics:staticFriction = 0.8 + } + + def MjcActuator "FL_hip" + { + uniform double[] mjc:biasPrm = [0, -20, -0.5, 0, 0, 0, 0, 0, 0, 0] + uniform token mjc:biasType = "affine" + uniform token mjc:ctrlLimited = "false" + uniform double mjc:ctrlRange:max = 23.7 + uniform double mjc:ctrlRange:min = -23.7 + uniform token mjc:forceLimited = "true" + uniform double mjc:forceRange:max = 23.7 + uniform double mjc:forceRange:min = -23.7 + uniform double[] mjc:gainPrm = [20, 0, 0, 0, 0, 0, 0, 0, 0, 0] + rel mjc:target = + } + + def MjcActuator "FL_thigh" + { + uniform double[] mjc:biasPrm = [0, -20, -0.5, 0, 0, 0, 0, 0, 0, 0] + uniform token mjc:biasType = "affine" + uniform token mjc:ctrlLimited = "false" + uniform double mjc:ctrlRange:max = 23.7 + uniform double mjc:ctrlRange:min = -23.7 + uniform token mjc:forceLimited = "true" + uniform double mjc:forceRange:max = 23.7 + uniform double mjc:forceRange:min = -23.7 + uniform double[] mjc:gainPrm = [20, 0, 0, 0, 0, 0, 0, 0, 0, 0] + rel mjc:target = + } + + def MjcActuator "FL_calf" + { + uniform double[] mjc:biasPrm = [0, -20, -0.5, 0, 0, 0, 0, 0, 0, 0] + uniform token mjc:biasType = "affine" + uniform token mjc:ctrlLimited = "false" + uniform double mjc:ctrlRange:max = 45.43 + uniform double mjc:ctrlRange:min = -45.43 + uniform token mjc:forceLimited = "true" + uniform double mjc:forceRange:max = 45.43 + uniform double mjc:forceRange:min = -45.43 + uniform double[] mjc:gainPrm = [20, 0, 0, 0, 0, 0, 0, 0, 0, 0] + rel mjc:target = + } + + def MjcActuator "FR_hip" + { + uniform double[] mjc:biasPrm = [0, -20, -0.5, 0, 0, 0, 0, 0, 0, 0] + uniform token mjc:biasType = "affine" + uniform token mjc:ctrlLimited = "false" + uniform double mjc:ctrlRange:max = 23.7 + uniform double mjc:ctrlRange:min = -23.7 + uniform token mjc:forceLimited = "true" + uniform double mjc:forceRange:max = 23.7 + uniform double mjc:forceRange:min = -23.7 + uniform double[] mjc:gainPrm = [20, 0, 0, 0, 0, 0, 0, 0, 0, 0] + rel mjc:target = + } + + def MjcActuator "FR_thigh" + { + uniform double[] mjc:biasPrm = [0, -20, -0.5, 0, 0, 0, 0, 0, 0, 0] + uniform token mjc:biasType = "affine" + uniform token mjc:ctrlLimited = "false" + uniform double mjc:ctrlRange:max = 23.7 + uniform double mjc:ctrlRange:min = -23.7 + uniform token mjc:forceLimited = "true" + uniform double mjc:forceRange:max = 23.7 + uniform double mjc:forceRange:min = -23.7 + uniform double[] mjc:gainPrm = [20, 0, 0, 0, 0, 0, 0, 0, 0, 0] + rel mjc:target = + } + + def MjcActuator "FR_calf" + { + uniform double[] mjc:biasPrm = [0, -20, -0.5, 0, 0, 0, 0, 0, 0, 0] + uniform token mjc:biasType = "affine" + uniform token mjc:ctrlLimited = "false" + uniform double mjc:ctrlRange:max = 45.43 + uniform double mjc:ctrlRange:min = -45.43 + uniform token mjc:forceLimited = "true" + uniform double mjc:forceRange:max = 45.43 + uniform double mjc:forceRange:min = -45.43 + uniform double[] mjc:gainPrm = [20, 0, 0, 0, 0, 0, 0, 0, 0, 0] + rel mjc:target = + } + + def MjcActuator "RL_hip" + { + uniform double[] mjc:biasPrm = [0, -20, -0.5, 0, 0, 0, 0, 0, 0, 0] + uniform token mjc:biasType = "affine" + uniform token mjc:ctrlLimited = "false" + uniform double mjc:ctrlRange:max = 23.7 + uniform double mjc:ctrlRange:min = -23.7 + uniform token mjc:forceLimited = "true" + uniform double mjc:forceRange:max = 23.7 + uniform double mjc:forceRange:min = -23.7 + uniform double[] mjc:gainPrm = [20, 0, 0, 0, 0, 0, 0, 0, 0, 0] + rel mjc:target = + } + + def MjcActuator "RL_thigh" + { + uniform double[] mjc:biasPrm = [0, -20, -0.5, 0, 0, 0, 0, 0, 0, 0] + uniform token mjc:biasType = "affine" + uniform token mjc:ctrlLimited = "false" + uniform double mjc:ctrlRange:max = 23.7 + uniform double mjc:ctrlRange:min = -23.7 + uniform token mjc:forceLimited = "true" + uniform double mjc:forceRange:max = 23.7 + uniform double mjc:forceRange:min = -23.7 + uniform double[] mjc:gainPrm = [20, 0, 0, 0, 0, 0, 0, 0, 0, 0] + rel mjc:target = + } + + def MjcActuator "RL_calf" + { + uniform double[] mjc:biasPrm = [0, -20, -0.5, 0, 0, 0, 0, 0, 0, 0] + uniform token mjc:biasType = "affine" + uniform token mjc:ctrlLimited = "false" + uniform double mjc:ctrlRange:max = 45.43 + uniform double mjc:ctrlRange:min = -45.43 + uniform token mjc:forceLimited = "true" + uniform double mjc:forceRange:max = 45.43 + uniform double mjc:forceRange:min = -45.43 + uniform double[] mjc:gainPrm = [20, 0, 0, 0, 0, 0, 0, 0, 0, 0] + rel mjc:target = + } + + def MjcActuator "RR_hip" + { + uniform double[] mjc:biasPrm = [0, -20, -0.5, 0, 0, 0, 0, 0, 0, 0] + uniform token mjc:biasType = "affine" + uniform token mjc:ctrlLimited = "false" + uniform double mjc:ctrlRange:max = 23.7 + uniform double mjc:ctrlRange:min = -23.7 + uniform token mjc:forceLimited = "true" + uniform double mjc:forceRange:max = 23.7 + uniform double mjc:forceRange:min = -23.7 + uniform double[] mjc:gainPrm = [20, 0, 0, 0, 0, 0, 0, 0, 0, 0] + rel mjc:target = + } + + def MjcActuator "RR_thigh" + { + uniform double[] mjc:biasPrm = [0, -20, -0.5, 0, 0, 0, 0, 0, 0, 0] + uniform token mjc:biasType = "affine" + uniform token mjc:ctrlLimited = "false" + uniform double mjc:ctrlRange:max = 23.7 + uniform double mjc:ctrlRange:min = -23.7 + uniform token mjc:forceLimited = "true" + uniform double mjc:forceRange:max = 23.7 + uniform double mjc:forceRange:min = -23.7 + uniform double[] mjc:gainPrm = [20, 0, 0, 0, 0, 0, 0, 0, 0, 0] + rel mjc:target = + } + + def MjcActuator "RR_calf" + { + uniform double[] mjc:biasPrm = [0, -20, -0.5, 0, 0, 0, 0, 0, 0, 0] + uniform token mjc:biasType = "affine" + uniform token mjc:ctrlLimited = "false" + uniform double mjc:ctrlRange:max = 45.43 + uniform double mjc:ctrlRange:min = -45.43 + uniform token mjc:forceLimited = "true" + uniform double mjc:forceRange:max = 45.43 + uniform double mjc:forceRange:min = -45.43 + uniform double[] mjc:gainPrm = [20, 0, 0, 0, 0, 0, 0, 0, 0, 0] + rel mjc:target = + } + } +} + +def PhysicsScene "PhysicsScene" ( + apiSchemas = ["NewtonSceneAPI", "MjcSceneAPI"] +) +{ + uniform token mjc:compiler:angle = "radian" + uniform token mjc:option:cone = "elliptic" + uniform double mjc:option:impratio = 100 + uniform int newton:maxSolverIterations = 100 + uniform int newton:timeStepsPerSecond = 500 + vector3f physics:gravityDirection = (0, 0, -1) + float physics:gravityMagnitude = 9.81 +} diff --git a/dimos/robot/unitree/go2/assets/meshes/base_0.obj b/dimos/robot/unitree/go2/assets/meshes/base_0.obj new file mode 100644 index 00000000000..5fe9df91055 --- /dev/null +++ b/dimos/robot/unitree/go2/assets/meshes/base_0.obj @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8cdc53719002a5ee737d867904e534a6d6fa930b616bdcc1b3131e577d4e46c0 +size 1330465 diff --git a/dimos/robot/unitree/go2/assets/meshes/base_0.usdc b/dimos/robot/unitree/go2/assets/meshes/base_0.usdc new file mode 100644 index 00000000000..0038b6ce998 --- /dev/null +++ b/dimos/robot/unitree/go2/assets/meshes/base_0.usdc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bdf0954da8b72a7c1912509d4138358e2ce66cea8489f9eb679970802f08659e +size 145178 diff --git a/dimos/robot/unitree/go2/assets/meshes/base_1.obj b/dimos/robot/unitree/go2/assets/meshes/base_1.obj new file mode 100644 index 00000000000..794801d4d61 --- /dev/null +++ b/dimos/robot/unitree/go2/assets/meshes/base_1.obj @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:39d2f4724ff64dd5a0640c308fd98902a2946660a8d033ec8dbf368fd5af0dd6 +size 811791 diff --git a/dimos/robot/unitree/go2/assets/meshes/base_1.usdc b/dimos/robot/unitree/go2/assets/meshes/base_1.usdc new file mode 100644 index 00000000000..0e24ca084ae --- /dev/null +++ b/dimos/robot/unitree/go2/assets/meshes/base_1.usdc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:60db2dd6c8e9da1bc1eaa9f9e265fa05539495f9dde6c228f08a50bc8243af41 +size 93854 diff --git a/dimos/robot/unitree/go2/assets/meshes/base_2.obj b/dimos/robot/unitree/go2/assets/meshes/base_2.obj new file mode 100644 index 00000000000..4f9b38d8d01 --- /dev/null +++ b/dimos/robot/unitree/go2/assets/meshes/base_2.obj @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:29cae463f95d678e4ede863c36c9b19b0d356b8340412add10670d70bdcc3095 +size 294090 diff --git a/dimos/robot/unitree/go2/assets/meshes/base_2.usdc b/dimos/robot/unitree/go2/assets/meshes/base_2.usdc new file mode 100644 index 00000000000..3c3c8de8e7e --- /dev/null +++ b/dimos/robot/unitree/go2/assets/meshes/base_2.usdc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d4a2fe5304c694d10166999f88b278101a4b8f887c316af480bab06a812489ca +size 34306 diff --git a/dimos/robot/unitree/go2/assets/meshes/base_3.obj b/dimos/robot/unitree/go2/assets/meshes/base_3.obj new file mode 100644 index 00000000000..a15aa64b2c3 --- /dev/null +++ b/dimos/robot/unitree/go2/assets/meshes/base_3.obj @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f51ba43bf300e68957a0c1aa83eaf3e4926f4ac7e64a0d6c33ae5d21fcbcf5f6 +size 379093 diff --git a/dimos/robot/unitree/go2/assets/meshes/base_3.usdc b/dimos/robot/unitree/go2/assets/meshes/base_3.usdc new file mode 100644 index 00000000000..0e95d6ed8a0 --- /dev/null +++ b/dimos/robot/unitree/go2/assets/meshes/base_3.usdc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fe4aeeaf2a78031bdae691a82ef56a102f9139167093917de62bbfbab4b52104 +size 43946 diff --git a/dimos/robot/unitree/go2/assets/meshes/base_4.obj b/dimos/robot/unitree/go2/assets/meshes/base_4.obj new file mode 100644 index 00000000000..b1f8c0da332 --- /dev/null +++ b/dimos/robot/unitree/go2/assets/meshes/base_4.obj @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:49ddec0129546e5dd2075d5beacf7f207baa2860aeff7626818e64d2efbf3821 +size 7776234 diff --git a/dimos/robot/unitree/go2/assets/meshes/base_4.usdc b/dimos/robot/unitree/go2/assets/meshes/base_4.usdc new file mode 100644 index 00000000000..06b2d72069c --- /dev/null +++ b/dimos/robot/unitree/go2/assets/meshes/base_4.usdc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:aa1cefc73622ef12333bdd9c6149cc3494906ef5930c524d1a246714f84e3d08 +size 801602 diff --git a/dimos/robot/unitree/go2/assets/meshes/calf_0.obj b/dimos/robot/unitree/go2/assets/meshes/calf_0.obj new file mode 100644 index 00000000000..3f35628886d --- /dev/null +++ b/dimos/robot/unitree/go2/assets/meshes/calf_0.obj @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4a818cd563c5b7e2a271504442d05fd777d38cbaa028d4a10ad29c9f87bb355e +size 877174 diff --git a/dimos/robot/unitree/go2/assets/meshes/calf_0.usdc b/dimos/robot/unitree/go2/assets/meshes/calf_0.usdc new file mode 100644 index 00000000000..624aecfcb88 --- /dev/null +++ b/dimos/robot/unitree/go2/assets/meshes/calf_0.usdc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a027596b564101bbc90dc6813e203631c6ed3e65ba82f912a00121717cc862ac +size 96538 diff --git a/dimos/robot/unitree/go2/assets/meshes/calf_1.obj b/dimos/robot/unitree/go2/assets/meshes/calf_1.obj new file mode 100644 index 00000000000..053c95f2f19 --- /dev/null +++ b/dimos/robot/unitree/go2/assets/meshes/calf_1.obj @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:51950c3a153ae3208592df86ea11c9d6756f8f645ebca5caf6910c61aeb03817 +size 326865 diff --git a/dimos/robot/unitree/go2/assets/meshes/calf_1.usdc b/dimos/robot/unitree/go2/assets/meshes/calf_1.usdc new file mode 100644 index 00000000000..d98525955e8 --- /dev/null +++ b/dimos/robot/unitree/go2/assets/meshes/calf_1.usdc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0d7cddd2273cbbdf4e76f9bc183eab355cc4ebd867dfbaa13c5f99e60dccd16d +size 37930 diff --git a/dimos/robot/unitree/go2/assets/meshes/calf_mirror_0.obj b/dimos/robot/unitree/go2/assets/meshes/calf_mirror_0.obj new file mode 100644 index 00000000000..ba259c36a94 --- /dev/null +++ b/dimos/robot/unitree/go2/assets/meshes/calf_mirror_0.obj @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6dc0406207a1c08a5953fd5962aca1a270fd06593d30d100205e05c1b32d4af2 +size 876538 diff --git a/dimos/robot/unitree/go2/assets/meshes/calf_mirror_0.usdc b/dimos/robot/unitree/go2/assets/meshes/calf_mirror_0.usdc new file mode 100644 index 00000000000..4c122f0da8b --- /dev/null +++ b/dimos/robot/unitree/go2/assets/meshes/calf_mirror_0.usdc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8b27304f37607e8c922d203a78de9b9f49b57df6a5ccda56774a793691a71993 +size 96430 diff --git a/dimos/robot/unitree/go2/assets/meshes/calf_mirror_1.obj b/dimos/robot/unitree/go2/assets/meshes/calf_mirror_1.obj new file mode 100644 index 00000000000..6a8527eea70 --- /dev/null +++ b/dimos/robot/unitree/go2/assets/meshes/calf_mirror_1.obj @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f84f5af90b914633165beb3a5493ee499169144c5797dcf04c6eb5842f74b31a +size 327261 diff --git a/dimos/robot/unitree/go2/assets/meshes/calf_mirror_1.usdc b/dimos/robot/unitree/go2/assets/meshes/calf_mirror_1.usdc new file mode 100644 index 00000000000..3a8e488dd2a --- /dev/null +++ b/dimos/robot/unitree/go2/assets/meshes/calf_mirror_1.usdc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a2cc51d09f4eec723243156a6e45c94f91d72372e4e33ffd660415d3f59dd7da +size 38006 diff --git a/dimos/robot/unitree/go2/assets/meshes/foot.obj b/dimos/robot/unitree/go2/assets/meshes/foot.obj new file mode 100644 index 00000000000..51a8bf807c4 --- /dev/null +++ b/dimos/robot/unitree/go2/assets/meshes/foot.obj @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:df9e78a7c0110d02557439010f2a5f11f0ec4fd907b32704145dbd6aa7affc0b +size 1144729 diff --git a/dimos/robot/unitree/go2/assets/meshes/foot.usdc b/dimos/robot/unitree/go2/assets/meshes/foot.usdc new file mode 100644 index 00000000000..bb06b449079 --- /dev/null +++ b/dimos/robot/unitree/go2/assets/meshes/foot.usdc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:34b59f98171216944299a4e7f383b6cc4097d4614fc5cacfadba7d8cc261b682 +size 181362 diff --git a/dimos/robot/unitree/go2/assets/meshes/hip_0.obj b/dimos/robot/unitree/go2/assets/meshes/hip_0.obj new file mode 100644 index 00000000000..cc11c34b045 --- /dev/null +++ b/dimos/robot/unitree/go2/assets/meshes/hip_0.obj @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cc29932ab4d251ce3f72389ec4c1f757e44eee138b87e772b648c2bc76b506cd +size 2863864 diff --git a/dimos/robot/unitree/go2/assets/meshes/hip_0.usdc b/dimos/robot/unitree/go2/assets/meshes/hip_0.usdc new file mode 100644 index 00000000000..b2b0d36e91b --- /dev/null +++ b/dimos/robot/unitree/go2/assets/meshes/hip_0.usdc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f601f6be95ade019bce65636481db4941f1b417cf805b2c25fb439896a2b4e85 +size 307938 diff --git a/dimos/robot/unitree/go2/assets/meshes/hip_1.obj b/dimos/robot/unitree/go2/assets/meshes/hip_1.obj new file mode 100644 index 00000000000..ce4ab76ed4e --- /dev/null +++ b/dimos/robot/unitree/go2/assets/meshes/hip_1.obj @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3fd5bd9b2ef6abe622c0739d3c67eaa1679b19b47132951972c4892a779ea7cf +size 2767918 diff --git a/dimos/robot/unitree/go2/assets/meshes/hip_1.usdc b/dimos/robot/unitree/go2/assets/meshes/hip_1.usdc new file mode 100644 index 00000000000..f99b04b2f77 --- /dev/null +++ b/dimos/robot/unitree/go2/assets/meshes/hip_1.usdc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e3b479d7d8540be8a8315c6fd010c36bf81dd771f5f5d42994ab3f17feddefab +size 295838 diff --git a/dimos/robot/unitree/go2/assets/meshes/thigh_0.obj b/dimos/robot/unitree/go2/assets/meshes/thigh_0.obj new file mode 100644 index 00000000000..89d373e7251 --- /dev/null +++ b/dimos/robot/unitree/go2/assets/meshes/thigh_0.obj @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bb3edfe6a7b04f4f4dff1ce7b05d426ddce941e276cc6372fb542055893c71ec +size 2841868 diff --git a/dimos/robot/unitree/go2/assets/meshes/thigh_0.usdc b/dimos/robot/unitree/go2/assets/meshes/thigh_0.usdc new file mode 100644 index 00000000000..a1ea59bda2e --- /dev/null +++ b/dimos/robot/unitree/go2/assets/meshes/thigh_0.usdc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fa500d407e3024ec41bd28d8c08415312842aac4f9a2cefc4a3388f38cfab678 +size 307550 diff --git a/dimos/robot/unitree/go2/assets/meshes/thigh_1.obj b/dimos/robot/unitree/go2/assets/meshes/thigh_1.obj new file mode 100644 index 00000000000..7a231d1de8a --- /dev/null +++ b/dimos/robot/unitree/go2/assets/meshes/thigh_1.obj @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3a631822f9f7d2114b1b79dcdff5abf95103630abb7d91d1f3b8b18117132ed2 +size 1491320 diff --git a/dimos/robot/unitree/go2/assets/meshes/thigh_1.usdc b/dimos/robot/unitree/go2/assets/meshes/thigh_1.usdc new file mode 100644 index 00000000000..edda64f1fe0 --- /dev/null +++ b/dimos/robot/unitree/go2/assets/meshes/thigh_1.usdc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9db5134864dc5a19686ed60eaa792a006b6f7dcf6843a05d8a487accf7ab1a0d +size 161766 diff --git a/dimos/robot/unitree/go2/assets/meshes/thigh_mirror_0.obj b/dimos/robot/unitree/go2/assets/meshes/thigh_mirror_0.obj new file mode 100644 index 00000000000..12995f29c75 --- /dev/null +++ b/dimos/robot/unitree/go2/assets/meshes/thigh_mirror_0.obj @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:07abaedce761f1dd48647384ac14ba1959276bc78d5a2a62051e8f1c6093933b +size 2817674 diff --git a/dimos/robot/unitree/go2/assets/meshes/thigh_mirror_0.usdc b/dimos/robot/unitree/go2/assets/meshes/thigh_mirror_0.usdc new file mode 100644 index 00000000000..6593b52c396 --- /dev/null +++ b/dimos/robot/unitree/go2/assets/meshes/thigh_mirror_0.usdc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9e66c1dbc8c609b816a392653eb67e62efc9f75fb14992e6ac24726a4470ec05 +size 306238 diff --git a/dimos/robot/unitree/go2/assets/meshes/thigh_mirror_1.obj b/dimos/robot/unitree/go2/assets/meshes/thigh_mirror_1.obj new file mode 100644 index 00000000000..3417868ae49 --- /dev/null +++ b/dimos/robot/unitree/go2/assets/meshes/thigh_mirror_1.obj @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:db984a3e983eabbc9b38183427cca9ab0bb3bdb049f9eab030e06edda36579d6 +size 1482261 diff --git a/dimos/robot/unitree/go2/assets/meshes/thigh_mirror_1.usdc b/dimos/robot/unitree/go2/assets/meshes/thigh_mirror_1.usdc new file mode 100644 index 00000000000..13e2b02826d --- /dev/null +++ b/dimos/robot/unitree/go2/assets/meshes/thigh_mirror_1.usdc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e16aff230853e6c399516f7adaf1c1ab1523dbe1ac7c0b8483f246cdbd98d7a8 +size 161402 diff --git a/dimos/robot/unitree/go2/blueprints/basic/unitree_go2_sim2_l1.py b/dimos/robot/unitree/go2/blueprints/basic/unitree_go2_sim2_l1.py new file mode 100644 index 00000000000..697e0c9eb9a --- /dev/null +++ b/dimos/robot/unitree/go2/blueprints/basic/unitree_go2_sim2_l1.py @@ -0,0 +1,62 @@ +# Copyright 2026 Dimensional Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +from dimos.core.coordination.blueprints import autoconnect +from dimos.core.global_config import global_config +from dimos.robot.assets.model import UsdModel +from dimos.robot.unitree.go2.blueprints.basic.unitree_go2_basic import rerun_config +from dimos.robot.unitree.go2.sim2 import Go2Sim +from dimos.robot.usd_manager import UsdManager +from dimos.sim2.assets.logistics import LOGISTICS +from dimos.sim2.camera import SimCamera +from dimos.sim2.l1 import SimL1 +from dimos.sim2.sim import sim_module +from dimos.sim2.spec import SimulatedRobot +from dimos.simulation.go2_legged.robot import CONTROL_DT +from dimos.visualization.vis_module import vis_module + +ROBOT = "go2" +SPOT = (0.0, 0.0, 0.35) +SCENE = "scene" +SCENE_MANAGER = f"{SCENE}/{UsdManager.name}" + +_room = UsdModel.from_file(LOGISTICS).with_frames({ROBOT: ("logistics", SPOT, (0.0, 0.0, 0.0))}) + +_lidar = SimL1.blueprint(frame_id=Go2Sim.l1_mount_frame, scene_manager=SCENE_MANAGER) +_camera = SimCamera.blueprint(frame_id=Go2Sim.camera_mount_frame, scene_manager=SCENE_MANAGER) + +_robot = UsdManager.blueprint( + model=Go2Sim.usd, mounts={Go2Sim.l1_mount_frame: _lidar, Go2Sim.camera_mount_frame: _camera} +).remappings([(UsdManager, "coordinator_joint_state", "joint_state")]) + +_scene = UsdManager.blueprint( + model=_room, + mounts={ROBOT: _robot}, + frame_id_prefix="", + tf_renames={"logistics": "world"}, +).namespace(SCENE, expose={"render_file_request", "file_info", "tf"}) + +unitree_go2_sim2_l1 = autoconnect( + sim_module( + scene_manager=SCENE_MANAGER, + robots=[SimulatedRobot(ROBOT, Go2Sim.usd, control_period=CONTROL_DT)], + ), + vis_module(viewer_backend=global_config.viewer, rerun_config=rerun_config), + Go2Sim.blueprint(robot_id=ROBOT), + _scene, + _robot, + _lidar, + _camera, +).global_config(n_workers=8, robot_model="unitree_go2") diff --git a/dimos/robot/unitree/go2/sim2.py b/dimos/robot/unitree/go2/sim2.py new file mode 100644 index 00000000000..e83bfea82f4 --- /dev/null +++ b/dimos/robot/unitree/go2/sim2.py @@ -0,0 +1,169 @@ +# Copyright 2026 Dimensional Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import asyncio +from collections.abc import AsyncIterator +import math +from pathlib import Path +from typing import Any + +from dimos_lcm.world_msgs import WorldAction, WorldObservations +import numpy as np + +from dimos.core.module import Module, ModuleConfig +from dimos.core.stream import In, Out +from dimos.msgs.geometry_msgs.PoseStamped import PoseStamped +from dimos.msgs.geometry_msgs.Transform import Transform +from dimos.msgs.geometry_msgs.Twist import Twist +from dimos.msgs.sensor_msgs.JointState import JointState +from dimos.msgs.tf2_msgs.TFMessage import TFMessage +from dimos.robot.assets.model import UsdModel +from dimos.robot.unitree.go2.constants import CMD_VEL_TIMEOUT +from dimos.robot.unitree.go2.go2_mid360_static_transforms import ( + CAMERA_XYZ, + L1_MOUNT, + L1_XYZ, + MID360_PITCH_DOWN, + MID360_XYZ, + OPTICAL_RPY, +) +from dimos.sim2.actions import JointCommand, world_action +from dimos.sim2.mujoco.sensor_world import rotation_matrix +from dimos.sim2.world_observations import ACT, WorldMirror +from dimos.simulation.go2_legged.policy import Proprioception, load_policy +from dimos.simulation.go2_legged.robot import COMMAND_SLEW, projected_gravity, yaw_of + + +def go2_drives() -> dict[str, tuple[float, float, float]]: + policy = load_policy(None) + return { + joint: (float(kp), float(kd), float(q)) + for joint, kp, kd, q in zip( + policy.joint_names, policy.kp, policy.kd, policy.default_pose, strict=True + ) + } + + +class Go2SimConfig(ModuleConfig): + robot_id: str = "go2" + policy: Path | None = None + odom_frame_id: str = "world" + heading_gain: float = 2.0 + + +class Go2Sim(Module): + usd = UsdModel.from_file(Path(__file__).parent / "assets" / "go2.usda").with_frames( + { + "utlidar_lidar": ( + "base", + L1_XYZ, + (math.radians(L1_MOUNT[0]), math.radians(L1_MOUNT[1]), math.radians(L1_MOUNT[2])), + ), + "front_camera": ("base", CAMERA_XYZ, (0.0, 0.0, 0.0)), + "mid360_link": ("front_camera", MID360_XYZ, (0.0, MID360_PITCH_DOWN, 0.0)), + "camera_optical": ("front_camera", (0.0, 0.0, 0.0), OPTICAL_RPY), + } + ) + l1_mount_frame = "utlidar_lidar" + lidar_mount_frame = "mid360_link" + camera_mount_frame = "camera_optical" + + config: Go2SimConfig + cmd_vel: In[Twist] + world_observations: In[WorldObservations] + actions: Out[WorldAction] + odom: Out[PoseStamped] + tf: Out[TFMessage] + joint_state: Out[JointState] + + def __init__(self, **kwargs: Any) -> None: + super().__init__(**kwargs) + self._policy = load_policy(self.config.policy) + self._desired = np.zeros(3) + self._desired_expires = 0.0 + self._command = np.zeros(3) + self._heading: float | None = None + + async def main(self) -> AsyncIterator[None]: + self._world = WorldMirror() + loop = asyncio.get_running_loop() + + def on_frame(world: WorldMirror) -> None: + if world.got(self.config.robot_id, ACT): + loop.call_soon_threadsafe(self._act) + + self._world.on_frame(on_frame) + self._world.subscribe(self.world_observations) + yield + + async def handle_cmd_vel(self, twist: Twist) -> None: + self._desired = np.array([twist.linear.x, twist.linear.y, twist.angular.z]) + self._desired_expires = self._world.stamp() + CMD_VEL_TIMEOUT + + def _act(self) -> None: + policy, world = self._policy, self._world + with world.lock: + base = world.entity(self.config.robot_id) + names, rows = world.joints(self.config.robot_id) + stamp = world.stamp() + if base is None or base.pose is None or base.velocity is None: + return + x, y, z, w = base.pose[3:] + quat = np.array([w, x, y, z]) + desired = self._desired if stamp < self._desired_expires else np.zeros(3) + self._command += np.clip(desired - self._command, -COMMAND_SLEW, COMMAND_SLEW) + if desired[2] == 0: + self._heading = yaw_of(quat) if self._heading is None else self._heading + turn = self.config.heading_gain * math.remainder(self._heading - yaw_of(quat), math.tau) + else: + self._heading, turn = None, 0.0 + joints = {name: row for name, row in zip(names, rows, strict=True)} + observation = Proprioception( + angular_velocity=rotation_matrix(quat).T @ base.velocity[3:], + gravity=projected_gravity(quat), + joint_position=np.array([joints[name][0] for name in policy.joint_names]), + joint_velocity=np.array([joints[name][1] for name in policy.joint_names]), + ) + targets = policy.act(observation, self._command + np.array([0.0, 0.0, turn])) + self.actions.publish( + world_action( + world, + JointCommand( + self.config.robot_id, + tuple(policy.joint_names), + position=tuple(map(float, targets)), + ), + ) + ) + self.joint_state.publish( + JointState( + ts=stamp, + name=list(policy.joint_names), + position=observation.joint_position.tolist(), + velocity=observation.joint_velocity.tolist(), + ) + ) + self._publish_pose(base.pose[:3], quat, stamp) + + def _publish_pose(self, position: np.ndarray, quat_wxyz: np.ndarray, stamp: float) -> None: + w, x, y, z = quat_wxyz + odom = PoseStamped( + ts=stamp, + frame_id=self.config.odom_frame_id, + position=position.tolist(), + orientation=[x, y, z, w], + ) + self.tf.publish(TFMessage(Transform.from_pose("base_link", odom))) + self.odom.publish(odom) diff --git a/dimos/robot/unitree/go2/test_usd.py b/dimos/robot/unitree/go2/test_usd.py new file mode 100644 index 00000000000..c68ee396e6a --- /dev/null +++ b/dimos/robot/unitree/go2/test_usd.py @@ -0,0 +1,35 @@ +# Copyright 2026 Dimensional Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import pytest + +pytest.importorskip("pxr") + + +def test_the_layer_carries_the_gains_the_policy_runs_on() -> None: + from dimos.robot.assets.usd import usd_drives + from dimos.robot.unitree.go2.sim2 import Go2Sim, go2_drives + + assert usd_drives(Go2Sim.usd.usd) == { + joint: pytest.approx(gains) for joint, gains in go2_drives().items() + } + + +def test_every_sensor_mount_the_rig_has_is_a_frame_of_the_layer() -> None: + from dimos.robot.unitree.go2.sim2 import Go2Sim + + frames = Go2Sim.usd.frame_names() + + assert {Go2Sim.l1_mount_frame, Go2Sim.lidar_mount_frame, Go2Sim.camera_mount_frame} <= frames diff --git a/dimos/sim2/assets/logistics.py b/dimos/sim2/assets/logistics.py new file mode 100644 index 00000000000..30d0e8d83c5 --- /dev/null +++ b/dimos/sim2/assets/logistics.py @@ -0,0 +1,18 @@ +# Copyright 2026 Dimensional Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +from pathlib import Path + +LOGISTICS = Path(__file__).parent / "logistics.usda" diff --git a/dimos/sim2/assets/logistics.usda b/dimos/sim2/assets/logistics.usda new file mode 100644 index 00000000000..1290bdf8d16 --- /dev/null +++ b/dimos/sim2/assets/logistics.usda @@ -0,0 +1,388 @@ +#usda 1.0 +( + customLayerData = { + string creator = "MuJoCo USD Converter v0.9.0" + } + defaultPrim = "logistics" + doc = """Generated from Composed Stage of root layer /tmp/claude-1000/-home-dimos/ff9ad774-151d-43c4-9774-fa96d8ad90f5/scratchpad/logistics/converted/logistics.usda +""" + kilogramsPerUnit = 1 + metersPerUnit = 1 + upAxis = "Z" +) + +over "Flattened_Prototype_1" +{ + def Shader "PreviewSurface" + { + uniform token info:id = "UsdPreviewSurface" + color3f inputs:diffuseColor.connect = + float inputs:metallic.connect = + float inputs:opacity.connect = + float inputs:roughness.connect = + token outputs:surface + } + + def Shader "OpenPBR" + { + uniform token info:id = "ND_open_pbr_surface_surfaceshader" + color3f inputs:base_color.connect = + float inputs:base_metalness.connect = + float inputs:geometry_opacity.connect = + float inputs:specular_roughness.connect = + token outputs:out + } +} + +def Xform "logistics" ( + apiSchemas = ["GeomModelAPI"] + assetInfo = { + string name = "logistics" + } + kind = "component" +) +{ + float3[] extentsHint = [(-10, -10, -1.3113022e-8), (10, 10, 3)] + + def Scope "Geometry" + { + def Plane "floor" ( + apiSchemas = ["PhysicsCollisionAPI", "NewtonCollisionAPI", "MjcCollisionAPI", "PhysicsMassAPI", "MaterialBindingAPI"] + ) + { + uniform token axis = "Z" + float3[] extent = [(-10, -10, -0), (10, 10, 0)] + double length = 20 + rel material:binding = + rel material:binding:physics = + uniform double[] mjc:solref = [0.02, 1] + float newton:contactGap = 0 + float physics:density = 1000 + color3f[] primvars:displayColor = [(0.5, 0.5, 0.5)] ( + interpolation = "constant" + ) + int[] primvars:displayColor:indices = None + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + int[] primvars:displayOpacity:indices = None + double width = 20 + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 1, 1) + double3 xformOp:translate = (0, 0, 0) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Cube "north_wall" ( + apiSchemas = ["PhysicsCollisionAPI", "NewtonCollisionAPI", "MjcCollisionAPI", "PhysicsMassAPI", "MaterialBindingAPI"] + ) + { + float3[] extent = [(-1, -1, -1), (1, 1, 1)] + rel material:binding:physics = + uniform double[] mjc:solref = [0.02, 1] + float newton:contactGap = 0 + float physics:density = 1000 + color3f[] primvars:displayColor = [(0.78, 0.8, 0.8)] ( + interpolation = "constant" + ) + int[] primvars:displayColor:indices = None + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + int[] primvars:displayOpacity:indices = None + double size = 2 + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (6, 0.1, 1.5) + double3 xformOp:translate = (0, 5, 1.5) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Cube "west_wall" ( + apiSchemas = ["PhysicsCollisionAPI", "NewtonCollisionAPI", "MjcCollisionAPI", "PhysicsMassAPI", "MaterialBindingAPI"] + ) + { + float3[] extent = [(-1, -1, -1), (1, 1, 1)] + rel material:binding:physics = + uniform double[] mjc:solref = [0.02, 1] + float newton:contactGap = 0 + float physics:density = 1000 + color3f[] primvars:displayColor = [(0.78, 0.8, 0.8)] ( + interpolation = "constant" + ) + int[] primvars:displayColor:indices = None + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + int[] primvars:displayOpacity:indices = None + double size = 2 + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (0.1, 5, 1.5) + double3 xformOp:translate = (-5, 0, 1.5) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Cube "shelf_left" ( + apiSchemas = ["PhysicsCollisionAPI", "NewtonCollisionAPI", "MjcCollisionAPI", "PhysicsMassAPI", "MaterialBindingAPI"] + ) + { + float3[] extent = [(-1, -1, -1), (1, 1, 1)] + rel material:binding:physics = + uniform double[] mjc:solref = [0.02, 1] + float newton:contactGap = 0 + float physics:density = 1000 + color3f[] primvars:displayColor = [(0.16, 0.36, 0.35)] ( + interpolation = "constant" + ) + int[] primvars:displayColor:indices = None + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + int[] primvars:displayOpacity:indices = None + double size = 2 + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (0.45, 1.2, 0.8) + double3 xformOp:translate = (3, 2, 0.8) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Cube "shelf_right" ( + apiSchemas = ["PhysicsCollisionAPI", "NewtonCollisionAPI", "MjcCollisionAPI", "PhysicsMassAPI", "MaterialBindingAPI"] + ) + { + float3[] extent = [(-1, -1, -1), (1, 1, 1)] + rel material:binding:physics = + uniform double[] mjc:solref = [0.02, 1] + float newton:contactGap = 0 + float physics:density = 1000 + color3f[] primvars:displayColor = [(0.16, 0.36, 0.35)] ( + interpolation = "constant" + ) + int[] primvars:displayColor:indices = None + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + int[] primvars:displayOpacity:indices = None + double size = 2 + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (0.45, 1.2, 0.8) + double3 xformOp:translate = (3, -2, 0.8) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Cube "crate" ( + apiSchemas = ["PhysicsCollisionAPI", "NewtonCollisionAPI", "MjcCollisionAPI", "PhysicsMassAPI", "MaterialBindingAPI"] + ) + { + float3[] extent = [(-1, -1, -1), (1, 1, 1)] + rel material:binding:physics = + uniform double[] mjc:solref = [0.02, 1] + float newton:contactGap = 0 + float physics:density = 1000 + color3f[] primvars:displayColor = [(0.74, 0.28, 0.18)] ( + interpolation = "constant" + ) + int[] primvars:displayColor:indices = None + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + int[] primvars:displayOpacity:indices = None + double size = 2 + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (0.4, 0.4, 0.3) + double3 xformOp:translate = (-2, 2, 0.3) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Cube "table" ( + apiSchemas = ["PhysicsCollisionAPI", "NewtonCollisionAPI", "MjcCollisionAPI", "PhysicsMassAPI", "MaterialBindingAPI"] + ) + { + float3[] extent = [(-1, -1, -1), (1, 1, 1)] + rel material:binding:physics = + uniform double[] mjc:solref = [0.02, 1] + float newton:contactGap = 0 + float physics:density = 1000 + color3f[] primvars:displayColor = [(0.5, 0.52, 0.55)] ( + interpolation = "constant" + ) + int[] primvars:displayColor:indices = None + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + int[] primvars:displayOpacity:indices = None + double size = 2 + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (1, 0.5, 0.06) + double3 xformOp:translate = (0, -3, 0.7) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Cube "Box" ( + apiSchemas = ["PhysicsCollisionAPI", "NewtonCollisionAPI", "MjcCollisionAPI", "PhysicsMassAPI", "MaterialBindingAPI"] + ) + { + float3[] extent = [(-1, -1, -1), (1, 1, 1)] + rel material:binding:physics = + uniform double[] mjc:solref = [0.02, 1] + float newton:contactGap = 0 + float physics:density = 1000 + color3f[] primvars:displayColor = [(0.22, 0.25, 0.25)] ( + interpolation = "constant" + ) + int[] primvars:displayColor:indices = None + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + int[] primvars:displayOpacity:indices = None + double size = 2 + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (0.06, 0.4, 0.33) + double3 xformOp:translate = (-0.8, -3, 0.33) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + + def Cube "Box_1" ( + apiSchemas = ["PhysicsCollisionAPI", "NewtonCollisionAPI", "MjcCollisionAPI", "PhysicsMassAPI", "MaterialBindingAPI"] + displayName = "Box" + uiHints = { + string displayName = "Box" + } + ) + { + float3[] extent = [(-1, -1, -1), (1, 1, 1)] + rel material:binding:physics = + uniform double[] mjc:solref = [0.02, 1] + float newton:contactGap = 0 + float physics:density = 1000 + color3f[] primvars:displayColor = [(0.22, 0.25, 0.25)] ( + interpolation = "constant" + ) + int[] primvars:displayColor:indices = None + float[] primvars:displayOpacity = [1] ( + interpolation = "constant" + ) + int[] primvars:displayOpacity:indices = None + double size = 2 + quatf xformOp:orient = (1, 0, 0, 0) + float3 xformOp:scale = (0.06, 0.4, 0.33) + double3 xformOp:translate = (0.8, -3, 0.33) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:orient", "xformOp:scale"] + } + } + + def Scope "Materials" + { + def Material "floor" ( + apiSchemas = ["MaterialXConfigAPI"] + instanceable = true + add references = + ) + { + string config:mtlx:version = "1.39" + color3f inputs:color = (0.635, 0.67, 0.67) ( + limits = { + dictionary hard = { + float3 maximum = (1, 1, 1) + float3 minimum = (0, 0, 0) + } + } + sdrMetadata = { + string uimax = "1, 1, 1" + string uimin = "0, 0, 0" + } + ) + float inputs:metallic = 0 ( + limits = { + dictionary hard = { + float maximum = 1 + float minimum = 0 + } + } + sdrMetadata = { + string uimax = "1" + string uimin = "0" + } + ) + float inputs:opacity = 1 ( + limits = { + dictionary hard = { + float maximum = 1 + float minimum = 0 + } + } + sdrMetadata = { + string uimax = "1" + string uimin = "0" + } + ) + float inputs:roughness = 0.5 ( + limits = { + dictionary hard = { + float maximum = 1 + float minimum = 0 + } + } + sdrMetadata = { + string uimax = "1" + string uimin = "0" + } + ) + token outputs:mtlx:surface.connect = + token outputs:surface.connect = + } + } + + def Scope "Physics" + { + def Material "PhysicsMaterial" ( + apiSchemas = ["PhysicsMaterialAPI", "NewtonMaterialAPI"] + ) + { + float newton:contactDamping = 100 + float newton:contactStiffness = 2500 + float newton:rollingFriction = 0.0001 + float newton:torsionalFriction = 0.005 + float physics:dynamicFriction = 1 + float physics:staticFriction = 1 + } + } +} + +def PhysicsScene "PhysicsScene" ( + apiSchemas = ["NewtonSceneAPI", "MjcSceneAPI"] +) +{ + uniform token mjc:option:cone = "elliptic" + uniform double mjc:option:impratio = 10 + uniform int newton:maxSolverIterations = 100 + uniform int newton:timeStepsPerSecond = 200 + vector3f physics:gravityDirection = (0, 0, -1) + float physics:gravityMagnitude = 9.81 +} + +# The MJCF said `` and a headlight ambient of 0.35; +# the converter drops lights entirely ("lights are not supported"), so they are written here. A +# layer that says nothing about its lighting gets whatever the viewer guesses, which for the +# office scene was a room lit from nowhere with flat black through the windows. +def DistantLight "sun" ( + prepend apiSchemas = ["ShadowAPI"] +) +{ + # Straight down, as the MJCF's dir="0 0 -1" asked for. + color3f inputs:color = (1, 1, 1) + float inputs:intensity = 2.0 + bool inputs:shadow:enable = 1 + quatf xformOp:orient = (0.70710678, -0.70710678, 0, 0) + uniform token[] xformOpOrder = ["xformOp:orient"] +} + +def DomeLight "ambient" ( + prepend apiSchemas = ["ShadowAPI"] +) +{ + # The headlight's ambient term: there is no bounce in the renderer, so this stands in for + # what would reach a surface after scattering off the walls. + color3f inputs:color = (1, 1, 1) + float inputs:intensity = 1.2 + bool inputs:shadow:enable = 0 +} diff --git a/dimos/sim2/l1.py b/dimos/sim2/l1.py new file mode 100644 index 00000000000..01bc00b78b8 --- /dev/null +++ b/dimos/sim2/l1.py @@ -0,0 +1,142 @@ +# Copyright 2026 Dimensional Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import asyncio +from collections.abc import AsyncIterator +import math + +from dimos_lcm.world_msgs import WorldObservations +import numpy as np +from numpy.typing import NDArray + +from dimos.core.core import rpc +from dimos.core.module import Module +from dimos.core.stream import In, Out +from dimos.msgs.sensor_msgs.PointCloud2 import PointCloud2 +from dimos.sim2.mujoco.sensor_world import WorldView, open_view, robot_ids, rotation_matrix +from dimos.sim2.mujoco.world import SensorMount +from dimos.sim2.sensor_usd import frame_usd +from dimos.sim2.sim import SceneConfig, scene_from +from dimos.sim2.usd_layer import usd_layer +from dimos.sim2.world_observations import WorldMirror +from dimos.simulation.sensors.lidar import Raycaster + +_KEY_BITS = 21 +_KEY_OFFSET = 1 << (_KEY_BITS - 1) + + +class SimL1Config(SceneConfig): + frame_id: str = "utlidar_lidar" + map_frame_id: str = "world" + period: float = 0.13 + points_per_second: float = 21_600.0 + max_range: float = 30.0 + cell: float = 0.05 + half_width_cells: int = 64 + z_range: tuple[float, float] = (-0.6, 1.3) + geom_groups: tuple[int, ...] = (0, 3) + seed: int = 0 + + +class SimL1(Module): + config: SimL1Config + lidar: Out[PointCloud2] + world_observations: In[WorldObservations] + + @rpc + def get_usd(self) -> list[str]: + return frame_usd(self.frame_id, name="sim_l1") + + async def main(self) -> AsyncIterator[None]: + self._world = WorldMirror() + self._world.subscribe(self.world_observations) + self._mapping = asyncio.create_task(self._map()) + yield + self._mapping.cancel() + + def _open(self) -> WorldView: + scene = scene_from(self.config.scene_manager, self.rpc, self.config.scene) + if scene is None: + raise ValueError( + f"{self.frame_id}: nothing says what the scene is, so there is nothing to map." + f" Give this L1 the scene_manager that composes the world." + ) + return open_view( + usd_layer(scene), + robot_ids(self._world), + SensorMount("lidar", self.frame_id, self.frame_id), + ) + + async def _map(self) -> None: + config, site = self.config, self.frame_id + view: WorldView | None = None + raycaster: Raycaster | None = None + rider: str | None = None + rng = np.random.default_rng(config.seed) + rays = int(config.points_per_second * config.period) + cells = np.zeros(0, dtype=np.int64) + async for _ in self._world.ticks(1 / config.period): + if view is None: + view = self._open() + raycaster = view.raycaster(config.geom_groups) + rider = view.owner(site) + assert raycaster is not None + view.update(self._world) + origin, orientation = view.pose(site) + directions = _hemisphere(rng, rays) @ rotation_matrix(orientation).T + distance, _ = raycaster.cast(origin, directions, config.max_range) + hit = distance > 0 + cells = np.union1d(cells, self._keys(origin + directions[hit] * distance[hit, None])) + cells = self._crop(cells, self._center(rider, origin)) + self.lidar.publish( + PointCloud2.from_numpy( + (self._indices(cells) + 0.5) * config.cell, + config.map_frame_id, + self._world.stamp(), + ) + ) + + def _center(self, rider: str | None, origin: NDArray[np.float64]) -> NDArray[np.float64]: + entity = self._world.entity(rider) if rider is not None else None + return origin if entity is None or entity.pose is None else entity.pose[:3] + + def _keys(self, points: NDArray[np.float64]) -> NDArray[np.int64]: + index = np.floor(points / self.config.cell).astype(np.int64) + _KEY_OFFSET + return np.unique((index[:, 0] << 2 * _KEY_BITS) | (index[:, 1] << _KEY_BITS) | index[:, 2]) + + def _indices(self, keys: NDArray[np.int64]) -> NDArray[np.int64]: + mask = (1 << _KEY_BITS) - 1 + return ( + np.stack([keys >> 2 * _KEY_BITS, (keys >> _KEY_BITS) & mask, keys & mask], axis=1) + - _KEY_OFFSET + ) + + def _crop(self, keys: NDArray[np.int64], center: NDArray[np.float64]) -> NDArray[np.int64]: + config = self.config + index = self._indices(keys) + low = np.floor(center[:2] / config.cell).astype(np.int64) - config.half_width_cells + inside = np.all( + (index[:, :2] >= low) & (index[:, :2] < low + 2 * config.half_width_cells), axis=1 + ) + z = (index[:, 2] + 0.5) * config.cell + inside &= (z >= config.z_range[0]) & (z <= config.z_range[1]) + return keys[inside] + + +def _hemisphere(rng: np.random.Generator, count: int) -> NDArray[np.float64]: + z = rng.uniform(0.0, 1.0, count) + azimuth = rng.uniform(0.0, 2 * math.pi, count) + ring = np.sqrt(1 - z**2) + return np.stack([ring * np.cos(azimuth), ring * np.sin(azimuth), z], axis=1) diff --git a/dimos/sim2/test_sim_time.py b/dimos/sim2/test_sim_time.py new file mode 100644 index 00000000000..ac7643cb14b --- /dev/null +++ b/dimos/sim2/test_sim_time.py @@ -0,0 +1,253 @@ +# Copyright 2026 Dimensional Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import asyncio +from collections.abc import AsyncIterator +import time +from typing import Any +import uuid + +from dimos_lcm.world_msgs import WorldAction, WorldObservations +import numpy as np +import pytest + +from dimos.core.coordination.blueprint_config.parser import BlueprintConfigParser +from dimos.core.coordination.blueprints import autoconnect +from dimos.core.coordination.module_coordinator import ModuleCoordinator +from dimos.core.core import rpc +from dimos.core.module import Module, ModuleConfig +from dimos.core.stream import In, Out +from dimos.msgs.sensor_msgs.Image import Image +from dimos.msgs.sensor_msgs.Imu import Imu +from dimos.msgs.sensor_msgs.PointCloud2 import PointCloud2 +from dimos.robot.assets.model import UsdModel +from dimos.robot.assets.usd import graft_usd, prefix_usd_frames, usd_with_drives +from dimos.robot.usd_manager import UsdManager +from dimos.sim2.actions import JointCommand, world_action +from dimos.sim2.assets.logistics import LOGISTICS +from dimos.sim2.assets.toy import toy_robot +from dimos.sim2.camera import SimCamera +from dimos.sim2.imu import SimImu +from dimos.sim2.l1 import SimL1 +from dimos.sim2.livox import LivoxSim +from dimos.sim2.sim import sim_module +from dimos.sim2.spec import SimulatedRobot +from dimos.sim2.world_observations import ACT, WorldMirror + +# Every scene here is read out of a USD layer, and a runner without usd-core cannot read one: +# linux-aarch64 has no wheel for it (pyproject's marker says so), so without this the tests +# fail there on dimos's own "Reading or writing USD needs usd-core (pxr)". Their neighbours +# in this directory skip the same way. +pytest.importorskip("pxr") + +L1_PERIOD = 0.13 +RATES = { + "color_image": 10.0, + "imu": 50.0, + "lidar": 10.0, + "livox_imu": 50.0, + "l1_lidar": 1 / L1_PERIOD, +} + + +# The scene manager and the world are named for this run alone. A zenoh domain is not enough: it +# prefixes key expressions, so it keeps the streams apart, while an RPC goes out on the *instance +# name*. `scene/usdmanager` and an unnamespaced `sim_module()` are exactly what three-g1-office-555 +# calls its own, so with that sim up the two worlds answered each other: this test's sensors asked +# the office's manager for a scene, the office's world asked this one, and since the two publish +# different asset names the live world died on a get_asset KeyError. Its teardown then stopped the +# office's world module too, which is the same name. The sensors need no such treatment -- every +# one of them is inside a robot's namespace over there. +def _names() -> tuple[str, str]: + """A unique scene namespace for this run, and the name its manager answers RPCs on.""" + scene = f"scene{uuid.uuid4().hex[:8]}" + return scene, f"{scene}/{UsdManager.name}" + + +FRAMES = {"eye": "head", "imu_link": "base", "livox": "head", "l1": "base"} + + +@pytest.fixture +def robot_model() -> UsdModel: + model = toy_robot().with_frames( + {frame: (body, (0.0, 0.0, 0.0), (0.0, 0.0, 0.0)) for frame, body in FRAMES.items()} + ) + return UsdModel.from_layer(usd_with_drives(model.usd, {"neck": (0.01, 0.001, 0.0)})) + + +@pytest.fixture +def composed(robot_model: UsdModel) -> UsdModel: + room = UsdModel.from_file(LOGISTICS).with_frames( + {"dog": ("logistics", (-9.0, -9.0, -8.7), (0.0, 0.0, 0.0))} + ) + return UsdModel.from_layer( + graft_usd(room.usd, {"dog": prefix_usd_frames(robot_model.usd, "dog")}) + ) + + +class _DogConfig(ModuleConfig): + control_period: float | None = None + think: float = 0.0 + + +class _Dog(Module): + config: _DogConfig + world_observations: In[WorldObservations] + actions: Out[WorldAction] + + async def main(self) -> AsyncIterator[None]: + world, loop = WorldMirror(), asyncio.get_running_loop() + + def act() -> None: + self.actions.publish(world_action(world, JointCommand("dog", ("neck",), effort=(0.0,)))) + + def on_frame(seen: WorldMirror) -> None: + if seen.got("dog", ACT): + loop.call_soon_threadsafe(loop.call_later, self.config.think, act) + + world.on_frame(on_frame) + world.subscribe(self.world_observations) + yield + + +class _Readings(Module): + color_image: In[Image] + imu: In[Imu] + lidar: In[PointCloud2] + livox_imu: In[Imu] + l1_lidar: In[PointCloud2] + + async def main(self) -> AsyncIterator[None]: + self._stamps: dict[str, list[float]] = {name: [] for name in RATES} + self._accelerations: list[float] = [] + self._started = time.monotonic() + for name in RATES: + getattr(self, name).subscribe( + lambda message, n=name: self._stamps[n].append(message.ts) + ) + self.imu.subscribe( + lambda m: self._accelerations.append( + float(np.linalg.norm(m.linear_acceleration.to_numpy())) + ) + ) + yield + + @rpc + def readings(self) -> dict[str, Any]: + return { + "stamps": self._stamps, + "accelerations": self._accelerations, + "wall": time.monotonic() - self._started, + } + + +def _run( + composed: UsdModel, + robot_model: UsdModel, + seconds: float, + speed: float = 1.0, + **dog: Any, +) -> dict[str, Any]: + run = uuid.uuid4().hex[:8] + scene, scene_manager = _names() + blueprint = autoconnect( + sim_module( + scene_manager=scene_manager, + speed=speed, + robots=[SimulatedRobot("dog", robot_model, control_period=dog.get("control_period"))], + instance_name=f"sim{run}", + ), + _Dog.blueprint(**dog), + UsdManager.blueprint( + model=composed, frame_id_prefix="", tf_renames={"logistics": "world"} + ).namespace(scene, expose={"render_file_request", "file_info", "tf"}), + SimCamera.blueprint( + frame_id="eye", + width=64, + height=48, + rate_hz=RATES["color_image"], + scene_manager=scene_manager, + ), + SimImu.blueprint(frame_id="imu_link", rate_hz=RATES["imu"], scene_manager=scene_manager), + LivoxSim.blueprint( + frame_id="livox", + rate_hz=RATES["lidar"], + imu_rate_hz=RATES["livox_imu"], + scene_manager=scene_manager, + ).remappings([(LivoxSim, "imu", "livox_imu")]), + SimL1.blueprint(frame_id="l1", period=L1_PERIOD, scene_manager=scene_manager).remappings( + [(SimL1, "lidar", "l1_lidar")] + ), + _Readings.blueprint(), + ).global_config(n_workers=9) + overrides = {"g": {"viewer": "none", "zenoh_domain": f"sim2-test-{uuid.uuid4().hex[:8]}"}} + parsed = BlueprintConfigParser(blueprint).parse(environ={}, overrides=overrides) + coordinator = ModuleCoordinator.build(blueprint, parsed) + try: + readings = coordinator.get_instance(_Readings) + deadline = time.monotonic() + seconds + MAX_EXTRA_WALL + time.sleep(seconds) + while time.monotonic() < deadline and not _steady(readings.readings()["stamps"]): + time.sleep(1.0) + return readings.readings() + finally: + coordinator.stop() + + +MAX_EXTRA_WALL = 60.0 +STEADY_STAMPS = 8 + + +def _steady(stamps: dict[str, list[float]]) -> bool: + if not all(stamps[name] for name in RATES): + return False + start = min(stamps[name][0] for name in RATES) + return all( + sum(stamp >= start + WARM_UP for stamp in stamps[name]) >= STEADY_STAMPS for name in RATES + ) + + +WARM_UP = 1.0 + + +def _per_sim_second(stamps: list[float], start: float) -> float: + steady = [stamp for stamp in stamps if stamp >= start + WARM_UP] + assert len(steady) > 5, f"only {len(steady)} after warming up" + return (len(steady) - 1) / (steady[-1] - steady[0]) + + +@pytest.mark.parametrize( + ("speed", "dog"), + [ + (0.5, {}), + (4.0, {}), + (1.0, {"control_period": 0.02, "think": 0.05}), + ], + ids=["half_speed", "four_times_speed", "slower_than_real_lockstep"], +) +def test_sensors_keep_their_rates_per_sim_second(composed, robot_model, speed, dog): + readings = _run(composed, robot_model, seconds=8.0, speed=speed, **dog) + stamps = readings["stamps"] + counts = {name: len(stamps[name]) for name in RATES} + assert all(count > 5 for count in counts.values()), counts + start = min(stamps[name][0] for name in RATES) + sim_seconds = stamps["imu"][-1] - stamps["imu"][0] + for name, rate in RATES.items(): + measured = _per_sim_second(stamps[name], start) + assert measured == pytest.approx(rate, rel=0.1), (name, measured) + if dog: + assert sim_seconds < 0.7 * readings["wall"] + settled = readings["accelerations"][len(readings["accelerations"]) // 2 :] + assert np.mean(settled) == pytest.approx(9.81, abs=0.3) diff --git a/experimental/gateway/annotations.yaml b/experimental/gateway/annotations.yaml index 9f9d27fa9e7..44a7c9aac66 100644 --- a/experimental/gateway/annotations.yaml +++ b/experimental/gateway/annotations.yaml @@ -40,6 +40,10 @@ robots: title: Go2 basic description: 'Connects to a Go2 and streams its camera, lidar and pose. The simplest way to get a Go2 going: drive it from Controller.' starter: 1 + unitree-go2-sim2-l1: + title: Go2 in sim2 with its L1 lidar + description: 'A simulated Go2 in sim2, scanning with its own L1: camera, lidar and pose, no robot needed.' + recommended_config: [] unitree-go2: title: Go2 with map + navigation description: Builds a 3D map and a costmap as it walks; click a spot to send it there, or let it explore on its own. diff --git a/pyproject.toml b/pyproject.toml index 78b2e2cc468..64df9f799de 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -879,6 +879,12 @@ ignore = [ "dimos/manipulation/manipulation_module.py", "dimos/manipulation/test_roboplan.py", "misc/DimSim/scenes/apartment/objects/manifest.json", + # A USD layer is the thing a robot or a scene travels as, and it is text on purpose: it stays + # out of LFS so a change to a scene shows up in a diff rather than as an opaque pointer. A + # clone that has not run `git lfs pull` still has to be able to read one, because a scene is + # handed over as a string and there is nothing to fetch it from. + "dimos/robot/*/*/assets/*.usda", + "dimos/simulation/scenes/*/*.usda", "misc/DimSim/src/engine.js", # Generated golden vectors (web/shared/fixtures/gen.ts); grows per panel kind. "web/shared/fixtures/manifests.json", diff --git a/scripts/mjcf_to_usd.py b/scripts/mjcf_to_usd.py new file mode 100755 index 00000000000..9067cc033b4 --- /dev/null +++ b/scripts/mjcf_to_usd.py @@ -0,0 +1,236 @@ +#!/usr/bin/env python3 +# Copyright 2026 Dimensional Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""A vendor MJCF as the USD dimos serves. Run once by hand; the result is committed. + +Why two steps. Google DeepMind's converter (``mujoco-usd`` on PyPI) is the only thing that reads +an MJCF into USD with its physics intact -- MjcActuator, MjcJointAPI and MjcCollisionAPI prims, +so MuJoCo can still simulate from the layer. But it **bakes** every mesh into the layer (the M20 +comes out 19 MB as text, with no reference arcs), and dimos serves each mesh separately by +content hash. + +So the converter runs first, in its own virtualenv, and this re-points the baked meshes at the +files they came from: + + python3 -m venv /tmp/mjcfusd + /tmp/mjcfusd/bin/pip install mujoco-usd-converter + # its own warning: usd-exchange ships OpenUSD and will not share with usd-core + /tmp/mjcfusd/bin/pip uninstall -y usd-core + /tmp/mjcfusd/bin/pip install --force-reinstall usd-exchange + /tmp/mjcfusd/bin/python -c " + import mujoco_usd_converter + mujoco_usd_converter.Converter().convert('.xml', '/tmp/converted')" + + # then, in dimos's own venv: + python scripts/mjcf_to_usd.py --mjcf .xml --converted /tmp/converted/.usda \\ + --meshes /meshes --out dimos/robot///assets/.usda + +Each mesh then ends up named twice, on purpose, because two kinds of consumer want two +different things from it: + +* a **reference arc** to a ``.usdc`` sidecar this writes beside the original. A reference + composes a USD layer and only a USD layer, so this is what makes the result valid USD. +* ``assetInfo["identifier"]``, naming the **original mesh file**, which is what dimos serves + over its own bus. It never composes the stage. + +Dropping either one costs a consumer its geometry, so both are authored. + +**Never install mujoco-usd into dimos's virtualenv.** It pulls usd-exchange, which bundles its +own OpenUSD and declares usd-core incompatible, and mujoco-core, which shadows mujoco. Removing +them afterwards takes usd-core's modules with it. +""" + +from __future__ import annotations + +import argparse +import os +from pathlib import Path +import re +import sys +from typing import Any +import xml.etree.ElementTree as ET + +from dimos.robot.assets.usd import hash_of_file +from dimos.utils.logging_config import setup_logger + +logger = setup_logger() + +_BAKED = frozenset({"points", "faceVertexIndices", "faceVertexCounts", "normals", "extent"}) +_KEPT_PRIMVARS = frozenset({"primvars:displayColor", "primvars:displayOpacity"}) +_SIDECAR_SUFFIX = ".usdc" + + +def write_sidecar(source: Path, pxr: Any) -> Path: + import trimesh + + out = source.with_suffix(_SIDECAR_SUFFIX) + loaded = trimesh.load_mesh(str(source), process=False) + if isinstance(loaded, trimesh.Scene): + loaded = loaded.to_geometry() + if len(loaded.faces) == 0: + raise ValueError( + f"{source} has no faces. If it is {source.stat().st_size} bytes it is a git-lfs " + f"pointer: run `git lfs checkout` on its directory first." + ) + + stage = pxr.Usd.Stage.CreateInMemory(out.name) + mesh = pxr.UsdGeom.Mesh.Define(stage, "/mesh") + mesh.CreatePointsAttr([pxr.Gf.Vec3f(*point) for point in loaded.vertices.tolist()]) + mesh.CreateFaceVertexIndicesAttr([int(i) for i in loaded.faces.reshape(-1).tolist()]) + mesh.CreateFaceVertexCountsAttr([3] * len(loaded.faces)) + bounds = loaded.bounds + if bounds is not None: + mesh.CreateExtentAttr( + [pxr.Gf.Vec3f(*bounds[0].tolist()), pxr.Gf.Vec3f(*bounds[1].tolist())] + ) + stage.SetDefaultPrim(mesh.GetPrim()) + stage.GetRootLayer().Export(str(out)) + return out + + +def mesh_files(mjcf: Path) -> dict[str, str]: + root = ET.fromstring(mjcf.read_text()) + files = {} + for mesh in root.iter("mesh"): + file = mesh.get("file") + name = mesh.get("name") or (Path(file).stem if file else None) + if name and file: + files[name] = file + for geom in root.iter("geom"): + name, mesh_name = geom.get("name"), geom.get("mesh") + if name and mesh_name and mesh_name in files: + files[name] = files[mesh_name] + return files + + +def _mesh_of(prim_name: str, files: dict[str, str]) -> str | None: + if prim_name in files: + return files[prim_name] + stripped = re.sub(r"_\d+$", "", prim_name) + return files.get(stripped) if stripped != prim_name else None + + +def rereference( + stage: Any, files: dict[str, str], meshes: Path, out_dir: Path, pxr: Any +) -> tuple[int, int]: + done = skipped = 0 + for prim in list(stage.Traverse()): + if not prim.IsA(pxr.UsdGeom.Mesh): + continue + file = _mesh_of(prim.GetName(), files) + if file is None: + logger.warning(f"{prim.GetPath()} has no mesh of that name in the MJCF; left baked") + skipped += 1 + continue + path = next( + ( + candidate + for candidate in ((meshes / file).resolve(), (meshes / Path(file).name).resolve()) + if candidate.is_file() + ), + None, + ) + if path is None: + logger.warning(f"no {file} under {meshes}; {prim.GetPath()} left baked") + skipped += 1 + continue + for property in list(prim.GetPropertyNames()): + if property.startswith(("xformOp", "material:")) or property in _KEPT_PRIMVARS: + continue + if property.startswith("primvars:") or property in _BAKED: + prim.RemoveProperty(property) + sidecar = write_sidecar(path, pxr) + relative = os.path.relpath(path, out_dir) + prim.GetReferences().ClearReferences() + prim.GetReferences().AddReference(os.path.relpath(sidecar, out_dir)) + digest = hash_of_file(str(path)) + info = prim.GetAssetInfo() or {} + info["identifier"] = pxr.Sdf.AssetPath(relative) + if digest: + info["version"] = digest + prim.SetAssetInfo(info) + done += 1 + return done, skipped + + +def name_joints(stage: Any, pxr: Any) -> int: + renamed = 0 + for prim in list(stage.Traverse()): + if not prim.IsA(pxr.UsdPhysics.Joint) or prim.GetName() != prim.GetTypeName(): + continue + targets = prim.GetRelationship("physics:body1").GetTargets() + if not targets: + logger.warning(f"{prim.GetPath()} is unnamed and moves nothing; left as it is") + continue + child = str(targets[0]).rsplit("/", 1)[-1] + edit = pxr.Sdf.NamespaceEdit.Rename(prim.GetPath(), f"{child}_joint") + batch = pxr.Sdf.BatchNamespaceEdit() + batch.Add(edit) + if stage.GetRootLayer().Apply(batch): + renamed += 1 + else: + logger.warning(f"could not rename {prim.GetPath()} to {child}_joint") + return renamed + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "--mjcf", required=True, type=Path, help="the vendor MJCF, for its mesh names" + ) + parser.add_argument("--converted", required=True, type=Path, help="the converter's stage") + parser.add_argument( + "--meshes", required=True, type=Path, help="the directory holding the mesh files" + ) + parser.add_argument("--out", required=True, type=Path, help="the .usda to write") + args = parser.parse_args() + + from pxr import Gf, Sdf, Usd, UsdGeom, UsdPhysics + + pxr = argparse.Namespace(Gf=Gf, Sdf=Sdf, Usd=Usd, UsdGeom=UsdGeom, UsdPhysics=UsdPhysics) + + for path in (args.mjcf, args.converted): + if not path.is_file(): + print(f"ERROR: no file at {path}", file=sys.stderr) + return 1 + + files = mesh_files(args.mjcf) + + stage = Usd.Stage.Open(str(args.converted)) + flat = Usd.Stage.Open(stage.Flatten()) + baked = any(prim.IsA(UsdGeom.Mesh) for prim in flat.Traverse()) + if baked and not files: + print( + f"ERROR: {args.converted} has meshes and {args.mjcf} names none; " + f"is --mjcf the file it was converted from?", + file=sys.stderr, + ) + return 1 + args.out.parent.mkdir(parents=True, exist_ok=True) + named = name_joints(flat, pxr) + done, skipped = rereference(flat, files, args.meshes, args.out.parent.resolve(), pxr) + + flat.GetRootLayer().Export(str(args.out)) + size = args.out.stat().st_size + print(f"{done} mesh(es) re-pointed with a .usdc sidecar, {skipped} left baked") + print(f"{named} unnamed joint(s) named after the body they move") + print(f"wrote {args.out} ({size / 1e6:.2f} MB)") + if skipped: + print("note: a baked mesh keeps the layer large; see the warnings above for why") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/usd_drives.py b/scripts/usd_drives.py new file mode 100644 index 00000000000..530e64cedfe --- /dev/null +++ b/scripts/usd_drives.py @@ -0,0 +1,47 @@ +# Copyright 2026 Dimensional Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +from __future__ import annotations + +import argparse +from importlib import import_module +from pathlib import Path + + +def main() -> None: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--layer", type=Path, required=True, help="the .usda to rewrite in place") + parser.add_argument( + "--from", + dest="source", + required=True, + help="module:callable returning {joint: (kp, kd, target)}", + ) + args = parser.parse_args() + + from dimos.robot.assets.usd import usd_drives, usd_with_drives + + module, _, name = args.source.partition(":") + drives = getattr(import_module(module), name)() + layer = usd_with_drives(args.layer.read_text(), drives) + args.layer.write_text(layer) + written = usd_drives(layer) + print(f"wrote {len(written)} drive(s) into {args.layer}") + for joint, (kp, kd, target) in sorted(written.items()): + print(f" {joint}: kp={kp:g} kd={kd:g} target={target:g}") + + +if __name__ == "__main__": + main()