Help with Controlling Joints of ROS 2 Car Model
Dear Ignition Gazebo / ROS 2 Community,
I’m currently working on a small ROS 2 (Humble) project where I spawn a four‑wheeled car model in Ignition Gazebo and control it via ros2_control
with a diff_drive_controller
. Everything builds and launches correctly, but I’m not sure how to publish joint commands or velocities for the wheels directly. Could someone advise me on the best practice for commanding the individual wheel joints (either via ApplyJointEffort
or through a ROS 2 controller)?
Project Structure
carro_ws/
└── src/
└── carro_tutorial_yotube_copy/
├── CMakeLists.txt
├── package.xml
├── setup.py
├── ros2_controllers.yaml
├── launch/
│ └── spawn_car.launch.py
├── worlds/
│ └── empty_with_commands.sdf
├── urdf/
│ ├── my_robot_com_mesh_1.urdf
│ ├── my_robot.urdf
│ ├── tentativa_gazebo_2.urdf
│ └── tentativa_gazebo.urdf
└── resource/
└── carro_tutorial_yotube_copy
Key Files
ros2_controllers.yaml
controller_manager:
ros__parameters:
update_rate: 100
joint_state_broadcaster:
type: joint_state_broadcaster/JointStateBroadcaster
diff_drive_controller:
type: diff_drive_controller/DiffDriveController
left_wheel_names: [ base_back_left_wheel_joint, base_front_left_wheel_joint ]
right_wheel_names: [ base_back_right_wheel_joint, base_front_right_wheel_joint ]
wheel_separation: 0.51
wheel_radius: 0.10
cmd_vel_timeout: 0.25
launch/spawn_car.launch.py
#!/usr/bin/env python3
import os
from launch import LaunchDescription
from launch.actions import TimerAction, IncludeLaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSource
from ament_index_python.packages import get_package_share_directory
from launch_ros.actions import Node
def generate_launch_description():
pkg_share = get_package_share_directory(‘carro_tutorial_yotube_copy’)
gz_launch = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
os.path.join(
get_package_share_directory('ros_gz_sim'),
'launch', 'gz_sim.launch.py'
)
),
launch_arguments={'gz_args': os.path.join(pkg_share, 'worlds', 'empty_with_commands.sdf')}.items()
)
# Publish URDF for ros2_control
urdf_file = os.path.join(pkg_share, 'urdf', 'my_robot_com_mesh_1.urdf')
with open(urdf_file, 'r') as inf:
robot_desc = inf.read()
rsp_node = Node(
package='robot_state_publisher',
executable='robot_state_publisher',
output='screen',
parameters=[{'robot_description': robot_desc}]
)
# Start controller_manager
ros2_control_node = Node(
package='controller_manager',
executable='ros2_control_node',
parameters=[os.path.join(pkg_share, 'ros2_controllers.yaml')],
output='screen'
)
# Spawn the car
spawn_node = Node(
package='ros_gz_sim',
executable='create',
name='spawn_car',
output='screen',
arguments=[
'--world', 'empty',
'--file', urdf_file,
'--name', 'meu_carro',
'--x', '0.0', '--y', '0.0', '--z', '0.4'
]
)
delayed_spawn = TimerAction(period=5.0, actions=[spawn_node])
return LaunchDescription([
gz_launch,
rsp_node,
ros2_control_node,
delayed_spawn
])
Questions
- Is it better to use the
/diff_drive_controller/cmd_vel
topic, or call ApplyJointEffort
services directly for each wheel joint?
- If using
ApplyJointEffort
, how do I correctly configure and call it from ROS 2 so that Ignition Gazebo applies torque to my joints?
- Any tips on tuning the
diff_drive_controller
parameters (wheel separation, radius, etc.) for realistic behavior?
Thank you for your assistance!
Best regards,
Duarte Diamantino