44import math
55import rclpy
66from 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
1010serial_port = "/dev/ttyUSB0" # Update with your port
1111baud_rate = 115200 # Adjust baud rate as per your actuator settings
1212ser = 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+
1420class 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
9475def main ():
9576 rclpy .init ()
0 commit comments