Reality123b commited on
Commit
c5ea85f
·
verified ·
1 Parent(s): d7f4b72

Add config.py

Browse files
Files changed (1) hide show
  1. fsd_model/config.py +352 -0
fsd_model/config.py ADDED
@@ -0,0 +1,352 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Configurable sensor and vehicle configuration for the FSD model.
3
+ Supports arbitrary sensor counts, types, and placements.
4
+ """
5
+
6
+ import math
7
+ import json
8
+ from dataclasses import dataclass, field, asdict
9
+ from typing import List, Optional, Tuple, Dict, Any
10
+ from enum import Enum
11
+
12
+
13
+ class SensorType(Enum):
14
+ CAMERA = "camera"
15
+ ULTRASONIC = "ultrasonic"
16
+ LIDAR = "lidar"
17
+ RADAR = "radar"
18
+
19
+
20
+ class CameraPosition(Enum):
21
+ FRONT_LEFT = "front_left"
22
+ FRONT_RIGHT = "front_right"
23
+ REAR_LEFT = "rear_left"
24
+ REAR_RIGHT = "rear_right"
25
+ LEFT_MIRROR = "left_mirror"
26
+ RIGHT_MIRROR = "right_mirror"
27
+ FRONT_CENTER = "front_center"
28
+ REAR_CENTER = "rear_center"
29
+ ROOF_FRONT = "roof_front"
30
+ ROOF_REAR = "roof_rear"
31
+ BUMPER_LEFT = "bumper_left"
32
+ BUMPER_RIGHT = "bumper_right"
33
+
34
+
35
+ class UltrasonicZone(Enum):
36
+ FRONT_LEFT_CORNER = "front_left_corner"
37
+ FRONT_LEFT = "front_left"
38
+ FRONT_CENTER_LEFT = "front_center_left"
39
+ FRONT_CENTER = "front_center"
40
+ FRONT_CENTER_RIGHT = "front_center_right"
41
+ FRONT_RIGHT = "front_right"
42
+ FRONT_RIGHT_CORNER = "front_right_corner"
43
+ LEFT_FRONT = "left_front"
44
+ LEFT_CENTER = "left_center"
45
+ LEFT_REAR = "left_rear"
46
+ RIGHT_FRONT = "right_front"
47
+ RIGHT_CENTER = "right_center"
48
+ RIGHT_REAR = "right_rear"
49
+ REAR_LEFT_CORNER = "rear_left_corner"
50
+ REAR_LEFT = "rear_left"
51
+ REAR_CENTER_LEFT = "rear_center_left"
52
+ REAR_CENTER = "rear_center"
53
+ REAR_CENTER_RIGHT = "rear_center_right"
54
+ REAR_RIGHT = "rear_right"
55
+ REAR_RIGHT_CORNER = "rear_right_corner"
56
+
57
+
58
+ @dataclass
59
+ class SensorPlacement:
60
+ x: float
61
+ y: float
62
+ z: float
63
+ yaw: float = 0.0
64
+ pitch: float = 0.0
65
+ roll: float = 0.0
66
+
67
+ def to_transform_matrix(self):
68
+ import numpy as np
69
+ yaw_r = math.radians(self.yaw)
70
+ pitch_r = math.radians(self.pitch)
71
+ roll_r = math.radians(self.roll)
72
+ Rz = np.array([
73
+ [math.cos(yaw_r), -math.sin(yaw_r), 0],
74
+ [math.sin(yaw_r), math.cos(yaw_r), 0],
75
+ [0, 0, 1]
76
+ ])
77
+ Ry = np.array([
78
+ [math.cos(pitch_r), 0, math.sin(pitch_r)],
79
+ [0, 1, 0],
80
+ [-math.sin(pitch_r), 0, math.cos(pitch_r)]
81
+ ])
82
+ Rx = np.array([
83
+ [1, 0, 0],
84
+ [0, math.cos(roll_r), -math.sin(roll_r)],
85
+ [0, math.sin(roll_r), math.cos(roll_r)]
86
+ ])
87
+ R = Rz @ Ry @ Rx
88
+ T = np.eye(4)
89
+ T[:3, :3] = R
90
+ T[:3, 3] = [self.x, self.y, self.z]
91
+ return T
92
+
93
+
94
+ @dataclass
95
+ class CameraSensorConfig:
96
+ name: str
97
+ position: CameraPosition
98
+ placement: SensorPlacement
99
+ resolution: Tuple[int, int] = (640, 480)
100
+ fov_horizontal: float = 120.0
101
+ fov_vertical: float = 90.0
102
+ fps: int = 30
103
+ encoding: str = "rgb"
104
+ distortion_model: str = "pinhole"
105
+ fx: Optional[float] = None
106
+ fy: Optional[float] = None
107
+ cx: Optional[float] = None
108
+ cy: Optional[float] = None
109
+
110
+ def __post_init__(self):
111
+ if self.fx is None:
112
+ self.fx = self.resolution[0] / (2 * math.tan(math.radians(self.fov_horizontal / 2)))
113
+ if self.fy is None:
114
+ self.fy = self.resolution[1] / (2 * math.tan(math.radians(self.fov_vertical / 2)))
115
+ if self.cx is None:
116
+ self.cx = self.resolution[0] / 2
117
+ if self.cy is None:
118
+ self.cy = self.resolution[1] / 2
119
+
120
+ def get_intrinsic_matrix(self):
121
+ import numpy as np
122
+ return np.array([
123
+ [self.fx, 0, self.cx],
124
+ [0, self.fy, self.cy],
125
+ [0, 0, 1]
126
+ ])
127
+
128
+
129
+ @dataclass
130
+ class UltrasonicSensorConfig:
131
+ name: str
132
+ zone: UltrasonicZone
133
+ placement: SensorPlacement
134
+ max_range: float = 5.0
135
+ min_range: float = 0.02
136
+ beam_angle: float = 30.0
137
+ frequency: float = 40000.0
138
+ update_rate: float = 20.0
139
+ accuracy: float = 0.01
140
+ noise_std: float = 0.005
141
+
142
+
143
+ @dataclass
144
+ class SensorConfig:
145
+ cameras: List[CameraSensorConfig] = field(default_factory=list)
146
+ ultrasonics: List[UltrasonicSensorConfig] = field(default_factory=list)
147
+
148
+ @property
149
+ def num_cameras(self) -> int:
150
+ return len(self.cameras)
151
+
152
+ @property
153
+ def num_ultrasonics(self) -> int:
154
+ return len(self.ultrasonics)
155
+
156
+ @property
157
+ def total_sensors(self) -> int:
158
+ return self.num_cameras + self.num_ultrasonics
159
+
160
+ def validate(self):
161
+ issues = []
162
+ if self.num_cameras == 0:
163
+ issues.append("WARNING: No cameras configured")
164
+ if self.num_ultrasonics == 0:
165
+ issues.append("WARNING: No ultrasonic sensors configured")
166
+ yaw_angles = sorted([c.placement.yaw for c in self.cameras])
167
+ if len(yaw_angles) >= 2:
168
+ gaps = []
169
+ for i in range(len(yaw_angles)):
170
+ next_i = (i + 1) % len(yaw_angles)
171
+ gap = (yaw_angles[next_i] - yaw_angles[i]) % 360
172
+ if gap == 0 and next_i != 0:
173
+ continue
174
+ gaps.append(gap)
175
+ max_gap = max(gaps) if gaps else 360
176
+ if max_gap > 120:
177
+ issues.append(f"WARNING: Camera coverage gap of {max_gap:.0f} degrees")
178
+ return issues
179
+
180
+ def get_sensor_summary(self) -> str:
181
+ lines = [f"Sensor Configuration Summary:"]
182
+ lines.append(f" Total sensors: {self.total_sensors}")
183
+ lines.append(f" Cameras: {self.num_cameras}")
184
+ for cam in self.cameras:
185
+ lines.append(f" - {cam.name}: {cam.position.value} | {cam.resolution[0]}x{cam.resolution[1]} | FOV: {cam.fov_horizontal}")
186
+ lines.append(f" Ultrasonic sensors: {self.num_ultrasonics}")
187
+ for us in self.ultrasonics:
188
+ lines.append(f" - {us.name}: {us.zone.value} | Range: {us.min_range}-{us.max_range}m")
189
+ issues = self.validate()
190
+ if issues:
191
+ lines.append(" Validation issues:")
192
+ for issue in issues:
193
+ lines.append(f" {issue}")
194
+ return "\n".join(lines)
195
+
196
+ def to_dict(self) -> Dict[str, Any]:
197
+ return asdict(self)
198
+
199
+ def save(self, path: str):
200
+ with open(path, 'w') as f:
201
+ json.dump(self.to_dict(), f, indent=2, default=str)
202
+
203
+
204
+ @dataclass
205
+ class VehicleConfig:
206
+ name: str = "FSD_Vehicle"
207
+ length: float = 4.5
208
+ width: float = 1.8
209
+ height: float = 1.5
210
+ wheelbase: float = 2.7
211
+ track_width: float = 1.5
212
+ max_speed_mph: float = 20.0
213
+ max_speed_ms: float = 8.94
214
+ max_steering_angle: float = 35.0
215
+ max_acceleration: float = 3.0
216
+ max_deceleration: float = 8.0
217
+ sensor_config: Optional[SensorConfig] = None
218
+
219
+ def __post_init__(self):
220
+ self.max_speed_ms = self.max_speed_mph * 0.44704
221
+ if self.sensor_config is None:
222
+ self.sensor_config = self._default_sensor_config()
223
+
224
+ def _default_sensor_config(self) -> SensorConfig:
225
+ cameras = self._create_default_cameras()
226
+ ultrasonics = self._create_default_ultrasonics()
227
+ return SensorConfig(cameras=cameras, ultrasonics=ultrasonics)
228
+
229
+ def _create_default_cameras(self) -> List[CameraSensorConfig]:
230
+ half_l = self.length / 2
231
+ half_w = self.width / 2
232
+ mirror_h = 1.1
233
+ cameras = [
234
+ CameraSensorConfig(
235
+ name="cam_front_left", position=CameraPosition.FRONT_LEFT,
236
+ placement=SensorPlacement(x=half_l - 0.3, y=half_w, z=0.8, yaw=-45, pitch=-5),
237
+ resolution=(640, 480), fov_horizontal=120,
238
+ ),
239
+ CameraSensorConfig(
240
+ name="cam_front_right", position=CameraPosition.FRONT_RIGHT,
241
+ placement=SensorPlacement(x=half_l - 0.3, y=-half_w, z=0.8, yaw=45, pitch=-5),
242
+ resolution=(640, 480), fov_horizontal=120,
243
+ ),
244
+ CameraSensorConfig(
245
+ name="cam_rear_left", position=CameraPosition.REAR_LEFT,
246
+ placement=SensorPlacement(x=-half_l + 0.3, y=half_w, z=0.8, yaw=-135, pitch=-5),
247
+ resolution=(640, 480), fov_horizontal=120,
248
+ ),
249
+ CameraSensorConfig(
250
+ name="cam_rear_right", position=CameraPosition.REAR_RIGHT,
251
+ placement=SensorPlacement(x=-half_l + 0.3, y=-half_w, z=0.8, yaw=135, pitch=-5),
252
+ resolution=(640, 480), fov_horizontal=120,
253
+ ),
254
+ CameraSensorConfig(
255
+ name="cam_left_mirror", position=CameraPosition.LEFT_MIRROR,
256
+ placement=SensorPlacement(x=0.8, y=half_w + 0.1, z=mirror_h, yaw=-90, pitch=-10),
257
+ resolution=(640, 480), fov_horizontal=90,
258
+ ),
259
+ CameraSensorConfig(
260
+ name="cam_right_mirror", position=CameraPosition.RIGHT_MIRROR,
261
+ placement=SensorPlacement(x=0.8, y=-(half_w + 0.1), z=mirror_h, yaw=90, pitch=-10),
262
+ resolution=(640, 480), fov_horizontal=90,
263
+ ),
264
+ ]
265
+ return cameras
266
+
267
+ def _create_default_ultrasonics(self) -> List[UltrasonicSensorConfig]:
268
+ half_l = self.length / 2
269
+ half_w = self.width / 2
270
+ bumper_h = 0.4
271
+ zones_front = [
272
+ UltrasonicZone.FRONT_LEFT_CORNER, UltrasonicZone.FRONT_LEFT,
273
+ UltrasonicZone.FRONT_CENTER_LEFT, UltrasonicZone.FRONT_CENTER,
274
+ UltrasonicZone.FRONT_CENTER_RIGHT, UltrasonicZone.FRONT_RIGHT,
275
+ UltrasonicZone.FRONT_RIGHT_CORNER,
276
+ ]
277
+ front_y = [half_w, half_w*0.66, half_w*0.33, 0.0, -half_w*0.33, -half_w*0.66, -half_w]
278
+ front_yaw = [-30, -15, -5, 0, 5, 15, 30]
279
+ ultrasonics = []
280
+ for i, (zone, y_pos, yaw) in enumerate(zip(zones_front, front_y, front_yaw)):
281
+ ultrasonics.append(UltrasonicSensorConfig(
282
+ name=f"us_front_{i}", zone=zone,
283
+ placement=SensorPlacement(x=half_l, y=y_pos, z=bumper_h, yaw=yaw),
284
+ max_range=5.0,
285
+ ))
286
+ zones_rear = [
287
+ UltrasonicZone.REAR_LEFT_CORNER, UltrasonicZone.REAR_LEFT,
288
+ UltrasonicZone.REAR_CENTER_LEFT, UltrasonicZone.REAR_CENTER,
289
+ UltrasonicZone.REAR_CENTER_RIGHT, UltrasonicZone.REAR_RIGHT,
290
+ UltrasonicZone.REAR_RIGHT_CORNER,
291
+ ]
292
+ rear_yaw = [-150, -165, -175, 180, 175, 165, 150]
293
+ for i, (zone, y_pos, yaw) in enumerate(zip(zones_rear, front_y, rear_yaw)):
294
+ ultrasonics.append(UltrasonicSensorConfig(
295
+ name=f"us_rear_{i}", zone=zone,
296
+ placement=SensorPlacement(x=-half_l, y=y_pos, z=bumper_h, yaw=yaw),
297
+ max_range=5.0,
298
+ ))
299
+ zones_left = [UltrasonicZone.LEFT_FRONT, UltrasonicZone.LEFT_CENTER, UltrasonicZone.LEFT_REAR]
300
+ left_x = [half_l * 0.5, 0.0, -half_l * 0.5]
301
+ for i, (zone, x_pos) in enumerate(zip(zones_left, left_x)):
302
+ ultrasonics.append(UltrasonicSensorConfig(
303
+ name=f"us_left_{i}", zone=zone,
304
+ placement=SensorPlacement(x=x_pos, y=half_w, z=bumper_h + 0.2, yaw=-90),
305
+ max_range=3.0,
306
+ ))
307
+ zones_right = [UltrasonicZone.RIGHT_FRONT, UltrasonicZone.RIGHT_CENTER, UltrasonicZone.RIGHT_REAR]
308
+ for i, (zone, x_pos) in enumerate(zip(zones_right, left_x)):
309
+ ultrasonics.append(UltrasonicSensorConfig(
310
+ name=f"us_right_{i}", zone=zone,
311
+ placement=SensorPlacement(x=x_pos, y=-half_w, z=bumper_h + 0.2, yaw=90),
312
+ max_range=3.0,
313
+ ))
314
+ return ultrasonics
315
+
316
+
317
+ def create_custom_config(
318
+ num_cameras=6, num_ultrasonics=20,
319
+ camera_placements=None, ultrasonic_placements=None,
320
+ max_speed_mph=20.0, **vehicle_kwargs
321
+ ) -> VehicleConfig:
322
+ config = VehicleConfig(max_speed_mph=max_speed_mph, **vehicle_kwargs)
323
+ if camera_placements is not None:
324
+ cameras = []
325
+ positions = list(CameraPosition)
326
+ for i, cp in enumerate(camera_placements):
327
+ pos = cp.get("position", positions[i % len(positions)])
328
+ if isinstance(pos, str):
329
+ pos = CameraPosition(pos)
330
+ placement = SensorPlacement(**cp.get("placement", {"x": 0, "y": 0, "z": 1.0}))
331
+ cameras.append(CameraSensorConfig(
332
+ name=cp.get("name", f"cam_{i}"), position=pos, placement=placement,
333
+ resolution=cp.get("resolution", (640, 480)),
334
+ fov_horizontal=cp.get("fov_horizontal", 120),
335
+ fov_vertical=cp.get("fov_vertical", 90),
336
+ ))
337
+ config.sensor_config.cameras = cameras
338
+ if ultrasonic_placements is not None:
339
+ ultrasonics = []
340
+ zones = list(UltrasonicZone)
341
+ for i, up in enumerate(ultrasonic_placements):
342
+ zone = up.get("zone", zones[i % len(zones)])
343
+ if isinstance(zone, str):
344
+ zone = UltrasonicZone(zone)
345
+ placement = SensorPlacement(**up.get("placement", {"x": 0, "y": 0, "z": 0.4}))
346
+ ultrasonics.append(UltrasonicSensorConfig(
347
+ name=up.get("name", f"us_{i}"), zone=zone, placement=placement,
348
+ max_range=up.get("max_range", 5.0),
349
+ beam_angle=up.get("beam_angle", 30.0),
350
+ ))
351
+ config.sensor_config.ultrasonics = ultrasonics
352
+ return config