I want to implement a 4 wheel robot with car like steering(having 2 front steerable wheels and 2 back drive wheels) in ros2 using ros2_control. Can you suggest me which ros2 controller should I use(with example)? Is there any example I could refer to? The ros2_control documentation is not detailed enough and their example are mostly differential drive based. Edit: Distro: ```ros2-humble``` # Directory Structure # ``` race_it_ws /src /race_it /description /race_car.xacro /ros2_ack.xacro /inertial.xacro /config /controller /my_controllers.yaml /launch /rsp.launch.py /hardware ## NOTE: I have just copied this folder from "ros2_control_demos/example_11" AS IT IS. ``` Complete source code is provided below. # Description # ## race_car.xacro(main urdf file) ## ``` <?xml version="1.0"?> <robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="simple_car"> <xacro:include filename="inertial.xacro"/> <!-- <xacro:include filename="gazebo_control_dd.xacro"/> --> <!-- <xacro:include filename="gazebo_control_steer.xacro"/> --> <xacro:property name="wheel_width" value="0.04"/> <xacro:property name="wheel_radius" value="0.1"/> <!-- Wheel macro --> <xacro:macro name="wheel" params="name"> <link name="${name}"> <visual> <geometry> <cylinder length="${wheel_width}" radius="${wheel_radius}" /> </geometry> <material name="blue"/> </visual> <collision> <geometry> <cylinder length="${wheel_width}" radius="${wheel_radius}" /> </geometry> </collision> <xacro:inertial_cylinder mass="0.1" length="${wheel_width}" radius="${wheel_radius}"/> </link> <gazebo reference="${name}"> <material>Gazebo/Blue</material> </gazebo> </xacro:macro> <!-- Base Link --> <link name="base_link"> </link> <!-- Chassis Link --> <link name="chassis_link"> <visual> <geometry> <box size="0.5 1.0 0.2"/> </geometry> <origin xyz="0 0 0" rpy="0 0 0"/> <material name="blue"> <color rgba="0 0 1 1"/> </material> </visual> <xacro:inertial_box mass="0.5" x="0.5" y="1.0" z="0.15"/> </link> <gazebo reference="chassis_link"> <material>Gazebo/Orange</material> </gazebo> <!-- Base Chassis Joint --> <joint name="base_chassis_joint" type="fixed"> <parent link="base_link"/> <child link="chassis_link"/> <origin xyz="0 0 0.2" rpy="0 0 0 "/> </joint> <!-- Front Left Wheel --> <xacro:wheel name="front_left_link"/> <joint name="front_left_joint" type="continuous"> <parent link="chassis_link"/> <child link="front_left_link"/> <origin xyz="-0.3 0.5 -0.1" rpy="0 ${pi/2} 0"/> <axis xyz="0 0 1"/> </joint> <gazebo reference="front_left_link"> <material>Gazebo/Red</material> <mu1 value="0.001"/> <mu2 value="0.001"/> </gazebo> <!-- Front Right Wheel --> <xacro:wheel name="front_right_link"/> <joint name="front_right_joint" type="continuous"> <parent link="chassis_link"/> <child link="front_right_link"/> <origin xyz="0.3 0.5 -0.1" rpy="0 ${pi/2} 0"/> <axis xyz="0 0 1"/> </joint> <gazebo reference="front_right_link"> <material>Gazebo/Red</material> <mu1 value="0.001"/> <mu2 value="0.001"/> </gazebo> <!-- Rear Left Wheel --> <xacro:wheel name="rear_left_link"/> <joint name="rear_left_joint" type="continuous"> <parent link="chassis_link"/> <child link="rear_left_link"/> <origin xyz="-0.3 -0.5 -0.1" rpy="0 ${pi/2} 0"/> <axis xyz="0 0 1"/> </joint> <!-- Rear Right Wheel --> <xacro:wheel name="rear_right_link" /> <joint name="rear_right_joint" type="continuous"> <parent link="chassis_link"/> <child link="rear_right_link"/> <origin xyz="0.3 -0.5 -0.1" rpy="0 ${pi/2} 0"/> <!-- Default value for x is "0.27" --> <axis xyz="0 0 1"/> </joint> <xacro:include filename="ros2_ack.xacro"/> </robot> ``` ## ros2_ack.xacro ## ``` <?xml version="1.0"?> <robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="simple_car"> <ros2_control name="carbot" type="system"> <hardware> <!-- <plugin>gazebo_ros2_control/GazeboSystem</plugin> --> <plugin>ros2_control_demo_example_11/CarlikeBotSystemHardware</plugin> <param name="example_param_hw_start_duration_sec">0</param> <param name="example_param_hw_stop_duration_sec">3.0</param> <param name="is_simulation">1</param> </hardware> <!-- Front Left Wheel --> <joint name="front_left_joint"> <command_interface name="position"/> <state_interface name="position" /> </joint> <!-- Front Right Wheel --> <joint name="front_right_joint"> <command_interface name="position"/> <state_interface name="position" /> </joint> <!-- Rear Left Wheel --> <joint name="rear_left_joint"> <command_interface name="velocity"/> <state_interface name="velocity" /> </joint> <!-- Rear Right Wheel --> <joint name="rear_right_joint"> <command_interface name="velocity"/> <state_interface name="velocity" /> </joint> </ros2_control> <gazebo> <plugin filename="libgazebo_ros2_control.so" name="gazebo_ros2_control"> <parameters>$(find race_it)/config/controller/my_controllers.yaml</parameters> </plugin> </gazebo> </robot> ``` # Controller Config # ## my_controllers.yaml ## ``` controller_manager: ros__parameters: update_rate: 30 use_sim_time: true asc: type: ackermann_steering_controller/AckermannSteeringController jsc: type: joint_state_broadcaster/JointStateBroadcaster asc: ros__parameters: reference_timeout: 2.0 front_steering: true open_loop: false velocity_rolling_window_size: 10 position_feedback: false use_stamped_vel: true #rear_wheels_names: [rear_right_joint, rear_left_joint] #front_wheels_names: [front_right_joint, front_left_joint] rear_wheels_names: [rear_right_joint, rear_left_joint] front_wheels_names: [front_right_joint, front_left_joint] wheelbase: 1.0 front_wheel_track: 0.55 rear_wheel_track: 0.55 front_wheels_radius: 0.1 rear_wheels_radius: 0.1 #jsc: # ros__parameters: # type: joint_state_controller/JointStateController ``` # Launch File # ## rsp.launch.py ## ``` import os from ament_index_python.packages import get_package_share_directory from launch import LaunchDescription from launch.substitutions import LaunchConfiguration from launch.actions import DeclareLaunchArgument from launch_ros.actions import Node import launch_ros from launch.actions import IncludeLaunchDescription from launch.launch_description_sources import PythonLaunchDescriptionSource import xacro def generate_launch_description(): # Check if we're told to use sim time use_sim_time = LaunchConfiguration('use_sim_time') # Process the URDF file pkg_path = os.path.join(get_package_share_directory('race_it')) #xacro_file = os.path.join(pkg_path,'description','robot.urdf.xacro') xacro_file = os.path.join(pkg_path,'description', 'race_car.xacro') robot_description_config = xacro.process_file(xacro_file) default_rviz_config_path = os.path.join(pkg_path, 'config/rviz/urdf_config.rviz') # Create a robot_state_publisher node params = {'robot_description': robot_description_config.toxml(), 'use_sim_time': use_sim_time} node_robot_state_publisher = Node( package='robot_state_publisher', executable='robot_state_publisher', output='screen', parameters=[params] ) joint_state_publisher_node = launch_ros.actions.Node( package='joint_state_publisher', executable='joint_state_publisher', name='joint_state_publisher' ) rviz_node = launch_ros.actions.Node( package='rviz2', executable='rviz2', name='rviz2', output='screen', arguments=['-d', default_rviz_config_path], ) gazebo = IncludeLaunchDescription( PythonLaunchDescriptionSource([os.path.join( get_package_share_directory('gazebo_ros'), 'launch', 'gazebo.launch.py')])#, # launch_arguments={'world': "my_bot/worlds/obstacles.world"}.items() ) spawn_entity = launch_ros.actions.Node( package='gazebo_ros', executable='spawn_entity.py', arguments=['-entity', '', '-topic', 'robot_description'], output='screen' ) # Launch! return LaunchDescription([ DeclareLaunchArgument( 'use_sim_time', default_value='true', description='Use sim time if true'), node_robot_state_publisher, joint_state_publisher_node, rviz_node, gazebo, spawn_entity ]) ```