lbr-stack / lbr_fri_ros2_stack

ROS 2 integration for KUKA LBR IIWA 7/14 and Med 7/14
https://lbr-stack.readthedocs.io/en/latest/lbr_fri_ros2_stack/lbr_fri_ros2_stack/doc/lbr_fri_ros2_stack.html
Apache License 2.0
154 stars 48 forks source link

about singularity #216

Open Enzo-let opened 3 weeks ago

Enzo-let commented 3 weeks ago

I appreciate you well. I have a question about running the lbr_demo/lbr_moveit, which is i find that the initial position of the robot is singularity, so i need to chang the initial position in the simulation. What i do is to change the lbr_movit_config with setup_assistant, but when i run the rviz.launch.py, the robot show like below. So i wanna know which files i need to change.I guess the mock.launch, and i change it like this : ` from launch import LaunchDescription from launch.actions import RegisterEventHandler, DeclareLaunchArgument from launch.event_handlers import OnProcessStart from launch.substitutions import LaunchConfiguration from lbr_bringup.description import LBRDescriptionMixin from lbr_bringup.ros2_control import LBRROS2ControlMixin

def generate_launch_description() -> LaunchDescription: ld = LaunchDescription()

# Launch arguments
ld.add_action(LBRDescriptionMixin.arg_model())
ld.add_action(LBRDescriptionMixin.arg_robot_name())
ld.add_action(LBRROS2ControlMixin.arg_ctrl_cfg_pkg())
ld.add_action(LBRROS2ControlMixin.arg_ctrl_cfg())
ld.add_action(LBRROS2ControlMixin.arg_ctrl())

# Define initial positions for KUKA robot joints
ld.add_action(DeclareLaunchArgument(
    'joint_a1',
    default_value='0.0',
    description='Initial position for joint a1'
))
ld.add_action(DeclareLaunchArgument(
    'joint_a2',
    default_value='-0.7854',
    description='Initial position for joint a2'
))
ld.add_action(DeclareLaunchArgument(
    'joint_a3',
    default_value='0.0',
    description='Initial position for joint a3'
))
ld.add_action(DeclareLaunchArgument(
    'joint_a4',
    default_value='1.3962',
    description='Initial position for joint a4'
))
ld.add_action(DeclareLaunchArgument(
    'joint_a5',
    default_value='0.0',
    description='Initial position for joint a5'
))
ld.add_action(DeclareLaunchArgument(
    'joint_a6',
    default_value='0.6109',
    description='Initial position for joint a6'
))
ld.add_action(DeclareLaunchArgument(
    'joint_a7',
    default_value='0.0',
    description='Initial position for joint a7'
))

# Robot description
robot_description = LBRDescriptionMixin.param_robot_description(mode="mock")

# Robot state publisher
robot_state_publisher = LBRROS2ControlMixin.node_robot_state_publisher(
    robot_description=robot_description, use_sim_time=False
)
ld.add_action(robot_state_publisher)

# ROS 2 control node
ros2_control_node = LBRROS2ControlMixin.node_ros2_control(use_sim_time=False)
ld.add_action(ros2_control_node)

# Joint state broadcaster and controller on ROS 2 control node start
joint_state_broadcaster = LBRROS2ControlMixin.node_controller_spawner(
    controller="joint_state_broadcaster"
)
controller = LBRROS2ControlMixin.node_controller_spawner(
    controller=LaunchConfiguration("ctrl")
)

# Event handler to start joint state broadcaster and controller
controller_event_handler = RegisterEventHandler(
    OnProcessStart(
        target_action=ros2_control_node,
        on_start=[
            joint_state_broadcaster,
            controller,
        ],
    )
)
ld.add_action(controller_event_handler)

return ld

` 3dd32838ccc21afa00de33d479296de

mhubii commented 3 weeks ago

hi @Enzo-let, this error shows due to a missing transform. Can you try to add a static transform publisher a la

from launch.substitutions import LaunchConfiguration, PathJoinSubstitution

# static transform world -> robot_name/world
ld.add_action(
    LBRDescriptionMixin.node_static_tf(
        tf=[0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
        parent="world",
        child=PathJoinSubstitution([LaunchConfiguration("robot_name"), "world"]),
    )
)

This should fix your particular issue. However, from what I can see reading through ros2_control (https://github.com/ros-controls/gazebo_ros2_control/pull/100), to configure an initial position, the URDF needs to be modified, i.e. here

https://github.com/lbr-stack/lbr_fri_ros2_stack/blob/cca24ae8a17d5644a414380d7ac420d37aec6686/lbr_ros2_control/config/lbr_system_interface.xacro#L109

In case you wish to create a pull request to modify this behavior, may I kindly ask you to target the rolling branch? Note that in the rolling branch frame naming changed from robot_name/link_i -> robot_name_link_i

Enzo-let commented 3 weeks ago

Sorry to response you so lately. There are so many classes recently so that i don't have so much time in the lab. I have started to modify it, but it still has so many problems. I will try to modify it again, if it still dosen't work, i hope i can get some suggestions from you. And about your PR suggestions, i got it. Thanks so much.

Enzo-let commented 3 weeks ago

I have tried my best to chang it. But it still didn't work. The error shows like below [ERROR] [launch]: Caught exception in launch (see debug for traceback): executed command failed. Command: /opt/ros/humble/bin/xacro /home/let/lbr-stack/install/lbr_description/share/lbr_description/urdf/iiwa14/iiwa14.xacro robot_name:=lbr port_id:=30200 mode:=mock Captured stderr output: error: Undefined parameters [initial_positions] when instantiating macro: lbr_system_interface (/home/let/lbr-stack/install/lbr_ros2_control/share/lbr_ros2_control/config/lbr_system_interface.xacro) instantiated from: iiwa14 (/home/let/lbr-stack/install/lbr_description/share/lbr_description/urdf/iiwa14/iiwa14_description.xacro) in file: /home/let/lbr-stack/install/lbr_description/share/lbr_description/urdf/iiwa14/iiwa14.xacro I will show you the file what i have changed it and highlight the code. The first file is mock.launch.py `from launch import LaunchDescription from launch.actions import RegisterEventHandler, DeclareLaunchArgument from launch.event_handlers import OnProcessStart from launch.substitutions import LaunchConfiguration, PathJoinSubstitution from lbr_bringup.description import LBRDescriptionMixin from lbr_bringup.ros2_control import LBRROS2ControlMixin

def generate_launch_description() -> LaunchDescription: ld = LaunchDescription()

# 添加初始位置参数
**ld.add_action(
    DeclareLaunchArgument(
        'initial_positions',
        default_value='{"A1": 0.0, "A2":  -0.7854, "A3": 0.0, "A4": 1.3962, "A5": 0.0, "A6": -0.9599, "A7": 0.0}',
        description='Initial joint positions as a JSON string.'
    )
)**

# 启动参数
ld.add_action(LBRDescriptionMixin.arg_model())
ld.add_action(LBRDescriptionMixin.arg_robot_name())
ld.add_action(LBRROS2ControlMixin.arg_ctrl_cfg_pkg())
ld.add_action(LBRROS2ControlMixin.arg_ctrl_cfg())
ld.add_action(LBRROS2ControlMixin.arg_ctrl())

# static transform world -> robot_name/world
ld.add_action(
    LBRDescriptionMixin.node_static_tf(
        tf=[0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
        parent="world",
        child=PathJoinSubstitution([LaunchConfiguration("robot_name"), "world"]),
    )
)

# robot description
robot_description = LBRDescriptionMixin.param_robot_description(mode="mock")

# robot state publisher
robot_state_publisher = LBRROS2ControlMixin.node_robot_state_publisher(
    robot_description=robot_description, use_sim_time=False
)
ld.add_action(robot_state_publisher)

# ros2 control node
ros2_control_node = **LBRROS2ControlMixin.node_ros2_control(use_sim_time=False, initial_positions=LaunchConfiguration('initial_positions'))**
ld.add_action(ros2_control_node)

# joint state broadcaster and controller on ros2 control node start
joint_state_broadcaster = LBRROS2ControlMixin.node_controller_spawner(
    controller="joint_state_broadcaster"
)
controller = LBRROS2ControlMixin.node_controller_spawner(
    controller=LaunchConfiguration("ctrl")
)

controller_event_handler = RegisterEventHandler(
    OnProcessStart(
        target_action=ros2_control_node,
        on_start=[
            joint_state_broadcaster,
            controller,
        ],
    )
)
ld.add_action(controller_event_handler)
return ld

The second file is lbr_systme_interface.xacro <?xml version="1.0"?>

mock_components/GenericSystem ign_ros2_control/IgnitionSystem lbr_ros2_control::SystemInterface ${system_parameters['hardware']['fri_client_sdk']['major_version']} ${system_parameters['hardware']['fri_client_sdk']['minor_version']} ${system_parameters['hardware']['client_command_mode']} ${system_parameters['hardware']['port_id']} ${system_parameters['hardware']['remote_host']} ${system_parameters['hardware']['rt_prio']} ${system_parameters['hardware']['pid_p']} ${system_parameters['hardware']['pid_i']} ${system_parameters['hardware']['pid_d']} ${system_parameters['hardware']['pid_i_max']} ${system_parameters['hardware']['pid_i_min']} ${system_parameters['hardware']['pid_antiwindup']} ${system_parameters['hardware']['command_guard_variant']} ${system_parameters['hardware']['external_torque_cutoff_frequency']} ${system_parameters['hardware']['measured_torque_cutoff_frequency']} ${system_parameters['hardware']['open_loop']} ${system_parameters['estimated_ft_sensor']['chain_root']} ${system_parameters['estimated_ft_sensor']['chain_tip']} ${system_parameters['estimated_ft_sensor']['damping']} ${system_parameters['estimated_ft_sensor']['force_x_th']} ${system_parameters['estimated_ft_sensor']['force_y_th']} ${system_parameters['estimated_ft_sensor']['force_z_th']} ${system_parameters['estimated_ft_sensor']['torque_x_th']} ${system_parameters['estimated_ft_sensor']['torque_y_th']} ${system_parameters['estimated_ft_sensor']['torque_z_th']} ** ${min_position} ${max_position} -${max_torque} ${max_torque} **${initial_position}** ${min_position} ${max_position} ${max_velocity} ${max_torque} ** ** ** ** ** ** **

The third file is lbr_bringup/lbr_bringup/ros2_control.py from typing import Dict, Optional, Union

from launch.actions import DeclareLaunchArgument from launch.substitutions import LaunchConfiguration, PathJoinSubstitution from launch_ros.actions import Node from launch_ros.substitutions import FindPackageShare

class LBRROS2ControlMixin: @staticmethod def arg_ctrl_cfg_pkg() -> DeclareLaunchArgument: return DeclareLaunchArgument( name="ctrl_cfg_pkg", default_value="lbr_ros2_control", description="Controller configuration package. The package containing the ctrl_cfg.", )

@staticmethod
def arg_ctrl_cfg() -> DeclareLaunchArgument:
    return DeclareLaunchArgument(
        name="ctrl_cfg",
        default_value="config/lbr_controllers.yaml",
        description="Relative path from ctrl_cfg_pkg to the controllers.",
    )

@staticmethod
def arg_ctrl() -> DeclareLaunchArgument:
    return DeclareLaunchArgument(
        name="ctrl",
        default_value="joint_trajectory_controller",
        description="Desired default controller. One of specified in ctrl_cfg.",
        choices=[
            "joint_trajectory_controller",
            "forward_position_controller",
            "lbr_joint_position_command_controller",
            "lbr_torque_command_controller",
            "lbr_wrench_command_controller",
        ],
    )

@staticmethod
def arg_use_sim_time() -> DeclareLaunchArgument:
    return DeclareLaunchArgument(
        name="use_sim_time",
        default_value="false",
        description="Use simulation (Gazebo) clock if true.",
    )

@staticmethod
**def arg_initial_positions() -> DeclareLaunchArgument:
    return DeclareLaunchArgument(
        name="initial_positions",
        default_value='{"A1": 0.0, "A2": 0.0, "A3": 0.0, "A4": 0.0, "A5": 0.0, "A6": 0.0, "A7": 0.0}',
        description="Initial joint positions as a JSON string."
    )**

@staticmethod
def node_ros2_control(
    robot_name: Optional[Union[LaunchConfiguration, str]] = LaunchConfiguration(
        "robot_name", default="lbr"
    ),
    use_sim_time: Optional[Union[LaunchConfiguration, bool]] = LaunchConfiguration(
        "use_sim_time", default="false"
    ),
    **initial_positions: Optional[Union[LaunchConfiguration, str]] = LaunchConfiguration(
        "initial_positions"**
    ),
    **kwargs,
) -> Node:
    return Node(
        package="controller_manager",
        executable="ros2_control_node",
        parameters=[
            {"use_sim_time": use_sim_time},
            PathJoinSubstitution(
                [
                    FindPackageShare(
                        LaunchConfiguration(
                            "ctrl_cfg_pkg", default="lbr_ros2_control"
                        )
                    ),
                    LaunchConfiguration(
                        "ctrl_cfg", default="config/lbr_controllers.yaml"
                    ),
                ]
            ),
            **{"initial_positions": initial_positions},**
        ],
        namespace=robot_name,
        remappings=[
            ("~/robot_description", "robot_description"),
        ],
        **kwargs,
    )

@staticmethod
def node_controller_spawner(
    robot_name: Optional[Union[LaunchConfiguration, str]] = LaunchConfiguration(
        "robot_name", default="lbr"
    ),
    controller: Optional[Union[LaunchConfiguration, str]] = LaunchConfiguration(
        "ctrl"
    ),
    **kwargs,
) -> Node:
    return Node(
        package="controller_manager",
        executable="spawner",
        output="screen",
        arguments=[
            controller,
            "--controller-manager",
            "controller_manager",
        ],
        namespace=robot_name,
        **kwargs,
    )

@staticmethod
def node_robot_state_publisher(
    robot_description: Dict[str, str],
    robot_name: Optional[LaunchConfiguration] = LaunchConfiguration(
        "robot_name", default="lbr"
    ),
    use_sim_time: Optional[Union[LaunchConfiguration, bool]] = LaunchConfiguration(
        "use_sim_time", default="false"
    ),
    **kwargs,
) -> Node:
    return Node(
        package="robot_state_publisher",
        executable="robot_state_publisher",
        output="screen",
        parameters=[
            robot_description,
            {"use_sim_time": use_sim_time},
            {
                "frame_prefix": PathJoinSubstitution([robot_name, ""])
            },
        ],
        namespace=robot_name,
        **kwargs,
    )

` What other files do i need to chang. I don't really know the logic of the code well, so I hope i can get your suggestions. Thank you very much, it really confuse me. Thanks again

mhubii commented 3 weeks ago

So basically initial_position is not a node parameter. initial_position is parsed from the urdf.

Specifying initial_position as parameter will not forward it to the urdf.

urdf -> robot_state_publisher -> ros2_control

https://github.com/lbr-stack/lbr_fri_ros2_stack/blob/218d723f817c61314988833611effddc5333ca6c/lbr_bringup/lbr_bringup/ros2_control.py#L139

you can e.g. change here what arguments are forwarded to the urdf

https://github.com/lbr-stack/lbr_fri_ros2_stack/blob/218d723f817c61314988833611effddc5333ca6c/lbr_bringup/lbr_bringup/description.py#L39

Enzo-let commented 2 weeks ago

Ok, I have changed it as your suggestions, but there are still same problems, Can you help me to say if the code of lbr_system_interface.xacro is correct. `<?xml version="1.0"?>

mock_components/GenericSystem ign_ros2_control/IgnitionSystem lbr_ros2_control::SystemInterface ${system_parameters['hardware']['fri_client_sdk']['major_version']} ${system_parameters['hardware']['fri_client_sdk']['minor_version']} ${system_parameters['hardware']['client_command_mode']} ${system_parameters['hardware']['port_id']} ${system_parameters['hardware']['remote_host']} ${system_parameters['hardware']['rt_prio']} ${system_parameters['hardware']['pid_p']} ${system_parameters['hardware']['pid_i']} ${system_parameters['hardware']['pid_d']} ${system_parameters['hardware']['pid_i_max']} ${system_parameters['hardware']['pid_i_min']} ${system_parameters['hardware']['pid_antiwindup']} ${system_parameters['hardware']['command_guard_variant']} ${system_parameters['hardware']['external_torque_cutoff_frequency']} ${system_parameters['hardware']['measured_torque_cutoff_frequency']} ${system_parameters['hardware']['open_loop']} ${system_parameters['estimated_ft_sensor']['chain_root']} ${system_parameters['estimated_ft_sensor']['chain_tip']} ${system_parameters['estimated_ft_sensor']['damping']} ${system_parameters['estimated_ft_sensor']['force_x_th']} ${system_parameters['estimated_ft_sensor']['force_y_th']} ${system_parameters['estimated_ft_sensor']['force_z_th']} ${system_parameters['estimated_ft_sensor']['torque_x_th']} ${system_parameters['estimated_ft_sensor']['torque_y_th']} ${system_parameters['estimated_ft_sensor']['torque_z_th']} ${min_position} ${max_position} -${max_torque} ${max_torque} ${initial_positions['A1']} ${initial_positions['A2']} ${initial_positions['A3']} ${initial_positions['A4']} ${initial_positions['A5']} ${initial_positions['A6']} ${initial_positions['A7']} ${min_position} ${max_position} ${max_velocity} ${max_torque} `
Enzo-let commented 2 weeks ago

the next is description.py ` from typing import Dict, List, Optional, Union from launch.actions import DeclareLaunchArgument from launch.substitutions import ( Command, FindExecutable, LaunchConfiguration, PathJoinSubstitution, ) from launch_ros.actions import Node from launch_ros.substitutions import FindPackageShare

class LBRDescriptionMixin: @staticmethod def param_robot_description( model: Optional[Union[LaunchConfiguration, str]] = LaunchConfiguration( "model", default="iiwa7" ), robot_name: Optional[Union[LaunchConfiguration, str]] = LaunchConfiguration( "robot_name", default="lbr" ), mode: Optional[Union[LaunchConfiguration, bool]] = LaunchConfiguration( "mode", default="mock" ), initial_positions: Optional[Union[LaunchConfiguration, str]] = LaunchConfiguration( "initial_positions", default="initial_positions.yaml" ), system_config_path: Optional[ Union[LaunchConfiguration, str] ] = PathJoinSubstitution( [ FindPackageShare( LaunchConfiguration("sys_cfg_pkg", default="lbr_description") ), LaunchConfiguration( "sys_cfg", default="ros2_control/lbr_system_config.yaml" ), ] ), ) -> Dict[str, str]: robot_description = { "robot_description": Command( [ FindExecutable(name="xacro"), " ", PathJoinSubstitution( [ FindPackageShare("lbr_description"), "urdf", model, model, ] ), ".xacro", " robot_name:=", robot_name, " mode:=", mode, " system_config_path:=", system_config_path, " initial_positions:=", initial_positions, # Corrected typo here ] ) } return robot_description

@staticmethod
def arg_model(default_value: str = "iiwa7") -> DeclareLaunchArgument:
    return DeclareLaunchArgument(
        name="model",
        default_value=default_value,
        description="The LBR model in use.",
        choices=["iiwa7", "iiwa14", "med7", "med14"],
    )

@staticmethod
def arg_robot_name(default_value: str = "lbr") -> DeclareLaunchArgument:
    return DeclareLaunchArgument(
        name="robot_name",
        default_value=default_value,
        description="The robot's name.",
    )

@staticmethod
def arg_mode(default_value: str = "mock") -> DeclareLaunchArgument:
    return DeclareLaunchArgument(
        name="mode",
        default_value=default_value,
        description="The mode to launch in.",
        choices=[
            "mock",
            "hardware",
            "gazebo",
        ],
    )

@staticmethod
def arg_initial_positions(default_value: str = "initial_positions.yaml") -> DeclareLaunchArgument:
    return DeclareLaunchArgument(
        name="initial_positions",
        default_value=default_value,
        description="Path to the initial positions YAML file.",
    )

@staticmethod
def param_robot_name() -> Dict[str, LaunchConfiguration]:
    return {"robot_name": LaunchConfiguration("robot_name", default="lbr")}

@staticmethod
def param_mode() -> Dict[str, LaunchConfiguration]:
    return {"mode": LaunchConfiguration("mode", default="mock")}

@staticmethod
def param_initial_positions() -> Dict[str, LaunchConfiguration]:
    return {"initial_positions": LaunchConfiguration("initial_positions", default="initial_positions.yaml")}  # Corrected typo here

@staticmethod
def node_static_tf(
    tf: List[float] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
    parent: Optional[Union[LaunchConfiguration, str]] = None,
    child: Optional[Union[LaunchConfiguration, str]] = None,
    **kwargs,
) -> Node:
    if len(tf) != 6:
        raise ValueError("tf must be a list of 6 floats.")
    label = ["--x", "--y", "--z", "--roll", "--pitch", "--yaw"]
    tf = [str(x) for x in tf]
    return Node(
        package="tf2_ros",
        executable="static_transform_publisher",
        output="screen",
        arguments=[item for pair in zip(label, tf) for item in pair]
        + [
            "--frame-id",
            parent,
            "--child-frame-id",
            child,
        ],
        **kwargs,
    )

`

mhubii commented 2 weeks ago

sorry @Enzo-let, could I kindly ask you to format your code snippets? This can be done via tripple ``` followed by the language specification and ending with tripple ```. Language specification can e.g. be python or bash or XML etc., i.e. ```python. This would help a lot

Refer e.g. https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#quoting-code

Enzo-let commented 2 weeks ago

It's ok. The lbr_system_interface.xacro is like below

<xacro:macro name="lbr_system_interface"
params="mode joint_limits system_parameters_path initial_positions">```

<xacro:property name="system_parameters"
value="${xacro.load_yaml(system_parameters_path)}" />
```<xacro:property name="initial_positions" value="${xacro.load_yaml(initial_positions, lazy_eval='false')}" />```

    <ros2_control name="lbr_system_interface" type="system">
        <!-- load plugin depending on mode -->
        <xacro:if value="${mode == 'mock'}">
            <hardware>
                <plugin>mock_components/GenericSystem</plugin>
            </hardware>
        </xacro:if>
        <xacro:if value="${mode == 'gazebo'}">
            <hardware>
                <plugin>ign_ros2_control/IgnitionSystem</plugin>
            </hardware>
        </xacro:if>
        <xacro:if value="${mode == 'hardware'}">
            <hardware>
                <plugin>lbr_ros2_control::SystemInterface</plugin>
                <param name="fri_client_sdk_major_version">${system_parameters['hardware']['fri_client_sdk']['major_version']}</param>
                <param name="fri_client_sdk_minor_version">${system_parameters['hardware']['fri_client_sdk']['minor_version']}</param>
                <param name="client_command_mode">${system_parameters['hardware']['client_command_mode']}</param>
                <param name="port_id">${system_parameters['hardware']['port_id']}</param>
                <param name="remote_host">${system_parameters['hardware']['remote_host']}</param>
                <param name="rt_prio">${system_parameters['hardware']['rt_prio']}</param>
                <param name="pid_p">${system_parameters['hardware']['pid_p']}</param>
                <param name="pid_i">${system_parameters['hardware']['pid_i']}</param>
                <param name="pid_d">${system_parameters['hardware']['pid_d']}</param>
                <param name="pid_i_max">${system_parameters['hardware']['pid_i_max']}</param>
                <param name="pid_i_min">${system_parameters['hardware']['pid_i_min']}</param>
                <param name="pid_antiwindup">${system_parameters['hardware']['pid_antiwindup']}</param>
                <param name="command_guard_variant">${system_parameters['hardware']['command_guard_variant']}</param>
                <param name="external_torque_cutoff_frequency">${system_parameters['hardware']['external_torque_cutoff_frequency']}</param>
                <param name="measured_torque_cutoff_frequency">${system_parameters['hardware']['measured_torque_cutoff_frequency']}</param>
                <param name="open_loop">${system_parameters['hardware']['open_loop']}</param>
            </hardware>
        </xacro:if>

        <!-- define lbr specific state interfaces as sensor, see
        https://github.com/ros-controls/roadmap/blob/master/design_drafts/components_architecture_and_urdf_examples.md -->
        <xacro:if value="${mode == 'hardware'}">
            <sensor name="auxiliary_sensor">
                <!-- see KUKA::FRI::LBRState -->
                <state_interface name="sample_time" />
                <state_interface name="session_state" />
                <state_interface name="connection_quality" />
                <state_interface name="safety_state" />
                <state_interface name="operation_mode" />
                <state_interface name="drive_state" />
                <state_interface name="client_command_mode" />
                <state_interface name="overlay_type" />
                <state_interface name="control_mode" />
                <state_interface name="time_stamp_sec" />
                <state_interface name="time_stamp_nano_sec" />
                <state_interface name="tracking_performance" />
            </sensor>

            <sensor
                name="estimated_ft_sensor">
                <param name="chain_root">${system_parameters['estimated_ft_sensor']['chain_root']}</param>
                <param name="chain_tip">${system_parameters['estimated_ft_sensor']['chain_tip']}</param>
                <param name="damping">${system_parameters['estimated_ft_sensor']['damping']}</param>
                <param name="force_x_th">${system_parameters['estimated_ft_sensor']['force_x_th']}</param>
                <param name="force_y_th">${system_parameters['estimated_ft_sensor']['force_y_th']}</param>
                <param name="force_z_th">${system_parameters['estimated_ft_sensor']['force_z_th']}</param>
                <param name="torque_x_th">${system_parameters['estimated_ft_sensor']['torque_x_th']}</param>
                <param name="torque_y_th">${system_parameters['estimated_ft_sensor']['torque_y_th']}</param>
                <param name="torque_z_th">${system_parameters['estimated_ft_sensor']['torque_z_th']}</param>
                <state_interface name="force.x" />
                <state_interface name="force.y" />
                <state_interface name="force.z" />
                <state_interface name="torque.x" />
                <state_interface name="torque.y" />
                <state_interface name="torque.z" />
            </sensor>

            <!-- FRI Cartesian impedance control mode -->
            <gpio
                name="wrench">
                <command_interface name="force.x" />
                <command_interface name="force.y" />
                <command_interface name="force.z" />
                <command_interface name="torque.x" />
                <command_interface name="torque.y" />
                <command_interface name="torque.z" />
            </gpio>
        </xacro:if>

        <!-- define joints and command/state interfaces for each joint -->
        <xacro:macro name="joint_interface"
            params="name min_position max_position max_velocity max_torque mode">
            <joint name="${name}">
                <command_interface name="position">
                    <param name="min">${min_position}</param>
                    <param name="max">${max_position}</param>
                </command_interface>
                <!-- only single command interface, refer
                https://github.com/ros-controls/gz_ros2_control/issues/182 -->
                <xacro:unless value="${mode == 'gazebo'}">
                    <command_interface name="effort">
                        <param name="min">-${max_torque}</param>
                        <param name="max"> ${max_torque}</param>
                    </command_interface>
                </xacro:unless>
             ```<state_interface name="position" />
                     <param name="initial_value">${initial_positions['A1']}</param>
                     <param name="initial_value">${initial_positions['A2']}</param>
                     <param name="initial_value">${initial_positions['A3']}</param>
                     <param name="initial_value">${initial_positions['A4']}</param>
                     <param name="initial_value">${initial_positions['A5']}</param>
                     <param name="initial_value">${initial_positions['A6']}</param>
                     <param name="initial_value">${initial_positions['A7']}</param>```
                <state_interface name="velocity" />
                <state_interface name="effort" />
                <xacro:if value="${mode == 'hardware'}">
                    <param name="min_position">${min_position}</param>
                    <param name="max_position">${max_position}</param>
                    <param name="max_velocity">${max_velocity}</param>
                    <param name="max_torque">${max_torque}</param>
                    <xacro:if value="${system_parameters['hardware']['fri_client_sdk']['major_version'] == 1}">
                        <state_interface name="commanded_joint_position" />
                    </xacro:if>
                    <state_interface name="commanded_torque" />
                    <state_interface name="external_torque" />
                    <state_interface name="ipo_joint_position" />
                </xacro:if>
            </joint>
        </xacro:macro>

        <xacro:joint_interface name="A1"
            min_position="${joint_limits['A1']['lower'] * PI / 180}"
            max_position="${joint_limits['A1']['upper'] * PI / 180}"
            max_velocity="${joint_limits['A1']['velocity'] * PI / 180}"
            max_torque="${joint_limits['A1']['effort']}"
        ``` initial_positions="${initial_positions['A1']}"```
            mode="${mode}" />
        <xacro:joint_interface name="A2"
            min_position="${joint_limits['A2']['lower'] * PI / 180}"
            max_position="${joint_limits['A2']['upper'] * PI / 180}"
            max_velocity="${joint_limits['A2']['velocity'] * PI / 180}"
            max_torque="${joint_limits['A2']['effort']}"
         ```initial_positions="${initial_positions['A2']}"```
            mode="${mode}" />
        <xacro:joint_interface name="A3"
            min_position="${joint_limits['A3']['lower'] * PI / 180}"
            max_position="${joint_limits['A3']['upper'] * PI / 180}"
            max_velocity="${joint_limits['A3']['velocity'] * PI / 180}"
            max_torque="${joint_limits['A3']['effort']}"
         ```initial_positions="${initial_positions['A3']}"```
            mode="${mode}" />
        <xacro:joint_interface name="A4"
            min_position="${joint_limits['A4']['lower'] * PI / 180}"
            max_position="${joint_limits['A4']['upper'] * PI / 180}"
            max_velocity="${joint_limits['A4']['velocity'] * PI / 180}"
            max_torque="${joint_limits['A4']['effort']}"
         ```initial_positions="${initial_positions['A4']}"```
            mode="${mode}" />
        <xacro:joint_interface name="A5"
            min_position="${joint_limits['A5']['lower'] * PI / 180}"
            max_position="${joint_limits['A5']['upper'] * PI / 180}"
            max_velocity="${joint_limits['A5']['velocity'] * PI / 180}"
            max_torque="${joint_limits['A5']['effort']}"
         ```initial_positions="${initial_positions['A5']}"```
            mode="${mode}" />
        <xacro:joint_interface name="A6"
            min_position="${joint_limits['A6']['lower'] * PI / 180}"
            max_position="${joint_limits['A6']['upper'] * PI / 180}"
            max_velocity="${joint_limits['A6']['velocity'] * PI / 180}"
            max_torque="${joint_limits['A6']['effort']}"
         ```initial_positions="${initial_positions['A6']}"```
            mode="${mode}" />
        <xacro:joint_interface name="A7"
            min_position="${joint_limits['A7']['lower'] * PI / 180}"
            max_position="${joint_limits['A7']['upper'] * PI / 180}"
            max_velocity="${joint_limits['A7']['velocity'] * PI / 180}"
            max_torque="${joint_limits['A7']['effort']}"
         ```initial_positions="${initial_positions['A7']}"```
            mode="${mode}" />
    </ros2_control>
</xacro:macro>

The description.py is like below

from typing import Dict, List, Optional, Union
from launch.actions import DeclareLaunchArgument
from launch.substitutions import (
Command,
FindExecutable,
LaunchConfiguration,
PathJoinSubstitution,
)
from launch_ros.actions import Node
from launch_ros.substitutions import FindPackageShare

class LBRDescriptionMixin:
@staticmethod
def param_robot_description(
model: Optional[Union[LaunchConfiguration, str]] = LaunchConfiguration(
"model", default="iiwa7"
),
robot_name: Optional[Union[LaunchConfiguration, str]] = LaunchConfiguration(
"robot_name", default="lbr"
),
mode: Optional[Union[LaunchConfiguration, bool]] = LaunchConfiguration(
"mode", default="mock"
),
```initial_positions: Optional[Union[LaunchConfiguration, str]] = LaunchConfiguration(
"initial_positions", default="initial_positions.yaml"```
),
system_config_path: Optional[
Union[LaunchConfiguration, str]
] = PathJoinSubstitution(
[
FindPackageShare(
LaunchConfiguration("sys_cfg_pkg", default="lbr_description")
),
LaunchConfiguration(
"sys_cfg", default="ros2_control/lbr_system_config.yaml"
),
]
),
) -> Dict[str, str]:
robot_description = {
"robot_description": Command(
[
FindExecutable(name="xacro"),
" ",
PathJoinSubstitution(
[
FindPackageShare("lbr_description"),
"urdf",
model,
model,
]
),
".xacro",
" robot_name:=",
robot_name,
" mode:=",
mode,
" system_config_path:=",
system_config_path,
```" initial_positions:=",
initial_positions, ```
]
)
}
return robot_description

@staticmethod
def arg_model(default_value: str = "iiwa7") -> DeclareLaunchArgument:
    return DeclareLaunchArgument(
        name="model",
        default_value=default_value,
        description="The LBR model in use.",
        choices=["iiwa7", "iiwa14", "med7", "med14"],
    )

@staticmethod
def arg_robot_name(default_value: str = "lbr") -> DeclareLaunchArgument:
    return DeclareLaunchArgument(
        name="robot_name",
        default_value=default_value,
        description="The robot's name.",
    )

@staticmethod
def arg_mode(default_value: str = "mock") -> DeclareLaunchArgument:
    return DeclareLaunchArgument(
        name="mode",
        default_value=default_value,
        description="The mode to launch in.",
        choices=[
            "mock",
            "hardware",
            "gazebo",
        ],
    )

@staticmethod
```def arg_initial_positions(default_value: str = "initial_positions.yaml") -> DeclareLaunchArgument:
    return DeclareLaunchArgument(
        name="initial_positions",
        default_value=default_value,
        description="Path to the initial positions YAML file.",
    )```

@staticmethod
def param_robot_name() -> Dict[str, LaunchConfiguration]:
    return {"robot_name": LaunchConfiguration("robot_name", default="lbr")}

@staticmethod
def param_mode() -> Dict[str, LaunchConfiguration]:
    return {"mode": LaunchConfiguration("mode", default="mock")}

@staticmethod
```def param_initial_positions() -> Dict[str, LaunchConfiguration]:
    return {"initial_positions": LaunchConfiguration("initial_positions", default="initial_positions.yaml")}```  

@staticmethod
def node_static_tf(
    tf: List[float] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
    parent: Optional[Union[LaunchConfiguration, str]] = None,
    child: Optional[Union[LaunchConfiguration, str]] = None,
    **kwargs,
) -> Node:
    if len(tf) != 6:
        raise ValueError("tf must be a list of 6 floats.")
    label = ["--x", "--y", "--z", "--roll", "--pitch", "--yaw"]
    tf = [str(x) for x in tf]
    return Node(
        package="tf2_ros",
        executable="static_transform_publisher",
        output="screen",
        arguments=[item for pair in zip(label, tf) for item in pair]
        + [
            "--frame-id",
            parent,
            "--child-frame-id",
            child,
        ],
        **kwargs,
    )