#include "survive_kalman_lighthouses.h" #include "survive_recording.h" #include "survive_reproject.h" #include "survive_reproject_gen2.h" #include "generated/kalman_kinematics.gen.h" #include "generated/lighthouse_model.gen.h" //#include "generated/survive_imu.generated.h" #include "generated/survive_reproject.aux.generated.h" // clang-format off STRUCT_CONFIG_SECTION(SurviveKalmanLighthouse) STRUCT_EXISTING_CONFIG_ITEM("report-covariance", t->report_covariance_cnt); STRUCT_CONFIG_ITEM("kalman-lighthouse-up-variance", "", -1, t->up_variance); STRUCT_CONFIG_ITEM("kalman-lighthouse-initial-pos-variance", "", 1e-1, t->initial_pos_var); STRUCT_CONFIG_ITEM("kalman-lighthouse-initial-rot-variance", "", 1e-2, t->initial_rot_var); STRUCT_CONFIG_ITEM("kalman-pos-variance-per-sec", "", 0, t->variance_per_sec.Lighthouse.Pos[0]); STRUCT_CONFIG_ITEM("kalman-rot-variance-per-sec", "", 0, t->variance_per_sec.Lighthouse.AxisAngleRot[0]); STRUCT_CONFIG_ITEM("kalman-bsd-variance", "", 0, t->base_variance); STRUCT_CONFIG_ITEM("kalman-bsd-phase-variance", "", 0, t->initial_variance.phase); STRUCT_CONFIG_ITEM("kalman-bsd-curve-variance", "", 0, t->initial_variance.curve); STRUCT_CONFIG_ITEM("kalman-bsd-tilt-variance", "", 0, t->initial_variance.tilt); STRUCT_CONFIG_ITEM("kalman-bsd-ogeephase-variance", "", -1e2, t->initial_variance.ogeephase); STRUCT_CONFIG_ITEM("kalman-bsd-ogeemag-variance", "", -1e2, t->initial_variance.ogeemag); STRUCT_CONFIG_ITEM("kalman-bsd-gibpha-variance", "", 0, t->initial_variance.gibpha); STRUCT_CONFIG_ITEM("kalman-bsd-gibmag-variance", "", 0, t->initial_variance.gibmag); END_STRUCT_CONFIG_SECTION(SurviveKalmanLighthouse) // clang-format on MEAS_MDL_CONFIG(lighthouse, imu, 10, 1e-1) MEAS_MDL_CONFIG(lighthouse, obs, 10, 0) //#define TRACK_IN_WORLD2LH FLT baseline_var = 1e-4; #ifdef TRACK_IN_WORLD2LH static SurvivePose survive_kalman_lighthouse_lh2world(SurviveKalmanLighthouse *tracker) { return InvertPoseRtn(&tracker->state); } static SurvivePose survive_kalman_lighthouse_world2lh(SurviveKalmanLighthouse *tracker) { return tracker->state; } #else static SurvivePose survive_kalman_lighthouse_lh2world(SurviveKalmanLighthouse *tracker) { return tracker->state.Lighthouse; } #endif bool lighthouse_integrate_imu_hfn(void *user, const struct CnMat *Z, const struct CnMat *x_t, struct CnMat *y, struct CnMat *H_k) { CN_CREATE_STACK_VEC(up, 3); if (y) { LighthouseIMUPrediction(&up, (const SurvivePose *)cn_as_const_vector(x_t)); cn_elementwise_subtract(y, Z, &up); } if (H_k) { LinmathAxisAnglePose error_state = {0}; LighthouseErrorIMUPrediction_jac_error_state(H_k, (const SurvivePose *)cn_as_const_vector(x_t), &error_state); } return true; } void integrate_imu(SurviveKalmanLighthouse *tracker) { LinmathVec3d up; BaseStationData *bsd = &tracker->ctx->bsd[tracker->lh]; normalize3d(up, bsd->accel); if (isnan(up[0])) return; FLT v = tracker->up_variance; if (v >= 0) { FLT v_diag[3] = {v, v, v}; CnMat R = cnVec(3, v_diag); CnMat Z = cnVec(3, up); struct cnkalman_update_extended_stats_t stats = {0}; cnkalman_meas_model_predict_update_stats(0, &tracker->imu_model, 0, &Z, &R, &stats); SurviveContext *ctx = tracker->ctx; SV_VERBOSE(100, "IMU norm: %7.7f / %7.7f", stats.bestnorm, stats.orignorm); } } void survive_kalman_lighthouse_update_position(SurviveKalmanLighthouse *tracker, const SurvivePose *pose) { if (tracker->updating == false) { tracker->state.Lighthouse = *(pose); SurviveContext *ctx = tracker->ctx; if (normnd(ctx->bsd[tracker->lh].variance.Pos, 6) > 0) { cn_set_diag(&tracker->model.P, (const FLT *)ctx->bsd[tracker->lh].variance.Pos); cnCopy(&tracker->model.P, &tracker->push_cov, 0); SurvivePose min_error = {.Pos = {1e-5, 1e-5, 1e-5}, .Rot = {1e-6, 1e-6, 1e-6, 1e-6}}; CnMat minError = cnVec(6, min_error.Pos); cn_add_diag(&tracker->push_cov, &minError, 5); tracker->state.Lighthouse = ctx->bsd[tracker->lh].Pose; tracker->push_state = tracker->state; } else { FLT pv = tracker->initial_pos_var, rv = tracker->initial_rot_var; SurviveLighthouseKalmanErrorModel baseline = { .Lighthouse = {.Pos = {pv, pv, pv}, .AxisAngleRot = {rv, rv, rv}}, .BSD0 = tracker->initial_variance, .BSD1 = tracker->initial_variance}; cnSetZero(&tracker->model.P); cn_set_diag(&tracker->model.P, (const FLT *)&baseline); integrate_imu(tracker); } } } void survive_kalman_lighthouse_report(SurviveKalmanLighthouse *tracker) { quatnormalize(tracker->state.Lighthouse.Rot, tracker->state.Lighthouse.Rot); SurvivePose lighthouse2world = survive_kalman_lighthouse_lh2world(tracker); FLT var_diag[3] = {0}; for (int i = 0; i < 3; i++) { var_diag[i] = cnMatrixGet(&tracker->model.P, i, i); } tracker->ctx->bsd[tracker->lh].confidence = 1. / norm3d(var_diag); assert(cn_is_finite(&tracker->model.state)); cn_get_diag(&tracker->model.P, (FLT *)&tracker->ctx->bsd[tracker->lh].variance, 6); SurviveContext *ctx = tracker->ctx; SV_VERBOSE(100, "LH%d %s " Point6_format, tracker->lh, survive_colorize("variance"), LINMATH_VEC6_EXPAND(((FLT *)&tracker->ctx->bsd[tracker->lh].variance))); SV_VERBOSE(100, "LH%d %s " Point7_format, tracker->lh, survive_colorize("pose "), LINMATH_VEC7_EXPAND(((FLT *)&tracker->state.Lighthouse))); FLT diff_p[7] = {0}; subnd(diff_p, lighthouse2world.Pos, survive_get_lighthouse_position(tracker->ctx, tracker->lh)->Pos, 7); FLT diff = normnd2(diff_p, 7); if (diff > 1e-4 || !tracker->ctx->bsd[tracker->lh].PositionSet) { tracker->updating = true; SURVIVE_INVOKE_HOOK(raw_lighthouse_pose, tracker->ctx, tracker->lh, &lighthouse2world); tracker->updating = false; } if (tracker->report_covariance_cnt > 0 && ++tracker->stats.reported_poses % tracker->report_covariance_cnt == 0) { SurviveContext *ctx = tracker->ctx; survive_recording_write_to_output(ctx->recptr, "LH%d FULL_COVARIANCE ", ctx->bsd[tracker->lh].mode); for (int i = 0; i < tracker->model.P.rows * tracker->model.P.cols; i++) { survive_recording_write_to_output_nopreamble(ctx->recptr, "%f ", tracker->model.P.data[i]); } survive_recording_write_to_output_nopreamble(ctx->recptr, "\n"); survive_recording_write_matrix(ctx->recptr, 0, 5, tracker->lh == 0 ? "bsd0" : "bsd1", &tracker->bsd_model.P); CN_CREATE_STACK_MAT(BSD, 2, sizeof(BaseStationCal) / sizeof(FLT)); memcpy(BSD.data, (FLT *)&tracker->state.BSD0, 2 * sizeof(BaseStationCal)); CnMat tempBSD = cnMat(2, sizeof(BaseStationCal) / sizeof(FLT), (FLT *)&ctx->bsd[tracker->lh].fcal); cn_elementwise_subtract(&BSD, &BSD, &tempBSD); survive_recording_write_matrix(ctx->recptr, 0, 5, tracker->lh == 0 ? "LH0" : "LH1", &BSD); } } struct map_light_data_ctx { SurviveKalmanLighthouse *tracker; SurviveObject *so; LightInfo savedLight[32]; uint32_t savedLight_idx; SurvivePose objectPose; }; static inline int get_axis(const struct PoserDataLight *pdl) { switch (pdl->hdr.pt) { case POSERDATA_LIGHT: return (((PoserDataLightGen1 *)pdl)->acode & 1); break; case POSERDATA_LIGHT_GEN2: return ((PoserDataLightGen2 *)pdl)->plane; break; default: assert(0); } return 0; } static SurviveLighthouseKalmanModel copy_model(const FLT *src, size_t state_size) { SurviveLighthouseKalmanModel rtn = {0}; memcpy(&rtn, src, sizeof(FLT) * state_size); return rtn; } static void state_update_fn(void *user, const struct CnMat *x0, const struct CnMat *E, struct CnMat *x1, struct CnMat *dX_wrt_error_state) { assert(x0->rows == 7); SurvivePose state = *(SurvivePose *)cn_as_const_vector(x0); if (x1) { SurvivePose _x1 = {0}; SurviveAxisAnglePose error_state = *(const SurviveAxisAnglePose *)cn_as_const_vector(E); SurvivePoseAddErrorModel(&_x1, &state, &error_state); quatnormalize(_x1.Rot, _x1.Rot); memcpy(cn_as_vector(x1), &_x1, sizeof(FLT) * x1->rows); } if (dX_wrt_error_state) { SurviveAxisAnglePose error_model = {0}; SurvivePoseAddErrorModel_jac_error_state(dX_wrt_error_state, &state, &error_model); assert(cn_is_finite(dX_wrt_error_state)); } assert(x1 == 0 || cn_is_finite(x1)); } void error_fn(void *user, const struct CnMat *x0, const struct CnMat *x1, struct CnMat *E, struct CnMat *E_jac_x) { SurviveLighthouseKalmanModel state0 = copy_model(cn_as_const_vector(x0), x0->rows); if (E_jac_x) { SurviveLighthouseKalmanModelToErrorModel_jac_x1(E_jac_x, &state0, &state0); } if (x1 && E) { SurviveLighthouseKalmanModel state1 = copy_model(cn_as_const_vector(x1), x1->rows); SurviveLighthouseKalmanErrorModel error_state = {0}; SurviveLighthouseKalmanModelToErrorModel(&error_state, &state1, &state0); memcpy(cn_as_vector(E), &error_state, sizeof(FLT) * E->rows); } } void survive_kalman_lighthouse_ootx(SurviveKalmanLighthouse *tracker) { tracker->state.BSD0 = tracker->ctx->bsd[tracker->lh].fcal[0]; tracker->state.BSD1 = tracker->ctx->bsd[tracker->lh].fcal[1]; } void minimize_error_state_model_fn(void *user, const struct CnMat *x0, const struct CnMat *x1, struct CnMat *error_state, struct CnMat *E_jac_x1) { if (error_state) { CnMat x1v = cnMatConstView(error_state->rows, 1, x1, 0, 0); CnMat x0v = cnMatConstView(error_state->rows, 1, x0, 0, 0); cnSub(error_state, &x1v, &x0v); } if (E_jac_x1) { cn_eye(E_jac_x1, 0); } } void minimize_integrate_update_fn(void *user, const struct CnMat *x0, const struct CnMat *error_state, struct CnMat *x1, struct CnMat *dX_wrt_error_state) { if (x1) { CnMat x1v = cnMatView(error_state->rows, 1, x1, 0, 0); CnMat x0v = cnMatConstView(error_state->rows, 1, x0, 0, 0); cnAddScaled(&x1v, error_state, 1, &x0v, 1); } if (dX_wrt_error_state) { cn_eye(dX_wrt_error_state, 0); } } void survive_kalman_lighthouse_init(SurviveKalmanLighthouse *tracker, SurviveContext *ctx, int lh) { memset(tracker, 0, sizeof(*tracker)); tracker->ctx = ctx; tracker->lh = lh; SurviveKalmanLighthouse_attach_config(ctx, tracker); FLT *f = (FLT *)&tracker->initial_variance; for (int i = 0; i < sizeof(tracker->initial_variance) / sizeof(FLT); i++) { f[i] = fmax(0, f[i] + tracker->base_variance); } tracker->state.BSD0 = ctx->bsd[lh].fcal[0]; tracker->state.BSD1 = ctx->bsd[lh].fcal[1]; tracker->state.Lighthouse.Rot[0] = 1; // cnkalman_state_init(&tracker->model, 7, 0, 0, tracker, (FLT*)&tracker->state); SurviveLighthouseKalmanErrorModel baseline = { .Lighthouse = {.Pos = {1e5, 1e5, 1e5}, .AxisAngleRot = {1e5, 1e5, 1e5}}, .BSD0 = tracker->initial_variance, .BSD1 = tracker->initial_variance}; int bsd_state_cnt = sizeof(tracker->state.BSD0) * 2 / sizeof(FLT); while (bsd_state_cnt && ((FLT *)(&baseline.BSD0))[bsd_state_cnt - 1] == 0) bsd_state_cnt--; cnkalman_error_state_init(&tracker->bsd_model, sizeof(tracker->state.BSD0) * 2 / sizeof(FLT), bsd_state_cnt / sizeof(FLT), 0, 0, minimize_error_state_model_fn, tracker, (FLT *)&tracker->state.BSD0); tracker->bsd_model.Update_fn = minimize_integrate_update_fn; cnkalman_error_state_init(&tracker->model, sizeof tracker->state.Lighthouse / sizeof(FLT), sizeof tracker->state.Lighthouse / sizeof(FLT) - 1, 0, 0, error_fn, tracker, (FLT *)&tracker->state); tracker->model.Update_fn = state_update_fn; for (int i = 1; i < 3; i++) { tracker->variance_per_sec.Lighthouse.Pos[i] = tracker->variance_per_sec.Lighthouse.Pos[0]; tracker->variance_per_sec.Lighthouse.AxisAngleRot[i] = tracker->variance_per_sec.Lighthouse.AxisAngleRot[0]; } tracker->model.state_variance_per_second = cnVec(6, (FLT *)&tracker->variance_per_sec); tracker->bsd_model.state_variance_per_second = cnVec(bsd_state_cnt, (FLT *)&tracker->variance_per_sec.BSD0); cnkalman_meas_model_init(&tracker->model, "IMU", &tracker->imu_model, lighthouse_integrate_imu_hfn); cnkalman_meas_model_t_lighthouse_imu_attach_config(ctx, &tracker->imu_model); tracker->imu_model.error_state_model = true; if (tracker->imu_model.term_criteria.max_iterations == -1) tracker->imu_model.term_criteria.max_iterations = 10; cnkalman_meas_model_init(&tracker->model, "obs", &tracker->obs_model, 0); cnkalman_meas_model_t_lighthouse_obs_attach_config(ctx, &tracker->obs_model); tracker->obs_model.error_state_model = false; if (tracker->obs_model.term_criteria.max_iterations == -1) tracker->obs_model.term_criteria.max_iterations = 10; tracker->push_cov = cnMat(tracker->model.error_state_size, tracker->model.error_state_size, tracker->push_cov_data); cn_set_diag(&tracker->model.P, (const FLT *)&baseline); cn_set_diag(&tracker->bsd_model.P, (const FLT *)&baseline.BSD0); } void survive_kalman_lighthouse_reset(SurviveKalmanLighthouse *tracker) { SurviveLighthouseKalmanErrorModel baseline = { .Lighthouse = {.Pos = {1e5, 1e5, 1e5}, .AxisAngleRot = {1e5, 1e5, 1e5}}, .BSD0 = tracker->initial_variance, .BSD1 = tracker->initial_variance}; cn_set_zero(&tracker->push_cov); cn_set_diag(&tracker->model.P, (const FLT *)&baseline); cn_set_diag(&tracker->bsd_model.P, (const FLT *)&baseline.BSD0); } SURVIVE_EXPORT void survive_kalman_lighthouse_integrate_observation(SurviveKalmanLighthouse *tracker, const SurvivePose *pose, const CnMat *Rlh) { if (tracker == 0) return; CN_CREATE_STACK_MAT(H, 7, tracker->model.state_cnt); CN_CREATE_STACK_MAT(R, 7, 7); FLT v = cn_trace(&tracker->model.P); bool trustAbsolutely = v > 1e2 || Rlh == 0 || cn_trace(&tracker->push_cov) == 0; if (Rlh) { cn_matrix_copy(&R, Rlh); } if (!trustAbsolutely) { #ifdef TRACK_IN_WORLD2LH SurvivePose Z = InvertPoseRtn(pose); #else SurvivePose Z = *pose; #endif cn_set_diag_val(&H, 1); if (cn_trace(&tracker->push_cov) > 0) { cnCopy(&tracker->push_cov, &tracker->model.P, 0); tracker->state = tracker->push_state; } FLT v[7] = {0}; cn_get_diag(&R, v, 7); SurvivePose min_error = {.Pos = {1e-5, 1e-5, 1e-5}, .Rot = {1e-6, 1e-6, 1e-6, 1e-6}}; CnMat minError = cnVec(7, min_error.Pos); cn_add_diag(&R, &minError, 1); CnMat Zp = cnMat(7, 1, (void *)&Z); cnkalman_meas_model_predict_update(0, &tracker->obs_model, &H, &Zp, &R); SurviveContext * ctx = tracker->ctx; SV_VERBOSE(10, "Observation for LH %d(ID: %08x) " SurvivePose_format " R " SurvivePose_format, tracker->lh, (unsigned)ctx->bsd[tracker->lh].BaseStationID, SURVIVE_POSE_EXPAND(Z), LINMATH_VEC7_EXPAND(v)); } else { tracker->state.Lighthouse = *pose; survive_covariance_pose2poseAA(&tracker->model.P, pose, &R); } integrate_imu(tracker); survive_kalman_lighthouse_report(tracker); } void survive_kalman_lighthouse_free(SurviveKalmanLighthouse *tracker) { SurviveKalmanLighthouse_detach_config(tracker->ctx, tracker); cnkalman_meas_model_t_lighthouse_imu_detach_config(tracker->ctx, &tracker->imu_model); cnkalman_meas_model_t_lighthouse_obs_detach_config(tracker->ctx, &tracker->obs_model); cnkalman_state_free(&tracker->model); cnkalman_state_free(&tracker->bsd_model); free(tracker); }