Imprecise control via rmpflow

Hello everyone,

I’m currently working on controlling a robot arm in Isaac Sim using rmpflow. While I’m able to drive the arm to a target position, I’m noticing that the arm isn’t reaching the position accurately. I suspect this is because rmpflow sets the joint target positions (joints act like a spring), which are not reached due to physical constraints like gravity, resulting in a small positional delta.

I’m wondering if anyone has had a similar issue and how they addressed it. Specifically, I’m curious if there’s an existing compensation or controller in Isaac Sim to address this issue, or if I need to implement my own controller.

Any suggestions or experiences would be greatly appreciated. Thank you!

Hi Axel. How close is it getting to the target?

Have you tried increasing the stiffness of the joint drives to a larger value? When close tracking is desired, we typically crank up the stiffness (e.g., to 10^10) and also increase damping (e.g., to 10^9).

There are also a couple of debugging features that might help distinguish whether the issue is with RMPflow convergence or with gain tuning. (It’s usually the latter.) See Lula RMPflow — Omniverse Robotics documentation

Hi Buck,

Thank you for your suggestions.

Our aim is to use simulation to train a machine learning agent and then apply the results to a real robot. We would like to avoid changing the stiffness parameters as this could create discrepancies between the simulated and real robot.

During my experimentation, I adjusted the articulation controller gain and was able to decrease the positional error slightly, from 1.1mm to 0.33mm, by multiplying the gain by a factor of 6. However, higher gain values resulted in strange robot behavior.

articulation_controller = my_robot.get_articulation_controller()

bad_proportional_gains = articulation_controller.get_gains()[0]*6
articulation_controller.set_gains(kps = bad_proportional_gains)

I was not happy with these results and therefore implemented a PID controller to compensate the target position error:

Here are the parameters I used for the controller:

        # parameters for position controller
        self.pos_controller_error_sum = np.array([0.0, 0.0, 0.0])
        self.pos_controller_last_error = np.array([0.0, 0.0, 0.0])
        self.pos_controller_windup_limit = np.array([0.05, 0.05, 0.05])
        self.pos_controller_Kp = np.array([0.1, 0.1, 0.1])
        self.pos_controller_Ki = np.array([0.05, 0.05, 0.05])
        self.pos_controller_Kd = np.array([0.05, 0.05, 0.05])

And this is the controller:

    def pid_controller(self, target_pos, current_position):
        Kp = self.pos_controller_Kp
        Ki = self.pos_controller_Ki
        Kd = self.pos_controller_Kd
        windup_limit = self.pos_controller_windup_limit
        error = target_pos - current_position
        error_rate = error - self.pos_controller_last_error
        self.pos_controller_error_sum += error

        # Anti-windup: Limit the integrator sum
        self.pos_controller_error_sum = np.clip(self.pos_controller_error_sum, a_max=windup_limit, a_min=-windup_limit)

        controller_offset = Kp * error + Ki * self.pos_controller_error_sum + Kd * error_rate

        self.pos_controller_last_error = error

        return controller_offset

Integrating a controller into the individual robot joint drives would likely yield the best results. The ability to add a PID controller to joint drives would also be an interesting feature for Omniverse, since many actuators (in the real world) have integrated controllers.