Skip to content

Commit ec43ea2

Browse files
Merge pull request #184 from krishauser/s2025_Simulation_gnss_sub
Subscribe to gnss topic from gazebo simulation
2 parents 3f9a74e + 45105e5 commit ec43ea2

1 file changed

Lines changed: 34 additions & 62 deletions

File tree

GEMstack/onboard/interface/gem_gazebo.py

Lines changed: 34 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
from .gem import *
22
from ...utils import settings
33
import math
4-
import time
4+
import time
55

66
# ROS Headers
77
import rospy
88
from sensor_msgs.msg import Image, PointCloud2, Imu, NavSatFix
9+
from septentrio_gnss_driver.msg import INSNavGeod
910
from geometry_msgs.msg import Vector3Stamped
1011
from 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

Comments
 (0)