#include #include #include #include #include #include #ifndef NOZLIB #include #endif #include "generated/survive_reproject.aux.generated.h" //#include "generated/survive_imu.generated.h" #include "mpfit/mpfit.h" #include "survive_default_devices.h" #include "survive_kalman_tracker.h" #include "survive_recording.h" #if !defined(__FreeBSD__) && !defined(__APPLE__) #include #include #endif STATIC_CONFIG_ITEM(OPTIMIZER_FTOL, "optimizer-ftol", 'f', "Relative chi-square convergence criterium", 0.) STATIC_CONFIG_ITEM(OPTIMIZER_XTOL, "optimizer-xtol", 'f', "Relative parameter convergence criterium", 0.0001) STATIC_CONFIG_ITEM(OPTIMIZER_GTOL, "optimizer-gtol", 'f', "Orthogonality convergence criterium", 0.) STATIC_CONFIG_ITEM(OPTIMIZER_COVTOL, "optimizer-covtol", 'f', "Range tolerance for covariance calculation", 0.) STATIC_CONFIG_ITEM(OPTIMIZER_EPSFCN, "optimizer-epsfcn", 'f', "Finite derivative step size", 0.) STATIC_CONFIG_ITEM(OPTIMIZER_STEPFACTOR, "optimizer-stepfactor", 'f', "Initial step bound", 0.) STATIC_CONFIG_ITEM(OPTIMIZER_DOUSERSCALE, "optimizer-douserscale", 'i', "Scale variables by user values", 0) STATIC_CONFIG_ITEM(OPTIMIZER_MAXITER, "optimizer-maxiter", 'i', "Maximum iterations", 0) STATIC_CONFIG_ITEM(OPTIMIZER_MAXFEV, "optimizer-maxfev", 'i', "Maximum function evals", 0) STATIC_CONFIG_ITEM(OPTIMIZER_NORMTOL, "optimizer-normtol", 'f', "Convergence for norm", 0.00005) STATIC_CONFIG_ITEM(OPTIMIZER_NPRINT, "optimizer-nprint", 'i', "", 0) STRUCT_CONFIG_SECTION(survive_optimizer_settings) STRUCT_CONFIG_ITEM("mpfit-disable-filter", "Model mpfit as quaternion", 0, t->disable_filter) STRUCT_CONFIG_ITEM("mpfit-quat-model", "Model mpfit as quaternion", 0, t->use_quat_model) STRUCT_CONFIG_ITEM("mpfit-lh-scale-correction", "", 0, t->lh_scale_correction) STRUCT_CONFIG_ITEM("mpfit-lh-offset-correction", "", 0, t->lh_offset_correction) STRUCT_CONFIG_ITEM("mpfit-no-pair-calc", "Don't process as pairs", 0, t->disallow_pair_calc) STRUCT_CONFIG_ITEM("mpfit-optimize-scale-threshold", "Treat scale as mutable", -1, t->optimize_scale_threshold) STRUCT_CONFIG_ITEM("mpfit-current-pos-bias", "", -1, t->current_pos_bias) STRUCT_CONFIG_ITEM("mpfit-current-rot-bias", "", -1, t->current_rot_bias) END_STRUCT_CONFIG_SECTION(survive_optimizer_settings) static char *object_parameter_names[] = {"Pose x", "Pose y", "Pose z", "Pose Rot w", "Pose Rot x", "Pose Rot y", "Pose Rot z"}; static char *vel_parameter_names[] = {"Vel x", "Vel y", "Vel z", "Vel Rot x", "Vel Rot y", "Vel Rot z"}; static void setup_pose_param_limits(survive_optimizer *mpfit_ctx, FLT *parameter, struct mp_par_struct *pose_param_info) { for (int i = 0; i < 7; i++) { pose_param_info[i].limited[0] = pose_param_info[i].limited[1] = (i >= 3 ? false : true); pose_param_info[i].limits[0] = -(i >= 3 ? 1.0001 : 20.); pose_param_info[i].limits[1] = -pose_param_info[i].limits[0]; } } int survive_optimizer_get_start_index(const survive_optimizer *ctx, enum survive_optimizer_parameter_type type) { int rtn = 0; for (int i = 0; i < ctx->parameterBlockCnt; i++) { if (ctx->parameters_info[i].param_type == type) return rtn; rtn += ctx->parameters_info[i].size; } return -1; } int survive_optimizer_get_block_index(const survive_optimizer *ctx, enum survive_optimizer_parameter_type type) { for (int i = 0; i < ctx->parameterBlockCnt; i++) { if (ctx->parameters_info[i].param_type == type) return i; } return -1; } survive_optimizer_parameter *survive_optimizer_get_start_parameter_info(const survive_optimizer *ctx, enum survive_optimizer_parameter_type type) { int index = survive_optimizer_get_block_index(ctx, type); if (index == -1) return 0; return &ctx->parameters_info[index]; } int get_lighthouse_correction_idx_for(survive_optimizer *optimizer, int obj, int lh, int axis) { survive_optimizer_parameter * lh_correction = survive_optimizer_get_start_parameter_info(optimizer, survive_optimizer_parameter_object_lighthouse_correction); if(!lh_correction) return -1; assert(obj == 0); return lh_correction->p_idx + lh * SURVIVE_CORRECTION_PARAMS + axis; } FLT get_lighthouse_correction_for(survive_optimizer *optimizer, int obj, int lh, int axis) { assert(axis < SURVIVE_CORRECTION_PARAMS); int idx = get_lighthouse_correction_idx_for(optimizer, obj, lh, axis); if(idx < 0) return 0; return optimizer->parameters[idx]; } void survive_optimizer_setup_pose_n(survive_optimizer *mpfit_ctx, const SurvivePose *pose, size_t n, bool isFixed, int use_jacobian_function) { if (pose) survive_optimizer_get_pose(mpfit_ctx)[n] = *pose; else survive_optimizer_get_pose(mpfit_ctx)[n] = (SurvivePose){.Rot = {1.}}; setup_pose_param_limits(mpfit_ctx, mpfit_ctx->parameters + n * 7, mpfit_ctx->mp_parameters_info + n * 7); for (size_t i = n * 7; i < 7 * (n + 1); i++) { mpfit_ctx->mp_parameters_info[i].fixed = isFixed; mpfit_ctx->mp_parameters_info[i].parname = object_parameter_names[i % 7]; if (use_jacobian_function != 0) { assert(mpfit_ctx->reprojectModel->reprojectAxisAngleFullJacObjPose); if (use_jacobian_function < 0) { mpfit_ctx->mp_parameters_info[i].side = 2; mpfit_ctx->mp_parameters_info[i].deriv_debug = 1; mpfit_ctx->mp_parameters_info[i].deriv_abstol = .01; mpfit_ctx->mp_parameters_info[i].deriv_reltol = .01; mpfit_ctx->mp_parameters_info[i].step = 1e-3; } else { mpfit_ctx->mp_parameters_info[i].side = 3; } } } if (!mpfit_ctx->disableVelocity) { int v_idx = survive_optimizer_get_velocity_index(mpfit_ctx); survive_optimizer_get_velocity(mpfit_ctx)[n] = (SurviveVelocity){0}; for (int i = 0; i < 6; i++) { mpfit_ctx->mp_parameters_info[i + v_idx].fixed = true; mpfit_ctx->mp_parameters_info[i + v_idx].parname = vel_parameter_names[i % 6]; mpfit_ctx->mp_parameters_info[i + v_idx].side = 0; } } survive_optimizer_parameter * lh_correction = survive_optimizer_get_start_parameter_info(mpfit_ctx, survive_optimizer_parameter_object_lighthouse_correction); if(lh_correction) { for (int i = 0; i < mpfit_ctx->poseLength; i++) { FLT* lh_obj_params = lh_correction[i].p; SurviveObject * so = mpfit_ctx->sos[i]; if (so == 0) break; struct mp_par_struct* info = lh_correction[i].pi; for(int lh = 0;lh < NUM_GEN2_LIGHTHOUSES;lh++) { for (int axis = 0; axis < SURVIVE_CORRECTION_PARAMS; axis++) { struct mp_par_struct* p_info = &info[lh*SURVIVE_CORRECTION_PARAMS+axis]; p_info->fixed = true; lh_obj_params[lh * SURVIVE_CORRECTION_PARAMS + axis] = so->lh_correction[lh][axis]; p_info->step = 1e-5; if (use_jacobian_function != 0 && axis != 2) { if (use_jacobian_function < 0) { p_info->side = 2; p_info->deriv_debug = 1; p_info->deriv_abstol = 1e-4; p_info->deriv_reltol = 1e-4; //p_info->step = 1e-1; } else { p_info->side = 3; } } } } } } if (mpfit_ctx->settings->optimize_scale_threshold >= 0) { int s_idx = survive_optimizer_get_sensor_scale_index(mpfit_ctx); for (int i = 0; i < mpfit_ctx->poseLength; i++) { mpfit_ctx->parameters[s_idx + i] = mpfit_ctx->sos[n]->sensor_scale; struct mp_par_struct *pinfo = &mpfit_ctx->mp_parameters_info[i + s_idx]; pinfo->fixed = mpfit_ctx->settings->optimize_scale_threshold > mpfit_ctx->sos[n]->sensor_scale_var; pinfo->parname = "scale"; if (use_jacobian_function != 0) { if (use_jacobian_function < 0) { pinfo->side = 2; pinfo->deriv_debug = 1; pinfo->deriv_abstol = .0001; pinfo->deriv_reltol = .0001; } else { pinfo->side = 3; } } pinfo->limited[0] = pinfo->limited[1] = true; pinfo->limits[0] = 1 - .1; pinfo->limits[1] = 1 + .1; survive_optimizer_measurement * meas = survive_optimizer_emplace_meas(mpfit_ctx, survive_optimizer_measurement_type_parameters_bias); meas->variance = mpfit_ctx->sos[n]->sensor_scale_var; meas->parameter_bias.parameter_index = s_idx; meas->parameter_bias.expected_value = mpfit_ctx->sos[n]->sensor_scale; } } } SURVIVE_EXPORT void survive_optimizer_remove_data_for_lh(survive_optimizer *optimizer, int cam_idx) { for (int i = 0; i < optimizer->measurementsCnt; i++) { survive_optimizer_measurement *meas = &optimizer->measurements[i]; if (meas->meas_type == survive_optimizer_measurement_type_light && meas->light.lh == cam_idx) { meas->invalid = true; } } } SURVIVE_EXPORT void survive_optimizer_fix_cam_pos(survive_optimizer *mpfit_ctx, int lh_idx) { survive_optimizer_measurement *meas = survive_optimizer_emplace_meas(mpfit_ctx, survive_optimizer_measurement_type_camera_position); meas->variance = 1e-7; meas->camera_pos.camera = lh_idx; SurvivePose *cam = &survive_optimizer_get_camera(mpfit_ctx)[lh_idx]; SurvivePose p = InvertPoseRtn(cam); copy3d(meas->camera_pos.pos, p.Pos); } void survive_optimizer_fix_cam_yaw(survive_optimizer *mpfit_ctx, int lh_idx) { survive_optimizer_measurement *meas = survive_optimizer_emplace_meas(mpfit_ctx, survive_optimizer_measurement_type_fixed_rotation); meas->variance = 1e-7; meas->fixed_rotation.obj = mpfit_ctx->poseLength + lh_idx; LinmathPoint3d dir = {1, 0, 0}; LinmathPoint3d pdir; LinmathQuat q; quatgetconjugate(q, survive_optimizer_get_camera(mpfit_ctx)[lh_idx].Rot); quatrotatevector(pdir, q, dir); // Plane that goes through (0, 0, 0), (0, 0, 1), and pdir copy3d(meas->fixed_rotation.match_vec, dir); meas->fixed_rotation.conjugate = true; meas->fixed_rotation.plane[0] = -pdir[1]; meas->fixed_rotation.plane[1] = pdir[0]; meas->fixed_rotation.plane[2] = 0; } void survive_optimizer_fix_obj_yaw(survive_optimizer *mpfit_ctx, int obj_idx) { survive_optimizer_measurement *meas = survive_optimizer_emplace_meas(mpfit_ctx, survive_optimizer_measurement_type_fixed_rotation); meas->variance = 1e-7; meas->fixed_rotation.obj = obj_idx; LinmathPoint3d dir = {1, 0, 0}; LinmathPoint3d pdir; quatrotatevector(pdir, survive_optimizer_get_pose(mpfit_ctx)[obj_idx].Rot, dir); // Plane that goes through (0, 0, 0), (0, 0, 1), and pdir copy3d(meas->fixed_rotation.match_vec, dir); meas->fixed_rotation.plane[0] = -pdir[1]; meas->fixed_rotation.plane[1] = pdir[0]; meas->fixed_rotation.plane[2] = 0; } void survive_optimizer_fix_camera(survive_optimizer *mpfit_ctx, int cam_idx) { int start = survive_optimizer_get_camera_index(mpfit_ctx) + cam_idx * 7; for (int i = start; i < start + 7; i++) { mpfit_ctx->parameters[i] = 0; mpfit_ctx->mp_parameters_info[i].fixed = 1; } } void survive_optimizer_setup_pose(survive_optimizer *mpfit_ctx, const SurvivePose *poses, bool isFixed, int use_jacobian_function) { for (int i = 0; i < mpfit_ctx->poseLength; i++) { survive_optimizer_setup_pose_n(mpfit_ctx, poses ? &poses[i] : 0, i, isFixed, use_jacobian_function); } } static char *lh_parameter_names[] = {"LH x", "LH y", "LH z", "LH Rot w", "LH Rot x", "LH Rot y", "LH Rot z" }; void survive_optimizer_setup_camera(survive_optimizer *mpfit_ctx, int8_t lh, const SurvivePose *pose, bool isFixed, int use_jacobian_function) { SurvivePose *cameras = survive_optimizer_get_camera(mpfit_ctx); int start = survive_optimizer_get_camera_index(mpfit_ctx) + lh * 7; bool poseIsInvalid = pose == 0; if (pose && !quatiszero(pose->Rot)) { InvertPose(&cameras[lh], pose); } else { cameras[lh] = (SurvivePose){ 0 }; poseIsInvalid = true; } setup_pose_param_limits(mpfit_ctx, mpfit_ctx->parameters + start, mpfit_ctx->mp_parameters_info + start); for (int i = start; i < start + 7; i++) { mpfit_ctx->mp_parameters_info[i].fixed = (isFixed || poseIsInvalid); mpfit_ctx->mp_parameters_info[i].parname = lh_parameter_names[i - start]; if (use_jacobian_function != 0 && mpfit_ctx->reprojectModel ->reprojectAxisAngleFullJacLhPose /*mpfit_ctx->reprojectModel->reprojectFullJacLhPose*/) { if (use_jacobian_function < 0) { mpfit_ctx->mp_parameters_info[i].side = 2; mpfit_ctx->mp_parameters_info[i].deriv_debug = 1; mpfit_ctx->mp_parameters_info[i].deriv_abstol = .0001; mpfit_ctx->mp_parameters_info[i].deriv_reltol = .0001; mpfit_ctx->mp_parameters_info[i].step = .0001; } else { mpfit_ctx->mp_parameters_info[i].side = 3; } } } } void survive_optimizer_setup_cameras(survive_optimizer *mpfit_ctx, SurviveContext *ctx, bool isFixed, int use_jacobian_function, bool useTruePosition) { for (int lh = 0; lh < mpfit_ctx->cameraLength; lh++) { const SurvivePose *cam_pos = useTruePosition ? survive_get_lighthouse_true_position(ctx, lh) : survive_get_lighthouse_position(ctx, lh); if (!quatiszero(cam_pos->Rot)) survive_optimizer_setup_camera(mpfit_ctx, lh, cam_pos, isFixed, use_jacobian_function); else { SurvivePose id = {.Rot = {1}}; survive_optimizer_setup_camera(mpfit_ctx, lh, &id, isFixed, use_jacobian_function); } } survive_optimizer_parameter *bsdParams = survive_optimizer_get_start_parameter_info(mpfit_ctx, survive_optimizer_parameter_camera_parameters); if (bsdParams) { assert(bsdParams->elem_size == mpfit_ctx->cameraLength); for (int lh = 0; lh < mpfit_ctx->cameraLength; lh++) { BaseStationCal *fcal = (BaseStationCal *)&bsdParams->p[lh * sizeof(BaseStationCal) * 2]; for (int axis = 0; axis < 2; axis++) fcal[axis] = *survive_basestation_cal(ctx, lh, axis); } } size_t start = survive_optimizer_get_calibration_index(mpfit_ctx); for (int i = start; i < start + 2 * sizeof(BaseStationCal) / sizeof(FLT) * mpfit_ctx->cameraLength; i++) { mpfit_ctx->mp_parameters_info[i].parname = "Fcal parameter"; mpfit_ctx->mp_parameters_info[i].fixed = true; } } int survive_optimizer_get_max_parameters_count(const survive_optimizer *ctx) { assert(ctx->poseLength < 20); int rtn = ctx->cameraLength * 7 + ctx->poseLength * 7 + ctx->ptsLength * 3 + 2 * ctx->cameraLength * sizeof(BaseStationCal) / sizeof(FLT); if (!ctx->disableVelocity) rtn += ctx->poseLength * 6; if (ctx->settings->optimize_scale_threshold >= 0) { rtn += ctx->poseLength; } rtn += ctx->poseLength * NUM_GEN2_LIGHTHOUSES * SURVIVE_CORRECTION_PARAMS; return rtn; } int survive_optimizer_get_parameters_count(const survive_optimizer *ctx) { return ctx->parametersCnt; } int survive_optimizer_get_free_parameters_count(const survive_optimizer *ctx) { int rtn = 0; for (int i = 0; i < survive_optimizer_get_parameters_count(ctx); i++) { rtn += ctx->mp_parameters_info[i].fixed == 0; } return rtn; } FLT *survive_optimizer_get_sensors(survive_optimizer *ctx, size_t idx) { if (ctx->ptsLength == 0) return ctx->sos[idx]->sensor_locations; return &ctx->parameters[survive_optimizer_get_sensors_index(ctx)]; } int survive_optimizer_get_sensors_index(const survive_optimizer *ctx) { return survive_optimizer_get_start_index(ctx, survive_optimizer_parameter_obj_points); } BaseStationCal *survive_optimizer_get_calibration(survive_optimizer *ctx, int lh) { int idx = survive_optimizer_get_calibration_index(ctx); if (idx < 0) return survive_basestation_cal(ctx->sos[0]->ctx, lh, 0); BaseStationCal *base = (BaseStationCal *)(&ctx->parameters[idx]); return &base[2 * lh]; } int survive_optimizer_get_calibration_index(const survive_optimizer *ctx) { return survive_optimizer_get_start_index(ctx, survive_optimizer_parameter_camera_parameters); } SurvivePose *survive_optimizer_get_camera(survive_optimizer *ctx) { assert(ctx->cameraLength > 0); return (SurvivePose *)&ctx->parameters[survive_optimizer_get_camera_index(ctx)]; } SURVIVE_EXPORT int survive_optimizer_get_velocity_index(const survive_optimizer *ctx) { return survive_optimizer_get_start_index(ctx, survive_optimizer_parameter_object_velocity); } SURVIVE_EXPORT int survive_optimizer_get_sensor_scale_index(const survive_optimizer *ctx) { return survive_optimizer_get_start_index(ctx, survive_optimizer_parameter_object_scale); } SURVIVE_EXPORT void survive_optimizer_disable_sensor_scale(survive_optimizer *ctx) { if (ctx->settings->optimize_scale_threshold >= 0) { int idx = survive_optimizer_get_sensor_scale_index(ctx); for (int i = idx; i < ctx->poseLength + idx; i++) { ctx->mp_parameters_info[i].fixed = true; } } } int survive_optimizer_get_camera_index(const survive_optimizer *ctx) { return survive_optimizer_get_start_index(ctx, survive_optimizer_parameter_camera); } SurvivePose *survive_optimizer_get_pose(survive_optimizer *ctx) { if (ctx->poseLength) return (SurvivePose *)ctx->parameters; return 0; } SurviveVelocity *survive_optimizer_get_velocity(survive_optimizer *ctx) { if (!ctx->disableVelocity) return (SurviveVelocity *)&ctx->parameters[survive_optimizer_get_velocity_index(ctx)]; return 0; } static inline FLT fix_infinity(FLT d) { // assert(isfinite(*d)); // assert(!isnan(d)); if (isnan(d)) { return 0; } if (!isfinite(d)) { return linmath_enforce_range(d, -1e3, 1e3); } return d; } typedef union LinmathDualPose { LinmathPose quatPose; LinmathAxisAnglePose axisAnglePose; } LinmathDualPose; static void ApplyDualPoseToPose(const survive_optimizer *mpfit_ctx, LinmathDualPose *out, const LinmathDualPose *p1, const LinmathDualPose *p2) { if(mpfit_ctx->settings->use_quat_model) { ApplyPoseToPose(&out->quatPose, &p1->quatPose, &p2->quatPose); quatnormalize(out->quatPose.Rot, out->quatPose.Rot); } else { ApplyAxisAnglePoseToPose(&out->axisAnglePose, &p1->axisAnglePose, &p2->axisAnglePose); } } static void ApplyDualPoseToPoint(const survive_optimizer *mpfit_ctx, LinmathVec3d out, const LinmathDualPose *p1, const LinmathVec3d pt) { if(mpfit_ctx->settings->use_quat_model) { ApplyPoseToPoint(out, &p1->quatPose, pt); } else { ApplyAxisAnglePoseToPoint(out, &p1->axisAnglePose, pt); } } static inline void run_pair_measurement(survive_optimizer *mpfunc_ctx, size_t meas_idx, const survive_optimizer_measurement *meas, const CnMat *ang_vel_jacb, const LinmathDualPose *obj2world, const LinmathDualPose *obj2lh, const LinmathDualPose *world2lh, const FLT *pt, FLT *deviates, FLT **derivs) { const survive_reproject_model_t *reprojectModel = mpfunc_ctx->reprojectModel; const int lh = meas->light.lh; const struct BaseStationCal *cal = survive_optimizer_get_calibration(mpfunc_ctx, lh); LinmathPoint3d sensorPtInLH; ApplyDualPoseToPoint(mpfunc_ctx, sensorPtInLH, obj2lh, pt); FLT out[2]; reprojectModel->reprojectXY(cal, sensorPtInLH, out); assert(meas[0].light.axis == 0); assert(meas[1].light.axis == 1); #ifndef NDEBUG if (reprojectModel->reprojectAxisFn[0]) { /*FLT check[] = {reprojectModel->reprojectAxisangleFullXyFn[0](obj2lh, pt, world2lh, cal), reprojectModel->reprojectAxisangleFullXyFn[1](obj2lh, pt, world2lh, cal + 1)}; */ FLT check[] = {reprojectModel->reprojectAxisFn[0](cal, sensorPtInLH), reprojectModel->reprojectAxisFn[1](cal, sensorPtInLH)}; for (int i = 0; i < 2; i++) assert(fabs(check[i] - out[i]) < 1e-5); } #endif for (int i = 0; i < 2; i++) { FLT correction = get_lighthouse_correction_for(mpfunc_ctx, meas->light.object, meas->light.lh, i); FLT error = fix_infinity(out[i] - meas[i].light.value - correction); deviates[i] = error / meas[i].variance; mpfunc_ctx->stats.sensor_error += error * error; mpfunc_ctx->stats.sensor_error_cnt++; } if (derivs) { int pose_size = mpfunc_ctx->settings->use_quat_model ? 7 : 6; int jac_offset_lh = (lh + mpfunc_ctx->poseLength) * 7; int jac_offset_obj = meas->light.object * 7; bool needsJacLH = false, needsJacObj = false; for(int i = 0;i < 7;i++) { needsJacLH |= derivs[jac_offset_lh+i] != 0; needsJacObj |= derivs[jac_offset_obj+i] != 0; } for(int i = 0;i < 2;i++) { int p_idx = get_lighthouse_correction_idx_for(mpfunc_ctx, meas->light.object, meas->light.lh, i); if (p_idx > -1 && derivs[p_idx]) { derivs[p_idx][meas_idx + i] = -1. / meas->variance; } } LinmathDualPose safe_pose = *obj2world, safe_world2lh = *world2lh; if(!mpfunc_ctx->settings->use_quat_model) { if (magnitude3d(safe_pose.axisAnglePose.AxisAngleRot) == 0) safe_pose.axisAnglePose.AxisAngleRot[0] = 1e-10; if (magnitude3d(safe_world2lh.axisAnglePose.AxisAngleRot) == 0) safe_world2lh.axisAnglePose.AxisAngleRot[0] = 1e-10; } // d deviate / d Pose(t-) // d Pose(t-) / d Pose(t) if (needsJacObj) { FLT jout[7 * 2] = {0}; if(mpfunc_ctx->settings->use_quat_model) { reprojectModel->reprojectFullJacObjPose(jout, &obj2world->quatPose, pt, &world2lh->quatPose, cal); } else { reprojectModel->reprojectAxisAngleFullJacObjPose(jout, &obj2world->axisAnglePose, pt, &world2lh->axisAnglePose, cal); } for (int i = 0; i < 2; i++) { FLT tmp[6]; copy3d(tmp, jout + 3 + i * pose_size); for (int j = 0; j < 3; j++) { jout[3 + j + i * pose_size] = dotnd_strided(tmp, ang_vel_jacb->data + j * 1, 3, 1, 3); } } for (int j = 0; j < pose_size; j++) { for (int k = 0; k < 2; k++) { if (isnan(jout[j + k * pose_size])) { jout[j + k * pose_size] = 0; } } if(derivs[jac_offset_obj + j]) { derivs[jac_offset_obj + j][meas_idx] = fix_infinity(jout[j] / meas[0].variance); derivs[jac_offset_obj + j][meas_idx + 1] = fix_infinity(jout[j + pose_size] / meas[1].variance); } } } // d deviate / d LH if (needsJacLH) { FLT out[7 * 2] = {0}; if(mpfunc_ctx->settings->use_quat_model) { reprojectModel->reprojectFullJacLhPose(out, &obj2world->quatPose, pt, &world2lh->quatPose, cal); } else { reprojectModel->reprojectAxisAngleFullJacLhPose(out, &obj2world->axisAnglePose, pt, &world2lh->axisAnglePose, cal); } for (int j = 0; j < pose_size; j++) { if(derivs[jac_offset_lh + j]) { derivs[jac_offset_lh + j][meas_idx] = fix_infinity(out[j] / meas[0].variance); derivs[jac_offset_lh + j][meas_idx + 1] = fix_infinity(out[j + pose_size] / meas[1].variance); } } } } } static void run_single_measurement(survive_optimizer *mpfunc_ctx, size_t meas_idx, const survive_optimizer_measurement *meas, const CnMat *ang_vel_jacb, const LinmathDualPose *obj2world, const LinmathDualPose *obj2lh, const LinmathDualPose *world2lh, const FLT *pt, FLT *deviates, FLT **derivs) { const survive_reproject_model_t *reprojectModel = mpfunc_ctx->reprojectModel; const int lh = meas->light.lh; const struct BaseStationCal *cal = survive_optimizer_get_calibration(mpfunc_ctx, lh); int pose_size = mpfunc_ctx->settings->use_quat_model ? 7 : 6; LinmathPoint3d sensorPtInLH; ApplyDualPoseToPoint(mpfunc_ctx, sensorPtInLH, obj2lh, pt); FLT out = reprojectModel->reprojectAxisFn[meas->light.axis](cal, sensorPtInLH); FLT correction = get_lighthouse_correction_for(mpfunc_ctx, meas->light.object, meas->light.lh, meas->light.axis); //SurviveObject * so = mpfunc_ctx->sos[meas->light.object]; //assert(so->lh_correction[meas->light.lh][meas->light.axis] == correction); FLT error = fix_infinity(out - meas->light.value - correction); deviates[0] = error / meas->variance; mpfunc_ctx->stats.sensor_error += error * error; mpfunc_ctx->stats.sensor_error_cnt++; if (derivs) { int pose_size = mpfunc_ctx->settings->use_quat_model ? 7 : 6; int jac_offset_lh = (lh + mpfunc_ctx->poseLength) * 7; int jac_offset_obj = meas->light.object * 7; bool needsJacLH = false, needsJacObj = false; for(int i = 0;i < 7;i++) { needsJacLH |= derivs[jac_offset_lh+i] != 0; needsJacObj |= derivs[jac_offset_obj+i] != 0; } int p_idx = get_lighthouse_correction_idx_for(mpfunc_ctx, meas->light.object, meas->light.lh, meas->light.axis); if(p_idx > -1 && derivs[p_idx]) { derivs[p_idx][meas_idx] = -1. / meas->variance; } FLT out[7] = {0}; // d Deviate / d Pose[t - 1] * d Pose[t - 1] / d Pose[t] if (needsJacObj) { if(mpfunc_ctx->settings->use_quat_model) { reprojectModel->reprojectAxisJacobFn[meas->light.axis](out, &obj2world->quatPose, pt, &world2lh->quatPose, cal + meas->light.axis); } else { reprojectModel->reprojectAxisAngleAxisJacobFn[meas->light.axis](out, &obj2world->axisAnglePose, pt, &world2lh->axisAnglePose, cal + meas->light.axis); } int ang_size = mpfunc_ctx->settings->use_quat_model ? 4 : 3; FLT tmp[4]; copynd(tmp, out + 3, ang_size); for (int j = 0; j < ang_size; j++) { out[3 + j] = dotnd_strided(tmp, ang_vel_jacb->data + j * 1, ang_size, 1, ang_size); } for (int j = 0; j < pose_size; j++) { if(derivs[jac_offset_obj + j]) { derivs[jac_offset_obj + j][meas_idx] = fix_infinity(out[j] / meas->variance); } } } if (needsJacLH) { if(mpfunc_ctx->settings->use_quat_model) { reprojectModel->reprojectAxisJacobLhPoseFn[meas->light.axis](out, &obj2world->quatPose, pt, &world2lh->quatPose, cal + meas->light.axis); } else { reprojectModel->reprojectAxisAngleAxisJacobLhPoseFn[meas->light.axis](out, &obj2world->axisAnglePose, pt, &world2lh->axisAnglePose, cal + meas->light.axis); } for (int j = 0; j < pose_size; j++) { if(derivs[jac_offset_lh + j]) { assert(derivs[jac_offset_lh + j]); derivs[jac_offset_lh + j][meas_idx] = fix_infinity(out[j] / meas->variance); } } } } } static void filter_measurements(survive_optimizer *optimizer, FLT *deviates) { struct SurviveObject *so = optimizer->sos[0]; SurviveContext *ctx = optimizer->sos[0] ? optimizer->sos[0]->ctx : 0; FLT lh_deviates[NUM_GEN2_LIGHTHOUSES] = {0}; size_t lh_meas_cnt[NUM_GEN2_LIGHTHOUSES] = {0}; FLT avg_dev = 0, lh_avg_dev = 0; optimizer->stats.total_meas_cnt += optimizer->measurementsCnt; size_t valid_meas = 0; for (int i = 0; i < optimizer->measurementsCnt; i++) { survive_optimizer_measurement *meas = &optimizer->measurements[i]; if (meas->meas_type == survive_optimizer_measurement_type_light) { SV_DATA_LOG("mpfit_meas_val[%d][%d][%d]", &meas->light.value, 1, meas->light.sensor_idx, meas->light.lh, meas->light.axis); SV_DATA_LOG("mpfit_meas_var[%d][%d][%d]", &meas->variance, 1, meas->light.sensor_idx, meas->light.lh, meas->light.axis); SV_DATA_LOG("mpfit_meas_err[%d][%d][%d]", deviates, 1, meas->light.sensor_idx, meas->light.lh, meas->light.axis); avg_dev += fabs(deviates[i] * meas->variance); valid_meas++; } } avg_dev = avg_dev / (FLT)valid_meas; SV_DATA_LOG("opt_avg_deviates", &avg_dev, 1); if (avg_dev < .01) { avg_dev = .01; } for (int i = 0; i < optimizer->measurementsCnt; i++) { survive_optimizer_measurement *meas = &optimizer->measurements[i]; if (meas->meas_type == survive_optimizer_measurement_type_light) { FLT chauvenet_criterion = linmath_chauvenet_criterion(deviates[i] * meas->variance, 0, avg_dev, optimizer->measurementsCnt); if (chauvenet_criterion < .5 && false) { meas->invalid = true; optimizer->stats.dropped_meas_cnt++; SV_VERBOSE(105, "Ignoring noisy data at lh %d sensor %d axis %2d val %f (err: %7.7f/dev: %7.7f/cnt: %d) " "chauv: %7.7f", meas->light.lh, meas->light.sensor_idx, meas->light.axis, meas->light.value, fabs(deviates[i] * meas->variance), avg_dev, (int)optimizer->measurementsCnt, chauvenet_criterion); deviates[i] = 0.; } else { SV_VERBOSE(1000, "Data at lh %d sensor %d axis %d val %f (%7.7f/%7.7f)", meas->light.lh, meas->light.sensor_idx, meas->light.axis, meas->light.value, fabs(deviates[i]), avg_dev); } } } for (int i = 0; i < optimizer->measurementsCnt; i++) { survive_optimizer_measurement *meas = &optimizer->measurements[i]; if (meas->meas_type == survive_optimizer_measurement_type_light && meas->invalid == false) { lh_deviates[meas->light.lh] += fabs(deviates[i] * meas->variance); lh_meas_cnt[meas->light.lh]++; } } size_t obs_lhs = 0; for (int i = 0; i < NUM_GEN2_LIGHTHOUSES; i++) { if (lh_meas_cnt[i]) { lh_deviates[i] = lh_deviates[i] / (FLT)lh_meas_cnt[i]; SV_DATA_LOG("opt_lh_deviate[%d]", &lh_deviates[i], 1, i); lh_avg_dev += lh_deviates[i]; obs_lhs++; optimizer->stats.total_lh_cnt++; } } FLT unbias_dev = lh_avg_dev / (obs_lhs - 1.); lh_avg_dev = lh_avg_dev / (FLT)obs_lhs; if (lh_avg_dev < .01) { lh_avg_dev = 0.01; } SV_DATA_LOG("opt_lh_avg_deviates", &lh_avg_dev, 1); if (obs_lhs > 2) { for (int i = 0; i < NUM_GEN2_LIGHTHOUSES; i++) { if (lh_meas_cnt[i] == 0) continue; FLT corrected_dev = unbias_dev - lh_deviates[i] / (obs_lhs - 1.); SV_DATA_LOG("opt_lh_corrected_dev[%d]", &corrected_dev, 1, i); FLT chauvenet_criterion = linmath_chauvenet_criterion(lh_deviates[i], 0, lh_avg_dev, obs_lhs); if (lh_deviates[i] > 100 * corrected_dev) { SV_VERBOSE(100, "Data from LH %d seems suspect for %s (%f/%10.10f -- %f)", i, optimizer->sos[0]->codename, lh_deviates[i], corrected_dev, lh_deviates[i] / corrected_dev); lh_meas_cnt[i] = 0; optimizer->stats.dropped_lh_cnt++; } else if (lh_deviates[i] > 0.) { SV_VERBOSE(500, "Data from LH %d seems OK for %s (%f/%f -- %f)", i, optimizer->sos[0]->codename, lh_deviates[i], lh_avg_dev, lh_deviates[i] / corrected_dev); } } } for (int i = 0; i < optimizer->measurementsCnt; i++) { survive_optimizer_measurement *meas = &optimizer->measurements[i]; if (meas->meas_type == survive_optimizer_measurement_type_light && lh_meas_cnt[meas->light.lh] == 0) { meas->invalid = true; } } optimizer->needsFiltering = false; } static int survive_optimizer_get_meas_size(const survive_optimizer *ctx) { int rtn = 0; for (int i = 0; i < ctx->measurementsCnt; i++) { assert(ctx->measurements[i].size > 0); rtn += ctx->measurements[i].size; } return rtn; } static void handle_accel_meas(survive_optimizer *mpfunc_ctx, survive_optimizer_measurement *meas, int meas_idx, FLT *deviates, FLT **derivs) { int ang_size = mpfunc_ctx->settings->use_quat_model ? 4 : 3; FLT deriv[4 * 3] = {0}; size_t deriv_idx = 0; LinmathPoint3d world_up = {0, 0, 1}; LinmathDualPose *pose = 0; FLT *expected = 0; FLT *tx_input = 0; if (meas->meas_type == survive_optimizer_measurement_type_object_accel) { int obj = meas->pose_acc.object; pose = (LinmathDualPose *)(&survive_optimizer_get_pose(mpfunc_ctx)[obj]); deriv_idx = obj * 7 + 3; tx_input = meas->pose_acc.acc; expected = world_up; } else { size_t lh = meas->camera_acc.camera; pose = &((LinmathDualPose *)survive_optimizer_get_camera(mpfunc_ctx))[lh]; deriv_idx = survive_optimizer_get_camera_index(mpfunc_ctx) + lh * 7 + 3; tx_input = world_up; expected = meas->camera_acc.acc; } LinmathPoint3d predicted = {0}; if (mpfunc_ctx->settings->use_quat_model) { assert(false); } else { gen_axisanglerotatevector(predicted, pose->axisAnglePose.AxisAngleRot, tx_input); gen_axisanglerotatevector_jac_axis_angle(deriv, pose->axisAnglePose.AxisAngleRot, tx_input); } FLT error = 0; for (int i = 0; i < 3; i++) { FLT e = predicted[i] - expected[i]; deviates[meas_idx + i] = e / meas->variance; error += e; for (int j = 0; j < ang_size && derivs; j++) { if (derivs[deriv_idx + j]) { derivs[deriv_idx + j][meas_idx + i] = fix_infinity(deriv[i * ang_size + j] / meas->variance); } } } mpfunc_ctx->stats.object_up_error += error * error; mpfunc_ctx->stats.object_up_error_cnt++; } static int mpfunc(int m, int n, FLT *p, FLT *deviates, FLT **derivs, void *private) { survive_optimizer *mpfunc_ctx = private; assert(survive_optimizer_get_meas_size(mpfunc_ctx) == m); assert(survive_optimizer_get_parameters_count(mpfunc_ctx) == n); mpfunc_ctx->stats.sensor_error = 0; mpfunc_ctx->stats.sensor_error_cnt = 0; mpfunc_ctx->stats.object_up_error = 0; mpfunc_ctx->stats.object_up_error_cnt = 0; mpfunc_ctx->stats.params_error = 0; mpfunc_ctx->stats.params_error_cnt = 0; mpfunc_ctx->parameters = p; LinmathDualPose *cameras = (LinmathDualPose*)survive_optimizer_get_camera(mpfunc_ctx); int start = survive_optimizer_get_camera_index(mpfunc_ctx); int pose_idx = -1; FLT calced_timecode = -1; LinmathDualPose obj2world = {0}; LinmathDualPose obj2lh[NUM_GEN2_LIGHTHOUSES] = {0}; int ang_size = mpfunc_ctx->settings->use_quat_model ? 4 : 3; int pose_size = ang_size + 3; CN_CREATE_STACK_MAT(ang_velocity_jac, ang_size, ang_size); cn_set_diag_val(&ang_velocity_jac, 1); int meas_count = m; int meas_idx = 0; for (int mea_block_idx = 0; mea_block_idx < mpfunc_ctx->measurementsCnt; mea_block_idx++) { survive_optimizer_measurement *meas = &mpfunc_ctx->measurements[mea_block_idx]; if (meas->invalid) { // mpfit zero initializes everything; so just let deviates/derivs stay 0 meas_idx += meas->size; continue; } switch (meas->meas_type) { case survive_optimizer_measurement_type_parameters_bias: { int parameter_index = meas->parameter_bias.parameter_index; FLT expected_value = meas->parameter_bias.expected_value; FLT param_deviation = (p[parameter_index] - expected_value) / (meas->variance + 1e-10); deviates[meas_idx] = param_deviation; if (derivs && derivs[parameter_index]) { derivs[parameter_index][meas_idx] = 1. / (meas->variance + 1e-10); } mpfunc_ctx->stats.params_error_cnt++; mpfunc_ctx->stats.params_error += deviates[meas_idx] * deviates[meas_idx]; break; } case survive_optimizer_measurement_type_light: { // If the next two measurements are joined; handle the full pair. This lets us just calculate // sensorPtInLH once const bool nextIsPair = mea_block_idx + 1 < mpfunc_ctx->measurementsCnt && mpfunc_ctx->disableVelocity == true && meas[1].meas_type == survive_optimizer_measurement_type_light && meas[0].light.axis == 0 && meas[1].light.axis == 1 && meas[0].light.sensor_idx == meas[1].light.sensor_idx && !meas[1].invalid && !mpfunc_ctx->settings->disallow_pair_calc; const int lh = meas->light.lh; const FLT *sensor_points = survive_optimizer_get_sensors(mpfunc_ctx, meas->light.object); LinmathDualPose *world2lh = &cameras[lh]; const FLT *ptp = &sensor_points[meas->light.sensor_idx * 3]; LinmathVec3d pt; copy3d(pt, ptp); // d a / d s = d a / d xyz * d xyz / d s int scale_idx = -1; LinmathVec3d xyzjac_scale = {0}; bool needsScaleJac = false; if (mpfunc_ctx->settings->optimize_scale_threshold >= 0 || mpfunc_ctx->settings->lh_scale_correction > 0) { scale_idx = survive_optimizer_get_sensor_scale_index(mpfunc_ctx) + meas->light.object; FLT scale = (1 + get_lighthouse_correction_for(mpfunc_ctx, meas->light.object, meas->light.lh, 2)); if(scale_idx >= 0) { scale *= p[scale_idx]; } SurvivePose imu2trackref = mpfunc_ctx->sos[meas->light.object]->imu2trackref; needsScaleJac = derivs && scale_idx >= 0 && derivs[scale_idx]; if (needsScaleJac) { gen_scale_sensor_pt_jac_scale(xyzjac_scale, pt, &imu2trackref, scale); } if (scale != 1) { gen_scale_sensor_pt(pt, pt, &imu2trackref, scale); } } bool needsNewObj2World = pose_idx != meas->light.object; if (calced_timecode != meas->time && !mpfunc_ctx->disableVelocity) { needsNewObj2World = true; } if (needsNewObj2World) { calced_timecode = meas->time; pose_idx = meas->light.object; assert(pose_idx < mpfunc_ctx->poseLength); obj2world = *(LinmathDualPose *)(&survive_optimizer_get_pose(mpfunc_ctx)[meas->light.object]); if (mpfunc_ctx->disableVelocity == false) { LinmathDualPose dPose = obj2world; FLT diff = mpfunc_ctx->timecode - meas->time; SurviveVelocity *v = survive_optimizer_get_velocity(mpfunc_ctx); if (mpfunc_ctx->settings->use_quat_model) { addscalednd(dPose.quatPose.Pos, obj2world.quatPose.Pos, v->Pos, -diff, 3); survive_apply_ang_velocity(dPose.quatPose.Rot, v->AxisAngleRot, -diff, obj2world.quatPose.Rot); gen_apply_ang_velocity_jac_q(ang_velocity_jac.data, v->AxisAngleRot, -diff, obj2world.quatPose.Rot); } else { addscalednd(dPose.axisAnglePose.Pos, obj2world.axisAnglePose.Pos, v->Pos, -diff, 3); survive_apply_ang_velocity_aa(dPose.axisAnglePose.AxisAngleRot, v->AxisAngleRot, -diff, obj2world.axisAnglePose.AxisAngleRot); gen_apply_ang_velocity_aa_jac_axis_angle2(ang_velocity_jac.data, v->AxisAngleRot, -diff, obj2world.axisAnglePose.AxisAngleRot); } obj2world = dPose; } if (mpfunc_ctx->settings->use_quat_model) { quatnormalize(obj2world.quatPose.Rot, obj2world.quatPose.Rot); } int lh_count = mpfunc_ctx->cameraLength > 0 ? mpfunc_ctx->cameraLength : mpfunc_ctx->sos[pose_idx]->ctx->activeLighthouses; // Precalc for all known lighthouses. We don't do this for velocity case since the velocity changes // obj2world constantly if (mpfunc_ctx->disableVelocity) { for (int lh = 0; lh < lh_count; lh++) { ApplyDualPoseToPose(mpfunc_ctx, &obj2lh[lh], &cameras[lh], &obj2world); } } } if (!mpfunc_ctx->disableVelocity) { ApplyDualPoseToPose(mpfunc_ctx, &obj2lh[lh], &cameras[lh], &obj2world); } const survive_reproject_model_t *reprojectModel = mpfunc_ctx->reprojectModel; if (needsScaleJac) { for (int meas_idx_jac = 0; meas_idx_jac < (1 + nextIsPair); meas_idx_jac++) { LinmathVec3d ptJac = {0}; int axis = meas[meas_idx_jac].light.axis; const struct BaseStationCal *cal = survive_optimizer_get_calibration(mpfunc_ctx, lh); if (mpfunc_ctx->settings->use_quat_model) { reprojectModel->reprojectAxisJacobSensorPt[axis](ptJac, &obj2world.quatPose, pt, &world2lh->quatPose, cal + axis); } else { reprojectModel->reprojectAxisAngleAxisJacobSensorPt[axis](ptJac, &obj2world.axisAnglePose, pt, &world2lh->axisAnglePose, cal + axis); } scale3d(ptJac, ptJac, 1. / meas[meas_idx_jac].variance); derivs[scale_idx][meas_idx + meas_idx_jac] = fix_infinity(dot3d(xyzjac_scale, ptJac)); } } if (nextIsPair) { run_pair_measurement(mpfunc_ctx, meas_idx, meas, &ang_velocity_jac, &obj2world, &obj2lh[lh], world2lh, pt, deviates + meas_idx, derivs); meas_idx++; mea_block_idx++; } else { run_single_measurement(mpfunc_ctx, meas_idx, meas, &ang_velocity_jac, &obj2world, &obj2lh[lh], world2lh, pt, deviates + meas_idx, derivs); } break; } case survive_optimizer_measurement_type_camera_accel: { handle_accel_meas(mpfunc_ctx, meas, meas_idx, deviates, derivs); break; } case survive_optimizer_measurement_type_camera_position: { LinmathVec3d pos; copy3d(pos, meas->camera_pos.pos); LinmathVec3d predicted; int lh = meas->camera_pos.camera; FLT deriv[7 * 3] = { 0 }; LinmathDualPose *world2lh = (LinmathDualPose *)&cameras[lh]; gen_apply_axisangle_pose_to_pt(predicted, &world2lh->axisAnglePose, pos); gen_apply_axisangle_pose_to_pt_jac_obj_p_axisangle(deriv, &world2lh->axisAnglePose, pos); int deriv_idx = survive_optimizer_get_camera_index(mpfunc_ctx) + lh * 7; for(int i = 0;i < 3;i++) { deviates[meas_idx + i] = predicted[i] / meas->variance; for (int j = 0; j < pose_size && derivs; j++) { if(derivs[deriv_idx + j]) { //printf("%7.7f %d %d\n", predicted[i], meas_idx + i, deriv_idx + j); derivs[deriv_idx + j][meas_idx + i] = fix_infinity(deriv[i * pose_size + j] / meas->variance); } } } } break; case survive_optimizer_measurement_type_fixed_rotation: { int obj = meas->fixed_rotation.obj; LinmathPoint3d predicted = {0, 0, 1}; FLT deriv[4 * 3] = {0}; LinmathDualPose *obj2world = (LinmathDualPose *)(&survive_optimizer_get_pose(mpfunc_ctx)[obj]); if (mpfunc_ctx->settings->use_quat_model) { assert(false); } else { LinmathVec3d r; scale3d(r, obj2world->axisAnglePose.AxisAngleRot, meas->fixed_rotation.conjugate ? -1 : 1); gen_axisanglerotatevector(predicted, r, meas->fixed_rotation.match_vec); gen_axisanglerotatevector_jac_axis_angle(deriv, r, meas->fixed_rotation.match_vec); scalend(deriv, deriv, meas->fixed_rotation.conjugate ? -1 : 1, 9); } const FLT *pl = meas->fixed_rotation.plane; FLT error = dot3d(predicted, pl); deviates[meas_idx] = error / meas->variance; int deriv_idx = obj * 7 + 3; for (int j = 0; j < ang_size && derivs; j++) { if (derivs[deriv_idx + j]) { derivs[deriv_idx + j][meas_idx] = fix_infinity( (pl[0] * deriv[j + 0] + pl[1] * deriv[j + 1 * ang_size] + pl[2] * deriv[j + 2 * ang_size]) / meas->variance); } } break; } case survive_optimizer_measurement_type_object_accel: { handle_accel_meas(mpfunc_ctx, meas, meas_idx, deviates, derivs); break; } } meas_idx += meas->size; } if (mpfunc_ctx->needsFiltering) { assert(derivs == 0); filter_measurements(mpfunc_ctx, deviates); } if (mpfunc_ctx->iteration_cb) { mpfunc_ctx->iteration_cb(mpfunc_ctx, m, n, p, deviates, derivs); } /* CnMat d = cnVec(m, deviates); cn_print_mat(&d); CnMat ps = cnVec(n, p); cn_print_mat(&ps); if(derivs) { CN_CREATE_STACK_MAT(J, m, n); for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { if (derivs && derivs[j]) cnMatrixSet(&J, i, j, derivs[j][i]); else cnMatrixSet(&J, i, j, NAN); } } cn_print_mat(&J); }*/ return 0; } const char *survive_optimizer_error(int status) { #define CASE(x) \ case x: \ return #x switch (status) { CASE(MP_ERR_INPUT); CASE(MP_ERR_NAN); CASE(MP_ERR_FUNC); CASE(MP_ERR_NPOINTS); CASE(MP_ERR_NFREE); CASE(MP_ERR_MEMORY); CASE(MP_ERR_INITBOUNDS); CASE(MP_ERR_BOUNDS); CASE(MP_ERR_PARAM); CASE(MP_ERR_DOF); /* Potential success status codes */ CASE(MP_OK_CHI); CASE(MP_OK_PAR); CASE(MP_OK_BOTH); CASE(MP_OK_DIR); CASE(MP_OK_NORM); CASE(MP_MAXITER); CASE(MP_FTOL); CASE(MP_GTOL); CASE(MP_XTOL); default: return "Unknown error"; } } static SurviveContext *cachedCtx = 0; static mp_config cachedCfg = {0}; static mp_config *survive_optimizer_get_cfg(SurviveContext *ctx) { if (ctx != cachedCtx) { cachedCfg = (mp_config){0}; cachedCfg.maxiter = survive_configf(ctx, OPTIMIZER_MAXITER_TAG, SC_GET, 0); cachedCfg.maxfev = survive_configf(ctx, OPTIMIZER_MAXFEV_TAG, SC_GET, 0); cachedCfg.ftol = survive_configf(ctx, OPTIMIZER_FTOL_TAG, SC_GET, 0); cachedCfg.normtol = survive_configf(ctx, OPTIMIZER_NORMTOL_TAG, SC_GET, 0); cachedCfg.xtol = survive_configf(ctx, OPTIMIZER_XTOL_TAG, SC_GET, 0); cachedCfg.gtol = survive_configf(ctx, OPTIMIZER_GTOL_TAG, SC_GET, 0); cachedCfg.covtol = survive_configf(ctx, OPTIMIZER_COVTOL_TAG, SC_GET, 0); cachedCfg.epsfcn = survive_configf(ctx, OPTIMIZER_EPSFCN_TAG, SC_GET, 0); cachedCfg.stepfactor = survive_configf(ctx, OPTIMIZER_STEPFACTOR_TAG, SC_GET, 0); cachedCfg.douserscale = survive_configi(ctx, OPTIMIZER_DOUSERSCALE_TAG, SC_GET, 0); cachedCfg.nprint = survive_configi(ctx, OPTIMIZER_NPRINT_TAG, SC_GET, 0); cachedCtx = ctx; } cachedCfg.iterproc = 0; return &cachedCfg; } mp_config precise_cfg = {0}; SURVIVE_EXPORT mp_config *survive_optimizer_precise_config() { return &precise_cfg; } #ifndef NDEBUG static inline bool sane_covariance(const CnMat *P) { #ifndef NDEBUG for (int i = 0; i < P->rows; i++) { if (cnMatrixGet(P, i, i) < 0) return false; } #ifdef USE_EIGEN return cnDet(P) > -1e-10; #endif #endif return true; } #endif int survive_optimizer_nonfixed_index(survive_optimizer *ctx, int idx) { if (idx < 0 || ctx->mp_parameters_info[idx].fixed) return -1; int rtn = 0; for (int i = 0; i < idx; i++) { if (!ctx->mp_parameters_info[i].fixed) rtn++; } return rtn; } int meas_cnt_for_obj_lh_axis(survive_optimizer *optimizer, int obj, int lh, int axis) { int rtn = 0; for(int i = 0;i < optimizer->measurementsCnt;i++) { survive_optimizer_measurement * meas = &optimizer->measurements[i]; if(meas->meas_type == survive_optimizer_measurement_type_light) { rtn += meas->light.lh == lh && meas->light.object == obj && meas->light.axis == axis; } } return rtn; } static inline int get_axis_count(const size_t *meas_for_lhs_axis, int min_to_count) { int num_axis = 0; for (int i = 0; i < NUM_GEN2_LIGHTHOUSES * 2; i++) { if (meas_for_lhs_axis[i] > min_to_count) num_axis++; } return num_axis; } int get_meas_for_lhs_axis(survive_optimizer *optimizer, int obj, size_t* meas_for_lhs_axis) { size_t mea_cnt = 0; for(int i = 0;i < optimizer->measurementsCnt;i++) { survive_optimizer_measurement * meas = &optimizer->measurements[i]; if(meas->invalid == false && meas->meas_type == survive_optimizer_measurement_type_light) { mea_cnt++; meas_for_lhs_axis[meas->light.lh * 2 + meas->light.axis]++; } } return mea_cnt; } void survive_optimizer_remove_invalid_meas(survive_optimizer *optimizer) { for (int i = 0; i < optimizer->measurementsCnt; i++) { if (optimizer->measurements[i].invalid) { optimizer->measurements[i] = optimizer->measurements[optimizer->measurementsCnt - 1]; optimizer->measurementsCnt--; i--; } } } SURVIVE_EXPORT void survive_optimizer_covariance_expand(survive_optimizer *optimizer, const struct CnMat *R_free, struct CnMat *R) { cn_set_zero(R); int idx = 0; int *ifree = alloca(sizeof(int) * R_free->rows); assert(R_free->rows == survive_optimizer_get_free_parameters_count(optimizer)); for (int i = 0; i < optimizer->parametersCnt; i++) { if (!optimizer->mp_parameters_info[i].fixed) { ifree[idx++] = i; } } for (int i = 0; i < R_free->rows; i++) { for (int j = 0; j < R_free->rows; j++) { cnMatrixSet(R, ifree[i], ifree[j], cnMatrixGet(R_free, i, j)); } } } int survive_optimizer_run(survive_optimizer *optimizer, struct mp_result_struct *result, struct CnMat *R) { SurviveContext *ctx = optimizer->sos[0] ? optimizer->sos[0]->ctx : 0; mp_config *cfg = optimizer->cfg; if (cfg == 0) cfg = survive_optimizer_get_cfg(ctx); SurvivePose *poses = survive_optimizer_get_pose(optimizer); int nonfixed_quat_cnt = 0; int *quat_idxs = alloca(sizeof(int) * (optimizer->poseLength + optimizer->cameraLength)); int *quat_free_idxs = alloca(sizeof(int) * (optimizer->poseLength + optimizer->cameraLength)); //int *quat_idxs = alloca(sizeof(int) * (optimizer->poseLength + optimizer->cameraLength)); int fixed_idxs = 0; if(!optimizer->settings->use_quat_model) { int nfree_til = 0; for (int i = 0; i < optimizer->poseLength + optimizer->cameraLength; i++) { for(int j = 0;j < 3;j++) nfree_til += !optimizer->mp_parameters_info[i * 7 + j].fixed; quattoaxisanglemag(poses[i].Rot, poses[i].Rot); poses[i].Rot[3] = 0; // NAN; if(optimizer->mp_parameters_info[i * 7 + 6].fixed == false) { quat_free_idxs[nonfixed_quat_cnt] = nfree_til; quat_idxs[nonfixed_quat_cnt++] = i * 7 + 3; nfree_til+=4; } optimizer->mp_parameters_info[i * 7 + 6].fixed = true; } } #ifndef NDEBUG for (int i = 0; i < survive_optimizer_get_parameters_count(optimizer); i++) { SurviveContext *ctx = (optimizer->sos == 0 || optimizer->sos[0] == 0) ? 0 : optimizer->sos[0]->ctx; //SV_VERBOSE(100, "Parameter %s(%d) is %d", // optimizer->mp_parameters_info[i].parname, i, // optimizer->mp_parameters_info[i].fixed) if ((optimizer->mp_parameters_info[i].limited[0] && optimizer->parameters[i] < optimizer->mp_parameters_info[i].limits[0]) || (optimizer->mp_parameters_info[i].limited[1] && optimizer->parameters[i] > optimizer->mp_parameters_info[i].limits[1]) || isnan(optimizer->parameters[i])) { SV_GENERAL_ERROR("Parameter %s(%d) is invalid. %f <= %f <= %f should be true", optimizer->mp_parameters_info[i].parname, i, optimizer->mp_parameters_info[i].limits[0], optimizer->parameters[i], optimizer->mp_parameters_info[i].limits[1]) survive_optimizer_serialize(optimizer, "debug.opt"); } } #endif // MPFit runs on temporary storage; so parameters is manipulated in mpfunc. Save it and restore it here. FLT *params = optimizer->parameters; optimizer->needsFiltering = !optimizer->nofilter && !optimizer->settings->disable_filter; FLT* deviates = alloca(survive_optimizer_get_meas_size(optimizer) * sizeof(FLT)); mpfunc(survive_optimizer_get_meas_size(optimizer), survive_optimizer_get_parameters_count(optimizer), params, deviates, 0, optimizer); survive_optimizer_parameter * lh_correction = survive_optimizer_get_start_parameter_info(optimizer, survive_optimizer_parameter_object_lighthouse_correction); if(lh_correction && (optimizer->settings->lh_scale_correction > 0 || optimizer->settings->lh_offset_correction > 0)) { for (int i = 0; i < optimizer->poseLength; i++) { size_t meas_for_lhs_axis[NUM_GEN2_LIGHTHOUSES * 2] = { 0 }; size_t valid_meas = get_meas_for_lhs_axis(optimizer, i, meas_for_lhs_axis); if(valid_meas < 10 || get_axis_count(meas_for_lhs_axis, 3) <= 3) continue; SurviveObject *so = optimizer->sos[i]; for (int lh = 0; lh < optimizer->cameraLength; lh++) { for (int axis = 0; axis < 3; axis++) { int idx = get_lighthouse_correction_idx_for(optimizer, i, lh, axis); if(idx > -1 && axis == 2 || meas_for_lhs_axis[lh * SURVIVE_CORRECTION_PARAMS + i] >= 6) { optimizer->mp_parameters_info[idx].fixed = false; survive_optimizer_measurement *meas = survive_optimizer_emplace_meas( optimizer, survive_optimizer_measurement_type_parameters_bias); meas->variance = so->lh_correction_variance[lh][axis]; meas->parameter_bias.parameter_index = idx; meas->parameter_bias.expected_value = 0; } SV_VERBOSE(110, "%s %d %d %d %f (%d, %d) %s %d", survive_colorize_codename(so), i, lh, axis, get_lighthouse_correction_for(optimizer, i, lh, axis), (int)meas_for_lhs_axis[lh * 2 + i], get_axis_count(meas_for_lhs_axis, 0), optimizer->mp_parameters_info[idx].parname, optimizer->mp_parameters_info[idx].fixed); } } } } survive_optimizer_remove_invalid_meas(optimizer); size_t meas_count = survive_optimizer_get_meas_size(optimizer); int nfree = survive_optimizer_get_free_parameters_count(optimizer); FLT *covar = R ? R->data : 0; int covar_size = nfree; CN_CREATE_STACK_MAT(R_aa, covar_size * (covar ? 1 : 0), covar_size * (covar ? 1 : 0)); result->covar_free = covar ? R_aa.data : 0; int param_cnt = survive_optimizer_get_parameters_count(optimizer); //CN_CREATE_STACK_VEC(PS, survive_optimizer_get_parameters_count(optimizer)); //result->xerror = PS.data; //CN_CREATE_STACK_VEC(MS, survive_optimizer_get_meas_size(optimizer)); //result->resid = MS.data; //CN_CREATE_STACK_MAT(J, nfree, meas_count); //result->jac = J.data; int rtn = mpfit(mpfunc, meas_count, survive_optimizer_get_parameters_count(optimizer), optimizer->parameters, optimizer->mp_parameters_info, cfg, optimizer, result); optimizer->parameters = params; FLT rchisqr = linmath_max(1, result->bestnorm / nfree); if (ctx) survive_recording_write_matrix(ctx->recptr, optimizer->sos[0], 10, "full_cov", &R_aa); assert(sane_covariance(&R_aa)); int totalPoseCount = optimizer->poseLength + optimizer->cameraLength; int pose_size = optimizer->settings->use_quat_model ? 7 : 6; int totalFreePoseCount = nfree / pose_size; if (covar) { *R = cnMat(R_aa.rows + nonfixed_quat_cnt, R_aa.rows + nonfixed_quat_cnt, R->data); CnMat R_q = cnMat(R->rows, R->cols, covar); if (optimizer->settings->optimize_scale_threshold >= 0) { int idx = survive_optimizer_get_sensor_scale_index(optimizer); int free_idx = survive_optimizer_nonfixed_index(optimizer, idx); if (free_idx >= 0) { for (int z = 0; z < 1; z++) { FLT scale_cov = cnMatrixGet(&R_aa, free_idx + z, free_idx + z); if (scale_cov > 0) { FLT scale = params[idx + z]; FLT y = scale - optimizer->sos[z]->sensor_scale; FLT k = optimizer->sos[z]->sensor_scale_var / (optimizer->sos[z]->sensor_scale_var + scale_cov); optimizer->sos[z]->sensor_scale += k * y; optimizer->sos[z]->sensor_scale_var *= (1 - k); } } } } if(optimizer->settings->use_quat_model) { cnCopy(&R_aa, &R_q, 0); } else { int idx = survive_optimizer_nonfixed_index(optimizer, survive_optimizer_get_start_index(optimizer, survive_optimizer_parameter_object_lighthouse_correction)); CN_CREATE_STACK_MAT(G, R->rows, R_aa.rows); CN_CREATE_STACK_MAT(G2, R->rows, R_aa.rows); CN_CREATE_STACK_MAT(Gp, 4, 3); assert(R->rows == R->cols); int cidx = 0; for (int z = 0; z < nonfixed_quat_cnt;z++) { int idx = quat_free_idxs[z]; for(;cidx < idx;cidx++) cnMatrixSet(&G, cidx, cidx - z, 1); gen_axisangle2quat_jac_axis_angle(Gp.data, params + idx); for (int i = 0; i < 4; i++) { for (int j = 0; j < 3; j++) { cnMatrixSet(&G, i + cidx, j + cidx - z, cnMatrixGet(&Gp, i, j)); } } cidx += 4; } for(;cidx < R->rows;cidx++) cnMatrixSet(&G, cidx, cidx - nonfixed_quat_cnt, 1); gemm_ABAt_add_scaled(&R_q, &G, &R_aa, 0, 1, 1, 0); assert(sane_covariance(&R_q)); } // https://lmfit.github.io/lmfit-py/fitting.html#uncertainties-in-variable-parameters-and-their-correlations if(!optimizer->dontScaleCov) { cn_multiply_scalar(&R_q, &R_q, rchisqr); } } bool wasSuccess = result->status > 0; if(lh_correction != 0 && wasSuccess) { for (int i = 0; i < optimizer->poseLength; i++) { SurviveObject *so = optimizer->sos[i]; struct mp_par_struct *info = lh_correction[i].pi; bool changed = false; for (int lh = 0; lh < optimizer->cameraLength; lh++) { for (int axis = 0; axis < SURVIVE_CORRECTION_PARAMS; axis++) { int cov_idx = survive_optimizer_nonfixed_index(optimizer, get_lighthouse_correction_idx_for(optimizer, i, lh, axis)); if(!info[lh * SURVIVE_CORRECTION_PARAMS + axis].fixed) { changed = true; FLT v = rchisqr * cnMatrixGet(&R_aa, cov_idx, cov_idx); FLT y = get_lighthouse_correction_for(optimizer, i, lh, axis) - so->lh_correction[lh][axis]; FLT k = so->lh_correction_variance[lh][axis] / (so->lh_correction_variance[lh][axis] + v); so->lh_correction[lh][axis] += k * y; so->lh_correction_variance[lh][axis] *= (1 - k); so->lh_correction_variance[lh][axis] += (axis == 2 ? optimizer->settings->lh_scale_correction : optimizer->settings->lh_offset_correction); SV_VERBOSE(110, "%s %d %d %d %+7.16f", survive_colorize_codename(so), i, lh, axis, get_lighthouse_correction_for(optimizer, i, lh, axis)); } } } if(changed) { CnMat v = cnMat(2, SURVIVE_CORRECTION_PARAMS, (FLT *)so->lh_correction); survive_recording_write_matrix(ctx->recptr, so, 5, "lhc", &v); } } } if(!optimizer->settings->use_quat_model) { for (int i = 0; i < totalPoseCount; i++) { quatfromaxisangle(poses[i].Rot, poses[i].Rot, norm3d(poses[i].Rot)); optimizer->mp_parameters_info[i * 7 + 6].fixed = optimizer->mp_parameters_info[i * 7 + 5].fixed; } } return rtn; } void survive_optimizer_set_reproject_model(survive_optimizer *optimizer, const survive_reproject_model_t *reprojectModel) { optimizer->reprojectModel = reprojectModel; } #ifdef NOZLIB #define gzFile FILE * #define gzopen fopen #define gzprintf fprintf #define gzclose fclose #endif void survive_optimizer_serialize(const survive_optimizer *opt, const char *fn) { FILE *f = fopen(fn, "w"); if(f == 0) return; if(opt->sos && opt->sos[0]) fprintf(f, "object %s\n", opt->sos[0]->codename); fprintf(f, "model %d\n", opt->reprojectModel != &survive_reproject_gen1_model); fprintf(f, "poseLength %d\n", opt->poseLength); fprintf(f, "cameraLength %d\n", opt->cameraLength); fprintf(f, "ptsLength %d\n", opt->ptsLength); fprintf(f, "\n"); fprintf(f, "parameters %d\n", survive_optimizer_get_parameters_count(opt)); fprintf(f, "# : " "\n"); for (int i = 0; i < survive_optimizer_get_parameters_count(opt); i++) { struct mp_par_struct *info = &opt->mp_parameters_info[i]; fprintf(f, "\t%16s:", opt->mp_parameters_info[i].parname); fprintf(f, " %12d", i); fprintf(f, " %12d", info->fixed); fprintf(f, " %+0.16f", opt->parameters[i]); fprintf(f, " %+16.f %+16.f", info->limits[0], info->limits[1]); fprintf(f, " %14d\n", info->side); } fprintf(f, "\n"); fprintf(f, "measurementsCnt %ld\n", opt->measurementsCnt); fprintf(f, "\t# \n"); for (int i = 0; i < opt->measurementsCnt; i++) { survive_optimizer_measurement *meas = &opt->measurements[i]; fprintf(f, "\t%d", meas->light.lh); fprintf(f, " %d", meas->light.axis); fprintf(f, " %2d", meas->light.sensor_idx); fprintf(f, " %d", meas->light.object); fprintf(f, " %+0.16f", meas->light.value); fprintf(f, " %+0.16f\n", meas->variance); } fclose(f); } survive_optimizer *survive_optimizer_load(const char *fn) { survive_optimizer *opt = calloc(sizeof(survive_optimizer), 1); FILE *f = fopen(fn, "r"); if (f == 0) return 0; int read_count = 0; #ifndef LINE_MAX #define LINE_MAX 2048 #endif char buffer[LINE_MAX] = { 0 }; char device_name[LINE_MAX] = {0}; opt->poseLength = 1; read_count = fscanf(f, "object %s\n", device_name); int model = 0; read_count = fscanf(f, "model %d\n", &model); opt->reprojectModel = model == 0 ? &survive_reproject_gen1_model : &survive_reproject_gen2_model; read_count = fscanf(f, "poseLength %d\n", &opt->poseLength); read_count = fscanf(f, "cameraLength %d\n", &opt->cameraLength); read_count = fscanf(f, "ptsLength %d\n", &opt->ptsLength); int param_count; read_count = fscanf(f, "parameters %d\n", ¶m_count); char *success = fgets(buffer, LINE_MAX, f); // fscanf(f, "\t#: \n"); assert(success); (void)read_count; assert(param_count == survive_optimizer_get_parameters_count(opt)); SURVIVE_OPTIMIZER_SETUP_HEAP_BUFFERS(*opt, 0); for (int i = 0; i < survive_optimizer_get_parameters_count(opt); i++) { struct mp_par_struct *info = &opt->mp_parameters_info[i]; read_count = fscanf(f, "\t"); char *b = calloc(128, 1); opt->mp_parameters_info[i].parname = b; char c = fgetc(f); while (c != ':') { *(b++) = c; c = fgetc(f); } int idx; read_count = fscanf(f, "%d ", &idx); read_count = fscanf(f, " %d", &info->fixed); read_count = fscanf(f, " " FLT_sformat, &opt->parameters[i]); read_count = fscanf(f, " " FLT_sformat " " FLT_sformat, &info->limits[0], &info->limits[1]); read_count = fscanf(f, " %d\n", &info->side); } read_count = fscanf(f, "\n"); read_count = fscanf(f, "measurementsCnt %lu\n", &opt->measurementsCnt); read_count = fscanf(f, "\t# \n"); for (int i = 0; i < opt->measurementsCnt; i++) { survive_optimizer_measurement *meas = &opt->measurements[i]; read_count = fscanf(f, "\t%hhu", &meas->light.lh); read_count = fscanf(f, " %hhu", &meas->light.axis); read_count = fscanf(f, " %hhu", &meas->light.sensor_idx); read_count = fscanf(f, " %d", &meas->light.object); read_count = fscanf(f, " " FLT_sformat, &meas->light.value); read_count = fscanf(f, " " FLT_sformat "\n", &meas->variance); } fclose(f); SurviveObject *so = survive_create_device(0, "SLV", opt, "SV0", 0); char filename[FILENAME_MAX] = {0}; snprintf(filename, FILENAME_MAX, "%s_config.json", device_name); FILE *fp = fopen(filename, "r"); if (fp) { fseek(fp, 0L, SEEK_END); int len = ftell(fp); fseek(fp, 0L, SEEK_SET); if (len > 0) { char *ct0conf = (char *)malloc(len); size_t read_size = fread(ct0conf, 1, len, fp); if (read_size != len) { fprintf(stderr, "Could not read full full config file %s\n", filename); } survive_default_config_process(so, ct0conf, len); fclose(fp); } } opt->sos[0] = so; return opt; } SURVIVE_EXPORT FLT survive_optimizer_current_norm(const survive_optimizer *opt) { int m = opt->measurementsCnt; int npar = survive_optimizer_get_parameters_count(opt); FLT *p = opt->parameters; FLT *deviates = alloca(sizeof(FLT) * m); FLT **derivs = 0; mpfunc(m, npar, p, deviates, 0, (void *)opt); FLT fnorm = 0; for (size_t i = 0; i < m; i++) { fnorm += deviates[i] * deviates[i]; } return fnorm; } SURVIVE_EXPORT int survive_optimizer_nonfixed_cnt(const survive_optimizer *ctx) { int rtn = 0; for (int i = 0; i < survive_optimizer_get_parameters_count(ctx); i++) { if (!ctx->mp_parameters_info[i].fixed) rtn++; } return rtn; } SURVIVE_EXPORT void survive_optimizer_get_nonfixed(const survive_optimizer *ctx, FLT *params) { for (int i = 0; i < survive_optimizer_get_parameters_count(ctx); i++) { if (!ctx->mp_parameters_info[i].fixed) *(params++) = ctx->parameters[i]; } } SURVIVE_EXPORT void survive_optimizer_set_nonfixed(survive_optimizer *ctx, FLT *params) { for (int i = 0; i < survive_optimizer_get_parameters_count(ctx); i++) { if (!ctx->mp_parameters_info[i].fixed) ctx->parameters[i] = *(params++); } } SURVIVE_EXPORT size_t survive_optimizer_get_total_buffer_size(const survive_optimizer *ctx) { size_t par_count = survive_optimizer_get_parameters_count(ctx); size_t sensor_cnt = 32; return par_count * (sizeof(FLT) + // parameters sizeof(struct mp_par_struct)) // parameters_info + ctx->poseLength * sizeof(survive_optimizer_measurement) * 2 * sensor_cnt * NUM_GEN2_LIGHTHOUSES; // measurements } SURVIVE_EXPORT void survive_optimizer_setup_buffers(survive_optimizer *ctx, FLT *parameter_buffer, survive_optimizer_parameter *parameter_info_buffer, struct mp_par_struct *mp_parameter_info_buffer, void *measurements_buffer, void *so_buffer) { size_t par_count = survive_optimizer_get_max_parameters_count(ctx); size_t meas_count = survive_optimizer_get_max_measurements_count(ctx); ctx->parameters = (FLT *)parameter_buffer; for (int i = 0; i < par_count; i++) { ctx->parameters[i] = NAN; } ctx->mp_parameters_info = mp_parameter_info_buffer; ctx->parameters_info = parameter_info_buffer; memset(ctx->parameters_info, 0, par_count * sizeof(survive_optimizer_parameter)); ctx->sos = so_buffer; ctx->measurements = (survive_optimizer_measurement *)(measurements_buffer); size_t measurementAllocSize = meas_count * sizeof(survive_optimizer_measurement); memset(ctx->measurements, 0, measurementAllocSize); // ctx->obj_up_vectors = (LinmathPoint3d *)((uint8_t *)measurements_buffer + measurementAllocSize); // ctx->cam_up_vectors = ctx->obj_up_vectors + ctx->poseLength; memset(ctx->mp_parameters_info, 0, sizeof(mp_par) * par_count); for (int i = 0; i < survive_optimizer_get_parameters_count(ctx); i++) { ctx->mp_parameters_info[i].fixed = 1; } survive_optimizer_emplace_params(ctx, survive_optimizer_parameter_object_pose, ctx->poseLength); survive_optimizer_emplace_params(ctx, survive_optimizer_parameter_camera, ctx->cameraLength); if (!ctx->disableVelocity) { survive_optimizer_emplace_params(ctx, survive_optimizer_parameter_object_velocity, ctx->poseLength); } if (ctx->settings->optimize_scale_threshold >= 0) { survive_optimizer_emplace_params(ctx, survive_optimizer_parameter_object_scale, ctx->poseLength); } if(true) { //survive_optimizer_emplace_params(ctx, survive_optimizer_parameter_object_lighthouse_correction, ctx->poseLength); } if (ctx->settings->current_pos_bias > 0) { for (int i = 0; i < ctx->poseLength; i++) { if (!quatiszero(ctx->sos[i]->OutPoseIMU.Rot)) { LinmathDualPose pose = { .quatPose = ctx->sos[i]->OutPoseIMU }; CnMat x = cnVec(7, (FLT*)&pose); CN_CREATE_STACK_VEC(P_q, 7); CN_CREATE_STACK_VEC(P_aa, 6); cnkalman_extrapolate_state(ctx->timecode, &ctx->sos[i]->tracker->model, &x, &P_q); if (!ctx->settings->use_quat_model) { survive_covariance_pose2poseAA(&P_aa, &pose.quatPose, &P_q); quattoaxisanglemag(pose.axisAnglePose.AxisAngleRot, pose.quatPose.Rot); } assert(i == 0); for(int z = 0; z < (ctx->settings->use_quat_model ? 7 : 6); z++) { survive_optimizer_measurement *meas = survive_optimizer_emplace_meas(ctx, survive_optimizer_measurement_type_parameters_bias); meas->parameter_bias.expected_value = ((FLT*)&pose.quatPose.Pos)[z]; meas->parameter_bias.parameter_index = z; meas->variance = sqrt(_P_q[z]) * ctx->settings->current_pos_bias; } } } } if (ctx->objectUpVectorVariance > 0) { for (int i = 0; i < ctx->poseLength; i++) { if(ctx->sos[i]) { FLT n = norm3d(ctx->sos[i]->activations.accel); if (isfinite(n) && fabs(1 - n) < .01) { survive_optimizer_measurement *meas = survive_optimizer_emplace_meas(ctx, survive_optimizer_measurement_type_object_accel); meas->pose_acc.object = i; survive_kalman_tracker_correct_imu(ctx->sos[i]->tracker, meas->pose_acc.acc, ctx->sos[i]->activations.accel); normalize3d(meas->pose_acc.acc, meas->pose_acc.acc); meas->variance = ctx->objectUpVectorVariance; } } } } } SURVIVE_EXPORT void *survive_optimizer_realloc(void *old_ptr, size_t size) { return realloc(old_ptr, size); } int survive_optimizer_get_max_measurements_count(const survive_optimizer *ctx) { int sensor_cnt = SENSORS_PER_OBJECT; assert(ctx->poseLength > 0); return ctx->poseLength * 2 * sensor_cnt * NUM_GEN2_LIGHTHOUSES + (ctx->settings->current_pos_bias <= 0 ? 0 : ctx->poseLength) + (ctx->poseLength + ctx->cameraLength) + survive_optimizer_get_max_parameters_count(ctx) ; } int meas_size(survive_optimizer *ctx, enum survive_optimizer_measurement_type type) { switch (type) { case survive_optimizer_measurement_type_light: return 1; case survive_optimizer_measurement_type_fixed_rotation: return 1; case survive_optimizer_measurement_type_camera_accel: case survive_optimizer_measurement_type_object_accel: return 3; case survive_optimizer_measurement_type_camera_position: return 3; case survive_optimizer_measurement_type_parameters_bias: return 1; default: assert(false); } return 0; } int params_size(survive_optimizer *ctx, enum survive_optimizer_parameter_type type) { switch (type) { case survive_optimizer_parameter_object_pose: return 7; case survive_optimizer_parameter_object_velocity: return 6; case survive_optimizer_parameter_camera: return 7; case survive_optimizer_parameter_camera_parameters: return sizeof(BaseStationCal) * 2 / sizeof(FLT); case survive_optimizer_parameter_obj_points: return 3; case survive_optimizer_parameter_object_scale: return 1; case survive_optimizer_parameter_object_lighthouse_correction: return NUM_GEN2_LIGHTHOUSES * SURVIVE_CORRECTION_PARAMS; default: assert(false); } return 0; } static inline const char* params_name(enum survive_optimizer_parameter_type type) { switch(type) { default: return "Unknown"; case survive_optimizer_parameter_none: return "None"; case survive_optimizer_parameter_object_pose: return "Obj Pose"; case survive_optimizer_parameter_object_velocity: return "Velocity"; case survive_optimizer_parameter_object_scale: return "Scale"; case survive_optimizer_parameter_camera: return "Camera"; case survive_optimizer_parameter_camera_parameters: return "Camera cal"; case survive_optimizer_parameter_obj_points: return "Points"; case survive_optimizer_parameter_object_lighthouse_correction: return "LH correction"; } } SURVIVE_EXPORT survive_optimizer_parameter * survive_optimizer_emplace_params(survive_optimizer *ctx, enum survive_optimizer_parameter_type type, int n) { assert(survive_optimizer_get_max_parameters_count(ctx) > ctx->parameterBlockCnt); assert(survive_optimizer_get_start_index(ctx, type) == -1); survive_optimizer_parameter *rtn = &ctx->parameters_info[ctx->parameterBlockCnt++]; rtn->param_type = type; rtn->size = params_size(ctx, type) * n; rtn->elem_size = n; rtn->pi = &ctx->mp_parameters_info[ctx->parametersCnt]; for(int i = 0;i < rtn->size;i++) { rtn->pi[i].fixed = true; rtn->pi[i].parname = params_name(type); } rtn->p_idx = ctx->parametersCnt; rtn->p = &ctx->parameters[ctx->parametersCnt]; ctx->parametersCnt += rtn->size; return rtn; } survive_optimizer_measurement *survive_optimizer_emplace_meas(survive_optimizer *ctx, enum survive_optimizer_measurement_type type) { assert(survive_optimizer_get_max_measurements_count(ctx) > ctx->measurementsCnt); survive_optimizer_measurement *rtn = &ctx->measurements[ctx->measurementsCnt++]; rtn->meas_type = type; rtn->size = meas_size(ctx, type); return rtn; } void survive_optimizer_pop_meas(survive_optimizer *ctx, int cnt) { assert(ctx->measurementsCnt >= cnt); ctx->measurementsCnt -= cnt; } SURVIVE_EXPORT FLT *survive_optimizer_obj_up_vector(survive_optimizer *ctx, int n) { for (int i = 0; i < ctx->measurementsCnt; i++) { if (ctx->measurements[i].meas_type == survive_optimizer_measurement_type_object_accel && ctx->measurements[i].pose_acc.object == n) { return ctx->measurements[i].pose_acc.acc; } } return 0; } SURVIVE_EXPORT FLT *survive_optimizer_cam_up_vector(survive_optimizer *ctx, int n) { for (int i = 0; i < ctx->measurementsCnt; i++) { if (ctx->measurements[i].meas_type == survive_optimizer_measurement_type_camera_accel && ctx->measurements[i].camera_acc.camera == n) { return ctx->measurements[i].pose_acc.acc; } } return 0; } void survive_optimizer_set_cam_up_vector(survive_optimizer *ctx, int i, FLT variance, const FLT *up) { FLT n = norm3d(up); if (!isfinite(n) || n == 0) return; if (variance > 0) { survive_optimizer_measurement *meas = survive_optimizer_emplace_meas(ctx, survive_optimizer_measurement_type_camera_accel); meas->camera_acc.camera = i; normalize3d(meas->camera_acc.acc, up); assert(isfinite(meas->camera_acc.acc[0])); meas->variance = variance; } } void survive_optimizer_set_obj_up_vector(survive_optimizer *ctx, int i, FLT variance, const FLT *up) { FLT *storage = survive_optimizer_obj_up_vector(ctx, i); if (storage == 0 && variance > 0) { survive_optimizer_measurement *meas = survive_optimizer_emplace_meas(ctx, survive_optimizer_measurement_type_object_accel); meas->pose_acc.object = i; normalize3d(meas->pose_acc.acc, up); meas->variance = variance; storage = meas->pose_acc.acc; } if (storage) { normalize3d(storage, up); } }