#usda 1.0 ( doc = """Bin-picking work cell for the OPC UA Vision guided-picking sample. The scene uses a branch-stable four-axis palletizer sized for this cell and the parallel gripper shared with IntentEnabledRobot. It adds a bin of mixed parts, a fixture to place them onto, and an eye-in-hand camera parented to the flange so the view moves with the arm. This is the worked example reproduced from the OPC UA Robotics-Vision Addendum: frame tree world -> robot_base -> flange -> gripper_tcp with camera_eih on the flange. Rendering contract: The scene is lit by a single DomeLight (intensity 1000). Do NOT reintroduce a DistantLight or any other bright directional/point light: at intensities that show geometry a DistantLight blows every surface to white regardless of displayColor, and the demo relies on the agent being able to pick "the red part" by looking at the frame. Under a DomeLight the same materials measure red (220, 37, 37), green (37, 208, 49), blue (37, 73, 233) - distinct enough for a language model to reason about. Frame tree contract: - /World is the WorldFrame origin. - /World/Robot/Palletizer/Base is the robot_base frame. - /World/Robot/Palletizer/Base/J1/J2/J3/Leveling/J4/Flange is the flange (mechanical interface). - /World/Robot/Palletizer/.../Flange/Camera is the eye-in-hand camera prim (UsdGeomCamera) that the Vision sensor renders from. - /World/Robot/Palletizer/.../Flange/Gripper/Tcp is the gripper_tcp frame. The camera is a child of the flange with no stack reset, so it travels with the arm as a real eye-in-hand sensor does: rotateY = -90 turns its view axis onto the flange +X tool direction. The authored joint angles put the flange over the bin at the sample's start pose, so it opens looking into the bin. """ defaultPrim = "World" metersPerUnit = 1 upAxis = "Z" ) def Xform "World" { # A fixed observer for the viewport, distinct from the eye-in-hand sensor on the # flange. Opening the viewer on /World/Robot/.../Camera shows what the tool sees; this # one shows the cell working, which is what someone watching the robot wants. # # It sits on the -Y side so +X reads left to right, which is what puts the fixture on # the left and the bin on the right. This is deliberately a low, centred front view: # the table surface is nearly edge-on, its legs stay visible, and the complete wrist, # gripper, bin and stack can be judged against one another throughout a cycle. # # The numbers were fitted to a reference framing and then corrected against what the # viewer actually rendered, because the analytic placement and the rendered result did # not agree - the framing came out offset and over-scaled. Treat them as measured # rather than derived: change one and the framing has to be re-checked against a # capture, not just recomputed. def Camera "ObserverCamera" { token projection = "perspective" float focalLength = 31.6 float horizontalAperture = 20.955 float verticalAperture = 16.989 float2 clippingRange = (0.1, 100.0) double3 xformOp:translate = (0.500, -5.400, 1.300) double xformOp:rotateX = 75.50 uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:rotateX"] } def Xform "Lights" { def DomeLight "Sky" { float inputs:intensity = 1000 color3f inputs:color = (1.0, 1.0, 1.0) } } def Xform "Robot" { def Cylinder "PedestalRiser" { uniform token axis = "Z" double height = 0.2000 double radius = 0.1650 color3f[] primvars:displayColor = [(0.16, 0.17, 0.18)] ( interpolation = "constant" ) double3 xformOp:translate = (0, 0, 0.8200) uniform token[] xformOpOrder = ["xformOp:translate"] } def Xform "Palletizer" ( references = @palletizer-arm.usda@ ) { double3 xformOp:translate = (0, 0, 0.9200) uniform token[] xformOpOrder = ["xformOp:translate"] over "Base" { over "J1" { double xformOp:rotateZ = 0.000 over "J2" { double xformOp:rotateY = 31.513 over "J3" { double xformOp:rotateY = -65.890 over "Leveling" { double xformOp:rotateY = 124.377 over "J4" { double xformOp:rotateX = 90.000 over "Flange" { def Xform "Gripper" ( references = @palletizer-gripper.usda@ ) { } def Camera "Camera" { token projection = "perspective" float focalLength = 12.24 float horizontalAperture = 14.688 float verticalAperture = 12.288 float2 clippingRange = (0.02, 5.0) double3 xformOp:translate = (0.0200, 0.0000, 0.1600) double xformOp:rotateY = -90 uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:rotateY"] } } } } } } } } } } def Xform "Floor" { def Cube "Slab" { double size = 1 color3f[] primvars:displayColor = [(0.36, 0.37, 0.40)] ( interpolation = "constant" ) double3 xformOp:translate = (0, 0, -0.0250) double3 xformOp:scale = (2.4000, 1.8000, 0.0250) uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:scale"] } } def Xform "Bench" { def Cube "Top" { double size = 1 color3f[] primvars:displayColor = [(0.58, 0.60, 0.60)] ( interpolation = "constant" ) # The work surface is z = 0.720. The robot base is independently raised # to z = 0.920 on PedestalRiser, so the wrist no longer has to fold back # on itself to reach parts lying close to the table. double3 xformOp:translate = (0, 0, 0.6850) double3 xformOp:scale = (1.4000, 0.9000, 0.0700) uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:scale"] } def Cube "LegA" { double size = 1 color3f[] primvars:displayColor = [(0.22, 0.23, 0.24)] ( interpolation = "constant" ) double3 xformOp:translate = (0.5800, 0.3300, 0.3250) double3 xformOp:scale = (0.0250, 0.0250, 0.6500) uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:scale"] } def Cube "LegB" { double size = 1 color3f[] primvars:displayColor = [(0.22, 0.23, 0.24)] ( interpolation = "constant" ) double3 xformOp:translate = (-0.5800, 0.3300, 0.3250) double3 xformOp:scale = (0.0250, 0.0250, 0.6500) uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:scale"] } def Cube "LegC" { double size = 1 color3f[] primvars:displayColor = [(0.22, 0.23, 0.24)] ( interpolation = "constant" ) double3 xformOp:translate = (0.5800, -0.3300, 0.3250) double3 xformOp:scale = (0.0250, 0.0250, 0.6500) uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:scale"] } def Cube "LegD" { double size = 1 color3f[] primvars:displayColor = [(0.22, 0.23, 0.24)] ( interpolation = "constant" ) double3 xformOp:translate = (-0.5800, -0.3300, 0.3250) double3 xformOp:scale = (0.0250, 0.0250, 0.6500) uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:scale"] } } def Xform "Bin" { def Cube "BinFloor" { double size = 1 color3f[] primvars:displayColor = [(0.42, 0.32, 0.20)] ( interpolation = "constant" ) double3 xformOp:translate = (0.6000, 0.0000, 0.7160) double3 xformOp:scale = (0.2800, 0.2400, 0.0060) uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:scale"] } def Cube "BinWallN" { double size = 1 color3f[] primvars:displayColor = [(0.42, 0.32, 0.20)] ( interpolation = "constant" ) double3 xformOp:translate = (0.6000, 0.1170, 0.7330) double3 xformOp:scale = (0.2800, 0.0060, 0.0400) uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:scale"] } def Cube "BinWallS" { double size = 1 color3f[] primvars:displayColor = [(0.42, 0.32, 0.20)] ( interpolation = "constant" ) double3 xformOp:translate = (0.6000, -0.1170, 0.7330) double3 xformOp:scale = (0.2800, 0.0060, 0.0400) uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:scale"] } def Cube "BinWallE" { double size = 1 color3f[] primvars:displayColor = [(0.42, 0.32, 0.20)] ( interpolation = "constant" ) double3 xformOp:translate = (0.7370, 0.0000, 0.7330) double3 xformOp:scale = (0.0060, 0.2400, 0.0400) uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:scale"] } def Cube "BinWallW" { double size = 1 color3f[] primvars:displayColor = [(0.42, 0.32, 0.20)] ( interpolation = "constant" ) double3 xformOp:translate = (0.4630, 0.0000, 0.7330) double3 xformOp:scale = (0.0060, 0.2400, 0.0400) uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:scale"] } } def Xform "Fixture" { def Cube "Base" { double size = 1 color3f[] primvars:displayColor = [(0.20, 0.20, 0.22)] ( interpolation = "constant" ) double3 xformOp:translate = (-0.6000, 0.0000, 0.7200) double3 xformOp:scale = (0.1400, 0.1400, 0.0180) uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:scale"] } def Cylinder "PegA" { uniform token axis = "Z" double height = 0.0400 double radius = 0.0090 color3f[] primvars:displayColor = [(0.95, 0.85, 0.20)] ( interpolation = "constant" ) double3 xformOp:translate = (-0.6500, 0.0500, 0.7490) uniform token[] xformOpOrder = ["xformOp:translate"] } def Cylinder "PegB" { uniform token axis = "Z" double height = 0.0400 double radius = 0.0090 color3f[] primvars:displayColor = [(0.95, 0.85, 0.20)] ( interpolation = "constant" ) double3 xformOp:translate = (-0.5500, 0.0500, 0.7490) uniform token[] xformOpOrder = ["xformOp:translate"] } def Cylinder "PegC" { uniform token axis = "Z" double height = 0.0400 double radius = 0.0090 color3f[] primvars:displayColor = [(0.95, 0.85, 0.20)] ( interpolation = "constant" ) double3 xformOp:translate = (-0.6000, -0.0500, 0.7490) uniform token[] xformOpOrder = ["xformOp:translate"] } } # Each part is an Xform carrying a single xformOp:transform, with its shape as a child. # The OpenUSD live binding drives the parent's transform, and the viewer sink authors # that op as a whole matrix - composing translation with a default identity rotation and # unit scale. Rotation and scale therefore have to live somewhere the binding does not # overwrite, which is the child. The composition is unchanged: the shape is scaled and # rotated about its own origin, then the parent translates it. def Xform "Parts" { def Xform "RedCube" { matrix4d xformOp:transform = ( (1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0.5200, -0.0800, 0.7400, 1) ) uniform token[] xformOpOrder = ["xformOp:transform"] def Cube "Shape" { double size = 0.0400 color3f[] primvars:displayColor = [(0.90, 0.15, 0.15)] ( interpolation = "constant" ) double xformOp:rotateZ = 20 uniform token[] xformOpOrder = ["xformOp:rotateZ"] } } def Xform "GreenCylinder" { matrix4d xformOp:transform = ( (1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0.6700, 0.0800, 0.7350, 1) ) uniform token[] xformOpOrder = ["xformOp:transform"] def Cylinder "Shape" { uniform token axis = "Z" double height = 0.0300 double radius = 0.0200 color3f[] primvars:displayColor = [(0.15, 0.85, 0.20)] ( interpolation = "constant" ) } } def Xform "BlueSphere" { matrix4d xformOp:transform = ( (1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0.6800, -0.0800, 0.7440, 1) ) uniform token[] xformOpOrder = ["xformOp:transform"] def Sphere "Shape" { double radius = 0.0240 color3f[] primvars:displayColor = [(0.15, 0.30, 0.95)] ( interpolation = "constant" ) } } def Xform "YellowSlab" { matrix4d xformOp:transform = ( (1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0.5200, 0.0600, 0.7290, 1) ) uniform token[] xformOpOrder = ["xformOp:transform"] def Cube "Shape" { double size = 1 color3f[] primvars:displayColor = [(0.95, 0.85, 0.15)] ( interpolation = "constant" ) double3 xformOp:scale = (0.0640, 0.0320, 0.0180) double xformOp:rotateZ = -15 uniform token[] xformOpOrder = ["xformOp:rotateZ", "xformOp:scale"] } } def Xform "OrangeBrick" { matrix4d xformOp:transform = ( (1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0.6000, 0.0000, 0.7320, 1) ) uniform token[] xformOpOrder = ["xformOp:transform"] def Cube "Shape" { double size = 1 color3f[] primvars:displayColor = [(0.95, 0.45, 0.10)] ( interpolation = "constant" ) double3 xformOp:scale = (0.0500, 0.0280, 0.0240) double xformOp:rotateZ = 40 uniform token[] xformOpOrder = ["xformOp:rotateZ", "xformOp:scale"] } } } }