Repository navigation
issue: Dual-IIWA7 Arm Setup using ROS2 FRI Stack #189
Description
issue: dual_kuka_fri
I want to create a dual-iiwa7 arm setup in ROS2 using the FRI stack. I have already created a custom Xacro file that uses the robot namespace to spawn two robots. However, for simplifying the issue, here I will only spawn one robot in mycustom moveit package named "green_moveit_config", which uses a custom {robot_name}. Our public Github repo is here.
In the Xacro file, I have also renamed link and joint names with a prefix to manage both arms in the same environment.
After creating the custom URDF, I used the MoveIt Setup Assistant to generate the MoveIt configurations.
I followed the instructions here in the documentation to modify the controller and other related files.
Additionally, I have remapped certain topics as follows to connect topics from our MoveIt package to topics from the robot driver (bringup.launch.py):
{robot_name}_display_planned_path->display_planned_path{robot_name}_joint_states->joint_states{robot_name}_monitored_planning_scene->monitored_planning_scene{robot_name}_planning_scene->planning_scene{robot_name}_planning_scene_world->planning_scene_world{robot_name}_robot_description->robot_description{robot_name}_robot_description_semantic->robot_description_semantic
However, I am encountering several issues:
Steps to Reproduce the Error:
- Launch the default
bringup.launch.pywithmodel:=iiwa7andrviz:=falseandmoveit:=falseandsim:=false. - Launch the custom MoveIt
ros2 launch green_moveit_config demo.launch.
Error:
I can visualize the current robot state and also planning to an new state works. However, the execution on the real robot fails.
There is a mismatch in joint names. We observe this when doing a ROS2 service call /kuka_green/controller_manager/list_controllers controller_manager_msgs/srv/ListControllers and get the following response:
response:
controller_manager_msgs.srv.ListControllers_Response(controller=[
controller_manager_msgs.msg.ControllerState(name='force_torque_broadcaster', state='active', type='force_torque_sensor_broadcaster/ForceTorqueSensorBroadcaster', claimed_interfaces=[], required_command_interfaces=[], required_state_interfaces=['estimated_ft_sensor/force.x', 'estimated_ft_sensor/force.y', 'estimated_ft_sensor/force.z', 'estimated_ft_sensor/torque.x', 'estimated_ft_sensor/torque.y', 'estimated_ft_sensor/torque.z'], is_chainable=False, is_chained=False, reference_interfaces=[], chain_connections=[]),
controller_manager_msgs.msg.ControllerState(name='joint_trajectory_controller', state='active', type='joint_trajectory_controller/JointTrajectoryController', claimed_interfaces=['A1/position', 'A2/position', 'A3/position', 'A4/position', 'A5/position', 'A6/position', 'A7/position'], required_command_interfaces=['A1/position', 'A2/position', 'A3/position', 'A4/position', 'A5/position', 'A6/position', 'A7/position'], required_state_interfaces=['A1/position', 'A1/velocity', 'A2/position', 'A2/velocity', 'A3/position', 'A3/velocity', 'A4/position', 'A4/velocity', 'A5/position', 'A5/velocity', 'A6/position', 'A6/velocity', 'A7/position', 'A7/velocity'], is_chainable=False, is_chained=False, reference_interfaces=[], chain_connections=[]),
controller_manager_msgs.msg.ControllerState(name='joint_state_broadcaster', state='active', type='joint_state_broadcaster/JointStateBroadcaster', claimed_interfaces=[], required_command_interfaces=[], required_state_interfaces=['A1/commanded_joint_position', 'A1/commanded_torque', 'A1/effort', 'A1/external_torque', 'A1/ipo_joint_position', 'A1/position', 'A1/velocity', 'A2/commanded_joint_position', 'A2/commanded_torque', 'A2/effort', 'A2/external_torque', 'A2/ipo_joint_position', 'A2/position', 'A2/velocity', 'A3/commanded_joint_position', 'A3/commanded_torque', 'A3/effort', 'A3/external_torque', 'A3/ipo_joint_position', 'A3/position', 'A3/velocity', 'A4/commanded_joint_position', 'A4/commanded_torque', 'A4/effort', 'A4/external_torque', 'A4/ipo_joint_position', 'A4/position', 'A4/velocity', 'A5/commanded_joint_position', 'A5/commanded_torque', 'A5/effort', 'A5/external_torque', 'A5/ipo_joint_position', 'A5/position', 'A5/velocity', 'A6/commanded_joint_position', 'A6/commanded_torque', 'A6/effort', 'A6/external_torque', 'A6/ipo_joint_position', 'A6/position', 'A6/velocity', 'A7/commanded_joint_position', 'A7/commanded_torque', 'A7/effort', 'A7/external_torque', 'A7/ipo_joint_position', 'A7/position', 'A7/velocity', 'auxiliary_sensor/client_command_mode', 'auxiliary_sensor/connection_quality', 'auxiliary_sensor/control_mode', 'auxiliary_sensor/drive_state', 'auxiliary_sensor/operation_mode', 'auxiliary_sensor/overlay_type', 'auxiliary_sensor/safety_state', 'auxiliary_sensor/sample_time', 'auxiliary_sensor/session_state', 'auxiliary_sensor/time_stamp_nano_sec', 'auxiliary_sensor/time_stamp_sec', 'auxiliary_sensor/tracking_performance', 'estimated_ft_sensor/force.x', 'estimated_ft_sensor/force.y', 'estimated_ft_sensor/force.z', 'estimated_ft_sensor/torque.x', 'estimated_ft_sensor/torque.y', 'estimated_ft_sensor/torque.z'], is_chainable=False, is_chained=False, reference_interfaces=[], chain_connections=[]),
controller_manager_msgs.msg.ControllerState(name='lbr_state_broadcaster', state='active', type='lbr_ros2_control/LBRStateBroadcaster', claimed_interfaces=[], required_command_interfaces=[], required_state_interfaces=['A1/commanded_joint_position', 'A1/commanded_torque', 'A1/effort', 'A1/external_torque', 'A1/ipo_joint_position', 'A1/position', 'A1/velocity', 'A2/commanded_joint_position', 'A2/commanded_torque', 'A2/effort', 'A2/external_torque', 'A2/ipo_joint_position', 'A2/position', 'A2/velocity', 'A3/commanded_joint_position', 'A3/commanded_torque', 'A3/effort', 'A3/external_torque', 'A3/ipo_joint_position', 'A3/position', 'A3/velocity', 'A4/commanded_joint_position', 'A4/commanded_torque', 'A4/effort', 'A4/external_torque', 'A4/ipo_joint_position', 'A4/position', 'A4/velocity', 'A5/commanded_joint_position', 'A5/commanded_torque', 'A5/effort', 'A5/external_torque', 'A5/ipo_joint_position', 'A5/position', 'A5/velocity', 'A6/commanded_joint_position', 'A6/commanded_torque', 'A6/effort', 'A6/external_torque', 'A6/ipo_joint_position', 'A6/position', 'A6/velocity', 'A7/commanded_joint_position', 'A7/commanded_torque', 'A7/effort', 'A7/external_torque', 'A7/ipo_joint_position', 'A7/position', 'A7/velocity', 'auxiliary_sensor/client_command_mode', 'auxiliary_sensor/connection_quality', 'auxiliary_sensor/control_mode', 'auxiliary_sensor/drive_state', 'auxiliary_sensor/operation_mode', 'auxiliary_sensor/overlay_type', 'auxiliary_sensor/safety_state', 'auxiliary_sensor/sample_time', 'auxiliary_sensor/session_state', 'auxiliary_sensor/time_stamp_nano_sec', 'auxiliary_sensor/time_stamp_sec', 'auxiliary_sensor/tracking_performance', 'estimated_ft_sensor/force.x', 'estimated_ft_sensor/force.y', 'estimated_ft_sensor/force.z', 'estimated_ft_sensor/torque.x', 'estimated_ft_sensor/torque.y', 'estimated_ft_sensor/torque.z'], is chainable=False, is chained=False, reference_interfaces=[], chain_connections=[])
])
We observe that joints are not renamed here.
Expected Behavior:
The system should correctly recognize and manage the custom link and joint names without any mismatches, and the controller manager should operate as expected based on the modified lbr_controller.yaml file.
Actual Behavior:
Mismatch in link and joint names leading to errors in the system's operation. The controller manager does not reflect the changes expected from the modified configuration.
Importantly, we noticed that, when the link and joint names are not changed in Xacro, and the launch file is launched with default robot_name:=lbr, it works. However, having same link names (ex. link0 for robot1 and robot2) is not a possibiiltiy for dual arm setup.
Therefore, we are currently stuck and desperately need your help!
Activity
mhubii commentedon Jun 9, 2024
thanks for raising this issue @rysabh.
Including two
macrosinto a customxacrocurrently does not yield the expected behavior. As you already correctly mention, this causes ambiguity in the joint / link names and all sorts of down-stream issues when trying to fix this.This should definitely be addressed properly.
For your current issue, this is where
ros2_controlobtains information from:lbr_fri_ros2_stack/lbr_ros2_control/config/lbr_system_interface.xacro
Line 9 in 18db6ff
You'll likely have to adapt the joint names further down the line, here:
lbr_fri_ros2_stack/lbr_ros2_control/config/lbr_system_interface.xacro
Line 118 in 18db6ff
Then change which file to include:
lbr_fri_ros2_stack/lbr_description/urdf/iiwa7/iiwa7_description.xacro
Line 10 in 18db6ff
However, I can see that this is quite the hacky and inconvenient solution.
rysabh commentedon Jun 10, 2024
Hi @mhubii, thank you for your reply.
As you could see in the following Xacro file which we have created and referenced at all the required places. Our Xacro file takes {robot_name} as an argument to rename joints and links. However, we have already tried this to no avail.
mhubii commentedon Jun 10, 2024
okay I see, will have to have a look. Thank you for sharing this detailed repository
mhubii commentedon Jun 11, 2024
hi @rysabh, some more sources for joint name inconsistency:
controller configurations (joint trajectory controller, this is the one you probably most care about, as moveit interacts through this controller):
lbr_fri_ros2_stack/lbr_ros2_control/config/lbr_controllers.yaml
Line 43 in 18db6ff
custom controllers, (the custom controllers in this stack contain hard-coded joint names, you can e.g. simply not spawn them). See:
lbr_fri_ros2_stack/lbr_ros2_control/include/lbr_ros2_control/lbr_state_broadcaster.hpp
Line 51 in 18db6ff
I am currently unsure what the best approach here would be.
samrudhmoode commentedon Jun 12, 2024
Hi @mhubii,
Thank you for your response. We implemented the suggested changes, but we are now encountering the following issue:
which eventually leads to
Despite the ROS 2 service list showing the presence of the controller manager, the bringup file fails to access it. As a result, the socket to the robot doesn't open, and we cannot establish a connection. Could there be another file we should check to resolve this issue?

mhubii commentedon Jun 13, 2024
this might be some namespace issue. Which nodes are shown when you do
Out of curiosity, has your
kuka_greena different mesh color? Happy to add a new description for it here.Will try to clean the urdf / launch files so dual setup in single
xacrobecomes feasiblesamrudhmoode commentedon Jun 14, 2024
Hi @mhubii! To clarify, I'm working with @rysabh on this project.
After adapting the changes you have mentioned, we've updated our launch command as follows:
ros2 launch lbr_bringup bringup.launch.py model:=kuka_green_iiwa7 sim:=false rviz:=true moveit:=true robot_name:=kuka_greenHere's the debug information from the command:
The following are the corresponding
ros2 node listAnd the reason for calling it
kuka_greenis just for the naming convention as we have named each kuka with a color and we haven't changed the mesh color.Below are additional details that might be helpful:

Here's the rqt_graph output:
This is what the MoveIt visualization looks like:

Topic List
Action List
Service List
Xacro File
Note: We have hard coded the link and joint names just to ensure there aren't any mistakes on our end!
Thanks for your support and prompt responses, @mhubii!. I hope this helps clarify the setup. Let me know if there are specific issues or additional information needed!
mhubii commentedon Jun 18, 2024
sorry been little sick these days. Maybe the easiest approach here would be to try and run
ros2_controlstandalone first, i.e. without MoveIt.These are the relevant errors:
consequentially, the
controller_managerdoes not existjust to better understand. In your multi-robot setup, ideally you would have a single
xacrofile:this fails because of name duplicates
samrudhmoode commentedon Jun 25, 2024
Hello @mhubii, I hope that you are feeling well.
We have changed our main xacro file in our custom MoveIt package as follows.
Also, we have changed the
iiwa7_description.xacroto modify link names and joint names usingrobot_namefor each robot.Now we can perform dual arm manipulation using our custom MoveIt package by launching three programs as follows: (1) the custom dual arm MoveIt package to handle motion planning, (2) the driver for robot_1 with moveit:=false, (3) the driver for robot_2 with moveit:=false. We achieve this by providing goals to the follow_joint_trajectory action of both robot drivers.
However, we're encountering a specific issue.
Our custom MoveIt package, successfully plans trajectories; however, execution fails when pressing the "execute" button in Rviz. Interestingly, if I modify the
iiwa7_description.xacroto remove Gazebo and ros2_control elements before running them through the MoveIt Setup Assistant, both planning and execution works. These control elements are shown below:However, if we want to visualize the live robot position in our custom MoveIt package, we need these lines enabled. If we disable these control elements and use follow_joint_trajectory action, we can plan and execute trajectories. However, no live view of real robots during execution in MoveIt.
Therefore, could you please help us in visualizing the live robot position during real robot execution? (which we believe could be possible using those control elements enabled in
iiwa7_description.xacro)mhubii commentedon Jun 25, 2024
thank you for coming back to this interesting topic! Can I execute your modifications here: https://github.com/rysabh/dual_kuka_fri ?
ahnwoohyeon commentedon Aug 19, 2024
I understand that when using 2 robots, we have to name the joint differently. If so, do I just need to change the name of the joint_state macro of joint_limit.yaml, lbr_system_interface?
lbr_fri_ros2_stack/lbr_ros2_control/include/lbr_ros2_control/lbr_state_broadcaster.hpp
Should I change the joint name of this code? If I have to use this method, I will have to apply it separately, so I would appreciate it if you could tell me how to use this method. It is blocked here.
mhubii commentedon Sep 10, 2024
yes so the issue is exactly that joints / links need to be named uniquely
robot name prefix in config files (#189)
controller modifications for name prefix (#189)
added a floating link with name prefix (#189)
added prefix (#189)
added unique name to system interface (#189)
added dual arm demo draft (#189)
garylvov commentedon Jan 8, 2026
Hi @mhubii ,
Thanks for your great work on this repo (and for quickly helping me on the last issue I had)!!!
I have a bimanual KUKA setup with which I want to use this repo. Does this repo work for 2 arms that have different IP addresses? I see the PR is still open but not sure if bimanual got added elsewhere
I'd be happy to help test anything, or to try to add this functionality if you point me in the right direction.
mhubii commentedon Jan 9, 2026
Hi @garylvov, yes, commanding 2 KUKAs:
ros2_control_nodes works just fine.ros2_control_nodemay require you to write a new URDF file.In case 1:
172.31.1.147and172.31.1.149(assuming your computer uses172.31.1.148)port_ids for the UDP connection:port_ids (e.g. 30200 and 30201):lbr_fri_ros2_stack/lbr_description/ros2_control/lbr_system_config.yaml
Line 6 in 9125bbc
robot_name: <lbr>and<lbr>_link_*with a distinct robot name (see below)ros2_control_nodes (e.g. usingrobot_1/robot_2):ros2 launch lbr_bringup hardware.launch.py \ robot_name:=robot_1 \ namespace:=robot_1 \ sys_cfg:=ros2_control/robot_1_system_config.yaml \ ctrl_cfg:=ros2_control/hardware_controllers_robot_1.yamlgarylvov commentedon Jan 9, 2026
Awesome thank you!!! Do you have any recommendations for reassigning the IP address in Sunrise Workbench? We had some issues when we tried tweaking it in the station setup.cat . We will try again today from your screenshot, thank you!
P.S: one of my colleagues has figured out how to add your package to the RoboStack project, we will PR it there soon!
mhubii commentedon Jan 9, 2026
Not sure, to change the IP, you'll have to fully reinstall, not just synchronize: https://lbr-stack.readthedocs.io/en/latest/_images/03_station_setup_installation1.png
woah that would be absolutely incredible! 🚀
garylvov commentedon Jan 9, 2026
Thank you!!!
added dual arm demo draft (#189)