Skip to content

Commit 5767ed6

Browse files
committed
First steps towards PWM control from ROS
1 parent a484113 commit 5767ed6

File tree

1 file changed

+21
-40
lines changed

1 file changed

+21
-40
lines changed

ros_ws/src/boatcontrol/boatcontrol/boatcontrolnode.py

Lines changed: 21 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,24 @@
44
import math
55
import rclpy
66
from rclpy.node import Node
7-
from std_msgs.msg import Float64
7+
from geometry_msgs.msg import TwistStamped
88

99
# Initialize serial communication for actuator control
1010
serial_port = "/dev/ttyUSB0" # Update with your port
1111
baud_rate = 115200 # Adjust baud rate as per your actuator settings
1212
ser = serial.Serial(serial_port, baud_rate, timeout=1)
1313

14+
def clip(x, lo, hi):
15+
return max(lo, min(hi, x))
16+
17+
def to_int8(u):
18+
return int(clip(round(127 * u), -127, 127))
19+
1420
class BoatControlNode(Node):
1521

1622
def __init__(self):
1723
super().__init__('boat_control_node')
18-
self.compass_pub = self.create_publisher(Float64, '/compass', 10)
19-
self.heading_sub = self.create_subscription(Float64, '/desired_heading', self.heading_callback, 10)
20-
self.speed_sub = self.create_subscription(Float64, '/desired_speed', self.speed_callback, 10)
21-
self.current_heading = None
22-
timer_period = 0.05 # Seconds
23-
self.timer = self.create_timer(timer_period, self.timer_callback)
24+
self.cmd_sub = self.create_subscription(TwistStamped, '/cmd_vel', self.props_callback, 10)
2425

2526
def start(self):
2627
time.sleep(0.5)
@@ -32,20 +33,21 @@ def start(self):
3233
time.sleep(0.1)
3334
self.send_speed_command(30)
3435

35-
def timer_callback(self):
36+
# Controls the propellors for the boat via duty cycle control.
37+
def props_callback(self, cmd_vel):
3638
self.send_enable_command()
37-
current_heading = self.get_heading_command()
38-
current_heading = self.get_heading_command()# dual reading to flush !
3939

40-
msg = Float64()
41-
msg.data = current_heading
42-
self.compass_pub.publish(msg)
40+
# Converting twist message to differntial drive control, includes clipping
41+
# This is an electronic speed controller
42+
v = cmd_vel.twist.linear.x # (m/s)
43+
w = cmd_vel.twist.angular.z # (rad/s)
4344

44-
def heading_callback(self, heading):
45-
self.send_nav_command(10, heading)
45+
B = 0.3 # (m)
46+
K = 0.05 # To be determined!
47+
left = (v - w*B/2) / K
48+
right = (v + w*B/2) / K
4649

47-
def speed_callback(self, speed):
48-
self.send_speed_command(speed)
50+
self.send_pwm_command(to_int8(left), to_int8(right))
4951

5052
def send_command(self, command):
5153
if ser.is_open:
@@ -67,29 +69,8 @@ def send_enable_command(self):
6769
self.send_command(f"POST WD 30") #enable motor for 30s
6870

6971
# Function to send command to the actuator
70-
def send_move_command(self, command):
71-
self.send_command(f"POST MOVE {command}")
72-
73-
def send_nav_command(self, duration, heading):
74-
self.send_command(f"POST NAV {duration} {heading}")
75-
76-
def send_speed_command(self, speed):
77-
self.send_command(f"POST SPEED {speed}")
78-
79-
# Function to send command to the actuator
80-
def get_heading_command(self):
81-
response = self.send_command(f"GET HEADING")
82-
83-
# Parse the heading from the response
84-
try:
85-
# Find the start and end of the heading value within the response string
86-
start = response.index("heading:") + len("heading:")
87-
end = response.index("}", start)
88-
heading_value = float(response[start:end].strip()) # Convert to float
89-
return heading_value
90-
except (ValueError, IndexError) as e:
91-
self.get_logger().info(f"Error parsing heading: {e}")
92-
return None
72+
def send_pwm_command(self, left, right):
73+
self.send_command(f"POST PWM {left} {right}")
9374

9475
def main():
9576
rclpy.init()

0 commit comments

Comments
 (0)