Cannot revolute beyond -2pi or 2pi using joint position control

Isaac Sim Version

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 22.04
Ubuntu 20.04
Windows 11
Windows 10
Other (please specify):

GPU Information

  • Model: RTX 4060
  • Driver Version: 535.183.01

Topic Description

Detailed Description

Expected result
    When given a control value of 4pi, rotate twice

Actual result
    After one rotation, an error occurs

Steps to Reproduce

  1. Set the ‘Lower Limit’ and ‘Upper Limit’ of the revolute joint to ‘Not Limited’
  2. Set the value of joint position to 4pi
    1. Apply action using ‘apply_action’ API

Error Messages

PhysX error: PxArticulationJointReducedCoordinate::setDriveTarget() only supports target angle in range [-2Pi, 2Pi] for joints of type PxArticulationJointType::eREVOLUTE

Additional Information

Additional Context

I want to control the joint to move more than once with position control.
Is it impossible to control more than one turn with position control method?

Hi @jinkk.kim, thanks for posting your issue. I am not able to reproduce your error. I tried to instantiate a Franka arm and increase the base joint limits to something high, then continuously control the base joint to rotate. My output looks like this:

Here is the code I am using:

# Copyright (c) 2022-2024, NVIDIA CORPORATION. All rights reserved.
#
# NVIDIA CORPORATION and its licensors retain all intellectual property
# and proprietary rights in and to this software, related documentation
# and any modifications thereto. Any use, reproduction, disclosure or
# distribution of this software and related documentation without an express
# license agreement from NVIDIA CORPORATION is strictly prohibited.
#

import numpy as np
from isaacsim import SimulationApp

# Create simulation app (not headless so we can see the robot)
simulation_app = SimulationApp({"headless": False})

from isaacsim.core.api import World
from isaacsim.core.utils.types import ArticulationAction
from isaacsim.robot.manipulators.examples.franka import Franka
from pxr import UsdPhysics


# Create world with CPU backend
my_world = World(stage_units_in_meters=1.0)
print("Using CPU backend")

# Add default ground plane
my_world.scene.add_default_ground_plane()

# Add Franka robot to the scene
my_franka = my_world.scene.add(
    Franka(
        prim_path="/World/Franka",
        name="my_franka",
        gripper_dof_names=["panda_finger_joint1", "panda_finger_joint2"],
        end_effector_prim_name="panda_rightfinger",
        gripper_open_position=np.array([0.4, 0.4]) / 0.01,
        gripper_closed_position=np.array([0.0, 0.0]),
    )
)

# Get the stage
stage = simulation_app.context.get_stage()

# Get the base joint (first joint) of the Franka robot
base_joint_path = "/World/Franka/panda_link0/panda_joint1"
base_joint = stage.GetPrimAtPath(base_joint_path)

# Remove position limits from the base joint
if base_joint:
    UsdPhysics.RevoluteJoint(base_joint).GetUpperLimitAttr().Set(100000.0)
    UsdPhysics.RevoluteJoint(base_joint).GetLowerLimitAttr().Set(-100000.0)
else:
    print("Could not find base joint at path:", base_joint_path)

# Reset the world to apply changes
my_world.reset()

# Get the articulation controller
articulation_controller = my_franka.get_articulation_controller()

# Switch to position control mode
articulation_controller.switch_control_mode("position")

# Initial joint positions
initial_positions = my_franka.get_joints_state().positions
print("Initial joint positions:", initial_positions)

# Continuous rotation parameters
rotation_speed = 5  # radians per second
base_joint_index = 0  # The index of the base joint in the articulation

# Main simulation loop
print("Starting continuous rotation of base joint")
start_time = 0

while simulation_app.is_running():
    # Get current time
    current_time = my_world.current_time
    if start_time == 0:
        start_time = current_time
    
    # Calculate the desired angle for continuous rotation
    elapsed_time = current_time - start_time
    target_angle = rotation_speed * elapsed_time
    
    # Create target positions (only changing the base joint)
    target_positions = np.copy(initial_positions)
    target_positions[base_joint_index] = target_angle
    print(target_positions)
    
    # Apply the action
    action = ArticulationAction(joint_positions=target_positions)
    articulation_controller.apply_action(action)
    
    # Step the simulation
    my_world.step(render=True)

# Cleanup
simulation_app.close()

Let me know if this works for you.

@michalin Thank you for providing example code.

The example you provide moves up to ±100000.0, but isn’t it impossible to move more than that?
If the upper and lower ranges are changed to 1000 and -1000 in the example code, the joint cannot rotate at ±1000

I want to make the joint rotate infinitely using position_control.
How should I modify it?

@michalin Can you check my issue? The question is above.

Hi @jinkk.kim, sorry for the delay. I’ll look into this right now.

It seems like if we set the joint limit to something large

UsdPhysics.RevoluteJoint(base_joint).GetUpperLimitAttr().Set(10e10)

then Drive API is ok, but if we set it to unlimited

UsdPhysics.RevoluteJoint(base_joint).GetUpperLimitAttr().Set(float('inf'))

then the error pops up. I am checking with the PhysX teams to see if this is a bug.

For now, just use a large number to unblock yourself as I think it largely achieves the behavior you are looking for.

Hi @jinkk.kim, the solution to have continuous revolute joint is to initialize the Articulation without joint limits. What happens during initialization of the Articulation is that it checks on whether each joint has limits enabled. If it does it will create a different type of Revolute joint eRevolute or a continuous version of it. If you have the eRevolute it will inevitably check whether you have reached the joint limit.

In your case, if you are loading the articulation from a URDF make sure that the limits for the joints you want to be continuous are removed.

Let me know if that helps.

@michalin Thank you for your reply.

My joint limit setting method is as follows.

  1. Open usd file in isaac sim GUI.
  2. Select the PhysicsRevolute type joint.
  3. Set ‘Lower Limit’ and ‘Upper Limit’ to ‘Not Limited’ and save usd file

Q1. Isn’t it the right way to set ‘Not Limited’ in isaac sim GUI?
Q2. The method you suggested is to set the joint type to ‘continuous’ in the URDF file and load the URDF to isaac sim GUI?
ref. urdf/XML/joint - ROS Wiki

Your USD probably already had a Revolute Joint initialized with a joint limit. That makes PhysX automatically choose a joint type called eRevolute which always expects a joint limit. If you want to load your robot from a USD the steps to fix this is:

  1. Open USD file
  2. Delete the joint attribute
  3. Add the joint attribute back in and leave the upper and lower limit to the default ‘Not Limited’

Hope that fixes it!

@michalin I did it the way you suggested, but it hasn’t been fixed.
The same error occurred.

Error Messages

PxArticulationJointReducedCoordinate::setDriveTarget() only supports target angle in range [-2Pi, 2Pi] for joints of type PxArticulationJointType::eREVOLUTE, FILE /builds/omniverse/physics/physx/source/physx/src/NpArticulationJointReducedCoordinate.cpp, LINE 298

The procedure I did is as follows.

  1. Open USD file.
  2. Select and delete the existing PhysicsRevolute type joint.
  3. Create Revolute joint, Define Body 0, Body 1. (Not revise upper and lower limit.)

Would it be possible for you to share a USD version that replicates this? I am try to debug on that.