|
| 1 | +# WBot Mobile Manipulator Example |
| 2 | + |
| 3 | +Bring up and control the WBot differential-drive base and 6-DoF arm with `ros2_control`. This example covers setup, launch, teleop, controller introspection, and mixing real base hardware with a mock arm. |
| 4 | + |
| 5 | + |
| 6 | + |
| 7 | +## 0. Setup (one time) |
| 8 | + |
| 9 | +```sh |
| 10 | +sudo apt install git-lfs |
| 11 | +git lfs install |
| 12 | + |
| 13 | +mkdir -p ~/colcon_control_ws/src |
| 14 | +cd ~/colcon_control_ws/src |
| 15 | +git clone https://github.com/ROSI-IPA/wbot.git |
| 16 | +git clone https://github.com/MarqRazz/piper_ros2.git |
| 17 | +cd .. |
| 18 | +rosdep install --from-paths src -iry |
| 19 | +colcon build --symlink-install |
| 20 | +source install/setup.bash |
| 21 | +``` |
| 22 | + |
| 23 | +## 1. Launch the robot |
| 24 | + |
| 25 | +```sh |
| 26 | +ros2 launch wbot_bringup wbot.launch.xml |
| 27 | +``` |
| 28 | + |
| 29 | +This starts the base, arm, controllers, and RViz. In another terminal, open `rqt_graph` if you want to inspect nodes. |
| 30 | + |
| 31 | +## 2. Teleop the base |
| 32 | + |
| 33 | +```sh |
| 34 | +ros2 run teleop_twist_keyboard teleop_twist_keyboard --ros-args -p stamped:=true |
| 35 | +``` |
| 36 | + |
| 37 | +Drive around and watch RViz to see odometry and the footprint moving. |
| 38 | + |
| 39 | +## 3. Inspect ros2_control state |
| 40 | + |
| 41 | +While the launch is running, query the control stack: |
| 42 | + |
| 43 | +```sh |
| 44 | +ros2 control list_controllers |
| 45 | +ros2 control list_controller_types |
| 46 | +ros2 control list_hardware_components -v |
| 47 | +ros2 control list_hardware_interfaces |
| 48 | +``` |
| 49 | + |
| 50 | +For full controller manager introspection: |
| 51 | + |
| 52 | +```sh |
| 53 | +ros2 topic echo /controller_manager/introspection_data/full |
| 54 | +``` |
| 55 | + |
| 56 | +Focus on `wbot_base_control.nonlimited` and `wbot_base_control.limited` as you drive to see command limiting in action. |
| 57 | + |
| 58 | +## 4. Visualize introspection values in PlotJuggler |
| 59 | + |
| 60 | +```sh |
| 61 | +ros2 run plotjuggler plotjuggler |
| 62 | +``` |
| 63 | + |
| 64 | +1. Start the ROS 2 subscriber. |
| 65 | +2. Add `/controller_manager/introspection_data/values` as a topic. |
| 66 | +3. Plot `wbot_base_control.nonlimited` and `wbot_base_control.limited`; they publish at 100 Hz. |
| 67 | + |
| 68 | +## 5. ros2_control configuration reference |
| 69 | + |
| 70 | +Hardware macro snippet: |
| 71 | + |
| 72 | +```xml |
| 73 | +<xacro:macro name="wbot_ros2_control" params=" |
| 74 | + name |
| 75 | + mock_hardware:=false |
| 76 | + enable_command_limiting:=false |
| 77 | + joint_command_topic:=/joint_command_topic |
| 78 | + joint_states_topic:=/joint_states_topic"> |
| 79 | + |
| 80 | + <ros2_control name="${name}" type="system"> |
| 81 | + <hardware> |
| 82 | + <xacro:if value="${mock_hardware}"> |
| 83 | + <plugin>mock_components/GenericSystem</plugin> |
| 84 | + <param name="fake_sensor_commands">false</param> |
| 85 | + <param name="state_following_offset">0.0</param> |
| 86 | + <param name="calculate_dynamics">true</param> |
| 87 | + </xacro:if> |
| 88 | + <xacro:unless value="${mock_hardware}"> |
| 89 | + <plugin>joint_state_topic_hardware_interface/JointStateTopicSystem</plugin> |
| 90 | + <param name="joint_commands_topic">${joint_command_topic}</param> |
| 91 | + <param name="joint_states_topic">${joint_states_topic}</param> |
| 92 | + <param name="sum_wrapped_joint_states">true</param> |
| 93 | + <param name="trigger_joint_command_threshold">-1</param> |
| 94 | + <param name="enable_command_limiting">${enable_command_limiting}</param> |
| 95 | + </xacro:unless> |
| 96 | + </hardware> |
| 97 | + <joint name="wbot_wheel_left_joint"> |
| 98 | + <command_interface name="velocity"> |
| 99 | + <param name="min">-16.00</param> |
| 100 | + <param name="max">16.00</param> |
| 101 | + </command_interface> |
| 102 | + <state_interface name="position"/> |
| 103 | + <state_interface name="velocity"/> |
| 104 | + </joint> |
| 105 | + <joint name="wbot_wheel_right_joint"> |
| 106 | + <command_interface name="velocity"> |
| 107 | + <param name="min">-16.00</param> |
| 108 | + <param name="max">13.0</param> |
| 109 | + </command_interface> |
| 110 | + <state_interface name="position"/> |
| 111 | + <state_interface name="velocity"/> |
| 112 | + </joint> |
| 113 | + </ros2_control> |
| 114 | +</xacro:macro> |
| 115 | +``` |
| 116 | + |
| 117 | +Controller YAML (base, arm, gripper): |
| 118 | + |
| 119 | +```yaml |
| 120 | +controller_manager: |
| 121 | + ros__parameters: |
| 122 | + update_rate: 100 # Hz |
| 123 | + joint_state_broadcaster: |
| 124 | + type: joint_state_broadcaster/JointStateBroadcaster |
| 125 | + diff_drive_base_controller: |
| 126 | + type: diff_drive_controller/DiffDriveController |
| 127 | + joint_trajectory_controller: |
| 128 | + type: joint_trajectory_controller/JointTrajectoryController |
| 129 | + gripper_controller: |
| 130 | + type: parallel_gripper_action_controller/GripperActionController |
| 131 | + |
| 132 | +diff_drive_base_controller: |
| 133 | + ros__parameters: |
| 134 | + left_wheel_names: ["wbot_wheel_left_joint"] |
| 135 | + right_wheel_names: ["wbot_wheel_right_joint"] |
| 136 | + wheel_separation: 0.287 |
| 137 | + wheel_radius: 0.033 |
| 138 | + wheel_separation_multiplier: 1.0 |
| 139 | + left_wheel_radius_multiplier: 1.0 |
| 140 | + right_wheel_radius_multiplier: 1.0 |
| 141 | + publish_rate: 50.0 |
| 142 | + odom_frame_id: odom |
| 143 | + base_frame_id: wbot_base_footprint |
| 144 | + pose_covariance_diagonal: [0.001, 0.001, 0.0, 0.0, 0.0, 0.01] |
| 145 | + twist_covariance_diagonal: [0.001, 0.0, 0.0, 0.0, 0.0, 0.01] |
| 146 | + open_loop: false |
| 147 | + position_feedback: true |
| 148 | + enable_odom_tf: true |
| 149 | + cmd_vel_timeout: 0.5 |
| 150 | + linear.x.has_velocity_limits: true |
| 151 | + linear.x.has_acceleration_limits: true |
| 152 | + linear.x.has_jerk_limits: false |
| 153 | + linear.x.max_velocity: 1.0 |
| 154 | + linear.x.min_velocity: -1.0 |
| 155 | + linear.x.max_acceleration: 2.5 |
| 156 | + linear.x.max_jerk: 0.0 |
| 157 | + linear.x.min_jerk: 0.0 |
| 158 | + angular.z.has_velocity_limits: true |
| 159 | + angular.z.has_acceleration_limits: true |
| 160 | + angular.z.has_jerk_limits: false |
| 161 | + angular.z.max_velocity: 2.0 |
| 162 | + angular.z.min_velocity: -2.0 |
| 163 | + angular.z.max_acceleration: 6.2 |
| 164 | + angular.z.min_acceleration: -6.2 |
| 165 | + angular.z.max_jerk: 0.0 |
| 166 | + angular.z.min_jerk: 0.0 |
| 167 | + |
| 168 | +joint_trajectory_controller: |
| 169 | + ros__parameters: |
| 170 | + joints: |
| 171 | + - wbot_arm_joint_1 |
| 172 | + - wbot_arm_joint_2 |
| 173 | + - wbot_arm_joint_3 |
| 174 | + - wbot_arm_joint_4 |
| 175 | + - wbot_arm_joint_5 |
| 176 | + - wbot_arm_joint_6 |
| 177 | + command_interfaces: |
| 178 | + - position |
| 179 | + state_interfaces: |
| 180 | + - position |
| 181 | + - velocity |
| 182 | + state_publish_rate: 0.0 |
| 183 | + action_monitor_rate: 20.0 |
| 184 | + allow_partial_joints_goal: false |
| 185 | + open_loop_control: false |
| 186 | + allow_nonzero_velocity_at_trajectory_end: true |
| 187 | + interpolation_method: splines |
| 188 | + constraints: |
| 189 | + stopped_velocity_tolerance: 0.01 |
| 190 | + goal_time: 0.0 |
| 191 | + joint_1: |
| 192 | + goal: 0.001 |
| 193 | + joint_2: |
| 194 | + goal: 0.001 |
| 195 | + joint_3: |
| 196 | + goal: 0.001 |
| 197 | + joint_4: |
| 198 | + goal: 0.001 |
| 199 | + joint_5: |
| 200 | + goal: 0.001 |
| 201 | + joint_6: |
| 202 | + goal: 0.001 |
| 203 | + |
| 204 | +gripper_controller: |
| 205 | + ros__parameters: |
| 206 | + action_monitor_rate: 20.0 |
| 207 | + allow_stalling: false |
| 208 | + goal_tolerance: 0.01 |
| 209 | + joint: wbot_arm_gripper_joint |
| 210 | + stall_timeout: 1.0 |
| 211 | + stall_velocity_threshold: 0.002 |
| 212 | +``` |
| 213 | +
|
| 214 | +## 6. Mobile manipulator with mixed hardware |
| 215 | +
|
| 216 | +If you have the base hardware but no arm, run the arm with mock hardware: |
| 217 | +
|
| 218 | +```sh |
| 219 | +ros2 launch wbot_bringup wbot_manipulator.launch.xml mock_hardware:=true |
| 220 | +``` |
| 221 | + |
| 222 | +List hardware components: |
| 223 | + |
| 224 | +```sh |
| 225 | +ros2 control list_hardware_components |
| 226 | +``` |
| 227 | + |
| 228 | +Example output: |
| 229 | + |
| 230 | +```text |
| 231 | +Hardware Component 1 |
| 232 | + name: wbot_arm_piper_control |
| 233 | + type: system |
| 234 | + plugin name: mock_components/GenericSystem |
| 235 | + state: id=3 label=active |
| 236 | + read/write rate: 100 Hz |
| 237 | + is_async: False |
| 238 | + command interfaces |
| 239 | + wbot_arm_joint_5/position [available] [claimed] |
| 240 | + wbot_arm_joint_6/position [available] [claimed] |
| 241 | + wbot_arm_joint_4/position [available] [claimed] |
| 242 | + wbot_arm_gripper_joint/position [available] [claimed] |
| 243 | + wbot_arm_joint_3/position [available] [claimed] |
| 244 | + wbot_arm_joint_2/position [available] [claimed] |
| 245 | + wbot_arm_joint_1/position [available] [claimed] |
| 246 | +Hardware Component 2 |
| 247 | + name: wbot_base_control |
| 248 | + type: system |
| 249 | + plugin name: mock_components/GenericSystem |
| 250 | + state: id=3 label=active |
| 251 | + read/write rate: 100 Hz |
| 252 | + is_async: False |
| 253 | + command interfaces |
| 254 | + wbot_wheel_right_joint/velocity [available] [claimed] |
| 255 | + wbot_wheel_left_joint/velocity [available] [claimed] |
| 256 | +``` |
| 257 | + |
| 258 | +## 7. Move the robot |
| 259 | + |
| 260 | +### Base (DiffDriveController) |
| 261 | + |
| 262 | +```sh |
| 263 | +ros2 run teleop_twist_keyboard teleop_twist_keyboard --ros-args -p stamped:=true |
| 264 | +``` |
| 265 | + |
| 266 | +The diff-drive controller computes wheel velocities and sends them to the embedded board while streaming state back. |
| 267 | + |
| 268 | +### Arm (JointTrajectoryController) |
| 269 | + |
| 270 | +The joint trajectory controller supports both an action and a topic: |
| 271 | + |
| 272 | +```sh |
| 273 | +ros2 action info /joint_trajectory_controller/follow_joint_trajectory |
| 274 | +ros2 topic info -v /joint_trajectory_controller/joint_trajectory |
| 275 | +``` |
| 276 | + |
| 277 | +Send a pose: |
| 278 | + |
| 279 | +```sh |
| 280 | +ros2 topic pub /joint_trajectory_controller/joint_trajectory trajectory_msgs/JointTrajectory "{ |
| 281 | + joint_names: [wbot_arm_joint_1, wbot_arm_joint_2, wbot_arm_joint_3, wbot_arm_joint_4, wbot_arm_joint_5, wbot_arm_joint_6], |
| 282 | + points: [ |
| 283 | + { positions: [0.0, 0.85, -0.75, 0.0, 0.5, 0.0], time_from_start: { sec: 2 } } |
| 284 | + ] |
| 285 | +}" -1 |
| 286 | +``` |
| 287 | + |
| 288 | +Return home: |
| 289 | + |
| 290 | +```sh |
| 291 | +ros2 topic pub /joint_trajectory_controller/joint_trajectory trajectory_msgs/JointTrajectory "{ |
| 292 | + joint_names: [wbot_arm_joint_1, wbot_arm_joint_2, wbot_arm_joint_3, wbot_arm_joint_4, wbot_arm_joint_5, wbot_arm_joint_6], |
| 293 | + points: [ |
| 294 | + { positions: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], time_from_start: { sec: 1 } } |
| 295 | + ] |
| 296 | +}" -1 |
| 297 | +``` |
| 298 | + |
| 299 | + |
| 300 | + |
| 301 | +### Gripper (GripperActionController) |
| 302 | + |
| 303 | +```sh |
| 304 | +ros2 action send_goal /gripper_controller/gripper_cmd control_msgs/action/ParallelGripperCommand "{command: {name: [wbot_arm_gripper_joint], position: [0.03]}}" |
| 305 | +ros2 action send_goal /gripper_controller/gripper_cmd control_msgs/action/ParallelGripperCommand "{command: {name: [wbot_arm_gripper_joint], position: [0.0]}}" |
| 306 | +``` |
| 307 | + |
| 308 | + |
0 commit comments