Rewrite dmonitoringmodeld in python (#29740)
* Added dmonitoringmodeld.py
* Removed dmonitoringmodeld.cc
* Use ModelRunner helpers from runners/__init__.py
* Fixed DriverStateResult field ordering
* Some bug fixes
* Set calib input
* Look ma, no loop!
* Bump dmonitoringmodeld cpu usage in test_onroad
* Fixed memory leak caused by np.ctypes.data_as
* Formatting fixes
* chmod +x
* remove USE_ONNX_MODEL
* Realtime priority 1, formatting fixes
old-commit-hash: 503fa121ee
test-msgs
parent
01ee6fd1bb
commit
5732002296
10 changed files with 160 additions and 289 deletions
@ -1,12 +0,0 @@ |
||||
#!/usr/bin/env bash |
||||
|
||||
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)" |
||||
cd $DIR |
||||
|
||||
if [ -f /TICI ]; then |
||||
export LD_LIBRARY_PATH="/usr/lib/aarch64-linux-gnu:/data/pythonpath/third_party/snpe/larch64:$LD_LIBRARY_PATH" |
||||
export ADSP_LIBRARY_PATH="/data/pythonpath/third_party/snpe/dsp/" |
||||
else |
||||
export LD_LIBRARY_PATH="$DIR/../../third_party/snpe/x86_64-linux-clang:$DIR/../../openpilot/third_party/snpe/x86_64:$LD_LIBRARY_PATH" |
||||
fi |
||||
exec ./_dmonitoringmodeld |
@ -1,69 +0,0 @@ |
||||
#include <sys/resource.h> |
||||
#include <limits.h> |
||||
|
||||
#include <cstdio> |
||||
#include <cstdlib> |
||||
|
||||
#include "cereal/visionipc/visionipc_client.h" |
||||
#include "common/params.h" |
||||
#include "common/swaglog.h" |
||||
#include "common/util.h" |
||||
#include "selfdrive/modeld/models/dmonitoring.h" |
||||
|
||||
ExitHandler do_exit; |
||||
|
||||
void run_model(DMonitoringModelState &model, VisionIpcClient &vipc_client) { |
||||
PubMaster pm({"driverStateV2"}); |
||||
SubMaster sm({"liveCalibration"}); |
||||
float calib[CALIB_LEN] = {0}; |
||||
// double last = 0;
|
||||
|
||||
while (!do_exit) { |
||||
VisionIpcBufExtra extra = {}; |
||||
VisionBuf *buf = vipc_client.recv(&extra); |
||||
if (buf == nullptr) continue; |
||||
|
||||
sm.update(0); |
||||
if (sm.updated("liveCalibration")) { |
||||
auto calib_msg = sm["liveCalibration"].getLiveCalibration().getRpyCalib(); |
||||
for (int i = 0; i < CALIB_LEN; i++) { |
||||
calib[i] = calib_msg[i]; |
||||
} |
||||
} |
||||
|
||||
double t1 = millis_since_boot(); |
||||
DMonitoringModelResult model_res = dmonitoring_eval_frame(&model, buf->addr, buf->width, buf->height, buf->stride, buf->uv_offset, calib); |
||||
double t2 = millis_since_boot(); |
||||
|
||||
// send dm packet
|
||||
dmonitoring_publish(pm, extra.frame_id, model_res, (t2 - t1) / 1000.0, model.output); |
||||
|
||||
// printf("dmonitoring process: %.2fms, from last %.2fms\n", t2 - t1, t1 - last);
|
||||
// last = t1;
|
||||
} |
||||
} |
||||
|
||||
int main(int argc, char **argv) { |
||||
setpriority(PRIO_PROCESS, 0, -15); |
||||
|
||||
// init the models
|
||||
DMonitoringModelState model; |
||||
dmonitoring_init(&model); |
||||
|
||||
Params().putBool("DmModelInitialized", true); |
||||
|
||||
LOGW("connecting to driver stream"); |
||||
VisionIpcClient vipc_client = VisionIpcClient("camerad", VISION_STREAM_DRIVER, true); |
||||
while (!do_exit && !vipc_client.connect(false)) { |
||||
util::sleep_for(100); |
||||
} |
||||
|
||||
// run the models
|
||||
if (vipc_client.connected) { |
||||
LOGW("connected with buffer size: %zu", vipc_client.buffers[0].len); |
||||
run_model(model, vipc_client); |
||||
} |
||||
|
||||
dmonitoring_free(&model); |
||||
return 0; |
||||
} |
@ -0,0 +1,157 @@ |
||||
#!/usr/bin/env python3 |
||||
import os |
||||
import gc |
||||
import math |
||||
import time |
||||
import ctypes |
||||
import numpy as np |
||||
from pathlib import Path |
||||
from typing import Tuple, Dict |
||||
|
||||
from cereal import messaging |
||||
from cereal.messaging import PubMaster, SubMaster |
||||
from cereal.visionipc import VisionIpcClient, VisionStreamType, VisionBuf |
||||
from openpilot.system.swaglog import cloudlog |
||||
from openpilot.common.params import Params |
||||
from openpilot.common.realtime import set_realtime_priority |
||||
from openpilot.selfdrive.modeld.runners import ModelRunner, Runtime |
||||
from openpilot.selfdrive.modeld.models.commonmodel_pyx import sigmoid |
||||
|
||||
CALIB_LEN = 3 |
||||
REG_SCALE = 0.25 |
||||
MODEL_WIDTH = 1440 |
||||
MODEL_HEIGHT = 960 |
||||
OUTPUT_SIZE = 84 |
||||
SEND_RAW_PRED = os.getenv('SEND_RAW_PRED') |
||||
MODEL_PATHS = { |
||||
ModelRunner.SNPE: Path(__file__).parent / 'models/dmonitoring_model_q.dlc', |
||||
ModelRunner.ONNX: Path(__file__).parent / 'models/dmonitoring_model.onnx'} |
||||
|
||||
class DriverStateResult(ctypes.Structure): |
||||
_fields_ = [ |
||||
("face_orientation", ctypes.c_float*3), |
||||
("face_position", ctypes.c_float*3), |
||||
("face_orientation_std", ctypes.c_float*3), |
||||
("face_position_std", ctypes.c_float*3), |
||||
("face_prob", ctypes.c_float), |
||||
("_unused_a", ctypes.c_float*8), |
||||
("left_eye_prob", ctypes.c_float), |
||||
("_unused_b", ctypes.c_float*8), |
||||
("right_eye_prob", ctypes.c_float), |
||||
("left_blink_prob", ctypes.c_float), |
||||
("right_blink_prob", ctypes.c_float), |
||||
("sunglasses_prob", ctypes.c_float), |
||||
("occluded_prob", ctypes.c_float), |
||||
("ready_prob", ctypes.c_float*4), |
||||
("not_ready_prob", ctypes.c_float*2)] |
||||
|
||||
class DMonitoringModelResult(ctypes.Structure): |
||||
_fields_ = [ |
||||
("driver_state_lhd", DriverStateResult), |
||||
("driver_state_rhd", DriverStateResult), |
||||
("poor_vision_prob", ctypes.c_float), |
||||
("wheel_on_right_prob", ctypes.c_float)] |
||||
|
||||
class ModelState: |
||||
inputs: Dict[str, np.ndarray] |
||||
output: np.ndarray |
||||
model: ModelRunner |
||||
|
||||
def __init__(self): |
||||
assert ctypes.sizeof(DMonitoringModelResult) == OUTPUT_SIZE * ctypes.sizeof(ctypes.c_float) |
||||
self.output = np.zeros(OUTPUT_SIZE, dtype=np.float32) |
||||
self.inputs = { |
||||
'input_imgs': np.zeros(MODEL_HEIGHT * MODEL_WIDTH, dtype=np.uint8), |
||||
'calib': np.zeros(CALIB_LEN, dtype=np.float32)} |
||||
|
||||
self.model = ModelRunner(MODEL_PATHS, self.output, Runtime.DSP, True, None) |
||||
self.model.addInput("input_imgs", None) |
||||
self.model.addInput("calib", self.inputs['calib']) |
||||
|
||||
def run(self, buf:VisionBuf, calib:np.ndarray) -> Tuple[np.ndarray, float]: |
||||
self.inputs['calib'][:] = calib |
||||
|
||||
v_offset = buf.height - MODEL_HEIGHT |
||||
h_offset = (buf.width - MODEL_WIDTH) // 2 |
||||
buf_data = buf.data.reshape(-1, buf.stride) |
||||
input_data = self.inputs['input_imgs'].reshape(MODEL_HEIGHT, MODEL_WIDTH) |
||||
input_data[:] = buf_data[v_offset:v_offset+MODEL_HEIGHT, h_offset:h_offset+MODEL_WIDTH] |
||||
|
||||
t1 = time.perf_counter() |
||||
self.model.setInputBuffer("input_imgs", self.inputs['input_imgs'].view(np.float32)) |
||||
self.model.execute() |
||||
t2 = time.perf_counter() |
||||
return self.output, t2 - t1 |
||||
|
||||
|
||||
def fill_driver_state(msg, ds_result: DriverStateResult): |
||||
msg.faceOrientation = [x * REG_SCALE for x in ds_result.face_orientation] |
||||
msg.faceOrientationStd = [math.exp(x) for x in ds_result.face_orientation_std] |
||||
msg.facePosition = [x * REG_SCALE for x in ds_result.face_position[:2]] |
||||
msg.facePositionStd = [math.exp(x) for x in ds_result.face_position_std[:2]] |
||||
msg.faceProb = sigmoid(ds_result.face_prob) |
||||
msg.leftEyeProb = sigmoid(ds_result.left_eye_prob) |
||||
msg.rightEyeProb = sigmoid(ds_result.right_eye_prob) |
||||
msg.leftBlinkProb = sigmoid(ds_result.left_blink_prob) |
||||
msg.rightBlinkProb = sigmoid(ds_result.right_blink_prob) |
||||
msg.sunglassesProb = sigmoid(ds_result.sunglasses_prob) |
||||
msg.occludedProb = sigmoid(ds_result.occluded_prob) |
||||
msg.readyProb = [sigmoid(x) for x in ds_result.ready_prob] |
||||
msg.notReadyProb = [sigmoid(x) for x in ds_result.not_ready_prob] |
||||
|
||||
def get_driverstate_packet(model_output: np.ndarray, frame_id: int, location_ts: int, execution_time: float, dsp_execution_time: float): |
||||
model_result = ctypes.cast(model_output.ctypes.data, ctypes.POINTER(DMonitoringModelResult)).contents |
||||
msg = messaging.new_message('driverStateV2') |
||||
ds = msg.driverStateV2 |
||||
ds.frameId = frame_id |
||||
ds.modelExecutionTime = execution_time |
||||
ds.dspExecutionTime = dsp_execution_time |
||||
ds.poorVisionProb = sigmoid(model_result.poor_vision_prob) |
||||
ds.wheelOnRightProb = sigmoid(model_result.wheel_on_right_prob) |
||||
ds.rawPredictions = model_output.tobytes() if SEND_RAW_PRED else b'' |
||||
fill_driver_state(ds.leftDriverData, model_result.driver_state_lhd) |
||||
fill_driver_state(ds.rightDriverData, model_result.driver_state_rhd) |
||||
return msg |
||||
|
||||
|
||||
def main(): |
||||
gc.disable() |
||||
set_realtime_priority(1) |
||||
|
||||
model = ModelState() |
||||
cloudlog.warning("models loaded, dmonitoringmodeld starting") |
||||
Params().put_bool("DmModelInitialized", True) |
||||
|
||||
cloudlog.warning("connecting to driver stream") |
||||
vipc_client = VisionIpcClient("camerad", VisionStreamType.VISION_STREAM_DRIVER, True) |
||||
while not vipc_client.connect(False): |
||||
time.sleep(0.1) |
||||
assert vipc_client.is_connected() |
||||
cloudlog.warning(f"connected with buffer size: {vipc_client.buffer_len}") |
||||
|
||||
sm = SubMaster(["liveCalibration"]) |
||||
pm = PubMaster(["driverStateV2"]) |
||||
|
||||
calib = np.zeros(CALIB_LEN, dtype=np.float32) |
||||
# last = 0 |
||||
|
||||
while True: |
||||
buf = vipc_client.recv() |
||||
if buf is None: |
||||
continue |
||||
|
||||
sm.update(0) |
||||
if sm.updated["liveCalibration"]: |
||||
calib[:] = np.array(sm["liveCalibration"].rpyCalib) |
||||
|
||||
t1 = time.perf_counter() |
||||
model_output, dsp_execution_time = model.run(buf, calib) |
||||
t2 = time.perf_counter() |
||||
|
||||
pm.send("driverStateV2", get_driverstate_packet(model_output, vipc_client.frame_id, vipc_client.timestamp_sof, t2 - t1, dsp_execution_time)) |
||||
# print("dmonitoring process: %.2fms, from last %.2fms\n" % (t2 - t1, t1 - last)) |
||||
# last = t1 |
||||
|
||||
|
||||
if __name__ == "__main__": |
||||
main() |
@ -1,133 +0,0 @@ |
||||
#include <cstring> |
||||
|
||||
#include "common/mat.h" |
||||
#include "common/modeldata.h" |
||||
#include "common/params.h" |
||||
#include "common/timing.h" |
||||
#include "system/hardware/hw.h" |
||||
|
||||
#include "selfdrive/modeld/models/dmonitoring.h" |
||||
|
||||
constexpr int MODEL_WIDTH = 1440; |
||||
constexpr int MODEL_HEIGHT = 960; |
||||
|
||||
template <class T> |
||||
static inline T *get_buffer(std::vector<T> &buf, const size_t size) { |
||||
if (buf.size() < size) buf.resize(size); |
||||
return buf.data(); |
||||
} |
||||
|
||||
void dmonitoring_init(DMonitoringModelState* s) { |
||||
|
||||
#ifdef USE_ONNX_MODEL |
||||
s->m = new ONNXModel("models/dmonitoring_model.onnx", &s->output[0], OUTPUT_SIZE, USE_DSP_RUNTIME, true); |
||||
#else |
||||
s->m = new SNPEModel("models/dmonitoring_model_q.dlc", &s->output[0], OUTPUT_SIZE, USE_DSP_RUNTIME, true); |
||||
#endif |
||||
|
||||
s->m->addInput("input_imgs", NULL, 0); |
||||
s->m->addInput("calib", s->calib, CALIB_LEN); |
||||
} |
||||
|
||||
void parse_driver_data(DriverStateResult &ds_res, const DMonitoringModelState* s, int out_idx_offset) { |
||||
for (int i = 0; i < 3; ++i) { |
||||
ds_res.face_orientation[i] = s->output[out_idx_offset+i] * REG_SCALE; |
||||
ds_res.face_orientation_std[i] = exp(s->output[out_idx_offset+6+i]); |
||||
} |
||||
for (int i = 0; i < 2; ++i) { |
||||
ds_res.face_position[i] = s->output[out_idx_offset+3+i] * REG_SCALE; |
||||
ds_res.face_position_std[i] = exp(s->output[out_idx_offset+9+i]); |
||||
} |
||||
for (int i = 0; i < 4; ++i) { |
||||
ds_res.ready_prob[i] = sigmoid(s->output[out_idx_offset+35+i]); |
||||
} |
||||
for (int i = 0; i < 2; ++i) { |
||||
ds_res.not_ready_prob[i] = sigmoid(s->output[out_idx_offset+39+i]); |
||||
} |
||||
ds_res.face_prob = sigmoid(s->output[out_idx_offset+12]); |
||||
ds_res.left_eye_prob = sigmoid(s->output[out_idx_offset+21]); |
||||
ds_res.right_eye_prob = sigmoid(s->output[out_idx_offset+30]); |
||||
ds_res.left_blink_prob = sigmoid(s->output[out_idx_offset+31]); |
||||
ds_res.right_blink_prob = sigmoid(s->output[out_idx_offset+32]); |
||||
ds_res.sunglasses_prob = sigmoid(s->output[out_idx_offset+33]); |
||||
ds_res.occluded_prob = sigmoid(s->output[out_idx_offset+34]); |
||||
} |
||||
|
||||
void fill_driver_data(cereal::DriverStateV2::DriverData::Builder ddata, const DriverStateResult &ds_res) { |
||||
ddata.setFaceOrientation(ds_res.face_orientation); |
||||
ddata.setFaceOrientationStd(ds_res.face_orientation_std); |
||||
ddata.setFacePosition(ds_res.face_position); |
||||
ddata.setFacePositionStd(ds_res.face_position_std); |
||||
ddata.setFaceProb(ds_res.face_prob); |
||||
ddata.setLeftEyeProb(ds_res.left_eye_prob); |
||||
ddata.setRightEyeProb(ds_res.right_eye_prob); |
||||
ddata.setLeftBlinkProb(ds_res.left_blink_prob); |
||||
ddata.setRightBlinkProb(ds_res.right_blink_prob); |
||||
ddata.setSunglassesProb(ds_res.sunglasses_prob); |
||||
ddata.setOccludedProb(ds_res.occluded_prob); |
||||
ddata.setReadyProb(ds_res.ready_prob); |
||||
ddata.setNotReadyProb(ds_res.not_ready_prob); |
||||
} |
||||
|
||||
DMonitoringModelResult dmonitoring_eval_frame(DMonitoringModelState* s, void* stream_buf, int width, int height, int stride, int uv_offset, float *calib) { |
||||
int v_off = height - MODEL_HEIGHT; |
||||
int h_off = (width - MODEL_WIDTH) / 2; |
||||
int yuv_buf_len = MODEL_WIDTH * MODEL_HEIGHT; |
||||
|
||||
uint8_t *raw_buf = (uint8_t *) stream_buf; |
||||
// vertical crop free
|
||||
uint8_t *raw_y_start = raw_buf + stride * v_off; |
||||
|
||||
uint8_t *net_input_buf = get_buffer(s->net_input_buf, yuv_buf_len); |
||||
|
||||
// here makes a uint8 copy
|
||||
for (int r = 0; r < MODEL_HEIGHT; ++r) { |
||||
memcpy(net_input_buf + r * MODEL_WIDTH, raw_y_start + r * stride + h_off, MODEL_WIDTH); |
||||
} |
||||
|
||||
// printf("preprocess completed. %d \n", yuv_buf_len);
|
||||
// FILE *dump_yuv_file = fopen("/tmp/rawdump.yuv", "wb");
|
||||
// fwrite(net_input_buf, yuv_buf_len, sizeof(uint8_t), dump_yuv_file);
|
||||
// fclose(dump_yuv_file);
|
||||
|
||||
double t1 = millis_since_boot(); |
||||
s->m->setInputBuffer("input_imgs", (float*)net_input_buf, yuv_buf_len / sizeof(float)); |
||||
for (int i = 0; i < CALIB_LEN; i++) { |
||||
s->calib[i] = calib[i]; |
||||
} |
||||
s->m->execute(); |
||||
double t2 = millis_since_boot(); |
||||
|
||||
DMonitoringModelResult model_res = {0}; |
||||
parse_driver_data(model_res.driver_state_lhd, s, 0); |
||||
parse_driver_data(model_res.driver_state_rhd, s, 41); |
||||
model_res.poor_vision_prob = sigmoid(s->output[82]); |
||||
model_res.wheel_on_right_prob = sigmoid(s->output[83]); |
||||
model_res.dsp_execution_time = (t2 - t1) / 1000.; |
||||
|
||||
return model_res; |
||||
} |
||||
|
||||
void dmonitoring_publish(PubMaster &pm, uint32_t frame_id, const DMonitoringModelResult &model_res, float execution_time, kj::ArrayPtr<const float> raw_pred) { |
||||
// make msg
|
||||
MessageBuilder msg; |
||||
auto framed = msg.initEvent().initDriverStateV2(); |
||||
framed.setFrameId(frame_id); |
||||
framed.setModelExecutionTime(execution_time); |
||||
framed.setDspExecutionTime(model_res.dsp_execution_time); |
||||
|
||||
framed.setPoorVisionProb(model_res.poor_vision_prob); |
||||
framed.setWheelOnRightProb(model_res.wheel_on_right_prob); |
||||
fill_driver_data(framed.initLeftDriverData(), model_res.driver_state_lhd); |
||||
fill_driver_data(framed.initRightDriverData(), model_res.driver_state_rhd); |
||||
|
||||
if (send_raw_pred) { |
||||
framed.setRawPredictions(raw_pred.asBytes()); |
||||
} |
||||
|
||||
pm.send("driverStateV2", msg); |
||||
} |
||||
|
||||
void dmonitoring_free(DMonitoringModelState* s) { |
||||
delete s->m; |
||||
} |
@ -1,50 +0,0 @@ |
||||
#pragma once |
||||
|
||||
#include <vector> |
||||
|
||||
#include "cereal/messaging/messaging.h" |
||||
#include "common/util.h" |
||||
#include "selfdrive/modeld/models/commonmodel.h" |
||||
#include "selfdrive/modeld/runners/run.h" |
||||
|
||||
#define CALIB_LEN 3 |
||||
|
||||
#define OUTPUT_SIZE 84 |
||||
#define REG_SCALE 0.25f |
||||
|
||||
typedef struct DriverStateResult { |
||||
float face_orientation[3]; |
||||
float face_orientation_std[3]; |
||||
float face_position[2]; |
||||
float face_position_std[2]; |
||||
float face_prob; |
||||
float left_eye_prob; |
||||
float right_eye_prob; |
||||
float left_blink_prob; |
||||
float right_blink_prob; |
||||
float sunglasses_prob; |
||||
float occluded_prob; |
||||
float ready_prob[4]; |
||||
float not_ready_prob[2]; |
||||
} DriverStateResult; |
||||
|
||||
typedef struct DMonitoringModelResult { |
||||
DriverStateResult driver_state_lhd; |
||||
DriverStateResult driver_state_rhd; |
||||
float poor_vision_prob; |
||||
float wheel_on_right_prob; |
||||
float dsp_execution_time; |
||||
} DMonitoringModelResult; |
||||
|
||||
typedef struct DMonitoringModelState { |
||||
RunModel *m; |
||||
float output[OUTPUT_SIZE]; |
||||
std::vector<uint8_t> net_input_buf; |
||||
float calib[CALIB_LEN]; |
||||
} DMonitoringModelState; |
||||
|
||||
void dmonitoring_init(DMonitoringModelState* s); |
||||
DMonitoringModelResult dmonitoring_eval_frame(DMonitoringModelState* s, void* stream_buf, int width, int height, int stride, int uv_offset, float *calib); |
||||
void dmonitoring_publish(PubMaster &pm, uint32_t frame_id, const DMonitoringModelResult &model_res, float execution_time, kj::ArrayPtr<const float> raw_pred); |
||||
void dmonitoring_free(DMonitoringModelState* s); |
||||
|
Loading…
Reference in new issue