Skip to content

Commit 5f785f7

Browse files
fix ros2 node spin and alvinxy
1 parent 0a4cebf commit 5f785f7

6 files changed

Lines changed: 83 additions & 29 deletions

File tree

GEMstack/knowledge/vehicle/gem_e4_sensor_environment.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@ ros_topics:
22
top_lidar: /ouster/points
33
front_camera: /oak/rgb/image_raw
44
front_depth: /oak/stereo/image_raw
5-
gnss: /septentrio_gnss/insnavgeod
5+
gnss: /insnavgeod
66

GEMstack/mathutils/transforms.py

Lines changed: 46 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
21
import numpy as np
32
from klampt.math import vectorops as vo
43
from klampt.math import so2
54
from typing import Tuple
5+
import math
66

77
def normalize_angle(angle : float) -> float:
88
"""Normalizes an angle to be in the range [0,2pi]"""
@@ -112,24 +112,54 @@ def yaw_to_heading(yaw : float, degrees=True) -> float:
112112
else:
113113
return np.radians(heading_degrees)
114114

115-
def lat_lon_to_xy(lat : float, lon : float, lat_reference : float, lon_reference : float):
116-
""" Conversion of Lat & Lon to X & Y.
117-
118-
Returns (x,y), where x is east in m, y is north in m.
115+
def lat_lon_to_xy(lat: float, lon: float, lat_ref: float, lon_ref: float) -> Tuple[float, float]:
116+
"""Convert geographic coordinates to local Cartesian coordinates.
117+
118+
Args:
119+
lat, lon: Target point (degrees)
120+
lat_ref, lon_ref: Reference origin point (degrees)
121+
122+
Returns:
123+
(east_x, north_y) in meters
119124
"""
120-
import alvinxy.alvinxy as axy
121-
# convert GNSS waypoints into local fixed frame reprented in x and y
122-
east_x, north_y = axy.ll2xy(lat, lon, lat_reference, lon_reference)
123-
return east_x, north_y
124-
125-
def xy_to_lat_lon(x_east : float, y_north : float, lat_reference : float, lon_reference : float):
126-
""" Conversion of X & Y to Lat & Lon.
125+
# Earth radius in meters
126+
R = 6371000.0
127+
128+
# Convert reference latitude to radians
129+
lat_ref_rad = math.radians(lat_ref)
130+
131+
# Calculate deltas in degrees
132+
delta_lat = lat - lat_ref
133+
delta_lon = lon - lon_ref
134+
135+
# Convert to meters
136+
x = R * math.radians(delta_lon) * math.cos(lat_ref_rad)
137+
y = R * math.radians(delta_lat)
138+
139+
return x, y
127140

128-
Returns (lat,lon), where lat and lon are in degrees.
141+
def xy_to_lat_lon(x: float, y: float, lat_ref: float, lon_ref: float) -> Tuple[float, float]:
142+
"""Convert local Cartesian coordinates back to geographic coordinates.
143+
144+
Args:
145+
x: Easting in meters
146+
y: Northing in meters
147+
lat_ref, lon_ref: Reference origin point (degrees)
148+
149+
Returns:
150+
(latitude, longitude) in degrees
129151
"""
130-
import alvinxy.alvinxy as axy
131-
# convert GNSS waypoints into local fixed frame reprented in x and y
132-
lat, lon = axy.xy2ll(x_east, y_north, lat_reference, lon_reference)
152+
R = 6371000.0
153+
lat_ref_rad = math.radians(lat_ref)
154+
155+
# Convert meters to degrees
156+
delta_lon = x / (R * math.cos(lat_ref_rad))
157+
delta_lat = y / R
158+
159+
# Calculate final coordinates
160+
lat = lat_ref + math.degrees(delta_lat)
161+
lon = lon_ref + math.degrees(delta_lon)
162+
133163
return lat, lon
134164

135165
def quaternion_to_euler(x : float, y : float, z : float, w : float):

GEMstack/onboard/execution/entrypoint.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,16 +47,17 @@ def caution_callback(k,variant):
4747
has_ros = False
4848
try:
4949
import rclpy
50+
from rclpy.executors import MultiThreadedExecutor
51+
import threading
5052
rclpy.init(args=sys.argv)
51-
rclpy.create_node('GEM_executor')#,disable_signals=True)
5253
has_ros = True
5354
except (ImportError,ModuleNotFoundError):
55+
5456
if mode == 'simulation':
5557
print(EXECUTION_PREFIX,"Warning, ROS not found, but simulation mode requested")
5658
else:
5759
print(EXECUTION_PREFIX,"Error, ROS not found on system.")
5860
raise
59-
6061
#create top-level components
6162
vehicle_interface = make_class(vehicle_interface_settings,'default','GEMstack.onboard.interface')
6263
mission_executor = make_class(mission_executor_settings,'execution','GEMstack.onboard.execution',{'vehicle_interface':vehicle_interface})
@@ -160,8 +161,10 @@ def caution_callback(k,variant):
160161
vehicle_interface.stop()
161162

162163
if has_ros:
163-
#need manual ros node shutdown due to disable_signals=True
164-
rclpy.shutdown()
164+
executor = MultiThreadedExecutor()
165+
executor.add_node(vehicle_interface.node)
166+
executor_thread = threading.Thread(target=executor.spin, daemon=True)
167+
executor_thread.start()
165168

166169
print(EXECUTION_PREFIX,"---------------- DONE ----------------")
167170
if log_settings and settings.get('run.after.show_log_folder',False):

GEMstack/onboard/interface/gem_hardware.py

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
from rclpy.clock import Clock
1010
from std_msgs.msg import String, Bool, Float32, Float64
1111
from sensor_msgs.msg import Image,PointCloud2
12+
13+
import threading
14+
from rclpy.executors import MultiThreadedExecutor
15+
1216
try:
1317
from novatel_gps_msgs.msg import NovatelPosition, NovatelXYZ, Inspva
1418
except ImportError:
@@ -29,7 +33,7 @@
2933
import numpy as np
3034
from ...utils import conversions
3135

32-
from rclpy.qos import QoSProfile, QoSReliabilityPolicy, QoSHistoryPolicy
36+
from rclpy.qos import QoSProfile, QoSReliabilityPolicy, QoSHistoryPolicy, QoSDurabilityPolicy, QoSLivelinessPolicy
3337

3438
# Define a default QoS profile
3539
qos_profile = QoSProfile(
@@ -116,6 +120,10 @@ def __init__(self):
116120

117121
#subscribers should go last because the callback might be called before the object is initialized
118122
self.enable_sub = self.node.create_subscription(Bool, '/pacmod/as_tx/enable', self.pacmod_enable_callback, qos_profile)
123+
executor = MultiThreadedExecutor()
124+
executor.add_node(self.node)
125+
executor_thread = threading.Thread(target=executor.spin, daemon=True)
126+
executor_thread.start()
119127

120128

121129
def start(self):
@@ -184,6 +192,7 @@ def callback_with_gnss_reading(inspva_msg: Inspva):
184192
self.gnss_sub = self.node.create_subscription(Inspva, topic, callback_with_gnss_reading, qos_profile)
185193
else:
186194
#assume it's septentrio on GEM e4
195+
print("type",type)
187196
if type is not None and (type is not INSNavGeod and type is not GNSSReading):
188197
raise ValueError("GEMHardwareInterface GEM e4 only supports INSNavGeod/GNSSReading for GNSS")
189198
if type is INSNavGeod:
@@ -201,7 +210,18 @@ def callback_with_gnss_reading(msg: INSNavGeod):
201210
)
202211
speed = np.sqrt(msg.ve**2 + msg.vn**2)
203212
callback(GNSSReading(pose,speed,('error' if msg.error else 'ok')))
204-
self.gnss_sub = self.node.create_subscription(INSNavGeod, topic, callback_with_gnss_reading, qos_profile)
213+
# gnss_qos_profile = QoSProfile(
214+
# reliability=QoSReliabilityPolicy.RELIABLE,
215+
# history=QoSHistoryPolicy.KEEP_LAST,
216+
# depth=10,
217+
# durability=QoSDurabilityPolicy.VOLATILE,
218+
# liveliness=QoSLivelinessPolicy.AUTOMATIC
219+
# )
220+
self.gnss_sub = self.node.create_subscription(INSNavGeod, topic, callback_with_gnss_reading, QoSProfile(
221+
reliability=QoSReliabilityPolicy.RELIABLE,
222+
durability=QoSDurabilityPolicy.VOLATILE,
223+
depth=10
224+
))
205225
elif name == 'top_lidar':
206226
topic = self.ros_sensor_topics[name]
207227
if type is not None and (type is not PointCloud2 and type is not np.ndarray):
@@ -210,7 +230,7 @@ def callback_with_gnss_reading(msg: INSNavGeod):
210230
self.top_lidar_sub = self.node.create_subscription(PointCloud2, topic, callback, qos_profile)
211231
else:
212232
def callback_with_numpy(msg : Image):
213-
#print("received image with size",msg.width,msg.height,"encoding",msg.encoding)
233+
# print("received image with size",msg.width,msg.height,"encoding",msg.encoding)
214234
points = conversions.ros_PointCloud2_to_numpy(msg, want_rgb=False)
215235
callback(points)
216236
self.top_lidar_sub = self.node.create_subscription(PointCloud2, topic, callback_with_numpy, qos_profile)
@@ -305,7 +325,8 @@ def send_command(self, command : GEMVehicleCommand):
305325
self.accel_cmd.f64_cmd = command.accelerator_pedal_position
306326
if command.brake_pedal_position > 0.0:
307327
self.accel_cmd.f64_cmd = 0.0
308-
self.brake_cmd.f64_cmd = command.brake_pedal_position
328+
print("command.brake_pedal_position",command.brake_pedal_position)
329+
self.brake_cmd.f64_cmd = float(command.brake_pedal_position)
309330
self.steer_cmd.angular_position = command.steering_wheel_angle
310331
self.steer_cmd.angular_velocity_limit = command.steering_wheel_speed
311332
print("**************************")

GEMstack/onboard/perception/state_estimation.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ def state_outputs(self) -> List[str]:
3737
return ['vehicle']
3838

3939
def healthy(self):
40-
#return self.gnss_pose is not None
41-
return True
40+
return self.gnss_pose is not None
41+
# return True
4242

4343
def update(self) -> VehicleState:
4444
if self.gnss_pose is None:

GEMstack/onboard/planning/recovery.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ def state_outputs(self):
2020

2121
def update(self):
2222
print("Stopping, current speed %.3f m/s"%(self.vehicle_interface.last_reading.speed))
23-
brake_amount = settings.get('control.recovery.brake_amount')
24-
brake_speed = settings.get('control.recovery.brake_speed')
23+
brake_amount = float(settings.get('control.recovery.brake_amount'))
24+
brake_speed = float(settings.get('control.recovery.brake_speed'))
2525
if self.vehicle_interface.last_command is not None:
2626
cmd = copy.copy(self.vehicle_interface.last_command)
2727
cmd.accelerator_pedal_position = 0.0

0 commit comments

Comments
 (0)