EKF global variable support (#1230)

* Add global variables to kalman filter code

* fix linter
old-commit-hash: e28832b359
commatwo_master
Willem Melching 5 years ago committed by GitHub
parent 6c6df5486a
commit ea1c97cab7
  1. 24
      selfdrive/locationd/kalman/helpers/ekf_sym.py
  2. 4
      selfdrive/locationd/kalman/helpers/sympy_helpers.py
  3. 30
      selfdrive/locationd/kalman/models/car_kf.py
  4. 17
      selfdrive/locationd/paramsd.py

@ -27,7 +27,7 @@ def null(H, eps=1e-12):
return np.transpose(null_space) return np.transpose(null_space)
def gen_code(name, f_sym, dt_sym, x_sym, obs_eqs, dim_x, dim_err, eskf_params=None, msckf_params=None, maha_test_kinds=[]): def gen_code(name, f_sym, dt_sym, x_sym, obs_eqs, dim_x, dim_err, eskf_params=None, msckf_params=None, maha_test_kinds=[], global_vars=None):
# optional state transition matrix, H modifier # optional state transition matrix, H modifier
# and err_function if an error-state kalman filter (ESKF) # and err_function if an error-state kalman filter (ESKF)
# is desired. Best described in "Quaternion kinematics # is desired. Best described in "Quaternion kinematics
@ -110,7 +110,7 @@ def gen_code(name, f_sym, dt_sym, x_sym, obs_eqs, dim_x, dim_err, eskf_params=No
sympy_functions.append(('He_%d' % kind, He_sym, [x_sym, ea_sym])) sympy_functions.append(('He_%d' % kind, He_sym, [x_sym, ea_sym]))
# Generate and wrap all th c code # Generate and wrap all th c code
header, code = sympy_into_c(sympy_functions) header, code = sympy_into_c(sympy_functions, global_vars)
extra_header = "#define DIM %d\n" % dim_x extra_header = "#define DIM %d\n" % dim_x
extra_header += "#define EDIM %d\n" % dim_err extra_header += "#define EDIM %d\n" % dim_err
extra_header += "#define MEDIM %d\n" % dim_main_err extra_header += "#define MEDIM %d\n" % dim_main_err
@ -140,6 +140,17 @@ def gen_code(name, f_sym, dt_sym, x_sym, obs_eqs, dim_x, dim_err, eskf_params=No
code += '\nextern "C"{\n' + extra_header + "\n}\n" code += '\nextern "C"{\n' + extra_header + "\n}\n"
code += "\n" + open(os.path.join(TEMPLATE_DIR, "ekf_c.c")).read() code += "\n" + open(os.path.join(TEMPLATE_DIR, "ekf_c.c")).read()
code += '\nextern "C"{\n' + extra_post + "\n}\n" code += '\nextern "C"{\n' + extra_post + "\n}\n"
if global_vars is not None:
global_code = '\nextern "C"{\n'
for var in global_vars:
global_code += f"\ndouble {var.name};\n"
global_code += f"\nvoid set_{var.name}(double x){{ {var.name} = x;}}\n"
extra_header += f"\nvoid set_{var.name}(double x);\n"
global_code += '\n}\n'
code = global_code + code
header += "\n" + extra_header header += "\n" + extra_header
write_code(name, code, header) write_code(name, code, header)
@ -147,7 +158,7 @@ def gen_code(name, f_sym, dt_sym, x_sym, obs_eqs, dim_x, dim_err, eskf_params=No
class EKF_sym(): class EKF_sym():
def __init__(self, name, Q, x_initial, P_initial, dim_main, dim_main_err, def __init__(self, name, Q, x_initial, P_initial, dim_main, dim_main_err,
N=0, dim_augment=0, dim_augment_err=0, maha_test_kinds=[]): N=0, dim_augment=0, dim_augment_err=0, maha_test_kinds=[], global_vars=None):
"""Generates process function and all observation functions for the kalman filter.""" """Generates process function and all observation functions for the kalman filter."""
self.msckf = N > 0 self.msckf = N > 0
self.N = N self.N = N
@ -168,6 +179,8 @@ class EKF_sym():
# tested for outlier rejection # tested for outlier rejection
self.maha_test_kinds = maha_test_kinds self.maha_test_kinds = maha_test_kinds
self.global_vars = global_vars
# process noise # process noise
self.Q = Q self.Q = Q
@ -226,6 +239,11 @@ class EKF_sym():
if self.msckf and kind in self.feature_track_kinds: if self.msckf and kind in self.feature_track_kinds:
self.Hes[kind] = wrap_2lists("He_%d" % kind) self.Hes[kind] = wrap_2lists("He_%d" % kind)
if self.global_vars is not None:
for var in self.global_vars:
fun_name = f"set_{var.name}"
setattr(self, fun_name, getattr(lib, fun_name))
# wrap the C++ predict function # wrap the C++ predict function
def _predict_blas(x, P, dt): def _predict_blas(x, P, dt):
lib.predict(ffi.cast("double *", x.ctypes.data), lib.predict(ffi.cast("double *", x.ctypes.data),

@ -46,11 +46,11 @@ def quat_matrix_r(p):
[p[3], p[2], -p[1], p[0]]]) [p[3], p[2], -p[1], p[0]]])
def sympy_into_c(sympy_functions): def sympy_into_c(sympy_functions, global_vars=None):
from sympy.utilities import codegen from sympy.utilities import codegen
routines = [] routines = []
for name, expr, args in sympy_functions: for name, expr, args in sympy_functions:
r = codegen.make_routine(name, expr, language="C99") r = codegen.make_routine(name, expr, language="C99", global_vars=global_vars)
# argument ordering input to sympy is broken with function with output arguments # argument ordering input to sympy is broken with function with output arguments
nargs = [] nargs = []

@ -78,6 +78,14 @@ class CarKalman():
} }
maha_test_kinds = [] # [ObservationKind.ROAD_FRAME_YAW_RATE, ObservationKind.ROAD_FRAME_XY_SPEED] maha_test_kinds = [] # [ObservationKind.ROAD_FRAME_YAW_RATE, ObservationKind.ROAD_FRAME_XY_SPEED]
global_vars = [
sp.Symbol('mass'),
sp.Symbol('rotational_inertia'),
sp.Symbol('center_to_front'),
sp.Symbol('center_to_rear'),
sp.Symbol('stiffness_front'),
sp.Symbol('stiffness_rear'),
]
@staticmethod @staticmethod
def generate_code(): def generate_code():
@ -85,28 +93,18 @@ class CarKalman():
name = CarKalman.name name = CarKalman.name
maha_test_kinds = CarKalman.maha_test_kinds maha_test_kinds = CarKalman.maha_test_kinds
# globals
m, j, aF, aR, cF_orig, cR_orig = CarKalman.global_vars
# make functions and jacobians with sympy # make functions and jacobians with sympy
# state variables # state variables
state_sym = sp.MatrixSymbol('state', dim_state, 1) state_sym = sp.MatrixSymbol('state', dim_state, 1)
state = sp.Matrix(state_sym) state = sp.Matrix(state_sym)
# Vehicle model constants # Vehicle model constants
# TODO: Read from car params at runtime
from selfdrive.controls.lib.vehicle_model import VehicleModel
from selfdrive.car.toyota.interface import CarInterface
from selfdrive.car.toyota.values import CAR
CP = CarInterface.get_params(CAR.COROLLA_TSS2)
VM = VehicleModel(CP)
m = VM.m
j = VM.j
aF = VM.aF
aR = VM.aR
x = state[States.STIFFNESS, :][0, 0] x = state[States.STIFFNESS, :][0, 0]
cF, cR = x * VM.cF, x * VM.cR cF, cR = x * cF_orig, x * cR_orig
angle_offset = state[States.ANGLE_OFFSET, :][0, 0] angle_offset = state[States.ANGLE_OFFSET, :][0, 0]
angle_offset_fast = state[States.ANGLE_OFFSET_FAST, :][0, 0] angle_offset_fast = state[States.ANGLE_OFFSET_FAST, :][0, 0]
sa = state[States.STEER_ANGLE, :][0, 0] sa = state[States.STEER_ANGLE, :][0, 0]
@ -149,13 +147,13 @@ class CarKalman():
[sp.Matrix([x]), ObservationKind.STIFFNESS, None], [sp.Matrix([x]), ObservationKind.STIFFNESS, None],
] ]
gen_code(name, f_sym, dt, state_sym, obs_eqs, dim_state, dim_state, maha_test_kinds=maha_test_kinds) gen_code(name, f_sym, dt, state_sym, obs_eqs, dim_state, dim_state, maha_test_kinds=maha_test_kinds, global_vars=CarKalman.global_vars)
def __init__(self): def __init__(self):
self.dim_state = self.x_initial.shape[0] self.dim_state = self.x_initial.shape[0]
# init filter # init filter
self.filter = EKF_sym(self.name, self.Q, self.x_initial, self.P_initial, self.dim_state, self.dim_state, maha_test_kinds=self.maha_test_kinds) self.filter = EKF_sym(self.name, self.Q, self.x_initial, self.P_initial, self.dim_state, self.dim_state, maha_test_kinds=self.maha_test_kinds, global_vars=self.global_vars)
@property @property
def x(self): def x(self):

@ -8,8 +8,16 @@ CARSTATE_DECIMATION = 5
class ParamsLearner: class ParamsLearner:
def __init__(self): def __init__(self, CP):
self.kf = CarKalman() self.kf = CarKalman()
self.kf.filter.set_mass(CP.mass) # pylint: disable=no-member
self.kf.filter.set_rotational_inertia(CP.rotationalInertia) # pylint: disable=no-member
self.kf.filter.set_center_to_front(CP.centerToFront) # pylint: disable=no-member
self.kf.filter.set_center_to_rear(CP.wheelbase - CP.centerToFront) # pylint: disable=no-member
self.kf.filter.set_stiffness_front(CP.tireStiffnessFront) # pylint: disable=no-member
self.kf.filter.set_stiffness_rear(CP.tireStiffnessRear) # pylint: disable=no-member
self.active = False self.active = False
self.speed = 0 self.speed = 0
@ -66,7 +74,12 @@ def main(sm=None, pm=None):
if pm is None: if pm is None:
pm = messaging.PubMaster(['liveParameters']) pm = messaging.PubMaster(['liveParameters'])
learner = ParamsLearner() # TODO: Read from car params at runtime
from selfdrive.car.toyota.interface import CarInterface
from selfdrive.car.toyota.values import CAR
CP = CarInterface.get_params(CAR.COROLLA_TSS2)
learner = ParamsLearner(CP)
while True: while True:
sm.update() sm.update()

Loading…
Cancel
Save