44import time
55
66# ROS Headers
7- import rospy
7+ import rclpy
8+ from rclpy .node import Node
9+ from rclpy .clock import Clock
810from std_msgs .msg import String , Bool , Float32 , Float64
911from sensor_msgs .msg import Image ,PointCloud2
1012try :
1719 pass
1820
1921from radar_msgs .msg import RadarTracks
20- from tf . transformations import euler_from_quaternion , quaternion_from_euler
22+ from tf_transformations import euler_from_quaternion , quaternion_from_euler
2123
2224# GEM PACMod Headers
23- from pacmod_msgs .msg import PositionWithSpeed , PacmodCmd , SystemRptFloat , VehicleSpeedRpt , GlobalRpt
25+ from pacmod2_msgs .msg import PositionWithSpeed , PacmodCmd , SystemRptFloat , VehicleSpeedRpt , GlobalRpt
2426
2527# OpenCV and cv2 bridge
2628import cv2
2729import numpy as np
2830from ...utils import conversions
2931
32+ from rclpy .qos import QoSProfile , QoSReliabilityPolicy , QoSHistoryPolicy
33+
34+ # Define a default QoS profile
35+ qos_profile = QoSProfile (
36+ reliability = QoSReliabilityPolicy .BEST_EFFORT , # Use RELIABLE if needed
37+ history = QoSHistoryPolicy .KEEP_LAST ,
38+ depth = 10
39+ )
40+
3041class GEMHardwareInterface (GEMInterface ):
3142 """Interface for connnecting to the physical GEM e2 vehicle."""
3243 def __init__ (self ):
3344 GEMInterface .__init__ (self )
45+ self .node = rclpy .create_node ('gem_hardware_interface' )
3446 self .max_send_rate = settings .get ('vehicle.max_command_rate' ,10.0 )
3547 self .ros_sensor_topics = settings .get ('vehicle.sensors.ros_topics' )
3648 self .last_command_time = 0.0
@@ -46,9 +58,9 @@ def __init__(self):
4658 self .last_reading .wiper_level = 0
4759 self .last_reading .headlights_on = False
4860
49- self .speed_sub = rospy . Subscriber ( "/pacmod/parsed_tx/vehicle_speed_rpt" , VehicleSpeedRpt , self .speed_callback )
50- self .steer_sub = rospy . Subscriber ( "/pacmod/parsed_tx/steer_rpt" , SystemRptFloat , self .steer_callback )
51- self .global_sub = rospy . Subscriber ( "/pacmod/parsed_tx/global_rpt" , GlobalRpt , self .global_callback )
61+ self .speed_sub = self . node . create_subscription ( VehicleSpeedRpt , "/pacmod/parsed_tx/vehicle_speed_rpt" , self .speed_callback , qos_profile )
62+ self .steer_sub = self . node . create_subscription ( SystemRptFloat , "/pacmod/parsed_tx/steer_rpt" , self .steer_callback , qos_profile )
63+ self .global_sub = self . node . create_subscription ( GlobalRpt , "/pacmod/parsed_tx/global_rpt" , self .global_callback , qos_profile )
5264 self .gnss_sub = None
5365 self .imu_sub = None
5466 self .front_radar_sub = None
@@ -60,36 +72,36 @@ def __init__(self):
6072
6173 # -------------------- PACMod setup --------------------
6274 # GEM vehicle enable
63- self .enable_pub = rospy . Publisher ( '/pacmod/as_rx/enable' , Bool , queue_size = 1 )
75+ self .enable_pub = self . node . create_publisher ( Bool , '/pacmod/as_rx/enable' , 1 )
6476 self .pacmod_enable = False
6577
6678 # GEM vehicle gear control, neutral, forward and reverse, publish once
67- self .gear_pub = rospy . Publisher ( '/pacmod/as_rx/shift_cmd' , PacmodCmd , queue_size = 1 )
79+ self .gear_pub = self . node . create_publisher ( PacmodCmd , '/pacmod/as_rx/shift_cmd' , 1 )
6880 self .gear_cmd = PacmodCmd ()
6981 self .gear_cmd .enable = True
7082 self .gear_cmd .ui16_cmd = PacmodCmd .SHIFT_NEUTRAL
7183
7284 # GEM vehicle brake control
73- self .brake_pub = rospy . Publisher ( '/pacmod/as_rx/brake_cmd' , PacmodCmd , queue_size = 1 )
85+ self .brake_pub = self . node . create_publisher ( PacmodCmd , '/pacmod/as_rx/brake_cmd' , 1 )
7486 self .brake_cmd = PacmodCmd ()
7587 self .brake_cmd .enable = False
7688 self .brake_cmd .clear = True
7789 self .brake_cmd .ignore = True
7890
7991 # GEM vehicle forward motion control
80- self .accel_pub = rospy . Publisher ( '/pacmod/as_rx/accel_cmd' , PacmodCmd , queue_size = 1 )
92+ self .accel_pub = self . node . create_publisher ( PacmodCmd , '/pacmod/as_rx/accel_cmd' , 1 )
8193 self .accel_cmd = PacmodCmd ()
8294 self .accel_cmd .enable = False
8395 self .accel_cmd .clear = True
8496 self .accel_cmd .ignore = True
8597
8698 # GEM vehicle turn signal control
87- self .turn_pub = rospy . Publisher ( '/pacmod/as_rx/turn_cmd' , PacmodCmd , queue_size = 1 )
99+ self .turn_pub = self . node . create_publisher ( PacmodCmd , '/pacmod/as_rx/turn_cmd' , 1 )
88100 self .turn_cmd = PacmodCmd ()
89101 self .turn_cmd .ui16_cmd = 1 # None
90102
91103 # GEM vechile steering wheel control
92- self .steer_pub = rospy . Publisher ( '/pacmod/as_rx/steer_cmd' , PositionWithSpeed , queue_size = 1 )
104+ self .steer_pub = self . node . create_publisher ( PositionWithSpeed , '/pacmod/as_rx/steer_cmd' , 1 )
93105 self .steer_cmd = PositionWithSpeed ()
94106 self .steer_cmd .angular_position = 0.0 # radians, -: clockwise, +: counter-clockwise
95107 self .steer_cmd .angular_velocity_limit = 2.0 # radians/second
@@ -103,7 +115,7 @@ def __init__(self):
103115 #TODO: publish TwistStamped to /front_radar/front_radar/vehicle_motion to get better radar tracks
104116
105117 #subscribers should go last because the callback might be called before the object is initialized
106- self .enable_sub = rospy . Subscriber ( '/pacmod/as_tx/enable' , Bool , self .pacmod_enable_callback )
118+ self .enable_sub = self . node . create_subscription ( Bool , '/pacmod/as_tx/enable' , self .pacmod_enable_callback , qos_profile )
107119
108120
109121 def start (self ):
@@ -117,7 +129,7 @@ def start(self):
117129 #this doesn't seem to work super well, need to send enable command multiple times
118130
119131 def time (self ):
120- seconds = rospy . get_time ()
132+ seconds = Clock (). now (). to_msg (). sec
121133 return seconds
122134
123135 def speed_callback (self ,msg : VehicleSpeedRpt ):
@@ -156,7 +168,7 @@ def subscribe_sensor(self, name, callback, type = None):
156168 if type is not None and (type is not Inspva and type is not GNSSReading ):
157169 raise ValueError ("GEMHardwareInterface GEM e2 only supports Inspva/GNSSReading for GNSS" )
158170 if type is Inspva :
159- self .gnss_sub = rospy . Subscriber ( topic , Inspva , callback )
171+ self .gnss_sub = self . node . create_subscription ( Inspva , topic , callback , qos_profile )
160172 else :
161173 def callback_with_gnss_reading (inspva_msg : Inspva ):
162174 pose = ObjectPose (ObjectFrameEnum .GLOBAL ,
@@ -169,13 +181,13 @@ def callback_with_gnss_reading(inspva_msg: Inspva):
169181 )
170182 speed = np .sqrt (inspva_msg .east_velocity ** 2 + inspva_msg .north_velocity ** 2 )
171183 callback (GNSSReading (pose ,speed ,inspva_msg .status ))
172- self .gnss_sub = rospy . Subscriber ( topic , Inspva , callback_with_gnss_reading )
184+ self .gnss_sub = self . node . create_subscription ( Inspva , topic , callback_with_gnss_reading , qos_profile )
173185 else :
174186 #assume it's septentrio on GEM e4
175187 if type is not None and (type is not INSNavGeod and type is not GNSSReading ):
176188 raise ValueError ("GEMHardwareInterface GEM e4 only supports INSNavGeod/GNSSReading for GNSS" )
177189 if type is INSNavGeod :
178- self .gnss_sub = rospy . Subscriber ( topic , INSNavGeod , callback )
190+ self .gnss_sub = self . node . create_subscription ( INSNavGeod , topic , callback , qos_profile )
179191 else :
180192 def callback_with_gnss_reading (msg : INSNavGeod ):
181193 pose = ObjectPose (ObjectFrameEnum .GLOBAL ,
@@ -189,47 +201,47 @@ def callback_with_gnss_reading(msg: INSNavGeod):
189201 )
190202 speed = np .sqrt (msg .ve ** 2 + msg .vn ** 2 )
191203 callback (GNSSReading (pose ,speed ,('error' if msg .error else 'ok' )))
192- self .gnss_sub = rospy . Subscriber ( topic , INSNavGeod , callback_with_gnss_reading )
204+ self .gnss_sub = self . node . create_subscription ( INSNavGeod , topic , callback_with_gnss_reading , qos_profile )
193205 elif name == 'top_lidar' :
194206 topic = self .ros_sensor_topics [name ]
195207 if type is not None and (type is not PointCloud2 and type is not np .ndarray ):
196208 raise ValueError ("GEMHardwareInterface only supports PointCloud2 or numpy array for top lidar" )
197209 if type is None or type is PointCloud2 :
198- self .top_lidar_sub = rospy . Subscriber ( topic , PointCloud2 , callback )
210+ self .top_lidar_sub = self . node . create_subscription ( PointCloud2 , topic , callback , qos_profile )
199211 else :
200212 def callback_with_numpy (msg : Image ):
201213 #print("received image with size",msg.width,msg.height,"encoding",msg.encoding)
202214 points = conversions .ros_PointCloud2_to_numpy (msg , want_rgb = False )
203215 callback (points )
204- self .top_lidar_sub = rospy . Subscriber ( topic , PointCloud2 , callback_with_numpy )
216+ self .top_lidar_sub = self . node . create_subscription ( PointCloud2 , topic , callback_with_numpy , qos_profile )
205217 elif name == 'front_radar' :
206218 if type is not None and type is not RadarTracks :
207219 raise ValueError ("GEMHardwareInterface only supports RadarTracks for front radar" )
208- self .front_radar_sub = rospy . Subscriber ( "/front_radar/front_radar/radar_tracks" , RadarTracks , callback )
220+ self .front_radar_sub = self . node . create_subscription ( RadarTracks , "/front_radar/front_radar/radar_tracks" , callback , qos_profile )
209221 elif name == 'front_camera' :
210222 topic = self .ros_sensor_topics [name ]
211223 if type is not None and (type is not Image and type is not cv2 .Mat ):
212224 raise ValueError ("GEMHardwareInterface only supports Image or OpenCV for front camera" )
213225 if type is None or type is Image :
214- self .front_camera_sub = rospy . Subscriber ( topic , Image , callback )
226+ self .front_camera_sub = self . node . create_subscription ( Image , topic , callback , qos_profile )
215227 else :
216228 def callback_with_cv2 (msg : Image ):
217229 #print("received image with size",msg.width,msg.height,"encoding",msg.encoding)
218230 cv_image = conversions .ros_Image_to_cv2 (msg , desired_encoding = "bgr8" )
219231 callback (cv_image )
220- self .front_camera_sub = rospy . Subscriber ( topic , Image , callback_with_cv2 )
232+ self .front_camera_sub = self . node . create_subscription ( Image , topic , callback_with_cv2 , qos_profile )
221233 elif name == 'front_depth' :
222234 topic = self .ros_sensor_topics [name ]
223235 if type is not None and (type is not Image and type is not cv2 .Mat ):
224236 raise ValueError ("GEMHardwareInterface only supports Image or OpenCV for front depth" )
225237 if type is None or type is Image :
226- self .front_depth_sub = rospy . Subscriber ( topic , Image , callback )
238+ self .front_depth_sub = self . node . create_subscription ( Image , topic , callback , qos_profile )
227239 else :
228240 def callback_with_cv2 (msg : Image ):
229241 #print("received image with size",msg.width,msg.height,"encoding",msg.encoding)
230242 cv_image = conversions .ros_Image_to_cv2 (msg , desired_encoding = "passthrough" )
231243 callback (cv_image )
232- self .front_depth_sub = rospy . Subscriber ( topic , Image , callback_with_cv2 )
244+ self .front_depth_sub = self . node . create_subscription ( Image , topic , callback_with_cv2 , qos_profile )
233245
234246
235247 # PACMod enable callback function
0 commit comments