Manual Control - Weber-State-Submarine-Project/Submarine GitHub Wiki
Manual Controller System for Motor Control
Introduction
This section explains the implementation of a manual control system for the boat using a Bluetooth-connected controller. For this project, a PS5 controller was used to provide input commands to the motors. It is important to note that during manual control, the boat cannot create maps due to robot localization issues, particularly during turning. The manual control system is primarily intended for retrieving the boat in scenarios where it cannot be reached manually.
Overview
The manual control system consists of the following components:
- Controller Input: Receives joystick input from the connected PS5 controller via the
joy
topic. - Motor Commands: Converts joystick input into motor power values and publishes these to the ESC motors via the
esc_topic
. - ROS 2 Node: Implements the
JoyToEsc
node to handle input processing and message publishing.
Code Walkthrough
Node Initialization
The node JoyToEsc
initializes two key components:
- Subscriber: Subscribes to the
joy
topic to receive joystick input in the form ofsensor_msgs/Joy
messages. - Publisher: Publishes motor commands to the
esc_topic
using the customorientation_msg/Esc
message type.
Joystick Input Processing
-
Joystick Axes:
- Vertical Axis (
msg.axes[1]
): Controls forward and backward motion (-1 for full reverse, 1 for full forward). - Horizontal Axis (
msg.axes[0]
): Controls left and right turns (-1 for full left, 1 for full right).
- Vertical Axis (
-
Vector Math for Motor Control:
- The left and right motor powers are calculated using vector math:
This ensures that:left_motor_power = (left_vertical - left_horizontal) / 2 * max_power right_motor_power = (left_vertical + left_horizontal) / 2 * max_power
- Forward motion applies equal power to both motors.
- Turning applies opposite power to the motors to create a pivot effect.
- The left and right motor powers are calculated using vector math:
-
Normalization:
- Motor power values are limited to the range
[-max_power, max_power]
to prevent exceeding the allowed range.
- Motor power values are limited to the range
-
Conversion:
- Motor power values are converted to integers to match the expected message type.
Publishing Motor Commands
-
Custom
Esc
Message:- The motor commands are published as two separate messages:
- One for the left motor (
motor_selection = 'Left'
). - One for the right motor (
motor_selection = 'Right'
).
- One for the left motor (
- The motor commands are published as two separate messages:
-
ESC Communication:
- Each motor command is sent to the
esc_topic
where the ESC nodes process them to control the motors.
- Each motor command is sent to the
Challenges and Considerations
-
Localization Issues:
- During manual control, turning movements disrupt robot localization, making it unsuitable for mapping.
-
Power Limitation:
- The
max_power
parameter can be adjusted to limit motor power, ensuring safe operation during manual control.
- The
-
Joystick Compatibility:
- The system is designed for a PS5 controller but can be adapted to other controllers with similar button and axis mappings.
Code Implementation
#!/usr/bin/env python
import rclpy
from rclpy.node import Node
from sensor_msgs.msg import Joy
from orientation_msg.msg import Esc
class JoyToEsc(Node):
def __init__(self):
super().__init__('joy_to_esc')
# Create subscriber to the 'joy' topic
self.subscription = self.create_subscription(
Joy,
'joy',
self.joy_callback,
10)
# Create publisher to send motor commands
self.publisher = self.create_publisher(Esc, 'esc_topic', 10)
def joy_callback(self, msg):
# Joystick's vertical axis is axis[1] and horizontal axis is axis[0]
left_vertical = msg.axes[1] # Forward/backward control (-1 to 1)
left_horizontal = msg.axes[0] # Left/right control (-1 to 1)
# Max motor power
max_power = 100 # Maximum motor power as a percentage (e.g., 100%)
# Vector Math for controller to power
left_motor_power = (left_vertical - left_horizontal) / 2 * max_power
right_motor_power = (left_vertical + left_horizontal) / 2 * max_power
# Normalize the motor values if they exceed max range (-100 to 100)
left_motor_power = max(min(left_motor_power, max_power), -max_power)
right_motor_power = max(min(right_motor_power, max_power), -max_power)
# Convert motor powers to integers to match the expected message type
left_motor_power = int(left_motor_power)
right_motor_power = int(right_motor_power)
# Create and publish the message to control ESCs
esc_msg = Esc()
esc_msg.motor_selection = 'Left'
esc_msg.power_percentage = left_motor_power
self.publisher.publish(esc_msg)
esc_msg.motor_selection = 'Right'
esc_msg.power_percentage = right_motor_power
self.publisher.publish(esc_msg)
#self.get_logger().info(f'Published left motor: {left_motor_power}, right motor: {right_motor_power}')
def main(args=None):
rclpy.init(args=args)
node = JoyToEsc()
try:
rclpy.spin(node)
except KeyboardInterrupt:
pass
node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()