Windows CI: make vcpkg download compatible with draft release (#1727)

* Windows CI: make vcpkg download compatible with draft release

* forcing nvdia driver dll to be ignored in fixup_bundle

* disabling opencv cudec and python

* disabling rtabmap-console --version on cuda build

* Added assert when bad imu data is provided. Added checks when zed sdk is returning nan imu data.

* Updated zed sdk5 resolution and quality

* Updated zed sdk5 resolution and quality

* Fixing parameters reloaded when testing camera. Also fixed app freezing when closing Preferences dialog after using test camera dialog

* cleanup and debug logs

* Added dialogs when starting/stopping sensors/detection. Fixed nullptr bug on MainWindow::handleEvents

* renamed

* found a way to expose ZED model downloads

* Updated default config path on Windows

* Fixed extra endline in file logging (windows)

* Fixed camera local transform when used with lidar and odomSensor

* CI(macos): build g2o from source with OpenMP (libomp include/link fix)

* Mac: added ceres dep, fixed omp not found by cmake

* Mac: bundle orbbec extensions

* stripping rpath of orbbec's extensions

* cleanup

* Realsense2 error as error (not warning)

* Fixed config ownership changed to root when sarting in sudo. LidarVLP16: fixed crash on Mac by reimplementing our own socket threading

* LidarVLP16: own socket implementation only for Mac (linux and windows use original PCL's reader)
This commit is contained in:
matlabbe
2026-07-04 18:16:37 -07:00
committed by GitHub
parent cb34c4bd37
commit d069becc72
27 changed files with 982 additions and 141 deletions
+243 -28
View File
@@ -39,9 +39,20 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <QtCore/QSettings>
#include <QtCore/QDir>
#include <QtCore/QFile>
#include <QtCore/QFileInfo>
#include <QtCore/QStandardPaths>
#include <QtCore/QTimer>
#include <QUrl>
#ifndef _WIN32
#include <unistd.h> // geteuid, chown
#include <sys/types.h>
#include <cstdlib> // atoi, getenv
#include <cerrno>
#include <cstring> // strerror
#endif
#include <QButtonGroup>
#include <QFileDialog>
#include <QInputDialog>
@@ -49,6 +60,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <QtGui/QStandardItemModel>
#include <QMainWindow>
#include <QProgressDialog>
#include <QApplication>
#include <QLabel>
#include <functional>
#include <QScrollBar>
#include <QStatusBar>
#include <QFormLayout>
@@ -437,12 +451,29 @@ PreferencesDialog::PreferencesDialog(QWidget * parent) :
_ui->comboBox_cameraStereo->setItemData(kSrcStereoZed - kSrcStereo, 0, Qt::UserRole - 1);
_ui->comboBox_odom_sensor->setItemData(2, 0, Qt::UserRole - 1);
}
else if(CameraStereoZed::sdkVersion() < 4)
else
{
_ui->comboBox_stereoZed_resolution->setItemData(1, 0, Qt::UserRole - 1);
_ui->comboBox_stereoZed_resolution->setItemData(4, 0, Qt::UserRole - 1);
_ui->comboBox_stereoZed_resolution->setItemData(6, 0, Qt::UserRole - 1);
_ui->comboBox_stereoZed_quality->setItemData(3, 0, Qt::UserRole - 1);
if(CameraStereoZed::sdkVersion() < 5)
{
// SDK 4.X
_ui->comboBox_stereoZed_quality->setItemData(5, 0, Qt::UserRole - 1); // NEURAL_ULTRA
_ui->comboBox_stereoZed_resolution->setItemData(4, 0, Qt::UserRole - 1); //HD1536
_ui->comboBox_stereoZed_resolution->setItemData(10, 0, Qt::UserRole - 1); //XVGA
_ui->comboBox_stereoZed_resolution->setItemData(11, 0, Qt::UserRole - 1); //TXGA
}
if(CameraStereoZed::sdkVersion() < 4)
{
// SDK 3.X
_ui->comboBox_stereoZed_quality->setItemData(3, 0, Qt::UserRole - 1); // NEURAL_LIGHT
_ui->comboBox_stereoZed_resolution->setItemData(1, 0, Qt::UserRole - 1); // HD4K
_ui->comboBox_stereoZed_resolution->setItemData(2, 0, Qt::UserRole - 1); // QHDPLUS
_ui->comboBox_stereoZed_resolution->setItemData(6, 0, Qt::UserRole - 1); // HD1200
_ui->comboBox_stereoZed_resolution->setItemData(8, 0, Qt::UserRole - 1); // SVGA
}
if(CameraStereoZed::sdkVersion() < 3)
{
_ui->comboBox_stereoZed_quality->setItemData(4, 0, Qt::UserRole - 1); // NEURAL
}
}
if (!CameraStereoTara::available())
{
@@ -985,7 +1016,7 @@ PreferencesDialog::PreferencesDialog(QWidget * parent) :
connect(_ui->lineEdit_vlp16_pcap_path, SIGNAL(textChanged(const QString &)), this, SLOT(makeObsoleteSourcePanel()));
connect(_ui->spinBox_vlp16_ip1, SIGNAL(valueChanged(int)), this, SLOT(makeObsoleteSourcePanel()));
connect(_ui->spinBox_vlp16_ip2, SIGNAL(valueChanged(int)), this, SLOT(makeObsoleteSourcePanel()));
connect(_ui->spinBox_vlp16_ip2, SIGNAL(valueChanged(int)), this, SLOT(makeObsoleteSourcePanel()));
connect(_ui->spinBox_vlp16_ip3, SIGNAL(valueChanged(int)), this, SLOT(makeObsoleteSourcePanel()));
connect(_ui->spinBox_vlp16_ip4, SIGNAL(valueChanged(int)), this, SLOT(makeObsoleteSourcePanel()));
connect(_ui->spinBox_vlp16_port, SIGNAL(valueChanged(int)), this, SLOT(makeObsoleteSourcePanel()));
connect(_ui->checkBox_vlp16_organized, SIGNAL(stateChanged(int)), this, SLOT(makeObsoleteSourcePanel()));
@@ -2440,10 +2471,10 @@ void PreferencesDialog::resetSettings(QGroupBox * groupBox)
_ui->lineEdit_lidar_local_transform->setText("0 0 0 0 0 0");
_ui->lineEdit_vlp16_pcap_path->clear();
_ui->spinBox_vlp16_ip1->setValue(192);
_ui->spinBox_vlp16_ip2->setValue(168);
_ui->spinBox_vlp16_ip3->setValue(1);
_ui->spinBox_vlp16_ip4->setValue(201);
_ui->spinBox_vlp16_ip1->setValue(0);
_ui->spinBox_vlp16_ip2->setValue(0);
_ui->spinBox_vlp16_ip3->setValue(0);
_ui->spinBox_vlp16_ip4->setValue(0);
_ui->spinBox_vlp16_port->setValue(2368);
_ui->checkBox_vlp16_organized->setChecked(false);
_ui->checkBox_vlp16_hostTime->setChecked(true);
@@ -2559,12 +2590,33 @@ QString PreferencesDialog::getWorkingDirectory() const
QString PreferencesDialog::getIniFilePath() const
{
#ifdef WIN32
// Windows: store settings in the standard per-user config location (%LOCALAPPDATA%\rtabmap)
// instead of a Unix-style dotfile in the home root. A fixed "rtabmap" folder (not the app name)
// is used so the main GUI and DatabaseViewer share the same rtabmap.ini. Other platforms keep
// ~/.rtabmap for consistency.
QString privatePath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + "/rtabmap";
#else
QString privatePath = QDir::homePath() + "/.rtabmap";
#endif
if(!QDir(privatePath).exists())
{
QDir::home().mkdir(".rtabmap");
QDir().mkpath(privatePath);
}
return privatePath + "/rtabmap.ini";
QString iniPath = privatePath + "/rtabmap.ini";
#ifdef WIN32
// One-time migration: if there's no ini at the new location but the legacy ~/.rtabmap/rtabmap.ini
// exists, copy it over so users keep their existing settings.
if(!QFile::exists(iniPath))
{
QString legacyIni = QDir::homePath() + "/.rtabmap/rtabmap.ini";
if(QFile::exists(legacyIni))
{
QFile::copy(legacyIni, iniPath);
}
}
#endif
return iniPath;
}
QString PreferencesDialog::getTmpIniFilePath() const
@@ -2572,6 +2624,41 @@ QString PreferencesDialog::getTmpIniFilePath() const
return getIniFilePath()+".tmp";
}
void PreferencesDialog::restoreConfigOwnership(const QString & filePath)
{
#ifndef _WIN32
// Only relevant when the process is effectively root (e.g. launched with
// sudo). getenv("SUDO_UID"/"SUDO_GID") give the invoking user.
if(geteuid() == 0)
{
const char * sudoUid = getenv("SUDO_UID");
const char * sudoGid = getenv("SUDO_GID");
if(sudoUid && sudoGid)
{
uid_t uid = (uid_t)atoi(sudoUid);
gid_t gid = (gid_t)atoi(sudoGid);
// Restore the config file and its containing directory so the user
// can still write preferences without sudo afterwards.
if(!filePath.isEmpty() && QFile::exists(filePath))
{
if(chown(filePath.toStdString().c_str(), uid, gid) != 0)
{
UWARN("Could not restore ownership of \"%s\" to uid=%d (%s).",
filePath.toStdString().c_str(), (int)uid, strerror(errno));
}
}
QString dir = QFileInfo(filePath).absolutePath();
if(!dir.isEmpty())
{
chown(dir.toStdString().c_str(), uid, gid);
}
}
}
#else
Q_UNUSED(filePath);
#endif
}
void PreferencesDialog::loadConfigFrom()
{
QString path = QFileDialog::getOpenFileName(this, tr("Load settings..."), this->getWorkingDirectory(), "*.ini");
@@ -3242,6 +3329,9 @@ void PreferencesDialog::writeSettings(const QString & filePath)
uInsert(_parameters, _modifiedParameters); // update cached parameters
_modifiedParameters.clear();
_obsoletePanels = kPanelDummy;
// Keep the ini writable by the invoking user even if we are running as root.
restoreConfigOwnership(filePath.isEmpty() ? getIniFilePath() : filePath);
}
void PreferencesDialog::writeGuiSettings(const QString & filePath) const
@@ -6635,6 +6725,16 @@ QString PreferencesDialog::getSourceDriverStr() const
return "";
}
QString PreferencesDialog::getSourceInitWarningMsg() const
{
if(getSourceDriver() == kSrcStereoZed)
{
return QString::fromStdString(
CameraStereoZed::getNeuralModelWarning(_ui->comboBox_stereoZed_quality->currentIndex()));
}
return QString();
}
QString PreferencesDialog::getSourceDevice() const
{
return _ui->lineEdit_sourceDevice->text();
@@ -6820,11 +6920,11 @@ double PreferencesDialog::getSourceScanForceGroundNormalsUp() const
Camera * PreferencesDialog::createCamera(bool useRawImages, bool useColor)
{
return createCamera(
this->getSourceDriver(),
_ui->lineEdit_sourceDevice->text(),
_ui->lineEdit_calibrationFile->text(),
useRawImages,
useColor,
this->getSourceDriver(),
_ui->lineEdit_sourceDevice->text(),
_ui->lineEdit_calibrationFile->text(),
useRawImages,
useColor,
false,
false);
}
@@ -7728,7 +7828,30 @@ void PreferencesDialog::setSLAMMode(bool enabled)
void PreferencesDialog::testOdometry()
{
QString startLabel = tr("Starting camera...");
QString initWarn = getSourceInitWarningMsg();
if(!initWarn.isEmpty())
{
startLabel += "\n\n" + initWarn;
}
QProgressDialog progress(startLabel, QString(), 0, 0, this);
if(!initWarn.isEmpty())
{
QLabel * wrapLabel = new QLabel(startLabel);
wrapLabel->setWordWrap(true);
progress.setLabel(wrapLabel); // QProgressDialog takes ownership
progress.setMinimumWidth(450);
}
progress.setWindowModality(Qt::ApplicationModal);
progress.setCancelButton(0);
progress.setMinimumDuration(0);
progress.setValue(0);
progress.show();
QApplication::processEvents();
QApplication::processEvents(); // make sure it is drawn
Camera * camera = this->createCamera();
progress.hide();
if(!camera)
{
return;
@@ -7776,12 +7899,13 @@ void PreferencesDialog::testOdometry()
_ui->odom_dataBufferSize->value());
odomThread.registerToEventsManager();
// parent = 0 (not 'this'): see testCamera() - avoids the nested-modality crash.
OdometryViewer * odomViewer = new OdometryViewer(10,
_ui->spinBox_decimation_odom->value(),
0.0f,
_ui->doubleSpinBox_maxDepth_odom->value(),
this->getOdomQualityWarnThr(),
this,
0,
this->getAllParameters());
odomViewer->setWindowTitle(tr("Odometry viewer"));
odomViewer->resize(1280, 480+QPushButton().minimumHeight());
@@ -7843,25 +7967,79 @@ void PreferencesDialog::testOdometry()
}
odomViewer->exec();
delete odomViewer;
UDEBUG("Dialog closed, stopping sensor...");
// Tear down the pipes first so no more events are routed to the threads/viewer being
// destroyed, then stop the threads, then delete the viewer. This avoids delivering
// events to a handler that is being torn down.
UEventsManager::removePipe(&cameraThread, &odomThread, "SensorEvent");
if(imuThread)
{
UEventsManager::removePipe(imuThread, &odomThread, "IMUEvent");
}
UEventsManager::removePipe(&odomThread, odomViewer, "OdometryEvent");
UEventsManager::removePipe(odomViewer, &odomThread, "OdometryResetEvent");
if(imuThread)
{
imuThread->join(true);
delete imuThread;
}
// Reuse the same dialog for the close. The device close() runs in cameraThread's destructor
// at function scope end (not in join()), so 'progress' stays visible across it. On Windows
// the first 2-3 RealSense closes per launch stall ~20s in the Motion Module stop().
progress.setLabelText(tr("Closing camera..."));
progress.show();
QApplication::processEvents();
QApplication::processEvents(); // make sure it is drawn
cameraThread.join(true);
odomThread.join(true);
// deleteLater() (not delete): see testCamera() - avoids a dangling OpenGL platform
// window that crashes in QWindowsWindow::alertWindow when Preferences later closes.
odomViewer->deleteLater();
if(imuThread)
{
delete imuThread;
}
}
void PreferencesDialog::testCamera()
{
CameraViewer * window = new CameraViewer(this, this->getAllParameters());
// Not parented to 'this': the Preferences dialog is itself application-modal, and making
// the viewer a modal *child* of it (nested modality) with an OpenGL/VTK native window
// crashes Qt (QWindowsWindow::alertWindow, this==nullptr) when Preferences later closes.
// exec() below still makes the viewer application-modal, so interaction stays blocked.
CameraViewer * window = new CameraViewer(nullptr, this->getAllParameters());
window->setWindowTitle(tr("Camera viewer"));
window->resize(1280, 480+QPushButton().minimumHeight());
window->registerToEventsManager();
QString startLabel = tr("Starting camera...");
QString initWarn = getSourceInitWarningMsg();
if(!initWarn.isEmpty())
{
startLabel += "\n\n" + initWarn;
}
QProgressDialog progress(startLabel, QString(), 0, 0, this);
if(!initWarn.isEmpty())
{
QLabel * wrapLabel = new QLabel(startLabel);
wrapLabel->setWordWrap(true);
progress.setLabel(wrapLabel); // QProgressDialog takes ownership
progress.setMinimumWidth(450);
}
progress.setWindowModality(Qt::ApplicationModal);
progress.setCancelButton(0);
progress.setMinimumDuration(0);
progress.setValue(0);
progress.show();
QApplication::processEvents();
QApplication::processEvents(); // make sure it is drawn
// createCamera() init()s the device on the GUI thread (required by ZED) and takes a few seconds.
Camera * camera = this->createCamera();
progress.hide();
if(camera)
{
SensorCaptureThread cameraThread(camera, this->getAllParameters());
@@ -7906,12 +8084,26 @@ void PreferencesDialog::testCamera()
cameraThread.start();
window->exec();
delete window;
cameraThread.join(true);
UDEBUG("Dialog closed, stopping sensor...");
UEventsManager::removePipe(&cameraThread, window, "SensorEvent");
// Reuse the same dialog for the close. The device close() runs in cameraThread's
// destructor at scope end (not in join()), so 'progress' - declared in the outer scope -
// stays visible across it. On Windows the first 2-3 RealSense closes per launch stall
// ~20s in the Motion Module stop() (librealsense warm-up); this keeps the user informed.
progress.setLabelText(tr("Closing camera..."));
progress.show();
QApplication::processEvents();
QApplication::processEvents(); // make sure it is drawn
cameraThread.join(true); // cameraThread's destructor (scope end) closes the device
// deleteLater() (not delete): defer destruction to the event loop so Qt finishes
// tearing down the OpenGL widget's context and window-proc subclass and drains
// pending activation messages first.
window->deleteLater();
}
else
{
delete window;
window->deleteLater();
}
}
@@ -8377,14 +8569,25 @@ void PreferencesDialog::calibrateOdomSensorExtrinsics()
void PreferencesDialog::testLidar()
{
CameraViewer * window = new CameraViewer(this, this->getAllParameters());
// Not parented to 'this': see testCamera() - avoids the nested-modality crash.
CameraViewer * window = new CameraViewer(nullptr, this->getAllParameters());
window->setWindowTitle(tr("Lidar viewer"));
window->setWindowFlags(Qt::Window);
window->resize(1280, 480+QPushButton().minimumHeight());
window->registerToEventsManager();
window->setDecimation(1);
QProgressDialog progress(tr("Starting lidar..."), QString(), 0, 0, this);
progress.setWindowModality(Qt::ApplicationModal);
progress.setCancelButton(0);
progress.setMinimumDuration(0);
progress.setValue(0);
progress.show();
QApplication::processEvents();
QApplication::processEvents(); // make sure it is drawn
Lidar * lidar = this->createLidar();
progress.hide();
if(lidar)
{
SensorCaptureThread lidarThread(lidar, this->getAllParameters());
@@ -8403,12 +8606,24 @@ void PreferencesDialog::testLidar()
lidarThread.start();
window->exec();
delete window;
lidarThread.join(true);
UDEBUG("Dialog closed, stopping sensor...");
UEventsManager::removePipe(&lidarThread, window, "SensorEvent");
// Reuse the same dialog for the close. The device close() runs in lidarThread's
// destructor at scope end (not in join()), so 'progress' - declared in the outer
// scope - stays visible across it.
progress.setLabelText(tr("Closing sensor..."));
progress.show();
QApplication::processEvents();
QApplication::processEvents(); // make sure it is drawn
lidarThread.join(true); // lidarThread's destructor (scope end) closes the device
// deleteLater() (not delete): see testCamera() - avoids a dangling OpenGL platform
// window that crashes in QWindowsWindow::alertWindow when Preferences later closes.
window->deleteLater();
}
else
{
delete window;
window->deleteLater();
}
}