Skip to content

Commit de4d6d0

Browse files
Adding new files for state machine logic
1 parent 4d99bb7 commit de4d6d0

4 files changed

Lines changed: 461 additions & 0 deletions

File tree

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
from ...utils import settings,config
2+
from ..component import Component
3+
from .execution import EXECUTION_PREFIX,ExecutorBase,ComponentExecutor,load_computation_graph,make_class
4+
import multiprocessing
5+
from typing import Dict,List,Optional
6+
import sys
7+
import os
8+
9+
def main():
10+
"""The main entrypoint for the execution stack."""
11+
#first, process settings variants
12+
if settings.get('variant',None) is not None:
13+
variant = settings.get('variant')
14+
variants = variant.split(',')
15+
def caution_callback(k,variant):
16+
print(EXECUTION_PREFIX,"variant {} defines key {} which is not found in normal settings".format(variant,k))
17+
for v in variants:
18+
if v not in settings.get('variants',{}):
19+
if v not in settings.get('run.variants',{}):
20+
print(EXECUTION_PREFIX,"Error, variant",v,"not found in settings")
21+
print(EXECUTION_PREFIX,"Available variants are",list(settings.get('variants',{}).keys())+list(settings.get('run.variants',{}).keys()))
22+
return 1
23+
else:
24+
overrides = settings.get('run.variants.'+v)
25+
print(EXECUTION_PREFIX,"APPYING VARIANT",overrides)
26+
config.update_recursive(settings.settings(),overrides,lambda k:caution_callback(k,v))
27+
else:
28+
overrides = settings.get('variants.'+v)
29+
print(EXECUTION_PREFIX,"APPYING VARIANT",overrides)
30+
config.update_recursive(settings.settings(),overrides,lambda k:caution_callback(k,v))
31+
#don't need this to be saved in the log
32+
if 'variants' in settings.settings():
33+
del settings.settings()['variants']
34+
if 'svariant' in settings.get('run'):
35+
del settings.get('run')['variants']
36+
37+
multiprocessing.set_start_method('spawn')
38+
runconfig = settings.get('run')
39+
mode = settings.get('run.mode')
40+
vehicle_interface_settings = settings.get('run.vehicle_interface')
41+
mission_executor_settings = settings.get('run.mission_execution')
42+
log_settings = settings.get('run.log',{})
43+
replay_settings = settings.get('run.replay',{})
44+
load_computation_graph()
45+
46+
#initialize ros node
47+
has_ros = False
48+
try:
49+
import rospy
50+
rospy.init_node('GEM_executor',disable_signals=True)
51+
has_ros = True
52+
except (ImportError,ModuleNotFoundError):
53+
if mode == 'simulation':
54+
print(EXECUTION_PREFIX,"Warning, ROS not found, but simulation mode requested")
55+
else:
56+
print(EXECUTION_PREFIX,"Error, ROS not found on system.")
57+
raise
58+
59+
#create top-level components
60+
vehicle_interface = make_class(vehicle_interface_settings,'default','GEMstack.onboard.interface')
61+
mission_executor = make_class(mission_executor_settings,'execution','GEMstack.onboard.execution',{'vehicle_interface':vehicle_interface})
62+
if not isinstance(mission_executor,ExecutorBase):
63+
raise ValueError("Mission executor must be an Executor")
64+
65+
recovery_pipeline_settings = settings.get('run.recovery')
66+
visualization_settings = settings.get('run.visualization',None)
67+
state_settings = settings.get('run.state')
68+
69+
# create pipelines and add to executor
70+
pipeline_settings = {'recovery': recovery_pipeline_settings}
71+
72+
for k,v in runconfig.items():
73+
if isinstance(v,dict) and ('perception' in v or 'planning' in v or 'other' in v):
74+
# possible pipeline
75+
if k not in pipeline_settings:
76+
print(EXECUTION_PREFIX,"Adding non-standard pipeline",k)
77+
pipeline_settings[k] = v
78+
79+
visualizers = []
80+
if isinstance(visualization_settings,dict):
81+
#one visualizer
82+
visualizers.append(mission_executor.make_component(visualization_settings,'visualization','GEMstack.onboard.visualization',{'vehicle_interface':vehicle_interface}))
83+
elif isinstance(visualization_settings,list):
84+
#multiple visualizers
85+
for v in visualization_settings:
86+
visualizers.append(mission_executor.make_component(v,'visualization','GEMstack.onboard.visualization',{'vehicle_interface':vehicle_interface}))
87+
for v in visualizers:
88+
mission_executor.always_run(v.c.__class__.__name__,v)
89+
90+
# mark the components that will be replayed rather than run
91+
if replay_settings:
92+
logfolder = replay_settings.get('log',None)
93+
if logfolder is not None:
94+
replay_components = replay_settings.get('components',[])
95+
mission_executor.replay_components(replay_components,logfolder)
96+
97+
#TODO: ROS topic replay
98+
logmeta = config.load_config_recursive(os.path.join(logfolder,'meta.yaml'))
99+
replay_topics = replay_settings.get('ros_topics',[])
100+
101+
#TODO: launch a roslog replay of the topics in ros_topics, disable in the vehicle interface
102+
103+
for state in state_settings:
104+
pipeline_settings[state] = settings.get('run.' + state)
105+
106+
for (name,s) in pipeline_settings.items():
107+
perception_settings = s.get('perception',{})
108+
planning_settings = s.get('planning',{})
109+
other_settings = s.get('other',{})
110+
111+
perception_components = {} #type: Dict[str,ComponentExecutor]
112+
for (k,v) in perception_settings.items():
113+
if v is None: continue
114+
perception_components[k] = mission_executor.make_component(v,k,'GEMstack.onboard.perception', {'vehicle_interface':vehicle_interface})
115+
planning_components = {} #type: Dict[str,ComponentExecutor]
116+
for (k,v) in planning_settings.items():
117+
if v is None: continue
118+
planning_components[k] = mission_executor.make_component(v,k,'GEMstack.onboard.planning', {'vehicle_interface':vehicle_interface})
119+
other_components = {} #type: Dict[str,ComponentExecutor]
120+
for (k,v) in other_settings.items():
121+
if v is None: continue
122+
other_components[k] = mission_executor.make_component(v,k,'GEMstack.onboard.other', {'vehicle_interface':vehicle_interface})
123+
124+
mission_executor.add_pipeline(name, perception_components, planning_components, other_components)
125+
126+
#configure logging
127+
if log_settings:
128+
topfolder = log_settings.get('folder','logs')
129+
prefix = log_settings.get('prefix','')
130+
from datetime import datetime
131+
default_suffix = datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
132+
suffix = log_settings.get('suffix',default_suffix)
133+
logfolder = os.path.join(topfolder,prefix+suffix)
134+
print(EXECUTION_PREFIX,"Logging to",logfolder)
135+
auto_plot = log_settings.get("auto_plot", False)
136+
os.makedirs(logfolder,exist_ok=True)
137+
138+
#configure logging for components
139+
mission_executor.set_log_folder(logfolder)
140+
mission_executor.set_auto_plot(auto_plot)
141+
#configure ROS logging
142+
log_topics = log_settings.get('ros_topics',[])
143+
rosbag_options = log_settings.get('rosbag_options','')
144+
mission_executor.log_ros_topics(log_topics, rosbag_options)
145+
#determine whether to log vehicle interface
146+
log_vehicle_interface = log_settings.get('vehicle_interface',False)
147+
if log_vehicle_interface:
148+
mission_executor.log_vehicle_interface(log_vehicle_interface)
149+
#determine whether to log components
150+
log_components = log_settings.get('components',[])
151+
mission_executor.log_components(log_components)
152+
#determine whether to log state
153+
log_state_attributes = log_settings.get('state',[])
154+
log_state_rate = log_settings.get('state_rate',None)
155+
if log_state_attributes:
156+
mission_executor.log_state(log_state_attributes,log_state_rate)
157+
158+
vehicle_interface.start()
159+
try:
160+
mission_executor.run()
161+
except Exception as e:
162+
raise
163+
finally:
164+
vehicle_interface.stop()
165+
166+
if has_ros:
167+
#need manual ros node shutdown due to disable_signals=True
168+
rospy.signal_shutdown('GEM_executor finished')
169+
170+
print(EXECUTION_PREFIX,"---------------- DONE ----------------")
171+
if log_settings and settings.get('run.after.show_log_folder',False):
172+
if sys.platform.startswith('linux'):
173+
os.system('nautilus '+logfolder)
174+
else:
175+
import webbrowser
176+
webbrowser.open(logfolder)
177+
return 0
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
from pydantic import BaseModel, Field
2+
from typing import List, Tuple, Union
3+
from ..component import Component
4+
from ...state import AllState, VehicleState, EntityRelation, EntityRelationEnum, Path, Trajectory, Route, ObjectFrameEnum, AgentState
5+
import requests
6+
7+
8+
### MODELS ###
9+
10+
class BoundingBox(BaseModel):
11+
x1: float = Field(..., ge=-90, le=90)
12+
x2: float = Field(..., ge=-90, le=90)
13+
y1: float = Field(..., ge=-90, le=90)
14+
y2: float = Field(..., ge=-90, le=90)
15+
16+
17+
class BoundingBoxRequester(Component):
18+
"""Follows the given route. Brakes if you have to yield or
19+
you are at the end of the route, otherwise accelerates to
20+
the desired speed.
21+
"""
22+
23+
def __init__(self, mode : str = 'real', params : dict = {"api_endpoint": "xxxx"}):
24+
self.api_endpoint = params['api_endpoint']
25+
self.bounding_box = None
26+
27+
def state_inputs(self):
28+
return ['all']
29+
30+
def state_outputs(self) -> List[str]:
31+
return self.bounding_box
32+
33+
def rate(self):
34+
return 10.0
35+
36+
def update(self, state: AllState):
37+
# request the bounding box, if found then update and then switch the state
38+
if self.bounding_box:
39+
return
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
tateimport numpy as np
2+
import matplotlib.pyplot as plt
3+
4+
5+
def is_inside_geofence(x, y, xmin, xmax, ymin, ymax):
6+
return xmin < x < xmax and ymin < y < ymax
7+
8+
9+
def max_visible_arc(circle_center, radius, geofence):
10+
xc, yc = circle_center
11+
(xmin, ymin), (xmax, ymax) = geofence
12+
13+
angles = np.linspace(0, 2 * np.pi, 500, endpoint=False)
14+
arc_segments = []
15+
curr_segment = []
16+
17+
first_inside = last_inside = False
18+
19+
for i, theta in enumerate(angles):
20+
x = xc + radius * np.cos(theta)
21+
y = yc + radius * np.sin(theta)
22+
23+
inside = is_inside_geofence(x, y, xmin, xmax, ymin, ymax)
24+
25+
if i == 0:
26+
first_inside = inside
27+
if i == len(angles) - 1:
28+
last_inside = inside
29+
30+
if inside:
31+
curr_segment.append((x, y))
32+
else:
33+
if curr_segment:
34+
arc_segments.append(curr_segment)
35+
curr_segment = []
36+
37+
if curr_segment:
38+
arc_segments.append(curr_segment)
39+
40+
# If arc wraps around from 2π back to 0, combine first and last segments
41+
if first_inside and last_inside and len(arc_segments) > 1:
42+
arc_segments[0] = arc_segments[-1] + arc_segments[0]
43+
arc_segments.pop()
44+
45+
if not arc_segments:
46+
return []
47+
48+
max_arc = max(arc_segments, key=len)
49+
return max_arc
50+
51+
52+
def plot_circle_with_geofence_and_arc(circle_center, radius, geofence, rem_pts):
53+
fig, ax = plt.subplots()
54+
55+
# Plot geofence
56+
(xmin, ymin), (xmax, ymax) = geofence
57+
rect = plt.Rectangle((xmin, ymin), xmax - xmin, ymax - ymin, linewidth=2, edgecolor='red', facecolor='none')
58+
ax.add_patch(rect)
59+
60+
rem_pts = np.array(rem_pts)
61+
ax.plot(rem_pts[:, 0], rem_pts[:, 1], color='blue', linewidth=3, label='Max Valid Arc')
62+
63+
ax.set_aspect('equal')
64+
ax.grid(True)
65+
plt.legend()
66+
plt.show()
67+
68+
69+
circle_center = (5, 6)
70+
radius = 0.5
71+
geofence = ((0, 0), (8, 8))
72+
73+
rem_points = max_visible_arc(circle_center, radius, geofence)
74+
plot_circle_with_geofence_and_arc(circle_center, radius, geofence, rem_points)

0 commit comments

Comments
 (0)