Skip to content

Commit b778da1

Browse files
authored
dmonitoringmodeld: clean up data structures (#36624)
* update onnx * get meta * start * cast * deprecate notready * more * line too long * 2
1 parent a1795f8 commit b778da1

File tree

7 files changed

+60
-88
lines changed

7 files changed

+60
-88
lines changed

cereal/log.capnp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2166,7 +2166,8 @@ struct DriverStateV2 {
21662166
leftBlinkProb @7 :Float32;
21672167
rightBlinkProb @8 :Float32;
21682168
sunglassesProb @9 :Float32;
2169-
notReadyProb @12 :List(Float32);
2169+
phoneProb @13 :Float32;
2170+
notReadyProbDEPRECATED @12 :List(Float32);
21702171
occludedProbDEPRECATED @10 :Float32;
21712172
readyProbDEPRECATED @11 :List(Float32);
21722173
}

selfdrive/modeld/SConscript

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ lenvCython.Program('models/commonmodel_pyx.so', 'models/commonmodel_pyx.pyx', LI
3232
tinygrad_files = ["#"+x for x in glob.glob(env.Dir("#tinygrad_repo").relpath + "/**", recursive=True, root_dir=env.Dir("#").abspath) if 'pycache' not in x]
3333

3434
# Get model metadata
35-
for model_name in ['driving_vision', 'driving_policy']:
35+
for model_name in ['driving_vision', 'driving_policy', 'dmonitoring_model']:
3636
fn = File(f"models/{model_name}").abspath
3737
script_files = [File(Dir("#selfdrive/modeld").File("get_model_metadata.py").abspath)]
3838
cmd = f'python3 {Dir("#selfdrive/modeld").abspath}/get_model_metadata.py {fn}.onnx'

selfdrive/modeld/dmonitoringmodeld.py

Lines changed: 33 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
import math
88
import time
99
import pickle
10-
import ctypes
1110
import numpy as np
1211
from pathlib import Path
1312

@@ -16,59 +15,31 @@
1615
from msgq.visionipc import VisionIpcClient, VisionStreamType, VisionBuf
1716
from openpilot.common.swaglog import cloudlog
1817
from openpilot.common.realtime import config_realtime_process
19-
from openpilot.common.transformations.model import dmonitoringmodel_intrinsics, DM_INPUT_SIZE
18+
from openpilot.common.transformations.model import dmonitoringmodel_intrinsics
2019
from openpilot.common.transformations.camera import _ar_ox_fisheye, _os_fisheye
2120
from openpilot.selfdrive.modeld.models.commonmodel_pyx import CLContext, MonitoringModelFrame
2221
from openpilot.selfdrive.modeld.parse_model_outputs import sigmoid
2322
from openpilot.selfdrive.modeld.runners.tinygrad_helpers import qcom_tensor_from_opencl_address
2423

25-
MODEL_WIDTH, MODEL_HEIGHT = DM_INPUT_SIZE
26-
CALIB_LEN = 3
27-
FEATURE_LEN = 512
28-
OUTPUT_SIZE = 83 + FEATURE_LEN
29-
3024
PROCESS_NAME = "selfdrive.modeld.dmonitoringmodeld"
3125
SEND_RAW_PRED = os.getenv('SEND_RAW_PRED')
3226
MODEL_PKL_PATH = Path(__file__).parent / 'models/dmonitoring_model_tinygrad.pkl'
33-
34-
# TODO: slice from meta
35-
class DriverStateResult(ctypes.Structure):
36-
_fields_ = [
37-
("face_orientation", ctypes.c_float*3),
38-
("face_position", ctypes.c_float*3),
39-
("face_orientation_std", ctypes.c_float*3),
40-
("face_position_std", ctypes.c_float*3),
41-
("face_prob", ctypes.c_float),
42-
("_unused_a", ctypes.c_float*8),
43-
("left_eye_prob", ctypes.c_float),
44-
("_unused_b", ctypes.c_float*8),
45-
("right_eye_prob", ctypes.c_float),
46-
("left_blink_prob", ctypes.c_float),
47-
("right_blink_prob", ctypes.c_float),
48-
("sunglasses_prob", ctypes.c_float),
49-
("_unused_c", ctypes.c_float),
50-
("_unused_d", ctypes.c_float*4),
51-
("not_ready_prob", ctypes.c_float*2)]
52-
53-
54-
class DMonitoringModelResult(ctypes.Structure):
55-
_fields_ = [
56-
("driver_state_lhd", DriverStateResult),
57-
("driver_state_rhd", DriverStateResult),
58-
("wheel_on_right_prob", ctypes.c_float),
59-
("features", ctypes.c_float*FEATURE_LEN)]
27+
METADATA_PATH = Path(__file__).parent / 'models/dmonitoring_model_metadata.pkl'
6028

6129

6230
class ModelState:
6331
inputs: dict[str, np.ndarray]
6432
output: np.ndarray
6533

6634
def __init__(self, cl_ctx):
67-
assert ctypes.sizeof(DMonitoringModelResult) == OUTPUT_SIZE * ctypes.sizeof(ctypes.c_float)
35+
with open(METADATA_PATH, 'rb') as f:
36+
model_metadata = pickle.load(f)
37+
self.input_shapes = model_metadata['input_shapes']
38+
self.output_slices = model_metadata['output_slices']
6839

6940
self.frame = MonitoringModelFrame(cl_ctx)
7041
self.numpy_inputs = {
71-
'calib': np.zeros((1, CALIB_LEN), dtype=np.float32),
42+
'calib': np.zeros(self.input_shapes['calib'], dtype=np.float32),
7243
}
7344

7445
self.tensor_inputs = {k: Tensor(v, device='NPY').realize() for k,v in self.numpy_inputs.items()}
@@ -84,9 +55,9 @@ def run(self, buf: VisionBuf, calib: np.ndarray, transform: np.ndarray) -> tuple
8455
if TICI:
8556
# The imgs tensors are backed by opencl memory, only need init once
8657
if 'input_img' not in self.tensor_inputs:
87-
self.tensor_inputs['input_img'] = qcom_tensor_from_opencl_address(input_img_cl.mem_address, (1, MODEL_WIDTH*MODEL_HEIGHT), dtype=dtypes.uint8)
58+
self.tensor_inputs['input_img'] = qcom_tensor_from_opencl_address(input_img_cl.mem_address, self.input_shapes['input_img'], dtype=dtypes.uint8)
8859
else:
89-
self.tensor_inputs['input_img'] = Tensor(self.frame.buffer_from_cl(input_img_cl).reshape((1, MODEL_WIDTH*MODEL_HEIGHT)), dtype=dtypes.uint8).realize()
60+
self.tensor_inputs['input_img'] = Tensor(self.frame.buffer_from_cl(input_img_cl).reshape(self.input_shapes['input_img']), dtype=dtypes.uint8).realize()
9061

9162

9263
output = self.model_run(**self.tensor_inputs).contiguous().realize().uop.base.buffer.numpy()
@@ -95,31 +66,31 @@ def run(self, buf: VisionBuf, calib: np.ndarray, transform: np.ndarray) -> tuple
9566
return output, t2 - t1
9667

9768

98-
def fill_driver_state(msg, ds_result: DriverStateResult):
99-
msg.faceOrientation = list(ds_result.face_orientation)
100-
msg.faceOrientationStd = [math.exp(x) for x in ds_result.face_orientation_std]
101-
msg.facePosition = list(ds_result.face_position[:2])
102-
msg.facePositionStd = [math.exp(x) for x in ds_result.face_position_std[:2]]
103-
msg.faceProb = float(sigmoid(ds_result.face_prob))
104-
msg.leftEyeProb = float(sigmoid(ds_result.left_eye_prob))
105-
msg.rightEyeProb = float(sigmoid(ds_result.right_eye_prob))
106-
msg.leftBlinkProb = float(sigmoid(ds_result.left_blink_prob))
107-
msg.rightBlinkProb = float(sigmoid(ds_result.right_blink_prob))
108-
msg.sunglassesProb = float(sigmoid(ds_result.sunglasses_prob))
109-
msg.notReadyProb = [float(sigmoid(x)) for x in ds_result.not_ready_prob]
110-
111-
112-
def get_driverstate_packet(model_output: np.ndarray, frame_id: int, location_ts: int, execution_time: float, gpu_execution_time: float):
113-
model_result = ctypes.cast(model_output.ctypes.data, ctypes.POINTER(DMonitoringModelResult)).contents
69+
def fill_driver_state(msg, model_output, output_slices, ds_suffix):
70+
face_descs = model_output[output_slices[f'face_descs_{ds_suffix}']]
71+
face_descs_std = face_descs[-6:]
72+
msg.faceOrientation = [float(x) for x in face_descs[:3]]
73+
msg.faceOrientationStd = [math.exp(x) for x in face_descs_std[:3]]
74+
msg.facePosition = [float(x) for x in face_descs[3:5]]
75+
msg.facePositionStd = [math.exp(x) for x in face_descs_std[3:5]]
76+
msg.faceProb = float(sigmoid(model_output[output_slices[f'face_prob_{ds_suffix}']][0]))
77+
msg.leftEyeProb = float(sigmoid(model_output[output_slices[f'left_eye_prob_{ds_suffix}']][0]))
78+
msg.rightEyeProb = float(sigmoid(model_output[output_slices[f'right_eye_prob_{ds_suffix}']][0]))
79+
msg.leftBlinkProb = float(sigmoid(model_output[output_slices[f'left_blink_prob_{ds_suffix}']][0]))
80+
msg.rightBlinkProb = float(sigmoid(model_output[output_slices[f'right_blink_prob_{ds_suffix}']][0]))
81+
msg.sunglassesProb = float(sigmoid(model_output[output_slices[f'sunglasses_prob_{ds_suffix}']][0]))
82+
msg.phoneProb = float(sigmoid(model_output[output_slices[f'using_phone_prob_{ds_suffix}']][0]))
83+
84+
def get_driverstate_packet(model_output: np.ndarray, output_slices: dict[str, slice], frame_id: int, location_ts: int, exec_time: float, gpu_exec_time: float):
11485
msg = messaging.new_message('driverStateV2', valid=True)
11586
ds = msg.driverStateV2
11687
ds.frameId = frame_id
117-
ds.modelExecutionTime = execution_time
118-
ds.gpuExecutionTime = gpu_execution_time
119-
ds.wheelOnRightProb = float(sigmoid(model_result.wheel_on_right_prob))
88+
ds.modelExecutionTime = exec_time
89+
ds.gpuExecutionTime = gpu_exec_time
90+
ds.wheelOnRightProb = float(sigmoid(model_output[output_slices['wheel_on_right']][0]))
12091
ds.rawPredictions = model_output.tobytes() if SEND_RAW_PRED else b''
121-
fill_driver_state(ds.leftDriverData, model_result.driver_state_lhd)
122-
fill_driver_state(ds.rightDriverData, model_result.driver_state_rhd)
92+
fill_driver_state(ds.leftDriverData, model_output, output_slices, 'lhd')
93+
fill_driver_state(ds.rightDriverData, model_output, output_slices, 'rhd')
12394
return msg
12495

12596

@@ -140,7 +111,7 @@ def main():
140111
sm = SubMaster(["liveCalibration"])
141112
pm = PubMaster(["driverStateV2"])
142113

143-
calib = np.zeros(CALIB_LEN, dtype=np.float32)
114+
calib = np.zeros(model.numpy_inputs['calib'].size, dtype=np.float32)
144115
model_transform = None
145116

146117
while True:
@@ -160,7 +131,8 @@ def main():
160131
model_output, gpu_execution_time = model.run(buf, calib, model_transform)
161132
t2 = time.perf_counter()
162133

163-
pm.send("driverStateV2", get_driverstate_packet(model_output, vipc_client.frame_id, vipc_client.timestamp_sof, t2 - t1, gpu_execution_time))
134+
msg = get_driverstate_packet(model_output, model.output_slices, vipc_client.frame_id, vipc_client.timestamp_sof, t2 - t1, gpu_execution_time)
135+
pm.send("driverStateV2", msg)
164136

165137

166138
if __name__ == "__main__":
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
version https://git-lfs.github.com/spec/v1
2-
oid sha256:3a53626ab84757813fb16a1441704f2ae7192bef88c331bdc2415be6981d204f
3-
size 7191776
2+
oid sha256:3446bf8b22e50e47669a25bf32460ae8baf8547037f346753e19ecbfcf6d4e59
3+
size 6954368

selfdrive/monitoring/helpers.py

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,12 @@ def __init__(self):
3737
self._BLINK_THRESHOLD = 0.865
3838

3939
if HARDWARE.get_device_type() == 'mici':
40-
self._EE_THRESH11 = 0.75
40+
self._PHONE_THRESH = 0.75
4141
else:
42-
self._EE_THRESH11 = 0.4
43-
self._EE_THRESH12 = 15.0
44-
self._EE_MAX_OFFSET1 = 0.06
45-
self._EE_MIN_OFFSET1 = 0.025
42+
self._PHONE_THRESH = 0.4
43+
self._PHONE_THRESH2 = 15.0
44+
self._PHONE_MAX_OFFSET = 0.06
45+
self._PHONE_MIN_OFFSET = 0.025
4646

4747
self._POSE_PITCH_THRESHOLD = 0.3133
4848
self._POSE_PITCH_THRESHOLD_SLACK = 0.3237
@@ -84,7 +84,7 @@ class DistractedType:
8484
NOT_DISTRACTED = 0
8585
DISTRACTED_POSE = 1 << 0
8686
DISTRACTED_BLINK = 1 << 1
87-
DISTRACTED_E2E = 1 << 2
87+
DISTRACTED_PHONE = 1 << 2
8888

8989
class DriverPose:
9090
def __init__(self, max_trackable):
@@ -142,9 +142,9 @@ def __init__(self, rhd_saved=False, settings=None, always_on=False):
142142
self.wheelpos_learner = RunningStatFilter()
143143
self.pose = DriverPose(self.settings._POSE_OFFSET_MAX_COUNT)
144144
self.blink = DriverBlink()
145-
self.eev1 = 0.
146-
self.ee1_offseter = RunningStatFilter(max_trackable=self.settings._POSE_OFFSET_MAX_COUNT)
147-
self.ee1_calibrated = False
145+
self.phone_prob = 0.
146+
self.phone_offseter = RunningStatFilter(max_trackable=self.settings._POSE_OFFSET_MAX_COUNT)
147+
self.phone_calibrated = False
148148

149149
self.always_on = always_on
150150
self.distracted_types = []
@@ -242,13 +242,13 @@ def _get_distracted_types(self):
242242
if (self.blink.left + self.blink.right)*0.5 > self.settings._BLINK_THRESHOLD:
243243
distracted_types.append(DistractedType.DISTRACTED_BLINK)
244244

245-
if self.ee1_calibrated:
246-
ee1_dist = self.eev1 > max(min(self.ee1_offseter.filtered_stat.M, self.settings._EE_MAX_OFFSET1), self.settings._EE_MIN_OFFSET1) \
247-
* self.settings._EE_THRESH12
245+
if self.phone_calibrated:
246+
using_phone = self.phone_prob > max(min(self.phone_offseter.filtered_stat.M, self.settings._PHONE_MAX_OFFSET), self.settings._PHONE_MIN_OFFSET) \
247+
* self.settings._PHONE_THRESH2
248248
else:
249-
ee1_dist = self.eev1 > self.settings._EE_THRESH11
250-
if ee1_dist:
251-
distracted_types.append(DistractedType.DISTRACTED_E2E)
249+
using_phone = self.phone_prob > self.settings._PHONE_THRESH
250+
if using_phone:
251+
distracted_types.append(DistractedType.DISTRACTED_PHONE)
252252

253253
return distracted_types
254254

@@ -267,8 +267,7 @@ def _update_states(self, driver_state, cal_rpy, car_speed, op_engaged, standstil
267267
self.wheel_on_right = self.wheel_on_right_last
268268
driver_data = driver_state.rightDriverData if self.wheel_on_right else driver_state.leftDriverData
269269
if not all(len(x) > 0 for x in (driver_data.faceOrientation, driver_data.facePosition,
270-
driver_data.faceOrientationStd, driver_data.facePositionStd,
271-
driver_data.notReadyProb)):
270+
driver_data.faceOrientationStd, driver_data.facePositionStd)):
272271
return
273272

274273
self.face_detected = driver_data.faceProb > self.settings._FACE_THRESHOLD
@@ -284,10 +283,10 @@ def _update_states(self, driver_state, cal_rpy, car_speed, op_engaged, standstil
284283
* (driver_data.sunglassesProb < self.settings._SG_THRESHOLD)
285284
self.blink.right = driver_data.rightBlinkProb * (driver_data.rightEyeProb > self.settings._EYE_THRESHOLD) \
286285
* (driver_data.sunglassesProb < self.settings._SG_THRESHOLD)
287-
self.eev1 = driver_data.notReadyProb[0]
286+
self.phone_prob = driver_data.phoneProb
288287

289288
self.distracted_types = self._get_distracted_types()
290-
self.driver_distracted = (DistractedType.DISTRACTED_E2E in self.distracted_types or DistractedType.DISTRACTED_POSE in self.distracted_types
289+
self.driver_distracted = (DistractedType.DISTRACTED_PHONE in self.distracted_types or DistractedType.DISTRACTED_POSE in self.distracted_types
291290
or DistractedType.DISTRACTED_BLINK in self.distracted_types) \
292291
and driver_data.faceProb > self.settings._FACE_THRESHOLD and self.pose.low_std
293292
self.driver_distraction_filter.update(self.driver_distracted)
@@ -297,11 +296,11 @@ def _update_states(self, driver_state, cal_rpy, car_speed, op_engaged, standstil
297296
if self.face_detected and car_speed > self.settings._POSE_CALIB_MIN_SPEED and self.pose.low_std and (not op_engaged or not self.driver_distracted):
298297
self.pose.pitch_offseter.push_and_update(self.pose.pitch)
299298
self.pose.yaw_offseter.push_and_update(self.pose.yaw)
300-
self.ee1_offseter.push_and_update(self.eev1)
299+
self.phone_offseter.push_and_update(self.phone_prob)
301300

302301
self.pose.calibrated = self.pose.pitch_offseter.filtered_stat.n > self.settings._POSE_OFFSET_MIN_COUNT and \
303302
self.pose.yaw_offseter.filtered_stat.n > self.settings._POSE_OFFSET_MIN_COUNT
304-
self.ee1_calibrated = self.ee1_offseter.filtered_stat.n > self.settings._POSE_OFFSET_MIN_COUNT
303+
self.phone_calibrated = self.phone_offseter.filtered_stat.n > self.settings._POSE_OFFSET_MIN_COUNT
305304

306305
if self.face_detected and not self.driver_distracted:
307306
if model_std_max > self.settings._DCAM_UNCERTAIN_ALERT_THRESHOLD:

selfdrive/monitoring/test_monitoring.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ def make_msg(face_detected, distracted=False, model_uncertain=False):
2525
ds.leftDriverData.faceOrientationStd = [1.*model_uncertain, 1.*model_uncertain, 1.*model_uncertain]
2626
ds.leftDriverData.facePositionStd = [1.*model_uncertain, 1.*model_uncertain]
2727
# TODO: test both separately when e2e is used
28-
ds.leftDriverData.notReadyProb = [0., 0.]
28+
ds.leftDriverData.phoneProb = 0.
2929
return ds
3030

3131

selfdrive/test/process_replay/model_replay.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ def generate_report(proposed, master, tmp, commit):
7777
(lambda x: get_idx_if_non_empty(x.leftDriverData.faceProb), "leftDriverData.faceProb"),
7878
(lambda x: get_idx_if_non_empty(x.leftDriverData.faceOrientation, 0), "leftDriverData.faceOrientation0"),
7979
(lambda x: get_idx_if_non_empty(x.leftDriverData.leftBlinkProb), "leftDriverData.leftBlinkProb"),
80-
(lambda x: get_idx_if_non_empty(x.leftDriverData.notReadyProb, 0), "leftDriverData.notReadyProb0"),
80+
(lambda x: get_idx_if_non_empty(x.leftDriverData.phoneProb), "leftDriverData.phoneProb"),
8181
(lambda x: get_idx_if_non_empty(x.rightDriverData.faceProb), "rightDriverData.faceProb"),
8282
], "driverStateV2")
8383

0 commit comments

Comments
 (0)