Fixed joint issue , kinova gen3 + isaacsim

Important: Isaac Sim support

Note: For Isaac Sim support, the community is gradually transitioning from this forum to the Isaac Sim GitHub repository so that questions and issues can be tracked, searched, and resolved more efficiently in one place. Whenever possible, please create a GitHub Discussion or Issue there instead of starting a new forum topic.

Note: For any Isaac Lab topics, please submit your topic to its GitHub repo ( GitHub - isaac-sim/IsaacLab: Unified framework for robot learning built on NVIDIA Isaac Sim · GitHub ) following the instructions provided on Isaac Lab’s Contributing Guidelines ( Contribution Guidelines — Isaac Lab Documentation ).

Please provide all relevant details below before submitting your post. This will help the community provide more accurate and timely assistance. After submitting, you can check the appropriate boxes. Remember, you can always edit your post later to include additional information if needed.

6.0.0
5.1.0
5.0.0
4.5.0
4.2.0
4.1.0
4.0.0
4.5.0
2023.1.1
2023.1.0-hotfix.1
Other (please specify):

Operating System

Ubuntu 24.04
Ubuntu 22.04
Ubuntu 20.04
Windows 11
Windows 10
Other (please specify):

GPU Information

  • Model:
  • Driver Version:

Topic Description

im trying to mount the kinova gen 3 on top of BD spot , like the image i provided here , however when i launch isaaclab it spawns sideways , also when i try to change the fixed joint localrot0 or 1 between the kinova and the spot , the spot changes location relative to the world but not kinova ,

the prims are these : Robot (articulation root ) → spot,kinova, spot_kinova_fixed_joint,

in the fixed joint spot is body0 and kinova is body1 ,
the parameters of the joint are as follows:
body0 : pos:(0,0,0) rot:(0,0,0) , body1: (-0.15,0,0.15) rot: (180,90,0)

the kinova’s prim has the same transform as the joint , i’ve tried to change the bodies , different rotation , etc , but none of them works , when kinova is mount on top of spot with rot as (0,0,0) , it works perfectly, what can i do to fix it?

also adding the config for the robot in isaaclab:

SPOT_KINOVA_CFG = ArticulationCfg(

spawn=sim_utils.UsdFileCfg(

    usd_path=SPOT_KINOVA_USD_PATH,

    activate_contact_sensors=True,

    rigid_props=sim_utils.RigidBodyPropertiesCfg(

        disable_gravity=False,

        retain_accelerations=False,

        linear_damping=0.0,

        angular_damping=0.0,

        max_linear_velocity=1000.0,

        max_angular_velocity=1000.0,

        max_depenetration_velocity=1.0,

    ),

    articulation_props=sim_utils.ArticulationRootPropertiesCfg(

        enabled_self_collisions=True,

        solver_position_iteration_count=4,

        solver_velocity_iteration_count=0,

    ),

),

init_state=ArticulationCfg.InitialStateCfg(

    pos=(0.0, 0.0, 0.8),  # Slightly higher to account for arm

    rot=(1.0,0.0,0.0, 0.0),  # Identity rotation (w, x, y, z)

    \# rot=(1.0, 0.0, 0.0, 0.0),  # Identity rotation (w, x, y, z)

    joint_pos={

        \# Spot leg joints (12 DOF)

        "\[fh\]l_hx": 0.1,  # all left hip_x

        "\[fh\]r_hx": -0.1,  # all right hip_x

        "f\[rl\]\_hy": 0.9,  # front hip_y

        "h\[rl\]\_hy": 1.1,  # hind hip_y

        ".\*\_kn": -1.5,  # all knees

        \# Kinova arm joints (7 DOF) - tucked position for stability

        "joint_1": 0.0,

        "joint_2": 0.785398,  # Shoulder down

        "joint_3": 0.0,   # Elbow rotated

        "joint_4":-1.570796,   # Elbow bent back

        "joint_5": 0.0,

        "joint_6": 0.785398,

        "joint_7": 0.0,

    },

    joint_vel={".\*": 0.0},

),

actuators={

    \# Spot hip actuators (8 DOF: hx and hy joints)

    "spot_hip": DelayedPDActuatorCfg(

        joint_names_expr=\[".\*\_h\[xy\]"\],

        effort_limit=45.0,

        stiffness=60.0,

        damping=1.5,

        min_delay=0,  # physics time steps (min: 2.0\*0=0.0ms)

        max_delay=4,  # physics time steps (max: 2.0\*4=8.0ms)

    ),

    \# Spot knee actuators (4 DOF: kn joints)

    "spot_knee": RemotizedPDActuatorCfg(

        joint_names_expr=\[".\*\_kn"\],

        joint_parameter_lookup=joint_parameter_lookup,

        effort_limit=None,  # torque limits are handled based on experimental data

        stiffness=60.0,

        damping=1.5,

        min_delay=0,  # physics time steps (min: 2.0\*0=0.0ms)

        max_delay=4,  # physics time steps (max: 2.0\*4=8.0ms)

    ),

    \# Kinova arm actuators (7 DOF: joint_1 through joint_7)

    "kinova_arm": ImplicitActuatorCfg(

        joint_names_expr=\["joint\_\[1-7\]"\],

        effort_limit={

            "joint\_\[1-4\]": 39.0,

            "joint\_\[5-7\]": 9.0,

        },

        stiffness={

            "joint\_\[1-4\]": 40.0,

            "joint\_\[5-7\]": 15.0,

        },

        damping={

            "joint\_\[1-4\]": 1.0,

            "joint\_\[5-7\]": 0.5,

        },

    ),

},

)

UPDATE: after some time i found that the problem is that the joint is treating the kinova as the parent , whether i place spot/body and body0 or body1, so no matter what rotation i create, kinova stays upright and only spot changes ,any ideas?

Hi @talsomech,

Thank you for posting the question. Would you mind sharing the USD file so I can take a look? I have also attached a USD of the setup that I think you are trying to reproduce if that is helpful.
kinova_spot_fixed_joint.usd (6.2 KB)

sure , thank you very much for you help , im adding the USD here , plus the code to create it

SpotKinova.zip (2.8 MB)

in isaac sim it spawns perfectly however in isaaclab is as you can see , if you did infact found a solution i would very much like to know what was the problem, should you need anything else youre more then welcome to ask,

i do have to note that when spawning spot+kinova regularly, as you gave me , it works in both isaac sim and isaaclab and spawns normally with the arm standing upright, however when changing the rotations (localrot0/1) in the joint in order for the arm to lay on the back instead of upright, then it stopped spawning normally is it possible since isaaclab does not read the xform transform of the kinova relative to /Robot? since if we set it to 0,0,0 and only change the joints transform , i get the same problem as only spot changes even in isaacsim whilst seems that the kinova always stays in the same original position even though the body0 is spot (tried also other way around ,still only spot changes locations)

@talsomech sorry for the delay on this topic. I am running into the same issue you are, and I’m looking into why this is happening. Will get back to you.

of course , thank you for the help!

Hi , i wanted to know if there is a solution and you didn’t forget about me ? it’s a very interesting problem i believe

Hi talsomech,

Sorry for the wait — turned out there were three separate problems compounding, not one. I have a working Isaac Lab repro now, using the canonical Spot and Kinova Gen3 USDs from the Isaac asset library.

Short version: the Kinova gen3n7_instanceable.usd ships with a hidden root_joint that fix-anchors it to world; that joint wins root selection in the merged articulation, so Isaac Lab’s reset moves the Kinova while Spot is dragged through the fixed joint to satisfy the constraint. On top of that, the fix-joint local frames and the inner-prim xforms have to both be set, and the asset hierarchy has to be Kinova-as-child-of-Spot.

What’s actually happening

Three independent issues:

  1. Hidden world-anchor on Kinova. gen3n7_instanceable.usd has a root_joint with body0=[] (world) and body1=base_link — that’s how URDF fix_root_link=True is encoded. PhysX treats it as anchoring base_link to world. When you fixed-joint Kinova to Spot, PhysX merges the two articulations and picks the anchored body (kinova.base_link) as the root of the merged articulation. Isaac Lab’s Articulation.write_root_pose_to_sim then writes to kinova.base_link; Spot rotates to satisfy mount_joint. That’s the “Kinova upright, Spot dangling sideways” symptom — and also why editing body0/body1 local frames only moved Spot, not Kinova.

    Note: physics:jointEnabled = false is not enough to neutralise that joint — PhysX still consumes disabled joints for articulation topology. The joint prim has to be removed from composition. prim.SetActive(False) works (it lives inside a reference, so Sdf.Stage.RemovePrim from the root layer doesn’t help).

  2. Joint local frames vs. xform. PhysX initialises articulation links by walking the joint tree from the root and computing child_world = parent_world * localFrame0 * inverse(localFrame1). Inner-prim USD xforms are only consulted as initial guesses — once Isaac Lab calls sim.reset(), the joint frames decide. If you encode the Kinova mount offset on /Robot/kinova’s xform and leave the fixed joint’s localPos0/Rot0/Pos1/Rot1 at identity, PhysX collocates Kinova on top of Spot the moment Lab simulates. Lab does read your xform — for the initial USD layout — but PhysX overwrites it the moment the simulation starts. That matches what you reported: it looks right before play, wrong as soon as Lab spawns and simulates.

  3. Initial USD pose has to satisfy the constraint. This one is subtle. With the joint local frames set correctly but the two base bodies’ starting USD poses both at identity, PhysX has a constraint violation at reset. For a floating articulation it doesn’t pin one body and move the other — the solver splits the residual across the whole tree and the composite picks up a half-rotation tilt. The fix is to also set /spot/kinova’s xform to the same offset so the bodies start at constraint-satisfying world poses.

How the Isaac Lab fix is implemented

Two ways to get there. If you’d rather do it through a UI, the Isaac Sim Robot Assembler does exactly this composite-articulation flow — pick a base robot, pick an arm, choose the mount link on each, set the offset, and it writes out a merged USD that handles steps 3–7 below for you. The Isaac Sim docs have a worked example using UR10e + Robotiq 2F-140 that maps one-to-one onto Spot + Kinova: https://docs.isaacsim.omniverse.nvidia.com/latest/robot_setup_tutorials/tutorial_import_assemble_manipulator.html#option-2-connect-the-ur10e-with-the-robotiq-2f-140-gripper-using-the-robot-assembler. Once the merged USD exists you point an Isaac Lab ArticulationCfg.spawn = sim_utils.UsdFileCfg(usd_path=...) at it and you’re done.

If you’d rather stay in code (which is what I’d recommend if you’re going to regenerate this composite across many configurations), the general flow is: author the composite USD tree yourself (instantiate Spot, instantiate Kinova as a child of Spot, then patch the prims), and then hand the result to Isaac Lab as an existing Articulation. The piece that makes this clean is ArticulationCfg(..., spawn=None) — that tells Lab to bind to a prim already on the stage instead of spawning a USD file, so you can apply the patches before Lab’s Articulation.__init__ parses the structure.

  1. Launch via AppLauncher and create a SimulationContext as usual.

  2. Instantiate Spot at /World/Robot/spot:

    from isaacsim.core.utils.stage import add_reference_to_stage
    from isaacsim.storage.native import get_assets_root_path
    
    assets_root = get_assets_root_path()
    add_reference_to_stage(
        usd_path=assets_root + "/Isaac/Robots/BostonDynamics/spot/spot.usd",
        prim_path="/World/Robot/spot",
    )
    

    spot.usd already authored ArticulationRootAPI on /spot; after the reference that ends up on /World/Robot/spot. Leave it there — once Kinova is nested inside, that single API covers the whole composite.

  3. Instantiate Kinova as a child of Spot at /World/Robot/spot/kinova:

    add_reference_to_stage(
        usd_path=assets_root + "/Isaac/Robots/Kinova/Gen3/gen3n7_instanceable.usd",
        prim_path="/World/Robot/spot/kinova",
    )
    

    Nesting (not siblings) is what keeps everything in one articulation subtree under /World/Robot/spot.

  4. Strip ArticulationRootAPI off the Kinova subtree. It came in from the asset; leaving it there forces PhysX to build two separate articulations and mount_joint degrades to a maximal-coordinate constraint:

    for prim in stage.Traverse():
        if str(prim.GetPath()).startswith("/World/Robot/spot/kinova") \
                and UsdPhysics.ArticulationRootAPI(prim):
            prim.RemoveAPI(UsdPhysics.ArticulationRootAPI)
    
  5. Deactivate Kinova’s world-anchor root_joint:

    stage.GetPrimAtPath("/World/Robot/spot/kinova/root_joint").SetActive(False)
    
  6. Author the FixedJoint inside Spot’s prim with the mount offset on the body0 side:

    fixed = UsdPhysics.FixedJoint.Define(stage, "/World/Robot/spot/mount_joint")
    fixed.GetBody0Rel().SetTargets(["/World/Robot/spot/body"])               # Spot side
    fixed.GetBody1Rel().SetTargets(["/World/Robot/spot/kinova/base_link"])   # Kinova side
    fixed.CreateLocalPos0Attr().Set(Gf.Vec3f(-0.15, 0.0, 0.15))
    fixed.CreateLocalRot0Attr().Set(Gf.Quatf(1.0))   # identity = both upright
    fixed.CreateLocalPos1Attr().Set(Gf.Vec3f(0.0, 0.0, 0.0))
    fixed.CreateLocalRot1Attr().Set(Gf.Quatf(1.0))
    
  7. Mirror the same offset on /spot/kinova’s xform so the initial USD pose agrees with the joint frames. The referenced asset already authored typed xformOp:translate/xformOp:orient ops, so you have to set existing op values rather than calling AddTranslateOp() (and the orient op is typed GfQuatd, not GfQuatf):

    prim = stage.GetPrimAtPath("/World/Robot/spot/kinova")
    xformable = UsdGeom.Xformable(prim)
    ops = {op.GetOpName(): op for op in xformable.GetOrderedXformOps()}
    ops["xformOp:translate"].Set(Gf.Vec3d(-0.15, 0.0, 0.15))
    ops["xformOp:orient"].Set(Gf.Quatd(1.0, 0.0, 0.0, 0.0))
    
  8. Hand the composite to Isaac Lab with spawn=None:

    import isaaclab.sim as sim_utils
    from isaaclab.assets import Articulation, ArticulationCfg
    
    cfg = ArticulationCfg(
        prim_path="/World/Robot/spot",   # the prim with ArticulationRootAPI
        spawn=None,                       # don't re-spawn; bind to what we authored
        init_state=ArticulationCfg.InitialStateCfg(
            pos=(0.0, 0.0, 0.6),
            # Spot's knees have a -2.79..-0.25 rad limit; default 0.0 is OOR.
            # These joint_pos match isaaclab_assets.SPOT_CFG so the stance is valid.
            joint_pos={
                "[fh]l_hx":  0.1,
                "[fh]r_hx": -0.1,
                "f[rl]_hy":  0.9,
                "h[rl]_hy":  1.1,
                ".*_kn":    -1.5,
            },
            joint_vel={".*": 0.0},
        ),
        actuators={},   # add actuators here if you want closed-loop control
    )
    composite = Articulation(cfg)
    

After sim.reset(), Lab sees one 26-body articulation rooted at spot.body, floating, with Kinova rigidly attached at the configured mount offset. Standing on its feet, no head-down snap.

Reproduction

fix_isaaclab.py does exactly this end-to-end. The helpers it uses (_set_xform to deal with the typed xform ops on the referenced asset, _disable_world_anchor_joints to walk a sub-tree and SetActive(False) any world-anchor joint) live in _common.py.

fix_isaaclab.py (3.9 KB)
_common.py (10.0 KB)

Hope that unblocks you. Let me know if your Kinova source has a different root_joint setup — if you imported the URDF yourself with fix_root_link=False, you can skip step 5.

Hello!

We noticed that this topic hasn’t received any recent updates from anyone reporting this issue, so we are closing it for now to help keep the forum organized.

If you are still experiencing this issue or have related questions, please create a GitHub Discussion or Issue in the Isaac Sim repository and include a link to this topic along with updated details. Mentioning or linking to this original topic provides helpful context and makes it easier for others to assist you.

Thank you for being part of the NVIDIA Isaac Sim community.

Best regards,
The NVIDIA Isaac Sim Forum Team