frankaemika / franka_ros

ROS integration for Franka research robots
https://frankaemika.github.io
Apache License 2.0
361 stars 311 forks source link

[feature_request] Adds ability to switch gazebo hardware interface #161

Closed rickstaa closed 2 years ago

rickstaa commented 3 years ago

I think it would be handy to have the ability to switch the hardware_interface that is used in the gazebo simulation for controlling the panda arm. This feature would allow users to test control the robot using different hardware interfaces, similar to the real robot.

Currently, the hardware_interface/JointEffortInterface is hardcoded into the panda_arm.urdf.xacro file. Because of this, the release of the Noetic panda_moveit_config package, which targets franka_ros v0.8.0, is blocked, as we would like to both use the JointPositionInterface in the gazebo simulation and on the real robot (see https://github.com/ros-planning/panda_moveit_config/pull/74 and https://github.com/ros-planning/panda_moveit_config/pull/68).

I also tried switching to the effort_controllers/JointTrajectoryController while using the following pid gains:

effort_joint_trajectory_controller:
  type: effort_controllers/JointTrajectoryController
  joints:
    - panda_joint1
    - panda_joint2
    - panda_joint3
    - panda_joint4
    - panda_joint5
    - panda_joint6
    - panda_joint7
  gains:
    panda_joint1: { p: 12000, d: 50, i: 0.0, i_clamp: 10000 }
    panda_joint2: { p: 30000, d: 100, i: 0.02, i_clamp: 10000 }
    panda_joint3: { p: 18000, d: 50, i: 0.01, i_clamp: 1 }
    panda_joint4: { p: 18000, d: 70, i: 0.01, i_clamp: 10000 }
    panda_joint5: { p: 12000, d: 70, i: 0.01, i_clamp: 1 }
    panda_joint6: { p: 7000, d: 50, i: 0.01, i_clamp: 1 }
    panda_joint7: { p: 2000, d: 20, i: 0.0, i_clamp: 1 }

Unfortunately, although these gains seem decent on the simulation robot (i.e. control has a slight offset), they lead to dangerous situations (i.e. very high velocities and acceleration) on the real robot, which causes the software emergency stop to be invoked).

I created #156 and #157 to add this feature.

rickstaa commented 3 years ago

After diving deeper into the gazebo_ros_control repo, I think I now understand why @frankaemika doesn't provide the position and velocity hardware interfaces in the franka_hw_sim.

The gazebo_ros_control currently only directly support the gazebo effort hardware interface. When a robot specifies a hardware_interface::PositionJointInterface or hardware_interface::PositionVelocityInterface in its RobotHWSim interface, the gazebo SetAngle and SetVelocity functions are used to set the position and velocity (see https://github.com/ros-simulation/gazebo_ros_pkgs/pull/135#discussion_r7950569). As these functions don't use any dynamics, they often fail when a robot has friction in the joints or interactions with the environment.

To solve this, the gazebo_ros_control gives users the ability to use a PID controller to control the robot position/velocity through the gazebo effort hardware interface. Users can enable this by setting gazebo_ros_control.pid_gains in the ROS parameter server (see default_robot_hw_sim.cpp#L203).

As the Panda robot only has effort controlled joints (see https://frankaemika.github.io/docs/libfranka.html#realtime-commands), it, therefore, doesn't make sense to ship the position and velocity joints in the franka_hw_sim.h interface.

As a result, I think merging #156 might not be the best solution. To keep the simulation as close as possible to the real simulation, the best thing to do would be to add the motion generators found in the FCI or equivalent PID controllers to the RobotHWSim interface. By doing this, people can specify joint position controller and joint velocity controllers, but under the hood, the Gazebo effort controllers would be used.

Merging #156 would only allow users to simulate joint position and joint velocity control using the earlier described logic of the gazebo_ros_control. As this does not seem to work out of the box (the robot has joint frictions), people need to tune the gazebo_ros_control.pid_gains gains and/or switch to using joint_effort_controllers/{joint_position_controller, joint_velocity_controller} controller. For both these options, they need to tune PID gains.

@gollth, @frankaemika Please correct me if my analysis is wrong.

rickstaa commented 3 years ago

@gollth I think for releasing the Noetic version of the panda_moveit_config it would be enough if you could provide add a joint_trajectory_example_controller with PID gains that work in the simulation but can also be safely used on the real robot.

Example controllers

Effort trajectory controller

The PID gains below do not work on the real robot (i.e. will lead to very big velocities) and cause small oscillations (i.e. non-zero velocities) in the simulation. This is because this PID controller was tuned for https://github.com/frankaemika/franka_ros/pull/126.

joint_position_example_controller:
  type: franka_example_controllers/JointPositionExampleController
  arm_id: $(arg arm_id)
  joint_names:
    - $(arg arm_id)_joint1
    - $(arg arm_id)_joint2
    - $(arg arm_id)_joint3
    - $(arg arm_id)_joint4
    - $(arg arm_id)_joint5
    - $(arg arm_id)_joint6
    - $(arg arm_id)_joint7

+joint_effort_trajectory_example_controller:
+  type: effort_controllers/JointTrajectoryController
+  joints:
+    - panda_joint1
+    - panda_joint2
+    - panda_joint3
+    - panda_joint4
+    - panda_joint5
+    - panda_joint6
+    - panda_joint7
+   constraints:
+     goal_time: 0.5
+     panda_joint1:
+       goal: 0.05
+     panda_joint2:
+       goal: 0.05
+     panda_joint3:
+       goal: 0.05
+     panda_joint4:
+       goal: 0.05
+     panda_joint5:
+       goal: 0.05
+     panda_joint6:
+       goal: 0.05
+     panda_joint7:
+       goal: 0.05
+  gains:
+    panda_joint1: { p: 12000, d: 50, i: 0.0, i_clamp: 10000 }
+    panda_joint2: { p: 30000, d: 100, i: 0.02, i_clamp: 10000 }
+    panda_joint3: { p: 18000, d: 50, i: 0.01, i_clamp: 1 }
+    panda_joint4: { p: 18000, d: 70, i: 0.01, i_clamp: 10000 }
+    panda_joint5: { p: 12000, d: 70, i: 0.01, i_clamp: 1 }
+    panda_joint6: { p: 7000, d: 50, i: 0.01, i_clamp: 1 }
+    panda_joint7: { p: 2000, d: 20, i: 0.0, i_clamp: 1 }

Position trajectory controller

The controller below works on the real robot but does not work in simulation as the gazebo_ros_control.pid_gains need to be set (see https://github.com/frankaemika/franka_ros/issues/161#issuecomment-921837993).

joint_position_example_controller:
  type: franka_example_controllers/JointPositionExampleController
  arm_id: $(arg arm_id)
  joint_names:
    - $(arg arm_id)_joint1
    - $(arg arm_id)_joint2
    - $(arg arm_id)_joint3
    - $(arg arm_id)_joint4
    - $(arg arm_id)_joint5
    - $(arg arm_id)_joint6
    - $(arg arm_id)_joint7

+joint_position_trajectory_example_controller:
+  type: position_controllers/JointTrajectoryController
+  joints:
+    - panda_joint1
+    - panda_joint2
+    - panda_joint3
+    - panda_joint4
+    - panda_joint5
+    - panda_joint6
+    - panda_joint7
+   constraints:
+     goal_time: 0.5
+     panda_joint1:
+       goal: 0.05
+     panda_joint2:
+       goal: 0.05
+     panda_joint3:
+       goal: 0.05
+     panda_joint4:
+       goal: 0.05
+     panda_joint5:
+       goal: 0.05
+     panda_joint6:
+       goal: 0.05
+     panda_joint7:
+       goal: 0.05
+  gains:
+    panda_joint1: { p: 12000, d: 50, i: 0.0, i_clamp: 10000 }
+    panda_joint2: { p: 30000, d: 100, i: 0.02, i_clamp: 10000 }
+    panda_joint3: { p: 18000, d: 50, i: 0.01, i_clamp: 1 }
+    panda_joint4: { p: 18000, d: 70, i: 0.01, i_clamp: 10000 }
+    panda_joint5: { p: 12000, d: 70, i: 0.01, i_clamp: 1 }
+    panda_joint6: { p: 7000, d: 50, i: 0.01, i_clamp: 1 }
+    panda_joint7: { p: 2000, d: 20, i: 0.0, i_clamp: 1 }
+
+ gazebo_ros_control:
+   pid_gains:
+     panda_joint1: { p: 12000, d: 50, i: 0.0, i_clamp: 10000 }
+     panda_joint2: { p: 30000, d: 100, i: 0.02, i_clamp: 10000 }
+     panda_joint3: { p: 18000, d: 50, i: 0.01, i_clamp: 1 }
+     panda_joint4: { p: 18000, d: 70, i: 0.01, i_clamp: 10000 }
+     panda_joint5: { p: 12000, d: 70, i: 0.01, i_clamp: 1 }
+     panda_joint6: { p: 7000, d: 50, i: 0.01, i_clamp: 1 }
+     panda_joint7: { p: 2000, d: 20, i: 0.0, i_clamp: 1 }
+     panda_finger_joint1: { p: 5, d: 3.0, i: 0, i_clamp: 1 }
+     panda_finger_joint2: { p: 5, d: 1.0, i: 0, i_clamp: 1 }
rickstaa commented 3 years ago

Problems with the earlier mentioned PID gains

Effort trajectory controller PID gains

The current PID gains found in the effort_joint_trajectory_controllers.yaml are not correct. With these PID gains.

This is because this PID controller was tuned for https://github.com/frankaemika/franka_ros/pull/126. These wrongly tuned PID gains lead to a number of problems.

Joint trajectory controller GOAL_TOLERANCE_VOILATED error

With the current PID gains the robot has small oscillations when the velocity should be zero (see the gif and the console log below). As a result, during trajectory control, the JointTrajectoryController throws a GOAL_TOLERANCE_VIOLATED error. As explained in [this ROS answers question](https://answers.ros.org/question/312123/goal_tolerance_violated-gazebomoveit] a quick fix would be to set the stopped_velocity_tolerance to 0. However, to really solve this issue, the PID gains should be retuned for the new franka_gazebo simulation.

small_oscillations

velocity: [-2.3700327160491814, -0.07111169060622406, 2.42011259637029, -0.1602890653322738, -0.5338011186773755, 0.25708581160465127, 1.144875244723069, 0.04420516084897766, -0.044204294583440176]

As can be seen from the picture above, the velocity of the first panda joint is very high, while we do not see this movement in the simulation. This is likely because the PID gains are very high, causing it to constantly switch the direction of the control. This assumption strengthened by looking at the velocity between time steps. One time step it is -2.36 then the next 2.36.

MoveIt gripper control execution timeout

In addition to the problem above, these wrongly tuned PID gains also prohibit MoveIt from correctly using the franka_gripper/gripper_action action service. This is because under the hood, this action uses the GRASP state of the controller state machine. With the fixed franka_gripper/gripper_action (i.e. after #173 has been merged) the state machine sometimes gets stuck in the GRASP state.

https://github.com/frankaemika/franka_ros/blob/054af531f43f3fc1ddb439cc4102a41f87a8e0da/franka_gazebo/src/franka_gripper_sim.cpp#L435-L439

This state only transitions to the HOLDING state when the gripper finger velocities are close to zero.

https://github.com/frankaemika/franka_ros/blob/054af531f43f3fc1ddb439cc4102a41f87a8e0da/franka_gazebo/src/franka_gripper_sim.cpp#L171-L191

Due to the badly tuned PID gains these velocities are never under the velocity threshold of 0.001.

https://github.com/frankaemika/franka_ros/blob/054af531f43f3fc1ddb439cc4102a41f87a8e0da/franka_gazebo/include/franka_gazebo/franka_gripper_sim.h#L36.

As a result, the controller never transitions to the HOLDING state.

https://github.com/frankaemika/franka_ros/blob/054af531f43f3fc1ddb439cc4102a41f87a8e0da/franka_gazebo/src/franka_gripper_sim.cpp#L441

Consequently, the third party services like MoveIt, which depend on the franka_gripper/gripper_action does not receive a action result before they timeout.

I think this is caused by the fact that the finger_joint velocities are a bit unstable (i.e., slightly increase) when the other joints are moving, or that the finger controller PID gains should be improved.

Investigation

Let's verify this hypothesis by inspecting the finger velocity commands while moving the arm arround. We can use the pick & place example that is given in the documentation. Below I added a gif of how the velocities behave when I move the arm arround. Keep in mind that the velocity threshold is set to 0.001.

velocities

Resting velocities

velocity: [-0.0008394972722331565, 0.002920198607601596, -0.0004664629045941222, 0.0018506860899074268, -0.008040965487597593, -0.0017851274417448255, 0.0021114798536561847, -5.6208884257551426e-05, 5.6490498751197266e-05]

From this we can see that the finger velocities are indeed a little bit influenced by the movement of the robot arm. I'm unsure if this is because of finger PID gains that can be improved or that this is due to instaiblities inside the gazebo simulation.

I however think that this should not cause to much problems as the velocity is only shortly above the threshold. I only becomes a problem when the arm controllers are tuned badly (see gif below).

velocities_effort

Resting velocities

velocity: [-2.3617382151641126, 0.00779549310441726, 2.398145283557499, 0.004950285110020838, -0.5145020878195646, -0.21680003827061464, -0.7459508604510294, 0.031055436989710014, -0.031054684808693785]
velocity: [2.3645298595526008, -0.007416133146815294, -2.4055287574456763, -0.0077949747761998645, 0.5515721516252698, 0.22718758256158672, 0.7415553930080468, -0.030523518801102368, 0.03052436581763448]

Position trajectory controller PID gains

For this controller both the gazebo_ros_control.pid_gains and joint_position_trajectory_example_controller.gains are not tuned (see GIFS below).

With only controller gains and no gazebo PID gains

gazebo_no_pid_gains

With controller gains and gazebo PID gains

position_pid_gains_problem

gollth commented 3 years ago

Wow, many things happening at once here 😲

After diving deeper into the gazebo_ros_control repo, I think I now understand why @frankaemika doesn't provide the position and velocity hardware interfaces in the franka_hw_sim.

The gazebo_ros_control currently only directly support the gazebo effort hardware interface. When a robot specifies a hardware_interface::PositionJointInterface or hardware_interface::PositionVelocityInterface in its RobotHWSim interface, the gazebo SetAngle and SetVelocity functions are used to set the position and velocity (see ros-simulation/gazebo_ros_pkgs#135 (comment)). As these functions don't use any dynamics, they often fail when a robot has friction in the joints or interactions with the environment.

To solve this, the gazebo_ros_control gives users the ability to use a PID controller to control the robot position/velocity through the gazebo effort hardware interface. Users can enable this by setting gazebo_ros_control.pid_gains in the ROS parameter server (see default_robot_hw_sim.cpp#L203).

As the Panda robot only has effort controlled joints (see https://frankaemika.github.io/docs/libfranka.html#realtime-commands), it, therefore, doesn't make sense to ship the position and velocity joints in the franka_hw_sim.h interface.

As a result, I think merging #156 might not be the best solution. To keep the simulation as close as possible to the real simulation, the best thing to do would be to add the motion generators found in the FCI or equivalent PID controllers to the RobotHWSim interface. By doing this, people can specify joint position controller and joint velocity controllers, but under the hood, the Gazebo effort controllers would be used.

Merging #156 would only allow users to simulate joint position and joint velocity control using the earlier described logic of the gazebo_ros_control. As this does not seem to work out of the box (the robot has joint frictions), people need to tune the gazebo_ros_control.pid_gains gains and/or switch to using joint_effort_controllers/{joint_position_controller, joint_velocity_controller} controller. For both these options, they need to tune PID gains.

@gollth, @frankaemika Please correct me if my analysis is wrong.

To answer this first part: You are right, this was one of the reasons we didn't provide positions and velocity interfaces. I totally agree with keeping the simulation as close to the real system as possible. However due to proprietary reasons, unfortunately we are not able to release the internal workings of the FCI. This includes open-sourcing the motion generators.

Since as you say user would need to tune gains in both cases, maybe the solution proposed in #156 is the next best solution, still?

gollth commented 3 years ago

Hi @rickstaa,

I had a look at the problem with the alternating high velocities of joint 1. On first glance it seems that your gains are way too high. I tested with the following gains and got rid of the oscillations:

With the current PID gains the robot has small oscillations when the velocity should be zero (see the gif and the console log below). As a result, during trajectory control, the JointTrajectoryController throws a GOAL_TOLERANCE_VIOLATED error. As explained in [this ROS answers question]([https://answers.ros.org/question/312123/goal_tolerance_violated-gazebomoveit]](https://answers.ros.org/question/312123/goal_tolerance_violated-gazebomoveit%5D) a quick fix would be to set the stopped_velocity_tolerance to 0. However, to really solve this issue, the PID gains should be retuned for the new franka_gazebo simulation.

joint_effort_trajectory_example_controller:
  type: effort_controllers/JointTrajectoryController
  joints:
    - $(arg arm_id)_joint1
    - $(arg arm_id)_joint2
    - $(arg arm_id)_joint3
    - $(arg arm_id)_joint4
    - $(arg arm_id)_joint5
    - $(arg arm_id)_joint6
    - $(arg arm_id)_joint7
  gains:
    $(arg arm_id)_joint1: { p: 600, d: 30, i: 0 }
    $(arg arm_id)_joint2: { p: 600, d: 30, i: 0 }
    $(arg arm_id)_joint3: { p: 600, d: 30, i: 0 }
    $(arg arm_id)_joint4: { p: 600, d: 30, i: 0 }
    $(arg arm_id)_joint5: { p: 250, d: 10, i: 0 }
    $(arg arm_id)_joint6: { p: 150, d: 10, i: 0 }
    $(arg arm_id)_joint7: { p:  50, d:  5, i: 0 }
  constraints:
    goal_time: 0.5
    $(arg arm_id)_joint1: { goal: 0.05 }
    $(arg arm_id)_joint2: { goal: 0.05 }
    $(arg arm_id)_joint3: { goal: 0.05 }
    $(arg arm_id)_joint4: { goal: 0.05 }
    $(arg arm_id)_joint5: { goal: 0.05 }
    $(arg arm_id)_joint6: { goal: 0.05 }
    $(arg arm_id)_joint7: { goal: 0.05 }

With this rough tuning the robot is still able to track the joint trajectories okayish in Gazebo. See position error in right plot is around 1.2 degrees peak. Also the jittering in RViz is (almost) gone. One could tune further of course. I will test on the real robot as well. Looks stable on the real hardware as well.

JointTrajectoryController-Tuning

If this is useful we can think about adding the tuned gains into the sim_controllers.yaml

rickstaa commented 3 years ago

Wow, many things happening at once here astonished

After diving deeper into the gazebo_ros_control repo, I think I now understand why @frankaemika doesn't provide the position and velocity hardware interfaces in the franka_hw_sim. The gazebo_ros_control currently only directly support the gazebo effort hardware interface. When a robot specifies a hardware_interface::PositionJointInterface or hardware_interface::PositionVelocityInterface in its RobotHWSim interface, the gazebo SetAngle and SetVelocity functions are used to set the position and velocity (see ros-simulation/gazebo_ros_pkgs#135 (comment)). As these functions don't use any dynamics, they often fail when a robot has friction in the joints or interactions with the environment. To solve this, the gazebo_ros_control gives users the ability to use a PID controller to control the robot position/velocity through the gazebo effort hardware interface. Users can enable this by setting gazebo_ros_control.pid_gains in the ROS parameter server (see default_robot_hw_sim.cpp#L203). As the Panda robot only has effort controlled joints (see https://frankaemika.github.io/docs/libfranka.html#realtime-commands), it, therefore, doesn't make sense to ship the position and velocity joints in the franka_hw_sim.h interface. As a result, I think merging #156 might not be the best solution. To keep the simulation as close as possible to the real simulation, the best thing to do would be to add the motion generators found in the FCI or equivalent PID controllers to the RobotHWSim interface. By doing this, people can specify joint position controller and joint velocity controllers, but under the hood, the Gazebo effort controllers would be used. Merging #156 would only allow users to simulate joint position and joint velocity control using the earlier described logic of the gazebo_ros_control. As this does not seem to work out of the box (the robot has joint frictions), people need to tune the gazebo_ros_control.pid_gains gains and/or switch to using joint_effort_controllers/{joint_position_controller, joint_velocity_controller} controller. For both these options, they need to tune PID gains. @gollth, @frankaemika Please correct me if my analysis is wrong.

To answer this first part: You are right, this was one of the reasons we didn't provide positions and velocity interfaces. I totally agree with keeping the simulation as close to the real system as possible. However due to proprietary reasons, unfortunately we are not able to release the internal workings of the FCI. This includes open-sourcing the motion generators.

Since as you say user would need to tune gains in both cases, maybe the solution proposed in #156 is the next best solution, still?

@gollth Thanks for your answer!

No problem, I think this is closely related to the discussion held in https://github.com/frankaemika/franka_ros/issues/143. On one side, you have to hide the internal workings of the robot and the controllers from your competitors, but also want to provide the best experience for researchers. Since even a .dll or .so can eventually be reverse engineered, I understand your decision not to share the motion controllers.

I agree with you that #156 would already be a significant improvement. This gives users the ability also to test position and velocity controllers in the simulation if they need to. In that case, it would be beneficial if you could provide working gazebo_ros_control.pid_gains. Nevertheless, in most of my research and my colleagues, the effort controllers are used.

The main reason why I created this pull request is that the new franka_gazebo simulation doesn't work anymore with the joint_position_example_controller that is provided by the franka_example_controllers package. I think this controller is used in the panda_moveit_config since it is easy to use for beginners, as on the real robot, it doesn't require the tuning of PID gains. I think for releasing the Noetic branch, merging #156, while finding gazebo_ros_control.pid_gains that makes the joint_position_example_controller work on the simulation would be enough. I quickly tried tuning them manually, but did not yet have the correct values.

Hi @rickstaa,

I had a look at the problem with the alternating high velocities of joint 1. On first glance it seems that your gains are way too high. I tested with the following gains and got rid of the oscillations:

Great, thanks a lot! I already found them suspiciously high, but like the gazebo_ros_control.pid_gains didn't yet find the time to investigate. Did you use an optimizer or also tuned them manually? I think adding the PID gains you found to https://github.com/frankaemika/franka_ros/blob/develop/franka_gazebo/config/sim_controllers.yaml might help people out.

For adjusting the gazebo_ros_control.pid_gains, we could add a dynamic reconfigure server to the https://github.com/ros-simulation/gazebo_ros_pkgs/blob/231a7219b36b8a6cdd100b59f66a3df2955df787/gazebo_ros_control/src/default_robot_hw_sim.cpp#L220 so that we can manually tune them. Or do you have another idea?

rickstaa commented 3 years ago

@gollth I just noticed that the gazebo_ros_control.pid_gains are not used in our case since we are not using the DefaultRobotHWSim but the franka_gazebo/FrankaHWSim simulation hardware interface.

As, a result my comments above are incorrect. To fix the position and velocity control, we need to implement the PID logic inside the franka_gazebo/FrankaHWSim class.

rickstaa commented 3 years ago

@gollth I managed to add motion generators into the FrankaHWSim see #181.

rickstaa commented 2 years ago

This feature was implemented in https://github.com/frankaemika/franka_ros/commit/2514ae1990d51ca808e6c49d22d24ae7a99db532.