mirror of
https://github.com/introlab/rtabmap.git
synced 2026-09-02 09:30:25 +08:00
0.19.2: Refactored SensorData interface. DBReader: Fixed GPS not published. #345: both g2o and gtsam working with GPS. g2o: added gravity edges.
This commit is contained in:
@@ -176,7 +176,7 @@ void CameraThread::postUpdate(SensorData * dataPtr, CameraInfo * info) const
|
||||
SensorData & data = *dataPtr;
|
||||
if(_colorOnly && !data.depthRaw().empty())
|
||||
{
|
||||
data.setDepthOrRightRaw(cv::Mat());
|
||||
data.setRGBDImage(data.imageRaw(), cv::Mat(), data.cameraModels());
|
||||
}
|
||||
|
||||
if(_distortionModel && !data.depthRaw().empty())
|
||||
@@ -187,7 +187,7 @@ void CameraThread::postUpdate(SensorData * dataPtr, CameraInfo * info) const
|
||||
{
|
||||
cv::Mat depth = data.depthRaw().clone();// make sure we are not modifying data in cached signatures.
|
||||
_distortionModel->undistort(depth);
|
||||
data.setDepthOrRightRaw(depth);
|
||||
data.setRGBDImage(data.imageRaw(), depth, data.cameraModels());
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -201,7 +201,7 @@ void CameraThread::postUpdate(SensorData * dataPtr, CameraInfo * info) const
|
||||
if(_bilateralFiltering && !data.depthRaw().empty())
|
||||
{
|
||||
UTimer timer;
|
||||
data.setDepthOrRightRaw(util2d::fastBilateralFiltering(data.depthRaw(), _bilateralSigmaS, _bilateralSigmaR));
|
||||
data.setRGBDImage(data.imageRaw(), util2d::fastBilateralFiltering(data.depthRaw(), _bilateralSigmaS, _bilateralSigmaR), data.cameraModels());
|
||||
if(info) info->timeBilateralFiltering = timer.ticks();
|
||||
}
|
||||
|
||||
@@ -217,8 +217,8 @@ void CameraThread::postUpdate(SensorData * dataPtr, CameraInfo * info) const
|
||||
}
|
||||
else
|
||||
{
|
||||
data.setImageRaw(util2d::decimate(data.imageRaw(), _imageDecimation));
|
||||
data.setDepthOrRightRaw(util2d::decimate(data.depthOrRightRaw(), _imageDecimation));
|
||||
cv::Mat image = util2d::decimate(data.imageRaw(), _imageDecimation);
|
||||
cv::Mat depthOrRight = util2d::decimate(data.depthOrRightRaw(), _imageDecimation);
|
||||
std::vector<CameraModel> models = data.cameraModels();
|
||||
for(unsigned int i=0; i<models.size(); ++i)
|
||||
{
|
||||
@@ -227,12 +227,18 @@ void CameraThread::postUpdate(SensorData * dataPtr, CameraInfo * info) const
|
||||
models[i] = models[i].scaled(1.0/double(_imageDecimation));
|
||||
}
|
||||
}
|
||||
data.setCameraModels(models);
|
||||
StereoCameraModel stereoModel = data.stereoCameraModel();
|
||||
if(stereoModel.isValidForProjection())
|
||||
if(!models.empty())
|
||||
{
|
||||
stereoModel.scale(1.0/double(_imageDecimation));
|
||||
data.setStereoCameraModel(stereoModel);
|
||||
data.setRGBDImage(image, depthOrRight, models);
|
||||
}
|
||||
else
|
||||
{
|
||||
StereoCameraModel stereoModel = data.stereoCameraModel();
|
||||
if(stereoModel.isValidForProjection())
|
||||
{
|
||||
stereoModel.scale(1.0/double(_imageDecimation));
|
||||
}
|
||||
data.setStereoImage(image, depthOrRight, stereoModel);
|
||||
}
|
||||
}
|
||||
if(info) info->timeImageDecimation = timer.ticks();
|
||||
@@ -243,11 +249,12 @@ void CameraThread::postUpdate(SensorData * dataPtr, CameraInfo * info) const
|
||||
UTimer timer;
|
||||
cv::Mat tmpRgb;
|
||||
cv::flip(data.imageRaw(), tmpRgb, 1);
|
||||
data.setImageRaw(tmpRgb);
|
||||
|
||||
UASSERT_MSG(data.cameraModels().size() <= 1 && !data.stereoCameraModel().isValidForProjection(), "Only single RGBD cameras are supported for mirroring.");
|
||||
if(data.cameraModels().size() && data.cameraModels()[0].cx())
|
||||
CameraModel tmpModel = data.cameraModels()[0];
|
||||
if(data.cameraModels()[0].cx())
|
||||
{
|
||||
CameraModel tmpModel(
|
||||
tmpModel = CameraModel(
|
||||
data.cameraModels()[0].fx(),
|
||||
data.cameraModels()[0].fy(),
|
||||
float(data.imageRaw().cols) - data.cameraModels()[0].cx(),
|
||||
@@ -255,14 +262,13 @@ void CameraThread::postUpdate(SensorData * dataPtr, CameraInfo * info) const
|
||||
data.cameraModels()[0].localTransform(),
|
||||
data.cameraModels()[0].Tx(),
|
||||
data.cameraModels()[0].imageSize());
|
||||
data.setCameraModel(tmpModel);
|
||||
}
|
||||
cv::Mat tmpDepth = data.depthOrRightRaw();
|
||||
if(!data.depthRaw().empty())
|
||||
{
|
||||
cv::Mat tmpDepth;
|
||||
cv::flip(data.depthRaw(), tmpDepth, 1);
|
||||
data.setDepthOrRightRaw(tmpDepth);
|
||||
}
|
||||
data.setRGBDImage(tmpRgb, tmpDepth, tmpModel);
|
||||
if(info) info->timeMirroring = timer.ticks();
|
||||
}
|
||||
|
||||
@@ -280,12 +286,11 @@ void CameraThread::postUpdate(SensorData * dataPtr, CameraInfo * info) const
|
||||
images.push_back(data.imageRaw().getUMat(cv::ACCESS_READ));
|
||||
images.push_back(data.rightRaw().getUMat(cv::ACCESS_READ));
|
||||
compensator->feed(topLeftCorners, images, masks);
|
||||
cv::Mat img = data.imageRaw().clone();
|
||||
compensator->apply(0, cv::Point(0,0), img, masks[0]);
|
||||
data.setImageRaw(img);
|
||||
img = data.rightRaw().clone();
|
||||
compensator->apply(1, cv::Point(0,0), img, masks[1]);
|
||||
data.setDepthOrRightRaw(img);
|
||||
cv::Mat imgLeft = data.imageRaw().clone();
|
||||
compensator->apply(0, cv::Point(0,0), imgLeft, masks[0]);
|
||||
cv::Mat imgRight = data.rightRaw().clone();
|
||||
compensator->apply(1, cv::Point(0,0), imgRight, masks[1]);
|
||||
data.setStereoImage(imgLeft, imgRight, data.stereoCameraModel());
|
||||
cv::detail::GainCompensator * gainCompensator = (cv::detail::GainCompensator*)compensator.get();
|
||||
UDEBUG("gains = %f %f ", gainCompensator->gains()[0], gainCompensator->gains()[1]);
|
||||
if(info) info->timeStereoExposureCompensation = timer.ticks();
|
||||
@@ -309,9 +314,7 @@ void CameraThread::postUpdate(SensorData * dataPtr, CameraInfo * info) const
|
||||
data.stereoCameraModel().localTransform(),
|
||||
-data.stereoCameraModel().baseline()*data.stereoCameraModel().left().fx(),
|
||||
data.stereoCameraModel().left().imageSize());
|
||||
data.setCameraModel(model);
|
||||
data.setDepthOrRightRaw(depth);
|
||||
data.setStereoCameraModel(StereoCameraModel());
|
||||
data.setRGBDImage(data.imageRaw(), depth, model);
|
||||
if(info) info->timeDisparity = timer.ticks();
|
||||
}
|
||||
if(_scanFromDepth &&
|
||||
@@ -367,7 +370,7 @@ void CameraThread::postUpdate(SensorData * dataPtr, CameraInfo * info) const
|
||||
}
|
||||
}
|
||||
}
|
||||
data.setLaserScanRaw(LaserScan(scan, (int)maxPoints, _scanRangeMax, format, baseToScan));
|
||||
data.setLaserScan(LaserScan(scan, (int)maxPoints, _scanRangeMax, format, baseToScan));
|
||||
if(info) info->timeScanFromDepth = timer.ticks();
|
||||
}
|
||||
else
|
||||
@@ -381,7 +384,7 @@ void CameraThread::postUpdate(SensorData * dataPtr, CameraInfo * info) const
|
||||
{
|
||||
UDEBUG("");
|
||||
// filter the scan after registration
|
||||
data.setLaserScanRaw(util3d::commonFiltering(data.laserScanRaw(), _scanDownsampleStep, _scanRangeMin, _scanRangeMax, _scanVoxelSize, _scanNormalsK, _scanNormalsRadius, _scanForceGroundNormalsUp));
|
||||
data.setLaserScan(util3d::commonFiltering(data.laserScanRaw(), _scanDownsampleStep, _scanRangeMin, _scanRangeMax, _scanVoxelSize, _scanNormalsK, _scanNormalsRadius, _scanForceGroundNormalsUp));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1760,10 +1760,9 @@ void DBDriverSqlite3::loadNodeDataQuery(std::list<Signature *> & signatures, boo
|
||||
viewPoint.z = sqlite3_column_double(ppStmt, index++);
|
||||
}
|
||||
|
||||
SensorData tmp = (*iter)->sensorData();
|
||||
LaserScan laserScan = tmp.laserScanCompressed();
|
||||
if(scan)
|
||||
{
|
||||
LaserScan laserScan;
|
||||
if(laserScanAngleMin < laserScanAngleMax && laserScanAngleInc != 0.0f)
|
||||
{
|
||||
laserScan = LaserScan(scanCompressed, (LaserScan::Format)laserScanFormat, laserScanMinRange, laserScanMaxRange, laserScanAngleMin, laserScanAngleMax, laserScanAngleInc, scanLocalTransform);
|
||||
@@ -1772,37 +1771,29 @@ void DBDriverSqlite3::loadNodeDataQuery(std::list<Signature *> & signatures, boo
|
||||
{
|
||||
laserScan = LaserScan(scanCompressed, laserScanMaxPts, laserScanMaxRange, (LaserScan::Format)laserScanFormat, scanLocalTransform);
|
||||
}
|
||||
(*iter)->sensorData().setLaserScan(laserScan);
|
||||
}
|
||||
if(models.size())
|
||||
if(images)
|
||||
{
|
||||
(*iter)->sensorData() = SensorData(
|
||||
laserScan,
|
||||
images?imageCompressed:tmp.imageCompressed(),
|
||||
images?depthOrRightCompressed:tmp.depthOrRightCompressed(),
|
||||
images?models:tmp.cameraModels(),
|
||||
(*iter)->id(),
|
||||
(*iter)->getStamp(),
|
||||
userData?userDataCompressed:tmp.userDataCompressed());
|
||||
if(models.size())
|
||||
{
|
||||
(*iter)->sensorData().setRGBDImage(imageCompressed, depthOrRightCompressed, models);
|
||||
}
|
||||
else
|
||||
{
|
||||
(*iter)->sensorData().setStereoImage(imageCompressed, depthOrRightCompressed, stereoModel);
|
||||
}
|
||||
}
|
||||
else
|
||||
if(userData)
|
||||
{
|
||||
(*iter)->sensorData() = SensorData(
|
||||
laserScan,
|
||||
images?imageCompressed:tmp.imageCompressed(),
|
||||
images?depthOrRightCompressed:tmp.depthOrRightCompressed(),
|
||||
images?stereoModel:tmp.stereoCameraModel(),
|
||||
(*iter)->id(),
|
||||
(*iter)->getStamp(),
|
||||
userData?userDataCompressed:tmp.userDataCompressed());
|
||||
(*iter)->sensorData().setUserData(userDataCompressed);
|
||||
}
|
||||
|
||||
if(occupancyGrid)
|
||||
{
|
||||
(*iter)->sensorData().setOccupancyGrid(groundCellsCompressed, obstacleCellsCompressed, emptyCellsCompressed, cellSize, viewPoint);
|
||||
}
|
||||
else
|
||||
{
|
||||
(*iter)->sensorData().setOccupancyGrid(tmp.gridGroundCellsCompressed(), tmp.gridObstacleCellsCompressed(), tmp.gridEmptyCellsCompressed(), tmp.gridCellSize(), tmp.gridViewPoint());
|
||||
}
|
||||
|
||||
rc = sqlite3_step(ppStmt); // next result...
|
||||
}
|
||||
UASSERT_MSG(rc == SQLITE_DONE, uFormat("DB error (%s): %s", _version.c_str(), sqlite3_errmsg(_ppDb)).c_str());
|
||||
|
||||
@@ -423,24 +423,23 @@ SensorData DBReader::getNextData(CameraInfo * info)
|
||||
{
|
||||
// select one camera
|
||||
int subImageWidth = data.imageRaw().cols/data.cameraModels().size();
|
||||
cv::Mat image;
|
||||
UASSERT(!data.imageRaw().empty() &&
|
||||
data.imageRaw().cols % data.cameraModels().size() == 0 &&
|
||||
_cameraIndex*subImageWidth < data.imageRaw().cols);
|
||||
data.setImageRaw(
|
||||
cv::Mat(data.imageRaw(),
|
||||
cv::Rect(_cameraIndex*subImageWidth, 0, subImageWidth, data.imageRaw().rows)).clone());
|
||||
image= cv::Mat(data.imageRaw(),
|
||||
cv::Rect(_cameraIndex*subImageWidth, 0, subImageWidth, data.imageRaw().rows)).clone();
|
||||
|
||||
cv::Mat depth;
|
||||
if(!data.depthOrRightRaw().empty())
|
||||
{
|
||||
UASSERT(data.depthOrRightRaw().cols % data.cameraModels().size() == 0 &&
|
||||
subImageWidth == data.depthOrRightRaw().cols/(int)data.cameraModels().size() &&
|
||||
_cameraIndex*subImageWidth < data.depthOrRightRaw().cols);
|
||||
data.setDepthOrRightRaw(
|
||||
cv::Mat(data.depthOrRightRaw(),
|
||||
cv::Rect(_cameraIndex*subImageWidth, 0, subImageWidth, data.depthOrRightRaw().rows)).clone());
|
||||
depth = cv::Mat(data.depthOrRightRaw(),
|
||||
cv::Rect(_cameraIndex*subImageWidth, 0, subImageWidth, data.depthOrRightRaw().rows)).clone();
|
||||
}
|
||||
CameraModel model = data.cameraModels().at(_cameraIndex);
|
||||
data.setCameraModel(model);
|
||||
data.setRGBDImage(image, depth, data.cameraModels().at(_cameraIndex));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -2643,21 +2643,10 @@ void Memory::removeRawData(int id, bool image, bool scan, bool userData)
|
||||
Signature * s = this->_getSignature(id);
|
||||
if(s)
|
||||
{
|
||||
if(image && (!_reextractLoopClosureFeatures || !_registrationPipeline->isImageRequired()))
|
||||
{
|
||||
s->sensorData().setImageRaw(cv::Mat());
|
||||
s->sensorData().setDepthOrRightRaw(cv::Mat());
|
||||
}
|
||||
if(scan && !_registrationPipeline->isScanRequired())
|
||||
{
|
||||
LaserScan scan = s->sensorData().laserScanRaw();
|
||||
scan.clear();
|
||||
s->sensorData().setLaserScanRaw(scan);
|
||||
}
|
||||
if(userData && !_registrationPipeline->isUserDataRequired())
|
||||
{
|
||||
s->sensorData().setUserDataRaw(cv::Mat());
|
||||
}
|
||||
s->sensorData().clearRawData(
|
||||
image && (!_reextractLoopClosureFeatures || !_registrationPipeline->isImageRequired()),
|
||||
scan && !_registrationPipeline->isScanRequired(),
|
||||
userData && !_registrationPipeline->isUserDataRequired());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3135,7 +3124,7 @@ Transform Memory::computeIcpTransformMulti(
|
||||
}
|
||||
|
||||
// scans are in base frame but for 2d scans, set the height so that correspondences matching works
|
||||
assembledData.setLaserScanRaw(
|
||||
assembledData.setLaserScan(
|
||||
LaserScan(assembledScan,
|
||||
fromScan.maxPoints()?fromScan.maxPoints():maxPoints,
|
||||
fromScan.rangeMax(),
|
||||
@@ -4103,7 +4092,7 @@ Signature * Memory::createSignature(const SensorData & inputData, const Transfor
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
data.setImageRaw(rectifiedImages);
|
||||
data.setRGBDImage(rectifiedImages, data.depthOrRightRaw(), data.cameraModels());
|
||||
}
|
||||
else if(data.stereoCameraModel().isValidForRectification())
|
||||
{
|
||||
@@ -4119,8 +4108,10 @@ Signature * Memory::createSignature(const SensorData & inputData, const Transfor
|
||||
}
|
||||
UASSERT(_rectStereoCameraModel.left().imageWidth() == data.stereoCameraModel().left().imageWidth());
|
||||
UASSERT(_rectStereoCameraModel.left().imageHeight() == data.stereoCameraModel().left().imageHeight());
|
||||
data.setImageRaw(_rectStereoCameraModel.left().rectifyImage(data.imageRaw()));
|
||||
data.setDepthOrRightRaw(_rectStereoCameraModel.right().rectifyImage(data.rightRaw()));
|
||||
data.setStereoImage(
|
||||
_rectStereoCameraModel.left().rectifyImage(data.imageRaw()),
|
||||
_rectStereoCameraModel.right().rectifyImage(data.rightRaw()),
|
||||
data.stereoCameraModel());
|
||||
imagesRectified = true;
|
||||
}
|
||||
else
|
||||
@@ -4159,24 +4150,41 @@ Signature * Memory::createSignature(const SensorData & inputData, const Transfor
|
||||
if(_imagePreDecimation > 1)
|
||||
{
|
||||
preDecimation = _imagePreDecimation;
|
||||
if(!decimatedData.rightRaw().empty() ||
|
||||
(decimatedData.depthRaw().rows == decimatedData.imageRaw().rows && decimatedData.depthRaw().cols == decimatedData.imageRaw().cols))
|
||||
{
|
||||
decimatedData.setDepthOrRightRaw(util2d::decimate(decimatedData.depthOrRightRaw(), _imagePreDecimation));
|
||||
}
|
||||
decimatedData.setImageRaw(util2d::decimate(decimatedData.imageRaw(), _imagePreDecimation));
|
||||
std::vector<CameraModel> cameraModels = decimatedData.cameraModels();
|
||||
for(unsigned int i=0; i<cameraModels.size(); ++i)
|
||||
{
|
||||
cameraModels[i] = cameraModels[i].scaled(1.0/double(_imagePreDecimation));
|
||||
}
|
||||
decimatedData.setCameraModels(cameraModels);
|
||||
StereoCameraModel stereoModel = decimatedData.stereoCameraModel();
|
||||
if(stereoModel.isValidForProjection())
|
||||
if(!cameraModels.empty())
|
||||
{
|
||||
stereoModel.scale(1.0/double(_imagePreDecimation));
|
||||
if(decimatedData.depthRaw().rows == decimatedData.imageRaw().rows &&
|
||||
decimatedData.depthRaw().cols == decimatedData.imageRaw().cols)
|
||||
{
|
||||
decimatedData.setRGBDImage(
|
||||
util2d::decimate(decimatedData.imageRaw(), _imagePreDecimation),
|
||||
util2d::decimate(decimatedData.depthOrRightRaw(), _imagePreDecimation),
|
||||
cameraModels);
|
||||
}
|
||||
else
|
||||
{
|
||||
decimatedData.setRGBDImage(
|
||||
util2d::decimate(decimatedData.imageRaw(), _imagePreDecimation),
|
||||
decimatedData.depthOrRightRaw(),
|
||||
cameraModels);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
StereoCameraModel stereoModel = decimatedData.stereoCameraModel();
|
||||
if(stereoModel.isValidForProjection())
|
||||
{
|
||||
stereoModel.scale(1.0/double(_imagePreDecimation));
|
||||
}
|
||||
decimatedData.setStereoImage(
|
||||
util2d::decimate(decimatedData.imageRaw(), _imagePreDecimation),
|
||||
util2d::decimate(decimatedData.depthOrRightRaw(), _imagePreDecimation),
|
||||
stereoModel);
|
||||
}
|
||||
decimatedData.setStereoCameraModel(stereoModel);
|
||||
}
|
||||
|
||||
UINFO("Extract features");
|
||||
@@ -5009,10 +5017,16 @@ Signature * Memory::createSignature(const SensorData & inputData, const Transfor
|
||||
s->setWordsDescriptors(wordsDescriptors);
|
||||
|
||||
// set raw data
|
||||
s->sensorData().setImageRaw(image);
|
||||
s->sensorData().setDepthOrRightRaw(depthOrRightImage);
|
||||
s->sensorData().setLaserScanRaw(laserScan);
|
||||
s->sensorData().setUserDataRaw(data.userDataRaw());
|
||||
if(!cameraModels.empty())
|
||||
{
|
||||
s->sensorData().setRGBDImage(image, depthOrRightImage, cameraModels, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
s->sensorData().setStereoImage(image, depthOrRightImage, stereoCameraModel, false);
|
||||
}
|
||||
s->sensorData().setLaserScan(laserScan, false);
|
||||
s->sensorData().setUserData(data.userDataRaw(), false);
|
||||
|
||||
s->sensorData().setGroundTruth(data.groundTruth());
|
||||
s->sensorData().setGPS(data.gps());
|
||||
@@ -5059,6 +5073,7 @@ Signature * Memory::createSignature(const SensorData & inputData, const Transfor
|
||||
if(!data.globalPose().isNull() && data.globalPoseCovariance().cols==6 && data.globalPoseCovariance().rows==6 && data.globalPoseCovariance().cols==CV_64FC1)
|
||||
{
|
||||
s->addLink(Link(s->id(), s->id(), Link::kPosePrior, data.globalPose(), data.globalPoseCovariance().inv()));
|
||||
UDEBUG("Added global pose prior: %s", data.globalPose().prettyPrint().c_str());
|
||||
|
||||
if(data.gps().stamp() > 0.0)
|
||||
{
|
||||
@@ -5070,14 +5085,17 @@ Signature * Memory::createSignature(const SensorData & inputData, const Transfor
|
||||
if(_gpsOrigin.stamp() <= 0.0)
|
||||
{
|
||||
_gpsOrigin = data.gps();
|
||||
UINFO("Added GPS origin: long=%f lat=%f alt=%f bearing=%f error=%f", data.gps().longitude(), data.gps().latitude(), data.gps().altitude(), data.gps().bearing(), data.gps().error());
|
||||
}
|
||||
cv::Point3f pt = data.gps().toGeodeticCoords().toENU_WGS84(_gpsOrigin.toGeodeticCoords());
|
||||
Transform gpsPose(pt.x, pt.y, pose.z(), 0, 0, -(data.gps().bearing()-90.0)*180.0/M_PI);
|
||||
cv::Mat gpsInfMatrix = cv::Mat::eye(6,6,CV_64FC1)/9999.0; // variance not used >= 9999
|
||||
if(data.gps().error() > 0.0)
|
||||
{
|
||||
// only set x, y and z as we don't know variance for other degrees of freedom.
|
||||
gpsInfMatrix.at<double>(0,0) = gpsInfMatrix.at<double>(1,1) = gpsInfMatrix.at<double>(2,2) = 1.0/data.gps().error();
|
||||
UDEBUG("Added GPS prior: x=%f y=%f z=%f yaw=%f", gpsPose.x(), gpsPose.y(), gpsPose.z(), gpsPose.theta());
|
||||
// only set x, y as we don't know variance for other degrees of freedom.
|
||||
gpsInfMatrix.at<double>(0,0) = gpsInfMatrix.at<double>(1,1) = 1.0/data.gps().error();
|
||||
gpsInfMatrix.at<double>(2,2) = 1; // z variance is set to avoid issues with g2o and gtsam requiring a prior on Z
|
||||
s->addLink(Link(s->id(), s->id(), Link::kPosePrior, gpsPose, gpsInfMatrix));
|
||||
}
|
||||
else
|
||||
|
||||
@@ -405,20 +405,27 @@ Transform Odometry::process(SensorData & data, const Transform & guessIn, Odomet
|
||||
{
|
||||
// Decimation of images with calibrations
|
||||
SensorData decimatedData = data;
|
||||
decimatedData.setImageRaw(util2d::decimate(decimatedData.imageRaw(), _imageDecimation));
|
||||
decimatedData.setDepthOrRightRaw(util2d::decimate(decimatedData.depthOrRightRaw(), _imageDecimation));
|
||||
cv::Mat rgbLeft = util2d::decimate(decimatedData.imageRaw(), _imageDecimation);
|
||||
cv::Mat depthRight = util2d::decimate(decimatedData.depthOrRightRaw(), _imageDecimation);
|
||||
std::vector<CameraModel> cameraModels = decimatedData.cameraModels();
|
||||
for(unsigned int i=0; i<cameraModels.size(); ++i)
|
||||
{
|
||||
cameraModels[i] = cameraModels[i].scaled(1.0/double(_imageDecimation));
|
||||
}
|
||||
decimatedData.setCameraModels(cameraModels);
|
||||
StereoCameraModel stereoModel = decimatedData.stereoCameraModel();
|
||||
if(stereoModel.isValidForProjection())
|
||||
if(!cameraModels.empty())
|
||||
{
|
||||
stereoModel.scale(1.0/double(_imageDecimation));
|
||||
decimatedData.setRGBDImage(rgbLeft, depthRight, cameraModels);
|
||||
}
|
||||
decimatedData.setStereoCameraModel(stereoModel);
|
||||
else
|
||||
{
|
||||
StereoCameraModel stereoModel = decimatedData.stereoCameraModel();
|
||||
if(stereoModel.isValidForProjection())
|
||||
{
|
||||
stereoModel.scale(1.0/double(_imageDecimation));
|
||||
}
|
||||
decimatedData.setStereoImage(rgbLeft, depthRight, stereoModel);
|
||||
}
|
||||
|
||||
|
||||
// compute transform
|
||||
t = this->computeTransform(decimatedData, guess, info);
|
||||
|
||||
@@ -176,13 +176,16 @@ void Optimizer::getConnectedGraph(
|
||||
std::multimap<int, int> biLinks;
|
||||
for(std::multimap<int, Link>::const_iterator iter=linksIn.begin(); iter!=linksIn.end(); ++iter)
|
||||
{
|
||||
UASSERT_MSG(graph::findLink(biLinks, iter->second.from(), iter->second.to()) == biLinks.end(),
|
||||
uFormat("Input links should be unique between two poses (%d->%d).",
|
||||
iter->second.from(), iter->second.to()).c_str());
|
||||
biLinks.insert(std::make_pair(iter->second.from(), iter->second.to()));
|
||||
if(iter->second.from() != iter->second.to())
|
||||
{
|
||||
biLinks.insert(std::make_pair(iter->second.to(), iter->second.from()));
|
||||
UASSERT_MSG(graph::findLink(biLinks, iter->second.from(), iter->second.to()) == biLinks.end(),
|
||||
uFormat("Input links should be unique between two poses (%d->%d).",
|
||||
iter->second.from(), iter->second.to()).c_str());
|
||||
biLinks.insert(std::make_pair(iter->second.from(), iter->second.to()));
|
||||
if(iter->second.from() != iter->second.to())
|
||||
{
|
||||
biLinks.insert(std::make_pair(iter->second.to(), iter->second.from()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,6 +200,14 @@ void Optimizer::getConnectedGraph(
|
||||
if(posesOut.empty())
|
||||
{
|
||||
posesOut.insert(*posesIn.find(fromId));
|
||||
// add prior links
|
||||
for(std::multimap<int, Link>::const_iterator pter=linksIn.find(fromId); pter!=linksIn.end() && pter->first==fromId; ++pter)
|
||||
{
|
||||
if(pter->second.from() == pter->second.to())
|
||||
{
|
||||
linksOut.insert(*pter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(std::multimap<int, int>::const_iterator iter=biLinks.find(fromId); iter!=biLinks.end() && iter->first==fromId; ++iter)
|
||||
@@ -211,6 +222,15 @@ void Optimizer::getConnectedGraph(
|
||||
if(!uContains(posesOut, toId))
|
||||
{
|
||||
posesOut.insert(*posesIn.find(toId));//std::make_pair(toId, posesOut.at(fromId) * (kter->second.from()==fromId?kter->second.transform():kter->second.transform().inverse())));
|
||||
// add prior links
|
||||
for(std::multimap<int, Link>::const_iterator pter=linksIn.find(toId); pter!=linksIn.end() && pter->first==toId; ++pter)
|
||||
{
|
||||
if(pter->second.from() == pter->second.to())
|
||||
{
|
||||
linksOut.insert(*pter);
|
||||
}
|
||||
}
|
||||
|
||||
if(curentPoses.find(toId) == curentPoses.end())
|
||||
{
|
||||
nextPoses.insert(toId);
|
||||
|
||||
@@ -794,7 +794,7 @@ Transform RegistrationIcp::computeTransformationImpl(
|
||||
// update output scans
|
||||
if(fromScan.is2d())
|
||||
{
|
||||
fromSignature.sensorData().setLaserScanRaw(
|
||||
fromSignature.sensorData().setLaserScan(
|
||||
LaserScan(
|
||||
util3d::laserScan2dFromPointCloud(*fromCloudNormals, fromScan.localTransform().inverse()),
|
||||
maxLaserScansFrom,
|
||||
@@ -804,7 +804,7 @@ Transform RegistrationIcp::computeTransformationImpl(
|
||||
}
|
||||
else
|
||||
{
|
||||
fromSignature.sensorData().setLaserScanRaw(
|
||||
fromSignature.sensorData().setLaserScan(
|
||||
LaserScan(
|
||||
util3d::laserScanFromPointCloud(*fromCloudNormals, fromScan.localTransform().inverse()),
|
||||
maxLaserScansFrom,
|
||||
@@ -814,7 +814,7 @@ Transform RegistrationIcp::computeTransformationImpl(
|
||||
}
|
||||
if(toScan.is2d())
|
||||
{
|
||||
toSignature.sensorData().setLaserScanRaw(
|
||||
toSignature.sensorData().setLaserScan(
|
||||
LaserScan(
|
||||
util3d::laserScan2dFromPointCloud(*toCloudNormals, (guess*toScan.localTransform()).inverse()),
|
||||
maxLaserScansTo,
|
||||
@@ -824,7 +824,7 @@ Transform RegistrationIcp::computeTransformationImpl(
|
||||
}
|
||||
else
|
||||
{
|
||||
toSignature.sensorData().setLaserScanRaw(
|
||||
toSignature.sensorData().setLaserScan(
|
||||
LaserScan(
|
||||
util3d::laserScanFromPointCloud(*toCloudNormals, (guess*toScan.localTransform()).inverse()),
|
||||
maxLaserScansTo,
|
||||
@@ -913,7 +913,7 @@ Transform RegistrationIcp::computeTransformationImpl(
|
||||
// update output scans
|
||||
if(fromScan.is2d())
|
||||
{
|
||||
fromSignature.sensorData().setLaserScanRaw(
|
||||
fromSignature.sensorData().setLaserScan(
|
||||
LaserScan(
|
||||
util3d::laserScan2dFromPointCloud(*fromCloudFiltered, fromScan.localTransform().inverse()),
|
||||
maxLaserScansFrom,
|
||||
@@ -923,7 +923,7 @@ Transform RegistrationIcp::computeTransformationImpl(
|
||||
}
|
||||
else
|
||||
{
|
||||
fromSignature.sensorData().setLaserScanRaw(
|
||||
fromSignature.sensorData().setLaserScan(
|
||||
LaserScan(
|
||||
util3d::laserScanFromPointCloud(*fromCloudFiltered, fromScan.localTransform().inverse()),
|
||||
maxLaserScansFrom,
|
||||
@@ -933,7 +933,7 @@ Transform RegistrationIcp::computeTransformationImpl(
|
||||
}
|
||||
if(toScan.is2d())
|
||||
{
|
||||
toSignature.sensorData().setLaserScanRaw(
|
||||
toSignature.sensorData().setLaserScan(
|
||||
LaserScan(
|
||||
util3d::laserScan2dFromPointCloud(*toCloudFiltered, (guess*toScan.localTransform()).inverse()),
|
||||
maxLaserScansTo,
|
||||
@@ -943,7 +943,7 @@ Transform RegistrationIcp::computeTransformationImpl(
|
||||
}
|
||||
else
|
||||
{
|
||||
toSignature.sensorData().setLaserScanRaw(
|
||||
toSignature.sensorData().setLaserScan(
|
||||
LaserScan(
|
||||
util3d::laserScanFromPointCloud(*toCloudFiltered, (guess*toScan.localTransform()).inverse()),
|
||||
maxLaserScansTo,
|
||||
|
||||
@@ -54,26 +54,8 @@ SensorData::SensorData(
|
||||
_stamp(stamp),
|
||||
_cellSize(0.0f)
|
||||
{
|
||||
if(image.rows == 1)
|
||||
{
|
||||
UASSERT(image.type() == CV_8UC1); // Bytes
|
||||
_imageCompressed = image;
|
||||
}
|
||||
else if(!image.empty())
|
||||
{
|
||||
UASSERT(image.type() == CV_8UC1 || // Mono
|
||||
image.type() == CV_8UC3); // RGB
|
||||
_imageRaw = image;
|
||||
}
|
||||
|
||||
if(userData.type() == CV_8UC1 && userData.rows == 1 && userData.cols > int(3*sizeof(int))) // Bytes
|
||||
{
|
||||
_userDataCompressed = userData; // assume compressed
|
||||
}
|
||||
else
|
||||
{
|
||||
_userDataRaw = userData;
|
||||
}
|
||||
setRGBDImage(image, cv::Mat(), CameraModel());
|
||||
setUserData(userData);
|
||||
}
|
||||
|
||||
// Mono constructor
|
||||
@@ -85,29 +67,10 @@ SensorData::SensorData(
|
||||
const cv::Mat & userData) :
|
||||
_id(id),
|
||||
_stamp(stamp),
|
||||
_cameraModels(std::vector<CameraModel>(1, cameraModel)),
|
||||
_cellSize(0.0f)
|
||||
{
|
||||
if(image.rows == 1)
|
||||
{
|
||||
UASSERT(image.type() == CV_8UC1); // Bytes
|
||||
_imageCompressed = image;
|
||||
}
|
||||
else if(!image.empty())
|
||||
{
|
||||
UASSERT(image.type() == CV_8UC1 || // Mono
|
||||
image.type() == CV_8UC3); // RGB
|
||||
_imageRaw = image;
|
||||
}
|
||||
|
||||
if(userData.type() == CV_8UC1 && userData.rows == 1 && userData.cols > int(3*sizeof(int))) // Bytes
|
||||
{
|
||||
_userDataCompressed = userData; // assume compressed
|
||||
}
|
||||
else
|
||||
{
|
||||
_userDataRaw = userData;
|
||||
}
|
||||
setRGBDImage(image, cv::Mat(), cameraModel);
|
||||
setUserData(userData);
|
||||
}
|
||||
|
||||
// RGB-D constructor
|
||||
@@ -120,41 +83,10 @@ SensorData::SensorData(
|
||||
const cv::Mat & userData) :
|
||||
_id(id),
|
||||
_stamp(stamp),
|
||||
_cameraModels(std::vector<CameraModel>(1, cameraModel)),
|
||||
_cellSize(0.0f)
|
||||
{
|
||||
if(rgb.rows == 1)
|
||||
{
|
||||
UASSERT(rgb.type() == CV_8UC1); // Bytes
|
||||
_imageCompressed = rgb;
|
||||
}
|
||||
else if(!rgb.empty())
|
||||
{
|
||||
UASSERT(rgb.type() == CV_8UC1 || // Mono
|
||||
rgb.type() == CV_8UC3); // RGB
|
||||
_imageRaw = rgb;
|
||||
}
|
||||
|
||||
if(depth.rows == 1)
|
||||
{
|
||||
UASSERT(depth.type() == CV_8UC1); // Bytes
|
||||
_depthOrRightCompressed = depth;
|
||||
}
|
||||
else if(!depth.empty())
|
||||
{
|
||||
UASSERT(depth.type() == CV_32FC1 || // Depth in meter
|
||||
depth.type() == CV_16UC1); // Depth in millimetre
|
||||
_depthOrRightRaw = depth;
|
||||
}
|
||||
|
||||
if(userData.type() == CV_8UC1 && userData.rows == 1 && userData.cols > int(3*sizeof(int))) // Bytes
|
||||
{
|
||||
_userDataCompressed = userData; // assume compressed
|
||||
}
|
||||
else
|
||||
{
|
||||
_userDataRaw = userData;
|
||||
}
|
||||
setRGBDImage(rgb, depth, cameraModel);
|
||||
setUserData(userData);
|
||||
}
|
||||
|
||||
// RGB-D constructor + laser scan
|
||||
@@ -168,49 +100,11 @@ SensorData::SensorData(
|
||||
const cv::Mat & userData) :
|
||||
_id(id),
|
||||
_stamp(stamp),
|
||||
_cameraModels(std::vector<CameraModel>(1, cameraModel)),
|
||||
_cellSize(0.0f)
|
||||
{
|
||||
if(rgb.rows == 1)
|
||||
{
|
||||
UASSERT(rgb.type() == CV_8UC1); // Bytes
|
||||
_imageCompressed = rgb;
|
||||
}
|
||||
else if(!rgb.empty())
|
||||
{
|
||||
UASSERT(rgb.type() == CV_8UC1 || // Mono
|
||||
rgb.type() == CV_8UC3); // RGB
|
||||
_imageRaw = rgb;
|
||||
}
|
||||
if(depth.rows == 1)
|
||||
{
|
||||
UASSERT(depth.type() == CV_8UC1); // Bytes
|
||||
_depthOrRightCompressed = depth;
|
||||
}
|
||||
else if(!depth.empty())
|
||||
{
|
||||
UASSERT(depth.type() == CV_32FC1 || // Depth in meter
|
||||
depth.type() == CV_16UC1); // Depth in millimetre
|
||||
_depthOrRightRaw = depth;
|
||||
}
|
||||
|
||||
if(!laserScan.isCompressed())
|
||||
{
|
||||
_laserScanRaw = laserScan;
|
||||
}
|
||||
else
|
||||
{
|
||||
_laserScanCompressed = laserScan;
|
||||
}
|
||||
|
||||
if(userData.type() == CV_8UC1 && userData.rows == 1 && userData.cols > int(3*sizeof(int))) // Bytes
|
||||
{
|
||||
_userDataCompressed = userData; // assume compressed
|
||||
}
|
||||
else
|
||||
{
|
||||
_userDataRaw = userData;
|
||||
}
|
||||
setRGBDImage(rgb, depth, cameraModel);
|
||||
setLaserScan(laserScan);
|
||||
setUserData(userData);
|
||||
}
|
||||
|
||||
// Multi-cameras RGB-D constructor
|
||||
@@ -223,40 +117,10 @@ SensorData::SensorData(
|
||||
const cv::Mat & userData) :
|
||||
_id(id),
|
||||
_stamp(stamp),
|
||||
_cameraModels(cameraModels),
|
||||
_cellSize(0.0f)
|
||||
{
|
||||
if(rgb.rows == 1)
|
||||
{
|
||||
UASSERT(rgb.type() == CV_8UC1); // Bytes
|
||||
_imageCompressed = rgb;
|
||||
}
|
||||
else if(!rgb.empty())
|
||||
{
|
||||
UASSERT(rgb.type() == CV_8UC1 || // Mono
|
||||
rgb.type() == CV_8UC3); // RGB
|
||||
_imageRaw = rgb;
|
||||
}
|
||||
if(depth.rows == 1)
|
||||
{
|
||||
UASSERT(depth.type() == CV_8UC1); // Bytes
|
||||
_depthOrRightCompressed = depth;
|
||||
}
|
||||
else if(!depth.empty())
|
||||
{
|
||||
UASSERT(depth.type() == CV_32FC1 || // Depth in meter
|
||||
depth.type() == CV_16UC1); // Depth in millimetre
|
||||
_depthOrRightRaw = depth;
|
||||
}
|
||||
|
||||
if(userData.type() == CV_8UC1 && userData.rows == 1 && userData.cols > int(3*sizeof(int))) // Bytes
|
||||
{
|
||||
_userDataCompressed = userData; // assume compressed
|
||||
}
|
||||
else
|
||||
{
|
||||
_userDataRaw = userData;
|
||||
}
|
||||
setRGBDImage(rgb, depth, cameraModels);
|
||||
setUserData(userData);
|
||||
}
|
||||
|
||||
// Multi-cameras RGB-D constructor + laser scan
|
||||
@@ -270,49 +134,11 @@ SensorData::SensorData(
|
||||
const cv::Mat & userData) :
|
||||
_id(id),
|
||||
_stamp(stamp),
|
||||
_cameraModels(cameraModels),
|
||||
_cellSize(0.0f)
|
||||
{
|
||||
if(rgb.rows == 1)
|
||||
{
|
||||
UASSERT(rgb.type() == CV_8UC1); // Bytes
|
||||
_imageCompressed = rgb;
|
||||
}
|
||||
else if(!rgb.empty())
|
||||
{
|
||||
UASSERT(rgb.type() == CV_8UC1 || // Mono
|
||||
rgb.type() == CV_8UC3); // RGB
|
||||
_imageRaw = rgb;
|
||||
}
|
||||
if(depth.rows == 1)
|
||||
{
|
||||
UASSERT(depth.type() == CV_8UC1); // Bytes
|
||||
_depthOrRightCompressed = depth;
|
||||
}
|
||||
else if(!depth.empty())
|
||||
{
|
||||
UASSERT(depth.type() == CV_32FC1 || // Depth in meter
|
||||
depth.type() == CV_16UC1); // Depth in millimetre
|
||||
_depthOrRightRaw = depth;
|
||||
}
|
||||
|
||||
if(!laserScan.isCompressed())
|
||||
{
|
||||
_laserScanRaw = laserScan;
|
||||
}
|
||||
else
|
||||
{
|
||||
_laserScanCompressed = laserScan;
|
||||
}
|
||||
|
||||
if(userData.type() == CV_8UC1 && userData.rows == 1 && userData.cols > int(3*sizeof(int))) // Bytes
|
||||
{
|
||||
_userDataCompressed = userData; // assume compressed
|
||||
}
|
||||
else
|
||||
{
|
||||
_userDataRaw = userData;
|
||||
}
|
||||
setRGBDImage(rgb, depth, cameraModels);
|
||||
setLaserScan(laserScan);
|
||||
setUserData(userData);
|
||||
}
|
||||
|
||||
// Stereo constructor
|
||||
@@ -325,42 +151,10 @@ SensorData::SensorData(
|
||||
const cv::Mat & userData):
|
||||
_id(id),
|
||||
_stamp(stamp),
|
||||
_stereoCameraModel(cameraModel),
|
||||
_cellSize(0.0f)
|
||||
{
|
||||
if(left.rows == 1)
|
||||
{
|
||||
UASSERT(left.type() == CV_8UC1); // Bytes
|
||||
_imageCompressed = left;
|
||||
}
|
||||
else if(!left.empty())
|
||||
{
|
||||
UASSERT(left.type() == CV_8UC1 || // Mono
|
||||
left.type() == CV_8UC3 || // RGB
|
||||
left.type() == CV_16UC1); // IR
|
||||
_imageRaw = left;
|
||||
}
|
||||
if(right.rows == 1)
|
||||
{
|
||||
UASSERT(right.type() == CV_8UC1); // Bytes
|
||||
_depthOrRightCompressed = right;
|
||||
}
|
||||
else if(!right.empty())
|
||||
{
|
||||
UASSERT(right.type() == CV_8UC1 || // Mono
|
||||
right.type() == CV_16UC1); // IR
|
||||
_depthOrRightRaw = right;
|
||||
}
|
||||
|
||||
if(userData.type() == CV_8UC1 && userData.rows == 1 && userData.cols > int(3*sizeof(int))) // Bytes
|
||||
{
|
||||
_userDataCompressed = userData; // assume compressed
|
||||
}
|
||||
else
|
||||
{
|
||||
_userDataRaw = userData;
|
||||
}
|
||||
|
||||
setStereoImage(left, right, cameraModel);
|
||||
setUserData(userData);
|
||||
}
|
||||
|
||||
// Stereo constructor + 2d laser scan
|
||||
@@ -374,48 +168,11 @@ SensorData::SensorData(
|
||||
const cv::Mat & userData) :
|
||||
_id(id),
|
||||
_stamp(stamp),
|
||||
_stereoCameraModel(cameraModel),
|
||||
_cellSize(0.0f)
|
||||
{
|
||||
if(left.rows == 1)
|
||||
{
|
||||
UASSERT(left.type() == CV_8UC1); // Bytes
|
||||
_imageCompressed = left;
|
||||
}
|
||||
else if(!left.empty())
|
||||
{
|
||||
UASSERT(left.type() == CV_8UC1 || // Mono
|
||||
left.type() == CV_8UC3); // RGB
|
||||
_imageRaw = left;
|
||||
}
|
||||
if(right.rows == 1)
|
||||
{
|
||||
UASSERT(right.type() == CV_8UC1); // Bytes
|
||||
_depthOrRightCompressed = right;
|
||||
}
|
||||
else if(!right.empty())
|
||||
{
|
||||
UASSERT(right.type() == CV_8UC1); // Mono
|
||||
_depthOrRightRaw = right;
|
||||
}
|
||||
|
||||
if(!laserScan.isCompressed())
|
||||
{
|
||||
_laserScanRaw = laserScan;
|
||||
}
|
||||
else
|
||||
{
|
||||
_laserScanCompressed = laserScan;
|
||||
}
|
||||
|
||||
if(userData.type() == CV_8UC1 && userData.rows == 1 && userData.cols > int(3*sizeof(int))) // Bytes
|
||||
{
|
||||
_userDataCompressed = userData; // assume compressed
|
||||
}
|
||||
else
|
||||
{
|
||||
_userDataRaw = userData;
|
||||
}
|
||||
setStereoImage(left, right, cameraModel);
|
||||
setLaserScan(laserScan);
|
||||
setUserData(userData);
|
||||
}
|
||||
|
||||
SensorData::SensorData(
|
||||
@@ -433,47 +190,208 @@ SensorData::~SensorData()
|
||||
{
|
||||
}
|
||||
|
||||
void SensorData::setRGBDImage(
|
||||
const cv::Mat & rgb,
|
||||
const cv::Mat & depth,
|
||||
const CameraModel & model,
|
||||
bool clearPreviousData)
|
||||
{
|
||||
std::vector<CameraModel> models;
|
||||
models.push_back(model);
|
||||
setRGBDImage(rgb, depth, models, clearPreviousData);
|
||||
}
|
||||
void SensorData::setRGBDImage(
|
||||
const cv::Mat & rgb,
|
||||
const cv::Mat & depth,
|
||||
const std::vector<CameraModel> & models,
|
||||
bool clearPreviousData)
|
||||
{
|
||||
if(!clearPreviousData && _stereoCameraModel.isValidForProjection())
|
||||
{
|
||||
UERROR("Sensor data has previously stereo images "
|
||||
"but clearPreviousData parameter is false. We "
|
||||
"will still clear previous data to avoid incompatibilities "
|
||||
"between raw and compressed data!");
|
||||
}
|
||||
bool clearData = clearPreviousData || _stereoCameraModel.isValidForProjection();
|
||||
|
||||
_stereoCameraModel = StereoCameraModel();
|
||||
_cameraModels = models;
|
||||
if(rgb.rows == 1)
|
||||
{
|
||||
UASSERT(rgb.type() == CV_8UC1); // Bytes
|
||||
_imageCompressed = rgb;
|
||||
if(clearData)
|
||||
{
|
||||
_imageRaw = cv::Mat();
|
||||
}
|
||||
}
|
||||
else if(!rgb.empty())
|
||||
{
|
||||
UASSERT(rgb.type() == CV_8UC1 || // Mono
|
||||
rgb.type() == CV_8UC3); // RGB
|
||||
_imageRaw = rgb;
|
||||
if(clearData)
|
||||
{
|
||||
_imageCompressed = cv::Mat();
|
||||
}
|
||||
}
|
||||
else if(clearData)
|
||||
{
|
||||
_imageRaw = cv::Mat();
|
||||
_imageCompressed = cv::Mat();
|
||||
}
|
||||
|
||||
if(depth.rows == 1)
|
||||
{
|
||||
UASSERT(depth.type() == CV_8UC1); // Bytes
|
||||
_depthOrRightCompressed = depth;
|
||||
if(clearData)
|
||||
{
|
||||
_depthOrRightRaw = cv::Mat();
|
||||
}
|
||||
}
|
||||
else if(!depth.empty())
|
||||
{
|
||||
UASSERT(depth.type() == CV_32FC1 || // Depth in meter
|
||||
depth.type() == CV_16UC1); // Depth in millimetre
|
||||
_depthOrRightRaw = depth;
|
||||
if(clearData)
|
||||
{
|
||||
_depthOrRightCompressed = cv::Mat();
|
||||
}
|
||||
}
|
||||
else if(clearData)
|
||||
{
|
||||
_depthOrRightRaw = cv::Mat();
|
||||
_depthOrRightCompressed = cv::Mat();
|
||||
}
|
||||
}
|
||||
void SensorData::setStereoImage(
|
||||
const cv::Mat & left,
|
||||
const cv::Mat & right,
|
||||
const StereoCameraModel & stereoCameraModel,
|
||||
bool clearPreviousData)
|
||||
{
|
||||
if(!clearPreviousData && !_cameraModels.empty())
|
||||
{
|
||||
UERROR("Sensor data has previously RGB-D/RGB images "
|
||||
"but clearPreviousData parameter is false. We "
|
||||
"will still clear previous data to avoid incompatibilities "
|
||||
"between raw and compressed data!");
|
||||
}
|
||||
bool clearData = clearPreviousData || !_cameraModels.empty();
|
||||
|
||||
_cameraModels.clear();
|
||||
_stereoCameraModel = stereoCameraModel;
|
||||
|
||||
if(left.rows == 1)
|
||||
{
|
||||
UASSERT(left.type() == CV_8UC1); // Bytes
|
||||
_imageCompressed = left;
|
||||
if(clearData)
|
||||
{
|
||||
_imageRaw = cv::Mat();
|
||||
}
|
||||
}
|
||||
else if(!left.empty())
|
||||
{
|
||||
UASSERT(left.type() == CV_8UC1 || // Mono
|
||||
left.type() == CV_8UC3); // RGB
|
||||
_imageRaw = left;
|
||||
if(clearData)
|
||||
{
|
||||
_imageCompressed = cv::Mat();
|
||||
}
|
||||
}
|
||||
else if(clearData)
|
||||
{
|
||||
_imageRaw = cv::Mat();
|
||||
_imageCompressed = cv::Mat();
|
||||
}
|
||||
|
||||
if(right.rows == 1)
|
||||
{
|
||||
UASSERT(right.type() == CV_8UC1); // Bytes
|
||||
_depthOrRightCompressed = right;
|
||||
if(clearData)
|
||||
{
|
||||
_depthOrRightRaw = cv::Mat();
|
||||
}
|
||||
}
|
||||
else if(!right.empty())
|
||||
{
|
||||
UASSERT(right.type() == CV_8UC1); // Mono
|
||||
_depthOrRightRaw = right;
|
||||
if(clearData)
|
||||
{
|
||||
_depthOrRightCompressed = cv::Mat();
|
||||
}
|
||||
}
|
||||
else if(clearData)
|
||||
{
|
||||
_depthOrRightRaw = cv::Mat();
|
||||
_depthOrRightCompressed = cv::Mat();
|
||||
}
|
||||
}
|
||||
void SensorData::setLaserScan(const LaserScan & laserScan, bool clearPreviousData)
|
||||
{
|
||||
if(!laserScan.isCompressed())
|
||||
{
|
||||
_laserScanRaw = laserScan;
|
||||
if(clearPreviousData)
|
||||
{
|
||||
_laserScanCompressed = LaserScan();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_laserScanCompressed = laserScan;
|
||||
if(clearPreviousData)
|
||||
{
|
||||
_laserScanRaw = LaserScan();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SensorData::setImageRaw(const cv::Mat & image)
|
||||
{
|
||||
UASSERT(image.empty() || image.rows > 1);
|
||||
_imageRaw = image;
|
||||
}
|
||||
void SensorData::setDepthOrRightRaw(const cv::Mat & image)
|
||||
{
|
||||
UASSERT(image.empty() || image.rows > 1);
|
||||
_depthOrRightRaw = image;
|
||||
}
|
||||
void SensorData::setLaserScanRaw(const LaserScan & scan)
|
||||
{
|
||||
UASSERT(scan.isEmpty() || !scan.isCompressed());
|
||||
_laserScanRaw = scan;
|
||||
}
|
||||
|
||||
void SensorData::setUserDataRaw(const cv::Mat & userDataRaw)
|
||||
{
|
||||
if(!userDataRaw.empty() && !_userDataRaw.empty())
|
||||
{
|
||||
UWARN("Cannot write new user data (%d bytes) over existing user "
|
||||
"data (%d bytes, %d compressed). Set user data of %d to null "
|
||||
"before setting a new one.",
|
||||
int(userDataRaw.total()*userDataRaw.elemSize()),
|
||||
int(_userDataRaw.total()*_userDataRaw.elemSize()),
|
||||
_userDataCompressed.cols,
|
||||
this->id());
|
||||
return;
|
||||
}
|
||||
_userDataRaw = userDataRaw;
|
||||
}
|
||||
|
||||
void SensorData::setUserData(const cv::Mat & userData)
|
||||
void SensorData::setUserData(const cv::Mat & userData, bool clearPreviousData)
|
||||
{
|
||||
if(!userData.empty() && (!_userDataCompressed.empty() || !_userDataRaw.empty()))
|
||||
if(clearPreviousData)
|
||||
{
|
||||
UWARN("Cannot write new user data (%d bytes) over existing user "
|
||||
"data (%d bytes, %d compressed). Set user data of %d to null "
|
||||
"before setting a new one.",
|
||||
int(userData.total()*userData.elemSize()),
|
||||
int(_userDataRaw.total()*_userDataRaw.elemSize()),
|
||||
_userDataCompressed.cols,
|
||||
this->id());
|
||||
return;
|
||||
_userDataRaw = cv::Mat();
|
||||
_userDataCompressed = cv::Mat();
|
||||
}
|
||||
_userDataRaw = cv::Mat();
|
||||
_userDataCompressed = cv::Mat();
|
||||
|
||||
if(!userData.empty())
|
||||
if(userData.type() == CV_8UC1 && userData.rows == 1 && userData.cols > int(3*sizeof(int))) // Bytes
|
||||
{
|
||||
if(userData.type() == CV_8UC1 && userData.rows == 1 && userData.cols > int(3*sizeof(int))) // Bytes
|
||||
_userDataCompressed = userData; // assume compressed
|
||||
}
|
||||
else
|
||||
{
|
||||
_userDataRaw = userData;
|
||||
if(!userData.empty())
|
||||
{
|
||||
_userDataCompressed = userData; // assume compressed
|
||||
}
|
||||
else
|
||||
{
|
||||
_userDataRaw = userData;
|
||||
_userDataCompressed = compressData2(userData);
|
||||
}
|
||||
}
|
||||
@@ -491,7 +409,8 @@ void SensorData::setOccupancyGrid(
|
||||
(!obstacles.empty() && (!_obstacleCellsCompressed.empty() || !_obstacleCellsRaw.empty())) ||
|
||||
(!empty.empty() && (!_emptyCellsCompressed.empty() || !_emptyCellsRaw.empty())))
|
||||
{
|
||||
UWARN("Occupancy grid cannot be overwritten! id=%d", this->id());
|
||||
UWARN("Occupancy grid cannot be overwritten! id=%d, Set occupancy grid of %d to null "
|
||||
"before setting a new one.", this->id());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -515,7 +434,6 @@ void SensorData::setOccupancyGrid(
|
||||
}
|
||||
else if(ground.type() == CV_8UC1)
|
||||
{
|
||||
UASSERT(ground.type() == CV_8UC1); // Bytes
|
||||
_groundCellsCompressed = ground;
|
||||
}
|
||||
}
|
||||
@@ -528,7 +446,6 @@ void SensorData::setOccupancyGrid(
|
||||
}
|
||||
else if(obstacles.type() == CV_8UC1)
|
||||
{
|
||||
UASSERT(obstacles.type() == CV_8UC1); // Bytes
|
||||
_obstacleCellsCompressed = obstacles;
|
||||
}
|
||||
}
|
||||
@@ -541,7 +458,6 @@ void SensorData::setOccupancyGrid(
|
||||
}
|
||||
else if(empty.type() == CV_8UC1)
|
||||
{
|
||||
UASSERT(empty.type() == CV_8UC1); // Bytes
|
||||
_emptyCellsCompressed = empty;
|
||||
}
|
||||
}
|
||||
@@ -868,6 +784,40 @@ long SensorData::getMemoryUsed() const // Return memory usage in Bytes
|
||||
_descriptors.total()*_descriptors.elemSize();
|
||||
}
|
||||
|
||||
void SensorData::clearCompressedData(bool images, bool scan, bool userData)
|
||||
{
|
||||
if(images)
|
||||
{
|
||||
_imageCompressed=cv::Mat();
|
||||
_depthOrRightCompressed=cv::Mat();
|
||||
}
|
||||
if(scan)
|
||||
{
|
||||
_laserScanCompressed.clear();
|
||||
}
|
||||
if(userData)
|
||||
{
|
||||
_userDataCompressed=cv::Mat();
|
||||
}
|
||||
}
|
||||
void SensorData::clearRawData(bool images, bool scan, bool userData)
|
||||
{
|
||||
if(images)
|
||||
{
|
||||
_imageRaw=cv::Mat();
|
||||
_depthOrRightRaw=cv::Mat();
|
||||
}
|
||||
if(scan)
|
||||
{
|
||||
_laserScanRaw.clear();
|
||||
}
|
||||
if(userData)
|
||||
{
|
||||
_userDataRaw=cv::Mat();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool SensorData::isPointVisibleFromCameras(const cv::Point3f & pt) const
|
||||
{
|
||||
if(_cameraModels.size() >= 1)
|
||||
|
||||
@@ -957,7 +957,7 @@ Transform OdometryF2M::computeTransform(
|
||||
if(mapScan.is2d())
|
||||
{
|
||||
|
||||
map_->sensorData().setLaserScanRaw(
|
||||
map_->sensorData().setLaserScan(
|
||||
LaserScan(
|
||||
mapScan.data(),
|
||||
0,
|
||||
@@ -967,7 +967,7 @@ Transform OdometryF2M::computeTransform(
|
||||
}
|
||||
else
|
||||
{
|
||||
map_->sensorData().setLaserScanRaw(
|
||||
map_->sensorData().setLaserScan(
|
||||
LaserScan(
|
||||
mapScan.data(),
|
||||
0,
|
||||
@@ -1137,7 +1137,7 @@ Transform OdometryF2M::computeTransform(
|
||||
if(lastFrame_->sensorData().laserScanRaw().is2d())
|
||||
{
|
||||
Transform mapViewpoint(-newFramePose.x(), -newFramePose.y(),0,0,0,0);
|
||||
map_->sensorData().setLaserScanRaw(
|
||||
map_->sensorData().setLaserScan(
|
||||
LaserScan(
|
||||
util3d::laserScan2dFromPointCloud(*mapCloudNormals, mapViewpoint),
|
||||
0,
|
||||
@@ -1148,7 +1148,7 @@ Transform OdometryF2M::computeTransform(
|
||||
else
|
||||
{
|
||||
Transform mapViewpoint(-newFramePose.x(), -newFramePose.y(), -newFramePose.z(),0,0,0);
|
||||
map_->sensorData().setLaserScanRaw(
|
||||
map_->sensorData().setLaserScan(
|
||||
LaserScan(
|
||||
util3d::laserScanFromPointCloud(*mapCloudNormals, mapViewpoint),
|
||||
0,
|
||||
|
||||
@@ -60,6 +60,8 @@ typedef Eigen::Matrix<double,Eigen::Dynamic,Eigen::Dynamic,Eigen::ColMajor> Matr
|
||||
#include "g2o/types/slam2d/types_slam2d.h"
|
||||
#include "g2o/types/slam3d/types_slam3d.h"
|
||||
#include "g2o/edge_se3_xyzprior.h"
|
||||
#include "g2o/edge_se3_gravity.h"
|
||||
#include "g2o/edge_sbacam_gravity.h"
|
||||
#ifdef G2O_HAVE_CSPARSE
|
||||
#include "g2o/solvers/csparse/linear_solver_csparse.h"
|
||||
#endif
|
||||
@@ -306,7 +308,7 @@ std::map<int, Transform> OptimizerG2O::optimize(
|
||||
{
|
||||
for(std::multimap<int, Link>::const_iterator iter=edgeConstraints.begin(); iter!=edgeConstraints.end(); ++iter)
|
||||
{
|
||||
if(iter->second.from() == iter->second.to())
|
||||
if(iter->second.from() == iter->second.to() && iter->second.type() == Link::kPosePrior)
|
||||
{
|
||||
rootId = 0;
|
||||
break;
|
||||
@@ -494,6 +496,7 @@ std::map<int, Transform> OptimizerG2O::optimize(
|
||||
1 / static_cast<double>(iter->second.infMatrix().at<double>(4,4)) >= 9999.0 ||
|
||||
1 / static_cast<double>(iter->second.infMatrix().at<double>(5,5)) >= 9999.0)
|
||||
{
|
||||
//GPS XYZ case
|
||||
EdgeSE3XYZPrior * priorEdge = new EdgeSE3XYZPrior();
|
||||
g2o::VertexSE3* v1 = (g2o::VertexSE3*)optimizer.vertex(id1);
|
||||
priorEdge->setVertex(0, v1);
|
||||
@@ -517,6 +520,7 @@ std::map<int, Transform> OptimizerG2O::optimize(
|
||||
}
|
||||
else
|
||||
{
|
||||
// XYZ+RPY case
|
||||
g2o::EdgeSE3Prior * priorEdge = new g2o::EdgeSE3Prior();
|
||||
g2o::VertexSE3* v1 = (g2o::VertexSE3*)optimizer.vertex(id1);
|
||||
priorEdge->setVertex(0, v1);
|
||||
@@ -536,6 +540,25 @@ std::map<int, Transform> OptimizerG2O::optimize(
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(!isSlam2d() && gravitySigma() > 0 && iter->second.type() == Link::kPoseOdom && poses.find(iter->first) != poses.end())
|
||||
{
|
||||
Eigen::Matrix<double, 6, 1> m;
|
||||
// Up vector in robot frame
|
||||
m.head<3>() = Eigen::Vector3d::UnitZ();
|
||||
// Observed Gravity vector in world frame
|
||||
float roll, pitch, yaw;
|
||||
iter->second.transform().getEulerAngles(roll, pitch, yaw);
|
||||
m.tail<3>() = Transform(0,0,0,roll,pitch,0).toEigen3d() * -Eigen::Vector3d::UnitZ();
|
||||
|
||||
Eigen::MatrixXd information = Eigen::MatrixXd::Identity(3, 3) * 1.0/(gravitySigma()*gravitySigma());
|
||||
|
||||
g2o::VertexSE3* v1 = (g2o::VertexSE3*)optimizer.vertex(id1);
|
||||
EdgeSE3Gravity* priorEdge(new EdgeSE3Gravity());
|
||||
priorEdge->setMeasurement(m);
|
||||
priorEdge->setInformation(information);
|
||||
priorEdge->vertices()[0] = v1;
|
||||
edge = priorEdge;
|
||||
}
|
||||
}
|
||||
else if(id1<0 || id2 < 0)
|
||||
{
|
||||
@@ -1386,7 +1409,38 @@ std::map<int, Transform> OptimizerG2O::optimizeBA(
|
||||
int id1 = iter->second.from();
|
||||
int id2 = iter->second.to();
|
||||
|
||||
if(id1 != id2) // not supporting prior
|
||||
if(id1 == id2)
|
||||
{
|
||||
#ifndef RTABMAP_ORB_SLAM2
|
||||
g2o::HyperGraph::Edge * edge = 0;
|
||||
if(gravitySigma() > 0 && iter->second.type() == Link::kPoseOdom && poses.find(iter->first) != poses.end())
|
||||
{
|
||||
Eigen::Matrix<double, 6, 1> m;
|
||||
// Up vector in robot frame
|
||||
m.head<3>() = Eigen::Vector3d::UnitZ();
|
||||
// Observed Gravity vector in world frame
|
||||
float roll, pitch, yaw;
|
||||
iter->second.transform().getEulerAngles(roll, pitch, yaw);
|
||||
m.tail<3>() = Transform(0,0,0,roll,pitch,0).toEigen3d() * -Eigen::Vector3d::UnitZ();
|
||||
|
||||
Eigen::MatrixXd information = Eigen::MatrixXd::Identity(3, 3) * 1.0/(gravitySigma()*gravitySigma());
|
||||
|
||||
g2o::VertexCam* v1 = (g2o::VertexCam*)optimizer.vertex(id1);
|
||||
EdgeSBACamGravity* priorEdge(new EdgeSBACamGravity());
|
||||
priorEdge->setMeasurement(m);
|
||||
priorEdge->setInformation(information);
|
||||
priorEdge->vertices()[0] = v1;
|
||||
edge = priorEdge;
|
||||
}
|
||||
if (edge && !optimizer.addEdge(edge))
|
||||
{
|
||||
delete edge;
|
||||
UERROR("Map: Failed adding constraint between %d and %d, skipping", id1, id2);
|
||||
return optimizedPoses;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else if(id1>0 && id2>0) // not supporting landmarks
|
||||
{
|
||||
UASSERT(!iter->second.transform().isNull());
|
||||
|
||||
|
||||
@@ -50,7 +50,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include <gtsam/nonlinear/NonlinearOptimizer.h>
|
||||
#include <gtsam/nonlinear/Marginals.h>
|
||||
#include <gtsam/nonlinear/Values.h>
|
||||
#include "optimizer/gtsam/GravityFactor.h"
|
||||
#include "gtsam/GravityFactor.h"
|
||||
#include "gtsam/GPSPose2XYFactor.h"
|
||||
#include "gtsam/GPSPose3XYZFactor.h"
|
||||
|
||||
#ifdef RTABMAP_VERTIGO
|
||||
#include "vertigo/gtsam/betweenFactorMaxMix.h"
|
||||
@@ -104,14 +106,27 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
||||
gtsam::NonlinearFactorGraph graph;
|
||||
|
||||
// detect if there is a global pose prior set, if so remove rootId
|
||||
bool gpsPriorOnly = false;
|
||||
if(!priorsIgnored())
|
||||
{
|
||||
for(std::multimap<int, Link>::const_iterator iter=edgeConstraints.begin(); iter!=edgeConstraints.end(); ++iter)
|
||||
{
|
||||
if(iter->second.from() == iter->second.to())
|
||||
if(iter->second.from() == iter->second.to() && iter->second.type() == Link::kPosePrior)
|
||||
{
|
||||
rootId = 0;
|
||||
break;
|
||||
if ((isSlam2d() && 1 / static_cast<double>(iter->second.infMatrix().at<double>(5,5)) < 9999) ||
|
||||
(1 / static_cast<double>(iter->second.infMatrix().at<double>(3,3)) < 9999.0 &&
|
||||
1 / static_cast<double>(iter->second.infMatrix().at<double>(4,4)) < 9999.0 &&
|
||||
1 / static_cast<double>(iter->second.infMatrix().at<double>(5,5)) < 9999.0))
|
||||
{
|
||||
// orientation is set, don't set root prior
|
||||
gpsPriorOnly = false;
|
||||
rootId = 0;
|
||||
break;
|
||||
}
|
||||
else if(gravitySigma()<=0)
|
||||
{
|
||||
gpsPriorOnly = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -128,7 +143,11 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
||||
}
|
||||
else
|
||||
{
|
||||
gtsam::noiseModel::Diagonal::shared_ptr priorNoise = gtsam::noiseModel::Diagonal::Variances((gtsam::Vector(6) << 1e-6, 1e-6, 1e-6, 1e-4, 1e-4, 1e-4).finished());
|
||||
gtsam::noiseModel::Diagonal::shared_ptr priorNoise = gtsam::noiseModel::Diagonal::Variances(
|
||||
(gtsam::Vector(6) <<
|
||||
(gpsPriorOnly?2:1e-2), gpsPriorOnly?2:1e-2, gpsPriorOnly?2:1e-2,
|
||||
1e-2, 1e-2, 1e-2
|
||||
).finished());
|
||||
graph.add(gtsam::PriorFactor<gtsam::Pose3>(rootId, gtsam::Pose3(initialPose.toEigen4d()), priorNoise));
|
||||
}
|
||||
}
|
||||
@@ -207,42 +226,65 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
||||
{
|
||||
if(isSlam2d())
|
||||
{
|
||||
Eigen::Matrix<double, 3, 3> information = Eigen::Matrix<double, 3, 3>::Identity();
|
||||
if(!isCovarianceIgnored())
|
||||
if (1 / static_cast<double>(iter->second.infMatrix().at<double>(5,5)) >= 9999.0)
|
||||
{
|
||||
information(0,0) = iter->second.infMatrix().at<double>(0,0); // x-x
|
||||
information(0,1) = iter->second.infMatrix().at<double>(0,1); // x-y
|
||||
information(0,2) = iter->second.infMatrix().at<double>(0,5); // x-theta
|
||||
information(1,0) = iter->second.infMatrix().at<double>(1,0); // y-x
|
||||
information(1,1) = iter->second.infMatrix().at<double>(1,1); // y-y
|
||||
information(1,2) = iter->second.infMatrix().at<double>(1,5); // y-theta
|
||||
information(2,0) = iter->second.infMatrix().at<double>(5,0); // theta-x
|
||||
information(2,1) = iter->second.infMatrix().at<double>(5,1); // theta-y
|
||||
information(2,2) = iter->second.infMatrix().at<double>(5,5); // theta-theta
|
||||
noiseModel::Diagonal::shared_ptr model = noiseModel::Diagonal::Variances(Vector2(
|
||||
1/iter->second.infMatrix().at<double>(0,0),
|
||||
1/iter->second.infMatrix().at<double>(1,1)));
|
||||
graph.add(GPSPose2XYFactor(id1, gtsam::Point2(iter->second.transform().x(), iter->second.transform().y()), model));
|
||||
}
|
||||
else
|
||||
{
|
||||
Eigen::Matrix<double, 3, 3> information = Eigen::Matrix<double, 3, 3>::Identity();
|
||||
if(!isCovarianceIgnored())
|
||||
{
|
||||
information(0,0) = iter->second.infMatrix().at<double>(0,0); // x-x
|
||||
information(0,1) = iter->second.infMatrix().at<double>(0,1); // x-y
|
||||
information(0,2) = iter->second.infMatrix().at<double>(0,5); // x-theta
|
||||
information(1,0) = iter->second.infMatrix().at<double>(1,0); // y-x
|
||||
information(1,1) = iter->second.infMatrix().at<double>(1,1); // y-y
|
||||
information(1,2) = iter->second.infMatrix().at<double>(1,5); // y-theta
|
||||
information(2,0) = iter->second.infMatrix().at<double>(5,0); // theta-x
|
||||
information(2,1) = iter->second.infMatrix().at<double>(5,1); // theta-y
|
||||
information(2,2) = iter->second.infMatrix().at<double>(5,5); // theta-theta
|
||||
}
|
||||
|
||||
gtsam::noiseModel::Gaussian::shared_ptr model = gtsam::noiseModel::Gaussian::Information(information);
|
||||
graph.add(gtsam::PriorFactor<gtsam::Pose2>(id1, gtsam::Pose2(iter->second.transform().x(), iter->second.transform().y(), iter->second.transform().theta()), model));
|
||||
gtsam::noiseModel::Gaussian::shared_ptr model = gtsam::noiseModel::Gaussian::Information(information);
|
||||
graph.add(gtsam::PriorFactor<gtsam::Pose2>(id1, gtsam::Pose2(iter->second.transform().x(), iter->second.transform().y(), iter->second.transform().theta()), model));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Eigen::Matrix<double, 6, 6> information = Eigen::Matrix<double, 6, 6>::Identity();
|
||||
if(!isCovarianceIgnored())
|
||||
if (1 / static_cast<double>(iter->second.infMatrix().at<double>(3,3)) >= 9999.0 ||
|
||||
1 / static_cast<double>(iter->second.infMatrix().at<double>(4,4)) >= 9999.0 ||
|
||||
1 / static_cast<double>(iter->second.infMatrix().at<double>(5,5)) >= 9999.0)
|
||||
{
|
||||
memcpy(information.data(), iter->second.infMatrix().data, iter->second.infMatrix().total()*sizeof(double));
|
||||
noiseModel::Diagonal::shared_ptr model = noiseModel::Diagonal::Precisions(Vector3(
|
||||
iter->second.infMatrix().at<double>(0,0),
|
||||
iter->second.infMatrix().at<double>(1,1),
|
||||
iter->second.infMatrix().at<double>(2,2)));
|
||||
graph.add(GPSPose3XYZFactor(id1, gtsam::Point3(iter->second.transform().x(), iter->second.transform().y(), iter->second.transform().z()), model));
|
||||
}
|
||||
else
|
||||
{
|
||||
Eigen::Matrix<double, 6, 6> information = Eigen::Matrix<double, 6, 6>::Identity();
|
||||
if(!isCovarianceIgnored())
|
||||
{
|
||||
memcpy(information.data(), iter->second.infMatrix().data, iter->second.infMatrix().total()*sizeof(double));
|
||||
}
|
||||
|
||||
Eigen::Matrix<double, 6, 6> mgtsam = Eigen::Matrix<double, 6, 6>::Identity();
|
||||
mgtsam.block(0,0,3,3) = information.block(3,3,3,3); // cov rotation
|
||||
mgtsam.block(3,3,3,3) = information.block(0,0,3,3); // cov translation
|
||||
mgtsam.block(0,3,3,3) = information.block(0,3,3,3); // off diagonal
|
||||
mgtsam.block(3,0,3,3) = information.block(3,0,3,3); // off diagonal
|
||||
gtsam::SharedNoiseModel model = gtsam::noiseModel::Gaussian::Information(mgtsam);
|
||||
Eigen::Matrix<double, 6, 6> mgtsam = Eigen::Matrix<double, 6, 6>::Identity();
|
||||
mgtsam.block(0,0,3,3) = information.block(3,3,3,3); // cov rotation
|
||||
mgtsam.block(3,3,3,3) = information.block(0,0,3,3); // cov translation
|
||||
mgtsam.block(0,3,3,3) = information.block(0,3,3,3); // off diagonal
|
||||
mgtsam.block(3,0,3,3) = information.block(3,0,3,3); // off diagonal
|
||||
gtsam::SharedNoiseModel model = gtsam::noiseModel::Gaussian::Information(mgtsam);
|
||||
|
||||
graph.add(gtsam::PriorFactor<gtsam::Pose3>(id1, gtsam::Pose3(iter->second.transform().toEigen4d()), model));
|
||||
graph.add(gtsam::PriorFactor<gtsam::Pose3>(id1, gtsam::Pose3(iter->second.transform().toEigen4d()), model));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(gravitySigma() > 0 && iter->second.type() == Link::kPoseOdom && poses.find(iter->first) != poses.end())
|
||||
else if(!isSlam2d() && gravitySigma() > 0 && iter->second.type() == Link::kPoseOdom && poses.find(iter->first) != poses.end())
|
||||
{
|
||||
Vector3 r = gtsam::Pose3(iter->second.transform().toEigen4d()).rotation().xyz();
|
||||
gtsam::Unit3 nG = gtsam::Rot3::RzRyRx(r.x(), r.y(), 0).rotate(gtsam::Unit3(0,0,-1));
|
||||
|
||||
88
corelib/src/optimizer/g2o/edge_sbacam_gravity.h
Normal file
88
corelib/src/optimizer/g2o/edge_sbacam_gravity.h
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
Copyright (c) 2010-2019, 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Adapted from EdgeSE3Gravity
|
||||
*/
|
||||
|
||||
#ifndef RTAB_G2O_EDGE_SBACAM_GRAVITY_H_
|
||||
#define RTAB_G2O_EDGE_SBACAM_GRAVITY_H_
|
||||
|
||||
#include "g2o/types/sba/types_sba.h"
|
||||
#include "g2o/core/base_unary_edge.h"
|
||||
namespace rtabmap {
|
||||
/**
|
||||
* \brief EdgeSBACamGravity
|
||||
* \brief g2o edge with gravity constraint
|
||||
*/
|
||||
class EdgeSBACamGravity : public g2o::BaseUnaryEdge<3, Eigen::Matrix<double, 6, 1>, g2o::VertexCam> {
|
||||
public:
|
||||
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
|
||||
EdgeSBACamGravity(){
|
||||
information().setIdentity();
|
||||
}
|
||||
virtual bool read(std::istream& is) {return false;} // not implemented
|
||||
virtual bool write(std::ostream& os) const {return false;} // not implemented
|
||||
|
||||
// return the error estimate as a 3-vector
|
||||
void computeError(){
|
||||
const g2o::VertexCam* v1 = static_cast<const g2o::VertexCam*>(_vertices[0]);
|
||||
|
||||
Eigen::Vector3d direction = _measurement.head<3>();
|
||||
Eigen::Vector3d measurement = _measurement.tail<3>();
|
||||
|
||||
Eigen::Vector3d ea;
|
||||
|
||||
Eigen::Matrix3d t = v1->estimate().rotation().toRotationMatrix();
|
||||
ea[0] = atan2(t (2, 1), t (2, 2));
|
||||
ea[1] = asin(-t (2, 0));
|
||||
ea[2] = atan2(t (1, 0), t (0, 0));
|
||||
|
||||
Eigen::Matrix3d rot =
|
||||
(Eigen::AngleAxisd(ea[1], Eigen::Vector3d::UnitY()) *
|
||||
Eigen::AngleAxisd(ea[0], Eigen::Vector3d::UnitX())).toRotationMatrix();
|
||||
|
||||
Eigen::Vector3d estimate = rot * -direction;
|
||||
_error = estimate - measurement;
|
||||
|
||||
//printf("%d : measured=%f %f %f est=%f %f %f error=%f %f %f\n", v1->id(),
|
||||
// measurement[0], measurement[1], measurement[2],
|
||||
// estimate[0], estimate[1], estimate[2],
|
||||
// _error[0], _error[1], _error[2]);
|
||||
}
|
||||
|
||||
// 6 values:
|
||||
// [0:2] Up vector in robot frame
|
||||
// [3:5] Observed gravity vector in world frame
|
||||
virtual void setMeasurement(const Eigen::Matrix<double, 6, 1>& m){
|
||||
_measurement.head<3>() = m.head<3>().normalized();
|
||||
_measurement.tail<3>() = m.tail<3>().normalized();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
89
corelib/src/optimizer/g2o/edge_se3_gravity.h
Normal file
89
corelib/src/optimizer/g2o/edge_se3_gravity.h
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
Copyright (c) 2010-2019, 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Adapted code from HDL graph slam:
|
||||
* https://github.com/koide3/hdl_graph_slam/blob/master/include/g2o/edge_se3_priorvec.hpp
|
||||
*/
|
||||
|
||||
#ifndef RTAB_G2O_EDGE_SE3_GRAVITY_H_
|
||||
#define RTAB_G2O_EDGE_SE3_GRAVITY_H_
|
||||
|
||||
#include "g2o/core/base_unary_edge.h"
|
||||
#include "g2o/types/slam3d/vertex_se3.h"
|
||||
|
||||
namespace rtabmap {
|
||||
|
||||
/*! \class EdgeSE3Gravity
|
||||
* \brief g2o edge with gravity constraint
|
||||
*/
|
||||
class EdgeSE3Gravity : public g2o::BaseUnaryEdge<3, Eigen::Matrix<double, 6, 1>, g2o::VertexSE3> {
|
||||
public:
|
||||
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
|
||||
EdgeSE3Gravity(){
|
||||
information().setIdentity();
|
||||
}
|
||||
virtual bool read(std::istream& is) {return false;} // not implemented
|
||||
virtual bool write(std::ostream& os) const {return false;} // not implemented
|
||||
|
||||
// return the error estimate as a 3-vector
|
||||
void computeError(){
|
||||
const g2o::VertexSE3* v1 = static_cast<const g2o::VertexSE3*>(_vertices[0]);
|
||||
|
||||
Eigen::Vector3d direction = _measurement.head<3>();
|
||||
Eigen::Vector3d measurement = _measurement.tail<3>();
|
||||
|
||||
Eigen::Vector3d ea;
|
||||
|
||||
Eigen::Matrix3d t = v1->estimate().linear();
|
||||
ea[0] = atan2(t (2, 1), t (2, 2));
|
||||
ea[1] = asin(-t (2, 0));
|
||||
ea[2] = atan2(t (1, 0), t (0, 0));
|
||||
|
||||
Eigen::Matrix3d rot =
|
||||
(Eigen::AngleAxisd(ea[1], Eigen::Vector3d::UnitY()) *
|
||||
Eigen::AngleAxisd(ea[0], Eigen::Vector3d::UnitX())).toRotationMatrix();
|
||||
|
||||
Eigen::Vector3d estimate = rot * -direction;
|
||||
_error = estimate - measurement;
|
||||
|
||||
//printf("%d : measured=%f %f %f est=%f %f %f error=%f %f %f\n", v1->id(),
|
||||
// measurement[0], measurement[1], measurement[2],
|
||||
// estimate[0], estimate[1], estimate[2],
|
||||
// _error[0], _error[1], _error[2]);
|
||||
}
|
||||
|
||||
// 6 values:
|
||||
// [0:2] Up vector in robot frame
|
||||
// [3:5] Observed gravity vector in world frame
|
||||
virtual void setMeasurement(const Eigen::Matrix<double, 6, 1>& m){
|
||||
_measurement.head<3>() = m.head<3>().normalized();
|
||||
_measurement.tail<3>() = m.tail<3>().normalized();
|
||||
}
|
||||
};
|
||||
}
|
||||
#endif
|
||||
@@ -87,10 +87,6 @@ void EdgeSE3XYZPrior::computeError() {
|
||||
_error = v->estimate().translation() - _measurement;
|
||||
}
|
||||
|
||||
void EdgeSE3XYZPrior::linearizeOplus() {
|
||||
_jacobianOplusXi << Eigen::Matrix3d::Identity();
|
||||
}
|
||||
|
||||
bool EdgeSE3XYZPrior::setMeasurementFromState() {
|
||||
const g2o::VertexSE3* v = static_cast<const g2o::VertexSE3*>(_vertices[0]);
|
||||
_measurement = v->estimate().translation();
|
||||
|
||||
@@ -63,7 +63,6 @@ public:
|
||||
virtual bool read(std::istream& is);
|
||||
virtual bool write(std::ostream& os) const;
|
||||
virtual void computeError();
|
||||
virtual void linearizeOplus();
|
||||
virtual bool setMeasurementFromState();
|
||||
|
||||
virtual double initialEstimatePossible(const g2o::OptimizableGraph::VertexSet& /*from*/, g2o::OptimizableGraph::Vertex* /*to*/) {return 1.;}
|
||||
|
||||
57
corelib/src/optimizer/gtsam/GPSPose2XYFactor.h
Normal file
57
corelib/src/optimizer/gtsam/GPSPose2XYFactor.h
Normal file
@@ -0,0 +1,57 @@
|
||||
|
||||
/**
|
||||
* Author: Mathieu Labbe
|
||||
* This file is a copy of GPSPose2Factor.h of gtsam examples
|
||||
*/
|
||||
|
||||
/**
|
||||
* A simple 2D 'GPS' like factor
|
||||
* The factor contains a X-Y position measurement (mx, my) for a Pose, but no rotation information
|
||||
* The error vector will be [x-mx, y-my]'
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <gtsam/nonlinear/NonlinearFactor.h>
|
||||
#include <gtsam/base/Matrix.h>
|
||||
#include <gtsam/base/Vector.h>
|
||||
#include <gtsam/geometry/Pose2.h>
|
||||
|
||||
|
||||
namespace rtabmap {
|
||||
|
||||
class GPSPose2XYFactor: public gtsam::NoiseModelFactor1<gtsam::Pose2> {
|
||||
|
||||
private:
|
||||
// measurement information
|
||||
double mx_, my_;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param poseKey associated pose varible key
|
||||
* @param model noise model for GPS snesor, in X-Y
|
||||
* @param m Point2 measurement
|
||||
*/
|
||||
GPSPose2XYFactor(gtsam::Key poseKey, const gtsam::Point2 m, gtsam::SharedNoiseModel model) :
|
||||
gtsam::NoiseModelFactor1<gtsam::Pose2>(model, poseKey), mx_(m.x()), my_(m.y()) {}
|
||||
|
||||
// error function
|
||||
// @param p the pose in Pose2
|
||||
// @param H the optional Jacobian matrix, which use boost optional and has default null pointer
|
||||
gtsam::Vector evaluateError(const gtsam::Pose2& p, boost::optional<gtsam::Matrix&> H = boost::none) const {
|
||||
|
||||
// note that use boost optional like a pointer
|
||||
// only calculate jacobian matrix when non-null pointer exists
|
||||
if (H) *H = (gtsam::Matrix23() << 1.0, 0.0, 0.0,
|
||||
0.0, 1.0, 0.0).finished();
|
||||
|
||||
// return error vector
|
||||
return (gtsam::Vector2() << p.x() - mx_, p.y() - my_).finished();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace gtsamexamples
|
||||
|
||||
53
corelib/src/optimizer/gtsam/GPSPose3XYZFactor.h
Normal file
53
corelib/src/optimizer/gtsam/GPSPose3XYZFactor.h
Normal file
@@ -0,0 +1,53 @@
|
||||
|
||||
/**
|
||||
* Author: Mathieu Labbe
|
||||
* This file is a copy of GPSPose2Factor.h of gtsam examples for Pose3
|
||||
*/
|
||||
|
||||
/**
|
||||
* A simple 3D 'GPS' like factor
|
||||
* The factor contains a X-Y-Z position measurement (mx, my, mz) for a Pose, but no rotation information
|
||||
* The error vector will be [x-mx, y-my, z-mz]'
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <gtsam/nonlinear/NonlinearFactor.h>
|
||||
#include <gtsam/base/Matrix.h>
|
||||
#include <gtsam/base/Vector.h>
|
||||
#include <gtsam/geometry/Pose3.h>
|
||||
|
||||
|
||||
namespace rtabmap {
|
||||
|
||||
class GPSPose3XYZFactor: public gtsam::NoiseModelFactor1<gtsam::Pose3> {
|
||||
|
||||
private:
|
||||
// measurement information
|
||||
double mx_, my_, mz_;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param poseKey associated pose variable key
|
||||
* @param model noise model for GPS sensor, in X-Y
|
||||
* @param m Point2 measurement
|
||||
*/
|
||||
GPSPose3XYZFactor(gtsam::Key poseKey, const gtsam::Point3 m, gtsam::SharedNoiseModel model) :
|
||||
gtsam::NoiseModelFactor1<gtsam::Pose3>(model, poseKey), mx_(m.x()), my_(m.y()), mz_(m.z()) {}
|
||||
|
||||
// error function
|
||||
// @param p the pose in Pose
|
||||
// @param H the optional Jacobian matrix, which use boost optional and has default null pointer
|
||||
gtsam::Vector evaluateError(const gtsam::Pose3& p, boost::optional<gtsam::Matrix&> H = boost::none) const {
|
||||
if(H)
|
||||
{
|
||||
p.translation(H);
|
||||
}
|
||||
return (gtsam::Vector3() << p.x() - mx_, p.y() - my_, p.z() - mz_).finished();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace gtsamexamples
|
||||
|
||||
@@ -9,6 +9,13 @@
|
||||
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Author: Mathieu Labbe
|
||||
* This file is a copy of AttitudeFactor.cpp of gtsam library but
|
||||
* with attitudeError() function overridden to ignore yaw errors.
|
||||
* For the noise model, use Sigmas(Vector2(0.1, 10)) (with second sigma high!)
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file GravityFactor.cpp
|
||||
* @author Frank Dellaert
|
||||
|
||||
Reference in New Issue
Block a user