11from .gem import *
22from ...utils import settings
33import math
4- import time
4+ import time
55
66# ROS Headers
77import rospy
88from sensor_msgs .msg import Image , PointCloud2 , Imu , NavSatFix
9+ from septentrio_gnss_driver .msg import INSNavGeod
910from geometry_msgs .msg import Vector3Stamped
1011from sensor_msgs .msg import JointState # For reading joint states from Gazebo
1112# Changed from AckermannDriveStamped
@@ -52,13 +53,9 @@ def __init__(self):
5253 self .last_reading .wiper_level = 0
5354 self .last_reading .headlights_on = False
5455
55-
5656
57-
5857
59- # IMU data subscriber
60- self .imu_sub = None
61- self .imu_data = None
58+
6259
6360 # GNSS data subscriber
6461 self .gnss_sub = None
@@ -77,12 +74,6 @@ def __init__(self):
7774 self .ackermann_cmd = AckermannDrive ()
7875 self .last_command = None # Store the last command
7976
80- # Subscribe to IMU topic by default
81- self .imu_sub = rospy .Subscriber ("/imu" , Imu , self .imu_callback )
82-
83- # Subscribe to GNSS Velocitu
84- self .gnss_vel_sub = rospy .Subscriber ("/gps/fix_velocity" , Vector3Stamped , self .gnss_vel_callback )
85-
8677 # Add clock subscription for simulation time
8778 self .sim_time = rospy .Time (0 )
8879 self .clock_sub = rospy .Subscriber ('/clock' , Clock , self .clock_callback )
@@ -97,33 +88,17 @@ def time(self):
9788 # Return Gazebo simulation time
9889 return self .sim_time .to_sec ()
9990
100- def imu_callback (self , msg : Imu ):
101- self .imu_data = msg
102-
103- def gnss_vel_callback (self , msg ):
104- self .last_reading .speed = np .linalg .norm ([msg .vector .x , msg .vector .y ] )
105-
10691 def get_reading (self ) -> GEMVehicleReading :
10792 return self .last_reading
108-
93+
10994
11095 def subscribe_sensor (self , name , callback , type = None ):
11196 if name == 'gnss' :
112- topic = self .ros_sensor_topics ['gps' ]
113- # Fuse IMU orientation with GNSS position
114- def gnss_callback_wrapper (gps_msg : NavSatFix ):
115- if self .imu_data is None :
116- return # Wait for IMU data
117-
118- # Get orientation from IMU
119- quaternion = (
120- self .imu_data .orientation .x ,
121- self .imu_data .orientation .y ,
122- self .imu_data .orientation .z ,
123- self .imu_data .orientation .w
124- )
125- print (f"[IMU] Orientation: { quaternion } " )
126- roll , pitch , yaw = euler_from_quaternion (quaternion )
97+ topic = self .ros_sensor_topics ['gnss' ]
98+ def gnss_callback_wrapper (gnss_msg : INSNavGeod ):
99+ roll , pitch , yaw = gnss_msg .roll , gnss_msg .pitch , gnss_msg .heading
100+ # Convert from degrees to radians
101+ roll , pitch , yaw = math .radians (roll ), math .radians (pitch ), math .radians (yaw )
127102
128103 # Transform yaw to correct frame - Gazebo typically uses ROS standard frame (x-forward)
129104 # while navigation uses x-east reference frame
@@ -133,31 +108,34 @@ def gnss_callback_wrapper(gps_msg: NavSatFix):
133108 # Convert IMU's yaw to heading (CW from North), then to navigation yaw (CCW from East)
134109 # This handles the coordinate frame differences between Gazebo and the navigation frame
135110 # Negate yaw to convert from ROS to heading
136- heading = transforms .yaw_to_heading (- yaw - np .pi / 2 , degrees = False )
111+ heading = transforms .yaw_to_heading (- yaw - np .pi / 2 , degrees = False )
137112 navigation_yaw = transforms .heading_to_yaw (
138113 heading , degrees = False )
139114
140115 # Create fused pose with transformed yaw
141116 pose = ObjectPose (
142117 frame = ObjectFrameEnum .GLOBAL ,
143- t = gps_msg .header .stamp ,
144- x = gps_msg .longitude ,
145- y = gps_msg .latitude ,
146- z = gps_msg . altitude ,
118+ t = gnss_msg .header .stamp ,
119+ x = gnss_msg .longitude ,
120+ y = gnss_msg .latitude ,
121+ z = gnss_msg . height ,
147122 roll = roll ,
148123 pitch = pitch ,
149124 yaw = navigation_yaw
150125 )
151126
127+ # Calculate speed from GNSS
128+ self .last_reading .speed = np .linalg .norm ([gnss_msg .ve , gnss_msg .vn ])
129+
152130 # Create GNSS reading with fused data
153131 reading = GNSSReading (
154132 pose = pose ,
155- speed = self .last_reading .speed ,
156- status = 'FIX ' if gps_msg . status . status >= 0 else 'NO_FIX '
133+ speed = self .last_reading .speed ,
134+ status = 'error ' if gnss_msg . error else 'ok '
157135 )
158136 # Added debug
159137 print (
160- f"[GNSS] Raw coordinates: Lat={ gps_msg .latitude :.6f} , Lon={ gps_msg .longitude :.6f} " )
138+ f"[GNSS] Raw coordinates: Lat={ gnss_msg .latitude :.6f} , Lon={ gnss_msg .longitude :.6f} " )
161139 # Added debug
162140 print (
163141 f"[GNSS-FUSED] Orientation: Roll={ roll :.2f} , Pitch={ pitch :.2f} , Yaw={ yaw :.2f} rad" )
@@ -166,7 +144,7 @@ def gnss_callback_wrapper(gps_msg: NavSatFix):
166144
167145 callback (reading )
168146
169- self .gnss_sub = rospy .Subscriber (topic , NavSatFix , gnss_callback_wrapper )
147+ self .gnss_sub = rospy .Subscriber (topic , INSNavGeod , gnss_callback_wrapper )
170148
171149 elif name == 'top_lidar' :
172150 topic = self .ros_sensor_topics [name ]
@@ -180,12 +158,6 @@ def callback_with_numpy(msg: PointCloud2):
180158 callback (points )
181159 self .top_lidar_sub = rospy .Subscriber (topic , PointCloud2 , callback_with_numpy )
182160
183- elif name == 'imu' :
184- topic = self .ros_sensor_topics [name ]
185- if type is not None and type is not Imu :
186- raise ValueError ("GEMGazeboInterface only supports Imu for IMU data" )
187- self .imu_sub = rospy .Subscriber (topic , Imu , callback )
188-
189161 elif name == 'front_camera' :
190162 topic = self .ros_sensor_topics [name ]
191163 if type is not None and (type is not Image and type is not cv2 .Mat ):
@@ -222,7 +194,7 @@ def send_command(self, command : GEMVehicleCommand):
222194 # Skip command, similar to hardware interface
223195 return
224196 self .last_command_time = t
225-
197+
226198 # Get current speed
227199 v = self .last_reading .speed
228200
@@ -231,36 +203,36 @@ def send_command(self, command : GEMVehicleCommand):
231203 self .last_reading .accelerator_pedal_position = command .accelerator_pedal_position
232204 self .last_reading .brake_pedal_position = command .brake_pedal_position
233205 self .last_reading .steering_wheel_angle = command .steering_wheel_angle
234-
206+
235207 # Convert pedal to acceleration
236208 accelerator_pedal_position = np .clip (command .accelerator_pedal_position , 0.0 , 1.0 )
237209 brake_pedal_position = np .clip (command .brake_pedal_position , 0.0 , 1.0 )
238-
210+
239211 # Zero out accelerator if brake is active (just like hardware interface)
240212 if brake_pedal_position > 0.0 :
241213 accelerator_pedal_position = 0.0
242-
214+
243215 # Calculate acceleration from pedal positions
244216 acceleration = pedal_positions_to_acceleration (accelerator_pedal_position , brake_pedal_position , v , 0 , 1 )
245-
217+
246218 # Apply reasonable limits to acceleration
247219 max_accel = settings .get ('vehicle.limits.max_acceleration' , 1.0 )
248220 max_decel = settings .get ('vehicle.limits.max_deceleration' , - 2.0 )
249221 acceleration = np .clip (acceleration , max_decel , max_accel )
250-
222+
251223 # Convert wheel angle to steering angle (front wheel angle)
252224 phides = steer2front (command .steering_wheel_angle )
253-
225+
254226 # Apply steering angle limits
255227 min_wheel_angle = settings .get ('vehicle.geometry.min_wheel_angle' , - 0.6 )
256228 max_wheel_angle = settings .get ('vehicle.geometry.max_wheel_angle' , 0.6 )
257229 phides = np .clip (phides , min_wheel_angle , max_wheel_angle )
258-
230+
259231 # Calculate target speed based on acceleration
260232 # Don't use infinite speed, instead calculate a reasonable target speed
261233 current_speed = v
262234 target_speed = current_speed
263-
235+
264236 if acceleration > 0 :
265237 # Accelerating - set target speed to current speed plus some increment
266238 # This is more realistic than infinite speed
@@ -270,16 +242,16 @@ def send_command(self, command : GEMVehicleCommand):
270242 # Braking - set target speed to zero if deceleration is significant
271243 if brake_pedal_position > 0.1 :
272244 target_speed = 0.0
273-
245+
274246 # Create and publish drive message
275247 msg = AckermannDrive ()
276248 msg .acceleration = acceleration
277249 msg .speed = target_speed
278250 msg .steering_angle = phides
279251 msg .steering_angle_velocity = command .steering_wheel_speed # Respect steering velocity limit
280-
252+
281253 # Debug output
282254 print (f"[ACKERMANN] Speed: { msg .speed :.2f} , Accel: { msg .acceleration :.2f} , Steer: { msg .steering_angle :.2f} " )
283-
255+
284256 self .ackermann_pub .publish (msg )
285- self .last_command = command
257+ self .last_command = command
0 commit comments