Camera refactoring (#315)

* Camera Refactoring part 1 (Mac OS X)

* fixed camera*** -> Camera***

* Fixed build for cameras Zed/RealSense/RealSense2

* increased version to 0.17.7

* fixed build for cameras K4W2 and FlyCapture2
This commit is contained in:
matlabbe
2018-10-01 19:33:56 -04:00
committed by GitHub
parent eb38b9cfab
commit 0059a4bc1b
116 changed files with 7716 additions and 6799 deletions

View File

@@ -0,0 +1,278 @@
/*
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/odometry/OdometryDVO.h"
#include "rtabmap/core/OdometryInfo.h"
#include "rtabmap/core/util2d.h"
#include "rtabmap/utilite/ULogger.h"
#include "rtabmap/utilite/UTimer.h"
#include "rtabmap/utilite/UStl.h"
#ifdef RTABMAP_DVO
#include <dvo/dense_tracking.h>
#include <dvo/core/surface_pyramid.h>
#include <dvo/core/rgbd_image.h>
#endif
namespace rtabmap {
OdometryDVO::OdometryDVO(const ParametersMap & parameters) :
Odometry(parameters),
#ifdef RTABMAP_DVO
dvo_(0),
reference_(0),
camera_(0),
lost_(false),
#endif
motionFromKeyFrame_(Transform::getIdentity())
{
}
OdometryDVO::~OdometryDVO()
{
#ifdef RTABMAP_DVO
delete dvo_;
delete reference_;
delete camera_;
#endif
}
void OdometryDVO::reset(const Transform & initialPose)
{
Odometry::reset(initialPose);
#ifdef RTABMAP_DVO
if(dvo_)
{
delete dvo_;
dvo_ = 0;
}
if(reference_)
{
delete reference_;
reference_ = 0;
}
if(camera_)
{
delete camera_;
camera_ = 0;
}
lost_ = false;
motionFromKeyFrame_.setIdentity();
previousLocalTransform_.setNull();
#endif
}
// return not null transform if odometry is correctly computed
Transform OdometryDVO::computeTransform(
SensorData & data,
const Transform & guess,
OdometryInfo * info)
{
Transform t;
#ifdef RTABMAP_DVO
UTimer timer;
if(data.imageRaw().empty() ||
data.imageRaw().rows != data.depthOrRightRaw().rows ||
data.imageRaw().cols != data.depthOrRightRaw().cols)
{
UERROR("Not supported input!");
return t;
}
if(!(data.cameraModels().size() == 1 && data.cameraModels()[0].isValidForReprojection()))
{
UERROR("Invalid camera model! Only single RGB-D camera supported by DVO. Try another odometry approach.");
return t;
}
if(dvo_ == 0)
{
dvo::DenseTracker::Config cfg = dvo::DenseTracker::getDefaultConfig();
dvo_ = new dvo::DenseTracker(cfg);
}
cv::Mat grey, grey_s16, depth_inpainted, depth_mask, depth_mono, depth_float;
if(data.imageRaw().type() != CV_32FC1)
{
if(data.imageRaw().type() == CV_8UC3)
{
cv::cvtColor(data.imageRaw(), grey, CV_BGR2GRAY);
}
else
{
grey = data.imageRaw();
}
grey.convertTo(grey_s16, CV_32F);
}
else
{
grey_s16 = data.imageRaw();
}
// make sure all zeros are NAN
if(data.depthRaw().type() == CV_32FC1)
{
depth_float = data.depthRaw();
for(int i=0; i<depth_float.rows; ++i)
{
for(int j=0; j<depth_float.cols; ++j)
{
float & d = depth_float.at<float>(i,j);
if(d == 0.0f)
{
d = NAN;
}
}
}
}
else if(data.depthRaw().type() == CV_16UC1)
{
depth_float = cv::Mat(data.depthRaw().size(), CV_32FC1);
for(int i=0; i<data.depthRaw().rows; ++i)
{
for(int j=0; j<data.depthRaw().cols; ++j)
{
float d = float(data.depthRaw().at<unsigned short>(i,j))/1000.0f;
depth_float.at<float>(i, j) = d==0.0f?NAN:d;
}
}
}
else
{
UFATAL("Unknown depth format!");
}
if(camera_ == 0)
{
dvo::core::IntrinsicMatrix intrinsics = dvo::core::IntrinsicMatrix::create(
data.cameraModels()[0].fx(),
data.cameraModels()[0].fy(),
data.cameraModels()[0].cx(),
data.cameraModels()[0].cy());
camera_ = new dvo::core::RgbdCameraPyramid(
data.cameraModels()[0].imageWidth(),
data.cameraModels()[0].imageHeight(),
intrinsics);
}
dvo::core::RgbdImagePyramid * current = new dvo::core::RgbdImagePyramid(*camera_, grey_s16, depth_float);
const Transform & localTransform = data.cameraModels()[0].localTransform();
cv::Mat covariance;
if(reference_ == 0)
{
reference_ = current;
if(!lost_)
{
t.setIdentity();
}
covariance = cv::Mat::eye(6,6,CV_64FC1) * 9999.0;
}
else
{
dvo::DenseTracker::Result result;
dvo_->match(*reference_, *current, result);
t = Transform::fromEigen3d(result.Transformation);
if(result.Information(0,0) > 0.0 && result.Information(0,0) != 1.0)
{
lost_ = false;
cv::Mat information = cv::Mat::eye(6,6, CV_64FC1);
memcpy(information.data, result.Information.data(), 36*sizeof(double));
//copy only diagonal to avoid g2o/gtsam errors on graph optimization
covariance = cv::Mat::eye(6,6,CV_64FC1);
covariance = information.inv().mul(covariance);
//covariance *= 100.0; // to be in the same scale than loop closure detection
Transform currentMotion = t;
t = motionFromKeyFrame_.inverse() * t;
// TODO make parameters?
if(currentMotion.getNorm() > 0.01 || currentMotion.getAngle() > 0.01)
{
if(info)
{
info->keyFrameAdded = true;
}
// new keyframe
delete reference_;
reference_ = current;
motionFromKeyFrame_.setIdentity();
}
else
{
delete current;
motionFromKeyFrame_ = currentMotion;
}
}
else
{
lost_ = true;
delete reference_;
delete current;
reference_ = 0; // this will make restart from the next frame
motionFromKeyFrame_.setIdentity();
t.setNull();
previousLocalTransform_.setNull();
covariance = cv::Mat::eye(6,6,CV_64FC1) * 9999.0;
UWARN("dvo failed to estimate motion, tracking will be reinitialized on next frame.");
}
if(!t.isNull() && !t.isIdentity() && !localTransform.isIdentity() && !localTransform.isNull())
{
// from camera frame to base frame
if(!previousLocalTransform_.isNull())
{
t = previousLocalTransform_ * t * localTransform.inverse();
}
else
{
t = localTransform * t * localTransform.inverse();
}
previousLocalTransform_ = localTransform;
}
}
if(info)
{
info->type = (int)kTypeDVO;
info->reg.covariance = covariance;
}
UINFO("Odom update time = %fs", timer.elapsed());
#else
UERROR("RTAB-Map is not built with DVO support! Select another visual odometry approach.");
#endif
return t;
}
} // namespace rtabmap

View File

@@ -0,0 +1,319 @@
/*
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/odometry/OdometryF2F.h"
#include "rtabmap/core/OdometryInfo.h"
#include "rtabmap/core/Registration.h"
#include "rtabmap/core/EpipolarGeometry.h"
#include "rtabmap/core/util3d_transforms.h"
#include "rtabmap/utilite/ULogger.h"
#include "rtabmap/utilite/UTimer.h"
#include "rtabmap/utilite/UStl.h"
namespace rtabmap {
OdometryF2F::OdometryF2F(const ParametersMap & parameters) :
Odometry(parameters),
keyFrameThr_(Parameters::defaultOdomKeyFrameThr()),
visKeyFrameThr_(Parameters::defaultOdomVisKeyFrameThr()),
scanKeyFrameThr_(Parameters::defaultOdomScanKeyFrameThr())
{
registrationPipeline_ = Registration::create(parameters);
Parameters::parse(parameters, Parameters::kOdomKeyFrameThr(), keyFrameThr_);
Parameters::parse(parameters, Parameters::kOdomVisKeyFrameThr(), visKeyFrameThr_);
Parameters::parse(parameters, Parameters::kOdomScanKeyFrameThr(), scanKeyFrameThr_);
UASSERT(keyFrameThr_>=0.0f && keyFrameThr_<=1.0f);
UASSERT(visKeyFrameThr_>=0);
UASSERT(scanKeyFrameThr_>=0.0f && scanKeyFrameThr_<=1.0f);
parameters_ = parameters;
}
OdometryF2F::~OdometryF2F()
{
delete registrationPipeline_;
}
void OdometryF2F::reset(const Transform & initialPose)
{
Odometry::reset(initialPose);
refFrame_ = Signature();
lastKeyFramePose_.setNull();
}
// return not null transform if odometry is correctly computed
Transform OdometryF2F::computeTransform(
SensorData & data,
const Transform & guess,
OdometryInfo * info)
{
UTimer timer;
Transform output;
if(!data.rightRaw().empty() && !data.stereoCameraModel().isValidForProjection())
{
UERROR("Calibrated stereo camera required");
return output;
}
if(!data.depthRaw().empty() &&
(data.cameraModels().size() != 1 || !data.cameraModels()[0].isValidForProjection()))
{
UERROR("Calibrated camera required (multi-cameras not supported).");
return output;
}
bool addKeyFrame = false;
RegistrationInfo regInfo;
UASSERT(!this->getPose().isNull());
if(lastKeyFramePose_.isNull())
{
lastKeyFramePose_ = this->getPose(); // reset to current pose
}
Transform motionSinceLastKeyFrame = lastKeyFramePose_.inverse()*this->getPose();
Signature newFrame(data);
if(refFrame_.sensorData().isValid())
{
float maxCorrespondenceDistance = 0.0f;
float pmOutlierRatio = 0.0f;
if(guess.isNull() &&
!registrationPipeline_->isImageRequired() &&
registrationPipeline_->isScanRequired() &&
this->framesProcessed() < 2)
{
// only on initialization (first frame to register), increase icp max correspondences in case the robot is already moving
maxCorrespondenceDistance = Parameters::defaultIcpMaxCorrespondenceDistance();
pmOutlierRatio = Parameters::defaultIcpPMOutlierRatio();
Parameters::parse(parameters_, Parameters::kIcpMaxCorrespondenceDistance(), maxCorrespondenceDistance);
Parameters::parse(parameters_, Parameters::kIcpPMOutlierRatio(), pmOutlierRatio);
ParametersMap params;
params.insert(ParametersPair(Parameters::kIcpMaxCorrespondenceDistance(), uNumber2Str(maxCorrespondenceDistance*3.0f)));
params.insert(ParametersPair(Parameters::kIcpPMOutlierRatio(), uNumber2Str(0.95f)));
registrationPipeline_->parseParameters(params);
}
Signature tmpRefFrame = refFrame_;
output = registrationPipeline_->computeTransformationMod(
tmpRefFrame,
newFrame,
// special case for ICP-only odom, set guess to identity if we just started or reset
!guess.isNull()?motionSinceLastKeyFrame*guess:!registrationPipeline_->isImageRequired()&&this->framesProcessed()<2?motionSinceLastKeyFrame:Transform(),
&regInfo);
if(maxCorrespondenceDistance>0.0f)
{
// set it back
ParametersMap params;
params.insert(ParametersPair(Parameters::kIcpMaxCorrespondenceDistance(), uNumber2Str(maxCorrespondenceDistance)));
params.insert(ParametersPair(Parameters::kIcpPMOutlierRatio(), uNumber2Str(pmOutlierRatio)));
registrationPipeline_->parseParameters(params);
}
if(output.isNull() && !guess.isNull() && registrationPipeline_->isImageRequired())
{
tmpRefFrame = refFrame_;
// reset matches, but keep already extracted features in newFrame.sensorData()
newFrame.setWords(std::multimap<int, cv::KeyPoint>());
newFrame.setWords3(std::multimap<int, cv::Point3f>());
newFrame.setWordsDescriptors(std::multimap<int, cv::Mat>());
UWARN("Failed to find a transformation with the provided guess (%s), trying again without a guess.", guess.prettyPrint().c_str());
// If optical flow is used, switch temporary to feature matching
int visCorTypeBackup = Parameters::defaultVisCorType();
Parameters::parse(parameters_, Parameters::kVisCorType(), visCorTypeBackup);
if(visCorTypeBackup == 1)
{
ParametersMap params;
params.insert(ParametersPair(Parameters::kVisCorType(), "0"));
registrationPipeline_->parseParameters(params);
}
output = registrationPipeline_->computeTransformationMod(
tmpRefFrame,
newFrame,
Transform(), // null guess
&regInfo);
if(visCorTypeBackup == 1)
{
ParametersMap params;
params.insert(ParametersPair(Parameters::kVisCorType(), "1"));
registrationPipeline_->parseParameters(params);
}
if(output.isNull())
{
UWARN("Trial with no guess still fail.");
}
else
{
UWARN("Trial with no guess succeeded.");
}
}
if(info && this->isInfoDataFilled())
{
std::list<std::pair<int, std::pair<cv::KeyPoint, cv::KeyPoint> > > pairs;
EpipolarGeometry::findPairsUnique(tmpRefFrame.getWords(), newFrame.getWords(), pairs);
info->refCorners.resize(pairs.size());
info->newCorners.resize(pairs.size());
std::map<int, int> idToIndex;
int i=0;
for(std::list<std::pair<int, std::pair<cv::KeyPoint, cv::KeyPoint> > >::iterator iter=pairs.begin();
iter!=pairs.end();
++iter)
{
info->refCorners[i] = iter->second.first.pt;
info->newCorners[i] = iter->second.second.pt;
idToIndex.insert(std::make_pair(iter->first, i));
++i;
}
info->cornerInliers.resize(regInfo.inliersIDs.size(), 1);
i=0;
for(; i<(int)regInfo.inliersIDs.size(); ++i)
{
info->cornerInliers[i] = idToIndex.at(regInfo.inliersIDs[i]);
}
Transform t = this->getPose()*motionSinceLastKeyFrame.inverse();
for(std::multimap<int, cv::Point3f>::const_iterator iter=tmpRefFrame.getWords3().begin(); iter!=tmpRefFrame.getWords3().end(); ++iter)
{
info->localMap.insert(std::make_pair(iter->first, util3d::transformPoint(iter->second, t)));
}
info->localMapSize = tmpRefFrame.getWords3().size();
info->words = newFrame.getWords();
info->localScanMapSize = tmpRefFrame.sensorData().laserScanRaw().size();
info->localScanMap = util3d::transformLaserScan(tmpRefFrame.sensorData().laserScanRaw(), tmpRefFrame.sensorData().laserScanRaw().localTransform().inverse()*t*tmpRefFrame.sensorData().laserScanRaw().localTransform());
}
}
else
{
//return Identity
output = Transform::getIdentity();
// a very high variance tells that the new pose is not linked with the previous one
regInfo.covariance = cv::Mat::eye(6,6,CV_64FC1)*9999.0;
}
if(!output.isNull())
{
output = motionSinceLastKeyFrame.inverse() * output;
// new key-frame?
if( (registrationPipeline_->isImageRequired() &&
(keyFrameThr_ == 0.0f ||
visKeyFrameThr_ == 0 ||
float(regInfo.inliers) <= keyFrameThr_*float(refFrame_.sensorData().keypoints().size()) ||
regInfo.inliers <= visKeyFrameThr_)) ||
(registrationPipeline_->isScanRequired() && (scanKeyFrameThr_ == 0.0f || regInfo.icpInliersRatio <= scanKeyFrameThr_)))
{
UDEBUG("Update key frame");
int features = newFrame.getWordsDescriptors().size();
if(registrationPipeline_->isImageRequired() && features == 0)
{
newFrame = Signature(data);
// this will generate features only for the first frame or if optical flow was used (no 3d words)
Signature dummy;
registrationPipeline_->computeTransformationMod(
newFrame,
dummy);
features = (int)newFrame.sensorData().keypoints().size();
}
if((features >= registrationPipeline_->getMinVisualCorrespondences()) &&
(registrationPipeline_->getMinGeometryCorrespondencesRatio()==0.0f ||
(newFrame.sensorData().laserScanRaw().size() &&
(newFrame.sensorData().laserScanRaw().maxPoints() == 0 || float(newFrame.sensorData().laserScanRaw().size())/float(newFrame.sensorData().laserScanRaw().maxPoints())>=registrationPipeline_->getMinGeometryCorrespondencesRatio()))))
{
refFrame_ = newFrame;
refFrame_.setWords(std::multimap<int, cv::KeyPoint>());
refFrame_.setWords3(std::multimap<int, cv::Point3f>());
refFrame_.setWordsDescriptors(std::multimap<int, cv::Mat>());
//reset motion
lastKeyFramePose_.setNull();
addKeyFrame = true;
}
else
{
if (!refFrame_.sensorData().isValid())
{
// Don't send odometry if we don't have a keyframe yet
output.setNull();
}
if(features < registrationPipeline_->getMinVisualCorrespondences())
{
UWARN("Too low 2D features (%d), keeping last key frame...", features);
}
if(registrationPipeline_->getMinGeometryCorrespondencesRatio()>0.0f && newFrame.sensorData().laserScanRaw().size()==0)
{
UWARN("Too low scan points (%d), keeping last key frame...", newFrame.sensorData().laserScanRaw().size());
}
else if(registrationPipeline_->getMinGeometryCorrespondencesRatio()>0.0f && newFrame.sensorData().laserScanRaw().maxPoints() != 0 && float(newFrame.sensorData().laserScanRaw().size())/float(newFrame.sensorData().laserScanRaw().maxPoints())<registrationPipeline_->getMinGeometryCorrespondencesRatio())
{
UWARN("Too low scan points ratio (%d < %d), keeping last key frame...", float(newFrame.sensorData().laserScanRaw().size())/float(newFrame.sensorData().laserScanRaw().maxPoints()), registrationPipeline_->getMinGeometryCorrespondencesRatio());
}
}
}
}
else if(!regInfo.rejectedMsg.empty())
{
UWARN("Registration failed: \"%s\"", regInfo.rejectedMsg.c_str());
}
data.setFeatures(newFrame.sensorData().keypoints(), newFrame.sensorData().keypoints3D(), newFrame.sensorData().descriptors());
if(info)
{
info->type = kTypeF2F;
info->features = newFrame.sensorData().keypoints().size();
info->keyFrameAdded = addKeyFrame;
if(this->isInfoDataFilled())
{
info->reg = regInfo;
}
else
{
info->reg = regInfo.copyWithoutData();
}
}
UINFO("Odom update time = %fs lost=%s inliers=%d, ref frame corners=%d, transform accepted=%s",
timer.elapsed(),
output.isNull()?"true":"false",
(int)regInfo.inliers,
(int)newFrame.sensorData().keypoints().size(),
!output.isNull()?"true":"false");
return output;
}
} // namespace rtabmap

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,415 @@
/*
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/odometry/OdometryFovis.h"
#include "rtabmap/core/OdometryInfo.h"
#include "rtabmap/core/util2d.h"
#include "rtabmap/utilite/ULogger.h"
#include "rtabmap/utilite/UTimer.h"
#include "rtabmap/utilite/UStl.h"
#ifdef RTABMAP_FOVIS
#include <libfovis/fovis.hpp>
#endif
namespace rtabmap {
OdometryFovis::OdometryFovis(const ParametersMap & parameters) :
Odometry(parameters)
#ifdef RTABMAP_FOVIS
,
fovis_(0),
rect_(0),
stereoCalib_(0),
depthImage_(0),
stereoDepth_(0),
lost_(false)
#endif
{
fovisParameters_ = Parameters::filterParameters(parameters, "OdomFovis");
if(parameters.find(Parameters::kOdomVisKeyFrameThr()) != parameters.end())
{
fovisParameters_.insert(*parameters.find(Parameters::kOdomVisKeyFrameThr()));
}
}
OdometryFovis::~OdometryFovis()
{
#ifdef RTABMAP_FOVIS
delete fovis_;
delete rect_;
delete stereoCalib_;
delete depthImage_;
delete stereoDepth_;
#endif
}
void OdometryFovis::reset(const Transform & initialPose)
{
Odometry::reset(initialPose);
#ifdef RTABMAP_FOVIS
if(fovis_)
{
delete fovis_;
fovis_ = 0;
}
if(rect_)
{
delete rect_;
rect_ = 0;
}
if(stereoCalib_)
{
delete stereoCalib_;
stereoCalib_ = 0;
}
if(depthImage_)
{
delete depthImage_;
depthImage_ = 0;
}
if(stereoDepth_)
{
delete stereoDepth_;
stereoDepth_ = 0;
}
lost_ = false;
previousLocalTransform_.setNull();
#endif
}
// return not null transform if odometry is correctly computed
Transform OdometryFovis::computeTransform(
SensorData & data,
const Transform & guess,
OdometryInfo * info)
{
UDEBUG("");
Transform t;
#ifdef RTABMAP_FOVIS
UTimer timer;
if(data.imageRaw().empty() ||
data.imageRaw().rows != data.depthOrRightRaw().rows ||
data.imageRaw().cols != data.depthOrRightRaw().cols)
{
UERROR("Not supported input!");
return t;
}
if(!((data.cameraModels().size() == 1 &&
data.cameraModels()[0].isValidForReprojection()) ||
(data.stereoCameraModel().isValidForProjection() &&
data.stereoCameraModel().left().isValidForReprojection() &&
data.stereoCameraModel().right().isValidForReprojection())))
{
UERROR("Invalid camera model! Mono cameras=%d (reproj=%d), Stereo camera=%d (reproj=%d|%d)",
(int)data.cameraModels().size(),
data.cameraModels().size() && data.cameraModels()[0].isValidForReprojection()?1:0,
data.stereoCameraModel().isValidForProjection()?1:0,
data.stereoCameraModel().left().isValidForReprojection()?1:0,
data.stereoCameraModel().right().isValidForReprojection()?1:0);
return t;
}
cv::Mat gray;
if(data.imageRaw().type() == CV_8UC3)
{
cv::cvtColor(data.imageRaw(), gray, CV_BGR2GRAY);
}
else if(data.imageRaw().type() == CV_8UC1)
{
gray = data.imageRaw();
}
else
{
UFATAL("Not supported color type!");
}
fovis::VisualOdometryOptions options;
if(fovis_ == 0 || (data.cameraModels().size() != 1 && stereoDepth_ == 0))
{
options = fovis::VisualOdometry::getDefaultOptions();
ParametersMap defaults = Parameters::getDefaultParameters("OdomFovis");
options["feature-window-size"] = uValue(fovisParameters_, Parameters::kOdomFovisFeatureWindowSize(), defaults.at(Parameters::kOdomFovisFeatureWindowSize()));
options["max-pyramid-level"] = uValue(fovisParameters_, Parameters::kOdomFovisMaxPyramidLevel(), defaults.at(Parameters::kOdomFovisMaxPyramidLevel()));
options["min-pyramid-level"] = uValue(fovisParameters_, Parameters::kOdomFovisMinPyramidLevel(), defaults.at(Parameters::kOdomFovisMinPyramidLevel()));
options["target-pixels-per-feature"] = uValue(fovisParameters_, Parameters::kOdomFovisTargetPixelsPerFeature(), defaults.at(Parameters::kOdomFovisTargetPixelsPerFeature()));
options["fast-threshold"] = uValue(fovisParameters_, Parameters::kOdomFovisFastThreshold(), defaults.at(Parameters::kOdomFovisFastThreshold()));
options["use-adaptive-threshold"] = uValue(fovisParameters_, Parameters::kOdomFovisUseAdaptiveThreshold(), defaults.at(Parameters::kOdomFovisUseAdaptiveThreshold()));
options["fast-threshold-adaptive-gain"] = uValue(fovisParameters_, Parameters::kOdomFovisFastThresholdAdaptiveGain(), defaults.at(Parameters::kOdomFovisFastThresholdAdaptiveGain()));
options["use-homography-initialization"] = uValue(fovisParameters_, Parameters::kOdomFovisUseHomographyInitialization(), defaults.at(Parameters::kOdomFovisUseHomographyInitialization()));
options["ref-frame-change-threshold"] = uValue(fovisParameters_, Parameters::kOdomVisKeyFrameThr(), uNumber2Str(Parameters::defaultOdomVisKeyFrameThr()));
// OdometryFrame
options["use-bucketing"] = uValue(fovisParameters_, Parameters::kOdomFovisUseBucketing(), defaults.at(Parameters::kOdomFovisUseBucketing()));
options["bucket-width"] = uValue(fovisParameters_, Parameters::kOdomFovisBucketWidth(), defaults.at(Parameters::kOdomFovisBucketWidth()));
options["bucket-height"] = uValue(fovisParameters_, Parameters::kOdomFovisBucketHeight(), defaults.at(Parameters::kOdomFovisBucketHeight()));
options["max-keypoints-per-bucket"] = uValue(fovisParameters_, Parameters::kOdomFovisMaxKeypointsPerBucket(), defaults.at(Parameters::kOdomFovisMaxKeypointsPerBucket()));
options["use-image-normalization"] = uValue(fovisParameters_, Parameters::kOdomFovisUseImageNormalization(), defaults.at(Parameters::kOdomFovisUseImageNormalization()));
// MotionEstimator
options["inlier-max-reprojection-error"] = uValue(fovisParameters_, Parameters::kOdomFovisInlierMaxReprojectionError(), defaults.at(Parameters::kOdomFovisInlierMaxReprojectionError()));
options["clique-inlier-threshold"] = uValue(fovisParameters_, Parameters::kOdomFovisCliqueInlierThreshold(), defaults.at(Parameters::kOdomFovisCliqueInlierThreshold()));
options["min-features-for-estimate"] = uValue(fovisParameters_, Parameters::kOdomFovisMinFeaturesForEstimate(), defaults.at(Parameters::kOdomFovisMinFeaturesForEstimate()));
options["max-mean-reprojection-error"] = uValue(fovisParameters_, Parameters::kOdomFovisMaxMeanReprojectionError(), defaults.at(Parameters::kOdomFovisMaxMeanReprojectionError()));
options["use-subpixel-refinement"] = uValue(fovisParameters_, Parameters::kOdomFovisUseSubpixelRefinement(), defaults.at(Parameters::kOdomFovisUseSubpixelRefinement()));
options["feature-search-window"] = uValue(fovisParameters_, Parameters::kOdomFovisFeatureSearchWindow(), defaults.at(Parameters::kOdomFovisFeatureSearchWindow()));
options["update-target-features-with-refined"] = uValue(fovisParameters_, Parameters::kOdomFovisUpdateTargetFeaturesWithRefined(), defaults.at(Parameters::kOdomFovisUpdateTargetFeaturesWithRefined()));
// StereoDepth
options["stereo-require-mutual-match"] = uValue(fovisParameters_, Parameters::kOdomFovisStereoRequireMutualMatch(), defaults.at(Parameters::kOdomFovisStereoRequireMutualMatch()));
options["stereo-max-dist-epipolar-line"] = uValue(fovisParameters_, Parameters::kOdomFovisStereoMaxDistEpipolarLine(), defaults.at(Parameters::kOdomFovisStereoMaxDistEpipolarLine()));
options["stereo-max-refinement-displacement"] = uValue(fovisParameters_, Parameters::kOdomFovisStereoMaxRefinementDisplacement(), defaults.at(Parameters::kOdomFovisStereoMaxRefinementDisplacement()));
options["stereo-max-disparity"] = uValue(fovisParameters_, Parameters::kOdomFovisStereoMaxDisparity(), defaults.at(Parameters::kOdomFovisStereoMaxDisparity()));
}
fovis::DepthSource * depthSource = 0;
cv::Mat depth;
cv::Mat right;
Transform localTransform = Transform::getIdentity();
if(data.cameraModels().size() == 1) //depth
{
UDEBUG("");
fovis::CameraIntrinsicsParameters rgb_params;
memset(&rgb_params, 0, sizeof(fovis::CameraIntrinsicsParameters));
rgb_params.width = data.cameraModels()[0].imageWidth();
rgb_params.height = data.cameraModels()[0].imageHeight();
rgb_params.fx = data.cameraModels()[0].fx();
rgb_params.fy = data.cameraModels()[0].fy();
rgb_params.cx = data.cameraModels()[0].cx()==0.0?double(rgb_params.width) / 2.0:data.cameraModels()[0].cx();
rgb_params.cy = data.cameraModels()[0].cy()==0.0?double(rgb_params.height) / 2.0:data.cameraModels()[0].cy();
localTransform = data.cameraModels()[0].localTransform();
if(rect_ == 0)
{
UINFO("Init rgbd fovis: %dx%d fx=%f fy=%f cx=%f cy=%f", rgb_params.width, rgb_params.height, rgb_params.fx, rgb_params.fy, rgb_params.cx, rgb_params.cy);
rect_ = new fovis::Rectification(rgb_params);
}
if(depthImage_ == 0)
{
depthImage_ = new fovis::DepthImage(rgb_params, rgb_params.width, rgb_params.height);
}
// make sure all zeros are NAN
if(data.depthRaw().type() == CV_32FC1)
{
depth = data.depthRaw();
for(int i=0; i<depth.rows; ++i)
{
for(int j=0; j<depth.cols; ++j)
{
float & d = depth.at<float>(i,j);
if(d == 0.0f)
{
d = NAN;
}
}
}
}
else if(data.depthRaw().type() == CV_16UC1)
{
depth = cv::Mat(data.depthRaw().size(), CV_32FC1);
for(int i=0; i<data.depthRaw().rows; ++i)
{
for(int j=0; j<data.depthRaw().cols; ++j)
{
float d = float(data.depthRaw().at<unsigned short>(i,j))/1000.0f;
depth.at<float>(i, j) = d==0.0f?NAN:d;
}
}
}
else
{
UFATAL("Unknown depth format!");
}
depthImage_->setDepthImage((float*)depth.data);
depthSource = depthImage_;
}
else // stereo
{
UDEBUG("");
// initialize left camera parameters
fovis::CameraIntrinsicsParameters left_parameters;
left_parameters.width = data.stereoCameraModel().left().imageWidth();
left_parameters.height = data.stereoCameraModel().left().imageHeight();
left_parameters.fx = data.stereoCameraModel().left().fx();
left_parameters.fy = data.stereoCameraModel().left().fy();
left_parameters.cx = data.stereoCameraModel().left().cx()==0.0?double(left_parameters.width) / 2.0:data.stereoCameraModel().left().cx();
left_parameters.cy = data.stereoCameraModel().left().cy()==0.0?double(left_parameters.height) / 2.0:data.stereoCameraModel().left().cy();
localTransform = data.stereoCameraModel().localTransform();
if(rect_ == 0)
{
UINFO("Init stereo fovis: %dx%d fx=%f fy=%f cx=%f cy=%f", left_parameters.width, left_parameters.height, left_parameters.fx, left_parameters.fy, left_parameters.cx, left_parameters.cy);
rect_ = new fovis::Rectification(left_parameters);
}
if(stereoCalib_ == 0)
{
// initialize right camera parameters
fovis::CameraIntrinsicsParameters right_parameters;
right_parameters.width = data.stereoCameraModel().right().imageWidth();
right_parameters.height = data.stereoCameraModel().right().imageHeight();
right_parameters.fx = data.stereoCameraModel().right().fx();
right_parameters.fy = data.stereoCameraModel().right().fy();
right_parameters.cx = data.stereoCameraModel().right().cx()==0.0?double(right_parameters.width) / 2.0:data.stereoCameraModel().right().cx();
right_parameters.cy = data.stereoCameraModel().right().cy()==0.0?double(right_parameters.height) / 2.0:data.stereoCameraModel().right().cy();
// as we use rectified images, rotation is identity
// and translation is baseline only
fovis::StereoCalibrationParameters stereo_parameters;
stereo_parameters.left_parameters = left_parameters;
stereo_parameters.right_parameters = right_parameters;
stereo_parameters.right_to_left_rotation[0] = 1.0;
stereo_parameters.right_to_left_rotation[1] = 0.0;
stereo_parameters.right_to_left_rotation[2] = 0.0;
stereo_parameters.right_to_left_rotation[3] = 0.0;
stereo_parameters.right_to_left_translation[0] = -data.stereoCameraModel().baseline();
stereo_parameters.right_to_left_translation[1] = 0.0;
stereo_parameters.right_to_left_translation[2] = 0.0;
stereoCalib_ = new fovis::StereoCalibration(stereo_parameters);
}
if(stereoDepth_ == 0)
{
stereoDepth_ = new fovis::StereoDepth(stereoCalib_, options);
}
if(data.rightRaw().type() == CV_8UC3)
{
cv::cvtColor(data.rightRaw(), right, CV_BGR2GRAY);
}
else if(data.rightRaw().type() == CV_8UC1)
{
right = data.rightRaw();
}
else
{
UFATAL("Not supported color type!");
}
stereoDepth_->setRightImage(right.data);
depthSource = stereoDepth_;
}
if(fovis_ == 0)
{
fovis_ = new fovis::VisualOdometry(rect_, options);
}
UDEBUG("");
fovis_->processFrame(gray.data, depthSource);
// get the motion estimate for this frame to the previous frame.
t = Transform::fromEigen3d(fovis_->getMotionEstimate());
cv::Mat covariance;
fovis::MotionEstimateStatusCode statusCode = fovis_->getMotionEstimator()->getMotionEstimateStatus();
if(statusCode > fovis::SUCCESS)
{
UWARN("Fovis error status: %s", fovis::MotionEstimateStatusCodeStrings[statusCode]);
t.setNull();
lost_ = true;
covariance = cv::Mat::eye(6,6, CV_64FC1)*9999.0;
previousLocalTransform_.setNull();
}
else if(lost_)
{
lost_ = false;
// we are not lost anymore but we don't know where we are now according to last valid pose
covariance = cv::Mat::eye(6,6, CV_64FC1)*9999.0;
previousLocalTransform_.setNull();
}
else
{
const Eigen::MatrixXd& cov = fovis_->getMotionEstimator()->getMotionEstimateCov();
if(cov.cols() == 6 && cov.rows() == 6 && cov(0,0) > 0.0)
{
covariance = cv::Mat::eye(6,6, CV_64FC1);
memcpy(covariance.data, cov.data(), 36*sizeof(double));
covariance *= 100.0; // to be in the same scale than loop closure detection
}
}
if(!t.isNull() && !t.isIdentity() && !localTransform.isIdentity() && !localTransform.isNull())
{
// from camera frame to base frame
if(!previousLocalTransform_.isNull())
{
t = previousLocalTransform_ * t * localTransform.inverse();
}
else
{
t = localTransform * t * localTransform.inverse();
}
previousLocalTransform_ = localTransform;
}
if(info)
{
info->type = (int)kTypeFovis;
info->keyFrameAdded = fovis_->getChangeReferenceFrames();
info->features = fovis_->getTargetFrame()->getNumDetectedKeypoints();
info->reg.matches = fovis_->getMotionEstimator()->getNumMatches();
info->reg.inliers = fovis_->getMotionEstimator()->getNumInliers();
info->reg.covariance = covariance;
if(this->isInfoDataFilled())
{
const fovis::FeatureMatch * matches = fovis_->getMotionEstimator()->getMatches();
int numMatches = fovis_->getMotionEstimator()->getNumMatches();
if(matches && numMatches>0)
{
info->refCorners.resize(numMatches);
info->newCorners.resize(numMatches);
info->cornerInliers.resize(numMatches);
int oi=0;
for (int i = 0; i < numMatches; ++i)
{
info->refCorners[i].x = matches[i].ref_keypoint->base_uv[0];
info->refCorners[i].y = matches[i].ref_keypoint->base_uv[1];
info->newCorners[i].x = matches[i].target_keypoint->base_uv[0];
info->newCorners[i].y = matches[i].target_keypoint->base_uv[1];
info->cornerInliers[oi++] = i;
}
info->cornerInliers.resize(oi);
}
}
}
UINFO("Odom update time = %fs status=%s", timer.elapsed(), fovis::MotionEstimateStatusCodeStrings[statusCode]);
#else
UERROR("RTAB-Map is not built with FOVIS support! Select another visual odometry approach.");
#endif
return t;
}
} // namespace rtabmap

View File

@@ -0,0 +1,304 @@
/*
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/odometry/OdometryLOAM.h"
#include "rtabmap/core/OdometryInfo.h"
#include "rtabmap/core/util2d.h"
#include "rtabmap/utilite/ULogger.h"
#include "rtabmap/utilite/UTimer.h"
#include "rtabmap/utilite/UStl.h"
#include "rtabmap/core/util3d.h"
#include <pcl/common/transforms.h>
float SCAN_PERIOD = 0.1f;
namespace rtabmap {
/**
* https://github.com/laboshinl/loam_velodyne/pull/66
*/
OdometryLOAM::OdometryLOAM(const ParametersMap & parameters) :
Odometry(parameters)
#ifdef RTABMAP_LOAM
,lastPose_(Transform::getIdentity())
,scanPeriod_(Parameters::defaultOdomLOAMScanPeriod())
,linVar_(Parameters::defaultOdomLOAMLinVar())
,angVar_(Parameters::defaultOdomLOAMAngVar())
,localMapping_(Parameters::defaultOdomLOAMLocalMapping())
,lost_(false)
#endif
{
#ifdef RTABMAP_LOAM
int velodyneType = 0;
Parameters::parse(parameters, Parameters::kOdomLOAMSensor(), velodyneType);
Parameters::parse(parameters, Parameters::kOdomLOAMScanPeriod(), scanPeriod_);
UASSERT(scanPeriod_>0.0f);
Parameters::parse(parameters, Parameters::kOdomLOAMLinVar(), linVar_);
UASSERT(linVar_>0.0f);
Parameters::parse(parameters, Parameters::kOdomLOAMAngVar(), angVar_);
UASSERT(angVar_>0.0f);
Parameters::parse(parameters, Parameters::kOdomLOAMLocalMapping(), localMapping_);
if(velodyneType == 1)
{
scanMapper_ = loam::MultiScanMapper::Velodyne_HDL_32();
}
else if(velodyneType == 2)
{
scanMapper_ = loam::MultiScanMapper::Velodyne_HDL_64E();
}
else
{
scanMapper_ = loam::MultiScanMapper::Velodyne_VLP_16();
}
laserOdometry_ = new loam::BasicLaserOdometry(scanPeriod_);
laserMapping_ = new loam::BasicLaserMapping(scanPeriod_);
#endif
}
OdometryLOAM::~OdometryLOAM()
{
#ifdef RTABMAP_LOAM
delete laserOdometry_;
delete laserMapping_;
#endif
}
void OdometryLOAM::reset(const Transform & initialPose)
{
Odometry::reset(initialPose);
#ifdef RTABMAP_LOAM
lastPose_.setIdentity();
scanRegistration_ = loam::BasicScanRegistration();
loam::RegistrationParams regParams;
regParams.scanPeriod = scanPeriod_;
scanRegistration_.configure(regParams);
delete laserOdometry_;
laserOdometry_ = new loam::BasicLaserOdometry(scanPeriod_);
delete laserMapping_;
laserMapping_ = new loam::BasicLaserMapping(scanPeriod_);
transformMaintenance_ = loam::BasicTransformMaintenance();
lost_ = false;
#endif
}
#ifdef RTABMAP_LOAM
std::vector<pcl::PointCloud<pcl::PointXYZI> > OdometryLOAM::segmentScanRings(const pcl::PointCloud<pcl::PointXYZ> & laserCloudIn)
{
std::vector<pcl::PointCloud<pcl::PointXYZI> > laserCloudScans;
size_t cloudSize = laserCloudIn.size();
// determine scan start and end orientations
float startOri = -std::atan2(laserCloudIn[0].y, laserCloudIn[0].x);
float endOri = -std::atan2(laserCloudIn[cloudSize - 1].y,
laserCloudIn[cloudSize - 1].x) + 2 * float(M_PI);
if (endOri - startOri > 3 * M_PI) {
endOri -= 2 * M_PI;
} else if (endOri - startOri < M_PI) {
endOri += 2 * M_PI;
}
bool halfPassed = false;
pcl::PointXYZI point;
laserCloudScans.resize(scanMapper_.getNumberOfScanRings());
// clear all scanline points
std::for_each(laserCloudScans.begin(), laserCloudScans.end(), [](auto&&v) {v.clear(); });
// extract valid points from input cloud
for (size_t i = 0; i < cloudSize; i++) {
point.x = laserCloudIn[i].y;
point.y = laserCloudIn[i].z;
point.z = laserCloudIn[i].x;
// skip NaN and INF valued points
if (!pcl_isfinite(point.x) ||
!pcl_isfinite(point.y) ||
!pcl_isfinite(point.z)) {
continue;
}
// skip zero valued points
if (point.x * point.x + point.y * point.y + point.z * point.z < 0.0001) {
continue;
}
// calculate vertical point angle and scan ID
float angle = std::atan(point.y / std::sqrt(point.x * point.x + point.z * point.z));
int scanID = scanMapper_.getRingForAngle(angle);
if (scanID >= scanMapper_.getNumberOfScanRings() || scanID < 0 ){
continue;
}
// calculate horizontal point angle
float ori = -std::atan2(point.x, point.z);
if (!halfPassed) {
if (ori < startOri - M_PI / 2) {
ori += 2 * M_PI;
} else if (ori > startOri + M_PI * 3 / 2) {
ori -= 2 * M_PI;
}
if (ori - startOri > M_PI) {
halfPassed = true;
}
} else {
ori += 2 * M_PI;
if (ori < endOri - M_PI * 3 / 2) {
ori += 2 * M_PI;
} else if (ori > endOri + M_PI / 2) {
ori -= 2 * M_PI;
}
}
// calculate relative scan time based on point orientation
float relTime = SCAN_PERIOD * (ori - startOri) / (endOri - startOri);
point.intensity = scanID + relTime;
// imu not used...
//scanRegistration_.projectPointToStartOfSweep(point, relTime);
laserCloudScans[scanID].push_back(point);
}
return laserCloudScans;
}
#endif
// return not null transform if odometry is correctly computed
Transform OdometryLOAM::computeTransform(
SensorData & data,
const Transform & guess,
OdometryInfo * info)
{
Transform t;
#ifdef RTABMAP_LOAM
UTimer timer;
if(data.laserScanRaw().isEmpty())
{
UERROR("LOAM works only with laser scans and the current input is empty. Aborting odometry update...");
return t;
}
else if(data.laserScanRaw().is2d())
{
UERROR("LOAM version used works only with 3D laser scans from Velodyne. Aborting odometry update...");
return t;
}
cv::Mat covariance = cv::Mat::eye(6,6,CV_64FC1)*9999;
if(!lost_)
{
pcl::PointCloud<pcl::PointXYZ>::Ptr laserCloudInPtr = util3d::laserScanToPointCloud(data.laserScanRaw());
std::vector<pcl::PointCloud<pcl::PointXYZI> > laserCloudScans = segmentScanRings(*laserCloudInPtr);
ros::Time stampT;
stampT.fromSec(data.stamp());
loam::Time scanTime = loam::fromROSTime(stampT);
scanRegistration_.processScanlines(scanTime, laserCloudScans);
*laserOdometry_->cornerPointsSharp() = scanRegistration_.cornerPointsSharp();
*laserOdometry_->cornerPointsLessSharp() = scanRegistration_.cornerPointsLessSharp();
*laserOdometry_->surfPointsFlat() = scanRegistration_.surfacePointsFlat();
*laserOdometry_->surfPointsLessFlat() = scanRegistration_.surfacePointsLessFlat();
*laserOdometry_->laserCloud() = scanRegistration_.laserCloud();
pcl::PointCloud<pcl::PointXYZ> imuTrans;
imuTrans.resize(4);
laserOdometry_->updateIMU(imuTrans);
laserOdometry_->process();
if(localMapping_)
{
laserMapping_->laserCloudCornerLast() = *laserOdometry_->lastCornerCloud();
laserMapping_->laserCloudSurfLast() = *laserOdometry_->lastSurfaceCloud();
laserMapping_->laserCloud() = *laserOdometry_->laserCloud();
laserMapping_->updateOdometry(laserOdometry_->transformSum());
laserMapping_->process(scanTime);
}
transformMaintenance_.updateOdometry(
laserOdometry_->transformSum().rot_x.rad(),
laserOdometry_->transformSum().rot_y.rad(),
laserOdometry_->transformSum().rot_z.rad(),
laserOdometry_->transformSum().pos.x(),
laserOdometry_->transformSum().pos.y(),
laserOdometry_->transformSum().pos.z());
transformMaintenance_.updateMappingTransform(laserMapping_->transformAftMapped(), laserMapping_->transformBefMapped());
transformMaintenance_.transformAssociateToMap();
const float * tm = transformMaintenance_.transformMapped();
Transform pose = Transform(tm[5], tm[3], tm[4], tm[2], tm[0], tm[1]);
if(!pose.isNull())
{
covariance = cv::Mat::eye(6,6,CV_64FC1);
covariance(cv::Range(0,3), cv::Range(0,3)) *= linVar_;
covariance(cv::Range(3,6), cv::Range(3,6)) *= angVar_;
t = lastPose_.inverse() * pose; // incremental
lastPose_ = pose;
const Transform & localTransform = data.laserScanRaw().localTransform();
if(!t.isNull() && !t.isIdentity() && !localTransform.isIdentity() && !localTransform.isNull())
{
// from laser frame to base frame
t = localTransform * t * localTransform.inverse();
}
if(info)
{
info->type = (int)kTypeLOAM;
info->localScanMapSize = laserMapping_->laserCloudSurroundDS().size();
if(covariance.cols == 6 && covariance.rows == 6 && covariance.type() == CV_64FC1)
{
info->reg.covariance = covariance;
}
if(this->isInfoDataFilled())
{
Transform rot(0,0,1,0,1,0,0,0,0,1,0,0);
pcl::PointCloud<pcl::PointXYZI> out;
pcl::transformPointCloud(laserMapping_->laserCloudSurroundDS(), out, rot.toEigen3f());
info->localScanMap = LaserScan::backwardCompatibility(util3d::laserScanFromPointCloud(out), 0, data.laserScanRaw().maxRange(), data.laserScanRaw().localTransform());
}
}
}
else
{
lost_ = true;
UWARN("LOAM failed to register the latest scan, odometry should be reset.");
}
}
UINFO("Odom update time = %fs, lost=%s", timer.elapsed(), lost_?"true":"false");
#else
UERROR("RTAB-Map is not built with LOAM support! Select another odometry approach.");
#endif
return t;
}
} // namespace rtabmap

View File

@@ -0,0 +1,994 @@
/*
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/odometry/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>
#include <pcl/common/transforms.h>
#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) :
msckf_vio::ImageProcessor(0)
{
UDEBUG("");
// 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] = rectified?model.left().fx():model.left().K_raw().at<double>(0,0);
cam0_intrinsics[1] = rectified?model.left().fy():model.left().K_raw().at<double>(1,1);
cam0_intrinsics[2] = rectified?model.left().cx():model.left().K_raw().at<double>(0,2);
cam0_intrinsics[3] = rectified?model.left().cy():model.left().K_raw().at<double>(1,2);
cam1_intrinsics[0] = rectified?model.right().fx():model.right().K_raw().at<double>(0,0);
cam1_intrinsics[1] = rectified?model.right().fy():model.right().K_raw().at<double>(1,1);
cam1_intrinsics[2] = rectified?model.right().cx():model.right().K_raw().at<double>(0,2);
cam1_intrinsics[3] = rectified?model.right().cy():model.right().K_raw().at<double>(1,2);
Transform imuCam = model.localTransform().inverse() * imuLocalTransform;
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, -model.baseline(),
0, 1, 0, 0,
0, 0, 1, 0);
}
else
{
cam0cam1 = model.stereoTransform();
}
UASSERT(!cam0cam1.isNull());
Transform imuCam1 = cam0cam1 * imuCam;
cv::Mat T_imu_cam1 = imuCam1.dataMatrix();
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: " << R_imu_cam0 << std::endl;
std::cout << "t_imu_cam0.t(): " << t_imu_cam0.t() << std::endl;
std::cout << "R_imu_cam1: " << R_imu_cam1 << std::endl;
std::cout << "t_imu_cam1.t(): " << t_imu_cam1.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("===========================================");
// Create feature detector.
detector_ptr = cv::FastFeatureDetector::create(
processor_config.fast_threshold);
}
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) :
msckf_vio::MsckfVio(0)
{
UDEBUG("");
// get all OdomMSCFK group to make sure all parameters are set
parameters_ = Parameters::getDefaultParameters("OdomMSCKF");
uInsert(parameters_, parameters_in);
// Frame id
publish_tf = false;
frame_rate = 1.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 = model.localTransform().inverse() * imuLocalTransform;
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, -model.baseline(),
0, 1, 0, 0,
0, 0, 1, 0);
}
else
{
cam0cam1 = model.stereoTransform();
}
msckf_vio::CAMState::T_cam0_cam1 = cam0cam1.toEigen3d().matrix();
msckf_vio::IMUState::T_imu_body = Transform::getIdentity().toEigen3d().matrix();
// Maximum number of camera states to be stored
Parameters::parse(parameters_, Parameters::kOdomMSCKFMaxCamStateSize(), 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(): " << T_imu_cam0.linear() << std::endl;
std::cout << "T_imu_cam0.translation().transpose(): " << T_imu_cam0.translation().transpose() << std::endl;
std::cout << "CAMState::T_cam0_cam1.linear(): " << msckf_vio::CAMState::T_cam0_cam1.linear() << std::endl;
std::cout << "CAMState::T_cam0_cam1.translation().transpose(): " << msckf_vio::CAMState::T_cam0_cam1.translation().transpose() << std::endl;
std::cout << "IMUState::T_imu_body.linear(): " << msckf_vio::IMUState::T_imu_body.linear() << std::endl;
std::cout << "IMUState::T_imu_body.translation().transpose(): " << msckf_vio::IMUState::T_imu_body.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,
pcl::PointCloud<pcl::PointXYZ>::Ptr & localMap) {
nav_msgs::Odometry odom;
// Return if the gravity vector has not been set.
if (!is_gravity_set)
{
UINFO("Gravity not set yet... waiting for 200 IMU msgs (%d/200)...", (int)imu_msg_buffer.size());
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(localMap);
//double publish_time = (
// ros::Time::now()-start_time).toSec();
// Reset the system if necessary.
onlineReset2();
/*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;
}
void onlineReset2() {
// Never perform online reset if position std threshold
// is non-positive.
if (position_std_threshold <= 0) return;
static long long int online_reset_counter = 0;
// Check the uncertainty of positions to determine if
// the system can be reset.
double position_x_std = std::sqrt(state_server.state_cov(12, 12));
double position_y_std = std::sqrt(state_server.state_cov(13, 13));
double position_z_std = std::sqrt(state_server.state_cov(14, 14));
if (position_x_std < position_std_threshold &&
position_y_std < position_std_threshold &&
position_z_std < position_std_threshold) return;
UWARN("Start %lld online reset procedure...",
++online_reset_counter);
UINFO("Stardard deviation in xyz: %f, %f, %f",
position_x_std, position_y_std, position_z_std);
// Remove all existing camera states.
state_server.cam_states.clear();
// Clear all exsiting features in the map.
map_server.clear();
// Reset the state covariance.
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;
UWARN("%lld online reset complete...", online_reset_counter);
return;
}
nav_msgs::Odometry publish(pcl::PointCloud<pcl::PointXYZ>::Ptr & feature_msg_ptr) {
// 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.
feature_msg_ptr.reset(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) {
Eigen::Vector3d feature_position =
msckf_vio::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;
}
private:
ParametersMap parameters_;
};
#endif
OdometryMSCKF::OdometryMSCKF(const ParametersMap & parameters) :
Odometry(parameters)
#ifdef RTABMAP_MSCKF_VIO
,
imageProcessor_(0),
msckf_(0),
parameters_(parameters),
flipXY_(-1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0),
previousPose_(Transform::getIdentity()),
initGravity_(false)
#endif
{
}
OdometryMSCKF::~OdometryMSCKF()
{
UDEBUG("");
#ifdef RTABMAP_MSCKF_VIO
delete imageProcessor_;
delete msckf_;
#endif
}
void OdometryMSCKF::reset(const Transform & initialPose)
{
Odometry::reset(initialPose);
#ifdef RTABMAP_MSCKF_VIO
if(!initGravity_)
{
if(imageProcessor_)
{
delete imageProcessor_;
imageProcessor_ = 0;
}
if(msckf_)
{
delete msckf_;
msckf_ = 0;
}
lastImu_ = IMU();
previousPose_.setIdentity();
}
initGravity_ = false;
#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())
{
UDEBUG("Image update stamp=%f", data.stamp());
if(data.stereoCameraModel().isValidForProjection())
{
if(msckf_ == 0)
{
UINFO("Initialization");
if(lastImu_.empty())
{
UWARN("Ignoring Image, waiting for imu to initialize...");
return t;
}
UINFO("Creating ImageProcessorNoROS...");
imageProcessor_ = new ImageProcessorNoROS(
parameters_,
lastImu_.localTransform(),
data.stereoCameraModel(),
this->imagesAlreadyRectified());
UINFO("Creating MsckfVioNoROS...");
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);
cam0Msg->encoding = sensor_msgs::image_encodings::MONO8;
cam1Msg->encoding = sensor_msgs::image_encodings::MONO8;
msckf_vio::CameraMeasurementPtr measurements = imageProcessor_->stereoCallback2(cam0Msg, cam1Msg);
pcl::PointCloud<pcl::PointXYZ>::Ptr localMap;
nav_msgs::Odometry odom = msckf_->featureCallback2(measurements, localMap);
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())
{
// pose in rtabmap/ros coordinates
p = flipXY_*p*lastImu_.localTransform();
if(this->getPose().rotation().isIdentity())
{
initGravity_ = true;
this->reset(this->getPose()*p.rotation());
}
if(previousPose_.isIdentity())
{
previousPose_ = p;
}
// make it incremental
Transform previousPoseInv = previousPose_.inverse();
t = previousPoseInv*p;
previousPose_ = p;
if(info)
{
info->type = this->getType();
info->features = measurements->features.size();
info->reg.covariance = cv::Mat::zeros(6, 6, CV_64FC1);
cv::Mat twistCov(6,6,CV_64FC1, odom.twist.covariance.elems);
// twist covariance is not in base frame, but in world frame,
// we have to convert the covariance in base frame
cv::Matx31f covWorldFrame(twistCov.at<double>(0, 0),
twistCov.at<double>(1, 1),
twistCov.at<double>(2, 2));
cv::Matx31f covBaseFrame = cv::Matx33f(previousPoseInv.rotationMatrix()) * covWorldFrame;
// we set only diagonal values as there is an issue with g2o and off-diagonal values
info->reg.covariance.at<double>(0, 0) = fabs(covBaseFrame.val[0])/10.0;
info->reg.covariance.at<double>(1, 1) = fabs(covBaseFrame.val[1])/10.0;
info->reg.covariance.at<double>(2, 2) = fabs(covBaseFrame.val[2])/10.0;
if(info->reg.covariance.at<double>(0, 0) < 0.0001)
{
info->reg.covariance.at<double>(0, 0) = 0.0001;
}
if(info->reg.covariance.at<double>(1, 1) < 0.0001)
{
info->reg.covariance.at<double>(1, 1) = 0.0001;
}
if(info->reg.covariance.at<double>(2, 2) < 0.0001)
{
info->reg.covariance.at<double>(2, 2) = 0.0001;
}
info->reg.covariance.at<double>(3, 3) = msckf_vio::IMUState::gyro_noise*10.0;
info->reg.covariance.at<double>(4, 4) = info->reg.covariance.at<double>(3, 3);
info->reg.covariance.at<double>(5, 5) = info->reg.covariance.at<double>(3, 3);
if(this->isInfoDataFilled())
{
if(localMap.get() && localMap->size())
{
Eigen::Affine3f flip = (this->getPose()*previousPoseInv*flipXY_).toEigen3f();
for(unsigned int i=0; i<localMap->size(); ++i)
{
pcl::PointXYZ pt = pcl::transformPoint(localMap->at(i), flip);
info->localMap.insert(std::make_pair(i, cv::Point3f(pt.x, pt.y, pt.z)));
}
}
if(this->imagesAlreadyRectified())
{
info->newCorners.resize(measurements->features.size());
float fx = data.stereoCameraModel().left().fx();
float fy = data.stereoCameraModel().left().fy();
float cx = data.stereoCameraModel().left().cx();
float cy = data.stereoCameraModel().left().cy();
info->reg.inliersIDs.resize(measurements->features.size());
for(unsigned int i=0; i<measurements->features.size(); ++i)
{
info->newCorners[i].x = measurements->features[i].u0*fx+cx;
info->newCorners[i].y = measurements->features[i].v0*fy+cy;
info->reg.inliersIDs[i] = i;
}
}
}
}
}
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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,500 @@
/*
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/odometry/OdometryOkvis.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_OKVIS
#include <iostream>
#include <fstream>
#include <stdlib.h>
#include <memory>
#include <functional>
#include <atomic>
#include <Eigen/Core>
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
#pragma GCC diagnostic ignored "-Woverloaded-virtual"
#include <opencv2/opencv.hpp>
#pragma GCC diagnostic pop
#include <okvis/VioParametersReader.hpp>
#include <okvis/ThreadedKFVio.hpp>
#include <okvis/cameras/PinholeCamera.hpp>
#include <okvis/cameras/NoDistortion.hpp>
#include <okvis/cameras/RadialTangentialDistortion.hpp>
#include <okvis/cameras/EquidistantDistortion.hpp>
#include <okvis/cameras/RadialTangentialDistortion8.hpp>
#include <boost/filesystem.hpp>
#endif
namespace rtabmap {
#ifdef RTABMAP_OKVIS
class OkvisCallbackHandler
{
public:
OkvisCallbackHandler()
{
}
Transform getLastTransform()
{
UDEBUG("");
Transform tf;
mutex_.lock();
tf = transform_;
mutex_.unlock();
return tf;
}
std::map<int, cv::Point3f> getLastLandmarks()
{
UDEBUG("");
std::map<int, cv::Point3f> landmarks;
mutexLandmarks_.lock();
landmarks = landmarks_;
mutexLandmarks_.unlock();
return landmarks;
}
public:
void fullStateCallback(
const okvis::Time & t, const okvis::kinematics::Transformation & T_WS,
const Eigen::Matrix<double, 9, 1> & /*speedAndBiases*/,
const Eigen::Matrix<double, 3, 1> & /*omega_S*/)
{
UDEBUG("");
Transform tf = Transform::fromEigen4d(T_WS.T());
mutex_.lock();
transform_ = tf;
mutex_.unlock();
}
void landmarksCallback(const okvis::Time & t,
const okvis::MapPointVector & landmarksVector,
const okvis::MapPointVector & /*transferredLandmarks*/)
{
UDEBUG("");
mutexLandmarks_.lock();
landmarks_.clear();
for(unsigned int i=0; i<landmarksVector.size(); ++i)
{
landmarks_.insert(std::make_pair((int)landmarksVector[i].id, cv::Point3f(landmarksVector[i].point[0], landmarksVector[i].point[1], landmarksVector[i].point[2])));
}
mutexLandmarks_.unlock();
}
private:
Transform transform_;
std::map<int, cv::Point3f> landmarks_;
UMutex mutex_;
UMutex mutexLandmarks_;
};
#endif
OdometryOkvis::OdometryOkvis(const ParametersMap & parameters) :
Odometry(parameters),
#ifdef RTABMAP_OKVIS
okvisCallbackHandler_(new OkvisCallbackHandler),
okvisEstimator_(0),
imagesProcessed_(0),
initGravity_(false),
#endif
okvisParameters_(parameters),
previousPose_(Transform::getIdentity())
{
#ifdef RTABMAP_OKVIS
Parameters::parse(parameters, Parameters::kOdomOKVISConfigPath(), configFilename_);
if(configFilename_.empty())
{
UERROR("OKVIS config file is empty (%s)!", Parameters::kOdomOKVISConfigPath().c_str());
}
#endif
}
OdometryOkvis::~OdometryOkvis()
{
UDEBUG("");
#ifdef RTABMAP_OKVIS
delete okvisEstimator_;
delete okvisCallbackHandler_;
#endif
}
void OdometryOkvis::reset(const Transform & initialPose)
{
Odometry::reset(initialPose);
#ifdef RTABMAP_OKVIS
if(!initGravity_)
{
if(okvisEstimator_)
{
delete okvisEstimator_;
okvisEstimator_ = 0;
}
lastImu_ = IMU();
imagesProcessed_ = 0;
previousPose_.setIdentity();
delete okvisCallbackHandler_;
okvisCallbackHandler_ = new OkvisCallbackHandler();
}
initGravity_ = false;
#endif
}
// return not null transform if odometry is correctly computed
Transform OdometryOkvis::computeTransform(
SensorData & data,
const Transform & guess,
OdometryInfo * info)
{
UDEBUG("");
Transform t;
#ifdef RTABMAP_OKVIS
UTimer timer;
okvis::Time timeOkvis = okvis::Time(data.stamp());
bool imuUpdated = false;
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(okvisEstimator_ != 0)
{
Eigen::Vector3d acc(data.imu().linearAcceleration()[0], data.imu().linearAcceleration()[1], data.imu().linearAcceleration()[2]);
Eigen::Vector3d ang(data.imu().angularVelocity()[0], data.imu().angularVelocity()[1], data.imu().angularVelocity()[2]);
imuUpdated = okvisEstimator_->addImuMeasurement(timeOkvis, acc, ang);
}
else
{
UWARN("Ignoring IMU, waiting for an image to initialize...");
lastImu_ = data.imu();
}
}
bool imageUpdated = false;
if(!data.imageRaw().empty())
{
UDEBUG("Image update stamp=%f", data.stamp());
std::vector<cv::Mat> images;
std::vector<CameraModel> models;
if(data.stereoCameraModel().isValidForProjection())
{
images.push_back(data.imageRaw());
images.push_back(data.rightRaw());
CameraModel mleft = data.stereoCameraModel().left();
// should be transform between IMU and camera
mleft.setLocalTransform(lastImu_.localTransform().inverse()*mleft.localTransform());
models.push_back(mleft);
CameraModel mright = data.stereoCameraModel().right();
// To support not rectified images
if(!imagesAlreadyRectified())
{
cv::Mat R = data.stereoCameraModel().R();
cv::Mat T = data.stereoCameraModel().T();
UASSERT(R.cols==3 && R.rows == 3);
UASSERT(T.cols==1 && T.rows == 3);
Transform extrinsics(R.at<double>(0,0), R.at<double>(0,1), R.at<double>(0,2), T.at<double>(0,0),
R.at<double>(1,0), R.at<double>(1,1), R.at<double>(1,2), T.at<double>(1,0),
R.at<double>(2,0), R.at<double>(2,1), R.at<double>(2,2), T.at<double>(2,0));
mright.setLocalTransform(mleft.localTransform() * extrinsics.inverse());
}
else
{
Transform extrinsics(1, 0, 0, 0,
0, 1, 0, data.stereoCameraModel().baseline(),
0, 0, 1, 0);
mright.setLocalTransform(extrinsics * mleft.localTransform());
}
models.push_back(mright);
}
else
{
UASSERT(int((data.imageRaw().cols/data.cameraModels().size())*data.cameraModels().size()) == data.imageRaw().cols);
int subImageWidth = data.imageRaw().cols/data.cameraModels().size();
for(unsigned int i=0; i<data.cameraModels().size(); ++i)
{
if(data.cameraModels()[i].isValidForProjection())
{
images.push_back(cv::Mat(data.imageRaw(), cv::Rect(subImageWidth*i, 0, subImageWidth, data.imageRaw().rows)));
CameraModel m = data.cameraModels()[i];
// should be transform between IMU and camera
m.setLocalTransform(lastImu_.localTransform().inverse()*m.localTransform());
models.push_back(m);
}
}
}
if(images.size())
{
// initialization
if(okvisEstimator_ == 0)
{
UDEBUG("Initialization");
if(lastImu_.empty())
{
UWARN("Ignoring Image, waiting for imu to initialize...");
return t;
}
okvis::VioParameters parameters;
if(configFilename_.empty())
{
UERROR("OKVIS config file is empty (%s)!", Parameters::kOdomOKVISConfigPath().c_str());
return t;
}
else
{
okvis::VioParametersReader vio_parameters_reader(configFilename_);
vio_parameters_reader.getParameters(parameters);
if(parameters.nCameraSystem.numCameras() > 0)
{
UWARN("Camera calibration included in OKVIS is ignored as calibration from received images will be used instead.");
}
parameters.nCameraSystem = okvis::cameras::NCameraSystem();
}
parameters.publishing.publishRate = parameters.imu.rate; // rate at which odometry updates are published only works properly if imu_rate/publish_rate is an integer!!
parameters.publishing.publishLandmarks = true; // select, if you want to publish landmarks at all
parameters.publishing.publishImuPropagatedState = true; // Should the state that is propagated with IMU messages be published? Or just the optimized ones?
parameters.publishing.landmarkQualityThreshold = 1.0e-2; // landmark with lower quality will not be published
parameters.publishing.maxLandmarkQuality = 0.05; // landmark with higher quality will be published with the maximum colour intensity
parameters.publishing.trackedBodyFrame = okvis::FrameName::B; // B or S, the frame of reference that will be expressed relative to the selected worldFrame
parameters.publishing.velocitiesFrame = okvis::FrameName::B; // Wc, B or S, the frames in which the velocities of the selected trackedBodyFrame will be expressed in
// non-hard coded parameters
parameters.imu.T_BS = okvis::kinematics::Transformation(lastImu_.localTransform().toEigen4d());
UINFO("Images are already rectified = %s", imagesAlreadyRectified()?"true":"false");
for(unsigned int i=0; i<models.size(); ++i)
{
okvis::cameras::NCameraSystem::DistortionType distType = okvis::cameras::NCameraSystem::NoDistortion;
std::shared_ptr<const okvis::cameras::CameraBase> cam;
if(!imagesAlreadyRectified())
{
if(models[i].D_raw().cols == 8)
{
okvis::cameras::RadialTangentialDistortion8 dist(
models[i].D_raw().at<double>(0,0),
models[i].D_raw().at<double>(0,1),
models[i].D_raw().at<double>(0,2),
models[i].D_raw().at<double>(0,3),
models[i].D_raw().at<double>(0,4),
models[i].D_raw().at<double>(0,5),
models[i].D_raw().at<double>(0,6),
models[i].D_raw().at<double>(0,7));
cam.reset(
new okvis::cameras::PinholeCamera<okvis::cameras::RadialTangentialDistortion8>(
models[i].imageWidth(),
models[i].imageHeight(),
models[i].K_raw().at<double>(0,0),
models[i].K_raw().at<double>(1,1),
models[i].K_raw().at<double>(0,2),
models[i].K_raw().at<double>(1,2),
dist));
distType = okvis::cameras::NCameraSystem::RadialTangential8;
UINFO("RadialTangential8");
}
else if(models[i].D_raw().cols == 6)
{
okvis::cameras::EquidistantDistortion dist(
models[i].D_raw().at<double>(0,0),
models[i].D_raw().at<double>(0,1),
models[i].D_raw().at<double>(0,4),
models[i].D_raw().at<double>(0,5));
cam.reset(new okvis::cameras::PinholeCamera<okvis::cameras::EquidistantDistortion>(
models[i].imageWidth(),
models[i].imageHeight(),
models[i].K_raw().at<double>(0,0),
models[i].K_raw().at<double>(1,1),
models[i].K_raw().at<double>(0,2),
models[i].K_raw().at<double>(1,2),
dist));
distType = okvis::cameras::NCameraSystem::Equidistant;
UINFO("Equidistant");
}
else if(models[i].D_raw().cols >= 4)
{
// To support not rectified images
okvis::cameras::RadialTangentialDistortion dist(
models[i].D_raw().at<double>(0,0),
models[i].D_raw().at<double>(0,1),
models[i].D_raw().at<double>(0,2),
models[i].D_raw().at<double>(0,3));
cam.reset(
new okvis::cameras::PinholeCamera<okvis::cameras::RadialTangentialDistortion>(
models[i].imageWidth(),
models[i].imageHeight(),
models[i].K_raw().at<double>(0,0),
models[i].K_raw().at<double>(1,1),
models[i].K_raw().at<double>(0,2),
models[i].K_raw().at<double>(1,2),
dist));
distType = okvis::cameras::NCameraSystem::RadialTangential;
UINFO("RadialTangential");
}
}
else // no distortion, rectified images
{
okvis::cameras::RadialTangentialDistortion dist(0,0,0,0);
cam.reset(
new okvis::cameras::PinholeCamera<okvis::cameras::RadialTangentialDistortion>(
models[i].imageWidth(),
models[i].imageHeight(),
models[i].K().at<double>(0,0),
models[i].K().at<double>(1,1),
models[i].K().at<double>(0,2),
models[i].K().at<double>(1,2),
dist));
distType = okvis::cameras::NCameraSystem::RadialTangential;
}
if(cam.get())
{
UINFO("model %d: %s", i, models[i].localTransform().prettyPrint().c_str());
Eigen::Vector3d r(models[i].localTransform().x(), models[i].localTransform().y(), models[i].localTransform().z());
parameters.nCameraSystem.addCamera(
std::shared_ptr<const okvis::kinematics::Transformation>(new okvis::kinematics::Transformation(r, models[i].localTransform().getQuaterniond().normalized())),
cam,
distType);
}
}
okvisEstimator_ = new okvis::ThreadedKFVio(parameters);
okvisEstimator_->setFullStateCallback(
std::bind(&OkvisCallbackHandler::fullStateCallback, okvisCallbackHandler_,
std::placeholders::_1, std::placeholders::_2,
std::placeholders::_3, std::placeholders::_4));
okvisEstimator_->setLandmarksCallback(
std::bind(&OkvisCallbackHandler::landmarksCallback, okvisCallbackHandler_,
std::placeholders::_1, std::placeholders::_2,
std::placeholders::_3));
okvisEstimator_->setBlocking(true);
}
for(unsigned int i=0; i<images.size(); ++i)
{
cv::Mat gray;
if(images[i].type() == CV_8UC3)
{
cv::cvtColor(images[i], gray, CV_BGR2GRAY);
}
else if(images[i].type() == CV_8UC1)
{
gray = images[i];
}
else
{
UFATAL("Not supported color type!");
}
imageUpdated = okvisEstimator_->addImage(timeOkvis, i, gray);
if(!imageUpdated)
{
UWARN("Image update with stamp %f delayed...", data.stamp());
}
}
if(imageUpdated)
{
++imagesProcessed_;
}
}
}
if((imageUpdated || imuUpdated) && imagesProcessed_ > 10)
{
Transform fixPos(-1,0,0,0, 0,-1,0,0, 0,0,1,0);
Transform fixRot(0,0,1,0, 0,-1,0,0, 1,0,0,0);
Transform p = okvisCallbackHandler_->getLastTransform();
if(!p.isNull())
{
p = fixPos * p * fixRot;
if(this->getPose().rotation().isIdentity())
{
initGravity_ = true;
this->reset(this->getPose()*p.rotation());
}
if(previousPose_.isIdentity())
{
previousPose_ = p;
}
// make it incremental
t = previousPose_.inverse()*p;
previousPose_ = p;
if(info)
{
info->reg.covariance = cv::Mat::eye(6,6, CV_64FC1);
info->reg.covariance *= this->framesProcessed() == 0?9999:0.0001;
// FIXME: the scale of landmarks doesn't seem to fit well the environment...
/*info->localMap = okvisCallbackHandler_->getLastLandmarks();
info->localMapSize = info->localMap.size();
for(std::map<int, cv::Point3f>::iterator iter=info->localMap.begin(); iter!=info->localMap.end(); ++iter)
{
iter->second = util3d::transformPoint(iter->second, fixPos);
}*/
}
}
if(imageUpdated)
{
UINFO("Odom update time = %fs p=%s", timer.elapsed(), p.prettyPrint().c_str());
}
}
#else
UERROR("RTAB-Map is not built with OKVIS support! Select another visual odometry approach.");
#endif
return t;
}
} // namespace rtabmap

View File

@@ -0,0 +1,308 @@
/*
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/odometry/OdometryViso2.h"
#include "rtabmap/core/OdometryInfo.h"
#include "rtabmap/core/util2d.h"
#include "rtabmap/utilite/ULogger.h"
#include "rtabmap/utilite/UTimer.h"
#include "rtabmap/utilite/UStl.h"
#ifdef RTABMAP_VISO2
#include <viso_stereo.h>
double computeFeatureFlow(const std::vector<Matcher::p_match>& matches)
{
double total_flow = 0.0;
for (size_t i = 0; i < matches.size(); ++i)
{
double x_diff = matches[i].u1c - matches[i].u1p;
double y_diff = matches[i].v1c - matches[i].v1p;
total_flow += sqrt(x_diff * x_diff + y_diff * y_diff);
}
return total_flow / matches.size();
}
#endif
namespace rtabmap {
OdometryViso2::OdometryViso2(const ParametersMap & parameters) :
Odometry(parameters),
#ifdef RTABMAP_VISO2
viso2_(0),
ref_frame_change_method_(0),
ref_frame_inlier_threshold_(Parameters::defaultOdomVisKeyFrameThr()),
ref_frame_motion_threshold_(5.0),
lost_(false),
keep_reference_frame_(false),
#endif
reference_motion_(Transform::getIdentity())
{
#ifdef RTABMAP_VISO2
Parameters::parse(parameters, Parameters::kOdomVisKeyFrameThr(), ref_frame_inlier_threshold_);
#endif
viso2Parameters_ = Parameters::filterParameters(parameters, "OdomViso2");
}
OdometryViso2::~OdometryViso2()
{
#ifdef RTABMAP_VISO2
delete viso2_;
#endif
}
void OdometryViso2::reset(const Transform & initialPose)
{
Odometry::reset(initialPose);
#ifdef RTABMAP_VISO2
if(viso2_)
{
delete viso2_;
viso2_ = 0;
}
lost_ = false;
reference_motion_.setIdentity();
previousLocalTransform_.setNull();
#endif
}
// return not null transform if odometry is correctly computed
Transform OdometryViso2::computeTransform(
SensorData & data,
const Transform & guess,
OdometryInfo * info)
{
Transform t;
#ifdef RTABMAP_VISO2
//based on https://github.com/srv/viso2/blob/indigo/viso2_ros/src/stereo_odometer.cpp
UTimer timer;
if(!data.depthRaw().empty())
{
UERROR("viso2 odometry doesn't support RGB-D data, only stereo. Aborting odometry update...");
return t;
}
if(data.imageRaw().empty() ||
data.imageRaw().rows != data.rightRaw().rows ||
data.imageRaw().cols != data.rightRaw().cols)
{
UERROR("Not compatible left (%dx%d) or right (%dx%d) image.",
data.imageRaw().rows,
data.imageRaw().cols,
data.rightRaw().rows,
data.rightRaw().cols);
return t;
}
if(!(data.stereoCameraModel().isValidForProjection() &&
data.stereoCameraModel().left().isValidForReprojection() &&
data.stereoCameraModel().right().isValidForReprojection()))
{
UERROR("Invalid stereo camera model!");
return t;
}
cv::Mat leftGray;
if(data.imageRaw().type() == CV_8UC3)
{
cv::cvtColor(data.imageRaw(), leftGray, CV_BGR2GRAY);
}
else if(data.imageRaw().type() == CV_8UC1)
{
leftGray = data.imageRaw();
}
else
{
UFATAL("Not supported color type!");
}
cv::Mat rightGray;
if(data.rightRaw().type() == CV_8UC3)
{
cv::cvtColor(data.rightRaw(), rightGray, CV_BGR2GRAY);
}
else if(data.rightRaw().type() == CV_8UC1)
{
rightGray = data.rightRaw();
}
else
{
UFATAL("Not supported color type!");
}
int32_t dims[] = {leftGray.cols, leftGray.rows, leftGray.cols};
cv::Mat covariance;
if(viso2_ == 0)
{
VisualOdometryStereo::parameters params;
params.base = params.match.base = data.stereoCameraModel().baseline();
params.calib.cu = params.match.cu = data.stereoCameraModel().left().cx();
params.calib.cv = params.match.cv = data.stereoCameraModel().left().cy();
params.calib.f = params.match.f = data.stereoCameraModel().left().fx();
Parameters::parse(viso2Parameters_, Parameters::kOdomViso2RansacIters(), params.ransac_iters);
Parameters::parse(viso2Parameters_, Parameters::kOdomViso2InlierThreshold(), params.inlier_threshold);
Parameters::parse(viso2Parameters_, Parameters::kOdomViso2Reweighting(), params.reweighting);
Parameters::parse(viso2Parameters_, Parameters::kOdomViso2MatchNmsN(), params.match.nms_n);
Parameters::parse(viso2Parameters_, Parameters::kOdomViso2MatchNmsTau(), params.match.nms_tau);
Parameters::parse(viso2Parameters_, Parameters::kOdomViso2MatchBinsize(), params.match.match_binsize);
Parameters::parse(viso2Parameters_, Parameters::kOdomViso2MatchRadius(), params.match.match_radius);
Parameters::parse(viso2Parameters_, Parameters::kOdomViso2MatchDispTolerance(), params.match.match_disp_tolerance);
Parameters::parse(viso2Parameters_, Parameters::kOdomViso2MatchOutlierDispTolerance(), params.match.outlier_disp_tolerance);
Parameters::parse(viso2Parameters_, Parameters::kOdomViso2MatchOutlierFlowTolerance(), params.match.outlier_flow_tolerance);
bool multistage = Parameters::defaultOdomViso2MatchMultiStage();
bool halfResolution = Parameters::defaultOdomViso2MatchHalfResolution();
Parameters::parse(viso2Parameters_, Parameters::kOdomViso2MatchMultiStage(), multistage);
Parameters::parse(viso2Parameters_, Parameters::kOdomViso2MatchHalfResolution() , halfResolution);
params.match.multi_stage = multistage?1:0;
params.match.half_resolution = halfResolution?1:0;
Parameters::parse(viso2Parameters_, Parameters::kOdomViso2MatchRefinement(), params.match.refinement);
Parameters::parse(viso2Parameters_, Parameters::kOdomViso2BucketMaxFeatures(), params.bucket.max_features);
Parameters::parse(viso2Parameters_, Parameters::kOdomViso2BucketWidth(), params.bucket.bucket_width);
Parameters::parse(viso2Parameters_, Parameters::kOdomViso2BucketHeight(), params.bucket.bucket_height);
viso2_ = new VisualOdometryStereo(params);
viso2_->process(leftGray.data, rightGray.data, dims);
t.setIdentity();
covariance = cv::Mat::eye(6,6, CV_64FC1)*9999.0;
}
else
{
bool success = viso2_->process(leftGray.data, rightGray.data, dims, lost_ || keep_reference_frame_);
if (success)
{
Matrix motionViso = Matrix::inv(viso2_->getMotion());
Transform motion(motionViso.val[0][0], motionViso.val[0][1], motionViso.val[0][2],motionViso.val[0][3],
motionViso.val[1][0], motionViso.val[1][1], motionViso.val[1][2],motionViso.val[1][3],
motionViso.val[2][0], motionViso.val[2][1], motionViso.val[2][2], motionViso.val[2][3]);
Transform camera_motion;
if(lost_ || keep_reference_frame_)
{
camera_motion = reference_motion_.inverse() * motion;
}
else
{
camera_motion = motion;
}
reference_motion_ = motion; // store last motion as reference
t=camera_motion;
//based on values set in viso2_ros
covariance = cv::Mat::eye(6,6, CV_64FC1);
covariance.at<double>(0,0) = 0.002;
covariance.at<double>(1,1) = 0.002;
covariance.at<double>(2,2) = 0.05;
covariance.at<double>(3,3) = 0.09;
covariance.at<double>(4,4) = 0.09;
covariance.at<double>(5,5) = 0.09;
lost_=false;
}
else
{
covariance = cv::Mat::eye(6,6, CV_64FC1)*9999.0;
lost_ = true;
}
if(success)
{
// Proceed depending on the reference frame change method
if(ref_frame_change_method_==1)
{
// calculate current feature flow
double feature_flow = computeFeatureFlow(viso2_->getMatches());
keep_reference_frame_ = (feature_flow < ref_frame_motion_threshold_);
}
else
{
keep_reference_frame_ = ref_frame_inlier_threshold_==0 || viso2_->getNumberOfInliers() > ref_frame_inlier_threshold_;
}
}
else
{
keep_reference_frame_ = false;
}
}
const Transform & localTransform = data.stereoCameraModel().localTransform();
if(!t.isNull() && !t.isIdentity() && !localTransform.isIdentity() && !localTransform.isNull())
{
// from camera frame to base frame
if(!previousLocalTransform_.isNull())
{
t = previousLocalTransform_ * t * localTransform.inverse();
}
else
{
t = localTransform * t * localTransform.inverse();
}
previousLocalTransform_ = localTransform;
}
if(info)
{
info->type = (int)kTypeViso2;
info->keyFrameAdded = !keep_reference_frame_;
info->reg.matches = viso2_->getNumberOfMatches();
info->reg.inliers = viso2_->getNumberOfInliers();
if(covariance.cols == 6 && covariance.rows == 6 && covariance.type() == CV_64FC1)
{
info->reg.covariance = covariance;
}
if(this->isInfoDataFilled())
{
std::vector<Matcher::p_match> matches = viso2_->getMatches();
info->refCorners.resize(matches.size());
info->newCorners.resize(matches.size());
info->cornerInliers.resize(matches.size());
for (size_t i = 0; i < matches.size(); ++i)
{
info->refCorners[i].x = matches[i].u1p;
info->refCorners[i].y = matches[i].v1p;
info->newCorners[i].x = matches[i].u1c;
info->newCorners[i].y = matches[i].v1c;
info->cornerInliers[i] = i;
}
}
}
UINFO("Odom update time = %fs lost=%s", timer.elapsed(), lost_?"true":"false");
#else
UERROR("RTAB-Map is not built with VISO2 support! Select another visual odometry approach.");
#endif
return t;
}
} // namespace rtabmap