gps fusion implemented
This commit is contained in:
122
global_fusion/src/Factors.h
Normal file
122
global_fusion/src/Factors.h
Normal file
@@ -0,0 +1,122 @@
|
||||
//
|
||||
// Created by admin1 on 25.08.2022.
|
||||
//
|
||||
|
||||
/*******************************************************
|
||||
* Copyright (C) 2019, Aerial Robotics Group, Hong Kong University of Science and Technology
|
||||
*
|
||||
* This file is part of VINS.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
*
|
||||
* Author: Qin Tong (qintonguav@gmail.com)
|
||||
*******************************************************/
|
||||
|
||||
#ifndef GLOBAL_FUSION_FACTORS_H
|
||||
#define GLOBAL_FUSION_FACTORS_H
|
||||
|
||||
#endif //GLOBAL_FUSION_FACTORS_H
|
||||
|
||||
#pragma once
|
||||
#include <ceres/ceres.h>
|
||||
#include <ceres/rotation.h>
|
||||
|
||||
template <typename T> inline
|
||||
void QuaternionInverse(const T q[4], T q_inverse[4])
|
||||
{
|
||||
q_inverse[0] = q[0];
|
||||
q_inverse[1] = -q[1];
|
||||
q_inverse[2] = -q[2];
|
||||
q_inverse[3] = -q[3];
|
||||
};
|
||||
|
||||
struct TError
|
||||
{
|
||||
TError(double t_x, double t_y, double t_z, double var)
|
||||
:t_x(t_x), t_y(t_y), t_z(t_z), var(var){}
|
||||
|
||||
template <typename T>
|
||||
bool operator()(const T* tj, T* residuals) const
|
||||
{
|
||||
residuals[0] = (tj[0] - T(t_x)) / T(var);
|
||||
residuals[1] = (tj[1] - T(t_y)) / T(var);
|
||||
residuals[2] = (tj[2] - T(t_z)) / T(var);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static ceres::CostFunction* Create(const double t_x, const double t_y, const double t_z, const double var)
|
||||
{
|
||||
return (new ceres::AutoDiffCostFunction<
|
||||
TError, 3, 3>(
|
||||
new TError(t_x, t_y, t_z, var)));
|
||||
}
|
||||
|
||||
double t_x, t_y, t_z, var;
|
||||
|
||||
};
|
||||
|
||||
struct RelativeRTError
|
||||
{
|
||||
RelativeRTError(double t_x, double t_y, double t_z,
|
||||
double q_w, double q_x, double q_y, double q_z,
|
||||
double t_var, double q_var)
|
||||
:t_x(t_x), t_y(t_y), t_z(t_z),
|
||||
q_w(q_w), q_x(q_x), q_y(q_y), q_z(q_z),
|
||||
t_var(t_var), q_var(q_var){}
|
||||
|
||||
template <typename T>
|
||||
bool operator()(const T* const w_q_i, const T* ti, const T* w_q_j, const T* tj, T* residuals) const
|
||||
{
|
||||
T t_w_ij[3];
|
||||
t_w_ij[0] = tj[0] - ti[0];
|
||||
t_w_ij[1] = tj[1] - ti[1];
|
||||
t_w_ij[2] = tj[2] - ti[2];
|
||||
|
||||
T i_q_w[4];
|
||||
QuaternionInverse(w_q_i, i_q_w);
|
||||
|
||||
T t_i_ij[3];
|
||||
ceres::QuaternionRotatePoint(i_q_w, t_w_ij, t_i_ij);
|
||||
|
||||
residuals[0] = (t_i_ij[0] - T(t_x)) / T(t_var);
|
||||
residuals[1] = (t_i_ij[1] - T(t_y)) / T(t_var);
|
||||
residuals[2] = (t_i_ij[2] - T(t_z)) / T(t_var);
|
||||
|
||||
T relative_q[4];
|
||||
relative_q[0] = T(q_w);
|
||||
relative_q[1] = T(q_x);
|
||||
relative_q[2] = T(q_y);
|
||||
relative_q[3] = T(q_z);
|
||||
|
||||
T q_i_j[4];
|
||||
ceres::QuaternionProduct(i_q_w, w_q_j, q_i_j);
|
||||
|
||||
T relative_q_inv[4];
|
||||
QuaternionInverse(relative_q, relative_q_inv);
|
||||
|
||||
T error_q[4];
|
||||
ceres::QuaternionProduct(relative_q_inv, q_i_j, error_q);
|
||||
|
||||
residuals[3] = T(2) * error_q[1] / T(q_var);
|
||||
residuals[4] = T(2) * error_q[2] / T(q_var);
|
||||
residuals[5] = T(2) * error_q[3] / T(q_var);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static ceres::CostFunction* Create(const double t_x, const double t_y, const double t_z,
|
||||
const double q_w, const double q_x, const double q_y, const double q_z,
|
||||
const double t_var, const double q_var)
|
||||
{
|
||||
return (new ceres::AutoDiffCostFunction<
|
||||
RelativeRTError, 6, 4, 3, 4, 3>(
|
||||
new RelativeRTError(t_x, t_y, t_z, q_w, q_x, q_y, q_z, t_var, q_var)));
|
||||
}
|
||||
|
||||
double t_x, t_y, t_z, t_norm;
|
||||
double q_w, q_x, q_y, q_z;
|
||||
double t_var, q_var;
|
||||
|
||||
};
|
||||
188
global_fusion/src/KITTIGPSTest.cpp
Normal file
188
global_fusion/src/KITTIGPSTest.cpp
Normal file
@@ -0,0 +1,188 @@
|
||||
//
|
||||
// Created by admin1 on 27.08.2022.
|
||||
//
|
||||
|
||||
/*******************************************************
|
||||
* Copyright (C) 2019, Aerial Robotics Group, Hong Kong University of Science and Technology
|
||||
*
|
||||
* This file is part of VINS.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
*
|
||||
* Author: Qin Tong (qintonguav@gmail.com)
|
||||
*******************************************************/
|
||||
|
||||
#include <iostream>
|
||||
#include <stdio.h>
|
||||
#include <cmath>
|
||||
#include <string>
|
||||
#include <ros/ros.h>
|
||||
#include <sensor_msgs/NavSatFix.h>
|
||||
|
||||
|
||||
using namespace std;
|
||||
|
||||
ros::Publisher pubGPS;
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
ros::init(argc, argv, "kitti_glob");
|
||||
ros::NodeHandle n("~");
|
||||
ros::console::set_logger_level(ROSCONSOLE_DEFAULT_NAME, ros::console::levels::Info);
|
||||
|
||||
pubGPS = n.advertise<sensor_msgs::NavSatFix>("/gps", 1000);
|
||||
|
||||
if(argc != 3)
|
||||
{
|
||||
printf("please intput: rosrun vins kitti_gps_test [config file] [data folder] \n"
|
||||
"for example: rosrun vins kitti_gps_test "
|
||||
"~/catkin_ws/src/VINS-Fusion/config/kitti_raw/kitti_10_03_config.yaml "
|
||||
"/media/tony-ws1/disk_D/kitti/2011_10_03/2011_10_03_drive_0027_sync/ \n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
string config_file = argv[1];
|
||||
printf("config_file: %s\n", argv[1]);
|
||||
string sequence = argv[2];
|
||||
printf("read sequence: %s\n", argv[2]);
|
||||
string dataPath = sequence + "/";
|
||||
|
||||
// load image list
|
||||
FILE* file;
|
||||
file = std::fopen((dataPath + "image_00/timestamps.txt").c_str() , "r");
|
||||
if(file == NULL){
|
||||
printf("cannot find file: %simage_00/timestamps.txt \n", dataPath.c_str());
|
||||
ROS_BREAK();
|
||||
return 0;
|
||||
}
|
||||
vector<double> imageTimeList;
|
||||
int year, month, day;
|
||||
int hour, minute;
|
||||
double second;
|
||||
while (fscanf(file, "%d-%d-%d %d:%d:%lf", &year, &month, &day, &hour, &minute, &second) != EOF)
|
||||
{
|
||||
//printf("%lf\n", second);
|
||||
imageTimeList.push_back(hour * 60 * 60 + minute * 60 + second);
|
||||
}
|
||||
std::fclose(file);
|
||||
|
||||
// load gps list
|
||||
vector<double> GPSTimeList;
|
||||
{
|
||||
FILE* file;
|
||||
file = std::fopen((dataPath + "oxts/timestamps.txt").c_str() , "r");
|
||||
if(file == NULL){
|
||||
printf("cannot find file: %soxts/timestamps.txt \n", dataPath.c_str());
|
||||
ROS_BREAK();
|
||||
return 0;
|
||||
}
|
||||
int year, month, day;
|
||||
int hour, minute;
|
||||
double second;
|
||||
while (fscanf(file, "%d-%d-%d %d:%d:%lf", &year, &month, &day, &hour, &minute, &second) != EOF)
|
||||
{
|
||||
//printf("%lf\n", second);
|
||||
GPSTimeList.push_back(hour * 60 * 60 + minute * 60 + second);
|
||||
}
|
||||
std::fclose(file);
|
||||
}
|
||||
|
||||
readParameters(config_file);
|
||||
estimator.setParameter();
|
||||
registerPub(n);
|
||||
|
||||
FILE* outFile;
|
||||
outFile = fopen((OUTPUT_FOLDER + "/vio.txt").c_str(),"w");
|
||||
if(outFile == NULL)
|
||||
printf("Output path dosen't exist: %s\n", OUTPUT_FOLDER.c_str());
|
||||
string leftImagePath, rightImagePath;
|
||||
cv::Mat imLeft, imRight;
|
||||
double baseTime;
|
||||
|
||||
for (size_t i = 0; i < imageTimeList.size(); i++)
|
||||
{
|
||||
if(ros::ok())
|
||||
{
|
||||
if(imageTimeList[0] < GPSTimeList[0])
|
||||
baseTime = imageTimeList[0];
|
||||
else
|
||||
baseTime = GPSTimeList[0];
|
||||
|
||||
//printf("base time is %f\n", baseTime);
|
||||
printf("process image %d\n", (int)i);
|
||||
stringstream ss;
|
||||
ss << setfill('0') << setw(10) << i;
|
||||
leftImagePath = dataPath + "image_00/data/" + ss.str() + ".png";
|
||||
rightImagePath = dataPath + "image_01/data/" + ss.str() + ".png";
|
||||
printf("%s\n", leftImagePath.c_str() );
|
||||
printf("%s\n", rightImagePath.c_str() );
|
||||
|
||||
imLeft = cv::imread(leftImagePath, cv::IMREAD_GRAYSCALE );
|
||||
imRight = cv::imread(rightImagePath, cv::IMREAD_GRAYSCALE );
|
||||
|
||||
double imgTime = imageTimeList[i] - baseTime;
|
||||
|
||||
// load gps
|
||||
FILE* GPSFile;
|
||||
string GPSFilePath = dataPath + "oxts/data/" + ss.str() + ".txt";
|
||||
GPSFile = std::fopen(GPSFilePath.c_str() , "r");
|
||||
if(GPSFile == NULL){
|
||||
printf("cannot find file: %s\n", GPSFilePath.c_str());
|
||||
ROS_BREAK();
|
||||
return 0;
|
||||
}
|
||||
double lat, lon, alt, roll, pitch, yaw;
|
||||
double vn, ve, vf, vl, vu;
|
||||
double ax, ay, az, af, al, au;
|
||||
double wx, wy, wz, wf, wl, wu;
|
||||
double pos_accuracy, vel_accuracy;
|
||||
double navstat, numsats;
|
||||
double velmode, orimode;
|
||||
|
||||
fscanf(GPSFile, "%lf %lf %lf %lf %lf %lf ", &lat, &lon, &alt, &roll, &pitch, &yaw);
|
||||
//printf("lat:%lf lon:%lf alt:%lf roll:%lf pitch:%lf yaw:%lf \n", lat, lon, alt, roll, pitch, yaw);
|
||||
fscanf(GPSFile, "%lf %lf %lf %lf %lf ", &vn, &ve, &vf, &vl, &vu);
|
||||
//printf("vn:%lf ve:%lf vf:%lf vl:%lf vu:%lf \n", vn, ve, vf, vl, vu);
|
||||
fscanf(GPSFile, "%lf %lf %lf %lf %lf %lf ", &ax, &ay, &az, &af, &al, &au);
|
||||
//printf("ax:%lf ay:%lf az:%lf af:%lf al:%lf au:%lf\n", ax, ay, az, af, al, au);
|
||||
fscanf(GPSFile, "%lf %lf %lf %lf %lf %lf ", &wx, &wy, &wz, &wf, &wl, &wu);
|
||||
//printf("wx:%lf wy:%lf wz:%lf wf:%lf wl:%lf wu:%lf\n", wx, wy, wz, wf, wl, wu);
|
||||
fscanf(GPSFile, "%lf %lf %lf %lf %lf %lf ", &pos_accuracy, &vel_accuracy, &navstat, &numsats, &velmode, &orimode);
|
||||
//printf("pos_accuracy:%lf vel_accuracy:%lf navstat:%lf numsats:%lf velmode:%lf orimode:%lf\n",
|
||||
// pos_accuracy, vel_accuracy, navstat, numsats, velmode, orimode);
|
||||
|
||||
std::fclose(GPSFile);
|
||||
|
||||
sensor_msgs::NavSatFix gps_position;
|
||||
gps_position.header.frame_id = "NED";
|
||||
gps_position.header.stamp = ros::Time(imgTime);
|
||||
gps_position.status.status = navstat;
|
||||
gps_position.status.service = numsats;
|
||||
gps_position.latitude = lat;
|
||||
gps_position.longitude = lon;
|
||||
gps_position.altitude = alt;
|
||||
gps_position.position_covariance[0] = pos_accuracy;
|
||||
//printf("pos_accuracy %f \n", pos_accuracy);
|
||||
pubGPS.publish(gps_position);
|
||||
|
||||
estimator.inputImage(imgTime, imLeft, imRight);
|
||||
|
||||
Eigen::Matrix<double, 4, 4> pose;
|
||||
estimator.getPoseInWorldFrame(pose);
|
||||
if(outFile != NULL)
|
||||
fprintf (outFile, "%f %f %f %f %f %f %f %f %f %f %f %f \n",pose(0,0), pose(0,1), pose(0,2),pose(0,3),
|
||||
pose(1,0), pose(1,1), pose(1,2),pose(1,3),
|
||||
pose(2,0), pose(2,1), pose(2,2),pose(2,3));
|
||||
|
||||
// cv::imshow("leftImage", imLeft);
|
||||
// cv::imshow("rightImage", imRight);
|
||||
// cv::waitKey(2);
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
if(outFile != NULL)
|
||||
fclose (outFile);
|
||||
return 0;
|
||||
}
|
||||
255
global_fusion/src/globalOpt.cpp
Normal file
255
global_fusion/src/globalOpt.cpp
Normal file
@@ -0,0 +1,255 @@
|
||||
//
|
||||
// Created by admin1 on 25.08.2022.
|
||||
//
|
||||
|
||||
/*******************************************************
|
||||
* Copyright (C) 2019, Aerial Robotics Group, Hong Kong University of Science and Technology
|
||||
*
|
||||
* This file is part of VINS.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
*
|
||||
* Author: Qin Tong (qintonguav@gmail.com)
|
||||
*******************************************************/
|
||||
|
||||
#include "globalOpt.h"
|
||||
#include "Factors.h"
|
||||
|
||||
GlobalOptimization::GlobalOptimization() {
|
||||
initGPS = false;
|
||||
newGPS = false;
|
||||
// I suppose the matrix which transforms the gps coordinates to vio (or reverse)
|
||||
WGPS_T_WVIO = Eigen::Matrix4d::Identity();
|
||||
threadOpt = std::thread(&GlobalOptimization::optimize, this);
|
||||
}
|
||||
|
||||
GlobalOptimization::~GlobalOptimization() {
|
||||
threadOpt.detach();
|
||||
}
|
||||
|
||||
void GlobalOptimization::GPS2XYZ(double latitude, double longitude, double altitude, double* xyz){
|
||||
if (!initGPS)
|
||||
{
|
||||
geoConverter.Reset(latitude, longitude, altitude);
|
||||
initGPS = true;
|
||||
}
|
||||
geoConverter.Forward(latitude, longitude, altitude, xyz[0], xyz[1], xyz[2]);
|
||||
}
|
||||
|
||||
void GlobalOptimization::inputOdom(double t, Eigen::Vector3d OdomP, Eigen::Quaterniond OdomQ){
|
||||
mPoseMap.lock();
|
||||
vector<double> localPose{OdomP.x(), OdomP.y(), OdomP.z(),
|
||||
OdomQ.w(), OdomQ.x(), OdomQ.y(), OdomQ.z()};
|
||||
localPoseMap[t] = localPose;
|
||||
|
||||
Eigen::Quaterniond globalQ;
|
||||
globalQ = WGPS_T_WVIO.block<3, 3>(0, 0) * OdomQ;
|
||||
Eigen::Vector3d globalP = WGPS_T_WVIO.block<3, 3>(0, 0) * OdomP + WGPS_T_WVIO.block<3, 1>(0, 3);
|
||||
vector<double> globalPose{globalP.x(), globalP.y(), globalP.z(),
|
||||
globalQ.w(), globalQ.x(), globalQ.y(), globalQ.z()};
|
||||
globalPoseMap[t] = globalPose;
|
||||
lastP = globalP;
|
||||
lastQ = globalQ;
|
||||
|
||||
geometry_msgs::PoseStamped pose_stamped;
|
||||
pose_stamped.header.stamp = ros::Time(t);
|
||||
pose_stamped.header.frame_id = "gps_global";
|
||||
pose_stamped.pose.position.x = lastP.x();
|
||||
pose_stamped.pose.position.y = lastP.y();
|
||||
pose_stamped.pose.position.z = lastP.z();
|
||||
pose_stamped.pose.orientation.x = lastQ.x();
|
||||
pose_stamped.pose.orientation.y = lastQ.y();
|
||||
pose_stamped.pose.orientation.z = lastQ.z();
|
||||
pose_stamped.pose.orientation.w = lastQ.w();
|
||||
global_path.header = pose_stamped.header;
|
||||
global_path.poses.push_back(pose_stamped);
|
||||
|
||||
mPoseMap.unlock();
|
||||
};
|
||||
|
||||
void GlobalOptimization::getGlobalOdom(Eigen::Vector3d &odomP, Eigen::Quaterniond &odomQ){
|
||||
odomP = lastP;
|
||||
odomQ = lastQ;
|
||||
}
|
||||
|
||||
void GlobalOptimization::inputGPS(double t, double latitude, double longitude, double altitude, double posAccuracy){
|
||||
double xyz[3];
|
||||
GPS2XYZ(latitude, longitude, altitude, xyz);
|
||||
vector<double> tmp{xyz[0], xyz[1], xyz[2], posAccuracy};
|
||||
//printf("new gps: t: %f x: %f y: %f z:%f \n", t, tmp[0], tmp[1], tmp[2]);
|
||||
GPSPositionMap[t] = tmp;
|
||||
newGPS = true;
|
||||
}
|
||||
|
||||
void GlobalOptimization::optimize(){
|
||||
while(true){
|
||||
if (newGPS){
|
||||
newGPS = false;
|
||||
printf("global optimization\n");
|
||||
TicToc globalOptimizationTime;
|
||||
|
||||
ceres::Problem problem;
|
||||
ceres::Solver::Options options;
|
||||
options.linear_solver_type = ceres::SPARSE_NORMAL_CHOLESKY;
|
||||
options.max_num_iterations = 5;
|
||||
ceres::Solver::Summary summary;
|
||||
ceres::LossFunction *loss_function;
|
||||
loss_function = new ceres::HuberLoss(1.0);
|
||||
ceres::LocalParameterization* local_parameterization = new ceres::QuaternionParameterization();
|
||||
|
||||
//add param
|
||||
mPoseMap.lock();
|
||||
int length = localPoseMap.size();
|
||||
double t_array[length][3];
|
||||
double q_array[length][4];
|
||||
map<double, vector<double>>::iterator iter;
|
||||
iter = globalPoseMap.begin();
|
||||
for (int i = 0; i < length; i++, iter++){
|
||||
t_array[i][0] = iter->second[0];
|
||||
t_array[i][1] = iter->second[1];
|
||||
t_array[i][2] = iter->second[2];
|
||||
q_array[i][0] = iter->second[3];
|
||||
q_array[i][1] = iter->second[4];
|
||||
q_array[i][2] = iter->second[5];
|
||||
q_array[i][3] = iter->second[6];
|
||||
problem.AddParameterBlock(q_array[i], 4, local_parameterization);
|
||||
problem.AddParameterBlock(t_array[i], 3);
|
||||
}
|
||||
map<double, vector<double>>::iterator iterVIO, iterVIONext, iterGPS;
|
||||
int i = 0;
|
||||
for (iterVIO = localPoseMap.begin(); iterVIO != localPoseMap.end(); iterVIO++, i++)
|
||||
{
|
||||
//vio factor
|
||||
iterVIONext = iterVIO;
|
||||
iterVIONext++;
|
||||
if(iterVIONext != localPoseMap.end())
|
||||
{
|
||||
Eigen::Matrix4d wTi = Eigen::Matrix4d::Identity();
|
||||
Eigen::Matrix4d wTj = Eigen::Matrix4d::Identity();
|
||||
wTi.block<3, 3>(0, 0) = Eigen::Quaterniond(iterVIO->second[3], iterVIO->second[4],
|
||||
iterVIO->second[5], iterVIO->second[6]).toRotationMatrix();
|
||||
wTi.block<3, 1>(0, 3) = Eigen::Vector3d(iterVIO->second[0], iterVIO->second[1], iterVIO->second[2]);
|
||||
wTj.block<3, 3>(0, 0) = Eigen::Quaterniond(iterVIONext->second[3], iterVIONext->second[4],
|
||||
iterVIONext->second[5], iterVIONext->second[6]).toRotationMatrix();
|
||||
wTj.block<3, 1>(0, 3) = Eigen::Vector3d(iterVIONext->second[0], iterVIONext->second[1], iterVIONext->second[2]);
|
||||
Eigen::Matrix4d iTj = wTi.inverse() * wTj;
|
||||
Eigen::Quaterniond iQj;
|
||||
iQj = iTj.block<3, 3>(0, 0);
|
||||
Eigen::Vector3d iPj = iTj.block<3, 1>(0, 3);
|
||||
|
||||
ceres::CostFunction* vio_function = RelativeRTError::Create(iPj.x(), iPj.y(), iPj.z(),
|
||||
iQj.w(), iQj.x(), iQj.y(), iQj.z(),
|
||||
0.1, 0.01);
|
||||
problem.AddResidualBlock(vio_function, NULL, q_array[i], t_array[i], q_array[i+1], t_array[i+1]);
|
||||
|
||||
/*
|
||||
double **para = new double *[4];
|
||||
para[0] = q_array[i];
|
||||
para[1] = t_array[i];
|
||||
para[3] = q_array[i+1];
|
||||
para[4] = t_array[i+1];
|
||||
|
||||
double *tmp_r = new double[6];
|
||||
double **jaco = new double *[4];
|
||||
jaco[0] = new double[6 * 4];
|
||||
jaco[1] = new double[6 * 3];
|
||||
jaco[2] = new double[6 * 4];
|
||||
jaco[3] = new double[6 * 3];
|
||||
vio_function->Evaluate(para, tmp_r, jaco);
|
||||
|
||||
std::cout << Eigen::Map<Eigen::Matrix<double, 6, 1>>(tmp_r).transpose() << std::endl
|
||||
<< std::endl;
|
||||
std::cout << Eigen::Map<Eigen::Matrix<double, 6, 4, Eigen::RowMajor>>(jaco[0]) << std::endl
|
||||
<< std::endl;
|
||||
std::cout << Eigen::Map<Eigen::Matrix<double, 6, 3, Eigen::RowMajor>>(jaco[1]) << std::endl
|
||||
<< std::endl;
|
||||
std::cout << Eigen::Map<Eigen::Matrix<double, 6, 4, Eigen::RowMajor>>(jaco[2]) << std::endl
|
||||
<< std::endl;
|
||||
std::cout << Eigen::Map<Eigen::Matrix<double, 6, 3, Eigen::RowMajor>>(jaco[3]) << std::endl
|
||||
<< std::endl;
|
||||
*/
|
||||
|
||||
}
|
||||
//gps factor
|
||||
double t = iterVIO->first;
|
||||
iterGPS = GPSPositionMap.find(t);
|
||||
if (iterGPS != GPSPositionMap.end())
|
||||
{
|
||||
ceres::CostFunction* gps_function = TError::Create(iterGPS->second[0], iterGPS->second[1],
|
||||
iterGPS->second[2], iterGPS->second[3]);
|
||||
//printf("inverse weight %f \n", iterGPS->second[3]);
|
||||
problem.AddResidualBlock(gps_function, loss_function, t_array[i]);
|
||||
|
||||
/*
|
||||
double **para = new double *[1];
|
||||
para[0] = t_array[i];
|
||||
|
||||
double *tmp_r = new double[3];
|
||||
double **jaco = new double *[1];
|
||||
jaco[0] = new double[3 * 3];
|
||||
gps_function->Evaluate(para, tmp_r, jaco);
|
||||
|
||||
std::cout << Eigen::Map<Eigen::Matrix<double, 3, 1>>(tmp_r).transpose() << std::endl
|
||||
<< std::endl;
|
||||
std::cout << Eigen::Map<Eigen::Matrix<double, 3, 3, Eigen::RowMajor>>(jaco[0]) << std::endl
|
||||
<< std::endl;
|
||||
*/
|
||||
}
|
||||
|
||||
}
|
||||
//mPoseMap.unlock();
|
||||
ceres::Solve(options, &problem, &summary);
|
||||
//std::cout << summary.BriefReport() << "\n";
|
||||
|
||||
// update global pose
|
||||
//mPoseMap.lock();
|
||||
iter = globalPoseMap.begin();
|
||||
for (int i = 0; i < length; i++, iter++)
|
||||
{
|
||||
vector<double> globalPose{t_array[i][0], t_array[i][1], t_array[i][2],
|
||||
q_array[i][0], q_array[i][1], q_array[i][2], q_array[i][3]};
|
||||
iter->second = globalPose;
|
||||
if(i == length - 1)
|
||||
{
|
||||
Eigen::Matrix4d WVIO_T_body = Eigen::Matrix4d::Identity();
|
||||
Eigen::Matrix4d WGPS_T_body = Eigen::Matrix4d::Identity();
|
||||
double t = iter->first;
|
||||
WVIO_T_body.block<3, 3>(0, 0) = Eigen::Quaterniond(localPoseMap[t][3], localPoseMap[t][4],
|
||||
localPoseMap[t][5], localPoseMap[t][6]).toRotationMatrix();
|
||||
WVIO_T_body.block<3, 1>(0, 3) = Eigen::Vector3d(localPoseMap[t][0], localPoseMap[t][1], localPoseMap[t][2]);
|
||||
WGPS_T_body.block<3, 3>(0, 0) = Eigen::Quaterniond(globalPose[3], globalPose[4],
|
||||
globalPose[5], globalPose[6]).toRotationMatrix();
|
||||
WGPS_T_body.block<3, 1>(0, 3) = Eigen::Vector3d(globalPose[0], globalPose[1], globalPose[2]);
|
||||
WGPS_T_WVIO = WGPS_T_body * WVIO_T_body.inverse();
|
||||
}
|
||||
}
|
||||
updateGlobalPath();
|
||||
//printf("global time %f \n", globalOptimizationTime.toc());
|
||||
mPoseMap.unlock();
|
||||
}
|
||||
std::chrono::milliseconds dura(2000);
|
||||
std::this_thread::sleep_for(dura);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void GlobalOptimization::updateGlobalPath()
|
||||
{
|
||||
global_path.poses.clear();
|
||||
map<double, vector<double>>::iterator iter;
|
||||
for (iter = globalPoseMap.begin(); iter != globalPoseMap.end(); iter++)
|
||||
{
|
||||
geometry_msgs::PoseStamped pose_stamped;
|
||||
pose_stamped.header.stamp = ros::Time(iter->first);
|
||||
pose_stamped.header.frame_id = "gps_global";
|
||||
pose_stamped.pose.position.x = iter->second[0];
|
||||
pose_stamped.pose.position.y = iter->second[1];
|
||||
pose_stamped.pose.position.z = iter->second[2];
|
||||
pose_stamped.pose.orientation.w = iter->second[3];
|
||||
pose_stamped.pose.orientation.x = iter->second[4];
|
||||
pose_stamped.pose.orientation.y = iter->second[5];
|
||||
pose_stamped.pose.orientation.z = iter->second[6];
|
||||
global_path.poses.push_back(pose_stamped);
|
||||
}
|
||||
}
|
||||
66
global_fusion/src/globalOpt.h
Normal file
66
global_fusion/src/globalOpt.h
Normal file
@@ -0,0 +1,66 @@
|
||||
//
|
||||
// Created by admin1 on 25.08.2022.
|
||||
//
|
||||
|
||||
/*******************************************************
|
||||
* Copyright (C) 2019, Aerial Robotics Group, Hong Kong University of Science and Technology
|
||||
*
|
||||
* This file is part of VINS.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
*
|
||||
* Author: Qin Tong (qintonguav@gmail.com)
|
||||
*******************************************************/
|
||||
|
||||
#ifndef GLOBAL_FUSION_GLOBALOPT_H
|
||||
#define GLOBAL_FUSION_GLOBALOPT_H
|
||||
|
||||
#endif //GLOBAL_FUSION_GLOBALOPT_H
|
||||
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <eigen3/Eigen/Dense>
|
||||
#include <eigen3/Eigen/Geometry>
|
||||
#include <ceres/ceres.h>
|
||||
#include <nav_msgs/Odometry.h>
|
||||
#include <nav_msgs/Path.h>
|
||||
#include "LocalCartesian.hpp"
|
||||
#include "tic_toc.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
class GlobalOptimization
|
||||
{
|
||||
public:
|
||||
GlobalOptimization();
|
||||
~GlobalOptimization();
|
||||
void inputGPS(double t, double latitude, double longitude, double altitude, double posAccuracy);
|
||||
void inputOdom(double t, Eigen::Vector3d OdomP, Eigen::Quaterniond OdomQ);
|
||||
void getGlobalOdom(Eigen::Vector3d &odomP, Eigen::Quaterniond &domQ);
|
||||
nav_msgs::Path global_path;
|
||||
|
||||
private:
|
||||
// this function is interesting. I suppose it transforms from the gps coordinate system to the
|
||||
// local (vins) coordinate system. Which has the center at the start of the sequence by the way.
|
||||
void GPS2XYZ(double latitude, double longitude, double altitude, double* xyz);
|
||||
void optimize();
|
||||
void updateGlobalPath();
|
||||
|
||||
// format t, tx, ty, tz, qw, qx, qy, qz
|
||||
map<double, vector<double>> localPoseMap;
|
||||
map<double, vector<double>> globalPoseMap;
|
||||
map<double, vector<double>> GPSPositionMap;
|
||||
bool initGPS;
|
||||
bool newGPS;
|
||||
GeographicLib::LocalCartesian geoConverter;
|
||||
std::mutex mPoseMap;
|
||||
Eigen::Matrix4d WGPS_T_WVIO;
|
||||
Eigen::Vector3d lastP;
|
||||
Eigen::Quaterniond lastQ;
|
||||
std::thread threadOpt;
|
||||
};
|
||||
203
global_fusion/src/globalOptNode.cpp
Normal file
203
global_fusion/src/globalOptNode.cpp
Normal file
@@ -0,0 +1,203 @@
|
||||
//
|
||||
// Created by admin1 on 25.08.2022.
|
||||
//
|
||||
|
||||
/*******************************************************
|
||||
* Copyright (C) 2019, Aerial Robotics Group, Hong Kong University of Science and Technology
|
||||
*
|
||||
* This file is part of VINS.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
*
|
||||
* Author: Qin Tong (qintonguav@gmail.com)
|
||||
*******************************************************/
|
||||
|
||||
#include "ros/ros.h"
|
||||
#include "globalOpt.h"
|
||||
#include <sensor_msgs/NavSatFix.h>
|
||||
#include <nav_msgs/Odometry.h>
|
||||
#include <nav_msgs/Path.h>
|
||||
#include <tf/transform_datatypes.h>
|
||||
#include <tf/transform_broadcaster.h>
|
||||
#include <eigen3/Eigen/Dense>
|
||||
#include <eigen3/Eigen/Geometry>
|
||||
#include <iostream>
|
||||
#include <stdio.h>
|
||||
#include <visualization_msgs/Marker.h>
|
||||
#include <visualization_msgs/MarkerArray.h>
|
||||
#include <fstream>
|
||||
#include <queue>
|
||||
#include <mutex>
|
||||
#include <math.h>
|
||||
|
||||
GlobalOptimization globalEstimator;
|
||||
ros::Publisher pub_global_odometry, pub_global_path, pub_car, pub_imu_tf_global;
|
||||
nav_msgs::Path *global_path;
|
||||
// we need to use pointer becuase otherwise TransformBroadcaster will be initializing here, before
|
||||
// ros::init() called
|
||||
std::shared_ptr<tf::TransformBroadcaster> TfBr;
|
||||
double last_vio_t = -1;
|
||||
std::queue<sensor_msgs::NavSatFixConstPtr> gpsQueue;
|
||||
std::mutex m_buf;
|
||||
|
||||
void publish_car_model(double t, Eigen::Vector3d t_w_car, Eigen::Quaterniond q_w_car)
|
||||
{
|
||||
visualization_msgs::MarkerArray markerArray_msg;
|
||||
visualization_msgs::Marker car_mesh;
|
||||
car_mesh.header.stamp = ros::Time(t);
|
||||
car_mesh.header.frame_id = "gps_global";
|
||||
car_mesh.type = visualization_msgs::Marker::MESH_RESOURCE;
|
||||
car_mesh.action = visualization_msgs::Marker::ADD;
|
||||
car_mesh.id = 0;
|
||||
|
||||
car_mesh.mesh_resource = "package://global_fusion/models/car.dae";
|
||||
|
||||
Eigen::Matrix3d rot;
|
||||
rot << 0, 0, -1, 0, -1, 0, -1, 0, 0;
|
||||
|
||||
Eigen::Quaterniond Q;
|
||||
Q = q_w_car * rot;
|
||||
car_mesh.pose.position.x = t_w_car.x();
|
||||
car_mesh.pose.position.y = t_w_car.y();
|
||||
car_mesh.pose.position.z = t_w_car.z();
|
||||
car_mesh.pose.orientation.w = Q.w();
|
||||
car_mesh.pose.orientation.x = Q.x();
|
||||
car_mesh.pose.orientation.y = Q.y();
|
||||
car_mesh.pose.orientation.z = Q.z();
|
||||
|
||||
car_mesh.color.a = 1.0;
|
||||
car_mesh.color.r = 1.0;
|
||||
car_mesh.color.g = 0.0;
|
||||
car_mesh.color.b = 0.0;
|
||||
|
||||
float major_scale = 2.0;
|
||||
|
||||
car_mesh.scale.x = major_scale;
|
||||
car_mesh.scale.y = major_scale;
|
||||
car_mesh.scale.z = major_scale;
|
||||
markerArray_msg.markers.push_back(car_mesh);
|
||||
pub_car.publish(markerArray_msg);
|
||||
}
|
||||
|
||||
void GPS_callback(const sensor_msgs::NavSatFixConstPtr &GPS_msg)
|
||||
{
|
||||
//printf("gps_callback! \n");
|
||||
m_buf.lock();
|
||||
gpsQueue.push(GPS_msg);
|
||||
m_buf.unlock();
|
||||
}
|
||||
|
||||
void vio_callback(const nav_msgs::Odometry::ConstPtr &pose_msg)
|
||||
{
|
||||
//printf("vio_callback! \n");
|
||||
double t = pose_msg->header.stamp.toSec();
|
||||
last_vio_t = t;
|
||||
Eigen::Vector3d vio_t(pose_msg->pose.pose.position.x, pose_msg->pose.pose.position.y, pose_msg->pose.pose.position.z);
|
||||
Eigen::Quaterniond vio_q;
|
||||
vio_q.w() = pose_msg->pose.pose.orientation.w;
|
||||
vio_q.x() = pose_msg->pose.pose.orientation.x;
|
||||
vio_q.y() = pose_msg->pose.pose.orientation.y;
|
||||
vio_q.z() = pose_msg->pose.pose.orientation.z;
|
||||
globalEstimator.inputOdom(t, vio_t, vio_q);
|
||||
|
||||
|
||||
m_buf.lock();
|
||||
while(!gpsQueue.empty())
|
||||
{
|
||||
sensor_msgs::NavSatFixConstPtr GPS_msg = gpsQueue.front();
|
||||
double gps_t = GPS_msg->header.stamp.toSec();
|
||||
printf("vio t: %f, gps t: %f \n", t, gps_t);
|
||||
// 10ms sync tolerance
|
||||
// PI: Let's change the sync tolerance from 10 ms to 100 ms
|
||||
// if(gps_t >= t - 0.01 && gps_t <= t + 0.01)
|
||||
if(gps_t >= t - 0.1 && gps_t <= t + 0.1)
|
||||
{
|
||||
//printf("receive GPS with timestamp %f\n", GPS_msg->header.stamp.toSec());
|
||||
double latitude = GPS_msg->latitude;
|
||||
double longitude = GPS_msg->longitude;
|
||||
double altitude = GPS_msg->altitude;
|
||||
//int numSats = GPS_msg->status.service;
|
||||
// it was made for KITTI dataset before, which only gives the one parameter. But here we have 3 variances
|
||||
// (the other values in the covariance matrix are 0).
|
||||
double pos_accuracy;
|
||||
pos_accuracy = std::sqrt( pow(GPS_msg->position_covariance[0], 2.0) + pow(GPS_msg->position_covariance[4], 2) + pow(GPS_msg->position_covariance[8], 2) );
|
||||
if(pos_accuracy <= 0)
|
||||
pos_accuracy = 1;
|
||||
//printf("receive covariance %lf \n", pos_accuracy);
|
||||
//if(GPS_msg->status.status > 8)
|
||||
globalEstimator.inputGPS(t, latitude, longitude, altitude, pos_accuracy);
|
||||
gpsQueue.pop();
|
||||
break;
|
||||
}
|
||||
else if(gps_t < t - 0.01)
|
||||
gpsQueue.pop();
|
||||
else if(gps_t > t + 0.01)
|
||||
break;
|
||||
}
|
||||
m_buf.unlock();
|
||||
|
||||
Eigen::Vector3d global_t;
|
||||
Eigen:: Quaterniond global_q;
|
||||
globalEstimator.getGlobalOdom(global_t, global_q);
|
||||
|
||||
nav_msgs::Odometry odometry;
|
||||
odometry.header = pose_msg->header;
|
||||
odometry.header.frame_id = "gps_global";
|
||||
odometry.child_frame_id = "cam0_gps";
|
||||
odometry.pose.pose.position.x = global_t.x();
|
||||
odometry.pose.pose.position.y = global_t.y();
|
||||
odometry.pose.pose.position.z = global_t.z();
|
||||
odometry.pose.pose.orientation.x = global_q.x();
|
||||
odometry.pose.pose.orientation.y = global_q.y();
|
||||
odometry.pose.pose.orientation.z = global_q.z();
|
||||
odometry.pose.pose.orientation.w = global_q.w();
|
||||
pub_global_odometry.publish(odometry);
|
||||
pub_global_path.publish(*global_path);
|
||||
publish_car_model(t, global_t, global_q);
|
||||
|
||||
// Publish the tf so that Camera can bind to it.
|
||||
tf::StampedTransform trans;
|
||||
trans.frame_id_ = "gps_global";
|
||||
trans.child_frame_id_ = "cam0_gps";
|
||||
trans.stamp_ = pose_msg->header.stamp;
|
||||
tf::Quaternion quat(global_q.x(), global_q.y(), global_q.z(), global_q.w());
|
||||
trans.setRotation(quat);
|
||||
tf::Vector3 orig(global_t.x(), global_t.y(), global_t.z());
|
||||
trans.setOrigin(orig);
|
||||
TfBr->sendTransform(trans);
|
||||
// pub_imu_tf_global.publish(trans);
|
||||
|
||||
// write result to file
|
||||
std::ofstream foutC("/home/admin1/workspace/catkin_ws_ov/src/open_vins/global_fusion/data/vio_global.csv", ios::app);
|
||||
foutC.setf(ios::fixed, ios::floatfield);
|
||||
foutC.precision(0);
|
||||
foutC << pose_msg->header.stamp.toSec() * 1e9 << ",";
|
||||
foutC.precision(5);
|
||||
foutC << global_t.x() << ","
|
||||
<< global_t.y() << ","
|
||||
<< global_t.z() << ","
|
||||
<< global_q.w() << ","
|
||||
<< global_q.x() << ","
|
||||
<< global_q.y() << ","
|
||||
<< global_q.z() << endl;
|
||||
foutC.close();
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
ros::init(argc, argv, "globalEstimator");
|
||||
ros::NodeHandle n("~");
|
||||
|
||||
global_path = &globalEstimator.global_path;
|
||||
TfBr = std::make_shared<tf::TransformBroadcaster>();
|
||||
|
||||
ros::Subscriber sub_GPS = n.subscribe("/gps", 100, GPS_callback);
|
||||
ros::Subscriber sub_vio = n.subscribe("/vins_estimator/odometry", 100, vio_callback);
|
||||
pub_global_path = n.advertise<nav_msgs::Path>("global_path", 100);
|
||||
pub_global_odometry = n.advertise<nav_msgs::Odometry>("global_odometry", 100);
|
||||
// pub_imu_tf_global = n.advertise<tf::StampedTransform>("tf_imu_global", 100);
|
||||
pub_car = n.advertise<visualization_msgs::MarkerArray>("car_model", 1000);
|
||||
ros::spin();
|
||||
return 0;
|
||||
}
|
||||
47
global_fusion/src/tic_toc.h
Normal file
47
global_fusion/src/tic_toc.h
Normal file
@@ -0,0 +1,47 @@
|
||||
//
|
||||
// Created by admin1 on 25.08.2022.
|
||||
//
|
||||
|
||||
/*******************************************************
|
||||
* Copyright (C) 2019, Aerial Robotics Group, Hong Kong University of Science and Technology
|
||||
*
|
||||
* This file is part of VINS.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
*******************************************************/
|
||||
|
||||
#ifndef GLOBAL_FUSION_TIC_TOC_H
|
||||
#define GLOBAL_FUSION_TIC_TOC_H
|
||||
|
||||
#endif //GLOBAL_FUSION_TIC_TOC_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ctime>
|
||||
#include <cstdlib>
|
||||
#include <chrono>
|
||||
|
||||
class TicToc
|
||||
{
|
||||
public:
|
||||
TicToc()
|
||||
{
|
||||
tic();
|
||||
}
|
||||
|
||||
void tic()
|
||||
{
|
||||
start = std::chrono::system_clock::now();
|
||||
}
|
||||
|
||||
double toc()
|
||||
{
|
||||
end = std::chrono::system_clock::now();
|
||||
std::chrono::duration<double> elapsed_seconds = end - start;
|
||||
return elapsed_seconds.count() * 1000;
|
||||
}
|
||||
|
||||
private:
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
};
|
||||
Reference in New Issue
Block a user