Fast-Lio/src/IMU_Processing.hpp

367 lines
13 KiB
C++
Raw Normal View History

2020-11-01 00:17:32 +08:00
#include <cmath>
#include <math.h>
#include <deque>
#include <mutex>
#include <thread>
#include <fstream>
#include <csignal>
#include <ros/ros.h>
#include <Exp_mat.h>
#include <Eigen/Eigen>
#include <common_lib.h>
#include <pcl/common/io.h>
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <condition_variable>
#include <nav_msgs/Odometry.h>
#include <pcl/common/transforms.h>
#include <pcl/kdtree/kdtree_flann.h>
#include <tf/transform_broadcaster.h>
#include <eigen_conversions/eigen_msg.h>
#include <pcl_conversions/pcl_conversions.h>
#include <sensor_msgs/Imu.h>
#include <sensor_msgs/PointCloud2.h>
#include <fast_lio/States.h>
#include <geometry_msgs/Vector3.h>
/// *************Preconfiguration
#define MAX_INI_COUNT (200)
2020-11-01 00:17:32 +08:00
const bool time_list(PointType &x, PointType &y) {return (x.curvature < y.curvature);};
/// *************IMU Process and undistortion
class ImuProcess
{
public:
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
ImuProcess();
~ImuProcess();
void Process(const MeasureGroup &meas, StatesGroup &state, PointCloudXYZI::Ptr pcl_un_);
void Reset();
void IMU_Initial(const MeasureGroup &meas, StatesGroup &state, int &N);
// Eigen::Matrix3d Exp(const Eigen::Vector3d &ang_vel, const double &dt);
void IntegrateGyr(const std::vector<sensor_msgs::Imu::ConstPtr> &v_imu);
void UndistortPcl(const MeasureGroup &meas, StatesGroup &state_inout, PointCloudXYZI &pcl_in_out);
ros::NodeHandle nh;
void Integrate(const sensor_msgs::ImuConstPtr &imu);
void Reset(double start_timestamp, const sensor_msgs::ImuConstPtr &lastimu);
double scale_gravity;
Eigen::Vector3d angvel_last;
Eigen::Vector3d acc_s_last;
Eigen::Matrix<double,DIM_OF_PROC_N,1> cov_proc_noise;
Eigen::Vector3d cov_acc;
Eigen::Vector3d cov_gyr;
std::ofstream fout;
private:
/*** Whether is the first frame, init for first frame ***/
bool b_first_frame_ = true;
bool imu_need_init_ = true;
int init_iter_num = 1;
Eigen::Vector3d mean_acc;
Eigen::Vector3d mean_gyr;
/*** Undistorted pointcloud ***/
PointCloudXYZI::Ptr cur_pcl_un_;
//// For timestamp usage
sensor_msgs::ImuConstPtr last_imu_;
/*** For gyroscope integration ***/
double start_timestamp_;
/// Making sure the equal size: v_imu_ and v_rot_
std::deque<sensor_msgs::ImuConstPtr> v_imu_;
std::vector<Eigen::Matrix3d> v_rot_pcl_;
std::vector<Pose6D> IMUpose;
};
ImuProcess::ImuProcess()
: b_first_frame_(true), imu_need_init_(true), last_imu_(nullptr), start_timestamp_(-1)
{
Eigen::Quaterniond q(0, 1, 0, 0);
Eigen::Vector3d t(0, 0, 0);
init_iter_num = 1;
scale_gravity = 1.0;
cov_acc = Eigen::Vector3d(0.1, 0.1, 0.1);
cov_gyr = Eigen::Vector3d(0.1, 0.1, 0.1);
mean_acc = Eigen::Vector3d(0, 0, -1.0);
mean_gyr = Eigen::Vector3d(0, 0, 0);
angvel_last = Zero3d;
cov_proc_noise = Eigen::Matrix<double,DIM_OF_PROC_N,1>::Zero();
// Lidar_offset_to_IMU = Eigen::Vector3d(0.0, 0.0, -0.0);
// fout.open(DEBUG_FILE_DIR("imu.txt"),std::ios::out);
}
ImuProcess::~ImuProcess() {fout.close();}
void ImuProcess::Reset()
{
ROS_WARN("Reset ImuProcess");
scale_gravity = 1.0;
angvel_last = Zero3d;
cov_proc_noise = Eigen::Matrix<double,DIM_OF_PROC_N,1>::Zero();
cov_acc = Eigen::Vector3d(0.1, 0.1, 0.1);
cov_gyr = Eigen::Vector3d(0.1, 0.1, 0.1);
mean_acc = Eigen::Vector3d(0, 0, -1.0);
mean_gyr = Eigen::Vector3d(0, 0, 0);
imu_need_init_ = true;
b_first_frame_ = true;
init_iter_num = 1;
last_imu_ = nullptr;
//gyr_int_.Reset(-1, nullptr);
start_timestamp_ = -1;
v_imu_.clear();
IMUpose.clear();
cur_pcl_un_.reset(new PointCloudXYZI());
fout.close();
}
void ImuProcess::IMU_Initial(const MeasureGroup &meas, StatesGroup &state_inout, int &N)
{
/** 1. initializing the gravity, gyro bias, acc and gyro covariance
** 2. normalize the acceleration measurenments to unit gravity **/
ROS_INFO("IMU Initializing: %.1f %%", double(N) / MAX_INI_COUNT * 100);
Eigen::Vector3d cur_acc, cur_gyr;
if (b_first_frame_)
{
Reset();
N = 1;
b_first_frame_ = false;
}
for (const auto &imu : meas.imu)
{
const auto &imu_acc = imu->linear_acceleration;
const auto &gyr_acc = imu->angular_velocity;
cur_acc << imu_acc.x, imu_acc.y, imu_acc.z;
cur_gyr << gyr_acc.x, gyr_acc.y, gyr_acc.z;
scale_gravity += (cur_acc.norm() - scale_gravity) / N;
mean_acc += (cur_acc - mean_acc) / N;
mean_gyr += (cur_gyr - mean_gyr) / N;
cov_acc = cov_acc * (N - 1.0) / N + (cur_acc - mean_acc).cwiseProduct(cur_acc - mean_acc) * (N - 1.0) / (N * N);
cov_gyr = cov_gyr * (N - 1.0) / N + (cur_gyr - mean_gyr).cwiseProduct(cur_gyr - mean_gyr) * (N - 1.0) / (N * N);
N ++;
}
state_inout.gravity = - mean_acc /scale_gravity * G_m_s2;
state_inout.rot_end = Eye3d; // Exp(mean_acc.cross(Eigen::Vector3d(0, 0, -1 / scale_gravity)));
state_inout.bias_g = mean_gyr;
}
void ImuProcess::UndistortPcl(const MeasureGroup &meas, StatesGroup &state_inout, PointCloudXYZI &pcl_out)
{
/*** add the imu of the last frame-tail to the of current frame-head ***/
auto v_imu = meas.imu;
v_imu.push_front(last_imu_);
const double &imu_beg_time = v_imu.front()->header.stamp.toSec();
const double &imu_end_time = v_imu.back()->header.stamp.toSec();
const double &pcl_beg_time = meas.lidar_beg_time;
/*** sort point clouds by offset time ***/
pcl_out = *(meas.lidar);
std::sort(pcl_out.points.begin(), pcl_out.points.end(), time_list);
const double &pcl_end_time = pcl_beg_time + pcl_out.points.back().curvature / double(1000);
std::cout<<"[ IMU Process ]: Process lidar from "<<pcl_beg_time<<" to "<<pcl_end_time<<", " \
<<meas.imu.size()<<" imu msgs from "<<imu_beg_time<<" to "<<imu_end_time<<std::endl;
/*** Initialize IMU pose ***/
IMUpose.clear();
// IMUpose.push_back(set_pose6d(0.0, Zero3d, Zero3d, state.vel_end, state.pos_end, state.rot_end));
IMUpose.push_back(set_pose6d(0.0, acc_s_last, angvel_last, state_inout.vel_end, state_inout.pos_end, state_inout.rot_end));
/*** forward propagation at each imu point ***/
Eigen::Vector3d acc_imu, angvel_avr, acc_avr, vel_imu(state_inout.vel_end), pos_imu(state_inout.pos_end);
Eigen::Matrix3d R_imu(state_inout.rot_end);
Eigen::MatrixXd F_x(Eigen::Matrix<double, DIM_OF_STATES, DIM_OF_STATES>::Identity());
Eigen::MatrixXd cov_w(Eigen::Matrix<double, DIM_OF_STATES, DIM_OF_STATES>::Zero());
double dt = 0;
for (auto it_imu = v_imu.begin(); it_imu != (v_imu.end() - 1); it_imu++)
{
auto &&head = *(it_imu);
auto &&tail = *(it_imu + 1);
angvel_avr<<0.5 * (head->angular_velocity.x + tail->angular_velocity.x),
0.5 * (head->angular_velocity.y + tail->angular_velocity.y),
0.5 * (head->angular_velocity.z + tail->angular_velocity.z);
acc_avr <<0.5 * (head->linear_acceleration.x + tail->linear_acceleration.x),
0.5 * (head->linear_acceleration.y + tail->linear_acceleration.y),
0.5 * (head->linear_acceleration.z + tail->linear_acceleration.z);
angvel_avr -= state_inout.bias_g;
acc_avr = acc_avr * G_m_s2 / scale_gravity - state_inout.bias_a;
#ifdef DEBUG_PRINT
// fout<<head->header.stamp.toSec()<<" "<<angvel_avr.transpose()<<" "<<acc_avr.transpose()<<std::endl;
#endif
2020-11-01 00:17:32 +08:00
dt = tail->header.stamp.toSec() - head->header.stamp.toSec();
/* covariance propagation */
Eigen::Matrix3d acc_avr_skew;
Eigen::Matrix3d Exp_f = Exp(angvel_avr, dt);
acc_avr_skew<<SKEW_SYM_MATRX(angvel_avr);
F_x.block<3,3>(0,0) = Exp(angvel_avr, - dt);
2020-11-01 00:17:32 +08:00
F_x.block<3,3>(0,9) = - Eye3d * dt;
// F_x.block<3,3>(3,0) = R_imu * off_vel_skew * dt;
F_x.block<3,3>(3,6) = Eye3d * dt;
F_x.block<3,3>(6,0) = - R_imu * acc_avr_skew * dt;
F_x.block<3,3>(6,12) = - R_imu * dt;
F_x.block<3,3>(6,15) = Eye3d * dt;
Eigen::Matrix3d cov_acc_diag(Eye3d), cov_gyr_diag(Eye3d);
cov_acc_diag.diagonal() = cov_acc;
cov_gyr_diag.diagonal() = cov_gyr;
cov_w.block<3,3>(0,0).diagonal() = cov_gyr * dt * dt * 10000;
cov_w.block<3,3>(3,3) = R_imu * cov_gyr_diag * R_imu.transpose() * dt * dt * 10000;
cov_w.block<3,3>(6,6) = R_imu * cov_acc_diag * R_imu.transpose() * dt * dt * 10000;
cov_w.block<3,3>(9,9).diagonal() = Eigen::Vector3d(0.0001, 0.0001, 0.0001) * dt * dt; // bias gyro covariance
cov_w.block<3,3>(12,12).diagonal() = Eigen::Vector3d(0.0001, 0.0001, 0.0001) * dt * dt; // bias acc covariance
state_inout.cov = F_x * state_inout.cov * F_x.transpose() + cov_w;
/* propogation of IMU attitude */
R_imu = R_imu * Exp_f;
/* Specific acceleration (global frame) of IMU */
acc_imu = R_imu * acc_avr + state_inout.gravity;
/* propogation of IMU */
pos_imu = pos_imu + vel_imu * dt + 0.5 * acc_imu * dt * dt;
/* velocity of IMU */
vel_imu = vel_imu + acc_imu * dt;
/* save the poses at each IMU measurements */
angvel_last = angvel_avr;
acc_s_last = acc_imu;
double &&offs_t = tail->header.stamp.toSec() - pcl_beg_time;
// std::cout<<"acc "<<acc_imu.transpose()<<"vel "<<acc_imu.transpose()<<"vel "<<pos_imu.transpose()<<std::endl;
IMUpose.push_back(set_pose6d(offs_t, acc_imu, angvel_avr, vel_imu, pos_imu, R_imu));
}
/*** calculated the pos and attitude prediction at the frame-end ***/
dt = pcl_end_time - imu_end_time;
state_inout.vel_end = vel_imu + acc_imu * dt;
state_inout.rot_end = R_imu * Exp(angvel_avr, dt);
state_inout.pos_end = pos_imu + vel_imu * dt + 0.5 * acc_imu * dt * dt;
auto pos_liD_e = state_inout.pos_end + state_inout.rot_end * Lidar_offset_to_IMU;
// auto R_liD_e = state_inout.rot_end * Lidar_R_to_IMU;
#ifdef DEBUG_PRINT
std::cout<<"[ IMU Process ]: vel "<<state_inout.vel_end.transpose()<<" pos "<<state_inout.pos_end.transpose()<<" ba"<<state_inout.bias_a.transpose()<<" bg "<<state_inout.bias_g.transpose()<<std::endl;
std::cout<<"propagated cov: "<<state_inout.cov.diagonal().transpose()<<std::endl;
#endif
/*** undistort each lidar point (backward propagation) ***/
auto it_pcl = pcl_out.points.end() - 1;
for (auto it_kp = IMUpose.end() - 1; it_kp != IMUpose.begin(); it_kp--)
{
auto head = it_kp - 1;
auto tail = it_kp;
R_imu<<MAT_FROM_ARRAY(head->rot);
acc_imu<<VEC_FROM_ARRAY(head->acc);
// std::cout<<"head imu acc: "<<acc_imu.transpose()<<std::endl;
vel_imu<<VEC_FROM_ARRAY(head->vel);
pos_imu<<VEC_FROM_ARRAY(head->pos);
angvel_avr<<VEC_FROM_ARRAY(head->gyr);
for(; it_pcl->curvature / double(1000) > head->offset_time; it_pcl --)
{
dt = it_pcl->curvature / double(1000) - head->offset_time;
/* Transform to the 'end' frame, using only the rotation
* Note: Compensation direction is INVERSE of Frame's moving direction
* So if we want to compensate a point at timestamp-i to the frame-e
* P_compensate = R_imu_e ^ T * (R_i * P_i + T_ei) where T_ei is represented in global frame */
Eigen::Matrix3d R_i(R_imu * Exp(angvel_avr, dt));
Eigen::Vector3d T_ei(pos_imu + vel_imu * dt + 0.5 * acc_imu * dt * dt + R_i * Lidar_offset_to_IMU - pos_liD_e);
Eigen::Vector3d P_i(it_pcl->x, it_pcl->y, it_pcl->z);
Eigen::Vector3d P_compensate = state_inout.rot_end.transpose() * (R_i * P_i + T_ei);
/// save Undistorted points and their rotation
it_pcl->x = P_compensate(0);
it_pcl->y = P_compensate(1);
it_pcl->z = P_compensate(2);
if (it_pcl == pcl_out.points.begin()) break;
}
}
}
void ImuProcess::Process(const MeasureGroup &meas, StatesGroup &stat, PointCloudXYZI::Ptr cur_pcl_un_)
{
double t1,t2,t3;
t1 = omp_get_wtime();
if(meas.imu.empty()) {std::cout<<"no imu data"<<std::endl;return;};
ROS_ASSERT(meas.lidar != nullptr);
if (imu_need_init_)
{
/// The very first lidar frame
IMU_Initial(meas, stat, init_iter_num);
imu_need_init_ = true;
last_imu_ = meas.imu.back();
if (init_iter_num > MAX_INI_COUNT)
{
imu_need_init_ = false;
// std::cout<<"mean acc: "<<mean_acc<<" acc measures in word frame:"<<state.rot_end.transpose()*mean_acc<<std::endl;
ROS_INFO("IMU Initials: Gravity: %.4f %.4f %.4f; state.bias_g: %.4f %.4f %.4f; acc covarience: %.8f %.8f %.8f; gry covarience: %.8f %.8f %.8f",\
stat.gravity[0], stat.gravity[1], stat.gravity[2], stat.bias_g[0], stat.bias_g[1], stat.bias_g[2], cov_acc[0], cov_acc[1], cov_acc[2], cov_gyr[0], cov_gyr[1], cov_gyr[2]);
}
return;
}
/// Undistort points the first point is assummed as the base frame
/// Compensate lidar points with IMU rotation (with only rotation now)
UndistortPcl(meas, stat, *cur_pcl_un_);
t2 = omp_get_wtime();
// {
// static ros::Publisher pub_UndistortPcl =
// nh.advertise<sensor_msgs::PointCloud2>("/livox_undistort", 100);
// sensor_msgs::PointCloud2 pcl_out_msg;
// pcl::toROSMsg(*cur_pcl_un_, pcl_out_msg);
// pcl_out_msg.header.stamp = ros::Time().fromSec(meas.lidar_beg_time);
// pcl_out_msg.header.frame_id = "/livox";
// pub_UndistortPcl.publish(pcl_out_msg);
// }
/// Record last measurements
last_imu_ = meas.imu.back();
t3 = omp_get_wtime();
std::cout<<"[ IMU Process ]: Time: "<<t3 - t1<<std::endl;
}