The wasd example (implemented via the WasdInterface class) uses two primary ROS 2 communication patterns to control the robot:
1. Service Calls for Discrete Actions
Discrete actions like standing, sitting, or stowing the arm are performed by calling ROS 2 services. The example uses service clients for the Trigger message type.
- Stand: calls the
stand service. - Sit: calls the
sit service. - Stow Arm: calls the
arm_stow service. - Power: calls the
power_on service.
2. Topic Publishing for Locomotion
Continuous movement (translation and rotation) is handled by publishing geometry_msgs/msg/Twist messages to the /cmd_vel topic.
To prevent the robot from moving indefinitely, the example uses a helper function _velocity_cmd_helper() that:
- Constructs a
Twist message with the requested linear (x, y) and angular (z) velocities. - Continuously publishes the message for a set
VELOCITY_CMD_DURATION. - Publishes a zeroed-out
Twist message (all fields at 0.0) once the duration expires to stop the robot.
3. Subscriptions for State Monitoring
The interface monitors robot state (such as battery levels) by subscribing to relevant topics. For example, it subscribes to /<robot_name>/status/battery_states using the BatteryStateArray message type to update the latest_battery_status member.
# Example of how a service call is triggered via a keybind mapping
self._command_dictionary = {
ord("v"): self._sit,
}
# Example of the service call implementation
def _sit(self) -> None:
self.cli_sit.call_async(Trigger.Request())
# Example of velocity command publishing pattern
def _velocity_cmd_helper(self, desc: str = "", v_x: float = 0.0, v_y: float = 0.0, v_rot: float = 0.0) -> None:
twist = Twist()
twist.linear.x = v_x
twist.linear.y = v_y
twist.angular.z = v_rot
start_time = time.time()
while time.time() - start_time < VELOCITY_CMD_DURATION:
self.pub_cmd_vel.publish(twist)
time.sleep(0.01)
self.pub_cmd_vel.publish(Twist())