Rewrite navmodeld in python (#29579)
* Added navmodeld.py
* Deleted navmodeld.cc
* Write SConscript config flags to config.py
* Remove deleted files from release/files_common
* Some more bug fixes
* Added config.py to gitignore
* Get rid of config.py
* Use set_realtime_priority
* A tiny bit more cleanup
* set realtime priority 1
* Use ModelRunner helper class from runners/__init__.py
* Formatting fixes
* mama mia that's a SPICY memory leak
old-commit-hash: 0c0af682a1
test-msgs
parent
dc6078a6c3
commit
f9daa1d1cc
10 changed files with 125 additions and 222 deletions
@ -1,71 +0,0 @@ |
||||
#include "selfdrive/modeld/models/nav.h" |
||||
|
||||
#include <cstdio> |
||||
#include <cstring> |
||||
|
||||
#include "common/mat.h" |
||||
#include "common/modeldata.h" |
||||
#include "common/timing.h" |
||||
|
||||
|
||||
void navmodel_init(NavModelState* s) { |
||||
#ifdef USE_ONNX_MODEL |
||||
s->m = new ONNXModel("models/navmodel.onnx", &s->output[0], NAV_NET_OUTPUT_SIZE, USE_DSP_RUNTIME, true); |
||||
#else |
||||
s->m = new SNPEModel("models/navmodel_q.dlc", &s->output[0], NAV_NET_OUTPUT_SIZE, USE_DSP_RUNTIME, true); |
||||
#endif |
||||
|
||||
s->m->addInput("map", NULL, 0); |
||||
} |
||||
|
||||
NavModelResult* navmodel_eval_frame(NavModelState* s, VisionBuf* buf) { |
||||
memcpy(s->net_input_buf, buf->addr, NAV_INPUT_SIZE); |
||||
|
||||
double t1 = millis_since_boot(); |
||||
s->m->setInputBuffer("map", (float*)s->net_input_buf, NAV_INPUT_SIZE/sizeof(float)); |
||||
s->m->execute(); |
||||
double t2 = millis_since_boot(); |
||||
|
||||
NavModelResult *model_res = (NavModelResult*)&s->output; |
||||
model_res->dsp_execution_time = (t2 - t1) / 1000.; |
||||
return model_res; |
||||
} |
||||
|
||||
void fill_plan(cereal::NavModelData::Builder &framed, const NavModelOutputPlan &plan) { |
||||
std::array<float, TRAJECTORY_SIZE> pos_x, pos_y; |
||||
std::array<float, TRAJECTORY_SIZE> pos_x_std, pos_y_std; |
||||
|
||||
for (int i=0; i<TRAJECTORY_SIZE; i++) { |
||||
pos_x[i] = plan.mean[i].x; |
||||
pos_y[i] = plan.mean[i].y; |
||||
pos_x_std[i] = exp(plan.std[i].x); |
||||
pos_y_std[i] = exp(plan.std[i].y); |
||||
} |
||||
|
||||
auto position = framed.initPosition(); |
||||
position.setX(to_kj_array_ptr(pos_x)); |
||||
position.setY(to_kj_array_ptr(pos_y)); |
||||
position.setXStd(to_kj_array_ptr(pos_x_std)); |
||||
position.setYStd(to_kj_array_ptr(pos_y_std)); |
||||
} |
||||
|
||||
void navmodel_publish(PubMaster &pm, VisionIpcBufExtra &extra, const NavModelResult &model_res, float execution_time, bool route_valid) { |
||||
// make msg
|
||||
MessageBuilder msg; |
||||
auto evt = msg.initEvent(); |
||||
auto framed = evt.initNavModel(); |
||||
evt.setValid(extra.valid && route_valid); |
||||
framed.setFrameId(extra.frame_id); |
||||
framed.setLocationMonoTime(extra.timestamp_sof); |
||||
framed.setModelExecutionTime(execution_time); |
||||
framed.setDspExecutionTime(model_res.dsp_execution_time); |
||||
framed.setFeatures(to_kj_array_ptr(model_res.features.values)); |
||||
framed.setDesirePrediction(to_kj_array_ptr(model_res.desire_pred.values)); |
||||
fill_plan(framed, model_res.plan); |
||||
|
||||
pm.send("navModel", msg); |
||||
} |
||||
|
||||
void navmodel_free(NavModelState* s) { |
||||
delete s->m; |
||||
} |
@ -1,57 +0,0 @@ |
||||
#pragma once |
||||
|
||||
#include "cereal/messaging/messaging.h" |
||||
#include "cereal/visionipc/visionipc_client.h" |
||||
#include "common/util.h" |
||||
#include "common/modeldata.h" |
||||
#include "selfdrive/modeld/models/commonmodel.h" |
||||
#include "selfdrive/modeld/runners/run.h" |
||||
|
||||
constexpr int NAV_INPUT_SIZE = 256*256; |
||||
constexpr int NAV_FEATURE_LEN = 256; |
||||
constexpr int NAV_INSTRUCTION_LEN = 150; |
||||
constexpr int NAV_DESIRE_LEN = 32; |
||||
|
||||
struct NavModelOutputXY { |
||||
float x; |
||||
float y; |
||||
}; |
||||
static_assert(sizeof(NavModelOutputXY) == sizeof(float)*2); |
||||
|
||||
struct NavModelOutputPlan { |
||||
std::array<NavModelOutputXY, TRAJECTORY_SIZE> mean; |
||||
std::array<NavModelOutputXY, TRAJECTORY_SIZE> std; |
||||
}; |
||||
static_assert(sizeof(NavModelOutputPlan) == sizeof(NavModelOutputXY)*TRAJECTORY_SIZE*2); |
||||
|
||||
struct NavModelOutputDesirePrediction { |
||||
std::array<float, NAV_DESIRE_LEN> values; |
||||
}; |
||||
static_assert(sizeof(NavModelOutputDesirePrediction) == sizeof(float)*NAV_DESIRE_LEN); |
||||
|
||||
struct NavModelOutputFeatures { |
||||
std::array<float, NAV_FEATURE_LEN> values; |
||||
}; |
||||
static_assert(sizeof(NavModelOutputFeatures) == sizeof(float)*NAV_FEATURE_LEN); |
||||
|
||||
struct NavModelResult { |
||||
const NavModelOutputPlan plan; |
||||
const NavModelOutputDesirePrediction desire_pred; |
||||
const NavModelOutputFeatures features; |
||||
float dsp_execution_time; |
||||
}; |
||||
static_assert(sizeof(NavModelResult) == sizeof(NavModelOutputPlan) + sizeof(NavModelOutputDesirePrediction) + sizeof(NavModelOutputFeatures) + sizeof(float)); |
||||
|
||||
constexpr int NAV_OUTPUT_SIZE = sizeof(NavModelResult) / sizeof(float); |
||||
constexpr int NAV_NET_OUTPUT_SIZE = NAV_OUTPUT_SIZE - 1; |
||||
|
||||
struct NavModelState { |
||||
RunModel *m; |
||||
uint8_t net_input_buf[NAV_INPUT_SIZE]; |
||||
float output[NAV_OUTPUT_SIZE]; |
||||
}; |
||||
|
||||
void navmodel_init(NavModelState* s); |
||||
NavModelResult* navmodel_eval_frame(NavModelState* s, VisionBuf* buf); |
||||
void navmodel_publish(PubMaster &pm, VisionIpcBufExtra &extra, const NavModelResult &model_res, float execution_time, bool route_valid); |
||||
void navmodel_free(NavModelState* s); |
@ -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 ./_navmodeld |
@ -1,70 +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/nav.h" |
||||
|
||||
ExitHandler do_exit; |
||||
|
||||
void run_model(NavModelState &model, VisionIpcClient &vipc_client) { |
||||
SubMaster sm({"navInstruction"}); |
||||
PubMaster pm({"navModel"}); |
||||
|
||||
//double last_ts = 0;
|
||||
//uint32_t last_frame_id = 0;
|
||||
VisionIpcBufExtra extra = {}; |
||||
|
||||
while (!do_exit) { |
||||
VisionBuf *buf = vipc_client.recv(&extra); |
||||
if (buf == nullptr) continue; |
||||
|
||||
sm.update(0); |
||||
|
||||
double t1 = millis_since_boot(); |
||||
NavModelResult *model_res = navmodel_eval_frame(&model, buf); |
||||
double t2 = millis_since_boot(); |
||||
|
||||
// send navmodel packet
|
||||
navmodel_publish(pm, extra, *model_res, (t2 - t1) / 1000.0, sm["navInstruction"].getValid()); |
||||
|
||||
//printf("navmodel process: %.2fms, from last %.2fms\n", t2 - t1, t1 - last_ts);
|
||||
//last_ts = t1;
|
||||
//last_frame_id = extra.frame_id;
|
||||
} |
||||
} |
||||
|
||||
int main(int argc, char **argv) { |
||||
setpriority(PRIO_PROCESS, 0, -15); |
||||
|
||||
// there exists a race condition when two processes try to create a
|
||||
// SNPE model runner at the same time, wait for dmonitoringmodeld to finish
|
||||
LOGW("waiting for dmonitoringmodeld to initialize"); |
||||
if (!Params().getBool("DmModelInitialized", true)) { |
||||
return 0; |
||||
} |
||||
|
||||
// init the models
|
||||
NavModelState model; |
||||
navmodel_init(&model); |
||||
LOGW("models loaded, navmodeld starting"); |
||||
|
||||
VisionIpcClient vipc_client = VisionIpcClient("navd", VISION_STREAM_MAP, 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); |
||||
} |
||||
|
||||
navmodel_free(&model); |
||||
return 0; |
||||
} |
@ -0,0 +1,118 @@ |
||||
#!/usr/bin/env python3 |
||||
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 |
||||
from openpilot.system.swaglog import cloudlog |
||||
from openpilot.common.params import Params |
||||
from openpilot.common.realtime import set_realtime_priority |
||||
from openpilot.selfdrive.modeld.constants import IDX_N |
||||
from openpilot.selfdrive.modeld.runners import ModelRunner, Runtime |
||||
|
||||
NAV_INPUT_SIZE = 256*256 |
||||
NAV_FEATURE_LEN = 256 |
||||
NAV_DESIRE_LEN = 32 |
||||
NAV_OUTPUT_SIZE = 2*2*IDX_N + NAV_DESIRE_LEN + NAV_FEATURE_LEN |
||||
MODEL_PATHS = { |
||||
ModelRunner.SNPE: Path(__file__).parent / 'models/navmodel_q.dlc', |
||||
ModelRunner.ONNX: Path(__file__).parent / 'models/navmodel.onnx'} |
||||
|
||||
class NavModelOutputXY(ctypes.Structure): |
||||
_fields_ = [ |
||||
("x", ctypes.c_float), |
||||
("y", ctypes.c_float)] |
||||
|
||||
class NavModelOutputPlan(ctypes.Structure): |
||||
_fields_ = [ |
||||
("mean", NavModelOutputXY*IDX_N), |
||||
("std", NavModelOutputXY*IDX_N)] |
||||
|
||||
class NavModelResult(ctypes.Structure): |
||||
_fields_ = [ |
||||
("plan", NavModelOutputPlan), |
||||
("desire_pred", ctypes.c_float*NAV_DESIRE_LEN), |
||||
("features", ctypes.c_float*NAV_FEATURE_LEN)] |
||||
|
||||
class ModelState: |
||||
inputs: Dict[str, np.ndarray] |
||||
output: np.ndarray |
||||
model: ModelRunner |
||||
|
||||
def __init__(self): |
||||
assert ctypes.sizeof(NavModelResult) == NAV_OUTPUT_SIZE * ctypes.sizeof(ctypes.c_float) |
||||
self.output = np.zeros(NAV_OUTPUT_SIZE, dtype=np.float32) |
||||
self.inputs = {'map': np.zeros(NAV_INPUT_SIZE, dtype=np.uint8)} |
||||
self.model = ModelRunner(MODEL_PATHS, self.output, Runtime.DSP, True, None) |
||||
self.model.addInput("map", None) |
||||
|
||||
def run(self, buf:np.ndarray) -> Tuple[np.ndarray, float]: |
||||
self.inputs['map'][:] = buf |
||||
|
||||
t1 = time.perf_counter() |
||||
self.model.setInputBuffer("map", self.inputs['map'].view(np.float32)) |
||||
self.model.execute() |
||||
t2 = time.perf_counter() |
||||
return self.output, t2 - t1 |
||||
|
||||
def get_navmodel_packet(model_output: np.ndarray, valid: bool, frame_id: int, location_ts: int, execution_time: float, dsp_execution_time: float): |
||||
model_result = ctypes.cast(model_output.ctypes.data, ctypes.POINTER(NavModelResult)).contents |
||||
msg = messaging.new_message('navModel') |
||||
msg.valid = valid |
||||
msg.navModel.frameId = frame_id |
||||
msg.navModel.locationMonoTime = location_ts |
||||
msg.navModel.modelExecutionTime = execution_time |
||||
msg.navModel.dspExecutionTime = dsp_execution_time |
||||
msg.navModel.features = model_result.features[:] |
||||
msg.navModel.desirePrediction = model_result.desire_pred[:] |
||||
msg.navModel.position.x = [p.x for p in model_result.plan.mean] |
||||
msg.navModel.position.y = [p.y for p in model_result.plan.mean] |
||||
msg.navModel.position.xStd = [math.exp(p.x) for p in model_result.plan.std] |
||||
msg.navModel.position.yStd = [math.exp(p.y) for p in model_result.plan.std] |
||||
return msg |
||||
|
||||
|
||||
def main(): |
||||
gc.disable() |
||||
set_realtime_priority(1) |
||||
|
||||
# there exists a race condition when two processes try to create a |
||||
# SNPE model runner at the same time, wait for dmonitoringmodeld to finish |
||||
cloudlog.warning("waiting for dmonitoringmodeld to initialize") |
||||
if not Params().get_bool("DmModelInitialized", True): |
||||
return |
||||
|
||||
model = ModelState() |
||||
cloudlog.warning("models loaded, navmodeld starting") |
||||
|
||||
vipc_client = VisionIpcClient("navd", VisionStreamType.VISION_STREAM_MAP, 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(["navInstruction"]) |
||||
pm = PubMaster(["navModel"]) |
||||
|
||||
while True: |
||||
buf = vipc_client.recv() |
||||
if buf is None: |
||||
continue |
||||
|
||||
sm.update(0) |
||||
t1 = time.perf_counter() |
||||
model_output, dsp_execution_time = model.run(buf.data[:buf.uv_offset]) |
||||
t2 = time.perf_counter() |
||||
|
||||
valid = vipc_client.valid and sm.valid["navInstruction"] |
||||
pm.send("navModel", get_navmodel_packet(model_output, valid, vipc_client.frame_id, vipc_client.timestamp_sof, t2 - t1, dsp_execution_time)) |
||||
|
||||
|
||||
if __name__ == "__main__": |
||||
main() |
Loading…
Reference in new issue