mirror of
https://github.com/introlab/rtabmap.git
synced 2026-09-02 17:40:23 +08:00
Devel: new feature msckf_vio
This commit is contained in:
@@ -69,6 +69,7 @@ SET(SRC_FILES
|
||||
OdometryOkvis.cpp
|
||||
OdometryORBSLAM2.cpp
|
||||
OdometryLOAM.cpp
|
||||
OdometryMSCKF.cpp
|
||||
|
||||
IMUThread.cpp
|
||||
|
||||
@@ -379,6 +380,17 @@ IF(okvis_FOUND)
|
||||
)
|
||||
ENDIF(okvis_FOUND)
|
||||
|
||||
IF(msckf_vio_FOUND)
|
||||
SET(INCLUDE_DIRS
|
||||
${msckf_vio_INCLUDE_DIRS}
|
||||
${INCLUDE_DIRS}
|
||||
)
|
||||
SET(LIBRARIES
|
||||
${msckf_vio_LIBRARIES}
|
||||
${LIBRARIES}
|
||||
)
|
||||
ENDIF(msckf_vio_FOUND)
|
||||
|
||||
IF(ORB_SLAM2_FOUND)
|
||||
SET(INCLUDE_DIRS
|
||||
${ORB_SLAM2_INCLUDE_DIRS} #before so that g2o includes are taken from ORB_SLAM2 directory before the official g2o one
|
||||
|
||||
831
corelib/src/OdometryMSCKF.cpp
Normal file
831
corelib/src/OdometryMSCKF.cpp
Normal file
@@ -0,0 +1,831 @@
|
||||
/*
|
||||
Copyright (c) 2010-2016, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the Universite de Sherbrooke nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "rtabmap/core/OdometryMSCKF.h"
|
||||
#include "rtabmap/core/OdometryInfo.h"
|
||||
#include "rtabmap/core/util3d_transforms.h"
|
||||
#include "rtabmap/utilite/ULogger.h"
|
||||
#include "rtabmap/utilite/UTimer.h"
|
||||
#include "rtabmap/utilite/UStl.h"
|
||||
#include "rtabmap/utilite/UThread.h"
|
||||
|
||||
#ifdef RTABMAP_MSCKF_VIO
|
||||
#include <msckf_vio/image_processor.h>
|
||||
#include <msckf_vio/msckf_vio.h>
|
||||
#include <msckf_vio/math_utils.hpp>
|
||||
#include <eigen_conversions/eigen_msg.h>
|
||||
#include <boost/math/distributions/chi_squared.hpp>
|
||||
#endif
|
||||
|
||||
namespace rtabmap {
|
||||
|
||||
#ifdef RTABMAP_MSCKF_VIO
|
||||
class ImageProcessorNoROS: public msckf_vio::ImageProcessor
|
||||
{
|
||||
public:
|
||||
ImageProcessorNoROS(
|
||||
const ParametersMap & parameters_in,
|
||||
const Transform & imuLocalTransform,
|
||||
const StereoCameraModel & model,
|
||||
bool rectified)
|
||||
{
|
||||
// Camera calibration parameters
|
||||
if(model.left().D_raw().cols == 6)
|
||||
{
|
||||
//equidistant
|
||||
cam0_distortion_model = "equidistant";
|
||||
cam0_distortion_coeffs[0] = rectified?0:model.left().D_raw().at<double>(0,0);
|
||||
cam0_distortion_coeffs[1] = rectified?0:model.left().D_raw().at<double>(0,1);
|
||||
cam0_distortion_coeffs[2] = rectified?0:model.left().D_raw().at<double>(0,4);
|
||||
cam0_distortion_coeffs[3] = rectified?0:model.left().D_raw().at<double>(0,5);
|
||||
}
|
||||
else
|
||||
{
|
||||
//radtan
|
||||
cam0_distortion_model = "radtan";
|
||||
cam0_distortion_coeffs[0] = rectified?0:model.left().D_raw().at<double>(0,0);
|
||||
cam0_distortion_coeffs[1] = rectified?0:model.left().D_raw().at<double>(0,1);
|
||||
cam0_distortion_coeffs[2] = rectified?0:model.left().D_raw().at<double>(0,2);
|
||||
cam0_distortion_coeffs[3] = rectified?0:model.left().D_raw().at<double>(0,3);
|
||||
}
|
||||
if(model.right().D_raw().cols == 6)
|
||||
{
|
||||
//equidistant
|
||||
cam1_distortion_model = "equidistant";
|
||||
cam1_distortion_coeffs[0] = rectified?0:model.right().D_raw().at<double>(0,0);
|
||||
cam1_distortion_coeffs[1] = rectified?0:model.right().D_raw().at<double>(0,1);
|
||||
cam1_distortion_coeffs[2] = rectified?0:model.right().D_raw().at<double>(0,4);
|
||||
cam1_distortion_coeffs[3] = rectified?0:model.right().D_raw().at<double>(0,5);
|
||||
}
|
||||
else
|
||||
{
|
||||
//radtan
|
||||
cam1_distortion_model = "radtan";
|
||||
cam1_distortion_coeffs[0] = rectified?0:model.right().D_raw().at<double>(0,0);
|
||||
cam1_distortion_coeffs[1] = rectified?0:model.right().D_raw().at<double>(0,1);
|
||||
cam1_distortion_coeffs[2] = rectified?0:model.right().D_raw().at<double>(0,2);
|
||||
cam1_distortion_coeffs[3] = rectified?0:model.right().D_raw().at<double>(0,3);
|
||||
}
|
||||
|
||||
cam0_resolution[0] = model.left().imageWidth();
|
||||
cam0_resolution[1] = model.left().imageHeight();
|
||||
|
||||
cam1_resolution[0] = model.right().imageWidth();
|
||||
cam1_resolution[1] = model.right().imageHeight();
|
||||
|
||||
cam0_intrinsics[0] = model.left().fx();
|
||||
cam0_intrinsics[1] = model.left().fy();
|
||||
cam0_intrinsics[2] = model.left().cx();
|
||||
cam0_intrinsics[3] = model.left().cy();
|
||||
|
||||
cam1_intrinsics[0] = model.right().fx();
|
||||
cam1_intrinsics[1] = model.right().fy();
|
||||
cam1_intrinsics[2] = model.right().cx();
|
||||
cam1_intrinsics[3] = model.right().cy();
|
||||
|
||||
Transform imuCam = imuLocalTransform.inverse() * model.localTransform();
|
||||
cv::Mat T_imu_cam0 = imuCam.dataMatrix();
|
||||
cv::Matx33d R_imu_cam0(T_imu_cam0(cv::Rect(0,0,3,3)));
|
||||
cv::Vec3d t_imu_cam0 = T_imu_cam0(cv::Rect(3,0,1,3));
|
||||
R_cam0_imu = R_imu_cam0.t();
|
||||
t_cam0_imu = -R_imu_cam0.t() * t_imu_cam0;
|
||||
|
||||
Transform cam0cam1;
|
||||
if(rectified)
|
||||
{
|
||||
cam0cam1 = Transform(1, 0, 0, 0,
|
||||
0, 1, 0, model.baseline(),
|
||||
0, 0, 1, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
cam0cam1 = model.stereoTransform();
|
||||
}
|
||||
cv::Mat T_cam0_cam1 = cam0cam1.dataMatrix();
|
||||
cv::Mat T_imu_cam1 = T_cam0_cam1 * T_imu_cam0;
|
||||
cv::Matx33d R_imu_cam1(T_imu_cam1(cv::Rect(0,0,3,3)));
|
||||
cv::Vec3d t_imu_cam1 = T_imu_cam1(cv::Rect(3,0,1,3));
|
||||
R_cam1_imu = R_imu_cam1.t();
|
||||
t_cam1_imu = -R_imu_cam1.t() * t_imu_cam1;
|
||||
|
||||
// Processor parameters
|
||||
// get all OdomMSCFK group to make sure all parameters are set
|
||||
ParametersMap parameters = Parameters::getDefaultParameters("OdomMSCKF");
|
||||
uInsert(parameters, parameters_in);
|
||||
|
||||
Parameters::parse(parameters, Parameters::kOdomMSCKFGridRow(), processor_config.grid_row); //4
|
||||
Parameters::parse(parameters, Parameters::kOdomMSCKFGridCol(), processor_config.grid_col); //4
|
||||
Parameters::parse(parameters, Parameters::kOdomMSCKFGridMinFeatureNum(), processor_config.grid_min_feature_num); //2
|
||||
Parameters::parse(parameters, Parameters::kOdomMSCKFGridMaxFeatureNum(), processor_config.grid_max_feature_num); //4
|
||||
Parameters::parse(parameters, Parameters::kOdomMSCKFPyramidLevels(), processor_config.pyramid_levels); //3
|
||||
Parameters::parse(parameters, Parameters::kOdomMSCKFPatchSize(), processor_config.patch_size); //31
|
||||
Parameters::parse(parameters, Parameters::kOdomMSCKFFastThreshold(), processor_config.fast_threshold); //20
|
||||
Parameters::parse(parameters, Parameters::kOdomMSCKFMaxIteration(), processor_config.max_iteration); //30
|
||||
Parameters::parse(parameters, Parameters::kOdomMSCKFTrackPrecision(), processor_config.track_precision); //0.01
|
||||
Parameters::parse(parameters, Parameters::kOdomMSCKFRansacThreshold(), processor_config.ransac_threshold); //3
|
||||
Parameters::parse(parameters, Parameters::kOdomMSCKFStereoThreshold(), processor_config.stereo_threshold); //3
|
||||
|
||||
UINFO("===========================================");
|
||||
UINFO("cam0_resolution: %d, %d",
|
||||
cam0_resolution[0], cam0_resolution[1]);
|
||||
UINFO("cam0_intrinscs: %f, %f, %f, %f",
|
||||
cam0_intrinsics[0], cam0_intrinsics[1],
|
||||
cam0_intrinsics[2], cam0_intrinsics[3]);
|
||||
UINFO("cam0_distortion_model: %s",
|
||||
cam0_distortion_model.c_str());
|
||||
UINFO("cam0_distortion_coefficients: %f, %f, %f, %f",
|
||||
cam0_distortion_coeffs[0], cam0_distortion_coeffs[1],
|
||||
cam0_distortion_coeffs[2], cam0_distortion_coeffs[3]);
|
||||
|
||||
UINFO("cam1_resolution: %d, %d",
|
||||
cam1_resolution[0], cam1_resolution[1]);
|
||||
UINFO("cam1_intrinscs: %f, %f, %f, %f",
|
||||
cam1_intrinsics[0], cam1_intrinsics[1],
|
||||
cam1_intrinsics[2], cam1_intrinsics[3]);
|
||||
UINFO("cam1_distortion_model: %s",
|
||||
cam1_distortion_model.c_str());
|
||||
UINFO("cam1_distortion_coefficients: %f, %f, %f, %f",
|
||||
cam1_distortion_coeffs[0], cam1_distortion_coeffs[1],
|
||||
cam1_distortion_coeffs[2], cam1_distortion_coeffs[3]);
|
||||
|
||||
std::cout << R_imu_cam0 << std::endl;
|
||||
std::cout << t_imu_cam0.t() << std::endl;
|
||||
|
||||
UINFO("grid_row: %d",
|
||||
processor_config.grid_row);
|
||||
UINFO("grid_col: %d",
|
||||
processor_config.grid_col);
|
||||
UINFO("grid_min_feature_num: %d",
|
||||
processor_config.grid_min_feature_num);
|
||||
UINFO("grid_max_feature_num: %d",
|
||||
processor_config.grid_max_feature_num);
|
||||
UINFO("pyramid_levels: %d",
|
||||
processor_config.pyramid_levels);
|
||||
UINFO("patch_size: %d",
|
||||
processor_config.patch_size);
|
||||
UINFO("fast_threshold: %d",
|
||||
processor_config.fast_threshold);
|
||||
UINFO("max_iteration: %d",
|
||||
processor_config.max_iteration);
|
||||
UINFO("track_precision: %f",
|
||||
processor_config.track_precision);
|
||||
UINFO("ransac_threshold: %f",
|
||||
processor_config.ransac_threshold);
|
||||
UINFO("stereo_threshold: %f",
|
||||
processor_config.stereo_threshold);
|
||||
UINFO("===========================================");
|
||||
}
|
||||
|
||||
virtual ~ImageProcessorNoROS() {}
|
||||
|
||||
msckf_vio::CameraMeasurementPtr stereoCallback2(
|
||||
const sensor_msgs::ImageConstPtr& cam0_img,
|
||||
const sensor_msgs::ImageConstPtr& cam1_img) {
|
||||
|
||||
//cout << "==================================" << endl;
|
||||
|
||||
// Get the current image.
|
||||
cam0_curr_img_ptr = cv_bridge::toCvShare(cam0_img,
|
||||
sensor_msgs::image_encodings::MONO8);
|
||||
cam1_curr_img_ptr = cv_bridge::toCvShare(cam1_img,
|
||||
sensor_msgs::image_encodings::MONO8);
|
||||
|
||||
// Build the image pyramids once since they're used at multiple places
|
||||
createImagePyramids();
|
||||
|
||||
// Detect features in the first frame.
|
||||
if (is_first_img) {
|
||||
ros::Time start_time = ros::Time::now();
|
||||
initializeFirstFrame();
|
||||
//UINFO("Detection time: %f",
|
||||
// (ros::Time::now()-start_time).toSec());
|
||||
is_first_img = false;
|
||||
|
||||
// Draw results.
|
||||
start_time = ros::Time::now();
|
||||
drawFeaturesStereo();
|
||||
//UINFO("Draw features: %f",
|
||||
// (ros::Time::now()-start_time).toSec());
|
||||
} else {
|
||||
// Track the feature in the previous image.
|
||||
ros::Time start_time = ros::Time::now();
|
||||
trackFeatures();
|
||||
//UINFO("Tracking time: %f",
|
||||
// (ros::Time::now()-start_time).toSec());
|
||||
|
||||
// Add new features into the current image.
|
||||
start_time = ros::Time::now();
|
||||
addNewFeatures();
|
||||
//UINFO("Addition time: %f",
|
||||
// (ros::Time::now()-start_time).toSec());
|
||||
|
||||
// Add new features into the current image.
|
||||
start_time = ros::Time::now();
|
||||
pruneGridFeatures();
|
||||
//UINFO("Prune grid features: %f",
|
||||
// (ros::Time::now()-start_time).toSec());
|
||||
|
||||
// Draw results.
|
||||
start_time = ros::Time::now();
|
||||
drawFeaturesStereo();
|
||||
//UINFO("Draw features: %f",
|
||||
// (ros::Time::now()-start_time).toSec());
|
||||
}
|
||||
|
||||
//ros::Time start_time = ros::Time::now();
|
||||
//updateFeatureLifetime();
|
||||
//UINFO("Statistics: %f",
|
||||
// (ros::Time::now()-start_time).toSec());
|
||||
|
||||
// Publish features in the current image.
|
||||
ros::Time start_time = ros::Time::now();
|
||||
msckf_vio::CameraMeasurementPtr measurements = publish();
|
||||
//UINFO("Publishing: %f",
|
||||
// (ros::Time::now()-start_time).toSec());
|
||||
|
||||
// Update the previous image and previous features.
|
||||
cam0_prev_img_ptr = cam0_curr_img_ptr;
|
||||
prev_features_ptr = curr_features_ptr;
|
||||
std::swap(prev_cam0_pyramid_, curr_cam0_pyramid_);
|
||||
|
||||
// Initialize the current features to empty vectors.
|
||||
curr_features_ptr.reset(new GridFeatures());
|
||||
for (int code = 0; code <
|
||||
processor_config.grid_row*processor_config.grid_col; ++code) {
|
||||
(*curr_features_ptr)[code] = std::vector<FeatureMetaData>(0);
|
||||
}
|
||||
|
||||
return measurements;
|
||||
}
|
||||
|
||||
msckf_vio::CameraMeasurementPtr publish() {
|
||||
|
||||
// Publish features.
|
||||
msckf_vio::CameraMeasurementPtr feature_msg_ptr(new msckf_vio::CameraMeasurement);
|
||||
feature_msg_ptr->header.stamp = cam0_curr_img_ptr->header.stamp;
|
||||
|
||||
std::vector<FeatureIDType> curr_ids(0);
|
||||
std::vector<cv::Point2f> curr_cam0_points(0);
|
||||
std::vector<cv::Point2f> curr_cam1_points(0);
|
||||
|
||||
for (const auto& grid_features : (*curr_features_ptr)) {
|
||||
for (const auto& feature : grid_features.second) {
|
||||
curr_ids.push_back(feature.id);
|
||||
curr_cam0_points.push_back(feature.cam0_point);
|
||||
curr_cam1_points.push_back(feature.cam1_point);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<cv::Point2f> curr_cam0_points_undistorted(0);
|
||||
std::vector<cv::Point2f> curr_cam1_points_undistorted(0);
|
||||
|
||||
undistortPoints(
|
||||
curr_cam0_points, cam0_intrinsics, cam0_distortion_model,
|
||||
cam0_distortion_coeffs, curr_cam0_points_undistorted);
|
||||
undistortPoints(
|
||||
curr_cam1_points, cam1_intrinsics, cam1_distortion_model,
|
||||
cam1_distortion_coeffs, curr_cam1_points_undistorted);
|
||||
|
||||
for (unsigned int i = 0; i < curr_ids.size(); ++i) {
|
||||
feature_msg_ptr->features.push_back(msckf_vio::FeatureMeasurement());
|
||||
feature_msg_ptr->features[i].id = curr_ids[i];
|
||||
feature_msg_ptr->features[i].u0 = curr_cam0_points_undistorted[i].x;
|
||||
feature_msg_ptr->features[i].v0 = curr_cam0_points_undistorted[i].y;
|
||||
feature_msg_ptr->features[i].u1 = curr_cam1_points_undistorted[i].x;
|
||||
feature_msg_ptr->features[i].v1 = curr_cam1_points_undistorted[i].y;
|
||||
}
|
||||
|
||||
//feature_pub.publish(feature_msg_ptr);
|
||||
|
||||
// Publish tracking info.
|
||||
/*TrackingInfoPtr tracking_info_msg_ptr(new TrackingInfo());
|
||||
tracking_info_msg_ptr->header.stamp = cam0_curr_img_ptr->header.stamp;
|
||||
tracking_info_msg_ptr->before_tracking = before_tracking;
|
||||
tracking_info_msg_ptr->after_tracking = after_tracking;
|
||||
tracking_info_msg_ptr->after_matching = after_matching;
|
||||
tracking_info_msg_ptr->after_ransac = after_ransac;
|
||||
tracking_info_pub.publish(tracking_info_msg_ptr);*/
|
||||
|
||||
return feature_msg_ptr;
|
||||
}
|
||||
};
|
||||
|
||||
class MsckfVioNoROS: public msckf_vio::MsckfVio
|
||||
{
|
||||
public:
|
||||
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
|
||||
|
||||
MsckfVioNoROS(const ParametersMap & parameters_in,
|
||||
const Transform & imuLocalTransform,
|
||||
const StereoCameraModel & model,
|
||||
bool rectified)
|
||||
{
|
||||
// get all OdomMSCFK group to make sure all parameters are set
|
||||
ParametersMap parameters = Parameters::getDefaultParameters("OdomMSCKF");
|
||||
uInsert(parameters, parameters_in);
|
||||
|
||||
// Frame id
|
||||
publish_tf = false;
|
||||
frame_rate = 40.0;
|
||||
Parameters::parse(parameters, Parameters::kOdomMSCKFPositionStdThreshold(), position_std_threshold); //8.0
|
||||
|
||||
Parameters::parse(parameters, Parameters::kOdomMSCKFRotationThreshold(), rotation_threshold); //0.2618
|
||||
Parameters::parse(parameters, Parameters::kOdomMSCKFTranslationThreshold(), translation_threshold); //0.4
|
||||
Parameters::parse(parameters, Parameters::kOdomMSCKFTrackingRateThreshold(), tracking_rate_threshold); //0.5
|
||||
|
||||
// Feature optimization parameters
|
||||
Parameters::parse(parameters, Parameters::kOdomMSCKFOptTranslationThreshold(), msckf_vio::Feature::optimization_config.translation_threshold); //0.2
|
||||
|
||||
// Noise related parameters
|
||||
Parameters::parse(parameters, Parameters::kOdomMSCKFNoiseGyro(), msckf_vio::IMUState::gyro_noise); //0.001
|
||||
Parameters::parse(parameters, Parameters::kOdomMSCKFNoiseAcc(), msckf_vio::IMUState::acc_noise); //0.01
|
||||
Parameters::parse(parameters, Parameters::kOdomMSCKFNoiseGyroBias(), msckf_vio::IMUState::gyro_bias_noise); //0.001
|
||||
Parameters::parse(parameters, Parameters::kOdomMSCKFNoiseAccBias(), msckf_vio::IMUState::acc_bias_noise); //0.01
|
||||
Parameters::parse(parameters, Parameters::kOdomMSCKFNoiseFeature(), msckf_vio::Feature::observation_noise); //0.01
|
||||
|
||||
// Use variance instead of standard deviation.
|
||||
msckf_vio::IMUState::gyro_noise *= msckf_vio::IMUState::gyro_noise;
|
||||
msckf_vio::IMUState::acc_noise *= msckf_vio::IMUState::acc_noise;
|
||||
msckf_vio::IMUState::gyro_bias_noise *= msckf_vio::IMUState::gyro_bias_noise;
|
||||
msckf_vio::IMUState::acc_bias_noise *= msckf_vio::IMUState::acc_bias_noise;
|
||||
msckf_vio::Feature::observation_noise *= msckf_vio::Feature::observation_noise;
|
||||
|
||||
// Set the initial IMU state.
|
||||
// The intial orientation and position will be set to the origin
|
||||
// implicitly. But the initial velocity and bias can be
|
||||
// set by parameters.
|
||||
// TODO: is it reasonable to set the initial bias to 0?
|
||||
//Parameters::parse(parameters, "initial_state/velocity/x", state_server.imu_state.velocity(0)); //0.0
|
||||
//Parameters::parse(parameters, "initial_state/velocity/y", state_server.imu_state.velocity(1)); //0.0
|
||||
//Parameters::parse(parameters, "initial_state/velocity/z", state_server.imu_state.velocity(2)); //0.0
|
||||
|
||||
// The initial covariance of orientation and position can be
|
||||
// set to 0. But for velocity, bias and extrinsic parameters,
|
||||
// there should be nontrivial uncertainty.
|
||||
double gyro_bias_cov, acc_bias_cov, velocity_cov;
|
||||
Parameters::parse(parameters, Parameters::kOdomMSCKFInitCovVel(), velocity_cov); //0.25
|
||||
Parameters::parse(parameters, Parameters::kOdomMSCKFInitCovGyroBias(), gyro_bias_cov); //1e-4
|
||||
Parameters::parse(parameters, Parameters::kOdomMSCKFInitCovAccBias(), acc_bias_cov); //1e-2
|
||||
|
||||
double extrinsic_rotation_cov, extrinsic_translation_cov;
|
||||
Parameters::parse(parameters, Parameters::kOdomMSCKFInitCovExRot(), extrinsic_rotation_cov); //3.0462e-4
|
||||
Parameters::parse(parameters, Parameters::kOdomMSCKFInitCovExTrans(), extrinsic_translation_cov); //1e-4
|
||||
|
||||
state_server.state_cov = Eigen::MatrixXd::Zero(21, 21);
|
||||
for (int i = 3; i < 6; ++i)
|
||||
state_server.state_cov(i, i) = gyro_bias_cov;
|
||||
for (int i = 6; i < 9; ++i)
|
||||
state_server.state_cov(i, i) = velocity_cov;
|
||||
for (int i = 9; i < 12; ++i)
|
||||
state_server.state_cov(i, i) = acc_bias_cov;
|
||||
for (int i = 15; i < 18; ++i)
|
||||
state_server.state_cov(i, i) = extrinsic_rotation_cov;
|
||||
for (int i = 18; i < 21; ++i)
|
||||
state_server.state_cov(i, i) = extrinsic_translation_cov;
|
||||
|
||||
// Transformation offsets between the frames involved.
|
||||
Transform imuCam = imuLocalTransform.inverse() * model.localTransform();
|
||||
Eigen::Isometry3d T_imu_cam0(imuCam.toEigen4d());
|
||||
Eigen::Isometry3d T_cam0_imu = T_imu_cam0.inverse();
|
||||
|
||||
state_server.imu_state.R_imu_cam0 = T_cam0_imu.linear().transpose();
|
||||
state_server.imu_state.t_cam0_imu = T_cam0_imu.translation();
|
||||
Transform cam0cam1;
|
||||
if(rectified)
|
||||
{
|
||||
cam0cam1 = Transform(1, 0, 0, 0,
|
||||
0, 1, 0, model.baseline(),
|
||||
0, 0, 1, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
cam0cam1 = model.stereoTransform();
|
||||
}
|
||||
msckf_vio::CAMState::T_cam0_cam1 = cam0cam1.toEigen3d().matrix();
|
||||
msckf_vio::IMUState::T_imu_body = imuLocalTransform.toEigen3d().matrix();
|
||||
|
||||
// Maximum number of camera states to be stored
|
||||
nh.param<int>("max_cam_state_size", max_cam_state_size, 30);
|
||||
|
||||
UINFO("===========================================");
|
||||
UINFO("fixed frame id: %s", fixed_frame_id.c_str());
|
||||
UINFO("child frame id: %s", child_frame_id.c_str());
|
||||
UINFO("publish tf: %d", publish_tf);
|
||||
UINFO("frame rate: %f", frame_rate);
|
||||
UINFO("position std threshold: %f", position_std_threshold);
|
||||
UINFO("Keyframe rotation threshold: %f", rotation_threshold);
|
||||
UINFO("Keyframe translation threshold: %f", translation_threshold);
|
||||
UINFO("Keyframe tracking rate threshold: %f", tracking_rate_threshold);
|
||||
UINFO("gyro noise: %.10f", msckf_vio::IMUState::gyro_noise);
|
||||
UINFO("gyro bias noise: %.10f", msckf_vio::IMUState::gyro_bias_noise);
|
||||
UINFO("acc noise: %.10f", msckf_vio::IMUState::acc_noise);
|
||||
UINFO("acc bias noise: %.10f", msckf_vio::IMUState::acc_bias_noise);
|
||||
UINFO("observation noise: %.10f", msckf_vio::Feature::observation_noise);
|
||||
UINFO("initial velocity: %f, %f, %f",
|
||||
state_server.imu_state.velocity(0),
|
||||
state_server.imu_state.velocity(1),
|
||||
state_server.imu_state.velocity(2));
|
||||
UINFO("initial gyro bias cov: %f", gyro_bias_cov);
|
||||
UINFO("initial acc bias cov: %f", acc_bias_cov);
|
||||
UINFO("initial velocity cov: %f", velocity_cov);
|
||||
UINFO("initial extrinsic rotation cov: %f",
|
||||
extrinsic_rotation_cov);
|
||||
UINFO("initial extrinsic translation cov: %f",
|
||||
extrinsic_translation_cov);
|
||||
|
||||
std::cout << T_imu_cam0.linear() << std::endl;
|
||||
std::cout << T_imu_cam0.translation().transpose() << std::endl;
|
||||
|
||||
UINFO("max camera state #: %d", max_cam_state_size);
|
||||
UINFO("===========================================");
|
||||
|
||||
//if (!loadParameters()) return false;
|
||||
//UINFO("Finish loading ROS parameters...");
|
||||
|
||||
// Initialize state server
|
||||
state_server.continuous_noise_cov =
|
||||
Eigen::Matrix<double, 12, 12>::Zero();
|
||||
state_server.continuous_noise_cov.block<3, 3>(0, 0) =
|
||||
Eigen::Matrix3d::Identity()*msckf_vio::IMUState::gyro_noise;
|
||||
state_server.continuous_noise_cov.block<3, 3>(3, 3) =
|
||||
Eigen::Matrix3d::Identity()*msckf_vio::IMUState::gyro_bias_noise;
|
||||
state_server.continuous_noise_cov.block<3, 3>(6, 6) =
|
||||
Eigen::Matrix3d::Identity()*msckf_vio::IMUState::acc_noise;
|
||||
state_server.continuous_noise_cov.block<3, 3>(9, 9) =
|
||||
Eigen::Matrix3d::Identity()*msckf_vio::IMUState::acc_bias_noise;
|
||||
|
||||
// Initialize the chi squared test table with confidence
|
||||
// level 0.95.
|
||||
for (int i = 1; i < 100; ++i) {
|
||||
boost::math::chi_squared chi_squared_dist(i);
|
||||
chi_squared_test_table[i] =
|
||||
boost::math::quantile(chi_squared_dist, 0.05);
|
||||
}
|
||||
|
||||
// if (!createRosIO()) return false;
|
||||
//UINFO("Finish creating ROS IO...");
|
||||
}
|
||||
virtual ~MsckfVioNoROS() {}
|
||||
|
||||
|
||||
nav_msgs::Odometry featureCallback2(
|
||||
const msckf_vio::CameraMeasurementConstPtr& msg) {
|
||||
|
||||
nav_msgs::Odometry odom;
|
||||
|
||||
// Return if the gravity vector has not been set.
|
||||
if (!is_gravity_set) return odom;
|
||||
|
||||
// Start the system if the first image is received.
|
||||
// The frame where the first image is received will be
|
||||
// the origin.
|
||||
if (is_first_img) {
|
||||
is_first_img = false;
|
||||
state_server.imu_state.time = msg->header.stamp.toSec();
|
||||
}
|
||||
|
||||
//static double max_processing_time = 0.0;
|
||||
static int critical_time_cntr = 0;
|
||||
double processing_start_time = ros::Time::now().toSec();
|
||||
|
||||
// Propogate the IMU state.
|
||||
// that are received before the image msg.
|
||||
ros::Time start_time = ros::Time::now();
|
||||
batchImuProcessing(msg->header.stamp.toSec());
|
||||
//double imu_processing_time = (
|
||||
// ros::Time::now()-start_time).toSec();
|
||||
|
||||
// Augment the state vector.
|
||||
start_time = ros::Time::now();
|
||||
stateAugmentation(msg->header.stamp.toSec());
|
||||
//double state_augmentation_time = (
|
||||
// ros::Time::now()-start_time).toSec();
|
||||
|
||||
// Add new observations for existing features or new
|
||||
// features in the map server.
|
||||
start_time = ros::Time::now();
|
||||
addFeatureObservations(msg);
|
||||
//double add_observations_time = (
|
||||
// ros::Time::now()-start_time).toSec();
|
||||
|
||||
// Perform measurement update if necessary.
|
||||
start_time = ros::Time::now();
|
||||
removeLostFeatures();
|
||||
double remove_lost_features_time = (
|
||||
ros::Time::now()-start_time).toSec();
|
||||
|
||||
start_time = ros::Time::now();
|
||||
pruneCamStateBuffer();
|
||||
double prune_cam_states_time = (
|
||||
ros::Time::now()-start_time).toSec();
|
||||
|
||||
// Publish the odometry.
|
||||
start_time = ros::Time::now();
|
||||
odom = publish(msg->header.stamp);
|
||||
//double publish_time = (
|
||||
// ros::Time::now()-start_time).toSec();
|
||||
|
||||
// Reset the system if necessary.
|
||||
onlineReset();
|
||||
|
||||
double processing_end_time = ros::Time::now().toSec();
|
||||
double processing_time =
|
||||
processing_end_time - processing_start_time;
|
||||
if (processing_time > 1.0/frame_rate) {
|
||||
++critical_time_cntr;
|
||||
UINFO("\033[1;31mTotal processing time %f/%d...\033[0m",
|
||||
processing_time, critical_time_cntr);
|
||||
//printf("IMU processing time: %f/%f\n",
|
||||
// imu_processing_time, imu_processing_time/processing_time);
|
||||
//printf("State augmentation time: %f/%f\n",
|
||||
// state_augmentation_time, state_augmentation_time/processing_time);
|
||||
//printf("Add observations time: %f/%f\n",
|
||||
// add_observations_time, add_observations_time/processing_time);
|
||||
printf("Remove lost features time: %f/%f\n",
|
||||
remove_lost_features_time, remove_lost_features_time/processing_time);
|
||||
printf("Remove camera states time: %f/%f\n",
|
||||
prune_cam_states_time, prune_cam_states_time/processing_time);
|
||||
//printf("Publish time: %f/%f\n",
|
||||
// publish_time, publish_time/processing_time);
|
||||
}
|
||||
|
||||
return odom;
|
||||
}
|
||||
|
||||
nav_msgs::Odometry publish(const ros::Time& time) {
|
||||
|
||||
// Convert the IMU frame to the body frame.
|
||||
const msckf_vio::IMUState& imu_state = state_server.imu_state;
|
||||
Eigen::Isometry3d T_i_w = Eigen::Isometry3d::Identity();
|
||||
T_i_w.linear() = msckf_vio::quaternionToRotation(imu_state.orientation).transpose();
|
||||
T_i_w.translation() = imu_state.position;
|
||||
|
||||
Eigen::Isometry3d T_b_w = msckf_vio::IMUState::T_imu_body * T_i_w *
|
||||
msckf_vio::IMUState::T_imu_body.inverse();
|
||||
Eigen::Vector3d body_velocity =
|
||||
msckf_vio::IMUState::T_imu_body.linear() * imu_state.velocity;
|
||||
|
||||
// Publish tf
|
||||
/*if (publish_tf) {
|
||||
tf::Transform T_b_w_tf;
|
||||
tf::transformEigenToTF(T_b_w, T_b_w_tf);
|
||||
tf_pub.sendTransform(tf::StampedTransform(
|
||||
T_b_w_tf, time, fixed_frame_id, child_frame_id));
|
||||
}*/
|
||||
|
||||
// Publish the odometry
|
||||
nav_msgs::Odometry odom_msg;
|
||||
odom_msg.header.stamp = time;
|
||||
odom_msg.header.frame_id = fixed_frame_id;
|
||||
odom_msg.child_frame_id = child_frame_id;
|
||||
|
||||
tf::poseEigenToMsg(T_b_w, odom_msg.pose.pose);
|
||||
tf::vectorEigenToMsg(body_velocity, odom_msg.twist.twist.linear);
|
||||
|
||||
// Convert the covariance.
|
||||
Eigen::Matrix3d P_oo = state_server.state_cov.block<3, 3>(0, 0);
|
||||
Eigen::Matrix3d P_op = state_server.state_cov.block<3, 3>(0, 12);
|
||||
Eigen::Matrix3d P_po = state_server.state_cov.block<3, 3>(12, 0);
|
||||
Eigen::Matrix3d P_pp = state_server.state_cov.block<3, 3>(12, 12);
|
||||
Eigen::Matrix<double, 6, 6> P_imu_pose = Eigen::Matrix<double, 6, 6>::Zero();
|
||||
P_imu_pose << P_pp, P_po, P_op, P_oo;
|
||||
|
||||
Eigen::Matrix<double, 6, 6> H_pose = Eigen::Matrix<double, 6, 6>::Zero();
|
||||
H_pose.block<3, 3>(0, 0) = msckf_vio::IMUState::T_imu_body.linear();
|
||||
H_pose.block<3, 3>(3, 3) = msckf_vio::IMUState::T_imu_body.linear();
|
||||
Eigen::Matrix<double, 6, 6> P_body_pose = H_pose *
|
||||
P_imu_pose * H_pose.transpose();
|
||||
|
||||
for (int i = 0; i < 6; ++i)
|
||||
for (int j = 0; j < 6; ++j)
|
||||
odom_msg.pose.covariance[6*i+j] = P_body_pose(i, j);
|
||||
|
||||
// Construct the covariance for the velocity.
|
||||
Eigen::Matrix3d P_imu_vel = state_server.state_cov.block<3, 3>(6, 6);
|
||||
Eigen::Matrix3d H_vel = msckf_vio::IMUState::T_imu_body.linear();
|
||||
Eigen::Matrix3d P_body_vel = H_vel * P_imu_vel * H_vel.transpose();
|
||||
for (int i = 0; i < 3; ++i)
|
||||
for (int j = 0; j < 3; ++j)
|
||||
odom_msg.twist.covariance[i*6+j] = P_body_vel(i, j);
|
||||
|
||||
// odom_pub.publish(odom_msg);
|
||||
|
||||
// Publish the 3D positions of the features that
|
||||
// has been initialized.
|
||||
/*pcl::PointCloud<pcl::PointXYZ>::Ptr feature_msg_ptr(
|
||||
new pcl::PointCloud<pcl::PointXYZ>());
|
||||
feature_msg_ptr->header.frame_id = fixed_frame_id;
|
||||
feature_msg_ptr->height = 1;
|
||||
for (const auto& item : map_server) {
|
||||
const auto& feature = item.second;
|
||||
if (feature.is_initialized) {
|
||||
Vector3d feature_position =
|
||||
IMUState::T_imu_body.linear() * feature.position;
|
||||
feature_msg_ptr->points.push_back(pcl::PointXYZ(
|
||||
feature_position(0), feature_position(1), feature_position(2)));
|
||||
}
|
||||
}
|
||||
feature_msg_ptr->width = feature_msg_ptr->points.size();
|
||||
|
||||
feature_pub.publish(feature_msg_ptr);*/
|
||||
|
||||
return odom_msg;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
OdometryMSCKF::OdometryMSCKF(const ParametersMap & parameters) :
|
||||
Odometry(parameters)
|
||||
#ifdef RTABMAP_MSCKF_VIO
|
||||
,
|
||||
imageProcessor_(0),
|
||||
msckf_(0),
|
||||
parameters_(parameters)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
OdometryMSCKF::~OdometryMSCKF()
|
||||
{
|
||||
UDEBUG("");
|
||||
#ifdef RTABMAP_MSCKF_VIO
|
||||
if(imageProcessor_)
|
||||
{
|
||||
delete imageProcessor_;
|
||||
}
|
||||
if(msckf_)
|
||||
{
|
||||
delete msckf_;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void OdometryMSCKF::reset(const Transform & initialPose)
|
||||
{
|
||||
Odometry::reset(initialPose);
|
||||
#ifdef RTABMAP_MSCKF_VIO
|
||||
if(imageProcessor_)
|
||||
{
|
||||
delete imageProcessor_;
|
||||
imageProcessor_ = 0;
|
||||
}
|
||||
if(msckf_)
|
||||
{
|
||||
delete msckf_;
|
||||
msckf_ = 0;
|
||||
}
|
||||
lastImu_ = IMU();
|
||||
#endif
|
||||
}
|
||||
|
||||
// return not null transform if odometry is correctly computed
|
||||
Transform OdometryMSCKF::computeTransform(
|
||||
SensorData & data,
|
||||
const Transform & guess,
|
||||
OdometryInfo * info)
|
||||
{
|
||||
UDEBUG("");
|
||||
Transform t;
|
||||
|
||||
#ifdef RTABMAP_MSCKF_VIO
|
||||
UTimer timer;
|
||||
|
||||
if(!data.imu().empty())
|
||||
{
|
||||
UDEBUG("IMU update stamp=%f acc=%f %f %f gyr=%f %f %f", data.stamp(),
|
||||
data.imu().linearAcceleration()[0],
|
||||
data.imu().linearAcceleration()[1],
|
||||
data.imu().linearAcceleration()[2],
|
||||
data.imu().angularVelocity()[0],
|
||||
data.imu().angularVelocity()[1],
|
||||
data.imu().angularVelocity()[2]);
|
||||
if(imageProcessor_ && msckf_)
|
||||
{
|
||||
sensor_msgs::ImuPtr msg(new sensor_msgs::Imu);
|
||||
msg->angular_velocity.x = data.imu().angularVelocity()[0];
|
||||
msg->angular_velocity.y = data.imu().angularVelocity()[1];
|
||||
msg->angular_velocity.z = data.imu().angularVelocity()[2];
|
||||
msg->linear_acceleration.x = data.imu().linearAcceleration()[0];
|
||||
msg->linear_acceleration.y = data.imu().linearAcceleration()[1];
|
||||
msg->linear_acceleration.z = data.imu().linearAcceleration()[2];
|
||||
msg->header.stamp.fromSec(data.stamp());
|
||||
imageProcessor_->imuCallback(msg);
|
||||
msckf_->imuCallback(msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
UWARN("Ignoring IMU, waiting for an image to initialize...");
|
||||
lastImu_ = data.imu();
|
||||
}
|
||||
}
|
||||
|
||||
if(!data.imageRaw().empty() && !data.rightRaw().empty())
|
||||
{
|
||||
if(data.stereoCameraModel().isValidForProjection())
|
||||
{
|
||||
if(msckf_ == 0)
|
||||
{
|
||||
UDEBUG("Initialization");
|
||||
if(lastImu_.empty())
|
||||
{
|
||||
UWARN("Ignoring Image, waiting for imu to initialize...");
|
||||
return t;
|
||||
}
|
||||
imageProcessor_ = new ImageProcessorNoROS(
|
||||
parameters_,
|
||||
lastImu_.localTransform(),
|
||||
data.stereoCameraModel(),
|
||||
this->imagesAlreadyRectified());
|
||||
msckf_ = new MsckfVioNoROS(
|
||||
parameters_,
|
||||
lastImu_.localTransform(),
|
||||
data.stereoCameraModel(),
|
||||
this->imagesAlreadyRectified());
|
||||
}
|
||||
|
||||
// Convert to ROS
|
||||
cv_bridge::CvImage cam0;
|
||||
cv_bridge::CvImage cam1;
|
||||
cam0.header.stamp.fromSec(data.stamp());
|
||||
cam1.header.stamp.fromSec(data.stamp());
|
||||
|
||||
if(data.imageRaw().type() == CV_8UC3)
|
||||
{
|
||||
cv::cvtColor(data.imageRaw(), cam0.image, CV_BGR2GRAY);
|
||||
}
|
||||
else
|
||||
{
|
||||
cam0.image = data.imageRaw();
|
||||
}
|
||||
if(data.rightRaw().type() == CV_8UC3)
|
||||
{
|
||||
cv::cvtColor(data.rightRaw(), cam1.image, CV_BGR2GRAY);
|
||||
}
|
||||
else
|
||||
{
|
||||
cam1.image = data.rightRaw();
|
||||
}
|
||||
sensor_msgs::ImagePtr cam0Msg(new sensor_msgs::Image);
|
||||
sensor_msgs::ImagePtr cam1Msg(new sensor_msgs::Image);
|
||||
cam0.toImageMsg(*cam0Msg);
|
||||
cam1.toImageMsg(*cam1Msg);
|
||||
|
||||
//msckf_vio::FeatureMeasurementPtr measurementsConst = measurements;
|
||||
nav_msgs::Odometry odom = msckf_->featureCallback2(
|
||||
imageProcessor_->stereoCallback2(cam0Msg, cam1Msg));
|
||||
|
||||
Transform p = Transform(
|
||||
odom.pose.pose.position.x,
|
||||
odom.pose.pose.position.y,
|
||||
odom.pose.pose.position.z,
|
||||
odom.pose.pose.orientation.x,
|
||||
odom.pose.pose.orientation.y,
|
||||
odom.pose.pose.orientation.z,
|
||||
odom.pose.pose.orientation.w);
|
||||
|
||||
if(!p.isNull())
|
||||
{
|
||||
// make it incremental
|
||||
t = this->getPose().inverse()*p;
|
||||
|
||||
if(info)
|
||||
{
|
||||
info->reg.covariance = cv::Mat(6,6,CV_64FC1, odom.twist.covariance.elems).clone();
|
||||
}
|
||||
}
|
||||
|
||||
UINFO("Odom update time = %fs p=%s", timer.elapsed(), p.prettyPrint().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
UERROR("RTAB-Map is not built with MSCKF_VIO support! Select another visual odometry approach.");
|
||||
#endif
|
||||
return t;
|
||||
}
|
||||
|
||||
} // namespace rtabmap
|
||||
@@ -679,6 +679,18 @@ ParametersMap Parameters::parseArguments(int argc, char * argv[], bool onlyParam
|
||||
{
|
||||
ignore = true;
|
||||
}
|
||||
#endif
|
||||
#ifndef RTABMAP_LOAM
|
||||
if(group.compare("OdomLOAM") == 0)
|
||||
{
|
||||
ignore = true;
|
||||
}
|
||||
#endif
|
||||
#ifndef RTABMAP_MSCKF_VIO
|
||||
if(group.compare("OdomMSCKF") == 0)
|
||||
{
|
||||
ignore = true;
|
||||
}
|
||||
#endif
|
||||
if(!ignore)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user