#include #include #include "locationd.h" using namespace EKFS; using namespace Eigen; ExitHandler do_exit; static VectorXd floatlist2vector(const capnp::List::Reader& floatlist) { VectorXd res(floatlist.size()); for (int i = 0; i < floatlist.size(); i++) { res[i] = floatlist[i]; } return res; } static Vector4d quat2vector(const Quaterniond& quat) { return Vector4d(quat.w(), quat.x(), quat.y(), quat.z()); } static Quaterniond vector2quat(const VectorXd& vec) { return Quaterniond(vec(0), vec(1), vec(2), vec(3)); } static void init_measurement(cereal::LiveLocationKalman::Measurement::Builder meas, const VectorXd& val, const VectorXd& std, bool valid) { meas.setValue(kj::arrayPtr(val.data(), val.size())); meas.setStd(kj::arrayPtr(std.data(), std.size())); meas.setValid(valid); } static MatrixXdr rotate_cov(const MatrixXdr& rot_matrix, const MatrixXdr& cov_in) { // To rotate a covariance matrix, the cov matrix needs to multiplied left and right by the transform matrix return ((rot_matrix * cov_in) * rot_matrix.transpose()); } static VectorXd rotate_std(const MatrixXdr& rot_matrix, const VectorXd& std_in) { // Stds cannot be rotated like values, only covariances can be rotated return rotate_cov(rot_matrix, std_in.array().square().matrix().asDiagonal()).diagonal().array().sqrt(); } Localizer::Localizer() { this->kf = std::make_unique(); this->reset_kalman(); this->calib = Vector3d(0.0, 0.0, 0.0); this->device_from_calib = MatrixXdr::Identity(3, 3); this->calib_from_device = MatrixXdr::Identity(3, 3); for (int i = 0; i < POSENET_STD_HIST_HALF * 2; i++) { this->posenet_stds.push_back(10.0); } VectorXd ecef_pos = this->kf->get_x().segment(STATE_ECEF_POS_START); this->converter = std::make_unique((ECEF) { .x = ecef_pos[0], .y = ecef_pos[1], .z = ecef_pos[2] }); } void Localizer::build_live_location(cereal::LiveLocationKalman::Builder& fix) { VectorXd predicted_state = this->kf->get_x(); MatrixXdr predicted_cov = this->kf->get_P(); VectorXd predicted_std = predicted_cov.diagonal().array().sqrt(); VectorXd fix_ecef = predicted_state.segment(STATE_ECEF_POS_START); ECEF fix_ecef_ecef = { .x = fix_ecef(0), .y = fix_ecef(1), .z = fix_ecef(2) }; VectorXd fix_ecef_std = predicted_std.segment(STATE_ECEF_POS_ERR_START); VectorXd vel_ecef = predicted_state.segment(STATE_ECEF_VELOCITY_START); VectorXd vel_ecef_std = predicted_std.segment(STATE_ECEF_VELOCITY_ERR_START); VectorXd fix_pos_geo_vec = this->get_position_geodetic(); //fix_pos_geo_std = np.abs(coord.ecef2geodetic(fix_ecef + fix_ecef_std) - fix_pos_geo) VectorXd orientation_ecef = quat2euler(vector2quat(predicted_state.segment(STATE_ECEF_ORIENTATION_START))); VectorXd orientation_ecef_std = predicted_std.segment(STATE_ECEF_ORIENTATION_ERR_START); MatrixXdr device_from_ecef = quat2rot(vector2quat(predicted_state.segment(STATE_ECEF_ORIENTATION_START))).transpose(); VectorXd calibrated_orientation_ecef = rot2euler(this->calib_from_device * device_from_ecef); VectorXd acc_calib = this->calib_from_device * predicted_state.segment(STATE_ACCELERATION_START); MatrixXdr acc_calib_cov = predicted_cov.block(STATE_ACCELERATION_ERR_START, STATE_ACCELERATION_ERR_START); VectorXd acc_calib_std = rotate_cov(this->calib_from_device, acc_calib_cov).diagonal().array().sqrt(); VectorXd ang_vel_calib = this->calib_from_device * predicted_state.segment(STATE_ANGULAR_VELOCITY_START); MatrixXdr vel_angular_cov = predicted_cov.block(STATE_ANGULAR_VELOCITY_ERR_START, STATE_ANGULAR_VELOCITY_ERR_START); VectorXd ang_vel_calib_std = rotate_cov(this->calib_from_device, vel_angular_cov).diagonal().array().sqrt(); VectorXd vel_device = device_from_ecef * vel_ecef; VectorXd device_from_ecef_eul = quat2euler(vector2quat(predicted_state.segment(STATE_ECEF_ORIENTATION_START))).transpose(); MatrixXdr condensed_cov(STATE_ECEF_ORIENTATION_ERR_LEN + STATE_ECEF_VELOCITY_ERR_LEN, STATE_ECEF_ORIENTATION_ERR_LEN + STATE_ECEF_VELOCITY_ERR_LEN); condensed_cov.topLeftCorner() = predicted_cov.block(STATE_ECEF_ORIENTATION_ERR_START, STATE_ECEF_ORIENTATION_ERR_START); condensed_cov.topRightCorner() = predicted_cov.block(STATE_ECEF_ORIENTATION_ERR_START, STATE_ECEF_VELOCITY_ERR_START); condensed_cov.bottomRightCorner() = predicted_cov.block(STATE_ECEF_VELOCITY_ERR_START, STATE_ECEF_VELOCITY_ERR_START); condensed_cov.bottomLeftCorner() = predicted_cov.block(STATE_ECEF_VELOCITY_ERR_START, STATE_ECEF_ORIENTATION_ERR_START); VectorXd H_input(device_from_ecef_eul.size() + vel_ecef.size()); H_input << device_from_ecef_eul, vel_ecef; MatrixXdr HH = this->kf->H(H_input); MatrixXdr vel_device_cov = (HH * condensed_cov) * HH.transpose(); VectorXd vel_device_std = vel_device_cov.diagonal().array().sqrt(); VectorXd vel_calib = this->calib_from_device * vel_device; VectorXd vel_calib_std = rotate_cov(this->calib_from_device, vel_device_cov).diagonal().array().sqrt(); VectorXd orientation_ned = ned_euler_from_ecef(fix_ecef_ecef, orientation_ecef); //orientation_ned_std = ned_euler_from_ecef(fix_ecef, orientation_ecef + orientation_ecef_std) - orientation_ned VectorXd nextfix_ecef = fix_ecef + vel_ecef; VectorXd ned_vel = this->converter->ecef2ned((ECEF) { .x = nextfix_ecef(0), .y = nextfix_ecef(1), .z = nextfix_ecef(2) }).to_vector() - converter->ecef2ned(fix_ecef_ecef).to_vector(); //ned_vel_std = self.converter->ecef2ned(fix_ecef + vel_ecef + vel_ecef_std) - self.converter->ecef2ned(fix_ecef + vel_ecef) VectorXd accDevice = predicted_state.segment(STATE_ACCELERATION_START); VectorXd accDeviceErr = predicted_std.segment(STATE_ACCELERATION_ERR_START); VectorXd angVelocityDevice = predicted_state.segment(STATE_ANGULAR_VELOCITY_START); VectorXd angVelocityDeviceErr = predicted_std.segment(STATE_ANGULAR_VELOCITY_ERR_START); Vector3d nans = Vector3d(NAN, NAN, NAN); // write measurements to msg init_measurement(fix.initPositionGeodetic(), fix_pos_geo_vec, nans, true); init_measurement(fix.initPositionECEF(), fix_ecef, fix_ecef_std, true); init_measurement(fix.initVelocityECEF(), vel_ecef, vel_ecef_std, true); init_measurement(fix.initVelocityNED(), ned_vel, nans, true); init_measurement(fix.initVelocityDevice(), vel_device, vel_device_std, true); init_measurement(fix.initAccelerationDevice(), accDevice, accDeviceErr, true); init_measurement(fix.initOrientationECEF(), orientation_ecef, orientation_ecef_std, true); init_measurement(fix.initCalibratedOrientationECEF(), calibrated_orientation_ecef, nans, this->calibrated); init_measurement(fix.initOrientationNED(), orientation_ned, nans, true); init_measurement(fix.initAngularVelocityDevice(), angVelocityDevice, angVelocityDeviceErr, true); init_measurement(fix.initVelocityCalibrated(), vel_calib, vel_calib_std, this->calibrated); init_measurement(fix.initAngularVelocityCalibrated(), ang_vel_calib, ang_vel_calib_std, this->calibrated); init_measurement(fix.initAccelerationCalibrated(), acc_calib, acc_calib_std, this->calibrated); double old_mean = 0.0, new_mean = 0.0; int i = 0; for (double x : this->posenet_stds) { if (i < POSENET_STD_HIST_HALF) { old_mean += x; } else { new_mean += x; } i++; } old_mean /= POSENET_STD_HIST_HALF; new_mean /= POSENET_STD_HIST_HALF; // experimentally found these values, no false positives in 20k minutes of driving bool std_spike = (new_mean / old_mean > 4.0 && new_mean > 7.0); fix.setPosenetOK(!(std_spike && this->car_speed > 5.0)); fix.setDeviceStable(!this->device_fell); this->device_fell = false; //fix.setGpsWeek(this->time.week); //fix.setGpsTimeOfWeek(this->time.tow); fix.setUnixTimestampMillis(this->unix_timestamp_millis); if (fix_ecef_std.norm() < 50.0 && this->calibrated) { fix.setStatus(cereal::LiveLocationKalman::Status::VALID); } else if (fix_ecef_std.norm() < 50.0) { fix.setStatus(cereal::LiveLocationKalman::Status::UNCALIBRATED); } else { fix.setStatus(cereal::LiveLocationKalman::Status::UNINITIALIZED); } } VectorXd Localizer::get_position_geodetic() { VectorXd fix_ecef = this->kf->get_x().segment(STATE_ECEF_POS_START); ECEF fix_ecef_ecef = { .x = fix_ecef(0), .y = fix_ecef(1), .z = fix_ecef(2) }; Geodetic fix_pos_geo = ecef2geodetic(fix_ecef_ecef); return Vector3d(fix_pos_geo.lat, fix_pos_geo.lon, fix_pos_geo.alt); } void Localizer::handle_sensors(double current_time, const capnp::List::Reader& log) { // TODO does not yet account for double sensor readings in the log for (int i = 0; i < log.size(); i++) { const cereal::SensorEventData::Reader& sensor_reading = log[i]; double sensor_time = 1e-9 * sensor_reading.getTimestamp(); // TODO: handle messages from two IMUs at the same time if (sensor_reading.getSource() == cereal::SensorEventData::SensorSource::LSM6DS3) { continue; } // Gyro Uncalibrated if (sensor_reading.getSensor() == SENSOR_GYRO_UNCALIBRATED && sensor_reading.getType() == SENSOR_TYPE_GYROSCOPE_UNCALIBRATED) { auto v = sensor_reading.getGyroUncalibrated().getV(); this->kf->predict_and_observe(sensor_time, OBSERVATION_PHONE_GYRO, { Vector3d(-v[2], -v[1], -v[0]) }); } // Accelerometer if (sensor_reading.getSensor() == SENSOR_ACCELEROMETER && sensor_reading.getType() == SENSOR_TYPE_ACCELEROMETER) { auto v = sensor_reading.getAcceleration().getV(); // check if device fell, estimate 10 for g // 40m/s**2 is a good filter for falling detection, no false positives in 20k minutes of driving this->device_fell |= (floatlist2vector(v) - Vector3d(10.0, 0.0, 0.0)).norm() > 40.0; this->kf->predict_and_observe(sensor_time, OBSERVATION_PHONE_ACCEL, { Vector3d(-v[2], -v[1], -v[0]) }); } } } void Localizer::handle_gps(double current_time, const cereal::GpsLocationData::Reader& log) { // ignore the message if the fix is invalid if (log.getFlags() % 2 == 0) { return; } this->last_gps_fix = current_time; Geodetic geodetic = { log.getLatitude(), log.getLongitude(), log.getAltitude() }; this->converter = std::make_unique(geodetic); VectorXd ecef_pos = this->converter->ned2ecef({ 0.0, 0.0, 0.0 }).to_vector(); VectorXd ecef_vel = this->converter->ned2ecef({ log.getVNED()[0], log.getVNED()[1], log.getVNED()[2] }).to_vector() - ecef_pos; MatrixXdr ecef_pos_R = Vector3d::Constant(std::pow(3.0 * log.getVerticalAccuracy(), 2)).asDiagonal(); MatrixXdr ecef_vel_R = Vector3d::Constant(std::pow(log.getSpeedAccuracy(), 2)).asDiagonal(); this->unix_timestamp_millis = log.getTimestamp(); double gps_est_error = (this->kf->get_x().head(3) - ecef_pos).norm(); VectorXd orientation_ecef = quat2euler(vector2quat(this->kf->get_x().segment(STATE_ECEF_ORIENTATION_START))); VectorXd orientation_ned = ned_euler_from_ecef({ ecef_pos(0), ecef_pos(1), ecef_pos(2) }, orientation_ecef); VectorXd orientation_ned_gps = Vector3d(0.0, 0.0, DEG2RAD(log.getBearingDeg())); VectorXd orientation_error = (orientation_ned - orientation_ned_gps).array() - M_PI; for (int i = 0; i < orientation_error.size(); i++) { orientation_error(i) = std::fmod(orientation_error(i), 2.0 * M_PI); if (orientation_error(i) < 0.0) { orientation_error(i) += 2.0 * M_PI; } orientation_error(i) -= M_PI; } VectorXd initial_pose_ecef_quat = quat2vector(euler2quat(ecef_euler_from_ned({ ecef_pos(0), ecef_pos(1), ecef_pos(2) }, orientation_ned_gps))); if (ecef_vel.norm() > 5.0 && orientation_error.norm() > 1.0) { LOGE("Locationd vs ubloxLocation orientation difference too large, kalman reset"); this->reset_kalman(NAN, initial_pose_ecef_quat, ecef_pos); this->kf->predict_and_observe(current_time, OBSERVATION_ECEF_ORIENTATION_FROM_GPS, { initial_pose_ecef_quat }); } else if (gps_est_error > 50.0) { LOGE("Locationd vs ubloxLocation position difference too large, kalman reset"); this->reset_kalman(NAN, initial_pose_ecef_quat, ecef_pos); } this->kf->predict_and_observe(current_time, OBSERVATION_ECEF_POS, { ecef_pos }, { ecef_pos_R }); this->kf->predict_and_observe(current_time, OBSERVATION_ECEF_VEL, { ecef_vel }, { ecef_vel_R }); } void Localizer::handle_car_state(double current_time, const cereal::CarState::Reader& log) { //this->kf->predict_and_observe(current_time, OBSERVATION_ODOMETRIC_SPEED, { (VectorXd(1) << log.getVEgoRaw()).finished() }); this->car_speed = std::abs(log.getVEgo()); if (this->car_speed < 1e-3) { this->kf->predict_and_observe(current_time, OBSERVATION_NO_ROT, { Vector3d(0.0, 0.0, 0.0) }); } } void Localizer::handle_cam_odo(double current_time, const cereal::CameraOdometry::Reader& log) { VectorXd rot_device = this->device_from_calib * floatlist2vector(log.getRot()); VectorXd trans_device = this->device_from_calib * floatlist2vector(log.getTrans()); VectorXd rot_calib_std = floatlist2vector(log.getRotStd()); VectorXd trans_calib_std = floatlist2vector(log.getTransStd()); this->posenet_stds.pop_front(); this->posenet_stds.push_back(trans_calib_std[0]); // Multiply by 10 to avoid to high certainty in kalman filter because of temporally correlated noise trans_calib_std *= 10.0; rot_calib_std *= 10.0; VectorXd rot_device_std = rotate_std(this->device_from_calib, rot_calib_std); VectorXd trans_device_std = rotate_std(this->device_from_calib, trans_calib_std); this->kf->predict_and_observe(current_time, OBSERVATION_CAMERA_ODO_ROTATION, { (VectorXd(rot_device.rows() + rot_device_std.rows()) << rot_device, rot_device_std).finished() }); this->kf->predict_and_observe(current_time, OBSERVATION_CAMERA_ODO_TRANSLATION, { (VectorXd(trans_device.rows() + trans_device_std.rows()) << trans_device, trans_device_std).finished() }); } void Localizer::handle_live_calib(double current_time, const cereal::LiveCalibrationData::Reader& log) { if (log.getRpyCalib().size() > 0) { this->calib = floatlist2vector(log.getRpyCalib()); this->device_from_calib = euler2rot(this->calib); this->calib_from_device = this->device_from_calib.transpose(); this->calibrated = log.getCalStatus() == 1; } } void Localizer::reset_kalman(double current_time) { VectorXd init_x = this->kf->get_initial_x(); this->reset_kalman(current_time, init_x.segment<4>(3), init_x.head(3)); } void Localizer::finite_check(double current_time) { bool all_finite = this->kf->get_x().array().isFinite().all() or this->kf->get_P().array().isFinite().all(); if (!all_finite){ LOGE("Non-finite values detected, kalman reset"); this->reset_kalman(current_time); } } void Localizer::reset_kalman(double current_time, VectorXd init_orient, VectorXd init_pos) { // too nonlinear to init on completely wrong VectorXd init_x = this->kf->get_initial_x(); MatrixXdr init_P = this->kf->get_initial_P(); init_x.segment<4>(3) = init_orient; init_x.head(3) = init_pos; this->kf->init_state(init_x, init_P, current_time); } void Localizer::handle_msg_bytes(const char *data, const size_t size) { AlignedBuffer aligned_buf; capnp::FlatArrayMessageReader cmsg(aligned_buf.align(data, size)); cereal::Event::Reader event = cmsg.getRoot(); this->handle_msg(event); } void Localizer::handle_msg(const cereal::Event::Reader& log) { double t = log.getLogMonoTime() * 1e-9; if (log.isSensorEvents()) { this->handle_sensors(t, log.getSensorEvents()); } else if (log.isGpsLocationExternal()) { this->handle_gps(t, log.getGpsLocationExternal()); } else if (log.isCarState()) { this->handle_car_state(t, log.getCarState()); } else if (log.isCameraOdometry()) { this->handle_cam_odo(t, log.getCameraOdometry()); } else if (log.isLiveCalibration()) { this->handle_live_calib(t, log.getLiveCalibration()); } this->finite_check(); } kj::ArrayPtr Localizer::get_message_bytes(MessageBuilder& msg_builder, uint64_t logMonoTime, bool inputsOK, bool sensorsOK, bool gpsOK) { cereal::Event::Builder evt = msg_builder.initEvent(); evt.setLogMonoTime(logMonoTime); cereal::LiveLocationKalman::Builder liveLoc = evt.initLiveLocationKalman(); this->build_live_location(liveLoc); liveLoc.setInputsOK(inputsOK); liveLoc.setSensorsOK(sensorsOK); liveLoc.setGpsOK(gpsOK); return msg_builder.toBytes(); } int Localizer::locationd_thread() { const std::initializer_list service_list = { "gpsLocationExternal", "sensorEvents", "cameraOdometry", "liveCalibration", "carState" }; PubMaster pm({ "liveLocationKalman" }); SubMaster sm(service_list, nullptr, { "gpsLocationExternal" }); Params params; while (!do_exit) { sm.update(); for (const char* service : service_list) { if (sm.updated(service) && sm.valid(service)) { const cereal::Event::Reader log = sm[service]; this->handle_msg(log); } } if (sm.updated("cameraOdometry")) { uint64_t logMonoTime = sm["cameraOdometry"].getLogMonoTime(); bool inputsOK = sm.allAliveAndValid(); bool sensorsOK = sm.alive("sensorEvents") && sm.valid("sensorEvents"); bool gpsOK = (logMonoTime / 1e9) - this->last_gps_fix < 1.0; MessageBuilder msg_builder; kj::ArrayPtr bytes = this->get_message_bytes(msg_builder, logMonoTime, inputsOK, sensorsOK, gpsOK); pm.send("liveLocationKalman", bytes.begin(), bytes.size()); if (sm.frame % 1200 == 0 && gpsOK) { // once a minute VectorXd posGeo = this->get_position_geodetic(); std::string lastGPSPosJSON = util::string_format( "{\"latitude\": %.15f, \"longitude\": %.15f, \"altitude\": %.15f}", posGeo(0), posGeo(1), posGeo(2)); std::thread([¶ms] (const std::string gpsjson) { params.put("LastGPSPosition", gpsjson); }, lastGPSPosJSON).detach(); } } } return 0; } int main() { setpriority(PRIO_PROCESS, 0, -20); Localizer localizer; return localizer.locationd_thread(); }