Added source odometry guess option in standalone app

This commit is contained in:
matlabbe
2025-09-28 16:31:42 -07:00
parent a552bdfb2f
commit 0f37bafd66
7 changed files with 389 additions and 336 deletions
@@ -29,6 +29,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define ODOMETRYTHREAD_H_
#include <rtabmap/core/rtabmap_core_export.h>
#include <rtabmap/core/SensorEvent.h>
#include <rtabmap/core/SensorData.h>
#include <rtabmap/utilite/UThread.h>
#include <rtabmap/utilite/UEventsHandler.h>
@@ -55,18 +56,19 @@ private:
// MAIN LOOP
//============================================================
virtual void mainLoop();
void addData(const SensorData & data);
bool getData(SensorData & data);
void addData(const SensorEvent & data);
bool getData(SensorEvent & data);
private:
USemaphore _dataAdded;
UMutex _dataMutex;
std::list<SensorData> _dataBuffer;
std::list<SensorEvent> _dataBuffer;
std::list<SensorData> _imuBuffer;
Odometry * _odometry;
unsigned int _dataBufferMaxSize;
bool _resetOdometry;
Transform _resetPose;
Transform _previousGuessPose;
double _oldestAsyncImuStamp;
double _newestAsyncImuStamp;
};
+13 -2
View File
@@ -625,6 +625,7 @@ Transform Odometry::process(SensorData & data, const Transform & guessIn, Odomet
if(!guessIn.isNull())
{
guess = guessIn;
UDEBUG("Using provided guess %s", guessIn.prettyPrint().c_str());
}
else if(!imus_.empty())
{
@@ -641,12 +642,16 @@ Transform Odometry::process(SensorData & data, const Transform & guessIn, Odomet
{
guess = guess.to3DoF();
}
UDEBUG("Adjusting guess from motion with IMU %s", guess.prettyPrint().c_str());
}
else if(!imuLastTransform_.isNull())
{
UWARN("Could not find imu transform at %f", data.stamp());
}
}
else if(!guess.isNull()) {
UDEBUG("Using guess from motion %s", guess.prettyPrint().c_str());
}
UTimer time;
@@ -1011,8 +1016,14 @@ Transform Odometry::process(SensorData & data, const Transform & guessIn, Odomet
--_resetCurrentCount;
if(_resetCurrentCount == 0)
{
UWARN("Odometry automatically reset to latest pose!");
this->reset(_pose);
if(!guess.isNull() && !guessFromMotion_) {
UWARN("Odometry automatically reset to latest pose (%s) + guess (%s)!", _pose.prettyPrint().c_str(), guess.prettyPrint().c_str());
this->reset(_pose * guess);
}
else {
UWARN("Odometry automatically reset to latest pose (%s)!", _pose.prettyPrint().c_str());
this->reset(_pose);
}
_resetCurrentCount = _resetCountdown;
if(info)
{
+36 -25
View File
@@ -63,7 +63,7 @@ bool OdometryThread::handleEvent(UEvent * event)
SensorEvent * sensorEvent = (SensorEvent*)event;
if(sensorEvent->getCode() == SensorEvent::kCodeData)
{
this->addData(sensorEvent->data());
this->addData(*sensorEvent);
}
}
else if(event->getClassName().compare("IMUEvent") == 0)
@@ -112,31 +112,42 @@ void OdometryThread::mainLoop()
_imuBuffer.clear();
_oldestAsyncImuStamp = 0.0;
_newestAsyncImuStamp = 0.0;
_previousGuessPose.setNull();
}
SensorData data;
if(getData(data))
SensorEvent event;
if(getData(event))
{
OdometryInfo info;
UDEBUG("Processing data...");
Transform pose = _odometry->process(data, &info);
Transform guess;
UDEBUG("event.info().odomPose=%s", event.info().odomPose.prettyPrint().c_str());
if(!_previousGuessPose.isNull() && !event.info().odomPose.isNull()) {
guess = _previousGuessPose.inverse() * event.info().odomPose;
}
SensorData data = event.data();
Transform pose = _odometry->process(data, guess , &info);
if(!data.imageRaw().empty() || !data.laserScanRaw().empty() || (pose.isNull() && data.imu().empty()))
{
UDEBUG("Odom pose = %s", pose.prettyPrint().c_str());
// a null pose notify that odometry could not be computed
this->post(new OdometryEvent(data, pose, info));
if(!pose.isNull()) {
_previousGuessPose = event.info().odomPose;
}
}
}
}
void OdometryThread::addData(const SensorData & data)
void OdometryThread::addData(const SensorEvent & event)
{
if(data.imu().empty())
if(event.data().imu().empty())
{
if(dynamic_cast<OdometryMono*>(_odometry) == 0)
{
if((data.imageRaw().empty() || data.depthOrRightRaw().empty() || (data.cameraModels().empty() && data.stereoCameraModels().empty())) &&
data.laserScanRaw().empty())
if((event.data().imageRaw().empty() || event.data().depthOrRightRaw().empty() || (event.data().cameraModels().empty() && event.data().stereoCameraModels().empty())) &&
event.data().laserScanRaw().empty())
{
ULOGGER_ERROR("Missing some information (images/scans empty or missing calibration)!?");
return;
@@ -145,7 +156,7 @@ void OdometryThread::addData(const SensorData & data)
else
{
// Mono can accept RGB only
if(data.imageRaw().empty() || (data.cameraModels().empty() && data.stereoCameraModels().empty()))
if(event.data().imageRaw().empty() || (event.data().cameraModels().empty() && event.data().stereoCameraModels().empty()))
{
ULOGGER_ERROR("Missing some information (image empty or missing calibration)!?");
return;
@@ -156,30 +167,30 @@ void OdometryThread::addData(const SensorData & data)
bool notify = true;
_dataMutex.lock();
{
if( !data.imageRaw().empty() ||
!data.imageCompressed().empty() ||
!data.laserScanRaw().isEmpty() ||
!data.laserScanCompressed().empty() ||
data.imu().empty())
if( !event.data().imageRaw().empty() ||
!event.data().imageCompressed().empty() ||
!event.data().laserScanRaw().isEmpty() ||
!event.data().laserScanCompressed().empty() ||
event.data().imu().empty())
{
if(_oldestAsyncImuStamp > 0.0 && data.stamp() < _oldestAsyncImuStamp) {
if(_oldestAsyncImuStamp > 0.0 && event.data().stamp() < _oldestAsyncImuStamp) {
UWARN("Received image/lidar with stamp (%f) older than oldest received imu "
"(%f), skipping that frame (imu buffer size=%ld). "
"When using async IMU, make sure IMU is published faster "
"than camera/lidar (assuming IMU latency is very small compared to camera/lidar).",
data.stamp(), _oldestAsyncImuStamp, _imuBuffer.size());
event.data().stamp(), _oldestAsyncImuStamp, _imuBuffer.size());
notify = false;
}
else if(_newestAsyncImuStamp > 0.0 && data.stamp()>=_newestAsyncImuStamp) {
else if(_newestAsyncImuStamp > 0.0 && event.data().stamp()>=_newestAsyncImuStamp) {
UWARN("Received image/lidar with stamp (%f) newer than latest received imu "
"(%f), skipping that frame (imu buffer size=%ld). "
"When using async IMU, make sure IMU is published faster "
"than camera/lidar (assuming IMU latency is very small compared to camera/lidar).",
data.stamp(), _newestAsyncImuStamp, _imuBuffer.size());
event.data().stamp(), _newestAsyncImuStamp, _imuBuffer.size());
notify = false;
}
else {
_dataBuffer.push_back(data);
_dataBuffer.push_back(event);
while(_dataBufferMaxSize > 0 && _dataBuffer.size() > _dataBufferMaxSize)
{
UDEBUG("Data buffer is full, the oldest data is removed to add the new one.");
@@ -190,11 +201,11 @@ void OdometryThread::addData(const SensorData & data)
}
else
{
_imuBuffer.push_back(data);
_imuBuffer.push_back(event.data());
if(_oldestAsyncImuStamp == 0) {
_oldestAsyncImuStamp = data.stamp();
_oldestAsyncImuStamp = event.data().stamp();
}
_newestAsyncImuStamp = data.stamp();
_newestAsyncImuStamp = event.data().stamp();
}
}
_dataMutex.unlock();
@@ -205,7 +216,7 @@ void OdometryThread::addData(const SensorData & data)
}
}
bool OdometryThread::getData(SensorData & data)
bool OdometryThread::getData(SensorEvent & event)
{
bool dataFilled = false;
_dataAdded.acquire();
@@ -219,12 +230,12 @@ bool OdometryThread::getData(SensorData & data)
_odometry->process(_imuBuffer.front());
double stamp =_imuBuffer.front().stamp();
_imuBuffer.pop_front();
if(stamp > _dataBuffer.front().stamp()) {
if(stamp > _dataBuffer.front().data().stamp()) {
break;
}
}
data = _dataBuffer.front();
event = _dataBuffer.front();
_dataBuffer.pop_front();
dataFilled = true;
}
@@ -173,6 +173,7 @@ public:
int getOdomRegistrationApproach() const;
double getOdomF2MGravitySigma() const;
bool isOdomDisabled() const;
bool isOdomAsGuessEnabled() const;
bool isOdomSensorAsGt() const;
bool isGroundTruthAligned() const;
+2 -2
View File
@@ -5995,7 +5995,7 @@ void MainWindow::startDetection()
_imuThread = 0;
}
if(!_sensorCapture->odomProvided() && !_preferencesDialog->isOdomDisabled())
if((!_sensorCapture->odomProvided() || _preferencesDialog->isOdomAsGuessEnabled()) && !_preferencesDialog->isOdomDisabled())
{
ParametersMap odomParameters = parameters;
if(_preferencesDialog->getOdomRegistrationApproach() < 3)
@@ -6053,7 +6053,7 @@ void MainWindow::startDetection()
}
}
if(_dataRecorder && _sensorCapture && _odomThread)
if(_dataRecorder && _sensorCapture)
{
UEventsManager::createPipe(_sensorCapture, _dataRecorder, "SensorEvent");
}
+9 -1
View File
@@ -918,6 +918,7 @@ PreferencesDialog::PreferencesDialog(QWidget * parent) :
connect(_ui->doubleSpinBox_odom_sensor_scale_factor, SIGNAL(valueChanged(double)), this, SLOT(makeObsoleteSourcePanel()));
connect(_ui->doubleSpinBox_odom_sensor_wait_time, SIGNAL(valueChanged(double)), this, SLOT(makeObsoleteSourcePanel()));
connect(_ui->checkBox_odom_sensor_use_as_gt, SIGNAL(stateChanged(int)), this, SLOT(makeObsoleteSourcePanel()));
connect(_ui->checkbox_passthrough_source_odom, SIGNAL(stateChanged(int)), this, SLOT(makeObsoleteSourcePanel()));
connect(_ui->comboBox_imuFilter_strategy, SIGNAL(currentIndexChanged(int)), this, SLOT(makeObsoleteSourcePanel()));
connect(_ui->comboBox_imuFilter_strategy, SIGNAL(currentIndexChanged(int)), _ui->stackedWidget_imuFilter, SLOT(setCurrentIndex(int)));
@@ -2309,6 +2310,7 @@ void PreferencesDialog::resetSettings(QGroupBox * groupBox)
_ui->doubleSpinBox_odom_sensor_scale_factor->setValue(1);
_ui->doubleSpinBox_odom_sensor_wait_time->setValue(100);
_ui->checkBox_odom_sensor_use_as_gt->setChecked(false);
_ui->checkbox_passthrough_source_odom->setChecked(false);
_ui->comboBox_imuFilter_strategy->setCurrentIndex(2);
_ui->doubleSpinBox_imuFilterMadgwickGain->setValue(Parameters::defaultImuFilterMadgwickGain());
@@ -2835,6 +2837,7 @@ void PreferencesDialog::readCameraSettings(const QString & filePath)
_ui->doubleSpinBox_odom_sensor_scale_factor->setValue(settings.value("odom_sensor_scale_factor", _ui->doubleSpinBox_odom_sensor_scale_factor->value()).toDouble());
_ui->doubleSpinBox_odom_sensor_wait_time->setValue(settings.value("odom_sensor_wait_time", _ui->doubleSpinBox_odom_sensor_wait_time->value()).toDouble());
_ui->checkBox_odom_sensor_use_as_gt->setChecked(settings.value("odom_sensor_odom_as_gt", _ui->checkBox_odom_sensor_use_as_gt->isChecked()).toBool());
_ui->checkbox_passthrough_source_odom->setChecked(settings.value("odom_sensor_as_guess", _ui->checkbox_passthrough_source_odom->isChecked()).toBool());
settings.endGroup(); // OdomSensor
settings.beginGroup("UsbCam");
@@ -3439,6 +3442,7 @@ void PreferencesDialog::writeCameraSettings(const QString & filePath) const
settings.setValue("odom_sensor_scale_factor", _ui->doubleSpinBox_odom_sensor_scale_factor->value());
settings.setValue("odom_sensor_wait_time", _ui->doubleSpinBox_odom_sensor_wait_time->value());
settings.setValue("odom_sensor_odom_as_gt", _ui->checkBox_odom_sensor_use_as_gt->isChecked());
settings.setValue("odom_sensor_as_guess", _ui->checkbox_passthrough_source_odom->isChecked());
settings.endGroup(); // OdomSensor
settings.beginGroup("UsbCam");
@@ -5777,7 +5781,7 @@ void PreferencesDialog::updateSourceGrpVisibility()
// Odom Sensor Group
_ui->frame_visual_odometry_sensor->setVisible(getOdomSourceDriver() != kSrcUndef); // Not Lidar None
_ui->groupBox_odom_sensor->setVisible(_ui->comboBox_sourceType->currentIndex() != 3); // Don't show when database is selected
_ui->comboBox_odom_sensor->setEnabled(_ui->comboBox_sourceType->currentIndex() != 3); // Don't enable when database is selected
// Lidar Sensor Group
_ui->comboBox_lidar_src->setEnabled(_ui->comboBox_sourceType->currentIndex() != 3); // Disable if database input
@@ -5911,6 +5915,10 @@ bool PreferencesDialog::isOdomDisabled() const
{
return _ui->checkbox_odomDisabled->isChecked();
}
bool PreferencesDialog::isOdomAsGuessEnabled() const
{
return _ui->checkbox_passthrough_source_odom->isChecked();
}
bool PreferencesDialog::isOdomSensorAsGt() const
{
return _ui->checkBox_odom_sensor_use_as_gt->isChecked();
+323 -303
View File
@@ -63,9 +63,9 @@
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<y>-3050</y>
<width>713</width>
<height>4705</height>
<height>4779</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_16">
@@ -95,7 +95,7 @@
<enum>QFrame::Raised</enum>
</property>
<property name="currentIndex">
<number>8</number>
<number>5</number>
</property>
<widget class="QWidget" name="page_22">
<layout class="QVBoxLayout" name="verticalLayout_29" stretch="0,0">
@@ -3165,22 +3165,16 @@ when using the file type, logs are saved in LogRtabmap.txt (located in the worki
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QDoubleSpinBox" name="general_doubleSpinBox_imgRate">
<property name="suffix">
<string> Hz</string>
<item row="10" column="1">
<widget class="QLabel" name="label_244">
<property name="text">
<string>Create a calibration file with known intrinsics (fx, fy, cx, cy). Useful if you already know the intrinsics of the source of images.</string>
</property>
<property name="decimals">
<number>1</number>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="maximum">
<double>100.000000000000000</double>
</property>
<property name="singleStep">
<double>0.100000000000000</double>
</property>
<property name="value">
<double>0.000000000000000</double>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
@@ -3194,13 +3188,6 @@ when using the file type, logs are saved in LogRtabmap.txt (located in the worki
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLineEdit" name="lineEdit_sourceDevice">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label_39">
<property name="text">
@@ -3214,40 +3201,34 @@ when using the file type, logs are saved in LogRtabmap.txt (located in the worki
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLineEdit" name="lineEdit_sourceLocalTransform">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Format (3 values): x y z&lt;br/&gt;Format (6 values): x y z roll pitch yaw&lt;br/&gt;Format (7 values): x y z qx qy qz qw&lt;br/&gt;Format (9 values, 3x3 rotation): r11 r12 r13 r21 r22 r23 r31 r32 r33&lt;br/&gt;Format (12 values, 3x4 transform): r11 r12 r13 tx r21 r22 r23 ty r31 r32 r33 tz&lt;/p&gt;&lt;p&gt;KITTI: /base_link to /gray_camera = 0 0 0 0 0 0&lt;br/&gt;KITTI: /base_link to /color_camera = 0 -0.06 0 0 0 0&lt;br/&gt;KITTI: /base_footprint to /gray_camera = 0 0 1.67 0 0 0&lt;br/&gt;KITTI: /base_footprint to /color_camera = 0 -0.06 1.67 0 0 0&lt;/p&gt;&lt;p&gt;EuRoC MAV: /base_link to /cam0 = T_BS*T_SC0 = 0.999661 0.0257743 -0.00375625 0.00981073 -0.0257154 0.999557 0.0149672 0.064677 0.00414035 -0.0148655 0.999881 -0.0216401&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>0 0 0 0 0 0</string>
</property>
</widget>
<item row="8" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_5" stretch="0,1">
<item>
<widget class="QToolButton" name="toolButton_source_path_calibration">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lineEdit_calibrationFile">
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="1">
<widget class="QLabel" name="label_42">
<item row="5" column="1">
<widget class="QLabel" name="label_681">
<property name="text">
<string>Local transform from /base_link to /camera_link (without optical transform). Mouse over the box to show formats. If odometry sensor is enabled, it is the transform to odometry sensor.</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="0">
<widget class="QCheckBox" name="source_mirroring">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="label_17">
<property name="text">
<string>Mirroring mode (flip image horizontally). It has no effect on database source.</string>
<string>Equalizes the histogram of grayscale images.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
@@ -3264,10 +3245,33 @@ when using the file type, logs are saved in LogRtabmap.txt (located in the worki
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="label_36">
<item row="6" column="0">
<widget class="QCheckBox" name="checkbox_rgbd_colorOnly">
<property name="text">
<string>Image decimation. RGB/Mono and depth images will be resized according to this value (size*1/decimation). Note that if depth images are captured, decimation should be a multiple of the depth image size. If depth images are smaller than RGB images, the decimation is first applied on RGB, if the resulting RGB image is still bigger than depth image, the depth is not decimated.</string>
<string/>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="QLabel" name="label_18">
<property name="text">
<string>Calibration file path (*.yaml). If empty, the GUID of the camera is used (for those having one). OpenNI and Freenect drivers use factory calibration by default (so they ignore this parameter). A calibrated camera is required for RGB-D SLAM mode.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QLabel" name="label_740">
<property name="text">
<string>Detect features inside camera thread using parameters set in Visual Registration panel.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
@@ -3302,102 +3306,6 @@ when using the file type, logs are saved in LogRtabmap.txt (located in the worki
</item>
</widget>
</item>
<item row="5" column="1">
<widget class="QLabel" name="label_681">
<property name="text">
<string>Equalizes the histogram of grayscale images.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QCheckBox" name="checkbox_rgbd_colorOnly">
<property name="text">
<string/>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QLabel" name="label_229">
<property name="text">
<string>Only RGB images (for RGB-D cameras) or left images (for stereo cameras) are published.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QCheckBox" name="checkbox_source_feature_detection">
<property name="text">
<string/>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QLabel" name="label_740">
<property name="text">
<string>Detect features inside camera thread using parameters set in Visual Registration panel.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="8" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_5" stretch="0,1">
<item>
<widget class="QToolButton" name="toolButton_source_path_calibration">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lineEdit_calibrationFile">
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item row="8" column="1">
<widget class="QLabel" name="label_18">
<property name="text">
<string>Calibration file path (*.yaml). If empty, the GUID of the camera is used (for those having one). OpenNI and Freenect drivers use factory calibration by default (so they ignore this parameter). A calibrated camera is required for RGB-D SLAM mode.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="9" column="0">
<widget class="QPushButton" name="pushButton_calibrate">
<property name="sizePolicy">
@@ -3424,23 +3332,17 @@ when using the file type, logs are saved in LogRtabmap.txt (located in the worki
</property>
</widget>
</item>
<item row="10" column="0">
<widget class="QPushButton" name="pushButton_calibrate_simple">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item row="3" column="0">
<widget class="QCheckBox" name="source_mirroring">
<property name="text">
<string>Create Calibration</string>
<string/>
</property>
</widget>
</item>
<item row="10" column="1">
<widget class="QLabel" name="label_244">
<item row="3" column="1">
<widget class="QLabel" name="label_17">
<property name="text">
<string>Create a calibration file with known intrinsics (fx, fy, cx, cy). Useful if you already know the intrinsics of the source of images.</string>
<string>Mirroring mode (flip image horizontally). It has no effect on database source.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
@@ -3463,6 +3365,104 @@ when using the file type, logs are saved in LogRtabmap.txt (located in the worki
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QCheckBox" name="checkbox_source_feature_detection">
<property name="text">
<string/>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QLabel" name="label_229">
<property name="text">
<string>Only RGB images (for RGB-D cameras) or left images (for stereo cameras) are published.</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="0">
<widget class="QDoubleSpinBox" name="general_doubleSpinBox_imgRate">
<property name="suffix">
<string> Hz</string>
</property>
<property name="decimals">
<number>1</number>
</property>
<property name="maximum">
<double>100.000000000000000</double>
</property>
<property name="singleStep">
<double>0.100000000000000</double>
</property>
<property name="value">
<double>0.000000000000000</double>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLineEdit" name="lineEdit_sourceDevice">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLineEdit" name="lineEdit_sourceLocalTransform">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Format (3 values): x y z&lt;br/&gt;Format (6 values): x y z roll pitch yaw&lt;br/&gt;Format (7 values): x y z qx qy qz qw&lt;br/&gt;Format (9 values, 3x3 rotation): r11 r12 r13 r21 r22 r23 r31 r32 r33&lt;br/&gt;Format (12 values, 3x4 transform): r11 r12 r13 tx r21 r22 r23 ty r31 r32 r33 tz&lt;/p&gt;&lt;p&gt;KITTI: /base_link to /gray_camera = 0 0 0 0 0 0&lt;br/&gt;KITTI: /base_link to /color_camera = 0 -0.06 0 0 0 0&lt;br/&gt;KITTI: /base_footprint to /gray_camera = 0 0 1.67 0 0 0&lt;br/&gt;KITTI: /base_footprint to /color_camera = 0 -0.06 1.67 0 0 0&lt;/p&gt;&lt;p&gt;EuRoC MAV: /base_link to /cam0 = T_BS*T_SC0 = 0.999661 0.0257743 -0.00375625 0.00981073 -0.0257154 0.999557 0.0149672 0.064677 0.00414035 -0.0148655 0.999881 -0.0216401&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>0 0 0 0 0 0</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="label_36">
<property name="text">
<string>Image decimation. RGB/Mono and depth images will be resized according to this value (size*1/decimation). Note that if depth images are captured, decimation should be a multiple of the depth image size. If depth images are smaller than RGB images, the decimation is first applied on RGB, if the resulting RGB image is still bigger than depth image, the depth is not decimated.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="10" column="0">
<widget class="QPushButton" name="pushButton_calibrate_simple">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Create Calibration</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="label_42">
<property name="text">
<string>Local transform from /base_link to /camera_link (without optical transform). Mouse over the box to show formats. If odometry sensor is enabled, it is the transform to odometry sensor.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
@@ -8292,7 +8292,7 @@ when using the file type, logs are saved in LogRtabmap.txt (located in the worki
<item>
<widget class="QGroupBox" name="groupBox_odom_sensor">
<property name="title">
<string>Visual Odometry Sensor</string>
<string>Odometry Sensor</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_178">
<item>
@@ -8337,6 +8337,26 @@ when using the file type, logs are saved in LogRtabmap.txt (located in the worki
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label_770">
<property name="text">
<string>Pass input odometry as guess to internal odometry. Otherwise, internal odometry is skipped and input odometry is sent directly to SLAM's back-end. This applies to all sources that can provide odometry (e.g., odometry file, database, ...).</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="checkbox_passthrough_source_odom">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item>
@@ -8354,10 +8374,111 @@ when using the file type, logs are saved in LogRtabmap.txt (located in the worki
<property name="bottomMargin">
<number>0</number>
</property>
<item row="5" column="1">
<widget class="QLabel" name="label_636">
<item row="0" column="1">
<widget class="QLabel" name="label_565">
<property name="text">
<string>Calibration file path (*.yaml) for the visual odometry sensor. If empty, the GUID of the camera is used (for those having one). Only required to calibrate extrinsics.</string>
<string>ID of the device, which might be a serial number, bus@address or the index of the device. If empty, the first device found is taken.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QLabel" name="label_638">
<property name="text">
<string>Use as ground truth. The odometry poses will be saved in ground truth field of sensor data instead of being used as odometry. The actual odometry will be computed by rtabmap using the camera sensor. This can be useful to compare rtabmap odometry versus odometry sensor, and to estimate a scale factor between the sensors (using &quot;rtabmap-report -- scale&quot;) that can be used above.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label_637">
<property name="text">
<string>Time offset between camera and visual odometry sensors.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QDoubleSpinBox" name="doubleSpinBox_odom_sensor_time_offset">
<property name="suffix">
<string> ms</string>
</property>
<property name="decimals">
<number>1</number>
</property>
<property name="minimum">
<double>-999.000000000000000</double>
</property>
<property name="maximum">
<double>999.000000000000000</double>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QPushButton" name="pushButton_odom_sensor_calibrate">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Calibrate Extrinsics</string>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QCheckBox" name="checkBox_odom_sensor_use_as_gt">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="label_634">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Extrinsics between pose frame and camera's left lens (without optical rotation). Default extrinsics match the 3D printed bracket &lt;a href=&quot; https://www.intelrealsense.com/depth-and-tracking-combined-get-started/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;here&lt;/span&gt;&lt;/a&gt; for T265+D400 setup. (&lt;a href=&quot;https://github.com/IntelRealSense/realsense-ros/blob/occupancy-mapping/realsense2_camera/meshes/mount_t265_d435.stl&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;stl&lt;/span&gt;&lt;/a&gt;). Not used if camera and visual odometry sensors are the same sensor.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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="QLabel" name="label_635">
<property name="text">
<string>Calibrate extrinsics between visual odometry sensor and camera. Both sensors should be already calibrated. See Calibrate button above to calibrate them individually.</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="QLabel" name="label_754">
<property name="text">
<string>Maximum wait time to get the pose for latest data captured.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
@@ -8391,16 +8512,32 @@ when using the file type, logs are saved in LogRtabmap.txt (located in the worki
</item>
</layout>
</item>
<item row="4" column="1">
<widget class="QLabel" name="label_635">
<item row="2" column="0">
<widget class="QDoubleSpinBox" name="doubleSpinBox_odom_sensor_wait_time">
<property name="suffix">
<string> ms</string>
</property>
<property name="decimals">
<number>0</number>
</property>
<property name="minimum">
<double>0.000000000000000</double>
</property>
<property name="maximum">
<double>1000.000000000000000</double>
</property>
<property name="value">
<double>100.000000000000000</double>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLineEdit" name="lineEdit_odom_sensor_extrinsics">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Format (3 values): x y z&lt;br/&gt;Format (6 values): x y z roll pitch yaw&lt;br/&gt;Format (7 values): x y z qx qy qz qw&lt;br/&gt;Format (9 values, 3x3 rotation): r11 r12 r13 r21 r22 r23 r31 r32 r33&lt;br/&gt;Format (12 values, 3x4 transform): r11 r12 r13 tx r21 r22 r23 ty r31 r32 r33 tz&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Calibrate extrinsics between visual odometry sensor and camera. Both sensors should be already calibrated. See Calibrate button above to calibrate them individually.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
<string>0.009 0.021 0.027 0.000 -0.018 0.005</string>
</property>
</widget>
</item>
@@ -8411,26 +8548,10 @@ when using the file type, logs are saved in LogRtabmap.txt (located in the worki
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QDoubleSpinBox" name="doubleSpinBox_odom_sensor_time_offset">
<property name="suffix">
<string> ms</string>
</property>
<property name="decimals">
<number>1</number>
</property>
<property name="minimum">
<double>-999.000000000000000</double>
</property>
<property name="maximum">
<double>999.000000000000000</double>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_565">
<item row="6" column="1">
<widget class="QLabel" name="label_639">
<property name="text">
<string>ID of the device, which might be a serial number, bus@address or the index of the device. If empty, the first device found is taken.</string>
<string>Scale factor between camera and visual odometry sensor. This factor is multiplied to translation components of each pose in the odometry trajectory.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
@@ -8440,10 +8561,10 @@ when using the file type, logs are saved in LogRtabmap.txt (located in the worki
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label_637">
<item row="5" column="1">
<widget class="QLabel" name="label_636">
<property name="text">
<string>Time offset between camera and visual odometry sensors.</string>
<string>Calibration file path (*.yaml) for the visual odometry sensor. If empty, the GUID of the camera is used (for those having one). Only required to calibrate extrinsics.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
@@ -8475,107 +8596,6 @@ when using the file type, logs are saved in LogRtabmap.txt (located in the worki
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QLabel" name="label_639">
<property name="text">
<string>Scale factor between camera and visual odometry sensor. This factor is multiplied to translation components of each pose in the odometry trajectory.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QLabel" name="label_638">
<property name="text">
<string>Use as ground truth. The odometry poses will be saved in ground truth field of sensor data instead of being used as odometry. The actual odometry will be computed by rtabmap using the camera sensor. This can be useful to compare rtabmap odometry versus odometry sensor, and to estimate a scale factor between the sensors (using &quot;rtabmap-report -- scale&quot;) that can be used above.</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_634">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Extrinsics between pose frame and camera's left lens (without optical rotation). Default extrinsics match the 3D printed bracket &lt;a href=&quot; https://www.intelrealsense.com/depth-and-tracking-combined-get-started/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;here&lt;/span&gt;&lt;/a&gt; for T265+D400 setup. (&lt;a href=&quot;https://github.com/IntelRealSense/realsense-ros/blob/occupancy-mapping/realsense2_camera/meshes/mount_t265_d435.stl&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;stl&lt;/span&gt;&lt;/a&gt;). Not used if camera and visual odometry sensors are the same sensor.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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="0">
<widget class="QPushButton" name="pushButton_odom_sensor_calibrate">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Calibrate Extrinsics</string>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QCheckBox" name="checkBox_odom_sensor_use_as_gt">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLineEdit" name="lineEdit_odom_sensor_extrinsics">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Format (3 values): x y z&lt;br/&gt;Format (6 values): x y z roll pitch yaw&lt;br/&gt;Format (7 values): x y z qx qy qz qw&lt;br/&gt;Format (9 values, 3x3 rotation): r11 r12 r13 r21 r22 r23 r31 r32 r33&lt;br/&gt;Format (12 values, 3x4 transform): r11 r12 r13 tx r21 r22 r23 ty r31 r32 r33 tz&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>0.009 0.021 0.027 0.000 -0.018 0.005</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="label_754">
<property name="text">
<string>Maximum wait time to get the pose for latest data captured.</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="0">
<widget class="QDoubleSpinBox" name="doubleSpinBox_odom_sensor_wait_time">
<property name="suffix">
<string> ms</string>
</property>
<property name="decimals">
<number>0</number>
</property>
<property name="minimum">
<double>0.000000000000000</double>
</property>
<property name="maximum">
<double>1000.000000000000000</double>
</property>
<property name="value">
<double>100.000000000000000</double>
</property>
</widget>
</item>
</layout>
</widget>
</item>