sunnypilot v2026.002.000

version: sunnypilot v2026.002.000 (staging)
date: 2026-07-21T07:44:02
master commit: 2c334ede443d7391d27575af8c854a095ba702a8
This commit is contained in:
github-actions[bot] committed 2026-07-21 07:44:02 +00:00
commit de77c89cf6
3995 files changed
+1260688

No files matched your search

+39
View File
@@ -0,0 +1,39 @@
"""
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
from enum import IntEnum
import hashlib
PARAMS_UPDATE_PERIOD = 3 # seconds
def get_file_hash(path: str) -> str:
sha256_hash = hashlib.sha256()
with open(path, "rb") as f:
for byte_block in iter(lambda: f.read(4096), b""):
sha256_hash.update(byte_block)
return sha256_hash.hexdigest()
class IntEnumBase(IntEnum):
@classmethod
def min(cls):
return min(cls)
@classmethod
def max(cls):
return max(cls)
def get_sanitize_int_param(key: str, min_val: int, max_val: int, params) -> int:
val: int = params.get(key, return_default=True)
clipped_val = max(min_val, min(max_val, val))
if clipped_val != val:
params.put(key, clipped_val, block=True)
return clipped_val
@@ -0,0 +1,100 @@
#define _USE_MATH_DEFINES
#include "sunnypilot/common/transformations/coordinates.hpp"
#include <iostream>
#include <cmath>
#include <eigen3/Eigen/Dense>
double a = 6378137; // lgtm [cpp/short-global-name]
double b = 6356752.3142; // lgtm [cpp/short-global-name]
double esq = 6.69437999014 * 0.001; // lgtm [cpp/short-global-name]
double e1sq = 6.73949674228 * 0.001;
static Geodetic to_degrees(Geodetic geodetic){
geodetic.lat = RAD2DEG(geodetic.lat);
geodetic.lon = RAD2DEG(geodetic.lon);
return geodetic;
}
static Geodetic to_radians(Geodetic geodetic){
geodetic.lat = DEG2RAD(geodetic.lat);
geodetic.lon = DEG2RAD(geodetic.lon);
return geodetic;
}
ECEF geodetic2ecef(const Geodetic &geodetic) {
auto g = to_radians(geodetic);
double xi = sqrt(1.0 - esq * pow(sin(g.lat), 2));
double x = (a / xi + g.alt) * cos(g.lat) * cos(g.lon);
double y = (a / xi + g.alt) * cos(g.lat) * sin(g.lon);
double z = (a / xi * (1.0 - esq) + g.alt) * sin(g.lat);
return {x, y, z};
}
Geodetic ecef2geodetic(const ECEF &e) {
// Convert from ECEF to geodetic using Ferrari's methods
// https://en.wikipedia.org/wiki/Geographic_coordinate_conversion#Ferrari.27s_solution
double x = e.x;
double y = e.y;
double z = e.z;
double r = sqrt(x * x + y * y);
double Esq = a * a - b * b;
double F = 54 * b * b * z * z;
double G = r * r + (1 - esq) * z * z - esq * Esq;
double C = (esq * esq * F * r * r) / (pow(G, 3));
double S = cbrt(1 + C + sqrt(C * C + 2 * C));
double P = F / (3 * pow((S + 1 / S + 1), 2) * G * G);
double Q = sqrt(1 + 2 * esq * esq * P);
double r_0 = -(P * esq * r) / (1 + Q) + sqrt(0.5 * a * a*(1 + 1.0 / Q) - P * (1 - esq) * z * z / (Q * (1 + Q)) - 0.5 * P * r * r);
double U = sqrt(pow((r - esq * r_0), 2) + z * z);
double V = sqrt(pow((r - esq * r_0), 2) + (1 - esq) * z * z);
double Z_0 = b * b * z / (a * V);
double h = U * (1 - b * b / (a * V));
double lat = atan((z + e1sq * Z_0) / r);
double lon = atan2(y, x);
return to_degrees({lat, lon, h});
}
LocalCoord::LocalCoord(const Geodetic &geodetic, const ECEF &e) {
init_ecef << e.x, e.y, e.z;
auto g = to_radians(geodetic);
ned2ecef_matrix <<
-sin(g.lat)*cos(g.lon), -sin(g.lon), -cos(g.lat)*cos(g.lon),
-sin(g.lat)*sin(g.lon), cos(g.lon), -cos(g.lat)*sin(g.lon),
cos(g.lat), 0, -sin(g.lat);
ecef2ned_matrix = ned2ecef_matrix.transpose();
}
NED LocalCoord::ecef2ned(const ECEF &e) {
Eigen::Vector3d ecef;
ecef << e.x, e.y, e.z;
Eigen::Vector3d ned = (ecef2ned_matrix * (ecef - init_ecef));
return {ned[0], ned[1], ned[2]};
}
ECEF LocalCoord::ned2ecef(const NED &n) {
Eigen::Vector3d ned;
ned << n.n, n.e, n.d;
Eigen::Vector3d ecef = (ned2ecef_matrix * ned) + init_ecef;
return {ecef[0], ecef[1], ecef[2]};
}
NED LocalCoord::geodetic2ned(const Geodetic &g) {
ECEF e = ::geodetic2ecef(g);
return ecef2ned(e);
}
Geodetic LocalCoord::ned2geodetic(const NED &n) {
ECEF e = ned2ecef(n);
return ::ecef2geodetic(e);
}
@@ -0,0 +1,43 @@
#pragma once
#include <eigen3/Eigen/Dense>
#define DEG2RAD(x) ((x) * M_PI / 180.0)
#define RAD2DEG(x) ((x) * 180.0 / M_PI)
struct ECEF {
double x, y, z;
Eigen::Vector3d to_vector() const {
return Eigen::Vector3d(x, y, z);
}
};
struct NED {
double n, e, d;
Eigen::Vector3d to_vector() const {
return Eigen::Vector3d(n, e, d);
}
};
struct Geodetic {
double lat, lon, alt;
bool radians=false;
};
ECEF geodetic2ecef(const Geodetic &g);
Geodetic ecef2geodetic(const ECEF &e);
class LocalCoord {
public:
Eigen::Matrix3d ned2ecef_matrix;
Eigen::Matrix3d ecef2ned_matrix;
Eigen::Vector3d init_ecef;
LocalCoord(const Geodetic &g, const ECEF &e);
LocalCoord(const Geodetic &g) : LocalCoord(g, ::geodetic2ecef(g)) {}
LocalCoord(const ECEF &e) : LocalCoord(::ecef2geodetic(e), e) {}
NED ecef2ned(const ECEF &e);
ECEF ned2ecef(const NED &n);
NED geodetic2ned(const Geodetic &g);
Geodetic ned2geodetic(const NED &n);
};
@@ -0,0 +1,143 @@
#define _USE_MATH_DEFINES
#include <iostream>
#include <cmath>
#include <eigen3/Eigen/Dense>
#include "sunnypilot/common/transformations/orientation.hpp"
#include "sunnypilot/common/transformations/coordinates.hpp"
Eigen::Quaterniond ensure_unique(const Eigen::Quaterniond &quat) {
if (quat.w() > 0){
return quat;
} else {
return Eigen::Quaterniond(-quat.w(), -quat.x(), -quat.y(), -quat.z());
}
}
Eigen::Quaterniond euler2quat(const Eigen::Vector3d &euler) {
Eigen::Quaterniond q;
q = Eigen::AngleAxisd(euler(2), Eigen::Vector3d::UnitZ())
* Eigen::AngleAxisd(euler(1), Eigen::Vector3d::UnitY())
* Eigen::AngleAxisd(euler(0), Eigen::Vector3d::UnitX());
return ensure_unique(q);
}
Eigen::Vector3d quat2euler(const Eigen::Quaterniond &quat) {
// TODO: switch to eigen implementation if the range of the Euler angles doesn't matter anymore
// Eigen::Vector3d euler = quat.toRotationMatrix().eulerAngles(2, 1, 0);
// return {euler(2), euler(1), euler(0)};
double gamma = atan2(2 * (quat.w() * quat.x() + quat.y() * quat.z()), 1 - 2 * (quat.x()*quat.x() + quat.y()*quat.y()));
double asin_arg_clipped = std::clamp(2 * (quat.w() * quat.y() - quat.z() * quat.x()), -1.0, 1.0);
double theta = asin(asin_arg_clipped);
double psi = atan2(2 * (quat.w() * quat.z() + quat.x() * quat.y()), 1 - 2 * (quat.y()*quat.y() + quat.z()*quat.z()));
return {gamma, theta, psi};
}
Eigen::Matrix3d quat2rot(const Eigen::Quaterniond &quat) {
return quat.toRotationMatrix();
}
Eigen::Quaterniond rot2quat(const Eigen::Matrix3d &rot) {
return ensure_unique(Eigen::Quaterniond(rot));
}
Eigen::Matrix3d euler2rot(const Eigen::Vector3d &euler) {
return quat2rot(euler2quat(euler));
}
Eigen::Vector3d rot2euler(const Eigen::Matrix3d &rot) {
return quat2euler(rot2quat(rot));
}
Eigen::Matrix3d rot_matrix(double roll, double pitch, double yaw) {
return euler2rot({roll, pitch, yaw});
}
Eigen::Matrix3d rot(const Eigen::Vector3d &axis, double angle) {
Eigen::Quaterniond q;
q = Eigen::AngleAxisd(angle, axis);
return q.toRotationMatrix();
}
Eigen::Vector3d ecef_euler_from_ned(const ECEF &ecef_init, const Eigen::Vector3d &ned_pose) {
/*
Using Rotations to Build Aerospace Coordinate Systems
Don Koks
https://apps.dtic.mil/dtic/tr/fulltext/u2/a484864.pdf
*/
LocalCoord converter = LocalCoord(ecef_init);
Eigen::Vector3d zero = ecef_init.to_vector();
Eigen::Vector3d x0 = converter.ned2ecef({1, 0, 0}).to_vector() - zero;
Eigen::Vector3d y0 = converter.ned2ecef({0, 1, 0}).to_vector() - zero;
Eigen::Vector3d z0 = converter.ned2ecef({0, 0, 1}).to_vector() - zero;
Eigen::Vector3d x1 = rot(z0, ned_pose(2)) * x0;
Eigen::Vector3d y1 = rot(z0, ned_pose(2)) * y0;
Eigen::Vector3d z1 = rot(z0, ned_pose(2)) * z0;
Eigen::Vector3d x2 = rot(y1, ned_pose(1)) * x1;
Eigen::Vector3d y2 = rot(y1, ned_pose(1)) * y1;
Eigen::Vector3d z2 = rot(y1, ned_pose(1)) * z1;
Eigen::Vector3d x3 = rot(x2, ned_pose(0)) * x2;
Eigen::Vector3d y3 = rot(x2, ned_pose(0)) * y2;
x0 = Eigen::Vector3d(1, 0, 0);
y0 = Eigen::Vector3d(0, 1, 0);
z0 = Eigen::Vector3d(0, 0, 1);
double psi = atan2(x3.dot(y0), x3.dot(x0));
double theta = atan2(-x3.dot(z0), sqrt(pow(x3.dot(x0), 2) + pow(x3.dot(y0), 2)));
y2 = rot(z0, psi) * y0;
z2 = rot(y2, theta) * z0;
double phi = atan2(y3.dot(z2), y3.dot(y2));
return {phi, theta, psi};
}
Eigen::Vector3d ned_euler_from_ecef(const ECEF &ecef_init, const Eigen::Vector3d &ecef_pose) {
/*
Using Rotations to Build Aerospace Coordinate Systems
Don Koks
https://apps.dtic.mil/dtic/tr/fulltext/u2/a484864.pdf
*/
LocalCoord converter = LocalCoord(ecef_init);
Eigen::Vector3d x0 = Eigen::Vector3d(1, 0, 0);
Eigen::Vector3d y0 = Eigen::Vector3d(0, 1, 0);
Eigen::Vector3d z0 = Eigen::Vector3d(0, 0, 1);
Eigen::Vector3d x1 = rot(z0, ecef_pose(2)) * x0;
Eigen::Vector3d y1 = rot(z0, ecef_pose(2)) * y0;
Eigen::Vector3d z1 = rot(z0, ecef_pose(2)) * z0;
Eigen::Vector3d x2 = rot(y1, ecef_pose(1)) * x1;
Eigen::Vector3d y2 = rot(y1, ecef_pose(1)) * y1;
Eigen::Vector3d z2 = rot(y1, ecef_pose(1)) * z1;
Eigen::Vector3d x3 = rot(x2, ecef_pose(0)) * x2;
Eigen::Vector3d y3 = rot(x2, ecef_pose(0)) * y2;
Eigen::Vector3d zero = ecef_init.to_vector();
x0 = converter.ned2ecef({1, 0, 0}).to_vector() - zero;
y0 = converter.ned2ecef({0, 1, 0}).to_vector() - zero;
z0 = converter.ned2ecef({0, 0, 1}).to_vector() - zero;
double psi = atan2(x3.dot(y0), x3.dot(x0));
double theta = atan2(-x3.dot(z0), sqrt(pow(x3.dot(x0), 2) + pow(x3.dot(y0), 2)));
y2 = rot(z0, psi) * y0;
z2 = rot(y2, theta) * z0;
double phi = atan2(y3.dot(z2), y3.dot(y2));
return {phi, theta, psi};
}
@@ -0,0 +1,17 @@
#pragma once
#include <eigen3/Eigen/Dense>
#include "sunnypilot/common/transformations/coordinates.hpp"
Eigen::Quaterniond ensure_unique(const Eigen::Quaterniond &quat);
Eigen::Quaterniond euler2quat(const Eigen::Vector3d &euler);
Eigen::Vector3d quat2euler(const Eigen::Quaterniond &quat);
Eigen::Matrix3d quat2rot(const Eigen::Quaterniond &quat);
Eigen::Quaterniond rot2quat(const Eigen::Matrix3d &rot);
Eigen::Matrix3d euler2rot(const Eigen::Vector3d &euler);
Eigen::Vector3d rot2euler(const Eigen::Matrix3d &rot);
Eigen::Matrix3d rot_matrix(double roll, double pitch, double yaw);
Eigen::Matrix3d rot(const Eigen::Vector3d &axis, double angle);
Eigen::Vector3d ecef_euler_from_ned(const ECEF &ecef_init, const Eigen::Vector3d &ned_pose);
Eigen::Vector3d ned_euler_from_ecef(const ECEF &ecef_init, const Eigen::Vector3d &ecef_pose);
+1
View File
@@ -0,0 +1 @@
#define SUNNYPILOT_VERSION "2026.002.000"
Whitespace-only changes.
+14
View File
@@ -0,0 +1,14 @@
"""
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
from openpilot.common.params import Params
def get_lat_delay(params: Params, stock_lat_delay: float) -> float:
if params.get_bool("LagdToggle"):
return float(params.get("LagdValueCache", return_default=True))
return stock_lat_delay
@@ -0,0 +1,38 @@
"""
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
from openpilot.cereal import log
from opendbc.car import structs
from openpilot.common.params import Params
class LagdToggle:
def __init__(self, CP: structs.CarParams):
self.CP = CP
self.params = Params()
self.lag = 0.0
self.lagd_toggle = self.params.get_bool("LagdToggle")
self.software_delay = self.params.get("LagdToggleDelay", return_default=True)
def read_params(self) -> None:
self.lagd_toggle = self.params.get_bool("LagdToggle")
self.software_delay = self.params.get("LagdToggleDelay", return_default=True)
def update(self, lag_msg: log.LiveDelayData) -> None:
self.read_params()
if not self.lagd_toggle:
steer_actuator_delay = self.CP.steerActuatorDelay
delay = self.software_delay
self.lag = (steer_actuator_delay + delay)
self.params.put("LagdValueCache", self.lag)
return
lateral_delay = lag_msg.liveDelay.lateralDelay
self.lag = lateral_delay
self.params.put("LagdValueCache", self.lag)
+73
View File
@@ -0,0 +1,73 @@
"""
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
from openpilot.common.params import Params
from opendbc.car import structs
from opendbc.safety import ALTERNATIVE_EXPERIENCE
from opendbc.sunnypilot.car.hyundai.values import HyundaiFlagsSP, HyundaiSafetyFlagsSP
from opendbc.sunnypilot.car.tesla.values import TeslaFlagsSP
MADS_NO_ACC_MAIN_BUTTON = ("rivian", "tesla")
class MadsSteeringModeOnBrake:
REMAIN_ACTIVE = 0
PAUSE = 1
DISENGAGE = 2
def get_mads_limited_brands(CP: structs.CarParams, CP_SP: structs.CarParamsSP) -> bool:
if CP.brand == 'rivian':
return True
if CP.brand == 'tesla':
return not CP_SP.flags & TeslaFlagsSP.HAS_VEHICLE_BUS
return False
def read_steering_mode_param(CP: structs.CarParams, CP_SP: structs.CarParamsSP, params: Params):
if get_mads_limited_brands(CP, CP_SP):
return MadsSteeringModeOnBrake.DISENGAGE
return params.get("MadsSteeringMode", return_default=True)
def set_alternative_experience(CP: structs.CarParams, CP_SP: structs.CarParamsSP, params: Params):
enabled = params.get_bool("Mads")
steering_mode = read_steering_mode_param(CP, CP_SP, params)
if enabled:
CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.ENABLE_MADS
if steering_mode == MadsSteeringModeOnBrake.DISENGAGE:
CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.MADS_DISENGAGE_LATERAL_ON_BRAKE
elif steering_mode == MadsSteeringModeOnBrake.PAUSE:
CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.MADS_PAUSE_LATERAL_ON_BRAKE
def set_car_specific_params(CP: structs.CarParams, CP_SP: structs.CarParamsSP, params: Params):
if CP.brand == "hyundai":
# TODO-SP: This should be separated from MADS module for future implementations
# Use "HyundaiLongitudinalMainCruiseToggleable" param
hyundai_cruise_main_toggleable = True
if hyundai_cruise_main_toggleable:
CP_SP.flags |= HyundaiFlagsSP.LONGITUDINAL_MAIN_CRUISE_TOGGLEABLE.value
CP_SP.safetyParam |= HyundaiSafetyFlagsSP.LONG_MAIN_CRUISE_TOGGLEABLE
# MADS Partial Support
# MADS is currently partially supported for these platforms due to lack of consistent states to engage controls
# Only MadsSteeringModeOnBrake.DISENGAGE is supported for these platforms
# TODO-SP: To enable MADS full support for Rivian and most Tesla, identify consistent signals for MADS toggling
mads_partial_support = get_mads_limited_brands(CP, CP_SP)
if mads_partial_support:
params.put("MadsSteeringMode", 2, block=True)
params.put_bool("MadsUnifiedEngagementMode", True, block=True)
# no ACC MAIN button for these brands
if CP.brand in MADS_NO_ACC_MAIN_BUTTON:
params.remove("MadsMainCruiseAllowed")
+222
View File
@@ -0,0 +1,222 @@
"""
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
from openpilot.cereal import log, custom
from opendbc.car import structs
from opendbc.car.hyundai.values import HyundaiFlags
from openpilot.common.params import Params
from openpilot.sunnypilot.mads.helpers import MadsSteeringModeOnBrake, read_steering_mode_param, MADS_NO_ACC_MAIN_BUTTON
from openpilot.sunnypilot.mads.state import StateMachine, GEARS_ALLOW_PAUSED_SILENT
State = custom.ModularAssistiveDrivingSystem.ModularAssistiveDrivingSystemState
ButtonType = structs.CarState.ButtonEvent.Type
EventName = log.OnroadEvent.EventName
EventNameSP = custom.OnroadEventSP.EventName
GearShifter = structs.CarState.GearShifter
SafetyModel = structs.CarParams.SafetyModel
SET_SPEED_BUTTONS = (ButtonType.accelCruise, ButtonType.resumeCruise, ButtonType.decelCruise, ButtonType.setCruise)
IGNORED_SAFETY_MODES = (SafetyModel.silent, SafetyModel.noOutput)
class ModularAssistiveDrivingSystem:
def __init__(self, selfdrive):
self.CP = selfdrive.CP
self.CP_SP = selfdrive.CP_SP
self.params = selfdrive.params
self.enabled = False
self.active = False
self.available = False
self.lateral_mismatch_counter = 0
self.allow_always = False
self.no_main_cruise = False
self.selfdrive = selfdrive
self.selfdrive.enabled_prev = False
self.state_machine = StateMachine(self)
self.events = self.selfdrive.events
self.events_sp = self.selfdrive.events_sp
self.disengage_on_accelerator = Params().get_bool("DisengageOnAccelerator")
if self.CP.brand == "hyundai":
if self.CP.flags & (HyundaiFlags.HAS_LDA_BUTTON | HyundaiFlags.CANFD):
self.allow_always = True
if self.CP.brand == "tesla":
self.allow_always = True
if self.CP.brand in MADS_NO_ACC_MAIN_BUTTON:
self.no_main_cruise = True
# read params on init
self.enabled_toggle = self.params.get_bool("Mads")
self.main_enabled_toggle = self.params.get_bool("MadsMainCruiseAllowed")
self.steering_mode_on_brake = read_steering_mode_param(self.CP, self.CP_SP, self.params)
self.unified_engagement_mode = self.params.get_bool("MadsUnifiedEngagementMode")
def read_params(self):
self.main_enabled_toggle = self.params.get_bool("MadsMainCruiseAllowed")
self.unified_engagement_mode = self.params.get_bool("MadsUnifiedEngagementMode")
def pedal_pressed_non_gas_pressed(self, CS: structs.CarState) -> bool:
# ignore `pedalPressed` events caused by gas presses
if self.events.has(EventName.pedalPressed) and not (CS.gasPressed and not self.selfdrive.CS_prev.gasPressed and self.disengage_on_accelerator):
return True
return False
def should_silent_lkas_enable(self, CS: structs.CarState) -> bool:
if self.steering_mode_on_brake == MadsSteeringModeOnBrake.PAUSE and (CS.brakePressed or CS.regenBraking or self.pedal_pressed_non_gas_pressed(CS)):
return False
if self.events_sp.contains_in_list(GEARS_ALLOW_PAUSED_SILENT):
return False
return True
def block_unified_engagement_mode(self) -> bool:
# UEM disabled
if not self.unified_engagement_mode:
return True
if self.enabled:
return True
if self.selfdrive.enabled and self.selfdrive.enabled_prev:
return True
return False
def get_wrong_car_mode(self, alert_only: bool) -> None:
if alert_only:
if self.events.has(EventName.wrongCarMode):
self.replace_event(EventName.wrongCarMode, EventNameSP.wrongCarModeAlertOnly)
else:
self.events.remove(EventName.wrongCarMode)
def transition_paused_state(self):
if self.state_machine.state != State.paused:
self.events_sp.add(EventNameSP.silentLkasDisable)
def replace_event(self, old_event: int, new_event: int):
self.events.remove(old_event)
self.events_sp.add(new_event)
def data_sample(self):
# When the safety and selfdrived do not agree on controls_allowed_lateral
# we want to disengage sunnypilot. However the status from the panda goes through
# another socket other than the CAN messages and one can arrive earlier than the other.
# Therefore we allow a mismatch for two samples, then we trigger the disengagement.
if not self.active or self.selfdrive.enabled:
self.lateral_mismatch_counter = 0
elif any(not ps.controlsAllowedLateral for ps in self.selfdrive.sm['pandaStates']
if ps.safetyModel not in IGNORED_SAFETY_MODES):
self.lateral_mismatch_counter += 1
def update_events(self, CS: structs.CarState):
if not self.selfdrive.enabled and self.enabled:
if CS.standstill:
if self.events.has(EventName.doorOpen):
self.replace_event(EventName.doorOpen, EventNameSP.silentDoorOpen)
self.transition_paused_state()
if self.events.has(EventName.seatbeltNotLatched):
self.replace_event(EventName.seatbeltNotLatched, EventNameSP.silentSeatbeltNotLatched)
self.transition_paused_state()
if self.events.has(EventName.wrongGear) and (CS.vEgo < 2.5 or CS.gearShifter == GearShifter.reverse):
self.replace_event(EventName.wrongGear, EventNameSP.silentWrongGear)
self.transition_paused_state()
if self.events.has(EventName.reverseGear):
self.replace_event(EventName.reverseGear, EventNameSP.silentReverseGear)
self.transition_paused_state()
if self.events.has(EventName.brakeHold):
self.replace_event(EventName.brakeHold, EventNameSP.silentBrakeHold)
self.transition_paused_state()
if self.events.has(EventName.parkBrake):
self.replace_event(EventName.parkBrake, EventNameSP.silentParkBrake)
self.transition_paused_state()
if self.steering_mode_on_brake == MadsSteeringModeOnBrake.PAUSE:
if self.pedal_pressed_non_gas_pressed(CS):
self.transition_paused_state()
self.events.remove(EventName.preEnableStandstill)
self.events.remove(EventName.belowEngageSpeed)
self.events.remove(EventName.speedTooLow)
self.events.remove(EventName.cruiseDisabled)
self.events.remove(EventName.manualRestart)
self.events.remove(EventName.espActive)
selfdrive_enable_events = self.events.has(EventName.pcmEnable) or self.events.has(EventName.buttonEnable)
set_speed_btns_enable = any(be.type in SET_SPEED_BUTTONS for be in CS.buttonEvents)
# wrongCarMode alert only or actively block control
self.get_wrong_car_mode(selfdrive_enable_events or set_speed_btns_enable)
if selfdrive_enable_events:
if self.pedal_pressed_non_gas_pressed(CS):
self.events_sp.add(EventNameSP.pedalPressedAlertOnly)
if self.block_unified_engagement_mode():
self.events.remove(EventName.pcmEnable)
self.events.remove(EventName.buttonEnable)
else:
if self.main_enabled_toggle:
if CS.cruiseState.available and not self.selfdrive.CS_prev.cruiseState.available:
self.events_sp.add(EventNameSP.lkasEnable)
for be in CS.buttonEvents:
if be.type == ButtonType.cancel:
if not self.selfdrive.enabled and self.selfdrive.enabled_prev:
self.events_sp.add(EventNameSP.manualLongitudinalRequired)
if be.type == ButtonType.lkas and be.pressed and (CS.cruiseState.available or self.allow_always):
if self.enabled:
if self.selfdrive.enabled:
self.events_sp.add(EventNameSP.manualSteeringRequired)
else:
self.events_sp.add(EventNameSP.lkasDisable)
else:
self.events_sp.add(EventNameSP.lkasEnable)
if not CS.cruiseState.available and not self.no_main_cruise:
self.events.remove(EventName.buttonEnable)
if self.selfdrive.CS_prev.cruiseState.available:
self.events_sp.add(EventNameSP.lkasDisable)
if self.steering_mode_on_brake == MadsSteeringModeOnBrake.DISENGAGE:
if self.pedal_pressed_non_gas_pressed(CS):
if self.enabled:
self.events_sp.add(EventNameSP.lkasDisable)
else:
# block lkasEnable if being sent, then send pedalPressedAlertOnly event
if self.events_sp.contains(EventNameSP.lkasEnable):
self.events_sp.remove(EventNameSP.lkasEnable)
self.events_sp.add(EventNameSP.pedalPressedAlertOnly)
if self.should_silent_lkas_enable(CS):
if self.state_machine.state == State.paused:
self.events_sp.add(EventNameSP.silentLkasEnable)
if self.lateral_mismatch_counter >= 200:
self.events_sp.add(EventNameSP.controlsMismatchLateral)
self.events.remove(EventName.pcmDisable)
self.events.remove(EventName.buttonCancel)
self.events.remove(EventName.pedalPressed)
self.events.remove(EventName.wrongCruiseMode)
def update(self, CS: structs.CarState):
if not self.enabled_toggle:
return
self.data_sample()
self.update_events(CS)
if not self.CP.passive and self.selfdrive.initialized:
self.enabled, self.active = self.state_machine.update()
# Copy of previous SelfdriveD states for MADS events handling
self.selfdrive.enabled_prev = self.selfdrive.enabled
+135
View File
@@ -0,0 +1,135 @@
"""
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
from openpilot.cereal import log, custom
from openpilot.selfdrive.selfdrived.events import ET
from openpilot.selfdrive.selfdrived.state import SOFT_DISABLE_TIME
from openpilot.common.realtime import DT_CTRL
State = custom.ModularAssistiveDrivingSystem.ModularAssistiveDrivingSystemState
EventName = log.OnroadEvent.EventName
EventNameSP = custom.OnroadEventSP.EventName
ACTIVE_STATES = (State.enabled, State.softDisabling, State.overriding)
ENABLED_STATES = (State.paused, *ACTIVE_STATES)
GEARS_ALLOW_PAUSED_SILENT = [EventNameSP.silentWrongGear, EventNameSP.silentReverseGear, EventNameSP.silentBrakeHold,
EventNameSP.silentDoorOpen, EventNameSP.silentSeatbeltNotLatched, EventNameSP.silentParkBrake]
GEARS_ALLOW_PAUSED = [EventName.wrongGear, EventName.reverseGear, EventName.brakeHold,
EventName.doorOpen, EventName.seatbeltNotLatched, EventName.parkBrake]
class StateMachine:
def __init__(self, mads):
self.selfdrive = mads.selfdrive
self.ss_state_machine = mads.selfdrive.state_machine
self._events = mads.selfdrive.events
self._events_sp = mads.selfdrive.events_sp
self.state = State.disabled
def add_current_alert_types(self, alert_type):
if not self.selfdrive.enabled:
self.ss_state_machine.current_alert_types.append(alert_type)
def check_contains(self, event_type: str) -> bool:
return bool(self._events.contains(event_type) or self._events_sp.contains(event_type))
def check_contains_in_list(self) -> bool:
return bool(self._events.contains_in_list(GEARS_ALLOW_PAUSED) or self._events_sp.contains_in_list(GEARS_ALLOW_PAUSED_SILENT))
def update(self):
# soft disable timer and current alert types are from the state machine of openpilot
# decrement the soft disable timer at every step, as it's reset on
# entrance in SOFT_DISABLING state
# ENABLED, SOFT DISABLING, PAUSED, OVERRIDING
if self.state != State.disabled:
# user and immediate disable always have priority in a non-disabled state
if self.check_contains(ET.USER_DISABLE):
if self._events_sp.has(EventNameSP.silentLkasDisable):
self.state = State.paused
else:
self.state = State.disabled
self.ss_state_machine.current_alert_types.append(ET.USER_DISABLE)
elif self.check_contains(ET.IMMEDIATE_DISABLE):
self.state = State.disabled
self.add_current_alert_types(ET.IMMEDIATE_DISABLE)
else:
# ENABLED
if self.state == State.enabled:
if self.check_contains(ET.SOFT_DISABLE):
self.state = State.softDisabling
if not self.selfdrive.enabled:
self.ss_state_machine.soft_disable_timer = int(SOFT_DISABLE_TIME / DT_CTRL)
self.ss_state_machine.current_alert_types.append(ET.SOFT_DISABLE)
elif self.check_contains(ET.OVERRIDE_LATERAL):
self.state = State.overriding
self.add_current_alert_types(ET.OVERRIDE_LATERAL)
# SOFT DISABLING
elif self.state == State.softDisabling:
if not self.check_contains(ET.SOFT_DISABLE):
# no more soft disabling condition, so go back to ENABLED
self.state = State.enabled
elif self.ss_state_machine.soft_disable_timer > 0:
self.add_current_alert_types(ET.SOFT_DISABLE)
elif self.ss_state_machine.soft_disable_timer <= 0:
self.state = State.disabled
# PAUSED
elif self.state == State.paused:
if self.check_contains(ET.ENABLE):
if self.check_contains(ET.NO_ENTRY):
self.add_current_alert_types(ET.NO_ENTRY)
else:
if self.check_contains(ET.OVERRIDE_LATERAL):
self.state = State.overriding
else:
self.state = State.enabled
self.add_current_alert_types(ET.ENABLE)
# OVERRIDING
elif self.state == State.overriding:
if self.check_contains(ET.SOFT_DISABLE):
self.state = State.softDisabling
if not self.selfdrive.enabled:
self.ss_state_machine.soft_disable_timer = int(SOFT_DISABLE_TIME / DT_CTRL)
self.ss_state_machine.current_alert_types.append(ET.SOFT_DISABLE)
elif not self.check_contains(ET.OVERRIDE_LATERAL):
self.state = State.enabled
else:
self.ss_state_machine.current_alert_types += [ET.OVERRIDE_LATERAL]
# DISABLED
elif self.state == State.disabled:
if self.check_contains(ET.ENABLE):
if self.check_contains(ET.NO_ENTRY):
if self.check_contains_in_list():
self.state = State.paused
self.add_current_alert_types(ET.NO_ENTRY)
else:
if self.check_contains(ET.OVERRIDE_LATERAL):
self.state = State.overriding
else:
self.state = State.enabled
self.add_current_alert_types(ET.ENABLE)
# check if MADS is engaged and actuators are enabled
enabled = self.state in ENABLED_STATES
active = self.state in ACTIVE_STATES
if active:
self.add_current_alert_types(ET.WARNING)
return enabled, active
@@ -0,0 +1,144 @@
"""
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
import pytest
from pytest_mock import MockerFixture
from openpilot.cereal import custom
from openpilot.common.realtime import DT_CTRL
from openpilot.sunnypilot.mads.state import StateMachine, SOFT_DISABLE_TIME
from openpilot.selfdrive.selfdrived.events import ET, NormalPermanentAlert, Events
from openpilot.sunnypilot.selfdrive.selfdrived.events import EventsSP, EVENTS_SP
State = custom.ModularAssistiveDrivingSystem.ModularAssistiveDrivingSystemState
EventNameSP = custom.OnroadEventSP.EventName
# The event types that maintain the current state
MAINTAIN_STATES = {State.enabled: (None,), State.disabled: (None,), State.softDisabling: (ET.SOFT_DISABLE,),
State.paused: (None,), State.overriding: (ET.OVERRIDE_LATERAL,)}
ALL_STATES = (State.schema.enumerants.values())
# The event types checked in DISABLED section of state machine
ENABLE_EVENT_TYPES = (ET.ENABLE, ET.OVERRIDE_LATERAL)
def make_event(event_types):
event = {}
for ev in event_types:
event[ev] = NormalPermanentAlert("alert")
EVENTS_SP[0] = event
return 0
class MockMADS:
def __init__(self, mocker: MockerFixture):
self.selfdrive = mocker.MagicMock()
self.selfdrive.state_machine = mocker.MagicMock()
self.selfdrive.events = Events()
self.selfdrive.events_sp = EventsSP()
class TestMADSStateMachine:
@pytest.fixture(autouse=True)
def setup_method(self, mocker: MockerFixture):
self.mads = MockMADS(mocker)
self.state_machine = StateMachine(self.mads)
self.events = self.mads.selfdrive.events
self.events_sp = self.mads.selfdrive.events_sp
self.mads.selfdrive.state_machine.soft_disable_timer = int(SOFT_DISABLE_TIME / DT_CTRL)
def clear_events(self):
self.events.clear()
self.events_sp.clear()
def test_immediate_disable(self):
for state in ALL_STATES:
for et in MAINTAIN_STATES[state]:
self.events_sp.add(make_event([et, ET.IMMEDIATE_DISABLE]))
self.state_machine.state = state
self.state_machine.update()
assert State.disabled == self.state_machine.state
self.clear_events()
def test_user_disable(self):
for state in ALL_STATES:
for et in MAINTAIN_STATES[state]:
self.events_sp.add(make_event([et, ET.USER_DISABLE]))
self.state_machine.state = state
self.state_machine.update()
assert State.disabled == self.state_machine.state
self.clear_events()
def test_user_disable_to_paused(self):
paused_events = (EventNameSP.silentLkasDisable, )
for state in ALL_STATES:
for et in MAINTAIN_STATES[state]:
self.events_sp.add(make_event([et, ET.USER_DISABLE]))
for en in paused_events:
self.events_sp.add(en)
self.state_machine.state = state
self.state_machine.update()
final_state = State.paused if self.events_sp.has(en) and state != State.disabled else State.disabled
assert self.state_machine.state == final_state
self.clear_events()
def test_soft_disable(self):
for state in ALL_STATES:
for et in MAINTAIN_STATES[state]:
self.events_sp.add(make_event([et, ET.SOFT_DISABLE]))
self.state_machine.state = state
self.state_machine.update()
assert self.state_machine.state == State.disabled if state == State.disabled else State.softDisabling
self.clear_events()
def test_soft_disable_timer(self):
self.state_machine.state = State.enabled
self.events_sp.add(make_event([ET.SOFT_DISABLE]))
self.state_machine.update()
for _ in range(int(SOFT_DISABLE_TIME / DT_CTRL)):
assert self.state_machine.state == State.softDisabling
self.mads.selfdrive.state_machine.soft_disable_timer -= 1
self.state_machine.update()
assert self.state_machine.state == State.disabled
self.clear_events()
def test_no_entry(self):
for et in ENABLE_EVENT_TYPES:
self.events_sp.add(make_event([ET.NO_ENTRY, et]))
self.state_machine.update()
assert self.state_machine.state == State.disabled
self.clear_events()
def test_no_entry_paused(self):
self.state_machine.state = State.paused
self.events_sp.add(make_event([ET.NO_ENTRY]))
self.state_machine.update()
assert self.state_machine.state == State.paused
self.clear_events()
def test_override_lateral(self):
self.state_machine.state = State.enabled
self.events_sp.add(make_event([ET.OVERRIDE_LATERAL]))
self.state_machine.update()
assert self.state_machine.state == State.overriding
self.clear_events()
def test_paused_to_enabled(self):
self.state_machine.state = State.paused
self.events_sp.add(make_event([ET.ENABLE]))
self.state_machine.update()
assert self.state_machine.state == State.enabled
self.clear_events()
def test_maintain_states(self):
for state in ALL_STATES:
for et in MAINTAIN_STATES[state]:
self.state_machine.state = state
self.events_sp.add(make_event([et]))
self.state_machine.update()
assert self.state_machine.state == state
self.clear_events()
@@ -0,0 +1,242 @@
"""
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
import pytest
from openpilot.cereal import log, custom
from opendbc.car import structs
from openpilot.selfdrive.selfdrived.events import Events
from openpilot.sunnypilot.selfdrive.selfdrived.events import EventsSP
from openpilot.sunnypilot.mads.helpers import MadsSteeringModeOnBrake, read_steering_mode_param
from openpilot.sunnypilot.mads.mads import ModularAssistiveDrivingSystem
from opendbc.sunnypilot.car.tesla.values import TeslaFlagsSP
State = custom.ModularAssistiveDrivingSystem.ModularAssistiveDrivingSystemState
EventName = log.OnroadEvent.EventName
EventNameSP = custom.OnroadEventSP.EventName
SafetyModel = structs.CarParams.SafetyModel
def make_car_state(brake_pressed=False, regen_braking=False, standstill=False, v_ego=0.0):
cs = structs.CarState()
cs.brakePressed = brake_pressed
cs.regenBraking = regen_braking
cs.standstill = standstill
cs.vEgo = v_ego
cs.cruiseState.available = True
return cs
def make_panda_state(mocker, controls_allowed_lateral=True):
ps = mocker.MagicMock()
ps.controlsAllowedLateral = controls_allowed_lateral
ps.safetyModel = SafetyModel.hyundai
return ps
def make_mads(mocker, steering_mode):
sd = mocker.MagicMock()
sd.CP = structs.CarParams()
sd.CP.brand = "hyundai"
sd.CP_SP = structs.CarParamsSP()
sd.params = mocker.MagicMock()
sd.params.get_bool = mocker.MagicMock(side_effect=lambda k: {
"Mads": True, "MadsMainCruiseAllowed": False,
"DisengageOnAccelerator": True, "MadsUnifiedEngagementMode": False,
}.get(k, False))
sd.params.get = mocker.MagicMock(return_value=steering_mode)
sd.events = Events()
sd.events_sp = EventsSP()
sd.enabled = False
sd.enabled_prev = False
sd.initialized = True
sd.CS_prev = make_car_state()
sd.sm = {'pandaStates': [make_panda_state(mocker)]}
sd.state_machine = mocker.MagicMock()
mads = ModularAssistiveDrivingSystem(sd)
mads.enabled_toggle = True
mads.steering_mode_on_brake = steering_mode
return mads, sd
def run_frames(mads, sd, cs, n=1):
for _ in range(n):
mads.update(cs)
sd.CS_prev = cs
sd.events.clear()
sd.events_sp.clear()
# should_silent_lkas_enable across all modes
class TestShouldSilentLkasEnable:
@pytest.mark.parametrize("brake,regen", [(True, False), (False, True)])
def test_pause_blocks_reenable_on_braking_at_standstill(self, mocker, brake, regen):
mads, _ = make_mads(mocker, MadsSteeringModeOnBrake.PAUSE)
cs = make_car_state(brake_pressed=brake, regen_braking=regen, standstill=True)
assert mads.should_silent_lkas_enable(cs) is False
def test_pause_allows_reenable_on_brake_release(self, mocker):
mads, _ = make_mads(mocker, MadsSteeringModeOnBrake.PAUSE)
cs = make_car_state(standstill=True)
assert mads.should_silent_lkas_enable(cs) is True
def test_remain_active_ignores_brake(self, mocker):
mads, _ = make_mads(mocker, MadsSteeringModeOnBrake.REMAIN_ACTIVE)
cs = make_car_state(brake_pressed=True, standstill=True)
assert mads.should_silent_lkas_enable(cs) is True
def test_disengage_ignores_brake_for_silent_enable(self, mocker):
mads, _ = make_mads(mocker, MadsSteeringModeOnBrake.DISENGAGE)
cs = make_car_state(brake_pressed=True, standstill=True)
assert mads.should_silent_lkas_enable(cs) is True
# pause
class TestPauseMode:
def test_stays_paused_at_standstill_brake_held(self, mocker):
mads, sd = make_mads(mocker, MadsSteeringModeOnBrake.PAUSE)
mads.state_machine.state = State.enabled
mads.enabled = True
mads.active = True
sd.events.add(EventName.pedalPressed)
run_frames(mads, sd, make_car_state(brake_pressed=True, v_ego=15.0))
assert mads.state_machine.state == State.paused
sd.sm['pandaStates'] = [make_panda_state(mocker, False)]
run_frames(mads, sd, make_car_state(brake_pressed=True, standstill=True), n=250)
assert mads.state_machine.state == State.paused
def test_resumes_on_brake_release_at_standstill(self, mocker):
mads, sd = make_mads(mocker, MadsSteeringModeOnBrake.PAUSE)
mads.state_machine.state = State.paused
mads.enabled = True
mads.active = False
run_frames(mads, sd, make_car_state(standstill=True))
assert mads.state_machine.state == State.enabled
def test_full_cycle_moving_to_standstill(self, mocker):
mads, sd = make_mads(mocker, MadsSteeringModeOnBrake.PAUSE)
mads.state_machine.state = State.enabled
mads.enabled = True
mads.active = True
sd.events.add(EventName.pedalPressed)
run_frames(mads, sd, make_car_state(brake_pressed=True, v_ego=15.0))
assert mads.state_machine.state == State.paused
sd.sm['pandaStates'] = [make_panda_state(mocker, False)]
run_frames(mads, sd, make_car_state(brake_pressed=True, standstill=True), n=250)
assert mads.state_machine.state == State.paused
sd.sm['pandaStates'] = [make_panda_state(mocker, True)]
run_frames(mads, sd, make_car_state(standstill=True))
assert mads.state_machine.state == State.enabled
# disengage
class TestDisengageMode:
def test_brake_while_enabled_disables(self, mocker):
mads, sd = make_mads(mocker, MadsSteeringModeOnBrake.DISENGAGE)
mads.state_machine.state = State.enabled
mads.enabled = True
mads.active = True
sd.events.add(EventName.pedalPressed)
run_frames(mads, sd, make_car_state(brake_pressed=True, v_ego=10.0))
assert mads.state_machine.state == State.disabled
def test_brake_sends_lkas_disable_when_enabled(self, mocker):
mads, sd = make_mads(mocker, MadsSteeringModeOnBrake.DISENGAGE)
mads.state_machine.state = State.enabled
mads.enabled = True
mads.active = True
sd.events.add(EventName.pedalPressed)
mads.update_events(make_car_state(brake_pressed=True, v_ego=5.0))
assert sd.events_sp.has(EventNameSP.lkasDisable)
# remain active
class TestRemainActiveMode:
def test_brake_does_not_pause_or_disable(self, mocker):
mads, sd = make_mads(mocker, MadsSteeringModeOnBrake.REMAIN_ACTIVE)
mads.state_machine.state = State.enabled
mads.enabled = True
mads.active = True
sd.events.add(EventName.pedalPressed)
run_frames(mads, sd, make_car_state(brake_pressed=True, v_ego=10.0))
assert mads.state_machine.state == State.enabled
# lateral mismatch counter
class TestLateralMismatchCounter:
def test_no_accumulation_while_paused(self, mocker):
mads, sd = make_mads(mocker, MadsSteeringModeOnBrake.PAUSE)
mads.state_machine.state = State.paused
mads.enabled = True
mads.active = False
sd.sm['pandaStates'] = [make_panda_state(mocker, False)]
run_frames(mads, sd, make_car_state(brake_pressed=True, standstill=True), n=250)
assert mads.lateral_mismatch_counter == 0
def test_accumulates_when_active_and_panda_disagrees(self, mocker):
mads, sd = make_mads(mocker, MadsSteeringModeOnBrake.PAUSE)
mads.enabled = True
mads.active = True
sd.sm['pandaStates'] = [make_panda_state(mocker, False)]
for _ in range(200):
mads.data_sample()
assert mads.lateral_mismatch_counter == 200
# brand restrictions
class TestBrandSteeringModeRestrictions:
def test_rivian_forced_to_disengage(self, mocker):
CP = structs.CarParams()
CP.brand = "rivian"
CP_SP = structs.CarParamsSP()
params = mocker.MagicMock()
assert read_steering_mode_param(CP, CP_SP, params) == MadsSteeringModeOnBrake.DISENGAGE
params.get.assert_not_called()
def test_tesla_without_vehicle_bus_forced_to_disengage(self, mocker):
CP = structs.CarParams()
CP.brand = "tesla"
CP_SP = structs.CarParamsSP()
CP_SP.flags = 0
params = mocker.MagicMock()
assert read_steering_mode_param(CP, CP_SP, params) == MadsSteeringModeOnBrake.DISENGAGE
def test_tesla_with_vehicle_bus_uses_param(self, mocker):
CP = structs.CarParams()
CP.brand = "tesla"
CP_SP = structs.CarParamsSP()
CP_SP.flags = TeslaFlagsSP.HAS_VEHICLE_BUS
params = mocker.MagicMock()
params.get = mocker.MagicMock(return_value=MadsSteeringModeOnBrake.REMAIN_ACTIVE)
assert read_steering_mode_param(CP, CP_SP, params) == MadsSteeringModeOnBrake.REMAIN_ACTIVE
@pytest.mark.parametrize("brand", ["hyundai", "toyota", "honda", "gm"])
def test_other_brands_use_param(self, mocker, brand):
CP = structs.CarParams()
CP.brand = brand
CP_SP = structs.CarParamsSP()
params = mocker.MagicMock()
params.get = mocker.MagicMock(return_value=MadsSteeringModeOnBrake.REMAIN_ACTIVE)
assert read_steering_mode_param(CP, CP_SP, params) == MadsSteeringModeOnBrake.REMAIN_ACTIVE
+5
View File
@@ -0,0 +1,5 @@
import os
from openpilot.common.basedir import BASEDIR
MAPD_BIN_DIR = os.path.join(BASEDIR, 'openpilot', 'third_party/mapd_pfeiferj')
MAPD_PATH = os.path.join(MAPD_BIN_DIR, 'mapd')
@@ -0,0 +1,22 @@
"""
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
from openpilot.common.swaglog import cloudlog
LOOK_AHEAD_HORIZON_TIME = 15. # s. Time horizon for look ahead of turn speed sections to provide on liveMapDataSP msg.
_DEBUG = False
_CLOUDLOG_DEBUG = False
ROAD_NAME_TIMEOUT = 30 # secs
R = 6373000.0 # approximate radius of earth in mts
QUERY_RADIUS = 3000 # mts. Radius to use on OSM data queries.
QUERY_RADIUS_OFFLINE = 2250 # mts. Radius to use on offline OSM data queries.
def get_debug(msg, log_to_cloud=True):
if _CLOUDLOG_DEBUG and log_to_cloud:
cloudlog.debug(msg)
if _DEBUG:
print(msg)
@@ -0,0 +1,65 @@
"""
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
from abc import abstractmethod, ABC
import openpilot.cereal.messaging as messaging
from openpilot.common.params import Params
from openpilot.common.constants import CV
from openpilot.selfdrive.car.cruise import V_CRUISE_UNSET
from openpilot.sunnypilot.navd.helpers import coordinate_from_param
MAX_SPEED_LIMIT = V_CRUISE_UNSET * CV.KPH_TO_MS
class BaseMapData(ABC):
def __init__(self):
self.params = Params()
self.sm = messaging.SubMaster(['liveLocationKalman'])
self.pm = messaging.PubMaster(['liveMapDataSP'])
self.localizer_valid = False
self.last_bearing = None
self.last_position = coordinate_from_param("LastGPSPositionLLK", self.params)
@abstractmethod
def update_location(self) -> None:
pass
@abstractmethod
def get_current_speed_limit(self) -> float:
pass
@abstractmethod
def get_next_speed_limit_and_distance(self) -> tuple[float, float]:
pass
@abstractmethod
def get_current_road_name(self) -> str:
pass
def publish(self) -> None:
speed_limit = self.get_current_speed_limit()
next_speed_limit, next_speed_limit_distance = self.get_next_speed_limit_and_distance()
mapd_sp_send = messaging.new_message('liveMapDataSP')
mapd_sp_send.valid = self.sm['liveLocationKalman'].gpsOK
live_map_data = mapd_sp_send.liveMapDataSP
live_map_data.speedLimitValid = bool(MAX_SPEED_LIMIT > speed_limit > 0)
live_map_data.speedLimit = speed_limit
live_map_data.speedLimitAheadValid = bool(MAX_SPEED_LIMIT > next_speed_limit > 0)
live_map_data.speedLimitAhead = next_speed_limit
live_map_data.speedLimitAheadDistance = next_speed_limit_distance
live_map_data.roadName = self.get_current_road_name()
self.pm.send('liveMapDataSP', mapd_sp_send)
def tick(self) -> None:
self.sm.update(0)
self.update_location()
self.publish()
@@ -0,0 +1,56 @@
"""
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
# DISCLAIMER: This code is intended principally for development and debugging purposes.
# Although it provides a standalone entry point to the program, users should refer
# to the actual implementations for consumption. Usage outside of development scenarios
# is not advised and could lead to unpredictable results.
import threading
import traceback
from openpilot.cereal import messaging
from openpilot.common.gps import get_gps_location_service
from openpilot.common.params import Params
from openpilot.common.realtime import config_realtime_process
from openpilot.sunnypilot.selfdrive.controls.lib.speed_limit.common import Policy
from openpilot.sunnypilot.selfdrive.controls.lib.speed_limit.speed_limit_resolver import SpeedLimitResolver
from openpilot.sunnypilot.mapd.live_map_data import get_debug
def excepthook(args):
get_debug(f'MapD: Threading exception:\n{args}')
traceback.print_exception(args.exc_type, args.exc_value, args.exc_traceback)
def live_map_data_sp_thread():
config_realtime_process([0, 1, 2, 3], 5)
params = Params()
gps_location_service = get_gps_location_service(params)
while True:
live_map_data_sp_thread_debug(gps_location_service)
def live_map_data_sp_thread_debug(gps_location_service):
_sub_master = messaging.SubMaster(['carState', 'livePose', 'liveMapDataSP', 'longitudinalPlanSP', 'carStateSP', gps_location_service])
_sub_master.update()
v_ego = _sub_master['carState'].vEgo
_resolver = SpeedLimitResolver()
_resolver.policy = Policy.car_state_priority
_resolver.update(v_ego, _sub_master)
print(_resolver.speed_limit, _resolver.distance, _resolver.source)
def main():
threading.excepthook = excepthook
live_map_data_sp_thread()
if __name__ == "__main__":
main()
@@ -0,0 +1,61 @@
"""
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
import json
import math
import platform
from openpilot.cereal import log
from openpilot.common.params import Params
from openpilot.sunnypilot.mapd.live_map_data.base_map_data import BaseMapData
from openpilot.sunnypilot.navd.helpers import Coordinate
class OsmMapData(BaseMapData):
def __init__(self):
super().__init__()
self.mem_params = Params("/dev/shm/params") if platform.system() != "Darwin" else self.params
def update_location(self) -> None:
location = self.sm['liveLocationKalman']
self.localizer_valid = (location.status == log.LiveLocationKalman.Status.valid) and location.positionGeodetic.valid
if self.localizer_valid:
self.last_bearing = math.degrees(location.calibratedOrientationNED.value[2])
self.last_position = Coordinate(location.positionGeodetic.value[0], location.positionGeodetic.value[1])
if self.last_position is None:
return
params = {
"latitude": self.last_position.latitude,
"longitude": self.last_position.longitude,
}
if self.last_bearing is not None:
params['bearing'] = self.last_bearing
self.mem_params.put("LastGPSPosition", json.dumps(params), block=True)
def get_current_speed_limit(self) -> float:
return float(self.mem_params.get("MapSpeedLimit") or 0.0)
def get_current_road_name(self) -> str:
return str(self.mem_params.get("RoadName") or "")
def get_next_speed_limit_and_distance(self) -> tuple[float, float]:
next_speed_limit_section_str = self.mem_params.get("NextMapSpeedLimit")
next_speed_limit_section = next_speed_limit_section_str if next_speed_limit_section_str else {}
next_speed_limit = next_speed_limit_section.get('speedlimit', 0.0)
next_speed_limit_latitude = next_speed_limit_section.get('latitude')
next_speed_limit_longitude = next_speed_limit_section.get('longitude')
next_speed_limit_distance = 0.0
if next_speed_limit_latitude and next_speed_limit_longitude:
next_speed_limit_coordinates = Coordinate(next_speed_limit_latitude, next_speed_limit_longitude)
next_speed_limit_distance = (self.last_position or Coordinate(0, 0)).distance_to(next_speed_limit_coordinates)
return next_speed_limit, next_speed_limit_distance
@@ -0,0 +1,42 @@
"""
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
# DISCLAIMER: This code is intended principally for development and debugging purposes.
# Although it provides a standalone entry point to the program, users should refer
# to the actual implementations for consumption. Usage outside of development scenarios
# is not advised and could lead to unpredictable results.
import threading
import traceback
from openpilot.common.realtime import Ratekeeper, config_realtime_process
from openpilot.sunnypilot.mapd.live_map_data import get_debug
from openpilot.sunnypilot.mapd.live_map_data.osm_map_data import OsmMapData
def excepthook(args):
get_debug(f'MapD: Threading exception:\n{args}')
traceback.print_exception(args.exc_type, args.exc_value, args.exc_traceback)
def live_map_data_sp_thread():
config_realtime_process([0, 1, 2, 3], 5)
live_map_sp = OsmMapData()
rk = Ratekeeper(1, print_delay_threshold=None)
while True:
live_map_sp.tick()
rk.keep_time()
def main():
threading.excepthook = excepthook
live_map_data_sp_thread()
if __name__ == "__main__":
main()
+154
View File
@@ -0,0 +1,154 @@
#!/usr/bin/env python3
"""
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
import logging
import os
import stat
import time
import traceback
import requests
from pathlib import Path
from urllib.request import urlopen
from openpilot.cereal import messaging
from openpilot.common.params import Params
from openpilot.common.hardware.hw import Paths
from openpilot.common.spinner import Spinner
from openpilot.common.version import is_prebuilt
from openpilot.sunnypilot.mapd import MAPD_PATH, MAPD_BIN_DIR
import openpilot.system.sentry as sentry
VERSION = "v1.12.0"
URL = f"https://github.com/pfeiferj/openpilot-mapd/releases/download/{VERSION}/mapd"
def update_installed_version(version: str, params: Params = None) -> None:
if params is None:
params = Params()
params.put("MapdVersion", version, block=True)
class MapdInstallManager:
def __init__(self, spinner_ref: Spinner):
self._spinner = spinner_ref
self._params = Params()
def download(self) -> None:
self.ensure_directories_exist()
self._download_file()
update_installed_version(VERSION, self._params)
def check_and_download(self) -> None:
if self.download_needed():
self.download()
def download_needed(self) -> bool:
return not os.path.exists(MAPD_PATH) or self.get_installed_version() != VERSION
@staticmethod
def ensure_directories_exist() -> None:
if not os.path.exists(Paths.mapd_root()):
os.makedirs(Paths.mapd_root())
if not os.path.exists(MAPD_BIN_DIR):
os.makedirs(MAPD_BIN_DIR)
@staticmethod
def _safe_write_and_set_executable(file_path: Path, content: bytes) -> None:
with open(file_path, 'wb') as output:
output.write(content)
output.flush()
os.fsync(output.fileno())
current_permissions = stat.S_IMODE(os.lstat(file_path).st_mode)
os.chmod(file_path, current_permissions | stat.S_IEXEC)
def _download_file(self, num_retries=5) -> None:
temp_file = Path(MAPD_PATH + ".tmp")
download_timeout = 60
for cnt in range(num_retries):
try:
response = requests.get(URL, stream=True, timeout=download_timeout)
response.raise_for_status()
self._safe_write_and_set_executable(temp_file, response.content)
# No exceptions encountered. Safe to replace original file.
temp_file.replace(MAPD_PATH)
return
except requests.exceptions.ReadTimeout:
self._spinner.update(f"ReadTimeout caught. Timeout is [{download_timeout}]. Retrying download... [{cnt}]")
time.sleep(0.5)
except requests.exceptions.RequestException as e:
self._spinner.update(f"RequestException caught: {e}. Retrying download... [{cnt}]")
time.sleep(0.5)
# Delete temp file if the process was not successful.
if temp_file.exists():
temp_file.unlink()
logging.error("Failed to download file after all retries")
def get_installed_version(self) -> str:
return str(self._params.get("MapdVersion") or "")
def wait_for_internet_connection(self, return_on_failure: bool = False) -> bool:
max_retries = 10
for retries in range(max_retries + 1):
self._spinner.update(f"Waiting for internet connection... [{retries}/{max_retries}]")
time.sleep(2)
try:
_ = urlopen('https://sentry.io', timeout=10)
return True
except Exception as e:
print(f'Wait for internet failed: {e}')
if return_on_failure and retries == max_retries:
return False
return False
def non_prebuilt_install(self) -> None:
sm = messaging.SubMaster(['deviceState'])
metered = sm['deviceState'].networkMetered
if metered:
self._spinner.update("Can't proceed with mapd install since network is metered!")
time.sleep(5)
return
try:
self.ensure_directories_exist()
if not self.download_needed():
self._spinner.update("Mapd is good!")
time.sleep(0.1)
return
if self.wait_for_internet_connection(return_on_failure=True):
self._spinner.update(f"Downloading pfeiferj's mapd [{self.get_installed_version()}] => [{VERSION}].")
time.sleep(0.1)
self.check_and_download()
self._spinner.close()
except Exception:
for i in range(6):
self._spinner.update("Failed to download OSM maps won't work until properly downloaded!" +
"Try again manually rebooting. " +
f"Boot will continue in {5 - i}s...")
time.sleep(1)
sentry.init(sentry.SentryProject.SELFDRIVE)
traceback.print_exc()
sentry.capture_exception()
if __name__ == "__main__":
spinner = Spinner()
install_manager = MapdInstallManager(spinner)
install_manager.ensure_directories_exist()
if is_prebuilt():
debug_msg = f"[DEBUG] This is prebuilt, no mapd install required. VERSION: [{VERSION}], Param [{install_manager.get_installed_version()}]"
spinner.update(debug_msg)
update_installed_version(VERSION)
else:
spinner.update(f"Checking if mapd is installed and valid. Prebuilt [{is_prebuilt()}]")
install_manager.non_prebuilt_install()
+144
View File
@@ -0,0 +1,144 @@
#!/usr/bin/env python3
"""
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
import json
import platform
import os
import glob
import shutil
from datetime import datetime
from openpilot.common.params import Params
from openpilot.common.realtime import Ratekeeper, config_realtime_process
from openpilot.common.swaglog import cloudlog
from openpilot.selfdrive.selfdrived.alertmanager import set_offroad_alert
from openpilot.sunnypilot.mapd.live_map_data.osm_map_data import OsmMapData
from openpilot.common.hardware.hw import Paths
from openpilot.sunnypilot.mapd import MAPD_PATH
from openpilot.sunnypilot.mapd.mapd_installer import VERSION, update_installed_version
# PFEIFER - MAPD {{
params = Params()
mem_params = Params("/dev/shm/params") if platform.system() != "Darwin" else params
# }} PFEIFER - MAPD
def get_files_for_cleanup() -> list[str]:
paths = [
f"{Paths.mapd_root()}/db",
f"{Paths.mapd_root()}/v*"
]
files_to_remove = []
for path in paths:
if os.path.exists(path):
files = glob.glob(path + '/**', recursive=True)
files_to_remove.extend(files)
# check for version and mapd files
if not os.path.isfile(MAPD_PATH):
files_to_remove.append(MAPD_PATH)
return files_to_remove
def cleanup_old_osm_data(files_to_remove: list[str]) -> None:
for file in files_to_remove:
# Remove trailing slash if path is file
if file.endswith('/') and os.path.isfile(file[:-1]):
file = file[:-1]
# Try to remove as file or symbolic link first
if os.path.islink(file) or os.path.isfile(file):
os.remove(file)
elif os.path.isdir(file): # If it's a directory
shutil.rmtree(file, ignore_errors=False)
def request_refresh_osm_location_data(nations: list[str], states: list[str] | None = None) -> None:
params.put("OsmDownloadedDate", str(datetime.now().timestamp()), block=True)
params.put_bool("OsmDbUpdatesCheck", False, block=True)
osm_download_locations = {
"nations": nations,
"states": states or []
}
print(f"Downloading maps for {json.dumps(osm_download_locations)}")
mem_params.put("OSMDownloadLocations", osm_download_locations, block=True)
def filter_nations_and_states(nations: list[str], states: list[str] | None = None) -> tuple[list[str], list[str]]:
"""Filters and prepares nation and state data for OSM map download.
If the nation is 'US' and a specific state is provided, the nation 'US' is removed from the list.
If the nation is 'US' and the state is 'All', the 'All' is removed from the list.
The idea behind these filters is that if a specific state in the US is provided,
there's no need to download map data for the entire US. Conversely,
if the state is unspecified (i.e., 'All'), we intend to download map data for the whole US,
and 'All' isn't a valid state name, so it's removed.
Parameters:
nations (list): A list of nations for which the map data is to be downloaded.
states (list, optional): A list of states for which the map data is to be downloaded. Defaults to None.
Returns:
tuple: Two lists. The first list is filtered nations and the second list is filtered states.
"""
if "US" in nations and states and not any(x.lower() == "all" for x in states):
# If a specific state in the US is provided, remove 'US' from nations
nations.remove("US")
elif "US" in nations and states and any(x.lower() == "all" for x in states):
# If 'All' is provided as a state (case invariant), remove those instances from states
states = [x for x in states if x.lower() != "all"]
elif "US" not in nations and states and any(x.lower() == "all" for x in states):
states.remove("All")
return nations, states or []
def update_osm_db() -> None:
if params.get_bool("OsmDbUpdatesCheck"):
cleanup_old_osm_data(get_files_for_cleanup())
country = params.get("OsmLocationName", return_default=True)
state = params.get("OsmStateName", return_default=True)
filtered_nations, filtered_states = filter_nations_and_states([country], [state])
request_refresh_osm_location_data(filtered_nations, filtered_states)
if not mem_params.get("OSMDownloadBounds"):
mem_params.put("OSMDownloadBounds", "", block=True)
if not mem_params.get("LastGPSPosition"):
mem_params.put("LastGPSPosition", "{}", block=True)
def main_thread():
update_installed_version(VERSION, params)
config_realtime_process([0, 1, 2, 3], 5)
rk = Ratekeeper(1, print_delay_threshold=None)
live_map_sp = OsmMapData()
# Create folder needed for OSM
try:
os.mkdir(Paths.mapd_root())
except FileExistsError:
pass
except PermissionError:
cloudlog.exception(f"mapd: failed to make {Paths.mapd_root()}")
while True:
show_alert = get_files_for_cleanup() and params.get_bool("OsmLocal")
set_offroad_alert("Offroad_OSMUpdateRequired", show_alert, "This alert will be cleared when new maps are downloaded.")
update_osm_db()
live_map_sp.tick()
rk.keep_time()
def main():
main_thread()
if __name__ == "__main__":
main()
Whitespace-only changes.
@@ -0,0 +1 @@
fdb3b49ee19956e6ce09fdc3373cbba557f1263b2180e9f344c1d4053852284b
@@ -0,0 +1,19 @@
"""
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
from openpilot.sunnypilot import get_file_hash
from openpilot.sunnypilot.mapd import MAPD_PATH
from openpilot.sunnypilot.mapd.update_version import MAPD_HASH_PATH
class TestMapdVersion:
def test_compare_versions(self):
mapd_hash = get_file_hash(MAPD_PATH)
with open(MAPD_HASH_PATH) as f:
current_hash = f.read().strip()
assert current_hash == mapd_hash, "Run sunnypilot/mapd/update_version.py to update the current mapd version and hash"
+97
View File
@@ -0,0 +1,97 @@
#!/usr/bin/env python3
"""
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
import argparse
import os
import re
from openpilot.sunnypilot import get_file_hash
from openpilot.common.basedir import BASEDIR
from openpilot.sunnypilot.mapd import MAPD_PATH
MAPD_HASH_PATH = os.path.join(BASEDIR, "openpilot", "sunnypilot", "mapd", "tests", "mapd_hash")
MAPD_VERSION_PATH = os.path.join(BASEDIR, "openpilot", "sunnypilot", "mapd", "mapd_installer.py")
def update_mapd_hash():
mapd_hash = get_file_hash(MAPD_PATH)
with open(MAPD_HASH_PATH, "w") as f:
f.write(mapd_hash)
print(f"Generated and updated new mapd hash to {MAPD_HASH_PATH}")
def get_current_mapd_version(path: str) -> str:
print("[GET CURRENT MAPD VERSION]")
with open(path) as f:
for line in f:
if line.strip().startswith("VERSION"):
# Match VERSION = 'v1.11.0' or VERSION="v1.11.0" (with optional spaces)
match = re.search(r'VERSION\s*=\s*[\'"]([^\'"]+)[\'"]', line)
if match:
ver = match.group(1)
print(f'Current mapd version: "{ver}"')
return ver
else:
print("[ERROR] VERSION line found but no quoted value detected.")
return ""
print("[ERROR] VERSION not found in file!")
return ""
def update_mapd_version(ver: str, path: str):
print("[CHANGE CURRENT MAPD VERSION]")
with open(path) as f:
lines = f.readlines()
found = False
new_lines = []
for line in lines:
if not found and line.startswith("VERSION ="):
new_lines.append(f'VERSION = "{ver}"\n')
found = True
new_lines.extend(lines[lines.index(line) + 1:])
break
else:
new_lines.append(line)
if not found:
print("[ERROR] VERSION line not found! Aborting without writing.")
return
with open(path, "w") as f:
f.writelines(new_lines)
print(f'New mapd version: "{ver}"')
print("[DONE]")
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Update mapd version and hash")
parser.add_argument("--new_ver", type=str, help="New mapd version")
args = parser.parse_args()
if not args.new_ver:
print("Warning: No new mapd version provided. Use --new_ver to specify")
print("Example:")
print(" python sunnypilot/mapd/update_version.py --new_ver \"v1.12.0\"")
print("Current mapd version and hash will not be updated! (aborted)")
exit(0)
current_ver = get_current_mapd_version(MAPD_VERSION_PATH)
new_ver = f"{args.new_ver}"
if current_ver == new_ver:
print(f'Proposed mapd version: "{new_ver}"')
confirm = input("Proposed mapd version is the same as the current mapd version. Confirm? (y/n): ").upper().strip()
if confirm != "Y":
print("Current mapd version and hash will not be updated! (aborted)")
exit(0)
update_mapd_version(new_ver, MAPD_VERSION_PATH)
update_mapd_hash()
Whitespace-only changes.
@@ -0,0 +1 @@
*_pyx.cpp
@@ -0,0 +1,5 @@
from pathlib import Path
MODEL_PATH = Path(__file__).parent / 'models/supercombo.onnx'
MODEL_PKL_PATH = Path(__file__).parent / 'models/supercombo_tinygrad.pkl'
METADATA_PATH = Path(__file__).parent / 'models/supercombo_metadata.pkl'
@@ -0,0 +1,39 @@
"""
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
import numpy as np
from openpilot.common.transformations.camera import DEVICE_CAMERAS
class CameraOffsetHelper:
def __init__(self):
self.camera_offset = 0.0
self.actual_camera_offset = 0.0
@staticmethod
def apply_camera_offset(model_transform, intrinsics, height, offset_param):
cy = intrinsics[1, 2]
shear = np.eye(3, dtype=np.float32)
shear[0, 1] = offset_param / height
shear[0, 2] = -offset_param / height * cy
model_transform = (shear @ model_transform).astype(np.float32)
return model_transform
def set_offset(self, offset):
self.camera_offset = offset
def update(self, model_transform_main, model_transform_extra, sm, main_wide_camera):
self.actual_camera_offset = (0.9 * self.actual_camera_offset) + (0.1 * self.camera_offset)
dc = DEVICE_CAMERAS[(str(sm['deviceState'].deviceType), str(sm['roadCameraState'].sensor))]
height = sm["liveCalibration"].height[0] if sm['liveCalibration'].height else 1.22
intrinsics_main = dc.ecam.intrinsics if main_wide_camera else dc.fcam.intrinsics
model_transform_main = self.apply_camera_offset(model_transform_main, intrinsics_main, height, self.actual_camera_offset)
intrinsics_extra = dc.ecam.intrinsics
model_transform_extra = self.apply_camera_offset(model_transform_extra, intrinsics_extra, height, self.actual_camera_offset)
return model_transform_main, model_transform_extra
+480
View File
@@ -0,0 +1,480 @@
#!/usr/bin/env python3
"""
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
import argparse
import os
import pickle
import time
from functools import partial
from collections import defaultdict
import numpy as np
from tinygrad.tensor import Tensor
from tinygrad.device import Device
from tinygrad.engine.jit import TinyJit
from openpilot.selfdrive.modeld.compile_modeld import (
NV12Frame, make_frame_prepare,
shift_and_sample, sample_skip, sample_desire,
)
MODEL_TYPES = ('vision_policy', 'supercombo', 'vision_multi_policy')
def _detect_desire_key(policy_input_shapes):
for k in policy_input_shapes:
if k.startswith('desire'):
return k
return None
def _detect_vision_keys(vision_input_shapes):
img_keys = sorted([k for k in vision_input_shapes if 'img' in k])
road_key = next((k for k in img_keys if 'big' not in k), None)
wide_key = next((k for k in img_keys if 'big' in k), None)
if road_key is None or wide_key is None:
raise ValueError(f"Cannot determine road/wide image keys from {list(vision_input_shapes.keys())}")
return road_key, wide_key
def make_split_input_queues(vision_input_shapes, policy_input_shapes, frame_skip, device):
road_key, _ = _detect_vision_keys(vision_input_shapes)
img = vision_input_shapes[road_key]
n_frames = img[1] // 6
img_buf_shape = (frame_skip * (n_frames - 1) + 1, 6, img[2], img[3])
fb = policy_input_shapes['features_buffer']
desire_key = _detect_desire_key(policy_input_shapes)
dp = policy_input_shapes[desire_key]
tc = policy_input_shapes.get('traffic_convention', (1, 2))
npy = {
'desire': np.zeros(dp[2], dtype=np.float32),
'traffic_convention': np.zeros(tc, dtype=np.float32),
'tfm': np.zeros((3, 3), dtype=np.float32),
'big_tfm': np.zeros((3, 3), dtype=np.float32),
}
handled = {'features_buffer', desire_key, 'traffic_convention'}
for key, shape in policy_input_shapes.items():
if key in handled:
continue
npy[key] = np.zeros(shape, dtype=np.float32)
input_queues = {
'img_q': Tensor(np.zeros(img_buf_shape, dtype=np.uint8), device=device).contiguous().realize(),
'big_img_q': Tensor(np.zeros(img_buf_shape, dtype=np.uint8), device=device).contiguous().realize(),
'feat_q': Tensor(np.zeros((frame_skip * (fb[1] - 1) + 1, fb[0], fb[2]), dtype=np.float32), device=device).contiguous().realize(),
'desire_q': Tensor(np.zeros((frame_skip * dp[1], dp[0], dp[2]), dtype=np.float32), device=device).contiguous().realize(),
**{k: Tensor(v, device='NPY').realize() for k, v in npy.items()},
}
return input_queues, npy
def make_run_split_policy(vision_runner, policy_runner, nv12: NV12Frame, model_w, model_h,
vision_features_slice, frame_skip, desire_key, extra_policy_keys,
vision_road_key, vision_wide_key, prepare_only=False):
frame_prepare = make_frame_prepare(nv12, model_w, model_h)
sample_skip_fn = partial(sample_skip, frame_skip=frame_skip)
sample_desire_fn = partial(sample_desire, frame_skip=frame_skip)
def run_policy(img_q, big_img_q, feat_q, desire_q, desire, traffic_convention, tfm, big_tfm, frame, big_frame, **extra):
npy_tensors = [tfm.to(Device.DEFAULT), big_tfm.to(Device.DEFAULT),
desire.to(Device.DEFAULT), traffic_convention.to(Device.DEFAULT)]
extra_device = {k: extra[k].to(Device.DEFAULT) for k in extra_policy_keys}
Tensor.realize(*npy_tensors, *extra_device.values())
tfm, big_tfm, desire, traffic_convention = npy_tensors
img = shift_and_sample(img_q, frame_prepare(frame, tfm).unsqueeze(0), sample_skip_fn)
big_img = shift_and_sample(big_img_q, frame_prepare(big_frame, big_tfm).unsqueeze(0), sample_skip_fn)
if prepare_only:
return img, big_img
vision_out = next(iter(vision_runner({vision_road_key: img, vision_wide_key: big_img}).values())).cast('float32')
new_feat = vision_out[:, vision_features_slice].reshape(1, -1).unsqueeze(0)
feat_buf = shift_and_sample(feat_q, new_feat, sample_skip_fn)
desire_buf = shift_and_sample(desire_q, desire.reshape(1, 1, -1), sample_desire_fn)
inputs = {'features_buffer': feat_buf, desire_key: desire_buf, 'traffic_convention': traffic_convention, **extra_device}
policy_out = next(iter(policy_runner(inputs).values())).cast('float32')
return vision_out, policy_out
return run_policy
def compile_split_policy(nv12: NV12Frame, model_w, model_h, prepare_only, frame_skip,
vision_runner, policy_runner, vision_metadata, policy_metadata):
print(f"Compiling combined policy JIT for {nv12.width}x{nv12.height} (prepare_only={prepare_only})...")
vision_features_slice = vision_metadata['output_slices']['hidden_state']
vision_input_shapes = vision_metadata['input_shapes']
policy_input_shapes = policy_metadata['input_shapes']
desire_key = _detect_desire_key(policy_input_shapes)
extra_policy_keys = [k for k in policy_input_shapes if k not in ('features_buffer', desire_key, 'traffic_convention')]
vision_road_key, vision_wide_key = _detect_vision_keys(vision_input_shapes)
_run = make_run_split_policy(vision_runner, policy_runner, nv12, model_w, model_h,
vision_features_slice, frame_skip, desire_key, extra_policy_keys,
vision_road_key, vision_wide_key, prepare_only)
run_policy_jit = TinyJit(_run, prune=True)
SEED = 42
def random_inputs_run_fn(fn, seed, test_val=None, test_buffers=None, expect_match=True):
input_queues, npy = make_split_input_queues(vision_input_shapes, policy_input_shapes, frame_skip, Device.DEFAULT)
np.random.seed(seed)
Tensor.manual_seed(seed)
testing = test_val is not None or test_buffers is not None
n_runs = 1 if testing else 3
for i in range(n_runs):
frame = Tensor.randint(nv12.size, low=0, high=256, dtype='uint8').realize()
big_frame = Tensor.randint(nv12.size, low=0, high=256, dtype='uint8').realize()
for v in npy.values():
v[:] = np.random.randn(*v.shape).astype(v.dtype)
Device.default.synchronize()
st = time.perf_counter()
outs = fn(**input_queues, frame=frame, big_frame=big_frame)
mt = time.perf_counter()
Device.default.synchronize()
et = time.perf_counter()
print(f" [{i+1}/{n_runs}] enqueue {(mt-st)*1e3:6.2f} ms -- total {(et-st)*1e3:6.2f} ms")
if i == 0:
val = [np.copy(v.numpy()) for v in outs]
buffers = [np.copy(v.numpy().copy()) for v in input_queues.values()]
if test_val is not None:
match = all(np.array_equal(a, b) for a, b in zip(val, test_val, strict=True))
assert match == expect_match, f"outputs {'differ from' if expect_match else 'match'} baseline (seed={seed})"
if test_buffers is not None:
match = all(np.array_equal(a, b) for a, b in zip(buffers, test_buffers, strict=True))
assert match == expect_match, f"buffers {'differ from' if expect_match else 'match'} baseline (seed={seed})"
return fn, val, buffers
print('capture + replay')
run_policy_jit, test_val, test_buffers = random_inputs_run_fn(run_policy_jit, SEED)
print('pickle round trip')
run_policy_jit = pickle.loads(pickle.dumps(run_policy_jit))
random_inputs_run_fn(run_policy_jit, SEED, test_val, test_buffers, expect_match=True)
random_inputs_run_fn(run_policy_jit, SEED+1, test_val, test_buffers, expect_match=False)
return run_policy_jit
def derive_frame_skip(vision_input_shapes, policy_input_shapes):
fb = policy_input_shapes.get('features_buffer')
if fb is None:
return 1
fb_history = fb[1]
if fb_history >= 99:
return 1
return 4
def make_supercombo_input_queues(input_shapes, frame_skip, device):
img_shape = input_shapes.get('img', input_shapes.get('input_imgs'))
if img_shape is None:
raise ValueError("No img input found in model shapes")
n_frames = img_shape[1] // 6
img_buf_shape = (frame_skip * (n_frames - 1) + 1, 6, img_shape[2], img_shape[3])
numpy_keys = {}
queue_keys = {}
for key, shape in input_shapes.items():
if 'img' in key:
continue
if len(shape) == 3 and shape[1] > 1:
if key.startswith('desire'):
numpy_keys[key] = np.zeros(shape[2], dtype=np.float32)
queue_keys[f'{key}_q'] = Tensor(
np.zeros((frame_skip * shape[1], shape[0], shape[2]), dtype=np.float32),
device=device).contiguous().realize()
elif key == 'features_buffer':
queue_keys['feat_q'] = Tensor(
np.zeros((frame_skip * (shape[1] - 1) + 1, shape[0], shape[2]), dtype=np.float32),
device=device).contiguous().realize()
else:
numpy_keys[key] = np.zeros(shape, dtype=np.float32)
elif len(shape) == 2:
numpy_keys[key] = np.zeros(shape, dtype=np.float32)
if 'traffic_convention' not in numpy_keys:
tc_shape = input_shapes.get('traffic_convention', (1, 2))
numpy_keys['traffic_convention'] = np.zeros(tc_shape, dtype=np.float32)
numpy_keys['tfm'] = np.zeros((3, 3), dtype=np.float32)
numpy_keys['big_tfm'] = np.zeros((3, 3), dtype=np.float32)
input_queues = {
'img_q': Tensor(np.zeros(img_buf_shape, dtype=np.uint8), device=device).contiguous().realize(),
'big_img_q': Tensor(np.zeros(img_buf_shape, dtype=np.uint8), device=device).contiguous().realize(),
**queue_keys,
**{k: Tensor(v, device='NPY').realize() for k, v in numpy_keys.items()},
}
return input_queues, numpy_keys
def make_run_supercombo(model_runner, nv12: NV12Frame, model_w, model_h,
features_slice, frame_skip, input_shapes, prepare_only=False):
frame_prepare = make_frame_prepare(nv12, model_w, model_h)
sample_skip_fn = partial(sample_skip, frame_skip=frame_skip)
sample_desire_fn = partial(sample_desire, frame_skip=frame_skip)
desire_key = _detect_desire_key(input_shapes)
if desire_key is None:
raise ValueError(f"No desire* key found in input_shapes: {list(input_shapes.keys())}")
road_img_key, wide_img_key = _detect_vision_keys(input_shapes)
extra_policy_keys = [k for k in input_shapes
if k not in (desire_key, 'features_buffer', 'traffic_convention')
and 'img' not in k]
def run_supercombo(img_q, big_img_q, feat_q, desire_q,
frame, big_frame, **kwargs):
desire = kwargs.get(desire_key)
traffic_convention = kwargs.get('traffic_convention')
tfm = kwargs['tfm']
big_tfm = kwargs['big_tfm']
tfm = tfm.to(Device.DEFAULT)
big_tfm = big_tfm.to(Device.DEFAULT)
desire = desire.to(Device.DEFAULT)
traffic_convention = traffic_convention.to(Device.DEFAULT)
Tensor.realize(tfm, big_tfm, desire, traffic_convention)
img = shift_and_sample(img_q, frame_prepare(frame, tfm).unsqueeze(0), sample_skip_fn)
big_img = shift_and_sample(big_img_q, frame_prepare(big_frame, big_tfm).unsqueeze(0), sample_skip_fn)
if prepare_only:
return img, big_img
desire_buf = shift_and_sample(desire_q, desire.reshape(1, 1, -1), sample_desire_fn)
feat_buf = sample_skip_fn(feat_q)
inputs = {road_img_key: img, wide_img_key: big_img,
desire_key: desire_buf, 'features_buffer': feat_buf,
'traffic_convention': traffic_convention}
for k in extra_policy_keys:
if k in kwargs:
inputs[k] = kwargs[k].to(Device.DEFAULT)
model_out = next(iter(model_runner(inputs).values())).cast('float32')
new_feat = model_out[:, features_slice].reshape(1, -1).unsqueeze(0)
shift_and_sample(feat_q, new_feat, sample_skip_fn)
return model_out
return run_supercombo
def make_run_vision_multi_policy(vision_runner, policy_runners, nv12: NV12Frame, model_w, model_h,
vision_features_slice, frame_skip, desire_key, extra_policy_keys,
vision_road_key, vision_wide_key, prepare_only=False):
frame_prepare = make_frame_prepare(nv12, model_w, model_h)
sample_skip_fn = partial(sample_skip, frame_skip=frame_skip)
sample_desire_fn = partial(sample_desire, frame_skip=frame_skip)
def run_multi_policy(img_q, big_img_q, feat_q, desire_q, desire,
traffic_convention, tfm, big_tfm, frame, big_frame, **extra):
npy_tensors = [tfm.to(Device.DEFAULT), big_tfm.to(Device.DEFAULT),
desire.to(Device.DEFAULT), traffic_convention.to(Device.DEFAULT)]
extra_device = {k: extra[k].to(Device.DEFAULT) for k in extra_policy_keys}
Tensor.realize(*npy_tensors, *extra_device.values())
tfm, big_tfm, desire, traffic_convention = npy_tensors
img = shift_and_sample(img_q, frame_prepare(frame, tfm).unsqueeze(0), sample_skip_fn)
big_img = shift_and_sample(big_img_q, frame_prepare(big_frame, big_tfm).unsqueeze(0), sample_skip_fn)
if prepare_only:
return img, big_img
vision_out = next(iter(vision_runner({vision_road_key: img, vision_wide_key: big_img}).values())).cast('float32')
new_feat = vision_out[:, vision_features_slice].reshape(1, -1).unsqueeze(0)
feat_buf = shift_and_sample(feat_q, new_feat, sample_skip_fn)
desire_buf = shift_and_sample(desire_q, desire.reshape(1, 1, -1), sample_desire_fn)
inputs = {'features_buffer': feat_buf, desire_key: desire_buf, 'traffic_convention': traffic_convention, **extra_device}
policy_outputs = []
for runner in policy_runners:
policy_out = next(iter(runner(inputs).values())).cast('float32')
policy_outputs.append(policy_out)
return (vision_out, *policy_outputs)
return run_multi_policy
def _warmup_and_serialize(run_jit, input_queues, npy, nv12):
for i in range(3):
np.random.seed(42 + i)
frame = Tensor.randint(nv12.size, low=0, high=256, dtype='uint8').realize()
big_frame = Tensor.randint(nv12.size, low=0, high=256, dtype='uint8').realize()
for v in npy.values():
v[:] = np.random.randn(*v.shape).astype(v.dtype)
Device.default.synchronize()
st = time.perf_counter()
run_jit(**input_queues, frame=frame, big_frame=big_frame)
mt = time.perf_counter()
Device.default.synchronize()
et = time.perf_counter()
print(f" [{i + 1}/3] enqueue {(mt - st) * 1e3:6.2f} ms -- total {(et - st) * 1e3:6.2f} ms")
return pickle.loads(pickle.dumps(run_jit))
def compile_supercombo(nv12: NV12Frame, model_w, model_h, prepare_only, frame_skip,
model_runner, metadata):
print(f"Compiling combined supercombo JIT for {nv12.width}x{nv12.height} (prepare_only={prepare_only})...")
features_slice = metadata['output_slices']['hidden_state']
input_shapes = metadata['input_shapes']
_run = make_run_supercombo(model_runner, nv12, model_w, model_h,
features_slice, frame_skip, input_shapes, prepare_only)
run_jit = TinyJit(_run, prune=True)
input_queues, npy = make_supercombo_input_queues(input_shapes, frame_skip, Device.DEFAULT)
run_jit = _warmup_and_serialize(run_jit, input_queues, npy, nv12)
return run_jit
def compile_multi_policy(nv12: NV12Frame, model_w, model_h, prepare_only, frame_skip,
vision_runner, policy_runners, vision_metadata, policy_metadata):
print(f"Compiling combined multi-policy JIT for {nv12.width}x{nv12.height} (prepare_only={prepare_only})...")
vision_features_slice = vision_metadata['output_slices']['hidden_state']
vision_input_shapes = vision_metadata['input_shapes']
policy_input_shapes = policy_metadata['input_shapes']
desire_key = _detect_desire_key(policy_input_shapes)
extra_policy_keys = [k for k in policy_input_shapes if k not in ('features_buffer', desire_key, 'traffic_convention')]
vision_road_key, vision_wide_key = _detect_vision_keys(vision_input_shapes)
_run = make_run_vision_multi_policy(vision_runner, policy_runners, nv12, model_w, model_h,
vision_features_slice, frame_skip, desire_key, extra_policy_keys,
vision_road_key, vision_wide_key, prepare_only)
run_jit = TinyJit(_run, prune=True)
input_queues, npy = make_split_input_queues(vision_input_shapes, policy_input_shapes, frame_skip, Device.DEFAULT)
run_jit = _warmup_and_serialize(run_jit, input_queues, npy, nv12)
return run_jit
def _parse_size(s):
w, h = s.lower().split('x')
return int(w), int(h)
if __name__ == "__main__":
from tinygrad.nn.onnx import OnnxRunner
from openpilot.system.camerad.cameras.nv12_info import get_nv12_info
from openpilot.selfdrive.modeld.get_model_metadata import make_metadata_dict
p = argparse.ArgumentParser(description="Compile combined JIT pkl for sunnypilot modeld_v2")
p.add_argument('--model-type', choices=MODEL_TYPES, required=True)
p.add_argument('--model-size', type=_parse_size, required=True, help='model input WxH')
p.add_argument('--camera-resolutions', type=_parse_size, nargs='+', required=True)
p.add_argument('--frame-skip', type=int, default=None, help='frame skip value (auto-derived if not provided)')
p.add_argument('--output', required=True)
p.add_argument('--vision-onnx', help='vision ONNX (for split models)')
p.add_argument('--policy-onnx', help='policy ONNX (for vision_policy)')
p.add_argument('--off-policy-onnx', help='off-policy ONNX (for vision_multi_policy)')
p.add_argument('--on-policy-onnx', help='on-policy ONNX (for vision_multi_policy)')
p.add_argument('--supercombo-onnx', help='supercombo ONNX (for supercombo)')
args = p.parse_args()
out = defaultdict(dict)
if args.model_type == 'vision_policy':
assert args.vision_onnx and args.policy_onnx
vision_runner = OnnxRunner(args.vision_onnx)
policy_runner = OnnxRunner(args.policy_onnx)
out['metadata']['vision'] = make_metadata_dict(args.vision_onnx)
out['metadata']['policy'] = make_metadata_dict(args.policy_onnx)
frame_skip = args.frame_skip if args.frame_skip is not None else derive_frame_skip(out['metadata']['vision']['input_shapes'],
out['metadata']['policy']['input_shapes'])
for cam_w, cam_h in args.camera_resolutions:
nv12 = NV12Frame(cam_w, cam_h, *get_nv12_info(cam_w, cam_h))
model_w, model_h = args.model_size
out[(cam_w, cam_h)] = {
name: compile_split_policy(nv12, model_w, model_h, prepare_only, frame_skip,
vision_runner, policy_runner,
out['metadata']['vision'], out['metadata']['policy'])
for name, prepare_only in [('warp_enqueue', True), ('run_policy', False)]
}
elif args.model_type == 'supercombo':
assert args.supercombo_onnx
model_runner = OnnxRunner(args.supercombo_onnx)
out['metadata']['model'] = make_metadata_dict(args.supercombo_onnx)
frame_skip = args.frame_skip if args.frame_skip is not None else derive_frame_skip({}, out['metadata']['model']['input_shapes'])
for cam_w, cam_h in args.camera_resolutions:
nv12 = NV12Frame(cam_w, cam_h, *get_nv12_info(cam_w, cam_h))
model_w, model_h = args.model_size
out[(cam_w, cam_h)] = {
name: compile_supercombo(nv12, model_w, model_h, prepare_only, frame_skip,
model_runner, out['metadata']['model'])
for name, prepare_only in [('warp_enqueue', True), ('run_policy', False)]
}
elif args.model_type == 'vision_multi_policy':
assert args.vision_onnx
vision_runner = OnnxRunner(args.vision_onnx)
out['metadata']['vision'] = make_metadata_dict(args.vision_onnx)
policy_runners = []
policy_onnxes = []
if args.policy_onnx:
policy_onnxes.append(('policy', args.policy_onnx))
if args.off_policy_onnx:
policy_onnxes.append(('off_policy', args.off_policy_onnx))
if args.on_policy_onnx:
policy_onnxes.append(('on_policy', args.on_policy_onnx))
for name, onnx_path in policy_onnxes:
runner = OnnxRunner(onnx_path)
policy_runners.append(runner)
out['metadata'][name] = make_metadata_dict(onnx_path)
first_policy_key = policy_onnxes[0][0]
frame_skip = args.frame_skip if args.frame_skip is not None else derive_frame_skip(out['metadata']['vision']['input_shapes'],
out['metadata'][first_policy_key]['input_shapes'])
for cam_w, cam_h in args.camera_resolutions:
nv12 = NV12Frame(cam_w, cam_h, *get_nv12_info(cam_w, cam_h))
model_w, model_h = args.model_size
out[(cam_w, cam_h)] = {
name: compile_multi_policy(nv12, model_w, model_h, prepare_only, frame_skip,
vision_runner, policy_runners,
out['metadata']['vision'], out['metadata'][first_policy_key])
for name, prepare_only in [('warp_enqueue', True), ('run_policy', False)]
}
with open(args.output, "wb") as f:
pickle.dump(out, f)
pkl_size = os.path.getsize(args.output)
print(f"Saved combined JIT to {args.output} ({pkl_size / 1e6:.2f} MB)")
from openpilot.common.file_chunker import chunk_file, get_chunk_targets
chunk_targets = get_chunk_targets(args.output, pkl_size)
chunk_file(args.output, chunk_targets)
num_chunks = len(chunk_targets) - 1
print(f"Chunked into {num_chunks} file(s)")
@@ -0,0 +1,84 @@
import numpy as np
def index_function(idx, max_val=192, max_idx=32):
return (max_val) * ((idx/max_idx)**2)
class ModelConstants:
# time and distance indices
IDX_N = 33
T_IDXS = [index_function(idx, max_val=10.0) for idx in range(IDX_N)]
X_IDXS = [index_function(idx, max_val=192.0) for idx in range(IDX_N)]
LEAD_T_IDXS = [0., 2., 4., 6., 8., 10.]
LEAD_T_OFFSETS = [0., 2., 4.]
META_T_IDXS = [2., 4., 6., 8., 10.]
# model inputs constants
MODEL_FREQ = 20
FEATURE_LEN = 512
FULL_HISTORY_BUFFER_LEN = 99
DESIRE_LEN = 8
TRAFFIC_CONVENTION_LEN = 2
LAT_PLANNER_STATE_LEN = 4
LATERAL_CONTROL_PARAMS_LEN = 2
PREV_DESIRED_CURV_LEN = 1
# model outputs constants
FCW_THRESHOLDS_5MS2 = np.array([.05, .05, .15, .15, .15], dtype=np.float32)
FCW_THRESHOLDS_3MS2 = np.array([.7, .7], dtype=np.float32)
FCW_5MS2_PROBS_WIDTH = 5
FCW_3MS2_PROBS_WIDTH = 2
DISENGAGE_WIDTH = 5
POSE_WIDTH = 6
WIDE_FROM_DEVICE_WIDTH = 3
LEAD_WIDTH = 4
LANE_LINES_WIDTH = 2
ROAD_EDGES_WIDTH = 2
PLAN_WIDTH = 15
DESIRE_PRED_WIDTH = 8
LAT_PLANNER_SOLUTION_WIDTH = 4
DESIRED_CURV_WIDTH = 1
NUM_LANE_LINES = 4
NUM_ROAD_EDGES = 2
LEAD_TRAJ_LEN = 6
DESIRE_PRED_LEN = 4
PLAN_MHP_N = 5
LEAD_MHP_N = 2
PLAN_MHP_SELECTION = 1
LEAD_MHP_SELECTION = 3
FCW_THRESHOLD_5MS2_HIGH = 0.15
FCW_THRESHOLD_5MS2_LOW = 0.05
FCW_THRESHOLD_3MS2 = 0.7
CONFIDENCE_BUFFER_LEN = 5
RYG_GREEN = 0.01165
RYG_YELLOW = 0.06157
POLY_PATH_DEGREE = 4
# model outputs slices
class Plan:
POSITION = slice(0, 3)
VELOCITY = slice(3, 6)
ACCELERATION = slice(6, 9)
T_FROM_CURRENT_EULER = slice(9, 12)
ORIENTATION_RATE = slice(12, 15)
class Meta:
ENGAGED = slice(0, 1)
# next 2, 4, 6, 8, 10 seconds
GAS_DISENGAGE = slice(1, 31, 6)
BRAKE_DISENGAGE = slice(2, 31, 6)
STEER_OVERRIDE = slice(3, 31, 6)
HARD_BRAKE_3 = slice(4, 31, 6)
HARD_BRAKE_4 = slice(5, 31, 6)
HARD_BRAKE_5 = slice(6, 31, 6)
# next 0, 2, 4, 6, 8, 10 seconds
GAS_PRESS = slice(31, 55, 4)
BRAKE_PRESS = slice(32, 55, 4)
LEFT_BLINKER = slice(33, 55, 4)
RIGHT_BLINKER = slice(34, 55, 4)
@@ -0,0 +1,218 @@
import os
import capnp
import numpy as np
from openpilot.cereal import log
from openpilot.sunnypilot.modeld_v2.constants import ModelConstants, Plan
from openpilot.sunnypilot.models.helpers import plan_x_idxs_helper
from openpilot.selfdrive.controls.lib.drive_helpers import get_curvature_from_plan
SEND_RAW_PRED = os.getenv('SEND_RAW_PRED')
ConfidenceClass = log.ModelDataV2.ConfidenceClass
def get_curvature_from_output(output, plan, vego, lat_action_t, mlsim):
if not mlsim:
if desired_curv := output.get('desired_curvature'): # If the model outputs the desired curvature, use that directly
return float(desired_curv[0, 0])
return float(get_curvature_from_plan(plan[:, Plan.T_FROM_CURRENT_EULER][:, 2], plan[:, Plan.ORIENTATION_RATE][:, 2],
ModelConstants.T_IDXS, vego, lat_action_t))
class PublishState:
def __init__(self):
self.disengage_buffer = np.zeros(ModelConstants.CONFIDENCE_BUFFER_LEN*ModelConstants.DISENGAGE_WIDTH, dtype=np.float32)
self.prev_brake_5ms2_probs = np.zeros(ModelConstants.FCW_5MS2_PROBS_WIDTH, dtype=np.float32)
self.prev_brake_3ms2_probs = np.zeros(ModelConstants.FCW_3MS2_PROBS_WIDTH, dtype=np.float32)
def fill_xyzt(builder, t, x, y, z, x_std=None, y_std=None, z_std=None):
builder.t = t
builder.x = x.tolist()
builder.y = y.tolist()
builder.z = z.tolist()
if x_std is not None:
builder.xStd = x_std.tolist()
if y_std is not None:
builder.yStd = y_std.tolist()
if z_std is not None:
builder.zStd = z_std.tolist()
def fill_xyvat(builder, t, x, y, v, a, x_std=None, y_std=None, v_std=None, a_std=None):
builder.t = t
builder.x = x.tolist()
builder.y = y.tolist()
builder.v = v.tolist()
builder.a = a.tolist()
if x_std is not None:
builder.xStd = x_std.tolist()
if y_std is not None:
builder.yStd = y_std.tolist()
if v_std is not None:
builder.vStd = v_std.tolist()
if a_std is not None:
builder.aStd = a_std.tolist()
def fill_xyz_poly(builder, degree, x, y, z):
xyz = np.stack([x, y, z], axis=1)
coeffs = np.polynomial.polynomial.polyfit(ModelConstants.T_IDXS, xyz, deg=degree)
builder.xCoefficients = coeffs[:, 0].tolist()
builder.yCoefficients = coeffs[:, 1].tolist()
builder.zCoefficients = coeffs[:, 2].tolist()
def fill_lane_line_meta(builder, lane_lines, lane_line_probs):
builder.leftY = lane_lines[1].y[0]
builder.leftProb = lane_line_probs[1]
builder.rightY = lane_lines[2].y[0]
builder.rightProb = lane_line_probs[2]
def fill_model_msg(base_msg: capnp._DynamicStructBuilder, extended_msg: capnp._DynamicStructBuilder,
net_output_data: dict[str, np.ndarray], action: log.ModelDataV2.Action,
publish_state: PublishState, vipc_frame_id: int, vipc_frame_id_extra: int,
frame_id: int, frame_drop: float, timestamp_eof: int, model_execution_time: float,
valid: bool, model_meta) -> None:
frame_age = frame_id - vipc_frame_id if frame_id > vipc_frame_id else 0
frame_drop_perc = frame_drop * 100
extended_msg.valid = valid
base_msg.valid = valid
driving_model_data = base_msg.drivingModelData
driving_model_data.frameId = vipc_frame_id
driving_model_data.frameIdExtra = vipc_frame_id_extra
driving_model_data.frameDropPerc = frame_drop_perc
driving_model_data.modelExecutionTime = model_execution_time
driving_model_data.action = action
modelV2 = extended_msg.modelV2
modelV2.frameId = vipc_frame_id
modelV2.frameIdExtra = vipc_frame_id_extra
modelV2.frameAge = frame_age
modelV2.frameDropPerc = frame_drop_perc
modelV2.timestampEof = timestamp_eof
modelV2.modelExecutionTime = model_execution_time
# plan
fill_xyzt(modelV2.position, ModelConstants.T_IDXS, *net_output_data['plan'][0,:,Plan.POSITION].T, *net_output_data['plan_stds'][0,:,Plan.POSITION].T)
fill_xyzt(modelV2.velocity, ModelConstants.T_IDXS, *net_output_data['plan'][0,:,Plan.VELOCITY].T)
fill_xyzt(modelV2.acceleration, ModelConstants.T_IDXS, *net_output_data['plan'][0,:,Plan.ACCELERATION].T)
fill_xyzt(modelV2.orientation, ModelConstants.T_IDXS, *net_output_data['plan'][0,:,Plan.T_FROM_CURRENT_EULER].T)
fill_xyzt(modelV2.orientationRate, ModelConstants.T_IDXS, *net_output_data['plan'][0,:,Plan.ORIENTATION_RATE].T)
# temporal pose
temporal_pose = modelV2.deprecated.temporalPose
if 'sim_pose' in net_output_data:
temporal_pose.trans = net_output_data['sim_pose'][0,:ModelConstants.POSE_WIDTH//2].tolist()
temporal_pose.transStd = net_output_data['sim_pose_stds'][0,:ModelConstants.POSE_WIDTH//2].tolist()
temporal_pose.rot = net_output_data['sim_pose'][0,ModelConstants.POSE_WIDTH//2:].tolist()
temporal_pose.rotStd = net_output_data['sim_pose_stds'][0,ModelConstants.POSE_WIDTH//2:].tolist()
else:
temporal_pose.trans = net_output_data['plan'][0,0,Plan.VELOCITY].tolist()
temporal_pose.transStd = net_output_data['plan_stds'][0,0,Plan.VELOCITY].tolist()
temporal_pose.rot = net_output_data['plan'][0,0,Plan.ORIENTATION_RATE].tolist()
temporal_pose.rotStd = net_output_data['plan_stds'][0,0,Plan.ORIENTATION_RATE].tolist()
# poly path
fill_xyz_poly(driving_model_data.path, ModelConstants.POLY_PATH_DEGREE, *net_output_data['plan'][0,:,Plan.POSITION].T)
# action (includes lateral planning now)
modelV2.action = action
# times at X_IDXS of edges and lines
LINE_T_IDXS: list[float] = plan_x_idxs_helper(ModelConstants, Plan, net_output_data)
# lane lines
modelV2.init('laneLines', 4)
for i in range(4):
lane_line = modelV2.laneLines[i]
fill_xyzt(lane_line, LINE_T_IDXS, np.array(ModelConstants.X_IDXS), net_output_data['lane_lines'][0,i,:,0], net_output_data['lane_lines'][0,i,:,1])
modelV2.laneLineStds = net_output_data['lane_lines_stds'][0,:,0,0].tolist()
modelV2.laneLineProbs = net_output_data['lane_lines_prob'][0,1::2].tolist()
fill_lane_line_meta(driving_model_data.laneLineMeta, modelV2.laneLines, modelV2.laneLineProbs)
# road edges
modelV2.init('roadEdges', 2)
for i in range(2):
road_edge = modelV2.roadEdges[i]
fill_xyzt(road_edge, LINE_T_IDXS, np.array(ModelConstants.X_IDXS), net_output_data['road_edges'][0,i,:,0], net_output_data['road_edges'][0,i,:,1])
modelV2.roadEdgeStds = net_output_data['road_edges_stds'][0,:,0,0].tolist()
# leads
modelV2.init('leadsV3', 3)
for i in range(3):
lead = modelV2.leadsV3[i]
fill_xyvat(lead, ModelConstants.LEAD_T_IDXS, *net_output_data['lead'][0,i].T, *net_output_data['lead_stds'][0,i].T)
lead.prob = net_output_data['lead_prob'][0,i].tolist()
lead.probTime = ModelConstants.LEAD_T_OFFSETS[i]
# meta
meta = modelV2.meta
meta.desireState = net_output_data['desire_state'][0].reshape(-1).tolist()
meta.desirePrediction = net_output_data['desire_pred'][0].reshape(-1).tolist()
meta.engagedProb = net_output_data['meta'][0,model_meta.ENGAGED].item()
meta.init('disengagePredictions')
disengage_predictions = meta.disengagePredictions
disengage_predictions.t = ModelConstants.META_T_IDXS
disengage_predictions.brakeDisengageProbs = net_output_data['meta'][0,model_meta.BRAKE_DISENGAGE].tolist()
disengage_predictions.gasDisengageProbs = net_output_data['meta'][0,model_meta.GAS_DISENGAGE].tolist()
disengage_predictions.steerOverrideProbs = net_output_data['meta'][0,model_meta.STEER_OVERRIDE].tolist()
disengage_predictions.brake3MetersPerSecondSquaredProbs = net_output_data['meta'][0,model_meta.HARD_BRAKE_3].tolist()
disengage_predictions.brake4MetersPerSecondSquaredProbs = net_output_data['meta'][0,model_meta.HARD_BRAKE_4].tolist()
disengage_predictions.brake5MetersPerSecondSquaredProbs = net_output_data['meta'][0,model_meta.HARD_BRAKE_5].tolist()
if hasattr(model_meta, 'GAS_PRESS') and hasattr(model_meta, 'BRAKE_PRESS'):
disengage_predictions.gasPressProbs = net_output_data['meta'][0,model_meta.GAS_PRESS].tolist()
disengage_predictions.brakePressProbs = net_output_data['meta'][0,model_meta.BRAKE_PRESS].tolist()
publish_state.prev_brake_5ms2_probs[:-1] = publish_state.prev_brake_5ms2_probs[1:]
publish_state.prev_brake_5ms2_probs[-1] = net_output_data['meta'][0,model_meta.HARD_BRAKE_5][0]
publish_state.prev_brake_3ms2_probs[:-1] = publish_state.prev_brake_3ms2_probs[1:]
publish_state.prev_brake_3ms2_probs[-1] = net_output_data['meta'][0,model_meta.HARD_BRAKE_3][0]
hard_brake_predicted = (publish_state.prev_brake_5ms2_probs > ModelConstants.FCW_THRESHOLDS_5MS2).all() and \
(publish_state.prev_brake_3ms2_probs > ModelConstants.FCW_THRESHOLDS_3MS2).all()
meta.hardBrakePredicted = hard_brake_predicted.item()
# confidence
if vipc_frame_id % (2*ModelConstants.MODEL_FREQ) == 0:
# any disengage prob
brake_disengage_probs = net_output_data['meta'][0,model_meta.BRAKE_DISENGAGE]
gas_disengage_probs = net_output_data['meta'][0,model_meta.GAS_DISENGAGE]
steer_override_probs = net_output_data['meta'][0,model_meta.STEER_OVERRIDE]
any_disengage_probs = 1-((1-brake_disengage_probs)*(1-gas_disengage_probs)*(1-steer_override_probs))
# independent disengage prob for each 2s slice
ind_disengage_probs = np.r_[any_disengage_probs[0], np.diff(any_disengage_probs) / (1 - any_disengage_probs[:-1])]
# rolling buf for 2, 4, 6, 8, 10s
publish_state.disengage_buffer[:-ModelConstants.DISENGAGE_WIDTH] = publish_state.disengage_buffer[ModelConstants.DISENGAGE_WIDTH:]
publish_state.disengage_buffer[-ModelConstants.DISENGAGE_WIDTH:] = ind_disengage_probs
score = 0.
for i in range(ModelConstants.DISENGAGE_WIDTH):
score += publish_state.disengage_buffer[i*ModelConstants.DISENGAGE_WIDTH+ModelConstants.DISENGAGE_WIDTH-1-i].item() / ModelConstants.DISENGAGE_WIDTH
if score < ModelConstants.RYG_GREEN:
modelV2.confidence = ConfidenceClass.green
elif score < ModelConstants.RYG_YELLOW:
modelV2.confidence = ConfidenceClass.yellow
else:
modelV2.confidence = ConfidenceClass.red
# raw prediction if enabled
if SEND_RAW_PRED:
modelV2.rawPredictions = net_output_data['raw_pred'].tobytes()
def fill_pose_msg(msg: capnp._DynamicStructBuilder, net_output_data: dict[str, np.ndarray],
vipc_frame_id: int, vipc_dropped_frames: int, timestamp_eof: int, live_calib_seen: bool) -> None:
msg.valid = live_calib_seen & (vipc_dropped_frames < 1)
cameraOdometry = msg.cameraOdometry
cameraOdometry.frameId = vipc_frame_id
cameraOdometry.timestampEof = timestamp_eof
cameraOdometry.trans = net_output_data['pose'][0,:3].tolist()
cameraOdometry.rot = net_output_data['pose'][0,3:].tolist()
cameraOdometry.wideFromDeviceEuler = net_output_data['wide_from_device_euler'][0,:].tolist()
cameraOdometry.roadTransformTrans = net_output_data['road_transform'][0,:3].tolist()
cameraOdometry.transStd = net_output_data['pose_stds'][0,:3].tolist()
cameraOdometry.rotStd = net_output_data['pose_stds'][0,3:].tolist()
cameraOdometry.wideFromDeviceEulerStd = net_output_data['wide_from_device_euler_stds'][0,:].tolist()
cameraOdometry.roadTransformTransStd = net_output_data['road_transform_stds'][0,:3].tolist()
+55
View File
@@ -0,0 +1,55 @@
#!/usr/bin/env python3
import sys
import pathlib
import codecs
import pickle
from typing import Any
from tinygrad.nn.onnx import OnnxPBParser
class MetadataOnnxPBParser(OnnxPBParser):
def _parse_ModelProto(self) -> dict:
obj: dict[str, Any] = {"graph": {"input": [], "output": []}, "metadata_props": []}
for fid, wire_type in self._parse_message(self.reader.len):
match fid:
case 7:
obj["graph"] = self._parse_GraphProto()
case 14:
obj["metadata_props"].append(self._parse_StringStringEntryProto())
case _:
self.reader.skip_field(wire_type)
return obj
def get_name_and_shape(value_info: dict[str, Any]) -> tuple[str, tuple[int, ...]]:
shape = tuple(int(dim) if isinstance(dim, int) else 0 for dim in value_info["parsed_type"].shape)
name = value_info["name"]
return name, shape
def get_metadata_value_by_name(model: dict[str, Any], name: str) -> str | Any:
for prop in model["metadata_props"]:
if prop["key"] == name:
return prop["value"]
return None
if __name__ == "__main__":
model_path = pathlib.Path(sys.argv[1])
model = MetadataOnnxPBParser(model_path).parse()
output_slices = get_metadata_value_by_name(model, 'output_slices')
assert output_slices is not None, 'output_slices not found in metadata'
metadata = {
'model_checkpoint': get_metadata_value_by_name(model, 'model_checkpoint'),
'output_slices': pickle.loads(codecs.decode(output_slices.encode(), "base64")),
'input_shapes': dict(get_name_and_shape(x) for x in model["graph"]["input"]),
'output_shapes': dict(get_name_and_shape(x) for x in model["graph"]["output"]),
}
metadata_path = model_path.parent / (model_path.stem + '_metadata.pkl')
with open(metadata_path, 'wb') as f:
pickle.dump(metadata, f)
print(f'saved metadata to {metadata_path}')
+75
View File
@@ -0,0 +1,75 @@
#!/usr/bin/env python3
import sys
import shutil
import pickle
import codecs
from pathlib import Path
from openpilot.common.hardware.hw import Paths
from openpilot.sunnypilot.modeld_v2.get_model_metadata import MetadataOnnxPBParser, get_name_and_shape, get_metadata_value_by_name
def generate_metadata_pkl(model_path, output_path):
try:
model = MetadataOnnxPBParser(model_path).parse()
output_slices = get_metadata_value_by_name(model, 'output_slices')
if not output_slices:
return False
metadata = {
'model_checkpoint': get_metadata_value_by_name(model, 'model_checkpoint'),
'output_slices': pickle.loads(codecs.decode(output_slices.encode(), "base64")),
'input_shapes': dict(get_name_and_shape(x) for x in model["graph"]["input"]),
'output_shapes': dict(get_name_and_shape(x) for x in model["graph"]["output"]),
}
with open(output_path, 'wb') as f:
pickle.dump(metadata, f)
return True
except Exception:
return False
def install_models(model_dir):
model_dir = Path(model_dir)
models = ["driving_off_policy", "driving_on_policy", "driving_vision"]
found_models = []
for model in models:
if (model_dir / f"{model}.onnx").exists():
found_models.append(model)
if not found_models:
return
try:
custom_name = input(f"Found models ({', '.join(found_models)}). Enter model short name (e.g. wmiv4): ").strip()
except EOFError:
return
if not custom_name:
print("No name provided, skipping installation.")
return
dest_dir = Path(Paths.model_root())
dest_dir.mkdir(parents=True, exist_ok=True)
for model in found_models:
onnx_path = model_dir / f"{model}.onnx"
tinygrad_pkl = model_dir / f"{model}_tinygrad.pkl"
metadata_pkl = model_dir / f"{model}_metadata.pkl"
if not metadata_pkl.exists():
generate_metadata_pkl(onnx_path, metadata_pkl)
dest_tinygrad = dest_dir / f"{model}_{custom_name}_tinygrad.pkl"
dest_metadata = dest_dir / f"{model}_{custom_name}_metadata.pkl"
if tinygrad_pkl.exists():
shutil.move(str(tinygrad_pkl), str(dest_tinygrad))
if metadata_pkl.exists():
shutil.move(str(metadata_pkl), str(dest_metadata))
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: install_models_pc.py <model_dir>")
sys.exit(1)
install_models(sys.argv[1])
@@ -0,0 +1,17 @@
from openpilot.sunnypilot.modeld_v2.constants import Meta
class Meta20hz(Meta):
ENGAGED = slice(0, 1)
# next 2, 4, 6, 8, 10 seconds
GAS_DISENGAGE = slice(1, 31, 6)
BRAKE_DISENGAGE = slice(2, 31, 6)
STEER_OVERRIDE = slice(3, 31, 6)
HARD_BRAKE_3 = slice(4, 31, 6)
HARD_BRAKE_4 = slice(5, 31, 6)
HARD_BRAKE_5 = slice(6, 31, 6)
# next 0, 2, 4, 6, 8, 10 seconds
GAS_PRESS = slice(31, 55, 4)
BRAKE_PRESS = slice(32, 55, 4)
LEFT_BLINKER = slice(33, 55, 4)
RIGHT_BLINKER = slice(34, 55, 4)
@@ -0,0 +1,26 @@
from openpilot.sunnypilot.modeld_v2.constants import Meta
from openpilot.cereal import custom
from openpilot.sunnypilot.modeld_v2.meta_20hz import Meta20hz
from openpilot.sunnypilot.models.helpers import get_active_bundle
ModelBundle = custom.ModelManagerSP.ModelBundle
def load_meta_constants():
"""
Determines and loads the appropriate meta model class based on the metadata provided. The function checks
specific keys and conditions within the provided metadata dictionary to identify the corresponding meta
model class to return.
:param model_metadata: Dictionary containing metadata about the model. It includes
details such as input shapes, output slices, and other configurations for identifying
metadata-dependent meta model classes.
:type model_metadata: dict
:return: The appropriate meta model class (Meta, MetaSimPose, or MetaTombRaider)
based on the conditions and metadata provided.
:rtype: type
"""
if (bundle := get_active_bundle()) and bundle.is20hz:
return Meta20hz
return Meta # Default
+4
View File
@@ -0,0 +1,4 @@
#!/usr/bin/env bash
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)"
exec "$DIR/modeld.py" "$@"
+458
View File
@@ -0,0 +1,458 @@
#!/usr/bin/env python3
"""
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
import os
from openpilot.common.hardware import TICI
os.environ['DEV'] = 'QCOM' if TICI else 'CPU'
USBGPU = "USBGPU" in os.environ
if USBGPU:
os.environ['DEV'] = 'AMD'
os.environ['AMD_IFACE'] = 'USB'
import pickle
import time
import numpy as np
import openpilot.cereal.messaging as messaging
from openpilot.cereal import log
from opendbc.car.structs import car
from setproctitle import setproctitle
from openpilot.cereal.messaging import PubMaster, SubMaster
from msgq.visionipc import VisionIpcClient, VisionStreamType, VisionBuf
from opendbc.car.car_helpers import get_demo_car_params
from openpilot.common.swaglog import cloudlog
from openpilot.common.params import Params
from openpilot.common.filter_simple import FirstOrderFilter
from openpilot.common.realtime import config_realtime_process, DT_MDL
from openpilot.common.transformations.camera import DEVICE_CAMERAS
from openpilot.common.transformations.model import get_warp_matrix
from openpilot.system import sentry
from openpilot.selfdrive.controls.lib.desire_helper import DesireHelper
from openpilot.selfdrive.controls.lib.drive_helpers import get_accel_from_plan, smooth_value
from openpilot.sunnypilot.modeld_v2.fill_model_msg import fill_model_msg, fill_pose_msg, PublishState, get_curvature_from_output
from openpilot.sunnypilot.modeld_v2.constants import Plan
from openpilot.sunnypilot.modeld_v2.meta_helper import load_meta_constants
from openpilot.sunnypilot.modeld_v2.camera_offset_helper import CameraOffsetHelper
from openpilot.sunnypilot.livedelay.helpers import get_lat_delay
from openpilot.sunnypilot.modeld_v2.modeld_base import ModelStateBase
from openpilot.sunnypilot.models.helpers import get_active_bundle
PROCESS_NAME = "openpilot.selfdrive.modeld.modeld_tinygrad"
def _pkl_exists(path):
from openpilot.common.file_chunker import get_manifest_path
return os.path.exists(path) or os.path.exists(get_manifest_path(path))
def _find_driving_pkl(bundle):
if (override := os.environ.get('COMBINED_MODEL_PKL')) and _pkl_exists(override):
return override
if bundle is None or not bundle.models:
return None
from openpilot.common.hardware.hw import Paths
model_root = Paths.model_root()
pkl_name = bundle.models[0].artifact.fileName
pkl_path = os.path.join(model_root, pkl_name)
if _pkl_exists(pkl_path):
return pkl_path
class FrameMeta:
frame_id: int = 0
timestamp_sof: int = 0
timestamp_eof: int = 0
def __init__(self, vipc=None):
if vipc is not None:
self.frame_id, self.timestamp_sof, self.timestamp_eof = vipc.frame_id, vipc.timestamp_sof, vipc.timestamp_eof
class ModelState(ModelStateBase):
inputs: dict[str, np.ndarray]
prev_desire: np.ndarray
def __init__(self, cam_w: int, cam_h: int):
ModelStateBase.__init__(self)
env_pkl = os.environ.get('COMBINED_MODEL_PKL')
if env_pkl and os.path.exists(env_pkl):
model_bundle = None
else:
model_bundle = get_active_bundle()
self.generation = model_bundle.generation if model_bundle is not None else None
overrides = {override.key: override.value for override in model_bundle.overrides} if model_bundle else {}
self.LAT_SMOOTH_SECONDS = float(overrides.get('lat', ".0"))
self.LONG_SMOOTH_SECONDS = float(overrides.get('long', ".0"))
self.MIN_LAT_CONTROL_SPEED = 0.3
self.PLANPLUS_CONTROL: float = 1.0
pkl_path = _find_driving_pkl(model_bundle)
assert pkl_path is not None, "No driving pkl found — all models must be compiled with compile_modeld.py"
self._init_combined(pkl_path, cam_w, cam_h, model_bundle)
def _init_combined(self, pkl_path, cam_w, cam_h, bundle):
from tinygrad.tensor import Tensor
from openpilot.system.camerad.cameras.nv12_info import get_nv12_info
from openpilot.sunnypilot.modeld_v2.compile_modeld import derive_frame_skip, make_split_input_queues
from tinygrad.device import Device
from openpilot.common.file_chunker import open_file_chunked
cloudlog.warning(f"loading combined pkl: {pkl_path}")
jits = pickle.load(open_file_chunked(pkl_path))
self.DEV = Device.DEFAULT
metadata = jits['metadata']
if 'model' in metadata:
model_metadata = metadata['model']
self.vision_output_slices = model_metadata['output_slices']
self.policy_output_slices = {}
self._policy_slices_list = []
self._combined_model_type = 'supercombo'
self._vision_input_names = [k for k in model_metadata['input_shapes'] if 'img' in k]
from openpilot.sunnypilot.modeld_v2.compile_modeld import make_supercombo_input_queues
frame_skip = derive_frame_skip({}, model_metadata['input_shapes'])
self.input_queues, self.numpy_inputs = make_supercombo_input_queues(model_metadata['input_shapes'], frame_skip, device=self.DEV)
else:
vision_metadata = metadata['vision']
policy_keys = [k for k in metadata if k != 'vision']
if policy_keys == ['policy']:
self._combined_model_type = 'split'
else:
self._combined_model_type = 'multi_policy'
self.vision_output_slices = vision_metadata['output_slices']
self._policy_keys = policy_keys
self._policy_slices_list = [metadata[k]['output_slices'] for k in policy_keys]
self.policy_output_slices = self._policy_slices_list[0]
self._has_on_policy = any('on' in k.lower() for k in policy_keys)
first_policy_metadata = metadata[policy_keys[0]]
vision_input_shapes = vision_metadata['input_shapes']
policy_input_shapes = first_policy_metadata['input_shapes']
self._vision_input_names = [k for k in vision_input_shapes if 'img' in k]
frame_skip = derive_frame_skip(vision_input_shapes, policy_input_shapes)
self.input_queues, self.numpy_inputs = make_split_input_queues(vision_input_shapes, policy_input_shapes, frame_skip, device=self.DEV)
from openpilot.sunnypilot.modeld_v2.parse_model_outputs_split import Parser as SplitParser
from openpilot.sunnypilot.modeld_v2.parse_model_outputs import Parser as CombinedParser
self.parser = SplitParser() if self._combined_model_type != 'supercombo' else CombinedParser()
is_20hz = bundle.is20hz if bundle else self._combined_model_type in ('split', 'multi_policy')
if is_20hz:
from openpilot.sunnypilot.models.split_model_constants import SplitModelConstants
self.constants = SplitModelConstants()
else:
from openpilot.sunnypilot.modeld_v2.constants import ModelConstants
self.constants = ModelConstants()
self.prev_desire = np.zeros(self.constants.DESIRE_LEN, dtype=np.float32)
self.full_frames: dict = {}
self._blob_cache: dict = {}
nv12_info = get_nv12_info(cam_w, cam_h)
self.frame_buf_params = dict.fromkeys(self._vision_input_names, nv12_info)
self._run_policy = jits[(cam_w, cam_h)]['run_policy']
self._warp_enqueue = jits[(cam_w, cam_h)]['warp_enqueue']
road_name = next(k for k in self._vision_input_names if 'big' not in k)
yuv_size = self.frame_buf_params[road_name][3]
self._warp_enqueue(
**self.input_queues,
frame=Tensor(np.zeros(yuv_size, dtype=np.uint8), device=self.DEV).contiguous().realize(),
big_frame=Tensor(np.zeros(yuv_size, dtype=np.uint8), device=self.DEV).contiguous().realize())
@property
def mlsim(self) -> bool:
return bool(self.generation is not None and self.generation >= 11)
@property
def vision_input_names(self) -> list[str]:
return self._vision_input_names
@property
def desire_key(self) -> str:
return next(k for k in self.numpy_inputs if k.startswith('desire'))
def run(self, bufs: dict[str, VisionBuf], transforms: dict[str, np.ndarray],
inputs: dict[str, np.ndarray], prepare_only: bool) -> dict[str, np.ndarray] | None:
from tinygrad.tensor import Tensor
for key in bufs.keys():
ptr = np.frombuffer(bufs[key].data, dtype=np.uint8).ctypes.data
yuv_size = self.frame_buf_params[key][3]
cache_key = (key, ptr)
if cache_key not in self._blob_cache:
self._blob_cache[cache_key] = Tensor.from_blob(ptr, (yuv_size,), dtype='uint8', device=self.DEV)
self.full_frames[key] = self._blob_cache[cache_key]
desire_key = self.desire_key
inputs[desire_key][0] = 0
self.numpy_inputs[desire_key][:] = np.where(inputs[desire_key] - self.prev_desire > .99, inputs[desire_key], 0)
self.prev_desire[:] = inputs[desire_key]
for key in ('traffic_convention', 'lateral_control_params'):
if key in self.numpy_inputs and key in inputs:
self.numpy_inputs[key][:] = inputs[key]
road_key = next(n for n in bufs if 'big' not in n)
wide_key = next(n for n in bufs if 'big' in n)
self.numpy_inputs['tfm'][:, :] = transforms[road_key].reshape(3, 3)
self.numpy_inputs['big_tfm'][:, :] = transforms[wide_key].reshape(3, 3)
if prepare_only:
self._warp_enqueue(**self.input_queues, frame=self.full_frames[road_key], big_frame=self.full_frames[wide_key])
return None
raw_outputs = self._run_policy(**self.input_queues, frame=self.full_frames[road_key], big_frame=self.full_frames[wide_key])
if self._combined_model_type == 'supercombo':
model_output = raw_outputs.numpy().flatten()
sliced = {k: model_output[np.newaxis, v] for k, v in self.vision_output_slices.items()}
outputs = self.parser.parse_outputs(sliced)
else:
vision_output = raw_outputs[0].numpy().flatten()
vision_sliced = {k: vision_output[np.newaxis, v] for k, v in self.vision_output_slices.items()}
outputs = self.parser.parse_vision_outputs(vision_sliced)
for i, policy_slices in enumerate(self._policy_slices_list):
policy_output = raw_outputs[i + 1].numpy().flatten()
policy_sliced = {k: policy_output[np.newaxis, v] for k, v in policy_slices.items()}
parsed = self.parser.parse_policy_outputs(policy_sliced)
if 'off' in self._policy_keys[i] and self._has_on_policy:
parsed.pop('plan', None)
outputs.update(parsed)
if 'planplus' in outputs and 'plan' in outputs:
outputs['plan'] = outputs['plan'] + outputs['planplus']
if 'desired_curvature' in outputs and 'prev_desired_curv' in self.numpy_inputs:
buf = self.numpy_inputs['prev_desired_curv']
buf[0, :-1] = buf[0, 1:]
buf[0, -1, :] = outputs['desired_curvature'][0, :] if not self.mlsim else 0
return outputs
def get_action_from_model(self, model_output: dict[str, np.ndarray], prev_action: log.ModelDataV2.Action,
lat_action_t: float, long_action_t: float, v_ego: float) -> log.ModelDataV2.Action:
plan = model_output['plan'][0]
desired_accel, should_stop = get_accel_from_plan(plan[:, Plan.VELOCITY][:, 0], plan[:, Plan.ACCELERATION][:, 0], self.constants.T_IDXS,
action_t=long_action_t)
desired_accel = smooth_value(desired_accel, prev_action.desiredAcceleration, self.LONG_SMOOTH_SECONDS)
curvature_plan = plan + (self.PLANPLUS_CONTROL - 1.0) * model_output['planplus'][0] if 'planplus' in model_output and self.PLANPLUS_CONTROL != 1.0 else plan
desired_curvature = get_curvature_from_output(model_output, curvature_plan, v_ego, lat_action_t, self.mlsim)
if self.generation is not None and self.generation >= 10: # smooth curvature for post FOF models
if v_ego > self.MIN_LAT_CONTROL_SPEED:
desired_curvature = smooth_value(desired_curvature, prev_action.desiredCurvature, self.LAT_SMOOTH_SECONDS)
else:
desired_curvature = prev_action.desiredCurvature
return log.ModelDataV2.Action(desiredCurvature=float(desired_curvature),desiredAcceleration=float(desired_accel), shouldStop=bool(should_stop))
def main(demo=False):
cloudlog.warning("modeld init")
sentry.set_tag("daemon", PROCESS_NAME)
cloudlog.bind(daemon=PROCESS_NAME)
setproctitle(PROCESS_NAME)
config_realtime_process(7, 54)
# visionipc clients
while True:
available_streams = VisionIpcClient.available_streams("camerad", block=False)
if available_streams:
use_extra_client = VisionStreamType.VISION_STREAM_WIDE_ROAD in available_streams and VisionStreamType.VISION_STREAM_ROAD in available_streams
main_wide_camera = VisionStreamType.VISION_STREAM_ROAD not in available_streams
break
time.sleep(.1)
vipc_client_main_stream = VisionStreamType.VISION_STREAM_WIDE_ROAD if main_wide_camera else VisionStreamType.VISION_STREAM_ROAD
vipc_client_main = VisionIpcClient("camerad", vipc_client_main_stream, True)
vipc_client_extra = VisionIpcClient("camerad", VisionStreamType.VISION_STREAM_WIDE_ROAD, False)
cloudlog.warning(f"vision stream set up, main_wide_camera: {main_wide_camera}, use_extra_client: {use_extra_client}")
while not vipc_client_main.connect(False):
time.sleep(0.1)
while use_extra_client and not vipc_client_extra.connect(False):
time.sleep(0.1)
cloudlog.warning(f"connected main cam with buffer size: {vipc_client_main.buffer_len} ({vipc_client_main.width} x {vipc_client_main.height})")
if use_extra_client:
cloudlog.warning(f"connected extra cam with buffer size: {vipc_client_extra.buffer_len} ({vipc_client_extra.width} x {vipc_client_extra.height})")
cloudlog.warning("loading model")
model = ModelState(cam_w=vipc_client_main.width, cam_h=vipc_client_main.height)
cloudlog.warning("models loaded, modeld starting")
# messaging
pm = PubMaster(["modelV2", "drivingModelData", "cameraOdometry", "modelDataV2SP"])
sm = SubMaster(["deviceState", "carState", "roadCameraState", "liveCalibration", "driverMonitoringState", "carControl", "liveDelay"])
publish_state = PublishState()
params = Params()
# setup filter to track dropped frames
frame_dropped_filter = FirstOrderFilter(0., 10., 1. / model.constants.MODEL_FREQ)
frame_id = 0
last_vipc_frame_id = 0
run_count = 0
model_transform_main = np.zeros((3, 3), dtype=np.float32)
model_transform_extra = np.zeros((3, 3), dtype=np.float32)
live_calib_seen = False
buf_main, buf_extra = None, None
meta_main = FrameMeta()
meta_extra = FrameMeta()
camera_offset_helper = CameraOffsetHelper()
if demo:
CP = get_demo_car_params()
else:
CP = messaging.log_from_bytes(params.get("CarParams", block=True), car.CarParams)
cloudlog.info("modeld got CarParams: %s", CP.brand)
# TODO Move smooth seconds to action function
long_delay = CP.longitudinalActuatorDelay + model.LONG_SMOOTH_SECONDS
prev_action = log.ModelDataV2.Action()
DH = DesireHelper()
meta_constants = load_meta_constants()
while True:
# Keep receiving frames until we are at least 1 frame ahead of previous extra frame
while meta_main.timestamp_sof < meta_extra.timestamp_sof + 25000000:
buf_main = vipc_client_main.recv()
meta_main = FrameMeta(vipc_client_main)
if buf_main is None:
break
if buf_main is None:
cloudlog.debug("vipc_client_main no frame")
continue
if use_extra_client:
# Keep receiving extra frames until frame id matches main camera
while True:
buf_extra = vipc_client_extra.recv()
meta_extra = FrameMeta(vipc_client_extra)
if buf_extra is None or meta_main.timestamp_sof < meta_extra.timestamp_sof + 25000000:
break
if buf_extra is None:
cloudlog.debug("vipc_client_extra no frame")
continue
if abs(meta_main.timestamp_sof - meta_extra.timestamp_sof) > 10000000:
cloudlog.error(f"frames out of sync! main: {meta_main.frame_id} ({meta_main.timestamp_sof / 1e9:.5f}),\
extra: {meta_extra.frame_id} ({meta_extra.timestamp_sof / 1e9:.5f})")
else:
# Use single camera
buf_extra = buf_main
meta_extra = meta_main
sm.update(0)
desire = DH.desire
is_rhd = sm["driverMonitoringState"].isRHD
frame_id = sm["roadCameraState"].frameId
v_ego = max(sm["carState"].vEgo, 0.)
if sm.frame % 60 == 0:
model.lat_delay = get_lat_delay(params, sm["liveDelay"].lateralDelay)
model.PLANPLUS_CONTROL = params.get("PlanplusControl", return_default=True)
camera_offset_helper.set_offset(params.get("CameraOffset", return_default=True))
lat_delay = model.lat_delay + model.LAT_SMOOTH_SECONDS
if sm.updated["liveCalibration"] and sm.seen['roadCameraState'] and sm.seen['deviceState']:
device_from_calib_euler = np.array(sm["liveCalibration"].rpyCalib, dtype=np.float32)
dc = DEVICE_CAMERAS[(str(sm['deviceState'].deviceType), str(sm['roadCameraState'].sensor))]
model_transform_main = get_warp_matrix(device_from_calib_euler, dc.ecam.intrinsics if main_wide_camera else dc.fcam.intrinsics, False).astype(np.float32)
model_transform_extra = get_warp_matrix(device_from_calib_euler, dc.ecam.intrinsics, True).astype(np.float32)
model_transform_main, model_transform_extra = camera_offset_helper.update(model_transform_main, model_transform_extra, sm, main_wide_camera)
live_calib_seen = True
traffic_convention = np.zeros(2)
traffic_convention[int(is_rhd)] = 1
vec_desire = np.zeros(model.constants.DESIRE_LEN, dtype=np.float32)
if desire >= 0 and desire < model.constants.DESIRE_LEN:
vec_desire[desire] = 1
# tracked dropped frames
vipc_dropped_frames = max(0, meta_main.frame_id - last_vipc_frame_id - 1)
frames_dropped = frame_dropped_filter.update(min(vipc_dropped_frames, 10))
if run_count < 10: # let frame drops warm up
frame_dropped_filter.x = 0.
frames_dropped = 0.
run_count = run_count + 1
frame_drop_ratio = frames_dropped / (1 + frames_dropped)
prepare_only = vipc_dropped_frames > 0
if prepare_only:
cloudlog.error(f"skipping model eval. Dropped {vipc_dropped_frames} frames")
bufs = {name: buf_extra if 'big' in name else buf_main for name in model.vision_input_names}
transforms = {name: model_transform_extra if 'big' in name else model_transform_main for name in model.vision_input_names}
inputs:dict[str, np.ndarray] = {
model.desire_key: vec_desire,
'traffic_convention': traffic_convention,
}
if 'lateral_control_params' in model.numpy_inputs:
inputs['lateral_control_params'] = np.array([v_ego, lat_delay], dtype=np.float32)
mt1 = time.perf_counter()
model_output = model.run(bufs, transforms, inputs, prepare_only)
mt2 = time.perf_counter()
model_execution_time = mt2 - mt1
if model_output is not None:
modelv2_send = messaging.new_message('modelV2')
drivingdata_send = messaging.new_message('drivingModelData')
posenet_send = messaging.new_message('cameraOdometry')
mdv2sp_send = messaging.new_message('modelDataV2SP')
action = model.get_action_from_model(model_output, prev_action, lat_delay + DT_MDL, long_delay + DT_MDL, v_ego)
prev_action = action
fill_model_msg(drivingdata_send, modelv2_send, model_output, action,
publish_state, meta_main.frame_id, meta_extra.frame_id, frame_id,
frame_drop_ratio, meta_main.timestamp_eof, model_execution_time, live_calib_seen, meta_constants)
desire_state = modelv2_send.modelV2.meta.desireState
l_lane_change_prob = desire_state[log.Desire.laneChangeLeft]
r_lane_change_prob = desire_state[log.Desire.laneChangeRight]
lane_change_prob = l_lane_change_prob + r_lane_change_prob
DH.update(sm['carState'], sm['carControl'].latActive, lane_change_prob)
modelv2_send.modelV2.meta.laneChangeState = DH.lane_change_state
modelv2_send.modelV2.meta.laneChangeDirection = DH.lane_change_direction
mdv2sp_send.modelDataV2SP.laneTurnDirection = DH.lane_turn_direction
drivingdata_send.drivingModelData.meta.laneChangeState = DH.lane_change_state
drivingdata_send.drivingModelData.meta.laneChangeDirection = DH.lane_change_direction
fill_pose_msg(posenet_send, model_output, meta_main.frame_id, vipc_dropped_frames, meta_main.timestamp_eof, live_calib_seen)
pm.send('modelV2', modelv2_send)
pm.send('drivingModelData', drivingdata_send)
pm.send('cameraOdometry', posenet_send)
pm.send('modelDataV2SP', mdv2sp_send)
last_vipc_frame_id = meta_main.frame_id
if __name__ == "__main__":
try:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--demo', action='store_true', help='A boolean for demo mode.')
args = parser.parse_args()
main(demo=args.demo)
except KeyboardInterrupt:
cloudlog.warning(f"child {PROCESS_NAME} got SIGINT")
except Exception:
sentry.capture_exception()
raise
@@ -0,0 +1,12 @@
"""
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
from openpilot.common.params import Params
class ModelStateBase:
def __init__(self):
self.lat_delay = Params().get("LagdValueCache", return_default=True)
@@ -0,0 +1,107 @@
import numpy as np
from openpilot.sunnypilot.modeld_v2.constants import ModelConstants
def safe_exp(x, out=None):
# -11 is around 10**14, more causes float16 overflow
return np.exp(np.clip(x, -np.inf, 11), out=out)
def sigmoid(x):
return 1. / (1. + safe_exp(-x))
def softmax(x, axis=-1):
x -= np.max(x, axis=axis, keepdims=True)
if x.dtype == np.float32 or x.dtype == np.float64:
safe_exp(x, out=x)
else:
x = safe_exp(x)
x /= np.sum(x, axis=axis, keepdims=True)
return x
class Parser:
def __init__(self, ignore_missing=False):
self.ignore_missing = ignore_missing
def check_missing(self, outs, name):
if name not in outs and not self.ignore_missing:
raise ValueError(f"Missing output {name}")
return name not in outs
def parse_categorical_crossentropy(self, name, outs, out_shape=None):
if self.check_missing(outs, name):
return
raw = outs[name]
if out_shape is not None:
raw = raw.reshape((raw.shape[0],) + out_shape)
outs[name] = softmax(raw, axis=-1)
def parse_binary_crossentropy(self, name, outs):
if self.check_missing(outs, name):
return
raw = outs[name]
outs[name] = sigmoid(raw)
def parse_mdn(self, name, outs, in_N=0, out_N=1, out_shape=None):
if self.check_missing(outs, name):
return
raw = outs[name]
raw = raw.reshape((raw.shape[0], max(in_N, 1), -1))
n_values = (raw.shape[2] - out_N)//2
pred_mu = raw[:,:,:n_values]
pred_std = safe_exp(raw[:,:,n_values: 2*n_values])
if in_N > 1:
weights = np.zeros((raw.shape[0], in_N, out_N), dtype=raw.dtype)
for i in range(out_N):
weights[:,:,i - out_N] = softmax(raw[:,:,i - out_N], axis=-1)
if out_N == 1:
for fidx in range(weights.shape[0]):
idxs = np.argsort(weights[fidx][:,0])[::-1]
weights[fidx] = weights[fidx][idxs]
pred_mu[fidx] = pred_mu[fidx][idxs]
pred_std[fidx] = pred_std[fidx][idxs]
full_shape = tuple([raw.shape[0], in_N] + list(out_shape))
outs[name + '_weights'] = weights
outs[name + '_hypotheses'] = pred_mu.reshape(full_shape)
outs[name + '_stds_hypotheses'] = pred_std.reshape(full_shape)
pred_mu_final = np.zeros((raw.shape[0], out_N, n_values), dtype=raw.dtype)
pred_std_final = np.zeros((raw.shape[0], out_N, n_values), dtype=raw.dtype)
for fidx in range(weights.shape[0]):
for hidx in range(out_N):
idxs = np.argsort(weights[fidx,:,hidx])[::-1]
pred_mu_final[fidx, hidx] = pred_mu[fidx, idxs[0]]
pred_std_final[fidx, hidx] = pred_std[fidx, idxs[0]]
else:
pred_mu_final = pred_mu
pred_std_final = pred_std
if out_N > 1:
final_shape = tuple([raw.shape[0], out_N] + list(out_shape))
else:
final_shape = tuple([raw.shape[0],] + list(out_shape))
outs[name] = pred_mu_final.reshape(final_shape)
outs[name + '_stds'] = pred_std_final.reshape(final_shape)
def parse_outputs(self, outs: dict[str, np.ndarray]) -> dict[str, np.ndarray]:
self.parse_mdn('plan', outs, in_N=ModelConstants.PLAN_MHP_N, out_N=ModelConstants.PLAN_MHP_SELECTION,
out_shape=(ModelConstants.IDX_N,ModelConstants.PLAN_WIDTH))
self.parse_mdn('lane_lines', outs, in_N=0, out_N=0, out_shape=(ModelConstants.NUM_LANE_LINES,ModelConstants.IDX_N,ModelConstants.LANE_LINES_WIDTH))
self.parse_mdn('road_edges', outs, in_N=0, out_N=0, out_shape=(ModelConstants.NUM_ROAD_EDGES,ModelConstants.IDX_N,ModelConstants.LANE_LINES_WIDTH))
self.parse_mdn('pose', outs, in_N=0, out_N=0, out_shape=(ModelConstants.POSE_WIDTH,))
self.parse_mdn('road_transform', outs, in_N=0, out_N=0, out_shape=(ModelConstants.POSE_WIDTH,))
if 'sim_pose' in outs:
self.parse_mdn('sim_pose', outs, in_N=0, out_N=0, out_shape=(ModelConstants.POSE_WIDTH,))
self.parse_mdn('wide_from_device_euler', outs, in_N=0, out_N=0, out_shape=(ModelConstants.WIDE_FROM_DEVICE_WIDTH,))
self.parse_mdn('lead', outs, in_N=ModelConstants.LEAD_MHP_N, out_N=ModelConstants.LEAD_MHP_SELECTION,
out_shape=(ModelConstants.LEAD_TRAJ_LEN,ModelConstants.LEAD_WIDTH))
if 'lat_planner_solution' in outs:
self.parse_mdn('lat_planner_solution', outs, in_N=0, out_N=0, out_shape=(ModelConstants.IDX_N,ModelConstants.LAT_PLANNER_SOLUTION_WIDTH))
if 'desired_curvature' in outs:
self.parse_mdn('desired_curvature', outs, in_N=0, out_N=0, out_shape=(ModelConstants.DESIRED_CURV_WIDTH,))
for k in ['lead_prob', 'lane_lines_prob', 'meta']:
self.parse_binary_crossentropy(k, outs)
self.parse_categorical_crossentropy('desire_state', outs, out_shape=(ModelConstants.DESIRE_PRED_WIDTH,))
self.parse_categorical_crossentropy('desire_pred', outs, out_shape=(ModelConstants.DESIRE_PRED_LEN,ModelConstants.DESIRE_PRED_WIDTH))
return outs
@@ -0,0 +1,154 @@
import numpy as np
from openpilot.sunnypilot.models.split_model_constants import SplitModelConstants
def safe_exp(x, out=None):
# -11 is around 10**14, more causes float16 overflow
return np.exp(np.clip(x, -np.inf, 11), out=out)
def sigmoid(x):
return 1. / (1. + safe_exp(-x))
def softmax(x, axis=-1):
x -= np.max(x, axis=axis, keepdims=True)
if x.dtype == np.float32 or x.dtype == np.float64:
safe_exp(x, out=x)
else:
x = safe_exp(x)
x /= np.sum(x, axis=axis, keepdims=True)
return x
class Parser:
def __init__(self, ignore_missing=False):
self.ignore_missing = ignore_missing
def check_missing(self, outs, name):
if name not in outs and not self.ignore_missing:
raise ValueError(f"Missing output {name}")
return name not in outs
def parse_categorical_crossentropy(self, name, outs, out_shape=None):
if self.check_missing(outs, name):
return
raw = outs[name]
if out_shape is not None:
raw = raw.reshape((raw.shape[0],) + out_shape)
outs[name] = softmax(raw, axis=-1)
def parse_binary_crossentropy(self, name, outs):
if self.check_missing(outs, name):
return
raw = outs[name]
outs[name] = sigmoid(raw)
def parse_mdn(self, name, outs, in_N=0, out_N=1, out_shape=None):
if self.check_missing(outs, name):
return
raw = outs[name]
raw = raw.reshape((raw.shape[0], max(in_N, 1), -1))
n_values = (raw.shape[2] - out_N)//2
pred_mu = raw[:,:,:n_values]
pred_std = safe_exp(raw[:,:,n_values: 2*n_values])
if in_N > 1:
weights = np.zeros((raw.shape[0], in_N, out_N), dtype=raw.dtype)
for i in range(out_N):
weights[:,:,i - out_N] = softmax(raw[:,:,i - out_N], axis=-1)
if out_N == 1:
for fidx in range(weights.shape[0]):
idxs = np.argsort(weights[fidx][:,0])[::-1]
weights[fidx] = weights[fidx][idxs]
pred_mu[fidx] = pred_mu[fidx][idxs]
pred_std[fidx] = pred_std[fidx][idxs]
full_shape = tuple([raw.shape[0], in_N] + list(out_shape))
outs[name + '_weights'] = weights
outs[name + '_hypotheses'] = pred_mu.reshape(full_shape)
outs[name + '_stds_hypotheses'] = pred_std.reshape(full_shape)
pred_mu_final = np.zeros((raw.shape[0], out_N, n_values), dtype=raw.dtype)
pred_std_final = np.zeros((raw.shape[0], out_N, n_values), dtype=raw.dtype)
for fidx in range(weights.shape[0]):
for hidx in range(out_N):
idxs = np.argsort(weights[fidx,:,hidx])[::-1]
pred_mu_final[fidx, hidx] = pred_mu[fidx, idxs[0]]
pred_std_final[fidx, hidx] = pred_std[fidx, idxs[0]]
else:
pred_mu_final = pred_mu
pred_std_final = pred_std
if out_N > 1:
final_shape = tuple([raw.shape[0], out_N] + list(out_shape))
else:
final_shape = tuple([raw.shape[0],] + list(out_shape))
outs[name] = pred_mu_final.reshape(final_shape)
outs[name + '_stds'] = pred_std_final.reshape(final_shape)
def is_mhp(self, outs, name, shape):
if self.check_missing(outs, name):
return False
if outs[name].shape[1] == 2 * shape:
return False
return True
def parse_dynamic_outputs(self, outs: dict[str, np.ndarray]) -> None:
if 'lead' in outs:
lead_mhp = self.is_mhp(outs, 'lead',
SplitModelConstants.LEAD_MHP_SELECTION * SplitModelConstants.LEAD_TRAJ_LEN * SplitModelConstants.LEAD_WIDTH)
lead_in_N, lead_out_N = (SplitModelConstants.LEAD_MHP_N, SplitModelConstants.LEAD_MHP_SELECTION) if lead_mhp else (0, 0)
lead_out_shape = (SplitModelConstants.LEAD_TRAJ_LEN, SplitModelConstants.LEAD_WIDTH) if lead_mhp else \
(SplitModelConstants.LEAD_MHP_SELECTION, SplitModelConstants.LEAD_TRAJ_LEN, SplitModelConstants.LEAD_WIDTH)
self.parse_mdn('lead', outs, in_N=lead_in_N, out_N=lead_out_N, out_shape=lead_out_shape)
if 'plan' in outs:
plan_mhp = self.is_mhp(outs, 'plan', SplitModelConstants.IDX_N * SplitModelConstants.PLAN_WIDTH)
plan_in_N, plan_out_N = (SplitModelConstants.PLAN_MHP_N, SplitModelConstants.PLAN_MHP_SELECTION) if plan_mhp else (0, 0)
self.parse_mdn('plan', outs, in_N=plan_in_N, out_N=plan_out_N,
out_shape=(SplitModelConstants.IDX_N, SplitModelConstants.PLAN_WIDTH))
if 'planplus' in outs:
self.parse_mdn('planplus', outs, in_N=0, out_N=0, out_shape=(SplitModelConstants.IDX_N, SplitModelConstants.PLAN_WIDTH))
def split_outputs(self, outs: dict[str, np.ndarray]) -> None:
if 'desired_curvature' in outs:
self.parse_mdn('desired_curvature', outs, in_N=0, out_N=0, out_shape=(SplitModelConstants.DESIRED_CURV_WIDTH,))
if 'desire_pred' in outs:
self.parse_categorical_crossentropy('desire_pred', outs, out_shape=(SplitModelConstants.DESIRE_PRED_LEN,SplitModelConstants.DESIRE_PRED_WIDTH))
if 'desire_state' in outs:
self.parse_categorical_crossentropy('desire_state', outs, out_shape=(SplitModelConstants.DESIRE_PRED_WIDTH,))
if 'lane_lines' in outs:
self.parse_mdn('lane_lines', outs, in_N=0, out_N=0,
out_shape=(SplitModelConstants.NUM_LANE_LINES,SplitModelConstants.IDX_N,SplitModelConstants.LANE_LINES_WIDTH))
if 'lane_lines_prob' in outs:
self.parse_binary_crossentropy('lane_lines_prob', outs)
if 'lead_prob' in outs:
self.parse_binary_crossentropy('lead_prob', outs)
if 'lat_planner_solution' in outs:
self.parse_mdn('lat_planner_solution', outs, in_N=0, out_N=0, out_shape=(SplitModelConstants.IDX_N,SplitModelConstants.LAT_PLANNER_SOLUTION_WIDTH))
if 'meta' in outs:
self.parse_binary_crossentropy('meta', outs)
if 'road_edges' in outs:
self.parse_mdn('road_edges', outs, in_N=0, out_N=0,
out_shape=(SplitModelConstants.NUM_ROAD_EDGES,SplitModelConstants.IDX_N,SplitModelConstants.LANE_LINES_WIDTH))
if 'sim_pose' in outs:
self.parse_mdn('sim_pose', outs, in_N=0, out_N=0, out_shape=(SplitModelConstants.POSE_WIDTH,))
def parse_vision_outputs(self, outs: dict[str, np.ndarray]) -> dict[str, np.ndarray]:
self.parse_mdn('pose', outs, in_N=0, out_N=0, out_shape=(SplitModelConstants.POSE_WIDTH,))
self.parse_mdn('wide_from_device_euler', outs, in_N=0, out_N=0, out_shape=(SplitModelConstants.WIDE_FROM_DEVICE_WIDTH,))
self.parse_mdn('road_transform', outs, in_N=0, out_N=0, out_shape=(SplitModelConstants.POSE_WIDTH,))
self.parse_dynamic_outputs(outs)
self.split_outputs(outs)
return outs
def parse_policy_outputs(self, outs: dict[str, np.ndarray]) -> dict[str, np.ndarray]:
self.parse_dynamic_outputs(outs)
self.split_outputs(outs)
return outs
def parse_outputs(self, outs: dict[str, np.ndarray]) -> dict[str, np.ndarray]:
outs = self.parse_vision_outputs(outs)
outs = self.parse_policy_outputs(outs)
return outs
Whitespace-only changes.
@@ -0,0 +1,204 @@
"""
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
import pickle
import pytest
import openpilot.sunnypilot.models.helpers as helpers
import openpilot.sunnypilot.modeld_v2.modeld as modeld_module
from openpilot.sunnypilot.modeld_v2.constants import ModelConstants
from openpilot.sunnypilot.models.split_model_constants import SplitModelConstants
class DummyOverride:
def __init__(self, key, value):
self.key = key
self.value = value
class DummyArtifact:
def __init__(self, file_name):
self.fileName = file_name
class DummyModelType:
def __init__(self, raw):
self.raw = raw
class DummyModel:
def __init__(self, type_str, artifact_file):
self.type = DummyModelType(type_str)
self.artifact = DummyArtifact(artifact_file)
class DummyBundle:
def __init__(self, is_20hz=False, models=None, generation=10):
self.overrides = [DummyOverride('lat', '.1'), DummyOverride('long', '.3')]
self.generation = generation
self.is20hz = is_20hz
self.models = models or []
class Archetype:
def __init__(self, name, metadata_structure, model_stubs, is_20hz,
expected_model_type, expected_constants_class, expected_parser_module,
expected_desire_key):
self.name = name
self.metadata_structure = metadata_structure
self.model_stubs = model_stubs
self.is_20hz = is_20hz
self.expected_model_type = expected_model_type
self.expected_constants_class = expected_constants_class
self.expected_parser_module = expected_parser_module
self.expected_desire_key = expected_desire_key
def _noop_jit(**kwargs):
pass
def _make_vision_policy_metadata(vision_input_shapes, policy_input_shapes,
vision_output_slices, policy_output_slices):
return {
'vision': {'input_shapes': vision_input_shapes, 'output_slices': vision_output_slices},
'policy': {'input_shapes': policy_input_shapes, 'output_slices': policy_output_slices},
}
def _make_multi_policy_metadata(vision_input_shapes, policy_input_shapes,
vision_output_slices, policy_output_slices):
return {
'vision': {'input_shapes': vision_input_shapes, 'output_slices': vision_output_slices},
'offPolicy': {'input_shapes': policy_input_shapes, 'output_slices': policy_output_slices},
}
def _make_tri_policy_metadata(vision_input_shapes, policy_input_shapes,
vision_output_slices, policy_output_slices):
return {
'vision': {'input_shapes': vision_input_shapes, 'output_slices': vision_output_slices},
'onPolicy': {'input_shapes': policy_input_shapes, 'output_slices': policy_output_slices},
'offPolicy': {'input_shapes': policy_input_shapes, 'output_slices': policy_output_slices},
}
def _make_supercombo_metadata(input_shapes, output_slices):
return {'model': {'input_shapes': input_shapes, 'output_slices': output_slices}}
SPLIT_VISION_INPUT_SHAPES = {'img': (1, 12, 128, 256), 'big_img': (1, 12, 128, 256)}
SPLIT_POLICY_INPUT_SHAPES = {'features_buffer': (1, 25, 512), 'desire_pulse': (1, 25, 8), 'traffic_convention': (1, 2)}
SPLIT_VISION_SLICES = {'hidden_state': slice(0, 512), 'pose': slice(512, 524)}
SPLIT_POLICY_SLICES = {'plan': slice(0, 495), 'meta': slice(495, 550)}
SUPERCOMBO_INPUT_SHAPES = {
'img': (1, 12, 128, 256), 'big_img': (1, 12, 128, 256),
'desire': (1, 100, 8), 'features_buffer': (1, 99, 512),
'lateral_control_params': (1, 2), 'prev_desired_curv': (1, 100, 1),
'traffic_convention': (1, 2),
}
SUPERCOMBO_SLICES = {'plan': slice(0, 495), 'hidden_state': slice(495, 1007), 'meta': slice(1007, 1062)}
CAM_W, CAM_H = 1928, 1208
ARCHETYPES = {
'vision_policy_split': Archetype(
name='vision_policy_split',
metadata_structure=_make_vision_policy_metadata(
SPLIT_VISION_INPUT_SHAPES, SPLIT_POLICY_INPUT_SHAPES,
SPLIT_VISION_SLICES, SPLIT_POLICY_SLICES),
model_stubs=[DummyModel('vision', 'driving_test_tinygrad.pkl'),
DummyModel('policy', 'driving_test_tinygrad.pkl')],
is_20hz=True,
expected_model_type='split',
expected_constants_class=SplitModelConstants,
expected_parser_module='parse_model_outputs_split',
expected_desire_key='desire',
),
'vision_multi_policy': Archetype(
name='vision_multi_policy',
metadata_structure=_make_multi_policy_metadata(
SPLIT_VISION_INPUT_SHAPES, SPLIT_POLICY_INPUT_SHAPES,
SPLIT_VISION_SLICES, SPLIT_POLICY_SLICES),
model_stubs=[DummyModel('vision', 'driving_test_tinygrad.pkl'),
DummyModel('offPolicy', 'driving_test_tinygrad.pkl')],
is_20hz=True,
expected_model_type='multi_policy',
expected_constants_class=SplitModelConstants,
expected_parser_module='parse_model_outputs_split',
expected_desire_key='desire',
),
'tri_policy': Archetype(
name='tri_policy',
metadata_structure=_make_tri_policy_metadata(
SPLIT_VISION_INPUT_SHAPES, SPLIT_POLICY_INPUT_SHAPES,
SPLIT_VISION_SLICES, SPLIT_POLICY_SLICES),
model_stubs=[DummyModel('vision', 'driving_test_tinygrad.pkl'),
DummyModel('onPolicy', 'driving_test_tinygrad.pkl'),
DummyModel('offPolicy', 'driving_test_tinygrad.pkl')],
is_20hz=True,
expected_model_type='multi_policy',
expected_constants_class=SplitModelConstants,
expected_parser_module='parse_model_outputs_split',
expected_desire_key='desire',
),
'supercombo_non20hz': Archetype(
name='supercombo_non20hz',
metadata_structure=_make_supercombo_metadata(SUPERCOMBO_INPUT_SHAPES, SUPERCOMBO_SLICES),
model_stubs=[DummyModel('supercombo', 'driving_test_tinygrad.pkl')],
is_20hz=False,
expected_model_type='supercombo',
expected_constants_class=ModelConstants,
expected_parser_module='parse_model_outputs',
expected_desire_key='desire',
),
}
def make_pkl_data(archetype):
return {
'metadata': archetype.metadata_structure,
(CAM_W, CAM_H): {'run_policy': _noop_jit, 'warp_enqueue': _noop_jit},
}
def write_pkl(tmp_path, archetype):
pkl_path = tmp_path / 'driving_test_tinygrad.pkl'
with open(pkl_path, 'wb') as f:
pickle.dump(make_pkl_data(archetype), f)
return pkl_path
def make_bundle(archetype):
return DummyBundle(
models=archetype.model_stubs,
is_20hz=archetype.is_20hz,
)
@pytest.fixture
def patch_modeld(monkeypatch):
def _patch(bundle):
monkeypatch.setattr(helpers, 'get_active_bundle', lambda params=None: bundle, raising=False)
monkeypatch.setattr(modeld_module, 'get_active_bundle', lambda params=None: bundle, raising=False)
return _patch
@pytest.fixture
def model_state_factory(tmp_path, monkeypatch, patch_modeld):
from openpilot.common.hardware import hw
def _create(archetype):
write_pkl(tmp_path, archetype)
bundle = make_bundle(archetype)
patch_modeld(bundle)
monkeypatch.setattr(hw.Paths, 'model_root', staticmethod(lambda: str(tmp_path)))
return modeld_module.ModelState(cam_w=CAM_W, cam_h=CAM_H)
return _create
@@ -0,0 +1,84 @@
"""
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
import numpy as np
from openpilot.common.transformations.camera import DEVICE_CAMERAS
from openpilot.common.transformations.model import get_warp_matrix
from openpilot.sunnypilot.modeld_v2.camera_offset_helper import CameraOffsetHelper
class MockStruct:
def __init__(self, **kwargs):
for k, v in kwargs.items():
setattr(self, k, v)
def __getitem__(self, item):
return getattr(self, item)
class TestCameraOffset:
def setup_method(self):
self.camera_offset = CameraOffsetHelper()
self.dc = DEVICE_CAMERAS[('mici', 'os04c10')]
def test_smoothing(self):
self.camera_offset.set_offset(0.2)
sm = MockStruct(
deviceState=MockStruct(deviceType='mici'),
roadCameraState=MockStruct(sensor='os04c10'),
liveCalibration=MockStruct(rpyCalib=[0.0, 0.0, 0.0], height=[1.22])
)
intrinsics_main = self.dc.fcam.intrinsics
intrinsics_extra = self.dc.ecam.intrinsics
device_from_calib_euler = np.array([0.0, 0.0, 0.0], dtype=np.float32)
main_transform = get_warp_matrix(device_from_calib_euler, intrinsics_main, False).astype(np.float32)
extra_transform = get_warp_matrix(device_from_calib_euler, intrinsics_extra, True).astype(np.float32)
self.camera_offset.update(main_transform, extra_transform, sm, False)
np.testing.assert_almost_equal(self.camera_offset.actual_camera_offset, 0.02)
self.camera_offset.update(main_transform, extra_transform, sm, False)
np.testing.assert_almost_equal(self.camera_offset.actual_camera_offset, 0.038)
def test_camera_offset_(self):
intrinsics = self.dc.fcam.intrinsics
transform = np.eye(3, dtype=np.float32)
height = 1.22
offset = 0.1
cy = intrinsics[1, 2]
expected_shear = np.eye(3, dtype=np.float32)
expected_shear[0, 1] = offset / height
expected_shear[0, 2] = -offset / height * cy
result = CameraOffsetHelper.apply_camera_offset(transform, intrinsics, height, offset)
np.testing.assert_array_almost_equal(result, expected_shear)
def test_update(self):
sm = MockStruct(
deviceState=MockStruct(deviceType='mici'),
roadCameraState=MockStruct(sensor='os04c10'),
liveCalibration=MockStruct(rpyCalib=[0.0, 0.0, 0.0], height=[1.22])
)
intrinsics_main = self.dc.fcam.intrinsics
intrinsics_extra = self.dc.ecam.intrinsics
device_from_calib_euler = np.array([0.0, 0.0, 0.0], dtype=np.float32)
main_transform = get_warp_matrix(device_from_calib_euler, intrinsics_main, False).astype(np.float32)
extra_transform = get_warp_matrix(device_from_calib_euler, intrinsics_extra, True).astype(np.float32)
self.camera_offset.set_offset(0.0) # test default offset doesn't change transformation
main_out, extra_out = self.camera_offset.update(main_transform, extra_transform, sm, False)
np.testing.assert_array_equal(main_out, main_transform)
np.testing.assert_array_equal(extra_out, extra_transform)
self.camera_offset.set_offset(0.2) # test valid offset changes transformation
main_out, extra_out = self.camera_offset.update(main_transform, extra_transform, sm, False)
assert not np.array_equal(main_out, main_transform)
assert not np.array_equal(extra_out, extra_transform)
assert main_out[0, 1] != 0.0
assert main_out[0, 2] != 0.0
@@ -0,0 +1,260 @@
"""
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
import pytest
import openpilot.sunnypilot.models.helpers as helpers
import openpilot.sunnypilot.modeld_v2.modeld as modeld_module
from openpilot.sunnypilot.modeld_v2.modeld import _find_driving_pkl
from openpilot.sunnypilot.modeld_v2.tests.conftest import DummyModel, DummyBundle, ARCHETYPES, CAM_W, CAM_H, \
SPLIT_VISION_INPUT_SHAPES, SPLIT_POLICY_INPUT_SHAPES
ModelState = modeld_module.ModelState
# Pkl discovery
class TestFindDrivingPkl:
def test_returns_none_when_no_bundle(self):
assert _find_driving_pkl(None) is None
def test_returns_none_when_no_models(self):
bundle = DummyBundle(models=[])
assert _find_driving_pkl(bundle) is None
def test_returns_none_when_pkl_not_on_disk(self):
bundle = DummyBundle(models=[
DummyModel('vision', 'driving_fof_tinygrad.pkl'),
DummyModel('policy', 'driving_fof_tinygrad.pkl'),
])
assert _find_driving_pkl(bundle) is None
def test_finds_pkl_by_artifact_name(self, tmp_path, monkeypatch):
(tmp_path / 'driving_fof_tinygrad.pkl').write_bytes(b'fake')
from openpilot.common.hardware import hw
monkeypatch.setattr(hw.Paths, 'model_root', staticmethod(lambda: str(tmp_path)))
bundle = DummyBundle(models=[
DummyModel('vision', 'driving_fof_tinygrad.pkl'),
DummyModel('policy', 'driving_fof_tinygrad.pkl'),
])
result = _find_driving_pkl(bundle)
assert result is not None
assert 'driving_fof_tinygrad.pkl' in result
# Init — assertion guard
class TestModelStateCombinedInit:
def test_asserts_when_no_pkl(self, monkeypatch):
bundle = DummyBundle(models=[], is_20hz=True)
monkeypatch.setattr(helpers, 'get_active_bundle', lambda params=None: bundle, raising=False)
monkeypatch.setattr(modeld_module, 'get_active_bundle', lambda params=None: bundle, raising=False)
with pytest.raises(AssertionError, match="No driving pkl found"):
ModelState(cam_w=CAM_W, cam_h=CAM_H)
class TestStockEquivalence:
def test_split_queue_keys_match_stock(self, model_state_factory):
from openpilot.selfdrive.modeld.compile_modeld import make_input_queues
from openpilot.sunnypilot.modeld_v2.compile_modeld import derive_frame_skip
state = model_state_factory(ARCHETYPES['vision_policy_split'])
frame_skip = derive_frame_skip(SPLIT_VISION_INPUT_SHAPES, SPLIT_POLICY_INPUT_SHAPES)
# action_t is a deep-model prerequisite the SP loader doesn't provide yet; see skip_keys below
stock_shapes = {**SPLIT_VISION_INPUT_SHAPES, **SPLIT_POLICY_INPUT_SHAPES, 'action_t': (1, 2)}
stock_queues, stock_npy = make_input_queues(stock_shapes, frame_skip, device='NPY')
# TODO-SP: remove action_t skip once SP adds prerequisite for deep models (action_t input queue)
# prev_feat is a stock QCOM corruption workaround handled inside the SP loader's JIT path
skip_keys = {'action_t', 'prev_feat'}
# stock packs the per-key policy inputs into packed_npy_inputs; the npy views carry the individual keys
stock_queue_keys = set(stock_queues.keys())
if 'packed_npy_inputs' in stock_queue_keys:
stock_queue_keys.remove('packed_npy_inputs')
stock_queue_keys |= set(stock_npy.keys())
assert set(state.input_queues.keys()) == stock_queue_keys - skip_keys, \
f"Queue keys differ: v2={set(state.input_queues.keys())}, stock={stock_queue_keys}"
assert set(state.numpy_inputs.keys()) == set(stock_npy.keys()) - skip_keys, \
f"Npy keys differ: v2={set(state.numpy_inputs.keys())}, stock={set(stock_npy.keys())}"
def test_split_queue_keys_work_with_desire_key(self, model_state_factory):
from openpilot.sunnypilot.modeld_v2.compile_modeld import derive_frame_skip, make_split_input_queues
policy_shapes_desire = {'features_buffer': (1, 25, 512), 'desire': (1, 25, 8), 'traffic_convention': (1, 2)}
frame_skip = derive_frame_skip(SPLIT_VISION_INPUT_SHAPES, policy_shapes_desire)
queues, npy = make_split_input_queues(SPLIT_VISION_INPUT_SHAPES, policy_shapes_desire, frame_skip, device='NPY')
assert 'desire_q' in queues
assert 'desire' in npy
assert 'img_q' in queues
assert 'feat_q' in queues
def test_split_vision_input_names_match_stock(self, model_state_factory):
state = model_state_factory(ARCHETYPES['vision_policy_split'])
assert state.vision_input_names == ['img', 'big_img']
def test_split_output_slices_preserved(self, model_state_factory):
arch = ARCHETYPES['vision_policy_split']
state = model_state_factory(arch)
assert state.vision_output_slices == arch.metadata_structure['vision']['output_slices']
assert state.policy_output_slices == arch.metadata_structure['policy']['output_slices']
ARCHETYPE_NAMES = list(ARCHETYPES.keys())
class TestModelTypeDetection:
@pytest.mark.parametrize("archetype_name", ARCHETYPE_NAMES)
def test_combined_model_type(self, archetype_name, model_state_factory):
arch = ARCHETYPES[archetype_name]
state = model_state_factory(arch)
assert state._combined_model_type == arch.expected_model_type, \
f"{arch.name}: got {state._combined_model_type}, expected {arch.expected_model_type}"
class TestConstantsSelection:
@pytest.mark.parametrize("archetype_name", ARCHETYPE_NAMES)
def test_constants_class(self, archetype_name, model_state_factory):
arch = ARCHETYPES[archetype_name]
state = model_state_factory(arch)
assert type(state.constants) is arch.expected_constants_class, \
f"{arch.name}: got {type(state.constants).__name__}, expected {arch.expected_constants_class.__name__}"
class TestParserSelection:
@pytest.mark.parametrize("archetype_name", ARCHETYPE_NAMES)
def test_parser_module(self, archetype_name, model_state_factory):
arch = ARCHETYPES[archetype_name]
state = model_state_factory(arch)
parser_module = type(state.parser).__module__
assert parser_module.endswith(arch.expected_parser_module), \
f"{arch.name}: parser from {parser_module}, expected module ending with {arch.expected_parser_module}"
class TestDesireKeyDetection:
@pytest.mark.parametrize("archetype_name", ARCHETYPE_NAMES)
def test_desire_key(self, archetype_name, model_state_factory):
arch = ARCHETYPES[archetype_name]
state = model_state_factory(arch)
assert state.desire_key == arch.expected_desire_key, \
f"{arch.name}: got {state.desire_key}, expected {arch.expected_desire_key}"
class TestVisionInputNames:
@pytest.mark.parametrize("archetype_name", ARCHETYPE_NAMES)
def test_vision_names_contain_img(self, archetype_name, model_state_factory):
arch = ARCHETYPES[archetype_name]
state = model_state_factory(arch)
assert len(state.vision_input_names) >= 1
for name in state.vision_input_names:
assert 'img' in name, f"{arch.name}: vision input name '{name}' missing 'img'"
class TestOutputSlices:
@pytest.mark.parametrize("archetype_name", ARCHETYPE_NAMES)
def test_vision_slices_populated(self, archetype_name, model_state_factory):
arch = ARCHETYPES[archetype_name]
state = model_state_factory(arch)
assert len(state.vision_output_slices) > 0, f"{arch.name}: vision_output_slices empty"
@pytest.mark.parametrize("archetype_name", ARCHETYPE_NAMES)
def test_policy_slices_match_type(self, archetype_name, model_state_factory):
arch = ARCHETYPES[archetype_name]
state = model_state_factory(arch)
if arch.expected_model_type == 'supercombo':
assert state.policy_output_slices == {}, f"{arch.name}: supercombo should have empty policy slices"
else:
assert len(state.policy_output_slices) > 0, f"{arch.name}: split/multi should have policy slices"
class TestInputQueueCreation:
@pytest.mark.parametrize("archetype_name", ARCHETYPE_NAMES)
def test_queues_not_empty(self, archetype_name, model_state_factory):
arch = ARCHETYPES[archetype_name]
state = model_state_factory(arch)
assert len(state.input_queues) > 0, f"{arch.name}: input_queues empty"
@pytest.mark.parametrize("archetype_name", ARCHETYPE_NAMES)
def test_npy_contains_transforms(self, archetype_name, model_state_factory):
arch = ARCHETYPES[archetype_name]
state = model_state_factory(arch)
assert 'tfm' in state.numpy_inputs, f"{arch.name}: 'tfm' missing from npy"
assert 'big_tfm' in state.numpy_inputs, f"{arch.name}: 'big_tfm' missing from npy"
assert state.numpy_inputs['tfm'].shape == (3, 3)
assert state.numpy_inputs['big_tfm'].shape == (3, 3)
@pytest.mark.parametrize("archetype_name", ARCHETYPE_NAMES)
def test_npy_contains_desire(self, archetype_name, model_state_factory):
arch = ARCHETYPES[archetype_name]
state = model_state_factory(arch)
assert arch.expected_desire_key in state.numpy_inputs, \
f"{arch.name}: '{arch.expected_desire_key}' missing from npy"
class TestFrameBufferParams:
@pytest.mark.parametrize("archetype_name", ARCHETYPE_NAMES)
def test_frame_buf_params_per_vision_input(self, archetype_name, model_state_factory):
arch = ARCHETYPES[archetype_name]
state = model_state_factory(arch)
for name in state.vision_input_names:
assert name in state.frame_buf_params, f"{arch.name}: frame_buf_params missing '{name}'"
nv12_info = state.frame_buf_params[name]
assert len(nv12_info) >= 4, f"{arch.name}: nv12_info for '{name}' too short"
class TestBundleOverrides:
@pytest.mark.parametrize("archetype_name", ARCHETYPE_NAMES)
def test_smoothing_params_from_overrides(self, archetype_name, model_state_factory):
arch = ARCHETYPES[archetype_name]
state = model_state_factory(arch)
assert state.LAT_SMOOTH_SECONDS == 0.1
assert state.LONG_SMOOTH_SECONDS == 0.3
@pytest.mark.parametrize("archetype_name", ARCHETYPE_NAMES)
def test_generation_from_bundle(self, archetype_name, model_state_factory):
arch = ARCHETYPES[archetype_name]
state = model_state_factory(arch)
assert state.generation == 10
class TestMlsimProperty:
def test_mlsim_false_for_gen10(self, model_state_factory):
state = model_state_factory(ARCHETYPES['supercombo_non20hz'])
assert state.mlsim is False
def test_mlsim_true_for_gen11(self, tmp_path, monkeypatch, patch_modeld):
from openpilot.common.hardware import hw
from openpilot.sunnypilot.modeld_v2.tests.conftest import write_pkl, ARCHETYPES as A
arch = A['supercombo_non20hz']
write_pkl(tmp_path, arch)
bundle = DummyBundle(models=arch.model_stubs, is_20hz=arch.is_20hz, generation=11)
patch_modeld(bundle)
monkeypatch.setattr(hw.Paths, 'model_root', staticmethod(lambda: str(tmp_path)))
state = ModelState(cam_w=CAM_W, cam_h=CAM_H)
assert state.mlsim is True
class TestCrossArchetypeMismatch:
def test_wrong_is_20hz_changes_constants(self, tmp_path, monkeypatch, patch_modeld):
from openpilot.common.hardware import hw
from openpilot.sunnypilot.modeld_v2.tests.conftest import write_pkl
from openpilot.sunnypilot.modeld_v2.constants import ModelConstants
arch = ARCHETYPES['vision_policy_split']
write_pkl(tmp_path, arch)
bundle = DummyBundle(models=arch.model_stubs, is_20hz=False)
patch_modeld(bundle)
monkeypatch.setattr(hw.Paths, 'model_root', staticmethod(lambda: str(tmp_path)))
state = ModelState(cam_w=CAM_W, cam_h=CAM_H)
assert type(state.constants) is ModelConstants, \
"Wrong is_20hz should produce wrong constants class"
@@ -0,0 +1,161 @@
"""
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
import numpy as np
import pytest
from openpilot.sunnypilot.modeld_v2.compile_modeld import derive_frame_skip, _detect_desire_key
class TestDeriveFrameSkip:
def test_non20hz_supercombo(self):
vision = {}
policy = {'features_buffer': (1, 99, 512), 'desire': (1, 100, 8)}
assert derive_frame_skip(vision, policy) == 1
def test_20hz_supercombo(self):
vision = {}
policy = {'features_buffer': (1, 24, 512), 'desire': (1, 25, 8)}
assert derive_frame_skip(vision, policy) == 4
def test_split_vision_policy(self):
vision = {'img': (1, 12, 128, 256)}
policy = {'features_buffer': (1, 25, 512), 'desire_pulse': (1, 25, 8)}
assert derive_frame_skip(vision, policy) == 4
def test_no_features_buffer(self):
assert derive_frame_skip({}, {}) == 1
class TestFrameSkipBufferLengthEquivalence:
@pytest.mark.parametrize("frame_skip,expected_buffer_length", [
(1, 2),
(4, 5),
])
def test_img_buffer_size_matches_warp_buffer_length(self, frame_skip, expected_buffer_length):
n_frames = 2
img_buf_dim0 = frame_skip * (n_frames - 1) + 1
assert img_buf_dim0 == expected_buffer_length, \
f"frame_skip={frame_skip}: img_buf[0]={img_buf_dim0}, expected {expected_buffer_length}"
@pytest.mark.parametrize("is_20hz,expected_frame_skip,expected_buffer_length", [
(False, 1, 2),
(True, 4, 5),
])
def test_is_20hz_to_frame_skip_to_buffer_length(self, is_20hz, expected_frame_skip, expected_buffer_length):
if is_20hz:
policy_shapes = {'features_buffer': (1, 24, 512)}
else:
policy_shapes = {'features_buffer': (1, 99, 512)}
frame_skip = derive_frame_skip({}, policy_shapes)
assert frame_skip == expected_frame_skip
n_frames = 2
img_buf_dim0 = frame_skip * (n_frames - 1) + 1
assert img_buf_dim0 == expected_buffer_length
class TestTemporalSamplingEquivalence:
def test_non20hz_desire_sampling_identity(self):
buf = np.random.randn(100, 1, 8).astype(np.float32)
frame_skip = 1
sampled = buf[::frame_skip].reshape(-1, 8)
assert sampled.shape == (100, 8)
np.testing.assert_array_equal(sampled, buf[:, 0, :])
def test_20hz_desire_sampling_max(self):
buf = np.zeros((100, 1, 8), dtype=np.float32)
buf[99, 0, 3] = 1.0
frame_skip = 4
reshaped = buf.reshape(-1, frame_skip, 1, 8).max(axis=1)
sampled = reshaped.reshape(-1, 8)
assert sampled.shape == (25, 8)
assert sampled[24, 3] == 1.0
assert sampled[23, 3] == 0.0
def test_split_features_buffer_sampling_skip(self):
buf = np.arange(100 * 512, dtype=np.float32).reshape(100, 1, 512)
frame_skip = 4
sampled = buf[::frame_skip].reshape(-1, 512)
assert sampled.shape == (25, 512)
np.testing.assert_array_equal(sampled[0], buf[0, 0])
np.testing.assert_array_equal(sampled[1], buf[4, 0])
np.testing.assert_array_equal(sampled[24], buf[96, 0])
def test_non20hz_features_buffer_sampling_identity(self):
buf = np.arange(99 * 512, dtype=np.float32).reshape(99, 1, 512)
frame_skip = 1
sampled = buf[::frame_skip].reshape(-1, 512)
assert sampled.shape == (99, 512)
np.testing.assert_array_equal(sampled, buf[:, 0, :])
class TestTemporalIdxEquivalence:
@pytest.mark.parametrize("mode,desire_shape,fb_shape,frame_skip", [
('non20hz', (1, 100, 8), (1, 99, 512), 1),
('20hz', (1, 25, 8), (1, 24, 512), 4),
('split', (1, 25, 8), (1, 25, 512), 4),
])
def test_features_buffer_idx_equivalence(self, mode, desire_shape, fb_shape, frame_skip):
history = fb_shape[1]
if mode == 'non20hz':
modelstate_idxs = np.arange(history)
buf_len = history
elif mode == '20hz':
buf_len = (history + 1) * 4
step = int(-buf_len / history)
modelstate_idxs = np.arange(step, step * (history + 1), step)[::-1]
elif mode == 'split':
buf_len = history * 4
skip = buf_len // history
modelstate_idxs = np.arange(buf_len)[-1 - (skip * (history - 1))::skip]
assert len(modelstate_idxs) == fb_shape[1], \
f"{mode}: ModelState idx count {len(modelstate_idxs)} != input shape {fb_shape[1]}"
@pytest.mark.parametrize("mode,desire_shape,fb_shape,frame_skip", [
('non20hz', (1, 100, 8), (1, 99, 512), 1),
('20hz', (1, 25, 8), (1, 24, 512), 4),
('split', (1, 25, 8), (1, 25, 512), 4),
])
def test_desire_idx_equivalence(self, mode, desire_shape, fb_shape, frame_skip):
history = desire_shape[1]
compile_desire_buf_len = frame_skip * history if mode != 'non20hz' else history
compile_sampled_count = compile_desire_buf_len // frame_skip if frame_skip > 1 else compile_desire_buf_len
assert compile_sampled_count == history, \
f"{mode}: compile desire samples {compile_sampled_count} != model input {history}"
class TestDetectDesireKey:
def test_finds_desire(self):
shapes = {'features_buffer': (1, 99, 512), 'desire': (1, 100, 8), 'traffic_convention': (1, 2)}
assert _detect_desire_key(shapes) == 'desire'
def test_finds_desire_pulse(self):
shapes = {'features_buffer': (1, 25, 512), 'desire_pulse': (1, 25, 8), 'traffic_convention': (1, 2)}
assert _detect_desire_key(shapes) == 'desire_pulse'
def test_returns_none_when_no_desire(self):
shapes = {'features_buffer': (1, 99, 512), 'traffic_convention': (1, 2)}
assert _detect_desire_key(shapes) is None
class TestOutputSlicePreservation:
def test_vision_hidden_state_slice_used_for_features(self):
mock_slices = {'hidden_state': slice(0, 512), 'plan': slice(512, 1024)}
features_slice = mock_slices['hidden_state']
fake_output = np.random.randn(1, 1024).astype(np.float32)
features = fake_output[:, features_slice]
assert features.shape == (1, 512)
def test_policy_output_slices_independent(self):
vision_slices = {'hidden_state': slice(0, 512)}
policy_slices = {'plan': slice(0, 495), 'meta': slice(495, 550)}
assert set(vision_slices.keys()) & set(policy_slices.keys()) == set(), \
"vision and policy slices should not overlap in keys"
@@ -0,0 +1,70 @@
import numpy as np
from openpilot.cereal import log
from openpilot.sunnypilot.modeld_v2.constants import Plan
from openpilot.sunnypilot.modeld_v2.modeld import ModelState
import openpilot.sunnypilot.modeld_v2.modeld as modeld
class MockStruct:
def __init__(self, **kwargs):
for k, v in kwargs.items():
setattr(self, k, v)
def test_recovery_power_scaling():
state = MockStruct(
PLANPLUS_CONTROL=0.75,
LONG_SMOOTH_SECONDS=0.3,
LAT_SMOOTH_SECONDS=0.1,
MIN_LAT_CONTROL_SPEED=0.3,
mlsim=True,
generation=12,
constants=MockStruct(T_IDXS=np.arange(100), DESIRE_LEN=8)
)
prev_action = log.ModelDataV2.Action()
recorded_vel: list = []
recorded_curv_plans: list = []
def mock_accel(plan_vel, plan_accel, t_idxs, action_t=0.0):
recorded_vel.append(plan_vel.copy())
return 0.0, False
def mock_curvature(output, plan, vego, lat_action_t, mlsim):
recorded_curv_plans.append(plan.copy())
return 0.0
modeld.get_accel_from_plan = mock_accel
modeld.get_curvature_from_output = mock_curvature
plan = np.random.rand(1, 100, 15).astype(np.float32)
planplus = np.random.rand(1, 100, 15).astype(np.float32)
merged_plan = plan + planplus
model_output: dict = {
'plan': merged_plan.copy(),
'planplus': planplus.copy()
}
test_cases: list = [
# (control, v_ego)
(0.55, 20.0),
(1.0, 25.0),
(1.5, 25.1),
(2.0, 20.0),
(0.75, 19.0),
(0.8, 25.1),
]
for control, v_ego in test_cases:
state.PLANPLUS_CONTROL = control
recorded_vel.clear()
recorded_curv_plans.clear()
ModelState.get_action_from_model(state, model_output, prev_action, 0.0, 0.0, v_ego)
expected_accel_plan_vel = plan[0, :, Plan.VELOCITY][:, 0] + planplus[0, :, Plan.VELOCITY][:, 0]
np.testing.assert_allclose(recorded_vel[0], expected_accel_plan_vel, rtol=1e-5, atol=1e-6)
# For the below, yes, I know this isn't the same slicing as fillmodlmsg. This is to show that the values are only scaled on curv
expected_curv_plan_vel = plan[0, :, Plan.VELOCITY][:, 0] + control * planplus[0, :, Plan.VELOCITY][:, 0]
np.testing.assert_allclose(recorded_curv_plans[0][:, Plan.VELOCITY][:, 0], expected_curv_plan_vel, rtol=1e-5, atol=1e-6)
@@ -0,0 +1,103 @@
import os
os.environ['DEV'] = 'CPU'
import pytest
import numpy as np
from openpilot.system.camerad.cameras.nv12_info import get_nv12_info
from openpilot.sunnypilot.modeld_v2.warp import CAMERA_CONFIGS
from openpilot.sunnypilot.modeld_v2.warp import Warp, MODEL_W, MODEL_H
VISION_NAME_PAIRS = [ # needed to account for supercombos input_imgs
('img', 'big_img'),
('input_imgs', 'big_input_imgs'),
]
class MockVisionBuf:
def __init__(self, w, h):
self.width = w
self.height = h
_, _, _, yuv_size = get_nv12_info(w, h)
self.data = np.zeros(yuv_size, dtype=np.uint8)
@pytest.mark.parametrize("buffer_length", [2, 5])
def test_warp_initialization(buffer_length):
warp = Warp(buffer_length)
assert warp.buffer_length == buffer_length
assert warp.img_buffer_shape == (buffer_length * 6, MODEL_H // 2, MODEL_W // 2)
@pytest.mark.parametrize("buffer_length", [2, 5])
@pytest.mark.parametrize("cam_w, cam_h", CAMERA_CONFIGS)
@pytest.mark.parametrize("road, wide", VISION_NAME_PAIRS)
def test_warp_process(buffer_length, cam_w, cam_h, road, wide):
warp = Warp(buffer_length)
mock_buf = MockVisionBuf(cam_w, cam_h)
transform = np.eye(3, dtype=np.float32).flatten()
bufs = {road: mock_buf, wide: mock_buf}
transforms = {road: transform, wide: transform}
out = warp.process(bufs, transforms)
assert isinstance(out, dict)
assert road in out and wide in out
assert out[road].shape == (1, 12, MODEL_H // 2, MODEL_W // 2)
assert out[wide].shape == (1, 12, MODEL_H // 2, MODEL_W // 2)
key = (cam_w, cam_h)
assert key in warp.jit_cache
out2 = warp.process(bufs, transforms)
assert out2[road].shape == out[road].shape
@pytest.mark.parametrize("road, wide", VISION_NAME_PAIRS)
def test_warp_buffer_shift(road, wide):
warp = Warp(2)
cam_w, cam_h = CAMERA_CONFIGS[1]
transform = np.eye(3, dtype=np.float32).flatten()
buf1 = MockVisionBuf(cam_w, cam_h)
buf1.data[0] = 255
bufs1 = {road: buf1, wide: buf1}
transforms = {road: transform, wide: transform}
out1 = warp.process(bufs1, transforms)
road1 = out1[road].numpy().copy()
buf2 = MockVisionBuf(cam_w, cam_h)
buf2.data[0] = 128
bufs2 = {road: buf2, wide: buf2}
out2 = warp.process(bufs2, transforms)
assert not np.array_equal(road1, out2[road].numpy())
@pytest.mark.parametrize("buffer_length", [2, 5])
@pytest.mark.parametrize("road, wide", VISION_NAME_PAIRS)
def test_warp_buffer_accumulation(buffer_length, road, wide):
warp = Warp(buffer_length)
cam_w, cam_h = CAMERA_CONFIGS[0]
transform = np.eye(3, dtype=np.float32).flatten()
transforms = {road: transform, wide: transform}
outputs = []
for i in range(buffer_length + 1):
buf = MockVisionBuf(cam_w, cam_h)
buf.data[:] = i * 10
out = warp.process({road: buf, wide: buf}, transforms)
outputs.append(out[road].numpy().copy())
assert warp.full_buffers['img'].shape == (buffer_length * 6, MODEL_H // 2, MODEL_W // 2)
for i in range(1, len(outputs)):
assert not np.array_equal(outputs[i - 1], outputs[i])
def test_warp_different_cameras_same_instance():
warp = Warp(2)
transform = np.eye(3, dtype=np.float32).flatten()
buf1 = MockVisionBuf(*CAMERA_CONFIGS[0])
warp.process({'img': buf1, 'big_img': buf1}, {'img': transform, 'big_img': transform})
assert len(warp.jit_cache) == 1
buf2 = MockVisionBuf(*CAMERA_CONFIGS[1])
warp.process({'img': buf2, 'big_img': buf2}, {'img': transform, 'big_img': transform})
assert len(warp.jit_cache) == 2
+171
View File
@@ -0,0 +1,171 @@
import pickle
import time
import numpy as np
from pathlib import Path
from tinygrad.tensor import Tensor
from tinygrad.engine.jit import TinyJit
from tinygrad.device import Device
from openpilot.system.camerad.cameras.nv12_info import get_nv12_info
from openpilot.common.transformations.model import MEDMODEL_INPUT_SIZE
from openpilot.common.transformations.camera import _ar_ox_fisheye, _os_fisheye
from openpilot.selfdrive.modeld.compile_modeld import NV12Frame, make_frame_prepare as _make_frame_prepare
CAMERA_CONFIGS = [
(_ar_ox_fisheye.width, _ar_ox_fisheye.height),
(_os_fisheye.width, _os_fisheye.height),
]
def make_frame_prepare(cam_w, cam_h, model_w, model_h):
nv12 = NV12Frame(cam_w, cam_h, *get_nv12_info(cam_w, cam_h))
return _make_frame_prepare(nv12, model_w, model_h)
def warp_pkl_path(w, h):
from openpilot.selfdrive.modeld.helpers import MODELS_DIR
return MODELS_DIR / f'warp_{w}x{h}_tinygrad.pkl'
def make_update_img_input(frame_prepare, model_w, model_h):
def update_img_input_tinygrad(tensor, frame, M_inv):
M_inv = M_inv.to(Device.DEFAULT)
new_img = frame_prepare(frame, M_inv)
tensor.assign(tensor[6:].cat(new_img, dim=0).contiguous())
return Tensor.cat(tensor[:6], tensor[-6:], dim=0).contiguous().reshape(1, 12, model_h//2, model_w//2)
return update_img_input_tinygrad
def make_update_both_imgs(frame_prepare, model_w, model_h):
update_img = make_update_img_input(frame_prepare, model_w, model_h)
def update_both_imgs_tinygrad(calib_img_buffer, new_img, M_inv,
calib_big_img_buffer, new_big_img, M_inv_big):
calib_img_pair = update_img(calib_img_buffer, new_img, M_inv)
calib_big_img_pair = update_img(calib_big_img_buffer, new_big_img, M_inv_big)
return calib_img_pair, calib_big_img_pair
return update_both_imgs_tinygrad
MODELS_DIR = Path(__file__).parent / 'models'
MODEL_W, MODEL_H = MEDMODEL_INPUT_SIZE
UPSTREAM_BUFFER_LENGTH = 5
def v2_warp_pkl_path(cam_w, cam_h, buffer_length):
return MODELS_DIR / f'warp_{cam_w}x{cam_h}_b{buffer_length}_tinygrad.pkl'
def compile_v2_warp(cam_w, cam_h, buffer_length):
_, _, _, yuv_size = get_nv12_info(cam_w, cam_h)
img_buffer_shape = (buffer_length * 6, MODEL_H // 2, MODEL_W // 2)
print(f"Compiling v2 warp for {cam_w}x{cam_h} buffer_length={buffer_length}...")
frame_prepare = make_frame_prepare(cam_w, cam_h, MODEL_W, MODEL_H)
update_both_imgs = make_update_both_imgs(frame_prepare, MODEL_W, MODEL_H)
update_img_jit = TinyJit(update_both_imgs, prune=True)
full_buffer = Tensor.zeros(img_buffer_shape, dtype='uint8').contiguous().realize()
big_full_buffer = Tensor.zeros(img_buffer_shape, dtype='uint8').contiguous().realize()
new_frame_np = np.random.randint(0, 256, yuv_size, dtype=np.uint8)
new_big_frame_np = np.random.randint(0, 256, yuv_size, dtype=np.uint8)
for i in range(10):
img_inputs = [full_buffer,
Tensor.from_blob(new_frame_np.ctypes.data, (yuv_size,), dtype='uint8').realize(),
Tensor(Tensor.randn(3, 3).mul(8).realize().numpy(), device='NPY')]
big_img_inputs = [big_full_buffer,
Tensor.from_blob(new_big_frame_np.ctypes.data, (yuv_size,), dtype='uint8').realize(),
Tensor(Tensor.randn(3, 3).mul(8).realize().numpy(), device='NPY')]
inputs = img_inputs + big_img_inputs
Device.default.synchronize()
st = time.perf_counter()
_ = update_img_jit(*inputs)
mt = time.perf_counter()
Device.default.synchronize()
et = time.perf_counter()
print(f" [{i+1}/10] enqueue {(mt-st)*1e3:6.2f} ms -- total {(et-st)*1e3:6.2f} ms")
pkl_path = v2_warp_pkl_path(cam_w, cam_h, buffer_length)
with open(pkl_path, "wb") as f:
pickle.dump(update_img_jit, f)
print(f" Saved to {pkl_path}")
jit = pickle.load(open(pkl_path, "rb"))
verify_frame = np.random.randint(0, 256, yuv_size, dtype=np.uint8)
verify_big_frame = np.random.randint(0, 256, yuv_size, dtype=np.uint8)
fresh_inputs = [
Tensor.zeros(img_buffer_shape, dtype='uint8').contiguous().realize(),
Tensor.from_blob(verify_frame.ctypes.data, (yuv_size,), dtype='uint8').realize(),
Tensor(Tensor.randn(3, 3).mul(8).realize().numpy(), device='NPY'),
Tensor.zeros(img_buffer_shape, dtype='uint8').contiguous().realize(),
Tensor.from_blob(verify_big_frame.ctypes.data, (yuv_size,), dtype='uint8').realize(),
Tensor(Tensor.randn(3, 3).mul(8).realize().numpy(), device='NPY'),
]
jit(*fresh_inputs)
class Warp:
def __init__(self, buffer_length=2):
self.buffer_length = buffer_length
self.img_buffer_shape = (buffer_length * 6, MODEL_H // 2, MODEL_W // 2)
self.jit_cache = {}
self.full_buffers = {k: Tensor.zeros(self.img_buffer_shape, dtype='uint8').contiguous().realize() for k in ['img', 'big_img']}
self._blob_cache: dict[int, Tensor] = {}
self._nv12_cache: dict[tuple[int, int], int] = {}
self.transforms_np = {k: np.zeros((3, 3), dtype=np.float32) for k in ['img', 'big_img']}
self.transforms = {k: Tensor(v, device='NPY').realize() for k, v in self.transforms_np.items()}
def process(self, bufs, transforms):
if not bufs:
return {}
road = next(n for n in bufs if 'big' not in n)
wide = next(n for n in bufs if 'big' in n)
cam_w, cam_h = bufs[road].width, bufs[road].height
key = (cam_w, cam_h)
if key not in self.jit_cache:
v2_pkl = v2_warp_pkl_path(cam_w, cam_h, self.buffer_length)
if v2_pkl.exists():
with open(v2_pkl, 'rb') as f:
self.jit_cache[key] = pickle.load(f)
elif self.buffer_length == UPSTREAM_BUFFER_LENGTH:
upstream_pkl = warp_pkl_path(cam_w, cam_h)
if upstream_pkl.exists():
with open(upstream_pkl, 'rb') as f:
self.jit_cache[key] = pickle.load(f)
if key not in self.jit_cache:
frame_prepare = make_frame_prepare(cam_w, cam_h, MODEL_W, MODEL_H)
update_both_imgs = make_update_both_imgs(frame_prepare, MODEL_W, MODEL_H)
self.jit_cache[key] = TinyJit(update_both_imgs, prune=True)
if key not in self._nv12_cache:
self._nv12_cache[key] = get_nv12_info(cam_w, cam_h)[3]
yuv_size = self._nv12_cache[key]
road_ptr = bufs[road].data.ctypes.data
wide_ptr = bufs[wide].data.ctypes.data
if road_ptr not in self._blob_cache:
self._blob_cache[road_ptr] = Tensor.from_blob(road_ptr, (yuv_size,), dtype='uint8')
if wide_ptr not in self._blob_cache:
self._blob_cache[wide_ptr] = Tensor.from_blob(wide_ptr, (yuv_size,), dtype='uint8')
road_blob = self._blob_cache[road_ptr]
wide_blob = self._blob_cache[wide_ptr] if wide_ptr != road_ptr else Tensor.from_blob(wide_ptr, (yuv_size,), dtype='uint8')
np.copyto(self.transforms_np['img'], transforms[road].reshape(3, 3))
np.copyto(self.transforms_np['big_img'], transforms[wide].reshape(3, 3))
Device.default.synchronize()
res = self.jit_cache[key](
self.full_buffers['img'], road_blob, self.transforms['img'],
self.full_buffers['big_img'], wide_blob, self.transforms['big_img'],
)
out_road = res[0].realize()
out_wide = res[1].realize()
return {road: out_road, wide: out_wide}
if __name__ == "__main__":
for cam_w, cam_h in CAMERA_CONFIGS:
for bl in [2, 5]:
compile_v2_warp(cam_w, cam_h, bl)
+63
View File
@@ -0,0 +1,63 @@
# Model Selector Version Compatibility
This document explains the version compatibility mechanism used by the Model Selector system, and the rationale behind certain version constraints and JSON file management strategies.
## Overview
The Model Selector is responsible for selecting and validating model bundles based on their metadata and version constraints. Each model bundle is distributed via a JSON file and includes a `minimumSelectorVersion` field indicating the minimum selector version required to load it.
To ensure robust compatibility and prevent mismatches between model expectations and selector capabilities, the selector enforces two version boundaries:
* **`REQUIRED_MIN_SELECTOR_VERSION`**: the oldest selector version we support.
* **`CURRENT_SELECTOR_VERSION`**: the current version of the selector logic.
## Version Compatibility Check
A model bundle is considered compatible if:
```python
REQUIRED_MIN_SELECTOR_VERSION <= bundle["minimumSelectorVersion"] <= CURRENT_SELECTOR_VERSION
```
This ensures:
* **Old bundles are rejected** if they rely on deprecated selector behavior.
* **Future bundles are ignored** if they expect logic that the current selector doesn't yet implement.
## Handling Breaking Changes
When a deep change in selector behavior requires *all* models to be recompiled (e.g., due to a major architectural update), we:
1. **Create a new JSON file** (e.g., from `models_v4.json` to `models_v5.json`).
2. **Assign updated `minimumSelectorVersion` values** in the new bundles.
This allows older selector versions to continue using the previous JSON file, while newer versions point to the new one, preventing cross-contamination.
## Why `REQUIRED_MIN_SELECTOR_VERSION` Still Matters
Despite using new JSON files to isolate breaking changes, `REQUIRED_MIN_SELECTOR_VERSION` plays a critical role:
### 1. **Cached Bundle Validation**
Model bundles are cached locally (e.g., in-memory or on disk). A user might have previously loaded a now-invalid bundle from an older JSON file.
`REQUIRED_MIN_SELECTOR_VERSION` prevents the selector from reloading or trusting that stale cached bundle, even if the original JSON is gone.
### 2. **Explicit Deprecation Boundary**
By raising `REQUIRED_MIN_SELECTOR_VERSION`, we declare older bundles officially unsupported, even if they technically still exist in a legacy JSON file.
### 3. **Avoiding Race Conditions**
Some clients may have intermittent access to updated JSONs. The runtime check ensures version compatibility is enforced independently of external file state.
## Summary
| Component | Purpose |
| ------------------------------- | --------------------------------------------------------------------- |
| `minimumSelectorVersion` | Declares the minimum selector version required to load a model bundle |
| `REQUIRED_MIN_SELECTOR_VERSION` | Prevents loading bundles that are too old (e.g., from stale cache) |
| `CURRENT_SELECTOR_VERSION` | Prevents loading bundles that are too new or forward-incompatible |
| JSON file renaming | Isolates bundles by selector generation to handle full recompiles |
This layered strategy ensures safe evolution of the model selection system while maintaining backward compatibility and runtime protection against stale or incompatible bundles.
Whitespace-only changes.
+121
View File
@@ -0,0 +1,121 @@
import numpy as np
def index_function(idx, max_val=192, max_idx=32):
return max_val * ((idx/max_idx)**2)
class ModelConstants:
# time and distance indices
IDX_N = 33
T_IDXS = [index_function(idx, max_val=10.0) for idx in range(IDX_N)]
X_IDXS = [index_function(idx, max_val=192.0) for idx in range(IDX_N)]
LEAD_T_IDXS = [0., 2., 4., 6., 8., 10.]
LEAD_T_OFFSETS = [0., 2., 4.]
META_T_IDXS = [2., 4., 6., 8., 10.]
# model inputs constants
MODEL_FREQ = 20
FEATURE_LEN = 512
HISTORY_BUFFER_LEN = 99
DESIRE_LEN = 8
TRAFFIC_CONVENTION_LEN = 2
NAV_FEATURE_LEN = 256
NAV_INSTRUCTION_LEN = 150
LAT_PLANNER_STATE_LEN = 4
LATERAL_CONTROL_PARAMS_LEN = 2
PREV_DESIRED_CURV_LEN = 1
# model outputs constants
FCW_THRESHOLDS_5MS2 = np.array([.05, .05, .15, .15, .15], dtype=np.float32)
FCW_THRESHOLDS_3MS2 = np.array([.7, .7], dtype=np.float32)
FCW_5MS2_PROBS_WIDTH = 5
FCW_3MS2_PROBS_WIDTH = 2
DISENGAGE_WIDTH = 5
POSE_WIDTH = 6
WIDE_FROM_DEVICE_WIDTH = 3
SIM_POSE_WIDTH = 6
LEAD_WIDTH = 4
LANE_LINES_WIDTH = 2
ROAD_EDGES_WIDTH = 2
PLAN_WIDTH = 15
DESIRE_PRED_WIDTH = 8
LAT_PLANNER_SOLUTION_WIDTH = 4
DESIRED_CURV_WIDTH = 1
NUM_LANE_LINES = 4
NUM_ROAD_EDGES = 2
LEAD_TRAJ_LEN = 6
DESIRE_PRED_LEN = 4
PLAN_MHP_N = 5
LEAD_MHP_N = 2
PLAN_MHP_SELECTION = 1
LEAD_MHP_SELECTION = 3
FCW_THRESHOLD_5MS2_HIGH = 0.15
FCW_THRESHOLD_5MS2_LOW = 0.05
FCW_THRESHOLD_3MS2 = 0.7
CONFIDENCE_BUFFER_LEN = 5
RYG_GREEN = 0.01165
RYG_YELLOW = 0.06157
POLY_PATH_DEGREE = 4
# model outputs slices
class Plan:
POSITION = slice(0, 3)
VELOCITY = slice(3, 6)
ACCELERATION = slice(6, 9)
T_FROM_CURRENT_EULER = slice(9, 12)
ORIENTATION_RATE = slice(12, 15)
class Meta:
ENGAGED = slice(0, 1)
# next 2, 4, 6, 8, 10 seconds
GAS_DISENGAGE = slice(1, 31, 6)
BRAKE_DISENGAGE = slice(2, 31, 6)
STEER_OVERRIDE = slice(3, 31, 6)
HARD_BRAKE_3 = slice(4, 31, 6)
HARD_BRAKE_4 = slice(5, 31, 6)
HARD_BRAKE_5 = slice(6, 31, 6)
# next 0, 2, 4, 6, 8, 10 seconds
GAS_PRESS = slice(31, 55, 4)
BRAKE_PRESS = slice(32, 55, 4)
LEFT_BLINKER = slice(33, 55, 4)
RIGHT_BLINKER = slice(34, 55, 4)
class MetaTombRaider:
ENGAGED = slice(0, 1)
# next 2, 4, 6, 8, 10 seconds
GAS_DISENGAGE = slice(1, 41, 8)
BRAKE_DISENGAGE = slice(2, 41, 8)
STEER_OVERRIDE = slice(3, 41, 8)
HARD_BRAKE_3 = slice(4, 41, 8)
HARD_BRAKE_4 = slice(5, 41, 8)
HARD_BRAKE_5 = slice(6, 41, 8)
GAS_PRESS = slice(7, 41, 8)
BRAKE_PRESS = slice(8, 41, 8)
# next 0, 2, 4, 6, 8, 10 seconds
LEFT_BLINKER = slice(41, 53, 2)
RIGHT_BLINKER = slice(42, 53, 2)
class MetaSimPose:
ENGAGED = slice(0, 1)
# next 2, 4, 6, 8, 10 seconds
GAS_DISENGAGE = slice(1, 36, 7)
BRAKE_DISENGAGE = slice(2, 36, 7)
STEER_OVERRIDE = slice(3, 36, 7)
HARD_BRAKE_3 = slice(4, 36, 7)
HARD_BRAKE_4 = slice(5, 36, 7)
HARD_BRAKE_5 = slice(6, 36, 7)
GAS_PRESS = slice(7, 36, 7)
# next 0, 2, 4, 6, 8, 10 seconds
LEFT_BLINKER = slice(36, 48, 2)
RIGHT_BLINKER = slice(37, 48, 2)
+61
View File
@@ -0,0 +1,61 @@
import argparse
import os
import hashlib
from openpilot.common.basedir import BASEDIR
from openpilot.sunnypilot import get_file_hash
from openpilot.sunnypilot.models.model_name import DEFAULT_MODEL
DEFAULT_MODEL_NAME_PATH = os.path.join(BASEDIR, "openpilot", "sunnypilot", "models", "model_name.py")
MODEL_HASH_PATH = os.path.join(BASEDIR, "openpilot", "sunnypilot", "models", "tests", "model_hash")
SUPERCOMBO_ONNX_PATH = os.path.join(BASEDIR, "openpilot", "selfdrive", "modeld", "models", "driving_supercombo.onnx")
def update_model_hash():
supercombo_hash = get_file_hash(SUPERCOMBO_ONNX_PATH)
combined_hash = hashlib.sha256(supercombo_hash.encode()).hexdigest()
with open(MODEL_HASH_PATH, "w") as f:
f.write(combined_hash)
print(f"Generated and updated new combined model hash to {MODEL_HASH_PATH}")
def get_current_default_model_name():
print("[GET DEFAULT MODEL NAME]")
name = DEFAULT_MODEL
print(f'Current default model name: "{name}"')
return name
def update_default_model_name(name: str):
print("[CHANGE DEFAULT MODEL NAME]")
with open(DEFAULT_MODEL_NAME_PATH, "w") as f:
f.write(f'DEFAULT_MODEL = "{name}"\n')
print(f'New default model name: "{name}"')
print("[DONE]")
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Update default model name and hash")
parser.add_argument("--new_name", type=str, help="New default model name")
args = parser.parse_args()
if not args.new_name:
print("Warning: No new default model name provided. Use --new_name to specify")
print("Default model name and hash will not be updated! (aborted)")
exit(0)
current_name = get_current_default_model_name()
new_name = args.new_name
if current_name == new_name:
print(f'Proposed default model name: "{new_name}"')
confirm = input("Proposed default model name is the same as the current default model name. Confirm? (y/n): ").upper().strip()
if confirm != "Y":
print("Default model name and hash will not be updated! (aborted)")
exit(0)
update_default_model_name(new_name)
update_model_hash()
+187
View File
@@ -0,0 +1,187 @@
"""
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
import time
import requests
from requests.exceptions import (SSLError, RequestException, HTTPError)
from openpilot.common.params import Params
from openpilot.common.swaglog import cloudlog
from openpilot.sunnypilot.models.helpers import is_bundle_version_compatible
from openpilot.cereal import custom
class ModelParser:
"""Handles parsing of model data into cereal objects"""
@staticmethod
def _parse_download_uri(download_uri_data) -> custom.ModelManagerSP.DownloadUri:
download_uri = custom.ModelManagerSP.DownloadUri()
download_uri.uri = download_uri_data.get("url")
download_uri.sha256 = download_uri_data.get("sha256")
return download_uri
@staticmethod
def _parse_artifact(artifact_data) -> custom.ModelManagerSP.Artifact:
artifact = custom.ModelManagerSP.Artifact()
artifact.fileName = artifact_data.get("file_name")
artifact.downloadUri = ModelParser._parse_download_uri(artifact_data.get("download_uri", {}))
return artifact
@staticmethod
def _parse_model(model_data) -> custom.ModelManagerSP.Model:
model = custom.ModelManagerSP.Model()
model.type = model_data.get("type")
model.artifact = ModelParser._parse_artifact(model_data.get("artifact", {}))
if metadata := model_data.get("metadata"):
model.metadata = ModelParser._parse_artifact(metadata)
return model
@staticmethod
def _parse_overrides(overrides_data: dict[str, str]) -> list[custom.ModelManagerSP.Override]:
overrides = []
for key, value in overrides_data.items():
override = custom.ModelManagerSP.Override()
override.key = key
override.value = value
overrides.append(override)
return overrides
@staticmethod
def _parse_bundle(bundle) -> custom.ModelManagerSP.ModelBundle:
model_bundle = custom.ModelManagerSP.ModelBundle()
model_bundle.index = int(bundle["index"])
model_bundle.internalName = bundle["short_name"]
model_bundle.displayName = bundle["display_name"]
model_bundle.models = [ModelParser._parse_model(model) for model in bundle.get("models",[])]
model_bundle.status = 0
model_bundle.generation = int(bundle["generation"])
model_bundle.environment = bundle["environment"]
model_bundle.runner = bundle.get("runner", custom.ModelManagerSP.Runner.snpe)
model_bundle.is20hz = bundle.get("is_20hz", False)
model_bundle.minimumSelectorVersion = int(bundle["minimum_selector_version"])
model_bundle.overrides = ModelParser._parse_overrides(bundle.get("overrides", {}))
model_bundle.ref = bundle.get("ref")
return model_bundle
@staticmethod
def parse_models(json_data: dict) -> list[custom.ModelManagerSP.ModelBundle]:
found_bundles = [ModelParser._parse_bundle(bundle) for bundle in json_data.get("bundles", [])]
return [bundle for bundle in found_bundles if is_bundle_version_compatible(bundle.to_dict())]
class ModelCache:
"""Handles caching of model data to avoid frequent remote fetches"""
def __init__(self, params: Params, cache_timeout: int = int(3600 * 1e9)):
self.params = params
self.cache_timeout = cache_timeout
self._LAST_SYNC_KEY = "ModelManager_LastSyncTime"
self._CACHE_KEY = "ModelManager_ModelsCache"
def _is_expired(self) -> bool:
"""Checks if the cache has expired"""
current_time = int(time.monotonic() * 1e9)
last_sync = self.params.get(self._LAST_SYNC_KEY) or 0
return bool(last_sync == 0) or (current_time - last_sync) >= self.cache_timeout
def get(self) -> tuple[dict, bool]:
"""
Retrieves cached model data and expiration status atomically.
Returns: Tuple of (cached_data, is_expired)
If no cached data exists or on error, returns an empty dict
"""
try:
cached_data = self.params.get(self._CACHE_KEY)
if not cached_data:
cloudlog.warning("No cached model data available")
return {}, True
return cached_data, self._is_expired()
except Exception as e:
cloudlog.exception(f"Error retrieving cached model data: {str(e)}")
return {}, True
def set(self, data: dict) -> None:
"""Updates the cache with new model data"""
self.params.put(self._CACHE_KEY, data, block=True)
self.params.put(self._LAST_SYNC_KEY, int(time.monotonic() * 1e9), block=True)
class ModelFetcher:
"""Handles fetching and caching of model data from remote source"""
MODEL_URL = "https://raw.githubusercontent.com/sunnypilot/sunnypilot-models/refs/heads/gh-pages/docs/driving_models_v17.json"
def __init__(self, params: Params):
self.params = params
self.model_cache = ModelCache(params)
self.model_parser = ModelParser()
def _fetch_and_cache_models(self) -> list[custom.ModelManagerSP.ModelBundle] | None:
"""Fetches fresh model data from remote and updates cache.
Returns None on transport errors. Raises on 404 and other fatal HTTP errors.
"""
try:
response = requests.get(self.MODEL_URL, timeout=10)
# Explicitly handle 404 differently
if response.status_code == 404:
cloudlog.error(f"Models URL returned 404 Not Found: {self.MODEL_URL}")
raise HTTPError(f"404 Not Found: {self.MODEL_URL}", response=response)
# Raise for any other 4xx/5xx
response.raise_for_status()
json_data = response.json()
self.model_cache.set(json_data)
cloudlog.debug("Successfully updated models cache")
return self.model_parser.parse_models(json_data)
except ConnectionError as e:
cloudlog.warning(f"DNS/connection error while fetching models: {e}")
except SSLError as e:
cloudlog.warning(f"SSL error while fetching models: {e}")
except RequestException as e:
cloudlog.warning(f"Request transport error while fetching models: {e}")
except Exception as e:
cloudlog.exception(f"Unexpected error fetching models: {e}")
return None
def get_available_bundles(self) -> list[custom.ModelManagerSP.ModelBundle]:
"""Gets the list of available models, with smart cache handling"""
cached_data, is_expired = self.model_cache.get()
if cached_data and not is_expired:
cloudlog.debug("Using valid cached models data")
return self.model_parser.parse_models(cached_data)
fetched_bundles = self._fetch_and_cache_models()
if fetched_bundles is not None:
return fetched_bundles
if not cached_data:
cloudlog.warning("Failed to fetch fresh data and no cache available")
cloudlog.warning("Failed to fetch fresh data. Using expired cache as fallback")
return self.model_parser.parse_models(cached_data)
if __name__ == "__main__":
params = Params()
model_fetcher = ModelFetcher(params)
bundles = model_fetcher.get_available_bundles()
for bundle in bundles:
for model in bundle.models:
model_overrides = {override.key: override.value for override in bundle.overrides}
# Print model details
print(f"Bundle: {bundle.internalName}, Type: {model.type}, Status: {bundle.status}, Overrides: {model_overrides}")
# Print artifact details
print(f"Artifact: {model.artifact.fileName}, Download URI: {model.artifact.downloadUri.uri}")
# Print metadata details
print(f"Metadata: {model.metadata.fileName}, Download URI: {model.metadata.downloadUri.uri}")
+207
View File
@@ -0,0 +1,207 @@
"""
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
import hashlib
import os
import pickle
from pathlib import Path
import numpy as np
from openpilot.cereal import custom
from openpilot.common.params import Params
from openpilot.common.swaglog import cloudlog
from openpilot.sunnypilot.models.constants import Meta, MetaSimPose, MetaTombRaider
from openpilot.common.hardware.hw import Paths
# SET ME TO THE EXACT JSON VERSION WE SET IN SUNNYPILOT_MODELS REPO
REQUIRED_JSON_VERSION = 15
CUSTOM_MODEL_PATH = Paths.model_root()
METADATA_PATH = Path(__file__).parent / '../models/supercombo_metadata.pkl'
ModelManager = custom.ModelManagerSP
_LAST_VALIDATED_RAW = None
def _compute_hash(file_path: str) -> str | None:
from openpilot.common.file_chunker import open_file_chunked
try:
with open_file_chunked(file_path) as file:
return hashlib.file_digest(file, "sha256").hexdigest().lower()
except FileNotFoundError:
return None
async def verify_file(file_path: str, expected_hash: str) -> bool:
file_hash = _compute_hash(file_path)
return file_hash == expected_hash.lower() if file_hash else False
def _verify_file(file_path: str, expected_hash: str) -> bool:
file_hash = _compute_hash(file_path)
return file_hash == expected_hash.lower() if file_hash else False
def is_bundle_version_compatible(bundle: dict) -> bool:
"""
The bundle parsed from the json specifies a `minimum_selector_version`, which defines the minimum selector version
required to load the model. This function ensures that:
the bundle MUST match the `REQUIRED_JSON_VERSION` set here in helpers.
"""
return bundle.get("minimumSelectorVersion", 0) == REQUIRED_JSON_VERSION
def _bundle_artifacts(bundle: custom.ModelManagerSP.ModelBundle) -> list[tuple[str, str]]:
artifacts = []
for model in getattr(bundle, 'models', []) or []:
for artifact in (getattr(model, 'artifact', None), getattr(model, 'metadata', None)):
if artifact and getattr(artifact, 'fileName', None) and getattr(artifact, 'downloadUri', None):
sha256 = getattr(artifact.downloadUri, 'sha256', None)
if sha256:
artifacts.append((artifact.fileName, sha256))
return artifacts
def _bundle_is_valid_locally(bundle: custom.ModelManagerSP.ModelBundle) -> bool:
model_root = Paths.model_root()
return all(_verify_file(os.path.join(model_root, file_name), expected_hash)
for file_name, expected_hash in _bundle_artifacts(bundle))
def _bundle_needs_reset(active_bundle: custom.ModelManagerSP.ModelBundle, available_bundles: list[custom.ModelManagerSP.ModelBundle] | None) -> bool:
if active_bundle is None:
return False
if available_bundles is not None:
matching_bundle = None
for bundle in available_bundles:
if getattr(active_bundle, 'ref', None) and getattr(bundle, 'ref', None):
if active_bundle.ref == bundle.ref:
matching_bundle = bundle
break
elif getattr(active_bundle, 'internalName', None) == getattr(bundle, 'internalName', None):
matching_bundle = bundle
break
if matching_bundle is None:
return True
if active_bundle.minimumSelectorVersion != matching_bundle.minimumSelectorVersion:
return True
active_runner = getattr(active_bundle, 'runner', None)
matching_runner = getattr(matching_bundle, 'runner', None)
if active_runner is not None and matching_runner is not None:
if getattr(active_runner, 'raw', active_runner) != getattr(matching_runner, 'raw', matching_runner):
return True
if set(_bundle_artifacts(active_bundle)) != set(_bundle_artifacts(matching_bundle)):
return True
return not _bundle_is_valid_locally(active_bundle)
def validate_active_bundle(params: Params, available_bundles: list[custom.ModelManagerSP.ModelBundle] | None = None) -> None:
global _LAST_VALIDATED_RAW
raw_bundle = params.get("ModelManager_ActiveBundle")
if not raw_bundle:
return
if raw_bundle == _LAST_VALIDATED_RAW:
return
active_bundle = get_active_bundle(params, raw_bundle_dict=raw_bundle)
if active_bundle is None or _bundle_needs_reset(active_bundle, available_bundles):
cloudlog.warning("Active model bundle invalid; resetting to default")
params.remove("ModelManager_ActiveBundle")
params.put("ModelRunnerTypeCache", int(custom.ModelManagerSP.Runner.stock), block=True)
_LAST_VALIDATED_RAW = None
else:
_LAST_VALIDATED_RAW = raw_bundle
def get_active_bundle(params: Params | None = None, raw_bundle_dict: dict | bytes | None = None) -> "custom.ModelManagerSP.ModelBundle | None":
params = params or Params()
try:
active_bundle_dict = raw_bundle_dict if raw_bundle_dict is not None else (params.get("ModelManager_ActiveBundle") or {})
if active_bundle_dict and is_bundle_version_compatible(active_bundle_dict):
return custom.ModelManagerSP.ModelBundle(**active_bundle_dict)
except Exception:
pass
return None
def get_active_model_runner(params: Params | None = None, force_check: bool = False) -> int:
params = params or Params()
cached_runner_type = params.get("ModelRunnerTypeCache")
if cached_runner_type is not None and not force_check:
return cached_runner_type
runner_type = custom.ModelManagerSP.Runner.stock
if active_bundle := get_active_bundle(params):
runner_type = active_bundle.runner.raw
if cached_runner_type != runner_type:
params.put("ModelRunnerTypeCache", int(runner_type), block=True)
return runner_type
def _get_model():
if bundle := get_active_bundle():
drive_model = next(model for model in bundle.models if model.type == ModelManager.Model.Type.supercombo)
return drive_model
return None
def load_metadata():
model = _get_model()
metadata_path = f"{CUSTOM_MODEL_PATH}/{model.metadata.fileName}" if model else METADATA_PATH
with open(metadata_path, 'rb') as f:
return pickle.load(f)
def prepare_inputs(model_metadata: dict) -> dict[str, np.ndarray]:
return {
key: np.zeros(shape, dtype=np.float32).flatten()
for key, shape in model_metadata['input_shapes'].items()
if 'img' not in key
}
def load_meta_constants(model_metadata: dict):
""" Loads the appropriate meta model class based on key shapes"""
if 'sim_pose' in model_metadata['input_shapes']:
return MetaSimPose
meta_slice = model_metadata['output_slices']['meta']
if (meta_slice.start, meta_slice.stop, meta_slice.step) == (5868, 5921, None):
return MetaTombRaider
return Meta
# The following method(s) are modeld helper methods
def plan_x_idxs_helper(constants, plan, model_output) -> list[float]:
# times at X_IDXS according to plan.
LINE_T_IDXS = [np.nan] * constants.IDX_N
LINE_T_IDXS[0] = 0.0
plan_x = model_output['plan'][0, :, plan.POSITION][:, 0].tolist()
for xidx in range(1, constants.IDX_N):
tidx = 0
# increment tidx until we find an element that's further away than the current xidx
while tidx < constants.IDX_N - 1 and plan_x[tidx + 1] < constants.X_IDXS[xidx]:
tidx += 1
if tidx == constants.IDX_N - 1:
# if the plan doesn't extend far enough, set plan_t to the max value (10s), then break
LINE_T_IDXS[xidx] = constants.T_IDXS[constants.IDX_N - 1]
break
# interpolate to find `t` for the current xidx
current_x_val = plan_x[tidx]
next_x_val = plan_x[tidx + 1]
p = (constants.X_IDXS[xidx] - current_x_val) / (next_x_val - current_x_val) if abs(
next_x_val - current_x_val) > 1e-9 else float('nan')
LINE_T_IDXS[xidx] = p * constants.T_IDXS[tidx + 1] + (1 - p) * constants.T_IDXS[tidx]
return LINE_T_IDXS
+298
View File
@@ -0,0 +1,298 @@
"""
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
import asyncio
import os
import time
import aiohttp
from openpilot.common.params import Params
from openpilot.common.realtime import Ratekeeper
from openpilot.common.swaglog import cloudlog
from openpilot.common.hardware.hw import Paths
from openpilot.cereal import messaging, custom
from openpilot.sunnypilot.models.fetcher import ModelFetcher
from openpilot.sunnypilot.models.helpers import get_active_bundle, validate_active_bundle, verify_file
class ModelManagerSP:
"""Manages model downloads and status reporting"""
def __init__(self):
self.params = Params()
self.model_fetcher = ModelFetcher(self.params)
self.pm = messaging.PubMaster(["modelManagerSP"])
self.available_models: list[custom.ModelManagerSP.ModelBundle] = []
self.selected_bundle: custom.ModelManagerSP.ModelBundle = None
self.active_bundle: custom.ModelManagerSP.ModelBundle = get_active_bundle(self.params)
self._chunk_size = 128 * 1000 # 128 KB chunks
self._download_start_times: dict[str, float] = {} # Track start time per model
def _sync_artifact_progress(self, source_artifact) -> None:
"""Mirror download progress to all artifacts sharing the same filename in the selected bundle."""
if not self.selected_bundle:
return
for model in self.selected_bundle.models:
for artifact in (model.artifact, model.metadata):
if artifact is not source_artifact and artifact.fileName == source_artifact.fileName:
artifact.downloadProgress.status = source_artifact.downloadProgress.status
artifact.downloadProgress.progress = source_artifact.downloadProgress.progress
artifact.downloadProgress.eta = source_artifact.downloadProgress.eta
def _calculate_eta(self, filename: str, progress: float) -> int:
"""Calculate ETA based on elapsed time and current progress"""
if filename not in self._download_start_times or progress <= 0:
return 60 # Default ETA for new downloads
elapsed_time = time.monotonic() - self._download_start_times[filename]
if elapsed_time <= 0:
return 60
# If we're at X% after Y seconds, we can estimate total time as (Y / X) * 100
total_estimated_time = (elapsed_time / progress) * 100
eta = total_estimated_time - elapsed_time
return max(1, int(eta)) # Return at least 1 second if download is ongoing
async def _download_file(self, url: str, path: str, model) -> None:
"""Downloads a file with progress tracking"""
self._download_start_times[model.fileName] = time.monotonic()
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
response.raise_for_status()
total_size = int(response.headers.get("content-length", 0))
bytes_downloaded = 0
with open(path, 'wb') as f:
async for chunk in response.content.iter_chunked(self._chunk_size): # type: bytes
f.write(chunk)
bytes_downloaded += len(chunk)
if self.params.get("ModelManager_DownloadIndex") is None:
raise Exception("Download cancelled")
if total_size > 0:
progress = (bytes_downloaded / total_size) * 100
model.downloadProgress.status = custom.ModelManagerSP.DownloadStatus.downloading
model.downloadProgress.progress = progress
model.downloadProgress.eta = self._calculate_eta(model.fileName, progress)
self._sync_artifact_progress(model)
self._report_status()
# Clean up start time after download completes
del self._download_start_times[model.fileName]
async def _download_chunked(self, base_url: str, base_path: str, artifact) -> None:
from openpilot.common.file_chunker import get_manifest_path, get_chunk_name
manifest_url = get_manifest_path(base_url)
manifest_path = get_manifest_path(base_path)
async with aiohttp.ClientSession() as session:
async with session.get(manifest_url) as resp:
if resp.status == 404:
raise FileNotFoundError
resp.raise_for_status()
num_chunks = int((await resp.read()).strip())
self._download_start_times[artifact.fileName] = time.monotonic()
for i in range(num_chunks):
chunk_url = get_chunk_name(base_url, i, num_chunks)
chunk_path = get_chunk_name(base_path, i, num_chunks)
chunk_downloaded = 0
async with aiohttp.ClientSession() as session:
async with session.get(chunk_url) as response:
response.raise_for_status()
chunk_size = int(response.headers.get("content-length", 0))
with open(chunk_path, 'wb') as f:
async for data in response.content.iter_chunked(self._chunk_size):
f.write(data)
chunk_downloaded += len(data)
if self.params.get("ModelManager_DownloadIndex") is None:
raise Exception("Download cancelled")
intra = chunk_downloaded / max(chunk_size, 1)
progress = min(99, (i + intra) / num_chunks * 100)
artifact.downloadProgress.status = custom.ModelManagerSP.DownloadStatus.downloading
artifact.downloadProgress.progress = progress
artifact.downloadProgress.eta = self._calculate_eta(artifact.fileName, progress)
self._sync_artifact_progress(artifact)
self._report_status()
with open(manifest_path, 'w') as f:
f.write(str(num_chunks))
if os.path.isfile(base_path):
os.remove(base_path)
del self._download_start_times[artifact.fileName]
async def _process_artifact(self, artifact, destination_path: str) -> None:
if not artifact.downloadUri.uri:
return None
url = artifact.downloadUri.uri
expected_hash = artifact.downloadUri.sha256
filename = artifact.fileName
full_path = os.path.join(destination_path, filename)
try:
if await verify_file(full_path, expected_hash):
artifact.downloadProgress.status = custom.ModelManagerSP.DownloadStatus.cached
artifact.downloadProgress.progress = 100
artifact.downloadProgress.eta = 0
self._sync_artifact_progress(artifact)
self._report_status()
return
try:
await self._download_chunked(url, full_path, artifact)
except (FileNotFoundError, aiohttp.ClientResponseError):
await self._download_file(url, full_path, artifact)
if not await verify_file(full_path, expected_hash):
raise ValueError(f"Hash validation failed for {filename}")
artifact.downloadProgress.status = custom.ModelManagerSP.DownloadStatus.downloaded
artifact.downloadProgress.progress = 100
artifact.downloadProgress.eta = 0
self._sync_artifact_progress(artifact)
self._report_status()
except Exception as e:
cloudlog.error(f"Error downloading {filename}: {str(e)}")
for f in [full_path] + [p for p in (os.path.join(destination_path, f) for f in os.listdir(destination_path)) if filename in p]:
if os.path.isfile(f):
os.remove(f)
artifact.downloadProgress.status = custom.ModelManagerSP.DownloadStatus.failed
artifact.downloadProgress.eta = 0
self._sync_artifact_progress(artifact)
self.selected_bundle.status = custom.ModelManagerSP.DownloadStatus.failed
self._report_status()
self._download_start_times.pop(artifact.fileName, None)
raise
async def _process_model(self, model, destination_path: str) -> None:
"""Processes a single model download including verification"""
model_artifact = model.artifact
metadata_artifact = model.metadata
await self._process_artifact(metadata_artifact, destination_path)
await self._process_artifact(model_artifact, destination_path)
def _report_status(self) -> None:
"""Reports current status through messaging system"""
msg = messaging.new_message('modelManagerSP', valid=True)
model_manager_state = msg.modelManagerSP
if self.selected_bundle:
model_manager_state.selectedBundle = self.selected_bundle
if self.active_bundle:
model_manager_state.activeBundle = self.active_bundle
model_manager_state.availableBundles = self.available_models
self.pm.send('modelManagerSP', msg)
async def _download_bundle(self, model_bundle: custom.ModelManagerSP.ModelBundle, destination_path: str) -> None:
"""Downloads all models in a bundle"""
self.selected_bundle = model_bundle
self.selected_bundle.status = custom.ModelManagerSP.DownloadStatus.downloading
os.makedirs(destination_path, exist_ok=True)
try:
seen_artifacts: set[str] = set()
for model in self.selected_bundle.models:
for artifact in (model.metadata, model.artifact):
if not artifact.fileName:
continue
if artifact.fileName in seen_artifacts:
artifact.downloadProgress.status = custom.ModelManagerSP.DownloadStatus.cached
artifact.downloadProgress.progress = 100
artifact.downloadProgress.eta = 0
else:
seen_artifacts.add(artifact.fileName)
await self._process_artifact(artifact, destination_path)
self.active_bundle = self.selected_bundle
self.active_bundle.status = custom.ModelManagerSP.DownloadStatus.downloaded
self.params.put("ModelManager_ActiveBundle", self.active_bundle.to_dict(), block=True)
self.selected_bundle = None
except Exception:
self.selected_bundle.status = custom.ModelManagerSP.DownloadStatus.failed
raise
finally:
self._report_status()
def download(self, model_bundle: custom.ModelManagerSP.ModelBundle, destination_path: str) -> None:
"""Main entry point for downloading a model bundle"""
asyncio.run(self._download_bundle(model_bundle, destination_path))
def main_thread(self) -> None:
"""Main thread for model management"""
rk = Ratekeeper(1, print_delay_threshold=None)
while True:
try:
self.available_models = self.model_fetcher.get_available_bundles()
validate_active_bundle(self.params, self.available_models)
self.active_bundle = get_active_bundle(self.params)
if (index_to_download := self.params.get("ModelManager_DownloadIndex")) is not None:
if model_to_download := next((model for model in self.available_models if model.index == index_to_download), None):
try:
self.download(model_to_download, Paths.model_root())
except Exception as e:
cloudlog.exception(e)
finally:
self.params.remove("ModelManager_DownloadIndex")
self.selected_bundle = None
if self.params.get("ModelManager_ClearCache"):
self.clear_model_cache()
self.params.remove("ModelManager_ClearCache")
self._report_status()
rk.keep_time()
except Exception as e:
cloudlog.exception(f"Error in main thread: {str(e)}")
rk.keep_time()
def clear_model_cache(self) -> None:
"""
Clears the model cache directory of all files except those in the active model bundle.
"""
# Get list of files used by active model bundle
active_files = []
if self.active_bundle is not None: # When the default model is active
for model in self.active_bundle.models:
if hasattr(model, 'artifact') and model.artifact.fileName:
active_files.append(model.artifact.fileName)
if hasattr(model, 'metadata') and model.metadata.fileName:
active_files.append(model.metadata.fileName)
# Remove all files except active ones (including their chunk files)
model_dir = Paths.model_root()
try:
for filename in os.listdir(model_dir):
base = filename.split('.chunk')[0] if '.chunk' in filename else filename
if base not in active_files and filename not in active_files:
file_path = os.path.join(model_dir, filename)
if os.path.isfile(file_path):
os.remove(file_path)
cloudlog.info("Model cache cleared, keeping active model files")
except Exception as e:
cloudlog.exception(f"Error clearing model cache: {str(e)}")
def main():
ModelManagerSP().main_thread()
if __name__ == "__main__":
main()
@@ -0,0 +1 @@
DEFAULT_MODEL = "CD210"
@@ -0,0 +1,15 @@
import os
import numpy as np
from openpilot.common.hardware.hw import Paths
from openpilot.cereal import custom
# Type definitions for clarity
NumpyDict = dict[str, np.ndarray]
ShapeDict = dict[str, tuple[int, ...]]
SliceDict = dict[str, slice]
ModelType = custom.ModelManagerSP.Model.Type
Model = custom.ModelManagerSP.Model
SEND_RAW_PRED = os.getenv('SEND_RAW_PRED')
CUSTOM_MODEL_PATH = Paths.model_root()
@@ -0,0 +1,28 @@
from openpilot.sunnypilot.models.helpers import get_active_bundle
from openpilot.sunnypilot.models.runners.model_runner import ModelRunner
from openpilot.sunnypilot.models.runners.tinygrad.tinygrad_runner import TinygradRunner, TinygradSplitRunner
from openpilot.sunnypilot.models.runners.constants import ModelType
def get_model_runner() -> ModelRunner:
"""
Factory function to create and return the appropriate ModelRunner instance.
Selects TinygradRunner, choosing TinygradSplitRunner if separate vision/policy
models are detected in the active bundle.
:return: An instance of a ModelRunner subclass (ONNXRunner, TinygradRunner, or TinygradSplitRunner).
"""
bundle = get_active_bundle()
if bundle and bundle.models:
model_types = {m.type.raw for m in bundle.models}
# Check if the bundle uses separate vision and policy models (legacy or new split format)
split_types = {ModelType.vision, ModelType.policy, ModelType.offPolicy, ModelType.onPolicy}
if model_types & split_types:
return TinygradSplitRunner()
# Otherwise, assume a single model (likely supercombo)
if bundle.models:
return TinygradRunner(bundle.models[0].type.raw)
# Default fallback to TinygradRunner with the supercombo type if bundle info is missing/incomplete
return TinygradRunner(ModelType.supercombo)
@@ -0,0 +1,174 @@
from abc import abstractmethod, ABC
import numpy as np
from openpilot.sunnypilot.models.helpers import get_active_bundle
from openpilot.sunnypilot.models.runners.constants import NumpyDict, ShapeDict, Model, SliceDict, SEND_RAW_PRED
from openpilot.common.hardware.hw import Paths
import pickle
CUSTOM_MODEL_PATH = Paths.model_root()
class ModelData:
"""
Stores metadata and configuration for a specific machine learning model.
This class loads model metadata (like input shapes and output slices)
from a pickle file associated with a model instance.
:param model: The machine learning model object containing metadata.
"""
def __init__(self, model: Model):
self.model = model
self.metadata = model.metadata
self.input_shapes: ShapeDict = {}
self.output_slices: SliceDict = {}
if self.metadata:
self._load_metadata()
def _load_metadata(self) -> None:
"""Loads input shapes and output slices from the model's metadata pickle file."""
metadata_path = f"{CUSTOM_MODEL_PATH}/{self.metadata.fileName}"
with open(metadata_path, 'rb') as f:
model_metadata = pickle.load(f)
self.input_shapes = model_metadata.get('input_shapes', {})
self.output_slices = model_metadata.get('output_slices', {})
class ModularRunner(ABC):
"""
Represents a modular runner for handling and slicing model outputs.
This abstract base class is designed to provide an interface for modular
parsing and processing of model outputs. Classes inheriting from it must
implement the specified abstract methods, defining how model outputs
should be handled and stored. The primary goal is to enable structured
parsing of outputs through a dictionary-based method mapping.
:ivar parser_method_dict: Mapping dictionary containing parser methods
for handling specific types of outputs.
:type parser_method_dict: dict
"""
@property
@abstractmethod
def parser_method_dict(self) -> dict:
pass
@parser_method_dict.setter
@abstractmethod
def parser_method_dict(self, value: dict) -> None:
pass
@abstractmethod
def _slice_outputs(self, model_outputs: np.ndarray) -> NumpyDict:
pass
class ModelRunner(ModularRunner):
"""
Abstract base class for managing and executing machine learning models.
Provides a common interface for loading models, preparing inputs, running
inference, and slicing/parsing outputs based on model metadata. Derived
classes implement the specifics of input preparation and model execution
for different frameworks (e.g., Tinygrad, ONNX).
"""
def __init__(self):
"""Initializes the model runner, loading the active model bundle."""
self.is_20hz: bool | None = None
self.is_20hz_3d: bool | None = None
self.models: dict[int, ModelData] = {}
self._model_data: ModelData | None = None # Active model data for current operation
self._parser_method_dict: dict = {}
self.inputs: dict = {}
self._parser = None
self._load_models()
self._constants = None
@property
def constants(self):
return self._constants
@property
def parser_method_dict(self) -> dict:
"""Returns the dictionary mapping model types to their respective parsing methods."""
return self._parser_method_dict
@parser_method_dict.setter
def parser_method_dict(self, value: dict) -> None:
"""Sets the dictionary mapping model types to their respective parsing methods."""
self._parser_method_dict = value
def _load_models(self) -> None:
"""Loads the active model bundle configuration and sets up ModelData."""
bundle = get_active_bundle()
if not bundle:
raise ValueError("No active model bundle found, why are we being executed?")
self.models = {model.type.raw: ModelData(model) for model in bundle.models}
self.is_20hz = bundle.is20hz
self.is_20hz_3d = False
@property
def input_shapes(self) -> ShapeDict:
"""Returns the input shapes for the currently active model."""
if self._model_data:
return self._model_data.input_shapes
raise ValueError("Model data is not available. Ensure the model is loaded correctly.")
@property
def output_slices(self) -> SliceDict:
"""Returns the output slices for the currently active model."""
if self._model_data:
return self._model_data.output_slices
raise ValueError("Model data is not available. Ensure the model is loaded correctly.")
@property
def vision_input_names(self) -> list[str]:
"""Returns the list of vision input names from the input shapes."""
if self._model_data:
return list(self._model_data.input_shapes.keys())
raise ValueError("Model data is not available. Ensure the model is loaded correctly.")
@abstractmethod
def prepare_inputs(self, numpy_inputs: NumpyDict) -> dict:
"""
Abstract method to prepare inputs for model inference.
:param numpy_inputs: Dictionary of numpy arrays for non-image inputs.
:return: Dictionary of prepared inputs ready for the model.
"""
raise NotImplementedError
@abstractmethod
def _run_model(self) -> NumpyDict:
"""
Abstract method to execute model inference with prepared inputs.
:return: Dictionary containing the model's raw output arrays.
"""
raise NotImplementedError
def _slice_outputs(self, model_outputs: np.ndarray) -> NumpyDict:
"""
Slices the raw model output array based on the output_slices metadata.
:param model_outputs: The raw numpy array output from the model.
:return: A dictionary where keys are output names and values are sliced numpy arrays.
"""
if not self._model_data:
raise ValueError("Model data is not available. Ensure the model is loaded correctly.")
sliced_outputs = {k: model_outputs[np.newaxis, v] for k, v in self._model_data.output_slices.items()}
if SEND_RAW_PRED:
sliced_outputs['raw_pred'] = model_outputs.copy() # Optionally include the full raw output
return sliced_outputs
def run_model(self) -> NumpyDict:
"""
Executes the model inference pipeline: runs the model and parses outputs.
:return: Dictionary containing the final parsed model outputs.
"""
return self._run_model() # Parsing is handled within specific runner implementations
@@ -0,0 +1,91 @@
import os
from abc import ABC
import numpy as np
from openpilot.sunnypilot.modeld_v2.parse_model_outputs import Parser as CombinedParser
from openpilot.sunnypilot.modeld_v2.parse_model_outputs_split import Parser as SplitParser
from openpilot.sunnypilot.models.runners.constants import ModelType, NumpyDict
from openpilot.sunnypilot.models.runners.model_runner import ModularRunner
from openpilot.common.hardware.hw import Paths
SEND_RAW_PRED = os.getenv('SEND_RAW_PRED')
CUSTOM_MODEL_PATH = Paths.model_root()
class OffPolicyTinygrad(ModularRunner, ABC):
"""
A TinygradRunner specialized for off-policy models.
Uses a SplitParser to handle outputs specific to the off-policy part of a split model setup.
"""
def __init__(self):
self._off_policy_parser = SplitParser()
self.parser_method_dict[ModelType.offPolicy] = self._parse_off_policy_outputs
def _parse_off_policy_outputs(self, model_outputs: np.ndarray) -> NumpyDict:
"""Parses off-policy model outputs using SplitParser."""
result: NumpyDict = self._off_policy_parser.parse_policy_outputs(self._slice_outputs(model_outputs))
return result
class OnPolicyTinygrad(ModularRunner, ABC):
"""
A TinygradRunner specialized for on-policy models.
Uses a SplitParser to handle outputs specific to the on-policy part of a split model setup.
"""
def __init__(self):
self._on_policy_parser = SplitParser()
self.parser_method_dict[ModelType.onPolicy] = self._parse_on_policy_outputs
def _parse_on_policy_outputs(self, model_outputs: np.ndarray) -> NumpyDict:
"""Parses on-policy model outputs using SplitParser."""
result: NumpyDict = self._on_policy_parser.parse_policy_outputs(self._slice_outputs(model_outputs))
return result
class PolicyTinygrad(ModularRunner, ABC):
"""
A TinygradRunner specialized for policy-only models.
Uses a SplitParser to handle outputs specific to the policy part of a split model setup.
"""
def __init__(self):
self._policy_parser = SplitParser()
self.parser_method_dict[ModelType.policy] = self._parse_policy_outputs
def _parse_policy_outputs(self, model_outputs: np.ndarray) -> NumpyDict:
"""Parses policy model outputs using SplitParser."""
result: NumpyDict = self._policy_parser.parse_policy_outputs(self._slice_outputs(model_outputs))
return result
class VisionTinygrad(ModularRunner, ABC):
"""
A TinygradRunner specialized for vision-only models.
Uses a SplitParser to handle outputs specific to the vision part of a split model setup.
"""
def __init__(self):
self._vision_parser = SplitParser()
self.parser_method_dict[ModelType.vision] = self._parse_vision_outputs
def _parse_vision_outputs(self, model_outputs: np.ndarray) -> NumpyDict:
"""Parses vision model outputs using SplitParser."""
result: NumpyDict = self._vision_parser.parse_vision_outputs(self._slice_outputs(model_outputs))
return result
class SupercomboTinygrad(ModularRunner, ABC):
"""
A TinygradRunner specialized for vision-only models.
Uses a SplitParser to handle outputs specific to the vision part of a split model setup.
"""
def __init__(self):
self._supercombo_parser = CombinedParser()
self.parser_method_dict[ModelType.supercombo] = self._parse_supercombo_outputs
def _parse_supercombo_outputs(self, model_outputs: np.ndarray) -> NumpyDict:
"""Parses vision model outputs using SplitParser."""
result: NumpyDict = self._supercombo_parser.parse_outputs(self._slice_outputs(model_outputs))
return result
@@ -0,0 +1,179 @@
import pickle
import numpy as np
from openpilot.sunnypilot.models.runners.constants import NumpyDict, ModelType, ShapeDict, CUSTOM_MODEL_PATH, SliceDict
from openpilot.sunnypilot.models.runners.model_runner import ModelRunner
from openpilot.sunnypilot.models.runners.tinygrad.model_types import PolicyTinygrad, VisionTinygrad, SupercomboTinygrad, OffPolicyTinygrad, OnPolicyTinygrad
from openpilot.sunnypilot.models.split_model_constants import SplitModelConstants
from openpilot.sunnypilot.modeld_v2.constants import ModelConstants
from tinygrad.tensor import Tensor
class TinygradRunner(ModelRunner, SupercomboTinygrad, PolicyTinygrad, VisionTinygrad, OffPolicyTinygrad, OnPolicyTinygrad):
"""
A ModelRunner implementation for executing Tinygrad models.
Handles loading Tinygrad model artifacts (.pkl), preparing inputs as Tinygrad
Tensors (potentially using QCOM extensions on TICI), running inference,
and parsing the outputs.
:param model_type: The type of model (e.g., supercombo) to load and run.
"""
def __init__(self, model_type: int = ModelType.supercombo):
ModelRunner.__init__(self)
SupercomboTinygrad.__init__(self)
PolicyTinygrad.__init__(self)
VisionTinygrad.__init__(self)
OffPolicyTinygrad.__init__(self)
OnPolicyTinygrad.__init__(self)
self._constants = ModelConstants
self._model_data = self.models.get(model_type)
if not self._model_data or not self._model_data.model:
raise ValueError(f"Model data for type {model_type} not available.")
artifact_filename = self._model_data.model.artifact.fileName
assert artifact_filename.endswith('_tinygrad.pkl'), \
f"Invalid model file {artifact_filename} for TinygradRunner"
model_pkl_path = f"{CUSTOM_MODEL_PATH}/{artifact_filename}"
with open(model_pkl_path, "rb") as f:
try:
# Load the compiled Tinygrad model runner function
self.model_run = pickle.load(f)
except FileNotFoundError as e:
# Provide a helpful error message if the model was built for a different platform
assert "/dev/kgsl-3d0" not in str(e), "Model was built on C3 or C3X, but is being loaded on PC"
raise
# Map input names to their required dtype and device from the loaded model
self.input_to_dtype = {}
self.input_to_device = {}
for idx, name in enumerate(self.model_run.captured.expected_names):
info = self.model_run.captured.expected_input_info[idx]
self.input_to_dtype[name] = info[2] # dtype
self.input_to_device[name] = info[3] # device
self._policy_cached = False
@property
def vision_input_names(self) -> list[str]:
"""Returns the list of vision input names from the input shapes."""
return [name for name in self.input_shapes.keys() if 'img' in name]
def prepare_policy_inputs(self, numpy_inputs: NumpyDict):
if not self._policy_cached:
for key, value in numpy_inputs.items():
self.inputs[key] = Tensor(value, device='NPY').realize()
self._policy_cached = True
def prepare_inputs(self, numpy_inputs: NumpyDict) -> dict:
"""Prepares all vision and policy inputs for the model."""
self.prepare_policy_inputs(numpy_inputs)
for key in self.vision_input_names:
if key in self.inputs:
self.inputs[key] = self.inputs[key].cast(self.input_to_dtype[key])
return self.inputs
def _run_model(self) -> NumpyDict:
"""Runs the Tinygrad model inference and parses the outputs."""
outputs = self.model_run(**self.inputs).contiguous().realize().uop.base.buffer.numpy().flatten()
return self._parse_outputs(outputs)
def _parse_outputs(self, model_outputs: np.ndarray) -> NumpyDict:
"""Parses the raw model outputs using the standard Parser."""
if self._model_data is None:
raise ValueError("Model data is not available. Ensure the model is loaded correctly.")
result: NumpyDict = self.parser_method_dict[self._model_data.model.type.raw](model_outputs)
return result
class TinygradSplitRunner(ModelRunner):
"""
A ModelRunner that coordinates separate TinygradVisionRunner and TinygradPolicyRunner instances.
Manages the execution of split vision and policy models, combining their inputs and outputs.
"""
def __init__(self):
super().__init__()
self.is_20hz_3d = True
self.vision_runner = TinygradRunner(ModelType.vision)
self.policy_runner = TinygradRunner(ModelType.policy) if self.models.get(ModelType.policy) else None
self.off_policy_runner = TinygradRunner(ModelType.offPolicy) if self.models.get(ModelType.offPolicy) else None
self.on_policy_runner = TinygradRunner(ModelType.onPolicy) if self.models.get(ModelType.onPolicy) else None
self._constants = SplitModelConstants
def _run_model(self) -> NumpyDict:
"""Runs both vision and policy models and merges their parsed outputs."""
vision_output = self.vision_runner.run_model()
outputs = {**vision_output}
if self.policy_runner:
policy_output = self.policy_runner.run_model()
outputs.update(policy_output)
if self.off_policy_runner:
off_policy_output = self.off_policy_runner.run_model()
if self.on_policy_runner:
off_policy_output.pop('plan', None)
outputs.update(off_policy_output)
if self.on_policy_runner:
on_policy_output = self.on_policy_runner.run_model()
outputs.update(on_policy_output)
if 'planplus' in outputs and 'plan' in outputs:
outputs['plan'] = outputs['plan'] + outputs['planplus']
return outputs
@property
def vision_input_names(self) -> list[str]:
"""Returns the list of vision input names from the vision runner."""
return list(self.vision_runner.vision_input_names)
@property
def input_shapes(self) -> ShapeDict:
"""Returns the combined input shapes from both vision and policy models."""
shapes = {**self.vision_runner.input_shapes}
if self.policy_runner:
shapes.update(self.policy_runner.input_shapes)
if self.off_policy_runner:
shapes.update(self.off_policy_runner.input_shapes)
if self.on_policy_runner:
shapes.update(self.on_policy_runner.input_shapes)
return shapes
@property
def output_slices(self) -> SliceDict:
"""Returns the combined output slices from both vision and policy models."""
slices = {**self.vision_runner.output_slices}
if self.policy_runner:
slices.update(self.policy_runner.output_slices)
if self.off_policy_runner:
slices.update(self.off_policy_runner.output_slices)
if self.on_policy_runner:
slices.update(self.on_policy_runner.output_slices)
return slices
def prepare_inputs(self, numpy_inputs: NumpyDict) -> dict:
"""Prepares inputs for both vision and policy models."""
if self.policy_runner:
self.policy_runner.prepare_policy_inputs(numpy_inputs)
for key in self.vision_input_names:
if key in self.inputs:
self.vision_runner.inputs[key] = self.inputs[key].cast(self.vision_runner.input_to_dtype[key])
inputs = {**self.vision_runner.inputs}
if self.policy_runner:
inputs.update(self.policy_runner.inputs)
if self.off_policy_runner:
self.off_policy_runner.prepare_policy_inputs(numpy_inputs)
inputs.update(self.off_policy_runner.inputs)
if self.on_policy_runner:
self.on_policy_runner.prepare_policy_inputs(numpy_inputs)
inputs.update(self.on_policy_runner.inputs)
return inputs
@@ -0,0 +1,94 @@
import numpy as np
def index_function(idx, max_val=192, max_idx=32):
return max_val * ((idx/max_idx)**2)
class SplitModelConstants:
# time and distance indices
IDX_N = 33
T_IDXS = [index_function(idx, max_val=10.0) for idx in range(IDX_N)]
X_IDXS = [index_function(idx, max_val=192.0) for idx in range(IDX_N)]
LEAD_T_IDXS = [0., 2., 4., 6., 8., 10.]
LEAD_T_OFFSETS = [0., 2., 4.]
META_T_IDXS = [2., 4., 6., 8., 10.]
# model inputs constants
MODEL_FREQ = 20
HISTORY_FREQ = 5
HISTORY_LEN_SECONDS = 5
TEMPORAL_SKIP = MODEL_FREQ // HISTORY_FREQ
FULL_HISTORY_BUFFER_LEN = MODEL_FREQ * HISTORY_LEN_SECONDS
INPUT_HISTORY_BUFFER_LEN = HISTORY_FREQ * HISTORY_LEN_SECONDS
FEATURE_LEN = 512
DESIRE_LEN = 8
TRAFFIC_CONVENTION_LEN = 2
LAT_PLANNER_STATE_LEN = 4
LATERAL_CONTROL_PARAMS_LEN = 2
PREV_DESIRED_CURV_LEN = 1
# model outputs constants
FCW_THRESHOLDS_5MS2 = np.array([.05, .05, .15, .15, .15], dtype=np.float32)
FCW_THRESHOLDS_3MS2 = np.array([.7, .7], dtype=np.float32)
FCW_5MS2_PROBS_WIDTH = 5
FCW_3MS2_PROBS_WIDTH = 2
DISENGAGE_WIDTH = 5
POSE_WIDTH = 6
WIDE_FROM_DEVICE_WIDTH = 3
LEAD_WIDTH = 4
LANE_LINES_WIDTH = 2
ROAD_EDGES_WIDTH = 2
PLAN_WIDTH = 15
DESIRE_PRED_WIDTH = 8
LAT_PLANNER_SOLUTION_WIDTH = 4
DESIRED_CURV_WIDTH = 1
NUM_LANE_LINES = 4
NUM_ROAD_EDGES = 2
LEAD_TRAJ_LEN = 6
DESIRE_PRED_LEN = 4
PLAN_MHP_N = 5
LEAD_MHP_N = 2
PLAN_MHP_SELECTION = 1
LEAD_MHP_SELECTION = 3
FCW_THRESHOLD_5MS2_HIGH = 0.15
FCW_THRESHOLD_5MS2_LOW = 0.05
FCW_THRESHOLD_3MS2 = 0.7
CONFIDENCE_BUFFER_LEN = 5
RYG_GREEN = 0.01165
RYG_YELLOW = 0.06157
POLY_PATH_DEGREE = 4
# model outputs slices
class Plan:
POSITION = slice(0, 3)
VELOCITY = slice(3, 6)
ACCELERATION = slice(6, 9)
T_FROM_CURRENT_EULER = slice(9, 12)
ORIENTATION_RATE = slice(12, 15)
class Meta:
ENGAGED = slice(0, 1)
# next 2, 4, 6, 8, 10 seconds
GAS_DISENGAGE = slice(1, 31, 6)
BRAKE_DISENGAGE = slice(2, 31, 6)
STEER_OVERRIDE = slice(3, 31, 6)
HARD_BRAKE_3 = slice(4, 31, 6)
HARD_BRAKE_4 = slice(5, 31, 6)
HARD_BRAKE_5 = slice(6, 31, 6)
# next 0, 2, 4, 6, 8, 10 seconds
GAS_PRESS = slice(31, 55, 4)
BRAKE_PRESS = slice(32, 55, 4)
LEFT_BLINKER = slice(33, 55, 4)
RIGHT_BLINKER = slice(34, 55, 4)
Whitespace-only changes.
@@ -0,0 +1 @@
49133798d9cd9cacf47085c7ef8122bfee88cd9c6192a8314c81bfb1b37f5809
@@ -0,0 +1,20 @@
"""
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
from openpilot.cereal import messaging, custom
if __name__ == "__main__":
sm = messaging.SubMaster(["modelManagerSP"])
while True:
sm.update(500)
if sm.updated:
msg = sm["modelManagerSP"]
for model in msg.selectedBundle.models:
if model.downloadProgress.status == custom.ModelManagerSP.DownloadStatus.downloading:
print("")
print(f"{model.fileName}: {model.downloadProgress}")
print("")
@@ -0,0 +1,22 @@
"""
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
from openpilot.sunnypilot import get_file_hash
from openpilot.sunnypilot.models.default_model import MODEL_HASH_PATH, SUPERCOMBO_ONNX_PATH
import hashlib
class TestDefaultModel:
def test_compare_onnx_hashes(self):
supercombo_hash = get_file_hash(SUPERCOMBO_ONNX_PATH)
combined_hash = hashlib.sha256(supercombo_hash.encode()).hexdigest()
with open(MODEL_HASH_PATH) as f:
current_hash = f.read().strip()
assert combined_hash == current_hash, "Run sunnypilot/models/default_model.py to update the default model name and hash"
@@ -0,0 +1,23 @@
import requests
from openpilot.sunnypilot.models.tinygrad_ref import get_tinygrad_ref
from openpilot.sunnypilot.models.fetcher import ModelFetcher
def fetch_tinygrad_ref():
response = requests.get(ModelFetcher.MODEL_URL, timeout=10)
response.raise_for_status()
json_data = response.json()
return json_data.get("tinygrad_ref")
def test_tinygrad_ref():
current_ref = get_tinygrad_ref()
remote_ref = fetch_tinygrad_ref()
assert remote_ref == current_ref, (
f"""tinygrad_repo ref does not match remote tinygrad_ref of current compiled driving models json.
Current: {current_ref}
Remote: {remote_ref}
Please run build-all workflow to update models."""
)
print("tinygrad_repo ref matches current compiled driving models json ref.")
@@ -0,0 +1,36 @@
import os
from openpilot.common.basedir import BASEDIR
def get_tinygrad_ref():
repo_path = os.path.join(BASEDIR, "tinygrad_repo")
git_path = os.path.join(repo_path, ".git")
try:
if os.path.isdir(git_path):
git_dir = git_path
else:
with open(git_path) as f:
line = f.read().strip()
git_dir = os.path.join(repo_path, line[8:])
with open(os.path.join(git_dir, "HEAD")) as f:
ref = f.read().strip()
if ref.startswith("ref:"):
with open(os.path.join(git_dir, ref.split(" ", 1)[1])) as f:
return f.read().strip()
return ref
except Exception as e:
print(f"Error getting tinygrad_repo ref: {e}")
return None
def main():
current_ref = get_tinygrad_ref()
if current_ref:
print(current_ref)
else:
print("")
if __name__ == "__main__":
main()
+189
View File
@@ -0,0 +1,189 @@
from __future__ import annotations
import json
import math
import numpy as np
from typing import Any, cast
from openpilot.common.constants import CV
from openpilot.common.params import Params
DIRECTIONS = ('left', 'right', 'straight')
MODIFIABLE_DIRECTIONS = ('left', 'right')
EARTH_MEAN_RADIUS = 6371007.2
SPEED_CONVERSIONS = {
'km/h': CV.KPH_TO_MS,
'mph': CV.MPH_TO_MS,
}
class Coordinate:
def __init__(self, latitude: float, longitude: float) -> None:
self.latitude = latitude
self.longitude = longitude
self.annotations: dict[str, float] = {}
@classmethod
def from_mapbox_tuple(cls, t: tuple[float, float]) -> Coordinate:
return cls(t[1], t[0])
def as_dict(self) -> dict[str, float]:
return {'latitude': self.latitude, 'longitude': self.longitude}
def __str__(self) -> str:
return f'Coordinate({self.latitude}, {self.longitude})'
def __repr__(self) -> str:
return self.__str__()
def __eq__(self, other) -> bool:
if not isinstance(other, Coordinate):
return False
return (self.latitude == other.latitude) and (self.longitude == other.longitude)
def __sub__(self, other: Coordinate) -> Coordinate:
return Coordinate(self.latitude - other.latitude, self.longitude - other.longitude)
def __add__(self, other: Coordinate) -> Coordinate:
return Coordinate(self.latitude + other.latitude, self.longitude + other.longitude)
def __mul__(self, c: float) -> Coordinate:
return Coordinate(self.latitude * c, self.longitude * c)
def dot(self, other: Coordinate) -> float:
return self.latitude * other.latitude + self.longitude * other.longitude
def distance_to(self, other: Coordinate) -> float:
# Haversine formula
dlat = math.radians(other.latitude - self.latitude)
dlon = math.radians(other.longitude - self.longitude)
haversine_dlat = math.sin(dlat / 2.0)
haversine_dlat *= haversine_dlat
haversine_dlon = math.sin(dlon / 2.0)
haversine_dlon *= haversine_dlon
y = haversine_dlat \
+ math.cos(math.radians(self.latitude)) \
* math.cos(math.radians(other.latitude)) \
* haversine_dlon
x = 2 * math.asin(math.sqrt(y))
return x * EARTH_MEAN_RADIUS
def minimum_distance(a: Coordinate, b: Coordinate, p: Coordinate):
if a.distance_to(b) < 0.01:
return a.distance_to(p)
ap = p - a
ab = b - a
t = np.clip(ap.dot(ab) / ab.dot(ab), 0.0, 1.0)
projection = a + ab * t
return projection.distance_to(p)
def distance_along_geometry(geometry: list[Coordinate], pos: Coordinate) -> float:
if len(geometry) <= 2:
return geometry[0].distance_to(pos)
# 1. Find segment that is closest to current position
# 2. Total distance is sum of distance to start of closest segment
# + all previous segments
total_distance = 0.0
total_distance_closest = 0.0
closest_distance = 1e9
for i in range(len(geometry) - 1):
d = minimum_distance(geometry[i], geometry[i + 1], pos)
if d < closest_distance:
closest_distance = d
total_distance_closest = total_distance + geometry[i].distance_to(pos)
total_distance += geometry[i].distance_to(geometry[i + 1])
return total_distance_closest
def coordinate_from_param(param: str, params: Params = None) -> Coordinate | None:
if params is None:
params = Params()
json_str = params.get(param)
if json_str is None:
return None
pos = json.loads(json_str)
if 'latitude' not in pos or 'longitude' not in pos:
return None
return Coordinate(pos['latitude'], pos['longitude'])
def string_to_direction(direction: str) -> str:
for d in DIRECTIONS:
if d in direction:
if 'slight' in direction and d in MODIFIABLE_DIRECTIONS:
return 'slight' + d.capitalize()
return d
return 'none'
def maxspeed_to_ms(maxspeed: dict[str, str | float]) -> float:
unit = cast(str, maxspeed['unit'])
speed = cast(float, maxspeed['speed'])
return float(SPEED_CONVERSIONS[unit] * speed)
def field_valid(dat: dict, field: str) -> bool:
return field in dat and dat[field] is not None
def parse_banner_instructions(banners: Any, distance_to_maneuver: float = 0.0) -> dict[str, Any] | None:
if not len(banners):
return None
instruction = {}
# A segment can contain multiple banners, find one that we need to show now
current_banner = banners[0]
for banner in banners:
if distance_to_maneuver < banner['distanceAlongGeometry']:
current_banner = banner
# Only show banner when close enough to maneuver
instruction['showFull'] = distance_to_maneuver < current_banner['distanceAlongGeometry']
# Primary
p = current_banner['primary']
if field_valid(p, 'text'):
instruction['maneuverPrimaryText'] = p['text']
if field_valid(p, 'type'):
instruction['maneuverType'] = p['type']
if field_valid(p, 'modifier'):
instruction['maneuverModifier'] = p['modifier']
# Secondary
if field_valid(current_banner, 'secondary'):
instruction['maneuverSecondaryText'] = current_banner['secondary']['text']
# Lane lines
if field_valid(current_banner, 'sub'):
lanes = []
for component in current_banner['sub']['components']:
if component['type'] != 'lane':
continue
lane = {
'active': component['active'],
'directions': [string_to_direction(d) for d in component['directions']],
}
if field_valid(component, 'active_direction'):
lane['activeDirection'] = string_to_direction(component['active_direction'])
lanes.append(lane)
instruction['lanes'] = lanes
return instruction
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 sunnypilot
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@@ -0,0 +1 @@
{"input_std":[[8.319796],[0.8197253],[0.2695665],[0.042975243],[0.82591987],[0.8242552],[0.82166827],[0.7989035],[0.78012997],[0.7541669],[0.727379],[0.042928882],[0.042931043],[0.04292182],[0.042691696],[0.042392965],[0.04186602],[0.041271493]],"model_test_loss":0.026207376271486282,"input_size":18,"current_date_and_time":"2023-08-05_00-22-46","input_mean":[[26.074522],[0.02839493],[0.0066744387],[0.002057746],[0.02693295],[0.026884235],[0.027213756],[0.03047188],[0.032183405],[0.03682154],[0.037578877],[0.0020827646],[0.0020767434],[0.002068271],[0.0019987107],[0.001959759],[0.0019052405],[0.0017531246]],"input_vars":["v_ego","lateral_accel","lateral_jerk","roll","lateral_accel_m03","lateral_accel_m02","lateral_accel_m01","lateral_accel_p03","lateral_accel_p06","lateral_accel_p10","lateral_accel_p15","roll_m03","roll_m02","roll_m01","roll_p03","roll_p06","roll_p10","roll_p15"],"output_size":1,"layers":[{"dense_1_b":[[-0.040739167],[2.3032],[0.12316159],[-0.2659433],[-0.14477858],[2.5866256],[0.007428121]],"dense_1_W":[[-0.00041187002,-0.43338582,0.00060599507,-0.1214792,0.11413983,-0.5961203,0.28133482,0.20372222,-0.3518884,0.004369656,0.059917737,-0.8609317,0.057816904,0.6169038,0.3990334,0.008638719,0.40356407,-0.16347249],[2.204209,-0.6977474,0.0003794136,-0.029842956,0.42360333,-0.03850665,0.77470416,0.13669024,-1.1576334,-0.2837559,0.290581,-0.20709048,0.06827377,0.39236906,0.2664924,-0.070944384,0.061474152,-0.18787181],[0.0027994395,1.1713752,3.9056025,-0.2533814,-0.5706249,-0.31465232,-0.28078023,0.9239433,0.8800892,0.45323586,-1.5842354,1.1251147,0.001203793,-0.631376,0.17771868,-0.5216376,-0.3776946,0.2864524],[-0.45875242,-0.35037747,-0.0044959267,0.2115901,0.49776042,0.40634266,0.16292186,0.085123494,-0.9553305,-0.6551008,1.0865217,-0.71530885,-0.122710146,0.41569617,0.053198084,-0.013457165,0.36482632,-0.3238176],[-0.002217434,1.7620963,-1.2475897,0.2861537,2.5786693,1.6882615,2.2971427,0.5035391,-3.1869397,-3.7319245,-1.4041471,-1.1169863,-0.17736337,0.21103925,0.1615338,-0.7596914,0.4106967,0.9845423],[2.4344306,-0.5410767,0.0015357351,-0.4139311,-0.2258391,0.22010726,-0.43920308,0.3511928,0.9479655,0.80018866,-0.516027,0.007953332,0.033446178,-0.082440384,0.35339966,-0.26683265,-0.33867463,0.3848125],[-0.006754755,0.48652485,-0.00026165723,-0.07224382,-0.032497358,0.3571908,-0.30286846,-0.38309038,0.36634418,0.2480767,-0.20110212,0.7545479,0.11547977,-0.7146525,-0.018818611,-0.31016228,0.010726242,0.2242711]],"activation":"σ"},{"dense_2_W":[[0.5528864,0.008744154,0.6015725,0.22935376,-0.51005757,-0.5996119,-0.061544895],[-1.4023882,0.050312754,-0.09053194,-0.5902193,0.3458193,-0.14508769,0.946796],[-1.1020836,-1.510418,0.81544393,-0.40635344,-0.4560999,-0.2849373,1.1164322],[-0.4434681,-0.6467564,0.35384342,-0.4493043,-0.23754148,-0.24804021,0.9237547],[-0.27906877,-0.9630674,0.42410734,-0.76862967,-0.59738564,-0.70335937,-0.18449919],[-0.50024533,-0.013576657,0.25596973,0.105690226,-0.52567744,0.80972326,0.7098216],[-0.8556806,-0.14960146,-0.14877279,0.25888273,-0.18901795,-0.16515623,0.7054368],[-1.041555,0.5246004,0.87523067,-0.4851678,-0.5188004,1.3508142,1.344452],[0.83991337,0.54517484,0.09614608,0.028868627,0.35332617,0.22425945,-0.24393064],[0.48884755,-0.6723522,0.161554,-0.18337585,0.010732531,0.042358696,-0.94669324],[0.7589174,0.44016284,-0.21002136,-0.7584538,-0.1012028,-0.44348133,-0.34792235],[0.68993133,-0.13230318,-0.20205282,0.33807474,0.2551999,-0.7216631,-0.9246813],[0.55567986,0.92921287,-0.3879664,-0.06419992,0.44321644,0.34334105,-0.7811811]],"activation":"σ","dense_2_b":[[0.009339096],[-0.027214382],[-0.19324711],[-0.1799901],[-0.3219004],[-0.0076973503],[0.031202752],[0.1887236],[-0.10121091],[-0.21036065],[-0.02065366],[-0.068702094],[0.096490316]]},{"dense_3_W":[[0.5728007,-0.3089611,0.28924087,-0.5445901,0.35486373,-0.44516575,-0.5423116,0.1779453,0.58426845,0.66963387,-0.31166366,0.6550927,0.50334716],[-0.40078798,-0.027345562,-0.5357739,-0.5126658,-0.5254064,-0.32021227,-0.44433862,-0.79833746,-0.10665151,0.20663711,-0.17090832,0.98109436,0.78809434],[-0.17410587,0.23138371,0.66809905,0.2743278,0.096785605,0.5667218,0.52852666,0.4389385,-0.4016732,0.05424836,-0.7506198,-0.71257013,-0.20795672]],"activation":"identity","dense_3_b":[[0.02470415],[0.1408834],[-0.05001292]]},{"dense_4_W":[[-0.3242929,-0.272474,0.8721407]],"dense_4_b":[[-0.046378713]],"activation":"identity"}]}
@@ -0,0 +1 @@
{"input_std":[[8.821153],[1.1607649],[0.47132117],[0.042723242],[1.137403],[1.1458488],[1.154415],[1.1424824],[1.1269064],[1.1112212],[1.0889144],[0.04246643],[0.04255451],[0.042637058],[0.04284904],[0.04293054],[0.04296549],[0.04289919]],"model_test_loss":0.010373540222644806,"input_size":18,"current_date_and_time":"2023-08-05_01-15-19","input_mean":[[22.519371],[-0.051268775],[0.008842468],[-0.0062438333],[-0.049525455],[-0.050104525],[-0.050982136],[-0.044812053],[-0.03977025],[-0.029485626],[-0.022126574],[-0.00628094],[-0.006273981],[-0.00626523],[-0.006110039],[-0.005949586],[-0.0056778532],[-0.005516688]],"input_vars":["v_ego","lateral_accel","lateral_jerk","roll","lateral_accel_m03","lateral_accel_m02","lateral_accel_m01","lateral_accel_p03","lateral_accel_p06","lateral_accel_p10","lateral_accel_p15","roll_m03","roll_m02","roll_m01","roll_p03","roll_p06","roll_p10","roll_p15"],"output_size":1,"layers":[{"dense_1_b":[[2.145696],[-2.4282207],[-0.0467647],[0.10926833],[0.0520818],[0.04222916],[-0.5462429]],"dense_1_W":[[0.89382285,0.05062796,0.92885584,0.07922598,0.5873072,1.1331865,-0.91537136,-0.13934332,0.17509349,-0.017687947,0.1421727,0.07907387,-0.43241176,-0.29524758,0.64221257,0.25150946,-0.05171897,-0.2579187],[-0.98185164,0.15350275,0.9519401,-0.09016293,0.5030077,0.777908,-0.5172474,-0.1343053,0.060967423,0.059980214,0.10476908,0.17575294,-0.20688392,-0.37559837,0.5713468,0.08849066,0.06383742,-0.21201873],[-0.0056540756,0.34154844,-0.0002563853,0.1807267,0.48449796,1.7143611,-1.3936676,-0.35775846,-0.5523075,-0.15023966,0.5988954,0.72132236,0.18023057,-0.46348006,-0.3817839,0.23941651,-0.06494792,-0.41726813],[-0.01459551,1.354641,6.4718165,-0.45382518,-0.35718468,0.69441503,-0.12716621,-0.10042224,-0.016146144,-0.4092775,-1.1707406,0.24052495,0.18990588,-0.1906599,0.070675336,0.2473157,-0.13808818,0.048208434],[-0.0009390727,1.1310523,0.0082692,-0.10876187,0.25259772,1.0159874,-0.72615826,0.107342415,0.0071694367,-0.065621555,0.13799563,-0.24110964,-0.073862016,-0.496919,-0.3644365,0.16880687,0.13885318,0.15355359],[-0.0016120744,-1.5514591,-0.000675083,0.40327275,0.8975076,-0.8923279,0.7910547,-0.40612024,-0.35091725,-0.101295106,0.38604048,-0.3228168,-0.17239958,-0.3530256,0.58767676,0.15752153,0.060442753,-0.32661223],[0.028779855,2.903487,-0.022664804,0.67652136,0.86524385,1.6721133,1.4576133,1.4279684,0.98554605,0.5130255,-0.469917,-0.0684012,0.4178242,0.3712093,-0.7501333,-0.06791517,0.33725083,-0.09968473]],"activation":"σ"},{"dense_2_W":[[0.72010493,-0.22845428,0.34250122,-0.10947913,-0.09878085,0.19209231,0.3437535],[-0.19205496,-0.07013072,-0.7416267,0.25280657,-0.1804019,0.4552836,0.12560841],[0.3121345,0.351005,0.5316866,0.03252624,-0.057188597,-0.9498188,0.07712814],[-0.16806532,0.25612888,0.43424875,-0.24325378,0.23957624,-0.14204793,-0.13016012],[-0.02399125,-0.71077454,0.03666176,-0.044081435,-0.495433,0.2724298,0.22011437],[-0.24902506,-0.59004927,-0.13717845,-0.5492086,-0.8540712,1.7531782,-0.18482044],[0.045421652,0.13694939,0.35765722,-0.28403643,0.13330263,-0.053298414,0.07322283],[-0.34804162,-0.38957596,-0.1432544,0.071290284,-0.538751,1.1790421,0.29278603],[0.45716798,-0.3516285,-0.2764383,0.4446462,0.76846844,-0.7801458,0.49539566],[0.07269321,-0.15379232,-0.8779124,-0.92490155,-0.91285044,1.7692323,0.028679641],[-0.763075,0.17418097,-0.2823778,0.04659932,-0.030475633,0.37596756,-0.66006523],[-0.30286422,-0.03590232,-0.012065834,-0.16185908,-0.2584895,0.3575371,-0.36891615],[-0.39941847,0.09128256,-0.32452282,-0.36513522,0.06978018,0.028525386,-0.7303688]],"activation":"σ","dense_2_b":[[0.048172507],[-0.07530213],[-0.340101],[-0.022203589],[0.009521725],[0.46328664],[0.004919447],[0.022925986],[-0.15940326],[0.40359482],[-0.14555077],[-0.02970779],[-0.24673922]]},{"dense_3_W":[[-0.32974115,-0.5017099,0.5581341,-0.22340928,-0.03365036,-0.20835784,0.537433,0.5391966,-0.36658195,-0.5869503,0.17776869,-0.013170913,0.49127203],[-0.29820803,0.28987518,-0.24515595,0.25772423,-0.04187076,0.69620353,-0.3389867,0.12667182,0.150237,0.5090255,0.49386412,-0.3637399,-0.01771473],[0.5784079,-0.0028831854,0.43896967,0.60790384,-0.4540079,-0.45909828,0.046820417,-0.56937873,0.530507,-0.1309796,0.32449916,-0.49291292,-0.07336644]],"activation":"identity","dense_3_b":[[0.0460099],[-0.07466251],[0.050609488]]},{"dense_4_W":[[0.13093115,-0.7773025,0.81287336]],"dense_4_b":[[0.057754416]],"activation":"identity"}]}
@@ -0,0 +1 @@
{"input_std":[[7.7137036],[0.9591701],[0.43025136],[0.042929597],[0.94636554],[0.9494532],[0.9542092],[0.9331239],[0.92229235],[0.90257126],[0.88295245],[0.04270026],[0.042758208],[0.042818923],[0.042808957],[0.04270763],[0.04245825],[0.04215921]],"model_test_loss":0.013933051377534866,"input_size":18,"current_date_and_time":"2023-08-05_03-45-12","input_mean":[[22.835394],[0.021439364],[0.0072024865],[-0.030873412],[0.021618834],[0.022110935],[0.02246376],[0.027221544],[0.027849164],[0.028268991],[0.032981515],[-0.030950077],[-0.030914158],[-0.030886404],[-0.030796975],[-0.03080609],[-0.030815149],[-0.030869272]],"input_vars":["v_ego","lateral_accel","lateral_jerk","roll","lateral_accel_m03","lateral_accel_m02","lateral_accel_m01","lateral_accel_p03","lateral_accel_p06","lateral_accel_p10","lateral_accel_p15","roll_m03","roll_m02","roll_m01","roll_p03","roll_p06","roll_p10","roll_p15"],"output_size":1,"layers":[{"dense_1_b":[[-0.04303998],[0.18157572],[-0.08682473],[2.9495513],[1.4374219],[0.6783021],[-2.7817473]],"dense_1_W":[[-0.0047104284,-0.6137708,0.002341153,0.0833492,0.08768307,-0.81527835,0.6300508,-0.16833228,-0.25419337,-0.031592067,0.122596905,-0.10794349,-0.06970466,0.1521993,-0.04051286,0.059251916,-0.11607053,0.024402162],[-0.06346717,-0.8082159,0.0040190252,0.3896669,0.06872366,-0.7609862,1.0698833,-0.06101861,0.1457311,0.19787341,-0.21086167,-0.07617529,-0.40358907,0.012452437,0.10700336,0.3594503,0.025611963,-0.14057784],[-0.010002668,-1.4602287,-5.6766963,0.03532623,-0.3498076,-0.005860239,0.092076786,-0.22858407,-0.4043539,0.277433,2.0291262,0.044144016,0.06367758,-0.2504535,0.3463553,-0.27935135,0.18728429,-0.15520296],[1.595175,1.1790471,0.33827057,-0.36523697,-0.028244315,0.88139707,-1.6474634,0.3107364,-0.13230316,0.019280735,-0.015979009,0.5001427,-0.13290524,-0.15675303,0.12809546,-0.09519324,-0.046115097,0.14121765],[0.01943066,0.3716076,-0.048877437,-0.3783698,0.8343788,-0.51018566,1.0465596,-0.123417296,-0.446517,0.0398392,0.5281696,-0.3002102,-0.4030578,-0.23385769,0.45266014,-0.15139927,-0.084088355,-0.8366423],[-0.05169121,0.34580684,-0.0006902231,-0.0828817,-0.67921466,1.1092558,-0.4253136,0.008052844,-0.08571924,0.14040273,-0.057995982,0.5459289,-0.1640265,-0.24621837,-0.44702664,0.25875458,-0.16363451,0.07772843],[-1.5420308,-0.0795151,0.32449803,0.09269649,-0.12254087,0.95711464,-0.32903105,0.03880426,-0.24505275,0.3849109,-0.085893035,0.1646195,-0.14589338,-0.44051296,-0.05752575,0.38210398,0.32775694,-0.34554252]],"activation":"σ"},{"dense_2_W":[[-1.1991313,-0.99167293,-0.51472193,0.16773303,0.02101984,0.2913248,1.2563334],[0.50807565,0.2559231,0.3110827,-0.7911515,-0.2996264,-0.76801026,0.21431924],[-0.85551465,-0.3015842,-0.283833,0.5011012,-1.0202202,0.6740454,0.972628],[0.98223805,1.0016109,-0.09864264,0.13172226,-0.12697673,-0.40963414,-0.35780042],[-1.1385525,-0.88758576,-0.2415131,1.0169281,-0.37200353,0.83228046,0.58273304],[0.21168564,-0.01694945,-0.13944875,0.12901805,-0.58597475,-0.23450175,0.07449124],[0.6887359,0.49634412,-0.4524045,-0.5046205,0.80216706,-0.37323585,0.15390924],[-0.86359656,-0.8294278,-0.10975485,-0.111551575,-0.21511969,1.1387116,0.014124521],[0.6704329,0.7197621,-0.16924202,-0.03436297,0.3256051,-0.6220993,-0.3785999],[0.88013136,0.211954,0.26013535,-0.5998456,-0.14124529,-1.0012022,0.30486783],[0.8889406,0.25632593,0.029823503,-0.6760066,0.40066594,-0.6796646,-0.42230535],[-0.09985791,-0.6314821,-0.6048359,-0.28479984,0.022334062,-0.11070772,-0.50172895],[-1.0790496,-0.81828046,-0.26459435,-0.15203167,-0.10264537,0.7340549,0.6219628]],"activation":"σ","dense_2_b":[[0.07743759],[-0.08656865],[-0.028110785],[0.011056744],[0.33631137],[-0.33865649],[0.093462616],[0.28041044],[-0.1485106],[-0.10288075],[-0.118501574],[-0.19659644],[0.23902157]]},{"dense_3_W":[[-0.21247761,-0.3863918,-0.35711917,-0.15159392,-0.3087554,0.27256498,0.059494548,0.79742235,-0.6449726,-0.3814191,-0.6635471,0.14567713,0.6240987],[-0.60485286,0.36261612,-0.3062913,0.5288856,-0.8237054,0.14184983,0.36521724,-0.6798562,0.19129165,0.76819366,0.3231601,0.028053993,-0.89101535],[0.5106036,-0.047764797,0.003893947,0.122273564,-0.616694,-0.30744067,0.10899531,-0.98274595,0.045314062,0.3392997,0.15401444,0.406467,-0.3275237]],"activation":"identity","dense_3_b":[[0.0072817504],[0.054671016],[0.3754705]]},{"dense_4_W":[[0.18990728,-1.0539979,-0.04924367]],"dense_4_b":[[-0.04299712]],"activation":"identity"}]}
@@ -0,0 +1 @@
{"input_std":[[6.227712],[1.1068953],[1.0287797],[0.031075474],[1.0763543],[1.0856286],[1.0965383],[1.126149],[1.1471479],[1.1817935],[1.207976],[0.03088383],[0.030962178],[0.031021496],[0.031159075],[0.031329848],[0.031462345],[0.031319857]],"model_test_loss":0.004972144495695829,"input_size":18,"current_date_and_time":"2023-08-31_18-04-43","input_mean":[[24.297255],[0.05408088],[-0.024366971],[0.017203888],[0.04666708],[0.049268622],[0.050475407],[0.05590255],[0.04882452],[0.04647568],[0.05159426],[0.017288238],[0.017341834],[0.017397404],[0.017483119],[0.017647635],[0.018217644],[0.018788869]],"input_vars":["v_ego","lateral_accel","lateral_jerk","roll","lateral_accel_m03","lateral_accel_m02","lateral_accel_m01","lateral_accel_p03","lateral_accel_p06","lateral_accel_p10","lateral_accel_p15","roll_m03","roll_m02","roll_m01","roll_p03","roll_p06","roll_p10","roll_p15"],"output_size":1,"layers":[{"dense_1_b":[[-0.022334022],[-0.100295104],[-0.110021666],[-0.37309307],[-0.18126963],[-0.90585893],[-0.5534994]],"dense_1_W":[[0.0030549474,1.145932,-0.09721581,0.18435507,-0.2573822,-0.19281419,0.43596926,0.1603594,0.10032455,-0.051014125,0.4603509,0.38878915,-0.22585483,-0.29935592,-0.04472448,-0.15199424,0.44078583,-0.19637495],[0.007835068,0.57188964,0.18070067,-0.2610156,-0.17529912,0.14483498,-0.23499107,0.4929414,0.10448897,-0.45648035,0.029684572,0.1314099,-0.16433047,0.2065975,0.07887834,-0.0060373824,-0.20691155,0.13691476],[-0.005327477,1.7886202,2.9505506,0.09841906,-1.9402149,-1.3852259,-0.043792397,0.660079,0.60579574,0.24349208,0.29671067,0.08202194,-0.13981113,0.36684915,0.057882063,-0.41412312,0.31871474,-0.02327852],[1.0903977,-1.1873779,0.14879608,0.040507846,0.040393896,0.13455993,0.9753803,0.085223004,-0.06493875,-0.20206894,0.18746802,-0.24526027,-0.08939371,0.2430548,0.13119027,0.15268146,0.20207612,-0.083613925],[0.0021948,3.2657847,-0.18798442,-0.12689658,-0.30403885,0.3367026,0.91909635,1.7143666,0.045331903,-0.31545398,-0.051042344,-0.3529171,0.29837632,0.09466574,-0.8073861,-0.0039647045,-0.1720164,0.5201512],[1.0928359,1.2096838,-0.14703602,0.018953208,-0.03811592,-0.2855322,-0.8143947,0.05326237,-0.066094,0.064099275,-0.06976516,-0.06933611,0.34876755,-0.25860986,-0.14344722,-0.07053271,-0.20750608,0.025129952],[-0.0055736825,0.64105284,0.10726179,-0.17012087,-0.001185817,0.5677425,-0.93299127,0.09094073,0.0109323105,0.2228847,-0.16700141,0.30421817,0.13674305,-0.49573237,-0.13086164,0.31447932,0.15766789,-0.16813341]],"activation":"σ"},{"dense_2_W":[[0.27341422,-0.47601527,-0.6289435,1.3114856,-0.18440256,-0.69279665,-0.58332384],[-0.34085867,-0.29880068,-0.9368486,0.2080702,0.6225143,-0.6358373,-1.1943169],[0.6918406,0.15412113,0.04159215,-0.6581899,0.24066101,-0.043641392,0.045506563],[-0.026726253,-0.81909335,0.16420767,0.00291583,-0.3189569,-0.5359374,-0.7172654],[-0.8483084,-0.35060802,-0.41203314,-0.5305087,0.47287297,-0.28290507,-0.8737458],[0.35316753,0.90733016,-0.2770915,0.20165613,0.20155826,0.36213335,-0.24496384],[-0.5022167,-0.5612638,0.38854852,-0.018215526,-0.19334692,-0.4468064,-0.86793435],[0.47059315,0.23489894,0.41844767,-0.08509097,-0.31178007,0.11894885,0.5030957],[0.24172585,0.24865924,-0.15847187,-0.7011813,0.48085362,0.14869425,0.23137042],[-0.618487,0.12818213,-0.6849712,0.29365247,0.20846452,-0.063214555,0.125115],[0.5387756,0.06612413,-0.4151303,-0.63775194,0.5008791,-0.059868183,0.5720243],[-0.32397416,-0.08635292,-0.4818767,-0.67876416,-0.5552992,0.2187673,-0.4787086],[-0.89717907,-0.42284042,0.31489655,-0.8514086,-0.7674914,-0.41609457,-0.006730729]],"activation":"σ","dense_2_b":[[0.44311488],[0.11209076],[-0.0894076],[0.1847591],[-0.1593725],[-0.022556875],[0.21991341],[-0.05744998],[-0.057585794],[-0.2627398],[-0.054548565],[0.105285086],[-0.19715697]]},{"dense_3_W":[[-0.35089782,-0.39416888,-0.30615157,-0.46847108,-0.26617768,0.43837857,-0.485962,-0.2902945,-0.17870793,0.2757293,0.6125576,-0.24665116,-0.21227285],[-0.37411463,0.19765407,-0.014807391,0.06321688,0.37360784,0.22288837,-0.116587795,0.029183213,0.6631047,0.11275381,-0.27042058,-0.44573787,0.14713542],[-0.50715375,-0.38213205,0.5145017,-0.6412086,-0.39256987,0.058481824,-0.437418,0.30487192,0.5463277,-0.17863718,0.47274628,-0.5652254,-0.2641463]],"activation":"identity","dense_3_b":[[0.0534896],[0.01632865],[0.023216043]]},{"dense_4_W":[[0.3922371,0.50241,1.2339215]],"dense_4_b":[[0.022297831]],"activation":"identity"}]}
@@ -0,0 +1 @@
{"input_std":[[9.281861],[1.8477924],[0.7977224],[0.047467366],[1.7969038],[1.8168812],[1.8351218],[1.785757],[1.7335743],[1.6658221],[1.5893887],[0.047346078],[0.0473731],[0.047383286],[0.047291175],[0.047300573],[0.04712479],[0.046799928]],"model_test_loss":0.027355343103408813,"input_size":18,"current_date_and_time":"2023-08-05_05-11-44","input_mean":[[21.655252],[-0.07694559],[-0.006081294],[-0.007598456],[-0.07309746],[-0.075890236],[-0.07804942],[-0.076106496],[-0.07184982],[-0.06528668],[-0.060404416],[-0.0077262702],[-0.0077046235],[-0.0076850485],[-0.007687444],[-0.007708868],[-0.0077959923],[-0.008017422]],"input_vars":["v_ego","lateral_accel","lateral_jerk","roll","lateral_accel_m03","lateral_accel_m02","lateral_accel_m01","lateral_accel_p03","lateral_accel_p06","lateral_accel_p10","lateral_accel_p15","roll_m03","roll_m02","roll_m01","roll_p03","roll_p06","roll_p10","roll_p15"],"output_size":1,"layers":[{"dense_1_b":[[-0.35776415],[-0.126288],[0.007988413],[0.03850029],[-0.056796823],[0.0072075897],[0.17376427]],"dense_1_W":[[0.010538558,6.7293744,-0.007391298,-1.035045,-0.47446147,1.0940393,0.08225446,-0.5795131,0.573115,1.82923,-0.16999252,0.8818023,0.03560901,-0.9470653,-1.042151,1.1532398,1.9009485,-1.2265956],[0.009010456,-1.3618345,0.039026346,0.22943486,-0.6669799,-0.768271,-0.16828564,-0.13406865,0.19760236,-0.18953702,-0.019137766,0.1468623,0.26367718,0.4732375,0.73679143,0.62450147,0.30198866,-0.93340015],[-0.044318866,2.2138615,9.620889,-0.66480356,-0.06849887,-0.038568456,-0.30580127,-0.088089585,0.31187677,0.5968372,-1.1462088,0.13234706,0.29166338,-0.69978577,-0.01790655,-0.097928315,-0.56950736,0.7560642],[1.3758256,0.8700429,-0.24295154,0.20832618,-0.7735097,1.0379355,-0.60753095,-0.5457418,-1.0892137,-0.6708325,0.24982774,0.09251328,0.08596103,-0.58034134,-0.3046114,-0.19754426,0.006461508,0.5989185],[0.06314328,-2.492993,-0.28200585,0.49902886,0.8513198,-0.5704965,1.0960953,-0.08426956,-0.76074624,0.10669376,0.02775254,-0.42194095,-0.34657434,-0.10863868,0.17019278,0.0963825,-0.10169116,0.21243806],[0.007984091,-1.9276509,-0.0013926353,0.04057924,1.4438432,1.3440262,-2.506722,1.700801,0.72410774,0.13471895,-0.18753268,0.7303607,0.018133093,-0.71643597,0.07050904,-0.30161944,0.085108586,0.061202843],[0.9413167,-0.9954305,-0.19510143,-0.4711845,-0.12398665,-0.45175523,1.0616771,0.28550953,-0.55543137,-0.21576759,-0.2364377,-0.012782618,-0.050565187,0.257694,-0.20975965,0.00022543047,-0.08760746,0.4983852]],"activation":"σ"},{"dense_2_W":[[-0.71804935,0.017023304,-0.099854074,-0.3262651,-0.402829,-0.12073083,0.13537839],[-0.44002575,-0.1071093,-0.58886194,-0.17319413,-0.21134079,0.23135148,0.0006880979],[-0.55711204,-0.8693639,-0.6443761,-0.7382846,0.18980889,-0.6082511,-0.63103676],[0.11338112,-0.5327033,0.2856197,0.32239884,-0.72682667,0.5302305,-0.45094746],[-0.35197002,-0.14440043,-0.025249843,-0.48697743,0.3340018,-0.25992322,0.0050561456],[0.34757507,0.15670523,-0.14263277,0.50704384,-0.020171141,0.6408679,-0.3074123],[0.40258837,-0.59363264,0.35948923,-0.060853776,-0.0072541097,0.89743376,-0.49789146],[-0.63476,0.29580453,0.1689314,-0.5061521,0.24901053,-0.23218995,0.57968193],[-0.66173035,0.50861925,-0.5845035,-0.6602214,0.8341883,-0.31437424,0.8046359],[0.038493533,0.15464582,-0.04848341,-0.57820857,-0.25891733,-0.47527292,0.21441916],[-0.15464531,-0.07454202,-0.8215851,-0.12614948,-0.5924209,0.00017916794,0.24154592],[0.6091424,-0.112086505,0.1144111,-0.31035227,-0.9237534,0.041003596,-0.3542808],[0.2989792,-0.23780549,0.116059326,-0.6056522,0.5499526,-0.9001413,0.5200723]],"activation":"σ","dense_2_b":[[-0.2638964],[-0.24607328],[-0.15493082],[-0.0071187904],[-0.23515384],[-0.09098133],[-0.034066215],[0.03609036],[-0.03842933],[-0.042302527],[-0.2439947],[-0.16342077],[-0.01030317]]},{"dense_3_W":[[0.45771673,-0.2084603,0.3570187,0.35835707,0.13684571,-0.582958,0.29889587,0.43543494,0.11841301,-0.26185623,-0.4988437,0.5752996,-0.28057483],[-0.19594137,0.050280698,-0.29057446,-0.2829161,-0.16987291,-0.21278952,-0.59946907,0.21295123,0.7040468,0.53549695,-0.52553934,-0.19560973,0.6233473],[-0.19091003,0.08669418,-0.5792192,0.57799137,-0.36263424,0.6037143,0.27898273,-0.30951327,-0.3572644,0.46720102,-0.5403428,0.32415462,-0.60570025]],"activation":"identity","dense_3_b":[[-0.0047377176],[0.023170695],[-0.021546118]]},{"dense_4_W":[[-0.12453941,-1.006034,0.96776205]],"dense_4_b":[[-0.02351544]],"activation":"identity"}]}
@@ -0,0 +1 @@
{"input_std":[[9.111214],[1.4682485],[0.57526964],[0.04511676],[1.4448825],[1.4523145],[1.460504],[1.4530647],[1.436759],[1.409998],[1.3768793],[0.04496706],[0.044998076],[0.04501952],[0.044998042],[0.04498694],[0.044818893],[0.04446676]],"model_test_loss":0.022049037739634514,"input_size":18,"current_date_and_time":"2023-08-05_05-39-06","input_mean":[[24.08155],[0.011368711],[0.0031438756],[-0.0055672466],[0.01115314],[0.0110932905],[0.010956869],[0.013351914],[0.012678603],[0.010665299],[0.007861591],[-0.0056356518],[-0.0056050173],[-0.005582452],[-0.0056302436],[-0.0057524433],[-0.0058822],[-0.006047828]],"input_vars":["v_ego","lateral_accel","lateral_jerk","roll","lateral_accel_m03","lateral_accel_m02","lateral_accel_m01","lateral_accel_p03","lateral_accel_p06","lateral_accel_p10","lateral_accel_p15","roll_m03","roll_m02","roll_m01","roll_p03","roll_p06","roll_p10","roll_p15"],"output_size":1,"layers":[{"dense_1_b":[[1.4668645],[0.0061378293],[0.14334162],[-1.507637],[-3.6057515],[-0.045111466],[0.18928593]],"dense_1_W":[[0.6566544,0.7743814,0.18751603,0.04858998,0.7569071,0.25307912,-2.068015,0.9910395,0.86449015,0.5162737,0.17964111,0.73959017,-0.13406877,-1.2188785,0.051854312,0.557273,0.42717713,-0.4706351],[0.025763594,2.3271172,7.5912685,-0.59063435,-1.0175278,0.07860616,-0.39720893,-1.3828933,0.27263463,0.5794174,-0.941214,0.5112712,0.31876096,-0.38133666,-0.17985544,0.50390506,-0.42106378,0.24962576],[0.05800794,4.917338,-0.050065108,0.5062318,2.31812,2.2346003,0.97258455,2.237544,3.1694891,3.0328865,2.4481356,0.87150276,0.23922867,-0.27206227,-0.76175624,-0.59474605,0.022229204,0.18751834],[0.91351914,0.42640725,0.21792875,-0.6499657,0.42711547,0.46455538,-0.7128904,0.6972329,0.07784378,0.15792462,0.86492527,0.16739717,0.3357893,-0.1592361,0.10722909,0.21031618,-0.019191844,-0.031056747],[-1.390049,0.89916164,0.30032212,0.0886132,0.81011575,-0.3205005,-2.085708,1.6698592,1.496016,0.86138624,0.12484511,0.47975743,-0.41812262,-0.7270122,-0.06460007,0.9231981,0.42574838,-0.64465237],[-0.001402944,-1.1569467,0.0005942974,0.063189715,-0.09013453,-1.2986813,1.6477252,-0.4343868,-0.08533708,-0.27364805,0.21535206,-0.34249792,-0.48679602,0.6684111,0.16701448,0.39430234,0.2199159,-0.27228448],[-1.1195272,0.015763488,-0.200724,0.05122529,0.40223718,0.5024332,-1.402779,-0.47795737,-0.24807225,-0.70503217,-0.21363245,0.76604843,-0.14105192,-0.4387646,-0.28273064,-0.21595778,0.03305617,0.22701742]],"activation":"σ"},{"dense_2_W":[[1.7721981,2.2011127,0.29388586,0.38426542,0.83704543,-2.9152303,0.5229557],[-0.36864644,0.42984635,-0.41658726,-0.041540317,0.18798253,0.38724944,-0.65996665],[-0.52116436,0.2358491,-0.2220775,-0.032654468,-0.48732167,0.3548198,-0.45838025],[-0.2911298,-0.46853513,0.03141412,-0.795555,-0.18514551,-0.08353839,-0.33849046],[-0.67049307,0.10367472,0.4490753,-0.55316794,-0.48152244,0.31841356,-0.14415056],[0.28696507,-1.2001104,-0.55151457,0.95023096,-1.3845363,-0.3174642,-0.3309214],[-0.6142683,0.0910831,-0.18282256,-0.44715023,0.37679994,0.44533804,0.027039269],[0.14790064,-0.27130985,-0.18719442,-0.13985236,-0.22100836,-0.6535676,-0.19135725],[-0.4416883,-0.37975168,-0.43114612,-0.07862877,0.20007153,0.95488816,-0.52858645],[-0.17171355,0.2863653,0.4144529,0.679835,0.34844747,-1.2170105,0.29281953],[0.06252894,0.095131,-0.13292563,-0.6726018,-0.39990836,-0.68776375,-0.36863],[0.7808943,-0.8408942,0.13730413,0.102169715,0.1682241,-0.5337488,1.5183735],[0.11278698,0.10173518,0.10506139,0.4002509,0.16067709,-1.1565727,0.75811535]],"activation":"σ","dense_2_b":[[-1.5210099],[-0.0006081214],[0.021954967],[0.032652497],[0.19418235],[-0.42663833],[0.11631468],[-0.27090475],[0.18836942],[-0.4026297],[-0.3532125],[-0.095932305],[-0.094078794]]},{"dense_3_W":[[-0.08552111,0.15208419,0.3206692,-0.46672416,-0.0039158463,-0.05807648,-0.5021984,0.15186986,-0.59495836,0.2405156,-0.5498271,0.070102595,0.26428217],[-0.63763195,0.14358082,0.45475405,0.4365134,0.5539486,-0.44107583,0.46288002,-0.1564132,0.39014947,-0.55990654,-0.05062592,-0.5582297,-0.45318148],[0.57558644,-0.5381224,-0.6278913,0.21171339,-0.06526249,-0.1312582,0.3146726,-0.45940813,-0.15466015,0.11860342,0.36926913,-0.16175088,0.17500523]],"activation":"identity","dense_3_b":[[-0.03552377],[0.059005316],[-0.037881076]]},{"dense_4_W":[[0.43992618,-0.9712287,0.43158254]],"dense_4_b":[[-0.04844147]],"activation":"identity"}]}
@@ -0,0 +1 @@
{"input_std":[[10.263361],[1.5407822],[1.6990757],[0.031916086],[1.4340314],[1.4340314],[1.4340314],[1.4340314],[1.4340314],[1.4340314],[1.4340314],[0.031784274],[0.031784274],[0.031784274],[0.031784274],[0.031784274],[0.031784274],[0.031784274]],"model_test_loss":0.050728149712085724,"input_size":18,"current_date_and_time":"2023-09-28_17-19-10","input_mean":[[21.170568],[0.0028904085],[-0.01997667],[-0.011566885],[0.0088682],[0.0088682],[0.0088682],[0.0088682],[0.0088682],[0.0088682],[0.0088682],[-0.011584887],[-0.011584887],[-0.011584887],[-0.011584887],[-0.011584887],[-0.011584887],[-0.011584887]],"input_vars":["v_ego","lateral_accel","lateral_jerk","roll","lateral_accel_m03","lateral_accel_m02","lateral_accel_m01","lateral_accel_p03","lateral_accel_p06","lateral_accel_p10","lateral_accel_p15","roll_m03","roll_m02","roll_m01","roll_p03","roll_p06","roll_p10","roll_p15"],"output_size":1,"layers":[{"dense_1_b":[[-0.8283137],[-0.6380811],[-0.14581172],[0.21757337],[-0.17584349],[-0.15822433],[-0.04526756]],"dense_1_W":[[-0.12061483,-2.6186888,-5.533526,1.2848519,0.10997799,0.09768942,0.29055217,-0.4338942,0.45353734,0.40870407,0.74510694,0.035301626,0.1254258,0.06881082,-0.43392724,-0.6579088,0.12848276,0.10206295],[0.47858974,1.0521514,0.13895132,-0.11845431,-0.6055809,-0.30129787,0.10536316,0.36635193,0.062468443,0.16439146,0.7524098,-0.08773286,0.08453637,0.14768569,0.054678865,0.04296218,-0.1013483,-0.017113626],[1.5098917,0.5894743,0.59322363,-0.40857914,-0.04409986,-0.059111845,-0.22999583,0.13513245,0.20424314,0.8996403,0.6257698,-0.012137316,-0.26447135,0.33300093,0.5780546,0.11556365,-0.21325883,-0.11405065],[-0.09270235,4.2095594,-0.03419901,-0.85176253,-0.792438,-1.1375993,-0.95709634,-0.5417718,0.13269264,0.34799594,0.8776221,0.23166949,0.25684002,0.14214455,0.31949127,-0.1842014,-0.048224565,0.06972774],[-0.07368613,7.987101,-1.5705101,-2.7268257,-1.6060512,-2.2150717,-1.7734491,-1.0044583,-0.9965295,-0.53904784,-0.71252525,0.82295734,0.079120055,0.8620408,0.040507376,0.5511871,0.57866305,-0.3000211],[-0.19393307,1.3685007,-0.81581175,-0.65915656,-0.26665,-0.18225093,-0.2348052,-0.613443,-0.4242962,-0.1411918,-0.40366364,0.14329708,-0.15040468,0.111927874,0.35063627,0.2598781,0.044979107,-0.13300987],[-0.15487364,1.5682242,-0.39758956,-1.613167,-1.2005973,-1.3606614,-1.4602566,-0.8778308,-0.8630755,-1.1338134,-1.5346371,0.44767708,-0.07334435,0.46988532,0.38771385,0.18633042,-0.31606805,0.49087706]],"activation":"σ"},{"dense_2_W":[[0.08773213,1.118686,-0.19026187,-0.18222769,0.05527968,-0.51330656,-0.036496755],[-0.5858637,-0.4254996,0.021144927,-0.08652764,-0.21240492,0.2946317,0.2629631],[0.20821263,-0.6136043,-0.2656985,-0.11885001,-0.23193532,0.573116,0.19101338],[1.3147112,0.8279403,-1.8892661,0.22684072,0.5924725,0.18479976,1.5572529],[0.09764118,0.8507632,0.54017293,-0.14499734,-0.08574703,-0.8551757,0.525648],[-0.63802594,-0.65069646,0.26210403,-0.24471927,0.48373717,-0.2968227,0.06695796],[-2.1634326,-0.6064817,-1.8872,1.6661983,0.24116987,-1.8317598,-1.4297333],[0.030689137,0.045585413,-0.619795,-0.3127205,0.00026456476,-1.2520658,-0.021226548],[-0.43981192,-0.6087822,0.26009014,-0.41205314,0.10397853,0.30045325,0.15389265],[-1.4882747,1.6382228,0.1060073,1.4234135,2.3527496,-1.4529006,-0.6220931],[0.32924128,-0.7916863,-0.16856577,-0.49799073,-0.3400066,0.34466445,0.47758287],[-0.3820367,-0.07035885,-0.770717,-0.08017813,-0.4997505,-0.5413408,-0.22099844],[-0.5534209,0.15586813,0.46922186,-0.285645,-0.43601796,-0.5457336,-0.09376932]],"activation":"σ","dense_2_b":[[-0.07499287],[0.056654293],[-0.033568453],[0.012748798],[-0.16169988],[-0.031001234],[0.4491975],[-0.18781444],[-0.022485357],[-0.4145325],[0.05514316],[-0.17725536],[-0.027867718]]},{"dense_3_W":[[0.48439038,0.11946673,-0.10710485,0.2569157,0.55854136,0.69526154,-0.045092683,-0.22340369,-0.057273377,-0.43256047,0.51690865,-0.45954293,0.5041862],[-0.39225766,0.5658039,0.13191655,-0.26512942,-0.45468462,0.029447814,-0.7955542,-0.47096017,0.19746478,-0.40087095,0.6816974,-0.313998,-0.2719639],[-0.20068921,-0.29952648,0.34961987,-0.66211593,-0.47286674,-0.1865282,0.25309294,0.27628332,0.5233655,0.30510098,0.5197866,-0.51454246,0.46288064]],"activation":"identity","dense_3_b":[[0.055204097],[0.070561714],[0.057622224]]},{"dense_4_W":[[-0.5929016,-1.4001877,-0.6662524]],"dense_4_b":[[-0.06424638]],"activation":"identity"}]}
@@ -0,0 +1 @@
{"input_std":[[9.342214],[1.5915664],[0.60113484],[0.048193663],[1.5680411],[1.57577],[1.5836853],[1.5711677],[1.5445132],[1.5007596],[1.4529978],[0.047915205],[0.04800539],[0.04808892],[0.04822398],[0.04817677],[0.047881734],[0.047405947]],"model_test_loss":0.019342588260769844,"input_size":18,"current_date_and_time":"2023-08-05_06-09-11","input_mean":[[22.757933],[-0.016342578],[-0.001405228],[-0.014619173],[-0.018091483],[-0.018382493],[-0.019270267],[-0.018759886],[-0.019559544],[-0.017848592],[-0.020014366],[-0.014564899],[-0.01457757],[-0.014600966],[-0.014757987],[-0.014915743],[-0.015121007],[-0.015359475]],"input_vars":["v_ego","lateral_accel","lateral_jerk","roll","lateral_accel_m03","lateral_accel_m02","lateral_accel_m01","lateral_accel_p03","lateral_accel_p06","lateral_accel_p10","lateral_accel_p15","roll_m03","roll_m02","roll_m01","roll_p03","roll_p06","roll_p10","roll_p15"],"output_size":1,"layers":[{"dense_1_b":[[-0.045577038],[-2.8160777],[-0.21418032],[2.9552083],[-0.06852827],[0.029141279],[-0.053249933]],"dense_1_W":[[0.0010664682,1.3813498,-8.153277,-0.009101015,-0.1484779,1.0008405,-1.5930991,-1.195274,0.6136762,0.3560462,-0.30077773,0.93531257,0.14550218,-0.40074933,-0.21716364,0.13834935,-0.0658936,-0.49427196],[-0.7338253,-0.037501235,-0.49922377,-1.0526338,-0.46141604,-1.3649396,0.7309814,-0.91667104,0.044683233,-0.18628967,-0.20878822,-0.2678598,0.48446324,0.51164204,0.09547851,0.41491362,-0.4524314,0.16594785],[-0.0032175342,3.5451593,-0.11421584,-0.2511033,0.27893174,0.6384996,0.80790603,1.3835542,1.8000495,2.249461,1.4026878,0.7851167,-0.35052946,0.041820426,-0.39790183,0.45703772,-0.30459648,0.18287674],[0.72289664,-0.77138704,-0.5025135,-0.49323097,-0.39893976,-0.8779149,0.6848096,-0.58729875,-0.16643623,-0.14427555,-0.15013328,-0.11467142,-0.27280045,0.48508415,0.52080667,-0.0029569597,-0.28170735,0.06367212],[0.00014026981,-2.0353239,0.0060915067,0.4794941,1.1882906,-1.512865,0.80642134,-0.26044324,-0.56213886,-0.365122,0.55078083,-0.34897435,-0.32031298,0.46106994,0.37792024,-0.14116105,0.15597256,-0.32811671],[0.0011494387,1.9060265,8.3941965,-0.3078165,-0.9530427,0.6589645,-1.2487054,-0.541133,0.13451256,0.2805466,-0.33187166,0.9900705,0.2821258,-0.51270896,-0.5130031,-0.28222615,0.042734932,0.27233443],[-0.0014444552,-1.0933362,0.003359957,-0.15052961,0.37165833,1.7087307,-1.4971043,0.673802,-0.108465396,-0.088505454,0.21561684,0.6252258,-0.15019403,-0.34727478,0.08958758,-0.1723621,-0.25557828,0.29778987]],"activation":"σ"},{"dense_2_W":[[0.20810607,-0.13481373,-0.15003344,-0.6067625,-0.31937903,-0.37380016,0.4666911],[0.71834767,0.15669753,-0.11389502,0.27131546,-0.060118295,0.039282244,0.52961355],[-0.27200606,0.43380752,-0.106262706,-0.49363258,0.35659012,-0.01706296,-1.0807354],[-0.79583454,-0.66428274,-0.42157334,-0.52979547,-0.2803004,-0.57637554,0.084149174],[0.1230394,-0.57023287,0.28639448,-0.506744,-0.9920808,0.7510901,0.8584937],[-0.41256845,0.35423175,0.13009231,0.5198333,0.7576573,-0.7053181,-0.3390053],[-0.15090485,0.33375353,-0.64164793,0.57386595,0.25700107,-0.15245672,-0.49020413],[-0.50120676,0.5210849,-0.3282951,0.4276323,1.0257447,-1.0398436,-0.8911315],[0.11402861,-0.41491523,0.247507,-0.39776865,-0.6089287,0.20646147,0.6692102],[0.09411492,0.31819808,0.41330767,0.024243973,0.2314893,0.08529045,0.14911193],[0.5179265,0.20473345,-0.36762783,-0.009045922,-0.19436747,-0.548099,-0.08656279],[-0.69121784,-0.14201449,0.3887621,0.10787631,1.0371574,-0.64747447,-0.73437464],[-0.07860244,-0.597764,0.019551078,0.010199122,-0.7493642,0.66619915,0.121666186]],"activation":"σ","dense_2_b":[[-0.04259814],[-0.053309314],[-0.28045595],[-0.23801634],[0.00944943],[-0.109921046],[-0.037457183],[0.24627604],[-0.0069056284],[-0.04725389],[-0.03361769],[0.044101644],[-0.0009850285]]},{"dense_3_W":[[-0.19631335,-0.47722518,-0.019853225,-0.29363275,-0.625923,0.35145283,0.62401354,0.14337404,-0.25707108,-0.51310915,0.2565006,0.61742216,-0.07952821],[0.5868974,-0.36786363,-0.0177682,-0.45121866,0.68608487,-0.55178356,-0.35236016,-0.7226455,-0.015394288,-0.10650332,0.5391039,0.17791761,0.34196886],[0.16821232,-0.35580373,0.26906335,0.41736925,-0.6979869,0.41801625,0.34707317,0.7784011,-0.10002936,0.32485273,-0.3644285,0.49092358,-0.43872768]],"activation":"identity","dense_3_b":[[-0.019481273],[0.031026587],[-0.03595322]]},{"dense_4_W":[[-0.72340566,0.21244061,-0.67248434]],"dense_4_b":[[0.026229527]],"activation":"identity"}]}
@@ -0,0 +1 @@
{"input_std":[[8.330973],[0.76358914],[0.44177866],[0.03320337],[0.7486537],[0.75224125],[0.7557512],[0.75438327],[0.7465672],[0.74091655],[0.72914463],[0.0329607],[0.03301792],[0.033070814],[0.03301661],[0.03287754],[0.032609064],[0.032365352]],"model_test_loss":0.017960982397198677,"input_size":18,"current_date_and_time":"2023-08-05_11-13-44","input_mean":[[26.671413],[0.058293253],[0.0043816683],[-0.009296047],[0.051497567],[0.053402483],[0.055134997],[0.052773204],[0.048979968],[0.049683023],[0.049251523],[-0.009440276],[-0.009414148],[-0.0093886675],[-0.009580273],[-0.009747584],[-0.0099527575],[-0.010216651]],"input_vars":["v_ego","lateral_accel","lateral_jerk","roll","lateral_accel_m03","lateral_accel_m02","lateral_accel_m01","lateral_accel_p03","lateral_accel_p06","lateral_accel_p10","lateral_accel_p15","roll_m03","roll_m02","roll_m01","roll_p03","roll_p06","roll_p10","roll_p15"],"output_size":1,"layers":[{"dense_1_b":[[1.0565631],[-0.21411526],[0.8872178],[-0.73335165],[0.15017867],[-0.11148372],[-0.47569525]],"dense_1_W":[[0.3191545,0.60174775,0.28932345,-0.22362822,-0.29199237,0.8730365,-0.521551,0.33030298,0.11159445,0.13276689,-0.38501248,0.6321162,0.3564933,0.05880718,-0.9028309,-0.1717296,-0.26462427,0.5646279],[-0.023215095,-0.07631244,-6.183804,0.4336958,-0.626333,-0.124013826,0.87823963,0.39341283,-1.2711039,-0.14306393,0.85833305,-0.31328923,-0.0057761483,-0.45113772,-0.13151798,0.48965174,-0.36885157,0.3205572],[-2.0049603,0.2562212,0.0074997903,0.15363267,-0.39047363,0.31743163,0.21375915,-0.08397038,-0.018758992,-0.03530787,0.008622982,-0.12050882,0.1763159,-0.0045240982,0.037064075,-0.22826771,-0.13217667,0.04430793],[1.9245242,0.17275631,0.007043335,0.38269743,-0.0008547418,0.339189,-0.33623248,0.24801533,-0.05911935,-0.28433016,0.1669682,0.35657904,-0.114900894,-0.49049523,-0.009977193,-0.22768717,0.10655727,-0.07729294],[0.087077305,-0.6003964,-0.1793809,0.25290787,0.029625213,-0.19629402,0.31878904,-0.08061085,-0.21837004,0.20829839,0.08959171,-0.30037507,0.05254617,-0.03698672,0.101752795,0.042104118,-0.12350318,0.0102602765],[0.0048545175,-0.21292417,0.016375653,-0.053975366,0.13703452,-1.074619,0.58369935,-0.17510407,-0.09377952,-0.14615338,0.07890737,-0.15442717,0.22381516,0.0459976,0.11773441,-0.018902443,0.06373771,-0.104368865],[-0.09855327,-0.1122513,-0.37817958,-0.008812584,-0.5383468,0.15305355,-0.7411861,-0.4013825,0.30445403,0.2985024,-0.095201105,0.47804987,-0.17235969,-0.47166833,0.08742673,-0.13090365,0.016420232,0.102743514]],"activation":"σ"},{"dense_2_W":[[0.15356968,0.03880223,-0.0033031541,0.71969205,-0.5551066,-1.2770053,0.852479],[-0.5814842,-0.4205401,-0.44136125,-0.20208618,0.62991315,1.2533134,-0.07128404],[-0.85485667,-0.27133262,-0.18317062,-0.5872431,0.0009068991,-0.8143884,0.24185239],[0.485521,-0.12876768,0.13680507,0.5191915,-0.33198348,-1.0602576,0.5135775],[0.9131491,-0.41206366,-0.11229955,0.8287815,0.013217909,-0.9175312,0.48983303],[-0.20012985,-0.18931809,-0.43908978,-0.2832052,0.8635579,0.22567339,0.18861422],[0.018180205,-0.3399698,-0.79971874,0.03282556,-0.17152967,0.065363236,-0.11356214],[-0.90014875,0.04525059,-0.62574875,0.21017492,-0.2622857,-0.64767957,-0.17940037],[0.053192608,-0.6683769,0.5183324,0.3161827,-0.43558958,-0.7137959,0.50653154],[-0.084774315,0.02843045,0.11588221,0.48853788,-0.6368693,-0.9299525,0.7869719],[0.12993711,0.85281557,-0.52333623,0.47304502,0.3320428,0.84226716,-0.51683784],[-0.24643111,0.40814662,-0.45649922,0.021663502,0.7520966,0.5363597,-0.51477396],[-0.30198774,-1.0936731,0.5198244,-0.6298923,-0.47961158,-0.46270132,0.17015655]],"activation":"σ","dense_2_b":[[-0.030014591],[0.112632275],[-0.38837513],[-0.009132882],[0.32848948],[0.05313135],[-0.1084323],[-0.38779393],[-0.17182945],[-0.17182226],[0.08077316],[-0.009312498],[-0.20723443]]},{"dense_3_W":[[-0.7191352,0.82575274,-0.4208178,-0.42105228,-0.12746234,0.6060775,-0.36437324,0.011044404,-0.06605932,-0.23161481,-0.07892281,-0.2586107,0.22741254],[-0.03922699,0.28218865,-0.08608041,-0.49147275,-0.75184256,0.37435916,0.46016482,-0.0026176786,-0.28095058,-0.18674251,0.6548586,0.23019525,-0.66313064],[-0.70486027,0.67592573,0.31197107,-0.25772044,-0.32144958,0.59903663,-0.013240984,-0.097765416,-0.4100128,-0.39269626,0.3077929,0.50466055,-0.20290402]],"activation":"identity","dense_3_b":[[0.06820397],[0.04168449],[0.03600891]]},{"dense_4_W":[[-0.5796537,-0.81390715,-0.64433306]],"dense_4_b":[[-0.04506098]],"activation":"identity"}]}
@@ -0,0 +1 @@
{"input_std":[[8.210334],[0.9762297],[0.46461004],[0.04707402],[0.9570847],[0.962571],[0.9688103],[0.9626872],[0.9493875],[0.93438673],[0.91365314],[0.046885576],[0.04696745],[0.047010563],[0.047022652],[0.047106303],[0.047075313],[0.0468335]],"model_test_loss":0.01662263832986355,"input_size":18,"current_date_and_time":"2023-08-05_06-34-39","input_mean":[[24.433825],[0.027294964],[-0.007775845],[-0.008583761],[0.028062655],[0.026877884],[0.025869267],[0.024155349],[0.025895124],[0.02763769],[0.028466335],[-0.00856314],[-0.008589166],[-0.008611988],[-0.008814955],[-0.008984508],[-0.009198656],[-0.009352716]],"input_vars":["v_ego","lateral_accel","lateral_jerk","roll","lateral_accel_m03","lateral_accel_m02","lateral_accel_m01","lateral_accel_p03","lateral_accel_p06","lateral_accel_p10","lateral_accel_p15","roll_m03","roll_m02","roll_m01","roll_p03","roll_p06","roll_p10","roll_p15"],"output_size":1,"layers":[{"dense_1_b":[[0.01408288],[1.6081693],[-0.06155718],[-1.54345],[-0.24193887],[-2.5958166],[-2.5204089]],"dense_1_W":[[0.0027263653,0.8206801,-0.0032928735,0.13611287,-0.076531366,1.5959642,-1.2799904,-0.06996241,0.03039119,0.02152609,0.0963275,0.5368505,0.14871612,-0.3396571,-0.2039869,-0.6619022,-0.039203983,0.4320533],[1.0668212,-0.75284845,0.1227131,0.26535362,0.13527365,-0.2295604,1.1708634,-0.021281645,-0.15021786,0.39643094,0.74283296,-0.3838045,0.003083275,-0.19208938,-0.0017936465,0.703765,-0.092454515,-0.31097198],[0.0027553164,0.7843536,4.634527,-0.26233014,-0.5298467,-0.5220332,-0.74772763,0.2625032,1.8427289,0.32081687,-1.2304753,0.90846735,0.22365782,0.250327,-0.6063961,-0.39930865,-0.18285267,0.079409026],[-1.1514753,-1.0265552,0.13074096,0.38378564,0.7118709,-0.35103342,1.0191414,-0.004307897,-0.1731455,0.13679343,1.0576029,-0.40426365,-0.26618227,0.048847493,0.23475589,0.2227605,0.085375875,-0.30894497],[-0.0047089225,-0.5524328,-0.0045054047,0.66928285,0.064004675,-1.3554454,0.75827724,-0.44520453,-0.070583,0.2953706,-0.17199413,-0.52929235,0.076917104,0.5385476,0.43512538,-0.24582133,0.1710678,-0.17150968],[-0.85862017,0.34829536,0.10180492,-0.16177164,-0.70526654,0.610196,-0.51517403,0.19511817,0.5740542,0.4342128,0.10650083,-0.08206996,-0.14800107,-0.11421001,0.25861365,0.49271822,0.13999887,-0.38721174],[-0.852335,-0.32791263,-0.10090505,-0.21708132,0.70464164,-0.4129493,0.19680835,-0.0039844504,-0.58808166,-0.5447895,-0.055174615,-0.009039755,0.3263737,0.39800122,-0.43388817,-0.2980978,-0.109957084,0.34861532]],"activation":"σ"},{"dense_2_W":[[-0.8770086,-0.024234872,-0.29209283,0.2774463,0.24086247,-0.38808098,0.76261544],[0.7931689,-0.4432339,0.5408512,-0.40607476,0.018381504,0.22573993,-1.2025113],[0.2774073,-0.28390488,0.48924458,-0.10028617,-1.1787524,0.55241233,-0.31903595],[1.187657,-0.90130055,-0.11544119,-0.48425758,-0.5087712,1.5533149,-0.44771603],[0.58252364,-0.7203072,0.46005324,0.2731612,-0.67982006,0.15357131,-0.57816696],[-0.9171702,0.37134174,-0.41417953,0.17179497,0.39356327,-0.10935133,-0.09958233],[-0.66176236,0.14740363,0.07868582,-0.40370503,0.3730389,-0.06752067,0.5966177],[0.057574127,-0.085722245,0.09327541,0.19917345,-0.112103276,0.4723292,-0.71419835],[-0.3262963,-0.03496345,-0.18610413,0.28829092,0.5575449,-0.96931815,0.2509937],[-0.14358501,-0.53553015,0.102502294,0.38049912,-0.011593848,-0.10357873,0.38666174],[1.0979931,-0.5266687,0.011019552,-0.36888388,-0.36552617,0.7437136,-1.0329511],[-0.99298805,0.033587284,-0.0815785,0.2939779,0.17858009,-0.36309564,0.7169502],[0.1916819,-0.3956481,0.23111235,-0.19816011,-0.79406476,0.9587695,-0.5586975]],"activation":"σ","dense_2_b":[[-0.08816166],[0.23371251],[-0.22228523],[-0.015670193],[-0.03642067],[-0.008211963],[-0.17989649],[0.0062512616],[-0.030759228],[-0.15077485],[0.01450626],[-0.07085153],[-0.13338038]]},{"dense_3_W":[[-0.13808444,0.27692232,0.13246109,0.13561308,-0.050800115,-0.3225275,-0.5869831,0.64456654,0.19247706,-0.48861238,0.47843403,-0.009448491,-0.38144258],[-0.18783484,-0.17080472,0.36493075,-0.6014318,-0.6958318,0.33951765,0.06917564,0.34324136,0.5521416,-0.65650636,0.07141213,0.48251265,-0.095275834],[0.67380184,-0.285928,-0.43182224,0.052221615,0.0014595015,0.21199639,-0.06704697,-0.40021017,0.5456471,0.4511299,-0.31108952,0.5755762,-0.41977003]],"activation":"identity","dense_3_b":[[0.05807602],[-0.052454475],[-0.061056226]]},{"dense_4_W":[[0.47078133,-0.7455223,-0.98688024]],"dense_4_b":[[0.054955926]],"activation":"identity"}]}
@@ -0,0 +1 @@
{"input_std":[[6.150079],[1.133713],[0.43403986],[0.044542935],[1.116597],[1.1228739],[1.1282343],[1.1087918],[1.0829574],[1.0503302],[1.0201406],[0.044353947],[0.04440376],[0.04444536],[0.044435233],[0.044352584],[0.04415751],[0.04374931]],"model_test_loss":0.021649373695254326,"input_size":18,"current_date_and_time":"2023-08-05_13-22-16","input_mean":[[28.718779],[0.027420705],[-0.0010486431],[-0.006476941],[0.03194379],[0.030714247],[0.029522395],[0.031175172],[0.03329464],[0.03204752],[0.030367676],[-0.006409366],[-0.0064388723],[-0.0064642774],[-0.006523428],[-0.0065177125],[-0.006653054],[-0.0068931]],"input_vars":["v_ego","lateral_accel","lateral_jerk","roll","lateral_accel_m03","lateral_accel_m02","lateral_accel_m01","lateral_accel_p03","lateral_accel_p06","lateral_accel_p10","lateral_accel_p15","roll_m03","roll_m02","roll_m01","roll_p03","roll_p06","roll_p10","roll_p15"],"output_size":1,"layers":[{"dense_1_b":[[-2.1557553],[0.15239237],[-0.7883996],[0.21607175],[-0.10778271],[-0.110362194],[-0.0008402114]],"dense_1_W":[[-0.39730522,-1.2665217,-0.018751338,-0.36011004,0.06413814,0.13557298,0.675142,-0.58107,-0.71815616,-0.67207223,0.8874161,-0.21224383,0.3109732,0.11582158,-0.10825705,0.20035155,0.13655844,-0.11123461],[0.08248368,-0.9288284,0.006650333,-0.3600497,0.048258215,-0.4385164,0.7812643,-0.36598027,-0.17953908,0.0043003927,0.14774609,-0.2284627,-0.15616533,0.35386756,0.5318769,0.081253365,-0.030911509,-0.12792926],[-0.012793936,-0.95197654,0.0013175827,0.4850853,0.06403457,-0.60757506,1.0891476,-0.15639184,-0.07080147,-0.141186,0.14856425,-0.6297083,-0.09511309,0.13792045,-0.003489132,0.39871147,0.23734719,-0.41034752],[0.036720853,-1.1706092,-0.0117205065,0.22939475,0.06332086,-0.27194244,0.6138069,0.0057003004,-0.23597574,-0.20802614,0.23779614,-0.2707945,-0.065516055,0.16179438,-0.1341947,0.014489582,0.27160132,-0.19034316],[-0.017165367,-0.31984007,-5.1292553,-0.09900438,1.891005,1.522304,1.3964776,-0.27762464,-3.1030765,-1.4986553,0.23631042,-0.58467907,-0.44058293,0.4239101,-0.07130837,0.06697376,0.22419664,0.49982306],[-0.029110994,-0.78375465,0.030992711,0.09522315,-0.32218152,-0.18287186,0.20446928,0.2509469,-0.32046452,-0.69563234,0.43957192,-0.2393486,0.0034883572,0.3692833,-0.1480584,-0.21708085,-0.04099675,0.16072796],[0.011984756,0.4372055,0.00038938643,-0.3804347,-0.14191024,0.83822465,-0.75622463,0.1774374,-0.15122366,-0.08789587,0.1162553,0.36315253,0.019167189,-0.015732778,-0.21518882,-0.17138954,-0.06687768,0.16531232]],"activation":"σ"},{"dense_2_W":[[-0.66123444,0.053167176,-0.67734295,-0.43834037,0.30200854,-0.6062845,0.49393648],[-0.5499434,-0.5910183,-0.04735582,-0.9205705,0.29150906,0.18296994,0.49427703],[-0.3087813,-0.6735604,0.1032743,-0.7492757,-0.47998416,0.38746807,-0.3792903],[0.22218491,-0.37423322,-0.68595016,-0.3057585,-0.67376715,0.18519343,0.26224977],[0.12493204,-0.6740838,-0.48158708,-0.11590075,-0.6646322,0.3766876,0.5211831],[0.033400588,-0.37467673,0.13653982,-0.7425377,-0.58915126,-0.44702393,0.15368707],[-0.03525442,0.6467697,0.27980375,0.53180575,0.5175674,-0.2184231,-1.3612056],[-0.26941165,0.034494866,0.54393685,0.823304,0.91855395,-0.5912671,-0.16972241],[0.5912191,0.50708044,0.8906907,0.4043412,-0.6052221,0.9453074,-0.9854745],[-0.1847966,0.29045746,-0.35760128,0.2358291,0.08363469,-0.28862038,-0.6393691],[0.082734466,-0.20940094,0.1263725,-0.61631376,-0.29357114,-0.22268835,0.48448983],[0.008772746,-0.3295119,-0.77102983,-0.37905028,0.1420826,0.19744958,0.12742513],[-0.34957173,-0.4635609,-0.6658351,-0.074581586,-0.137647,-0.031496365,0.17163728]],"activation":"σ","dense_2_b":[[-0.011190517],[-0.11420608],[-0.11911637],[-0.047058385],[-0.05209559],[-0.0067813573],[-0.6960103],[-0.2218484],[-0.638528],[-0.1287715],[-0.13393182],[-0.13222918],[-0.08612358]]},{"dense_3_W":[[-0.56854904,-0.004440354,0.20522526,-0.5189793,-0.5598319,0.004857475,0.7723675,0.08466752,0.3211879,0.22073807,-0.50451374,0.14052922,-0.15547366],[0.4760816,0.49406376,0.28560668,-0.27424216,0.0395443,0.63189626,-0.5996514,-0.59683996,-0.2216528,0.065270856,-0.40737736,0.13047104,-0.32319623],[0.35351506,0.5386659,0.32769755,0.12265874,0.56839216,0.5655456,-0.034122158,-0.18965866,-0.329834,0.10452169,-0.11418242,0.32698897,0.31875592]],"activation":"identity","dense_3_b":[[0.17258255],[-0.16791473],[-0.16998908]]},{"dense_4_W":[[-0.9664698,0.46322697,0.7462367]],"dense_4_b":[[-0.17601651]],"activation":"identity"}]}
@@ -0,0 +1 @@
{"input_std":[[5.693015],[1.1106057],[0.4839505],[0.047381964],[1.0865142],[1.0940344],[1.1007035],[1.0835435],[1.057553],[1.0248959],[0.99939334],[0.047141436],[0.047186267],[0.047211904],[0.04706877],[0.046951234],[0.04684611],[0.04661448]],"model_test_loss":0.013941051438450813,"input_size":18,"current_date_and_time":"2023-08-05_08-16-38","input_mean":[[28.37103],[0.03974344],[-0.014893848],[-0.00037599605],[0.044908416],[0.043708824],[0.042289667],[0.039325528],[0.043006305],[0.04660932],[0.04483933],[-0.00043650295],[-0.0004215664],[-0.0004140165],[-0.0003983723],[-0.00044116066],[-0.00051853625],[-0.00058758573]],"input_vars":["v_ego","lateral_accel","lateral_jerk","roll","lateral_accel_m03","lateral_accel_m02","lateral_accel_m01","lateral_accel_p03","lateral_accel_p06","lateral_accel_p10","lateral_accel_p15","roll_m03","roll_m02","roll_m01","roll_p03","roll_p06","roll_p10","roll_p15"],"output_size":1,"layers":[{"dense_1_b":[[-0.1451855],[-0.15737621],[-0.060310073],[0.6073345],[-0.05780308],[-0.16048814],[-0.23519869]],"dense_1_W":[[0.69470924,-0.6133493,0.0012405561,0.019553972,0.06448226,-0.73440015,1.0196836,-0.032631803,-0.21402587,-0.03535706,0.08963821,-0.4151518,0.04951377,0.21540186,0.08403593,0.30007103,0.0721286,-0.21808833],[-0.0055281483,-0.97871476,0.053892706,0.29578617,0.24488142,-0.9456837,0.31968737,0.23062654,-0.3851994,-0.14804605,0.12488454,-0.59180176,0.1747962,0.582284,-0.056728594,0.052842837,-0.25234208,0.08868016],[0.19908331,-0.23178522,-0.00033343132,0.29788065,-0.2630549,-0.9329808,0.9836051,-0.025557669,-0.046352006,0.021415278,-0.10175714,-0.44916221,0.41586125,-0.027728092,-0.11277255,0.28242412,-0.4416653,0.19147082],[0.49298579,0.29445317,0.0024654444,-0.034087177,-0.32976052,0.7320162,-0.13650697,-0.18142147,0.048409745,0.3485382,-0.1782508,0.17317066,0.0046146074,-0.455786,0.23675595,0.17629597,-0.2908207,0.05100971],[0.0025918921,0.60664314,0.08437282,0.0038275314,-0.26773483,0.7368417,-0.8442401,0.35636544,0.09660225,0.06810851,-0.23716143,0.15216075,0.4089829,-0.25967675,-0.5667884,-0.037151016,-0.22253121,0.35003844],[-0.0069139847,0.5928849,5.9585037,-0.2729994,-1.1027155,-0.5361705,-0.39928573,-0.21191321,1.6408015,0.88942456,-0.5805952,0.711463,1.0614796,0.75551856,-0.25333017,-1.0107019,-0.6404496,-0.4423781],[0.77807605,0.4254286,-0.0070603983,0.13885634,-0.11155952,0.92952013,-1.1097448,0.25786868,0.18906161,-0.19558257,0.03620436,0.5284809,0.28771606,-0.39166483,-0.412538,-0.41851315,-0.53765815,0.6955264]],"activation":"σ"},{"dense_2_W":[[0.14673255,-0.25427318,-0.41763714,-0.57974076,-0.56608886,-0.18331228,0.027660826],[-0.22915831,-0.55615884,-0.4508235,0.34225336,0.5593746,0.40537748,0.7960525],[0.21956417,-0.07734256,0.27312982,-0.3854605,-0.08650003,-0.27906924,0.26293612],[-0.77091247,-0.7756027,-0.9580333,0.765381,0.32493275,-0.51463944,0.7389473],[0.5907749,0.26036587,0.5915251,0.1214773,-0.029054264,-0.13793655,-0.038385104],[0.2536978,-0.44341302,0.088218234,-0.07722079,-0.69654363,-0.04497505,-0.37380072],[0.1492596,0.50270057,0.46113217,0.08208802,-0.44300494,-0.049104072,-0.38107514],[-0.033503003,0.31365246,-0.23077266,-0.51006895,0.10363347,-0.780032,-0.6072311],[0.19008167,0.41587985,0.41156146,-0.36104697,-0.25369507,-0.020715002,0.101388365],[-0.12538931,-0.93145704,-0.38526508,0.757801,0.4460356,0.22134012,0.059445396],[-0.010972688,-0.04331066,-0.5205074,-0.61291635,-0.38937822,-0.5410802,-0.776376],[-2.1453147,-0.70847136,-1.2064056,-0.31943244,0.13231355,1.2215211,-0.11054935],[-0.57651573,-0.17007175,-0.34664136,0.4060285,0.8317285,0.13207953,0.11835387]],"activation":"σ","dense_2_b":[[-0.10427355],[0.11536775],[-0.10146174],[0.08055071],[-0.1175017],[-0.09294055],[-0.12414659],[-0.12894793],[-0.122823305],[-0.0023050748],[-0.09449869],[-0.25891626],[0.0091852965]]},{"dense_3_W":[[0.018232554,0.26810202,0.2565264,0.30350316,0.51860917,0.3029135,0.5572722,0.37361154,0.45107833,-0.48839125,0.3939663,-0.0054893196,0.054042112],[0.4726983,-0.7600223,-0.2362871,-0.7305271,0.3979434,-0.16793822,0.08976205,-0.10595847,-0.5852833,-0.20750386,-0.19634347,-0.16581649,-0.49387524],[0.102563694,-0.77121973,0.49441037,-0.74888134,0.4121642,0.45769525,-0.36120865,0.5654234,0.5058435,0.45810163,0.20354177,-0.564163,-0.42752033]],"activation":"identity","dense_3_b":[[-0.09124249],[-0.07674238],[-0.08837357]]},{"dense_4_W":[[-0.9619778,-0.7475,-0.82428706]],"dense_4_b":[[0.088022344]],"activation":"identity"}]}
@@ -0,0 +1 @@
{"input_std":[[6.6771574],[0.8306398],[0.44399694],[0.03148828],[0.82453376],[0.82558274],[0.8276523],[0.8249276],[0.82540375],[0.8268513],[0.8293405],[0.031258367],[0.031317297],[0.031381857],[0.031565305],[0.031729955],[0.031927522],[0.031937882]],"model_test_loss":0.0016914892476052046,"input_size":18,"current_date_and_time":"2023-08-05_15-54-16","input_mean":[[25.397608],[0.061366253],[-0.004759293],[-0.017377611],[0.063574664],[0.0629952],[0.06279838],[0.06257633],[0.0642723],[0.06428019],[0.06443743],[-0.017316736],[-0.01732337],[-0.017324336],[-0.017258912],[-0.017273892],[-0.01738509],[-0.017547127]],"input_vars":["v_ego","lateral_accel","lateral_jerk","roll","lateral_accel_m03","lateral_accel_m02","lateral_accel_m01","lateral_accel_p03","lateral_accel_p06","lateral_accel_p10","lateral_accel_p15","roll_m03","roll_m02","roll_m01","roll_p03","roll_p06","roll_p10","roll_p15"],"output_size":1,"layers":[{"dense_1_b":[[-0.5071787],[-0.011610117],[-0.25543153],[-0.2288577],[0.3640026],[-0.20951404],[-0.018084347]],"dense_1_W":[[-0.32076025,0.5609776,0.06455551,0.4650965,-0.3128346,0.29808214,-0.068447836,-0.080615535,-0.1533624,0.086725675,0.0019198896,0.38526085,-0.33096933,-0.4344235,-0.24709408,-0.21656084,0.12910959,0.10089391],[-0.013731939,-0.37048897,1.5160967,1.216611,-0.026065614,-0.012039334,-1.7950301,-0.6103514,1.727483,2.0623763,-0.9575297,0.39149958,-0.218174,-0.51533777,-0.76304054,-0.3384101,-0.10145121,0.34581316],[0.014144666,0.22945884,-0.45639136,0.08440587,-0.19207898,-0.5290964,0.54655564,-0.07094287,-0.41206077,-0.23082434,0.77867293,-0.83082336,-0.31685916,0.3376802,0.18800995,0.28112486,0.5808552,0.0882255],[0.55997515,-0.45515752,-0.3620537,-0.21491247,-0.12751056,0.6437451,0.4513876,0.14484537,-0.012545343,-0.70398337,-0.03420965,0.5712778,-0.34471962,0.2639977,0.15245198,-0.09942475,-0.52654004,-0.39195266],[0.16048487,-2.0932982,0.060834095,-0.43821463,-0.12897933,0.8929607,0.08955616,1.5023427,0.47960326,-0.24697156,-0.2284945,0.3664966,0.36733755,-0.70950145,0.25744,0.16433853,-0.4799496,0.3186391],[0.0031760861,-0.32184288,-0.033980735,-0.4582301,-0.07663507,-0.7593102,0.50883085,0.22713356,-0.20064494,0.3521629,-0.22366937,-0.07778892,-0.2534047,0.56217426,0.41395634,-0.12142341,-0.0027042534,0.03405264],[-0.0017348339,-0.29796287,0.062474936,-0.11221718,-0.23059554,-0.626899,0.10690498,0.5434746,-0.05614545,-0.061325278,-0.2274304,0.29413497,-0.38511482,0.10846904,-0.15861528,0.35446325,-0.38156375,0.18584998]],"activation":"σ"},{"dense_2_W":[[0.08477807,-0.35302192,-0.40779448,-0.5867174,0.13918135,-0.6664157,-0.3491646],[0.3792094,-0.030590685,-0.21691003,0.13814875,-0.22226094,-0.66838443,0.11945859],[0.114434734,0.3219143,-0.65471864,-0.5160347,-0.10297699,-0.546052,0.23859331],[0.4386374,-0.13452144,-0.0146616185,0.408655,0.29078993,-0.45133266,-0.6718732],[0.04953622,-0.309141,-0.028895855,-0.081540965,-0.04211146,0.19563156,0.1549496],[-0.07437576,0.18886302,-0.11578516,-0.02629349,0.5117754,-0.8601534,-0.27231035],[0.07671942,0.0293902,-0.5813721,0.3454169,-0.053973634,0.046247624,-0.35544363],[0.22220099,-0.53176254,0.49601117,0.16283734,-0.41594207,0.14338993,-0.23063558],[0.50082463,0.3444267,-0.5869549,0.31520668,0.3332609,-0.5623406,0.0112578],[-0.05211133,-0.24212956,-0.37504154,-0.24755752,-0.61364114,0.61201805,0.5261763],[0.16144137,-0.5551527,-0.27000764,0.48719284,-0.20183928,0.4279106,0.5559925],[-0.38015607,-0.17513199,0.0054852753,0.49040842,-0.5847223,-0.315286,0.41083756],[0.027273893,0.37111732,-0.29921013,0.35153958,0.6107227,0.058014646,-0.48178723]],"activation":"σ","dense_2_b":[[-0.13192527],[-0.11472918],[-0.2398955],[-0.012978796],[-0.24606323],[-0.16946991],[-0.09656156],[-0.011122704],[-0.13971844],[0.025623204],[-0.01150639],[0.031865247],[-0.14259984]]},{"dense_3_W":[[-0.49097103,-0.38446474,0.19654791,-0.04500209,-0.353884,-0.59052265,-0.26697198,0.25850582,-0.4691939,-0.3658384,0.49351692,-0.21503085,0.43769556],[-0.49650973,-0.24257205,0.27875185,0.056984637,-0.084106766,0.21747197,-0.14523055,-0.062517725,-0.12865941,-0.4967132,-0.47404718,-0.39828455,0.20428851],[0.51754504,0.44354475,-0.12756172,0.60820955,-0.16570193,-0.24837321,0.3779718,-0.12072089,0.33560675,-0.29205063,0.42597872,-0.57351613,0.275068]],"activation":"identity","dense_3_b":[[0.03487869],[-0.013982357],[-0.02665594]]},{"dense_4_W":[[-0.40196383,0.97064435,0.88333386]],"dense_4_b":[[-0.019127952]],"activation":"identity"}]}
@@ -0,0 +1 @@
{"input_std":[[6.8682947],[1.3247597],[0.52117354],[0.053211],[1.3196762],[1.321261],[1.3220409],[1.3065661],[1.2855599],[1.2555516],[1.221194],[0.05287544],[0.052969508],[0.053053025],[0.053157352],[0.05310392],[0.052811027],[0.052315235]],"model_test_loss":0.004652302712202072,"input_size":18,"current_date_and_time":"2023-08-05_17-08-23","input_mean":[[25.097141],[0.030100366],[0.008127931],[0.0017367124],[0.025092127],[0.026649075],[0.027925659],[0.031518627],[0.031421654],[0.03279364],[0.028935509],[0.0016365603],[0.0016674129],[0.0016875914],[0.001713633],[0.0017992958],[0.0018609823],[0.0017497891]],"input_vars":["v_ego","lateral_accel","lateral_jerk","roll","lateral_accel_m03","lateral_accel_m02","lateral_accel_m01","lateral_accel_p03","lateral_accel_p06","lateral_accel_p10","lateral_accel_p15","roll_m03","roll_m02","roll_m01","roll_p03","roll_p06","roll_p10","roll_p15"],"output_size":1,"layers":[{"dense_1_b":[[-0.01906636],[-0.026147846],[-0.06956214],[-0.12893584],[3.3396657],[-0.11347563],[3.4662268]],"dense_1_W":[[0.0016520686,0.43010652,-0.016737647,-0.5847046,0.42132026,0.6080183,-0.36023164,0.04929399,-0.05639926,0.055730373,0.07496767,0.40559384,0.5495037,-0.78057534,-0.3052922,-0.4512687,-0.15058433,0.049945023],[0.0036362763,0.904483,0.001981453,-0.33253717,0.25831643,0.7194124,-0.29999894,-0.4751608,-0.22190008,0.22535524,0.18900982,-0.04392861,0.30455038,-0.40629083,0.551194,0.3208965,0.2746982,-0.6420702],[-0.0013973061,-1.2801182,-4.660272,-0.2299565,0.5036499,1.121957,0.6687826,0.08227576,-0.87890655,-0.816766,0.5726014,-0.48010656,-0.7163965,0.08434925,0.26663804,0.6864054,0.38185596,-0.08777598],[-0.024978189,0.45179686,-0.43001783,-0.19626012,-0.145855,0.3520434,-0.9694731,0.11912776,-0.069412105,-0.65245837,0.3938456,0.88234675,0.2711265,-0.09912691,-0.12296636,0.14305602,-0.12646228,0.12271214],[0.6889864,-1.4383845,-0.5985031,0.43910387,-0.14574279,-0.5830182,0.28500378,-0.18694921,-0.5173389,-0.15667093,0.1595579,-0.3700834,0.11795122,0.28467125,-0.20037529,0.44890952,-0.43353197,0.15507948],[-0.016126553,-0.5084034,-0.3446227,0.13306846,0.16979276,-0.6851119,0.97122914,-0.11992911,-0.38304567,-0.3581892,0.4410937,-0.085785575,-0.17046288,0.43981537,0.31712607,0.29649833,0.20096925,-0.42831364],[0.70960057,1.176625,0.618315,-0.35273266,0.50546986,0.41693547,-0.30839127,0.19819334,0.5911515,0.2822198,-0.22344977,0.06145853,-0.052926417,0.008550686,-0.02682976,-0.34092346,0.41492197,-0.16889408]],"activation":"σ"},{"dense_2_W":[[0.3105403,-0.13943449,-0.5671977,0.7278831,-0.9538581,-0.53992945,0.2537109],[0.25039518,-0.19284537,-0.33849826,0.19139016,-0.13403177,-0.07362739,-0.71186584],[0.077256136,-0.6989092,-0.236067,-0.53454214,0.4003753,0.3068613,-0.50189966],[-0.3387507,0.08022835,-0.13841416,-0.21556431,-0.16097516,0.82565576,-1.0068827],[-0.5129783,-0.64633214,0.6501237,-0.31724972,-0.41725558,0.6616404,-0.8662519],[0.4270751,0.67877465,-0.30108768,-0.16842516,-0.82080853,-0.8198072,-0.14678568],[0.42951444,0.47302562,-0.59648263,0.50122297,-0.4004039,-0.8508191,-0.5640101],[0.31044075,0.4447596,-0.0133176055,0.39672032,-0.13207492,-0.1053544,0.3750294],[-0.49185774,-0.6246744,0.2108288,0.036430668,-0.19068871,-0.684725,-0.5135398],[0.6451895,0.15162985,0.35664967,0.539209,0.24193677,-0.0060984846,-0.38601643],[0.33735067,0.48711634,0.3457868,0.40235066,-0.5442683,-0.8894964,0.083602294],[0.6303491,0.47514284,-0.25302443,0.048399102,-0.6309426,0.071017355,-0.42443976],[-0.028995385,0.6780545,-0.48130322,-0.10078704,-0.35888222,-0.038346574,0.21966858]],"activation":"σ","dense_2_b":[[-0.16421281],[-0.0066147824],[0.2712916],[-0.037723377],[0.009121119],[-0.24584663],[-0.17050697],[-0.28670987],[-0.15715508],[-0.09428354],[-0.21343407],[-0.17752221],[-0.17326371]]},{"dense_3_W":[[0.39673528,0.5029898,-0.77688295,-0.20485383,-0.69867265,0.52573097,0.5760262,-0.057309195,0.38914645,0.11605935,0.3120174,0.22832489,0.5434368],[0.49983713,0.12314057,-0.23971559,-0.29951656,0.01370866,0.3070287,-0.101687245,0.34138146,-0.15633298,-0.5573778,0.6135126,-0.24733001,-0.3987656],[0.041431323,-0.5985834,-0.5442451,-0.052750558,-0.7950125,-0.18662111,0.56161785,0.04896858,-0.3108859,0.44375634,-0.21281773,0.40877533,0.062354703]],"activation":"identity","dense_3_b":[[-0.08339912],[-0.0697586],[-0.070846304]]},{"dense_4_W":[[0.82060516,0.39197603,1.0617381]],"dense_4_b":[[-0.07490547]],"activation":"identity"}]}
@@ -0,0 +1 @@
{"input_std":[[10.192138],[1.2619681],[0.99538904],[0.03173082],[1.390097],[1.3528733],[1.3105861],[1.0981697],[0.9660985],[0.83304054],[0.73382217],[0.031833883],[0.031806096],[0.03176805],[0.031585068],[0.031403936],[0.031049846],[0.03052816]],"model_test_loss":0.08113811910152435,"input_size":18,"current_date_and_time":"2023-09-01_15-46-16","input_mean":[[17.870602],[0.06094058],[0.76994574],[-0.018258877],[0.07383096],[0.06954971],[0.06553388],[0.045131978],[0.034243006],[0.017750643],[0.0021489365],[-0.018112624],[-0.018160766],[-0.018217338],[-0.018421978],[-0.018636575],[-0.018845296],[-0.019152297]],"input_vars":["v_ego","lateral_accel","lateral_jerk","roll","lateral_accel_m03","lateral_accel_m02","lateral_accel_m01","lateral_accel_p03","lateral_accel_p06","lateral_accel_p10","lateral_accel_p15","roll_m03","roll_m02","roll_m01","roll_p03","roll_p06","roll_p10","roll_p15"],"output_size":1,"layers":[{"dense_1_b":[[0.60173887],[-1.2184482],[-2.5489855],[-0.13953118],[0.6530774],[-1.9541978],[0.6348104]],"dense_1_W":[[0.9151844,-0.69396037,-0.0015744013,0.13417375,0.47273952,0.053828586,-0.2703337,-0.06767218,-0.06996197,0.33860308,0.6809081,0.062160317,0.20090835,-0.39224413,0.20620485,-0.011424855,0.16862637,-0.2757232],[0.28972548,-0.02519699,-0.0015914065,0.16588296,0.18867342,0.054137114,0.08766244,-0.10144043,-0.00023482303,0.035658572,0.3292638,0.025065169,0.02802059,0.040422786,-0.28468195,0.014785016,-0.040343814,0.020054903],[-0.88864183,0.81139094,0.005142466,0.07717855,-0.3736464,-0.48089615,-0.33839747,0.72515595,-0.20632601,0.2192427,0.6845602,0.045920767,-0.0058251703,-0.10204114,0.0328523,0.1720919,0.22784986,-0.39458126],[0.04597544,-0.5142907,-0.039751884,-0.1760501,-2.0647182,-2.079671,-1.7764702,2.893614,2.1160357,0.62820613,-0.58175343,0.13933943,-0.07837348,0.2579554,-0.21817936,0.058899857,0.0867017,-0.2388197],[1.008347,0.6241744,0.0006175654,0.24427246,0.3880548,-0.71216303,-0.08657326,0.43654963,-0.002103242,-0.35102853,-0.72472453,-0.14762312,0.058478996,-0.27061918,0.08358053,-0.05671504,-0.39794087,0.38000095],[-0.75398517,-0.31240758,-0.0035077638,0.042402916,0.03954997,0.38596287,0.3007671,-0.71371967,0.24568759,-0.18542135,-0.63920254,0.012125213,-0.13132948,0.1092853,-0.09679227,-0.19704841,-0.06888792,0.28378332],[-0.2624838,0.19304885,-0.0020451967,-0.42735267,0.5025606,0.020195944,-0.100870304,-0.38624248,-0.1858948,0.15579382,0.32803276,0.24784385,0.31001303,-0.23803873,0.1500903,-0.2607993,0.30487004,-0.11202888]],"activation":"σ"},{"dense_2_W":[[-1.2400194,0.17729802,1.6934681,0.7075426,-0.11226228,-0.83732116,0.33559176],[0.5745533,-0.80457217,-0.6967583,0.08430375,-0.5941207,0.6564719,-0.70027584],[0.013842665,-1.1106777,0.12004836,-0.7758639,-0.7202531,2.1383853,-0.26417783],[0.53778696,-0.9851601,-0.16901778,-0.63449734,-0.5228541,1.452694,-0.5041905],[-0.5252082,0.9113041,0.96822953,-0.2888941,0.19632195,-0.06139024,0.23265024],[0.8556627,-0.5657162,-0.92154366,-0.028895194,-0.5141267,0.72371626,-0.751295],[0.75094503,-0.17778125,-0.7352838,-0.43035126,-0.514307,0.9411462,-0.9285261],[-0.23282954,-0.20877087,-0.41192415,-0.89742875,-1.3174541,-0.15595351,-0.15537289],[-0.7422097,0.3961414,0.3959817,-0.16982703,0.80190444,-0.8246868,1.0364479],[0.84093606,-0.5901947,-0.6232177,-0.35342738,-0.49815804,1.1040002,-0.95394325],[-0.97321624,1.2088536,0.85022783,0.002327647,0.43647796,-0.4797704,0.095545724],[-0.75402004,0.8438213,0.258789,0.092278235,0.5893333,-1.1115283,0.98487294],[0.4652842,-1.1645225,-0.80774826,0.1144249,-0.14423822,0.46821204,-0.4388547]],"activation":"σ","dense_2_b":[[-0.29847553],[0.11241894],[-0.03942593],[-0.045420405],[-0.1453599],[-0.002497385],[0.035479035],[-0.20913747],[-0.13740407],[0.07032733],[-0.21931723],[-0.09019325],[-0.07747503]]},{"dense_3_W":[[-0.3440359,0.13350633,-0.2793149,0.06343626,0.46869245,0.35055122,-0.29592252,0.56705844,-0.35353094,-0.16574235,-0.4968441,0.23517826,0.2515689],[0.034386467,-0.9523351,-0.6150173,-0.43728954,0.3108096,-0.14083374,-0.5916635,-0.62779754,0.5980072,-0.72700554,0.7860504,0.84748363,-0.5247365],[0.67116046,-0.74161327,-0.18781546,-0.6720034,0.46030635,-0.91020125,-0.81438667,0.05195494,0.5849977,-0.87362975,0.42857662,0.74513984,-0.15408307]],"activation":"identity","dense_3_b":[[0.08475188],[0.005057218],[0.099355556]]},{"dense_4_W":[[-0.005187234,0.74306834,1.0122273]],"dense_4_b":[[0.092436805]],"activation":"identity"}]}
@@ -0,0 +1 @@
{"input_std":[[9.589161],[1.6526152],[1.0296179],[0.04015747],[1.6503558],[1.6541231],[1.6558622],[1.6062601],[1.5521498],[1.4774255],[1.3901185],[0.04013376],[0.040141042],[0.040149838],[0.04013493],[0.040134083],[0.040017467],[0.03965995]],"model_test_loss":0.07666975259780884,"input_size":18,"current_date_and_time":"2023-09-01_17-07-09","input_mean":[[19.16097],[0.00023039858],[0.024997516],[0.0015251928],[-0.006199064],[-0.00511049],[-0.0036440035],[0.008816405],[0.013828324],[0.023007024],[0.03203006],[0.001499254],[0.0015097901],[0.0015188308],[0.0013762622],[0.0012122631],[0.000992458],[0.00083205965]],"input_vars":["v_ego","lateral_accel","lateral_jerk","roll","lateral_accel_m03","lateral_accel_m02","lateral_accel_m01","lateral_accel_p03","lateral_accel_p06","lateral_accel_p10","lateral_accel_p15","roll_m03","roll_m02","roll_m01","roll_p03","roll_p06","roll_p10","roll_p15"],"output_size":1,"layers":[{"dense_1_b":[[-1.8661245],[-1.5837955],[0.037068017],[0.004549992],[-0.0406839],[-3.848253],[-0.14788117]],"dense_1_W":[[1.2300965,2.5310688,-0.033882245,0.9913731,-1.4768931,-0.7604761,-0.016176367,1.7826718,0.97190374,-0.3524215,-0.12235129,0.7071725,0.08818652,-0.7926816,-1.0068728,-1.0829469,-0.41093853,0.41296145],[1.0410483,-2.4898872,0.031385656,-0.8418668,1.7300308,0.6604722,-0.22182828,-1.4516648,-0.7956896,-0.0052562924,0.3286437,-0.90743065,0.20010695,0.67364526,0.8678244,0.9458278,0.45917323,-0.4489016],[0.0042952006,-3.7682223,0.03752664,4.481959,-0.025753722,0.30079904,-0.80541074,-0.8990208,-2.967934,0.3142363,-2.034866,-0.29074013,-0.10882145,-1.6433034,-2.345314,0.3792549,0.5183762,-0.3828322],[0.0351331,0.4335407,-0.72822434,-1.1574517,0.33417597,-0.78198606,-1.2777231,0.73203385,0.5243073,0.3935317,1.6471862,-0.66806847,-1.2980176,-0.995225,0.578557,1.5086068,1.178342,0.093525246],[-0.034333024,0.2399623,0.7163214,0.4258642,0.051805,-0.27961543,0.13171966,-0.043525357,-0.11124441,0.7758175,-2.7231858,1.9812765,0.95424885,-0.516098,-0.3156276,-0.42034206,-0.43856654,-0.9159853],[-3.23608,0.62202907,-0.93510664,-0.31767273,-2.2818325,-0.97344154,-0.47527224,0.8959574,-0.07933676,-1.693229,0.29464307,1.5444574,0.3701407,-0.7764469,0.8500272,1.3425361,-1.7767322,-2.944946],[0.006447974,0.48928446,0.5253896,0.9540034,1.3740003,0.38989055,-0.19158112,-0.6142965,-1.4193783,-0.640289,2.1344078,-0.42348352,-0.5467218,0.13846552,-0.060432374,-0.4456313,0.39513004,-0.17451167]],"activation":"σ"},{"dense_2_W":[[0.75902075,1.1818918,0.018134926,-0.23670888,0.58994615,-0.3951398,1.2811865],[-0.08184867,0.43210924,0.42716455,-0.7389914,-0.42377943,0.07209329,0.11910895],[0.31956765,-0.7480974,-0.19422513,-0.552509,0.07628548,0.6062507,-0.7245184],[0.44447437,-0.041922443,0.13054374,0.43873626,0.6574541,-0.19836801,0.14220871],[-0.0037564328,-0.023460802,-0.6449217,0.5678689,0.5152597,-0.3669055,0.08844841],[0.409556,-0.89736575,0.58308125,-0.16765638,-0.2244067,-0.19765973,-0.13677762],[-2.7548218,3.2813652,1.3716445,-0.96946454,-0.47919014,0.23553993,-0.7080141],[-1.617764,-1.0463123,-0.82542264,0.027710665,0.9964338,-0.6431054,0.35499138],[0.742922,-0.4490962,-0.4472653,0.3055844,-0.051618654,0.53054106,0.6369738],[0.5263451,0.72935253,-1.4179851,-0.7053557,0.10821838,-1.4098617,0.006451496],[-0.32364398,0.039208733,0.77577096,-0.9521098,-1.4651095,-0.26427552,-0.2297292],[-0.5094321,-0.17350426,-0.05012927,-0.42607546,-0.0424127,-0.1761333,-0.87716746],[-0.07377011,-0.38419786,-0.05855057,-0.5455437,-0.9797012,-0.30153883,0.23297796]],"activation":"σ","dense_2_b":[[0.28105608],[0.062468283],[-0.008618305],[-0.010698113],[-0.022834778],[-0.19800517],[0.1661619],[-0.59834546],[0.022396281],[-0.39137623],[0.30498576],[-0.03527624],[0.02751272]]},{"dense_3_W":[[-0.0075695775,-0.27568746,0.09120304,-0.3263692,-0.4094352,-0.0785218,0.37749985,-0.3643959,0.5081887,-0.34113538,0.13756286,-0.17055227,0.43868554],[0.25493667,-0.3818253,-0.41074437,0.32864282,0.3402426,-0.13885908,-0.5030987,0.19186679,0.65157133,-0.14876194,-0.5428901,-0.08314285,-0.57639486],[0.57302797,-0.51000684,-0.18249922,-0.07923711,0.3147604,-0.16800596,-0.7434311,0.7116399,0.39529055,-0.40308496,0.15564886,-0.41269818,-0.55772877]],"activation":"identity","dense_3_b":[[0.033527393],[0.026104666],[0.027110972]]},{"dense_4_W":[[-0.087499,1.1460565,0.99249434]],"dense_4_b":[[0.025334533]],"activation":"identity"}]}
@@ -0,0 +1 @@
{"input_std":[[9.558472],[1.1135993],[0.35501274],[0.045823593],[1.1077218],[1.1080649],[1.1080468],[1.0821772],[1.0575596],[1.0226434],[0.98659563],[0.045627955],[0.045635287],[0.04563911],[0.045467082],[0.045263745],[0.04491611],[0.044533264]],"model_test_loss":0.039052750915288925,"input_size":18,"current_date_and_time":"2023-08-05_18-06-12","input_mean":[[26.136148],[0.014033704],[0.008476357],[-0.007713262],[0.014445406],[0.015386044],[0.016355403],[0.020506991],[0.024135882],[0.026870549],[0.02935421],[-0.007780166],[-0.007770314],[-0.0077639683],[-0.0077210055],[-0.007739751],[-0.007798479],[-0.00789042]],"input_vars":["v_ego","lateral_accel","lateral_jerk","roll","lateral_accel_m03","lateral_accel_m02","lateral_accel_m01","lateral_accel_p03","lateral_accel_p06","lateral_accel_p10","lateral_accel_p15","roll_m03","roll_m02","roll_m01","roll_p03","roll_p06","roll_p10","roll_p15"],"output_size":1,"layers":[{"dense_1_b":[[0.1487079],[4.2139845],[-0.5853092],[0.3086539],[0.032987785],[-4.7810106],[0.016952101]],"dense_1_W":[[0.033758555,1.0444518,3.6611536,-0.40075377,0.7095827,1.3611814,-2.1352289,0.96489286,1.8126475,-0.62583995,-1.966803,-0.1734821,0.15852115,-0.3967776,-0.14440706,0.8157315,0.17390618,-0.23260796],[2.1500878,-0.45053038,1.2581933,-0.05246604,0.5019536,1.7019817,-1.8193591,1.3068173,1.0780432,0.6126558,-1.1160796,0.19838658,-0.084695324,-1.2091074,0.5595109,0.74877197,0.30207658,-0.6453137],[0.458296,-0.21370786,0.0019734737,-0.30963984,0.8088786,-1.7420303,0.60466176,0.14915113,-0.12689793,0.052364115,-0.042618364,-0.5170238,0.26220888,0.18760274,0.37973043,0.15806,0.0022146539,-0.07045257],[-0.66763306,-0.4096175,-0.0016955894,0.04425929,0.21008201,-1.7331837,0.9467672,0.58569306,0.23827136,-0.2415081,-0.15301658,-0.28669143,-0.2599793,-0.11007419,0.6052918,0.17952923,0.24594603,-0.31397706],[1.0145541,-0.3317505,-1.4991609,0.013690285,-0.9575213,-1.383604,0.028956244,0.41788918,0.24457058,-0.094164655,-0.13030116,0.17948826,-0.03794582,0.01309437,0.35534826,-0.26917398,-0.29215142,0.27303904],[-2.7000756,0.31637338,1.4000112,0.08876083,0.6678543,0.9836755,-1.5750684,1.1129849,0.9943397,0.5684742,-1.1145275,0.5888766,-0.13174123,-0.9658693,-0.413828,0.7263344,0.11292254,-0.23350726],[-0.12401558,5.324601,7.171819,-0.19341947,2.5473533,3.2746146,3.0563648,3.367963,2.8771386,2.6749191,2.9917984,-0.14726077,-0.13394955,-0.76174796,1.426001,1.2234408,-0.7090158,-0.6628128]],"activation":"σ"},{"dense_2_W":[[0.5621434,0.992517,-0.6983796,-0.91716,-0.054338105,0.607339,0.45212823],[0.22474639,0.22895367,0.6111215,0.046989255,-0.030409865,-0.61481434,0.10029722],[0.16454774,-0.29756203,1.1110828,0.59235257,-0.18411039,-0.6023632,-0.23720202],[-0.3528499,-0.6570399,0.49941105,0.47395417,0.27583507,0.08116056,0.20804141],[0.07139761,-0.070093624,0.5590774,0.7634218,-0.49971566,0.5975401,0.7334796],[0.44444433,1.8885437,0.35532534,-0.82639766,1.279296,-0.86160696,2.6706583],[0.44448185,-0.24610248,-2.495731,-0.89490926,0.11747124,0.62402964,0.5182057],[0.23551048,0.42742857,-0.5775537,-0.6272091,-0.27731383,-0.09396491,-0.51651603],[0.42865852,0.32447976,-0.29923168,0.340756,0.013832209,0.41082326,0.21667421],[-0.32736444,0.4769656,0.977129,0.5856738,0.10249221,-0.54967755,-0.2428743],[0.25369433,-0.2204579,-1.0649059,-0.2733619,-0.049029216,0.4437149,-0.11450898],[-0.24868779,0.44613084,-0.27166238,0.5614397,0.5230158,-0.24676293,0.17946567],[-0.2634003,-0.48613355,-0.04385666,-0.65893304,-0.30746424,0.15454659,-0.53632504]],"activation":"σ","dense_2_b":[[0.19822486],[0.017528722],[-0.010497578],[-0.06445187],[-0.039755665],[0.17356244],[0.025937805],[0.023149397],[0.002431535],[0.006409766],[0.0033689209],[-0.057484128],[-0.2775116]]},{"dense_3_W":[[-0.647888,0.20968881,0.6033078,0.67215437,0.20472567,-0.14839841,0.13453817,-0.14604588,0.00843544,0.2830037,-0.54673946,0.60688186,-0.4934583],[-0.18505706,0.2708935,-0.37541804,-0.3327015,-0.14934263,-0.59043384,-0.5975454,-0.34455526,0.34356502,-0.1237087,0.032155972,-0.11825379,0.032004077],[-0.12307207,-0.14105205,-0.69863325,-0.65025413,-0.45728275,0.5245935,0.46122637,0.372422,0.2177389,-0.6267706,0.32869118,0.34049797,-0.5515101]],"activation":"identity","dense_3_b":[[0.028380467],[0.04344171],[-0.028022127]]},{"dense_4_W":[[-1.151408,-1.0946046,0.9045305]],"dense_4_b":[[-0.033447456]],"activation":"identity"}]}
@@ -0,0 +1 @@
{"input_std":[[8.880729],[1.3764424],[0.4036752],[0.044919465],[1.357929],[1.3648189],[1.3701559],[1.3636794],[1.3427843],[1.3031353],[1.2558507],[0.044844456],[0.04486737],[0.044886783],[0.044833656],[0.044710048],[0.04439941],[0.044009637]],"model_test_loss":0.03750712797045708,"input_size":18,"current_date_and_time":"2023-08-06_03-39-56","input_mean":[[23.76268],[0.025191484],[-0.0054714084],[-0.010682941],[0.027966013],[0.026647689],[0.025542766],[0.022714077],[0.020973299],[0.023003811],[0.02802845],[-0.010661417],[-0.010671549],[-0.010681824],[-0.0107730925],[-0.010828986],[-0.010959005],[-0.011070088]],"input_vars":["v_ego","lateral_accel","lateral_jerk","roll","lateral_accel_m03","lateral_accel_m02","lateral_accel_m01","lateral_accel_p03","lateral_accel_p06","lateral_accel_p10","lateral_accel_p15","roll_m03","roll_m02","roll_m01","roll_p03","roll_p06","roll_p10","roll_p15"],"output_size":1,"layers":[{"dense_1_b":[[4.5776205],[-0.23610623],[0.105955176],[0.07035416],[-0.44049412],[-4.875287],[0.95284677]],"dense_1_W":[[2.6162603,0.96552926,-1.1709021,0.68316555,-0.83422256,-1.2780359,0.2215925,-0.363209,0.3182224,0.050232545,-0.16977811,0.15275992,0.01305875,-0.24862735,-0.3824611,-0.32907623,-0.08854755,0.29336992],[-0.033986192,-0.4997676,-3.4389174,0.7424149,0.664207,0.14749247,0.5930936,-0.7861187,-1.5643623,-0.66575646,1.7639327,-0.63187826,-0.30908775,0.22797343,0.36383292,-0.43728524,-0.08584815,0.0859059],[-0.399227,0.95630413,-0.00024232137,-0.15680003,-0.657746,0.88117325,-0.28739563,-0.40365663,0.13200954,0.17651056,-0.119062886,0.5858267,-0.21834603,-0.06490571,-0.011869801,-0.46006832,0.055071417,0.16692579],[-0.5654201,-0.9509558,0.00096591073,-0.043163724,0.4077403,-1.0859221,0.6999001,0.40714318,-0.08873732,-0.07508248,-0.009093482,-0.46344966,-0.19199978,0.4810677,0.48018777,-0.28993973,0.41181335,-0.27362508],[-0.027356783,-4.2967224,-0.03678484,-0.32151344,-0.73972255,-2.5777402,-3.2287362,-2.5549042,-1.4497441,-0.43103775,0.6453438,-0.17506233,-0.31516784,0.5563384,0.85075074,-0.57829696,-0.19835036,-0.024371902],[-2.47195,1.7926816,-1.1769953,0.16563822,-0.82507175,-1.0493809,-0.32214803,-1.1703736,0.2470295,0.2510944,-0.11734796,0.13303131,0.030326199,-0.00082738034,0.023316598,-0.33300236,-0.31752133,0.38321474],[2.1512134,1.7400922,-1.7055479,-0.3629506,-1.7764045,-0.28031406,-1.3791944,-0.80302006,-0.122044094,0.8002589,-0.3441996,-0.07645867,0.23166081,-0.38216463,0.64409703,0.11736102,0.22685654,-0.30116174]],"activation":"σ"},{"dense_2_W":[[-0.52089065,-0.064282656,0.9368965,-0.78820074,0.18612167,0.050849117,0.27317494],[0.71986425,0.006931284,-0.083075926,-1.1616415,0.027728708,-0.928848,0.83347905],[0.8144789,0.20632064,-0.71373665,-0.06819771,0.9545801,-0.4286985,0.42867258],[0.71121395,0.06992666,-0.8302117,0.07830932,0.25297147,0.0047203694,-0.0066684796],[-0.5690574,-0.14917742,-0.3785819,0.9758459,0.28018865,0.41372767,-0.21782406],[0.13315567,-0.4577933,-0.1426717,0.19464079,0.008886417,-0.116589725,0.08225403],[-0.9950312,-1.3679215,0.091107205,0.9272021,1.1016536,-1.3227009,-1.2095336],[-0.3986191,-0.9487305,0.38871515,-0.08156303,0.95174986,-0.6304789,0.13525334],[0.2225127,-0.2169731,0.9058736,-0.5802039,-0.24850035,-0.28582534,0.3907818],[-0.5881804,-0.044960845,-0.90182215,0.8056298,0.5574692,1.0463717,-0.5374416],[-1.2164664,-0.40320966,0.53783023,0.27278855,-0.49228838,-0.14903766,-0.8090045],[-1.2831542,0.4933893,0.021853147,0.071227364,-1.4159098,0.76011354,-0.8172033],[0.078262046,-0.5118518,0.22079428,-0.02778036,-0.222457,0.23040481,-0.66731334]],"activation":"σ","dense_2_b":[[0.05072228],[-0.1091474],[0.1004324],[0.021801215],[0.03440032],[-0.07559257],[0.18204498],[-0.008477362],[0.033497173],[-0.15622234],[-0.088286996],[-0.1264465],[-0.09580737]]},{"dense_3_W":[[-0.49234378,-0.12957415,0.5802615,0.4467619,0.590399,0.24340731,-0.5555279,-0.66425663,-0.4981453,0.60517937,-0.56258386,-0.26889455,-0.35886267],[-0.60045487,-0.55883497,-0.3071459,0.07875048,0.5536381,-0.32903612,0.14948905,0.46808386,0.28372547,0.3629737,0.22399715,-0.5433762,-0.15223768],[-0.46732837,0.03954109,0.09032384,-0.38590652,0.39412647,-0.153768,0.03560084,-0.11765058,0.35194537,0.3046047,-0.094702706,0.3176626,-0.32317603]],"activation":"identity","dense_3_b":[[0.051931284],[0.06253407],[0.04909886]]},{"dense_4_W":[[-1.265387,-0.37792373,-0.20574242]],"dense_4_b":[[-0.04775983]],"activation":"identity"}]}
@@ -0,0 +1 @@
{"input_std":[[8.358706],[1.0686424],[0.34114116],[0.044889364],[1.068699],[1.068358],[1.0675814],[1.045456],[1.0242518],[0.99177456],[0.9590156],[0.04477875],[0.04480059],[0.04482342],[0.04474932],[0.04458978],[0.044322193],[0.043946933]],"model_test_loss":0.035726360976696014,"input_size":18,"current_date_and_time":"2023-08-06_06-15-03","input_mean":[[24.615007],[-0.03660665],[0.0016246386],[-0.006489807],[-0.0361033],[-0.035993658],[-0.036171526],[-0.03401853],[-0.031212736],[-0.02772113],[-0.025683384],[-0.0064613656],[-0.0064720884],[-0.006487653],[-0.0065700756],[-0.0065773614],[-0.0066702664],[-0.0067906133]],"input_vars":["v_ego","lateral_accel","lateral_jerk","roll","lateral_accel_m03","lateral_accel_m02","lateral_accel_m01","lateral_accel_p03","lateral_accel_p06","lateral_accel_p10","lateral_accel_p15","roll_m03","roll_m02","roll_m01","roll_p03","roll_p06","roll_p10","roll_p15"],"output_size":1,"layers":[{"dense_1_b":[[2.9387486],[1.7947302],[-2.419479],[-0.046714567],[-0.07710784],[-0.011005453],[2.2013001]],"dense_1_W":[[1.5500982,-0.562216,0.82173073,1.1276568,-1.1044303,2.0344303,-0.5962017,0.43611562,0.6137577,0.54552734,-0.40140414,0.13477002,-0.33703992,-0.995159,-0.5198411,0.6988811,0.23579559,-0.43389362],[1.9096831,0.4647536,-1.062686,-0.18994598,-0.7350096,1.1260368,-1.2783738,-0.017420385,-0.32890928,-0.6363917,0.1548712,0.10676455,0.18015237,-0.0953862,-0.13519493,0.2339727,0.07321837,-0.04309801],[-1.3224562,0.034447707,0.7349111,0.15789251,-0.54802585,0.91166073,-0.5362905,0.5046039,0.30484954,0.5900371,-0.38941008,0.33457753,-0.27889228,-0.6909375,-0.14148402,0.97603106,-0.13406259,-0.30469695],[-0.0017459231,-0.09968895,-0.014508084,0.23403876,0.836069,-1.5712544,0.19792208,0.34136322,0.15050764,-0.2420643,-0.05131571,-0.09686847,-0.47325152,0.21186271,0.033762608,0.17737845,0.0014673215,-0.12473483],[0.00029148505,-0.6372151,-0.0053334557,-0.05010357,0.99977154,-1.0182008,-0.14067085,0.3974594,-0.2723742,0.04584643,0.073329695,-0.34156838,-0.11684916,0.11896246,0.34009737,0.6478362,0.13086675,-0.43019354],[-0.0077469624,-0.1509263,-3.2484512,0.16014525,-0.5749223,-0.9640095,0.9543776,-0.7414716,-0.7872326,0.2741354,1.5463,-0.3455074,0.08961764,0.027934242,0.045294646,0.23221987,-0.1519019,8.7261986e-5],[2.0402718,-0.72642046,1.1085396,0.08754884,0.35070625,-0.522105,1.2273571,-0.049034014,0.7027618,0.56165534,-0.22607273,0.21941654,0.19681507,0.25899538,-1.0231488,-0.19570431,-0.065851234,0.37131593]],"activation":"σ"},{"dense_2_W":[[-0.60671103,-0.10295715,-0.2275175,0.703453,0.5107893,-0.019612279,0.060930736],[0.32735822,0.09359731,1.1961514,-0.5468047,-0.6979488,-0.8116451,-0.8816686],[0.28230911,0.26441634,0.32522315,-0.2709414,-0.97729933,-0.1853032,-0.6816012],[0.85672635,0.40473634,0.06306575,0.043324664,-0.09925389,-0.6707503,0.48766825],[0.3574693,0.24644808,0.8436976,-0.73305976,-0.87197644,-0.17207824,-0.34952453],[1.2755688,1.0824895,0.84866655,-0.5946056,-0.93967986,-0.64516634,0.18630773],[0.2624753,-0.12604286,-0.24697196,-0.36811814,0.14403982,0.43120542,0.1445742],[0.44066232,0.1534366,0.25932267,-0.65874845,-0.26324567,-0.31705675,-0.831388],[-0.030025106,-0.31389698,0.17181242,0.3696615,0.008856971,-0.5338608,-0.019544462],[0.053924862,0.46772835,0.94262034,-0.71287125,-1.2770078,0.48717985,0.09874367],[-0.5596553,0.1355415,-0.6685472,0.5694037,0.5835357,-0.050862506,-0.30772945],[0.26585215,0.15441975,-0.27715218,0.17482373,0.30534902,0.13441981,0.24543025],[-0.700537,-0.30525044,-0.40211698,0.23966432,-0.07130374,0.3517828,-0.30104256]],"activation":"σ","dense_2_b":[[0.0006462817],[-0.045901865],[-0.08447791],[0.018081808],[0.028199553],[0.15011074],[0.024169737],[-0.07767265],[0.003401174],[-0.09530233],[0.026245346],[-0.019555494],[0.029535577]]},{"dense_3_W":[[-0.4269903,0.38330346,0.3818396,-0.104428,-0.010993002,0.25576162,-0.14891353,0.5712297,0.43450522,-0.18818101,-0.40029064,0.113605164,-0.41539222],[-0.4444491,0.6585591,0.5286078,0.014278995,0.3499434,0.3896106,-0.37057588,0.058567323,-0.5987489,0.35249916,-0.5103405,-0.0007771231,-0.20807561],[-0.5559521,0.25394833,-0.35546854,0.4645985,0.3801913,0.20466149,-0.20871717,0.37793857,-0.093845844,0.5868493,0.12606087,-0.4579316,-0.5141172]],"activation":"identity","dense_3_b":[[-0.039426688],[-0.038285036],[-0.0388908]]},{"dense_4_W":[[0.98477775,0.89640814,0.94241107]],"dense_4_b":[[-0.040733967]],"activation":"identity"}]}
@@ -0,0 +1 @@
{"input_std":[[9.261368],[1.1474106],[0.37148234],[0.044279538],[1.1430647],[1.1446478],[1.1447154],[1.1187278],[1.0942419],[1.0602791],[1.0217831],[0.04425829],[0.044268712],[0.04427185],[0.044111896],[0.04397081],[0.04367603],[0.043223266]],"model_test_loss":0.041262850165367126,"input_size":18,"current_date_and_time":"2023-08-05_23-22-02","input_mean":[[24.15998],[-0.008219966],[0.004739006],[-0.013221193],[-0.008523361],[-0.008609849],[-0.007986069],[-0.006765848],[-0.006281763],[-0.008096367],[-0.011848589],[-0.013220585],[-0.013206273],[-0.013198256],[-0.013279306],[-0.0133954],[-0.01351665],[-0.013652078]],"input_vars":["v_ego","lateral_accel","lateral_jerk","roll","lateral_accel_m03","lateral_accel_m02","lateral_accel_m01","lateral_accel_p03","lateral_accel_p06","lateral_accel_p10","lateral_accel_p15","roll_m03","roll_m02","roll_m01","roll_p03","roll_p06","roll_p10","roll_p15"],"output_size":1,"layers":[{"dense_1_b":[[-0.011715764],[-0.16971639],[0.44084886],[1.8919995],[-0.57473946],[0.3265129],[0.665929]],"dense_1_W":[[-0.035294082,0.58739567,0.0068268306,0.5267725,-1.3068384,2.0108788,-0.8904008,-0.86464506,0.8847079,0.49210623,-0.27763724,0.6851789,-0.11328691,-0.61491024,-0.7021547,0.0578637,0.082666315,0.0749281],[-0.08059325,-0.77528465,0.93356776,-1.1226226,0.030571632,1.2159668,-0.3739228,2.0636218,1.051139,0.24026142,-0.8241013,0.51684606,0.45198372,-0.85868824,0.9150932,0.14920434,0.0996434,-0.23813146],[1.05331,0.6632812,0.586634,0.10609487,-0.16541673,1.0143963,-0.6739981,0.43193975,0.5483419,0.020105261,-0.2659555,0.27416015,0.3780343,-0.38164294,-0.04144785,-0.49568477,-0.6130558,0.6570192],[1.4749159,0.076626115,-0.88682747,0.046201207,-0.23487571,-1.1125354,0.055821165,-0.57242775,-1.03109,0.059293248,0.2898471,-0.65886724,0.19274879,0.39950013,-0.10046821,0.34578046,0.20585975,-0.28454834],[-1.0492898,0.8025768,-0.019681327,0.11809792,-0.3740394,1.1517578,-0.6477998,-0.59586203,-1.4050065,-0.8667816,0.755093,-0.04370645,0.32285377,-0.16848978,-0.21646905,-0.34349748,-0.28047857,0.49415973],[0.056685824,0.9262534,-5.3872395,0.7328496,-1.2155392,-1.1581563,-0.12605561,-1.2297785,-0.95465136,1.0993868,1.4996752,-1.0285505,-0.33488074,0.33622772,0.34516957,-0.14087252,0.36723128,-0.2868416],[0.9570638,0.24864312,-0.017469333,0.54736155,-0.49265823,1.0806886,-0.30185512,0.02353294,-1.477074,-0.70588714,0.615638,-0.07056425,0.046274792,-0.32864115,-0.3693316,0.0012585309,-0.105926886,0.20974532]],"activation":"σ"},{"dense_2_W":[[0.79805297,0.07995367,0.33311036,-0.1088086,-0.28585342,-0.11676164,0.3217794],[-0.6564382,-0.650472,-0.32395652,0.48536372,-0.39954782,-0.9293625,-0.3131151],[0.065390795,-0.36014688,0.035044394,-0.47919765,-0.21676321,-0.68095744,-0.58225524],[-1.4269842,-0.22010052,0.762021,1.5709931,-0.39910436,1.7860814,1.1529905],[0.81025195,-0.19804797,0.2811143,-0.13832566,0.21581413,-0.2242298,0.10002656],[-2.4172528,-2.9861536,-2.1297307,-2.3552132,2.0055087,3.841171,-1.6320437],[-0.15224119,-0.008424982,-0.7476414,-0.7229817,0.34834322,-0.62080944,-0.6412587],[-0.32463408,0.056613863,-0.013071884,-0.057739817,0.5155227,0.12653896,0.14592259],[-0.5659917,-0.43365908,-0.31137735,-0.3897718,-0.26343766,0.8946887,-0.6234644],[-1.2267396,0.0675833,-0.036197066,-0.0106164375,-0.65109164,0.3789509,-0.7397455],[-0.47206897,-0.3735633,-0.4683122,0.17489713,-0.69122815,-0.68286866,-0.23052849],[0.92053163,0.41530177,0.23816848,-0.3726253,0.27367908,0.71565676,0.19845398],[0.22949272,0.53540593,0.100375816,0.39428094,0.19491827,-0.0047999,-0.036115997]],"activation":"σ","dense_2_b":[[-0.080741115],[-0.098717436],[-0.04195425],[0.57887644],[-0.0376275],[-0.7313109],[-0.15826389],[-0.03734272],[-0.17071353],[0.06530881],[-0.16700919],[-0.10229606],[-0.0077384445]]},{"dense_3_W":[[0.6059949,0.4993691,-0.0014475341,0.40003,-0.44542313,-0.71407944,0.2787467,0.16292632,-0.4138824,0.27650607,-0.27067518,-0.14248835,0.17812036],[0.13085166,0.48013842,0.34201995,0.64287025,-0.600294,0.86557233,-0.14552544,-0.08809264,0.41238344,0.7531989,0.587104,-0.4580716,-0.6428474],[0.61981463,-0.5255673,0.47925967,-0.6429044,0.02112309,0.09041386,0.27671653,-0.054121528,-0.24291937,-0.610705,0.07889088,0.042044822,0.39591196]],"activation":"identity","dense_3_b":[[0.007450639],[-0.024559],[0.020729255]]},{"dense_4_W":[[0.2550702,-1.0187347,1.2050446]],"dense_4_b":[[0.021735787]],"activation":"identity"}]}
@@ -0,0 +1 @@
{"input_std":[[6.17857],[0.44787604],[0.5243947],[0.032028187],[0.44404522],[0.4443511],[0.44520274],[0.45595846],[0.4604483],[0.47209886],[0.4884655],[0.03209721],[0.03205],[0.03203256],[0.03195497],[0.031864975],[0.03177169],[0.03154828]],"model_test_loss":0.0009851785143837333,"input_size":18,"current_date_and_time":"2024-01-04_15-35-20","input_mean":[[21.90655],[-0.29117808],[-0.08760989],[-0.009034546],[-0.2664771],[-0.27346689],[-0.2819304],[-0.3144748],[-0.33650345],[-0.36093968],[-0.38976616],[-0.009162463],[-0.009145338],[-0.009107736],[-0.008843513],[-0.008647824],[-0.0083346395],[-0.007997973]],"input_vars":["v_ego","lateral_accel","lateral_jerk","roll","lateral_accel_m03","lateral_accel_m02","lateral_accel_m01","lateral_accel_p03","lateral_accel_p06","lateral_accel_p10","lateral_accel_p15","roll_m03","roll_m02","roll_m01","roll_p03","roll_p06","roll_p10","roll_p15"],"output_size":1,"layers":[{"dense_1_b":[[-0.06760431],[-0.2907365],[-0.4082806],[-0.20224898],[0.29265383],[0.26244283],[0.15451357]],"dense_1_W":[[0.014618384,0.37313232,0.035592113,-0.3403019,0.033235013,0.24829742,-0.49047577,0.28937504,-0.2026583,-0.2760167,0.21064562,0.08079155,0.11982211,0.042024616,-0.052886724,-0.06286289,-0.13398048,0.13160145],[-0.0048215007,0.30684328,0.020128243,-0.41616872,-0.20515934,-0.2511921,-0.13518195,0.37878683,0.26875132,0.113668166,-0.27679917,0.66174066,-0.052989658,-0.1855979,-0.02353523,-0.30120054,0.091657996,0.09602743],[-0.0042930744,-0.15373556,0.08812353,-0.31896785,0.019456651,-0.20545581,0.05433538,-0.23081444,0.43445846,0.11084425,-0.0024109932,-0.6938004,-1.024248,-0.4391781,-0.0550922,0.07583203,0.79354477,-1.8509296],[0.04390958,-0.18022121,-0.08688032,0.19231437,-0.14530687,-0.27636525,0.4690427,0.4344902,0.16017663,-0.25027603,-0.2452524,0.5537437,0.13898967,0.038036723,0.6355534,0.5031342,1.3155322,-0.14124991],[-0.006398488,-0.7886078,0.03451426,-0.054085355,0.25506452,0.4975867,1.0672406,-0.27192074,-0.91774124,0.2306121,-0.3410087,-0.16050793,0.15241678,-0.17949696,-0.31961542,0.22702731,0.419533,0.00035671022],[-0.0077034663,-0.78213346,-0.016133381,0.10805307,0.14032482,0.36524558,0.12367749,0.24540052,-0.059624504,-0.3247948,0.021967877,-0.039682694,-0.2721095,-0.10098841,0.21639878,-0.22641253,0.14136238,0.19991797],[-0.048534065,-0.20369655,0.07626118,-0.31831193,0.2750742,-0.2912584,0.088218614,0.09158782,0.06355577,-0.23128918,0.2086949,-0.44672933,0.2343581,-0.052767944,-0.26756912,-0.29403126,-0.0006949583,-0.3037349]],"activation":"σ"},{"dense_2_W":[[-0.19747548,0.3752813,0.005352072,0.22516197,-0.279574,-0.29499897,0.514051],[-0.23818707,-0.442092,0.21234472,-0.5240696,-0.666385,0.13287802,-0.70440364],[-0.12623425,-0.12563375,0.17045951,-0.4611073,0.13094379,-0.2603494,-0.578133],[-0.3201878,-0.472269,-0.45254824,-0.17459284,-0.13989829,-0.07893383,-0.15434109],[0.6313669,0.59430647,0.029979968,0.40872103,-0.6338095,-0.48483965,-0.16215326],[0.12921973,-0.35855725,-0.6317136,-0.24155544,0.018117908,0.6665686,-0.09261251],[-0.5263865,-0.500698,0.26361403,0.1989874,0.16189028,0.4017576,-0.50330746],[0.19345485,0.0116930995,-0.1151553,0.38370124,-0.51925737,-0.66009927,0.54345274],[-0.10049859,-0.07803794,-0.05125394,-0.5760374,0.22012971,-0.4562151,-0.24986024],[-0.035831526,0.17925176,0.08670547,-0.6276373,-0.24000542,0.09890566,0.005909479],[0.026487896,0.10827987,-0.5441871,0.10627476,-0.12671272,0.25334272,0.090102024],[0.11269248,-0.5982834,-0.20034944,-0.4165159,0.48223287,0.06892533,0.32945293],[0.14122471,-0.84526354,-0.102686554,-0.42417887,-0.056950103,-0.013891049,-0.043740742]],"activation":"σ","dense_2_b":[[0.021066276],[-0.18323943],[-0.08896128],[-0.059529945],[0.01516111],[-0.03864309],[-0.01915316],[0.0072613885],[-0.13141903],[-0.1963338],[-0.20056123],[-0.0918762],[-0.09109566]]},{"dense_3_W":[[-0.5145814,-0.15164766,0.41466355,0.5054458,-0.56660354,0.37653998,0.2958398,0.1300162,0.15582272,-0.14694406,0.089647524,0.0200251,-0.6487758],[0.18533824,-0.14857687,0.36485723,-0.25733942,-0.5232801,0.39276004,0.36414713,-0.54408604,-0.4820063,0.19759995,-0.12671192,0.39148358,0.189427],[0.5235357,-0.23839255,0.41131777,-0.43199104,-0.042662866,-0.042696618,-0.30467755,-0.119463064,-0.49081296,0.2406254,-0.07923712,-0.0021057576,-0.42410713]],"activation":"identity","dense_3_b":[[-0.036107603],[-0.036152013],[0.03689696]]},{"dense_4_W":[[-0.48288086,-0.87119794,0.8186898]],"dense_4_b":[[0.0358588]],"activation":"identity"}]}
@@ -0,0 +1 @@
{"input_std":[[9.079519],[1.117335],[0.32821873],[0.04513038],[1.1160785],[1.1158687],[1.1147403],[1.0893666],[1.0659956],[1.0314285],[0.99783367],[0.04497426],[0.044967793],[0.044959538],[0.04481279],[0.044600893],[0.044237226],[0.043830905]],"model_test_loss":0.05013991519808769,"input_size":18,"current_date_and_time":"2023-08-06_08-46-45","input_mean":[[24.945726],[0.056781527],[-0.006332743],[-0.0054867254],[0.057909872],[0.057552766],[0.05728289],[0.054190118],[0.05235296],[0.048357345],[0.0448629],[-0.005489833],[-0.005468405],[-0.005448704],[-0.005441959],[-0.0055107307],[-0.0055533946],[-0.005588048]],"input_vars":["v_ego","lateral_accel","lateral_jerk","roll","lateral_accel_m03","lateral_accel_m02","lateral_accel_m01","lateral_accel_p03","lateral_accel_p06","lateral_accel_p10","lateral_accel_p15","roll_m03","roll_m02","roll_m01","roll_p03","roll_p06","roll_p10","roll_p15"],"output_size":1,"layers":[{"dense_1_b":[[-1.3742944],[2.0560386],[0.070786394],[0.08766052],[-7.2637663],[4.9462605],[0.020425294]],"dense_1_W":[[-0.88963157,0.47497272,1.1810282,0.6939777,-0.36621186,1.347931,-0.7933228,0.85621536,0.66951233,-0.11786804,-0.6730566,0.112765215,-0.28919625,-0.33895963,-0.62124485,0.25326887,0.21473351,-0.12489331],[1.223516,-0.036567718,-1.247157,0.09799731,-0.97558075,0.16902182,-1.0449882,-0.046705905,0.29223862,0.30116007,-0.09839103,0.46722287,-0.0449582,-0.39868206,0.07668579,-0.11536017,-0.0691049,0.10267897],[0.018545184,0.81794965,3.4399781,0.052234847,-0.4958513,0.38696352,-0.638153,0.87363166,0.86702484,0.7031613,-1.9006683,0.85754114,0.8998447,-0.52486235,-1.0076646,-0.66781485,0.2555468,0.17366274],[0.0037442108,1.4584165,0.0061913775,0.24082592,-0.7234466,1.0945269,-0.21835686,-0.038823094,-0.20537247,-0.4526858,0.3522938,0.7368147,-0.4907756,-0.72209734,-0.3003073,0.24139397,-0.27028123,0.28593597],[-3.720445,0.5471661,-2.9256604,-0.84999114,-0.35371152,-1.650107,-0.30913082,-1.5157434,-1.4253404,-0.5012027,1.8229349,-0.22352915,0.07850824,1.2852895,-0.17709053,0.0015369024,0.4303836,-0.3013633],[2.7063103,0.4795686,-2.1412866,-0.37747622,-0.52522975,-1.1443468,-0.3860132,-0.81000876,-0.8656079,-0.38408586,1.1732165,-0.33476815,0.35176495,0.67967117,-0.24029131,-0.15793729,0.59702957,-0.33229384],[0.003850781,1.0522044,0.0023779364,1.4077666,-0.2233461,-1.323892,0.2490657,0.9962225,-0.21043883,-0.48657963,0.0066762604,-0.8950598,-0.65639627,-0.10381974,-0.033756495,0.3061354,-0.09620744,0.075370125]],"activation":"σ"},{"dense_2_W":[[0.01117936,0.6720228,0.39750826,0.30468678,-0.34063298,0.11394216,-1.026796],[-0.07938874,-0.29046267,-0.15366286,-0.94418544,0.72881997,-0.072467156,1.1634737],[-0.7841681,-0.38053507,-0.48102236,-0.7339148,-0.94196475,-0.09320883,0.17724545],[-0.11575878,0.41173506,0.7931331,0.7659729,0.45459372,-0.4762705,0.04162107],[-0.54039043,-0.6239058,0.12097131,-1.0940313,0.6270558,0.8035503,0.8422599],[-0.006883268,0.1846561,0.4628913,0.89182067,-0.281233,-0.5460564,-0.49594778],[-0.37237987,-0.50528663,-0.34381047,-0.9888458,0.66827786,0.75729847,0.9049922],[-0.6351278,-0.39599782,-1.7942646,0.2172901,1.2180948,-1.0186263,-0.29467586],[0.098499954,-0.7321613,-0.4204465,-0.5589947,-0.49682683,0.02701407,-0.56642413],[-0.055568255,-1.0158917,-0.13512869,-0.75265217,0.32634935,-0.10900366,1.1009233],[0.9196104,-0.29001886,0.27778128,0.40752462,-0.71603805,-0.78771234,-0.2509219],[-0.27494332,-0.27294615,-0.87608117,-0.5456548,0.23442413,0.46402624,0.4014235],[-0.0657572,0.39627385,-0.032163464,0.37446278,0.30043706,-0.3939967,-0.17288771]],"activation":"σ","dense_2_b":[[-0.12896457],[-0.06757774],[-0.29207176],[0.057776056],[0.03819644],[-0.0075618257],[0.022483658],[-0.30520216],[-0.30780953],[-0.22291888],[0.073764615],[-0.11618854],[-0.0932309]]},{"dense_3_W":[[-0.20723751,0.46280366,0.37977389,-0.28764504,0.56071246,-0.42746708,0.743715,-0.41921702,0.39859435,0.35315046,-0.18542276,0.28128248,-0.18067148],[-0.6656129,-0.15142722,-0.022595402,-0.48696524,0.37018472,-0.6183299,0.50862163,0.035134673,-0.46468532,0.35520592,-0.6777795,0.2590716,0.46181098],[-0.32565305,0.33916017,-0.4280441,-0.21963115,0.6359937,-0.44887698,0.2442765,0.13551079,-0.47778323,-0.4541174,-0.59640217,0.13698937,-0.27695638]],"activation":"identity","dense_3_b":[[-0.07102677],[-0.02868969],[-0.030784609]]},{"dense_4_W":[[-1.024929,-0.3087716,-0.3174253]],"dense_4_b":[[0.053275082]],"activation":"identity"}]}
@@ -0,0 +1 @@
{"input_std":[[9.167076],[0.8396103],[0.39224938],[0.038149882],[0.83500266],[0.8364939],[0.83795094],[0.8280286],[0.82101065],[0.80936265],[0.79514086],[0.03816296],[0.03813872],[0.0381162],[0.037860528],[0.037643477],[0.03726596],[0.036896862]],"model_test_loss":0.019166288897395134,"input_size":18,"current_date_and_time":"2023-08-06_11-19-09","input_mean":[[24.623522],[0.012142224],[-0.0077460455],[-0.0040752436],[0.01259905],[0.011912992],[0.011499013],[0.008575823],[0.006231978],[0.001843956],[-0.0011071449],[-0.0041566766],[-0.004162808],[-0.0041667568],[-0.0041206917],[-0.004187086],[-0.004389174],[-0.0045910473]],"input_vars":["v_ego","lateral_accel","lateral_jerk","roll","lateral_accel_m03","lateral_accel_m02","lateral_accel_m01","lateral_accel_p03","lateral_accel_p06","lateral_accel_p10","lateral_accel_p15","roll_m03","roll_m02","roll_m01","roll_p03","roll_p06","roll_p10","roll_p15"],"output_size":1,"layers":[{"dense_1_b":[[-0.10681055],[-0.05877009],[-5.810432],[0.108804524],[5.817238],[-1.4135033],[-0.04275369]],"dense_1_W":[[-0.010944928,0.298581,-0.0004137291,0.46320775,-0.68514436,1.5652411,-0.62197584,-0.70986384,-0.07209713,0.14426921,0.09893353,0.2957121,0.24033338,-0.63492715,-0.24635397,0.21067435,0.16446242,-0.4790833],[-0.002262276,-0.7669907,0.0011565682,0.2969979,0.11738408,-1.199179,0.32905388,0.35275638,-0.34200913,0.22103965,-0.188085,-0.00050096336,-0.36091766,0.18886286,0.356193,0.50661796,-0.18447432,-0.3530909],[-2.7729337,-0.046491493,-2.223908,0.51880556,-1.5391709,-1.8928696,1.8811203,0.22442053,-0.20638244,0.44581792,-0.10464485,-0.11026335,-0.41476744,0.71887964,-0.6184063,0.07461815,-0.70264786,0.5291843],[0.088751696,-0.7015499,-2.262332,0.07993792,-0.5452428,0.36403584,-0.888494,0.26214746,0.52286375,0.47499555,0.12439757,0.32842526,-0.22486946,-0.2082452,0.2083624,0.20134373,-0.26366135,0.040187743],[2.7838297,0.36513317,-2.2177417,0.17293313,-1.3490273,-1.395796,0.7361196,0.024529908,0.0147317415,0.50988925,-0.16947655,-0.14311421,-0.13544203,0.6211999,-0.28396562,-0.19654925,-0.4721205,0.44046035],[-1.0456268,0.006558085,0.029179003,-0.24681818,0.33209732,-0.31439346,1.0280732,1.6322477,0.33988294,-1.1288414,-0.7784953,-1.3079332,-0.922044,-0.7579679,-0.7932985,0.090359144,2.2930832,3.7051785],[-0.032393374,0.15797108,2.6865292,-0.022740832,0.7215196,0.9335919,-0.9160322,0.63953984,-0.10847628,-0.41109005,-0.58336747,0.8728414,0.2319681,-0.77523047,-0.6650054,-0.19625007,-0.0422657,0.4323157]],"activation":"σ"},{"dense_2_W":[[-1.0469242,2.310652,2.5330374,-1.1560289,0.87019974,-0.24008566,-2.4145112],[-1.5431111,1.6602669,1.4227277,0.44263628,2.124669,-0.881401,-1.636305],[-0.7812676,0.3887846,0.046831988,-0.0021416398,0.0068075457,0.26359168,-0.40856928],[0.5853211,-0.020535953,0.1977803,0.11166494,-0.7388882,-0.6265164,0.19296904],[-1.4384844,-0.07708128,-0.62513274,0.1822617,-0.83729374,-0.20433801,0.08106154],[0.79002935,-0.51513207,-0.17725268,0.70090765,-0.37455943,-0.00461776,0.6140211],[0.38233972,0.11824663,-0.048423085,-0.22234191,0.071728714,0.09328836,0.5405535],[1.0066482,0.12065403,-0.67822254,0.91310203,0.3381496,-0.46563134,0.6765602],[-0.058819644,0.04884941,0.44360304,-0.16011114,-0.493316,-0.18134312,0.31395072],[-1.1391008,1.2845155,0.65725374,-0.75513005,1.1359249,-0.5923027,-0.46509168],[-0.28455976,0.122011006,0.19924502,-0.016063396,-0.19426562,-0.7366286,-0.30496517],[-1.1961275,-0.44696903,0.2981926,-0.6047452,0.60021174,0.7954056,0.67303944],[0.66179645,-0.35565352,0.4263759,-0.23354627,-0.5218455,-0.14847569,-0.45979464]],"activation":"σ","dense_2_b":[[-0.70381194],[-0.41715994],[-0.18704335],[-0.20373428],[-0.23588026],[-0.022451308],[0.010537296],[0.10221664],[0.014627381],[0.060474735],[-0.033638965],[-0.051280532],[-0.20772089]]},{"dense_3_W":[[0.15796849,0.6079072,0.5830924,-0.5528727,-0.1478945,-0.27996355,-0.30358565,-0.6117205,0.22281085,0.6154891,0.0165758,0.616245,-0.5880501],[-0.1846488,-0.46482992,-0.33469623,-0.15623315,-0.20466302,0.642436,0.34555972,0.60815287,0.43956748,-0.5986671,0.3041771,-0.26802206,0.03272353],[-0.88071394,-0.01339668,-0.4710771,-0.08369313,-0.47736472,-0.29117015,0.5252153,0.36675686,0.37659007,-0.38053805,0.07111416,-0.030936832,-0.3208197]],"activation":"identity","dense_3_b":[[-0.04642651],[0.037014484],[0.055794742]]},{"dense_4_W":[[-0.5493109,0.48931855,0.5176042]],"dense_4_b":[[0.04616592]],"activation":"identity"}]}
@@ -0,0 +1 @@
{"input_std":[[7.1964235],[0.87585396],[0.34334365],[0.04189015],[0.88570154],[0.8828632],[0.8788816],[0.85152715],[0.8326557],[0.8105437],[0.790872],[0.041831378],[0.04184441],[0.041850727],[0.041653063],[0.041487437],[0.041109927],[0.0406368]],"model_test_loss":0.016751321032643318,"input_size":18,"current_date_and_time":"2023-08-06_13-52-18","input_mean":[[27.281948],[0.057562165],[0.025426663],[0.001435362],[0.049740613],[0.051872276],[0.053928934],[0.06504589],[0.07349952],[0.08201744],[0.088106535],[0.0013536362],[0.0013746973],[0.001394585],[0.0014019185],[0.001435841],[0.0014179434],[0.0013371226]],"input_vars":["v_ego","lateral_accel","lateral_jerk","roll","lateral_accel_m03","lateral_accel_m02","lateral_accel_m01","lateral_accel_p03","lateral_accel_p06","lateral_accel_p10","lateral_accel_p15","roll_m03","roll_m02","roll_m01","roll_p03","roll_p06","roll_p10","roll_p15"],"output_size":1,"layers":[{"dense_1_b":[[-0.0041493755],[0.21777502],[0.7115575],[-2.2624984],[0.10148076],[-1.9139864],[-2.6148589]],"dense_1_W":[[0.0027682746,-0.5923534,-0.0008324875,-0.96639884,-0.43687794,1.0992304,0.44394565,-0.15681875,-0.19448969,0.23627451,0.027064066,1.0438472,0.38998574,-0.3910353,0.07241692,-0.3872127,0.24487913,-0.00981091],[-0.0063605076,0.41579154,4.60037,0.15178576,-0.41990525,-0.9291791,-1.8990772,-1.2513685,1.3644165,1.6527833,0.822613,0.6267062,0.3986885,-0.65917814,-0.68864816,0.038168486,-0.43694118,0.5313064],[0.1826715,0.21753219,2.6482086,0.49831605,0.04197241,0.48248503,-0.85676134,-0.088123225,0.5933527,-0.101084374,-0.07996661,0.39820936,-0.25201222,-0.5013944,-0.18441774,0.085484736,-0.20741902,0.15278186],[0.20413034,0.7558973,0.0039974884,0.09515875,0.023952896,0.12318843,-0.24863702,0.28330338,0.598279,-0.0031794424,0.12692909,0.26882735,0.002343814,-0.20356114,-0.4958486,-0.2599618,-0.3243958,-0.15502147],[0.0008160921,-0.75155866,3.2958338,0.18005188,-0.48001406,-0.37221822,-0.014907395,0.34556574,-0.36695525,0.5220802,0.9252095,-0.6326275,0.31536806,0.40089336,-0.24786693,-0.16599582,0.26964852,-0.11618827],[-0.6374511,0.50671804,3.4275095,0.45084175,0.014429362,0.16759996,-0.40217194,-0.1596027,0.27413476,-0.020348325,0.03291187,0.29519597,0.0606238,-0.77494323,-0.04082934,-0.019097991,0.021767832,0.017387142],[0.24087234,-0.5286611,-0.0010711186,0.50206137,-0.37612498,-0.065321624,0.3152248,-0.3156515,-0.31909347,-0.5960725,0.07865838,-0.57626325,-0.28874642,0.36706463,0.55150944,0.021375967,0.53079313,0.0685934]],"activation":"σ"},{"dense_2_W":[[-0.61061996,-0.3219788,-0.61355484,-0.41679764,0.9514292,0.089833274,-0.08153515],[0.5216678,0.35865775,0.14493388,0.75064963,-0.9559691,-0.11178071,-0.26124665],[-0.07039463,-0.54774076,-0.7926992,-0.28961974,0.25917596,0.06013633,0.6089615],[-0.29096878,-0.55007404,-0.6435847,0.022112146,0.31680974,-0.22504552,0.35249886],[-0.6804567,-0.94920105,-0.8136485,0.3378733,0.597489,0.46872833,0.28344125],[-0.8909486,0.17668937,-0.50494355,-0.51266,0.76238817,-0.08368523,0.21360965],[-0.08500846,-1.3190831,-0.40328535,0.17221951,0.9102355,-0.42406878,-0.14185876],[0.41169032,0.12483639,-0.43139625,1.0178162,-0.7741975,0.49720922,-0.0029358894],[-0.11413294,0.35335183,0.5509884,0.60089856,-0.07613763,-0.18951175,-0.5859379],[-0.30574977,0.66385657,0.29187888,0.15835138,-0.13989422,0.93255055,0.16954228],[0.9125405,0.33926046,-0.45827863,0.20637102,-1.3337029,0.99624145,-0.6220122],[1.0066077,0.5247419,0.3793085,0.006325064,-0.7290911,-0.32310325,0.17187376],[-0.6349526,-0.62067527,-0.291574,0.05529339,0.7121756,-0.08375974,0.22644123]],"activation":"σ","dense_2_b":[[0.04084762],[-0.06363596],[0.024858473],[0.10673008],[0.13199006],[0.10104594],[-0.008044478],[-0.10162189],[-0.15080203],[-0.11178794],[-0.24866438],[-0.17219713],[-0.008406249]]},{"dense_3_W":[[0.40549794,-0.4494019,0.2861901,0.37104332,0.5890352,0.58301944,0.40222168,-0.49054152,-0.31034032,-0.07984426,-0.5973653,-0.20854333,0.5224267],[-0.6481895,0.81970197,-0.5651685,-0.54807013,-0.5982727,-0.21111657,-0.7437358,-0.18910314,-0.22034706,0.69472176,0.7825804,0.5047144,0.30723065],[-0.2815957,-0.25135168,-0.03530872,-0.47208393,-0.14322938,0.72277945,0.027914466,-0.113597095,-0.31811848,0.057283014,0.22687551,0.074457414,0.35829133]],"activation":"identity","dense_3_b":[[-0.064762406],[0.042542093],[0.06525171]]},{"dense_4_W":[[-1.0957879,0.49216008,-0.0023324802]],"dense_4_b":[[0.054476205]],"activation":"identity"}]}
@@ -0,0 +1 @@
{"input_std":[[8.67131],[1.0606116],[0.3141555],[0.047306955],[1.0598024],[1.059542],[1.058586],[1.0353245],[1.0132898],[0.98134106],[0.9488499],[0.0472228],[0.047223553],[0.04722212],[0.047007233],[0.04672726],[0.046201415],[0.045611456]],"model_test_loss":0.030893046408891678,"input_size":18,"current_date_and_time":"2023-08-06_16-03-17","input_mean":[[24.826109],[-0.0015730427],[-0.0024845933],[-0.006542893],[-0.0021615939],[-0.0027822834],[-0.0034116579],[-0.002972936],[-0.0019527061],[-0.0018800201],[-0.00055315095],[-0.0066206236],[-0.0066137947],[-0.0066051884],[-0.0065096957],[-0.00645474],[-0.0064896797],[-0.0065450436]],"input_vars":["v_ego","lateral_accel","lateral_jerk","roll","lateral_accel_m03","lateral_accel_m02","lateral_accel_m01","lateral_accel_p03","lateral_accel_p06","lateral_accel_p10","lateral_accel_p15","roll_m03","roll_m02","roll_m01","roll_p03","roll_p06","roll_p10","roll_p15"],"output_size":1,"layers":[{"dense_1_b":[[2.0666385],[-2.1265264],[-0.009373159],[2.1585333],[0.037653618],[-0.017867725],[3.2116225]],"dense_1_W":[[1.1914837,-0.5645276,6.58303e-5,-0.11589882,0.37734768,-0.27381,-0.19863185,0.824706,0.4373293,-0.20449224,-0.15180561,0.35472932,0.3237236,-0.10154249,-0.578405,-0.2772973,0.10017485,0.25640446],[-1.0303509,0.29729548,1.9708672,-0.5768468,-0.028354015,0.42035812,0.3361436,0.3343814,0.37328744,0.8638404,-0.2677837,-0.15447378,0.49736965,-0.08687722,-0.66492444,-0.032154877,1.1899335,-0.48110774],[0.01983638,-0.24834296,-3.3855686,0.6749708,0.07187869,-0.024789268,0.37607005,-0.47755113,-1.0089839,-0.81658024,1.3305435,-0.97285813,-0.6226301,0.6422294,0.9579225,0.49827808,-0.48402268,-0.29993263],[1.2045997,0.62863886,0.0019727307,0.17700921,-0.8630537,0.98878884,-0.16361095,-0.29803157,-0.7254775,-0.15229262,0.3722205,-0.22486125,-0.06222317,-0.37571073,0.4424153,0.06388154,0.5859267,-0.56900877],[-0.013524671,0.31598365,0.0006406281,-0.25858632,-0.27701482,0.7357323,-0.35521507,-0.4483451,0.44878978,0.2829642,-0.2362616,0.62810344,-0.121423006,-0.38609955,0.0749125,-0.17727378,-0.38110465,0.35318217],[-0.0052065155,-0.5593012,0.002159691,-0.08422028,0.23008162,-1.022624,0.32955936,0.431307,-0.1006895,-0.030441636,-0.08401783,-0.3437167,-0.22863047,0.7158902,-0.17199361,0.09197799,0.19186375,-0.1833609],[1.4867296,-0.15920977,2.3333318,-0.23857078,-0.0040042745,1.2148064,0.10699171,0.22722282,0.5444096,1.349413,-0.47866884,0.40205494,-0.11314535,-0.5150614,-0.4630753,0.030254288,0.7952247,-0.2603006]],"activation":"σ"},{"dense_2_W":[[-0.17723514,-0.23341042,0.57393736,-0.4927488,-0.927548,0.58540887,-0.5868284],[0.66296387,0.29695207,0.20052847,-0.62608564,-1.0550549,1.0728254,0.0891513],[-0.16829221,-0.0679403,-0.36510104,0.23858383,1.1178643,-0.6089432,-0.31060657],[-1.8900537,-0.27381107,-0.5241167,-0.7089269,-1.1469728,-0.0641088,-0.41346708],[-0.51642907,-0.43836012,-0.31121448,0.47541407,1.169322,-0.2952497,-0.37012473],[0.8616518,0.12941492,-1.1983941,1.7177894,1.0778812,-1.3677566,1.5917606],[-0.407816,0.042307053,-0.53986156,0.5610484,0.7355027,-0.8320296,0.05677452],[1.4589415,-1.2519528,0.8708634,0.6212387,-0.9204132,1.7146589,0.13543351],[-0.055101812,-0.19029272,0.013408977,-0.46497017,-0.92594385,0.9864242,0.13991055],[0.5894197,-0.28149623,0.41216704,0.216877,-0.51424307,0.83235174,0.14314461],[-2.6734824,2.668342,-1.7240498,-1.0608208,0.54306996,-1.3263385,-0.19313851],[-0.82157856,-0.23220612,1.0158018,-2.0316136,0.06581752,0.7996686,-1.5219475],[0.5740489,0.18412288,-0.14555724,-0.5847231,-1.1873335,1.0647899,0.008006016]],"activation":"σ","dense_2_b":[[-0.29801813],[-0.25020108],[-0.022192547],[0.026752593],[-0.027117254],[0.30578387],[0.053404447],[0.23486306],[-0.07118241],[-0.09914868],[-0.18535325],[-0.32233533],[-0.04571355]]},{"dense_3_W":[[0.43566743,0.43129563,-0.6076586,-0.46461692,-0.40563574,-0.60545087,0.47773033,0.15361485,0.54101425,-0.14459106,-1.1077414,0.30648795,0.5856095],[0.07340811,-0.49523342,0.6213182,0.19087262,0.06972069,0.8834424,0.5443848,-0.50467765,-0.38180703,-0.4638601,0.65264165,-0.8575672,-0.5819035],[0.33573294,0.619451,-0.046216093,-0.66367626,-0.64414126,-0.084879674,-0.60994107,0.5997581,-0.2552046,0.10798684,-0.29609105,0.45087123,-0.3904423]],"activation":"identity","dense_3_b":[[0.064981125],[0.043677732],[-0.029785963]]},{"dense_4_W":[[-0.35612056,0.9586297,-0.2525408]],"dense_4_b":[[0.03888263]],"activation":"identity"}]}
@@ -0,0 +1 @@
{"input_std":[[8.118591],[1.012343],[0.30252126],[0.04747358],[1.0110137],[1.0105052],[1.0093575],[0.98329234],[0.96060896],[0.93123335],[0.90310067],[0.04717917],[0.047231637],[0.047265887],[0.047122534],[0.046797972],[0.046346754],[0.045773227]],"model_test_loss":0.03557046875357628,"input_size":18,"current_date_and_time":"2023-08-06_17-27-23","input_mean":[[24.204573],[-0.024622405],[0.0015252293],[-0.0032674451],[-0.023904368],[-0.02417538],[-0.024461435],[-0.023585865],[-0.021832237],[-0.020321053],[-0.019801456],[-0.0032891335],[-0.0032849729],[-0.0032711467],[-0.0032068372],[-0.0030881383],[-0.0030222118],[-0.0030280964]],"input_vars":["v_ego","lateral_accel","lateral_jerk","roll","lateral_accel_m03","lateral_accel_m02","lateral_accel_m01","lateral_accel_p03","lateral_accel_p06","lateral_accel_p10","lateral_accel_p15","roll_m03","roll_m02","roll_m01","roll_p03","roll_p06","roll_p10","roll_p15"],"output_size":1,"layers":[{"dense_1_b":[[-0.7401334],[-4.296874],[0.051964045],[-1.5580355],[0.59131175],[0.9876563],[-0.059906512]],"dense_1_W":[[0.39198807,0.14618506,-0.0005603469,0.5384161,0.583554,0.90904,-0.9356289,-0.347572,0.054832783,-0.01266942,0.20926124,0.40405145,0.26462293,-0.5968776,-0.4170079,-0.24383324,-0.36820978,0.23693004],[-1.4237757,0.44019157,1.7405101,0.107415356,0.73406404,0.38215244,-0.6587738,0.6303781,0.9956125,0.3772358,-0.60114783,0.28546256,-0.21894793,-0.5291562,0.028785085,-0.0466827,-0.02260468,-0.14874777],[-0.011811628,0.5467675,3.1406043,-0.5367604,0.16197537,0.72486085,-1.0583096,0.6693704,0.23038347,0.16671906,-0.6996211,0.9920219,0.24508859,-0.8121351,-0.11340726,-0.124802016,0.0040163123,-0.03318359],[-0.89146703,-0.43514004,-1.1770084,-0.113605686,-0.2791039,-0.6336671,0.52206266,-0.37517008,-0.583943,0.13963065,0.10783694,-0.019744247,0.09936149,0.34897432,0.077529624,-0.12277383,-0.21316914,0.31482303],[-0.5459662,0.6174858,-1.5395053e-5,0.23975974,0.1680196,0.6688479,-0.5717515,-0.2257085,-0.37668842,0.37691414,0.016556103,0.5753194,0.43969232,-0.63690066,-0.79015404,0.094646364,-0.26196766,0.13351451],[1.219549,0.6434229,1.5366393,0.29663944,-0.1569686,0.1139518,1.0193243,0.13182868,0.1257156,0.4684622,-0.33492422,-0.1404477,-0.20704125,0.21394883,-0.60481495,0.20762968,-0.2884613,0.04056693],[0.0022561958,-0.10740119,-2.973164,-0.24940717,-0.11246171,-0.017514866,-1.2795074,-0.021132804,0.8575916,0.07600141,-0.029272523,0.5688567,0.09931985,-0.10324848,-0.2901189,-0.37775588,0.39807785,0.3334187]],"activation":"σ"},{"dense_2_W":[[0.5666278,0.1050696,0.5731484,-0.6657482,0.57242334,-0.6813752,0.650503],[-0.3233396,-0.44350263,-0.16690572,0.123169236,-0.8172176,0.5242723,-0.45013273],[-0.1649446,0.012782408,-0.80965835,0.28827253,-0.6245722,0.2006493,-1.087686],[0.96152276,0.35989368,0.22035821,-0.80648047,0.4686015,-0.6329297,-0.0023717843],[-0.11855806,-0.91366136,-0.90975124,0.47533065,0.41179633,0.05490874,0.1064329],[-0.73532724,-0.6584749,-0.5160826,0.34969312,0.062357668,0.13114415,-0.32903922],[-0.72250515,1.2826318,-1.2006475,0.9427826,-1.371755,0.20815341,-1.0248761],[-0.4109795,-0.64342046,-0.22680378,0.12261962,-0.15706545,0.4520076,-0.09530116],[0.5485182,0.3166386,0.60656774,-1.0352023,0.35866728,-0.37757927,-0.37554651],[-0.7688577,-0.31978396,-0.42268845,0.59393954,-0.4435929,0.47090167,-0.31540674],[-1.0920306,-1.0558451,0.11860894,0.49140632,-0.10677766,0.47734076,-0.398253],[-0.030640373,0.41173688,-0.98270494,0.5081087,-0.5071277,-0.3028872,-0.8938836],[0.4381069,0.943475,0.6218088,-0.742083,-0.38795125,-0.054727156,0.31301942]],"activation":"σ","dense_2_b":[[-0.09199342],[0.03208081],[-0.04846608],[-0.1532128],[-0.012221367],[-0.15162058],[0.14393868],[-0.17525378],[-0.38610223],[0.03283754],[0.02243522],[-0.046554077],[-0.10267613]]},{"dense_3_W":[[0.76260686,-0.14955303,-0.07697563,0.795136,-0.5793355,-0.39125565,-0.850312,0.054931063,0.53414947,-0.3139965,-0.5467518,-0.38860124,-0.034747276],[0.26200438,-0.37439495,-0.4377097,0.17163786,-0.30471745,0.30637982,0.06850715,-0.34865668,-0.35463956,-0.1303642,-0.39582416,-0.5477129,0.69954056],[-0.38012898,0.3671737,0.4092681,0.1147916,-0.33737427,0.28946012,-0.2992325,-0.067024715,0.4072253,-0.31171417,0.33009604,0.12577192,-0.27651602]],"activation":"identity","dense_3_b":[[0.05052462],[0.061525963],[0.01540329]]},{"dense_4_W":[[1.5063055,0.714805,0.00043001983]],"dense_4_b":[[0.047914207]],"activation":"identity"}]}
@@ -0,0 +1 @@
{"input_std":[[8.957924],[1.0602055],[0.3483377],[0.044007655],[1.0696881],[1.0669217],[1.0629013],[1.022558],[0.99117285],[0.95355713],[0.91828537],[0.043950602],[0.043960184],[0.043955244],[0.04370865],[0.043465454],[0.04305762],[0.042518806]],"model_test_loss":0.03983215242624283,"input_size":18,"current_date_and_time":"2023-08-06_20-30-54","input_mean":[[24.622238],[0.017521156],[0.00757826],[0.0011451022],[0.016594924],[0.017204277],[0.018039215],[0.021406805],[0.02439622],[0.026556514],[0.028528417],[0.001193815],[0.0011970581],[0.0011935198],[0.0011202878],[0.001060377],[0.00095698517],[0.0008617843]],"input_vars":["v_ego","lateral_accel","lateral_jerk","roll","lateral_accel_m03","lateral_accel_m02","lateral_accel_m01","lateral_accel_p03","lateral_accel_p06","lateral_accel_p10","lateral_accel_p15","roll_m03","roll_m02","roll_m01","roll_p03","roll_p06","roll_p10","roll_p15"],"output_size":1,"layers":[{"dense_1_b":[[-0.07002942],[-0.16705358],[-2.9611764],[0.34919006],[0.515888],[-3.0217357],[-0.3703743]],"dense_1_W":[[-0.001037722,-0.23034889,0.00070932484,0.19091949,-0.35902733,-0.8921682,0.30972338,0.3144616,0.13060336,-0.1961081,-0.1309547,-0.729507,-0.079179555,0.49697357,0.02997426,0.37342516,-0.24386461,0.3236473],[0.9159012,-0.010550688,-2.401509,0.30691516,0.05056717,0.5097978,-1.1722806,0.17247927,0.34559217,-0.24330676,0.04055541,0.46409106,0.20967084,-0.82562864,-0.4943086,0.2081027,0.2530364,-0.0926206],[-0.93078494,0.5642841,2.5035574,-0.35270843,0.18849362,0.87544584,-0.2044937,0.73466784,0.47539774,0.07997192,-0.76057374,0.3159093,0.40554965,-0.5378864,-0.09868344,-0.3483285,0.16861473,0.19001411],[-0.8801367,-0.10308599,-2.2299495,0.04202499,-0.05023124,0.13920471,-0.78326404,0.39430746,0.23543742,0.14010431,-0.23845911,0.2567459,0.059797786,-0.57988393,0.113685064,0.14517607,0.16730362,-0.17216507],[0.14910753,-0.31030202,-2.4019601,0.1968272,-0.40163112,-0.23238596,0.6703164,0.07859055,-0.14317967,0.07362763,-0.04258365,-0.509903,-0.38269565,0.7657585,0.030617407,-0.11210979,0.30991936,-0.27056235],[-0.9013445,-0.74709606,-2.4476326,0.38786903,-0.3051019,-0.585037,0.06283271,-0.3787836,-0.61694026,-0.032208323,0.70738953,-0.2658627,-0.35824144,0.5567474,-0.033071913,0.1493693,-0.014829384,-0.16828252],[0.06442954,-0.869912,-2.9056792,0.1365006,0.16463475,-0.5906101,1.1546344,-0.06482889,-0.37295368,-0.1456135,0.2937723,-0.5213806,-0.57213086,0.38927227,0.49697468,0.21409509,0.63319594,-0.7391959]],"activation":"σ"},{"dense_2_W":[[-0.17591965,-0.07106175,-0.40103337,0.56516284,-0.6760929,-0.15387549,-0.89492965],[-0.45764968,-1.0195202,-0.41464484,-0.2810938,-0.028325718,0.09300495,0.35360244],[0.8125133,-0.54840755,-0.6930447,-0.89210993,0.56695956,0.44670337,0.5310144],[-1.0358114,0.33042994,0.11012975,0.23487902,0.24800783,-0.4299027,-0.46129325],[1.2276291,-0.5162977,0.22191486,0.16638434,0.23429595,-0.22487517,0.28309682],[1.0107075,-1.0881076,-1.008768,-0.5629702,-0.05878989,0.7923787,0.7309502],[0.49559563,-0.35230458,-0.7740999,0.02204071,0.24508348,0.075914145,0.75374156],[-0.21838671,0.42116773,0.5945599,0.30151197,-0.24432571,-0.46020442,-0.33258623],[-0.021219064,-0.25157404,-0.35190338,0.0668151,-0.6102883,0.1603531,-0.79725957],[0.0735072,0.5327899,-0.13316682,0.18007801,-0.7896261,0.08766711,-0.64606833],[0.6770077,-0.8900657,-0.26981142,-0.38599125,0.09799069,0.5758287,0.66643196],[-0.82503897,0.3003343,0.15965503,0.37416932,-0.644764,0.043236665,0.19912353],[-0.9234739,0.57710344,-0.4265305,0.3661275,-0.030128404,-0.4210636,-0.7627061]],"activation":"σ","dense_2_b":[[0.07509254],[-0.45370674],[-0.08009313],[-0.21234833],[-0.08377176],[-0.056009393],[-0.10460085],[-0.003972127],[-0.21230274],[-0.0108628925],[-0.14868702],[0.04881384],[0.053917274]]},{"dense_3_W":[[-0.43450725,0.28928438,0.63958466,0.26900065,0.59471947,0.045950003,-0.08964677,0.14317968,-0.1560713,0.4362775,0.6118739,-0.48017085,-0.17556131],[0.7623459,-0.3990245,-0.53529125,0.28715876,-0.25771093,0.25057483,-0.4946012,0.14644647,-0.02983568,-0.065986775,-0.55813897,0.34047204,0.29583102],[-0.093450435,-0.42808297,0.068437345,-0.1521326,0.11618435,0.6433002,0.44079673,-0.6619756,-0.2237737,-0.65349114,-0.118622996,-0.61334574,-0.14610668]],"activation":"identity","dense_3_b":[[-0.06914952],[0.05192733],[-0.03913266]]},{"dense_4_W":[[-0.4807093,1.1989409,-1.1321285]],"dense_4_b":[[0.044341844]],"activation":"identity"}]}
@@ -0,0 +1 @@
{"input_std":[[8.738584],[1.5957829],[0.58238095],[0.048208784],[1.5838295],[1.5881729],[1.5908173],[1.5664002],[1.5340934],[1.4806168],[1.4221525],[0.047998115],[0.04803774],[0.048071872],[0.048102442],[0.04803064],[0.047811132],[0.04743304]],"model_test_loss":0.007402807008475065,"input_size":18,"current_date_and_time":"2023-08-06_22-19-42","input_mean":[[21.779322],[-0.078570016],[-0.0073580677],[-0.0065681813],[-0.07246284],[-0.0734754],[-0.07447408],[-0.07268334],[-0.069738925],[-0.06446174],[-0.057134904],[-0.0064781737],[-0.006475137],[-0.006476324],[-0.006474212],[-0.006491243],[-0.00650736],[-0.00642134]],"input_vars":["v_ego","lateral_accel","lateral_jerk","roll","lateral_accel_m03","lateral_accel_m02","lateral_accel_m01","lateral_accel_p03","lateral_accel_p06","lateral_accel_p10","lateral_accel_p15","roll_m03","roll_m02","roll_m01","roll_p03","roll_p06","roll_p10","roll_p15"],"output_size":1,"layers":[{"dense_1_b":[[-2.1603358],[-0.419876],[0.084331356],[-0.07068766],[2.0145605],[-0.63310754],[1.434537]],"dense_1_W":[[-0.31878927,-0.47954968,-0.260915,-0.23391017,-0.14123324,-0.16656223,0.34148905,-0.55040246,0.048909064,-0.084660634,-0.05452977,-0.14122179,0.19401318,0.18124454,0.29322624,-0.11721254,0.058719337,0.049679324],[1.2746987,0.53341556,-0.5038306,-0.3943573,-0.3552817,0.6985798,-0.7583908,-0.071790285,0.10988926,-0.1613498,-0.43469277,0.20283195,0.37326258,-0.2713744,0.053292856,0.12493837,0.112449214,0.071374826],[-0.005748887,0.4395075,-0.023206176,-0.00010381389,0.10724853,0.79803634,-0.10418732,-0.17200217,0.07352905,-0.04382284,0.12897137,0.27188876,-0.2986032,0.1732639,-0.43260622,-0.13877538,0.108885035,0.037624177],[-0.009819267,0.7978249,3.3524475,0.21963945,-1.1893661,0.12886666,-0.12307468,-0.0057551884,0.89947134,0.2433565,-0.7049134,0.41656977,0.5013669,-0.4488184,-0.19362147,-0.21371745,-0.12667105,-0.11616998],[0.5350432,-0.6518214,-0.29382524,-0.186406,-0.17762733,-0.1445377,0.13506992,-0.35136354,0.24111918,-0.36675584,0.021335155,-0.05516007,-0.15420103,0.4554361,0.4533603,-0.34406003,0.006251238,0.1428859],[1.1166015,-0.29759768,0.48270738,0.43025842,0.0056440467,-0.4213741,0.4217568,0.15726581,0.074272975,0.1225627,0.32488325,-0.059570342,-0.34078333,0.076991476,-0.26384223,-0.06351244,0.1692961,-0.21616662],[0.098687425,-0.3278237,-0.21179028,0.0031954458,-0.20943744,-0.13822405,0.548112,-0.34726304,-0.3400807,0.25883827,-0.17620128,-0.36422935,0.52832425,-0.1289231,-0.026027037,0.18089916,0.1012807,-0.07116273]],"activation":"σ"},{"dense_2_W":[[1.3021795,-0.4139669,-0.7120532,-0.74666166,-0.18984732,0.048739053,-0.25614926],[-0.45348325,-0.5127636,-0.35794353,0.021264847,-0.73585445,0.02490475,-0.37029886],[-1.0356512,0.21161588,0.15873429,0.17991234,-0.3031208,0.07505193,-0.33505473],[1.5017016,-0.31481206,-1.1198424,-0.40624765,-0.09357655,0.51827437,-0.055450503],[-0.6468184,0.053211562,-0.6933904,-0.13317326,-0.24886934,-0.3967814,-0.6371098],[-0.41236708,0.3045984,0.51569694,0.026683038,-0.66206634,0.038440548,-0.3386708],[-0.58338356,-0.09002029,-0.13582824,0.008655593,-0.69411683,-0.21923552,-0.3192624],[-0.6897999,0.12905283,0.67996913,0.1456075,0.25029323,-0.34134966,-0.6459057],[-0.252227,0.043475367,-0.9427369,-0.14302255,-0.10663869,0.31176025,-0.35857844],[0.22877508,-0.5189181,-0.41833526,0.20187113,-0.1781152,-0.16428699,-0.2800521],[-0.65191513,0.44252837,0.6581859,0.25468537,-0.9424999,-0.32678768,-0.8593621],[0.43401113,-0.24967693,-0.281952,-0.284015,0.7110927,0.27138582,0.9720163],[-1.1512148,-0.18058608,0.45996955,-0.5937282,0.31828657,0.15207982,-0.22012103]],"activation":"σ","dense_2_b":[[0.0048766164],[-0.056708604],[-0.123050824],[-0.07202907],[-0.25869218],[-0.20529492],[-0.091088444],[-0.091022156],[-0.32449347],[-0.17546864],[-0.0612454],[0.04344016],[-0.074389376]]},{"dense_3_W":[[0.25576973,-0.575117,-0.6076316,0.36512917,-0.41574427,-0.40301278,-0.4620185,-0.5097215,0.122016355,0.3936914,-0.041409556,-0.059126735,0.07365807],[0.29514056,-0.25677466,-0.18109803,-0.030853858,-0.029449929,-0.28013232,-0.025037538,-0.4706025,0.02876151,-0.48724273,-0.6757201,0.6601352,-0.20265144],[-0.7641006,0.21497111,-0.28698337,-0.379376,-0.31239873,-0.26711258,0.02512146,-0.31214246,0.077003516,-0.18367848,0.6396449,-0.61027414,0.5836878]],"activation":"identity","dense_3_b":[[0.047669664],[0.041923303],[-0.020977087]]},{"dense_4_W":[[-0.6845342,-1.0030532,0.7527504]],"dense_4_b":[[-0.03611581]],"activation":"identity"}]}
Loaded 100 of 3995 files, more files were not shown because too many files have changed in this diff. Show more