Include camera conversion in vision CPU budget

This commit is contained in:
firestar5683
2026-10-23 11:45:33 -05:00
parent b00a235274
commit a75d3fd787
3 changed files with 57 additions and 48 deletions
@@ -247,7 +247,8 @@ def main() -> None:
inference_interval_ns = _inference_interval_ns(processing_interval_ns, int(core.last_detection_at * 1e9), pair[0])
if pair[0] - last_inference_ns < inference_interval_ns:
continue
last_inference_ns = pair[0]
# Budget camera conversion together with inference, as in the original Dom loop.
last_inference_ns = admission_pair[0]
try:
# The observation belongs to the original camera frame, not the end of
# CPU inference. Completion never restamps an older camera observation.
@@ -294,57 +294,65 @@ class TestVisionProducer(unittest.TestCase):
self.assertEqual(messages[0].speedMps, 0)
def test_followup_confirms_brief_candidate_without_changing_thresholds(self):
core = VisionModelCore.__new__(VisionModelCore)
core.is_metric = False
core.history = deque()
core.reset()
client = Mock()
client.is_connected.return_value = True
camera = Mock(return_value=client)
road = {VisionStreamType.VISION_STREAM_NARROW_ROAD}
camera.available_streams.side_effect = [road] * 3 + [EndProducer()]
sources = (1_000_000_000, 1_100_000_000, 1_300_000_000)
buffers = [(object(), t + 4_000_000_000, i) for i, t in enumerate(sources)]
frames = [(np.full((2, 2, 3), i), b[1], b[2]) for i, b in enumerate(buffers)]
current = [0, 0]
pending = iter(buffers)
messages = []
for conversion_ns, sources, expected in (
(0, (1_000_000_000, 1_100_000_000, 1_300_000_000), ['unknown', 'valid', 'valid']),
(80_000_000, (1_000_000_000, 1_200_000_000, 1_400_000_000), ['unknown', 'unknown']),
):
with self.subTest(conversion_ns=conversion_ns):
core = VisionModelCore.__new__(VisionModelCore)
core.is_metric = False
core.history = deque()
core.reset()
client = Mock()
client.is_connected.return_value = True
camera = Mock(return_value=client)
road = {VisionStreamType.VISION_STREAM_NARROW_ROAD}
camera.available_streams.side_effect = [road] * 3 + [EndProducer()]
buffers = [(object(), t + 4_000_000_000, i) for i, t in enumerate(sources)]
frames = [(np.full((2, 2, 3), i), b[1], b[2]) for i, b in enumerate(buffers)]
current = [0, 0]
pending = iter(buffers)
messages = []
def receive(_client):
buffer = next(pending)
current[:] = [buffer[2], 0]
return buffer
def receive(_client, pending=pending, current=current):
buffer = next(pending)
current[:] = [buffer[2], 0]
return buffer
def clock_pair():
now = sources[current[0]] + (10_000_000 if current[1] >= 2 else 0)
current[1] += 1
self.sm.logMonoTime['drivingModelData'] = now
return now, now + 4_000_000_000
def clock_pair(sources=sources, current=current, conversion_ns=conversion_ns):
now = sources[current[0]] + (0, conversion_ns, conversion_ns + 10_000_000)[min(current[1], 2)]
current[1] += 1
self.sm.logMonoTime['drivingModelData'] = now
return now, now + 4_000_000_000
def send(_service, event):
messages.append(messaging.log_from_bytes(event.to_bytes()).slcVisionObservation.vision)
def send(_service, event, messages=messages):
messages.append(messaging.log_from_bytes(event.to_bytes()).slcVisionObservation.vision)
with patch('msgq.visionipc.VisionIpcClient', camera), \
patch('openpilot.common.params.Params', return_value=SimpleNamespace(get_bool=lambda _: False)), \
patch.object(messaging, 'PubMaster', return_value=SimpleNamespace(send=send)), \
patch.object(producer, 'VisionModelCore', return_value=core), \
patch.object(producer, '_camera_buffer', side_effect=receive), \
patch.object(producer, '_convert_camera_buffer', side_effect=lambda buffer: frames[buffer[2]]), \
patch.object(producer, 'clock_pair_ns', side_effect=clock_pair), \
patch.object(producer.time, 'sleep'), \
patch.object(core, 'infer', side_effect=lambda frame: Detection(55, 0.60) if frame[0, 0, 0] < 2 else None), \
self.assertRaises(EndProducer):
producer.main()
with patch('msgq.visionipc.VisionIpcClient', camera), \
patch('openpilot.common.params.Params', return_value=SimpleNamespace(get_bool=lambda _: False)), \
patch.object(messaging, 'PubMaster', return_value=SimpleNamespace(send=send)), \
patch.object(producer, 'VisionModelCore', return_value=core), \
patch.object(producer, '_camera_buffer', side_effect=receive), \
patch.object(producer, '_convert_camera_buffer', side_effect=lambda buffer, frames=frames: frames[buffer[2]]), \
patch.object(producer, 'clock_pair_ns', side_effect=clock_pair), \
patch.object(producer.time, 'sleep'), \
patch.object(core, 'infer', side_effect=lambda frame: Detection(55, 0.60) if frame[0, 0, 0] < 2 else None), \
self.assertRaises(EndProducer):
producer.main()
self.assertEqual([str(message.status) for message in messages], ['unknown', 'valid', 'valid'])
self.assertEqual([message.frameId for message in messages], [0, 1, 2])
self.assertEqual(messages[1].supportCount, 2)
self.assertEqual(messages[1].observedMonoTime, 1_100_000_000)
self.assertEqual(messages[2].observedMonoTime, 1_100_000_000)
self.assertEqual(messages[1].validUntilMonoTime, 3_100_000_000)
self.assertEqual(messages[2].validUntilMonoTime, 3_300_000_000)
self.assertEqual(core.last_detection_at, 1.1)
self.assertEqual(messages[1].confidence, messages[2].confidence)
self.assertEqual([str(message.status) for message in messages], expected)
if conversion_ns:
self.assertEqual([message.frameId for message in messages], [0, 2])
self.assertEqual(core.last_detection_at, 1.)
continue
self.assertEqual([message.frameId for message in messages], [0, 1, 2])
self.assertEqual(messages[1].supportCount, 2)
self.assertEqual(messages[1].observedMonoTime, 1_100_000_000)
self.assertEqual(messages[2].observedMonoTime, 1_100_000_000)
self.assertEqual(messages[1].validUntilMonoTime, 3_100_000_000)
self.assertEqual(messages[2].validUntilMonoTime, 3_300_000_000)
self.assertEqual(core.last_detection_at, 1.1)
self.assertEqual(messages[1].confidence, messages[2].confidence)
def test_followup_preserves_processing_cost_and_expires(self):
interval = producer._inference_interval_ns
+1 -1
View File
@@ -1 +1 @@
{"protected_inventory_sha256": "2ae7eb2cde24cdf5dc6ab526f0c0c2c30735fdcb0f33df2f51eca15eee349a83", "schema_version": 1}
{"protected_inventory_sha256": "66e62824cc864d2942c8eb3ddbd302f84ba0223df8513290f3ef2c4244a4ac1d", "schema_version": 1}