Compare commits

...

11 Commits

Author SHA1 Message Date
matlabbe
c1b07fbdc0 Fixed Semaphore::value() on Windows. 2024-09-01 01:16:36 -07:00
matlabbe
f7f5903510 Windows: fixed build error with OusterSDK dep 2024-08-31 20:59:35 -07:00
matlabbe
ed5d95cb55 Windows: Fixed WinSock.h already declared error 2024-08-31 17:41:16 -07:00
matlabbe
1eee89fcf0 Updated --version and about dialog 2024-08-25 18:31:20 -07:00
matlabbe
72c62285eb removed debug line 2024-08-24 20:26:16 -07:00
matlabbe
29f0565b5c Added OSF file support. Added ability to pause lidar capture if from file. Updated Preferences->"test odometry" button to support lidar only or both. 2024-08-24 20:23:38 -07:00
matlabbe
fc61efbc47 Added VLP16 in cmake status 2024-08-18 21:17:59 -07:00
matlabbe
7aa143fa95 Fixed fatal error when reaching end of file 2024-08-18 21:08:49 -07:00
matlabbe
7253450251 Integrated to UI 2024-08-18 21:04:08 -07:00
matlabbe
0de0fe3ec6 Updated LidarViewer with ouster 2024-08-18 19:22:49 -07:00
matlabbe
b44dd537a7 Ouster SDK integration 2024-08-17 18:23:20 -07:00
34 changed files with 2777 additions and 1220 deletions

View File

@@ -205,6 +205,7 @@ option(WITH_REALSENSE2 "Include RealSense support" ON)
option(WITH_MYNTEYE "Include mynteye-s support" ON) option(WITH_MYNTEYE "Include mynteye-s support" ON)
option(WITH_DEPTHAI "Include depthai-core support" OFF) option(WITH_DEPTHAI "Include depthai-core support" OFF)
option(WITH_XVSDK "Include XVisio SDK support" OFF) option(WITH_XVSDK "Include XVisio SDK support" OFF)
option(WITH_OUSTER "Include Ouster SDK support" ON)
option(WITH_OCTOMAP "Include OctoMap support" ON) option(WITH_OCTOMAP "Include OctoMap support" ON)
option(WITH_GRIDMAP "Include GridMap support" ON) option(WITH_GRIDMAP "Include GridMap support" ON)
option(WITH_CPUTSDF "Include CPUTSDF support" OFF) option(WITH_CPUTSDF "Include CPUTSDF support" OFF)
@@ -671,6 +672,13 @@ IF(WITH_XVSDK)
ENDIF(xvsdk_FOUND) ENDIF(xvsdk_FOUND)
ENDIF(WITH_XVSDK) ENDIF(WITH_XVSDK)
IF(WITH_OUSTER)
FIND_PACKAGE(OusterSDK QUIET)
IF(OusterSDK_FOUND)
MESSAGE(STATUS "Found OusterSDK (targets)")
ENDIF(OusterSDK_FOUND)
ENDIF(WITH_OUSTER)
IF(WITH_OCTOMAP) IF(WITH_OCTOMAP)
FIND_PACKAGE(octomap QUIET) FIND_PACKAGE(octomap QUIET)
IF(octomap_FOUND) IF(octomap_FOUND)
@@ -1018,6 +1026,9 @@ IF(NOT xvsdk_FOUND)
ELSE() ELSE()
SET(CONF_WITH_XVSDK 1) SET(CONF_WITH_XVSDK 1)
ENDIF() ENDIF()
IF(NOT OusterSDK_FOUND)
SET(OUSTER "//")
ENDIF()
IF(NOT octomap_FOUND) IF(NOT octomap_FOUND)
SET(OCTOMAP "//") SET(OCTOMAP "//")
SET(CONF_WITH_OCTOMAP 0) SET(CONF_WITH_OCTOMAP 0)
@@ -1674,6 +1685,21 @@ ELSE()
MESSAGE(STATUS " With XVisio SDK = NO (xvsdk not found)") MESSAGE(STATUS " With XVisio SDK = NO (xvsdk not found)")
ENDIF() ENDIF()
MESSAGE(STATUS "")
MESSAGE(STATUS " LiDAR Drivers:")
IF(PCL_VERSION VERSION_GREATER_EQUAL "1.8.0")
MESSAGE(STATUS " With Velodyne VLP16 = YES")
ELSE()
MESSAGE(STATUS " With Velodyne VLP16 = NO (PCL>=1.8 required)")
ENDIF()
IF(OusterSDK_FOUND)
MESSAGE(STATUS " With Ouster SDK ${OusterSDK_VERSION} = YES")
ELSEIF(NOT WITH_OUSTER)
MESSAGE(STATUS " With Ouster SDK = NO (WITH_OUSTER=OFF)")
ELSE()
MESSAGE(STATUS " With Ouster SDK = NO (OusterSDK not found)")
ENDIF()
MESSAGE(STATUS "") MESSAGE(STATUS "")
MESSAGE(STATUS " Odometry Approaches:") MESSAGE(STATUS " Odometry Approaches:")
IF(loam_velodyne_FOUND) IF(loam_velodyne_FOUND)

View File

@@ -70,6 +70,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@MYNTEYE@#define RTABMAP_MYNTEYE @MYNTEYE@#define RTABMAP_MYNTEYE
@DEPTHAI@#define RTABMAP_DEPTHAI @DEPTHAI@#define RTABMAP_DEPTHAI
@XVSDK@#define RTABMAP_XVSDK @XVSDK@#define RTABMAP_XVSDK
@OUSTER@#define RTABMAP_OUSTER
@OCTOMAP@#define RTABMAP_OCTOMAP @OCTOMAP@#define RTABMAP_OCTOMAP
@GRIDMAP@#define RTABMAP_GRIDMAP @GRIDMAP@#define RTABMAP_GRIDMAP
@CPUTSDF@#define RTABMAP_CPUTSDF @CPUTSDF@#define RTABMAP_CPUTSDF

View File

@@ -41,7 +41,6 @@ public:
kMadgwick=0, kMadgwick=0,
kComplementaryFilter=1}; kComplementaryFilter=1};
public: public:
static IMUFilter * create(const ParametersMap & parameters = ParametersMap());
static IMUFilter * create(IMUFilter::Type type, const ParametersMap & parameters = ParametersMap()); static IMUFilter * create(IMUFilter::Type type, const ParametersMap & parameters = ParametersMap());
public: public:

View File

@@ -0,0 +1,72 @@
/*
Copyright (c) 2010-2024, Mathieu Labbe
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 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.
*/
#ifndef CORELIB_INCLUDE_RTABMAP_CORE_LIDAR_LIDAROUSTER_H_
#define CORELIB_INCLUDE_RTABMAP_CORE_LIDAR_LIDAROUSTER_H_
#include <rtabmap/core/Lidar.h>
namespace rtabmap {
class IMUFilter;
class OusterCaptureThread;
class RTABMAP_CORE_EXPORT LidarOuster :public Lidar {
public:
static bool available();
public:
LidarOuster(
const std::string& ipOrHostnameOrPcapOrOsf,
const std::string& dataDestinationOrJson = "",
int lidarMode = 0,
int timestampMode = 0,
bool useReflectivityForIntensityChannel = true,
bool publishIMU = false,
float frameRate = 0.0f,
Transform localTransform = Transform::getIdentity());
virtual ~LidarOuster();
SensorData takeScan(SensorCaptureInfo * info = 0) {return takeData(info);}
virtual bool init(const std::string & calibrationFolder = ".", const std::string & cameraName = "") override;
virtual std::string getSerial() const override;
protected:
virtual SensorData captureData(SensorCaptureInfo * info = 0) override;
private:
OusterCaptureThread * ousterCaptureThread_;
bool imuPublished_;
bool useReflectivityForIntensityChannel_;
std::string ipOrHostnameOrPcapOrOsf_;
std::string dataDestinationOrJson_;
int lidarMode_;
int timestampMode_;
};
} /* namespace rtabmap */
#endif /* CORELIB_INCLUDE_RTABMAP_CORE_LIDAR_LIDAROUSTER_H_ */

View File

@@ -43,6 +43,8 @@ struct PointXYZIT {
}; };
class RTABMAP_CORE_EXPORT LidarVLP16 :public Lidar, public pcl::VLPGrabber { class RTABMAP_CORE_EXPORT LidarVLP16 :public Lidar, public pcl::VLPGrabber {
public:
static bool available();
public: public:
LidarVLP16( LidarVLP16(
const std::string& pcapFile, const std::string& pcapFile,

View File

@@ -41,6 +41,8 @@ SET(SRC_FILES
camera/CameraMyntEye.cpp camera/CameraMyntEye.cpp
camera/CameraDepthAI.cpp camera/CameraDepthAI.cpp
camera/CameraSeerSense.cpp camera/CameraSeerSense.cpp
lidar/LidarOuster.cpp
EpipolarGeometry.cpp EpipolarGeometry.cpp
VisualWord.cpp VisualWord.cpp
@@ -391,6 +393,15 @@ IF(xvsdk_FOUND)
) )
ENDIF(xvsdk_FOUND) ENDIF(xvsdk_FOUND)
IF(OusterSDK_FOUND)
SET(LIBRARIES
${LIBRARIES}
OusterSDK::ouster_client
OusterSDK::ouster_pcap
OusterSDK::ouster_osf
)
ENDIF(OusterSDK_FOUND)
IF(TARGET OpenMP::OpenMP_CXX) IF(TARGET OpenMP::OpenMP_CXX)
SET(LIBRARIES SET(LIBRARIES
${LIBRARIES} ${LIBRARIES}

View File

@@ -35,13 +35,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
namespace rtabmap { namespace rtabmap {
IMUFilter * IMUFilter::create(const ParametersMap & parameters)
{
int type = Parameters::defaultKpDetectorStrategy();
Parameters::parse(parameters, Parameters::kKpDetectorStrategy(), type);
return create((IMUFilter::Type)type, parameters);
}
IMUFilter * IMUFilter::create(IMUFilter::Type type, const ParametersMap & parameters) IMUFilter * IMUFilter::create(IMUFilter::Type type, const ParametersMap & parameters)
{ {
#ifndef RTABMAP_MADGWICK #ifndef RTABMAP_MADGWICK
@@ -62,7 +55,6 @@ IMUFilter * IMUFilter::create(IMUFilter::Type type, const ParametersMap & parame
#endif #endif
default: default:
filter = new ComplementaryFilter(parameters); filter = new ComplementaryFilter(parameters);
type = IMUFilter::kComplementaryFilter;
break; break;
} }

View File

@@ -646,7 +646,7 @@ Transform Odometry::process(SensorData & data, const Transform & guessIn, Odomet
dt > 0 && dt > 0 &&
!guess.isNull()) !guess.isNull())
{ {
UDEBUG("Deskewing begin"); UDEBUG("Deskewing begin (with imu=%d)", !imus_.empty()?1:0);
// Recompute velocity // Recompute velocity
float vx,vy,vz, vroll,vpitch,vyaw; float vx,vy,vz, vroll,vpitch,vyaw;
guess.getTranslationAndEulerAngles(vx,vy,vz, vroll,vpitch,vyaw); guess.getTranslationAndEulerAngles(vx,vy,vz, vroll,vpitch,vyaw);
@@ -672,7 +672,7 @@ Transform Odometry::process(SensorData & data, const Transform & guessIn, Odomet
Transform imuLastScan = Transform::getTransform(imus_, Transform imuLastScan = Transform::getTransform(imus_,
data.stamp() + data.stamp() +
data.laserScanRaw().data().ptr<float>(0, data.laserScanRaw().size()-1)[data.laserScanRaw().getTimeOffset()]); data.laserScanRaw().data().ptr<float>(0, data.laserScanRaw().size()-1)[data.laserScanRaw().getTimeOffset()]);
if(!imuFirstScan.isNull() && !imuLastScan.isNull()) if(!imuFirstScan.isNull() || !imuLastScan.isNull())
{ {
Transform orientation = imuFirstScan.inverse() * imuLastScan; Transform orientation = imuFirstScan.inverse() * imuLastScan;
orientation.getEulerAngles(vroll, vpitch, vyaw); orientation.getEulerAngles(vroll, vpitch, vyaw);
@@ -689,6 +689,18 @@ Transform Odometry::process(SensorData & data, const Transform & guessIn, Odomet
vyaw /= scanTime; vyaw /= scanTime;
} }
} }
if(imuFirstScan.isNull())
{
UWARN("We are receiving IMUs but we could not find one for the "
"first lidar stamp %f to be used for deskewing.",
data.stamp() + data.laserScanRaw().data().ptr<float>(0, 0)[data.laserScanRaw().getTimeOffset()]);
}
if(imuLastScan.isNull())
{
UWARN("We are receiving IMUs but we could not find one for the "
"last lidar stamp %f to be used for deskewing.",
data.stamp() + data.laserScanRaw().data().ptr<float>(0, data.laserScanRaw().size()-1)[data.laserScanRaw().getTimeOffset()]);
}
} }
Transform velocity(vx,vy,vz,vroll,vpitch,vyaw); Transform velocity(vx,vy,vz,vroll,vpitch,vyaw);
@@ -703,6 +715,7 @@ Transform Odometry::process(SensorData & data, const Transform & guessIn, Odomet
if(data.laserScanRaw().isOrganized()) if(data.laserScanRaw().isOrganized())
{ {
// Laser scans should be dense passing this point // Laser scans should be dense passing this point
UDEBUG("Densify scan");
data.setLaserScan(data.laserScanRaw().densify()); data.setLaserScan(data.laserScanRaw().densify());
} }

View File

@@ -814,6 +814,12 @@ ParametersMap Parameters::parseArguments(int argc, char * argv[], bool onlyParam
std::cout << str << std::setw(spacing - str.size()) << "true" << std::endl; std::cout << str << std::setw(spacing - str.size()) << "true" << std::endl;
#else #else
std::cout << str << std::setw(spacing - str.size()) << "false" << std::endl; std::cout << str << std::setw(spacing - str.size()) << "false" << std::endl;
#endif
str = "With Ouster SDK:";
#ifdef RTABMAP_OUSTER
std::cout << str << std::setw(spacing - str.size()) << "true" << std::endl;
#else
std::cout << str << std::setw(spacing - str.size()) << "false" << std::endl;
#endif #endif
str = "With libpointmatcher:"; str = "With libpointmatcher:";
#ifdef RTABMAP_POINTMATCHER #ifdef RTABMAP_POINTMATCHER

View File

@@ -0,0 +1,700 @@
/*
Copyright (c) 2010-2024, Mathieu Labbe
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 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/Version.h>
#ifdef RTABMAP_OUSTER
// Ouster should be included first to avoid conflicts
// with defines set by Windows.h included indirectly below
#include "ouster/client.h"
#include "ouster/os_pcap.h"
#include "ouster/types.h"
#include "ouster/impl/build.h"
#include "ouster/lidar_scan.h"
#include "ouster/osf/reader.h"
#include "ouster/osf/stream_lidar_scan.h"
#include "ouster/osf/meta_lidar_sensor.h"
#include "ouster/osf/meta_extrinsics.h"
#include "ouster/osf/meta_streaming_info.h"
#endif
#include <rtabmap/core/lidar/LidarOuster.h>
#include <rtabmap/utilite/UEventsManager.h>
#include <rtabmap/utilite/UStl.h>
#include <rtabmap/core/IMUFilter.h>
#include <rtabmap/utilite/UTimer.h>
#include <rtabmap/utilite/UFile.h>
namespace rtabmap {
#ifdef RTABMAP_OUSTER
class OusterCaptureThread : public UThread {
public:
OusterCaptureThread(
std::shared_ptr<ouster::sensor::client> & clientHandle,
const ouster::sensor::sensor_info & info,
bool imuPublished,
bool useReflectivityForIntensityChannel) :
scanBatcher_(info),
pf_(ouster::sensor::get_format(info)),
clientHandle_(clientHandle),
info_(info),
intensityChannel_(useReflectivityForIntensityChannel?
ouster::sensor::ChanField::REFLECTIVITY:
ouster::sensor::ChanField::SIGNAL),
endOfFileReached_(false)
{
UDEBUG("");
init(imuPublished);
}
OusterCaptureThread(
std::shared_ptr<ouster::sensor_utils::playback_handle> & pcapHandle,
const ouster::sensor::sensor_info & info,
bool imuPublished,
bool useReflectivityForIntensityChannel) :
scanBatcher_(info),
pf_(ouster::sensor::get_format(info)),
pcapHandle_(pcapHandle),
info_(info),
intensityChannel_(useReflectivityForIntensityChannel?
ouster::sensor::ChanField::REFLECTIVITY:
ouster::sensor::ChanField::SIGNAL),
endOfFileReached_(false)
{
UDEBUG("");
init(imuPublished);
}
OusterCaptureThread(
std::shared_ptr<ouster::osf::Reader> & osfReader,
const ouster::sensor::sensor_info & info,
bool imuPublished,
bool useReflectivityForIntensityChannel) :
scanBatcher_(info),
pf_(ouster::sensor::get_format(info)),
osfReader_(osfReader),
osfIter_(osfReader_->messages().begin()),
info_(info),
intensityChannel_(useReflectivityForIntensityChannel?
ouster::sensor::ChanField::REFLECTIVITY:
ouster::sensor::ChanField::SIGNAL),
endOfFileReached_(false)
{
UDEBUG("");
init(imuPublished);
}
virtual ~OusterCaptureThread()
{
this->join(true);
delete imuFilter_;
}
const ouster::sensor::sensor_info & getInfo() const {
return info_;
}
bool getScan(LaserScan & output, double & stamp)
{
if(clientHandle_.get() == 0)
{
// We are reading from file, call mainLoop till a scan is received or end of stream
while(scanReady_.value() == 0 && !endOfFileReached_)
this->mainLoop();
}
if(!endOfFileReached_ && !scanReady_.acquire(1, 5000))
{
UERROR("Not received any frames since 5 seconds, try to restart the camera again.");
}
else if(!endOfFileReached_ || this->isRunning())
{
ouster::LidarScan scan;
{
UScopeMutex s(scanMutex_);
scan = lastScan_;
lastScan_ = ouster::LidarScan();
}
UASSERT(scan.has_field(ouster::sensor::ChanField::RANGE));
UASSERT(scan.has_field(intensityChannel_));
ouster::LidarScan::Points cloud = ouster::cartesian(scan, lut_);
// Convert to our LaserScan format XYZIT
output = LaserScan(cv::Mat(cv::Size(scan.w,scan.h), CV_32FC(5)), LaserScan::kXYZIT, 0, 0, 0, 0, 0, lidarSensorT_);
auto timestamp = scan.timestamp();
auto scan_ts = timestamp[scan.w-1]; // stamp with last reading
//UWARN("Prepare scan %f -> %f", double(timestamp[0])/10e8, double(timestamp[scan.w-1])/10e8);
auto intensityOffset = output.getIntensityOffset();
auto timeOffset = output.getTimeOffset();
float bad_point = std::numeric_limits<float>::quiet_NaN ();
ouster::FieldType intensityType = scan.field_type(intensityChannel_==ouster::sensor::ChanField::REFLECTIVITY?"REFLECTIVITY":"SIGNAL");
for (size_t u = 0; u < scan.h; u++) {
for (size_t v = 0; v < scan.w; v++) {
auto ts = scan_ts-timestamp[v];
auto index = u*scan.w+v;
if(cloud(index, 0) != 0.0)
{
output.field(index, 0) = cloud(index, 0);
output.field(index, 1) = cloud(index, 1);
output.field(index, 2) = cloud(index, 2);
}
else
{
output.field(index, 0) = output.field(index, 1) = output.field(index, 2) = bad_point;
}
if(intensityType.element_type == ouster::sensor::ChanFieldType::UINT32)
{
output.field(index, intensityOffset) = scan.field<uint32_t>(intensityChannel_).data()[index];
}
else if(intensityType.element_type == ouster::sensor::ChanFieldType::UINT16)
{
output.field(index, intensityOffset) = scan.field<uint16_t>(intensityChannel_).data()[index];
}
else if(intensityType.element_type == ouster::sensor::ChanFieldType::UINT8)
{
output.field(index, intensityOffset) = scan.field<uint8_t>(intensityChannel_).data()[index];
}
else
{
UFATAL("Intensity format %d not supported!", intensityType.element_type);
}
output.field(index, timeOffset) = -float(ts)/10e8;
}
}
stamp = double(scan_ts)/10e8;
return true;
}
return false;
}
IMU getIMU(double stamp, int maxWaitTimeMs = 100)
{
IMU imu;
if(!imuBuffer_.empty())
{
imuMutex_.lock();
int waitTry = 0;
while(maxWaitTimeMs>0 && imuBuffer_.rbegin()->first < stamp && waitTry < maxWaitTimeMs)
{
imuMutex_.unlock();
++waitTry;
uSleep(1);
imuMutex_.lock();
}
if(imuBuffer_.rbegin()->first < stamp)
{
if(maxWaitTimeMs > 0)
{
UWARN("Could not find imus to interpolate at scan time %f after waiting %d ms (last is %f)...", stamp/1000.0, maxWaitTimeMs, imuBuffer_.rbegin()->first/1000.0);
}
}
else
{
std::map<double, IMU>::const_iterator iterB = imuBuffer_.lower_bound(stamp);
std::map<double, IMU>::const_iterator iterA = iterB;
if(iterA != imuBuffer_.begin())
{
iterA = --iterA;
}
if(iterB == imuBuffer_.end())
{
iterB = --iterB;
}
if(stamp >= iterA->first && stamp <= iterB->first)
{
float t = (stamp-iterA->first) / (iterB->first-iterA->first);
cv::Vec4d quatA = iterA->second.orientation();
cv::Vec3d accA = iterA->second.linearAcceleration();
cv::Vec3d gyroA = iterA->second.angularVelocity();
cv::Vec4d quatB = iterB->second.orientation();
cv::Vec3d accB = iterB->second.linearAcceleration();
cv::Vec3d gyroB = iterB->second.angularVelocity();
Eigen::Quaterniond qa(quatA[3], quatA[0], quatA[1], quatA[2]);
Eigen::Quaterniond qb(quatB[3], quatB[0], quatB[1], quatB[2]);
Eigen::Quaterniond qres = qa.slerp(t, qb);
accA[0] = accA[0] + t*(accB[0] - accA[0]);
accA[1] = accA[1] + t*(accB[1] - accA[1]);
accA[2] = accA[2] + t*(accB[2] - accA[2]);
gyroA[0] = gyroA[0] + t*(gyroB[0] - gyroA[0]);
gyroA[1] = gyroA[1] + t*(gyroB[1] - gyroA[1]);
gyroA[2] = gyroA[2] + t*(gyroB[2] - gyroA[2]);
imu = IMU(cv::Vec4d(qres.x(), qres.y(), qres.z(), qres.w()), iterA->second.orientationCovariance(),
gyroA, iterA->second.angularVelocityCovariance(),
accA, iterA->second.linearAccelerationCovariance(),
imuLocalTransform_);
}
}
imuMutex_.unlock();
}
return imu;
}
private:
void init(bool imuPublished)
{
imuFilter_ = 0;
UINFO("imuPublished=%d", imuPublished?1:0);
size_t w = info_.format.columns_per_frame;
size_t h = info_.format.pixels_per_column;
lut_ = ouster::make_xyz_lut(info_);
// Buffer to store raw packet data
lidarPacket_ = ouster::sensor::LidarPacket (pf_.lidar_packet_size);
imuPacket_ = ouster::sensor::ImuPacket(pf_.imu_packet_size);
const std::array<ouster::FieldType, 2> reducedSlots{
{{ouster::sensor::ChanField::RANGE, ouster::sensor::ChanFieldType::UINT32},
{intensityChannel_, ouster::sensor::ChanFieldType::UINT32}}};
scanBuffer_ = ouster::LidarScan(w, h, reducedSlots.begin(), reducedSlots.end());
lidarSensorT_ = Transform::fromEigen4d(info_.lidar_to_sensor_transform);
lidarSensorT_.x() /= 1000;
lidarSensorT_.y() /= 1000;
lidarSensorT_.z() /= 1000;
UINFO("Lidar to sensor: %s", lidarSensorT_.prettyPrint().c_str());
if(imuPublished)
{
imuLocalTransform_ = Transform::fromEigen4d(info_.imu_to_sensor_transform);
imuLocalTransform_.x() /= 1000;
imuLocalTransform_.y() /= 1000;
imuLocalTransform_.z() /= 1000;
UINFO("IMU to sensor: %s", imuLocalTransform_.prettyPrint().c_str());
imuFilter_ = IMUFilter::create(IMUFilter::kMadgwick);
}
}
virtual void mainLoop()
{
bool isLidarPacket = false;
bool isImuPacket = false;
if(clientHandle_)
{
// wait until sensor data is available
ouster::sensor::client_state st = ouster::sensor::poll_client(*clientHandle_);
// check for timeout
if (st == ouster::sensor::TIMEOUT){
UERROR("Client has timed out");
this->kill();
return;
}
if (st & ouster::sensor::EXIT){
UERROR("Exit was requested");
this->kill();
return;
}
// check for error status
if (st & ouster::sensor::CLIENT_ERROR) {
UERROR("Sensor client returned error state!");
this->kill();
return;
}
if (st & ouster::sensor::LIDAR_DATA) {
if (!ouster::sensor::read_lidar_packet(*clientHandle_, lidarPacket_)) {
UERROR("Failed to read a lidar packet of the expected size!");
this->kill();
return;
}
isLidarPacket = true;
}
if (imuFilter_ && st & ouster::sensor::IMU_DATA) {
if (!ouster::sensor::read_imu_packet(*clientHandle_, imuPacket_)) {
UERROR("Failed to read an imu packet of the expected size!");
this->kill();
return;
}
isImuPacket = true;
}
}
else if(pcapHandle_) //pcap
{
ouster::sensor_utils::packet_info packet_info;
if(!ouster::sensor_utils::next_packet_info(*pcapHandle_, packet_info))
{
UWARN("No more data");
endOfFileReached_ = true;
return;
}
if(packet_info.dst_port == info_.config.udp_port_lidar)
{
auto packet_size = ouster::sensor_utils::read_packet(
*pcapHandle_, lidarPacket_.buf.data(), lidarPacket_.buf.size());
if (packet_size == pf_.lidar_packet_size) {
isLidarPacket = true;
}
}
else if(imuFilter_ && packet_info.dst_port == info_.config.udp_port_imu)
{
// async IMU, can be used for deskewing in Odometry
auto packet_size = ouster::sensor_utils::read_packet(
*pcapHandle_, imuPacket_.buf.data(), imuPacket_.buf.size());
if (packet_size == pf_.imu_packet_size) {
isImuPacket = true;
}
}
}
else if(osfReader_.get())
{
// read next message from OSF in timestamp order
if(osfIter_ == osfReader_->messages().end())
{
UWARN("No more data");
endOfFileReached_ = true;
return;
}
if(osfIter_->is<ouster::osf::LidarScanStream>())
{
// Decoding LidarScan messages
auto ls = osfIter_->decode_msg<ouster::osf::LidarScanStream>();
{
UScopeMutex s(scanMutex_);
bool notify = lastScan_.w == 0;
lastScan_ = *ls;
if(lastScan_.w != 0 && notify)
{
scanReady_.release();
}
}
}
//else if(m.is<ouster::osf::LidarImuStream>()) // Seems not implemented in ouster-sdk
++osfIter_;
}
else {
UFATAL("");
}
if (isLidarPacket) {
// batcher will return "true" when the current scan is complete
if (scanBatcher_(lidarPacket_, scanBuffer_)) {
// retry until we receive a full set of valid measurements
// (accounting for azimuth_window settings if any)
if (scanBuffer_.complete(info_.format.column_window))
{
{
UScopeMutex s(scanMutex_);
bool notify = lastScan_.w == 0;
lastScan_ = scanBuffer_;
if(lastScan_.w != 0 && notify)
{
scanReady_.release();
}
}
}
}
}
// async IMU, can be used for deskewing in Odometry
if (isImuPacket) {
static const double standard_g = 9.80665;
uchar* buf = imuPacket_.buf.data();
uint64_t ts = pf_.imu_gyro_ts(buf);
double imuStamp = double(ts)/10e8;
cv::Vec3d linearAcc;
cv::Vec3d angularVel;
linearAcc[0] = pf_.imu_la_x(buf) * standard_g;
linearAcc[1] = pf_.imu_la_y(buf) * standard_g;
linearAcc[2] = pf_.imu_la_z(buf) * standard_g;
angularVel[0] = pf_.imu_av_x(buf) * M_PI / 180.0;
angularVel[1] = pf_.imu_av_y(buf) * M_PI / 180.0;
angularVel[2] = pf_.imu_av_z(buf) * M_PI / 180.0;
imuFilter_->update(
angularVel[0], angularVel[1], angularVel[2],
linearAcc[0], linearAcc[1], linearAcc[2],
imuStamp);
cv::Vec4d quat;
imuFilter_->getOrientation(quat[0], quat[1], quat[2], quat[3]);
IMU imu(quat, cv::Mat::eye(3,3,CV_64FC1)*6e-4,
angularVel, cv::Mat::eye(3,3,CV_64FC1)*6e-4,
linearAcc, cv::Mat::eye(3,3,CV_64FC1)*0.01,
imuLocalTransform_);
UEventsManager::post(new IMUEvent(imu, imuStamp));
UScopeMutex s(imuMutex_);
imuBuffer_.insert(imuBuffer_.end(), std::make_pair(imuStamp, imu));
if(imuBuffer_.size() > 1000)
{
imuBuffer_.erase(imuBuffer_.begin());
}
}
}
virtual void mainLoopEnd()
{
scanReady_.release();
}
private:
UMutex scanMutex_;
UMutex imuMutex_;
USemaphore scanReady_;
ouster::ScanBatcher scanBatcher_;
ouster::sensor::packet_format pf_;
ouster::LidarScan scanBuffer_;
ouster::LidarScan lastScan_;
ouster::sensor::LidarPacket lidarPacket_;
ouster::sensor::ImuPacket imuPacket_;
std::shared_ptr<ouster::sensor::client> clientHandle_;
std::shared_ptr<ouster::sensor_utils::playback_handle> pcapHandle_;
std::shared_ptr<ouster::osf::Reader> osfReader_;
ouster::osf::MessagesStreamingIter osfIter_;
ouster::sensor::sensor_info info_;
ouster::sensor::cf_type intensityChannel_;
ouster::XYZLut lut_;
Transform lidarSensorT_;
Transform imuLocalTransform_;
IMUFilter * imuFilter_;
std::map<double, IMU> imuBuffer_;
bool endOfFileReached_;
};
#endif
LidarOuster::LidarOuster(
const std::string& ipOrHostnameOrPcapOrOsf,
const std::string& dataDestinationOrJson,
int lidarMode,
int timestampMode,
bool useReflectivityForIntensityChannel,
bool publishIMU,
float frameRate,
Transform localTransform) :
Lidar(frameRate, localTransform),
ousterCaptureThread_(0),
imuPublished_(publishIMU),
useReflectivityForIntensityChannel_(useReflectivityForIntensityChannel),
ipOrHostnameOrPcapOrOsf_(ipOrHostnameOrPcapOrOsf),
dataDestinationOrJson_(dataDestinationOrJson),
lidarMode_(lidarMode),
timestampMode_(timestampMode)
{
UASSERT(!ipOrHostnameOrPcapOrOsf.empty());
#ifdef RTABMAP_OUSTER
UASSERT(lidarMode>=ouster::sensor::lidar_mode::MODE_UNSPEC && lidarMode<=ouster::sensor::lidar_mode::MODE_4096x5);
UASSERT(timestampMode>=ouster::sensor::timestamp_mode::TIME_FROM_UNSPEC && timestampMode<=ouster::sensor::timestamp_mode::TIME_FROM_PTP_1588);
#endif
}
LidarOuster::~LidarOuster()
{
#ifdef RTABMAP_OUSTER
if(ousterCaptureThread_)
ousterCaptureThread_->join(true);
delete ousterCaptureThread_;
#endif
}
bool LidarOuster::available()
{
#ifdef RTABMAP_OUSTER
return true;
#else
return false;
#endif
}
std::string LidarOuster::getSerial() const
{
#ifdef RTABMAP_OUSTER
if(ousterCaptureThread_) {
return ousterCaptureThread_->getInfo().sn.c_str();
}
#endif
return "";
}
bool LidarOuster::init(const std::string &, const std::string &)
{
#ifdef RTABMAP_OUSTER
if(ousterCaptureThread_)
ousterCaptureThread_->join(true);
delete ousterCaptureThread_;
ousterCaptureThread_ = 0;
bool readingFromFile = false;
ouster::sensor::sensor_info info;
std::string ext = uToLowerCase(UFile::getExtension(ipOrHostnameOrPcapOrOsf_));
if(ext == "pcap")
{
UINFO("Using PCAP file \"%s\" and JSON file \"%s\"", ipOrHostnameOrPcapOrOsf_.c_str(), dataDestinationOrJson_.c_str());
if(dataDestinationOrJson_.empty())
{
UERROR("A JSON path should be provided when a PCAP path is used.");
return false;
}
UDEBUG("");
auto pcapHandle = ouster::sensor_utils::replay_initialize(ipOrHostnameOrPcapOrOsf_);
if(pcapHandle.get() == 0) {
UERROR("Failed to open pcap file \"%s\"!", ipOrHostnameOrPcapOrOsf_.c_str());
return false;
}
info = ouster::sensor::metadata_from_json(dataDestinationOrJson_);
ousterCaptureThread_ = new OusterCaptureThread(
pcapHandle,
info,
imuPublished_,
useReflectivityForIntensityChannel_);
readingFromFile = true;
}
else if(ext == "osf")
{
UINFO("Using OSF file \"%s\"", ipOrHostnameOrPcapOrOsf_.c_str());
if(imuPublished_)
{
UWARN("Imu publishing it not supported for OSF files.");
imuPublished_ = false;
}
try {
std::shared_ptr<ouster::osf::Reader> osfReader(new ouster::osf::Reader(ipOrHostnameOrPcapOrOsf_));
auto sensors = osfReader->meta_store().find<ouster::osf::LidarSensor>();
UASSERT(sensors.size() >= 1);
// Use first sensor and get its sensor_info
info = sensors.begin()->second->info();
ousterCaptureThread_ = new OusterCaptureThread(
osfReader,
info,
imuPublished_,
useReflectivityForIntensityChannel_);
readingFromFile = true;
}
catch(std::exception & e) {
UERROR("Failed creating OSF reader: %s", e.what());
return false;
}
}
else // ip / hostname
{
UINFO("Using sensor hostname \"%s\" and destination (optional) \"%s\"",
ipOrHostnameOrPcapOrOsf_.c_str(), dataDestinationOrJson_.c_str());
auto clientHandle = ouster::sensor::init_client(
ipOrHostnameOrPcapOrOsf_,
dataDestinationOrJson_,
(ouster::sensor::lidar_mode)lidarMode_,
(ouster::sensor::timestamp_mode)timestampMode_);
if(!clientHandle) {
UERROR("Failed to connect to sensor! Verify that the Ouster can be reached on the network at this address \"%s\".",
ipOrHostnameOrPcapOrOsf_.c_str());
return false;
}
UINFO("Connection to sensor succeeded");
auto metadata = ouster::sensor::get_metadata(*clientHandle);
// Raw metadata can be parsed into a `sensor_info` struct
info = ouster::sensor::sensor_info(metadata);
ousterCaptureThread_ = new OusterCaptureThread(
clientHandle,
info,
imuPublished_,
useReflectivityForIntensityChannel_);
}
UINFO("Firmware version: %s", info.fw_rev.c_str());
UINFO("Serial number: %s", info.sn.c_str());
UINFO("Product line: %s", info.prod_line.c_str());
UINFO("Scan dimensions: %ld x %ld", info.format.columns_per_frame, info.format.pixels_per_column);
//UINFO("Column window: [%d,%d]", info.column_window.first, info.column_window.second);
if(!readingFromFile)
{
ousterCaptureThread_->start();
}
else if(this->getFrameRate() == 0.0f)
{
std::string lidarMode = ouster::sensor::to_string(info.config.lidar_mode.value_or(ouster::sensor::lidar_mode::MODE_UNSPEC));
std::list<std::string> lidarModeSplit = uSplit(lidarMode, 'x');
if(lidarModeSplit.size() == 2) {
int rate = uStr2Int(*lidarModeSplit.rbegin());
UASSERT(rate > 0);
this->setFrameRate(rate);
UINFO("Setting frame rate to %d Hz (lidar mode = %s)", rate, lidarMode.c_str());
}
else
{
UERROR("Unknown lidar mode \"%s\", framerate is set to 10.", lidarMode.c_str());
this->setFrameRate(10);
}
}
return true;
#else
UERROR("RTAB-Map is not built with OusterSDK");
return false;
#endif
}
SensorData LidarOuster::captureData(SensorCaptureInfo * info)
{
SensorData data;
#ifdef RTABMAP_OUSTER
double stamp = 0.0;
LaserScan scan;
if(!ousterCaptureThread_->getScan(scan, stamp))
{
// End of stream
return data;
}
data.setLaserScan(scan);
data.setStamp(stamp);
IMU imu = ousterCaptureThread_->getIMU(stamp);
if(!imu.empty())
{
data.setIMU(imu);
}
#else
UERROR("RTAB-Map is not built with OusterSDK");
#endif
return data;
}
} /* namespace rtabmap */

View File

@@ -124,6 +124,15 @@ LidarVLP16::~LidarVLP16()
UDEBUG("Stopped lidar!"); UDEBUG("Stopped lidar!");
} }
bool LidarVLP16::available()
{
#if PCL_VERSION_COMPARE(>=, 1, 8, 0)
return true;
#else
return false;
#endif
}
void LidarVLP16::setOrganized(bool enable) void LidarVLP16::setOrganized(bool enable)
{ {
organized_ = true; organized_ = true;

View File

@@ -1148,6 +1148,7 @@ Transform OdometryF2M::computeTransform(
} }
else else
{ {
UDEBUG("Initialize first key frame");
// Just generate keypoints for the new signature // Just generate keypoints for the new signature
// For scan, we want to use reading filters, so set dummy's scan and set back to reference afterwards // For scan, we want to use reading filters, so set dummy's scan and set back to reference afterwards
Signature dummy; Signature dummy;

View File

@@ -328,6 +328,7 @@ LaserScan commonFiltering(
scan = util3d::adjustNormalsToViewPoint(scan, Eigen::Vector3f(0,0,10), groundNormalsUp); scan = util3d::adjustNormalsToViewPoint(scan, Eigen::Vector3f(0,0,10), groundNormalsUp);
} }
} }
UDEBUG("scan size=%d format=%d, organized=%d", scan.size(), (int)scan.format(), scan.isOrganized()?1:0);
return scan; return scan;
} }

View File

@@ -1,3 +1,5 @@
# Kept for backward compatibility, see also lidar3d_icp_indoor.ini or lidar3d_icp_outdoor.ini
# Could be used with LiDARs like Velodyne, RoboSense, Ouster # Could be used with LiDARs like Velodyne, RoboSense, Ouster
[Camera] [Camera]

View File

@@ -0,0 +1,46 @@
# Could be used with LiDARs like Velodyne, RoboSense, Ouster
[Camera]
Scan\downsampleStep = 1
Scan\fromDepth = false
Scan\normalsK = 0
Scan\normalsRadius = 0
Scan\normalsUp = false
Scan\rangeMax = 0
Scan\rangeMin = 0
Scan\voxelSize = 0
[Gui]
General\showClouds0 = false
General\showClouds1 = false
[Core]
# Would be 0.01 for odom and 0.2 for mapping:
Icp\CorrespondenceRatio = 0.1
Icp\Epsilon = 0.001
Icp\FiltersEnabled = 2
Icp\Iterations = 10
Icp\OutlierRatio = 0.7
# ~10x the voxel size:
Icp\MaxCorrespondenceDistance = 1
Icp\MaxTranslation = 2
# Uncomment if lidar can see ground most of the time (on a car or wheeled robot):
#Icp\PointToPlaneGroundNormalsUp = 0.8
Icp\PointToPlaneK = 20
Icp\PointToPlaneMinComplexity = 0
# Make sure PM is used:
Icp\Strategy = 1
Icp\VoxelSize = 0.1
Mem\NotLinkedNodesKept = false
Mem\STMSize = 30
Odom\Deskewing = true
Odom\GuessSmoothingDelay = 0.3
Odom\ScanKeyFrameThr = 0.6
OdomF2M\ScanMaxSize = 15000
# Match voxel size:
OdomF2M\ScanSubtractRadius = 0.1
RGBD\AngularUpdate = 0.05
RGBD\LinearUpdate = 0.05
RGBD\ProximityMaxGraphDepth = 0
RGBD\ProximityPathMaxNeighbors = 1
Reg\Strategy = 1

View File

@@ -0,0 +1,46 @@
# Could be used with LiDARs like Velodyne, RoboSense, Ouster
[Camera]
Scan\downsampleStep = 1
Scan\fromDepth = false
Scan\normalsK = 0
Scan\normalsRadius = 0
Scan\normalsUp = false
Scan\rangeMax = 0
Scan\rangeMin = 0
Scan\voxelSize = 0
[Gui]
General\showClouds0 = false
General\showClouds1 = false
[Core]
# Would be 0.01 for odom and 0.2 for mapping:
Icp\CorrespondenceRatio = 0.1
Icp\Epsilon = 0.001
Icp\FiltersEnabled = 2
Icp\Iterations = 10
Icp\OutlierRatio = 0.7
# ~10x the voxel size:
Icp\MaxCorrespondenceDistance = 5
Icp\MaxTranslation = 5
# Uncomment if lidar can see ground most of the time (on a car or wheeled robot):
#Icp\PointToPlaneGroundNormalsUp = 0.8
Icp\PointToPlaneK = 20
Icp\PointToPlaneMinComplexity = 0
# Make sure PM is used:
Icp\Strategy = 1
Icp\VoxelSize = 0.5
Mem\NotLinkedNodesKept = false
Mem\STMSize = 30
Odom\Deskewing = true
Odom\GuessSmoothingDelay = 0.3
Odom\ScanKeyFrameThr = 0.6
OdomF2M\ScanMaxSize = 15000
# Match voxel size:
OdomF2M\ScanSubtractRadius = 0.5
RGBD\AngularUpdate = 0.05
RGBD\LinearUpdate = 0.05
RGBD\ProximityMaxGraphDepth = 0
RGBD\ProximityPathMaxNeighbors = 1
Reg\Strategy = 1

View File

@@ -190,9 +190,21 @@ protected Q_SLOTS:
{ {
// update camera position // update camera position
cloudViewer_->updateCameraTargetPosition(odometryCorrection_*odom.pose()); cloudViewer_->updateCameraTargetPosition(odometryCorrection_*odom.pose());
if( odom.data().imu().orientation().val[0]!=0 ||
odom.data().imu().orientation().val[1]!=0 ||
odom.data().imu().orientation().val[2]!=0 ||
odom.data().imu().orientation().val[3]!=0)
{
Eigen::Vector3f gravity(0,0,-1);
Transform orientation(0,0,0, odom.data().imu().orientation()[0], odom.data().imu().orientation()[1], odom.data().imu().orientation()[2], odom.data().imu().orientation()[3]);
gravity = (orientation* odom.data().imu().localTransform().inverse()*(odometryCorrection_*odom.pose()).rotation().inverse()).toEigen3f()*gravity;
cloudViewer_->addOrUpdateLine("odom_imu_orientation", odometryCorrection_*odom.pose(), (odometryCorrection_*odom.pose()).translation()*Transform(gravity[0], gravity[1], gravity[2], 0, 0, 0)*odom.pose().rotation().inverse(), Qt::yellow, true, true);
}
} }
} }
cloudViewer_->update(); cloudViewer_->update();
cloudViewer_->refreshView();
lastOdometryProcessed_ = true; lastOdometryProcessed_ = true;
} }
@@ -295,6 +307,7 @@ protected Q_SLOTS:
odometryCorrection_ = stats.mapCorrection(); odometryCorrection_ = stats.mapCorrection();
cloudViewer_->update(); cloudViewer_->update();
cloudViewer_->refreshView();
processingStatistics_ = false; processingStatistics_ = false;
} }

View File

@@ -26,6 +26,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Should be first on windows to avoid "WinSock.h has already been included" error // Should be first on windows to avoid "WinSock.h has already been included" error
#include "rtabmap/core/lidar/LidarVLP16.h" #include "rtabmap/core/lidar/LidarVLP16.h"
#include "rtabmap/core/lidar/LidarOuster.h"
#include <rtabmap/core/Odometry.h> #include <rtabmap/core/Odometry.h>
#include "rtabmap/core/Rtabmap.h" #include "rtabmap/core/Rtabmap.h"
@@ -35,6 +36,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "rtabmap/utilite/UEventsManager.h" #include "rtabmap/utilite/UEventsManager.h"
#include "rtabmap/utilite/UStl.h" #include "rtabmap/utilite/UStl.h"
#include "rtabmap/utilite/UDirectory.h" #include "rtabmap/utilite/UDirectory.h"
#include "rtabmap/utilite/UFile.h"
#include <QApplication> #include <QApplication>
#include <stdio.h> #include <stdio.h>
#include <pcl/io/pcd_io.h> #include <pcl/io/pcd_io.h>
@@ -47,11 +49,20 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
void showUsage() void showUsage()
{ {
printf("\nUsage:\n" printf("\nUsage:\n"
"rtabmap-lidar_mapping IP PORT\n" "rtabmap-lidar_mapping [OPTIONS] DRIVER ...\n"
"rtabmap-lidar_mapping PCAP_FILEPATH\n" "rtabmap-lidar_mapping [OPTIONS] 0 IP PORT (with VLP16 LiDAR)\n"
"rtabmap-lidar_mapping [OPTIONS] 1 IP (with Ouster LiDAR)\n"
"rtabmap-lidar_mapping [OPTIONS] DRIVER PCAP_FILEPATH [JSON_FILEPATH]\n"
"\n" "\n"
"Example:" "DRIVER: 0=VLP16, 1=Ouster\n"
" rtabmap-lidar_mapping 192.168.1.201 2368\n\n"); "JSON_FILEPATH: should be set if DRIVER=1\n"
"Options:\n"
" --resolution # Resolution of the map (default 0.05 m), indoor: 0.05-0.3, outdoor: 0.3-0.5\n"
"\n"
"Examples:\n"
" rtabmap-lidar_mapping 0 192.168.1.201 2368\n"
" rtabmap-lidar_mapping 0 scan.pcap\n"
" rtabmap-lidar_mapping 1 scan.pcap config.json\n\n");
exit(1); exit(1);
} }
@@ -59,42 +70,109 @@ using namespace rtabmap;
int main(int argc, char * argv[]) int main(int argc, char * argv[])
{ {
ULogger::setType(ULogger::kTypeConsole); ULogger::setType(ULogger::kTypeConsole);
ULogger::setLevel(ULogger::kWarning); ULogger::setLevel(ULogger::kInfo);
std::string filepath; std::string pcapFile;
std::string jsonFile;
std::string ip; std::string ip;
int port = 2368; int port = 2368;
if(argc < 2) int driver = 0;
float resolution = 0.05;
if(argc < 3)
{ {
showUsage(); showUsage();
} }
else if(argc == 2)
{
filepath = argv[1];
}
else else
{ {
ip = argv[1]; int i=1;
port = uStr2Int(argv[2]); while(i<argc-3)
{
if(strcmp("--resolution", argv[i]) == 0)
{
++i;
resolution = uStr2Float(argv[i]);
++i;
}
else
{
break;
}
}
driver = uStr2Int(argv[i++]);
if(driver <0 || driver>1)
{
printf("Not supported driver %d!\n", driver);
showUsage();
}
std::string ext = UFile::getExtension(uToLowerCase(argv[i]));
if(ext == "pcap" || ext == "osf")
{
pcapFile = argv[i++];
if(driver == 1 && ext == "pcap")
{
if(i<argc) {
jsonFile=argv[i];
}
else {
printf("Missing JSON file\n");
showUsage();
}
}
}
else if(i<argc-1) {
ip = argv[i++];
if(i<argc) {
port = uStr2Int(argv[i]);
}
else {
printf("Missing port for driver 0\n");
showUsage();
}
}
else {
printf("Only %d argument(s) provided \n", argc-1);
showUsage();
}
} }
printf("Using driver=%s\n", driver==0?"VLP16":"Ouster");
// Here is the pipeline that we will use: // Here is the pipeline that we will use:
// LidarVLP16 -> "SensorEvent" -> OdometryThread -> "OdometryEvent" -> RtabmapThread -> "RtabmapEvent" // LidarVLP16 -> "SensorEvent" -> OdometryThread -> "OdometryEvent" -> RtabmapThread -> "RtabmapEvent"
// Create the Lidar sensor, it will send a SensorEvent // Create the Lidar sensor, it will send a SensorEvent
LidarVLP16 * lidar; Lidar * lidar = 0;
if(!ip.empty()) if(!ip.empty())
{ {
printf("Using ip=%s port=%d\n", ip.c_str(), port); if(driver == 0)
lidar = new LidarVLP16(boost::asio::ip::address_v4::from_string(ip), port); {
printf("Using ip=%s port=%d\n", ip.c_str(), port);
lidar = new LidarVLP16(boost::asio::ip::address_v4::from_string(ip), port);
((LidarVLP16*)lidar)->setOrganized(true); //faster deskewing
}
else if(driver == 1)
{
printf("Using ip=%s\n", ip.c_str());
lidar = new LidarOuster(ip, 0, 0);
}
} }
else else
{ {
filepath = uReplaceChar(filepath, '~', UDirectory::homeDir()); pcapFile = uReplaceChar(pcapFile, '~', UDirectory::homeDir());
printf("Using file=%s\n", filepath.c_str()); printf("Using file=%s\n", pcapFile.c_str());
lidar = new LidarVLP16(filepath); if(driver == 0)
{
lidar = new LidarVLP16(pcapFile);
((LidarVLP16*)lidar)->setOrganized(true); //faster deskewing
lidar->setFrameRate(10); // default
}
else if(driver == 1)
{
jsonFile = uReplaceChar(jsonFile, '~', UDirectory::homeDir());
printf("Using config=%s\n", jsonFile.c_str());
lidar = new LidarOuster(pcapFile, jsonFile);
// frame rate is set based on the lidar mode
}
} }
lidar->setOrganized(true); //faster deskewing
if(!lidar->init()) if(!lidar->init())
{ {
@@ -112,8 +190,6 @@ int main(int argc, char * argv[])
ParametersMap params; ParametersMap params;
float resolution = 0.05;
// ICP parameters // ICP parameters
params.insert(ParametersPair(Parameters::kRegStrategy(), "1")); params.insert(ParametersPair(Parameters::kRegStrategy(), "1"));
params.insert(ParametersPair(Parameters::kIcpFiltersEnabled(), "2")); params.insert(ParametersPair(Parameters::kIcpFiltersEnabled(), "2"));
@@ -211,16 +287,15 @@ int main(int argc, char * argv[])
} }
if(cloud->size()) if(cloud->size())
{ {
printf("Voxel grid filtering of the assembled cloud (voxel=%f, %d points)\n", 0.01f, (int)cloud->size()); printf("Voxel grid filtering of the assembled cloud (voxel=%f, %d points)\n", resolution, (int)cloud->size());
cloud = util3d::voxelize(cloud, 0.01f); cloud = util3d::voxelize(cloud, resolution);
printf("Saving rtabmap_cloud.pcd... done! (%d points)\n", (int)cloud->size()); pcl::io::savePLYFile("rtabmap_cloud.ply", *cloud); // to save in PLY format
pcl::io::savePCDFile("rtabmap_cloud.pcd", *cloud); printf("Saving rtabmap_cloud.ply... done! (%d points)\n", (int)cloud->size());
//pcl::io::savePLYFile("rtabmap_cloud.ply", *cloud); // to save in PLY format
} }
else else
{ {
printf("Saving rtabmap_cloud.pcd... failed! The cloud is empty.\n"); printf("Saving point cloud failed! The cloud is empty.\n");
} }
// Save trajectory // Save trajectory

View File

@@ -194,6 +194,7 @@ protected Q_SLOTS:
void selectDepthAIOAKDPro(); void selectDepthAIOAKDPro();
void selectXvisioSeerSense(); void selectXvisioSeerSense();
void selectVLP16(); void selectVLP16();
void selectOuster();
void dumpTheMemory(); void dumpTheMemory();
void dumpThePrediction(); void dumpThePrediction();
void sendGoal(); void sendGoal();

View File

@@ -121,6 +121,7 @@ public:
kSrcLidar = 400, kSrcLidar = 400,
kSrcLidarVLP16 = 400, kSrcLidarVLP16 = 400,
kSrcLidarOuster = 401,
}; };
public: public:
@@ -403,6 +404,8 @@ private Q_SLOTS:
void selectSourceRealsense2JsonPath(); void selectSourceRealsense2JsonPath();
void selectSourceDepthaiBlobPath(); void selectSourceDepthaiBlobPath();
void selectVlp16PcapPath(); void selectVlp16PcapPath();
void selectOusterPcapPath();
void selectOusterJsonPath();
void updateSourceGrpVisibility(); void updateSourceGrpVisibility();
void testOdometry(); void testOdometry();
void testCamera(); void testCamera();

View File

@@ -29,6 +29,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "rtabmap/core/Parameters.h" #include "rtabmap/core/Parameters.h"
#include "rtabmap/core/CameraRGBD.h" #include "rtabmap/core/CameraRGBD.h"
#include "rtabmap/core/CameraStereo.h" #include "rtabmap/core/CameraStereo.h"
#include "rtabmap/core/lidar/LidarOuster.h"
#include "rtabmap/core/Optimizer.h" #include "rtabmap/core/Optimizer.h"
#include "ui_aboutDialog.h" #include "ui_aboutDialog.h"
#include <opencv2/core/version.hpp> #include <opencv2/core/version.hpp>
@@ -158,6 +159,8 @@ AboutDialog::AboutDialog(QWidget * parent) :
_ui->label_depthai->setText(CameraDepthAI::available() ? "Yes" : "No"); _ui->label_depthai->setText(CameraDepthAI::available() ? "Yes" : "No");
_ui->label_depthai_license->setEnabled(CameraDepthAI::available()); _ui->label_depthai_license->setEnabled(CameraDepthAI::available());
_ui->label_xvsdk->setText(CameraSeerSense::available() ? "Yes" : "No"); _ui->label_xvsdk->setText(CameraSeerSense::available() ? "Yes" : "No");
_ui->label_ouster->setText(LidarOuster::available() ? "Yes" : "No");
_ui->label_ouster_license->setEnabled(LidarOuster::available());
_ui->label_toro->setText(Optimizer::isAvailable(Optimizer::kTypeTORO)?"Yes":"No"); _ui->label_toro->setText(Optimizer::isAvailable(Optimizer::kTypeTORO)?"Yes":"No");
_ui->label_toro_license->setEnabled(Optimizer::isAvailable(Optimizer::kTypeTORO)?true:false); _ui->label_toro_license->setEnabled(Optimizer::isAvailable(Optimizer::kTypeTORO)?true:false);

View File

@@ -181,6 +181,8 @@ add_definitions(${PCL_DEFINITIONS})
SET(RESOURCES SET(RESOURCES
${PROJECT_SOURCE_DIR}/data/presets/camera_tof_icp.ini ${PROJECT_SOURCE_DIR}/data/presets/camera_tof_icp.ini
${PROJECT_SOURCE_DIR}/data/presets/lidar3d_icp.ini ${PROJECT_SOURCE_DIR}/data/presets/lidar3d_icp.ini
${PROJECT_SOURCE_DIR}/data/presets/lidar3d_icp_indoor.ini
${PROJECT_SOURCE_DIR}/data/presets/lidar3d_icp_outdoor.ini
) )
foreach(arg ${RESOURCES}) foreach(arg ${RESOURCES})

View File

@@ -46,5 +46,7 @@
<file>images/astra.png</file> <file>images/astra.png</file>
<file>images/oakdpro.png</file> <file>images/oakdpro.png</file>
<file>images/seer_sense_DS80.png</file> <file>images/seer_sense_DS80.png</file>
<file>images/ouster.png</file>
<file>images/vlp16.png</file>
</qresource> </qresource>
</RCC> </RCC>

View File

@@ -25,6 +25,9 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
// Should be first on windows to avoid "WinSock.h has already been included" error
#include "rtabmap/core/lidar/LidarVLP16.h"
#include "rtabmap/gui/MainWindow.h" #include "rtabmap/gui/MainWindow.h"
#include "ui_mainWindow.h" #include "ui_mainWindow.h"
@@ -32,6 +35,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "rtabmap/core/CameraRGB.h" #include "rtabmap/core/CameraRGB.h"
#include "rtabmap/core/CameraStereo.h" #include "rtabmap/core/CameraStereo.h"
#include "rtabmap/core/Lidar.h" #include "rtabmap/core/Lidar.h"
#include "rtabmap/core/lidar/LidarOuster.h"
#include "rtabmap/core/IMUThread.h" #include "rtabmap/core/IMUThread.h"
#include "rtabmap/core/DBReader.h" #include "rtabmap/core/DBReader.h"
#include "rtabmap/core/Parameters.h" #include "rtabmap/core/Parameters.h"
@@ -471,6 +475,7 @@ MainWindow::MainWindow(PreferencesDialog * prefDialog, QWidget * parent, bool sh
connect(_ui->actionDepthAI_oakdpro, SIGNAL(triggered()), this, SLOT(selectDepthAIOAKDPro())); connect(_ui->actionDepthAI_oakdpro, SIGNAL(triggered()), this, SLOT(selectDepthAIOAKDPro()));
connect(_ui->actionXvisio_SeerSense, SIGNAL(triggered()), this, SLOT(selectXvisioSeerSense())); connect(_ui->actionXvisio_SeerSense, SIGNAL(triggered()), this, SLOT(selectXvisioSeerSense()));
connect(_ui->actionVelodyne_VLP_16, SIGNAL(triggered()), this, SLOT(selectVLP16())); connect(_ui->actionVelodyne_VLP_16, SIGNAL(triggered()), this, SLOT(selectVLP16()));
connect(_ui->actionOuster_SDK, SIGNAL(triggered()), this, SLOT(selectOuster()));
_ui->actionFreenect->setEnabled(CameraFreenect::available()); _ui->actionFreenect->setEnabled(CameraFreenect::available());
_ui->actionOpenNI_PCL->setEnabled(CameraOpenni::available()); _ui->actionOpenNI_PCL->setEnabled(CameraOpenni::available());
_ui->actionOpenNI_PCL_ASUS->setEnabled(CameraOpenni::available()); _ui->actionOpenNI_PCL_ASUS->setEnabled(CameraOpenni::available());
@@ -499,6 +504,8 @@ MainWindow::MainWindow(PreferencesDialog * prefDialog, QWidget * parent, bool sh
_ui->actionDepthAI_oakdlite->setEnabled(CameraDepthAI::available()); _ui->actionDepthAI_oakdlite->setEnabled(CameraDepthAI::available());
_ui->actionDepthAI_oakdpro->setEnabled(CameraDepthAI::available()); _ui->actionDepthAI_oakdpro->setEnabled(CameraDepthAI::available());
_ui->actionXvisio_SeerSense->setEnabled(CameraSeerSense::available()); _ui->actionXvisio_SeerSense->setEnabled(CameraSeerSense::available());
_ui->actionVelodyne_VLP_16->setEnabled(LidarVLP16::available());
_ui->actionOuster_SDK->setEnabled(LidarOuster::available());
this->updateSelectSourceMenu(); this->updateSelectSourceMenu();
connect(_ui->actionPreferences, SIGNAL(triggered()), this, SLOT(openPreferences())); connect(_ui->actionPreferences, SIGNAL(triggered()), this, SLOT(openPreferences()));
@@ -5305,6 +5312,7 @@ void MainWindow::updateSelectSourceMenu()
_ui->actionDepthAI_oakdpro->setChecked(_preferencesDialog->getSourceDriver() == PreferencesDialog::kSrcStereoDepthAI); _ui->actionDepthAI_oakdpro->setChecked(_preferencesDialog->getSourceDriver() == PreferencesDialog::kSrcStereoDepthAI);
_ui->actionXvisio_SeerSense->setChecked(_preferencesDialog->getSourceDriver() == PreferencesDialog::kSrcSeerSense); _ui->actionXvisio_SeerSense->setChecked(_preferencesDialog->getSourceDriver() == PreferencesDialog::kSrcSeerSense);
_ui->actionVelodyne_VLP_16->setChecked(_preferencesDialog->getLidarSourceDriver() == PreferencesDialog::kSrcLidarVLP16); _ui->actionVelodyne_VLP_16->setChecked(_preferencesDialog->getLidarSourceDriver() == PreferencesDialog::kSrcLidarVLP16);
_ui->actionOuster_SDK->setChecked(_preferencesDialog->getLidarSourceDriver() == PreferencesDialog::kSrcLidarOuster);
} }
void MainWindow::changeImgRateSetting() void MainWindow::changeImgRateSetting()
@@ -7248,6 +7256,11 @@ void MainWindow::selectVLP16()
_preferencesDialog->selectSourceDriver(PreferencesDialog::kSrcLidarVLP16); _preferencesDialog->selectSourceDriver(PreferencesDialog::kSrcLidarVLP16);
} }
void MainWindow::selectOuster()
{
_preferencesDialog->selectSourceDriver(PreferencesDialog::kSrcLidarOuster);
}
void MainWindow::dumpTheMemory() void MainWindow::dumpTheMemory()
{ {
this->post(new RtabmapEventCmd(RtabmapEventCmd::kCmdDumpMemory)); this->post(new RtabmapEventCmd(RtabmapEventCmd::kCmdDumpMemory));

View File

@@ -185,6 +185,8 @@ void OdometryViewer::processData(const rtabmap::OdometryEvent & odom)
bool lost = false; bool lost = false;
bool lostStateChanged = false; bool lostStateChanged = false;
imageView_->setVisible(!odom.data().imageRaw().empty() || !odom.data().depthOrRightRaw().empty());
if(odom.pose().isNull()) if(odom.pose().isNull())
{ {
UDEBUG("odom lost"); // use last pose UDEBUG("odom lost"); // use last pose
@@ -211,7 +213,7 @@ void OdometryViewer::processData(const rtabmap::OdometryEvent & odom)
cloudView_->setBackgroundColor(Qt::black); cloudView_->setBackgroundColor(Qt::black);
} }
timeLabel_->setText(QString("%1 s").arg(odom.info().timeEstimation)); timeLabel_->setText(QString("%1 s").arg(odom.info().timeEstimation));
if(cloudShown_->isChecked() && if(cloudShown_->isChecked() &&
!odom.data().imageRaw().empty() && !odom.data().imageRaw().empty() &&
@@ -219,8 +221,8 @@ void OdometryViewer::processData(const rtabmap::OdometryEvent & odom)
(odom.data().stereoCameraModels().size() || odom.data().cameraModels().size())) (odom.data().stereoCameraModels().size() || odom.data().cameraModels().size()))
{ {
UDEBUG("New pose = %s, quality=%d", odom.pose().prettyPrint().c_str(), quality); UDEBUG("New pose = %s, quality=%d", odom.pose().prettyPrint().c_str(), quality);
if(!odom.data().depthRaw().empty()) if(!odom.data().depthRaw().empty())
{ {
if(odom.data().imageRaw().cols % decimationSpin_->value() == 0 && if(odom.data().imageRaw().cols % decimationSpin_->value() == 0 &&
odom.data().imageRaw().rows % decimationSpin_->value() == 0) odom.data().imageRaw().rows % decimationSpin_->value() == 0)
@@ -238,14 +240,14 @@ void OdometryViewer::processData(const rtabmap::OdometryEvent & odom)
} }
} }
else else
{ {
validDecimationValue_ = decimationSpin_->value(); validDecimationValue_ = decimationSpin_->value();
} }
// visualization: buffering the clouds // visualization: buffering the clouds
// Create the new cloud // Create the new cloud
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud; pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud;
pcl::IndicesPtr validIndices(new std::vector<int>); pcl::IndicesPtr validIndices(new std::vector<int>);
cloud = util3d::cloudRGBFromSensorData( cloud = util3d::cloudRGBFromSensorData(
odom.data(), odom.data(),
validDecimationValue_, validDecimationValue_,
@@ -257,7 +259,7 @@ void OdometryViewer::processData(const rtabmap::OdometryEvent & odom)
if(voxelSpin_->value()) if(voxelSpin_->value())
{ {
cloud = util3d::voxelize(cloud, validIndices, voxelSpin_->value()); cloud = util3d::voxelize(cloud, validIndices, voxelSpin_->value());
} }
if(cloud->size()) if(cloud->size())
{ {
@@ -489,6 +491,7 @@ void OdometryViewer::processData(const rtabmap::OdometryEvent & odom)
imageView_->update(); imageView_->update();
cloudView_->update(); cloudView_->update();
cloudView_->refreshView();
QApplication::processEvents(); QApplication::processEvents();
processingData_ = false; processingData_ = false;
} }

View File

@@ -27,10 +27,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include <pcl/pcl_config.h> #include <pcl/pcl_config.h>
#if PCL_VERSION_COMPARE(>=, 1, 8, 0)
// Should be first on windows to avoid "WinSock.h has already been included" error // Should be first on windows to avoid "WinSock.h has already been included" error
#include "rtabmap/core/lidar/LidarVLP16.h" #include "rtabmap/core/lidar/LidarVLP16.h"
#endif #include "rtabmap/core/lidar/LidarOuster.h"
#include "rtabmap/gui/PreferencesDialog.h" #include "rtabmap/gui/PreferencesDialog.h"
#include "rtabmap/gui/DatabaseViewer.h" #include "rtabmap/gui/DatabaseViewer.h"
@@ -93,6 +92,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Presets // Presets
#include "camera_tof_icp_ini.h" #include "camera_tof_icp_ini.h"
#include "lidar3d_icp_ini.h" #include "lidar3d_icp_ini.h"
#include "lidar3d_icp_indoor_ini.h"
#include "lidar3d_icp_outdoor_ini.h"
#include <opencv2/opencv_modules.hpp> #include <opencv2/opencv_modules.hpp>
#include <rtabmap/core/SensorCaptureThread.h> #include <rtabmap/core/SensorCaptureThread.h>
@@ -430,6 +431,16 @@ PreferencesDialog::PreferencesDialog(QWidget * parent) :
_ui->openni2_exposure->setEnabled(CameraOpenNI2::exposureGainAvailable()); _ui->openni2_exposure->setEnabled(CameraOpenNI2::exposureGainAvailable());
_ui->openni2_gain->setEnabled(CameraOpenNI2::exposureGainAvailable()); _ui->openni2_gain->setEnabled(CameraOpenNI2::exposureGainAvailable());
//LiDARs
if (!LidarVLP16::available())
{
_ui->comboBox_lidar_src->setItemData(kSrcLidarVLP16 - kSrcLidar, 0, Qt::UserRole - 1);
}
if (!LidarOuster::available())
{
_ui->comboBox_lidar_src->setItemData(kSrcLidarOuster - kSrcLidar, 0, Qt::UserRole - 1);
}
#if PCL_VERSION_COMPARE(<, 1, 7, 2) #if PCL_VERSION_COMPARE(<, 1, 7, 2)
_ui->checkBox_showFrustums->setEnabled(false); _ui->checkBox_showFrustums->setEnabled(false);
_ui->checkBox_showFrustums->setChecked(false); _ui->checkBox_showFrustums->setChecked(false);
@@ -480,6 +491,8 @@ PreferencesDialog::PreferencesDialog(QWidget * parent) :
connect(_ui->pushButton_resetConfig, SIGNAL(clicked()), this, SLOT(resetConfig())); connect(_ui->pushButton_resetConfig, SIGNAL(clicked()), this, SLOT(resetConfig()));
connect(_ui->pushButton_presets_camera_tof_icp, SIGNAL(clicked()), this, SLOT(loadPreset())); connect(_ui->pushButton_presets_camera_tof_icp, SIGNAL(clicked()), this, SLOT(loadPreset()));
connect(_ui->pushButton_presets_lidar_3d_icp, SIGNAL(clicked()), this, SLOT(loadPreset())); connect(_ui->pushButton_presets_lidar_3d_icp, SIGNAL(clicked()), this, SLOT(loadPreset()));
connect(_ui->pushButton_presets_lidar_3d_icp_indoor, SIGNAL(clicked()), this, SLOT(loadPreset()));
connect(_ui->pushButton_presets_lidar_3d_icp_outdoor, SIGNAL(clicked()), this, SLOT(loadPreset()));
connect(_ui->radioButton_basic, SIGNAL(toggled(bool)), this, SLOT(setupTreeView())); connect(_ui->radioButton_basic, SIGNAL(toggled(bool)), this, SLOT(setupTreeView()));
connect(_ui->pushButton_testOdometry, SIGNAL(clicked()), this, SLOT(testOdometry())); connect(_ui->pushButton_testOdometry, SIGNAL(clicked()), this, SLOT(testOdometry()));
connect(_ui->pushButton_test_camera, SIGNAL(clicked()), this, SLOT(testCamera())); connect(_ui->pushButton_test_camera, SIGNAL(clicked()), this, SLOT(testCamera()));
@@ -917,6 +930,13 @@ PreferencesDialog::PreferencesDialog(QWidget * parent) :
connect(_ui->checkBox_vlp16_organized, SIGNAL(stateChanged(int)), this, SLOT(makeObsoleteSourcePanel())); connect(_ui->checkBox_vlp16_organized, SIGNAL(stateChanged(int)), this, SLOT(makeObsoleteSourcePanel()));
connect(_ui->checkBox_vlp16_hostTime, SIGNAL(stateChanged(int)), this, SLOT(makeObsoleteSourcePanel())); connect(_ui->checkBox_vlp16_hostTime, SIGNAL(stateChanged(int)), this, SLOT(makeObsoleteSourcePanel()));
connect(_ui->checkBox_vlp16_stamp_last, SIGNAL(stateChanged(int)), this, SLOT(makeObsoleteSourcePanel())); connect(_ui->checkBox_vlp16_stamp_last, SIGNAL(stateChanged(int)), this, SLOT(makeObsoleteSourcePanel()));
connect(_ui->lineEdit_ouster_ip_hostname, SIGNAL(textChanged(const QString &)), this, SLOT(makeObsoleteSourcePanel()));
connect(_ui->comboBox_ouster_lidar_mode, SIGNAL(currentIndexChanged(int)), this, SLOT(makeObsoleteSourcePanel()));
connect(_ui->comboBox_ouster_timestamp, SIGNAL(currentIndexChanged(int)), this, SLOT(makeObsoleteSourcePanel()));
connect(_ui->toolButton_ouster_pcap_path, SIGNAL(clicked()), this, SLOT(selectOusterPcapPath()));
connect(_ui->toolButton_ouster_json_path, SIGNAL(clicked()), this, SLOT(selectOusterJsonPath()));
connect(_ui->lineEdit_ouster_pcap_path, SIGNAL(textChanged(const QString &)), this, SLOT(makeObsoleteSourcePanel()));
connect(_ui->lineEdit_ouster_json_path, SIGNAL(textChanged(const QString &)), this, SLOT(makeObsoleteSourcePanel()));
//Rtabmap basic //Rtabmap basic
connect(_ui->general_doubleSpinBox_timeThr, SIGNAL(valueChanged(double)), _ui->general_doubleSpinBox_timeThr_2, SLOT(setValue(double))); connect(_ui->general_doubleSpinBox_timeThr, SIGNAL(valueChanged(double)), _ui->general_doubleSpinBox_timeThr_2, SLOT(setValue(double)));
@@ -2286,6 +2306,13 @@ void PreferencesDialog::resetSettings(QGroupBox * groupBox)
_ui->checkBox_vlp16_organized->setChecked(false); _ui->checkBox_vlp16_organized->setChecked(false);
_ui->checkBox_vlp16_hostTime->setChecked(true); _ui->checkBox_vlp16_hostTime->setChecked(true);
_ui->checkBox_vlp16_stamp_last->setChecked(true); _ui->checkBox_vlp16_stamp_last->setChecked(true);
_ui->lineEdit_ouster_ip_hostname->clear();
_ui->comboBox_ouster_lidar_mode->setCurrentIndex(0);
_ui->comboBox_ouster_timestamp->setCurrentIndex(0);
_ui->lineEdit_ouster_pcap_path->clear();
_ui->lineEdit_ouster_json_path->clear();
_ui->checkBox_ouster_reflectivity->setChecked(true);
_ui->checkBox_ouster_imu->setChecked(false);
_ui->groupBox_depthFromScan->setChecked(false); _ui->groupBox_depthFromScan->setChecked(false);
_ui->groupBox_depthFromScan_fillHoles->setChecked(true); _ui->groupBox_depthFromScan_fillHoles->setChecked(true);
@@ -2850,6 +2877,16 @@ void PreferencesDialog::readCameraSettings(const QString & filePath)
_ui->checkBox_vlp16_stamp_last->setChecked(settings.value("stampLast", _ui->checkBox_vlp16_stamp_last->isChecked()).toBool()); _ui->checkBox_vlp16_stamp_last->setChecked(settings.value("stampLast", _ui->checkBox_vlp16_stamp_last->isChecked()).toBool());
settings.endGroup(); // VLP16 settings.endGroup(); // VLP16
settings.beginGroup("Ouster");
_ui->lineEdit_ouster_ip_hostname->setText(settings.value("ip",_ui->lineEdit_ouster_ip_hostname->text()).toString());
_ui->comboBox_ouster_lidar_mode->setCurrentIndex(settings.value("lidarMode", _ui->comboBox_ouster_lidar_mode->currentIndex()).toInt());
_ui->comboBox_ouster_timestamp->setCurrentIndex(settings.value("timestamp", _ui->comboBox_ouster_timestamp->currentIndex()).toInt());
_ui->lineEdit_ouster_pcap_path->setText(settings.value("pcapPath",_ui->lineEdit_ouster_pcap_path->text()).toString());
_ui->lineEdit_ouster_json_path->setText(settings.value("jsonPath",_ui->lineEdit_ouster_json_path->text()).toString());
_ui->checkBox_ouster_reflectivity->setChecked(settings.value("reflectivity",_ui->checkBox_ouster_reflectivity->isChecked()).toBool());
_ui->checkBox_ouster_imu->setChecked(settings.value("imu",_ui->checkBox_ouster_imu->isChecked()).toBool());
settings.endGroup(); // Ouster
settings.endGroup(); // Lidar settings.endGroup(); // Lidar
_calibrationDialog->loadSettings(settings, "CalibrationDialog"); _calibrationDialog->loadSettings(settings, "CalibrationDialog");
@@ -3004,6 +3041,14 @@ void PreferencesDialog::loadPreset()
{ {
loadPreset(LIDAR3D_ICP_INI); loadPreset(LIDAR3D_ICP_INI);
} }
else if(sender() == _ui->pushButton_presets_lidar_3d_icp_indoor)
{
loadPreset(LIDAR3D_ICP_INDOOR_INI);
}
else if(sender() == _ui->pushButton_presets_lidar_3d_icp_outdoor)
{
loadPreset(LIDAR3D_ICP_OUTDOOR_INI);
}
else else
{ {
UERROR("Unknown sender!"); UERROR("Unknown sender!");
@@ -3448,6 +3493,16 @@ void PreferencesDialog::writeCameraSettings(const QString & filePath) const
settings.setValue("stampLast", _ui->checkBox_vlp16_stamp_last->isChecked()); settings.setValue("stampLast", _ui->checkBox_vlp16_stamp_last->isChecked());
settings.endGroup(); // VLP16 settings.endGroup(); // VLP16
settings.beginGroup("Ouster");
settings.setValue("ip",_ui->lineEdit_ouster_ip_hostname->text());
settings.setValue("lidarMode", _ui->comboBox_ouster_lidar_mode->currentIndex());
settings.setValue("timestamp", _ui->comboBox_ouster_timestamp->currentIndex());
settings.setValue("pcapPath",_ui->lineEdit_ouster_pcap_path->text());
settings.setValue("jsonPath",_ui->lineEdit_ouster_json_path->text());
settings.setValue("reflectivity",_ui->checkBox_ouster_reflectivity->isChecked());
settings.setValue("imu",_ui->checkBox_ouster_imu->isChecked());
settings.endGroup(); // Ouster
settings.endGroup(); // Lidar settings.endGroup(); // Lidar
_calibrationDialog->saveSettings(settings, "CalibrationDialog"); _calibrationDialog->saveSettings(settings, "CalibrationDialog");
@@ -4263,7 +4318,7 @@ void PreferencesDialog::selectSourceDriver(Src src, int variant)
} }
else if(src >= kSrcLidar) else if(src >= kSrcLidar)
{ {
_ui->comboBox_lidar_src->setCurrentIndex(kSrcLidarVLP16 - kSrcLidar + 1); _ui->comboBox_lidar_src->setCurrentIndex(src - kSrcLidar + 1);
} }
if(previousCameraSrc == kSrcUndef && src < kSrcDatabase && if(previousCameraSrc == kSrcUndef && src < kSrcDatabase &&
@@ -4277,7 +4332,7 @@ void PreferencesDialog::selectSourceDriver(Src src, int variant)
} }
else if(previousLidarSrc== kSrcUndef && src >= kSrcLidar && else if(previousLidarSrc== kSrcUndef && src >= kSrcLidar &&
QMessageBox::question(this, tr("LiDAR Source..."), QMessageBox::question(this, tr("LiDAR Source..."),
tr("Do you want to use \"LiDAR 3D ICP\" preset?"), tr("Do you want to use \"LiDAR 3D ICP\" preset? You can open Preferences->General Settings to select indoor or outdoor presets afterwards."),
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) == QMessageBox::Yes) QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) == QMessageBox::Yes)
{ {
loadPreset(LIDAR3D_ICP_INI); loadPreset(LIDAR3D_ICP_INI);
@@ -4748,6 +4803,38 @@ void PreferencesDialog::selectVlp16PcapPath()
} }
} }
void PreferencesDialog::selectOusterPcapPath()
{
QString dir = _ui->lineEdit_ouster_pcap_path->text();
if (dir.isEmpty())
{
dir = getWorkingDirectory();
}
QString path = QFileDialog::getOpenFileName(this, tr("Select file"), dir, tr("Ouster recording (*.pcap *.osf)"));
if (!path.isEmpty())
{
_ui->lineEdit_ouster_pcap_path->setText(path);
if(QFileInfo(path).suffix() == "pcap")
{
selectOusterJsonPath();
}
}
}
void PreferencesDialog::selectOusterJsonPath()
{
QString dir = _ui->lineEdit_ouster_json_path->text();
if (dir.isEmpty())
{
dir = getWorkingDirectory();
}
QString path = QFileDialog::getOpenFileName(this, tr("Select file"), dir, tr("Ouster recording config (*.json)"));
if (!path.isEmpty())
{
_ui->lineEdit_ouster_json_path->setText(path);
}
}
void PreferencesDialog::setParameter(const std::string & key, const std::string & value) void PreferencesDialog::setParameter(const std::string & key, const std::string & value)
{ {
UDEBUG("%s=%s", key.c_str(), value.c_str()); UDEBUG("%s=%s", key.c_str(), value.c_str());
@@ -5698,6 +5785,7 @@ void PreferencesDialog::updateSourceGrpVisibility()
} }
_ui->stackedWidget_lidar_src->setVisible(_ui->comboBox_lidar_src->currentIndex() > 0); _ui->stackedWidget_lidar_src->setVisible(_ui->comboBox_lidar_src->currentIndex() > 0);
_ui->groupBox_vlp16->setVisible(_ui->comboBox_lidar_src->currentIndex()-1 == kSrcLidarVLP16-kSrcLidar); _ui->groupBox_vlp16->setVisible(_ui->comboBox_lidar_src->currentIndex()-1 == kSrcLidarVLP16-kSrcLidar);
_ui->groupBox_ouster->setVisible(_ui->comboBox_lidar_src->currentIndex()-1 == kSrcLidarOuster-kSrcLidar);
_ui->frame_lidar_sensor->setVisible(_ui->comboBox_lidar_src->currentIndex() > 0 || _ui->checkBox_source_scanFromDepth->isChecked()); // Not Lidar None or database input _ui->frame_lidar_sensor->setVisible(_ui->comboBox_lidar_src->currentIndex() > 0 || _ui->checkBox_source_scanFromDepth->isChecked()); // Not Lidar None or database input
_ui->pushButton_test_lidar->setEnabled(_ui->comboBox_lidar_src->currentIndex() > 0); _ui->pushButton_test_lidar->setEnabled(_ui->comboBox_lidar_src->currentIndex() > 0);
@@ -6987,9 +7075,8 @@ Lidar * PreferencesDialog::createLidar()
{ {
Lidar * lidar = 0; Lidar * lidar = 0;
Src driver = getLidarSourceDriver(); Src driver = getLidarSourceDriver();
if(driver == kSrcLidarVLP16) if(driver >= kSrcLidarVLP16 && driver <= kSrcLidarOuster)
{ {
#if PCL_VERSION_COMPARE(>=, 1, 8, 0)
Transform localTransform = Transform::fromString(_ui->lineEdit_lidar_local_transform->text().replace("PI_2", QString::number(3.141592/2.0)).toStdString()); Transform localTransform = Transform::fromString(_ui->lineEdit_lidar_local_transform->text().replace("PI_2", QString::number(3.141592/2.0)).toStdString());
if(localTransform.isNull()) if(localTransform.isNull())
{ {
@@ -6997,33 +7084,73 @@ Lidar * PreferencesDialog::createLidar()
_ui->lineEdit_lidar_local_transform->text().toStdString().c_str()); _ui->lineEdit_lidar_local_transform->text().toStdString().c_str());
localTransform = Transform::getIdentity(); localTransform = Transform::getIdentity();
} }
if(!_ui->lineEdit_vlp16_pcap_path->text().isEmpty()) if(driver == kSrcLidarVLP16)
{ {
// PCAP mode if(!_ui->lineEdit_vlp16_pcap_path->text().isEmpty())
lidar = new LidarVLP16( {
_ui->lineEdit_vlp16_pcap_path->text().toStdString(), // PCAP mode
_ui->checkBox_vlp16_organized->isChecked(), lidar = new LidarVLP16(
_ui->checkBox_vlp16_stamp_last->isChecked(), _ui->lineEdit_vlp16_pcap_path->text().toStdString(),
this->getGeneralInputRate(), _ui->checkBox_vlp16_organized->isChecked(),
localTransform); _ui->checkBox_vlp16_stamp_last->isChecked(),
this->getGeneralInputRate(),
localTransform);
}
else
{
// Connect to sensor
lidar = new LidarVLP16(
boost::asio::ip::address_v4::from_string(uFormat("%ld.%ld.%ld.%ld",
(size_t)_ui->spinBox_vlp16_ip1->value(),
(size_t)_ui->spinBox_vlp16_ip2->value(),
(size_t)_ui->spinBox_vlp16_ip3->value(),
(size_t)_ui->spinBox_vlp16_ip4->value())),
_ui->spinBox_vlp16_port->value(),
_ui->checkBox_vlp16_organized->isChecked(),
_ui->checkBox_vlp16_hostTime->isChecked(),
_ui->checkBox_vlp16_stamp_last->isChecked(),
this->getGeneralInputRate(),
localTransform);
}
} }
else else // Ouster
{ {
// Connect to sensor if(!_ui->lineEdit_ouster_pcap_path->text().isEmpty())
{
lidar = new LidarVLP16( // PCAP/OSF mode
boost::asio::ip::address_v4::from_string(uFormat("%ld.%ld.%ld.%ld", lidar = new LidarOuster(
(size_t)_ui->spinBox_vlp16_ip1->value(), _ui->lineEdit_ouster_pcap_path->text().toStdString(),
(size_t)_ui->spinBox_vlp16_ip2->value(), _ui->lineEdit_ouster_json_path->text().toStdString(),
(size_t)_ui->spinBox_vlp16_ip3->value(), 0, // lidar mode ignored
(size_t)_ui->spinBox_vlp16_ip4->value())), 0, // timestamp mode ignored
_ui->spinBox_vlp16_port->value(), _ui->checkBox_ouster_reflectivity->isChecked(),
_ui->checkBox_vlp16_organized->isChecked(), _ui->checkBox_ouster_imu->isChecked(),
_ui->checkBox_vlp16_hostTime->isChecked(), this->getGeneralInputRate(),
_ui->checkBox_vlp16_stamp_last->isChecked(), localTransform);
this->getGeneralInputRate(), }
localTransform); else if(!_ui->lineEdit_ouster_ip_hostname->text().isEmpty())
{
// Connect to sensor
lidar = new LidarOuster(
_ui->lineEdit_ouster_ip_hostname->text().toStdString(),
"", // data destination ignored
_ui->comboBox_ouster_lidar_mode->currentIndex(),
_ui->comboBox_ouster_timestamp->currentIndex(),
_ui->checkBox_ouster_reflectivity->isChecked(),
_ui->checkBox_ouster_imu->isChecked(),
this->getGeneralInputRate(),
localTransform);
}
else
{
QMessageBox::warning(this,
tr("RTAB-Map"),
tr("Ouster: An IP/hostname or PCAP path should be set..."));
return lidar; // 0
}
} }
if(!lidar->init()) if(!lidar->init())
{ {
@@ -7034,12 +7161,6 @@ Lidar * PreferencesDialog::createLidar()
delete lidar; delete lidar;
lidar = 0; lidar = 0;
} }
#else
UWARN("Lidar cannot be used with rtabmap built with PCL < 1.8... ");
QMessageBox::warning(this,
tr("RTAB-Map"),
tr("Lidar initialization failed..."));
#endif
} }
return lidar; return lidar;
} }
@@ -7182,8 +7303,9 @@ void PreferencesDialog::setSLAMMode(bool enabled)
void PreferencesDialog::testOdometry() void PreferencesDialog::testOdometry()
{ {
Camera * camera = this->createCamera(); Camera * camera = this->getSourceDriver()!=-1?this->createCamera():0;
if(!camera) Lidar * lidar = this->getLidarSourceDriver()!=-1?this->createLidar():0;
if(!camera && !lidar)
{ {
return; return;
} }
@@ -7241,18 +7363,30 @@ void PreferencesDialog::testOdometry()
odomViewer->resize(1280, 480+QPushButton().minimumHeight()); odomViewer->resize(1280, 480+QPushButton().minimumHeight());
odomViewer->registerToEventsManager(); odomViewer->registerToEventsManager();
SensorCaptureThread cameraThread(camera, this->getAllParameters()); // take ownership of camera SensorCaptureThread * cameraThread;
cameraThread.setMirroringEnabled(isSourceMirroring()); if(lidar && camera)
cameraThread.setColorOnly(_ui->checkbox_rgbd_colorOnly->isChecked()); {
cameraThread.setImageDecimation(_ui->spinBox_source_imageDecimation->value()); cameraThread = new SensorCaptureThread(lidar, camera, this->getAllParameters()); // take ownership of lidar and camera
cameraThread.setHistogramMethod(_ui->comboBox_source_histogramMethod->currentIndex()); }
else if(camera)
{
cameraThread = new SensorCaptureThread(camera, this->getAllParameters()); // take ownership of camera
}
else // lidar
{
cameraThread = new SensorCaptureThread(lidar, this->getAllParameters()); // take ownership of lidar
}
cameraThread->setMirroringEnabled(isSourceMirroring());
cameraThread->setColorOnly(_ui->checkbox_rgbd_colorOnly->isChecked());
cameraThread->setImageDecimation(_ui->spinBox_source_imageDecimation->value());
cameraThread->setHistogramMethod(_ui->comboBox_source_histogramMethod->currentIndex());
if(_ui->checkbox_source_feature_detection->isChecked()) if(_ui->checkbox_source_feature_detection->isChecked())
{ {
cameraThread.enableFeatureDetection(this->getAllParameters()); cameraThread->enableFeatureDetection(this->getAllParameters());
} }
cameraThread.setStereoToDepth(_ui->checkbox_stereo_depthGenerated->isChecked()); cameraThread->setStereoToDepth(_ui->checkbox_stereo_depthGenerated->isChecked());
cameraThread.setStereoExposureCompensation(_ui->checkBox_stereo_exposureCompensation->isChecked()); cameraThread->setStereoExposureCompensation(_ui->checkBox_stereo_exposureCompensation->isChecked());
cameraThread.setScanParameters( cameraThread->setScanParameters(
_ui->checkBox_source_scanFromDepth->isChecked(), _ui->checkBox_source_scanFromDepth->isChecked(),
_ui->spinBox_source_scanDownsampleStep->value(), _ui->spinBox_source_scanDownsampleStep->value(),
_ui->doubleSpinBox_source_scanRangeMin->value(), _ui->doubleSpinBox_source_scanRangeMin->value(),
@@ -7264,23 +7398,23 @@ void PreferencesDialog::testOdometry()
_ui->checkBox_source_scanDeskewing->isChecked()); _ui->checkBox_source_scanDeskewing->isChecked());
if(_ui->comboBox_imuFilter_strategy->currentIndex()>0 && dynamic_cast<DBReader*>(camera) == 0) if(_ui->comboBox_imuFilter_strategy->currentIndex()>0 && dynamic_cast<DBReader*>(camera) == 0)
{ {
cameraThread.enableIMUFiltering(_ui->comboBox_imuFilter_strategy->currentIndex()-1, this->getAllParameters(), _ui->checkBox_imuFilter_baseFrameConversion->isChecked()); cameraThread->enableIMUFiltering(_ui->comboBox_imuFilter_strategy->currentIndex()-1, this->getAllParameters(), _ui->checkBox_imuFilter_baseFrameConversion->isChecked());
} }
if(isDepthFilteringAvailable()) if(isDepthFilteringAvailable())
{ {
if(_ui->groupBox_bilateral->isChecked()) if(_ui->groupBox_bilateral->isChecked())
{ {
cameraThread.enableBilateralFiltering( cameraThread->enableBilateralFiltering(
_ui->doubleSpinBox_bilateral_sigmaS->value(), _ui->doubleSpinBox_bilateral_sigmaS->value(),
_ui->doubleSpinBox_bilateral_sigmaR->value()); _ui->doubleSpinBox_bilateral_sigmaR->value());
} }
if(!_ui->lineEdit_source_distortionModel->text().isEmpty()) if(!_ui->lineEdit_source_distortionModel->text().isEmpty())
{ {
cameraThread.setDistortionModel(_ui->lineEdit_source_distortionModel->text().toStdString()); cameraThread->setDistortionModel(_ui->lineEdit_source_distortionModel->text().toStdString());
} }
} }
UEventsManager::createPipe(&cameraThread, &odomThread, "SensorEvent"); UEventsManager::createPipe(cameraThread, &odomThread, "SensorEvent");
if(imuThread) if(imuThread)
{ {
UEventsManager::createPipe(imuThread, &odomThread, "IMUEvent"); UEventsManager::createPipe(imuThread, &odomThread, "IMUEvent");
@@ -7289,7 +7423,7 @@ void PreferencesDialog::testOdometry()
UEventsManager::createPipe(odomViewer, &odomThread, "OdometryResetEvent"); UEventsManager::createPipe(odomViewer, &odomThread, "OdometryResetEvent");
odomThread.start(); odomThread.start();
cameraThread.start(); cameraThread->start();
if(imuThread) if(imuThread)
{ {
@@ -7304,8 +7438,9 @@ void PreferencesDialog::testOdometry()
imuThread->join(true); imuThread->join(true);
delete imuThread; delete imuThread;
} }
cameraThread.join(true); cameraThread->join(true);
odomThread.join(true); odomThread.join(true);
delete cameraThread;
} }
void PreferencesDialog::testCamera() void PreferencesDialog::testCamera()

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

BIN
guilib/src/images/vlp16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

File diff suppressed because it is too large Load Diff

View File

@@ -356,7 +356,32 @@
<property name="title"> <property name="title">
<string>LiDAR</string> <string>LiDAR</string>
</property> </property>
<addaction name="actionVelodyne_VLP_16"/> <property name="icon">
<iconset resource="../GuiLib.qrc">
<normaloff>:/images/vlp16.png</normaloff>:/images/vlp16.png</iconset>
</property>
<widget class="QMenu" name="menuVelodyne">
<property name="title">
<string>Velodyne</string>
</property>
<property name="icon">
<iconset resource="../GuiLib.qrc">
<normaloff>:/images/vlp16.png</normaloff>:/images/vlp16.png</iconset>
</property>
<addaction name="actionVelodyne_VLP_16"/>
</widget>
<widget class="QMenu" name="menuOuster">
<property name="title">
<string>Ouster</string>
</property>
<property name="icon">
<iconset resource="../GuiLib.qrc">
<normaloff>:/images/ouster.png</normaloff>:/images/ouster.png</iconset>
</property>
<addaction name="actionOuster_SDK"/>
</widget>
<addaction name="menuVelodyne"/>
<addaction name="menuOuster"/>
</widget> </widget>
<addaction name="menuRGB_D_camera"/> <addaction name="menuRGB_D_camera"/>
<addaction name="menuStereo_camera"/> <addaction name="menuStereo_camera"/>
@@ -1749,6 +1774,14 @@
<string>Xvisio</string> <string>Xvisio</string>
</property> </property>
</action> </action>
<action name="actionOuster_SDK">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Ouster SDK</string>
</property>
</action>
</widget> </widget>
<customwidgets> <customwidgets>
<customwidget> <customwidget>

View File

@@ -63,9 +63,9 @@
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>-3879</y>
<width>713</width> <width>713</width>
<height>4653</height> <height>4737</height>
</rect> </rect>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_16"> <layout class="QVBoxLayout" name="verticalLayout_16">
@@ -95,7 +95,7 @@
<enum>QFrame::Raised</enum> <enum>QFrame::Raised</enum>
</property> </property>
<property name="currentIndex"> <property name="currentIndex">
<number>7</number> <number>5</number>
</property> </property>
<widget class="QWidget" name="page_22"> <widget class="QWidget" name="page_22">
<layout class="QVBoxLayout" name="verticalLayout_29" stretch="0,0"> <layout class="QVBoxLayout" name="verticalLayout_29" stretch="0,0">
@@ -369,10 +369,16 @@
</item> </item>
<item> <item>
<layout class="QGridLayout" name="gridLayout_135" columnstretch="0,1"> <layout class="QGridLayout" name="gridLayout_135" columnstretch="0,1">
<item row="0" column="0"> <item row="1" column="1">
<widget class="QPushButton" name="pushButton_presets_camera_tof_icp"> <widget class="QLabel" name="label_753">
<property name="text"> <property name="text">
<string>TOF Camera ICP</string> <string>e.g., Velodyne, RoboSense and Ouster LiDARs.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property> </property>
</widget> </widget>
</item> </item>
@@ -395,12 +401,46 @@
<item row="1" column="0"> <item row="1" column="0">
<widget class="QPushButton" name="pushButton_presets_lidar_3d_icp"> <widget class="QPushButton" name="pushButton_presets_lidar_3d_icp">
<property name="text"> <property name="text">
<string>LiDAR 3D ICP</string> <string>LiDAR 3D ICP Legacy (5 cm voxel)</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="1"> <item row="2" column="0">
<widget class="QLabel" name="label_753"> <widget class="QPushButton" name="pushButton_presets_lidar_3d_icp_indoor">
<property name="text">
<string>LiDAR 3D ICP Indoor (10 cm voxel)</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QPushButton" name="pushButton_presets_camera_tof_icp">
<property name="text">
<string>TOF Camera ICP</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QPushButton" name="pushButton_presets_lidar_3d_icp_outdoor">
<property name="text">
<string>LiDAR 3D ICP Oudoor (50 cm voxel)</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="label_763">
<property name="text">
<string>e.g., Velodyne, RoboSense and Ouster LiDARs.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="label_764">
<property name="text"> <property name="text">
<string>e.g., Velodyne, RoboSense and Ouster LiDARs.</string> <string>e.g., Velodyne, RoboSense and Ouster LiDARs.</string>
</property> </property>
@@ -8465,6 +8505,11 @@ when using the file type, logs are saved in LogRtabmap.txt (located in the worki
<string>VLP-16</string> <string>VLP-16</string>
</property> </property>
</item> </item>
<item>
<property name="text">
<string>Ouster SDK</string>
</property>
</item>
</widget> </widget>
</item> </item>
<item row="0" column="1"> <item row="0" column="1">
@@ -8761,7 +8806,7 @@ when using the file type, logs are saved in LogRtabmap.txt (located in the worki
<item> <item>
<widget class="QStackedWidget" name="stackedWidget_lidar_src"> <widget class="QStackedWidget" name="stackedWidget_lidar_src">
<property name="currentIndex"> <property name="currentIndex">
<number>1</number> <number>2</number>
</property> </property>
<widget class="QWidget" name="page_98"> <widget class="QWidget" name="page_98">
<layout class="QVBoxLayout" name="verticalLayout_177"> <layout class="QVBoxLayout" name="verticalLayout_177">
@@ -9033,6 +9078,243 @@ when using the file type, logs are saved in LogRtabmap.txt (located in the worki
</item> </item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="page_100">
<layout class="QVBoxLayout" name="verticalLayout_181">
<item>
<widget class="QGroupBox" name="groupBox_ouster">
<property name="title">
<string>Ouster SDK</string>
</property>
<layout class="QGridLayout" name="gridLayout_136" columnstretch="0,0,1">
<item row="4" column="0">
<widget class="QToolButton" name="toolButton_ouster_pcap_path">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QCheckBox" name="checkBox_ouster_reflectivity">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="5" column="2">
<widget class="QLabel" name="label_760">
<property name="text">
<string>Path to a *.JSON config file corresponding to PCAP recording.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="label_758">
<property name="text">
<string>IP Address or Hostname.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QLabel" name="label_7482">
<property name="toolTip">
<string>This can speedup deskewing.</string>
</property>
<property name="text">
<string>Lidar mode.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="lineEdit_ouster_ip_hostname"/>
</item>
<item row="3" column="1">
<widget class="QComboBox" name="comboBox_ouster_timestamp">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;TIME_FROM_INTERNAL_OSC: Use the internal clock.&lt;/p&gt;&lt;p&gt;TIME_FROM_SYNC_PULSE_IN: A free running counter synced to the SYNC_PULSE_IN input counts seconds (# of pulses) and nanoseconds since sensor turn on.&lt;/p&gt;&lt;p&gt;TIME_FROM_PTP_1588: Synchronize with an external PTP master.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<item>
<property name="text">
<string>Default</string>
</property>
</item>
<item>
<property name="text">
<string>From Internal OSC</string>
</property>
</item>
<item>
<property name="text">
<string>From Sync Pulse In</string>
</property>
</item>
<item>
<property name="text">
<string>From PTP 1588</string>
</property>
</item>
</widget>
</item>
<item row="8" column="1">
<spacer name="verticalSpacer_32">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="4" column="2">
<widget class="QLabel" name="label_759">
<property name="text">
<string>Path to a *.PCAP or *.OSF file. IP is ignored if PCAP/OSF file is used. A JSON file should be provided if PCAP is used, see below.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QLabel" name="label_755">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If the lidar is not connected to a GPS, the lidar's clock may be not in sync with the host computer's clock. By enabling this, as soon as a packet is received, it will be stamped with host time &amp;quot;now&amp;quot;. Note that it affects only the final scan timsestamp, not the individual relative timestamp for each point. It is recommended to enable this if you combine it with other sensors above and it is not connected to a GPS.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Timestamp mode.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="comboBox_ouster_lidar_mode">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;MODE_512x10: 10 scans of 512 columns per second&lt;/p&gt;&lt;p&gt;MODE_512x20: 20 scans of 512 columns per second&lt;/p&gt;&lt;p&gt;MODE_1024x10: 10 scans of 1024 columns per second&lt;/p&gt;&lt;p&gt;MODE_1024x20: 20 scans of 1024 columns per second&lt;/p&gt;&lt;p&gt;MODE_2048x10: 10 scans of 2048 columns per second&lt;/p&gt;&lt;p&gt;MODE_4096x5: 5 scans of 4096 columns per second. Only available on select sensors&lt;br/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<item>
<property name="text">
<string>Default</string>
</property>
</item>
<item>
<property name="text">
<string>512x10</string>
</property>
</item>
<item>
<property name="text">
<string>512x20</string>
</property>
</item>
<item>
<property name="text">
<string>1024x10</string>
</property>
</item>
<item>
<property name="text">
<string>1024x20</string>
</property>
</item>
<item>
<property name="text">
<string>2048x10</string>
</property>
</item>
<item>
<property name="text">
<string>4096x5</string>
</property>
</item>
</widget>
</item>
<item row="5" column="1">
<widget class="QLineEdit" name="lineEdit_ouster_json_path">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="6" column="2">
<widget class="QLabel" name="label_761">
<property name="text">
<string>Use Reflectivity instead of Signal for intensity channel.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="lineEdit_ouster_pcap_path">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QToolButton" name="toolButton_ouster_json_path">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QCheckBox" name="checkBox_ouster_imu">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="7" column="2">
<widget class="QLabel" name="label_762">
<property name="text">
<string>Publish IMU.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget> </widget>
</item> </item>
</layout> </layout>

View File

@@ -22,9 +22,7 @@ ENDIF(OPENCV_NONFREE_FOUND)
IF(TARGET rtabmap_gui) IF(TARGET rtabmap_gui)
ADD_SUBDIRECTORY( CameraRGBD ) ADD_SUBDIRECTORY( CameraRGBD )
IF(PCL_VERSION VERSION_GREATER_EQUAL "1.8") ADD_SUBDIRECTORY( LidarViewer )
ADD_SUBDIRECTORY( LidarViewer )
ENDIF()
ADD_SUBDIRECTORY( DatabaseViewer ) ADD_SUBDIRECTORY( DatabaseViewer )
ADD_SUBDIRECTORY( EpipolarGeometry ) ADD_SUBDIRECTORY( EpipolarGeometry )
ADD_SUBDIRECTORY( OdometryViewer ) ADD_SUBDIRECTORY( OdometryViewer )

View File

@@ -37,7 +37,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "rtabmap/utilite/UFile.h" #include "rtabmap/utilite/UFile.h"
#include "rtabmap/utilite/UDirectory.h" #include "rtabmap/utilite/UDirectory.h"
#include "rtabmap/utilite/UConversion.h" #include "rtabmap/utilite/UConversion.h"
#include <pcl/visualization/cloud_viewer.h>
#include <stdio.h> #include <stdio.h>
#include <signal.h> #include <signal.h>
@@ -49,6 +48,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <pcl/visualization/image_viewer.h> #include <pcl/visualization/image_viewer.h>
#include <pcl/console/parse.h> #include <pcl/console/parse.h>
#include <rtabmap/core/lidar/LidarVLP16.h> #include <rtabmap/core/lidar/LidarVLP16.h>
#include <rtabmap/core/lidar/LidarOuster.h>
using namespace pcl; using namespace pcl;
using namespace pcl::console; using namespace pcl::console;
@@ -57,8 +57,14 @@ using namespace pcl::visualization;
void showUsage() void showUsage()
{ {
printf("\nUsage:\n" printf("\nUsage:\n"
"rtabmap-lidar_viewer IP PORT driver\n" "rtabmap-lidar_viewer DRIVER IP [PORT]\n"
" driver Driver number to use: 0=VLP16 (default IP and port are 192.168.1.201 2368)\n"); "\n"
"DRIVER: 0=VLP16, 1=Ouster\n"
"PORT: should be set if DRIVER=0\n"
"\n"
"Examples:\n"
" rtabmap-lidar_viewer 0 192.168.1.201 2368\n"
" rtabmap-lidar_viewer 1 192.168.1.2\n\n");
exit(1); exit(1);
} }
@@ -80,28 +86,45 @@ int main(int argc, char * argv[])
int driver = 0; int driver = 0;
std::string ip; std::string ip;
int port = 2368; int port = 2368;
if(argc < 4) if(argc < 3)
{ {
printf("Not enough arguments.\n");
showUsage(); showUsage();
} }
else else
{ {
ip = argv[1]; driver = uStr2Int(argv[1]);
port = uStr2Int(argv[2]); if(driver < 0 || driver > 1)
driver = atoi(argv[3]);
if(driver < 0 || driver > 0)
{ {
UERROR("driver should be 0."); printf("driver should be 0 or 1.\n");
showUsage(); showUsage();
} }
}
printf("Using driver %d (ip=%s port=%d)\n", driver, ip.c_str(), port);
rtabmap::LidarVLP16 * lidar = 0; ip = argv[2];
if(driver==0)
{
if(argc>=3)
{
port = uStr2Int(argv[3]);
}
else
{
printf("IP and PORT should be set with VLP16 driver\n");
showUsage();
}
}
}
printf("Using driver %d (ip=%s%s)\n", driver, ip.c_str(), driver==0?uFormat(" port=%d", port).c_str():"");
rtabmap::Lidar * lidar = 0;
if(driver == 0) if(driver == 0)
{ {
lidar = new rtabmap::LidarVLP16(boost::asio::ip::address_v4::from_string(ip), port); lidar = new rtabmap::LidarVLP16(boost::asio::ip::address_v4::from_string(ip), port);
} }
else if(driver == 1)
{
lidar = new rtabmap::LidarOuster(ip);
}
else else
{ {
UFATAL(""); UFATAL("");
@@ -109,7 +132,7 @@ int main(int argc, char * argv[])
if(!lidar->init()) if(!lidar->init())
{ {
printf("Lidar init failed! Please select another driver (see \"--help\").\n"); printf("Lidar init failed!\n");
delete lidar; delete lidar;
exit(1); exit(1);
} }
@@ -121,18 +144,18 @@ int main(int argc, char * argv[])
signal(SIGTERM, &sighandler); signal(SIGTERM, &sighandler);
signal(SIGINT, &sighandler); signal(SIGINT, &sighandler);
rtabmap::SensorData data = lidar->takeScan(); rtabmap::SensorData data = lidar->takeData();
while(!data.laserScanRaw().empty() && (viewer==0 || !viewer->wasStopped()) && running) while(!data.laserScanRaw().empty() && (viewer==0 || !viewer->wasStopped()) && running)
{ {
pcl::PointCloud<pcl::PointXYZI>::Ptr cloud = rtabmap::util3d::laserScanToPointCloudI(data.laserScanRaw(), data.laserScanRaw().localTransform()); pcl::PointCloud<pcl::PointXYZI>::Ptr cloud = rtabmap::util3d::laserScanToPointCloudI(data.laserScanRaw(), data.laserScanRaw().localTransform());
viewer->showCloud(cloud, "cloud");
printf("Scan size: %ld points\n", cloud->size()); printf("Scan size: %ld points\n", cloud->size());
viewer->showCloud(cloud, "cloud");
int c = cv::waitKey(10); // wait 10 ms or for key stroke int c = cv::waitKey(10); // wait 10 ms or for key stroke
if(c == 27) if(c == 27)
break; // if ESC, break and quit break; // if ESC, break and quit
data = lidar->takeScan(); data = lidar->takeData();
} }
printf("Closing...\n"); printf("Closing...\n");
if(viewer) if(viewer)

View File

@@ -60,10 +60,10 @@ public:
*/ */
USemaphore( int initValue = 0 ) USemaphore( int initValue = 0 )
{ {
#ifdef _WIN32
S = CreateSemaphore(0,initValue,SEM_VALUE_MAX,0);
#else
_available = initValue; _available = initValue;
#ifdef _WIN32
S = CreateSemaphoreW(0,initValue,SEM_VALUE_MAX,0);
#else
pthread_mutex_init(&_waitMutex, NULL); pthread_mutex_init(&_waitMutex, NULL);
pthread_cond_init(&_cond, NULL); pthread_cond_init(&_cond, NULL);
#endif #endif
@@ -88,12 +88,15 @@ public:
* @return true on success, false on error/timeout * @return true on success, false on error/timeout
*/ */
#ifdef _WIN32 #ifdef _WIN32
bool acquire(int n = 1, int ms = 0) const bool acquire(int n = 1, int ms = 0)
{ {
int rt = 0; int rt = 0;
while(n-- > 0 && rt==0) while(n-- > 0 && rt==0)
{ {
rt = WaitForSingleObject((HANDLE)S, ms<=0?INFINITE:ms); rt = WaitForSingleObject((HANDLE)S, ms<=0?INFINITE:ms);
if (rt == 0) {
_available -= 1;
}
} }
return rt == 0; return rt == 0;
} }
@@ -136,12 +139,17 @@ public:
* @return false if the semaphore can't be taken without waiting (value <= 0), true otherwise * @return false if the semaphore can't be taken without waiting (value <= 0), true otherwise
*/ */
#ifdef _WIN32 #ifdef _WIN32
int acquireTry() const bool acquireTry()
{ {
return ((WaitForSingleObject((HANDLE)S,INFINITE)==WAIT_OBJECT_0)?0:EAGAIN); if(WaitForSingleObject((HANDLE)S, INFINITE) == WAIT_OBJECT_0)
{
_available -= 1;
return true;
}
return false;
} }
#else #else
int acquireTry(int n) bool acquireTry(int n)
{ {
pthread_mutex_lock(&_waitMutex); pthread_mutex_lock(&_waitMutex);
if(n > _available) if(n > _available)
@@ -160,9 +168,11 @@ public:
* signaling waiting threads (which called acquire()). * signaling waiting threads (which called acquire()).
*/ */
#ifdef _WIN32 #ifdef _WIN32
int release(int n = 1) const void release(int n = 1)
{ {
return (ReleaseSemaphore((HANDLE)S,n,0)?0:ERANGE); if (ReleaseSemaphore((HANDLE)S, n, 0)) {
_available += n;
}
} }
#else #else
void release(int n = 1) void release(int n = 1)
@@ -181,7 +191,7 @@ public:
#ifdef _WIN32 #ifdef _WIN32
int value() const int value() const
{ {
LONG V = -1; ReleaseSemaphore((HANDLE)S,0,&V); return V; return _available;
} }
#else #else
int value() int value()
@@ -204,20 +214,21 @@ public:
{ {
CloseHandle(S); CloseHandle(S);
S = CreateSemaphore(0,init,SEM_VALUE_MAX,0); S = CreateSemaphore(0,init,SEM_VALUE_MAX,0);
_available = init;
} }
#endif #endif
private: private:
void operator=(const USemaphore &){} void operator=(const USemaphore &){}
#ifdef _WIN32 #ifdef _WIN32
USemaphore(const USemaphore &S){} USemaphore(const USemaphore &S) :S(0), _available(0) {}
HANDLE S; HANDLE S;
#else #else
USemaphore(const USemaphore &):_available(0){} USemaphore(const USemaphore &):_available(0){}
pthread_mutex_t _waitMutex; pthread_mutex_t _waitMutex;
pthread_cond_t _cond; pthread_cond_t _cond;
int _available;
#endif #endif
int _available;
}; };
#endif // USEMAPHORE_H #endif // USEMAPHORE_H