Ros2 support for KUKA iiwa

Hi everyone,
We have been working on the past month on a open-source Driver for the KUKA iiwa collaborative robots using ros2_control and KUKA’s Fast Robot Interface (FRI). Here you can access our Github repository iiwa_ros2.

What’s new ?
Our iiwa_ros2 package contains launch and configuration setups to quickly get started using the driver both in simulation and using the real system.

Currently, the following features are available:

  • integration with ros2_control
  • robot drivers for KUKA Fast Robot Interface (FRI) protocol for position, velocity and torque control
  • dedicated sensors and broadcasters to get data from the robot
  • integration with Gazebo
  • integration with Moveit2

We greatly appreciate all feedback and suggestions to improve our package!
Thank you!

5 Likes

Hi @mcbed,

This looks great! I just did a quick look at the repository and I have a few questions about design decisions.

Why did you decide to have multiple hardware interface-plugins for different combination of output control values, i.e., positions, velocity, effort. Would it make sense to add them into one plugin to reduce code duplication?

I see that your “JointTorqueBroadcaster” (you call it " ExternalTorqueSensor") is basically just publishing a vector of doubles. This seems straightforward to generalize. Would it make sense to create a generic Controller in ros2_controller that has this capability?

Thanks!

Denis

Hi @destogl,
Thank you for your feedback!
You are right about the code duplication using multiple hardware interfaces. We will merge them into a single one with our next update.
We decided to create a dedicated external torque broadcaster as a particular sensor for the 7DOF iiwa that provides such data. However, a generic broadcaster would be in fact very helpful as other system information such as for example joint currents could easily be published and not only restricted to the generic JointState type. We will see if we can propose such a broadcaster.

Thanks!

Maciej

This sounds great, let us know if we can help you with something, or join us in our bi-weekly Meetings of Control WG. This Wednesday is one. There Webcam directly answer any questions you might have.

In the meantime, JointStateBroadcaster publishes „/dynamic_joint_state“ topic. This exposes data from all state interfaces defined for your hardware. So all data should be already there (but in generic format).

I will be happy to join the meeting then.

If we use the „/dynamic_joint_state“ topic to get the sensor data, do you think that there is a point in creating a generic sensor broadcaster ?

If you are using /dynamic_joint_state you will have to parse the values from message. Using specialized broadcaster or generic broadcaster that publishes simple array of doubles which is then simpler to use in other nodes. But of course, it depends on the concrete use-case.

Hi @mcbed .
Great work on the driver. I seem to be having trouble launching the simulator. Gazebo seems to not be able to reach /controller_manager ([spawner_joint_state_broadcaster]: Waiting for /controller_manager services). I’m trying to test torque control but i dont think it works without the simulator. I would appreciate if you could lend me a hand with that.
I want to reiterate that this driver seems extremely useful and thank you for taking the time to develop and share it.

Hi @DiegoNavaca,
Thanks for your interest in this driver ! The problem you encountered is due to an issue with urdf description as gazebo searches for the model files inside the ~/.gazebo/models folder and not in the provided packages. To deal with it, you can simply make a copy of the iiwa_description package and paste it into the gazebo model folder. I think that there is a more elegant solution and any idea for improvement is welcome.

Hello again @mcbed ,
First of all, thank you for your help. I’m sorry to have to keep bothering you but the solution you gave me only seems to work on the iiwa_gazebo.launch.py script, but when I use the iiwa.launch.py script (in order to use other controllers and interfaces) the driver keeps getting a similar error. It seems the controller manager dies at launch:
“”"
[ERROR] [ros2_control_node-1]: process has died [pid 37119, exit code -6, cmd ‘/opt/ros/foxy/lib/controller_manager/ros2_control_node --ros-args --params-file /tmp/launch_params__cm7pf42 --params-file /home/control/ros2_ws/install/iiwa_description/share/iiwa_description/config/iiwa_controllers.yaml’].
“”"
I would be grateful if you could help me find the problem.
Cheers.

In the current state using gazebo requires the use of the gazebo launch file as the launch configuration is different, but you are right that having all in iiwa.launch.py as stand-alone is a more readable solution. I will add this feature to the launch file next week. Thanks for your feedback !

Hi @mcbed
I was trying to launch the robot iiwa on rviz. And i was wondering if itś possible to use this robot with MoveItConfigsBuilder, and if so, how to do it?
There is an example in the moveit2 humble tutorials for the robot panda where it is used: def launch_setup(context, *args, **kwargs):

moveit_config = (
    MoveItConfigsBuilder("moveit_resources_panda")
    .robot_description(file_path="config/panda.urdf.xacro")
    .trajectory_execution(file_path="config/gripper_moveit_controllers.yaml")
    .planning_scene_monitor(
        publish_robot_description=True, publish_robot_description_semantic=True
    )
    .planning_pipelines(
        pipelines=["ompl", "chomp", "pilz_industrial_motion_planner"]
    )
    .to_moveit_configs()
)

This is present in the node: ros2 launch moveit2_tutorials demo.launch.py . I was trying to do something similar with iiwa using the package you provided.

You might checkout the LBR-Stack as an alternative: GitHub - lbr-stack/lbr_fri_ros2_stack: ROS 1/2 integration for KUKA LBR IIWA 7/14 and Med 7/14

Can be launched via:

ros2 launch lbr_bringup bringup.launch.py \
    model:=iiwa7 # [iiwa7, iiwa14, med7, med14] \
    sim:=true # [true, false] \
    rviz:=true # [true, false] \
    moveit:=true # [true, false]

More MoveIt documentation here: LBR MoveIt Config — LBR-Stack 1.4.3 documentation (some of this documentation is outdated but will be updated with 1.5.0 release)

Disclaimer I am the main author of the above.

Hello, yes i alreadyu tried that package and i did it. I made a code similar to the one used to the robot panda from the moveit2 tutorials present in: ros2 launch moveit2_tutorials demo.launch.py

The code that i did works. I can plan the robot trajetory for example using the planner OMPL, however i can´t execute. I dont know why but moveit itś not reading the controller_manager for executing the movement of the robot. The command plan in rviz works, but the execute one no. THis is the code that i was using: import os
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument, OpaqueFunction
from launch.substitutions import LaunchConfiguration, PathJoinSubstitution
from launch.conditions import IfCondition, UnlessCondition
from launch_ros.actions import Node
from launch_ros.substitutions import FindPackageShare
from launch.actions import ExecuteProcess
from ament_index_python.packages import get_package_share_directory
from moveit_configs_utils import MoveItConfigsBuilder
from lbr_description import LBRDescriptionMixin, RVizMixin
from lbr_bringup import LBRMoveGroupMixin

def generate_launch_description():

declared_arguments = []
declared_arguments.append(
    DeclareLaunchArgument(
        "rviz_config",
        default_value="miariiwa1.rviz",
        description="RViz configuration file",
    )
)

return LaunchDescription(
    declared_arguments + [OpaqueFunction(function=launch_setup)]
)

def launch_setup(context, *args, **kwargs):

moveit_config = (
    MoveItConfigsBuilder("iiwa14")
    .robot_description(os.path.join(
            get_package_share_directory("lbr_description"),
            "urdf/iiwa14/iiwa14.urdf.xacro",
        )
    )
    .trajectory_execution(file_path="config/moveit_controllers.yaml")
    .planning_scene_monitor(
        publish_robot_description=True, publish_robot_description_semantic=True
    )
    .planning_pipelines(
        pipelines=["ompl", "pilz_industrial_motion_planner"]
    )
    .robot_description_kinematics(file_path="config/kinematics.yaml")
    .to_moveit_configs()   
)

# Start the actual move_group node/action server
run_move_group_node = Node(
    package="moveit_ros_move_group",
    executable="move_group",
    output="screen",
    parameters=[moveit_config.to_dict()],
)

rviz_base = LaunchConfiguration("rviz_config")
rviz_config = PathJoinSubstitution(
    #[FindPackageShare("lbr_description"), "config", rviz_base]
    [FindPackageShare("miar_robot"), "launch", rviz_base]
    
)

# RViz
rviz_node = Node(
    package="rviz2",
    executable="rviz2",
    name="rviz2",
    output="log",
    arguments=["-d", rviz_config],
    parameters=[
        moveit_config.robot_description,
        moveit_config.robot_description_semantic,
        moveit_config.robot_description_kinematics,
        moveit_config.planning_pipelines,
        moveit_config.joint_limits,
    ],
)

# Static TF
static_tf = Node(
    package="tf2_ros",
    executable="static_transform_publisher",
    name="static_transform_publisher",
    output="log",
    arguments=["0.0", "0.0", "0.0", "0.0", "0.0", "0.0", "world", "link_0"],
)

# Publish TF
robot_state_publisher = Node(
    package="robot_state_publisher",
    executable="robot_state_publisher",
    name="robot_state_publisher",
    output="both",
    parameters=[moveit_config.robot_description],
)

# ros2_control using FakeSystem as hardware
ros2_controllers_path = os.path.join(
    get_package_share_directory("lbr_ros2_control"),
    "config",
    "lbr_controllers.yaml",
)
ros2_control_node = Node(
    package="controller_manager",
    executable="ros2_control_node",
    parameters=[moveit_config.robot_description, ros2_controllers_path],
    output="both",
)

joint_state_broadcaster_spawner = Node(
    package="controller_manager",
    executable="spawner",
    arguments=[
        "joint_state_broadcaster",
        "--controller-manager-timeout",
        "300",
        "--controller-manager",
        "/controller_manager",
    ],
)

arm_controller_spawner = Node(
    package="controller_manager",
    executable="spawner",
    arguments=["joint_trajectory_controller", "-c", "/controller_manager"],
)

nodes_to_start = [
    rviz_node,
    static_tf,
    robot_state_publisher,
    run_move_group_node,
    ros2_control_node,
    joint_state_broadcaster_spawner,
    arm_controller_spawner,
]

return nodes_to_start

If someone understands the problem that i have i will be really thankful. The error that appears when i run is: [spawner-6] [INFO] [1712674899.497416360] [spawner_joint_state_broadcaster]: Waiting for ‘/controller_manager’ node to exist

As this might be somewhat unrelated, feel free to open an issue on GitHub, then I can have a look for you.