mirror of
https://github.com/introlab/rtabmap_ros.git
synced 2026-10-06 17:57:45 +08:00
Refactoring common depth/stereo/laser msgs conversion into shared methods in MsgConversion.h between CoreWrapper.cpp and GuiWrapper.cpp (avoiding some duplicated codes)
This commit is contained in:
@@ -39,6 +39,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include <tf_conversions/tf_eigen.h>
|
||||
#include <image_geometry/pinhole_camera_model.h>
|
||||
#include <image_geometry/stereo_camera_model.h>
|
||||
#include <sensor_msgs/image_encodings.h>
|
||||
#include <cv_bridge/cv_bridge.h>
|
||||
#include <laser_geometry/laser_geometry.h>
|
||||
#include <rtabmap/core/util3d_surface.h>
|
||||
|
||||
namespace rtabmap_ros {
|
||||
|
||||
@@ -864,4 +868,440 @@ void odomInfoToROS(const rtabmap::OdometryInfo & info, rtabmap_ros::OdomInfo & m
|
||||
msg.localScanMap = rtabmap::compressData(info.localScanMap);
|
||||
}
|
||||
|
||||
rtabmap::Transform getTransform(
|
||||
const std::string & fromFrameId,
|
||||
const std::string & toFrameId,
|
||||
const ros::Time & stamp,
|
||||
tf::TransformListener & listener,
|
||||
double waitForTransform)
|
||||
{
|
||||
// TF ready?
|
||||
rtabmap::Transform transform;
|
||||
try
|
||||
{
|
||||
if(waitForTransform > 0.0 && !stamp.isZero())
|
||||
{
|
||||
//if(!tfBuffer_.canTransform(fromFrameId, toFrameId, stamp, ros::Duration(1)))
|
||||
std::string errorMsg;
|
||||
if(!listener.waitForTransform(fromFrameId, toFrameId, stamp, ros::Duration(waitForTransform), ros::Duration(0.01), &errorMsg))
|
||||
{
|
||||
ROS_WARN("Could not get transform from %s to %s after %f seconds (for stamp=%f)! Error=\"%s\".",
|
||||
fromFrameId.c_str(), toFrameId.c_str(), waitForTransform, stamp.toSec(), errorMsg.c_str());
|
||||
return transform;
|
||||
}
|
||||
}
|
||||
|
||||
tf::StampedTransform tmp;
|
||||
listener.lookupTransform(fromFrameId, toFrameId, stamp, tmp);
|
||||
transform = rtabmap_ros::transformFromTF(tmp);
|
||||
}
|
||||
catch(tf::TransformException & ex)
|
||||
{
|
||||
ROS_WARN("%s",ex.what());
|
||||
}
|
||||
return transform;
|
||||
}
|
||||
|
||||
// get moving transform accordingly to a fixed frame. For example get
|
||||
// transform between moving /base_link between two stamps accordingly to /odom frame.
|
||||
rtabmap::Transform getTransform(
|
||||
const std::string & sourceTargetFrame,
|
||||
const std::string & fixedFrame,
|
||||
const ros::Time & stampSource,
|
||||
const ros::Time & stampTarget,
|
||||
tf::TransformListener & listener,
|
||||
double waitForTransform)
|
||||
{
|
||||
// TF ready?
|
||||
rtabmap::Transform transform;
|
||||
try
|
||||
{
|
||||
ros::Time stamp = stampSource>stampTarget?stampSource:stampTarget;
|
||||
if(waitForTransform > 0.0 && !stamp.isZero())
|
||||
{
|
||||
std::string errorMsg;
|
||||
if(!listener.waitForTransform(sourceTargetFrame, fixedFrame, stamp, ros::Duration(waitForTransform), ros::Duration(0.01), &errorMsg))
|
||||
{
|
||||
ROS_WARN("Could not get transform from %s to %s accordingly to %s after %f seconds (for stamps=%f -> %f)! Error=\"%s\".",
|
||||
sourceTargetFrame.c_str(), sourceTargetFrame.c_str(), fixedFrame.c_str(), waitForTransform, stampSource.toSec(), stampTarget.toSec(), errorMsg.c_str());
|
||||
return transform;
|
||||
}
|
||||
}
|
||||
|
||||
tf::StampedTransform tmp;
|
||||
listener.lookupTransform(sourceTargetFrame, stampTarget, sourceTargetFrame, stampSource, fixedFrame, tmp);
|
||||
transform = rtabmap_ros::transformFromTF(tmp);
|
||||
}
|
||||
catch(tf::TransformException & ex)
|
||||
{
|
||||
ROS_WARN("%s",ex.what());
|
||||
}
|
||||
return transform;
|
||||
}
|
||||
|
||||
bool convertRGBDMsgs(
|
||||
const std::vector<sensor_msgs::ImageConstPtr> & imageMsgs,
|
||||
const std::vector<sensor_msgs::ImageConstPtr> & depthMsgs,
|
||||
const std::vector<sensor_msgs::CameraInfoConstPtr> & cameraInfoMsgs,
|
||||
const std::string & frameId,
|
||||
const std::string & odomFrameId,
|
||||
const ros::Time & odomStamp,
|
||||
cv::Mat & rgb,
|
||||
cv::Mat & depth,
|
||||
std::vector<rtabmap::CameraModel> & cameraModels,
|
||||
tf::TransformListener & listener,
|
||||
double waitForTransform)
|
||||
{
|
||||
UASSERT(imageMsgs.size()>0 &&
|
||||
imageMsgs.size() == depthMsgs.size() &&
|
||||
imageMsgs.size() == cameraInfoMsgs.size());
|
||||
|
||||
int imageWidth = imageMsgs[0]->width;
|
||||
int imageHeight = imageMsgs[0]->height;
|
||||
int cameraCount = imageMsgs.size();
|
||||
for(unsigned int i=0; i<imageMsgs.size(); ++i)
|
||||
{
|
||||
if(!(imageMsgs[i]->encoding.compare(sensor_msgs::image_encodings::TYPE_8UC1) == 0 ||
|
||||
imageMsgs[i]->encoding.compare(sensor_msgs::image_encodings::MONO8) ==0 ||
|
||||
imageMsgs[i]->encoding.compare(sensor_msgs::image_encodings::MONO16) ==0 ||
|
||||
imageMsgs[i]->encoding.compare(sensor_msgs::image_encodings::BGR8) == 0 ||
|
||||
imageMsgs[i]->encoding.compare(sensor_msgs::image_encodings::RGB8) == 0) ||
|
||||
!(depthMsgs[i]->encoding.compare(sensor_msgs::image_encodings::TYPE_16UC1) == 0 ||
|
||||
depthMsgs[i]->encoding.compare(sensor_msgs::image_encodings::TYPE_32FC1) == 0 ||
|
||||
depthMsgs[i]->encoding.compare(sensor_msgs::image_encodings::MONO16) == 0))
|
||||
{
|
||||
ROS_ERROR("Input type must be image=mono8,mono16,rgb8,bgr8 and image_depth=32FC1,16UC1,mono16");
|
||||
return false;
|
||||
}
|
||||
|
||||
UASSERT_MSG(imageMsgs[i]->width == imageWidth && imageMsgs[i]->height == imageHeight,
|
||||
uFormat("imageWidth=%d vs %d imageHeight=%d vs %d",
|
||||
imageWidth,
|
||||
imageMsgs[i]->width,
|
||||
imageHeight,
|
||||
imageMsgs[i]->height).c_str());
|
||||
UASSERT_MSG(depthMsgs[i]->width == imageWidth && depthMsgs[i]->height == imageHeight,
|
||||
uFormat("imageWidth=%d vs %d imageHeight=%d vs %d",
|
||||
imageWidth,
|
||||
depthMsgs[i]->width,
|
||||
imageHeight,
|
||||
depthMsgs[i]->height).c_str());
|
||||
|
||||
rtabmap::Transform localTransform = rtabmap_ros::getTransform(frameId, depthMsgs[i]->header.frame_id, depthMsgs[i]->header.stamp, listener, waitForTransform);
|
||||
if(localTransform.isNull())
|
||||
{
|
||||
ROS_ERROR("TF of received depth image %d at time %fs is not set!", i, depthMsgs[i]->header.stamp.toSec());
|
||||
return false;
|
||||
}
|
||||
// sync with odometry stamp
|
||||
if(odomStamp != depthMsgs[i]->header.stamp)
|
||||
{
|
||||
rtabmap::Transform sensorT = getTransform(
|
||||
frameId,
|
||||
odomFrameId,
|
||||
odomStamp,
|
||||
depthMsgs[i]->header.stamp,
|
||||
listener,
|
||||
waitForTransform);
|
||||
if(sensorT.isNull())
|
||||
{
|
||||
ROS_WARN("Could not get odometry value for depth image stamp (%fs). Latest odometry "
|
||||
"stamp is %fs. The depth image pose will not be synchronized with odometry.", depthMsgs[i]->header.stamp.toSec(), odomStamp.toSec());
|
||||
}
|
||||
else
|
||||
{
|
||||
localTransform = sensorT * localTransform;
|
||||
}
|
||||
}
|
||||
|
||||
cv_bridge::CvImageConstPtr ptrImage;
|
||||
if(imageMsgs[i]->encoding.compare(sensor_msgs::image_encodings::TYPE_8UC1)==0)
|
||||
{
|
||||
ptrImage = cv_bridge::toCvShare(imageMsgs[i]);
|
||||
}
|
||||
else if(imageMsgs[i]->encoding.compare(sensor_msgs::image_encodings::MONO8) == 0 ||
|
||||
imageMsgs[i]->encoding.compare(sensor_msgs::image_encodings::MONO16) == 0)
|
||||
{
|
||||
ptrImage = cv_bridge::toCvShare(imageMsgs[i], "mono8");
|
||||
}
|
||||
else
|
||||
{
|
||||
ptrImage = cv_bridge::toCvShare(imageMsgs[i], "bgr8");
|
||||
}
|
||||
cv_bridge::CvImageConstPtr ptrDepth = cv_bridge::toCvShare(depthMsgs[i]);
|
||||
cv::Mat subDepth = ptrDepth->image;
|
||||
|
||||
// initialize
|
||||
if(rgb.empty())
|
||||
{
|
||||
rgb = cv::Mat(imageHeight, imageWidth*cameraCount, ptrImage->image.type());
|
||||
}
|
||||
if(depth.empty())
|
||||
{
|
||||
depth = cv::Mat(imageHeight, imageWidth*cameraCount, subDepth.type());
|
||||
}
|
||||
|
||||
if(ptrImage->image.type() == rgb.type())
|
||||
{
|
||||
ptrImage->image.copyTo(cv::Mat(rgb, cv::Rect(i*imageWidth, 0, imageWidth, imageHeight)));
|
||||
}
|
||||
else
|
||||
{
|
||||
ROS_ERROR("Some RGB images are not the same type!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(subDepth.type() == depth.type())
|
||||
{
|
||||
subDepth.copyTo(cv::Mat(depth, cv::Rect(i*imageWidth, 0, imageWidth, imageHeight)));
|
||||
}
|
||||
else
|
||||
{
|
||||
ROS_ERROR("Some Depth images are not the same type!");
|
||||
return false;
|
||||
}
|
||||
|
||||
cameraModels.push_back(rtabmap_ros::cameraModelFromROS(*cameraInfoMsgs[i], localTransform));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool convertStereoMsg(
|
||||
const sensor_msgs::ImageConstPtr& leftImageMsg,
|
||||
const sensor_msgs::ImageConstPtr& rightImageMsg,
|
||||
const sensor_msgs::CameraInfoConstPtr& leftCamInfoMsg,
|
||||
const sensor_msgs::CameraInfoConstPtr& rightCamInfoMsg,
|
||||
const std::string & frameId,
|
||||
const std::string & odomFrameId,
|
||||
const ros::Time & odomStamp,
|
||||
cv::Mat & left,
|
||||
cv::Mat & right,
|
||||
rtabmap::StereoCameraModel & stereoModel,
|
||||
tf::TransformListener & listener,
|
||||
double waitForTransform)
|
||||
{
|
||||
UASSERT(leftImageMsg.get() && rightImageMsg.get());
|
||||
UASSERT(leftCamInfoMsg.get() && rightCamInfoMsg.get());
|
||||
|
||||
if(!(leftImageMsg->encoding.compare(sensor_msgs::image_encodings::MONO8) == 0 ||
|
||||
leftImageMsg->encoding.compare(sensor_msgs::image_encodings::MONO16) == 0 ||
|
||||
leftImageMsg->encoding.compare(sensor_msgs::image_encodings::BGR8) == 0 ||
|
||||
leftImageMsg->encoding.compare(sensor_msgs::image_encodings::RGB8) == 0) ||
|
||||
!(rightImageMsg->encoding.compare(sensor_msgs::image_encodings::MONO8) == 0 ||
|
||||
rightImageMsg->encoding.compare(sensor_msgs::image_encodings::MONO16) == 0 ||
|
||||
rightImageMsg->encoding.compare(sensor_msgs::image_encodings::BGR8) == 0 ||
|
||||
rightImageMsg->encoding.compare(sensor_msgs::image_encodings::RGB8) == 0))
|
||||
{
|
||||
ROS_ERROR("Input type must be image=mono8,mono16,rgb8,bgr8");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(leftImageMsg->encoding.compare(sensor_msgs::image_encodings::MONO8) == 0 ||
|
||||
leftImageMsg->encoding.compare(sensor_msgs::image_encodings::MONO16) == 0)
|
||||
{
|
||||
left = cv_bridge::toCvCopy(leftImageMsg, "mono8")->image;
|
||||
}
|
||||
else
|
||||
{
|
||||
left = cv_bridge::toCvCopy(leftImageMsg, "bgr8")->image;
|
||||
}
|
||||
right = cv_bridge::toCvCopy(rightImageMsg, "mono8")->image;
|
||||
|
||||
rtabmap::Transform localTransform = getTransform(frameId, leftImageMsg->header.frame_id, leftImageMsg->header.stamp, listener, waitForTransform);
|
||||
if(localTransform.isNull())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// sync with odometry stamp
|
||||
if(odomStamp != leftImageMsg->header.stamp)
|
||||
{
|
||||
rtabmap::Transform sensorT = getTransform(
|
||||
frameId,
|
||||
odomFrameId,
|
||||
odomStamp,
|
||||
leftImageMsg->header.stamp,
|
||||
listener,
|
||||
waitForTransform);
|
||||
if(sensorT.isNull())
|
||||
{
|
||||
ROS_WARN("Could not get odometry value for stereo msg stamp (%fs). Latest odometry "
|
||||
"stamp is %fs. The stereo image pose will not be synchronized with odometry.", leftImageMsg->header.stamp.toSec(), odomStamp.toSec());
|
||||
}
|
||||
else
|
||||
{
|
||||
localTransform = sensorT * localTransform;
|
||||
}
|
||||
}
|
||||
|
||||
stereoModel = rtabmap_ros::stereoCameraModelFromROS(*leftCamInfoMsg, *rightCamInfoMsg, localTransform);
|
||||
|
||||
if(stereoModel.baseline() > 10.0)
|
||||
{
|
||||
static bool shown = false;
|
||||
if(!shown)
|
||||
{
|
||||
ROS_WARN("Detected baseline (%f m) is quite large! Is your "
|
||||
"right camera_info P(0,3) correctly set? Note that "
|
||||
"baseline=-P(0,3)/P(0,0). You may need to calibrate your camera. "
|
||||
"This warning is printed only once.",
|
||||
stereoModel.baseline());
|
||||
shown = true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool convertScanMsg(
|
||||
const sensor_msgs::LaserScanConstPtr& scan2dMsg,
|
||||
const std::string & frameId,
|
||||
const std::string & odomFrameId,
|
||||
const ros::Time & odomStamp,
|
||||
cv::Mat & scan,
|
||||
rtabmap::Transform & scanLocalTransform,
|
||||
tf::TransformListener & listener,
|
||||
double waitForTransform)
|
||||
{
|
||||
// make sure the frame of the laser is updated too
|
||||
rtabmap::Transform tmpT = getTransform(
|
||||
odomFrameId,
|
||||
scan2dMsg->header.frame_id,
|
||||
scan2dMsg->header.stamp + ros::Duration().fromSec(scan2dMsg->ranges.size()*scan2dMsg->time_increment),
|
||||
listener,
|
||||
waitForTransform);
|
||||
if(tmpT.isNull())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
scanLocalTransform = getTransform(
|
||||
frameId,
|
||||
scan2dMsg->header.frame_id,
|
||||
scan2dMsg->header.stamp,
|
||||
listener,
|
||||
waitForTransform);
|
||||
if(scanLocalTransform.isNull())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//transform in frameId_ frame
|
||||
sensor_msgs::PointCloud2 scanOut;
|
||||
laser_geometry::LaserProjection projection;
|
||||
projection.transformLaserScanToPointCloud(odomFrameId, *scan2dMsg, scanOut, listener);
|
||||
pcl::PointCloud<pcl::PointXYZ>::Ptr pclScan(new pcl::PointCloud<pcl::PointXYZ>);
|
||||
pcl::fromROSMsg(scanOut, *pclScan);
|
||||
|
||||
//transform back in laser frame
|
||||
rtabmap::Transform laserToOdom = getTransform(
|
||||
scan2dMsg->header.frame_id,
|
||||
odomFrameId,
|
||||
scan2dMsg->header.stamp,
|
||||
listener,
|
||||
waitForTransform);
|
||||
if(laserToOdom.isNull())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// sync with odometry stamp
|
||||
if(odomStamp != scan2dMsg->header.stamp)
|
||||
{
|
||||
rtabmap::Transform sensorT = getTransform(
|
||||
frameId,
|
||||
odomFrameId,
|
||||
odomStamp,
|
||||
scan2dMsg->header.stamp,
|
||||
listener,
|
||||
waitForTransform);
|
||||
if(sensorT.isNull())
|
||||
{
|
||||
ROS_WARN("Could not get odometry value for laser scan stamp (%fs). Latest odometry "
|
||||
"stamp is %fs. The laser scan pose will not be synchronized with odometry.", scan2dMsg->header.stamp.toSec(), odomStamp.toSec());
|
||||
}
|
||||
else
|
||||
{
|
||||
scanLocalTransform = sensorT * scanLocalTransform;
|
||||
}
|
||||
}
|
||||
scan = rtabmap::util3d::laserScan2dFromPointCloud(*pclScan, laserToOdom); // put back in laser frame
|
||||
return true;
|
||||
}
|
||||
|
||||
bool convertScan3dMsg(
|
||||
const sensor_msgs::PointCloud2ConstPtr & scan3dMsg,
|
||||
const std::string & frameId,
|
||||
const std::string & odomFrameId,
|
||||
const ros::Time & odomStamp,
|
||||
int scanCloudNormalK,
|
||||
cv::Mat & scan,
|
||||
rtabmap::Transform & scanLocalTransform,
|
||||
tf::TransformListener & listener,
|
||||
double waitForTransform)
|
||||
{
|
||||
bool containNormals = false;
|
||||
for(unsigned int i=0; i<scan3dMsg->fields.size(); ++i)
|
||||
{
|
||||
if(scan3dMsg->fields[i].name.compare("normal_x") == 0)
|
||||
{
|
||||
containNormals = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
scanLocalTransform = getTransform(frameId, scan3dMsg->header.frame_id, scan3dMsg->header.stamp, listener, waitForTransform);
|
||||
if(scanLocalTransform.isNull())
|
||||
{
|
||||
ROS_ERROR("TF of received scan cloud at time %fs is not set, aborting rtabmap update.", scan3dMsg->header.stamp.toSec());
|
||||
return false;
|
||||
}
|
||||
|
||||
// sync with odometry stamp
|
||||
if(odomStamp != scan3dMsg->header.stamp)
|
||||
{
|
||||
rtabmap::Transform sensorT = getTransform(
|
||||
frameId,
|
||||
odomFrameId,
|
||||
odomStamp,
|
||||
scan3dMsg->header.stamp,
|
||||
listener,
|
||||
waitForTransform);
|
||||
if(sensorT.isNull())
|
||||
{
|
||||
ROS_WARN("Could not get odometry value for laser scan stamp (%fs). Latest odometry "
|
||||
"stamp is %fs. The 3d laser scan pose will not be synchronized with odometry.", scan3dMsg->header.stamp.toSec(), odomStamp.toSec());
|
||||
}
|
||||
else
|
||||
{
|
||||
scanLocalTransform = sensorT * scanLocalTransform;
|
||||
}
|
||||
}
|
||||
|
||||
if(containNormals)
|
||||
{
|
||||
pcl::PointCloud<pcl::PointNormal>::Ptr pclScan(new pcl::PointCloud<pcl::PointNormal>);
|
||||
pcl::fromROSMsg(*scan3dMsg, *pclScan);
|
||||
|
||||
scan = rtabmap::util3d::laserScanFromPointCloud(*pclScan);
|
||||
}
|
||||
else
|
||||
{
|
||||
pcl::PointCloud<pcl::PointXYZ>::Ptr pclScan(new pcl::PointCloud<pcl::PointXYZ>);
|
||||
pcl::fromROSMsg(*scan3dMsg, *pclScan);
|
||||
|
||||
if(scanCloudNormalK > 0)
|
||||
{
|
||||
//compute normals
|
||||
pcl::PointCloud<pcl::Normal>::Ptr normals = rtabmap::util3d::computeNormals(pclScan, scanCloudNormalK);
|
||||
pcl::PointCloud<pcl::PointNormal>::Ptr pclScanNormal(new pcl::PointCloud<pcl::PointNormal>);
|
||||
pcl::concatenateFields(*pclScan, *normals, *pclScanNormal);
|
||||
scan = rtabmap::util3d::laserScanFromPointCloud(*pclScanNormal);
|
||||
}
|
||||
else
|
||||
{
|
||||
scan = rtabmap::util3d::laserScanFromPointCloud(*pclScan);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user