mirror of
https://github.com/introlab/rtabmap.git
synced 2026-09-11 14:00:20 +08:00
Compare commits
83
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
41dde1ec8c | ||
|
|
0af33521a0 | ||
|
|
6f34498651 | ||
|
|
bb95cc081a | ||
|
|
4756474367 | ||
|
|
49aa566613 | ||
|
|
a9146f5be6 | ||
|
|
27ca7ec058 | ||
|
|
8d0692eab0 | ||
|
|
afe8aadff1 | ||
|
|
440e143d87 | ||
|
|
72833c89bb | ||
|
|
f04067fa16 | ||
|
|
c1ef13a8d6 | ||
|
|
37f8fa63c5 | ||
|
|
0cf2e45c81 | ||
|
|
f48db0ee51 | ||
|
|
2314c4dfa5 | ||
|
|
ebba7e4878 | ||
|
|
aa6d20775f | ||
|
|
9cbe84e445 | ||
|
|
cc86af4399 | ||
|
|
79ff91d293 | ||
|
|
aa7546ec62 | ||
|
|
a8fd7f043d | ||
|
|
a9f63bd5fd | ||
|
|
8fd701aabe | ||
|
|
1ea8fa2e06 | ||
|
|
51cfc37923 | ||
|
|
26bab2ba15 | ||
|
|
20409d2bf6 | ||
|
|
94bd3601fc | ||
|
|
f94a963463 | ||
|
|
5b985f69be | ||
|
|
0151f8cdfb | ||
|
|
018b804a46 | ||
|
|
8051be45b3 | ||
|
|
0ef907757b | ||
|
|
d6cf470402 | ||
|
|
b3b8fbddbe | ||
|
|
b47c4093a6 | ||
|
|
e23c38f471 | ||
|
|
040de14ec8 | ||
|
|
e6bc39028a | ||
|
|
da55168f60 | ||
|
|
48f529b4c6 | ||
|
|
42faef3941 | ||
|
|
7db14533f1 | ||
|
|
93ca4ed8d7 | ||
|
|
92aff2129f | ||
|
|
af10d6b778 | ||
|
|
6f715b4a8d | ||
|
|
72bd453078 | ||
|
|
86d5c99f5d | ||
|
|
07c3b8c901 | ||
|
|
26573456c2 | ||
|
|
af7ff60a6c | ||
|
|
106874845f | ||
|
|
1e7ad82af6 | ||
|
|
65867c47ae | ||
|
|
c7da53ac3d | ||
|
|
16c0d2fec5 | ||
|
|
90d7cc3045 | ||
|
|
3268707c00 | ||
|
|
3093df8e71 | ||
|
|
1edb6a8647 | ||
|
|
0bf2160939 | ||
|
|
e75baec73e | ||
|
|
82fb7ff5d2 | ||
|
|
d1b5d62d21 | ||
|
|
e612d103bf | ||
|
|
c97f0c10dd | ||
|
|
e3a60c8672 | ||
|
|
e9c6180d56 | ||
|
|
5e4fd171e2 | ||
|
|
f44a4fc478 | ||
|
|
a20e161a5b | ||
|
|
b04f607828 | ||
|
|
a3076392b5 | ||
|
|
210aadb92b | ||
|
|
1def668578 | ||
|
|
b9d7e8077c | ||
|
|
098db33547 |
-152
@@ -1,152 +0,0 @@
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- devel
|
||||
|
||||
os: Visual Studio 2015
|
||||
|
||||
clone_folder: c:\projects\rtabmap
|
||||
|
||||
platform: x64
|
||||
configuration: Release
|
||||
|
||||
init:
|
||||
- cmake --version
|
||||
- call "C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.cmd" /x64
|
||||
- call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86_amd64
|
||||
|
||||
install:
|
||||
# To download from google drive
|
||||
- set PATH=C:\Python38-x64;C:\Python38-x64\Scripts;%PATH%
|
||||
- ps: py -m pip --disable-pip-version-check install gdown>=5.1.0
|
||||
# Qt
|
||||
- set QTDIR=C:\Qt\5.10.1\msvc2015_64
|
||||
# make sure Qt bin path is before cmake bin path to avoid copying qt5 dlls from cmake before qt installation
|
||||
- set PATH=%QTDIR%\bin;%PATH%
|
||||
# Boost
|
||||
- set PATH=%PATH%;C:\Libraries\boost_1_62_0\lib64-msvc-14.0
|
||||
# Openni2
|
||||
- ps: wget 'https://dl.dropboxusercontent.com/s/d98jv79l6oy9fxz/OpenNI2.exe?dl=0' -outfile OpenNI2.exe
|
||||
- cmd: OpenNI2.exe -o"C:\Program Files" -y
|
||||
- ECHO "Installed OpenNI2:"
|
||||
- ps: "ls \"C:/Program Files/OpenNI2\""
|
||||
- set PATH=%PATH%;C:\Program Files\OpenNI2\Redist
|
||||
- set OPENNI2_INCLUDE64=C:\Program Files\OpenNI2\Include
|
||||
- set OPENNI2_LIB64=C:\Program Files\OpenNI2\Lib
|
||||
- set OPENNI2_REDIST64=C:\Program Files\OpenNI2\Redist
|
||||
# OpenCV
|
||||
#- appveyor-retry appveyor DownloadFile http://downloads.sourceforge.net/project/opencvlibrary/4.5.2/opencv-4.5.2-vc14_vc15.exe
|
||||
#- cmd: opencv-4.5.2-vc14_vc15.exe -o"C:\Program Files" -y
|
||||
#- ECHO "Installed OpenCV:"
|
||||
#- ps: "ls \"C:/Program Files/opencv/build\""
|
||||
#- set PATH=%PATH%;C:\Program Files\opencv\build\x64\vc14\bin
|
||||
- ps: wget 'https://dl.dropboxusercontent.com/s/o6ofn491bc0jso1/opencv450_vc14.exe?dl=0' -outfile opencv.exe
|
||||
- cmd: opencv.exe -o"C:\Program Files" -y
|
||||
- ECHO "Installed OpenCV:"
|
||||
- ps: "ls \"C:/Program Files/opencv\""
|
||||
- set PATH=%PATH%;C:\Program Files\opencv\x64\vc14\bin
|
||||
# VTK (including QVTK)
|
||||
- ps: wget 'https://dl.dropboxusercontent.com/s/1l33b5l3f3y52gf/VTK-6_3-msvc140.exe?dl=0' -outfile VTK-6_3.exe
|
||||
- cmd: VTK-6_3.exe -o"C:\Program Files" -y
|
||||
- ECHO "Installed VTK:"
|
||||
- ps: "ls \"C:/Program Files/VTK\""
|
||||
- set PATH=%PATH%;C:\Program Files\VTK\bin
|
||||
# QHull
|
||||
- ps: wget 'https://dl.dropboxusercontent.com/s/9widnk9msdsh2b8/Qhull-msvc140.exe?dl=0' -outfile Qhull.exe
|
||||
- cmd: Qhull.exe -o"C:\Program Files" -y
|
||||
- ECHO "Installed QHull:"
|
||||
- ps: "ls \"C:/Program Files/Qhull\""
|
||||
- set PATH=%PATH%;C:\Program Files\Qhull\bin
|
||||
# FLANN
|
||||
- ps: wget 'https://dl.dropboxusercontent.com/s/7k58jbmqa51sxmh/FLANN-msvc140.exe?dl=0' -outfile FLANN.exe
|
||||
- cmd: FLANN.exe -o"C:\Program Files" -y
|
||||
- ECHO "Installed FLANN:"
|
||||
- ps: "ls \"C:/Program Files/FLANN\""
|
||||
- set PATH=%PATH%;C:\Program Files\FLANN\bin
|
||||
# Eigen
|
||||
- ps: wget 'https://dl.dropboxusercontent.com/s/3v6i9i8dxj4o8ji/Eigen.exe?dl=0' -outfile Eigen.exe
|
||||
- cmd: Eigen.exe -o"C:\Program Files" -y
|
||||
- ECHO "Installed Eigen:"
|
||||
- ps: "ls \"C:/Program Files/Eigen\""
|
||||
# PCL
|
||||
- ps: wget 'https://dl.dropboxusercontent.com/s/2iayr4lyqa50i9j/PCL_181_August2018_x64_vc14.exe?dl=0' -outfile PCL_1.8.1.exe
|
||||
- cmd: PCL_1.8.1.exe -o"C:\Program Files" -y
|
||||
- ECHO "Installed PCL:"
|
||||
- ps: "ls \"C:/Program Files/PCL\""
|
||||
- set PATH=%PATH%;C:\Program Files\PCL\bin
|
||||
# zlib
|
||||
- ps: gdown -q 0B46akLGdg-uaYm9MTTI4MUtUcmc
|
||||
- ps: Expand-Archive zlib-1.2.8-vc2010-x64.zip -DestinationPath 'C:\Program Files'
|
||||
- ECHO "Installed zlib:"
|
||||
- ps: "ls \"C:/Program Files/zlib\""
|
||||
- set PATH=%PATH%;C:\Program Files\zlib\bin
|
||||
# g2o
|
||||
- ps: wget 'https://dl.dropboxusercontent.com/s/ht74s5pa21wokzw/g2o.exe?dl=0' -outfile g2o.exe
|
||||
- cmd: g2o.exe -o"C:\Program Files" -y
|
||||
- ECHO "Installed g2o:"
|
||||
- ps: "ls \"C:/Program Files/g2o\""
|
||||
- set PATH=%PATH%;C:\Program Files\g2o\bin
|
||||
# GTSAM
|
||||
- ps: wget 'https://dl.dropboxusercontent.com/s/0fpr6r4cgsqmvhf/GTSAM-4_0_0_alpha2-msvc140.exe?dl=0' -outfile GTSAM.exe
|
||||
- cmd: GTSAM.exe -o"C:\Program Files" -y
|
||||
- ECHO "Installed GTSAM:"
|
||||
- ps: "ls \"C:/Program Files/GTSAM\""
|
||||
- set PATH=%PATH%;C:\Program Files\GTSAM\bin
|
||||
# OctoMap
|
||||
- ps: wget 'https://dl.dropboxusercontent.com/s/6jpxu0nm8ne6e54/octomap_x64_vc14.exe?dl=0' -outfile octomap.exe
|
||||
- cmd: octomap.exe -o"C:\Program Files" -y
|
||||
- ECHO "Installed OctoMap:"
|
||||
- ps: "ls \"C:/Program Files/octomap-distribution\""
|
||||
- set PATH=%PATH%;C:\Program Files\octomap-distribution\bin
|
||||
# CPU-TSDF
|
||||
- ps: wget 'https://dl.dropboxusercontent.com/s/mgges9va1uzxr0q/cpu_tsdf_sept2015_x64_vc14.exe?dl=0' -outfile cpu_tsdf.exe
|
||||
- cmd: cpu_tsdf.exe -o"C:\Program Files" -y
|
||||
- ECHO "Installed CPU-TSDF:"
|
||||
- ps: "ls \"C:/Program Files/cpu_tsdf\""
|
||||
- set PATH=%PATH%;C:\Program Files\cpu_tsdf\bin
|
||||
# Open Chisel
|
||||
- ps: wget 'https://dl.dropboxusercontent.com/s/0aaphcde4acrinm/open_chisel_x64_vc14.exe?dl=0' -outfile open_chisel.exe
|
||||
- cmd: open_chisel.exe -o"C:\Program Files" -y
|
||||
- ECHO "Installed Open Chisel:"
|
||||
- ps: "ls \"C:/Program Files/open_chisel\""
|
||||
- set PATH=%PATH%;C:\Program Files\open_chisel\bin
|
||||
# yaml-cpp
|
||||
- ps: wget 'https://dl.dropboxusercontent.com/s/22qfvftwj6zq8tj/yaml-cpp_x64_vc14.exe?dl=0' -outfile yaml-cpp.exe
|
||||
- cmd: yaml-cpp.exe -o"C:\Program Files" -y
|
||||
- ECHO "Installed yaml-cpp:"
|
||||
- ps: "ls \"C:/Program Files/yaml-cpp\""
|
||||
# RealSense2
|
||||
- ps: wget 'https://github.com/IntelRealSense/librealsense/releases/download/v2.40.0/Intel.RealSense.SDK-WIN10-2.40.0.2482.exe' -outfile realsense2.exe
|
||||
- cmd: realsense2.exe /VERYSILENT
|
||||
- ECHO "Installed RealSense2:"
|
||||
- ps: "ls \"C:/Program Files (x86)/Intel RealSense SDK 2.0\""
|
||||
- set PATH=%PATH%;C:\Program Files (x86)\Intel RealSense SDK 2.0\bin\x64
|
||||
- set RealSense2_ROOT_DIR=C:\Program Files (x86)\Intel RealSense SDK 2.0
|
||||
# Kinect 4 Azure
|
||||
- ps: wget 'https://download.microsoft.com/download/3/d/6/3d6d9e99-a251-4cf3-8c6a-8e108e960b4b/Azure%20Kinect%20SDK%201.4.1.exe' -outfile azure.exe
|
||||
- cmd: azure.exe /quiet
|
||||
- ECHO "Installed Kinect For Azure:"
|
||||
- ps: "ls \"C:/Program Files/Azure Kinect SDK v1.4.1\""
|
||||
- set PATH=%PATH%;C:\Program Files\Azure Kinect SDK v1.4.1\tools
|
||||
- set K4A_ROOT_DIR=C:\Program Files\Azure Kinect SDK v1.4.1
|
||||
|
||||
before_build:
|
||||
- cd c:\projects\rtabmap\build
|
||||
- ECHO %PROGRAMFILES%
|
||||
- ECHO %PATH%
|
||||
- cmake -G "Visual Studio 14 2015 Win64" -DOpenCV_DIR="C:\Program Files\opencv\build" -DPCL_DIR="C:\Program Files\PCL\cmake" -DCPUTSDF_DIR="C:\Program Files\cpu_tsdf\share\cpu_tsdf" -Dyaml-cpp_DIR="C:\Program Files\yaml-cpp\CMake" -DBUILD_AS_BUNDLE=ON ..
|
||||
|
||||
after_build :
|
||||
- cmake --build . --config Release --target package
|
||||
|
||||
artifacts:
|
||||
- path: build\RTABMap-*
|
||||
|
||||
notifications:
|
||||
- provider: Email
|
||||
to:
|
||||
- matlabbe@gmail.com
|
||||
on_build_success: false
|
||||
on_build_failure: false
|
||||
on_build_status_changed: true
|
||||
@@ -4,7 +4,7 @@
|
||||
},
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"extensions": ["ms-vscode.cpptools-themes", "ms-vscode.cmake-tools"]
|
||||
"extensions": ["ms-vscode.cpptools-themes", "ms-vscode.cmake-tools", "ms-vscode.cpptools-extension-pack"]
|
||||
}
|
||||
},
|
||||
"workspaceMount": "source=${localWorkspaceFolder},target=/home/vscode/rtabmap,type=bind",
|
||||
@@ -14,5 +14,17 @@
|
||||
"terminal.integrated.defaultProfile.linux": "bash"
|
||||
},
|
||||
"remoteUser": "vscode",
|
||||
"runArgs": ["--privileged", "--network=host"]
|
||||
"hostRequirements": {
|
||||
"gpu": "optional"
|
||||
},
|
||||
"runArgs": ["--privileged",
|
||||
"--network=host",
|
||||
"--gpus=all",
|
||||
//"--runtime=nvidia", // uncommment this if rtabmap doesn't show up in nvidia-smi on the host computer
|
||||
"--env=DISPLAY",
|
||||
"--env=QT_X11_NO_MITSHM=1",
|
||||
"--volume=/tmp/.X11-unix:/tmp/.X11-unix"],
|
||||
"containerEnv": {
|
||||
"NVIDIA_VISIBLE_DEVICES": "all"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,11 +6,12 @@ ENV DEBIAN_FRONTEND=noninteractive
|
||||
# Install ROS2
|
||||
RUN apt update && \
|
||||
apt install software-properties-common -y && \
|
||||
add-apt-repository universe && \
|
||||
add-apt-repository universe -y && \
|
||||
apt update && \
|
||||
apt install curl -y && \
|
||||
curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg && \
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(. /etc/os-release && echo $UBUNTU_CODENAME) main" | tee /etc/apt/sources.list.d/ros2.list > /dev/null && \
|
||||
export ROS_APT_SOURCE_VERSION=$(curl -s https://api.github.com/repos/ros-infrastructure/ros-apt-source/releases/latest | grep -F "tag_name" | awk -F\" '{print $4}') && \
|
||||
curl -L -o /tmp/ros2-apt-source.deb "https://github.com/ros-infrastructure/ros-apt-source/releases/download/${ROS_APT_SOURCE_VERSION}/ros2-apt-source_${ROS_APT_SOURCE_VERSION}.$(. /etc/os-release && echo ${UBUNTU_CODENAME:-${VERSION_CODENAME}})_all.deb" && \
|
||||
apt install /tmp/ros2-apt-source.deb && \
|
||||
apt-get clean && rm -rf /var/lib/apt/lists/
|
||||
|
||||
# Install build dependencies
|
||||
|
||||
@@ -14,5 +14,17 @@
|
||||
"terminal.integrated.defaultProfile.linux": "bash"
|
||||
},
|
||||
"remoteUser": "vscode",
|
||||
"runArgs": ["--privileged", "--network=host"]
|
||||
"hostRequirements": {
|
||||
"gpu": "optional"
|
||||
},
|
||||
"runArgs": ["--privileged",
|
||||
"--network=host",
|
||||
"--gpus=all",
|
||||
//"--runtime=nvidia", // uncommment this if rtabmap doesn't show up in nvidia-smi on the host computer
|
||||
"--env=DISPLAY",
|
||||
"--env=QT_X11_NO_MITSHM=1",
|
||||
"--volume=/tmp/.X11-unix:/tmp/.X11-unix"],
|
||||
"containerEnv": {
|
||||
"NVIDIA_VISIBLE_DEVICES": "all"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
name: 'Install Windows Dependencies with CUDA'
|
||||
description: 'Installs PCL, Qt, VTK, g2o and others'
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Set up MSVC Developer Command Prompt
|
||||
uses: ilammy/msvc-dev-cmd@v1
|
||||
with:
|
||||
arch: x64
|
||||
|
||||
- name: Install CUDA
|
||||
uses: Jimver/cuda-toolkit@v0.2.30
|
||||
id: cuda-toolkit
|
||||
with:
|
||||
cuda: '13.0.0'
|
||||
use-github-cache: True
|
||||
|
||||
- name: Verify CUDA
|
||||
shell: bash
|
||||
run: |
|
||||
nvcc --version
|
||||
echo "CUDA Path: $CUDA_PATH"
|
||||
|
||||
- name: Cache vcpkg
|
||||
id: cache-vcpkg
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ runner.workspace }}/vcpkg_installed
|
||||
key: ${{ runner.os }}-vcpkg-export-66c0373d-x64-vs2022-cuda130_v1
|
||||
|
||||
- name: Download and Install vcpkg
|
||||
if: steps.cache-vcpkg.outputs.cache-hit != 'true'
|
||||
shell: pwsh
|
||||
run: |
|
||||
$install_dir = "${{ runner.workspace }}\vcpkg_installed"
|
||||
$archivePath = "${{ runner.workspace }}\vcpkg-export.7z"
|
||||
|
||||
# The file has been built locally with bundle-windows-deps.bat
|
||||
$url = "https://github.com/introlab/rtabmap/releases/download/0.23.1/vcpkg-export-66c0373d-x64-vs2022-cuda130.7z"
|
||||
|
||||
Invoke-WebRequest -Uri $url -OutFile $archivePath
|
||||
& 7z x $archivePath "-o$install_dir" -y
|
||||
|
||||
- name: Add vcpkg to PATH and env variable
|
||||
shell: pwsh
|
||||
run: |
|
||||
$vcpkg_path = "${{ runner.workspace }}\vcpkg_installed"
|
||||
echo "VCPKG_EXPORT_PATH=$vcpkg_path" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
|
||||
echo "$vcpkg_path\installed\x64-windows-release\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
||||
echo "${{env.CUDA_PATH}}\bin\x64" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
||||
echo "${{env.CUDA_PATH}}\extras\CUPTI\lib64" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
||||
echo "$vcpkg_path\installed\x64-windows-release\tools\python3\Lib\site-packages\torch\lib" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
||||
echo "$vcpkg_path\installed\x64-windows-release\tools\python3\Lib\site-packages\numpy.libs" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
||||
@@ -0,0 +1,37 @@
|
||||
name: 'Install Windows Dependencies'
|
||||
description: 'Installs PCL, Qt, VTK, g2o and others'
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Set up MSVC Developer Command Prompt
|
||||
uses: ilammy/msvc-dev-cmd@v1
|
||||
with:
|
||||
arch: x64
|
||||
|
||||
- name: Cache vcpkg
|
||||
id: cache-vcpkg
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ runner.workspace }}/vcpkg_installed
|
||||
key: ${{ runner.os }}-vcpkg-export-66c0373d-x64-vs2022-v4
|
||||
|
||||
- name: Download and Install vcpkg
|
||||
if: steps.cache-vcpkg.outputs.cache-hit != 'true'
|
||||
shell: pwsh
|
||||
run: |
|
||||
$install_dir = "${{ runner.workspace }}\vcpkg_installed"
|
||||
$archivePath = "${{ runner.workspace }}\vcpkg-export.7z"
|
||||
|
||||
# The file has been built locally with bundle-windows-deps.bat
|
||||
$url = "https://github.com/introlab/rtabmap/releases/download/0.23.1/vcpkg-export-66c0373d-x64-vs2022.7z"
|
||||
|
||||
Invoke-WebRequest -Uri $url -OutFile $archivePath
|
||||
& 7z x $archivePath "-o$install_dir" -y
|
||||
|
||||
- name: Add vcpkg to PATH and env variable
|
||||
shell: pwsh
|
||||
run: |
|
||||
$vcpkg_path = "${{ runner.workspace }}\vcpkg_installed"
|
||||
echo "VCPKG_EXPORT_PATH=$vcpkg_path" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
|
||||
echo "$vcpkg_path\installed\x64-windows-release\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
||||
echo "$vcpkg_path\installed\x64-windows-release\tools\python3\Lib\site-packages\numpy.libs" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
||||
@@ -0,0 +1,15 @@
|
||||
name: Cleanup PR Artifacts
|
||||
on:
|
||||
pull_request:
|
||||
types: [closed]
|
||||
|
||||
jobs:
|
||||
delete-artifacts:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: write
|
||||
steps:
|
||||
- name: Delete PR Artifacts
|
||||
uses: geekyeggo/delete-artifact@v5
|
||||
with:
|
||||
name: build-output-*
|
||||
@@ -0,0 +1,76 @@
|
||||
name: CMake-Linux
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- '**'
|
||||
|
||||
env:
|
||||
BUILD_TYPE: Release
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: ${{ matrix.build_name }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
build_name: [ubuntu-22.04, ubuntu-24.04, ubuntu-24.04-with-opengv]
|
||||
include:
|
||||
- build_name: ubuntu-22.04
|
||||
os: ubuntu-22.04
|
||||
extra_deps: "libunwind-dev libceres-dev"
|
||||
extra_cmake_def: "-DWITH_CERES=ON -DWITH_PYTHON=ON"
|
||||
- build_name: ubuntu-24.04
|
||||
os: ubuntu-24.04
|
||||
extra_deps: "libg2o-dev libceres-dev"
|
||||
extra_cmake_def: "-DWITH_CERES=ON -DWITH_PYTHON=ON"
|
||||
- build_name: ubuntu-24.04-with-opengv
|
||||
os: ubuntu-24.04
|
||||
extra_deps: "libg2o-dev libceres-dev"
|
||||
extra_cmake_def: "-DWITH_CERES=ON -DWITH_PYTHON=ON -DBUILD_OPENGV=ON"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install Linux Dependencies
|
||||
run: |
|
||||
DEBIAN_FRONTEND=noninteractive
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install libopencv-dev libpcl-dev git cmake software-properties-common libyaml-cpp-dev ${{ matrix.extra_deps }}
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Install Python Dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install numpy pybind11
|
||||
|
||||
- name: Configure CMake
|
||||
run: |
|
||||
cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DPython3_EXECUTABLE=$(which python3) -Dpybind11_DIR=$(python3 -m pybind11 --cmakedir) ${{ matrix.extra_cmake_def }}
|
||||
|
||||
- name: Build
|
||||
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
|
||||
|
||||
- name: Info
|
||||
working-directory: ${{github.workspace}}/build/bin
|
||||
run: |
|
||||
./rtabmap-console --version
|
||||
|
||||
# - name: Test
|
||||
# working-directory: ${{github.workspace}}/build
|
||||
# # Execute tests defined by the CMake configuration.
|
||||
# # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
|
||||
# run: ctest -C ${{env.BUILD_TYPE}}
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
name: CMake-MacOS
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- '**'
|
||||
|
||||
env:
|
||||
BUILD_TYPE: Release
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: ${{ matrix.build_name }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
build_name: [macos-sequoia-intel, macos-sequoia-apple-silicon, macos-tahoe-intel, macos-tahoe-apple-silicon]
|
||||
include:
|
||||
- build_name: macos-sequoia-intel
|
||||
os: macos-15-intel
|
||||
extra_deps: ""
|
||||
extra_cmake_def: '-DBUILD_AS_BUNDLE=ON'
|
||||
- build_name: macos-sequoia-apple-silicon
|
||||
os: macos-15
|
||||
extra_deps: ""
|
||||
extra_cmake_def: '-DBUILD_AS_BUNDLE=ON'
|
||||
- build_name: macos-tahoe-intel
|
||||
os: macos-26-intel
|
||||
extra_deps: ""
|
||||
extra_cmake_def: '-DBUILD_AS_BUNDLE=ON'
|
||||
- build_name: macos-tahoe-apple-silicon
|
||||
os: macos-26
|
||||
extra_deps: ""
|
||||
extra_cmake_def: '-DBUILD_AS_BUNDLE=ON'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install Brew Dependencies
|
||||
run: |
|
||||
# Update brew and install from Brewfile if present, or specific packages
|
||||
brew install pcl opencv octomap g2o pdal
|
||||
|
||||
- name: Configure CMake
|
||||
run: |
|
||||
cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} ${{ matrix.extra_cmake_def }}
|
||||
|
||||
- name: Build
|
||||
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
|
||||
|
||||
- name: Info
|
||||
working-directory: ${{github.workspace}}/build/bin
|
||||
run: |
|
||||
./rtabmap-console --version
|
||||
|
||||
# - name: Build MacOS Package
|
||||
# run: |
|
||||
# cmake --build ${{ github.workspace }}/build --config ${{ env.BUILD_TYPE }} --target package
|
||||
|
||||
# - name: Upload RTABMap Artifacts (DMG)
|
||||
# uses: actions/upload-artifact@v4
|
||||
# with:
|
||||
# name: RTABMap-Binaries-${{ matrix.build_name }}-zip
|
||||
# path: |
|
||||
# build/RTABMap-*.dmg
|
||||
# compression-level: 0
|
||||
# if-no-files-found: warn
|
||||
# retention-days: ${{ github.event_name == 'pull_request' && 1 || 90 }}
|
||||
|
||||
# - name: Test
|
||||
# working-directory: ${{github.workspace}}/build
|
||||
# # Execute tests defined by the CMake configuration.
|
||||
# # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
|
||||
# run: ctest -C ${{env.BUILD_TYPE}}
|
||||
|
||||
@@ -12,13 +12,17 @@ env:
|
||||
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
|
||||
BUILD_TYPE: Release
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
# The CMake configure and build commands are platform agnostic and should work equally
|
||||
# well on Windows or Mac. You can convert this to a matrix build if you need
|
||||
# cross-platform coverage.
|
||||
# See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
|
||||
name: Build on ros ${{ matrix.ros_distribution }} and ${{ matrix.os }}
|
||||
name: ${{ matrix.ros_distribution }}-${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -42,7 +46,7 @@ jobs:
|
||||
sudo add-apt-repository universe
|
||||
sudo apt update && sudo apt install curl -y
|
||||
export ROS_APT_SOURCE_VERSION=$(curl -s https://api.github.com/repos/ros-infrastructure/ros-apt-source/releases/latest | grep -F "tag_name" | awk -F\" '{print $4}')
|
||||
curl -L -o /tmp/ros2-apt-source.deb "https://github.com/ros-infrastructure/ros-apt-source/releases/download/${ROS_APT_SOURCE_VERSION}/ros2-apt-source_${ROS_APT_SOURCE_VERSION}.$(. /etc/os-release && echo $VERSION_CODENAME)_all.deb"
|
||||
curl -L -o /tmp/ros2-apt-source.deb "https://github.com/ros-infrastructure/ros-apt-source/releases/download/${ROS_APT_SOURCE_VERSION}/ros2-apt-source_${ROS_APT_SOURCE_VERSION}.$(. /etc/os-release && echo ${UBUNTU_CODENAME:-${VERSION_CODENAME}})_all.deb"
|
||||
sudo apt install /tmp/ros2-apt-source.deb
|
||||
sudo apt update
|
||||
|
||||
@@ -52,6 +56,11 @@ jobs:
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Point rosdep to latest Rolling on Noble until github runners support ubuntu 26.04
|
||||
if: matrix.ros_distribution == 'rolling'
|
||||
run: |
|
||||
echo "ROSDISTRO_INDEX_URL=https://raw.githubusercontent.com/ros/rosdistro/6527fa694360d609c1491528ed50437bd853af5b/index-v4.yaml" >> $GITHUB_ENV
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
source /opt/ros/${{ matrix.ros_distribution }}/setup.bash
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
name: CMake-Windows
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- '**'
|
||||
|
||||
env:
|
||||
BUILD_TYPE: Release
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: ${{ matrix.build_name }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
build_name: [windows-2022, windows-2022-cuda]
|
||||
include:
|
||||
- build_name: windows-2022
|
||||
os: windows-2022
|
||||
extra_deps: ""
|
||||
extra_cmake_def: '-DBUILD_AS_BUNDLE=ON -DWITH_PYTHON=ON -DWITH_TORCH=OFF'
|
||||
- build_name: windows-2022-cuda
|
||||
os: windows-2022
|
||||
extra_deps: ""
|
||||
extra_cmake_def: '-DBUILD_AS_BUNDLE=ON -DWITH_PYTHON=ON -DWITH_TORCH=ON'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install Windows Dependencies
|
||||
if: matrix.build_name == 'windows-2022'
|
||||
uses: ./.github/actions/install-windows-deps
|
||||
|
||||
- name: Install Windows Dependencies with CUDA
|
||||
if: matrix.build_name == 'windows-2022-cuda'
|
||||
uses: ./.github/actions/install-windows-cuda-deps
|
||||
|
||||
- name: Configure CMake
|
||||
run: |
|
||||
cmake `
|
||||
-B ${{github.workspace}}/build `
|
||||
-DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} `
|
||||
${{ matrix.extra_cmake_def }} `
|
||||
-DVCPKG_MANIFEST_INSTALL=OFF `
|
||||
-DVCPKG_TARGET_TRIPLET=x64-windows-release `
|
||||
-DVCPKG_INSTALLED_DIR="${{env.VCPKG_EXPORT_PATH}}/installed" `
|
||||
-DCMAKE_TOOLCHAIN_FILE=${{env.VCPKG_EXPORT_PATH}}/scripts/buildsystems/vcpkg.cmake `
|
||||
-DTorch_DIR=${{env.VCPKG_EXPORT_PATH}}/installed/x64-windows-release/tools/python3/Lib/site-packages/torch/share/cmake/Torch
|
||||
|
||||
- name: Build
|
||||
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
|
||||
|
||||
- name: Build Windows Package
|
||||
shell: pwsh
|
||||
run: |
|
||||
if ("${{ github.event_name }}" -eq "pull_request") {
|
||||
cpack --config build/CPackConfig.cmake -G ZIP -B build
|
||||
} else {
|
||||
cmake --build ${{ github.workspace }}/build --config ${{ env.BUILD_TYPE }} --target package
|
||||
}
|
||||
|
||||
- name: Rename CUDA artifacts
|
||||
if: matrix.build_name == 'windows-2022-cuda'
|
||||
shell: pwsh
|
||||
run: Get-ChildItem -Path "build" -Filter "RTABMap-*" | Rename-Item -NewName { $_.BaseName + "_cuda" + $_.Extension }
|
||||
|
||||
- name: Info
|
||||
working-directory: ${{github.workspace}}/build/bin
|
||||
run: |
|
||||
./rtabmap-console --version
|
||||
|
||||
- name: Upload RTABMap Artifacts (ZIP)
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: RTABMap-Binaries-${{ matrix.build_name }}-zip
|
||||
path: |
|
||||
build/RTABMap-*.zip
|
||||
compression-level: 0
|
||||
if-no-files-found: warn
|
||||
retention-days: ${{ github.event_name == 'pull_request' && 1 || 90 }}
|
||||
|
||||
- name: Upload RTABMap Artifacts (Installer)
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: RTABMap-Binaries-${{ matrix.build_name }}-exe
|
||||
path: |
|
||||
build/RTABMap-*.exe
|
||||
compression-level: 0
|
||||
if-no-files-found: warn
|
||||
retention-days: ${{ github.event_name == 'pull_request' && 1 || 90 }}
|
||||
|
||||
# - name: Test
|
||||
# working-directory: ${{github.workspace}}/build
|
||||
# # Execute tests defined by the CMake configuration.
|
||||
# # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
|
||||
# run: ctest -C ${{env.BUILD_TYPE}}
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
name: CMake
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- '**'
|
||||
|
||||
env:
|
||||
BUILD_TYPE: Release
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-24.04, ubuntu-22.04]
|
||||
include:
|
||||
- os: ubuntu-22.04
|
||||
extra_deps: "libunwind-dev libceres-dev"
|
||||
extra_cmake_def: ""
|
||||
- os: ubuntu-24.04
|
||||
extra_deps: "libg2o-dev libceres-dev"
|
||||
extra_cmake_def: "-DWITH_CERES=ON"
|
||||
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
DEBIAN_FRONTEND=noninteractive
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install libopencv-dev libpcl-dev git cmake software-properties-common libyaml-cpp-dev ${{ matrix.extra_deps }}
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Configure CMake
|
||||
run: |
|
||||
cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} ${{ matrix.extra_cmake_def }}
|
||||
|
||||
- name: Build
|
||||
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
|
||||
|
||||
- name: Info
|
||||
working-directory: ${{github.workspace}}/build/bin
|
||||
run: |
|
||||
./rtabmap-console --version
|
||||
|
||||
# - name: Test
|
||||
# working-directory: ${{github.workspace}}/build
|
||||
# # Execute tests defined by the CMake configuration.
|
||||
# # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
|
||||
# run: ctest -C ${{env.BUILD_TYPE}}
|
||||
|
||||
+166
-32
@@ -1,5 +1,6 @@
|
||||
# Top-Level CmakeLists.txt
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
|
||||
PROJECT( RTABMap )
|
||||
SET(PROJECT_PREFIX rtabmap)
|
||||
|
||||
@@ -11,6 +12,7 @@ IF(NOT MULTI_ARCH AND NOT DEFINED CMAKE_INSTALL_LIBDIR)
|
||||
ENDIF(NOT MULTI_ARCH AND NOT DEFINED CMAKE_INSTALL_LIBDIR)
|
||||
|
||||
INCLUDE(GNUInstallDirs)
|
||||
INCLUDE(FetchContent)
|
||||
|
||||
####### local cmake modules #######
|
||||
SET(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake_modules")
|
||||
@@ -20,9 +22,17 @@ SET(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake_modules")
|
||||
#######################
|
||||
SET(RTABMAP_MAJOR_VERSION 0)
|
||||
SET(RTABMAP_MINOR_VERSION 23)
|
||||
SET(RTABMAP_PATCH_VERSION 2)
|
||||
SET(RTABMAP_PATCH_VERSION 7)
|
||||
SET(RTABMAP_VERSION
|
||||
${RTABMAP_MAJOR_VERSION}.${RTABMAP_MINOR_VERSION}.${RTABMAP_PATCH_VERSION})
|
||||
|
||||
# Make sure we have valid version so that RTABMAP_VERSION_COMPARE logic in Version.h works
|
||||
IF(RTABMAP_MINOR_VERSION GREATER 99)
|
||||
MESSAGE(FATAL_ERROR "RTABMAP_MINOR_VERSION must be < 100, bump major version and restart minor to 0!")
|
||||
ENDIF()
|
||||
IF(RTABMAP_PATCH_VERSION GREATER 99)
|
||||
MESSAGE(FATAL_ERROR "RTABMAP_PATCH_VERSION must be < 100, bump minor version and restart patch to 0!")
|
||||
ENDIF()
|
||||
|
||||
SET(PROJECT_VERSION "${RTABMAP_VERSION}")
|
||||
|
||||
@@ -83,14 +93,6 @@ IF(MINGW)
|
||||
SET(CMAKE_SHARED_LINKER_FLAGS "-Wl,--enable-auto-import")
|
||||
ENDIF(MINGW)
|
||||
|
||||
# GCC 4 required
|
||||
IF(UNIX OR MINGW)
|
||||
EXEC_PROGRAM( gcc ARGS "-dumpversion" OUTPUT_VARIABLE GCC_VERSION )
|
||||
IF(GCC_VERSION VERSION_LESS "4.0.0")
|
||||
MESSAGE(FATAL_ERROR "GCC ${GCC_VERSION} found, but version 4.x.x minimum is required")
|
||||
ENDIF(GCC_VERSION VERSION_LESS "4.0.0")
|
||||
ENDIF(UNIX OR MINGW)
|
||||
|
||||
#The CDT Error Parser cannot handle error messages that span
|
||||
#more than one line, which is the default gcc behavior.
|
||||
#In order to force gcc to generate single line error messages with no line wrapping
|
||||
@@ -198,6 +200,7 @@ option(WITH_CCCORELIB "Include CCCoreLib support" OFF)
|
||||
option(WITH_OPEN3D "Include Open3D support" OFF)
|
||||
option(WITH_LOAM "Include LOAM support" OFF)
|
||||
option(WITH_FLOAM "Include FLOAM support" OFF)
|
||||
option(WITH_LIOSAM "Include LIO-SAM support" OFF)
|
||||
option(WITH_FLYCAPTURE2 "Include FlyCapture2/Triclops support" ON)
|
||||
option(WITH_ZED "Include ZED sdk support" ON)
|
||||
option(WITH_ZEDOC "Include ZED Open Capture support" ON)
|
||||
@@ -226,6 +229,8 @@ option(WITH_MADGWICK "Include Madgwick IMU filtering support" ON)
|
||||
option(WITH_FASTCV "Include FastCV support" ON)
|
||||
option(WITH_OPENMP "Include OpenMP support" ON)
|
||||
option(WITH_OPENGV "Include OpenGV support" ON)
|
||||
option(BUILD_OPENGV "Build OpenGV internally instead of using the system one" OFF)
|
||||
option(WITH_APRILTAG "Include AprilTag support" OFF)
|
||||
IF(MOBILE_BUILD)
|
||||
option(PCL_OMP "With PCL OMP implementations" OFF)
|
||||
ELSE()
|
||||
@@ -533,6 +538,16 @@ ENDIF(WITH_G2O)
|
||||
IF(WITH_GTSAM)
|
||||
# Force config mode to ignore PCL's findGTSAM.cmake file
|
||||
FIND_PACKAGE(GTSAM CONFIG QUIET)
|
||||
IF(GTSAM_FOUND)
|
||||
# For issue https://github.com/introlab/rtabmap/pull/1626
|
||||
FIND_FILE(GTSAM_NOISE_MODEL_FACTOR_N_FILE gtsam/nonlinear/NoiseModelFactorN.h
|
||||
PATHS ${GTSAM_INCLUDE_DIR}
|
||||
NO_DEFAULT_PATH)
|
||||
IF(GTSAM_NOISE_MODEL_FACTOR_N_FILE)
|
||||
MESSAGE(STATUS "GTSAM with NoiseModelFactorN.h")
|
||||
ADD_DEFINITIONS("-DGTSAM_WITH_NOISE_MODEL_FACTOR_N")
|
||||
ENDIF(GTSAM_NOISE_MODEL_FACTOR_N_FILE)
|
||||
ENDIF(GTSAM_FOUND)
|
||||
ENDIF(WITH_GTSAM)
|
||||
|
||||
IF(WITH_MRPT)
|
||||
@@ -625,6 +640,12 @@ IF(WITH_FLOAM)
|
||||
FIND_PACKAGE(Ceres REQUIRED)
|
||||
ENDIF(floam_FOUND)
|
||||
ENDIF(WITH_FLOAM)
|
||||
IF(WITH_LIOSAM)
|
||||
find_package(lio_sam QUIET)
|
||||
IF(lio_sam_FOUND)
|
||||
MESSAGE(STATUS "Found lio_sam: ${lio_sam_INCLUDE_DIRS}")
|
||||
ENDIF(lio_sam_FOUND)
|
||||
ENDIF(WITH_LIOSAM)
|
||||
|
||||
SET(ZED_FOUND FALSE)
|
||||
IF(WITH_ZED)
|
||||
@@ -637,7 +658,8 @@ IF(WITH_ZED)
|
||||
IF(CUDA_FOUND)
|
||||
MESSAGE(STATUS "Found CUDA: ${CUDA_INCLUDE_DIRS}")
|
||||
ELSE()
|
||||
MESSAGE(FATAL_ERROR "CUDA is required to build with Zed sdk! Set -DWITH_ZED=OFF if you don't have CUDA.")
|
||||
MESSAGE(WARNING "CUDA is required to build with Zed sdk! Set -DWITH_ZED=OFF if you don't have CUDA.")
|
||||
SET(ZED_FOUND FALSE)
|
||||
ENDIF()
|
||||
ENDIF(ZED_FOUND)
|
||||
ENDIF(WITH_ZED)
|
||||
@@ -782,8 +804,6 @@ IF(WITH_OKVIS)
|
||||
MESSAGE(STATUS "Found okvis: ${OKVIS_INCLUDE_DIRS}")
|
||||
find_package(brisk 2 REQUIRED)
|
||||
MESSAGE(STATUS "Found brisk: ${BRISK_INCLUDE_DIRS}")
|
||||
find_package(opengv REQUIRED)
|
||||
MESSAGE(STATUS "Found opengv: ${OPENGV_INCLUDE_DIRS}")
|
||||
find_package(Ceres 1.9.0 REQUIRED EXACT) # OKVIS requires this specific version
|
||||
MESSAGE(STATUS "Found ceres ${Ceres_VERSION}: ${CERES_INCLUDE_DIRS}")
|
||||
ENDIF(okvis_FOUND)
|
||||
@@ -847,12 +867,72 @@ IF(WITH_FASTCV)
|
||||
ENDIF(FastCV_FOUND)
|
||||
ENDIF(WITH_FASTCV)
|
||||
|
||||
IF(WITH_OPENGV)
|
||||
FIND_PACKAGE(opengv QUIET)
|
||||
IF(opengv_FOUND)
|
||||
MESSAGE(STATUS "Found OpenGV: ${opengv_INCLUDE_DIRS}")
|
||||
ENDIF(opengv_FOUND)
|
||||
ENDIF(WITH_OPENGV)
|
||||
IF(WITH_APRILTAG)
|
||||
FIND_PACKAGE(apriltag QUIET)
|
||||
IF(apriltag_FOUND)
|
||||
get_target_property(APRILTAG_LOCATION apriltag::apriltag LOCATION)
|
||||
get_target_property(APRILTAG_INCLUDES apriltag::apriltag INTERFACE_INCLUDE_DIRECTORIES)
|
||||
FIND_FILE(apriltag_aruco_4x4_50_header NAMES tagAruco4x4_50.h PATH_SUFFIXES aruco PATHS ${APRILTAG_INCLUDES} NO_DEFAULT_PATH)
|
||||
SET(WITH_APRILTAG_ARUCO NO)
|
||||
IF(apriltag_aruco_4x4_50_header)
|
||||
SET(WITH_APRILTAG_ARUCO YES)
|
||||
ENDIF()
|
||||
MESSAGE(STATUS "Found apriltag (with aruco=${WITH_APRILTAG_ARUCO}): ${APRILTAG_LOCATION} ${APRILTAG_INCLUDES}")
|
||||
ENDIF(apriltag_FOUND)
|
||||
ENDIF(WITH_APRILTAG)
|
||||
|
||||
IF(WITH_OPENGV OR okvis_FOUND)
|
||||
if(NOT BUILD_OPENGV)
|
||||
FIND_PACKAGE(opengv QUIET)
|
||||
endif()
|
||||
|
||||
if(opengv_FOUND)
|
||||
MESSAGE(STATUS "Found system-installed OpenGV: ${opengv_INCLUDE_DIRS}")
|
||||
elseif(BUILD_OPENGV)
|
||||
SET(PCL_USING_MARCHNATIVE OFF)
|
||||
if(PCL_COMPILE_OPTIONS)
|
||||
if("${PCL_COMPILE_OPTIONS}" MATCHES "-march=native")
|
||||
set(PCL_USING_MARCHNATIVE ON)
|
||||
endif()
|
||||
elseif("${PCL_DEFINITIONS}" MATCHES "-march=native")
|
||||
set(PCL_USING_MARCHNATIVE ON)
|
||||
endif()
|
||||
SET(MSG_EXTRA "without -march-native (not used by PCL)")
|
||||
if(PCL_USING_MARCHNATIVE)
|
||||
set(MSG_EXTRA "with -march-native (used by PCL)")
|
||||
endif()
|
||||
|
||||
message(STATUS "Download/Build OpenGV internally (BUILD_OPENGV=ON) ${MSG_EXTRA}.")
|
||||
function(add_submodule_opengv)
|
||||
FetchContent_Declare(
|
||||
opengv
|
||||
GIT_REPOSITORY https://github.com/laurentkneip/opengv.git
|
||||
GIT_TAG 91f4b19c73450833a40e463ad3648aae80b3a7f3
|
||||
PATCH_COMMAND ${CMAKE_COMMAND}
|
||||
-DPATCH_FILE=${CMAKE_CURRENT_LIST_DIR}/patches/opengv_91f4b19c.patch
|
||||
-P ${CMAKE_CURRENT_LIST_DIR}/patches/apply_patch.cmake
|
||||
)
|
||||
set(BUILD_SHARED_LIBS OFF)
|
||||
set(BUILD_TESTS OFF)
|
||||
set(CMAKE_BUILD_TYPE Release)
|
||||
set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
|
||||
# Eigen should have been already added by PCL, just populate the compatible variables
|
||||
IF(EIGEN_INCLUDE_DIRS)
|
||||
set(EIGEN_INCLUDE_DIRS "${EIGEN_INCLUDE_DIRS}" CACHE PATH "Eigen include dirs" FORCE)
|
||||
set(EIGEN_INCLUDE_DIR "${EIGEN_INCLUDE_DIRS}" CACHE PATH "Eigen include dir" FORCE)
|
||||
ELSEIF(Eigen3_INCLUDE_DIRS)
|
||||
set(EIGEN_INCLUDE_DIRS "${Eigen3_INCLUDE_DIRS}" CACHE PATH "Eigen include dirs" FORCE)
|
||||
set(EIGEN_INCLUDE_DIR "${Eigen3_INCLUDE_DIRS}" CACHE PATH "Eigen include dir" FORCE)
|
||||
ENDIF()
|
||||
set(BUILD_WITH_MARCHNATIVE ${PCL_USING_MARCHNATIVE})
|
||||
FetchContent_MakeAvailable(opengv)
|
||||
endfunction()
|
||||
|
||||
add_submodule_opengv()
|
||||
set(opengv_FOUND TRUE)
|
||||
set(opengv_VERSION "internal")
|
||||
endif()
|
||||
ENDIF()
|
||||
|
||||
IF(WITH_ORB_SLAM AND NOT G2O_FOUND)
|
||||
FIND_PACKAGE(ORB_SLAM)
|
||||
@@ -862,7 +942,7 @@ IF(WITH_ORB_SLAM AND NOT G2O_FOUND)
|
||||
ENDIF(WITH_ORB_SLAM AND NOT G2O_FOUND)
|
||||
|
||||
IF(WITH_CUVSLAM)
|
||||
FIND_PACKAGE(CuVSLAM)
|
||||
FIND_PACKAGE(CuVSLAM 14.0.0)
|
||||
IF(CUVSLAM_FOUND)
|
||||
MESSAGE(STATUS "Found cuVSLAM: ${CUVSLAM_INCLUDE_DIRS}")
|
||||
ENDIF()
|
||||
@@ -874,7 +954,7 @@ IF(NOT (APPLE OR WIN32) AND BUILD_WITH_RPATH_NOT_RUNPATH)
|
||||
ENDIF()
|
||||
|
||||
IF(NOT MSVC)
|
||||
IF(Qt6_FOUND OR (G2O_FOUND AND G2O_CPP11 EQUAL 1) OR TORCH_FOUND OR MRPT_FOUND)
|
||||
IF(Qt6_FOUND OR (G2O_FOUND AND G2O_CPP11 EQUAL 1) OR (GTSAM_FOUND AND GTSAM_VERSION VERSION_GREATER_EQUAL "4.3.0") OR TORCH_FOUND OR MRPT_FOUND)
|
||||
# Qt6 requires c++17
|
||||
include(CheckCXXCompilerFlag)
|
||||
CHECK_CXX_COMPILER_FLAG("-std=c++17" COMPILER_SUPPORTS_CXX17)
|
||||
@@ -1001,6 +1081,12 @@ ENDIF(NOT Open3D_FOUND)
|
||||
IF(NOT FastCV_FOUND)
|
||||
SET(FASTCV "//")
|
||||
ENDIF(NOT FastCV_FOUND)
|
||||
IF(NOT apriltag_FOUND)
|
||||
SET(APRILTAG "//")
|
||||
SET(APRILTAG_ARUCO "//")
|
||||
ELSEIF(NOT WITH_APRILTAG_ARUCO)
|
||||
SET(APRILTAG_ARUCO "//")
|
||||
ENDIF()
|
||||
IF(NOT opengv_FOUND OR NOT WITH_OPENGV)
|
||||
SET(OPENGV "//")
|
||||
ENDIF(NOT opengv_FOUND OR NOT WITH_OPENGV)
|
||||
@@ -1019,6 +1105,9 @@ ENDIF(NOT loam_velodyne_FOUND)
|
||||
IF(NOT floam_FOUND)
|
||||
SET(FLOAM "//")
|
||||
ENDIF(NOT floam_FOUND)
|
||||
IF(NOT lio_sam_FOUND)
|
||||
SET(LIOSAM "//")
|
||||
ENDIF(NOT lio_sam_FOUND)
|
||||
IF(NOT Freenect_FOUND)
|
||||
SET(FREENECT "//")
|
||||
ENDIF()
|
||||
@@ -1283,7 +1372,17 @@ install(FILES package.xml DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/${PROJECT_PR
|
||||
#######################
|
||||
IF(BUILD_AS_BUNDLE)
|
||||
SET(CMAKE_INSTALL_SYSTEM_RUNTIME_COMPONENT runtime)
|
||||
IF(WIN32)
|
||||
set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP TRUE)
|
||||
set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "
|
||||
ExecWait '\\\"$INSTDIR\\\\vc_redist.x64.exe\\\" /quiet /norestart'
|
||||
")
|
||||
ENDIF()
|
||||
INCLUDE(InstallRequiredSystemLibraries)
|
||||
set(CPACK_NSIS_COMPONENT_INSTALL OFF)
|
||||
set(CPACK_ARCHIVE_COMPONENT_INSTALL ON)
|
||||
set(CPACK_COMPONENTS_GROUPING ALL_COMPONENTS_IN_ONE)
|
||||
set(CPACK_COMPONENTS_ALL runtime)
|
||||
ENDIF(BUILD_AS_BUNDLE)
|
||||
|
||||
SET(CPACK_PACKAGE_NAME "${PROJECT_NAME}")
|
||||
@@ -1397,28 +1496,33 @@ ENDIF(PCL_COMPILE_OPTIONS)
|
||||
MESSAGE(STATUS "")
|
||||
MESSAGE(STATUS "Optional dependencies ('*' affects some default parameters) :")
|
||||
IF(OpenCV_FOUND)
|
||||
IF(OPENCV_ARUCO_FOUND)
|
||||
set(ARUCO_STR "YES")
|
||||
ELSE()
|
||||
set(ARUCO_STR "NO")
|
||||
ENDIF()
|
||||
IF(OpenCV_VERSION_MAJOR EQUAL 2)
|
||||
IF(OPENCV_NONFREE_FOUND)
|
||||
MESSAGE(STATUS " *With OpenCV 2 nonfree module (SIFT/SURF) = YES (License: Non commercial)")
|
||||
MESSAGE(STATUS " *With OpenCV 2 nonfree module (SIFT/SURF) = YES, aruco = ${ARUCO_STR} (License: Non commercial)")
|
||||
ELSE()
|
||||
MESSAGE(STATUS " *With OpenCV 2 nonfree module (SIFT/SURF) = NO (not found, License: BSD)")
|
||||
MESSAGE(STATUS " *With OpenCV 2 nonfree module (SIFT/SURF) = NO, aruco = ${ARUCO_STR} (not found, License: BSD)")
|
||||
ENDIF()
|
||||
ELSE()
|
||||
IF(OPENCV_XFEATURES2D_FOUND)
|
||||
IF(NONFREE STREQUAL "//")
|
||||
IF((OpenCV_VERSION_MAJOR LESS 4) OR ((OpenCV_VERSION_MAJOR EQUAL 4) AND (OpenCV_VERSION_MINOR LESS 5)))
|
||||
MESSAGE(STATUS " *With OpenCV ${OpenCV_VERSION} xfeatures2d = YES, nonfree = NO (License: BSD)")
|
||||
MESSAGE(STATUS " *With OpenCV ${OpenCV_VERSION} xfeatures2d = YES, nonfree = NO, aruco = ${ARUCO_STR} (License: BSD)")
|
||||
ELSE()
|
||||
MESSAGE(STATUS " *With OpenCV ${OpenCV_VERSION} xfeatures2d = YES, nonfree = NO (License: Apache 2)")
|
||||
MESSAGE(STATUS " *With OpenCV ${OpenCV_VERSION} xfeatures2d = YES, nonfree = NO, aruco = ${ARUCO_STR} (License: Apache 2)")
|
||||
ENDIF()
|
||||
ELSE()
|
||||
MESSAGE(STATUS " *With OpenCV ${OpenCV_VERSION} xfeatures2d = YES, nonfree = YES (License: Non commercial)")
|
||||
MESSAGE(STATUS " *With OpenCV ${OpenCV_VERSION} xfeatures2d = YES, nonfree = YES, aruco = ${ARUCO_STR} (License: Non commercial)")
|
||||
ENDIF()
|
||||
ELSE()
|
||||
IF((OpenCV_VERSION_MAJOR LESS 4) OR ((OpenCV_VERSION_MAJOR EQUAL 4) AND (OpenCV_VERSION_MINOR LESS 5)))
|
||||
MESSAGE(STATUS " *With OpenCV ${OpenCV_VERSION} xfeatures2d = NO, nonfree = NO (License: BSD)")
|
||||
MESSAGE(STATUS " *With OpenCV ${OpenCV_VERSION} xfeatures2d = NO, nonfree = NO, aruco = ${ARUCO_STR} (License: BSD)")
|
||||
ELSE()
|
||||
MESSAGE(STATUS " *With OpenCV ${OpenCV_VERSION} xfeatures2d = NO, nonfree = NO (License: Apache 2)")
|
||||
MESSAGE(STATUS " *With OpenCV ${OpenCV_VERSION} xfeatures2d = NO, nonfree = NO, aruco = ${ARUCO_STR} (License: Apache 2)")
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
@@ -1453,15 +1557,26 @@ MESSAGE(STATUS " With ORB OcTree = NO (WITH_ORB_OCTREE=OFF)")
|
||||
ENDIF()
|
||||
|
||||
IF(TORCH_FOUND)
|
||||
MESSAGE(STATUS " With SuperPoint = YES (License: GPLv3) libtorch=${Torch_VERSION}")
|
||||
MESSAGE(STATUS " With SuperPoint = YES (License: GPLv3) libtorch=${Torch_VERSION}")
|
||||
ELSEIF(NOT WITH_TORCH)
|
||||
MESSAGE(STATUS " With SuperPoint = NO (WITH_TORCH=OFF)")
|
||||
MESSAGE(STATUS " With SuperPoint = NO (WITH_TORCH=OFF)")
|
||||
ELSE()
|
||||
MESSAGE(STATUS " With SuperPoint = NO (libtorch not found)")
|
||||
MESSAGE(STATUS " With SuperPoint = NO (libtorch not found)")
|
||||
ENDIF()
|
||||
|
||||
IF(TORCH_FOUND AND WITH_PYTHON AND Python3_FOUND)
|
||||
MESSAGE(STATUS " With Superpoint Rpautrat = YES (Liscense: MIT) libtorch=${Torch_VERSION}")
|
||||
ELSEIF(NOT WITH_TORCH)
|
||||
MESSAGE(STATUS " With Superpoint Rpautrat = NO (WITH_TORCH=OFF)")
|
||||
ELSEIF(NOT WITH_PYTHON)
|
||||
MESSAGE(STATUS " With Superpoint Rpautrat = NO (WITH_PYTHON=OFF)")
|
||||
ELSE()
|
||||
MESSAGE(STATUS " Wtih Superpoint Rpautrat = NO (libtorch and/or python3 not found)")
|
||||
ENDIF()
|
||||
|
||||
|
||||
IF(WITH_PYTHON AND Python3_FOUND)
|
||||
MESSAGE(STATUS " With Python${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR} = YES (License: PSF)")
|
||||
MESSAGE(STATUS " With Python${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR} = YES (License: PSF)")
|
||||
ELSEIF(NOT WITH_PYTHON)
|
||||
MESSAGE(STATUS " With Python3 = NO (WITH_PYTHON=OFF)")
|
||||
ELSE()
|
||||
@@ -1482,6 +1597,14 @@ ELSE()
|
||||
MESSAGE(STATUS " With FastCV = NO (FastCV not found)")
|
||||
ENDIF()
|
||||
|
||||
IF(apriltag_FOUND)
|
||||
MESSAGE(STATUS " With AprilTag ${apriltag_VERSION} = YES (aruco=${WITH_APRILTAG_ARUCO}) (License: BSD 2-Clause License)")
|
||||
ELSEIF(NOT WITH_APRILTAG)
|
||||
MESSAGE(STATUS " With AprilTag = NO (WITH_APRILTAG=OFF)")
|
||||
ELSE()
|
||||
MESSAGE(STATUS " With AprilTag = NO (apriltag not found)")
|
||||
ENDIF()
|
||||
|
||||
IF(PDAL_FOUND)
|
||||
MESSAGE(STATUS " With PDAL ${PDAL_VERSION} = YES (License: BSD)")
|
||||
ELSEIF(NOT WITH_PDAL)
|
||||
@@ -1591,7 +1714,11 @@ MESSAGE(STATUS " With Open3D = NO (Open3D not found)")
|
||||
ENDIF()
|
||||
|
||||
IF(opengv_FOUND AND WITH_OPENGV)
|
||||
IF(opengv_VERSION STREQUAL "internal")
|
||||
MESSAGE(STATUS " With OpenGV (internal) = YES (License: BSD)")
|
||||
ELSE()
|
||||
MESSAGE(STATUS " With OpenGV ${opengv_VERSION} = YES (License: BSD)")
|
||||
ENDIF()
|
||||
ELSEIF(NOT WITH_OPENGV)
|
||||
MESSAGE(STATUS " With OpenGV = NO (WITH_OPENGV=OFF)")
|
||||
ELSE()
|
||||
@@ -1706,7 +1833,7 @@ ELSE()
|
||||
MESSAGE(STATUS " With FlyCapture2/Triclops = NO (Point Grey SDK not found)")
|
||||
ENDIF()
|
||||
|
||||
IF(ZED_FOUND AND CUDA_FOUND)
|
||||
IF(ZED_FOUND)
|
||||
MESSAGE(STATUS " With ZED = YES")
|
||||
ELSEIF(NOT WITH_ZED)
|
||||
MESSAGE(STATUS " With ZED = NO (WITH_ZED=OFF)")
|
||||
@@ -1798,6 +1925,13 @@ MESSAGE(STATUS " With floam = NO (WITH_FLOAM=OFF)")
|
||||
ELSE()
|
||||
MESSAGE(STATUS " With floam = NO (floam not found)")
|
||||
ENDIF()
|
||||
IF(lio_sam_FOUND)
|
||||
MESSAGE(STATUS " With lio_sam = YES (License: BSD)")
|
||||
ELSEIF(NOT WITH_LIOSAM)
|
||||
MESSAGE(STATUS " With lio_sam = NO (WITH_LIOSAM=OFF)")
|
||||
ELSE()
|
||||
MESSAGE(STATUS " With lio_sam = NO (lio_sam not found)")
|
||||
ENDIF()
|
||||
|
||||
IF(libfovis_FOUND)
|
||||
MESSAGE(STATUS " With libfovis = YES (License: GPLv2)")
|
||||
|
||||
@@ -7,7 +7,7 @@ rtabmap
|
||||
[![Downloads][downloads-image]][downloads]
|
||||
[![License][license-image]][license]
|
||||
|
||||
[release-image]: https://img.shields.io/badge/release-0.21.4-green.svg?style=flat
|
||||
[release-image]: https://img.shields.io/badge/release-0.23.1-green.svg?style=flat
|
||||
[releases]: https://github.com/introlab/rtabmap/releases
|
||||
|
||||
[downloads-image]: https://img.shields.io/github/downloads/introlab/rtabmap/total?label=downloads
|
||||
@@ -35,13 +35,12 @@ This project is supported by [IntRoLab - Intelligent / Interactive / Integrated
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Linux</td>
|
||||
<td><a href="https://github.com/introlab/rtabmap/actions/workflows/cmake.yml"><img src="https://github.com/introlab/rtabmap/actions/workflows/cmake.yml/badge.svg" alt="Build Status"/> <br> <a href="https://github.com/introlab/rtabmap/actions/workflows/cmake-ros.yml"><img src="https://github.com/introlab/rtabmap/actions/workflows/cmake-ros.yml/badge.svg" alt="Build Status"/> <br> <a href="https://github.com/introlab/rtabmap/actions/workflows/docker.yml"><img src="https://github.com/introlab/rtabmap/actions/workflows/docker.yml/badge.svg" alt="Build Status"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Windows</td>
|
||||
<td><a href="https://ci.appveyor.com/project/matlabbe/rtabmap/branch/master"><img src="https://ci.appveyor.com/api/projects/status/hr73xspix9oqa26h/branch/master?svg=true" alt="Build Status"/>
|
||||
<td>
|
||||
<a href="https://github.com/introlab/rtabmap/actions/workflows/cmake-linux.yml"><img src="https://github.com/introlab/rtabmap/actions/workflows/cmake-linux.yml/badge.svg" alt="CMake Linux Build Status"/> <br>
|
||||
<a href="https://github.com/introlab/rtabmap/actions/workflows/cmake-windows.yml"><img src="https://github.com/introlab/rtabmap/actions/workflows/cmake-windows.yml/badge.svg" alt="CMake Windows Build Status"/> <br>
|
||||
<a href="https://github.com/introlab/rtabmap/actions/workflows/cmake-macos.yml"><img src="https://github.com/introlab/rtabmap/actions/workflows/cmake-macos.yml/badge.svg" alt="CMake MaCOS Build Status"/> <br>
|
||||
<a href="https://github.com/introlab/rtabmap/actions/workflows/cmake-ros.yml"><img src="https://github.com/introlab/rtabmap/actions/workflows/cmake-ros.yml/badge.svg" alt="CMake ROS Build Status"/> <br>
|
||||
<a href="https://github.com/introlab/rtabmap/actions/workflows/docker.yml"><img src="https://github.com/introlab/rtabmap/actions/workflows/docker.yml/badge.svg" alt="Docker Build Status"/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@@ -59,7 +58,7 @@ This project is supported by [IntRoLab - Intelligent / Interactive / Integrated
|
||||
<td><a href="http://build.ros.org/job/Nbin_ufv8_uFv8__rtabmap__ubuntu_focal_arm64__binary/"><img src="http://build.ros.org/buildStatus/icon?job=Nbin_ufv8_uFv8__rtabmap__ubuntu_focal_arm64__binary" alt="Build Status"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="3">ROS 2</td>
|
||||
<td rowspan="4">ROS 2</td>
|
||||
<td>Humble</td>
|
||||
<td><a href="http://build.ros2.org/job/Hbin_uJ64__rtabmap__ubuntu_jammy_amd64__binary/"><img src="http://build.ros2.org/buildStatus/icon?job=Hbin_uJ64__rtabmap__ubuntu_jammy_amd64__binary" alt="Build Status"/></td>
|
||||
</tr>
|
||||
@@ -67,6 +66,10 @@ This project is supported by [IntRoLab - Intelligent / Interactive / Integrated
|
||||
<td>Jazzy</td>
|
||||
<td><a href="http://build.ros2.org/job/Jbin_uN64__rtabmap__ubuntu_noble_amd64__binary/"><img src="http://build.ros2.org/buildStatus/icon?job=Jbin_uN64__rtabmap__ubuntu_noble_amd64__binary" alt="Build Status"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Kilted</td>
|
||||
<td><a href="http://build.ros2.org/job/Kbin_uN64__rtabmap__ubuntu_noble_amd64__binary/"><img src="http://build.ros2.org/buildStatus/icon?job=Kbin_uN64__rtabmap__ubuntu_noble_amd64__binary" alt="Build Status"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Rolling</td>
|
||||
<td><a href="http://build.ros2.org/job/Rbin_uJ64__rtabmap__ubuntu_jammy_amd64__binary/"><img src="http://build.ros2.org/buildStatus/icon?job=Rbin_uJ64__rtabmap__ubuntu_jammy_amd64__binary" alt="Build Status"/></td>
|
||||
|
||||
+4
-1
@@ -35,7 +35,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#define RTABMAP_VERSION_MINOR @PROJECT_VERSION_MINOR@
|
||||
#define RTABMAP_VERSION_PATCH @PROJECT_VERSION_PATCH@
|
||||
|
||||
#define RTABMAP_VERSION_COMPARE(major, minor, patch) (major>=@PROJECT_VERSION_MAJOR@ || (major==@PROJECT_VERSION_MAJOR@ && minor>=@PROJECT_VERSION_MINOR@) || (major==@PROJECT_VERSION_MAJOR@ && minor==@PROJECT_VERSION_MINOR@ && patch >=@PROJECT_VERSION_PATCH@))
|
||||
#define RTABMAP_VERSION_COMPARE(OP, MAJOR, MINOR, PATCH) (RTABMAP_VERSION_MAJOR*10000+RTABMAP_VERSION_MINOR*100+RTABMAP_VERSION_PATCH OP MAJOR*10000+MINOR*100+PATCH)
|
||||
|
||||
@NONFREE@#define RTABMAP_NONFREE
|
||||
@TORO@#define RTABMAP_TORO
|
||||
@@ -63,6 +63,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
@CUDASIFT@#define RTABMAP_CUDASIFT
|
||||
@LOAM@#define RTABMAP_LOAM
|
||||
@FLOAM@#define RTABMAP_FLOAM
|
||||
@LIOSAM@#define RTABMAP_LIOSAM
|
||||
@DC1394@#define RTABMAP_DC1394
|
||||
@FLYCAPTURE2@#define RTABMAP_FLYCAPTURE2
|
||||
@ZED@#define RTABMAP_ZED
|
||||
@@ -92,6 +93,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
@TORCH@#define RTABMAP_TORCH
|
||||
@PYTHON@#define RTABMAP_PYTHON
|
||||
@MADGWICK@#define RTABMAP_MADGWICK
|
||||
@APRILTAG@#define RTABMAP_APRILTAG
|
||||
@APRILTAG_ARUCO@#define RTABMAP_APRILTAG_WITH_ARUCO
|
||||
|
||||
#include <pcl/pcl_config.h>
|
||||
|
||||
|
||||
@@ -3000,7 +3000,7 @@ void RTABMapApp::setTrajectoryMode(bool enabled)
|
||||
void RTABMapApp::setGraphOptimization(bool enabled)
|
||||
{
|
||||
graphOptimization_ = enabled;
|
||||
if((sensorCaptureThread_ == 0) && rtabmap_ && rtabmap_->getMemory()->getLastWorkingSignature()!=0)
|
||||
if((sensorCaptureThread_ == 0) && rtabmap_ && rtabmap_->getMemory()->getLastWorkingSignature(false)!=0)
|
||||
{
|
||||
std::map<int, rtabmap::Transform> poses;
|
||||
std::multimap<int, rtabmap::Link> links;
|
||||
|
||||
@@ -1078,7 +1078,7 @@
|
||||
"\"$(SRCROOT)/RTABMapApp/Libraries/include\"",
|
||||
"\"$(SRCROOT)/RTABMapApp/Libraries/include/eigen3\"",
|
||||
"\"$(SRCROOT)/RTABMapApp/Libraries/include/pcl-1.15\"",
|
||||
"\"$(SRCROOT)/RTABMapApp/Libraries/include/rtabmap-0.22\"",
|
||||
"\"$(SRCROOT)/RTABMapApp/Libraries/include/rtabmap-0.23\"",
|
||||
"\"$(SRCROOT)/../android/jni/tango-gl/include\"",
|
||||
"\"$(SRCROOT)/../android/jni/third-party/include\"",
|
||||
"\"$(SRCROOT)/RTABMapApp/Libraries/lib/vtk.framework/Headers\"",
|
||||
@@ -1139,7 +1139,7 @@
|
||||
"\"$(SRCROOT)/RTABMapApp/Libraries/include\"",
|
||||
"\"$(SRCROOT)/RTABMapApp/Libraries/include/eigen3\"",
|
||||
"\"$(SRCROOT)/RTABMapApp/Libraries/include/pcl-1.15\"",
|
||||
"\"$(SRCROOT)/RTABMapApp/Libraries/include/rtabmap-0.22\"",
|
||||
"\"$(SRCROOT)/RTABMapApp/Libraries/include/rtabmap-0.23\"",
|
||||
"\"$(SRCROOT)/../android/jni/tango-gl/include\"",
|
||||
"\"$(SRCROOT)/../android/jni/third-party/include\"",
|
||||
"\"$(SRCROOT)/RTABMapApp/Libraries/lib/vtk.framework/Headers\"",
|
||||
|
||||
@@ -288,6 +288,6 @@ cmake -DANDROID_PREBUILD=ON ../../../../..
|
||||
cmake --build . --config Release
|
||||
mkdir -p ios
|
||||
cd ios
|
||||
cmake -G Xcode -DCMAKE_SYSTEM_NAME=iOS -DCMAKE_OSX_ARCHITECTURES=arm64 -DCMAKE_OSX_SYSROOT=$sysroot -DBUILD_SHARED_LIBS=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_DEPLOYMENT_TARGET=12.0 -DCMAKE_INSTALL_PREFIX=$prefix -DCMAKE_FIND_ROOT_PATH=$prefix -DWITH_QT=OFF -DBUILD_APP=OFF -DBUILD_TOOLS=OFF -DWITH_TORO=OFF -DWITH_VERTIGO=OFF -DWITH_MADGWICK=OFF -DWITH_ORB_OCTREE=ON -DBUILD_EXAMPLES=OFF -DWITH_LIBLAS=ON ../../../../../..
|
||||
cmake -G Xcode -DCMAKE_SYSTEM_NAME=iOS -DCMAKE_OSX_ARCHITECTURES=arm64 -DCMAKE_OSX_SYSROOT=$sysroot -DBUILD_SHARED_LIBS=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_DEPLOYMENT_TARGET=12.0 -DCMAKE_INSTALL_PREFIX=$prefix -DCMAKE_FIND_ROOT_PATH=$prefix -DWITH_QT=OFF -DBUILD_APP=OFF -DBUILD_TOOLS=OFF -DWITH_TORO=OFF -DWITH_VERTIGO=OFF -DWITH_MADGWICK=OFF -DWITH_ORB_OCTREE=ON -DBUILD_EXAMPLES=OFF -DWITH_LIBLAS=ON -DWITH_OPENGV=OFF ../../../../../..
|
||||
cmake --build . --config Release
|
||||
cmake --build . --config Release --target install
|
||||
|
||||
+120
-19
@@ -95,9 +95,11 @@ IF(BUILD_AS_BUNDLE AND (APPLE OR WIN32))
|
||||
DESTINATION ${thirdparty_dest_dir}
|
||||
COMPONENT runtime
|
||||
REGEX ".*pdb" EXCLUDE)
|
||||
INSTALL(FILES "${OpenNI2_BIN_DIR}/OpenNI.ini"
|
||||
IF(NOT WIN32)
|
||||
INSTALL(FILES "${OpenNI2_BIN_DIR}/OpenNI.ini"
|
||||
DESTINATION ${thirdparty_dest_dir}
|
||||
COMPONENT runtime)
|
||||
ENDIF()
|
||||
ENDIF(OpenNI2_FOUND)
|
||||
|
||||
IF(k4a_FOUND)
|
||||
@@ -138,23 +140,107 @@ IF(BUILD_AS_BUNDLE AND (APPLE OR WIN32))
|
||||
ENDIF(OrbbecSDK_FOUND)
|
||||
|
||||
IF(Torch_FOUND)
|
||||
# Install needed cudnn_ops_infer64_8.dll and cudnn_cnn_infer64_8.dll
|
||||
# TODO: should be a more general way to include them if version is different
|
||||
# Install needed cudnn dlls
|
||||
IF(WIN32 AND CUDA_FOUND)
|
||||
find_file(CUDNN_OPS_DLL NAMES cudnn_ops_infer64_8.dll)
|
||||
find_file(CUDNN_CNN_DLL NAMES cudnn_cnn_infer64_8.dll)
|
||||
IF(CUDNN_OPS_DLL AND CUDNN_CNN_DLL)
|
||||
MESSAGE(STATUS "Found ${CUDNN_OPS_DLL}")
|
||||
MESSAGE(STATUS "Found ${CUDNN_CNN_DLL}")
|
||||
INSTALL(FILES ${CUDNN_OPS_DLL} ${CUDNN_CNN_DLL}
|
||||
DESTINATION ${thirdparty_dest_dir}
|
||||
COMPONENT runtime)
|
||||
ELSE()
|
||||
MESSAGE(AUTHOR_WARNING "Using Torch with CUDA, but cudnn_ops_infer64_8.dll and cudnn_cnn_infer64_8.dll are not found on the PATH, so it won't be added to package.")
|
||||
find_path(cuDNN_BIN_DIR NAMES cudnn.dll cudnn64.dll cudnn64_9.dll)
|
||||
IF(NOT cuDNN_BIN_DIR)
|
||||
MESSAGE(FATAL_ERROR "cudnn dlls not found! Make sure the dlls are in a directory on your PATH.")
|
||||
ENDIF(NOT cuDNN_BIN_DIR)
|
||||
MESSAGE(STATUS "cuDNN_BIN_DIR = ${cuDNN_BIN_DIR}")
|
||||
file(GLOB CUDNN_DLLS "${cuDNN_BIN_DIR}/cudnn*.dll")
|
||||
IF(CUDNN_DLLS)
|
||||
MESSAGE(STATUS "Found cuDNN DLLs: ${CUDNN_DLLS}")
|
||||
INSTALL(FILES ${CUDNN_DLLS}
|
||||
DESTINATION ${thirdparty_dest_dir}
|
||||
COMPONENT runtime)
|
||||
ENDIF()
|
||||
ENDIF(WIN32 AND CUDA_FOUND)
|
||||
ENDIF(Torch_FOUND)
|
||||
|
||||
set(python_pyd_dir "")
|
||||
IF(Python3_FOUND)
|
||||
# bundle python3
|
||||
IF(WIN32)
|
||||
set(python_pyd_dir "bin/Lib/site-packages")
|
||||
set(PYTHON_ZIP_NAME "python${Python3_VERSION_MAJOR}${Python3_VERSION_MINOR}.zip")
|
||||
get_filename_component(PYTHON_ROOT "${Python3_EXECUTABLE}" DIRECTORY)
|
||||
file(TO_CMAKE_PATH "${Python3_STDLIB}" SANITIZED_STDLIB)
|
||||
|
||||
MESSAGE(STATUS "Python3_EXECUTABLE=${Python3_EXECUTABLE}")
|
||||
MESSAGE(STATUS "Python3_STDLIB=${SANITIZED_STDLIB}")
|
||||
|
||||
install(FILES "${Python3_EXECUTABLE}" DESTINATION bin COMPONENT runtime)
|
||||
|
||||
# when using python-opencv, it expects python3.dll, not python312.dll
|
||||
get_filename_component(VCPKG_TRIPLET_ROOT "${PYTHON_TOOLS_DIR}/../../" ABSOLUTE)
|
||||
set(VCPKG_BIN_DIR "${VCPKG_TRIPLET_ROOT}/bin")
|
||||
find_file(PYTHON3_STABLE_DLL
|
||||
NAMES python3.dll
|
||||
PATHS "${VCPKG_BIN_DIR}"
|
||||
NO_DEFAULT_PATH
|
||||
)
|
||||
if(PYTHON3_STABLE_DLL)
|
||||
message(STATUS "Found python3.dll at: ${PYTHON3_STABLE_DLL}")
|
||||
install(FILES "${PYTHON3_STABLE_DLL}" DESTINATION bin COMPONENT runtime)
|
||||
endif()
|
||||
|
||||
# install python Lib in python312.zip (without site-packages, which is installed separatly afterwards)
|
||||
file(TO_CMAKE_PATH "${Python3_STDLIB}" SANITIZED_STDLIB)
|
||||
file(GLOB LIB_CONTENTS RELATIVE "${SANITIZED_STDLIB}" "${SANITIZED_STDLIB}/*")
|
||||
list(REMOVE_ITEM LIB_CONTENTS "site-packages")
|
||||
install(CODE "
|
||||
execute_process(
|
||||
COMMAND \"${CMAKE_COMMAND}\" -E tar cf \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/bin/${PYTHON_ZIP_NAME}\" --format=zip -- ${LIB_CONTENTS}
|
||||
WORKING_DIRECTORY \"${SANITIZED_STDLIB}\"
|
||||
)
|
||||
" COMPONENT runtime)
|
||||
set(PYTHON_SITE_PACKAGES "${SANITIZED_STDLIB}/site-packages")
|
||||
|
||||
install(DIRECTORY "${PYTHON_SITE_PACKAGES}/"
|
||||
DESTINATION "${thirdparty_dest_dir}/Lib/site-packages"
|
||||
COMPONENT runtime
|
||||
PATTERN "*.exe" EXCLUDE
|
||||
PATTERN "*.lib" EXCLUDE
|
||||
PATTERN "*.hpp" EXCLUDE
|
||||
PATTERN "*.h" EXCLUDE
|
||||
PATTERN "*/torch/*" EXCLUDE
|
||||
)
|
||||
if(EXISTS "${PYTHON_SITE_PACKAGES}/torch")
|
||||
install(DIRECTORY "${PYTHON_SITE_PACKAGES}/torch"
|
||||
DESTINATION "${thirdparty_dest_dir}/Lib/site-packages/"
|
||||
COMPONENT runtime
|
||||
PATTERN "*.exe" EXCLUDE
|
||||
PATTERN "*.lib" EXCLUDE
|
||||
PATTERN "*.hpp" EXCLUDE
|
||||
PATTERN "*.h" EXCLUDE
|
||||
PATTERN "*.dll" EXCLUDE
|
||||
)
|
||||
file(GLOB_RECURSE PY_DLL_FILES "${PYTHON_SITE_PACKAGES}/torch/*.dll")
|
||||
if(PY_DLL_FILES)
|
||||
install(FILES ${PY_DLL_FILES} DESTINATION bin COMPONENT runtime)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# install python DDLs
|
||||
file(GLOB_RECURSE PY_DLL_FILES \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${python_pyd_dir}/*.dll\")
|
||||
install(DIRECTORY "${PYTHON_ROOT}/DLLs"
|
||||
DESTINATION "${thirdparty_dest_dir}/"
|
||||
COMPONENT runtime
|
||||
FILES_MATCHING
|
||||
PATTERN "*.dll"
|
||||
PATTERN "*.pyd"
|
||||
)
|
||||
|
||||
# install our python scripts in share for convenience
|
||||
install(DIRECTORY "${PROJECT_SOURCE_DIR}/corelib/src/python/"
|
||||
DESTINATION share
|
||||
COMPONENT runtime
|
||||
FILES_MATCHING
|
||||
PATTERN "*.py"
|
||||
)
|
||||
ENDIF(WIN32)
|
||||
ENDIF(Python3_FOUND)
|
||||
|
||||
IF(Qt6_FOUND)
|
||||
# Reference: https://doc-snapshots.qt.io/qt6-6.4/qt-deploy-runtime-dependencies.html
|
||||
# The following script must only be executed at install time
|
||||
@@ -248,8 +334,8 @@ IF(BUILD_AS_BUNDLE AND (APPLE OR WIN32))
|
||||
SET(DIRS "${QT_LIBRARY_DIRS}" "\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/lib")
|
||||
IF(APPLE)
|
||||
SET(DIRS ${DIRS} /usr/local /usr/local/lib /opt/homebrew /opt/homebrew/lib /opt/homebrew/lib/gcc/current)
|
||||
ENDIF(APPLE)
|
||||
|
||||
ENDIF(APPLE)
|
||||
|
||||
# Now the work of copying dependencies into the bundle/package
|
||||
# The quotes are escaped and variables to use at install time have their $ escaped
|
||||
# An alternative is the do a configure_file() on a script and use install(SCRIPT ...).
|
||||
@@ -257,11 +343,26 @@ IF(BUILD_AS_BUNDLE AND (APPLE OR WIN32))
|
||||
# over.
|
||||
# To find dependencies, cmake use "otool" on Apple and "dumpbin" on Windows (make sure you have one of them).
|
||||
install(CODE "
|
||||
file(GLOB_RECURSE QTPLUGINS \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${plugin_dest_dir}/*${CMAKE_SHARED_LIBRARY_SUFFIX}\")
|
||||
# Glob Qt Plugins
|
||||
file(GLOB_RECURSE ALL_LIBS \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${plugin_dest_dir}/*${CMAKE_SHARED_LIBRARY_SUFFIX}\")
|
||||
|
||||
if(NOT \"${python_pyd_dir}\" STREQUAL \"\")
|
||||
file(GLOB_RECURSE PYD_FILES \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${python_pyd_dir}/*.pyd\")
|
||||
if(PYD_FILES)
|
||||
list(APPEND ALL_LIBS \${PYD_FILES})
|
||||
endif()
|
||||
if(WIN32)
|
||||
file(GLOB_RECURSE DLL_FILES \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${python_pyd_dir}/torch/*.dll\")
|
||||
if(DLL_FILES)
|
||||
list(APPEND ALL_LIBS \${DLL_FILES})
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(BU_CHMOD_BUNDLE_ITEMS ON)
|
||||
include(\"BundleUtilities\")
|
||||
fixup_bundle(\"${APPS}\" \"\${QTPLUGINS}\" \"${DIRS}\")
|
||||
" COMPONENT runtime)
|
||||
|
||||
fixup_bundle(\"${APPS}\" \"\${ALL_LIBS}\" \"${DIRS}\")
|
||||
" COMPONENT runtime)
|
||||
|
||||
ENDIF(BUILD_AS_BUNDLE AND (APPLE OR WIN32))
|
||||
|
||||
|
||||
@@ -0,0 +1,227 @@
|
||||
@echo off
|
||||
setlocal enabledelayedexpansion
|
||||
|
||||
:: --- CONFIGURATION ---
|
||||
set "VCPKG_ROOT=%~dp0vcpkg"
|
||||
set "EXPORT_DIR=%~dp0vcpkg_binaries"
|
||||
set "TRIPLET=x64-windows-release"
|
||||
set "SEVENZIP_EXE=C:\Program Files\7-Zip\7z.exe"
|
||||
|
||||
set "VCPKG_JSON=%~dp0vcpkg.json"
|
||||
for /f "usebackq tokens=*" %%a in (`powershell -NoProfile -Command "(Get-Content '%VCPKG_JSON%' -Raw | ConvertFrom-Json).'builtin-baseline'"` ) do set "VCPKG_COMMIT=%%a"
|
||||
echo [+] Detected VCPKG baseline commit: %VCPKG_COMMIT%
|
||||
|
||||
if "%VCPKG_COMMIT%"=="" (
|
||||
echo [X] Error: Could not find 'builtin-baseline' in %VCPKG_JSON%
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
set VCPKG_COMMIT_SHORT=%VCPKG_COMMIT:~0,8%
|
||||
|
||||
:: 1. Setup Local vcpkg
|
||||
if not exist "%VCPKG_ROOT%" (
|
||||
echo [+] Local vcpkg not found. Cloning...
|
||||
git clone https://github.com/microsoft/vcpkg.git "%VCPKG_ROOT%"
|
||||
)
|
||||
pushd "%VCPKG_ROOT%"
|
||||
git checkout %VCPKG_COMMIT%
|
||||
call .\bootstrap-vcpkg.bat
|
||||
popd
|
||||
|
||||
:: 2. Install vcpkg dependencies
|
||||
echo [+] Installing dependencies via vcpkg manifest...
|
||||
"%VCPKG_ROOT%\vcpkg.exe" install ^
|
||||
--triplet=%TRIPLET% ^
|
||||
--host-triplet=%TRIPLET% ^
|
||||
--clean-after-build ^
|
||||
--x-feature=tools ^
|
||||
--x-feature=k4w2 ^
|
||||
--x-feature=octomap ^
|
||||
--x-feature=openmp ^
|
||||
--x-feature=realsense2 ^
|
||||
--x-feature=openni2 ^
|
||||
--x-feature=gtsam-deps ^
|
||||
--x-feature=python ^
|
||||
--x-feature=libpointmatcher-deps || exit /b !errorlevel!
|
||||
|
||||
:: 3. Export
|
||||
echo [+] Exporting built binaries to raw folder...
|
||||
set "VS_LOCATOR=%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe"
|
||||
for /f "usebackq tokens=*" %%i in (`"%VS_LOCATOR%" -latest -property catalog_productLineVersion`) do set VS_YEAR=vs%%i
|
||||
set TARGET_NAME=vcpkg-export-%VCPKG_COMMIT_SHORT%-x64-%VS_YEAR%
|
||||
set TARGET_FULL_PATH=%EXPORT_DIR%\%TARGET_NAME%
|
||||
if exist "%TARGET_FULL_PATH%" rd /s /q "%TARGET_FULL_PATH%"
|
||||
"%VCPKG_ROOT%\vcpkg.exe" export --raw --output-dir="%EXPORT_DIR%" --triplet=%TRIPLET% || exit /b !errorlevel!
|
||||
|
||||
:: Find the actual exported folder name (it usually contains a date/hash)
|
||||
for /d %%i in ("%EXPORT_DIR%\vcpkg-export-20??????-??????") do set "FINAL_EXPORT_PATH=%%i"
|
||||
|
||||
echo [+] Rename folder %FINAL_EXPORT_PATH% to %TARGET_NAME%
|
||||
ren "%FINAL_EXPORT_PATH%" "%TARGET_NAME%" || exit /b !errorlevel!
|
||||
set "FINAL_EXPORT_PATH=%TARGET_FULL_PATH%"
|
||||
|
||||
echo [+] Add numpy...
|
||||
:: We install numpy<2 to be compatible with SuperPoint and SuperGlue scripts
|
||||
%FINAL_EXPORT_PATH%/installed/%TRIPLET%/tools/python3/python.exe -m ensurepip --upgrade || exit /b %errorlevel%
|
||||
%FINAL_EXPORT_PATH%/installed/%TRIPLET%/tools/python3/python.exe -m pip install --upgrade pip || exit /b !errorlevel!
|
||||
%FINAL_EXPORT_PATH%/installed/%TRIPLET%/tools/python3/python.exe -m pip install "numpy<2" || exit /b !errorlevel!
|
||||
|
||||
:: 4. Other dependencies not in vcpkg
|
||||
:: libnabo
|
||||
echo [+] Building libnabo...
|
||||
if not exist libnabo (
|
||||
echo [+] Downloading...
|
||||
git clone https://github.com/ethz-asl/libnabo.git
|
||||
cd libnabo
|
||||
:: Jan 27, 2022
|
||||
git checkout c925c47
|
||||
git apply ../patches/libnabo_c925c47.patch
|
||||
cd ..
|
||||
)
|
||||
cd libnabo
|
||||
cmake -S . -B build -GNinja ^
|
||||
-DVCPKG_MANIFEST_INSTALL=OFF ^
|
||||
-DVCPKG_TARGET_TRIPLET=x64-windows-release ^
|
||||
-DVCPKG_INSTALLED_DIR="%FINAL_EXPORT_PATH%\installed" ^
|
||||
-DCMAKE_TOOLCHAIN_FILE="%FINAL_EXPORT_PATH%\scripts\buildsystems\vcpkg.cmake" ^
|
||||
-DCMAKE_INSTALL_PREFIX="%FINAL_EXPORT_PATH%\installed\%TRIPLET%" ^
|
||||
-DCMAKE_BUILD_TYPE=Release ^
|
||||
-DSHARED_LIBS=FALSE ^
|
||||
-DLIBNABO_BUILD_DOXYGEN=OFF ^
|
||||
-DLIBNABO_BUILD_EXAMPLES=OFF ^
|
||||
-DLIBNABO_BUILD_PYTHON=OFF ^
|
||||
-DLIBNABO_BUILD_TESTS=OFF || exit /b !errorlevel!
|
||||
cmake --build build --config Release --target install || exit /b !errorlevel!
|
||||
cd ..
|
||||
|
||||
:: libpointmatcher
|
||||
echo [+] Building libpointmatcher...
|
||||
if not exist libpointmatcher (
|
||||
echo [+] Downloading and applying patch...
|
||||
git clone https://github.com/ethz-asl/libpointmatcher.git
|
||||
cd libpointmatcher
|
||||
:: Mar 17, 2023
|
||||
git checkout 7dc58e5
|
||||
git apply ../patches/pointmatcher_7dc58e5.patch
|
||||
cd ..
|
||||
)
|
||||
cd libpointmatcher
|
||||
cmake -S . -B build -GNinja ^
|
||||
-DVCPKG_MANIFEST_INSTALL=OFF ^
|
||||
-DVCPKG_TARGET_TRIPLET=x64-windows-release ^
|
||||
-DVCPKG_INSTALLED_DIR="%FINAL_EXPORT_PATH%\installed" ^
|
||||
-DCMAKE_TOOLCHAIN_FILE="%FINAL_EXPORT_PATH%\scripts\buildsystems\vcpkg.cmake" ^
|
||||
-DCMAKE_INSTALL_PREFIX="%FINAL_EXPORT_PATH%\installed\%TRIPLET%" ^
|
||||
-DCMAKE_BUILD_TYPE=Release ^
|
||||
-DBUILD_TESTS=OFF ^
|
||||
-DBUILD_SHARED_LIBS=ON ^
|
||||
-DPOINTMATCHER_BUILD_EVALUATIONS=OFF ^
|
||||
-DPOINTMATCHER_BUILD_EXAMPLES=OFF ^
|
||||
-DCMAKE_CXX_FLAGS="-DBOOST_TIMER_ENABLE_DEPRECATED /EHsc -DBOOST_EXCEPTION_DISABLE" || exit /b !errorlevel!
|
||||
cmake --build build --config Release --target install || exit /b !errorlevel!
|
||||
cd ..
|
||||
|
||||
:: We remove the files in the top-level CMake directory to force use of share/libpointmatcher/cmake
|
||||
if exist "%FINAL_EXPORT_PATH%\installed\%TRIPLET%\CMake\" (
|
||||
rd /s /q "%FINAL_EXPORT_PATH%\installed\%TRIPLET%\CMake"
|
||||
)
|
||||
|
||||
:: gtsam
|
||||
echo [+] Building gtsam...
|
||||
if not exist gtsam (
|
||||
echo [+] Downloading and applying patch...
|
||||
git clone https://github.com/borglab/gtsam.git
|
||||
cd gtsam
|
||||
:: June 18, 2025
|
||||
git checkout 4.3a0-ros
|
||||
git cherry-pick 18af4e6
|
||||
git apply ../patches/gtsam_4_3a0-ros.patch
|
||||
cd ..
|
||||
)
|
||||
cd gtsam
|
||||
cmake -S . -B build -GNinja ^
|
||||
-DVCPKG_MANIFEST_INSTALL=OFF ^
|
||||
-DVCPKG_TARGET_TRIPLET=x64-windows-release ^
|
||||
-DVCPKG_INSTALLED_DIR="%FINAL_EXPORT_PATH%\installed" ^
|
||||
-DCMAKE_TOOLCHAIN_FILE="%FINAL_EXPORT_PATH%\scripts\buildsystems\vcpkg.cmake" ^
|
||||
-DCMAKE_INSTALL_PREFIX="%FINAL_EXPORT_PATH%\installed\%TRIPLET%" ^
|
||||
-DCMAKE_BUILD_TYPE=Release ^
|
||||
-DGTSAM_BUILD_EXAMPLES_ALWAYS=OFF ^
|
||||
-DGTSAM_BUILD_TESTS=OFF ^
|
||||
-DGTSAM_BUILD_UNSTABLE=OFF ^
|
||||
-DGTSAM_USE_SYSTEM_EIGEN=ON ^
|
||||
-DGTSAM_BUILD_WITH_PRECOMPILED_HEADERS=OFF ^
|
||||
-DGTSAM_UNSTABLE_BUILD_PYTHON=OFF ^
|
||||
-DGTSAM_WITH_EIGEN_MKL=OFF ^
|
||||
-DGTSAM_WITH_EIGEN_MKL_OPENMP=OFF ^
|
||||
-DCMAKE_CXX_FLAGS="-DBOOST_TIMER_ENABLE_DEPRECATED -DBOOST_BIND_GLOBAL_PLACEHOLDERS" || exit /b !errorlevel!
|
||||
cmake --build build --config Release --target install || exit /b !errorlevel!
|
||||
cd ..
|
||||
|
||||
:: opengv
|
||||
echo [+] Building opengv...
|
||||
if not exist opengv (
|
||||
echo [+] Downloading and applying patch...
|
||||
git clone https://github.com/laurentkneip/opengv.git
|
||||
cd opengv
|
||||
:: Aug 6, 2020
|
||||
git checkout 91f4b19c73450833a40e463ad3648aae80b3a7f3
|
||||
git apply ../patches/opengv_91f4b19c.patch
|
||||
cd ..
|
||||
)
|
||||
cd opengv
|
||||
cmake -S . -B build -GNinja ^
|
||||
-DVCPKG_MANIFEST_INSTALL=OFF ^
|
||||
-DVCPKG_TARGET_TRIPLET=x64-windows-release ^
|
||||
-DVCPKG_INSTALLED_DIR="%FINAL_EXPORT_PATH%\installed" ^
|
||||
-DCMAKE_TOOLCHAIN_FILE="%FINAL_EXPORT_PATH%\scripts\buildsystems\vcpkg.cmake" ^
|
||||
-DCMAKE_INSTALL_PREFIX="%FINAL_EXPORT_PATH%\installed\%TRIPLET%" ^
|
||||
-DBUILD_TESTS=OFF ^
|
||||
-DBUILD_SHARED_LIBS=ON || exit /b !errorlevel!
|
||||
cmake --build build --config Release --target install || exit /b !errorlevel!
|
||||
cd ..
|
||||
|
||||
:: 5. ZIP the folder
|
||||
echo [+] Creating final package with 7-Zip...
|
||||
:: Rip off pdb files
|
||||
cd /d "%FINAL_EXPORT_PATH%"
|
||||
del /s /q /f *.pdb >nul 2>&1
|
||||
|
||||
cd ..
|
||||
|
||||
set "FINAL_ZIP=%TARGET_NAME%.7z"
|
||||
|
||||
:: compress contents without the root folder
|
||||
"%SEVENZIP_EXE%" u -t7z -mx9 "%FINAL_ZIP%" "%FINAL_EXPORT_PATH%\*" -up0q0
|
||||
|
||||
if !errorlevel! EQU 0 (
|
||||
echo [!] Success! Package created at %FINAL_ZIP%
|
||||
) else (
|
||||
echo [X] 7-Zip failed with error code !errorlevel!
|
||||
)
|
||||
|
||||
|
||||
:: Example building rtabmap afterwards
|
||||
goto :EndComment
|
||||
|
||||
set VCPKG_UNZIPPED_EXPORT_PATH=%USERPROFILE%\Downloads\vcpkg-export-########-x64-vs2022
|
||||
set PATH=%VCPKG_UNZIPPED_EXPORT_PATH%\installed\x64-windows-release\bin;%PATH%
|
||||
|
||||
cmake -B build -GNinja ^
|
||||
-DCMAKE_BUILD_TYPE=Release ^
|
||||
-DBUILD_AS_BUNDLE=ON -DWITH_PYTHON=ON ^
|
||||
-DWITH_ZED=OFF ^
|
||||
-DVCPKG_MANIFEST_INSTALL=OFF ^
|
||||
-DVCPKG_TARGET_TRIPLET=x64-windows-release ^
|
||||
-DVCPKG_INSTALLED_DIR="%VCPKG_UNZIPPED_EXPORT_PATH%/installed" ^
|
||||
-DCMAKE_TOOLCHAIN_FILE=%VCPKG_UNZIPPED_EXPORT_PATH%/scripts/buildsystems/vcpkg.cmake ^
|
||||
-DGTSAM_DIR=%VCPKG_UNZIPPED_EXPORT_PATH%\installed\x64-windows-release\CMake
|
||||
|
||||
cmake --build build --config Release --target package
|
||||
|
||||
:: To install CPU pytorch inside rtabmap package afterwards.
|
||||
:: Note that python.exe is the one in the bin directory of the package, not the system one.
|
||||
python.exe -m pip install torch torchvision opencv-python-headless "numpy<2"
|
||||
|
||||
:EndComment
|
||||
@@ -0,0 +1,239 @@
|
||||
@echo off
|
||||
setlocal enabledelayedexpansion
|
||||
|
||||
IF NOT DEFINED CUDA_PATH (
|
||||
echo [ERROR] CUDA_PATH is not set.
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
set PATH=%CUDA_PATH%\bin;%PATH%
|
||||
set PATH=%CUDA_PATH%\bin\x64;%PATH%
|
||||
set PATH=%CUDA_PATH%\extras\CUPTI\lib64;%PATH%
|
||||
|
||||
:: CUDA Toolkit should be manually installed on the computer before running this script
|
||||
:: We assume also that cuDNN is merged into CUDA installed directory.
|
||||
where nvcc >nul 2>&1
|
||||
if !errorlevel! neq 0 (
|
||||
echo [ERROR] nvcc was not found in your PATH.
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
for /f "tokens=5" %%a in ('nvcc --version ^| findstr "release"') do (
|
||||
set "RAW_VER=%%a"
|
||||
:: This removes the trailing comma
|
||||
set "CUDA_VER=!RAW_VER:,=!"
|
||||
set "CUDA_VER_SHORT=!CUDA_VER:.=!"
|
||||
)
|
||||
if "!CUDA_VER!"=="" (
|
||||
echo [ERROR] Could not parse CUDA version.
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
echo Installed CUDA Toolkit: %CUDA_VER%
|
||||
|
||||
:: --- CONFIGURATION ---
|
||||
set "VCPKG_ROOT=%~dp0vcpkg"
|
||||
set "EXPORT_DIR=%~dp0vcpkg_binaries"
|
||||
set "TRIPLET=x64-windows-release"
|
||||
set "SEVENZIP_EXE=C:\Program Files\7-Zip\7z.exe"
|
||||
|
||||
set "VCPKG_JSON=%~dp0vcpkg.json"
|
||||
for /f "usebackq tokens=*" %%a in (`powershell -NoProfile -Command "(Get-Content '%VCPKG_JSON%' -Raw | ConvertFrom-Json).'builtin-baseline'"` ) do set "VCPKG_COMMIT=%%a"
|
||||
|
||||
if "%VCPKG_COMMIT%"=="" (
|
||||
echo [X] Error: Could not find 'builtin-baseline' in %VCPKG_JSON%
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
set VCPKG_COMMIT_SHORT=%VCPKG_COMMIT:~0,8%
|
||||
set "VS_LOCATOR=%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe"
|
||||
for /f "usebackq tokens=*" %%i in (`"%VS_LOCATOR%" -latest -property catalog_productLineVersion`) do set VS_YEAR=vs%%i
|
||||
set ORG_TARGET_NAME=vcpkg-export-%VCPKG_COMMIT_SHORT%-x64-%VS_YEAR%
|
||||
set TARGET_NAME=%ORG_TARGET_NAME%-cuda%CUDA_VER_SHORT%
|
||||
set VCPKG_EXPORT_PATH=%EXPORT_DIR%\%ORG_TARGET_NAME%
|
||||
set FINAL_EXPORT_PATH=%EXPORT_DIR%\%TARGET_NAME%
|
||||
|
||||
if not exist "%FINAL_EXPORT_PATH%" (
|
||||
if not exist "%VCPKG_EXPORT_PATH%" (
|
||||
call bundle_windows_deps.bat || exit /b !errorlevel!
|
||||
)
|
||||
echo [+] Copying %VCPKG_EXPORT_PATH% to %FINAL_EXPORT_PATH%
|
||||
xcopy "%VCPKG_EXPORT_PATH%" "%FINAL_EXPORT_PATH%\" /E /I /H /Y /Q || exit /b !errorlevel!
|
||||
echo [+] Remove opencv built by vcpkg
|
||||
rmdir /s /q "%FINAL_EXPORT_PATH%\installed\%TRIPLET%\include\opencv4" || exit /b !errorlevel!
|
||||
rmdir /s /q "%FINAL_EXPORT_PATH%\installed\%TRIPLET%\share\opencv4" || exit /b !errorlevel!
|
||||
rmdir /s /q "%FINAL_EXPORT_PATH%\installed\%TRIPLET%\share\opencv" || exit /b !errorlevel!
|
||||
del "%FINAL_EXPORT_PATH%\installed\%TRIPLET%\bin\opencv*" || exit /b !errorlevel!
|
||||
del "%FINAL_EXPORT_PATH%\installed\%TRIPLET%\lib\opencv*" || exit /b !errorlevel!
|
||||
:: bundle cudnn runtime libraries
|
||||
xcopy "%CUDA_PATH%\bin\x64\cudnn*.dll" "%FINAL_EXPORT_PATH%\installed\%TRIPLET%\bin\" /Y
|
||||
)
|
||||
|
||||
:: pytorch deps
|
||||
%FINAL_EXPORT_PATH%/installed/%TRIPLET%/tools/python3/python.exe -m pip install numpy packaging "setuptools<82" pyyaml typing_extensions
|
||||
|
||||
git config --global core.longpaths true
|
||||
|
||||
:: pytorch, build with local cuda libraries to avoid duplicating them when we install rtabmap
|
||||
echo [+] Building pytorch with cuda support...
|
||||
if not exist pytorch (
|
||||
echo [+] Downloading pytorch...
|
||||
git clone https://github.com/pytorch/pytorch || exit /b !errorlevel!
|
||||
cd pytorch
|
||||
:: Jan 21, 2026
|
||||
git checkout v2.10.0
|
||||
git submodule update --init --recursive || exit /b !errorlevel!
|
||||
cd ..
|
||||
)
|
||||
|
||||
set "PYTHONHOME=%FINAL_EXPORT_PATH%\installed\%TRIPLET%\tools\python3"
|
||||
set "Python_ROOT_DIR=%FINAL_EXPORT_PATH%\installed\%TRIPLET%"
|
||||
set CMAKE_GENERATOR=Ninja
|
||||
set BUILD_TEST=0
|
||||
set ATEN_NO_TEST=1
|
||||
set INSTALL_TEST=OFF
|
||||
set "LIB=%FINAL_EXPORT_PATH%\installed\%TRIPLET%\lib;%LIB%"
|
||||
set "%FINAL_EXPORT_PATH%\installed\%TRIPLET%\include;INCLUDE=%INCLUDE%"
|
||||
|
||||
:: check if torch is installed
|
||||
%PYTHONHOME%/python.exe -m pip show torch >nul 2>&1
|
||||
if !errorlevel! neq 0 (
|
||||
cd pytorch
|
||||
%PYTHONHOME%/python.exe setup.py install || exit /b !errorlevel!
|
||||
cd ..
|
||||
)
|
||||
if exist "%PYTHONHOME%\Lib\site-packages\torch\test" rd /s /q %PYTHONHOME%\Lib\site-packages\torch\test"
|
||||
del "%PYTHONHOME%\Lib\site-packages\torch\bin\test_*" || exit /b !errorlevel!
|
||||
|
||||
echo [+] Building torchvision...
|
||||
if not exist torchvision (
|
||||
echo [+] Downloading torchvision...
|
||||
git clone https://github.com/pytorch/vision.git torchvision || exit /b !errorlevel!
|
||||
cd torchvision
|
||||
:: Jan 6, 2026
|
||||
git checkout v0.25.0
|
||||
git submodule update --init --recursive || exit /b !errorlevel!
|
||||
cd ..
|
||||
)
|
||||
cd torchvision
|
||||
set PATH=%FINAL_EXPORT_PATH%\installed\%TRIPLET%\bin;%PATH%
|
||||
set DISTUTILS_USE_SDK=1
|
||||
set TORCHVISION_INCLUDE=%FINAL_EXPORT_PATH%\installed\%TRIPLET%\include
|
||||
set TORCHVISION_LIBRARY=%FINAL_EXPORT_PATH%\installed\%TRIPLET%\lib
|
||||
%FINAL_EXPORT_PATH%/installed/%TRIPLET%/tools/python3/python.exe -m pip install . -v --no-build-isolation || exit /b !errorlevel!
|
||||
cd ..
|
||||
|
||||
:: opencv_cuda
|
||||
echo [+] Building opencv with cuda support...
|
||||
if not exist opencv (
|
||||
echo [+] Downloading opencv...
|
||||
git clone https://github.com/opencv/opencv.git || exit /b !errorlevel!
|
||||
cd opencv
|
||||
:: 4.13.0 minimum required to be compatible with cuda 13
|
||||
:: Dec 31, 2025
|
||||
git checkout 4.13.0
|
||||
cd ..
|
||||
)
|
||||
if not exist opencv_contrib (
|
||||
echo [+] Downloading opencv_contrib...
|
||||
git clone https://github.com/opencv/opencv_contrib.git || exit /b !errorlevel!
|
||||
cd opencv
|
||||
:: 4.13.0 minimum required to be compatible with cuda 13
|
||||
:: Dec 31, 2025
|
||||
git checkout 4.13.0
|
||||
cd ..
|
||||
)
|
||||
cd opencv
|
||||
cmake -S . -B build -GNinja ^
|
||||
-DVCPKG_MANIFEST_INSTALL=OFF ^
|
||||
-DVCPKG_TARGET_TRIPLET=%TRIPLET% ^
|
||||
-DVCPKG_INSTALLED_DIR="%FINAL_EXPORT_PATH%\installed" ^
|
||||
-DCMAKE_TOOLCHAIN_FILE="%FINAL_EXPORT_PATH%\scripts\buildsystems\vcpkg.cmake" ^
|
||||
-DCMAKE_INSTALL_PREFIX="%FINAL_EXPORT_PATH%\installed\%TRIPLET%" ^
|
||||
-DCMAKE_BUILD_TYPE=Release ^
|
||||
-DOPENCV_BIN_INSTALL_PATH="bin" ^
|
||||
-DOPENCV_LIB_INSTALL_PATH="lib" ^
|
||||
-DOPENCV_CONFIG_INSTALL_PATH="share/opencv" ^
|
||||
-DOPENCV_EXTRA_MODULES_PATH=../opencv_contrib/modules ^
|
||||
-DBUILD_SHARED_LIBS=ON ^
|
||||
-DBUILD_TESTS=OFF ^
|
||||
-DBUILD_PERF_TESTS=OFF ^
|
||||
-DOPENCV_ENABLE_NONFREE=ON ^
|
||||
-DBUILD_opencv_apps=OFF ^
|
||||
-DBUILD_opencv_python3=ON ^
|
||||
-DPYTHON3_EXECUTABLE=%FINAL_EXPORT_PATH%/installed/%TRIPLET%/tools/python3/python.exe ^
|
||||
-DPYTHON3_PACKAGES_PATH=bin/Lib/site-packages ^
|
||||
-DBUILD_opencv_java_bindings_generator=OFF ^
|
||||
-DWITH_CUDA=ON ^
|
||||
-DWITH_VTK=OFF ^
|
||||
-DWITH_TBB=ON || exit /b !errorlevel!
|
||||
cmake --build build --config Release --target install || exit /b !errorlevel!
|
||||
:: move cv2 package under tools/python3
|
||||
robocopy "%FINAL_EXPORT_PATH%\installed\%TRIPLET%\bin\Lib" "%FINAL_EXPORT_PATH%\installed\%TRIPLET%\tools\python3\Lib" /E /MOVE /NFL /NDL /NJH /NC /NS /NP
|
||||
cd ..
|
||||
|
||||
|
||||
:: 5. ZIP the folder
|
||||
echo [+] Creating final package with 7-Zip...
|
||||
:: Rip off pdb files
|
||||
cd /d "%FINAL_EXPORT_PATH%"
|
||||
del /s /q /f *.pdb >nul 2>&1
|
||||
cd ..
|
||||
|
||||
set "FINAL_ZIP=%TARGET_NAME%.7z"
|
||||
|
||||
:: compress contents without the root folder
|
||||
"%SEVENZIP_EXE%" u -t7z -mx9 "%FINAL_ZIP%" "%FINAL_EXPORT_PATH%\*" -up0q0 || exit /b !errorlevel!
|
||||
|
||||
if !errorlevel! EQU 0 (
|
||||
echo [!] Success! Package created at %FINAL_ZIP%
|
||||
) else (
|
||||
echo [X] 7-Zip failed with error code !errorlevel!
|
||||
)
|
||||
|
||||
:: Example building rtabmap with opencv cuda and libtorch afterwards
|
||||
goto :EndComment
|
||||
|
||||
:: Set path of unzipped deps
|
||||
set VCPKG_UNZIPPED_EXPORT_PATH=
|
||||
|
||||
set TRIPLET=x64-windows-release
|
||||
set PATH=%VCPKG_UNZIPPED_EXPORT_PATH%\installed\%TRIPLET%\bin;%PATH%
|
||||
set PATH=%VCPKG_UNZIPPED_EXPORT_PATH%\installed\%TRIPLET%\tools\python3;%PATH%
|
||||
set PATH=%CUDA_PATH%\bin;%PATH%
|
||||
set PATH=%CUDA_PATH%\bin\x64;%PATH%
|
||||
set PATH=%CUDA_PATH%\extras\CUPTI\lib64;%PATH%
|
||||
set PATH=%VCPKG_UNZIPPED_EXPORT_PATH%\installed\%TRIPLET%\tools\python3\Lib\site-packages\torch\lib;%PATH%
|
||||
set PATH=%VCPKG_UNZIPPED_EXPORT_PATH%\installed\%TRIPLET%\tools\python3\Lib\site-packages\numpy.libs;%PATH%
|
||||
|
||||
:: Other dependencies
|
||||
|
||||
:: For ZED, modify zed-config.cmake and remove all dependencies
|
||||
set PATH=%PATH%;%ZED_SDK_ROOT_DIR%\bin
|
||||
|
||||
:: For kinect 4 windows SDK v2, move kinect20.dll from system32 to KINECTSDK20_DIR\bin
|
||||
:: For kinect 4 windows SDK v1, move kinect10.dll and KinectAudio10.dll to KINECTSDK20_DIR\bin
|
||||
set PATH=%PATH%;%KINECTSDK20_DIR%\bin
|
||||
|
||||
cmake -B build_cuda -GNinja ^
|
||||
-DCMAKE_BUILD_TYPE=Release ^
|
||||
-DBUILD_AS_BUNDLE=ON ^
|
||||
-DWITH_PYTHON=ON ^
|
||||
-DWITH_TORCH=ON ^
|
||||
-DWITH_ZED=ON ^
|
||||
-DVCPKG_MANIFEST_INSTALL=OFF ^
|
||||
-DVCPKG_TARGET_TRIPLET=%TRIPLET% ^
|
||||
-DVCPKG_INSTALLED_DIR="%VCPKG_UNZIPPED_EXPORT_PATH%/installed" ^
|
||||
-DCMAKE_TOOLCHAIN_FILE=%VCPKG_UNZIPPED_EXPORT_PATH%/scripts/buildsystems/vcpkg.cmake ^
|
||||
-DGTSAM_DIR=%VCPKG_UNZIPPED_EXPORT_PATH%\installed\%TRIPLET%\CMake ^
|
||||
-DTorch_DIR=%VCPKG_UNZIPPED_EXPORT_PATH%\installed\%TRIPLET%\tools\python3\Lib\site-packages\torch\share\cmake\Torch
|
||||
|
||||
cmake --build build_cuda --config Release --target package
|
||||
|
||||
:: Generate superpoint weights (from share directory of the installed package)
|
||||
curl -L -O "https://raw.githubusercontent.com/magicleap/SuperPointPretrainedNetwork/master/demo_superpoint.py"
|
||||
curl -L -O "https://github.com/magicleap/SuperPointPretrainedNetwork/raw/refs/heads/master/superpoint_v1.pth"
|
||||
..\bin\python.exe rtabmap_trace_superpoint.py
|
||||
|
||||
:EndComment
|
||||
@@ -4,6 +4,7 @@
|
||||
#
|
||||
# It sets the following variables:
|
||||
# CUVSLAM_FOUND - Set to false, or undefined, if cuVSLAM isn't found.
|
||||
# CUVSLAM_VERSION - The version of cuVSLAM found (e.g., "14.0.0").
|
||||
# CUVSLAM_INCLUDE_DIRS - The cuVSLAM include directory.
|
||||
# CUVSLAM_LIBRARIES - The cuVSLAM library to link against.
|
||||
|
||||
@@ -33,7 +34,18 @@ find_library(CUVSLAM_LIBRARY
|
||||
)
|
||||
|
||||
if(CUVSLAM_INCLUDE_DIRS AND CUVSLAM_LIBRARY)
|
||||
set(CUVSLAM_FOUND TRUE)
|
||||
# Extract version from cuvslam.h header
|
||||
file(STRINGS "${CUVSLAM_INCLUDE_DIRS}/cuvslam.h" CUVSLAM_VERSION_MAJOR_LINE
|
||||
REGEX "^#define CUVSLAM_API_VERSION_MAJOR")
|
||||
file(STRINGS "${CUVSLAM_INCLUDE_DIRS}/cuvslam.h" CUVSLAM_VERSION_MINOR_LINE
|
||||
REGEX "^#define CUVSLAM_API_VERSION_MINOR")
|
||||
|
||||
if(CUVSLAM_VERSION_MAJOR_LINE AND CUVSLAM_VERSION_MINOR_LINE)
|
||||
string(REGEX MATCH "[0-9]+" CUVSLAM_VERSION_MAJOR "${CUVSLAM_VERSION_MAJOR_LINE}")
|
||||
string(REGEX MATCH "[0-9]+" CUVSLAM_VERSION_MINOR "${CUVSLAM_VERSION_MINOR_LINE}")
|
||||
set(CUVSLAM_VERSION "${CUVSLAM_VERSION_MAJOR}.${CUVSLAM_VERSION_MINOR}.0")
|
||||
endif()
|
||||
|
||||
set(CUVSLAM_LIBRARIES
|
||||
${CUVSLAM_LIBRARY}
|
||||
${CUDA_LIBRARIES}
|
||||
@@ -46,11 +58,35 @@ if(CUVSLAM_INCLUDE_DIRS AND CUVSLAM_LIBRARY)
|
||||
)
|
||||
endif()
|
||||
|
||||
# Version compatibility check - cuVSLAM only guarantees API compatibility within the same major version
|
||||
set(CUVSLAM_VERSION_MISMATCH_REASON "")
|
||||
if(CuVSLAM_FIND_VERSION AND CUVSLAM_VERSION)
|
||||
string(REGEX MATCH "^[0-9]+" REQUESTED_MAJOR_VERSION "${CuVSLAM_FIND_VERSION}")
|
||||
if(NOT CUVSLAM_VERSION_MAJOR EQUAL REQUESTED_MAJOR_VERSION)
|
||||
set(CUVSLAM_VERSION_MISMATCH_REASON "Major version mismatch: found ${CUVSLAM_VERSION_MAJOR}.x but requested ${REQUESTED_MAJOR_VERSION}.x.\ncuVSLAM only guarantees API compatibility within the same major version.\nPlease install cuVSLAM ${REQUESTED_MAJOR_VERSION}.x or update CMakeLists.txt to request version ${CUVSLAM_VERSION_MAJOR}.0.0")
|
||||
|
||||
if(CuVSLAM_FIND_REQUIRED)
|
||||
message(FATAL_ERROR
|
||||
"cuVSLAM major version mismatch: found version ${CUVSLAM_VERSION} but version ${CuVSLAM_FIND_VERSION} is required.\n"
|
||||
"cuVSLAM only guarantees API compatibility within the same major version.\n"
|
||||
"Found major version ${CUVSLAM_VERSION_MAJOR} is not compatible with requested major version ${REQUESTED_MAJOR_VERSION}.\n"
|
||||
"Please install cuVSLAM ${REQUESTED_MAJOR_VERSION}.x or update CMakeLists.txt to request version ${CUVSLAM_VERSION_MAJOR}.x."
|
||||
)
|
||||
else()
|
||||
# Clear the found variables to indicate incompatibility
|
||||
unset(CUVSLAM_LIBRARIES)
|
||||
unset(CUVSLAM_INCLUDE_DIRS)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Handle the QUIET and REQUIRED arguments
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(CuVSLAM
|
||||
FOUND_VAR CUVSLAM_FOUND
|
||||
REQUIRED_VARS CUVSLAM_LIBRARIES CUVSLAM_INCLUDE_DIRS
|
||||
VERSION_VAR CUVSLAM_VERSION
|
||||
REASON_FAILURE_MESSAGE "${CUVSLAM_VERSION_MISMATCH_REASON}"
|
||||
HANDLE_COMPONENTS
|
||||
)
|
||||
|
||||
@@ -64,16 +100,6 @@ if(CUVSLAM_FOUND)
|
||||
INTERFACE_LINK_LIBRARIES "${CUVSLAM_LIBRARIES};Eigen3::Eigen"
|
||||
)
|
||||
endif()
|
||||
|
||||
# Show which cuVSLAM was found only if not quiet
|
||||
if(NOT CUVSLAM_FIND_QUIETLY)
|
||||
message(STATUS "Found cuVSLAM: ${CUVSLAM_LIBRARIES}")
|
||||
endif()
|
||||
else()
|
||||
# Fatal error if cuVSLAM is required but not found
|
||||
if(CUVSLAM_FIND_REQUIRED)
|
||||
message(FATAL_ERROR "Could not find cuVSLAM library")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
mark_as_advanced(CUVSLAM_INCLUDE_DIRS CUVSLAM_LIBRARY)
|
||||
|
||||
@@ -101,16 +101,16 @@ IF(G2O_STUFF_LIBRARY AND G2O_CORE_LIBRARY AND G2O_INCLUDE_DIR AND G2O_CONFIG_FIL
|
||||
${G2O_TYPES_SBA}
|
||||
${G2O_STUFF_LIBRARY})
|
||||
|
||||
IF(CSPARSE_FOUND)
|
||||
IF(CSPARSE_FOUND AND G2O_SOLVER_CSPARSE AND G2O_SOLVER_CSPARSE_EXTENSION)
|
||||
SET(G2O_INCLUDE_DIRS
|
||||
${G2O_INCLUDE_DIRS}
|
||||
${CSPARSE_INCLUDE_DIR})
|
||||
SET(G2O_LIBRARIES
|
||||
${G2O_LIBRARIES}
|
||||
${G2O_SOLVER_CSPARSE}
|
||||
${G2O_SOLVER_CSPARSE_EXTENSION}
|
||||
${CSPARSE_LIBRARY})
|
||||
ENDIF(CSPARSE_FOUND)
|
||||
${G2O_SOLVER_CSPARSE}
|
||||
${G2O_SOLVER_CSPARSE_EXTENSION}
|
||||
${CSPARSE_LIBRARY})
|
||||
ENDIF(CSPARSE_FOUND AND G2O_SOLVER_CSPARSE AND G2O_SOLVER_CSPARSE_EXTENSION)
|
||||
|
||||
IF(G2O_SOLVER_CHOLMOD)
|
||||
SET(G2O_INCLUDE_DIRS
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
find_path(ORB_SLAM_INCLUDE_DIR NAMES System.h PATHS $ENV{ORB_SLAM_ROOT_DIR}/include)
|
||||
find_library(ORB_SLAM2_LIBRARY NAMES ORB_SLAM2 PATHS $ENV{ORB_SLAM_ROOT_DIR}/lib)
|
||||
find_library(ORB_SLAM3_LIBRARY NAMES ORB_SLAM3 PATHS $ENV{ORB_SLAM_ROOT_DIR}/lib)
|
||||
find_path(DBoW2_INCLUDE_DIR NAMES DBoW2/BowVector.h PATHS $ENV{ORB_SLAM_ROOT_DIR}/Thirdparty/DBoW2 NO_DEFAULT_PATH)
|
||||
find_path(g2o_INCLUDE_DIR NAMES g2o/core/sparse_optimizer.h PATHS $ENV{ORB_SLAM_ROOT_DIR}/Thirdparty/g2o NO_DEFAULT_PATH)
|
||||
find_path(sophus_INCLUDE_DIR NAMES sophus/se3.hpp PATHS $ENV{ORB_SLAM_ROOT_DIR}/Thirdparty/Sophus NO_DEFAULT_PATH)
|
||||
find_library(g2o_LIBRARY NAMES g2o PATHS $ENV{ORB_SLAM_ROOT_DIR}/Thirdparty/g2o/lib NO_DEFAULT_PATH)
|
||||
@@ -22,9 +23,9 @@ IF(ORB_SLAM2_LIBRARY)
|
||||
ELSEIF(ORB_SLAM3_LIBRARY)
|
||||
SET(ORB_SLAM_VERSION 3)
|
||||
SET(ORB_SLAM_LIBRARY ${ORB_SLAM3_LIBRARY})
|
||||
IF(g2o_INCLUDE_DIR AND sophus_INCLUDE_DIR) # ORB_SLAM3 v1
|
||||
SET(g2o_INCLUDE_DIR ${g2o_INCLUDE_DIR} ${sophus_INCLUDE_DIR})
|
||||
ENDIF(g2o_INCLUDE_DIR AND sophus_INCLUDE_DIR)
|
||||
IF(g2o_INCLUDE_DIR AND sophus_INCLUDE_DIR AND DBoW2_INCLUDE_DIR) # ORB_SLAM3 v1
|
||||
SET(g2o_INCLUDE_DIR ${g2o_INCLUDE_DIR} ${sophus_INCLUDE_DIR} ${DBoW2_INCLUDE_DIR} $ENV{ORB_SLAM_ROOT_DIR})
|
||||
ENDIF(g2o_INCLUDE_DIR AND sophus_INCLUDE_DIR AND DBoW2_INCLUDE_DIR)
|
||||
ENDIF()
|
||||
|
||||
IF (ORB_SLAM_INCLUDE_DIR AND ORB_SLAM_LIBRARY AND DBoW2_LIBRARY AND g2o_INCLUDE_DIR AND g2o_LIBRARY)
|
||||
|
||||
@@ -38,7 +38,7 @@ class IMUFilter;
|
||||
|
||||
/**
|
||||
* Class Camera
|
||||
*
|
||||
*
|
||||
*/
|
||||
class RTABMAP_CORE_EXPORT Camera : public SensorCapture
|
||||
{
|
||||
@@ -48,7 +48,7 @@ public:
|
||||
SensorData takeImage(SensorCaptureInfo * info = 0) {return takeData(info);}
|
||||
float getImageRate() const {return getFrameRate();}
|
||||
void setImageRate(float imageRate) {setFrameRate(imageRate);}
|
||||
void setInterIMUPublishing(bool enabled, IMUFilter * filter = 0); // Take ownership of filter
|
||||
void setInterIMUPublishing(bool enabled, IMUFilter * filter = 0, bool baseFrameConversion = false); // Take ownership of filter
|
||||
bool isInterIMUPublishing() const {return publishInterIMU_;}
|
||||
|
||||
bool initFromFile(const std::string & calibrationPath);
|
||||
@@ -73,6 +73,7 @@ private:
|
||||
private:
|
||||
IMUFilter * imuFilter_;
|
||||
bool publishInterIMU_;
|
||||
bool imuBaseFrameConversion_;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -134,7 +134,7 @@ public:
|
||||
public:
|
||||
// Mutex-protected methods of abstract versions below
|
||||
|
||||
bool openConnection(const std::string & url, bool overwritten = false);
|
||||
bool openConnection(const std::string & url, bool overwritten = false, bool readOnly = false);
|
||||
void closeConnection(bool save = true, const std::string & outputUrl = "");
|
||||
bool isConnected() const;
|
||||
unsigned long getMemoryUsed() const; // In bytes
|
||||
@@ -193,7 +193,7 @@ public:
|
||||
protected:
|
||||
DBDriver(const ParametersMap & parameters = ParametersMap());
|
||||
|
||||
virtual bool connectDatabaseQuery(const std::string & url, bool overwritten = false) = 0;
|
||||
virtual bool connectDatabaseQuery(const std::string & url, bool overwritten = false, bool readOnly = false) = 0;
|
||||
virtual void disconnectDatabaseQuery(bool save = true, const std::string & outputUrl = "") = 0;
|
||||
virtual bool isConnectedQuery() const = 0;
|
||||
virtual unsigned long getMemoryUsedQuery() const = 0; // In bytes
|
||||
|
||||
@@ -51,7 +51,7 @@ public:
|
||||
void setTempStore(int tempStore);
|
||||
|
||||
protected:
|
||||
virtual bool connectDatabaseQuery(const std::string & url, bool overwritten = false);
|
||||
virtual bool connectDatabaseQuery(const std::string & url, bool overwritten = false, bool readOnly = false);
|
||||
virtual void disconnectDatabaseQuery(bool save = true, const std::string & outputUrl = "");
|
||||
virtual bool isConnectedQuery() const;
|
||||
virtual unsigned long getMemoryUsedQuery() const; // In bytes
|
||||
|
||||
@@ -59,6 +59,8 @@ public:
|
||||
int startMapId = 0,
|
||||
int stopMapId = -1,
|
||||
bool priorsIgnored = false,
|
||||
bool imuIgnored = false,
|
||||
bool intermediateNodesAreNormalNodes = false,
|
||||
const std::vector<Transform> & cameraLocalTransformOverrides = std::vector<Transform>());
|
||||
DBReader(const std::list<std::string> & databasePaths,
|
||||
float frameRate = 0.0f, // -1 = use Database stamps, 0 = inf
|
||||
@@ -74,6 +76,8 @@ public:
|
||||
int startMapId = 0,
|
||||
int stopMapId = -1,
|
||||
bool priorsIgnored = false,
|
||||
bool imuIgnored = false,
|
||||
bool intermediateNodesAreNormalNodes = false,
|
||||
const std::vector<Transform> & cameraLocalTransformOverrides = std::vector<Transform>());
|
||||
virtual ~DBReader();
|
||||
|
||||
@@ -104,9 +108,11 @@ private:
|
||||
int _stopId;
|
||||
std::vector<unsigned int> _cameraIndices;
|
||||
bool _intermediateNodesIgnored;
|
||||
bool _intermediateNodesAreNormalNodes;
|
||||
bool _landmarksIgnored;
|
||||
bool _featuresIgnored;
|
||||
bool _priorsIgnored;
|
||||
bool _imuIgnored;
|
||||
int _startMapId;
|
||||
int _stopMapId;
|
||||
std::vector<Transform> _cameraLocalTransformOverrides;
|
||||
|
||||
@@ -104,6 +104,7 @@ namespace rtabmap {
|
||||
|
||||
class ORBextractor;
|
||||
class SPDetector;
|
||||
class SPDetectorRpautrat;
|
||||
|
||||
class Stereo;
|
||||
#if CV_MAJOR_VERSION < 3
|
||||
@@ -129,7 +130,8 @@ public:
|
||||
kFeatureSurfFreak=12, //new 0.20.4
|
||||
kFeatureGfttDaisy=13, //new 0.20.6
|
||||
kFeatureSurfDaisy=14, //new 0.20.6
|
||||
kFeaturePyDetector=15}; //new 0.20.8
|
||||
kFeaturePyDetector=15, //new 0.20.8
|
||||
kFeatureSuperPointRpautrat=16}; // new 0.23.3
|
||||
|
||||
static std::string typeName(Type type)
|
||||
{
|
||||
@@ -164,6 +166,8 @@ public:
|
||||
return "GFTT+Daisy";
|
||||
case kFeatureSurfDaisy:
|
||||
return "SURF+Daisy";
|
||||
case kFeatureSuperPointRpautrat:
|
||||
return "SUPERPOINT-RPAUTRAT";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
@@ -305,7 +309,8 @@ private:
|
||||
bool preciseUpscale_;
|
||||
bool rootSIFT_;
|
||||
bool gpu_;
|
||||
float guaussianThreshold_;
|
||||
float gaussianThreshold_;
|
||||
float maxGaussianThreshold_;
|
||||
bool upscale_;
|
||||
|
||||
cv::Ptr<CV_SIFT> sift_;
|
||||
@@ -626,6 +631,31 @@ private:
|
||||
bool cuda_;
|
||||
};
|
||||
|
||||
//SuperPointRpautrat
|
||||
class RTABMAP_CORE_EXPORT SuperPointRpautrat : public Feature2D
|
||||
{
|
||||
public:
|
||||
SuperPointRpautrat(const ParametersMap & parameters = ParametersMap());
|
||||
virtual ~SuperPointRpautrat();
|
||||
|
||||
virtual void parseParameters(const ParametersMap & parameters);
|
||||
virtual Feature2D::Type getType() const { return kFeatureSuperPointRpautrat; }
|
||||
|
||||
private:
|
||||
virtual std::vector<cv::KeyPoint> generateKeypointsImpl(const cv::Mat & image, const cv::Rect & roi, const cv::Mat & mask = cv::Mat());
|
||||
virtual cv::Mat generateDescriptorsImpl(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints) const;
|
||||
|
||||
cv::Ptr<SPDetectorRpautrat> superPoint_;
|
||||
|
||||
std::string superpointWeightsPath_;
|
||||
std::string superpointModelPath_;
|
||||
std::string outputDir_;
|
||||
float threshold_;
|
||||
bool nms_;
|
||||
int minDistance_;
|
||||
bool cuda_;
|
||||
};
|
||||
|
||||
//GFTT_DAISY
|
||||
class RTABMAP_CORE_EXPORT GFTT_DAISY : public GFTT
|
||||
{
|
||||
|
||||
@@ -118,15 +118,18 @@ Transform RTABMAP_CORE_EXPORT calcRMSE(
|
||||
float & rotational_max,
|
||||
bool align2D = false);
|
||||
|
||||
void RTABMAP_CORE_EXPORT computeMaxGraphErrors(
|
||||
struct MaxGraphErrors
|
||||
{
|
||||
float linear=-1.0f; // absolute error (m) of the link with maximum linear error
|
||||
float angular=-1.0f; // absolute error (rad) of the link with maximum angular error
|
||||
float linearRatio=-1.0f; // Ratio = absolute error (m) / linear std (m), of the link with maximum linear error
|
||||
float angularRatio=-1.0f; // Ratio = absolute error (rad) / angular std (rad), of the link with maximum angular error
|
||||
Link linearLink; // link with maximum linear error
|
||||
Link angularLink; // link with maximum angular error
|
||||
};
|
||||
MaxGraphErrors RTABMAP_CORE_EXPORT computeMaxGraphErrors(
|
||||
const std::map<int, Transform> & poses,
|
||||
const std::multimap<int, Link> & links,
|
||||
float & maxLinearErrorRatio,
|
||||
float & maxAngularErrorRatio,
|
||||
float & maxLinearError,
|
||||
float & maxAngularError,
|
||||
const Link ** maxLinearErrorLink = 0,
|
||||
const Link ** maxAngularErrorLink = 0,
|
||||
bool for3DoF = false);
|
||||
|
||||
std::vector<double> RTABMAP_CORE_EXPORT getMaxOdomInf(const std::multimap<int, Link> & links);
|
||||
@@ -277,7 +280,8 @@ std::list<std::pair<int, Transform> > RTABMAP_CORE_EXPORT computePath(
|
||||
bool lookInDatabase = true,
|
||||
bool updateNewCosts = false,
|
||||
float linearVelocity = 0.0f, // m/sec
|
||||
float angularVelocity = 0.0f); // rad/sec
|
||||
float angularVelocity = 0.0f, // rad/sec
|
||||
bool ignoreDirectLinks = false);
|
||||
|
||||
/**
|
||||
* Find the nearest node of the target pose
|
||||
@@ -336,9 +340,7 @@ RTABMAP_DEPRECATED std::map<int, Transform> RTABMAP_CORE_EXPORT getPosesInRadius
|
||||
RTABMAP_DEPRECATED std::map<int, Transform> RTABMAP_CORE_EXPORT getPosesInRadius(const Transform & targetPose, const std::map<int, Transform> & nodes, float radius, float angle = 0.0f);
|
||||
|
||||
float RTABMAP_CORE_EXPORT computePathLength(
|
||||
const std::vector<std::pair<int, Transform> > & path,
|
||||
unsigned int fromIndex = 0,
|
||||
unsigned int toIndex = 0);
|
||||
const std::vector<std::pair<int, Transform> > & path);
|
||||
|
||||
// assuming they are all linked in map order
|
||||
float RTABMAP_CORE_EXPORT computePathLength(
|
||||
|
||||
@@ -48,7 +48,8 @@ public:
|
||||
kXYZNormal=8,
|
||||
kXYZINormal=9,
|
||||
kXYZRGBNormal=10,
|
||||
kXYZIT=11};
|
||||
kXYZIT=11,
|
||||
kXYZIRT=12};
|
||||
|
||||
static std::string formatName(const Format & format);
|
||||
static int channels(const Format & format);
|
||||
@@ -57,6 +58,7 @@ public:
|
||||
static bool isScanHasRGB(const Format & format);
|
||||
static bool isScanHasIntensity(const Format & format);
|
||||
static bool isScanHasTime(const Format & format);
|
||||
static bool isScanHasRing(const Format & format);
|
||||
static LaserScan backwardCompatibility(
|
||||
const cv::Mat & oldScanFormat,
|
||||
int maxPoints = 0,
|
||||
@@ -135,6 +137,7 @@ public:
|
||||
bool hasRGB() const {return isScanHasRGB(format_);}
|
||||
bool hasIntensity() const {return isScanHasIntensity(format_);}
|
||||
bool hasTime() const {return isScanHasTime(format_);}
|
||||
bool hasRing() const {return isScanHasRing(format_);}
|
||||
bool isCompressed() const {return !data_.empty() && data_.type()==CV_8UC1;}
|
||||
bool isOrganized() const {return data_.rows > 1;}
|
||||
LaserScan clone() const;
|
||||
@@ -143,7 +146,8 @@ public:
|
||||
int getIntensityOffset() const {return hasIntensity()?(is2d()?2:3):-1;}
|
||||
int getRGBOffset() const {return hasRGB()?(is2d()?2:3):-1;}
|
||||
int getNormalsOffset() const {return hasNormals()?(2 + (is2d()?0:1) + ((hasRGB() || hasIntensity())?1:0)):-1;}
|
||||
int getTimeOffset() const {return hasTime()?4:-1;}
|
||||
int getRingOffset() const {return format_==kXYZIRT?4:-1;}
|
||||
int getTimeOffset() const {return format_==kXYZIT?4:(format_==kXYZIRT?5:-1);}
|
||||
|
||||
float & field(unsigned int pointIndex, unsigned int channelOffset);
|
||||
|
||||
|
||||
@@ -57,6 +57,12 @@ private:
|
||||
};
|
||||
|
||||
class RTABMAP_CORE_EXPORT MarkerDetector {
|
||||
|
||||
public:
|
||||
enum Strategy {
|
||||
kStrategyOpencv,
|
||||
kStrategyApriltag
|
||||
};
|
||||
|
||||
public:
|
||||
MarkerDetector(const ParametersMap & parameters = ParametersMap());
|
||||
@@ -84,15 +90,19 @@ public:
|
||||
cv::Mat * imageWithDetections = 0);
|
||||
|
||||
private:
|
||||
#ifdef HAVE_OPENCV_ARUCO
|
||||
cv::Ptr<cv::aruco::DetectorParameters> detectorParams_;
|
||||
float markerLength_;
|
||||
Strategy strategy_;
|
||||
float markerLength_;
|
||||
std::map<int, float> markerLengths_;
|
||||
float maxDepthError_;
|
||||
float maxRange_;
|
||||
float minRange_;
|
||||
int dictionaryId_;
|
||||
#ifdef HAVE_OPENCV_ARUCO
|
||||
cv::Ptr<cv::aruco::DetectorParameters> detectorParams_;
|
||||
cv::Ptr<cv::aruco::Dictionary> dictionary_;
|
||||
#endif
|
||||
void * apriltagLibDetector_;
|
||||
void * apriltagLibFamily_;
|
||||
};
|
||||
|
||||
} /* namespace rtabmap */
|
||||
|
||||
@@ -140,10 +140,12 @@ public:
|
||||
float radius,
|
||||
const std::map<int, Transform> & optimizedPoses,
|
||||
int maxGraphDepth) const;
|
||||
void convertToIntermediate(int locationId);
|
||||
void deleteLocation(int locationId, std::list<int> * deletedWords = 0);
|
||||
void saveLocationData(int locationId);
|
||||
void removeLink(int idA, int idB);
|
||||
void removeRawData(int id, bool image = true, bool scan = true, bool userData = true);
|
||||
void removeRawData(int id, bool image = true, bool scan = true, bool userData = true, bool occupancyGrid = true);
|
||||
int reduceNode(int id, float maxDistance = 0.0f, bool keepLinkedInDb = false, int direction = 0);
|
||||
|
||||
//getters
|
||||
const std::map<int, double> & getWorkingMem() const {return _workingMem;}
|
||||
@@ -161,7 +163,7 @@ public:
|
||||
float getSimilarityThreshold() const {return _similarityThreshold;}
|
||||
std::map<int, int> getWeights() const;
|
||||
int getLastSignatureId() const;
|
||||
const Signature * getLastWorkingSignature() const;
|
||||
const Signature * getLastWorkingSignature(bool ignoreIntermediateNodes) const;
|
||||
std::map<int, Link> getNodesObservingLandmark(int landmarkId, bool lookInDatabase) const;
|
||||
int getSignatureIdByLabel(const std::string & label, bool lookInDatabase = true) const;
|
||||
bool labelSignature(int id, const std::string & label);
|
||||
@@ -211,6 +213,7 @@ public:
|
||||
std::set<int> getAllSignatureIds(bool ignoreChildren = true) const;
|
||||
bool memoryChanged() const {return _memoryChanged;}
|
||||
bool isIncremental() const {return _incrementalMemory;}
|
||||
bool isReadOnly() const {return !_incrementalMemory && _localizationReadOnly;}
|
||||
bool isLocalizationDataSaved() const {return _localizationDataSaved;}
|
||||
const Signature * getSignature(int id) const;
|
||||
bool isInSTM(int signatureId) const {return _stMem.find(signatureId) != _stMem.end();}
|
||||
@@ -276,6 +279,7 @@ private:
|
||||
void initCountId();
|
||||
void rehearsal(Signature * signature, Statistics * stats = 0);
|
||||
bool rehearsalMerge(int oldId, int newId);
|
||||
bool canBeReduced(const Link & link, float maxDistance, int direction);
|
||||
|
||||
const std::map<int, Signature*> & getSignatures() const {return _signatures;}
|
||||
|
||||
@@ -307,6 +311,7 @@ private:
|
||||
std::string _rgbCompressionFormat;
|
||||
std::string _depthCompressionFormat;
|
||||
bool _incrementalMemory;
|
||||
bool _localizationReadOnly;
|
||||
bool _localizationDataSaved;
|
||||
bool _flannIndexSaved;
|
||||
bool _reduceGraph;
|
||||
|
||||
@@ -57,7 +57,8 @@ public:
|
||||
kTypeOpenVINS = 10,
|
||||
kTypeFLOAM = 11,
|
||||
kTypeOpen3D = 12,
|
||||
kTypeCuVSLAM = 13
|
||||
kTypeCuVSLAM = 13,
|
||||
kTypeLIOSAM = 14
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
@@ -213,17 +213,18 @@ class RTABMAP_CORE_EXPORT Parameters
|
||||
RTABMAP_PARAM_STR(Mem, DepthCompressionFormat, ".rvl", "Depth image compression format for 16UC1 depth type. It should be \".png\" or \".rvl\". If depth type is 32FC1, \".png\" is used.");
|
||||
RTABMAP_PARAM(Mem, STMSize, unsigned int, 10, "Short-term memory size.");
|
||||
RTABMAP_PARAM(Mem, IncrementalMemory, bool, true, "SLAM mode, otherwise it is Localization mode.");
|
||||
RTABMAP_PARAM(Mem, LocalizationReadOnly, bool, false, uFormat("In localization mode, open the database in read-only mode (ignored if %s=true). Currrenty incompatible with memory management (%s and %s cannot be used) and if there are disjoint sessions in working memory. Last localization pose won't be saved back in the database at the end of the session, so the robot will always restart to original last localization pose, unless %s is used or an external initial pose is provided on initialization.", kMemIncrementalMemory().c_str(), kRtabmapLoopThr().c_str(), kRtabmapMemoryThr().c_str(), kRGBDStartAtOrigin().c_str()).c_str());
|
||||
RTABMAP_PARAM(Mem, LocalizationDataSaved, bool, false, uFormat("Save localization data during localization session (when %s=false). When enabled, the database will then also grow in localization mode. This mode would be used only for debugging purpose.", kMemIncrementalMemory().c_str()).c_str());
|
||||
RTABMAP_PARAM(Mem, ReduceGraph, bool, false, uFormat("Reduce graph. Merge nodes when loop closures are added (ignoring those with user data). Note that this approach assumes that 100%% of the loop closures accepted are good, so it is highly recommended to enable \"%s\" at the same time.", kRGBDOptimizeMaxError().c_str()));
|
||||
RTABMAP_PARAM(Mem, RecentWmRatio, float, 0.2, "Ratio of locations after the last loop closure in WM that cannot be transferred.");
|
||||
RTABMAP_PARAM(Mem, TransferSortingByWeightId, bool, false, "On transfer, signatures are sorted by weight->ID only (i.e. the oldest of the lowest weighted signatures are transferred first). If false, the signatures are sorted by weight->Age->ID (i.e. the oldest inserted in WM of the lowest weighted signatures are transferred first). Note that retrieval updates the age, not the ID.");
|
||||
RTABMAP_PARAM(Mem, RehearsalIdUpdatedToNewOne, bool, false, "On merge, update to new id. When false, no copy.");
|
||||
RTABMAP_PARAM(Mem, RehearsalIdUpdatedToNewOne, bool, false, uFormat("On merge, update to new id. When false, no copy. Keep this disable if %s=true.", kRtabmapCreateIntermediateNodes().c_str()));
|
||||
RTABMAP_PARAM(Mem, RehearsalWeightIgnoredWhileMoving, bool, false, "When the robot is moving, weights are not updated on rehearsal.");
|
||||
RTABMAP_PARAM(Mem, GenerateIds, bool, true, "True=Generate location IDs, False=use input image IDs.");
|
||||
RTABMAP_PARAM(Mem, BadSignaturesIgnored, bool, false, "Bad signatures are ignored.");
|
||||
RTABMAP_PARAM(Mem, InitWMWithAllNodes, bool, false, "Initialize the Working Memory with all nodes in Long-Term Memory. When false, it is initialized with nodes of the previous session.");
|
||||
RTABMAP_PARAM(Mem, DepthAsMask, bool, true, "Use depth image as mask when extracting features for vocabulary.");
|
||||
RTABMAP_PARAM(Mem, DepthMaskFloorThr, float, 0.0, uFormat("Filter floor from depth mask below specified threshold (m) before extracting features. 0 means disabled, negative means remove all objects above the floor threshold instead. Ignored if %s is false.", kMemDepthAsMask().c_str()));
|
||||
RTABMAP_PARAM(Mem, DepthMaskFloorThr, float, 0.0, uFormat("Filter floor from depth mask below specified threshold (m) before extracting features. 0 means disabled. Ignored if %s is false.", kMemDepthAsMask().c_str()));
|
||||
RTABMAP_PARAM(Mem, StereoFromMotion, bool, false, uFormat("Triangulate features without depth using stereo from motion (odometry). It would be ignored if %s is true and the feature detector used supports masking.", kMemDepthAsMask().c_str()));
|
||||
RTABMAP_PARAM(Mem, ImagePreDecimation, unsigned int, 1, uFormat("Decimation of the RGB image before visual feature detection. If depth size is larger than decimated RGB size, depth is decimated to be always at most equal to RGB size. If %s is true and if depth is smaller than decimated RGB, depth may be interpolated to match RGB size for feature detection.",kMemDepthAsMask().c_str()));
|
||||
RTABMAP_PARAM(Mem, ImagePostDecimation, unsigned int, 1, uFormat("Decimation of the RGB image before saving it to database. If depth size is larger than decimated RGB size, depth is decimated to be always at most equal to RGB size. Decimation is done from the original image. If set to same value than %s, data already decimated is saved (no need to re-decimate the image).", kMemImagePreDecimation().c_str()));
|
||||
@@ -248,20 +249,20 @@ class RTABMAP_CORE_EXPORT Parameters
|
||||
RTABMAP_PARAM(Kp, MinDepth, float, 0, "Filter extracted keypoints by depth.");
|
||||
RTABMAP_PARAM(Kp, MaxFeatures, int, 500, "Maximum features extracted from the images (0 means not bounded, <0 means no extraction).");
|
||||
RTABMAP_PARAM(Kp, SSC, bool, false, "If true, SSC (Suppression via Square Covering) is applied to limit keypoints.");
|
||||
RTABMAP_PARAM(Kp, BadSignRatio, float, 0.5, "Bad signature ratio (less than Ratio x AverageWordsPerImage = bad).");
|
||||
RTABMAP_PARAM(Kp, BadSignRatio, float, 0.5, uFormat("Bad signature ratio. If %s=0, the ratio is computed from the average number of words per signature (less than Ratio x AverageWordsPerImage = bad).", kKpMaxFeatures().c_str()));
|
||||
RTABMAP_PARAM(Kp, NndrRatio, float, 0.8, "NNDR ratio (A matching pair is detected, if its distance is closer than X times the distance of the second nearest neighbor.)");
|
||||
#if CV_MAJOR_VERSION > 2 && !defined(HAVE_OPENCV_XFEATURES2D)
|
||||
// OpenCV>2 without xFeatures2D module doesn't have BRIEF
|
||||
RTABMAP_PARAM(Kp, DetectorStrategy, int, 8, "0=SURF 1=SIFT 2=ORB 3=FAST/FREAK 4=FAST/BRIEF 5=GFTT/FREAK 6=GFTT/BRIEF 7=BRISK 8=GFTT/ORB 9=KAZE 10=ORB-OCTREE 11=SuperPoint 12=SURF/FREAK 13=GFTT/DAISY 14=SURF/DAISY 15=PyDetector");
|
||||
RTABMAP_PARAM(Kp, DetectorStrategy, int, 8, "0=SURF 1=SIFT 2=ORB 3=FAST/FREAK 4=FAST/BRIEF 5=GFTT/FREAK 6=GFTT/BRIEF 7=BRISK 8=GFTT/ORB 9=KAZE 10=ORB-OCTREE 11=SuperPoint 12=SURF/FREAK 13=GFTT/DAISY 14=SURF/DAISY 15=PyDetector 16=SuperPoint-Rpautrat");
|
||||
#else
|
||||
RTABMAP_PARAM(Kp, DetectorStrategy, int, 6, "0=SURF 1=SIFT 2=ORB 3=FAST/FREAK 4=FAST/BRIEF 5=GFTT/FREAK 6=GFTT/BRIEF 7=BRISK 8=GFTT/ORB 9=KAZE 10=ORB-OCTREE 11=SuperPoint 12=SURF/FREAK 13=GFTT/DAISY 14=SURF/DAISY 15=PyDetector");
|
||||
RTABMAP_PARAM(Kp, DetectorStrategy, int, 6, "0=SURF 1=SIFT 2=ORB 3=FAST/FREAK 4=FAST/BRIEF 5=GFTT/FREAK 6=GFTT/BRIEF 7=BRISK 8=GFTT/ORB 9=KAZE 10=ORB-OCTREE 11=SuperPoint 12=SURF/FREAK 13=GFTT/DAISY 14=SURF/DAISY 15=PyDetector 16=SuperPoint-Rpautrat");
|
||||
#endif
|
||||
RTABMAP_PARAM(Kp, TfIdfLikelihoodUsed, bool, true, "Use of the td-idf strategy to compute the likelihood.");
|
||||
RTABMAP_PARAM(Kp, Parallelized, bool, true, "If the dictionary update and signature creation were parallelized.");
|
||||
RTABMAP_PARAM_STR(Kp, RoiRatios, "0.0 0.0 0.0 0.0", "Region of interest ratios [left, right, top, bottom].");
|
||||
RTABMAP_PARAM_STR(Kp, DictionaryPath, "", "Path of the pre-computed dictionary");
|
||||
RTABMAP_PARAM(Kp, NewWordsComparedTogether, bool, true, "When adding new words to dictionary, they are compared also with each other (to detect same words in the same signature).");
|
||||
RTABMAP_PARAM(Kp, FlannIndexSaved, bool, false, uFormat("Save FLANN index during localization session (when %s=false). The FLANN index will be saved to database after the first time localization mode is used, then on next sessions, the index is reloaded from the database instead of being rebuilt again. This can save significant loading time when the visual word dictionary is big (>1M words). Note that if the dictionary is modified (parameters or data), the index will be rebuilt and saved again on the next session.", kMemIncrementalMemory().c_str()).c_str());
|
||||
RTABMAP_PARAM(Kp, FlannIndexSaved, bool, false, uFormat("Save FLANN index during localization session (when %s=false). The FLANN index will be saved to database after the first time localization mode is used, then on next sessions, the index is reloaded from the database instead of being rebuilt again. This can save significant loading time when the visual word dictionary is big (>1M words). Note that if the dictionary is modified (parameters or data), the index will be rebuilt and saved again on the next session. Ignored on initialization if %s is enabled.", kMemIncrementalMemory().c_str(), kMemInitWMWithAllNodes().c_str()).c_str());
|
||||
RTABMAP_PARAM(Kp, SerializeWithChecksum, bool, true, "On serialization of the FLANN index, compute checksum of the data used by the FLANN index. This adds a slight overhead on serialization/deserialization to make sure that the dictionary data correspond to same data used when the index was built.");
|
||||
RTABMAP_PARAM(Kp, SubPixWinSize, int, 3, "See cv::cornerSubPix().");
|
||||
RTABMAP_PARAM(Kp, SubPixIterations, int, 0, "See cv::cornerSubPix(). 0 disables sub pixel refining.");
|
||||
@@ -293,7 +294,8 @@ class RTABMAP_CORE_EXPORT Parameters
|
||||
RTABMAP_PARAM(SIFT, PreciseUpscale, bool, false, "Whether to enable precise upscaling in the scale pyramid (OpenCV >= 4.8).");
|
||||
RTABMAP_PARAM(SIFT, RootSIFT, bool, false, "Apply RootSIFT normalization of the descriptors.");
|
||||
RTABMAP_PARAM(SIFT, Gpu, bool, false, "CudaSift: Use GPU version of SIFT. This option is enabled only if RTAB-Map is built with CudaSift dependency and GPUs are detected.");
|
||||
RTABMAP_PARAM(SIFT, GaussianThreshold, float, 2.0, "CudaSift: Threshold on difference of Gaussians for feature pruning. The higher the threshold, the less features are produced by the detector.");
|
||||
RTABMAP_PARAM(SIFT, GaussianThreshold, float, 2.0, "CudaSift: Threshold on difference of Gaussians for feature pruning. The higher the threshold, the less features with low response/hessian are produced by the detector.");
|
||||
RTABMAP_PARAM(SIFT, MaxGaussianThreshold, float, 0.0, uFormat("CudaSift: Maximum threshold on difference of Gaussians for feature pruning (ignored if smaller or equal than %s). The lower the threshold, the less features with high response/hessian are produced by the detector.", kSIFTGaussianThreshold().c_str()));
|
||||
RTABMAP_PARAM(SIFT, Upscale, bool, false, "CudaSift: Whether to enable upscaling.");
|
||||
|
||||
RTABMAP_PARAM(BRIEF, Bytes, int, 32, "Bytes is a length of descriptor in bytes. It can be equal 16, 32 or 64 bytes.");
|
||||
@@ -346,6 +348,13 @@ class RTABMAP_CORE_EXPORT Parameters
|
||||
RTABMAP_PARAM(SuperPoint, NMSRadius, int, 4, uFormat("[%s=true] Minimum distance (pixels) between keypoints.", kSuperPointNMS().c_str()));
|
||||
RTABMAP_PARAM(SuperPoint, Cuda, bool, true, "Use Cuda device for Torch, otherwise CPU device is used by default.");
|
||||
|
||||
RTABMAP_PARAM_STR(SuperPointRpautrat, WeightsPath, "", "[Required] SuperPoint weights file (*.pth).");
|
||||
RTABMAP_PARAM_STR(SuperPointRpautrat, ModelPath, "", "[Required] SuperPoint python model file (superpoint_pytorch.py).");
|
||||
RTABMAP_PARAM(SuperPointRpautrat, Threshold, float, 0.005, "Detector response threshold to accept keypoint.");
|
||||
RTABMAP_PARAM(SuperPointRpautrat, NMS, bool, true, "If true, non-maximum suppression is applied to detected keypoints.");
|
||||
RTABMAP_PARAM(SuperPointRpautrat, NMSRadius, int, 4, uFormat("[%s=true] Minimum distance (pixels) between keypoints.", kSuperPointRpautratNMS().c_str()));
|
||||
RTABMAP_PARAM(SuperPointRpautrat, Cuda, bool, true, "Use Cuda device for Torch, otherwise CPU device is used by default.");
|
||||
|
||||
RTABMAP_PARAM_STR(PyDetector, Path, "", "Path to python script file (see available ones in rtabmap/corelib/src/python/*). See the header to see where the script should be copied.");
|
||||
RTABMAP_PARAM(PyDetector, Cuda, bool, true, "Use cuda.");
|
||||
|
||||
@@ -362,14 +371,15 @@ class RTABMAP_CORE_EXPORT Parameters
|
||||
|
||||
// RGB-D SLAM
|
||||
RTABMAP_PARAM(RGBD, Enabled, bool, true, "Activate metric SLAM. If set to false, classic RTAB-Map loop closure detection is done using only images and without any metric information.");
|
||||
RTABMAP_PARAM(RGBD, LinearUpdate, float, 0.1, "Minimum linear displacement (m) to update the map. Rehearsal is done prior to this, so weights are still updated.");
|
||||
RTABMAP_PARAM(RGBD, AngularUpdate, float, 0.1, "Minimum angular displacement (rad) to update the map. Rehearsal is done prior to this, so weights are still updated.");
|
||||
RTABMAP_PARAM(RGBD, LinearUpdate, float, 0.1, uFormat("Minimum linear displacement (m) to update the map. Rehearsal is done prior to this, so weights are still updated. To update the map when not moving, both %s and %s should be set to 0.", Parameters::kRGBDLinearUpdate().c_str(), Parameters::kRGBDAngularUpdate().c_str()));
|
||||
RTABMAP_PARAM(RGBD, AngularUpdate, float, 0.1, uFormat("Minimum angular displacement (rad) to update the map. Rehearsal is done prior to this, so weights are still updated. To update the map when not moving, both %s and %s should be set to 0.", Parameters::kRGBDLinearUpdate().c_str(), Parameters::kRGBDAngularUpdate().c_str()));
|
||||
RTABMAP_PARAM(RGBD, LinearSpeedUpdate, float, 0.0, "Maximum linear speed (m/s) to update the map (0 means not limit).");
|
||||
RTABMAP_PARAM(RGBD, AngularSpeedUpdate, float, 0.0, "Maximum angular speed (rad/s) to update the map (0 means not limit).");
|
||||
RTABMAP_PARAM(RGBD, AggressiveLoopThr, float, 0.05, uFormat("Loop closure threshold used (overriding %s) when a new mapping session is not yet linked to a map of the highest loop closure hypothesis. In localization mode, this threshold is used when there are no loop closure constraints with any map in the cache (%s). In all cases, the goal is to aggressively loop on a previous map in the database. Only used when %s is enabled. Set 1 to disable.", kRtabmapLoopThr().c_str(), kRGBDMaxOdomCacheSize().c_str(), kRGBDEnabled().c_str()));
|
||||
RTABMAP_PARAM(RGBD, NewMapOdomChangeDistance, float, 0, "A new map is created if a change of odometry translation greater than X m is detected (0 m = disabled).");
|
||||
RTABMAP_PARAM(RGBD, OptimizeFromGraphEnd, bool, false, "Optimize graph from the newest node. If false, the graph is optimized from the oldest node of the current graph (this adds an overhead computation to detect to oldest node of the current graph, but it can be useful to preserve the map referential from the oldest node). Warning when set to false: when some nodes are transferred, the first referential of the local map may change, resulting in momentary changes in robot/map position (which are annoying in teleoperation).");
|
||||
RTABMAP_PARAM(RGBD, OptimizeMaxError, float, 3.0, uFormat("Reject loop closures if optimization error ratio is greater than this value (0=disabled). Ratio is computed as absolute error over standard deviation of each link. This will help to detect when a wrong loop closure is added to the graph. If used with \"%s\", the disabled loop closure links will be removed.", kOptimizerRobust().c_str()));
|
||||
RTABMAP_PARAM(RGBD, OptimizeMaxErrorRepairRadius, float, 0.0, uFormat("If two consecutive loop closures are rejected by %s on the same old loop closure link, we will remove that old link, and other old links under that radius if necessary, until optimization is accepted. When optimization is accepted, the old loop closure links are removed from the graph. This feature is useful to reject bad loop closures that were accepted previously. Set to 0 to disable this feature.", kRGBDOptimizeMaxError().c_str()));
|
||||
RTABMAP_PARAM(RGBD, MaxLoopClosureDistance, float, 0.0, "Reject loop closures/localizations if the distance from the map is over this distance (0=disabled).");
|
||||
RTABMAP_PARAM(RGBD, ForceOdom3DoF, bool, true, uFormat("Force odometry pose to be 3DoF if %s=true.", kRegForce3DoF().c_str()));
|
||||
RTABMAP_PARAM(RGBD, StartAtOrigin, bool, false, uFormat("If true, rtabmap will assume the robot is starting from origin of the map. If false, rtabmap will assume the robot is restarting from the last saved localization pose from previous session (the place where it shut down previously). Used only in localization mode (%s=false).", kMemIncrementalMemory().c_str()));
|
||||
@@ -456,7 +466,7 @@ class RTABMAP_CORE_EXPORT Parameters
|
||||
RTABMAP_PARAM(GTSAM, IncRelinearizeSkip, int, 1, "Only relinearize any variables every X calls to ISAM2::update(). See GTSAM::ISAM2 doc for more info.");
|
||||
|
||||
// Odometry
|
||||
RTABMAP_PARAM(Odom, Strategy, int, 0, "0=Frame-to-Map (F2M) 1=Frame-to-Frame (F2F) 2=Fovis 3=viso2 4=DVO-SLAM 5=ORB_SLAM2 6=OKVIS 7=LOAM 8=MSCKF_VIO 9=VINS-Fusion 10=OpenVINS 11=FLOAM 12=Open3D 13=cuVSLAM");
|
||||
RTABMAP_PARAM(Odom, Strategy, int, 0, "0=Frame-to-Map (F2M) 1=Frame-to-Frame (F2F) 2=Fovis 3=viso2 4=DVO-SLAM 5=ORB_SLAM 6=OKVIS 7=LOAM 8=MSCKF_VIO 9=VINS-Fusion 10=OpenVINS 11=FLOAM 12=Open3D 13=cuVSLAM 14=LIO-SAM");
|
||||
RTABMAP_PARAM(Odom, ResetCountdown, int, 0, "Automatically reset odometry after X consecutive images where odometry cannot be computed (a value of 0 disables auto-reset). When a reset occurs, odometry resumes from the last successfully computed pose with large covariance to trigger a new map. If external odometry is used, it will also be reset based on the motion estimated relative to the last computed pose but no large covariance will be received, so that a new map won't be triggered.");
|
||||
RTABMAP_PARAM(Odom, Holonomic, bool, true, "If the robot is holonomic (strafing commands can be issued). If not, y value will be estimated from x and yaw values (y=x*tan(yaw)).");
|
||||
RTABMAP_PARAM(Odom, FillInfoData, bool, true, "Fill info with data (inliers/outliers features).");
|
||||
@@ -551,7 +561,7 @@ class RTABMAP_CORE_EXPORT Parameters
|
||||
RTABMAP_PARAM(OdomViso2, BucketWidth, double, 50, "Width of bucket.");
|
||||
RTABMAP_PARAM(OdomViso2, BucketHeight, double, 50, "Height of bucket.");
|
||||
|
||||
// Odometry ORB_SLAM2
|
||||
// Odometry ORB_SLAM
|
||||
RTABMAP_PARAM_STR(OdomORBSLAM, VocPath, "", "Path to ORB vocabulary (*.txt).");
|
||||
RTABMAP_PARAM(OdomORBSLAM, Bf, double, 0.076, "Fake IR projector baseline (m) used only when stereo is not used.");
|
||||
RTABMAP_PARAM(OdomORBSLAM, ThDepth, double, 40.0, "Close/Far threshold. Baseline times.");
|
||||
@@ -675,6 +685,24 @@ class RTABMAP_CORE_EXPORT Parameters
|
||||
RTABMAP_PARAM(OdomOpen3D, MaxDepth, float, 3.0, "Maximum depth.");
|
||||
RTABMAP_PARAM(OdomOpen3D, Method, int, 0, "Registration method: 0=PointToPlane, 1=Intensity, 2=Hybrid.");
|
||||
|
||||
// Odometry cuVSLAM
|
||||
RTABMAP_PARAM(OdomCuVSLAM, MulticamMode, int, 0, "cuVSLAM multicam_mode setting: 0=moderate, 1=performance, 2=precision.");
|
||||
|
||||
// Odometry LIO-SAM
|
||||
RTABMAP_PARAM_STR(OdomLIOSAM, ConfigPath, "", "Path to LIO-SAM params.yaml config file. When set, sensor/IMU/feature parameters are loaded from the file and the individual parameters below are ignored.");
|
||||
RTABMAP_PARAM(OdomLIOSAM, Sensor, int, 0, "LiDAR sensor: 0=Velodyne, 1=Ouster, 2=Livox");
|
||||
RTABMAP_PARAM(OdomLIOSAM, NScan, int, 16, "Number of LiDAR channels (16, 32, 64, 128).");
|
||||
RTABMAP_PARAM(OdomLIOSAM, HorizonScan, int, 1800, "Horizontal resolution (Velodyne:1800, Ouster:512/1024/2048).");
|
||||
RTABMAP_PARAM(OdomLIOSAM, ImuAccNoise, float, 0.01, "IMU accelerometer white noise.");
|
||||
RTABMAP_PARAM(OdomLIOSAM, ImuGyrNoise, float, 0.001, "IMU gyroscope white noise.");
|
||||
RTABMAP_PARAM(OdomLIOSAM, ImuAccBiasN, float, 0.0002,"IMU accelerometer bias noise.");
|
||||
RTABMAP_PARAM(OdomLIOSAM, ImuGyrBiasN, float, 0.00003,"IMU gyroscope bias noise.");
|
||||
RTABMAP_PARAM(OdomLIOSAM, ImuGravity, float, 9.80511,"Gravity magnitude.");
|
||||
RTABMAP_PARAM(OdomLIOSAM, EdgeThreshold,float, 1.0, "Edge feature curvature threshold.");
|
||||
RTABMAP_PARAM(OdomLIOSAM, SurfThreshold,float, 0.1, "Surface feature curvature threshold.");
|
||||
RTABMAP_PARAM(OdomLIOSAM, LinVar, float, 0.01, "Linear output variance.");
|
||||
RTABMAP_PARAM(OdomLIOSAM, AngVar, float, 0.01, "Angular output variance.");
|
||||
|
||||
// Common registration parameters
|
||||
RTABMAP_PARAM(Reg, RepeatOnce, bool, true, "Do a second registration with the output of the first registration as guess. Only done if no guess was provided for the first registration (like on loop closure). It can be useful if the registration approach used can use a guess to get better matches.");
|
||||
RTABMAP_PARAM(Reg, Strategy, int, 0, "0=Vis, 1=Icp, 2=VisIcp");
|
||||
@@ -704,16 +732,16 @@ class RTABMAP_CORE_EXPORT Parameters
|
||||
RTABMAP_PARAM(Vis, Iterations, int, 300, "Maximum iterations to compute the transform.");
|
||||
#if CV_MAJOR_VERSION > 2 && !defined(HAVE_OPENCV_XFEATURES2D)
|
||||
// OpenCV>2 without xFeatures2D module doesn't have BRIEF
|
||||
RTABMAP_PARAM(Vis, FeatureType, int, 8, "0=SURF 1=SIFT 2=ORB 3=FAST/FREAK 4=FAST/BRIEF 5=GFTT/FREAK 6=GFTT/BRIEF 7=BRISK 8=GFTT/ORB 9=KAZE 10=ORB-OCTREE 11=SuperPoint 12=SURF/FREAK 13=GFTT/DAISY 14=SURF/DAISY 15=PyDetector");
|
||||
RTABMAP_PARAM(Vis, FeatureType, int, 8, "0=SURF 1=SIFT 2=ORB 3=FAST/FREAK 4=FAST/BRIEF 5=GFTT/FREAK 6=GFTT/BRIEF 7=BRISK 8=GFTT/ORB 9=KAZE 10=ORB-OCTREE 11=SuperPoint 12=SURF/FREAK 13=GFTT/DAISY 14=SURF/DAISY 15=PyDetector 16=SuperPoint-Rpautrat");
|
||||
#else
|
||||
RTABMAP_PARAM(Vis, FeatureType, int, 6, "0=SURF 1=SIFT 2=ORB 3=FAST/FREAK 4=FAST/BRIEF 5=GFTT/FREAK 6=GFTT/BRIEF 7=BRISK 8=GFTT/ORB 9=KAZE 10=ORB-OCTREE 11=SuperPoint 12=SURF/FREAK 13=GFTT/DAISY 14=SURF/DAISY 15=PyDetector");
|
||||
RTABMAP_PARAM(Vis, FeatureType, int, 6, "0=SURF 1=SIFT 2=ORB 3=FAST/FREAK 4=FAST/BRIEF 5=GFTT/FREAK 6=GFTT/BRIEF 7=BRISK 8=GFTT/ORB 9=KAZE 10=ORB-OCTREE 11=SuperPoint 12=SURF/FREAK 13=GFTT/DAISY 14=SURF/DAISY 15=PyDetector 16=SuperPoint-Rpautrat");
|
||||
#endif
|
||||
RTABMAP_PARAM(Vis, MaxFeatures, int, 1000, "0 no limits.");
|
||||
RTABMAP_PARAM(Vis, SSC, bool, false, "If true, SSC (Suppression via Square Covering) is applied to limit keypoints.");
|
||||
RTABMAP_PARAM(Vis, MaxDepth, float, 0, "Max depth of the features (0 means no limit).");
|
||||
RTABMAP_PARAM(Vis, MinDepth, float, 0, "Min depth of the features (0 means no limit).");
|
||||
RTABMAP_PARAM(Vis, DepthAsMask, bool, true, "Use depth image as mask when extracting features.");
|
||||
RTABMAP_PARAM(Vis, DepthMaskFloorThr, float, 0.0, uFormat("Filter floor from depth mask below specified threshold (m) before extracting features. 0 means disabled, negative means remove all objects above the floor threshold instead. Ignored if %s is false.", kVisDepthAsMask().c_str()));
|
||||
RTABMAP_PARAM(Vis, DepthMaskFloorThr, float, 0.0, uFormat("Filter floor from depth mask below specified threshold (m) before extracting features. 0 means disabled. Ignored if %s is false.", kVisDepthAsMask().c_str()));
|
||||
RTABMAP_PARAM_STR(Vis, RoiRatios, "0.0 0.0 0.0 0.0", "Region of interest ratios [left, right, top, bottom].");
|
||||
RTABMAP_PARAM(Vis, SubPixWinSize, int, 3, "See cv::cornerSubPix().");
|
||||
RTABMAP_PARAM(Vis, SubPixIterations, int, 0, "See cv::cornerSubPix(). 0 disables sub pixel refining.");
|
||||
@@ -729,8 +757,11 @@ class RTABMAP_CORE_EXPORT Parameters
|
||||
RTABMAP_PARAM(Vis, CorFlowIterations, int, 30, uFormat("[%s=1] See cv::calcOpticalFlowPyrLK(). Used for optical flow approach.", kVisCorType().c_str()));
|
||||
RTABMAP_PARAM(Vis, CorFlowEps, float, 0.01, uFormat("[%s=1] See cv::calcOpticalFlowPyrLK(). Used for optical flow approach.", kVisCorType().c_str()));
|
||||
RTABMAP_PARAM(Vis, CorFlowMaxLevel, int, 3, uFormat("[%s=1] See cv::calcOpticalFlowPyrLK(). Used for optical flow approach.", kVisCorType().c_str()));
|
||||
RTABMAP_PARAM(Vis, CorFlowGpu, bool, false, uFormat("[%s=1] Enable GPU version of the optical flow approach (only available if OpenCV is built with CUDA).", kVisCorType().c_str()));
|
||||
#if defined(RTABMAP_G2O) || defined(RTABMAP_ORB_SLAM)
|
||||
RTABMAP_PARAM(Vis, CorFlowUseMinEigenVals, bool, true, uFormat("[%s=1] See cv::calcOpticalFlowPyrLK(). Used for optical flow approach. Use minimum eigen values as an error measure, otherwise L1 distance between patches is used as an error measure.", kVisCorType().c_str()));
|
||||
RTABMAP_PARAM(Vis, CorFlowMinEigThreshold, float, 1e-4, uFormat("[%s=true] If the minimum eigenvalue of a feature's spatial gradient matrix is less than this threshold, then the feature is filtered out.", kVisCorFlowUseMinEigenVals().c_str()));
|
||||
RTABMAP_PARAM(Vis, CorFlowErrorThreshold, float, 20, uFormat("[%s=false] Filter out features with error greater than this threshold.", kVisCorFlowUseMinEigenVals().c_str()));
|
||||
RTABMAP_PARAM(Vis, CorFlowGpu, bool, false, uFormat("[%s=1] Enable GPU version of the optical flow approach (only available if OpenCV is built with CUDA). Note that %s is not used in the GPU implementation.", kVisCorType().c_str(), kVisCorFlowUseMinEigenVals().c_str()));
|
||||
#if defined(RTABMAP_G2O) || defined(RTABMAP_ORB_SLAM)
|
||||
RTABMAP_PARAM(Vis, BundleAdjustment, int, 1, "Optimization with bundle adjustment: 0=disabled, 1=g2o, 2=cvsba, 3=Ceres.");
|
||||
#else
|
||||
RTABMAP_PARAM(Vis, BundleAdjustment, int, 0, "Optimization with bundle adjustment: 0=disabled, 1=g2o, 2=cvsba, 3=Ceres.");
|
||||
@@ -807,7 +838,10 @@ class RTABMAP_CORE_EXPORT Parameters
|
||||
RTABMAP_PARAM(Stereo, OpticalFlow, bool, true, "Use optical flow to find stereo correspondences, otherwise a simple block matching approach is used.");
|
||||
RTABMAP_PARAM(Stereo, SSD, bool, true, uFormat("[%s=false] Use Sum of Squared Differences (SSD) window, otherwise Sum of Absolute Differences (SAD) window is used.", kStereoOpticalFlow().c_str()));
|
||||
RTABMAP_PARAM(Stereo, Eps, double, 0.01, uFormat("[%s=true] Epsilon stop criterion.", kStereoOpticalFlow().c_str()));
|
||||
RTABMAP_PARAM(Stereo, Gpu, bool, false, uFormat("[%s=true] Enable GPU version of the optical flow approach (only available if OpenCV is built with CUDA).", kStereoOpticalFlow().c_str()));
|
||||
RTABMAP_PARAM(Stereo, UseMinEigenVals, bool, true, uFormat("[%s=true] Use minimum eigen values as an error measure, otherwise L1 distance between patches is used as an error measure.", kStereoOpticalFlow().c_str()));
|
||||
RTABMAP_PARAM(Stereo, MinEigThreshold, double, 1e-4, uFormat("[%s=true] If the minimum eigenvalue of a feature's spatial gradient matrix is less than this threshold, then the feature is filtered out.", kStereoUseMinEigenVals().c_str()));
|
||||
RTABMAP_PARAM(Stereo, ErrorThreshold, double, 50, uFormat("[%s=false] Filter out features with error greater than this threshold.", kStereoUseMinEigenVals().c_str()));
|
||||
RTABMAP_PARAM(Stereo, Gpu, bool, false, uFormat("[%s=true] Enable GPU version of the optical flow approach (only available if OpenCV is built with CUDA). Note that %s is not used in the GPU implementation.", kStereoOpticalFlow().c_str(), kStereoUseMinEigenVals().c_str()));
|
||||
|
||||
RTABMAP_PARAM(Stereo, DenseStrategy, int, 0, "0=cv::StereoBM, 1=cv::StereoSGBM");
|
||||
|
||||
@@ -883,19 +917,29 @@ class RTABMAP_CORE_EXPORT Parameters
|
||||
RTABMAP_PARAM(GridGlobal, ProbClampingMax, float, 0.971, "Probability clamping maximum (value between 0 and 1).");
|
||||
RTABMAP_PARAM(GridGlobal, FloodFillDepth, unsigned int, 0, "Flood fill filter (0=disabled), used to remove empty cells outside the map. The flood fill is done at the specified depth (between 1 and 16) of the OctoMap.");
|
||||
|
||||
RTABMAP_PARAM(Marker, Dictionary, int, 0, "Dictionary to use: DICT_ARUCO_4X4_50=0, DICT_ARUCO_4X4_100=1, DICT_ARUCO_4X4_250=2, DICT_ARUCO_4X4_1000=3, DICT_ARUCO_5X5_50=4, DICT_ARUCO_5X5_100=5, DICT_ARUCO_5X5_250=6, DICT_ARUCO_5X5_1000=7, DICT_ARUCO_6X6_50=8, DICT_ARUCO_6X6_100=9, DICT_ARUCO_6X6_250=10, DICT_ARUCO_6X6_1000=11, DICT_ARUCO_7X7_50=12, DICT_ARUCO_7X7_100=13, DICT_ARUCO_7X7_250=14, DICT_ARUCO_7X7_1000=15, DICT_ARUCO_ORIGINAL = 16, DICT_APRILTAG_16h5=17, DICT_APRILTAG_25h9=18, DICT_APRILTAG_36h10=19, DICT_APRILTAG_36h11=20");
|
||||
RTABMAP_PARAM(Marker, Length, float, 0, "The length (m) of the markers' side. 0 means automatic marker length estimation using the depth image (the camera should look at the marker perpendicularly for initialization).");
|
||||
RTABMAP_PARAM(Marker, Strategy, int, 0, "Marker detection implementation: 0=OpenCV, 1=AprilTag");
|
||||
RTABMAP_PARAM(Marker, Dictionary, int, 0, "Dictionary to use: DICT_ARUCO_4X4_50=0, DICT_ARUCO_4X4_100=1, DICT_ARUCO_4X4_250=2, DICT_ARUCO_4X4_1000=3, DICT_ARUCO_5X5_50=4, DICT_ARUCO_5X5_100=5, DICT_ARUCO_5X5_250=6, DICT_ARUCO_5X5_1000=7, DICT_ARUCO_6X6_50=8, DICT_ARUCO_6X6_100=9, DICT_ARUCO_6X6_250=10, DICT_ARUCO_6X6_1000=11, DICT_ARUCO_7X7_50=12, DICT_ARUCO_7X7_100=13, DICT_ARUCO_7X7_250=14, DICT_ARUCO_7X7_1000=15, DICT_ARUCO_ORIGINAL = 16, DICT_APRILTAG_16h5=17, DICT_APRILTAG_25h9=18, DICT_APRILTAG_36h10=19, DICT_APRILTAG_36h11=20, DICT_ARUCO_MIP_36H12=21");
|
||||
RTABMAP_PARAM(Marker, Length, float, 0, "The length (m) of the markers' side. Value <=0 means automatic marker length estimation using the depth image (the camera should look at the marker perpendicularly for initialization). If 0, the length is estimated only on the first marker detected, then re-used for all next detections (i.e., this assumes that markers have all the same length). With <0, the length is estimated once for each unique marker, then re-used for next detections with the same marker ID.");
|
||||
RTABMAP_PARAM_STR(Marker, Lengths, "", uFormat("List of markers to detect. Format is the marker's ID followed by its length (in meters), multiple markers are separated by a vertical line (\"id1 length|id2 length\"). We can also define a range of markers with \"id1:id2 length\" (id2 included). If empty, all markers of the chosen dictionary can be detected and their length is set/estimated based on %s. For example, to detect markers 12 and 14 with lengths of 8 and 15 cm respectively, and all markers between 30 and 40 with a length of 10 cm, set \"12 0.08|14 0.15|30:40 0.1\".", kMarkerLength().c_str()).c_str());
|
||||
RTABMAP_PARAM(Marker, MaxDepthError, float, 0.01, uFormat("Maximum depth error between all corners of a marker when estimating the marker length (when %s is 0). The smaller it is, the more perpendicular the camera should be toward the marker to initialize the length.", kMarkerLength().c_str()));
|
||||
RTABMAP_PARAM(Marker, VarianceLinear, float, 0.001, uFormat("Linear variance to set on marker detections. If %s is enabled and %s=2 (GTSAM): it is the variance of the range factor, with 9999 to disable range factor and to do only bearing.", kMarkerVarianceOrientationIgnored().c_str(), kOptimizerStrategy().c_str()));
|
||||
RTABMAP_PARAM(Marker, VarianceAngular, float, 0.01, uFormat("Angular variance to set on marker detections. If %s is enabled, it is ignored with %s=1 (g2o) and it corresponds to bearing variance with %s=2 (GTSAM).", kMarkerVarianceOrientationIgnored().c_str(), kOptimizerStrategy().c_str(), kOptimizerStrategy().c_str()));
|
||||
RTABMAP_PARAM(Marker, VarianceOrientationIgnored, bool, false, uFormat("When this setting is false, the landmark's orientation is optimized during graph optimization. When this setting is true, only the position of the landmark is optimized. This can be useful when the landmark's orientation estimation is not reliable. Note that for %s=1 (g2o), only %s needs be set if we ignore orientation. For %s=2 (GTSAM), instead of optimizing the landmark's position directly, a bearing/range factor is used, with %s as the variance of the range factor (with 9999 to optimize the position with only a bearing factor) and %s as the variance of the bearing factor (pitch/yaw).", kOptimizerStrategy().c_str(), kMarkerVarianceLinear().c_str(), kOptimizerStrategy().c_str(), kMarkerVarianceLinear().c_str(), kMarkerVarianceAngular().c_str()));
|
||||
RTABMAP_PARAM(Marker, CornerRefinementMethod, int, 0, "Corner refinement method (0: None, 1: Subpixel, 2:contour, 3: AprilTag2). For OpenCV <3.3.0, this is \"doCornerRefinement\" parameter: set 0 for false and 1 for true.");
|
||||
RTABMAP_PARAM(Marker, MaxRange, float, 0.0, "Maximum range in which markers will be detected. <=0 for unlimited range.");
|
||||
RTABMAP_PARAM(Marker, MinRange, float, 0.0, "Miniminum range in which markers will be detected. <=0 for unlimited range.");
|
||||
RTABMAP_PARAM_STR(Marker, Priors, "", "World prior locations of the markers. The map will be transformed in marker's world frame when a tag is detected. Format is the marker's ID followed by its position (angles in rad), markers are separated by vertical line (\"id1 x y z roll pitch yaw|id2 x y z roll pitch yaw\"). Example: \"1 0 0 1 0 0 0|2 1 0 1 0 0 1.57\" (marker 2 is 1 meter forward than marker 1 with 90 deg yaw rotation).");
|
||||
RTABMAP_PARAM_STR(Marker, Priors, "", "World prior locations of the markers. The map will be transformed in marker's world frame when a tag is detected. Format is the marker's ID followed by its position (angles in rad), multiple markers are separated by vertical line (\"id1 x y z roll pitch yaw|id2 x y z roll pitch yaw\"). Example: \"1 0 0 1 0 0 0|2 1 0 1 0 0 1.57\" (marker 2 is 1 meter forward than marker 1 with 90 deg yaw rotation).");
|
||||
RTABMAP_PARAM(Marker, PriorsVarianceLinear, float, 0.001, "Linear variance to set on marker priors.");
|
||||
RTABMAP_PARAM(Marker, PriorsVarianceAngular, float, 0.001, "Angular variance to set on marker priors.");
|
||||
|
||||
RTABMAP_PARAM(MarkerAprilTag, NThreads, int, 1, "How many threads should be used?");
|
||||
RTABMAP_PARAM(MarkerAprilTag, QuadDecimate, float, 1.0, "Detection of quads can be done on a lower-resolution image, improving speed at a cost of pose accuracy and a slight decrease in detection rate. Decoding the binary payload is still done at full resolution.");
|
||||
RTABMAP_PARAM(MarkerAprilTag, QuadSigma, float, 0.0, "What Gaussian blur should be applied to the segmented image (used for quad detection?) Parameter is the standard deviation in pixels. Very noisy images benefit from non-zero values (e.g. 0.8).");
|
||||
RTABMAP_PARAM(MarkerAprilTag, RefineEdges, bool, true, uFormat("When true, the edges of the each quad are adjusted to \"snap to\" strong gradients nearby. This is useful when decimation is employed, as it can increase the quality of the initial quad estimate substantially. Generally recommended to be on (true). Very computationally inexpensive. Option is ignored if %s = 1.", kMarkerAprilTagQuadDecimate().c_str()));
|
||||
RTABMAP_PARAM(MarkerAprilTag, DecodeSharpening, double, 0.25, "How much sharpening should be done to decoded images? This can help decode small tags but may or may not help in odd lighting conditions or low light conditions.");
|
||||
RTABMAP_PARAM(MarkerAprilTag, Debug, bool, false, uFormat("When true, write a variety of debugging images to the working directory where the app started (not %s) at various stages through the detection process. (Somewhat slow).", kRtabmapWorkingDirectory().c_str()));
|
||||
|
||||
RTABMAP_PARAM(MarkerOpenCV, CornerRefinementMethod, int, 0, "Corner refinement method for OpenCV strategy (0: None, 1: Subpixel, 2:contour, 3: AprilTag2). For OpenCV <3.3.0, this is \"doCornerRefinement\" parameter: set 0 for false and 1 for true.");
|
||||
|
||||
RTABMAP_PARAM(ImuFilter, MadgwickGain, double, 0.1, "Gain of the filter. Higher values lead to faster convergence but more noise. Lower values lead to slower convergence but smoother signal, belongs in [0, 1].");
|
||||
RTABMAP_PARAM(ImuFilter, MadgwickZeta, double, 0.0, "Gyro drift gain (approx. rad/s), belongs in [-1, 1].");
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ public:
|
||||
inliersMeanDistance(0.0f),
|
||||
inliersDistribution(0.0f),
|
||||
matches(0),
|
||||
variance(0.0f),
|
||||
icpInliersRatio(0),
|
||||
icpTranslation(0.0f),
|
||||
icpRotation(0.0f),
|
||||
@@ -64,6 +65,7 @@ public:
|
||||
output.inliersDistribution = inliersDistribution;
|
||||
output.matches = matches;
|
||||
output.matchesPerCam = matchesPerCam;
|
||||
output.variance = variance;
|
||||
output.icpInliersRatio = icpInliersRatio;
|
||||
output.icpTranslation = icpTranslation;
|
||||
output.icpRotation = icpRotation;
|
||||
@@ -85,6 +87,7 @@ public:
|
||||
float inliersDistribution;
|
||||
std::vector<int> inliersIDs;
|
||||
int matches;
|
||||
float variance;
|
||||
std::vector<int> matchesIDs;
|
||||
std::vector<int> projectedIDs; // "From" IDs
|
||||
std::vector<int> inliersPerCam;
|
||||
|
||||
@@ -91,6 +91,9 @@ private:
|
||||
float _flowEps;
|
||||
int _flowMaxLevel;
|
||||
bool _flowGpu;
|
||||
bool _flowUseMinEigenVals;
|
||||
float _flowMinEigThreshold;
|
||||
float _flowErrorThreshold;
|
||||
float _nndr;
|
||||
int _nnType;
|
||||
bool _gmsWithRotation;
|
||||
|
||||
@@ -35,6 +35,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include "rtabmap/core/Statistics.h"
|
||||
#include "rtabmap/core/Link.h"
|
||||
#include "rtabmap/core/ProgressState.h"
|
||||
#include "rtabmap/core/Graph.h"
|
||||
|
||||
#include <opencv2/core/core.hpp>
|
||||
#include <list>
|
||||
@@ -209,7 +210,8 @@ public:
|
||||
bool intraSession = true,
|
||||
bool interSession = true,
|
||||
const ProgressState * state = 0,
|
||||
float clusterRadiusMin = 0.0f);
|
||||
float clusterRadiusMin = 0.0f,
|
||||
int toFromMapId = -1);
|
||||
bool globalBundleAdjustment(
|
||||
int optimizerType = 1 /*g2o*/,
|
||||
bool rematchFeatures = true,
|
||||
@@ -263,6 +265,13 @@ private:
|
||||
std::multimap<int, Link> * constraints = 0,
|
||||
double * error = 0,
|
||||
int * iterationsDone = 0) const;
|
||||
std::list<std::pair<int, int> > repairGraph(
|
||||
graph::MaxGraphErrors & maxGraphErrors,
|
||||
std::map<int, Transform> & poses,
|
||||
std::multimap<int, Link> & constraints,
|
||||
double & optimizationError,
|
||||
int & optimizationIterations,
|
||||
cv::Mat & optimizationCovariance);
|
||||
void updateGoalIndex();
|
||||
bool computePath(int targetNode, std::map<int, Transform> nodes, const std::multimap<int, rtabmap::Link> & constraints);
|
||||
|
||||
@@ -319,6 +328,7 @@ private:
|
||||
std::string _databasePath;
|
||||
bool _optimizeFromGraphEnd;
|
||||
float _optimizationMaxError;
|
||||
float _optimizationMaxErrorRepairRadius;
|
||||
bool _startNewMapOnLoopClosure;
|
||||
bool _startNewMapOnGoodSignature;
|
||||
float _goalReachedRadius; // meters
|
||||
@@ -378,6 +388,7 @@ private:
|
||||
std::map<int, Transform> _odomCachePoses; // used in localization mode to reject loop closures
|
||||
std::multimap<int, Link> _odomCacheConstraints; // used in localization mode to reject loop closures
|
||||
std::map<int, Transform> _markerPriors;
|
||||
std::pair<int, int> _lastRejectedLoopClosureIds;
|
||||
|
||||
std::set<int> _nodesToRepublish;
|
||||
|
||||
|
||||
@@ -106,7 +106,6 @@ private:
|
||||
unsigned int _dataBufferMaxSize;
|
||||
float _rate;
|
||||
bool _createIntermediateNodes;
|
||||
UTimer * _frameRateTimer;
|
||||
double _previousStamp;
|
||||
|
||||
Rtabmap * _rtabmap;
|
||||
|
||||
@@ -212,6 +212,12 @@ public:
|
||||
_userDataCompressed.empty() &&
|
||||
_keypoints.size() == 0 &&
|
||||
_descriptors.empty() &&
|
||||
_groundCellsRaw.empty() &&
|
||||
_groundCellsCompressed.empty() &&
|
||||
_obstacleCellsRaw.empty() &&
|
||||
_obstacleCellsCompressed.empty() &&
|
||||
_emptyCellsRaw.empty() &&
|
||||
_emptyCellsCompressed.empty() &&
|
||||
imu_.empty());
|
||||
}
|
||||
|
||||
@@ -309,8 +315,6 @@ public:
|
||||
const cv::Mat & empty,
|
||||
float cellSize,
|
||||
const cv::Point3f & viewPoint);
|
||||
// remove raw occupancy grids
|
||||
void clearOccupancyGridRaw() {_groundCellsRaw = cv::Mat(); _obstacleCellsRaw = cv::Mat();}
|
||||
const cv::Mat & gridGroundCellsRaw() const {return _groundCellsRaw;}
|
||||
const cv::Mat & gridGroundCellsCompressed() const {return _groundCellsCompressed;}
|
||||
const cv::Mat & gridObstacleCellsRaw() const {return _obstacleCellsRaw;}
|
||||
@@ -355,12 +359,12 @@ public:
|
||||
* Clear compressed rgb/depth (left/right) images, compressed laser scan and compressed user data.
|
||||
* Raw data are kept is set.
|
||||
*/
|
||||
void clearCompressedData(bool images = true, bool scan = true, bool userData = true);
|
||||
void clearCompressedData(bool images = true, bool scan = true, bool userData = true, bool occupancyGrid = true);
|
||||
/**
|
||||
* Clear raw rgb/depth (left/right) images, raw laser scan and raw user data.
|
||||
* Compressed data are kept is set.
|
||||
*/
|
||||
void clearRawData(bool images = true, bool scan = true, bool userData = true);
|
||||
void clearRawData(bool images = true, bool scan = true, bool userData = true, bool occupancyGrid = true);
|
||||
|
||||
bool isPointVisibleFromCameras(const cv::Point3f & pt) const; // assuming point is in robot frame
|
||||
|
||||
|
||||
@@ -67,6 +67,7 @@ class RTABMAP_CORE_EXPORT Statistics
|
||||
RTABMAP_STATS(Loop, Visual_inliers,);
|
||||
RTABMAP_STATS(Loop, Visual_inliers_ratio,);
|
||||
RTABMAP_STATS(Loop, Visual_matches,);
|
||||
RTABMAP_STATS(Loop, Visual_variance,);
|
||||
RTABMAP_STATS(Loop, Distance_since_last_loc, m);
|
||||
RTABMAP_STATS(Loop, Last_id,);
|
||||
RTABMAP_STATS(Loop, Optimization_max_error, m);
|
||||
@@ -75,6 +76,14 @@ class RTABMAP_CORE_EXPORT Statistics
|
||||
RTABMAP_STATS(Loop, Optimization_max_ang_error_ratio, );
|
||||
RTABMAP_STATS(Loop, Optimization_error, );
|
||||
RTABMAP_STATS(Loop, Optimization_iterations, );
|
||||
RTABMAP_STATS(Loop, Optimization_max_error_from_id, );
|
||||
RTABMAP_STATS(Loop, Optimization_max_error_to_id, );
|
||||
RTABMAP_STATS(Loop, Optimization_max_ang_error_from_id, );
|
||||
RTABMAP_STATS(Loop, Optimization_max_ang_error_to_id, );
|
||||
RTABMAP_STATS(Loop, Optimization_max_error_removed_from_id, );
|
||||
RTABMAP_STATS(Loop, Optimization_max_error_removed_to_id, );
|
||||
RTABMAP_STATS(Loop, Optimization_max_error_removed_count, );
|
||||
RTABMAP_STATS(Loop, Optimization_factors, );
|
||||
RTABMAP_STATS(Loop, Linear_variance,);
|
||||
RTABMAP_STATS(Loop, Angular_variance,);
|
||||
RTABMAP_STATS(Loop, Landmark_detected,);
|
||||
|
||||
@@ -96,16 +96,23 @@ public:
|
||||
#endif
|
||||
|
||||
float epsilon() const {return epsilon_;}
|
||||
bool usingMinEigenVals() const {return useMinEigenVals_;}
|
||||
float minEigThreshold() const {return minEigThreshold_;}
|
||||
float errorThreshold() const {return errorThreshold_;}
|
||||
virtual bool isGpuEnabled() const;
|
||||
|
||||
private:
|
||||
void updateStatus(
|
||||
const std::vector<cv::Point2f> & leftCorners,
|
||||
const std::vector<cv::Point2f> & rightCorners,
|
||||
std::vector<unsigned char> & status) const;
|
||||
std::vector<unsigned char> & status,
|
||||
std::vector<float> err = {}) const;
|
||||
|
||||
private:
|
||||
float epsilon_;
|
||||
bool useMinEigenVals_;
|
||||
float minEigThreshold_;
|
||||
float errorThreshold_;
|
||||
bool gpu_;
|
||||
};
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ public:
|
||||
std::vector<int> findNN(const std::list<VisualWord *> & vws) const;
|
||||
std::vector<int> findNN(const cv::Mat & descriptors) const;
|
||||
|
||||
void addWordRef(int wordId, int signatureId);
|
||||
bool addWordRef(int wordId, int signatureId);
|
||||
void removeAllWordRef(int wordId, int signatureId);
|
||||
const VisualWord * getWord(int id) const;
|
||||
VisualWord * getUnusedWord(int id) const;
|
||||
|
||||
@@ -102,10 +102,11 @@ public:
|
||||
}
|
||||
|
||||
// 0=Raw, 1=RGBD-SLAM motion capture (10=without change of coordinate frame, 11=10+ID), 2=KITTI, 3=TORO, 4=g2o, 5=NewCollege(t,x,y), 6=Malaga Urban GPS, 7=St Lucia INS, 8=Karlsruhe, 9=EuRoC MAV, 12=rgbd_bonn
|
||||
void setGroundTruthPath(const std::string & filePath, int format = 0)
|
||||
void setGroundTruthPath(const std::string & filePath, int format = 0, const Transform & localTransform = Transform::getIdentity())
|
||||
{
|
||||
_groundTruthPath = filePath;
|
||||
_groundTruthFormat = format;
|
||||
_groundTruthLocalTransform = localTransform;
|
||||
}
|
||||
|
||||
void setMaxPoseTimeDiff(double diff) {_maxPoseTimeDiff = diff;}
|
||||
@@ -164,6 +165,7 @@ private:
|
||||
int _odometryFormat;
|
||||
std::string _groundTruthPath;
|
||||
int _groundTruthFormat;
|
||||
Transform _groundTruthLocalTransform;
|
||||
double _maxPoseTimeDiff;
|
||||
|
||||
std::list<double> _stamps;
|
||||
|
||||
@@ -52,10 +52,10 @@ public:
|
||||
// deviceId can be either an index (e.g., "0"), an UID (e.g, "2-1-2" or "gmsl-1") or a serial ("AAA6454S")
|
||||
CameraOrbbecSDK(
|
||||
std::string deviceId = "",
|
||||
int colorWidth = 800,
|
||||
int colorHeight = 600,
|
||||
int depthWidth = 800,
|
||||
int depthHeight = 600,
|
||||
unsigned int colorWidth = 800,
|
||||
unsigned int colorHeight = 600,
|
||||
unsigned int depthWidth = 800,
|
||||
unsigned int depthHeight = 600,
|
||||
float imageRate = 0.0f,
|
||||
const Transform & localTransform = Transform::getIdentity());
|
||||
virtual ~CameraOrbbecSDK();
|
||||
@@ -79,10 +79,10 @@ private:
|
||||
|
||||
#ifdef RTABMAP_ORBBEC_SDK
|
||||
std::string deviceId_;
|
||||
int colorWidth_;
|
||||
int colorHeight_;
|
||||
int depthWidth_;
|
||||
int depthHeight_;
|
||||
unsigned int colorWidth_;
|
||||
unsigned int colorHeight_;
|
||||
unsigned int depthWidth_;
|
||||
unsigned int depthHeight_;
|
||||
ob::Pipeline * pipeline_;
|
||||
ob::Pipeline * imuPipeline_;
|
||||
ob::Align * alignFilter_;
|
||||
|
||||
@@ -146,6 +146,7 @@ private:
|
||||
Transform dualExtrinsics_;
|
||||
std::string jsonConfig_;
|
||||
bool closing_;
|
||||
bool playback_;
|
||||
|
||||
static Transform realsense2PoseRotation_;
|
||||
static Transform realsense2PoseRotationInv_;
|
||||
|
||||
@@ -57,7 +57,6 @@ protected:
|
||||
private:
|
||||
cv::Mat map_;
|
||||
cv::Mat mapInfo_;
|
||||
std::map<int, std::pair<int, int> > cellCount_; //<node Id, cells>
|
||||
|
||||
float minMapSize_;
|
||||
bool erode_;
|
||||
|
||||
@@ -41,11 +41,11 @@ LaserScan laserScanFromPointCloud(const PointCloud2T & cloud, bool filterNaNs, b
|
||||
return LaserScan();
|
||||
}
|
||||
//determine the output type
|
||||
int fieldStates[8] = {0}; // x,y,z,normal_x,normal_y,normal_z,rgb,intensity
|
||||
int fieldStates[10] = {0}; // x,y,z,normal_x,normal_y,normal_z,rgb,intensity,time,ring
|
||||
#if PCL_VERSION_COMPARE(>=, 1, 10, 0)
|
||||
std::uint32_t fieldOffsets[8] = {0};
|
||||
std::uint32_t fieldOffsets[10] = {0};
|
||||
#else
|
||||
pcl::uint32_t fieldOffsets[8] = {0};
|
||||
pcl::uint32_t fieldOffsets[10] = {0};
|
||||
#endif
|
||||
for(unsigned int i=0; i<cloud.fields.size(); ++i)
|
||||
{
|
||||
@@ -102,6 +102,42 @@ LaserScan laserScanFromPointCloud(const PointCloud2T & cloud, bool filterNaNs, b
|
||||
fieldStates[7] = 1;
|
||||
fieldOffsets[7] = cloud.fields[i].offset;
|
||||
}
|
||||
else if(cloud.fields[i].name.compare("time") == 0)
|
||||
{
|
||||
if(cloud.fields[i].datatype != pcl::PCLPointField::FLOAT32)
|
||||
{
|
||||
static bool warningShown = false;
|
||||
if(!warningShown)
|
||||
{
|
||||
UWARN("The input scan cloud has an \"time\" field "
|
||||
"but the datatype (%d) is not supported. Time will be ignored. "
|
||||
"This message is only shown once.", cloud.fields[i].datatype);
|
||||
warningShown = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
fieldStates[8] = 1;
|
||||
fieldOffsets[8] = cloud.fields[i].offset;
|
||||
}
|
||||
else if(cloud.fields[i].name.compare("ring") == 0)
|
||||
{
|
||||
if(cloud.fields[i].datatype != pcl::PCLPointField::UINT16)
|
||||
{
|
||||
static bool warningShown = false;
|
||||
if(!warningShown)
|
||||
{
|
||||
UWARN("The input scan cloud has an \"ring\" field "
|
||||
"but the datatype (%d) is not supported. Ring will be ignored. "
|
||||
"This message is only shown once.", cloud.fields[i].datatype);
|
||||
warningShown = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
fieldStates[9] = 1;
|
||||
fieldOffsets[9] = cloud.fields[i].offset;
|
||||
}
|
||||
else
|
||||
{
|
||||
UDEBUG("Ignoring \"%s\" field", cloud.fields[i].name.c_str());
|
||||
@@ -117,6 +153,8 @@ LaserScan laserScanFromPointCloud(const PointCloud2T & cloud, bool filterNaNs, b
|
||||
bool hasNormals = fieldStates[3] || fieldStates[4] || fieldStates[5];
|
||||
bool hasIntensity = fieldStates[7];
|
||||
bool hasRGB = !hasIntensity&&fieldStates[6];
|
||||
bool hasTime = hasIntensity&&fieldStates[8];
|
||||
bool hasRing = hasIntensity&&fieldStates[9];
|
||||
bool is3D = fieldStates[0] && fieldStates[1] && fieldStates[2];
|
||||
|
||||
LaserScan::Format format;
|
||||
@@ -140,7 +178,18 @@ LaserScan laserScanFromPointCloud(const PointCloud2T & cloud, bool filterNaNs, b
|
||||
}
|
||||
else if(!hasNormals && hasIntensity)
|
||||
{
|
||||
format = LaserScan::kXYZI;
|
||||
if(hasTime && hasRing)
|
||||
{
|
||||
format = LaserScan::kXYZIRT;
|
||||
}
|
||||
else if(hasTime)
|
||||
{
|
||||
format = LaserScan::kXYZIT;
|
||||
}
|
||||
else
|
||||
{
|
||||
format = LaserScan::kXYZI;
|
||||
}
|
||||
}
|
||||
else if(!hasNormals && hasRGB)
|
||||
{
|
||||
@@ -183,6 +232,7 @@ LaserScan laserScanFromPointCloud(const PointCloud2T & cloud, bool filterNaNs, b
|
||||
transformRot = transform.rotation();
|
||||
}
|
||||
int oi=0;
|
||||
UASSERT(cloud.height == 1 || cloud.row_step != 0);
|
||||
for (uint32_t row = 0; row < (uint32_t)cloud.height; ++row)
|
||||
{
|
||||
const uint8_t* row_data = &cloud.data[row * cloud.row_step];
|
||||
@@ -242,26 +292,45 @@ LaserScan laserScanFromPointCloud(const PointCloud2T & cloud, bool filterNaNs, b
|
||||
{
|
||||
ptr[0] = *(float*)(msg_data + fieldOffsets[0]);
|
||||
ptr[1] = *(float*)(msg_data + fieldOffsets[1]);
|
||||
ptr[2] = *(float*)(msg_data + fieldOffsets[3]);
|
||||
ptr[3] = *(float*)(msg_data + fieldOffsets[4]);
|
||||
ptr[4] = *(float*)(msg_data + fieldOffsets[5]);
|
||||
if(format == LaserScan::kXYZIT)
|
||||
{
|
||||
ptr[2] = *(float*)(msg_data + fieldOffsets[2]);
|
||||
ptr[3] = *(float*)(msg_data + fieldOffsets[7]);
|
||||
ptr[4] = *(float*)(msg_data + fieldOffsets[8]);
|
||||
}
|
||||
else // kXYNormal
|
||||
{
|
||||
ptr[2] = *(float*)(msg_data + fieldOffsets[3]);
|
||||
ptr[3] = *(float*)(msg_data + fieldOffsets[4]);
|
||||
ptr[4] = *(float*)(msg_data + fieldOffsets[5]);
|
||||
}
|
||||
valid = uIsFinite(ptr[0]) && uIsFinite(ptr[1]) && uIsFinite(ptr[2]) && uIsFinite(ptr[3]) && uIsFinite(ptr[4]);
|
||||
}
|
||||
else if(laserScan.channels() == 6)
|
||||
{
|
||||
ptr[0] = *(float*)(msg_data + fieldOffsets[0]);
|
||||
ptr[1] = *(float*)(msg_data + fieldOffsets[1]);
|
||||
if(format == LaserScan::kXYINormal)
|
||||
{
|
||||
ptr[2] = *(float*)(msg_data + fieldOffsets[7]);
|
||||
}
|
||||
else // XYZNormal
|
||||
if(format == LaserScan::kXYZIRT)
|
||||
{
|
||||
ptr[2] = *(float*)(msg_data + fieldOffsets[2]);
|
||||
ptr[3] = *(float*)(msg_data + fieldOffsets[7]);
|
||||
ptr[4] = float(*(unsigned short*)(msg_data + fieldOffsets[9])); // Convert 16U to float
|
||||
ptr[5] = *(float*)(msg_data + fieldOffsets[8]);
|
||||
}
|
||||
else // with normal
|
||||
{
|
||||
if(format == LaserScan::kXYINormal)
|
||||
{
|
||||
ptr[2] = *(float*)(msg_data + fieldOffsets[7]);
|
||||
}
|
||||
else // XYZNormal
|
||||
{
|
||||
ptr[2] = *(float*)(msg_data + fieldOffsets[2]);
|
||||
}
|
||||
ptr[3] = *(float*)(msg_data + fieldOffsets[3]);
|
||||
ptr[4] = *(float*)(msg_data + fieldOffsets[4]);
|
||||
ptr[5] = *(float*)(msg_data + fieldOffsets[5]);
|
||||
}
|
||||
ptr[3] = *(float*)(msg_data + fieldOffsets[3]);
|
||||
ptr[4] = *(float*)(msg_data + fieldOffsets[4]);
|
||||
ptr[5] = *(float*)(msg_data + fieldOffsets[5]);
|
||||
valid = uIsFinite(ptr[0]) && uIsFinite(ptr[1]) && uIsFinite(ptr[2]) && uIsFinite(ptr[3]) && uIsFinite(ptr[4]) && uIsFinite(ptr[5]);
|
||||
}
|
||||
else if(laserScan.channels() == 7)
|
||||
|
||||
@@ -31,6 +31,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <rtabmap/core/Odometry.h>
|
||||
#include <memory>
|
||||
#include <deque>
|
||||
#include <array>
|
||||
|
||||
#ifdef RTABMAP_CUVSLAM
|
||||
#include <cuvslam.h>
|
||||
@@ -51,6 +53,7 @@ public:
|
||||
|
||||
private:
|
||||
virtual Transform computeTransform(SensorData & image, const Transform & guess = Transform(), OdometryInfo * info = 0);
|
||||
virtual void cleanupCuVSLAMResources();
|
||||
|
||||
private:
|
||||
#ifdef RTABMAP_CUVSLAM
|
||||
@@ -65,9 +68,21 @@ private:
|
||||
bool lost_;
|
||||
bool tracking_;
|
||||
bool planar_constraints_;
|
||||
int multicam_mode_;
|
||||
Transform previous_pose_;
|
||||
double last_timestamp_;
|
||||
|
||||
// Configuration Thresholds
|
||||
double velocity_ratio_threshold_high_ = 1.5; // The maximum velocity ratio of guess / estimated velocity needed to detect lost state.
|
||||
double velocity_ratio_threshold_low_ = 0.5; // The minimum velocity ratio of guess / estimated velocity needed to detect lost state.
|
||||
double velocity_difference_threshold_ = 0.1; // The maximum velocity difference between the guess and the estimated velocity needed to detect lost state.
|
||||
double zero_estimated_velocity_threshold_ = 0.00001; // The minimum cuVSLAM estimated velocity needed to detect lost state.
|
||||
double min_landmarks_threshold_ = 30; // The minimum number of landmarks needed to start tracking after an initialization.
|
||||
|
||||
// Forward cuVLSAM covariance directly to RTAB-Map.
|
||||
// When true this disables covariance based lost detection.
|
||||
bool use_raw_covariance_ = false;
|
||||
|
||||
//visualization
|
||||
std::vector<CUVSLAM_Observation> observations_;
|
||||
std::vector<CUVSLAM_Landmark> landmarks_;
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
Copyright (c) 2010-2016, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the Universite de Sherbrooke nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef ODOMETRYLIOSAM_H_
|
||||
#define ODOMETRYLIOSAM_H_
|
||||
|
||||
#include <rtabmap/core/Odometry.h>
|
||||
|
||||
#ifdef RTABMAP_LIOSAM
|
||||
#include <Eigen/Core>
|
||||
#include <Eigen/Geometry>
|
||||
#include <vector>
|
||||
namespace lio_sam { class LioSamCore; }
|
||||
#endif
|
||||
|
||||
namespace rtabmap {
|
||||
|
||||
class RTABMAP_CORE_EXPORT OdometryLIOSAM : public Odometry
|
||||
{
|
||||
public:
|
||||
OdometryLIOSAM(const rtabmap::ParametersMap & parameters = rtabmap::ParametersMap());
|
||||
virtual ~OdometryLIOSAM();
|
||||
|
||||
virtual void reset(const Transform & initialPose = Transform::getIdentity());
|
||||
virtual Odometry::Type getType() {return Odometry::kTypeLIOSAM;}
|
||||
virtual bool canProcessAsyncIMU() const {return true;}
|
||||
|
||||
private:
|
||||
virtual Transform computeTransform(SensorData & data, const Transform & guess = Transform(), OdometryInfo * info = 0);
|
||||
|
||||
#ifdef RTABMAP_LIOSAM
|
||||
bool init(const Transform & imuLocalTransform, const Transform & lidarLocalTransform);
|
||||
#endif
|
||||
|
||||
private:
|
||||
#ifdef RTABMAP_LIOSAM
|
||||
lio_sam::LioSamCore * lioSam_;
|
||||
Transform lastPose_;
|
||||
bool lost_;
|
||||
float linVar_;
|
||||
float angVar_;
|
||||
ParametersMap parameters_;
|
||||
Transform imuLocalTransform_; // base_link -> imu_link (cached for deferred init)
|
||||
|
||||
// Buffered IMU samples received before initialization
|
||||
struct ImuSample {
|
||||
double stamp;
|
||||
Eigen::Vector3d acc;
|
||||
Eigen::Vector3d gyro;
|
||||
Eigen::Quaterniond orientation;
|
||||
};
|
||||
std::vector<ImuSample> imuBuffer_;
|
||||
#endif
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* ODOMETRYLIOSAM_H_ */
|
||||
@@ -41,6 +41,12 @@ public:
|
||||
static bool isCSparseAvailable();
|
||||
static bool isCholmodAvailable();
|
||||
|
||||
public:
|
||||
static bool loadGraph(
|
||||
const std::string & fileName,
|
||||
std::map<int, Transform> & poses,
|
||||
std::multimap<int, Link> & edgeConstraints);
|
||||
|
||||
public:
|
||||
OptimizerG2O(const ParametersMap & parameters = ParametersMap());
|
||||
virtual ~OptimizerG2O() {}
|
||||
|
||||
@@ -29,6 +29,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#define OPTIMIZERGTSAM_H_
|
||||
|
||||
#include <rtabmap/core/Optimizer.h>
|
||||
#include <tuple>
|
||||
|
||||
namespace gtsam {
|
||||
class ISAM2;
|
||||
@@ -58,25 +59,25 @@ public:
|
||||
double * finalError = 0,
|
||||
int * iterationsDone = 0);
|
||||
|
||||
// True when iSAM2 (incremental) backend is active.
|
||||
bool isIncremental() const {return isam2_ != 0;}
|
||||
|
||||
// Number of factors we believe are live in iSAM2.
|
||||
std::size_t getTrackedFactorsCount() const {
|
||||
return trackedFactors_.size() + (lastRootFactorIndex_.first != 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
// Live factor count reported by iSAM2 itself (should match getTrackedFactorsCount()).
|
||||
std::size_t getISAM2LiveFactorsCount() const;
|
||||
|
||||
private:
|
||||
int internalOptimizerType_;
|
||||
|
||||
gtsam::ISAM2 * isam2_;
|
||||
struct ConstraintToFactor {
|
||||
ConstraintToFactor(int _from, int _to, std::uint64_t _factorIndice)
|
||||
{
|
||||
from = _from;
|
||||
to = _to;
|
||||
factorIndice = _factorIndice;
|
||||
}
|
||||
int from;
|
||||
int to;
|
||||
std::uint64_t factorIndice;
|
||||
};
|
||||
|
||||
std::vector<ConstraintToFactor> lastAddedConstraints_;
|
||||
int lastSwitchId_;
|
||||
std::set<int> addedPoses_;
|
||||
std::map<int, bool> isLandmarkWithRotation_; // persists across iSAM2 incremental calls
|
||||
std::map<std::tuple<int, int, int>, std::uint64_t> trackedFactors_; // iSAM2 tracked constraints
|
||||
std::pair<int, std::uint64_t> lastRootFactorIndex_;
|
||||
};
|
||||
|
||||
|
||||
@@ -39,12 +39,26 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include <rtabmap/core/Parameters.h>
|
||||
#include <opencv2/core/core.hpp>
|
||||
#include <rtabmap/core/ProgressState.h>
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <list>
|
||||
|
||||
namespace rtabmap
|
||||
{
|
||||
|
||||
// Point type carrying xyz + intensity + ring (laser line index) + time
|
||||
// (per-point acquisition offset, seconds from the scan start). Matches the
|
||||
// layout expected by LIO-SAM's Velodyne feature extractor so it can be fed
|
||||
// directly via util3d::laserScanFromPointCloud().
|
||||
struct EIGEN_ALIGN16 PointXYZIRT
|
||||
{
|
||||
PCL_ADD_POINT4D;
|
||||
float intensity;
|
||||
std::uint16_t ring;
|
||||
float time;
|
||||
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
|
||||
};
|
||||
|
||||
namespace util3d
|
||||
{
|
||||
|
||||
@@ -297,6 +311,9 @@ LaserScan RTABMAP_CORE_EXPORT laserScanFromPointCloud(const pcl::PointCloud<pcl:
|
||||
// return CV_32FC4 (x,y,z,I)
|
||||
LaserScan RTABMAP_CORE_EXPORT laserScanFromPointCloud(const pcl::PointCloud<pcl::PointXYZI> & cloud, const Transform & transform = Transform(), bool filterNaNs = true);
|
||||
LaserScan RTABMAP_CORE_EXPORT laserScanFromPointCloud(const pcl::PointCloud<pcl::PointXYZI> & cloud, const pcl::IndicesPtr & indices, const Transform & transform = Transform(), bool filterNaNs = true);
|
||||
// return CV_32FC6 (x,y,z,I,ring,time)
|
||||
LaserScan RTABMAP_CORE_EXPORT laserScanFromPointCloud(const pcl::PointCloud<rtabmap::PointXYZIRT> & cloud, const Transform & transform = Transform(), bool filterNaNs = true);
|
||||
LaserScan RTABMAP_CORE_EXPORT laserScanFromPointCloud(const pcl::PointCloud<rtabmap::PointXYZIRT> & cloud, const pcl::IndicesPtr & indices, const Transform & transform = Transform(), bool filterNaNs = true);
|
||||
// return CV_32FC7 (x,y,z,rgb,normal_x,normal_y,normal_z)
|
||||
LaserScan RTABMAP_CORE_EXPORT laserScanFromPointCloud(const pcl::PointCloud<pcl::PointXYZRGB> & cloud, const pcl::PointCloud<pcl::Normal> & normals, const Transform & transform = Transform(), bool filterNaNs = true);
|
||||
LaserScan RTABMAP_CORE_EXPORT laserScanFromPointCloud(const pcl::PointCloud<pcl::PointXYZRGBNormal> & cloud, const Transform & transform = Transform(), bool filterNaNs = true);
|
||||
@@ -512,6 +529,15 @@ LaserScan RTABMAP_CORE_EXPORT deskew(
|
||||
} // namespace util3d
|
||||
} // namespace rtabmap
|
||||
|
||||
POINT_CLOUD_REGISTER_POINT_STRUCT(rtabmap::PointXYZIRT,
|
||||
(float, x, x)
|
||||
(float, y, y)
|
||||
(float, z, z)
|
||||
(float, intensity, intensity)
|
||||
(std::uint16_t, ring, ring)
|
||||
(float, time, time)
|
||||
)
|
||||
|
||||
#include "rtabmap/core/impl/util3d.hpp"
|
||||
|
||||
#endif /* UTIL3D_H_ */
|
||||
|
||||
@@ -80,6 +80,7 @@ std::map<int, cv::Point3f> RTABMAP_CORE_EXPORT generateWords3DMono(
|
||||
Transform & cameraTransform,
|
||||
float ransacReprojThreshold = 3.0f,
|
||||
float ransacConfidence = 0.99f,
|
||||
int varianceMedianRatio = 4,
|
||||
const std::map<int, cv::Point3f> & refGuess3D = std::map<int, cv::Point3f>(),
|
||||
double * variance = 0,
|
||||
std::vector<int> * matchesOut = 0);
|
||||
|
||||
+39
-29
@@ -98,6 +98,7 @@ SET(SRC_FILES
|
||||
odometry/OdometryORBSLAM3.cpp
|
||||
odometry/OdometryLOAM.cpp
|
||||
odometry/OdometryFLOAM.cpp
|
||||
odometry/OdometryLIOSAM.cpp
|
||||
odometry/OdometryMSCKF.cpp
|
||||
odometry/OdometryVINSFusion.cpp
|
||||
odometry/OdometryOpenVINS.cpp
|
||||
@@ -215,11 +216,21 @@ IF(TORCH_FOUND)
|
||||
${SRC_FILES}
|
||||
superpoint_torch/SuperPoint.cc
|
||||
)
|
||||
SET(INCLUDE_DIRS
|
||||
SET(INCLUDE_DIRS
|
||||
${TORCH_INCLUDE_DIRS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/superpoint_torch
|
||||
${INCLUDE_DIRS}
|
||||
)
|
||||
IF(WITH_PYTHON AND Python3_FOUND)
|
||||
SET(SRC_FILES
|
||||
${SRC_FILES}
|
||||
superpoint_rpautrat/SuperpointRpautrat.cpp
|
||||
)
|
||||
SET(INCLUDE_DIRS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/superpoint_rpautrat
|
||||
${INCLUDE_DIRS}
|
||||
)
|
||||
ENDIF(WITH_PYTHON AND Python3_FOUND)
|
||||
ENDIF(TORCH_FOUND)
|
||||
|
||||
IF(WITH_PYTHON AND Python3_FOUND)
|
||||
@@ -522,6 +533,13 @@ IF(FastCV_FOUND)
|
||||
)
|
||||
ENDIF(FastCV_FOUND)
|
||||
|
||||
IF(apriltag_FOUND)
|
||||
SET(LIBRARIES
|
||||
apriltag::apriltag
|
||||
${LIBRARIES}
|
||||
)
|
||||
ENDIF(apriltag_FOUND)
|
||||
|
||||
IF(opengv_FOUND)
|
||||
SET(LIBRARIES
|
||||
${LIBRARIES}
|
||||
@@ -594,6 +612,18 @@ IF(floam_FOUND)
|
||||
)
|
||||
ENDIF(floam_FOUND)
|
||||
|
||||
IF(lio_sam_FOUND)
|
||||
SET(INCLUDE_DIRS
|
||||
${INCLUDE_DIRS}
|
||||
${lio_sam_INCLUDE_DIRS}
|
||||
)
|
||||
link_directories(${lio_sam_LIBRARY_DIRS})
|
||||
SET(LIBRARIES
|
||||
${LIBRARIES}
|
||||
lio_sam_core
|
||||
)
|
||||
ENDIF(lio_sam_FOUND)
|
||||
|
||||
IF(ZED_FOUND)
|
||||
SET(INCLUDE_DIRS
|
||||
${INCLUDE_DIRS}
|
||||
@@ -781,37 +811,10 @@ IF(CUVSLAM_FOUND)
|
||||
ENDIF(CUVSLAM_FOUND)
|
||||
|
||||
IF(GTSAM_FOUND)
|
||||
# Make sure GTSAM is built with system Eigen, not the included one in its package
|
||||
IF(GTSAM_INCLUDE_DIR)
|
||||
SET(INCLUDE_DIRS
|
||||
${INCLUDE_DIRS}
|
||||
${GTSAM_INCLUDE_DIR}
|
||||
)
|
||||
ELSE()
|
||||
SET(INCLUDE_DIRS
|
||||
${INCLUDE_DIRS}
|
||||
${GTSAM_INCLUDE_DIRS}
|
||||
)
|
||||
ENDIF()
|
||||
SET(SRC_FILES
|
||||
${SRC_FILES}
|
||||
optimizer/gtsam/GravityFactor.cpp
|
||||
)
|
||||
IF(WIN32)
|
||||
# GTSAM should be built in STATIC on Windows to avoid "error C2338: THIS_METHOD_IS_ONLY_FOR_1x1_EXPRESSIONS" when building GTSAM
|
||||
add_definitions("-DGTSAM_IMPORT_STATIC")
|
||||
ENDIF(WIN32)
|
||||
SET(LIBRARIES
|
||||
${LIBRARIES}
|
||||
gtsam # Windows: Place static libs at the end
|
||||
gtsam
|
||||
)
|
||||
IF(WIN32)
|
||||
#explicitly add metis target on windows (after gtsam target)
|
||||
SET(LIBRARIES
|
||||
${LIBRARIES}
|
||||
metis
|
||||
)
|
||||
ENDIF(WIN32)
|
||||
ENDIF(GTSAM_FOUND)
|
||||
|
||||
IF(WITH_MADGWICK)
|
||||
@@ -838,6 +841,13 @@ SET(RESOURCES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/resources/backward_compatibility/DatabaseSchema_0_16_0.sql
|
||||
)
|
||||
|
||||
IF(TORCH_FOUND AND WITH_PYTHON AND Python3_FOUND)
|
||||
SET(RESOURCES
|
||||
${RESOURCES}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/superpoint_rpautrat/superpoint_to_torchscript.py
|
||||
)
|
||||
ENDIF()
|
||||
|
||||
foreach(arg ${RESOURCES})
|
||||
get_filename_component(filename ${arg} NAME)
|
||||
string(REPLACE "." "_" output ${filename})
|
||||
|
||||
+12
-3
@@ -39,7 +39,8 @@ namespace rtabmap
|
||||
Camera::Camera(float imageRate, const Transform & localTransform) :
|
||||
SensorCapture(imageRate, localTransform*CameraModel::opticalRotation()),
|
||||
imuFilter_(0),
|
||||
publishInterIMU_(false)
|
||||
publishInterIMU_(false),
|
||||
imuBaseFrameConversion_(false)
|
||||
{}
|
||||
|
||||
Camera::~Camera()
|
||||
@@ -52,15 +53,23 @@ bool Camera::initFromFile(const std::string & calibrationPath)
|
||||
return init(UDirectory::getDir(calibrationPath), uSplit(UFile::getName(calibrationPath), '.').front());
|
||||
}
|
||||
|
||||
void Camera::setInterIMUPublishing(bool enabled, IMUFilter * filter)
|
||||
void Camera::setInterIMUPublishing(bool enabled, IMUFilter * filter, bool baseFrameConversion)
|
||||
{
|
||||
publishInterIMU_ = enabled;
|
||||
delete imuFilter_;
|
||||
imuFilter_ = filter;
|
||||
imuBaseFrameConversion_ = baseFrameConversion;
|
||||
}
|
||||
|
||||
void Camera::postInterIMU(const IMU & imu, double stamp)
|
||||
void Camera::postInterIMU(const IMU & imu_in, double stamp)
|
||||
{
|
||||
IMU imu = imu_in;
|
||||
if(imuBaseFrameConversion_)
|
||||
{
|
||||
UASSERT(!imu.localTransform().isNull());
|
||||
imu.convertToBaseFrame();
|
||||
}
|
||||
|
||||
if(imuFilter_)
|
||||
{
|
||||
imuFilter_->update(
|
||||
|
||||
@@ -353,6 +353,22 @@ bool CameraModel::load(const std::string & filePath)
|
||||
data[0], data[1], data[2], data[3],
|
||||
data[4], data[5], data[6], data[7],
|
||||
data[8], data[9], data[10], data[11]);
|
||||
Transform detCheck = localTransform_.clone();
|
||||
localTransform_.normalizeRotation(); /// Normalize by default
|
||||
float det = detCheck.toEigen3f().linear().determinant();
|
||||
if(fabs(det - 1.0f) > 0.0001)
|
||||
{
|
||||
std::stringstream streamBefore, streamAfter;
|
||||
streamBefore << detCheck << std::endl;
|
||||
streamAfter << localTransform_ << std::endl;
|
||||
UWARN("The camera model's local_transform from \"%s\" doesn't "
|
||||
"have a normalized rotation matrix (dertminant=%f). We will normalize "
|
||||
"it for convenience.\nWas:\n%sNow\n%s",
|
||||
filePath.c_str(),
|
||||
det,
|
||||
streamBefore.str().c_str(),
|
||||
streamAfter.str().c_str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -45,7 +45,7 @@ DBDriver * DBDriver::create(const ParametersMap & parameters)
|
||||
|
||||
DBDriver::DBDriver(const ParametersMap & parameters) :
|
||||
_emptyTrashesTime(0),
|
||||
_timestampUpdate(true)
|
||||
_timestampUpdate(false)
|
||||
{
|
||||
this->parseParameters(parameters);
|
||||
}
|
||||
@@ -73,7 +73,15 @@ void DBDriver::closeConnection(bool save, const std::string & outputUrl)
|
||||
else
|
||||
{
|
||||
_trashesMutex.lock();
|
||||
for(auto & iter: _trashSignatures)
|
||||
{
|
||||
delete iter.second;
|
||||
}
|
||||
_trashSignatures.clear();
|
||||
for(auto & iter: _trashVisualWords)
|
||||
{
|
||||
delete iter.second;
|
||||
}
|
||||
_trashVisualWords.clear();
|
||||
_trashesMutex.unlock();
|
||||
}
|
||||
@@ -83,12 +91,12 @@ void DBDriver::closeConnection(bool save, const std::string & outputUrl)
|
||||
UDEBUG("");
|
||||
}
|
||||
|
||||
bool DBDriver::openConnection(const std::string & url, bool overwritten)
|
||||
bool DBDriver::openConnection(const std::string & url, bool overwritten, bool readOnly)
|
||||
{
|
||||
UDEBUG("");
|
||||
_url = url;
|
||||
_dbSafeAccessMutex.lock();
|
||||
if(this->connectDatabaseQuery(url, overwritten))
|
||||
if(this->connectDatabaseQuery(url, overwritten, readOnly))
|
||||
{
|
||||
_dbSafeAccessMutex.unlock();
|
||||
return true;
|
||||
|
||||
@@ -320,7 +320,7 @@ bool DBDriverSqlite3::getDatabaseVersionQuery(std::string & version) const
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DBDriverSqlite3::connectDatabaseQuery(const std::string & url, bool overwritten)
|
||||
bool DBDriverSqlite3::connectDatabaseQuery(const std::string & url, bool overwritten, bool readOnly)
|
||||
{
|
||||
this->disconnectDatabaseQuery();
|
||||
// Open a database connection
|
||||
@@ -332,7 +332,7 @@ bool DBDriverSqlite3::connectDatabaseQuery(const std::string & url, bool overwri
|
||||
if(!url.empty())
|
||||
{
|
||||
dbFileExist = UFile::exists(url.c_str());
|
||||
if(dbFileExist && overwritten)
|
||||
if(dbFileExist && overwritten && !readOnly)
|
||||
{
|
||||
UINFO("Deleting database %s...", url.c_str());
|
||||
UASSERT(UFile::erase(url.c_str()) == 0);
|
||||
@@ -354,12 +354,12 @@ bool DBDriverSqlite3::connectDatabaseQuery(const std::string & url, bool overwri
|
||||
{
|
||||
ULOGGER_INFO("Using empty database in the memory.");
|
||||
}
|
||||
rc = sqlite3_open_v2(":memory:", &_ppDb, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 0);
|
||||
rc = sqlite3_open_v2(":memory:", &_ppDb, readOnly ? SQLITE_OPEN_READONLY : SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
ULOGGER_INFO("Using database \"%s\" from the hard drive.", url.c_str());
|
||||
rc = sqlite3_open_v2(url.c_str(), &_ppDb, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 0);
|
||||
rc = sqlite3_open_v2(url.c_str(), &_ppDb, readOnly ? SQLITE_OPEN_READONLY : SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 0);
|
||||
}
|
||||
if(rc != SQLITE_OK)
|
||||
{
|
||||
@@ -4343,7 +4343,7 @@ void DBDriverSqlite3::loadLinksQuery(std::list<Signature *> & signatures) const
|
||||
|
||||
void DBDriverSqlite3::updateQuery(const std::list<Signature *> & nodes, bool updateTimestamp) const
|
||||
{
|
||||
UDEBUG("nodes = %d", nodes.size());
|
||||
UDEBUG("nodes = %d, updateTimestamp = %s", nodes.size(), updateTimestamp?"true":"false");
|
||||
if(_ppDb && nodes.size())
|
||||
{
|
||||
UTimer timer;
|
||||
@@ -4382,7 +4382,7 @@ void DBDriverSqlite3::updateQuery(const std::list<Signature *> & nodes, bool upd
|
||||
{
|
||||
s = *i;
|
||||
int index = 1;
|
||||
if(s)
|
||||
if(s && (s->isModified() || updateTimestamp))
|
||||
{
|
||||
rc = sqlite3_bind_int(ppStmt, index++, s->getWeight());
|
||||
UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error (%s): %s", _version.c_str(), sqlite3_errmsg(_ppDb)).c_str());
|
||||
@@ -4406,7 +4406,7 @@ void DBDriverSqlite3::updateQuery(const std::list<Signature *> & nodes, bool upd
|
||||
|
||||
//step
|
||||
rc=sqlite3_step(ppStmt);
|
||||
UASSERT_MSG(rc == SQLITE_DONE, uFormat("DB error (%s): %s", _version.c_str(), sqlite3_errmsg(_ppDb)).c_str());
|
||||
UASSERT_MSG(rc == SQLITE_DONE, uFormat("DB error (%s): %s (node id = %d map=%d)", _version.c_str(), sqlite3_errmsg(_ppDb), s->id(), s->mapId()).c_str());
|
||||
|
||||
rc = sqlite3_reset(ppStmt);
|
||||
UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error (%s): %s", _version.c_str(), sqlite3_errmsg(_ppDb)).c_str());
|
||||
@@ -4419,14 +4419,7 @@ void DBDriverSqlite3::updateQuery(const std::list<Signature *> & nodes, bool upd
|
||||
ULOGGER_DEBUG("Update Node table, Time=%fs", timer.ticks());
|
||||
|
||||
// Update links part1
|
||||
if(uStrNumCmp(_version, "0.18.3") >= 0)
|
||||
{
|
||||
query = uFormat("DELETE FROM Link WHERE from_id=? and type!=%d;", (int)Link::kLandmark);
|
||||
}
|
||||
else
|
||||
{
|
||||
query = uFormat("DELETE FROM Link WHERE from_id=?;");
|
||||
}
|
||||
query = uFormat("DELETE FROM Link WHERE from_id=?;");
|
||||
rc = sqlite3_prepare_v2(_ppDb, query.c_str(), -1, &ppStmt, 0);
|
||||
UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error (%s): %s", _version.c_str(), sqlite3_errmsg(_ppDb)).c_str());
|
||||
for(std::list<Signature *>::const_iterator j=nodes.begin(); j!=nodes.end(); ++j)
|
||||
@@ -4461,6 +4454,12 @@ void DBDriverSqlite3::updateQuery(const std::list<Signature *> & nodes, bool upd
|
||||
{
|
||||
stepLink(ppStmt, i->second);
|
||||
}
|
||||
// Save landmarks
|
||||
const std::map<int, Link> & landmarks = (*j)->getLandmarks();
|
||||
for(std::map<int, Link>::const_iterator i=landmarks.begin(); i!=landmarks.end(); ++i)
|
||||
{
|
||||
stepLink(ppStmt, i->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Finalize (delete) the statement
|
||||
@@ -5751,7 +5750,7 @@ cv::Mat DBDriverSqlite3::loadOptimizedMeshQuery(
|
||||
|
||||
void DBDriverSqlite3::saveFlannIndexQuery(const std::vector<unsigned char> & data) const
|
||||
{
|
||||
UDEBUG("");
|
||||
UDEBUG("data size = %ld bytes", data.size());
|
||||
if(_ppDb && uStrNumCmp(_version, "0.23.0") >= 0)
|
||||
{
|
||||
UTimer timer;
|
||||
|
||||
@@ -56,6 +56,8 @@ DBReader::DBReader(const std::string & databasePath,
|
||||
int startMapId,
|
||||
int stopMapId,
|
||||
bool priorsIgnored,
|
||||
bool imuIgnored,
|
||||
bool intermediateNodesAreNormalNodes,
|
||||
const std::vector<Transform> & cameraLocalTransformOverrides) :
|
||||
Camera(frameRate),
|
||||
_paths(uSplit(databasePath, ';')),
|
||||
@@ -66,9 +68,11 @@ DBReader::DBReader(const std::string & databasePath,
|
||||
_stopId(stopId),
|
||||
_cameraIndices(cameraIndices),
|
||||
_intermediateNodesIgnored(intermediateNodesIgnored),
|
||||
_intermediateNodesAreNormalNodes(intermediateNodesAreNormalNodes),
|
||||
_landmarksIgnored(landmarksIgnored),
|
||||
_featuresIgnored(featuresIgnored),
|
||||
_priorsIgnored(priorsIgnored),
|
||||
_imuIgnored(imuIgnored),
|
||||
_startMapId(startMapId),
|
||||
_stopMapId(stopMapId),
|
||||
_cameraLocalTransformOverrides(cameraLocalTransformOverrides),
|
||||
@@ -96,6 +100,8 @@ DBReader::DBReader(const std::list<std::string> & databasePaths,
|
||||
int startMapId,
|
||||
int stopMapId,
|
||||
bool priorsIgnored,
|
||||
bool imuIgnored,
|
||||
bool intermediateNodesAreNormalNodes,
|
||||
const std::vector<Transform> & cameraLocalTransformOverrides) :
|
||||
Camera(frameRate),
|
||||
_paths(databasePaths),
|
||||
@@ -106,9 +112,11 @@ DBReader::DBReader(const std::list<std::string> & databasePaths,
|
||||
_stopId(stopId),
|
||||
_cameraIndices(cameraIndices),
|
||||
_intermediateNodesIgnored(intermediateNodesIgnored),
|
||||
_intermediateNodesAreNormalNodes(intermediateNodesAreNormalNodes),
|
||||
_landmarksIgnored(landmarksIgnored),
|
||||
_featuresIgnored(featuresIgnored),
|
||||
_priorsIgnored(priorsIgnored),
|
||||
_imuIgnored(imuIgnored),
|
||||
_startMapId(startMapId),
|
||||
_stopMapId(stopMapId),
|
||||
_cameraLocalTransformOverrides(cameraLocalTransformOverrides),
|
||||
@@ -463,14 +471,17 @@ SensorData DBReader::getNextData(SensorCaptureInfo * info)
|
||||
}
|
||||
|
||||
Transform gravityTransform;
|
||||
std::multimap<int, Link> gravityLinks;
|
||||
_dbDriver->loadLinks(*_currentId, gravityLinks, Link::kGravity);
|
||||
if( gravityLinks.size() &&
|
||||
!gravityLinks.begin()->second.transform().isNull() &&
|
||||
gravityLinks.begin()->second.infMatrix().cols == 6 &&
|
||||
gravityLinks.begin()->second.infMatrix().rows == 6)
|
||||
if(!_imuIgnored)
|
||||
{
|
||||
gravityTransform = gravityLinks.begin()->second.transform();
|
||||
std::multimap<int, Link> gravityLinks;
|
||||
_dbDriver->loadLinks(*_currentId, gravityLinks, Link::kGravity);
|
||||
if( gravityLinks.size() &&
|
||||
!gravityLinks.begin()->second.transform().isNull() &&
|
||||
gravityLinks.begin()->second.infMatrix().cols == 6 &&
|
||||
gravityLinks.begin()->second.infMatrix().rows == 6)
|
||||
{
|
||||
gravityTransform = gravityLinks.begin()->second.transform();
|
||||
}
|
||||
}
|
||||
|
||||
Landmarks landmarks;
|
||||
@@ -742,7 +753,7 @@ SensorData DBReader::getNextData(SensorCaptureInfo * info)
|
||||
data.setStereoCameraModels(combinedStereoModels);
|
||||
}
|
||||
}
|
||||
data.setId(seq);
|
||||
data.setId(!_intermediateNodesAreNormalNodes && s->getWeight()==-1 ? -1 : seq);
|
||||
data.setStamp(s->getStamp());
|
||||
data.setGroundTruth(s->getGroundTruthPose());
|
||||
if(!globalPose.isNull())
|
||||
|
||||
+172
-47
@@ -47,6 +47,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#ifdef RTABMAP_TORCH
|
||||
#include "superpoint_torch/SuperPoint.h"
|
||||
#endif
|
||||
#if defined(RTABMAP_TORCH) && defined(RTABMAP_PYTHON)
|
||||
#include "superpoint_rpautrat/SuperpointRpautrat.h"
|
||||
#endif
|
||||
|
||||
#ifdef RTABMAP_PYTHON
|
||||
#include "python/PyDetector.h"
|
||||
@@ -300,7 +303,7 @@ void Feature2D::limitKeypoints(std::vector<cv::KeyPoint> & keypoints, std::vecto
|
||||
cv::Mat descriptorsTmp;
|
||||
if(ssc)
|
||||
{
|
||||
ULOGGER_DEBUG("too much words (%d), removing words with SSC", keypoints.size());
|
||||
ULOGGER_DEBUG("too many words (%d), removing words with SSC", keypoints.size());
|
||||
|
||||
// Sorting keypoints by deacreasing order of strength
|
||||
std::vector<float> responseVector;
|
||||
@@ -416,7 +419,7 @@ void Feature2D::limitKeypoints(const std::vector<cv::KeyPoint> & keypoints, std:
|
||||
inliers.resize(keypoints.size(), false);
|
||||
if(ssc)
|
||||
{
|
||||
ULOGGER_DEBUG("too much words (%d), removing words with SSC", keypoints.size());
|
||||
ULOGGER_DEBUG("too many words (%d), removing words with SSC", keypoints.size());
|
||||
|
||||
// Sorting keypoints by deacreasing order of strength
|
||||
std::vector<float> responseVector;
|
||||
@@ -463,7 +466,7 @@ void Feature2D::limitKeypoints(const std::vector<cv::KeyPoint> & keypoints, std:
|
||||
minimumHessian = iter->first;
|
||||
}
|
||||
}
|
||||
ULOGGER_DEBUG("%d keypoints removed, (kept %d), minimum response=%f", removed, maxKeypoints, minimumHessian);
|
||||
ULOGGER_DEBUG("%d keypoints removed, (kept %d), minimum response=%f", removed, keypoints.size()-removed, minimumHessian);
|
||||
ULOGGER_DEBUG("filter keypoints time = %f s", timer.ticks());
|
||||
}
|
||||
else
|
||||
@@ -730,9 +733,14 @@ Feature2D * Feature2D::create(Feature2D::Type type, const ParametersMap & parame
|
||||
feature2D = new ORBOctree(parameters);
|
||||
break;
|
||||
#ifdef RTABMAP_TORCH
|
||||
case Feature2D::kFeatureSuperPointTorch:
|
||||
feature2D = new SuperPointTorch(parameters);
|
||||
break;
|
||||
case Feature2D::kFeatureSuperPointTorch:
|
||||
feature2D = new SuperPointTorch(parameters);
|
||||
break;
|
||||
#endif
|
||||
#if defined(RTABMAP_TORCH) && defined(RTABMAP_PYTHON)
|
||||
case Feature2D::kFeatureSuperPointRpautrat:
|
||||
feature2D = new SuperPointRpautrat(parameters);
|
||||
break;
|
||||
#endif
|
||||
case Feature2D::kFeatureSurfFreak:
|
||||
feature2D = new SURF_FREAK(parameters);
|
||||
@@ -831,7 +839,7 @@ std::vector<cv::KeyPoint> Feature2D::generateKeypoints(const cv::Mat & image, co
|
||||
cv::Rect roi(globalRoi.x + j*colSize, globalRoi.y + i*rowSize, colSize, rowSize);
|
||||
std::vector<cv::KeyPoint> subKeypoints;
|
||||
subKeypoints = this->generateKeypointsImpl(image, roi, mask);
|
||||
if (this->getType() != Feature2D::Type::kFeaturePyDetector)
|
||||
if (this->getType() != Feature2D::Type::kFeaturePyDetector && this->getType() != Feature2D::Type::kFeatureSuperPointRpautrat)
|
||||
{
|
||||
limitKeypoints(subKeypoints, maxFeatures, roi.size(), this->getSSC());
|
||||
}
|
||||
@@ -1243,7 +1251,8 @@ SIFT::SIFT(const ParametersMap & parameters) :
|
||||
preciseUpscale_(Parameters::defaultSIFTPreciseUpscale()),
|
||||
rootSIFT_(Parameters::defaultSIFTRootSIFT()),
|
||||
gpu_(Parameters::defaultSIFTGpu()),
|
||||
guaussianThreshold_(Parameters::defaultSIFTGaussianThreshold()),
|
||||
gaussianThreshold_(Parameters::defaultSIFTGaussianThreshold()),
|
||||
maxGaussianThreshold_(Parameters::defaultSIFTMaxGaussianThreshold()),
|
||||
upscale_(Parameters::defaultSIFTUpscale()),
|
||||
cudaSiftData_(0),
|
||||
cudaSiftMemory_(0),
|
||||
@@ -1276,23 +1285,25 @@ void SIFT::parseParameters(const ParametersMap & parameters)
|
||||
Parameters::parse(parameters, Parameters::kSIFTPreciseUpscale(), preciseUpscale_);
|
||||
Parameters::parse(parameters, Parameters::kSIFTRootSIFT(), rootSIFT_);
|
||||
Parameters::parse(parameters, Parameters::kSIFTGpu(), gpu_);
|
||||
Parameters::parse(parameters, Parameters::kSIFTGaussianThreshold(), guaussianThreshold_);
|
||||
Parameters::parse(parameters, Parameters::kSIFTGaussianThreshold(), gaussianThreshold_);
|
||||
Parameters::parse(parameters, Parameters::kSIFTMaxGaussianThreshold(), maxGaussianThreshold_);
|
||||
Parameters::parse(parameters, Parameters::kSIFTUpscale(), upscale_);
|
||||
|
||||
if(gpu_)
|
||||
{
|
||||
#ifdef RTABMAP_CUDASIFT
|
||||
// Check if there is a cuda device
|
||||
if(InitCuda(0, ULogger::level() == ULogger::kDebug)) {
|
||||
UDEBUG("Init SiftData");
|
||||
if(cudaSiftData_ == 0) {
|
||||
if(cudaSiftData_==0)
|
||||
{
|
||||
if(InitCuda(0, ULogger::level() == ULogger::kDebug)) {
|
||||
UDEBUG("Init SiftData");
|
||||
cudaSiftData_ = new SiftData();
|
||||
InitSiftData(*cudaSiftData_, 8192, true, true);
|
||||
}
|
||||
}
|
||||
else{
|
||||
UWARN("No cuda device(s) detected, CudaSift is not available! Using SIFT CPU version instead.");
|
||||
gpu_ = false;
|
||||
else{
|
||||
UWARN("No cuda device(s) detected, CudaSift is not available! Using SIFT CPU version instead.");
|
||||
gpu_ = false;
|
||||
}
|
||||
}
|
||||
#else
|
||||
UWARN("RTAB-Map is not built with CudaSift so %s cannot be used!", Parameters::kSIFTGpu().c_str());
|
||||
@@ -1355,7 +1366,7 @@ std::vector<cv::KeyPoint> SIFT::generateKeypointsImpl(const cv::Mat & image, con
|
||||
numOctaves = 7; // hard-coded limit in CudaSift
|
||||
}
|
||||
float initBlur = sigma_; /* Amount of initial Gaussian blurring in standard deviations */
|
||||
float thresh = guaussianThreshold_; /* Threshold on difference of Gaussians for feature pruning */
|
||||
float thresh = gaussianThreshold_; /* Threshold on difference of Gaussians for feature pruning */
|
||||
float edgeLimit = edgeThreshold_;
|
||||
float minScale = 0.0f; /* Minimum acceptable scale to remove fine-scale features */
|
||||
UDEBUG("numOctaves=%d initBlur=%f thresh=%f edgeLimit=%f minScale=%f upScale=%s w=%d h=%d", numOctaves, initBlur, thresh, edgeLimit, minScale, upscale_?"true":"false", w, h);
|
||||
@@ -1380,15 +1391,9 @@ std::vector<cv::KeyPoint> SIFT::generateKeypointsImpl(const cv::Mat & image, con
|
||||
cudaSiftDescriptors_ = cv::Mat();
|
||||
if(cudaSiftData_->numPts)
|
||||
{
|
||||
int maxKeypoints = this->getMaxFeatures();
|
||||
if(maxKeypoints == 0 || maxKeypoints > cudaSiftData_->numPts)
|
||||
{
|
||||
maxKeypoints = cudaSiftData_->numPts;
|
||||
}
|
||||
|
||||
// Re-using same implementation of limitKeypoints() directly here to avoid doubling memory copies
|
||||
// Sort words by hessian
|
||||
std::multimap<float, int> hessianMap; // <hessian,id>
|
||||
keypoints.resize(cudaSiftData_->numPts);
|
||||
cudaSiftDescriptors_ = cv::Mat(cudaSiftData_->numPts, 128, CV_32FC1);
|
||||
size_t k=0;
|
||||
for(int i=0; i<cudaSiftData_->numPts; ++i)
|
||||
{
|
||||
// Ignore keypoints with invalid descriptors
|
||||
@@ -1405,29 +1410,40 @@ std::vector<cv::KeyPoint> SIFT::generateKeypointsImpl(const cv::Mat & image, con
|
||||
continue;
|
||||
}
|
||||
|
||||
//Keep track of the data, to be easier to manage the data in the next step
|
||||
hessianMap.insert(std::pair<float, int>(cudaSiftData_->h_data[i].sharpness, i));
|
||||
}
|
||||
if(i>0 &&
|
||||
cudaSiftData_->h_data[i].subsampling == cudaSiftData_->h_data[i-1].subsampling &&
|
||||
fabs(cudaSiftData_->h_data[i].xpos-cudaSiftData_->h_data[i-1].xpos) +
|
||||
fabs(cudaSiftData_->h_data[i].xpos-cudaSiftData_->h_data[i-1].ypos) < 0.1f)
|
||||
{
|
||||
// Same feature, skip doubles
|
||||
continue;
|
||||
}
|
||||
|
||||
if((int)hessianMap.size() < maxKeypoints)
|
||||
{
|
||||
maxKeypoints = hessianMap.size();
|
||||
}
|
||||
float response = abs(cudaSiftData_->h_data[i].sharpness);
|
||||
if(maxGaussianThreshold_>gaussianThreshold_ && response > maxGaussianThreshold_)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
std::multimap<float, int>::reverse_iterator iter = hessianMap.rbegin();
|
||||
keypoints.resize(maxKeypoints);
|
||||
cudaSiftDescriptors_ = cv::Mat(maxKeypoints, 128, CV_32FC1);
|
||||
for(unsigned int k=0; k<keypoints.size() && iter!=hessianMap.rend(); ++k, ++iter)
|
||||
{
|
||||
int i = iter->second;
|
||||
float *desc = cudaSiftData_->h_data[i].data;
|
||||
cv::Mat(1, 128, CV_32FC1, desc).copyTo(cudaSiftDescriptors_.row(k));
|
||||
keypoints[k].pt.x = cudaSiftData_->h_data[i].xpos;
|
||||
keypoints[k].pt.y = cudaSiftData_->h_data[i].ypos;
|
||||
keypoints[k].size = 2.0f*cudaSiftData_->h_data[i].scale; // x2 because the scale is more like a radius than a diameter, see CudaSift's ExtractSiftDescriptors function to see how they convert scale to patch size
|
||||
keypoints[k].angle = cudaSiftData_->h_data[i].orientation;
|
||||
keypoints[k].response = cudaSiftData_->h_data[i].sharpness;
|
||||
keypoints[k].response = response;
|
||||
keypoints[k].octave = log2(cudaSiftData_->h_data[i].subsampling)-(upscale_?1:0);
|
||||
++k;
|
||||
}
|
||||
if(k < keypoints.size())
|
||||
{
|
||||
UDEBUG("keypoints extracted = %d, valid=%d", keypoints.size(), k);
|
||||
keypoints.resize(k);
|
||||
cudaSiftDescriptors_.resize(k);
|
||||
}
|
||||
if(this->getMaxFeatures() != 0 && this->getMaxFeatures() < (int)keypoints.size())
|
||||
{
|
||||
// Call limitKeypoints() now to filter the descriptors.
|
||||
this->limitKeypoints(keypoints, cudaSiftDescriptors_, this->getMaxFeatures(), cv::Size(w,h), this->getSSC());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1449,12 +1465,13 @@ std::vector<cv::KeyPoint> SIFT::generateKeypointsImpl(const cv::Mat & image, con
|
||||
|
||||
cv::Mat SIFT::generateDescriptorsImpl(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints) const
|
||||
{
|
||||
cv::Mat descriptors;
|
||||
#ifdef RTABMAP_CUDASIFT
|
||||
if(gpu_)
|
||||
{
|
||||
if((int)keypoints.size() == cudaSiftDescriptors_.rows)
|
||||
{
|
||||
return cudaSiftDescriptors_.clone();
|
||||
descriptors = cudaSiftDescriptors_.clone();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1462,19 +1479,25 @@ cv::Mat SIFT::generateDescriptorsImpl(const cv::Mat & image, std::vector<cv::Key
|
||||
return cv::Mat();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#endif
|
||||
|
||||
UASSERT(!image.empty() && image.channels() == 1 && image.depth() == CV_8U);
|
||||
cv::Mat descriptors;
|
||||
UASSERT(!image.empty() && image.channels() == 1 && image.depth() == CV_8U);
|
||||
#if CV_MAJOR_VERSION < 3 || (CV_MAJOR_VERSION == 4 && CV_MINOR_VERSION <= 3) || (CV_MAJOR_VERSION == 3 && (CV_MINOR_VERSION < 4 || (CV_MINOR_VERSION==4 && CV_SUBMINOR_VERSION<11)))
|
||||
#ifdef RTABMAP_NONFREE
|
||||
sift_->compute(image, keypoints, descriptors);
|
||||
sift_->compute(image, keypoints, descriptors);
|
||||
#else
|
||||
UWARN("RTAB-Map is not built with OpenCV nonfree module so SIFT cannot be used!");
|
||||
UWARN("RTAB-Map is not built with OpenCV nonfree module so SIFT cannot be used!");
|
||||
#endif
|
||||
#else // >=4.4, >=3.4.11
|
||||
sift_->compute(image, keypoints, descriptors);
|
||||
sift_->compute(image, keypoints, descriptors);
|
||||
#endif
|
||||
|
||||
#ifdef RTABMAP_CUDASIFT
|
||||
}
|
||||
#endif
|
||||
|
||||
if( rootSIFT_ && !descriptors.empty())
|
||||
{
|
||||
UDEBUG("Performing RootSIFT...");
|
||||
@@ -2615,6 +2638,108 @@ cv::Mat SuperPointTorch::generateDescriptorsImpl(const cv::Mat & image, std::vec
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////
|
||||
//SuperPointRpautrat
|
||||
//////////////////////////
|
||||
SuperPointRpautrat::SuperPointRpautrat(const ParametersMap & parameters) :
|
||||
superpointWeightsPath_(Parameters::defaultSuperPointRpautratWeightsPath()),
|
||||
superpointModelPath_(Parameters::defaultSuperPointRpautratModelPath()),
|
||||
outputDir_(""),
|
||||
threshold_(Parameters::defaultSuperPointRpautratThreshold()),
|
||||
nms_(Parameters::defaultSuperPointRpautratNMS()),
|
||||
minDistance_(Parameters::defaultSuperPointRpautratNMSRadius()),
|
||||
cuda_(Parameters::defaultSuperPointRpautratCuda())
|
||||
{
|
||||
parseParameters(parameters);
|
||||
}
|
||||
|
||||
SuperPointRpautrat::~SuperPointRpautrat()
|
||||
{
|
||||
}
|
||||
|
||||
void SuperPointRpautrat::parseParameters(const ParametersMap & parameters)
|
||||
{
|
||||
Feature2D::parseParameters(parameters);
|
||||
|
||||
#if defined(RTABMAP_TORCH) && defined(RTABMAP_PYTHON)
|
||||
std::string previousWeightsPath = superpointWeightsPath_;
|
||||
std::string previousModelPath = superpointModelPath_;
|
||||
bool previousCuda = cuda_;
|
||||
float previousThreshold = threshold_;
|
||||
bool previousNms = nms_;
|
||||
int previousMinDistance = minDistance_;
|
||||
|
||||
Parameters::parse(parameters, Parameters::kSuperPointRpautratWeightsPath(), superpointWeightsPath_);
|
||||
Parameters::parse(parameters, Parameters::kSuperPointRpautratModelPath(), superpointModelPath_);
|
||||
Parameters::parse(parameters, Parameters::kSuperPointRpautratThreshold(), threshold_);
|
||||
Parameters::parse(parameters, Parameters::kSuperPointRpautratNMS(), nms_);
|
||||
Parameters::parse(parameters, Parameters::kSuperPointRpautratNMSRadius(), minDistance_);
|
||||
Parameters::parse(parameters, Parameters::kSuperPointRpautratCuda(), cuda_);
|
||||
Parameters::parse(parameters, Parameters::kRtabmapWorkingDirectory(), outputDir_);
|
||||
|
||||
// If working directory is not set, use the default
|
||||
if(outputDir_.empty())
|
||||
{
|
||||
outputDir_ = Parameters::createDefaultWorkingDirectory();
|
||||
}
|
||||
|
||||
// Reinitialize detector if model-affecting parameters changed
|
||||
if(superPoint_.get() == 0 ||
|
||||
superpointWeightsPath_.compare(previousWeightsPath) != 0 ||
|
||||
superpointModelPath_.compare(previousModelPath) != 0 ||
|
||||
previousCuda != cuda_ ||
|
||||
previousThreshold != threshold_ ||
|
||||
previousNms != nms_ ||
|
||||
previousMinDistance != minDistance_)
|
||||
{
|
||||
superPoint_ = cv::Ptr<SPDetectorRpautrat>(new SPDetectorRpautrat(superpointWeightsPath_, superpointModelPath_, outputDir_, threshold_, nms_, minDistance_, cuda_, this->getMaxFeatures(), this->getSSC()));
|
||||
}
|
||||
else if(superPoint_.get() != 0)
|
||||
{
|
||||
// Update post-processing parameters without reinitializing
|
||||
superPoint_->setMaxFeatures(this->getMaxFeatures());
|
||||
superPoint_->setSSC(this->getSSC());
|
||||
}
|
||||
#else
|
||||
UWARN("RTAB-Map is not built with Torch support so SuperPoint Rpautrat feature cannot be used!");
|
||||
#endif
|
||||
}
|
||||
|
||||
std::vector<cv::KeyPoint> SuperPointRpautrat::generateKeypointsImpl(const cv::Mat & image, const cv::Rect & roi, const cv::Mat & mask)
|
||||
{
|
||||
#if defined(RTABMAP_TORCH) && defined(RTABMAP_PYTHON)
|
||||
UASSERT(!image.empty() && image.channels() == 1 && image.depth() == CV_8U);
|
||||
if(roi.x!=0 || roi.y !=0)
|
||||
{
|
||||
UERROR("SuperPoint Rpautrat: Not supporting ROI (%d,%d,%d,%d). Make sure %s, %s, %s, %s, %s, %s are all set to default values.",
|
||||
roi.x, roi.y, roi.width, roi.height,
|
||||
Parameters::kKpRoiRatios().c_str(),
|
||||
Parameters::kVisRoiRatios().c_str(),
|
||||
Parameters::kVisGridRows().c_str(),
|
||||
Parameters::kVisGridCols().c_str(),
|
||||
Parameters::kKpGridRows().c_str(),
|
||||
Parameters::kKpGridCols().c_str());
|
||||
return std::vector<cv::KeyPoint>();
|
||||
}
|
||||
return superPoint_->detect(image, mask);
|
||||
#else
|
||||
UWARN("RTAB-Map is not built with Torch support so SuperPoint Rpautrat feature cannot be used!");
|
||||
return std::vector<cv::KeyPoint>();
|
||||
#endif
|
||||
}
|
||||
|
||||
cv::Mat SuperPointRpautrat::generateDescriptorsImpl(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints) const
|
||||
{
|
||||
#if defined(RTABMAP_TORCH) && defined(RTABMAP_PYTHON)
|
||||
UASSERT(!image.empty() && image.channels() == 1 && image.depth() == CV_8U);
|
||||
return superPoint_->compute(keypoints);
|
||||
#else
|
||||
UWARN("RTAB-Map is not built with Torch support so SuperPoint Rpautrat feature cannot be used!");
|
||||
return cv::Mat();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////
|
||||
//GFTT-DAISY
|
||||
//////////////////////////
|
||||
|
||||
@@ -96,7 +96,7 @@ std::vector<unsigned char> FlannIndex::serializeIndex(bool computeChecksum) cons
|
||||
#else
|
||||
UTimer timer;
|
||||
const int headerSizeBytes = sizeof(int)*FLANN_INDEX_HEADER_SIZE;
|
||||
std::vector<unsigned char> indexData(1024*1024*100 + headerSizeBytes); // Max 100 MB
|
||||
std::vector<unsigned char> indexData(1024*1024*1024 + headerSizeBytes); // Max 1 GB
|
||||
FILE* indexDataPtr = fmemopen(indexData.data()+headerSizeBytes, indexData.size() - headerSizeBytes, "wb");
|
||||
long bytes_written = 0;
|
||||
if (indexDataPtr) {
|
||||
|
||||
@@ -139,14 +139,25 @@ bool GlobalMap::update(const std::map<int, Transform> & poses)
|
||||
std::list<std::pair<int, Transform> > orderedPoses;
|
||||
|
||||
// add old poses that were not in the current map (they were just retrieved from LTM)
|
||||
int nodesNotAssembled = 0;
|
||||
int nodesNotInCache = 0;
|
||||
for(std::map<int, Transform>::const_iterator iter=poses.lower_bound(1); iter!=poses.end(); ++iter)
|
||||
{
|
||||
if(!isNodeAssembled(iter->first))
|
||||
{
|
||||
UDEBUG("Pose %d not found in current added poses, it will be added to map", iter->first);
|
||||
orderedPoses.push_back(*iter);
|
||||
if(uContains(cache(), iter->first))
|
||||
{
|
||||
++nodesNotAssembled;
|
||||
//UDEBUG("Pose %d not found in current added poses, it will be added to map", iter->first);
|
||||
orderedPoses.push_back(*iter);
|
||||
}
|
||||
else
|
||||
{
|
||||
++nodesNotInCache;
|
||||
}
|
||||
}
|
||||
}
|
||||
UDEBUG("%d nodes will be assembled in the map and %d nodes won't (no local grids in cache for them)", nodesNotAssembled, nodesNotInCache);
|
||||
|
||||
// insert zero after
|
||||
if(poses.find(0) != poses.end())
|
||||
|
||||
+37
-64
@@ -218,7 +218,14 @@ bool importPoses(
|
||||
else if(format == 4) // g2o
|
||||
{
|
||||
std::multimap<int, Link> constraintsTmp;
|
||||
UERROR("Cannot import from g2o format because it is not yet supported!");
|
||||
if(OptimizerG2O::loadGraph(filePath, poses, constraintsTmp))
|
||||
{
|
||||
if(constraints)
|
||||
{
|
||||
*constraints = constraintsTmp;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else
|
||||
@@ -451,9 +458,12 @@ bool importPoses(
|
||||
}
|
||||
double stamp = uStr2Double(strList.front());
|
||||
strList.pop_front();
|
||||
if(format==11)
|
||||
if(strList.size() == 8 && (format==10 || format==11 || format==12))
|
||||
{
|
||||
id = uStr2Int(strList.back());
|
||||
if(format==11)
|
||||
{
|
||||
id = uStr2Int(strList.back());
|
||||
}
|
||||
strList.pop_back();
|
||||
}
|
||||
str = uJoin(strList, " ");
|
||||
@@ -924,21 +934,12 @@ Transform calcRMSE (
|
||||
return t;
|
||||
}
|
||||
|
||||
void computeMaxGraphErrors(
|
||||
MaxGraphErrors computeMaxGraphErrors(
|
||||
const std::map<int, Transform> & poses,
|
||||
const std::multimap<int, Link> & links,
|
||||
float & maxLinearErrorRatio,
|
||||
float & maxAngularErrorRatio,
|
||||
float & maxLinearError,
|
||||
float & maxAngularError,
|
||||
const Link ** maxLinearErrorLink,
|
||||
const Link ** maxAngularErrorLink,
|
||||
bool force3DoF)
|
||||
{
|
||||
maxLinearErrorRatio = -1;
|
||||
maxAngularErrorRatio = -1;
|
||||
maxLinearError = -1;
|
||||
maxAngularError = -1;
|
||||
MaxGraphErrors maxError;
|
||||
|
||||
UDEBUG("poses=%d links=%d", (int)poses.size(), (int)links.size());
|
||||
for(std::multimap<int, Link>::const_iterator iter=links.begin(); iter!=links.end(); ++iter)
|
||||
@@ -960,19 +961,7 @@ void computeMaxGraphErrors(
|
||||
iter->second.to(),
|
||||
t2.prettyPrint().c_str());
|
||||
|
||||
if(maxLinearErrorLink)
|
||||
{
|
||||
*maxLinearErrorLink = 0;
|
||||
}
|
||||
if(maxAngularErrorLink)
|
||||
{
|
||||
*maxAngularErrorLink = 0;
|
||||
}
|
||||
maxLinearErrorRatio = -1;
|
||||
maxAngularErrorRatio = -1;
|
||||
maxLinearError = -1;
|
||||
maxAngularError = -1;
|
||||
return;
|
||||
return MaxGraphErrors();
|
||||
}
|
||||
|
||||
Transform t;
|
||||
@@ -996,14 +985,11 @@ void computeMaxGraphErrors(
|
||||
UASSERT(iter->second.transVariance(false)>0.0);
|
||||
float stddevLinear = sqrt(iter->second.transVariance(false));
|
||||
float linearErrorRatio = linearError/stddevLinear;
|
||||
if(linearErrorRatio > maxLinearErrorRatio)
|
||||
if(linearErrorRatio > maxError.linearRatio)
|
||||
{
|
||||
maxLinearError = linearError;
|
||||
maxLinearErrorRatio = linearErrorRatio;
|
||||
if(maxLinearErrorLink)
|
||||
{
|
||||
*maxLinearErrorLink = &iter->second;
|
||||
}
|
||||
maxError.linear = linearError;
|
||||
maxError.linearRatio = linearErrorRatio;
|
||||
maxError.linearLink = iter->second;
|
||||
}
|
||||
|
||||
// For landmark links, don't compute angular error if it doesn't estimate orientation
|
||||
@@ -1028,18 +1014,16 @@ void computeMaxGraphErrors(
|
||||
UASSERT(iter->second.rotVariance(false)>0.0);
|
||||
float stddevAngular = sqrt(iter->second.rotVariance(false));
|
||||
float angularErrorRatio = angularError/stddevAngular;
|
||||
if(angularErrorRatio > maxAngularErrorRatio)
|
||||
if(angularErrorRatio > maxError.angularRatio)
|
||||
{
|
||||
maxAngularError = angularError;
|
||||
maxAngularErrorRatio = angularErrorRatio;
|
||||
if(maxAngularErrorLink)
|
||||
{
|
||||
*maxAngularErrorLink = &iter->second;
|
||||
}
|
||||
maxError.angular = angularError;
|
||||
maxError.angularRatio = angularErrorRatio;
|
||||
maxError.angularLink = iter->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return maxError;
|
||||
}
|
||||
|
||||
std::vector<double> getMaxOdomInf(const std::multimap<int, Link> & links)
|
||||
@@ -2017,19 +2001,21 @@ std::list<std::pair<int, Transform> > computePath(
|
||||
bool lookInDatabase,
|
||||
bool updateNewCosts,
|
||||
float linearVelocity, // m/sec
|
||||
float angularVelocity) // rad/sec
|
||||
float angularVelocity, // rad/sec
|
||||
bool ignoreDirectLinks)
|
||||
{
|
||||
UASSERT(memory!=0);
|
||||
UASSERT(fromId>=0);
|
||||
UASSERT(toId!=0);
|
||||
std::list<std::pair<int, Transform> > path;
|
||||
UDEBUG("fromId=%d, toId=%d, lookInDatabase=%d, updateNewCosts=%d, linearVelocity=%f, angularVelocity=%f",
|
||||
UDEBUG("fromId=%d, toId=%d, lookInDatabase=%d, updateNewCosts=%d, linearVelocity=%f, angularVelocity=%f ignoreDirectLinks=%d",
|
||||
fromId,
|
||||
toId,
|
||||
lookInDatabase?1:0,
|
||||
updateNewCosts?1:0,
|
||||
linearVelocity,
|
||||
angularVelocity);
|
||||
angularVelocity,
|
||||
ignoreDirectLinks?1:0);
|
||||
|
||||
std::multimap<int, Link> allLinks;
|
||||
if(lookInDatabase)
|
||||
@@ -2107,7 +2093,9 @@ std::list<std::pair<int, Transform> > computePath(
|
||||
}
|
||||
for(std::multimap<int, Link>::const_iterator iter = links.begin(); iter!=links.end(); ++iter)
|
||||
{
|
||||
if(iter->second.from() != iter->second.to())
|
||||
if(iter->second.from() != iter->second.to() &&
|
||||
(!ignoreDirectLinks ||
|
||||
(!(iter->second.from()==fromId && iter->second.to()==toId) && !(iter->second.to()==fromId && iter->second.from()==toId))))
|
||||
{
|
||||
Transform nextPose = currentNode->pose()*iter->second.transform();
|
||||
float cost = 0.0f;
|
||||
@@ -2393,26 +2381,15 @@ std::map<int, Transform> getPosesInRadius(const Transform & targetPose, const st
|
||||
|
||||
|
||||
float computePathLength(
|
||||
const std::vector<std::pair<int, Transform> > & path,
|
||||
unsigned int fromIndex,
|
||||
unsigned int toIndex)
|
||||
const std::vector<std::pair<int, Transform> > & path)
|
||||
{
|
||||
float length = 0.0f;
|
||||
if(path.size() > 1)
|
||||
{
|
||||
UASSERT(fromIndex < path.size() && toIndex < path.size() && fromIndex <= toIndex);
|
||||
if(fromIndex >= toIndex)
|
||||
for(unsigned int i=0; i<path.size()-1; ++i)
|
||||
{
|
||||
toIndex = (unsigned int)path.size()-1;
|
||||
length+=path[i].second.getDistance(path[i+1].second);
|
||||
}
|
||||
float x=0, y=0, z=0;
|
||||
for(unsigned int i=fromIndex; i<toIndex-1; ++i)
|
||||
{
|
||||
x += fabs(path[i].second.x() - path[i+1].second.x());
|
||||
y += fabs(path[i].second.y() - path[i+1].second.y());
|
||||
z += fabs(path[i].second.z() - path[i+1].second.z());
|
||||
}
|
||||
length = sqrt(x*x + y*y + z*z);
|
||||
}
|
||||
return length;
|
||||
}
|
||||
@@ -2423,19 +2400,15 @@ float computePathLength(
|
||||
float length = 0.0f;
|
||||
if(path.size() > 1)
|
||||
{
|
||||
float x=0, y=0, z=0;
|
||||
std::map<int, Transform>::const_iterator iter=path.begin();
|
||||
Transform previousPose = iter->second;
|
||||
++iter;
|
||||
for(; iter!=path.end(); ++iter)
|
||||
{
|
||||
const Transform & currentPose = iter->second;
|
||||
x += fabs(previousPose.x() - currentPose.x());
|
||||
y += fabs(previousPose.y() - currentPose.y());
|
||||
z += fabs(previousPose.z() - currentPose.z());
|
||||
length+=previousPose.getDistance(currentPose);
|
||||
previousPose = currentPose;
|
||||
}
|
||||
length = sqrt(x*x + y*y + z*z);
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
@@ -135,8 +135,20 @@ void IMUThread::mainLoop()
|
||||
std::stringstream stream(line);
|
||||
std::string s;
|
||||
std::getline(stream, s, ',');
|
||||
std::string nanoseconds = s.substr(s.size() - 9, 9);
|
||||
std::string seconds = s.substr(0, s.size() - 9);
|
||||
|
||||
double stamp = 0.0;
|
||||
if(s.find('.') != std::string::npos)
|
||||
{
|
||||
// Normal [epoch] timestamp
|
||||
stamp = uStr2Double(s);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Assume EuRoC format
|
||||
std::string nanoseconds = s.substr(s.size() - 9, 9);
|
||||
std::string seconds = s.substr(0, s.size() - 9);
|
||||
stamp = double(uStr2Int(seconds)) + double(uStr2Int(nanoseconds))*1e-9;
|
||||
}
|
||||
|
||||
cv::Vec3d gyr;
|
||||
for (int j = 0; j < 3; ++j) {
|
||||
@@ -150,7 +162,6 @@ void IMUThread::mainLoop()
|
||||
acc[j] = uStr2Double(s);
|
||||
}
|
||||
|
||||
double stamp = double(uStr2Int(seconds)) + double(uStr2Int(nanoseconds))*1e-9;
|
||||
if(previousStamp_>0 && stamp > previousStamp_)
|
||||
{
|
||||
captureDelay_ = stamp - previousStamp_;
|
||||
|
||||
@@ -68,6 +68,9 @@ std::string LaserScan::formatName(const Format & format)
|
||||
case kXYZIT:
|
||||
name = "XYZIT";
|
||||
break;
|
||||
case kXYZIRT:
|
||||
name = "XYZIRT";
|
||||
break;
|
||||
default:
|
||||
name = "Unknown";
|
||||
break;
|
||||
@@ -96,6 +99,7 @@ int LaserScan::channels(const Format & format)
|
||||
break;
|
||||
case kXYZNormal:
|
||||
case kXYINormal:
|
||||
case kXYZIRT:
|
||||
channels = 6;
|
||||
break;
|
||||
case kXYZINormal:
|
||||
@@ -123,11 +127,15 @@ bool LaserScan::isScanHasRGB(const Format & format)
|
||||
}
|
||||
bool LaserScan::isScanHasIntensity(const Format & format)
|
||||
{
|
||||
return format==kXYZI || format==kXYZINormal || format == kXYI || format == kXYINormal || format==kXYZIT;
|
||||
return format==kXYZI || format==kXYZINormal || format == kXYI || format == kXYINormal || format==kXYZIT || format==kXYZIRT;
|
||||
}
|
||||
bool LaserScan::isScanHasTime(const Format & format)
|
||||
{
|
||||
return format==kXYZIT;
|
||||
return format==kXYZIT || format==kXYZIRT;
|
||||
}
|
||||
bool LaserScan::isScanHasRing(const Format & format)
|
||||
{
|
||||
return format==kXYZIRT;
|
||||
}
|
||||
|
||||
LaserScan LaserScan::backwardCompatibility(
|
||||
@@ -404,7 +412,7 @@ void LaserScan::init(
|
||||
UASSERT_MSG(data.channels() != 3 || (data.channels() == 3 && (format == kXYZ || format == kXYI)), uFormat("format=%s", LaserScan::formatName(format).c_str()).c_str());
|
||||
UASSERT_MSG(data.channels() != 4 || (data.channels() == 4 && (format == kXYZI || format == kXYZRGB)), uFormat("format=%s", LaserScan::formatName(format).c_str()).c_str());
|
||||
UASSERT_MSG(data.channels() != 5 || (data.channels() == 5 && (format == kXYNormal || format == kXYZIT)), uFormat("format=%s", LaserScan::formatName(format).c_str()).c_str());
|
||||
UASSERT_MSG(data.channels() != 6 || (data.channels() == 6 && (format == kXYINormal || format == kXYZNormal)), uFormat("format=%s", LaserScan::formatName(format).c_str()).c_str());
|
||||
UASSERT_MSG(data.channels() != 6 || (data.channels() == 6 && (format == kXYINormal || format == kXYZNormal || format == kXYZIRT)), uFormat("format=%s", LaserScan::formatName(format).c_str()).c_str());
|
||||
UASSERT_MSG(data.channels() != 7 || (data.channels() == 7 && (format == kXYZRGBNormal || format == kXYZINormal)), uFormat("format=%s", LaserScan::formatName(format).c_str()).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
+752
-209
File diff suppressed because it is too large
Load Diff
+851
-416
File diff suppressed because it is too large
Load Diff
@@ -35,6 +35,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include "rtabmap/core/odometry/OdometryORBSLAM3.h"
|
||||
#include "rtabmap/core/odometry/OdometryLOAM.h"
|
||||
#include "rtabmap/core/odometry/OdometryFLOAM.h"
|
||||
#include "rtabmap/core/odometry/OdometryLIOSAM.h"
|
||||
#include "rtabmap/core/odometry/OdometryMSCKF.h"
|
||||
#include "rtabmap/core/odometry/OdometryVINSFusion.h"
|
||||
#include "rtabmap/core/odometry/OdometryOpenVINS.h"
|
||||
@@ -101,6 +102,9 @@ Odometry * Odometry::create(Odometry::Type & type, const ParametersMap & paramet
|
||||
case Odometry::kTypeFLOAM:
|
||||
odometry = new OdometryFLOAM(parameters);
|
||||
break;
|
||||
case Odometry::kTypeLIOSAM:
|
||||
odometry = new OdometryLIOSAM(parameters);
|
||||
break;
|
||||
case Odometry::kTypeMSCKF:
|
||||
odometry = new OdometryMSCKF(parameters);
|
||||
break;
|
||||
@@ -653,7 +657,7 @@ Transform Odometry::process(SensorData & data, const Transform & guessIn, Odomet
|
||||
UWARN("Could not find imu transform at %f", data.stamp());
|
||||
}
|
||||
}
|
||||
else if(!guess.isNull()) {
|
||||
else if(!guess.isNull() && (!data.imageRaw().empty() || !data.laserScanRaw().isEmpty())) {
|
||||
UDEBUG("Using guess from motion %s", guess.prettyPrint().c_str());
|
||||
}
|
||||
|
||||
|
||||
@@ -188,7 +188,7 @@ void OdometryThread::addData(const SensorEvent & event)
|
||||
"(%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)."
|
||||
"Current camera/lidar delay is %fs.",
|
||||
"Current camera/lidar delay with system time is %fs.",
|
||||
event.data().stamp(), _oldestAsyncImuStamp, _imuBuffer.size(), UTimer::now() - event.data().stamp());
|
||||
notify = false;
|
||||
}
|
||||
@@ -197,7 +197,7 @@ void OdometryThread::addData(const SensorEvent & event)
|
||||
"(%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). "
|
||||
"Current camera/lidar delay is %fs.",
|
||||
"Current camera/lidar delay with system time is %fs.",
|
||||
event.data().stamp(), _newestAsyncImuStamp, _imuBuffer.size(), UTimer::now() - event.data().stamp());
|
||||
notify = false;
|
||||
}
|
||||
|
||||
+93
-51
@@ -185,6 +185,52 @@ Optimizer * Optimizer::create(Optimizer::Type type, const ParametersMap & parame
|
||||
return optimizer;
|
||||
}
|
||||
|
||||
class LinkIdKey
|
||||
{
|
||||
public:
|
||||
LinkIdKey(int id, Link::Type type) :
|
||||
id_(id),
|
||||
type_(type) {}
|
||||
bool operator<(const LinkIdKey & k) const
|
||||
{
|
||||
// landmark, sort by smallest to largest landmark id, after normal links
|
||||
if(id_ < 0 && k.id_ < 0)
|
||||
{
|
||||
return id_ > k.id_;
|
||||
}
|
||||
else if(id_ < 0) {
|
||||
return false;
|
||||
}
|
||||
else if(k.id_ < 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if(type_ == Link::kNeighbor && k.type_ != Link::kNeighbor)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if(type_ != Link::kNeighbor && k.type_ == Link::kNeighbor)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if(type_ == Link::kNeighborMerged && k.type_ != Link::kNeighbor && k.type_ != Link::kNeighborMerged)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if(k.type_ == Link::kNeighborMerged && type_ != Link::kNeighbor && type_ != Link::kNeighborMerged)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// normal link, sort by smallest to largest id
|
||||
return id_ < k.id_;
|
||||
}
|
||||
}
|
||||
int id_;
|
||||
Link::Type type_;
|
||||
};
|
||||
|
||||
void Optimizer::getConnectedGraph(
|
||||
int fromId,
|
||||
const std::map<int, Transform> & posesIn,
|
||||
@@ -194,13 +240,14 @@ void Optimizer::getConnectedGraph(
|
||||
{
|
||||
UDEBUG("IN: fromId=%d poses=%d links=%d priorsIgnored=%d landmarksIgnored=%d", fromId, (int)posesIn.size(), (int)linksIn.size(), priorsIgnored()?1:0, landmarksIgnored()?1:0);
|
||||
UASSERT(fromId>0);
|
||||
UASSERT(uContains(posesIn, fromId));
|
||||
UASSERT_MSG(uContains(posesIn, fromId), uFormat("poses=%ld (first=%d last=%d) fromId=%d",
|
||||
posesIn.size(), posesIn.empty()?0:posesIn.begin()->first, posesIn.empty()?0:posesIn.rbegin()->first, fromId).c_str());
|
||||
|
||||
posesOut.clear();
|
||||
linksOut.clear();
|
||||
|
||||
std::set<int> nextPoses;
|
||||
nextPoses.insert(fromId);
|
||||
std::map<LinkIdKey, Transform> nextPoses;
|
||||
nextPoses.insert(std::make_pair(LinkIdKey(fromId, Link::kUndef), posesIn.find(fromId)->second));
|
||||
std::multimap<int, std::pair<int, Link::Type> > biLinks;
|
||||
for(std::multimap<int, Link>::const_iterator iter=linksIn.begin(); iter!=linksIn.end(); ++iter)
|
||||
{
|
||||
@@ -214,22 +261,27 @@ void Optimizer::getConnectedGraph(
|
||||
}
|
||||
}
|
||||
|
||||
while(nextPoses.size())
|
||||
while(!nextPoses.empty())
|
||||
{
|
||||
int currentId = *nextPoses.rbegin(); // fill up all nodes before landmarks
|
||||
nextPoses.erase(*nextPoses.rbegin());
|
||||
// Fill up all nodes before landmarks
|
||||
// For nodes, fill up all neightbor nodes before loop closure ones
|
||||
int currentId = nextPoses.begin()->first.id_;
|
||||
Transform currentPose = nextPoses.begin()->second;
|
||||
nextPoses.erase(nextPoses.begin());
|
||||
|
||||
if(posesOut.empty())
|
||||
if(posesOut.find(currentId) != posesOut.end()) {
|
||||
// Already added from priority list
|
||||
continue;
|
||||
}
|
||||
|
||||
posesOut.insert(std::make_pair(currentId, currentPose));
|
||||
|
||||
// add prior links
|
||||
for(std::multimap<int, Link>::const_iterator pter=linksIn.find(currentId); pter!=linksIn.end() && pter->first==currentId; ++pter)
|
||||
{
|
||||
posesOut.insert(std::make_pair(currentId, posesIn.find(currentId)->second));
|
||||
|
||||
// add prior links
|
||||
for(std::multimap<int, Link>::const_iterator pter=linksIn.find(currentId); pter!=linksIn.end() && pter->first==currentId; ++pter)
|
||||
if(pter->second.from() == pter->second.to() && (!priorsIgnored() || pter->second.type() != Link::kPosePrior))
|
||||
{
|
||||
if(pter->second.from() == pter->second.to() && (!priorsIgnored() || pter->second.type() != Link::kPosePrior))
|
||||
{
|
||||
linksOut.insert(*pter);
|
||||
}
|
||||
linksOut.insert(*pter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,52 +292,42 @@ void Optimizer::getConnectedGraph(
|
||||
if(posesIn.find(toId) != posesIn.end() && (!landmarksIgnored() || toId>0))
|
||||
{
|
||||
std::multimap<int, Link>::const_iterator kter = graph::findLink(linksIn, currentId, toId, true, type);
|
||||
if(nextPoses.find(toId) == nextPoses.end())
|
||||
UASSERT(kter!=linksIn.end());
|
||||
if(!uContains(posesOut, toId))
|
||||
{
|
||||
if(!uContains(posesOut, toId))
|
||||
const Transform & poseToIn = posesIn.at(toId);
|
||||
Transform t = kter->second.from()==currentId?kter->second.transform():kter->second.transform().inverse();
|
||||
Transform pose;
|
||||
if(isSlam2d() && kter->second.type() == Link::kLandmark && toId>0 && (poseToIn.is3DoF() || poseToIn.is4DoF()))
|
||||
{
|
||||
const Transform & poseToIn = posesIn.at(toId);
|
||||
Transform t = kter->second.from()==currentId?kter->second.transform():kter->second.transform().inverse();
|
||||
if(isSlam2d() && kter->second.type() == Link::kLandmark && toId>0 && (poseToIn.is3DoF() || poseToIn.is4DoF()))
|
||||
if(poseToIn.is3DoF())
|
||||
{
|
||||
if(poseToIn.is3DoF())
|
||||
{
|
||||
posesOut.insert(std::make_pair(toId, (posesOut.at(currentId) * t).to3DoF()));
|
||||
}
|
||||
else
|
||||
{
|
||||
posesOut.insert(std::make_pair(toId, (posesOut.at(currentId) * t).to4DoF()));
|
||||
}
|
||||
pose = (posesOut.at(currentId) * t).to3DoF();
|
||||
}
|
||||
else
|
||||
{
|
||||
posesOut.insert(std::make_pair(toId, posesOut.at(currentId)* t));
|
||||
pose = (posesOut.at(currentId) * t).to4DoF();
|
||||
}
|
||||
|
||||
// add prior links
|
||||
for(std::multimap<int, Link>::const_iterator pter=linksIn.find(toId); pter!=linksIn.end() && pter->first==toId; ++pter)
|
||||
{
|
||||
if(pter->second.from() == pter->second.to() && (!priorsIgnored() || pter->second.type() != Link::kPosePrior))
|
||||
{
|
||||
linksOut.insert(*pter);
|
||||
}
|
||||
}
|
||||
|
||||
nextPoses.insert(toId);
|
||||
}
|
||||
else
|
||||
{
|
||||
pose = posesOut.at(currentId)* t;
|
||||
}
|
||||
|
||||
// only add unique links
|
||||
if(graph::findLink(linksOut, currentId, toId, true, kter->second.type()) == linksOut.end())
|
||||
nextPoses.insert(std::make_pair(LinkIdKey(toId, type), pose));
|
||||
}
|
||||
|
||||
// only add unique links
|
||||
if(graph::findLink(linksOut, currentId, toId, true, kter->second.type()) == linksOut.end())
|
||||
{
|
||||
if(kter->second.to() < 0)
|
||||
{
|
||||
if(kter->second.to() < 0)
|
||||
{
|
||||
// For landmarks, make sure fromId is the landmark
|
||||
linksOut.insert(std::make_pair(kter->second.to(), kter->second.inverse()));
|
||||
}
|
||||
else
|
||||
{
|
||||
linksOut.insert(*kter);
|
||||
}
|
||||
// For landmarks, make sure fromId is the landmark
|
||||
linksOut.insert(std::make_pair(kter->second.to(), kter->second.inverse()));
|
||||
}
|
||||
else
|
||||
{
|
||||
linksOut.insert(*kter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+36
-10
@@ -113,11 +113,14 @@ ParametersMap Parameters::deserialize(const std::string & parameters)
|
||||
std::list<std::string> tuplets = uSplit(parameters, ';');
|
||||
for(std::list<std::string>::iterator iter=tuplets.begin(); iter!=tuplets.end(); ++iter)
|
||||
{
|
||||
std::list<std::string> p = uSplit(*iter, ':');
|
||||
if(p.size() == 2)
|
||||
// Split on the FIRST ':' only. Using uSplit() here would discard
|
||||
// empty tokens, so a tuplet like "Marker/Lengths:" (legitimate empty
|
||||
// string value) would lose the value side and be dropped entirely.
|
||||
size_t colonPos = iter->find(':');
|
||||
if(colonPos != std::string::npos && colonPos > 0)
|
||||
{
|
||||
std::string key = p.front();
|
||||
std::string value = p.back();
|
||||
std::string key = iter->substr(0, colonPos);
|
||||
std::string value = iter->substr(colonPos + 1);
|
||||
|
||||
// look for old parameter name
|
||||
bool addParameter = true;
|
||||
@@ -168,6 +171,7 @@ bool Parameters::isFeatureParameter(const std::string & parameter)
|
||||
group.compare("BRISK") == 0 ||
|
||||
group.compare("KAZE") == 0 ||
|
||||
group.compare("SuperPoint") == 0 ||
|
||||
group.compare("SuperPointRpautrat") == 0 ||
|
||||
group.compare("PyDetector") == 0;
|
||||
}
|
||||
|
||||
@@ -182,6 +186,7 @@ rtabmap::ParametersMap Parameters::getDefaultOdometryParameters(bool stereo, boo
|
||||
(stereo && group.compare("Stereo") == 0) ||
|
||||
(icp && group.compare("Icp") == 0) ||
|
||||
(vis && Parameters::isFeatureParameter(iter->first)) ||
|
||||
group.compare("OdomCuVSLAM") == 0 ||
|
||||
group.compare("Reg") == 0 ||
|
||||
group.compare("Optimizer") == 0 ||
|
||||
group.compare("g2o") == 0 ||
|
||||
@@ -236,6 +241,9 @@ const std::map<std::string, std::pair<bool, std::string> > & Parameters::getRemo
|
||||
{
|
||||
// removed parameters
|
||||
|
||||
// 0.23.7
|
||||
removedParameters_.insert(std::make_pair("Marker/CornerRefinementMethod", std::make_pair(true, Parameters::kMarkerOpenCVCornerRefinementMethod())));
|
||||
|
||||
// 0.23.1
|
||||
removedParameters_.insert(std::make_pair("OdomVINS/ConfigPath", std::make_pair(true, Parameters::kOdomVINSFusionConfigPath())));
|
||||
|
||||
@@ -288,7 +296,7 @@ const std::map<std::string, std::pair<bool, std::string> > & Parameters::getRemo
|
||||
removedParameters_.insert(std::make_pair("Aruco/MaxDepthError", std::make_pair(true, Parameters::kMarkerMaxDepthError())));
|
||||
removedParameters_.insert(std::make_pair("Aruco/VarianceLinear", std::make_pair(true, Parameters::kMarkerVarianceLinear())));
|
||||
removedParameters_.insert(std::make_pair("Aruco/VarianceAngular", std::make_pair(true, Parameters::kMarkerVarianceAngular())));
|
||||
removedParameters_.insert(std::make_pair("Aruco/CornerRefinementMethod", std::make_pair(true, Parameters::kMarkerCornerRefinementMethod())));
|
||||
removedParameters_.insert(std::make_pair("Aruco/CornerRefinementMethod", std::make_pair(true, Parameters::kMarkerOpenCVCornerRefinementMethod())));
|
||||
|
||||
// 0.17.5
|
||||
removedParameters_.insert(std::make_pair("Grid/OctoMapOccupancyThr", std::make_pair(true, Parameters::kGridGlobalOccupancyThr())));
|
||||
@@ -661,6 +669,12 @@ ParametersMap Parameters::parseArguments(int argc, char * argv[], bool onlyParam
|
||||
std::cout << str << std::setw(spacing - str.size()) << "true" << std::endl;
|
||||
#else
|
||||
std::cout << str << std::setw(spacing - str.size()) << "false" << std::endl;
|
||||
#endif
|
||||
str = "With SuperPoint Rpautrat:";
|
||||
#if defined(RTABMAP_TORCH) && defined(RTABMAP_PYTHON)
|
||||
std::cout << str << std::setw(spacing - str.size()) << "true" << std::endl;
|
||||
#else
|
||||
std::cout << str << std::setw(spacing - str.size()) << "false" << std::endl;
|
||||
#endif
|
||||
str = "With Python3:";
|
||||
#ifdef RTABMAP_PYTHON
|
||||
@@ -673,6 +687,12 @@ ParametersMap Parameters::parseArguments(int argc, char * argv[], bool onlyParam
|
||||
std::cout << str << std::setw(spacing - str.size()) << "true" << std::endl;
|
||||
#else
|
||||
std::cout << str << std::setw(spacing - str.size()) << "false" << std::endl;
|
||||
#endif
|
||||
str = "With AprilTag:";
|
||||
#ifdef RTABMAP_APRILTAG
|
||||
std::cout << str << std::setw(spacing - str.size()) << "true" << std::endl;
|
||||
#else
|
||||
std::cout << str << std::setw(spacing - str.size()) << "false" << std::endl;
|
||||
#endif
|
||||
str = "With OpenGV:";
|
||||
#ifdef RTABMAP_OPENGV
|
||||
@@ -895,6 +915,12 @@ ParametersMap Parameters::parseArguments(int argc, char * argv[], bool onlyParam
|
||||
std::cout << str << std::setw(spacing - str.size()) << "true" << std::endl;
|
||||
#else
|
||||
std::cout << str << std::setw(spacing - str.size()) << "false" << std::endl;
|
||||
#endif
|
||||
str = "With LIO-SAM:";
|
||||
#ifdef RTABMAP_LIOSAM
|
||||
std::cout << str << std::setw(spacing - str.size()) << "true" << std::endl;
|
||||
#else
|
||||
std::cout << str << std::setw(spacing - str.size()) << "false" << std::endl;
|
||||
#endif
|
||||
str = "With FOVIS:";
|
||||
#ifdef RTABMAP_FOVIS
|
||||
@@ -1112,8 +1138,8 @@ ParametersMap Parameters::parseArguments(int argc, char * argv[], bool onlyParam
|
||||
ignore = true;
|
||||
}
|
||||
#endif
|
||||
#ifndef RTABMAP_ORBSLAM2
|
||||
if(group.compare("OdomORBSLAM2") == 0)
|
||||
#ifndef RTABMAP_ORB_SLAM
|
||||
if(group.compare("OdomORBSLAM") == 0)
|
||||
{
|
||||
ignore = true;
|
||||
}
|
||||
@@ -1219,13 +1245,13 @@ void readINIImpl(const CSimpleIniA & ini, const std::string & configFilePath, Pa
|
||||
std::vector<std::string> version = uListToVector(uSplit((*iter).second, '.'));
|
||||
if(version.size() == 3)
|
||||
{
|
||||
if(!RTABMAP_VERSION_COMPARE(std::atoi(version[0].c_str()), std::atoi(version[1].c_str()), std::atoi(version[2].c_str())))
|
||||
if(RTABMAP_VERSION_COMPARE(<, std::atoi(version[0].c_str()), std::atoi(version[1].c_str()), std::atoi(version[2].c_str())))
|
||||
{
|
||||
if(configFilePath.find(".rtabmap") != std::string::npos)
|
||||
{
|
||||
UWARN("Version in the config file \"%s\" is more recent (\"%s\") than "
|
||||
"current RTAB-Map version used (\"%s\"). The config file will be upgraded "
|
||||
"to new version.",
|
||||
"current RTAB-Map version used (\"%s\"). The config file will be downgraded "
|
||||
"to current RTAB-Map version if saved.",
|
||||
configFilePath.c_str(),
|
||||
(*iter).second,
|
||||
RTABMAP_VERSION);
|
||||
|
||||
@@ -529,6 +529,7 @@ Transform RegistrationIcp::computeTransformationImpl(
|
||||
double toComplexity = util3d::computeNormalsComplexity(toScan, guess, &complexityVectorsTo, &complexityValuesTo);
|
||||
float complexity = fromComplexity<toComplexity?fromComplexity:toComplexity;
|
||||
info.icpStructuralComplexity = complexity;
|
||||
UDEBUG("structural complexity: from=%f to=%f", fromComplexity, toComplexity);
|
||||
if(complexity < _pointToPlaneMinComplexity)
|
||||
{
|
||||
tooLowComplexityForPlaneToPlane = true;
|
||||
|
||||
@@ -84,6 +84,9 @@ RegistrationVis::RegistrationVis(const ParametersMap & parameters, Registration
|
||||
_flowEps(Parameters::defaultVisCorFlowEps()),
|
||||
_flowMaxLevel(Parameters::defaultVisCorFlowMaxLevel()),
|
||||
_flowGpu(Parameters::defaultVisCorFlowGpu()),
|
||||
_flowUseMinEigenVals(Parameters::defaultVisCorFlowUseMinEigenVals()),
|
||||
_flowMinEigThreshold(Parameters::defaultVisCorFlowMinEigThreshold()),
|
||||
_flowErrorThreshold(Parameters::defaultVisCorFlowErrorThreshold()),
|
||||
_nndr(Parameters::defaultVisCorNNDR()),
|
||||
_nnType(Parameters::defaultVisCorNNType()),
|
||||
_gmsWithRotation(Parameters::defaultGMSWithRotation()),
|
||||
@@ -145,6 +148,9 @@ void RegistrationVis::parseParameters(const ParametersMap & parameters)
|
||||
Parameters::parse(parameters, Parameters::kVisCorFlowEps(), _flowEps);
|
||||
Parameters::parse(parameters, Parameters::kVisCorFlowMaxLevel(), _flowMaxLevel);
|
||||
Parameters::parse(parameters, Parameters::kVisCorFlowGpu(), _flowGpu);
|
||||
Parameters::parse(parameters, Parameters::kVisCorFlowUseMinEigenVals(), _flowUseMinEigenVals);
|
||||
Parameters::parse(parameters, Parameters::kVisCorFlowMinEigThreshold(), _flowMinEigThreshold);
|
||||
Parameters::parse(parameters, Parameters::kVisCorFlowErrorThreshold(), _flowErrorThreshold);
|
||||
Parameters::parse(parameters, Parameters::kVisCorNNDR(), _nndr);
|
||||
Parameters::parse(parameters, Parameters::kVisCorNNType(), _nnType);
|
||||
Parameters::parse(parameters, Parameters::kGMSWithRotation(), _gmsWithRotation);
|
||||
@@ -286,6 +292,10 @@ void RegistrationVis::parseParameters(const ParametersMap & parameters)
|
||||
{
|
||||
uInsert(_featureParameters, ParametersPair(Parameters::kKpGridCols(), parameters.at(Parameters::kVisGridCols())));
|
||||
}
|
||||
if(uContains(parameters, Parameters::kRtabmapWorkingDirectory()))
|
||||
{
|
||||
uInsert(_featureParameters, ParametersPair(Parameters::kRtabmapWorkingDirectory(), parameters.at(Parameters::kRtabmapWorkingDirectory())));
|
||||
}
|
||||
|
||||
delete _detectorFrom;
|
||||
delete _detectorTo;
|
||||
@@ -317,6 +327,7 @@ Transform RegistrationVis::computeTransformationImpl(
|
||||
UDEBUG("%s=%d", Parameters::kVisPnPFlags().c_str(), _PnPFlags);
|
||||
UDEBUG("%s=%f", Parameters::kVisPnPMaxVariance().c_str(), _PnPMaxVar);
|
||||
UDEBUG("%s=%f", Parameters::kVisPnPSplitLinearCovComponents().c_str(), _PnPSplitLinearCovarianceComponents);
|
||||
UDEBUG("%s=%f", Parameters::kVisPnPVarianceMedianRatio().c_str(), _PnPVarMedianRatio);
|
||||
UDEBUG("%s=%d", Parameters::kVisCorType().c_str(), _correspondencesApproach);
|
||||
UDEBUG("%s=%d", Parameters::kVisCorFlowWinSize().c_str(), _flowWinSize);
|
||||
UDEBUG("%s=%d", Parameters::kVisCorFlowIterations().c_str(), _flowIterations);
|
||||
@@ -329,11 +340,12 @@ Transform RegistrationVis::computeTransformationImpl(
|
||||
UDEBUG("Feature Detector = %d", (int)_detectorFrom->getType());
|
||||
UDEBUG("guess=%s", guess.prettyPrint().c_str());
|
||||
|
||||
UDEBUG("Input(%d): from=%d words, %d 3D words, %d words descriptors, %d kpts, %d kpts3D, %d descriptors, image=%dx%d models=%d stereo=%d",
|
||||
UDEBUG("Input(%d): from=%d words, %d 3D words, %d words descriptors, %d words kpts, %d kpts, %d kpts3D, %d descriptors, image=%dx%d models=%d stereo=%d",
|
||||
fromSignature.id(),
|
||||
(int)fromSignature.getWords().size(),
|
||||
(int)fromSignature.getWords3().size(),
|
||||
(int)fromSignature.getWordsDescriptors().rows,
|
||||
(int)fromSignature.getWordsKpts().size(),
|
||||
(int)fromSignature.sensorData().keypoints().size(),
|
||||
(int)fromSignature.sensorData().keypoints3D().size(),
|
||||
fromSignature.sensorData().descriptors().rows,
|
||||
@@ -342,11 +354,12 @@ Transform RegistrationVis::computeTransformationImpl(
|
||||
(int)fromSignature.sensorData().cameraModels().size(),
|
||||
(int)fromSignature.sensorData().stereoCameraModels().size());
|
||||
|
||||
UDEBUG("Input(%d): to=%d words, %d 3D words, %d words descriptors, %d kpts, %d kpts3D, %d descriptors, image=%dx%d models=%d stereo=%d",
|
||||
UDEBUG("Input(%d): to=%d words, %d 3D words, %d words descriptors, %d words kpts, %d kpts, %d kpts3D, %d descriptors, image=%dx%d models=%d stereo=%d",
|
||||
toSignature.id(),
|
||||
(int)toSignature.getWords().size(),
|
||||
(int)toSignature.getWords3().size(),
|
||||
(int)toSignature.getWordsDescriptors().rows,
|
||||
(int)toSignature.getWordsKpts().size(),
|
||||
(int)toSignature.sensorData().keypoints().size(),
|
||||
(int)toSignature.sensorData().keypoints3D().size(),
|
||||
toSignature.sensorData().descriptors().rows,
|
||||
@@ -434,16 +447,7 @@ Transform RegistrationVis::computeTransformationImpl(
|
||||
{
|
||||
UASSERT(!fromSignature.sensorData().cameraModels().empty());
|
||||
UDEBUG("Masking floor (threshold=%f)", _maskFloorThreshold);
|
||||
if(_maskFloorThreshold<0.0f)
|
||||
{
|
||||
cv::Mat depthBelow;
|
||||
util3d::filterFloor(depthMask, fromSignature.sensorData().cameraModels(), _maskFloorThreshold*-1.0f, &depthBelow);
|
||||
depthMask = depthBelow;
|
||||
}
|
||||
else
|
||||
{
|
||||
depthMask = util3d::filterFloor(depthMask, fromSignature.sensorData().cameraModels(), _maskFloorThreshold);
|
||||
}
|
||||
depthMask = util3d::filterFloor(depthMask, fromSignature.sensorData().cameraModels(), _maskFloorThreshold);
|
||||
UDEBUG("Masking floor done.");
|
||||
}
|
||||
|
||||
@@ -649,6 +653,7 @@ Transform RegistrationVis::computeTransformationImpl(
|
||||
// Find features in the new left image
|
||||
UDEBUG("guessSet = %d", guessSet?1:0);
|
||||
std::vector<unsigned char> status;
|
||||
std::vector<float> err;
|
||||
#ifdef HAVE_OPENCV_CUDAOPTFLOW
|
||||
if (_flowGpu)
|
||||
{
|
||||
@@ -678,7 +683,6 @@ Transform RegistrationVis::computeTransformationImpl(
|
||||
else
|
||||
#endif
|
||||
{
|
||||
std::vector<float> err;
|
||||
UDEBUG("cv::calcOpticalFlowPyrLK() begin");
|
||||
cv::calcOpticalFlowPyrLK(
|
||||
imageFrom,
|
||||
@@ -690,7 +694,8 @@ Transform RegistrationVis::computeTransformationImpl(
|
||||
cv::Size(_flowWinSize, _flowWinSize),
|
||||
guessSet ? 0 : _flowMaxLevel,
|
||||
cv::TermCriteria(cv::TermCriteria::COUNT + cv::TermCriteria::EPS, _flowIterations, _flowEps),
|
||||
cv::OPTFLOW_LK_GET_MIN_EIGENVALS | (guessSet ? cv::OPTFLOW_USE_INITIAL_FLOW : 0), 1e-4);
|
||||
(_flowUseMinEigenVals ? cv::OPTFLOW_LK_GET_MIN_EIGENVALS : 0) | (guessSet ? cv::OPTFLOW_USE_INITIAL_FLOW : 0),
|
||||
_flowMinEigThreshold);
|
||||
UDEBUG("cv::calcOpticalFlowPyrLK() end");
|
||||
}
|
||||
|
||||
@@ -699,11 +704,14 @@ Transform RegistrationVis::computeTransformationImpl(
|
||||
std::vector<cv::Point3f> kptsFrom3DKept(kptsFrom3D.size());
|
||||
std::vector<int> orignalWordsFromIdsCpy = orignalWordsFromIds;
|
||||
int ki = 0;
|
||||
UASSERT((status.empty() || cornersTo.size() == status.size()) &&
|
||||
(err.empty() || cornersTo.size() == err.size()));
|
||||
for(unsigned int i=0; i<status.size(); ++i)
|
||||
{
|
||||
if(status[i] &&
|
||||
uIsInBounds(cornersTo[i].x, 0.0f, float(imageTo.cols)) &&
|
||||
uIsInBounds(cornersTo[i].y, 0.0f, float(imageTo.rows)))
|
||||
uIsInBounds(cornersTo[i].y, 0.0f, float(imageTo.rows)) &&
|
||||
(_flowUseMinEigenVals || err.empty() || err[i] < _flowErrorThreshold))
|
||||
{
|
||||
if(orignalWordsFromIdsCpy.size())
|
||||
{
|
||||
@@ -800,16 +808,7 @@ Transform RegistrationVis::computeTransformationImpl(
|
||||
{
|
||||
UASSERT(!toSignature.sensorData().cameraModels().empty());
|
||||
UDEBUG("Masking floor (threshold=%f)", _maskFloorThreshold);
|
||||
if(_maskFloorThreshold<0.0f)
|
||||
{
|
||||
cv::Mat depthBelow;
|
||||
util3d::filterFloor(depthMask, toSignature.sensorData().cameraModels(), _maskFloorThreshold*-1.0f, &depthBelow);
|
||||
depthMask = depthBelow;
|
||||
}
|
||||
else
|
||||
{
|
||||
depthMask = util3d::filterFloor(depthMask, toSignature.sensorData().cameraModels(), _maskFloorThreshold);
|
||||
}
|
||||
depthMask = util3d::filterFloor(depthMask, toSignature.sensorData().cameraModels(), _maskFloorThreshold);
|
||||
UDEBUG("Masking floor done.");
|
||||
}
|
||||
|
||||
@@ -1626,6 +1625,7 @@ Transform RegistrationVis::computeTransformationImpl(
|
||||
cameraTransform,
|
||||
_PnPReprojError,
|
||||
0.99f,
|
||||
_PnPVarMedianRatio,
|
||||
words3A, // for scale estimation
|
||||
&variance,
|
||||
&matchesV);
|
||||
@@ -1666,14 +1666,10 @@ Transform RegistrationVis::computeTransformationImpl(
|
||||
UINFO(msg.c_str());
|
||||
}
|
||||
}
|
||||
else if(fromSignature.getWords().size() == 0)
|
||||
else
|
||||
{
|
||||
msg = uFormat("No enough features (%d)", (int)fromSignature.getWords().size());
|
||||
UWARN(msg.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
msg = uFormat("No camera model");
|
||||
msg = uFormat("No enough features < %s=%d (from=%d to=%d)",
|
||||
Parameters::kVisMinInliers().c_str(), _minInliers, (int)fromSignature.getWords().size(), (int)toSignature.getWords().size());
|
||||
UWARN(msg.c_str());
|
||||
}
|
||||
}
|
||||
@@ -2180,6 +2176,8 @@ Transform RegistrationVis::computeTransformationImpl(
|
||||
// We take the second eigen value
|
||||
info.inliersDistribution = pca_analysis.eigenvalues.at<float>(0, 1);
|
||||
|
||||
UDEBUG("Visual distribution: %f (eigen values = %f %f)", info.inliersDistribution, pca_analysis.eigenvalues.at<float>(0, 0), pca_analysis.eigenvalues.at<float>(0, 1));
|
||||
|
||||
if(info.inliersDistribution < _minInliersDistributionThr)
|
||||
{
|
||||
msg = uFormat("The distribution (%f) of inliers is under %s threshold (%f)",
|
||||
@@ -2202,6 +2200,10 @@ Transform RegistrationVis::computeTransformationImpl(
|
||||
info.matches = matchesCount;
|
||||
info.rejectedMsg = msg;
|
||||
info.covariance = covariance;
|
||||
if(!covariance.empty())
|
||||
{
|
||||
info.variance = covariance.at<double>(0,0);
|
||||
}
|
||||
|
||||
UDEBUG("inliers=%d/%d", info.inliers, info.matches);
|
||||
UDEBUG("transform=%s", transform.prettyPrint().c_str());
|
||||
|
||||
+875
-446
File diff suppressed because it is too large
Load Diff
@@ -47,8 +47,7 @@ RtabmapThread::RtabmapThread(Rtabmap * rtabmap) :
|
||||
_dataBufferMaxSize(Parameters::defaultRtabmapImageBufferSize()),
|
||||
_rate(Parameters::defaultRtabmapDetectionRate()),
|
||||
_createIntermediateNodes(Parameters::defaultRtabmapCreateIntermediateNodes()),
|
||||
_frameRateTimer(new UTimer()),
|
||||
_previousStamp(0.0),
|
||||
_previousStamp(-1.0),
|
||||
_rtabmap(rtabmap),
|
||||
_paused(false),
|
||||
lastPose_(Transform::getIdentity())
|
||||
@@ -62,8 +61,6 @@ RtabmapThread::~RtabmapThread()
|
||||
UEventsManager::removeHandler(this);
|
||||
|
||||
close(true);
|
||||
|
||||
delete _frameRateTimer;
|
||||
}
|
||||
|
||||
void RtabmapThread::pushNewState(State newState, const RtabmapEventCmd & cmdEvent)
|
||||
@@ -88,7 +85,7 @@ void RtabmapThread::clearBufferedData()
|
||||
_newMapEvents.clear();
|
||||
lastPose_.setIdentity();
|
||||
covariance_ = cv::Mat();
|
||||
_previousStamp = 0;
|
||||
_previousStamp = -1;
|
||||
}
|
||||
_dataMutex.unlock();
|
||||
|
||||
@@ -500,9 +497,10 @@ void RtabmapThread::addData(const OdometryEvent & odomEvent)
|
||||
bool ignoreFrame = false;
|
||||
if(_rate>0.0f)
|
||||
{
|
||||
if((_previousStamp>=0.0 && odomEvent.data().stamp()>_previousStamp && odomEvent.data().stamp() - _previousStamp < 1.0f/_rate) ||
|
||||
((_previousStamp<=0.0 || odomEvent.data().stamp()<=_previousStamp) && _frameRateTimer->getElapsedTime() < 1.0f/_rate))
|
||||
if((_previousStamp>=0.0 && odomEvent.data().stamp()>_previousStamp && odomEvent.data().stamp() - _previousStamp < 1.0f/_rate))
|
||||
{
|
||||
UDEBUG("Ignoring frame %f (previous stamp=%f, period=%f)",
|
||||
odomEvent.data().stamp(), _previousStamp, 1.0/_rate);
|
||||
ignoreFrame = true;
|
||||
}
|
||||
}
|
||||
@@ -540,7 +538,6 @@ void RtabmapThread::addData(const OdometryEvent & odomEvent)
|
||||
}
|
||||
else if(!ignoreFrame)
|
||||
{
|
||||
_frameRateTimer->start();
|
||||
_previousStamp = odomEvent.data().stamp();
|
||||
}
|
||||
|
||||
@@ -559,7 +556,6 @@ void RtabmapThread::addData(const OdometryEvent & odomEvent)
|
||||
// set negative id so rtabmap will detect it as an intermediate node
|
||||
SensorData tmp = odomEvent.data();
|
||||
tmp.setId(-1);
|
||||
tmp.setFeatures(std::vector<cv::KeyPoint>(), std::vector<cv::Point3f>(), cv::Mat());// remove features
|
||||
_dataBuffer.push_back(OdometryEvent(tmp, odomEvent.pose(), odomInfo));
|
||||
}
|
||||
else
|
||||
|
||||
@@ -960,7 +960,6 @@ void SensorCaptureThread::postUpdate(SensorData * dataPtr, SensorCaptureInfo * i
|
||||
{
|
||||
UASSERT(!data.imu().localTransform().isNull());
|
||||
imu.convertToBaseFrame();
|
||||
|
||||
}
|
||||
_imuFilter->update(
|
||||
imu.angularVelocity()[0],
|
||||
|
||||
@@ -976,7 +976,7 @@ unsigned long SensorData::getMemoryUsed() const // Return memory usage in Bytes
|
||||
(_descriptors.empty()?0:_descriptors.total()*_descriptors.elemSize());
|
||||
}
|
||||
|
||||
void SensorData::clearCompressedData(bool images, bool scan, bool userData)
|
||||
void SensorData::clearCompressedData(bool images, bool scan, bool userData, bool occupancyGrid)
|
||||
{
|
||||
if(images)
|
||||
{
|
||||
@@ -992,14 +992,32 @@ void SensorData::clearCompressedData(bool images, bool scan, bool userData)
|
||||
{
|
||||
_userDataCompressed=cv::Mat();
|
||||
}
|
||||
if(occupancyGrid)
|
||||
{
|
||||
_groundCellsCompressed=cv::Mat();
|
||||
_emptyCellsCompressed=cv::Mat();
|
||||
_obstacleCellsCompressed=cv::Mat();
|
||||
|
||||
if( _groundCellsCompressed.empty() && _groundCellsRaw.empty() &&
|
||||
_obstacleCellsCompressed.empty() && _obstacleCellsRaw.empty() &&
|
||||
_emptyCellsCompressed.empty() && _emptyCellsRaw.empty())
|
||||
{
|
||||
_cellSize = 0.0f;
|
||||
_viewPoint = cv::Point3f();
|
||||
}
|
||||
}
|
||||
}
|
||||
void SensorData::clearRawData(bool images, bool scan, bool userData)
|
||||
void SensorData::clearRawData(bool images, bool scan, bool userData, bool occupancyGrid)
|
||||
{
|
||||
if(images)
|
||||
{
|
||||
_imageRaw=cv::Mat();
|
||||
_depthOrRightRaw=cv::Mat();
|
||||
_depthConfidenceRaw=cv::Mat();
|
||||
#ifdef HAVE_OPENCV_CUDEV
|
||||
_imageRawGpu = cv::cuda::GpuMat();
|
||||
_depthOrRightRawGpu = cv::cuda::GpuMat();
|
||||
#endif
|
||||
}
|
||||
if(scan)
|
||||
{
|
||||
@@ -1009,6 +1027,20 @@ void SensorData::clearRawData(bool images, bool scan, bool userData)
|
||||
{
|
||||
_userDataRaw=cv::Mat();
|
||||
}
|
||||
if(occupancyGrid)
|
||||
{
|
||||
_groundCellsRaw=cv::Mat();
|
||||
_emptyCellsRaw=cv::Mat();
|
||||
_obstacleCellsRaw=cv::Mat();
|
||||
|
||||
if( _groundCellsCompressed.empty() && _groundCellsRaw.empty() &&
|
||||
_obstacleCellsCompressed.empty() && _obstacleCellsRaw.empty() &&
|
||||
_emptyCellsCompressed.empty() && _emptyCellsRaw.empty())
|
||||
{
|
||||
_cellSize = 0.0f;
|
||||
_viewPoint = cv::Point3f();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
+22
-6
@@ -113,6 +113,9 @@ std::vector<cv::Point2f> Stereo::computeCorrespondences(
|
||||
StereoOpticalFlow::StereoOpticalFlow(const ParametersMap & parameters) :
|
||||
Stereo(parameters),
|
||||
epsilon_(Parameters::defaultStereoEps()),
|
||||
useMinEigenVals_(Parameters::defaultStereoUseMinEigenVals()),
|
||||
minEigThreshold_(Parameters::defaultStereoMinEigThreshold()),
|
||||
errorThreshold_(Parameters::defaultStereoErrorThreshold()),
|
||||
gpu_(Parameters::defaultStereoGpu())
|
||||
{
|
||||
this->parseParameters(parameters);
|
||||
@@ -122,6 +125,9 @@ void StereoOpticalFlow::parseParameters(const ParametersMap & parameters)
|
||||
{
|
||||
Stereo::parseParameters(parameters);
|
||||
Parameters::parse(parameters, Parameters::kStereoEps(), epsilon_);
|
||||
Parameters::parse(parameters, Parameters::kStereoUseMinEigenVals(), useMinEigenVals_);
|
||||
Parameters::parse(parameters, Parameters::kStereoMinEigThreshold(), minEigThreshold_);
|
||||
Parameters::parse(parameters, Parameters::kStereoErrorThreshold(), errorThreshold_);
|
||||
Parameters::parse(parameters, Parameters::kStereoGpu(), gpu_);
|
||||
#ifndef HAVE_OPENCV_CUDAOPTFLOW
|
||||
if(gpu_)
|
||||
@@ -171,11 +177,18 @@ std::vector<cv::Point2f> StereoOpticalFlow::computeCorrespondences(
|
||||
err,
|
||||
this->winSize(),
|
||||
this->maxLevel(),
|
||||
cv::TermCriteria(cv::TermCriteria::COUNT+cv::TermCriteria::EPS, this->iterations(), epsilon_),
|
||||
cv::OPTFLOW_LK_GET_MIN_EIGENVALS, 1e-4);
|
||||
cv::TermCriteria(cv::TermCriteria::COUNT+cv::TermCriteria::EPS, this->iterations(), this->epsilon()),
|
||||
this->usingMinEigenVals() ? cv::OPTFLOW_LK_GET_MIN_EIGENVALS : 0, this->minEigThreshold());
|
||||
UDEBUG("util2d::calcOpticalFlowPyrLKStereo() end");
|
||||
}
|
||||
updateStatus(leftCorners, rightCorners, status);
|
||||
if(this->usingMinEigenVals())
|
||||
{
|
||||
updateStatus(leftCorners, rightCorners, status);
|
||||
}
|
||||
else
|
||||
{
|
||||
updateStatus(leftCorners, rightCorners, status, err);
|
||||
}
|
||||
return rightCorners;
|
||||
}
|
||||
|
||||
@@ -227,14 +240,17 @@ std::vector<cv::Point2f> StereoOpticalFlow::computeCorrespondences(
|
||||
void StereoOpticalFlow::updateStatus(
|
||||
const std::vector<cv::Point2f> & leftCorners,
|
||||
const std::vector<cv::Point2f> & rightCorners,
|
||||
std::vector<unsigned char> & status) const
|
||||
std::vector<unsigned char> & status,
|
||||
std::vector<float> err) const
|
||||
{
|
||||
UASSERT(leftCorners.size() == rightCorners.size() && status.size() == leftCorners.size());
|
||||
UASSERT(
|
||||
leftCorners.size() == rightCorners.size() && status.size() == leftCorners.size() &&
|
||||
(err.empty() || err.size() == leftCorners.size()));
|
||||
int countFlowRejected = 0;
|
||||
int countDisparityRejected = 0;
|
||||
for(unsigned int i=0; i<status.size(); ++i)
|
||||
{
|
||||
if(status[i]!=0)
|
||||
if(status[i]!=0 && (err.empty() || err[i] < this->errorThreshold()))
|
||||
{
|
||||
float disparity = leftCorners[i].x - rightCorners[i].x;
|
||||
if(disparity <= this->minDisparity() || disparity > this->maxDisparity())
|
||||
|
||||
@@ -52,6 +52,26 @@ Transform::Transform(
|
||||
r11, r12, r13, o14,
|
||||
r21, r22, r23, o24,
|
||||
r31, r32, r33, o34);
|
||||
|
||||
if( r11>0.0f || r12>0.0f || r13>0.0f ||
|
||||
r21>0.0f || r22>0.0f || r23>0.0f ||
|
||||
r31>0.0f || r32>0.0f || r33>0.0f)
|
||||
{
|
||||
Eigen::Matrix3f m;
|
||||
m << r11, r12, r13,
|
||||
r21, r22, r23,
|
||||
r31, r32, r33;
|
||||
float d = m.determinant();
|
||||
if(fabs(d-1.0f) > 0.0001)
|
||||
{
|
||||
UWARN("Created transform doesn't have normalized rotation. Any transformation with this transform can cause unexpected results!"
|
||||
" Determinant([%f %f %f;%f %f %f;%f %f %f])=%f",
|
||||
r11, r12, r13,
|
||||
r21, r22, r23,
|
||||
r31, r32, r33,
|
||||
d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Transform::Transform(const cv::Mat & transformationMatrix)
|
||||
@@ -509,6 +529,11 @@ Transform Transform::fromString(const std::string & string)
|
||||
numbers[4], numbers[5], numbers[6], numbers[7],
|
||||
numbers[8], numbers[9], numbers[10], numbers[11]);
|
||||
}
|
||||
// Always normalize
|
||||
if(!t.isNull())
|
||||
{
|
||||
t.normalizeRotation();
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
|
||||
@@ -571,19 +571,36 @@ void VWDictionary::update()
|
||||
else if(_strategy >= kNNBruteForce &&
|
||||
_notIndexedWords.size() &&
|
||||
_removedIndexedWords.size() == 0 &&
|
||||
_visualWords.size() &&
|
||||
_dataTree.rows)
|
||||
_visualWords.size())
|
||||
{
|
||||
const int IMGIDX_SHIFT = 18;
|
||||
const int IMGIDX_ONE = (1 << IMGIDX_SHIFT); // a limit defined in https://github.com/opencv/opencv/blob/4.x/modules/features2d/src/matchers.cpp
|
||||
if(_dataTree.rows >= IMGIDX_ONE)
|
||||
{
|
||||
UWARN("%s=%d is not a FLANN strategy and the number of words in the vocabulary (%d) is over %d (IMGIDX_ONE), so opencv may "
|
||||
"assert on an IMGIDX_ONE check when adding new words. Use a FLANN strategy instead (%s<%d).",
|
||||
Parameters::kKpNNStrategy().c_str(), _strategy, _dataTree.rows, IMGIDX_ONE, Parameters::kKpNNStrategy().c_str(), kNNBruteForce);
|
||||
}
|
||||
|
||||
//just add not indexed words
|
||||
int i = _dataTree.rows;
|
||||
_dataTree.reserve(_dataTree.rows + _notIndexedWords.size());
|
||||
if(!_dataTree.empty()) {
|
||||
_dataTree.reserve(_dataTree.rows + _notIndexedWords.size());
|
||||
}
|
||||
for(std::set<int>::iterator iter=_notIndexedWords.begin(); iter!=_notIndexedWords.end(); ++iter)
|
||||
{
|
||||
VisualWord* w = uValue(_visualWords, *iter, (VisualWord*)0);
|
||||
UASSERT(w);
|
||||
UASSERT(w->getDescriptor().cols == _dataTree.cols);
|
||||
UASSERT(w->getDescriptor().type() == _dataTree.type());
|
||||
_dataTree.push_back(w->getDescriptor());
|
||||
if(_dataTree.empty())
|
||||
{
|
||||
_dataTree = w->getDescriptor().clone();
|
||||
}
|
||||
else
|
||||
{
|
||||
UASSERT(w->getDescriptor().cols == _dataTree.cols);
|
||||
UASSERT(w->getDescriptor().type() == _dataTree.type());
|
||||
_dataTree.push_back(w->getDescriptor());
|
||||
}
|
||||
_mapIndexId.insert(_mapIndexId.end(), std::pair<int, int>(i, w->id()));
|
||||
std::pair<std::map<int, int>::iterator, bool> inserted = _mapIdIndex.insert(std::pair<int, int>(w->id(), i));
|
||||
UASSERT(inserted.second);
|
||||
@@ -860,7 +877,7 @@ int VWDictionary::getNextId()
|
||||
return ++_lastWordId;
|
||||
}
|
||||
|
||||
void VWDictionary::addWordRef(int wordId, int signatureId)
|
||||
bool VWDictionary::addWordRef(int wordId, int signatureId)
|
||||
{
|
||||
VisualWord * vw = 0;
|
||||
vw = uValue(_visualWords, wordId, vw);
|
||||
@@ -870,10 +887,12 @@ void VWDictionary::addWordRef(int wordId, int signatureId)
|
||||
_totalActiveReferences += 1;
|
||||
|
||||
_unusedWords.erase(vw->id());
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
UERROR("Not found word %d (dict size=%d)", wordId, (int)_visualWords.size());
|
||||
UWARN("Not found word %d (dict size=%d)", wordId, (int)_visualWords.size());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -996,7 +1015,7 @@ std::list<int> VWDictionary::addNewWords(
|
||||
if(_flannIndex->isBuilt() || (!_dataTree.empty() && _dataTree.rows >= (int)k))
|
||||
{
|
||||
//Find nearest neighbors
|
||||
UDEBUG("newPts.total()=%d ", descriptors.rows);
|
||||
UDEBUG("newPts.total()=%d _strategy=%d", descriptors.rows, _strategy);
|
||||
|
||||
if(_strategy == kNNFlannNaive || _strategy == kNNFlannKdTree || _strategy == kNNFlannLSH)
|
||||
{
|
||||
|
||||
@@ -63,6 +63,7 @@ CameraImages::CameraImages() :
|
||||
_syncImageRateWithStamps(true),
|
||||
_odometryFormat(0),
|
||||
_groundTruthFormat(0),
|
||||
_groundTruthLocalTransform(Transform::getIdentity()),
|
||||
_maxPoseTimeDiff(0.02),
|
||||
_captureDelay(0.0)
|
||||
{}
|
||||
@@ -93,6 +94,7 @@ CameraImages::CameraImages(const std::string & path,
|
||||
_syncImageRateWithStamps(true),
|
||||
_odometryFormat(0),
|
||||
_groundTruthFormat(0),
|
||||
_groundTruthLocalTransform(Transform::getIdentity()),
|
||||
_maxPoseTimeDiff(0.02),
|
||||
_captureDelay(0.0)
|
||||
{
|
||||
@@ -478,27 +480,43 @@ bool CameraImages::init(const std::string & calibrationFolder, const std::string
|
||||
if(success && _odometryPath.size() && odometry_.empty())
|
||||
{
|
||||
success = readPoses(odometry_, _stamps, _odometryPath, _odometryFormat, _maxPoseTimeDiff);
|
||||
if(!success)
|
||||
{
|
||||
UERROR("Failed to read odometry poses.");
|
||||
}
|
||||
|
||||
if(success)
|
||||
{
|
||||
for(size_t i=0; i<odometry_.size(); ++i)
|
||||
{
|
||||
// linear cov = 0.0001
|
||||
cv::Mat covariance = cv::Mat::eye(6,6,CV_64FC1) * (i==0?9999.0:0.0001);
|
||||
if(i!=0)
|
||||
{
|
||||
// angular cov = 0.000001
|
||||
covariance.at<double>(3,3) *= 0.01;
|
||||
covariance.at<double>(4,4) *= 0.01;
|
||||
covariance.at<double>(5,5) *= 0.01;
|
||||
}
|
||||
covariances_.push_back(covariance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(success && _groundTruthPath.size())
|
||||
{
|
||||
success = readPoses(groundTruth_, _stamps, _groundTruthPath, _groundTruthFormat, _maxPoseTimeDiff);
|
||||
}
|
||||
|
||||
if(!odometry_.empty())
|
||||
{
|
||||
for(size_t i=0; i<odometry_.size(); ++i)
|
||||
if(!success)
|
||||
{
|
||||
// linear cov = 0.0001
|
||||
cv::Mat covariance = cv::Mat::eye(6,6,CV_64FC1) * (i==0?9999.0:0.0001);
|
||||
if(i!=0)
|
||||
UERROR("Failed to read ground truth poses.");
|
||||
}
|
||||
else if(!_groundTruthLocalTransform.isIdentity())
|
||||
{
|
||||
Transform gtInv = _groundTruthLocalTransform.inverse();
|
||||
for(auto pose: groundTruth_)
|
||||
{
|
||||
// angular cov = 0.000001
|
||||
covariance.at<double>(3,3) *= 0.01;
|
||||
covariance.at<double>(4,4) *= 0.01;
|
||||
covariance.at<double>(5,5) *= 0.01;
|
||||
pose = pose*gtInv; // pose of base_link, assuming ground truth frame and base frame are rigidly fixed
|
||||
}
|
||||
covariances_.push_back(covariance);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -607,7 +625,7 @@ bool CameraImages::readPoses(
|
||||
}
|
||||
if(validPoses != (int)inOutStamps.size())
|
||||
{
|
||||
UWARN("%d valid poses of %d stamps", validPoses, (int)inOutStamps.size());
|
||||
UWARN("%d/%ld valid poses of %ld stamps", validPoses, outputPoses.size(), inOutStamps.size());
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -756,32 +774,19 @@ SensorData CameraImages::captureImage(SensorCaptureInfo * info)
|
||||
|
||||
if(_stamps.size())
|
||||
{
|
||||
stamp = _stamps.front();
|
||||
_stamps.pop_front();
|
||||
if(_stamps.size())
|
||||
{
|
||||
_captureDelay = _stamps.front() - stamp;
|
||||
}
|
||||
UERROR("stamps cannot be used when startAt < 0");
|
||||
}
|
||||
if(odometry_.size())
|
||||
{
|
||||
odometryPose = odometry_.front();
|
||||
odometry_.pop_front();
|
||||
if(covariances_.size())
|
||||
{
|
||||
covariance = covariances_.front();
|
||||
covariances_.pop_front();
|
||||
}
|
||||
UERROR("odometry cannot be used when startAt < 0");
|
||||
}
|
||||
if(groundTruth_.size())
|
||||
{
|
||||
groundTruthPose = groundTruth_.front();
|
||||
groundTruth_.pop_front();
|
||||
UERROR("groundTruth cannot be used when startAt < 0");
|
||||
}
|
||||
if(_models.size() && !model.isValidForProjection())
|
||||
{
|
||||
model = _models.front();
|
||||
_models.pop_front();
|
||||
UERROR("models cannot be used when startAt < 0");
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -792,6 +797,7 @@ SensorData CameraImages::captureImage(SensorCaptureInfo * info)
|
||||
{
|
||||
imageFilePath = _path + imageFileName;
|
||||
scanFilePath = _scanPath + scanFileName;
|
||||
size_t stampsSize = _stamps.size();
|
||||
if(_stamps.size())
|
||||
{
|
||||
stamp = _stamps.front();
|
||||
@@ -803,6 +809,8 @@ SensorData CameraImages::captureImage(SensorCaptureInfo * info)
|
||||
}
|
||||
if(odometry_.size())
|
||||
{
|
||||
UASSERT_MSG(stampsSize==0 || stampsSize == odometry_.size(),
|
||||
uFormat("Stamps=%ld odometry=%ld", _stamps.size(), odometry_.size()).c_str());
|
||||
odometryPose = odometry_.front();
|
||||
odometry_.pop_front();
|
||||
if(covariances_.size())
|
||||
@@ -813,11 +821,15 @@ SensorData CameraImages::captureImage(SensorCaptureInfo * info)
|
||||
}
|
||||
if(groundTruth_.size())
|
||||
{
|
||||
UASSERT_MSG(stampsSize==0 || stampsSize == groundTruth_.size(),
|
||||
uFormat("Stamps=%ld groundTruth=%ld", _stamps.size(), groundTruth_.size()).c_str());
|
||||
groundTruthPose = groundTruth_.front();
|
||||
groundTruth_.pop_front();
|
||||
}
|
||||
if(_models.size() && !model.isValidForProjection())
|
||||
{
|
||||
UASSERT_MSG(stampsSize==0 || stampsSize == _models.size(),
|
||||
uFormat("Stamps=%ld models=%ld", _stamps.size(), _models.size()).c_str());
|
||||
model = _models.front();
|
||||
_models.pop_front();
|
||||
}
|
||||
@@ -834,6 +846,7 @@ SensorData CameraImages::captureImage(SensorCaptureInfo * info)
|
||||
|
||||
imageFilePath = _path + imageFileName;
|
||||
scanFilePath = _scanPath + scanFileName;
|
||||
size_t stampsSize = _stamps.size();
|
||||
if(_stamps.size())
|
||||
{
|
||||
stamp = _stamps.front();
|
||||
@@ -845,6 +858,8 @@ SensorData CameraImages::captureImage(SensorCaptureInfo * info)
|
||||
}
|
||||
if(odometry_.size())
|
||||
{
|
||||
UASSERT_MSG(stampsSize==0 || stampsSize == odometry_.size(),
|
||||
uFormat("Stamps=%ld odometry=%ld", stampsSize, odometry_.size()).c_str());
|
||||
odometryPose = odometry_.front();
|
||||
odometry_.pop_front();
|
||||
if(covariances_.size())
|
||||
@@ -855,11 +870,15 @@ SensorData CameraImages::captureImage(SensorCaptureInfo * info)
|
||||
}
|
||||
if(groundTruth_.size())
|
||||
{
|
||||
UASSERT_MSG(stampsSize==0 || stampsSize == groundTruth_.size(),
|
||||
uFormat("Stamps=%ld groundTruth=%ld", _stamps.size(), groundTruth_.size()).c_str());
|
||||
groundTruthPose = groundTruth_.front();
|
||||
groundTruth_.pop_front();
|
||||
}
|
||||
if(_models.size() && !model.isValidForProjection())
|
||||
{
|
||||
UASSERT_MSG(stampsSize==0 || stampsSize == _models.size(),
|
||||
uFormat("Stamps=%ld models=%ld", _stamps.size(), _models.size()).c_str());
|
||||
model = _models.front();
|
||||
_models.pop_front();
|
||||
}
|
||||
|
||||
@@ -155,10 +155,10 @@ bool CameraOrbbecSDK::available()
|
||||
|
||||
CameraOrbbecSDK::CameraOrbbecSDK(
|
||||
std::string deviceId,
|
||||
int colorWidth,
|
||||
int colorHeight,
|
||||
int depthWidth,
|
||||
int depthHeight,
|
||||
unsigned int colorWidth,
|
||||
unsigned int colorHeight,
|
||||
unsigned int depthWidth,
|
||||
unsigned int depthHeight,
|
||||
float imageRate,
|
||||
const Transform & localTransform) :
|
||||
Camera(imageRate, localTransform)
|
||||
@@ -327,22 +327,36 @@ bool CameraOrbbecSDK::init(const std::string & calibrationFolder, const std::str
|
||||
UINFO("Resolution: %ldx%ld, FPS: %ld, Format: %d",
|
||||
profile->getWidth(), profile->getHeight(), profile->getFps(), profile->getFormat(), j==0?" (default)":"");
|
||||
|
||||
// Get maximum frame rate based on resolution selected
|
||||
if(sensors->getSensorType(i) == OB_SENSOR_DEPTH) {
|
||||
if(profile->getFps() > maxDepthFps) {
|
||||
if( profile->getFps() > maxDepthFps &&
|
||||
depthWidth_ == profile->getWidth() &&
|
||||
depthHeight_ == profile->getHeight())
|
||||
{
|
||||
maxDepthFps = profile->getFps();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(profile->getFps() > maxColorFps) {
|
||||
if( profile->getFps() > maxColorFps &&
|
||||
colorWidth_ == profile->getWidth() &&
|
||||
colorHeight_ == profile->getHeight())
|
||||
{
|
||||
maxColorFps = profile->getFps();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Note that for TOF camera, we want maximum frame rate to better
|
||||
// sync rgb and depth. For stereo cameras, use the specified frame rate.
|
||||
if(this->getImageRate()!=0.0f && device->getDeviceInfo()->getDeviceType() != OB_TOF_CAMERA)
|
||||
{
|
||||
maxColorFps = maxDepthFps = (unsigned int)this->getImageRate();
|
||||
this->setImageRate(0);
|
||||
}
|
||||
|
||||
std::shared_ptr<ob::Config> imuConfig;
|
||||
if(imuPublished_)
|
||||
{
|
||||
|
||||
@@ -30,6 +30,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include <rtabmap/utilite/UThreadC.h>
|
||||
#include <rtabmap/utilite/UConversion.h>
|
||||
#include <rtabmap/utilite/UStl.h>
|
||||
#include <rtabmap/utilite/UDirectory.h>
|
||||
#include <opencv2/imgproc/types_c.h>
|
||||
|
||||
#ifdef RTABMAP_REALSENSE2
|
||||
@@ -78,7 +79,8 @@ CameraRealSense2::CameraRealSense2(
|
||||
cameraDepthFps_(30),
|
||||
globalTimeSync_(true),
|
||||
dualMode_(false),
|
||||
closing_(false)
|
||||
closing_(false),
|
||||
playback_(false)
|
||||
#endif
|
||||
{
|
||||
UDEBUG("");
|
||||
@@ -130,6 +132,7 @@ void CameraRealSense2::close()
|
||||
}
|
||||
|
||||
closing_ = false;
|
||||
playback_ = false;
|
||||
}
|
||||
|
||||
void CameraRealSense2::imu_callback(rs2::frame frame)
|
||||
@@ -492,64 +495,76 @@ bool CameraRealSense2::init(const std::string & calibrationFolder, const std::st
|
||||
clockSyncWarningShown_ = false;
|
||||
imuGlobalSyncWarningShown_ = false;
|
||||
|
||||
rs2::device_list list = ctx_.query_devices();
|
||||
if (0 == list.size())
|
||||
if(uStrContains(deviceId_, ".bag"))
|
||||
{
|
||||
UERROR("No RealSense2 devices were found!");
|
||||
return false;
|
||||
// playback (bag recorded by realsense-viewer)
|
||||
dev_.resize(1);
|
||||
dev_[0] = ctx_.load_device(uReplaceChar(deviceId_, '~', UDirectory::homeDir()));
|
||||
playback_ = true;
|
||||
UINFO("Device ID is a bag (\"%s\"), using playback mode", deviceId_.c_str());
|
||||
}
|
||||
|
||||
bool found=false;
|
||||
try
|
||||
else
|
||||
{
|
||||
for (rs2::device dev : list)
|
||||
rs2::device_list list = ctx_.query_devices();
|
||||
if (0 == list.size())
|
||||
{
|
||||
auto sn = dev.get_info(RS2_CAMERA_INFO_SERIAL_NUMBER);
|
||||
auto pid_str = dev.get_info(RS2_CAMERA_INFO_PRODUCT_ID);
|
||||
auto name = dev.get_info(RS2_CAMERA_INFO_NAME);
|
||||
UERROR("No RealSense2 devices were found!");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t pid;
|
||||
std::stringstream ss;
|
||||
ss << std::hex << pid_str;
|
||||
ss >> pid;
|
||||
UINFO("Device \"%s\" with serial number %s was found with product ID=%d.", name, sn, (int)pid);
|
||||
if(dualMode_ && pid == 0x0B37)
|
||||
bool found=false;
|
||||
try
|
||||
{
|
||||
for (rs2::device dev : list)
|
||||
{
|
||||
// Dual setup: device[0] = D400, device[1] = T265
|
||||
// T265
|
||||
dev_.resize(2);
|
||||
dev_[1] = dev;
|
||||
}
|
||||
else if (!found && (deviceId_.empty() || deviceId_ == sn || uStrContains(name, uToUpperCase(deviceId_))))
|
||||
{
|
||||
if(dev_.empty())
|
||||
auto sn = dev.get_info(RS2_CAMERA_INFO_SERIAL_NUMBER);
|
||||
auto pid_str = dev.get_info(RS2_CAMERA_INFO_PRODUCT_ID);
|
||||
auto name = dev.get_info(RS2_CAMERA_INFO_NAME);
|
||||
|
||||
uint16_t pid;
|
||||
std::stringstream ss;
|
||||
ss << std::hex << pid_str;
|
||||
ss >> pid;
|
||||
UINFO("Device \"%s\" with serial number %s was found with product ID=%d.", name, sn, (int)pid);
|
||||
if(dualMode_ && pid == 0x0B37)
|
||||
{
|
||||
dev_.resize(1);
|
||||
// Dual setup: device[0] = D400, device[1] = T265
|
||||
// T265
|
||||
dev_.resize(2);
|
||||
dev_[1] = dev;
|
||||
}
|
||||
else if (!found && (deviceId_.empty() || deviceId_ == sn || uStrContains(name, uToUpperCase(deviceId_))))
|
||||
{
|
||||
if(dev_.empty())
|
||||
{
|
||||
dev_.resize(1);
|
||||
}
|
||||
dev_[0] = dev;
|
||||
found=true;
|
||||
}
|
||||
dev_[0] = dev;
|
||||
found=true;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(const rs2::error & error)
|
||||
{
|
||||
UWARN("%s. Is the camera already used with another app?", error.what());
|
||||
catch(const rs2::error & error)
|
||||
{
|
||||
UWARN("%s. Is the camera already used with another app?", error.what());
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
if(dualMode_ && dev_.size()==2)
|
||||
{
|
||||
UERROR("Dual setup is enabled, but a D400 camera is not detected!");
|
||||
dev_.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
UERROR("The requested device \"%s\" is NOT found!", deviceId_.c_str());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
if(dualMode_ && dev_.size()==2)
|
||||
{
|
||||
UERROR("Dual setup is enabled, but a D400 camera is not detected!");
|
||||
dev_.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
UERROR("The requested device \"%s\" is NOT found!", deviceId_.c_str());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else if(dualMode_ && dev_.size()!=2)
|
||||
if(dualMode_ && dev_.size()!=2)
|
||||
{
|
||||
UERROR("Dual setup is enabled, but a T265 camera is not detected!");
|
||||
dev_.clear();
|
||||
@@ -634,7 +649,14 @@ bool CameraRealSense2::init(const std::string & calibrationFolder, const std::st
|
||||
sensors[1] = elem;
|
||||
if(sensors[1].supports(rs2_option::RS2_OPTION_EMITTER_ENABLED))
|
||||
{
|
||||
sensors[1].set_option(rs2_option::RS2_OPTION_EMITTER_ENABLED, emitterEnabled_);
|
||||
if(!sensors[1].is_option_read_only(rs2_option::RS2_OPTION_EMITTER_ENABLED))
|
||||
{
|
||||
sensors[1].set_option(rs2_option::RS2_OPTION_EMITTER_ENABLED, emitterEnabled_);
|
||||
}
|
||||
else if(!emitterEnabled_)
|
||||
{
|
||||
UWARN("rs2_option::RS2_OPTION_EMITTER_ENABLED option is read-only, cannot disable IR emitter.");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ("Coded-Light Depth Sensor" == module_name)
|
||||
@@ -712,14 +734,14 @@ bool CameraRealSense2::init(const std::string & calibrationFolder, const std::st
|
||||
for (auto& profile : profiles)
|
||||
{
|
||||
auto video_profile = profile.as<rs2::video_stream_profile>();
|
||||
UINFO("%s %d %d %d %d %s type=%d", rs2_format_to_string(
|
||||
video_profile.format()),
|
||||
video_profile.width(),
|
||||
video_profile.height(),
|
||||
video_profile.fps(),
|
||||
video_profile.stream_index(),
|
||||
video_profile.stream_name().c_str(),
|
||||
video_profile.stream_type());
|
||||
UINFO("%s %d %d %d %d %s type=%d",
|
||||
rs2_format_to_string(profile.format()),
|
||||
video_profile.get()?video_profile.width():-1,
|
||||
video_profile.get()?video_profile.height():-1,
|
||||
profile.fps(),
|
||||
profile.stream_index(),
|
||||
profile.stream_name().c_str(),
|
||||
profile.stream_type());
|
||||
}
|
||||
}
|
||||
int pi = 0;
|
||||
@@ -728,10 +750,12 @@ bool CameraRealSense2::init(const std::string & calibrationFolder, const std::st
|
||||
auto video_profile = profile.as<rs2::video_stream_profile>();
|
||||
if(!stereo)
|
||||
{
|
||||
if( (video_profile.width() == cameraWidth_ &&
|
||||
if( (video_profile.get() &&
|
||||
video_profile.width() == cameraWidth_ &&
|
||||
video_profile.height() == cameraHeight_ &&
|
||||
video_profile.fps() == cameraFps_) ||
|
||||
(strcmp(sensors[i].get_info(RS2_CAMERA_INFO_NAME), "L500 Depth Sensor")==0 &&
|
||||
((strcmp(sensors[i].get_info(RS2_CAMERA_INFO_NAME), "L500 Depth Sensor")==0 ||
|
||||
(playback_ && strcmp(sensors[i].get_info(RS2_CAMERA_INFO_NAME), "Stereo Module")==0)) &&
|
||||
video_profile.width() == cameraDepthWidth_ &&
|
||||
video_profile.height() == cameraDepthHeight_ &&
|
||||
video_profile.fps() == cameraDepthFps_))
|
||||
@@ -740,7 +764,7 @@ bool CameraRealSense2::init(const std::string & calibrationFolder, const std::st
|
||||
|
||||
// rgb or ir left
|
||||
if((!ir_ && video_profile.format() == RS2_FORMAT_RGB8 && video_profile.stream_type() == RS2_STREAM_COLOR) ||
|
||||
(ir_ && video_profile.format() == RS2_FORMAT_Y8 && (video_profile.stream_index() == 1 || isL500)))
|
||||
(ir_ && video_profile.format() == RS2_FORMAT_Y8 && (video_profile.stream_index() == 1 || isL500)))
|
||||
{
|
||||
if(!profilesPerSensor[i].empty())
|
||||
{
|
||||
@@ -778,7 +802,7 @@ bool CameraRealSense2::init(const std::string & calibrationFolder, const std::st
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(video_profile.format() == RS2_FORMAT_MOTION_XYZ32F || video_profile.format() == RS2_FORMAT_6DOF)
|
||||
else if(profile.format() == RS2_FORMAT_MOTION_XYZ32F || profile.format() == RS2_FORMAT_6DOF)
|
||||
{
|
||||
//D435i:
|
||||
//MOTION_XYZ32F 0 0 200 (gyro)
|
||||
@@ -817,6 +841,7 @@ bool CameraRealSense2::init(const std::string & calibrationFolder, const std::st
|
||||
{
|
||||
//T265:
|
||||
if(!dualMode_ &&
|
||||
video_profile.get() &&
|
||||
video_profile.format() == RS2_FORMAT_Y8 &&
|
||||
video_profile.width() == 848 &&
|
||||
video_profile.height() == 800 &&
|
||||
@@ -865,7 +890,7 @@ bool CameraRealSense2::init(const std::string & calibrationFolder, const std::st
|
||||
}
|
||||
added = true;
|
||||
}
|
||||
else if(video_profile.format() == RS2_FORMAT_MOTION_XYZ32F || video_profile.format() == RS2_FORMAT_6DOF)
|
||||
else if(profile.format() == RS2_FORMAT_MOTION_XYZ32F || profile.format() == RS2_FORMAT_6DOF)
|
||||
{
|
||||
//MOTION_XYZ32F 0 0 200
|
||||
//MOTION_XYZ32F 0 0 62
|
||||
@@ -878,20 +903,29 @@ bool CameraRealSense2::init(const std::string & calibrationFolder, const std::st
|
||||
}
|
||||
if (!added)
|
||||
{
|
||||
UERROR("Given stream configuration is not supported by the device! "
|
||||
"Stream Index: %d, Width: %d, Height: %d, FPS: %d", i, cameraWidth_, cameraHeight_, cameraFps_);
|
||||
if(strcmp(sensors[i].get_info(RS2_CAMERA_INFO_NAME), "L500 Depth Sensor")==0 ||
|
||||
(playback_ && strcmp(sensors[i].get_info(RS2_CAMERA_INFO_NAME), "Stereo Module")==0))
|
||||
{
|
||||
UERROR("Given stream configuration is not supported by the device! "
|
||||
"Stream Index: %d, Width: %d, Height: %d, FPS: %d", i, cameraDepthWidth_, cameraDepthHeight_, cameraDepthFps_);
|
||||
}
|
||||
else
|
||||
{
|
||||
UERROR("Given stream configuration is not supported by the device! "
|
||||
"Stream Index: %d, Width: %d, Height: %d, FPS: %d", i, cameraWidth_, cameraHeight_, cameraFps_);
|
||||
}
|
||||
UERROR("Available configurations:");
|
||||
for (auto& profile : profiles)
|
||||
{
|
||||
auto video_profile = profile.as<rs2::video_stream_profile>();
|
||||
UERROR("%s %d %d %d %d %s type=%d", rs2_format_to_string(
|
||||
video_profile.format()),
|
||||
video_profile.width(),
|
||||
video_profile.height(),
|
||||
video_profile.fps(),
|
||||
video_profile.stream_index(),
|
||||
video_profile.stream_name().c_str(),
|
||||
video_profile.stream_type());
|
||||
UERROR("%s %d %d %d %d %s type=%d",
|
||||
rs2_format_to_string(profile.format()),
|
||||
video_profile.get()?video_profile.width():-1,
|
||||
video_profile.get()?video_profile.height():-1,
|
||||
profile.fps(),
|
||||
profile.stream_index(),
|
||||
profile.stream_name().c_str(),
|
||||
profile.stream_type());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -1075,19 +1109,26 @@ bool CameraRealSense2::init(const std::string & calibrationFolder, const std::st
|
||||
{
|
||||
auto video_profile = profilesPerSensor[i][j].as<rs2::video_stream_profile>();
|
||||
UINFO("Opening: %s %d %d %d %d %s type=%d", rs2_format_to_string(
|
||||
video_profile.format()),
|
||||
video_profile.width(),
|
||||
video_profile.height(),
|
||||
video_profile.fps(),
|
||||
video_profile.stream_index(),
|
||||
video_profile.stream_name().c_str(),
|
||||
video_profile.stream_type());
|
||||
profilesPerSensor[i][j].format()),
|
||||
video_profile.get()?video_profile.width():-1,
|
||||
video_profile.get()?video_profile.height():-1,
|
||||
profilesPerSensor[i][j].fps(),
|
||||
profilesPerSensor[i][j].stream_index(),
|
||||
profilesPerSensor[i][j].stream_name().c_str(),
|
||||
profilesPerSensor[i][j].stream_type());
|
||||
}
|
||||
if(globalTimeSync_ && sensors[i].supports(rs2_option::RS2_OPTION_GLOBAL_TIME_ENABLED))
|
||||
{
|
||||
float value = sensors[i].get_option(rs2_option::RS2_OPTION_GLOBAL_TIME_ENABLED);
|
||||
UINFO("Set RS2_OPTION_GLOBAL_TIME_ENABLED=1 (was %f) for sensor %d", value, (int)i);
|
||||
sensors[i].set_option(rs2_option::RS2_OPTION_GLOBAL_TIME_ENABLED, 1);
|
||||
float value = sensors[i].get_option(rs2_option::RS2_OPTION_GLOBAL_TIME_ENABLED);
|
||||
UINFO("Set RS2_OPTION_GLOBAL_TIME_ENABLED=1 (was %f) for sensor %d", value, (int)i);
|
||||
if(!sensors[i].is_option_read_only(rs2_option::RS2_OPTION_GLOBAL_TIME_ENABLED))
|
||||
{
|
||||
sensors[i].set_option(rs2_option::RS2_OPTION_GLOBAL_TIME_ENABLED, 1);
|
||||
}
|
||||
else if(value != 1)
|
||||
{
|
||||
UWARN("rs2_option::RS2_OPTION_GLOBAL_TIME_ENABLED option is read-only, cannot enable it.");
|
||||
}
|
||||
}
|
||||
sensors[i].open(profilesPerSensor[i]);
|
||||
if(sensors[i].is<rs2::depth_sensor>())
|
||||
@@ -1525,11 +1566,21 @@ SensorData CameraRealSense2::captureImage(SensorCaptureInfo * info)
|
||||
else
|
||||
{
|
||||
UERROR("Missing frames (received %d, needed=%d)", (int)frameset.size(), desiredFramesetSize);
|
||||
if(frameset.size()>0)
|
||||
{
|
||||
for (auto it = frameset.begin(); it != frameset.end(); ++it)
|
||||
{
|
||||
UERROR("Received frame only from %s", (*it).get_profile().stream_name().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(const std::exception& ex)
|
||||
{
|
||||
UERROR("An error has occurred during frame callback: %s", ex.what());
|
||||
if(!playback_)
|
||||
{
|
||||
UERROR("An error has occurred during frame callback: %s", ex.what());
|
||||
}
|
||||
}
|
||||
#else
|
||||
UERROR("CameraRealSense2: RTAB-Map is not built with RealSense2 support!");
|
||||
|
||||
@@ -92,7 +92,6 @@ void OccupancyGrid::clear()
|
||||
{
|
||||
map_ = cv::Mat();
|
||||
mapInfo_ = cv::Mat();
|
||||
cellCount_.clear();
|
||||
GlobalMap::clear();
|
||||
}
|
||||
|
||||
@@ -223,14 +222,14 @@ void OccupancyGrid::assemble(const std::list<std::pair<int, Transform> > & newPo
|
||||
|
||||
if(!cache().empty())
|
||||
{
|
||||
UDEBUG("Updating from cache");
|
||||
UDEBUG("Updating %ld poses from cache", newPoses.size());
|
||||
for(std::list<std::pair<int, Transform> >::const_iterator iter = newPoses.begin(); iter!=newPoses.end(); ++iter)
|
||||
{
|
||||
if(uContains(cache(), iter->first))
|
||||
{
|
||||
const LocalGrid & localGrid = cache().at(iter->first);
|
||||
|
||||
UDEBUG("Adding grid %d: ground=%d obstacles=%d empty=%d", iter->first, localGrid.groundCells.cols, localGrid.obstacleCells.cols, localGrid.emptyCells.cols);
|
||||
//UDEBUG("Adding grid %d: ground=%d obstacles=%d empty=%d", iter->first, localGrid.groundCells.cols, localGrid.obstacleCells.cols, localGrid.emptyCells.cols);
|
||||
|
||||
//ground
|
||||
cv::Mat ground;
|
||||
@@ -433,11 +432,6 @@ void OccupancyGrid::assemble(const std::list<std::pair<int, Transform> > & newPo
|
||||
if(iter != emptyLocalMaps.end() || jter!=occupiedLocalMaps.end())
|
||||
{
|
||||
addAssembledNode(kter->first, kter->second);
|
||||
std::map<int, std::pair<int, int> >::iterator cter = cellCount_.find(kter->first);
|
||||
if(cter == cellCount_.end() && kter->first > 0)
|
||||
{
|
||||
cter = cellCount_.insert(std::make_pair(kter->first, std::pair<int,int>(0,0))).first;
|
||||
}
|
||||
if(iter!=emptyLocalMaps.end())
|
||||
{
|
||||
for(int i=0; i<iter->second.cols; ++i)
|
||||
@@ -459,30 +453,12 @@ void OccupancyGrid::assemble(const std::list<std::pair<int, Transform> > & newPo
|
||||
// cannot rewrite on cells referred by more recent nodes
|
||||
continue;
|
||||
}
|
||||
if(nodeId > 0)
|
||||
{
|
||||
std::map<int, std::pair<int, int> >::iterator eter = cellCount_.find(nodeId);
|
||||
UASSERT_MSG(eter != cellCount_.end(), uFormat("current pose=%d nodeId=%d", kter->first, nodeId).c_str());
|
||||
if(value == 0)
|
||||
{
|
||||
eter->second.first -= 1;
|
||||
}
|
||||
else if(value == 100)
|
||||
{
|
||||
eter->second.second -= 1;
|
||||
}
|
||||
if(kter->first < 0)
|
||||
{
|
||||
eter->second.first += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(kter->first > 0)
|
||||
{
|
||||
info[0] = (float)kter->first;
|
||||
info[1] = ptf[0];
|
||||
info[2] = ptf[1];
|
||||
cter->second.first+=1;
|
||||
}
|
||||
value = 0; // free space
|
||||
|
||||
@@ -533,23 +509,6 @@ void OccupancyGrid::assemble(const std::list<std::pair<int, Transform> > & newPo
|
||||
// cannot rewrite on cells referred by more recent nodes
|
||||
continue;
|
||||
}
|
||||
if(nodeId>0)
|
||||
{
|
||||
std::map<int, std::pair<int, int> >::iterator eter = cellCount_.find(nodeId);
|
||||
UASSERT_MSG(eter != cellCount_.end(), uFormat("current pose=%d nodeId=%d", kter->first, nodeId).c_str());
|
||||
if(value == 0)
|
||||
{
|
||||
eter->second.first -= 1;
|
||||
}
|
||||
else if(value == 100)
|
||||
{
|
||||
eter->second.second -= 1;
|
||||
}
|
||||
if(kter->first < 0)
|
||||
{
|
||||
eter->second.first += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(kter->first > 0)
|
||||
{
|
||||
@@ -557,7 +516,6 @@ void OccupancyGrid::assemble(const std::list<std::pair<int, Transform> > & newPo
|
||||
info[1] = float(i) * cellSize_ + xMin;
|
||||
info[2] = float(j) * cellSize_ + yMin;
|
||||
info[3] = logOddsClampingMin_;
|
||||
cter->second.first+=1;
|
||||
}
|
||||
value = -2; // free space (footprint)
|
||||
}
|
||||
@@ -585,30 +543,12 @@ void OccupancyGrid::assemble(const std::list<std::pair<int, Transform> > & newPo
|
||||
// cannot rewrite on cells referred by more recent nodes
|
||||
continue;
|
||||
}
|
||||
if(nodeId>0)
|
||||
{
|
||||
std::map<int, std::pair<int, int> >::iterator eter = cellCount_.find(nodeId);
|
||||
UASSERT_MSG(eter != cellCount_.end(), uFormat("current pose=%d nodeId=%d", kter->first, nodeId).c_str());
|
||||
if(value == 0)
|
||||
{
|
||||
eter->second.first -= 1;
|
||||
}
|
||||
else if(value == 100)
|
||||
{
|
||||
eter->second.second -= 1;
|
||||
}
|
||||
if(kter->first < 0)
|
||||
{
|
||||
eter->second.second += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(kter->first > 0)
|
||||
{
|
||||
info[0] = (float)kter->first;
|
||||
info[1] = ptf[0];
|
||||
info[2] = ptf[1];
|
||||
cter->second.second+=1;
|
||||
}
|
||||
|
||||
// update odds
|
||||
@@ -651,20 +591,6 @@ void OccupancyGrid::assemble(const std::list<std::pair<int, Transform> > & newPo
|
||||
mapInfo_ = mapInfo;
|
||||
minValues_[0] = xMin;
|
||||
minValues_[1] = yMin;
|
||||
|
||||
// clean cellCount_
|
||||
for(std::map<int, std::pair<int, int> >::iterator iter= cellCount_.begin(); iter!=cellCount_.end();)
|
||||
{
|
||||
UASSERT(iter->second.first >= 0 && iter->second.second >= 0);
|
||||
if(iter->second.first == 0 && iter->second.second == 0)
|
||||
{
|
||||
cellCount_.erase(iter++);
|
||||
}
|
||||
else
|
||||
{
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -677,7 +603,6 @@ unsigned long OccupancyGrid::getMemoryUsed() const
|
||||
|
||||
memoryUsage += map_.total() * map_.elemSize();
|
||||
memoryUsage += mapInfo_.total() * mapInfo_.elemSize();
|
||||
memoryUsage += cellCount_.size()*(sizeof(int)*3 + sizeof(std::pair<int, int>) + sizeof(std::map<int, std::pair<int, int> >::iterator)) + sizeof(std::map<int, std::pair<int, int> >);
|
||||
|
||||
return memoryUsage;
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include "rtabmap/core/OdometryInfo.h"
|
||||
#include "rtabmap/utilite/ULogger.h"
|
||||
#include "rtabmap/utilite/UTimer.h"
|
||||
#include <cmath>
|
||||
|
||||
#ifdef RTABMAP_CUVSLAM
|
||||
#include "rtabmap/core/CameraModel.h"
|
||||
@@ -95,6 +96,7 @@ bool initializeCuVSLAM(const SensorData & data,
|
||||
CUVSLAM_TrackerHandle & cuvslam_handle,
|
||||
CUVSLAM_GroundConstraintHandle & ground_constraint_handle,
|
||||
bool planar_constraints,
|
||||
int multicam_mode,
|
||||
std::vector<uint8_t *> & gpu_left_image_data,
|
||||
std::vector<uint8_t *> & gpu_right_image_data,
|
||||
std::vector<size_t> & gpu_left_image_sizes,
|
||||
@@ -103,7 +105,7 @@ bool initializeCuVSLAM(const SensorData & data,
|
||||
std::vector<std::array<float, 12>> & intrinsics,
|
||||
cudaStream_t & cuda_stream);
|
||||
|
||||
CUVSLAM_Configuration CreateConfiguration(const SensorData & data);
|
||||
CUVSLAM_Configuration CreateConfiguration(const SensorData & data, int multicam_mode);
|
||||
|
||||
bool prepareImages(const SensorData & data,
|
||||
std::vector<CUVSLAM_Image> & cuvslam_images,
|
||||
@@ -113,10 +115,7 @@ bool prepareImages(const SensorData & data,
|
||||
std::vector<size_t> & gpu_right_image_sizes,
|
||||
cudaStream_t & cuda_stream);
|
||||
|
||||
cv::Mat convertCuVSLAMCovariance(const float * cuvslam_covariance);
|
||||
|
||||
void printCovarianceMatrix(const cv::Mat & cov, const std::string & label);
|
||||
void printRawCuvslamCovariance(const float * cuvslam_covariance, const std::string & label);
|
||||
cv::Mat convertCuVSLAMCovariance(const float * cuvslam_covariance, bool use_raw_covariance);
|
||||
|
||||
|
||||
// ============================================================================
|
||||
@@ -161,24 +160,6 @@ Transform FromcuVSLAMPose(const CUVSLAM_Pose & cuvslam_pose)
|
||||
return rtabmap_transform;
|
||||
}
|
||||
|
||||
void PrintConfiguration(const CUVSLAM_Configuration & cfg)
|
||||
{
|
||||
UINFO("Use use_gpu: %s", cfg.use_gpu ? "true" : "false");
|
||||
UINFO("Enable IMU Fusion: %s", cfg.enable_imu_fusion ? "true" : "false");
|
||||
if (cfg.enable_imu_fusion) {
|
||||
UINFO("gyroscope_noise_density: %f",
|
||||
cfg.imu_calibration.gyroscope_noise_density);
|
||||
UINFO("gyroscope_random_walk: %f",
|
||||
cfg.imu_calibration.gyroscope_random_walk);
|
||||
UINFO("accelerometer_noise_density: %f",
|
||||
cfg.imu_calibration.accelerometer_noise_density);
|
||||
UINFO("accelerometer_random_walk: %f",
|
||||
cfg.imu_calibration.accelerometer_random_walk);
|
||||
UINFO("frequency: %f",
|
||||
cfg.imu_calibration.frequency);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace rtabmap
|
||||
|
||||
#endif
|
||||
@@ -199,6 +180,7 @@ OdometryCuVSLAM::OdometryCuVSLAM(const ParametersMap & parameters) :
|
||||
lost_(false),
|
||||
tracking_(false),
|
||||
planar_constraints_(false),
|
||||
multicam_mode_(0),
|
||||
previous_pose_(Transform::getIdentity()),
|
||||
last_timestamp_(-1.0),
|
||||
observations_(5000),
|
||||
@@ -212,6 +194,12 @@ OdometryCuVSLAM::OdometryCuVSLAM(const ParametersMap & parameters) :
|
||||
{
|
||||
#ifdef RTABMAP_CUVSLAM
|
||||
Parameters::parse(parameters, Parameters::kRegForce3DoF(), planar_constraints_);
|
||||
Parameters::parse(parameters, Parameters::kOdomCuVSLAMMulticamMode(), multicam_mode_);
|
||||
UASSERT(multicam_mode_ >= 0 && multicam_mode_ <= 2);
|
||||
UINFO("%s=%d", Parameters::kOdomCuVSLAMMulticamMode().c_str(), multicam_mode_);
|
||||
// Warm up GPU and create CUDA context before tracker initialization
|
||||
// Supposedly this will speed up the tracker initialization
|
||||
CUVSLAM_WarmUpGPU();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -248,7 +236,14 @@ OdometryCuVSLAM::~OdometryCuVSLAM()
|
||||
void OdometryCuVSLAM::reset(const Transform & initialPose)
|
||||
{
|
||||
Odometry::reset(initialPose);
|
||||
|
||||
|
||||
#ifdef RTABMAP_CUVSLAM
|
||||
this->cleanupCuVSLAMResources();
|
||||
#endif
|
||||
}
|
||||
|
||||
void OdometryCuVSLAM::cleanupCuVSLAMResources()
|
||||
{
|
||||
#ifdef RTABMAP_CUVSLAM
|
||||
// Clean up cuVSLAM handles
|
||||
if(cuvslam_handle_)
|
||||
@@ -300,9 +295,19 @@ Transform OdometryCuVSLAM::computeTransform(
|
||||
#ifdef RTABMAP_CUVSLAM
|
||||
UTimer timer;
|
||||
|
||||
UDEBUG("=== computeTransform ENTRY === lost_=%s, tracking_=%s, initialized_=%s",
|
||||
lost_ ? "true" : "false",
|
||||
tracking_ ? "true" : "false",
|
||||
initialized_ ? "true" : "false");
|
||||
|
||||
// If we are lost after tracking has begun, return null transform
|
||||
// We wait until a reset is triggered.
|
||||
if(lost_ && tracking_) {
|
||||
UDEBUG("EARLY EXIT: lost_ && tracking_ is true, returning null");
|
||||
if(info) {
|
||||
info->reg.covariance = cv::Mat::eye(6, 6, CV_64FC1) * 9999.0;
|
||||
info->timeEstimation = timer.ticks();
|
||||
}
|
||||
return Transform();
|
||||
}
|
||||
|
||||
@@ -330,6 +335,7 @@ Transform OdometryCuVSLAM::computeTransform(
|
||||
cuvslam_handle_,
|
||||
ground_constraint_handle_,
|
||||
planar_constraints_,
|
||||
multicam_mode_,
|
||||
gpu_left_image_data_,
|
||||
gpu_right_image_data_,
|
||||
gpu_left_image_sizes_,
|
||||
@@ -341,18 +347,8 @@ Transform OdometryCuVSLAM::computeTransform(
|
||||
UERROR("Failed to initialize cuVSLAM tracker");
|
||||
return Transform();
|
||||
}
|
||||
|
||||
initialized_ = true;
|
||||
if(info)
|
||||
{
|
||||
info->type = 0;
|
||||
info->reg.covariance = cv::Mat::eye(6, 6, CV_64FC1) * 9999.0;
|
||||
info->timeEstimation = timer.ticks();
|
||||
}
|
||||
last_timestamp_ = data.stamp();
|
||||
return Transform();
|
||||
}
|
||||
|
||||
|
||||
// Prepare images for cuVSLAM
|
||||
std::vector<CUVSLAM_Image> cuvslam_image_objects;
|
||||
if(!prepareImages(
|
||||
@@ -368,10 +364,9 @@ Transform OdometryCuVSLAM::computeTransform(
|
||||
return Transform();
|
||||
}
|
||||
|
||||
// Process IMU data if available
|
||||
// Not using the IMU yet
|
||||
if(!data.imu().empty())
|
||||
{
|
||||
// TODO: Implement IMU processing
|
||||
UWARN("IMU data available but processing not implemented yet");
|
||||
}
|
||||
|
||||
@@ -394,13 +389,14 @@ Transform OdometryCuVSLAM::computeTransform(
|
||||
predicted_pose = TocuVSLAMPose(absolute_guess);
|
||||
predicted_pose_ptr = &predicted_pose;
|
||||
}
|
||||
|
||||
|
||||
CUVSLAM_PoseEstimate vo_pose_estimate;
|
||||
const CUVSLAM_Status vo_status = CUVSLAM_TrackGpuMem(
|
||||
cuvslam_handle_,
|
||||
cuvslam_image_objects.data(),
|
||||
cuvslam_image_objects.size(),
|
||||
predicted_pose_ptr, // can safely handle nullptr if no guess is provided
|
||||
nullptr, // depth_image (not used in this mode)
|
||||
predicted_pose_ptr, // can safely handle nullptr if no guess is provided
|
||||
&vo_pose_estimate
|
||||
);
|
||||
|
||||
@@ -409,23 +405,37 @@ Transform OdometryCuVSLAM::computeTransform(
|
||||
// Provide specific error message
|
||||
const char * error_msg = "Unknown error";
|
||||
switch(vo_status) {
|
||||
case 1: error_msg = "CUVSLAM_TRACKING_LOST"; break;
|
||||
case 2: error_msg = "CUVSLAM_INVALID_PARAMETER"; break;
|
||||
case 3: error_msg = "CUVSLAM_INVALID_IMAGE_FORMAT or CUVSLAM_INVALID_CAMERA_CONFIG"; break;
|
||||
case 4: error_msg = "CUVSLAM_GPU_MEMORY_ERROR"; break;
|
||||
case 5: error_msg = "CUVSLAM_INITIALIZATION_ERROR"; break;
|
||||
default: error_msg = "Unknown cuVSLAM error"; break;
|
||||
case CUVSLAM_TRACKING_LOST: error_msg = "CUVSLAM_TRACKING_LOST"; break;
|
||||
case CUVSLAM_INVALID_ARG: error_msg = "CUVSLAM_INVALID_PARAMETER"; break;
|
||||
case CUVSLAM_CAN_NOT_LOCALIZE: error_msg = "CUVSLAM_CAN_NOT_LOCALIZE"; break;
|
||||
case CUVSLAM_GENERIC_ERROR: error_msg = "CUVSLAM_GENERIC_ERROR"; break;
|
||||
case CUVSLAM_UNSUPPORTED_NUMBER_OF_CAMERAS: error_msg = "CUVSLAM_UNSUPPORTED_NUMBER_OF_CAMERAS"; break;
|
||||
case CUVSLAM_SLAM_IS_NOT_INITIALIZED: error_msg = "CUVSLAM_SLAM_IS_NOT_INITIALIZED"; break;
|
||||
default: error_msg = "Unknown cuVSLAM error"; break;
|
||||
}
|
||||
|
||||
UERROR("cuVSLAM tracking error: %d (%s)", vo_status, error_msg);
|
||||
|
||||
|
||||
// Update timing information even on failure
|
||||
last_timestamp_ = data.stamp();
|
||||
|
||||
if(info)
|
||||
{
|
||||
// Report very high uncertainty to upstream consumers
|
||||
info->reg.covariance = cv::Mat::eye(6, 6, CV_64FC1) * 9999.0;
|
||||
info->timeEstimation = timer.ticks();
|
||||
}
|
||||
|
||||
last_timestamp_ = data.stamp();
|
||||
|
||||
// The cuVSLAM tracking status never reports lost in my testing.
|
||||
// Thus we use covariance to detect lost state.
|
||||
if(vo_status == CUVSLAM_TRACKING_LOST)
|
||||
{
|
||||
UWARN("LOST: cuVSLAM reported CUVSLAM_TRACKING_LOST");
|
||||
lost_ = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
UWARN("cuVSLAM tracking error: %d (%s)", vo_status, error_msg);
|
||||
}
|
||||
|
||||
return Transform();
|
||||
}
|
||||
|
||||
@@ -434,51 +444,41 @@ Transform OdometryCuVSLAM::computeTransform(
|
||||
for(int i = 0; i < 6; i++)
|
||||
{
|
||||
float & diag_val = vo_pose_estimate.covariance[i*6+i];
|
||||
// We allow 1.0 as a valid value, since cuVSLAM sends identity covariance for the first few frames.
|
||||
if(!std::isfinite(diag_val) || diag_val <= 0.0 || (diag_val > 0.1 && diag_val != 1.0))
|
||||
|
||||
// conditions for immediate failure and tracking loss
|
||||
if(!std::isfinite(diag_val) || diag_val < 0.0)
|
||||
{
|
||||
diag_val = 9999.0; // Set to high uncertainty
|
||||
diag_val = 9999.0;
|
||||
valid_covariance = false;
|
||||
}
|
||||
// Tracker returns identity covariance and 0.0 values after initialization before motion.
|
||||
if(std::abs(diag_val) < 1e-7f)
|
||||
{
|
||||
diag_val = 0.0001;
|
||||
}
|
||||
if(diag_val > 0.1) {
|
||||
valid_covariance = false;
|
||||
|
||||
// If we don't have a guess, we can't use velocity difference to detect lost state.
|
||||
// Thus at this point, we are lost. Warn the user that cuVSLAM probably needs a guess to work well.
|
||||
if(guess.isNull()) {
|
||||
UWARN("No guess provided, but covariance is invalid: %.8f", diag_val);
|
||||
UWARN("We cannot use velocity difference to detect lost state without a guess!");
|
||||
UWARN("Without a guess cuVSLAM is prone to getting lost easily!");
|
||||
UWARN("It is highly recommended to provide a guess to cuVSLAM!");
|
||||
lost_ = true;
|
||||
if(info) {
|
||||
info->reg.covariance = cv::Mat::eye(6, 6, CV_64FC1) * 9999.0;
|
||||
info->timeEstimation = timer.ticks();
|
||||
}
|
||||
return Transform();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Convert to RTABMAP covariance format and scale to meet RTABMAP expectations
|
||||
cv::Mat covMat = convertCuVSLAMCovariance(vo_pose_estimate.covariance);
|
||||
|
||||
// Handle invalid covariance. Protect against low velocity cases.
|
||||
if(!valid_covariance) {
|
||||
double velocity_ms = 9999.0;
|
||||
double angular_velocity_rad_s = 9999.0;
|
||||
if(!guess.isNull()) {
|
||||
double time_s = data.stamp() - last_timestamp_;
|
||||
velocity_ms = guess.getNorm() / time_s;
|
||||
angular_velocity_rad_s = guess.getAngle(Transform::getIdentity()) / time_s;
|
||||
}
|
||||
if(velocity_ms < 0.1 && angular_velocity_rad_s < 0.1 && last_timestamp_ != -1.0) {
|
||||
covMat = cv::Mat::eye(6, 6, CV_64FC1) * 0.0001;
|
||||
} else {
|
||||
// If we have already begun tracking, now we are lost.
|
||||
if(tracking_) {
|
||||
UWARN("LOST: Velocity is high and covariance is invalid, setting lost to true");
|
||||
lost_ = true;
|
||||
}
|
||||
// Still send covariance for debugging
|
||||
if(info) {
|
||||
info->reg.covariance = covMat;
|
||||
}
|
||||
return Transform();
|
||||
}
|
||||
}
|
||||
cv::Mat covMat = convertCuVSLAMCovariance(vo_pose_estimate.covariance, use_raw_covariance_);
|
||||
|
||||
// Tracking was successful and the covariance is valid, set tracking to true
|
||||
tracking_ = true;
|
||||
|
||||
if(info)
|
||||
{
|
||||
info->reg.covariance = covMat;
|
||||
info->timeEstimation = timer.ticks();
|
||||
}
|
||||
|
||||
// Apply ground constraint
|
||||
if(planar_constraints_) {
|
||||
if(CUVSLAM_GroundConstraintAddNextPose(ground_constraint_handle_, &vo_pose_estimate.pose) != CUVSLAM_SUCCESS) {
|
||||
@@ -491,14 +491,62 @@ Transform OdometryCuVSLAM::computeTransform(
|
||||
}
|
||||
}
|
||||
|
||||
// Convert cuVSLAM pose to RTAB-Map Transform
|
||||
// Convert cuVSLAM absolute pose to incremental RTAB-Map Transform
|
||||
Transform current_pose = FromcuVSLAMPose(vo_pose_estimate.pose);
|
||||
current_pose = canonical_pose_cuvslam * current_pose * cuvslam_pose_canonical;
|
||||
|
||||
// Calculate incremental transform
|
||||
UASSERT(!previous_pose_.isNull());
|
||||
Transform transform = previous_pose_.inverse() * current_pose;
|
||||
|
||||
// Compute guess and estimated velocity and report lost if velocity ratio is high and covariance is invalid
|
||||
double time_delta_s = 0.0;
|
||||
double guess_velocity_ms = 0.0;
|
||||
double estimated_velocity_ms = 0.0;
|
||||
|
||||
if(!guess.isNull() && last_timestamp_ > 0.0 && !use_raw_covariance_ && !valid_covariance) {
|
||||
time_delta_s = data.stamp() - last_timestamp_;
|
||||
|
||||
guess_velocity_ms = guess.getNorm() / time_delta_s;
|
||||
estimated_velocity_ms = transform.getNorm() / time_delta_s;
|
||||
double velocity_ratio = estimated_velocity_ms / guess_velocity_ms;
|
||||
double velocity_difference = std::abs(estimated_velocity_ms - guess_velocity_ms);
|
||||
|
||||
// Check if the expected and predicted velocities are divergent.
|
||||
// Also ensure estimated velocity is not zero.
|
||||
// In rapid deceleration cases, estimated velocity zeros out faster then the guess but we aren't lost yet. So we need to check for this.
|
||||
bool zero_estimated_velocity = estimated_velocity_ms < zero_estimated_velocity_threshold_;
|
||||
bool invalid_velocity_ratio = velocity_ratio > velocity_ratio_threshold_high_ || velocity_ratio < velocity_ratio_threshold_low_;
|
||||
bool invalid_velocity_difference = velocity_difference > velocity_difference_threshold_;
|
||||
|
||||
if(invalid_velocity_ratio && invalid_velocity_difference && !zero_estimated_velocity) {
|
||||
UWARN("Velocity ratio is high and covariance is invalid: %.4f, returning null transform", velocity_ratio);
|
||||
lost_ = true;
|
||||
if(info) {
|
||||
info->reg.covariance = cv::Mat::eye(6, 6, CV_64FC1) * 9999.0;
|
||||
info->timeEstimation = timer.ticks();
|
||||
}
|
||||
return Transform();
|
||||
} else {
|
||||
covMat = cv::Mat::eye(6, 6, CV_64FC1) * 0.0001;
|
||||
}
|
||||
}
|
||||
|
||||
// At this point we have passed the covariance lost checks, so we are tracking.
|
||||
tracking_ = true;
|
||||
|
||||
if(info)
|
||||
{
|
||||
info->reg.covariance = covMat;
|
||||
info->timeEstimation = timer.ticks();
|
||||
}
|
||||
|
||||
// extract 3D VO landmarks for visualization
|
||||
// This will be used to determine if we have enough features to start tracking.
|
||||
CUVSLAM_LandmarkVector landmark_vector;
|
||||
landmark_vector.max = landmarks_.size();
|
||||
landmark_vector.landmarks = landmarks_.data();
|
||||
CUVSLAM_Status landmark_status = CUVSLAM_GetLastLandmarks(cuvslam_handle_, &landmark_vector);
|
||||
int landmarks_num = landmark_vector.num;
|
||||
|
||||
// Fill info with visualization data
|
||||
if(info) {
|
||||
if(data.stereoCameraModels().size()==1) {
|
||||
@@ -522,11 +570,6 @@ Transform OdometryCuVSLAM::computeTransform(
|
||||
info->type = kTypeF2M;
|
||||
}
|
||||
|
||||
// extract 3D VO landmarks for visualization
|
||||
CUVSLAM_LandmarkVector landmark_vector;
|
||||
landmark_vector.max = landmarks_.size();
|
||||
landmark_vector.landmarks = landmarks_.data();
|
||||
CUVSLAM_Status landmark_status = CUVSLAM_GetLastLandmarks(cuvslam_handle_, &landmark_vector);
|
||||
std::vector<Transform> local_transform_inv(data.stereoCameraModels().size());
|
||||
for(size_t i=0; i<data.stereoCameraModels().size(); ++i) {
|
||||
local_transform_inv[i] = data.stereoCameraModels()[i].localTransform().inverse();
|
||||
@@ -552,11 +595,36 @@ Transform OdometryCuVSLAM::computeTransform(
|
||||
info->reg.inliersIDs.push_back(landmark.id);
|
||||
break;
|
||||
}
|
||||
|
||||
// Update landmarks number based on which landmarks were successfully reprojected in the current frame
|
||||
landmarks_num = info->words.size();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we are in a multi-camera setup and successfully reprojected landmarks into camera frames,
|
||||
// use the number of successfully reprojected landmarks instead of the raw cuVSLAM landmark count.
|
||||
if(data.stereoCameraModels().size() > 1) {
|
||||
landmarks_num = (int)info->words.size();
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we have enough features to start tracking. Otherwise we are lost.
|
||||
if(landmarks_num < min_landmarks_threshold_ && !initialized_) {
|
||||
if(info) {
|
||||
info->reg.covariance = cv::Mat::eye(6, 6, CV_64FC1) * 9999.0;
|
||||
info->timeEstimation = timer.ticks();
|
||||
}
|
||||
// Free GPU resources and reset state. Prevent memory leaks on init loops.
|
||||
cleanupCuVSLAMResources();
|
||||
lost_ = true;
|
||||
tracking_ = false;
|
||||
initialized_ = false;
|
||||
return Transform();
|
||||
} else {
|
||||
initialized_ = true;
|
||||
}
|
||||
|
||||
previous_pose_ = current_pose;
|
||||
@@ -566,7 +634,7 @@ Transform OdometryCuVSLAM::computeTransform(
|
||||
UERROR("cuVSLAM support not compiled in RTAB-Map");\
|
||||
return Transform();
|
||||
#endif
|
||||
|
||||
|
||||
}
|
||||
|
||||
#ifdef RTABMAP_CUVSLAM
|
||||
@@ -579,6 +647,7 @@ bool initializeCuVSLAM(const SensorData & data,
|
||||
CUVSLAM_TrackerHandle & cuvslam_handle,
|
||||
CUVSLAM_GroundConstraintHandle & ground_constraint_handle,
|
||||
bool planar_constraints,
|
||||
int multicam_mode,
|
||||
std::vector<uint8_t *> & gpu_left_image_data,
|
||||
std::vector<uint8_t *> & gpu_right_image_data,
|
||||
std::vector<size_t> & gpu_left_image_sizes,
|
||||
@@ -627,9 +696,9 @@ bool initializeCuVSLAM(const SensorData & data,
|
||||
rtabmap::Transform extrinsics = cuvslam_pose_canonical * stereoModel.localTransform() * optical_pose_cuvslam;
|
||||
cam_left.pose = TocuVSLAMPose(extrinsics);
|
||||
cam_left.border_top = 0;
|
||||
cam_left.border_bottom = leftModel.imageHeight();
|
||||
cam_left.border_bottom = 0;
|
||||
cam_left.border_left = 0;
|
||||
cam_left.border_right = leftModel.imageWidth();
|
||||
cam_left.border_right = 0;
|
||||
|
||||
// Right camera
|
||||
cam_right.parameters = intrinsics_right.data();
|
||||
@@ -649,9 +718,9 @@ bool initializeCuVSLAM(const SensorData & data,
|
||||
extrinsics = cuvslam_pose_canonical * stereoModel.localTransform() * baseline_transform * optical_pose_cuvslam;
|
||||
cam_right.pose = TocuVSLAMPose(extrinsics);
|
||||
cam_right.border_top = 0;
|
||||
cam_right.border_bottom = rightModel.imageHeight();
|
||||
cam_right.border_bottom = 0;
|
||||
cam_right.border_left = 0;
|
||||
cam_right.border_right = rightModel.imageWidth();
|
||||
cam_right.border_right = 0;
|
||||
}
|
||||
|
||||
// Set up camera rig
|
||||
@@ -659,12 +728,12 @@ bool initializeCuVSLAM(const SensorData & data,
|
||||
camera_rig.cameras = cuvslam_cameras.data();
|
||||
camera_rig.num_cameras = cuvslam_cameras.size();
|
||||
|
||||
const CUVSLAM_Configuration configuration = CreateConfiguration(data);
|
||||
PrintConfiguration(configuration);
|
||||
const CUVSLAM_Configuration configuration = CreateConfiguration(data, multicam_mode);
|
||||
|
||||
// Create tracker
|
||||
CUVSLAM_TrackerHandle tracker_handle;
|
||||
UTimer create_timer; create_timer.start();
|
||||
|
||||
const CUVSLAM_Status status_tracker = CUVSLAM_CreateTracker(&tracker_handle, &camera_rig, &configuration);
|
||||
|
||||
if (status_tracker != CUVSLAM_SUCCESS) {
|
||||
@@ -706,12 +775,11 @@ Implementation based on Isaac ROS VisualSlamNode::VisualSlamImpl::CreateConfigur
|
||||
Source: isaac_ros_visual_slam/isaac_ros_visual_slam/src/impl/visual_slam_impl.cpp:379-422
|
||||
https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_visual_slam/blob/19be8c781a55dee9cfbe9f097adca3986638feb1/isaac_ros_visual_slam/src/impl/visual_slam_impl.cpp#L379-L422
|
||||
*/
|
||||
CUVSLAM_Configuration CreateConfiguration(const SensorData & data)
|
||||
CUVSLAM_Configuration CreateConfiguration(const SensorData & data, int multicam_mode)
|
||||
{
|
||||
CUVSLAM_Configuration configuration;
|
||||
CUVSLAM_InitDefaultConfiguration(&configuration);
|
||||
|
||||
configuration.multicam_mode = data.stereoCameraModels().size()>1?1:0;
|
||||
|
||||
// Core Visual Odometry Settings
|
||||
configuration.use_motion_model = 1; // Enable motion model for better tracking
|
||||
@@ -726,19 +794,12 @@ CUVSLAM_Configuration CreateConfiguration(const SensorData & data)
|
||||
configuration.enable_landmarks_export = 0; // SLAM feature (optional)
|
||||
configuration.enable_reading_slam_internals = 0; // SLAM feature (optional)
|
||||
|
||||
// IMU Configuration (If we later implement IMU support)
|
||||
configuration.enable_imu_fusion = 0; //data.imu().empty()?0:1;
|
||||
configuration.debug_imu_mode = 0; // Disable IMU debug mode
|
||||
// imu_calibration.gyroscope_noise_density = 0.0002f;
|
||||
// imu_calibration.gyroscope_random_walk = 0.00003f;
|
||||
// imu_calibration.accelerometer_noise_density = 0.01f;
|
||||
// imu_calibration.accelerometer_random_walk = 0.001f;
|
||||
// imu_calibration.frequency = 200.0f;
|
||||
// configuration.imu_calibration = imu_calibration;
|
||||
|
||||
// configuration.max_frame_delta_ms = 100.0; // Maximum frame interval (100ms default)
|
||||
|
||||
// SLAM-specific parameters (disabled)
|
||||
// Odometry configuration (Vision-only, no IMU)
|
||||
configuration.odometry_mode = CUVSLAM_OdometryMode::Multicamera;
|
||||
configuration.multicam_mode = multicam_mode; // moderate (0), performance (1) or precision (2).
|
||||
configuration.debug_imu_mode = 0;
|
||||
|
||||
// SLAM parameters (disabled)
|
||||
configuration.planar_constraints = 0;
|
||||
configuration.slam_throttling_time_ms = 0;
|
||||
configuration.slam_max_map_size = 0;
|
||||
@@ -907,6 +968,11 @@ bool prepareImages(const SensorData & data,
|
||||
left_cuvslam_image.camera_index = camera_index;
|
||||
left_cuvslam_image.pitch = left_image_slice.step;
|
||||
left_cuvslam_image.image_encoding = left_encoding;
|
||||
// Mask fields (not used in this implementation)
|
||||
left_cuvslam_image.input_mask = nullptr;
|
||||
left_cuvslam_image.mask_width = 0;
|
||||
left_cuvslam_image.mask_height = 0;
|
||||
left_cuvslam_image.mask_pitch = 0;
|
||||
|
||||
cuvslam_images.push_back(left_cuvslam_image);
|
||||
|
||||
@@ -931,6 +997,11 @@ bool prepareImages(const SensorData & data,
|
||||
right_cuvslam_image.camera_index = camera_index;
|
||||
right_cuvslam_image.pitch = right_image_slice.step;
|
||||
right_cuvslam_image.image_encoding = right_encoding;
|
||||
// Mask fields (not used in this implementation)
|
||||
right_cuvslam_image.input_mask = nullptr;
|
||||
right_cuvslam_image.mask_width = 0;
|
||||
right_cuvslam_image.mask_height = 0;
|
||||
right_cuvslam_image.mask_pitch = 0;
|
||||
|
||||
cuvslam_images.push_back(right_cuvslam_image);
|
||||
|
||||
@@ -953,10 +1024,11 @@ Convert cuVSLAM covariance to RTAB-Map format.
|
||||
Based on Isaac ROS implementation: FromcuVSLAMCovariance()
|
||||
Source: isaac_ros_visual_slam/src/impl/cuvslam_ros_conversion.cpp:275-299
|
||||
*/
|
||||
cv::Mat convertCuVSLAMCovariance(const float * cuvslam_covariance)
|
||||
cv::Mat convertCuVSLAMCovariance(const float * cuvslam_covariance, bool use_raw_covariance)
|
||||
{
|
||||
|
||||
// Scale cuvslam covariance to make it more realistic
|
||||
const double scaling_factor = 10.0;
|
||||
const double scaling_factor = use_raw_covariance ? 1.0 : 10.0;
|
||||
|
||||
// Handle null covariance pointer
|
||||
if(cuvslam_covariance == nullptr)
|
||||
@@ -982,32 +1054,36 @@ cv::Mat convertCuVSLAMCovariance(const float * cuvslam_covariance)
|
||||
block_canonical_pose_cuvslam.block<3, 3>(0, 0) = canonical_pose_cuvslam_mat;
|
||||
block_canonical_pose_cuvslam.block<3, 3>(3, 3) = canonical_pose_cuvslam_mat;
|
||||
|
||||
// Map cuVSLAM covariance array to Eigen matrix
|
||||
Eigen::Matrix<float, 6, 6> covariance_mat =
|
||||
// Map cuVSLAM covariance array to Eigen matrix and convert to double for numerical stability
|
||||
Eigen::Matrix<float, 6, 6> covariance_mat_float =
|
||||
Eigen::Map<Eigen::Matrix<float, 6, 6, Eigen::StorageOptions::AutoAlign>>(const_cast<float*>(covariance));
|
||||
Eigen::Matrix<double, 6, 6> covariance_mat = covariance_mat_float.cast<double>();
|
||||
|
||||
// Reorder covariance matrix elements
|
||||
// Reorder covariance matrix elements (in double precision)
|
||||
// The covariance matrix from cuVSLAM arranges elements as follows:
|
||||
// (rotation about X axis, rotation about Y axis, rotation about Z axis, x, y, z)
|
||||
// However, in RTAB-Map, the order is:
|
||||
// (x, y, z, rotation about X axis, rotation about Y axis, rotation about Z axis)
|
||||
Eigen::Matrix<float, 6, 6> rtabmap_covariance_mat = Eigen::Matrix<float, 6, 6>::Zero();
|
||||
Eigen::Matrix<double, 6, 6> rtabmap_covariance_mat = Eigen::Matrix<double, 6, 6>::Zero();
|
||||
rtabmap_covariance_mat.block<3, 3>(0, 0) = covariance_mat.block<3, 3>(3, 3); // translation-translation
|
||||
rtabmap_covariance_mat.block<3, 3>(0, 3) = covariance_mat.block<3, 3>(3, 0); // translation-rotation
|
||||
rtabmap_covariance_mat.block<3, 3>(3, 0) = covariance_mat.block<3, 3>(0, 3); // rotation-translation
|
||||
rtabmap_covariance_mat.block<3, 3>(3, 3) = covariance_mat.block<3, 3>(0, 0); // rotation-rotation
|
||||
|
||||
// Apply coordinate system transformation
|
||||
Eigen::Matrix<float, 6, 6> covariance_mat_change_basis =
|
||||
block_canonical_pose_cuvslam * rtabmap_covariance_mat * block_canonical_pose_cuvslam.transpose();
|
||||
// Convert transformation matrix to double for numerical stability in matrix operations
|
||||
Eigen::Matrix<double, 6, 6> block_canonical_pose_cuvslam_double = block_canonical_pose_cuvslam.cast<double>();
|
||||
|
||||
// Convert Eigen matrix to OpenCV Mat
|
||||
// Apply coordinate system transformation (in double precision)
|
||||
Eigen::Matrix<double, 6, 6> covariance_mat_change_basis =
|
||||
block_canonical_pose_cuvslam_double * rtabmap_covariance_mat * block_canonical_pose_cuvslam_double.transpose();
|
||||
|
||||
// Convert Eigen matrix to OpenCV Mat (already in double precision)
|
||||
cv::Mat cv_covariance(6, 6, CV_64FC1);
|
||||
for(int i = 0; i < 6; i++)
|
||||
{
|
||||
for(int j = 0; j < 6; j++)
|
||||
{
|
||||
cv_covariance.at<double>(i, j) = static_cast<double>(covariance_mat_change_basis(i, j));
|
||||
cv_covariance.at<double>(i, j) = covariance_mat_change_basis(i, j);
|
||||
// for angular values, scale again to make it more realistic
|
||||
if(i > 2 || j > 2) {
|
||||
cv_covariance.at<double>(i, j) *= scaling_factor;
|
||||
|
||||
@@ -0,0 +1,478 @@
|
||||
/*
|
||||
Copyright (c) 2010-2016, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the Universite de Sherbrooke nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "rtabmap/core/odometry/OdometryLIOSAM.h"
|
||||
#include "rtabmap/core/OdometryInfo.h"
|
||||
#include "rtabmap/core/util3d.h"
|
||||
#include "rtabmap/utilite/ULogger.h"
|
||||
#include "rtabmap/utilite/UTimer.h"
|
||||
#include "rtabmap/utilite/UStl.h"
|
||||
#include "rtabmap/utilite/UDirectory.h"
|
||||
#include "rtabmap/utilite/UFile.h"
|
||||
|
||||
#ifdef RTABMAP_LIOSAM
|
||||
#include <LioSamCore.h>
|
||||
#include <pcl/common/transforms.h>
|
||||
#endif
|
||||
|
||||
namespace rtabmap {
|
||||
|
||||
static ParametersMap disableDeskewing(ParametersMap params) {
|
||||
// LIO-SAM performs its own internal deskewing via imageProjection.
|
||||
// The base-class deskew must be disabled so that the original per-point
|
||||
// timestamps reach LIO-SAM intact.
|
||||
params[Parameters::kOdomDeskewing()] = "false";
|
||||
return params;
|
||||
}
|
||||
|
||||
OdometryLIOSAM::OdometryLIOSAM(const ParametersMap & parameters) :
|
||||
Odometry(disableDeskewing(parameters))
|
||||
#ifdef RTABMAP_LIOSAM
|
||||
,lioSam_(0)
|
||||
,lastPose_(Transform::getIdentity())
|
||||
,lost_(false)
|
||||
,linVar_(Parameters::defaultOdomLIOSAMLinVar())
|
||||
,angVar_(Parameters::defaultOdomLIOSAMAngVar())
|
||||
,parameters_(parameters)
|
||||
#endif
|
||||
{
|
||||
#ifdef RTABMAP_LIOSAM
|
||||
Parameters::parse(parameters, Parameters::kOdomLIOSAMLinVar(), linVar_);
|
||||
UASSERT(linVar_ > 0.0f);
|
||||
Parameters::parse(parameters, Parameters::kOdomLIOSAMAngVar(), angVar_);
|
||||
UASSERT(angVar_ > 0.0f);
|
||||
#endif
|
||||
}
|
||||
|
||||
OdometryLIOSAM::~OdometryLIOSAM()
|
||||
{
|
||||
#ifdef RTABMAP_LIOSAM
|
||||
delete lioSam_;
|
||||
#endif
|
||||
}
|
||||
|
||||
void OdometryLIOSAM::reset(const Transform & initialPose)
|
||||
{
|
||||
Odometry::reset(initialPose);
|
||||
#ifdef RTABMAP_LIOSAM
|
||||
if(lioSam_)
|
||||
{
|
||||
lioSam_->reset();
|
||||
}
|
||||
lastPose_ = Transform::getIdentity();
|
||||
lost_ = false;
|
||||
imuLocalTransform_ = Transform();
|
||||
imuBuffer_.clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef RTABMAP_LIOSAM
|
||||
bool OdometryLIOSAM::init(const Transform & imuLocalTransform, const Transform & lidarLocalTransform)
|
||||
{
|
||||
ParamServer config;
|
||||
|
||||
// Check if a config file path was provided
|
||||
std::string configPath;
|
||||
Parameters::parse(parameters_, Parameters::kOdomLIOSAMConfigPath(), configPath);
|
||||
if(!configPath.empty())
|
||||
{
|
||||
configPath = uReplaceChar(configPath, '~', UDirectory::homeDir());
|
||||
if(!UFile::exists(configPath))
|
||||
{
|
||||
UERROR("LIO-SAM config file not found: %s", configPath.c_str());
|
||||
return false;
|
||||
}
|
||||
UINFO("Loading LIO-SAM parameters from config file: %s", configPath.c_str());
|
||||
config = loadParamsFromYaml(configPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
UINFO("No LIO-SAM config file provided, using rtabmap parameters");
|
||||
|
||||
// Build ParamServer from individual rtabmap parameters
|
||||
int sensorType = Parameters::defaultOdomLIOSAMSensor();
|
||||
Parameters::parse(parameters_, Parameters::kOdomLIOSAMSensor(), sensorType);
|
||||
if(sensorType == 1)
|
||||
config.sensor = SensorType::OUSTER;
|
||||
else if(sensorType == 2)
|
||||
config.sensor = SensorType::LIVOX;
|
||||
else
|
||||
config.sensor = SensorType::VELODYNE;
|
||||
|
||||
config.N_SCAN = Parameters::defaultOdomLIOSAMNScan();
|
||||
Parameters::parse(parameters_, Parameters::kOdomLIOSAMNScan(), config.N_SCAN);
|
||||
|
||||
config.Horizon_SCAN = Parameters::defaultOdomLIOSAMHorizonScan();
|
||||
Parameters::parse(parameters_, Parameters::kOdomLIOSAMHorizonScan(), config.Horizon_SCAN);
|
||||
|
||||
config.imuAccNoise = Parameters::defaultOdomLIOSAMImuAccNoise();
|
||||
Parameters::parse(parameters_, Parameters::kOdomLIOSAMImuAccNoise(), config.imuAccNoise);
|
||||
|
||||
config.imuGyrNoise = Parameters::defaultOdomLIOSAMImuGyrNoise();
|
||||
Parameters::parse(parameters_, Parameters::kOdomLIOSAMImuGyrNoise(), config.imuGyrNoise);
|
||||
|
||||
config.imuAccBiasN = Parameters::defaultOdomLIOSAMImuAccBiasN();
|
||||
Parameters::parse(parameters_, Parameters::kOdomLIOSAMImuAccBiasN(), config.imuAccBiasN);
|
||||
|
||||
config.imuGyrBiasN = Parameters::defaultOdomLIOSAMImuGyrBiasN();
|
||||
Parameters::parse(parameters_, Parameters::kOdomLIOSAMImuGyrBiasN(), config.imuGyrBiasN);
|
||||
|
||||
config.imuGravity = Parameters::defaultOdomLIOSAMImuGravity();
|
||||
Parameters::parse(parameters_, Parameters::kOdomLIOSAMImuGravity(), config.imuGravity);
|
||||
|
||||
config.edgeThreshold = Parameters::defaultOdomLIOSAMEdgeThreshold();
|
||||
Parameters::parse(parameters_, Parameters::kOdomLIOSAMEdgeThreshold(), config.edgeThreshold);
|
||||
|
||||
config.surfThreshold = Parameters::defaultOdomLIOSAMSurfThreshold();
|
||||
Parameters::parse(parameters_, Parameters::kOdomLIOSAMSurfThreshold(), config.surfThreshold);
|
||||
|
||||
// Set reasonable defaults for params not exposed via rtabmap
|
||||
config.downsampleRate = 1;
|
||||
config.lidarMinRange = 1.0f;
|
||||
config.lidarMaxRange = 1000.0f;
|
||||
config.imuRPYWeight = 0.01f;
|
||||
config.odometrySurfLeafSize = 0.2f;
|
||||
config.mappingCornerLeafSize = 0.2f;
|
||||
config.mappingSurfLeafSize = 0.4f;
|
||||
config.z_tollerance = FLT_MAX;
|
||||
config.rotation_tollerance = FLT_MAX;
|
||||
config.numberOfCores = 4;
|
||||
config.mappingProcessInterval = 0.01;
|
||||
config.surroundingkeyframeAddingDistThreshold = 1.0f;
|
||||
config.surroundingkeyframeAddingAngleThreshold = 0.2f;
|
||||
config.surroundingKeyframeDensity = 1.0f;
|
||||
config.surroundingKeyframeSearchRadius = 50.0f;
|
||||
config.loopClosureEnableFlag = false; // rtabmap handles loop closures
|
||||
config.loopClosureFrequency = 1.0f;
|
||||
config.surroundingKeyframeSize = 50;
|
||||
config.historyKeyframeSearchRadius = 10.0f;
|
||||
config.historyKeyframeSearchTimeDiff = 30.0f;
|
||||
config.historyKeyframeSearchNum = 25;
|
||||
config.historyKeyframeFitnessScore = 0.3f;
|
||||
config.globalMapVisualizationSearchRadius = 1e3f;
|
||||
config.globalMapVisualizationPoseDensity = 10.0f;
|
||||
config.globalMapVisualizationLeafSize = 1.0f;
|
||||
config.edgeFeatureMinValidNum = 10;
|
||||
config.surfFeatureMinValidNum = 100;
|
||||
config.savePCD = false;
|
||||
config.useImuHeadingInitialization = false;
|
||||
config.useGpsElevation = false;
|
||||
config.gpsCovThreshold = 2.0f;
|
||||
config.poseCovThreshold = 25.0f;
|
||||
}
|
||||
|
||||
// Always override extrinsics from sensor local transforms when available.
|
||||
// This ensures the IMU-to-lidar transform matches the actual sensor setup
|
||||
// regardless of what the config file says.
|
||||
// imuLocalTransform = T_base_imu (base_link -> imu_link)
|
||||
// lidarLocalTransform = T_base_lidar (base_link -> lidar_link)
|
||||
// LIO-SAM's imuConverter() expects T_lidar_imu:
|
||||
// T_lidar_imu = T_base_lidar^{-1} * T_base_imu
|
||||
if(!imuLocalTransform.isNull() && !lidarLocalTransform.isNull())
|
||||
{
|
||||
Transform T_lidar_imu = lidarLocalTransform.inverse() * imuLocalTransform;
|
||||
Eigen::Matrix4d T = T_lidar_imu.toEigen4d();
|
||||
Eigen::Matrix3d rot = T.block<3,3>(0,0);
|
||||
Eigen::Vector3d trans = T.block<3,1>(0,3);
|
||||
config.extRotV = {rot(0,0), rot(0,1), rot(0,2),
|
||||
rot(1,0), rot(1,1), rot(1,2),
|
||||
rot(2,0), rot(2,1), rot(2,2)};
|
||||
config.extRPYV = config.extRotV;
|
||||
config.extTransV = {trans(0), trans(1), trans(2)};
|
||||
UINFO("LIO-SAM extrinsics (T_lidar_imu) computed from sensor local transforms: %s", T_lidar_imu.prettyPrint().c_str());
|
||||
}
|
||||
else if(config.extRotV.size() != 9 || config.extTransV.size() != 3)
|
||||
{
|
||||
// No valid extrinsics from sensor data or config file
|
||||
UERROR("Cannot compute IMU-to-lidar extrinsics: IMU local transform %s, lidar local transform %s. "
|
||||
"Both must be valid, or the config file must contain valid extrinsics.",
|
||||
imuLocalTransform.isNull() ? "is null" : "is valid",
|
||||
lidarLocalTransform.isNull() ? "is null" : "is valid");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
UINFO("Using extrinsics from config file (sensor local transforms not available)");
|
||||
}
|
||||
|
||||
// Set the global extrinsics used by imuConverter
|
||||
extRot = Eigen::Map<const Eigen::Matrix<double, 3, 3, Eigen::RowMajor> >(config.extRotV.data());
|
||||
extRPY = Eigen::Map<const Eigen::Matrix<double, 3, 3, Eigen::RowMajor> >(config.extRPYV.data());
|
||||
extTrans = Eigen::Map<const Eigen::Matrix<double, 3, 1> >(config.extTransV.data());
|
||||
extQRPY = Eigen::Quaterniond(extRPY).inverse();
|
||||
|
||||
lioSam_ = new lio_sam::LioSamCore(config);
|
||||
|
||||
// Replay buffered IMU samples
|
||||
UINFO("Replaying %d buffered IMU samples into LIO-SAM", (int)imuBuffer_.size());
|
||||
for(const ImuSample & s : imuBuffer_)
|
||||
{
|
||||
lioSam_->addImu(s.stamp, s.acc, s.gyro, s.orientation);
|
||||
}
|
||||
imuBuffer_.clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
Transform OdometryLIOSAM::computeTransform(
|
||||
SensorData & data,
|
||||
const Transform & guess,
|
||||
OdometryInfo * info)
|
||||
{
|
||||
Transform t;
|
||||
#ifdef RTABMAP_LIOSAM
|
||||
UTimer timer;
|
||||
UTimer timerTotal;
|
||||
|
||||
// Handle async IMU data (canProcessAsyncIMU() == true means
|
||||
// the base class sends IMU-only data directly to computeTransform)
|
||||
if(!data.imu().empty())
|
||||
{
|
||||
Eigen::Quaterniond qd(
|
||||
data.imu().orientation()[3], // w
|
||||
data.imu().orientation()[0], // x
|
||||
data.imu().orientation()[1], // y
|
||||
data.imu().orientation()[2]); // z
|
||||
Eigen::Vector3d acc(
|
||||
data.imu().linearAcceleration()[0],
|
||||
data.imu().linearAcceleration()[1],
|
||||
data.imu().linearAcceleration()[2]);
|
||||
Eigen::Vector3d gyro(
|
||||
data.imu().angularVelocity()[0],
|
||||
data.imu().angularVelocity()[1],
|
||||
data.imu().angularVelocity()[2]);
|
||||
|
||||
// Deferred initialization: need both IMU and lidar local transforms
|
||||
// to compute T_lidar_imu extrinsics for LIO-SAM.
|
||||
if(!lioSam_)
|
||||
{
|
||||
// Cache IMU local transform when first available
|
||||
if(imuLocalTransform_.isNull() && !data.imu().localTransform().isNull())
|
||||
{
|
||||
imuLocalTransform_ = data.imu().localTransform();
|
||||
}
|
||||
|
||||
// Try to initialize if we have both transforms
|
||||
if(!imuLocalTransform_.isNull() && !data.laserScanRaw().isEmpty() &&
|
||||
!data.laserScanRaw().localTransform().isNull())
|
||||
{
|
||||
if(!init(imuLocalTransform_, data.laserScanRaw().localTransform()))
|
||||
{
|
||||
UERROR("Failed to initialize LIO-SAM");
|
||||
return t;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Buffer IMU until we can initialize
|
||||
ImuSample s;
|
||||
s.stamp = data.stamp();
|
||||
s.acc = acc;
|
||||
s.gyro = gyro;
|
||||
s.orientation = qd;
|
||||
imuBuffer_.push_back(s);
|
||||
|
||||
if(data.laserScanRaw().isEmpty())
|
||||
{
|
||||
return t;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(lioSam_)
|
||||
{
|
||||
lioSam_->addImu(data.stamp(), acc, gyro, qd);
|
||||
}
|
||||
|
||||
// IMU-only: no pose to return
|
||||
if(data.laserScanRaw().isEmpty())
|
||||
{
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
if(!lioSam_)
|
||||
{
|
||||
// A scan arrived without IMU in the same message.
|
||||
// Try to init if the IMU local transform was already cached.
|
||||
if(!imuLocalTransform_.isNull() && !data.laserScanRaw().isEmpty() &&
|
||||
!data.laserScanRaw().localTransform().isNull())
|
||||
{
|
||||
if(!init(imuLocalTransform_, data.laserScanRaw().localTransform()))
|
||||
{
|
||||
UERROR("Failed to initialize LIO-SAM");
|
||||
return t;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UDEBUG("LIO-SAM not yet initialized, waiting for IMU (have=%s) and lidar (need scan) local transforms...",
|
||||
imuLocalTransform_.isNull() ? "no" : "yes");
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
if(data.laserScanRaw().isEmpty())
|
||||
{
|
||||
UERROR("LIO-SAM requires laser scans and the current input is empty. Aborting odometry update...");
|
||||
return t;
|
||||
}
|
||||
else if(data.laserScanRaw().is2d())
|
||||
{
|
||||
UERROR("LIO-SAM requires 3D laser scans. Aborting odometry update...");
|
||||
return t;
|
||||
}
|
||||
|
||||
cv::Mat covariance = cv::Mat::eye(6, 6, CV_64FC1) * 9999;
|
||||
if(!lost_)
|
||||
{
|
||||
const LaserScan & scan = data.laserScanRaw();
|
||||
if(scan.format() != LaserScan::kXYZIRT)
|
||||
{
|
||||
UERROR("LIO-SAM requires a scan in format %s (got %s). "
|
||||
"Populate the scan via util3d::laserScanFromPointCloud<PointXYZIRT>() "
|
||||
"so that per-point ring and time fields are available.",
|
||||
LaserScan::formatName(LaserScan::kXYZIRT).c_str(),
|
||||
scan.formatName().c_str());
|
||||
return t;
|
||||
}
|
||||
|
||||
// Split the kXYZIRT scan into the three parallel buffers LIO-SAM expects.
|
||||
const int numPoints = scan.size();
|
||||
const int ringOffset = scan.getRingOffset();
|
||||
const int timeOffset = scan.getTimeOffset();
|
||||
pcl::PointCloud<pcl::PointXYZI>::Ptr laserCloudIn(new pcl::PointCloud<pcl::PointXYZI>);
|
||||
laserCloudIn->reserve(numPoints);
|
||||
std::vector<int> rings;
|
||||
std::vector<float> times;
|
||||
rings.reserve(numPoints);
|
||||
times.reserve(numPoints);
|
||||
for(int i=0; i<numPoints; ++i)
|
||||
{
|
||||
const int row = i / scan.data().cols;
|
||||
const int col = i - row * scan.data().cols;
|
||||
const float * ptr = scan.data().ptr<float>(row, col);
|
||||
pcl::PointXYZI pt;
|
||||
pt.x = ptr[0];
|
||||
pt.y = ptr[1];
|
||||
pt.z = ptr[2];
|
||||
pt.intensity = ptr[3];
|
||||
laserCloudIn->push_back(pt);
|
||||
rings.push_back(static_cast<int>(ptr[ringOffset]));
|
||||
times.push_back(ptr[timeOffset]);
|
||||
}
|
||||
UDEBUG("Scan split: %fs, points=%d", timer.ticks(), (int)laserCloudIn->size());
|
||||
|
||||
// Process scan. Retrieve the deskewed (motion-compensated) cloud
|
||||
// produced by LIO-SAM's image projection stage so we can propagate
|
||||
// it back into SensorData: otherwise downstream consumers such as
|
||||
// loop closure registration would still see the raw pre-deskew scan.
|
||||
Eigen::Affine3f poseOut;
|
||||
Eigen::MatrixXd covOut;
|
||||
pcl::PointCloud<pcl::PointXYZI>::Ptr deskewedCloud(new pcl::PointCloud<pcl::PointXYZI>);
|
||||
bool ok = lioSam_->processScan(data.stamp(), laserCloudIn, rings, times, poseOut, covOut, deskewedCloud);
|
||||
UDEBUG("LIO-SAM process: %fs", timer.ticks());
|
||||
|
||||
if(ok)
|
||||
{
|
||||
// Replace the raw scan on SensorData with LIO-SAM's deskewed
|
||||
// cloud so downstream stages (loop closure registration in
|
||||
// particular) use the motion-compensated points instead of
|
||||
// the raw pre-deskew scan. The deskewed cloud is still in the
|
||||
// lidar frame, so the existing localTransform/rangeMax apply.
|
||||
if(deskewedCloud && !deskewedCloud->empty())
|
||||
{
|
||||
const LaserScan & rawScan = data.laserScanRaw();
|
||||
LaserScan deskewedScan(
|
||||
util3d::laserScanFromPointCloud(*deskewedCloud),
|
||||
rawScan.maxPoints(),
|
||||
rawScan.rangeMax(),
|
||||
rawScan.localTransform());
|
||||
data.setLaserScan(deskewedScan);
|
||||
UDEBUG("Replaced raw scan with deskewed cloud (%d -> %d points)",
|
||||
(int)laserCloudIn->size(), (int)deskewedCloud->size());
|
||||
}
|
||||
|
||||
Transform pose = Transform::fromEigen3f(poseOut);
|
||||
|
||||
if(!pose.isNull())
|
||||
{
|
||||
covariance = cv::Mat::eye(6, 6, CV_64FC1);
|
||||
covariance(cv::Range(0, 3), cv::Range(0, 3)) *= linVar_;
|
||||
covariance(cv::Range(3, 6), cv::Range(3, 6)) *= angVar_;
|
||||
|
||||
t = lastPose_.inverse() * pose; // incremental
|
||||
lastPose_ = pose;
|
||||
|
||||
const Transform & localTransform = data.laserScanRaw().localTransform();
|
||||
if(!t.isNull() && !t.isIdentity() && !localTransform.isIdentity() && !localTransform.isNull())
|
||||
{
|
||||
// from laser frame to base frame
|
||||
t = localTransform * t * localTransform.inverse();
|
||||
}
|
||||
|
||||
if(info)
|
||||
{
|
||||
info->type = (int)kTypeLIOSAM;
|
||||
if(covariance.cols == 6 && covariance.rows == 6 && covariance.type() == CV_64FC1)
|
||||
{
|
||||
info->reg.covariance = covariance;
|
||||
}
|
||||
|
||||
if(this->isInfoDataFilled())
|
||||
{
|
||||
pcl::PointCloud<pcl::PointXYZI>::Ptr localMap = lioSam_->getLocalMap();
|
||||
if(localMap && !localMap->empty())
|
||||
{
|
||||
info->localScanMapSize = localMap->size();
|
||||
info->localScanMap = LaserScan(util3d::laserScanFromPointCloud(*localMap), 0, data.laserScanRaw().rangeMax(), data.laserScanRaw().localTransform());
|
||||
}
|
||||
UDEBUG("Fill info data: %fs", timer.ticks());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
lost_ = true;
|
||||
UWARN("LIO-SAM failed to register the latest scan, odometry should be reset.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UDEBUG("LIO-SAM processScan returned false (may be initializing)");
|
||||
}
|
||||
}
|
||||
UINFO("LIO-SAM odom update time = %fs, lost=%s", timerTotal.elapsed(), lost_ ? "true" : "false");
|
||||
|
||||
#else
|
||||
UERROR("RTAB-Map is not built with LIO-SAM support! Select another odometry approach.");
|
||||
#endif
|
||||
return t;
|
||||
}
|
||||
|
||||
} // namespace rtabmap
|
||||
@@ -239,7 +239,7 @@ Transform OdometryMono::computeTransform(SensorData & data, const Transform & gu
|
||||
{
|
||||
UDEBUG("");
|
||||
bool newPtsAdded = false;
|
||||
const Signature * newS = memory_->getLastWorkingSignature();
|
||||
const Signature * newS = memory_->getLastWorkingSignature(false);
|
||||
UDEBUG("newWords=%d", (int)newS->getWords().size());
|
||||
nFeatures = (int)newS->getWords().size();
|
||||
if((int)newS->getWords().size() > minInliers_)
|
||||
@@ -646,7 +646,7 @@ Transform OdometryMono::computeTransform(SensorData & data, const Transform & gu
|
||||
info->type = 1;
|
||||
}
|
||||
|
||||
const Signature * refS = memory_->getLastWorkingSignature();
|
||||
const Signature * refS = memory_->getLastWorkingSignature(false);
|
||||
|
||||
std::vector<cv::Point2f> refCorners(firstFrameGuessCorners_.size());
|
||||
std::vector<cv::Point2f> refCornersGuess(firstFrameGuessCorners_.size());
|
||||
@@ -775,6 +775,7 @@ Transform OdometryMono::computeTransform(SensorData & data, const Transform & gu
|
||||
cameraTransform,
|
||||
fundMatrixReprojError_,
|
||||
fundMatrixConfidence_,
|
||||
4,
|
||||
refWords3Guess); // for scale estimation
|
||||
|
||||
if(cameraTransform.getNorm() < minTranslation_*5)
|
||||
@@ -803,10 +804,10 @@ Transform OdometryMono::computeTransform(SensorData & data, const Transform & gu
|
||||
if(!refWords3.empty())
|
||||
{
|
||||
UDEBUG("Added %d/%d valid 3D features", (int)refWords3.size(), (int)localMap_.size());
|
||||
keyFrameWords3D_.insert(std::make_pair(memory_->getLastWorkingSignature()->id(), refWords3));
|
||||
keyFrameWords3D_.insert(std::make_pair(memory_->getLastWorkingSignature(false)->id(), refWords3));
|
||||
}
|
||||
keyFramePoses_.insert(std::make_pair(memory_->getLastWorkingSignature()->id(), this->getPose()));
|
||||
keyFrameModels_.insert(std::make_pair(memory_->getLastWorkingSignature()->id(), newModel));
|
||||
keyFramePoses_.insert(std::make_pair(memory_->getLastWorkingSignature(false)->id(), this->getPose()));
|
||||
keyFrameModels_.insert(std::make_pair(memory_->getLastWorkingSignature(false)->id(), newModel));
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -828,7 +829,7 @@ Transform OdometryMono::computeTransform(SensorData & data, const Transform & gu
|
||||
// generate kpts
|
||||
if(memory_->update(SensorData(data)))
|
||||
{
|
||||
const Signature * s = memory_->getLastWorkingSignature();
|
||||
const Signature * s = memory_->getLastWorkingSignature(false);
|
||||
const std::multimap<int, int> & words = s->getWords();
|
||||
if((int)words.size() > minInliers_ && !s->getWordsKpts().empty())
|
||||
{
|
||||
|
||||
@@ -32,6 +32,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include "rtabmap/utilite/UTimer.h"
|
||||
#include "rtabmap/utilite/UStl.h"
|
||||
#include "rtabmap/utilite/UDirectory.h"
|
||||
#include "rtabmap/utilite/UFile.h"
|
||||
#include <pcl/common/transforms.h>
|
||||
#include <opencv2/imgproc/types_c.h>
|
||||
#include <rtabmap/core/odometry/OdometryORBSLAM3.h>
|
||||
@@ -116,6 +117,13 @@ bool OdometryORBSLAM3::init(const rtabmap::CameraModel & model1, const rtabmap::
|
||||
}
|
||||
//Load ORB Vocabulary
|
||||
vocabularyPath = uReplaceChar(vocabularyPath, '~', UDirectory::homeDir());
|
||||
if(!UFile::exists(vocabularyPath))
|
||||
{
|
||||
UERROR("ORB_SLAM vocabulary path \"%s\" doesn't exist! (Parameter name=\"%s\")",
|
||||
vocabularyPath.c_str(),
|
||||
rtabmap::Parameters::kOdomORBSLAMVocPath().c_str());
|
||||
return false;
|
||||
}
|
||||
UWARN("Loading ORB Vocabulary: \"%s\". This could take a while...", vocabularyPath.c_str());
|
||||
|
||||
// Create configuration file
|
||||
@@ -240,7 +248,7 @@ bool OdometryORBSLAM3::init(const rtabmap::CameraModel & model1, const rtabmap::
|
||||
//# IMU Parameters TODO: hard-coded, not used
|
||||
//#--------------------------------------------------------------------------------------------
|
||||
// Transformation from camera 0 to body-frame (imu)
|
||||
rtabmap::Transform camImuT = model1.localTransform()*imuLocalTransform_;
|
||||
rtabmap::Transform camImuT = imuLocalTransform_.inverse()*model1.localTransform();
|
||||
ofs << "IMU.T_b_c1: !!opencv-matrix" << std::endl;
|
||||
ofs << " rows: 4" << std::endl;
|
||||
ofs << " cols: 4" << std::endl;
|
||||
@@ -340,14 +348,16 @@ bool OdometryORBSLAM3::init(const rtabmap::CameraModel & model1, const rtabmap::
|
||||
|
||||
ofs.close();
|
||||
|
||||
ORB_SLAM3::System::eSensor sensor =
|
||||
stereo?(withIMU?ORB_SLAM3::System::IMU_STEREO:ORB_SLAM3::System::STEREO):
|
||||
(withIMU?ORB_SLAM3::System::IMU_RGBD:ORB_SLAM3::System::RGBD);
|
||||
UINFO("Initializing ORB_SLAM3 system with sensor %d...", (int)sensor);
|
||||
orbslam_ = new ORB_SLAM3::System(
|
||||
vocabularyPath,
|
||||
configPath,
|
||||
stereo && withIMU?ORB_SLAM3::System::IMU_STEREO:
|
||||
stereo?ORB_SLAM3::System::STEREO:
|
||||
withIMU?ORB_SLAM3::System::IMU_RGBD:
|
||||
ORB_SLAM3::System::RGBD,
|
||||
sensor,
|
||||
false);
|
||||
UINFO("Initializing ORB_SLAM3 system with sensor %d... done!", (int)sensor);
|
||||
return true;
|
||||
#else
|
||||
UERROR("RTAB-Map is not built with ORB_SLAM support! Select another visual odometry approach.");
|
||||
@@ -373,6 +383,7 @@ Transform OdometryORBSLAM3::computeTransform(
|
||||
{
|
||||
if(lastImuStamp_ == 0.0 || lastImuStamp_ < data.stamp())
|
||||
{
|
||||
UDEBUG("Adding IMU %f", data.stamp());
|
||||
orbslamImus_.push_back(ORB_SLAM3::IMU::Point(
|
||||
data.imu().linearAcceleration().val[0],
|
||||
data.imu().linearAcceleration().val[1],
|
||||
@@ -419,7 +430,14 @@ Transform OdometryORBSLAM3::computeTransform(
|
||||
(data.stereoCameraModels().size() == 1 &&
|
||||
data.stereoCameraModels()[0].isValidForProjection())))
|
||||
{
|
||||
UERROR("Invalid camera model!");
|
||||
if(data.cameraModels().size() > 1 || data.stereoCameraModels().size() > 1)
|
||||
{
|
||||
UERROR("Multi-camera not supported with ORB_SLAM integration!");
|
||||
}
|
||||
else
|
||||
{
|
||||
UERROR("Invalid camera model!");
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
@@ -432,6 +450,7 @@ Transform OdometryORBSLAM3::computeTransform(
|
||||
if(lastImageStamp_ == 0.0)
|
||||
{
|
||||
lastImageStamp_ = data.stamp();
|
||||
UDEBUG("Waiting for another image to initialize...");
|
||||
return t;
|
||||
}
|
||||
|
||||
@@ -457,6 +476,7 @@ Transform OdometryORBSLAM3::computeTransform(
|
||||
rightMono = cv::Mat();
|
||||
cv::cvtColor(data.imageRaw(), rightMono, CV_BGR2GRAY);
|
||||
}
|
||||
UDEBUG("Adding Stereo Frame %f", data.stamp());
|
||||
Tcw = orbslam_->TrackStereo(leftMono, rightMono, data.stamp(), orbslamImus_);
|
||||
orbslamImus_.clear();
|
||||
}
|
||||
@@ -472,15 +492,22 @@ Transform OdometryORBSLAM3::computeTransform(
|
||||
{
|
||||
depth = util2d::cvtDepthToFloat(data.depthRaw());
|
||||
}
|
||||
UDEBUG("Adding RGBD Frame %f", data.stamp());
|
||||
Tcw = orbslam_->TrackRGBD(data.imageRaw(), depth, data.stamp(), orbslamImus_);
|
||||
orbslamImus_.clear();
|
||||
}
|
||||
|
||||
Transform previousPoseInv = previousPose_.inverse();
|
||||
std::vector<ORB_SLAM3::MapPoint*> mapPoints = orbslam_->GetTrackedMapPoints();
|
||||
if(orbslam_->isLost() || mapPoints.empty())
|
||||
std::vector<ORB_SLAM3::MapPoint*> trackedMapPoints = orbslam_->GetTrackedMapPoints();
|
||||
if(orbslam_->isLost() || trackedMapPoints.empty())
|
||||
{
|
||||
covariance = cv::Mat::eye(6,6,CV_64FC1)*9999.0f;
|
||||
if(!imuLocalTransform_.isNull()) {
|
||||
UWARN("ORBSLAM lost tracking! If it is on initialization, try moving the sensor in a circle for a couple of seconds.");
|
||||
}
|
||||
else {
|
||||
UWARN("ORBSLAM lost tracking!");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -490,14 +517,16 @@ Transform OdometryORBSLAM3::computeTransform(
|
||||
|
||||
if(!p.isNull())
|
||||
{
|
||||
if(!localTransform.isNull())
|
||||
if(!imuLocalTransform_.isNull())
|
||||
{
|
||||
if(originLocalTransform_.isNull())
|
||||
{
|
||||
originLocalTransform_ = localTransform;
|
||||
}
|
||||
// transform in base frame
|
||||
p = originLocalTransform_ * p.inverse() * localTransform.inverse();
|
||||
// Transform p from optical-imu system (x->left, y->back and z->up) to ros system, then remove camera local transform
|
||||
p = Transform(0,0,0,0,0,-M_PI/2) * p.inverse() * localTransform.inverse();
|
||||
}
|
||||
else
|
||||
{
|
||||
UASSERT(!localTransform.isNull());
|
||||
// Transform p from optical system (x->right, y->down and z->forward) to ros system, then remove camera local transform
|
||||
p = CameraModel::opticalRotation() * p.inverse() * localTransform.inverse();
|
||||
}
|
||||
t = previousPoseInv*p;
|
||||
}
|
||||
@@ -534,12 +563,14 @@ Transform OdometryORBSLAM3::computeTransform(
|
||||
}
|
||||
}
|
||||
|
||||
size_t mapPointsSize = 0;
|
||||
if(info)
|
||||
{
|
||||
info->lost = t.isNull();
|
||||
info->type = (int)kTypeORBSLAM;
|
||||
info->reg.covariance = covariance;
|
||||
info->localMapSize = mapPoints.size();
|
||||
std::vector<ORB_SLAM3::MapPoint*> mapPoints = orbslam_->GetAllMapPoints();
|
||||
info->localMapSize = mapPointsSize = mapPoints.size();
|
||||
info->localKeyFrames = 0;
|
||||
|
||||
if(this->isInfoDataFilled())
|
||||
@@ -549,20 +580,20 @@ Transform OdometryORBSLAM3::computeTransform(
|
||||
info->reg.inliersIDs.resize(kpts.size());
|
||||
int oi = 0;
|
||||
|
||||
UASSERT(mapPoints.size() == kpts.size());
|
||||
UASSERT(trackedMapPoints.size() == kpts.size());
|
||||
for (unsigned int i = 0; i < kpts.size(); ++i)
|
||||
{
|
||||
int wordId;
|
||||
if(mapPoints[i] != 0)
|
||||
if(trackedMapPoints[i] != 0)
|
||||
{
|
||||
wordId = mapPoints[i]->mnId;
|
||||
wordId = trackedMapPoints[i]->mnId;
|
||||
}
|
||||
else
|
||||
{
|
||||
wordId = -(i+1);
|
||||
}
|
||||
info->words.insert(std::make_pair(wordId, kpts[i]));
|
||||
if(mapPoints[i] != 0)
|
||||
if(trackedMapPoints[i] != 0)
|
||||
{
|
||||
info->reg.matchesIDs[oi] = wordId;
|
||||
info->reg.inliersIDs[oi] = wordId;
|
||||
@@ -574,7 +605,15 @@ Transform OdometryORBSLAM3::computeTransform(
|
||||
info->reg.inliers = oi;
|
||||
info->reg.matches = oi;
|
||||
|
||||
Eigen::Affine3f fixRot = (this->getPose()*previousPoseInv*originLocalTransform_).toEigen3f();
|
||||
Eigen::Affine3f fixRot;
|
||||
if(!imuLocalTransform_.isNull())
|
||||
{
|
||||
fixRot = (this->getPose()*previousPoseInv*Transform(0,0,0,0,0,-M_PI/2)).toEigen3f();
|
||||
}
|
||||
else
|
||||
{
|
||||
fixRot = (this->getPose()*previousPoseInv*CameraModel::opticalRotation()).toEigen3f();
|
||||
}
|
||||
for (unsigned int i = 0; i < mapPoints.size(); ++i)
|
||||
{
|
||||
if(mapPoints[i])
|
||||
@@ -587,7 +626,8 @@ Transform OdometryORBSLAM3::computeTransform(
|
||||
}
|
||||
}
|
||||
|
||||
UINFO("Odom update time = %fs, map points=%ld, lost=%s", timer.elapsed(), mapPoints.size(), t.isNull()?"true":"false");
|
||||
UINFO("Odom update time = %fs, tracked points=%ld, map points=%ld, lost=%s",
|
||||
timer.elapsed(), trackedMapPoints.size(), mapPointsSize, t.isNull()?"true":"false");
|
||||
|
||||
#else
|
||||
UERROR("RTAB-Map is not built with ORB_SLAM support! Select another visual odometry approach.");
|
||||
|
||||
@@ -39,7 +39,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include <estimator/estimator.h>
|
||||
#include <estimator/parameters.h>
|
||||
#include <camodocal/camera_models/PinholeCamera.h>
|
||||
#include <camodocal/camera_models/EquidistantCamera.h>
|
||||
#include <camodocal/camera_models/PinholeFullCamera.h>
|
||||
#include <utility/visualization.h>
|
||||
#endif
|
||||
|
||||
@@ -49,10 +49,12 @@ namespace rtabmap {
|
||||
class VinsFusionEstimator: public Estimator
|
||||
{
|
||||
public:
|
||||
VinsFusionEstimator(
|
||||
const Transform & imuLocalTransform,
|
||||
VinsFusionEstimator() : Estimator()
|
||||
{}
|
||||
|
||||
bool init(const Transform & imuLocalTransform,
|
||||
const StereoCameraModel & model,
|
||||
bool rectified) : Estimator()
|
||||
bool rectified)
|
||||
{
|
||||
MULTIPLE_THREAD = 0;
|
||||
setParameter();
|
||||
@@ -63,71 +65,133 @@ public:
|
||||
//overwrite camera calibration only if received model is radtan, otherwise use config
|
||||
UASSERT(NUM_OF_CAM >= 1 && NUM_OF_CAM <=2);
|
||||
|
||||
if( (NUM_OF_CAM == 2 && model.left().D_raw().cols == 4 && model.right().D_raw().cols == 4) ||
|
||||
(NUM_OF_CAM == 1 && model.left().D_raw().cols == 4))
|
||||
if( (NUM_OF_CAM == 2 && (rectified || (model.left().D_raw().cols >= 4 && model.right().D_raw().cols >= 4))) ||
|
||||
(NUM_OF_CAM == 1 && (rectified || model.left().D_raw().cols >= 4)))
|
||||
{
|
||||
UWARN("Overwriting VINS camera calibration config with received pinhole model... rectified=%d", rectified?1:0);
|
||||
UINFO("Setting up VINS camera calibration config with received pinhole model... rectified=%d distortion coefficients=%d",
|
||||
rectified?1:0, model.left().D_raw().cols);
|
||||
featureTracker.m_camera.clear();
|
||||
|
||||
camodocal::PinholeCameraPtr camera( new camodocal::PinholeCamera );
|
||||
camodocal::PinholeCamera::Parameters params(
|
||||
model.name(),
|
||||
model.left().imageWidth(), model.left().imageHeight(),
|
||||
rectified?0:model.left().D_raw().at<double>(0,0),
|
||||
rectified?0:model.left().D_raw().at<double>(0,1),
|
||||
rectified?0:model.left().D_raw().at<double>(0,2),
|
||||
rectified?0:model.left().D_raw().at<double>(0,3),
|
||||
rectified?model.left().fx():model.left().K_raw().at<double>(0,0),
|
||||
rectified?model.left().fy():model.left().K_raw().at<double>(1,1),
|
||||
rectified?model.left().cx():model.left().K_raw().at<double>(0,2),
|
||||
rectified?model.left().cy():model.left().K_raw().at<double>(1,2));
|
||||
camera->setParameters(params);
|
||||
featureTracker.m_camera.push_back(camera);
|
||||
double fx = 0.0;
|
||||
if(!rectified && model.left().D_raw().cols >= 8)
|
||||
{
|
||||
if(model.left().D_raw().cols > 8)
|
||||
{
|
||||
UWARN("Received %d distortion coefficients, but only the first 8 are supported, ignoring the last coefficents.",
|
||||
model.left().D_raw().cols);
|
||||
}
|
||||
camodocal::PinholeFullCameraPtr camera( new camodocal::PinholeFullCamera );
|
||||
camodocal::PinholeFullCamera::Parameters params(
|
||||
model.name(),
|
||||
model.left().imageWidth(), model.left().imageHeight(),
|
||||
model.left().D_raw().at<double>(0,0), // k1
|
||||
model.left().D_raw().at<double>(0,1), // k1
|
||||
model.left().D_raw().at<double>(0,4), // k3
|
||||
model.left().D_raw().at<double>(0,5), // k4
|
||||
model.left().D_raw().at<double>(0,6), // k5
|
||||
model.left().D_raw().at<double>(0,7), // k6
|
||||
model.left().D_raw().at<double>(0,2), // p1
|
||||
model.left().D_raw().at<double>(0,3), // p1
|
||||
model.left().K_raw().at<double>(0,0), // fx
|
||||
model.left().K_raw().at<double>(1,1), // fy
|
||||
model.left().K_raw().at<double>(0,2), // cx
|
||||
model.left().K_raw().at<double>(1,2)); // cy
|
||||
camera->setParameters(params);
|
||||
featureTracker.m_camera.push_back(camera);
|
||||
fx = params.fx();
|
||||
if(NUM_OF_CAM == 2)
|
||||
{
|
||||
UASSERT(model.left().D_raw().cols == model.right().D_raw().cols);
|
||||
camodocal::PinholeFullCameraPtr camera2( new camodocal::PinholeFullCamera );
|
||||
camodocal::PinholeFullCamera::Parameters params2(
|
||||
model.name(),
|
||||
model.right().imageWidth(), model.right().imageHeight(),
|
||||
model.right().D_raw().at<double>(0,0), // k1
|
||||
model.right().D_raw().at<double>(0,1), // k2
|
||||
model.right().D_raw().at<double>(0,4), // k3
|
||||
model.right().D_raw().at<double>(0,5), // k4
|
||||
model.right().D_raw().at<double>(0,6), // k5
|
||||
model.right().D_raw().at<double>(0,7), // k6
|
||||
model.right().D_raw().at<double>(0,2), // p1
|
||||
model.right().D_raw().at<double>(0,3), // p2
|
||||
model.right().K_raw().at<double>(0,0), // fx
|
||||
model.right().K_raw().at<double>(1,1), // fy
|
||||
model.right().K_raw().at<double>(0,2), // cx
|
||||
model.right().K_raw().at<double>(1,2)); // cy
|
||||
camera2->setParameters(params2);
|
||||
featureTracker.m_camera.push_back(camera2);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!rectified)
|
||||
{
|
||||
if(model.left().D_raw().cols == 6) {
|
||||
UERROR("Fisheye camera model support not implemented! Provide rectified images instead (see %s).",
|
||||
Parameters::kRtabmapImagesAlreadyRectified().c_str());
|
||||
return false;
|
||||
}
|
||||
if(model.left().D_raw().cols > 4)
|
||||
{
|
||||
UWARN("Received %d distortion coefficients, but only 4 or 8 are supported, ignoring the last coefficents.",
|
||||
model.left().D_raw().cols);
|
||||
}
|
||||
}
|
||||
|
||||
camodocal::PinholeCameraPtr camera( new camodocal::PinholeCamera );
|
||||
camodocal::PinholeCamera::Parameters params(
|
||||
model.name(),
|
||||
model.left().imageWidth(), model.left().imageHeight(),
|
||||
rectified?0:model.left().D_raw().at<double>(0,0), // k1
|
||||
rectified?0:model.left().D_raw().at<double>(0,1), // k2
|
||||
rectified?0:model.left().D_raw().at<double>(0,2), // p1
|
||||
rectified?0:model.left().D_raw().at<double>(0,3), // p2
|
||||
rectified?model.left().fx():model.left().K_raw().at<double>(0,0),
|
||||
rectified?model.left().fy():model.left().K_raw().at<double>(1,1),
|
||||
rectified?model.left().cx():model.left().K_raw().at<double>(0,2),
|
||||
rectified?model.left().cy():model.left().K_raw().at<double>(1,2));
|
||||
camera->setParameters(params);
|
||||
featureTracker.m_camera.push_back(camera);
|
||||
fx = params.fx();
|
||||
if(NUM_OF_CAM == 2)
|
||||
{
|
||||
UASSERT(model.left().D_raw().cols == model.right().D_raw().cols);
|
||||
camodocal::PinholeCameraPtr camera2( new camodocal::PinholeCamera );
|
||||
camodocal::PinholeCamera::Parameters params2(
|
||||
model.name(),
|
||||
model.right().imageWidth(), model.right().imageHeight(),
|
||||
rectified?0:model.right().D_raw().at<double>(0,0), // k1
|
||||
rectified?0:model.right().D_raw().at<double>(0,1), // k2
|
||||
rectified?0:model.right().D_raw().at<double>(0,2), // p1
|
||||
rectified?0:model.right().D_raw().at<double>(0,3), // p2
|
||||
rectified?model.right().fx():model.right().K_raw().at<double>(0,0),
|
||||
rectified?model.right().fy():model.right().K_raw().at<double>(1,1),
|
||||
rectified?model.right().cx():model.right().K_raw().at<double>(0,2),
|
||||
rectified?model.right().cy():model.right().K_raw().at<double>(1,2));
|
||||
camera2->setParameters(params2);
|
||||
featureTracker.m_camera.push_back(camera2);
|
||||
}
|
||||
}
|
||||
|
||||
double originalParalax = MIN_PARALLAX * FOCAL_LENGTH;
|
||||
// If you have compiler error about FOCAL_LENGTH being const, make sure to use the following patch:
|
||||
// If you have compiler error about FOCAL_LENGTH being const, make sure to use the following patch for ROS1:
|
||||
// https://gist.github.com/matlabbe/795ab37067367dca58bbadd8201d986c#file-vins-fusion_pull136-patch
|
||||
FOCAL_LENGTH = params.fx();
|
||||
// Use this patch for ROS2: https://gist.github.com/matlabbe/ebbb343cd744da9d6d6d6ded2e1557fd
|
||||
FOCAL_LENGTH = fx;
|
||||
MIN_PARALLAX = originalParalax / FOCAL_LENGTH;
|
||||
ProjectionTwoFrameOneCamFactor::sqrt_info = FOCAL_LENGTH / 1.5 * Matrix2d::Identity();
|
||||
ProjectionTwoFrameTwoCamFactor::sqrt_info = FOCAL_LENGTH / 1.5 * Matrix2d::Identity();
|
||||
ProjectionOneFrameTwoCamFactor::sqrt_info = FOCAL_LENGTH / 1.5 * Matrix2d::Identity();
|
||||
|
||||
if(NUM_OF_CAM == 2)
|
||||
{
|
||||
camodocal::PinholeCameraPtr camera( new camodocal::PinholeCamera );
|
||||
camodocal::PinholeCamera::Parameters params(
|
||||
model.name(),
|
||||
model.right().imageWidth(), model.right().imageHeight(),
|
||||
rectified?0:model.right().D_raw().at<double>(0,0),
|
||||
rectified?0:model.right().D_raw().at<double>(0,1),
|
||||
rectified?0:model.right().D_raw().at<double>(0,2),
|
||||
rectified?0:model.right().D_raw().at<double>(0,3),
|
||||
rectified?model.right().fx():model.right().K_raw().at<double>(0,0),
|
||||
rectified?model.right().fy():model.right().K_raw().at<double>(1,1),
|
||||
rectified?model.right().cx():model.right().K_raw().at<double>(0,2),
|
||||
rectified?model.right().cy():model.right().K_raw().at<double>(1,2));
|
||||
camera->setParameters(params);
|
||||
featureTracker.m_camera.push_back(camera);
|
||||
}
|
||||
}
|
||||
else if(rectified)
|
||||
else
|
||||
{
|
||||
UWARN("Images are rectified but received calibration cannot be "
|
||||
"used, make sure calibration in config file doesn't have "
|
||||
"distortion or send raw images to VINS odometry.");
|
||||
if(!featureTracker.m_camera.empty())
|
||||
{
|
||||
if(featureTracker.m_camera.front()->imageWidth() != model.left().imageWidth() ||
|
||||
featureTracker.m_camera.front()->imageHeight() != model.left().imageHeight())
|
||||
{
|
||||
UERROR("Received images don't have same size (%dx%d) than in the config file (%dx%d)!",
|
||||
model.left().imageWidth(),
|
||||
model.left().imageHeight(),
|
||||
featureTracker.m_camera.front()->imageWidth(),
|
||||
featureTracker.m_camera.front()->imageHeight());
|
||||
}
|
||||
UERROR("Received stereo camera model is not compatible with VINS-Fusion.");
|
||||
if(!rectified && model.left().D_raw().cols != 4) {
|
||||
UERROR("When raw images are provided (%s=false), we expect 4 distortion coefficients (k1,k2,p1,p2), received %d",
|
||||
Parameters::kRtabmapImagesAlreadyRectified().c_str(),
|
||||
model.left().D_raw().cols);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Transform imuCam0 = imuLocalTransform.inverse() * model.localTransform();
|
||||
@@ -165,6 +229,7 @@ public:
|
||||
cout << " new intrinsic cam " << i << endl << featureTracker.m_camera[i]->parametersToString() << endl;
|
||||
}
|
||||
f_manager.setRic(ric);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Copy of original inputImage() so that overridden processMeasurements() is used and threading is disabled.
|
||||
@@ -350,10 +415,16 @@ Transform OdometryVINSFusion::computeTransform(
|
||||
{
|
||||
// intialize
|
||||
UINFO("Initializing with image %f", data.stamp());
|
||||
vinsEstimator_ = new VinsFusionEstimator(
|
||||
lastImu_.localTransform().isNull()?Transform::getIdentity():lastImu_.localTransform(),
|
||||
data.stereoCameraModels()[0],
|
||||
this->imagesAlreadyRectified());
|
||||
vinsEstimator_ = new VinsFusionEstimator();
|
||||
if(!vinsEstimator_->init(
|
||||
lastImu_.localTransform().isNull()?Transform::getIdentity():lastImu_.localTransform(),
|
||||
data.stereoCameraModels()[0],
|
||||
this->imagesAlreadyRectified()))
|
||||
{
|
||||
delete vinsEstimator_;
|
||||
vinsEstimator_ = 0;
|
||||
return Transform();
|
||||
}
|
||||
|
||||
if(USE_IMU) {
|
||||
double dx = lastImu_.linearAcceleration().val[0];
|
||||
|
||||
@@ -61,7 +61,6 @@ typedef Eigen::Matrix<double,Eigen::Dynamic,Eigen::Dynamic,Eigen::ColMajor> Matr
|
||||
#include "g2o/types/slam3d/types_slam3d.h"
|
||||
#include "g2o/edge_se3_xyzprior.h" // Include after types_slam3d.h to be ignored on newest g2o versions
|
||||
#include "g2o/edge_se3_gravity.h"
|
||||
#include "g2o/edge_sbacam_gravity.h"
|
||||
#include "g2o/edge_xy_prior.h" // Include after types_slam2d.h to be ignored on newest g2o versions
|
||||
#include "g2o/edge_xyz_prior.h" // Include after types_slam3d.h to be ignored on newest g2o versions
|
||||
#ifdef G2O_HAVE_CSPARSE
|
||||
@@ -77,6 +76,19 @@ typedef Eigen::Matrix<double,Eigen::Dynamic,Eigen::Dynamic,Eigen::ColMajor> Matr
|
||||
#include "g2o/types/types_sba.h"
|
||||
#include "g2o/types/types_six_dof_expmap.h"
|
||||
#include "g2o/solvers/linear_solver_eigen.h"
|
||||
#include "g2o/edge_se3_expmap.h"
|
||||
#endif
|
||||
|
||||
#if defined(RTABMAP_G2O) || defined(RTABMAP_ORB_SLAM)
|
||||
namespace rtabmap {
|
||||
#ifdef RTABMAP_ORB_SLAM
|
||||
typedef g2o::VertexSE3Expmap VertexCam;
|
||||
#else
|
||||
typedef g2o::VertexCam VertexCam;
|
||||
#endif
|
||||
}
|
||||
#include "g2o/edge_sbacam_gravity.h"
|
||||
#include "g2o/edge_sbacam_prior.h"
|
||||
#endif
|
||||
|
||||
typedef g2o::BlockSolver< g2o::BlockSolverTraits<-1, -1> > SlamBlockSolver;
|
||||
@@ -216,7 +228,7 @@ std::map<int, Transform> OptimizerG2O::optimize(
|
||||
outputCovariance = cv::Mat::eye(6,6,CV_64FC1);
|
||||
std::map<int, Transform> optimizedPoses;
|
||||
#ifdef RTABMAP_G2O
|
||||
UDEBUG("Optimizing graph...");
|
||||
UDEBUG("Optimizing graph... (rootId=%d)", rootId);
|
||||
|
||||
#ifndef RTABMAP_VERTIGO
|
||||
if(this->isRobust())
|
||||
@@ -348,6 +360,9 @@ std::map<int, Transform> OptimizerG2O::optimize(
|
||||
{
|
||||
if(!priorsIgnored() && iter->second.type() == Link::kPosePrior)
|
||||
{
|
||||
if(rootId!=0) {
|
||||
UDEBUG("Removed rootId=%d because there are priors.");
|
||||
}
|
||||
rootId = 0;
|
||||
break;
|
||||
}
|
||||
@@ -590,7 +605,9 @@ std::map<int, Transform> OptimizerG2O::optimize(
|
||||
g2o::EdgeSE2Prior * priorEdge = new g2o::EdgeSE2Prior();
|
||||
g2o::VertexSE2* v1 = (g2o::VertexSE2*)optimizer.vertex(id1);
|
||||
priorEdge->setVertex(0, v1);
|
||||
priorEdge->setMeasurement(g2o::SE2(iter->second.transform().x(), iter->second.transform().y(), iter->second.transform().theta()));
|
||||
auto pose = g2o::SE2(iter->second.transform().x(), iter->second.transform().y(), iter->second.transform().theta());
|
||||
v1->setEstimate(pose); // This will help g2o to converge faster (https://github.com/introlab/rtabmap_ros/issues/1371)
|
||||
priorEdge->setMeasurement(pose);
|
||||
priorEdge->setParameterId(0, PARAM_OFFSET);
|
||||
Eigen::Matrix<double, 3, 3> information = Eigen::Matrix<double, 3, 3>::Identity();
|
||||
if(!isCovarianceIgnored())
|
||||
@@ -671,6 +688,7 @@ std::map<int, Transform> OptimizerG2O::optimize(
|
||||
Eigen::Isometry3d pose;
|
||||
pose = a.linear();
|
||||
pose.translation() = a.translation();
|
||||
v1->setEstimate(pose); // This will help g2o to converge faster (https://github.com/introlab/rtabmap_ros/issues/1371)
|
||||
priorEdge->setMeasurement(pose);
|
||||
priorEdge->setParameterId(0, PARAM_OFFSET);
|
||||
Eigen::Matrix<double, 6, 6> information = Eigen::Matrix<double, 6, 6>::Identity();
|
||||
@@ -1001,8 +1019,8 @@ std::map<int, Transform> OptimizerG2O::optimize(
|
||||
g2o::EdgeSE3 * e = new g2o::EdgeSE3();
|
||||
g2o::VertexSE3* v1 = (g2o::VertexSE3*)optimizer.vertex(id1);
|
||||
g2o::VertexSE3* v2 = (g2o::VertexSE3*)optimizer.vertex(id2);
|
||||
UASSERT(v1 != 0);
|
||||
UASSERT(v2 != 0);
|
||||
UASSERT_MSG(v1 != 0, uFormat("v1=%d v2=%d", id1, id2).c_str());
|
||||
UASSERT_MSG(v2 != 0, uFormat("v1=%d v2=%d", id1, id2).c_str());
|
||||
e->setVertex(0, v1);
|
||||
e->setVertex(1, v2);
|
||||
e->setMeasurement(constraint);
|
||||
@@ -1169,7 +1187,7 @@ std::map<int, Transform> OptimizerG2O::optimize(
|
||||
|
||||
if(i>0 && optimizer.activeRobustChi2() > 1000000000000.0)
|
||||
{
|
||||
UERROR("g2o: Large optimimzation error detected (%f), aborting optimization!");
|
||||
UERROR("g2o: Large optimization error detected (%f), aborting optimization!");
|
||||
return optimizedPoses;
|
||||
}
|
||||
|
||||
@@ -1218,7 +1236,7 @@ std::map<int, Transform> OptimizerG2O::optimize(
|
||||
|
||||
if(optimizer.activeRobustChi2() > 1000000000000.0)
|
||||
{
|
||||
UERROR("g2o: Large optimimzation error detected (%f), aborting optimization!");
|
||||
UERROR("g2o: Large optimization error detected (%f), aborting optimization!");
|
||||
return optimizedPoses;
|
||||
}
|
||||
|
||||
@@ -1408,81 +1426,6 @@ std::map<int, Transform> OptimizerG2O::optimize(
|
||||
return optimizedPoses;
|
||||
}
|
||||
|
||||
#ifdef RTABMAP_ORB_SLAM
|
||||
/**
|
||||
* \brief 3D edge between two SBAcam
|
||||
*/
|
||||
class EdgeSE3Expmap : public g2o::BaseBinaryEdge<6, g2o::SE3Quat, g2o::VertexSE3Expmap, g2o::VertexSE3Expmap>
|
||||
{
|
||||
public:
|
||||
EIGEN_MAKE_ALIGNED_OPERATOR_NEW;
|
||||
EdgeSE3Expmap(): BaseBinaryEdge<6, g2o::SE3Quat, g2o::VertexSE3Expmap, g2o::VertexSE3Expmap>(){}
|
||||
bool read(std::istream& is)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool write(std::ostream& os) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void computeError()
|
||||
{
|
||||
const g2o::VertexSE3Expmap* v1 = dynamic_cast<const g2o::VertexSE3Expmap*>(_vertices[0]);
|
||||
const g2o::VertexSE3Expmap* v2 = dynamic_cast<const g2o::VertexSE3Expmap*>(_vertices[1]);
|
||||
g2o::SE3Quat delta = _inverseMeasurement * (v1->estimate().inverse()*v2->estimate());
|
||||
_error[0]=delta.translation().x();
|
||||
_error[1]=delta.translation().y();
|
||||
_error[2]=delta.translation().z();
|
||||
_error[3]=delta.rotation().x();
|
||||
_error[4]=delta.rotation().y();
|
||||
_error[5]=delta.rotation().z();
|
||||
}
|
||||
|
||||
virtual void setMeasurement(const g2o::SE3Quat& meas){
|
||||
_measurement=meas;
|
||||
_inverseMeasurement=meas.inverse();
|
||||
}
|
||||
|
||||
virtual double initialEstimatePossible(const g2o::OptimizableGraph::VertexSet& , g2o::OptimizableGraph::Vertex* ) { return 1.;}
|
||||
virtual void initialEstimate(const g2o::OptimizableGraph::VertexSet& from_, g2o::OptimizableGraph::Vertex* ){
|
||||
g2o::VertexSE3Expmap* from = static_cast<g2o::VertexSE3Expmap*>(_vertices[0]);
|
||||
g2o::VertexSE3Expmap* to = static_cast<g2o::VertexSE3Expmap*>(_vertices[1]);
|
||||
if (from_.count(from) > 0)
|
||||
to->setEstimate((g2o::SE3Quat) from->estimate() * _measurement);
|
||||
else
|
||||
from->setEstimate((g2o::SE3Quat) to->estimate() * _inverseMeasurement);
|
||||
}
|
||||
|
||||
virtual bool setMeasurementData(const double* d){
|
||||
Eigen::Map<const g2o::Vector7d> v(d);
|
||||
_measurement.fromVector(v);
|
||||
_inverseMeasurement = _measurement.inverse();
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool getMeasurementData(double* d) const{
|
||||
Eigen::Map<g2o::Vector7d> v(d);
|
||||
v = _measurement.toVector();
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual int measurementDimension() const {return 7;}
|
||||
|
||||
virtual bool setMeasurementFromState() {
|
||||
const g2o::VertexSE3Expmap* v1 = dynamic_cast<const g2o::VertexSE3Expmap*>(_vertices[0]);
|
||||
const g2o::VertexSE3Expmap* v2 = dynamic_cast<const g2o::VertexSE3Expmap*>(_vertices[1]);
|
||||
_measurement = (v1->estimate().inverse()*v2->estimate());
|
||||
_inverseMeasurement = _measurement.inverse();
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
g2o::SE3Quat _inverseMeasurement;
|
||||
};
|
||||
#endif
|
||||
|
||||
std::map<int, Transform> OptimizerG2O::optimizeBA(
|
||||
int rootId,
|
||||
const std::map<int, Transform> & poses,
|
||||
@@ -1553,7 +1496,13 @@ std::map<int, Transform> OptimizerG2O::optimizeBA(
|
||||
#endif // RTABMAP_ORB_SLAM
|
||||
|
||||
#ifndef RTABMAP_ORB_SLAM
|
||||
if(optimizer_ == 1)
|
||||
// ISSUE: It seems the fatal error
|
||||
// "[SetJac] infinite jac" happens relatively
|
||||
// easily with GaussNewton on SBA problem,
|
||||
// ignore optimizer_ and always use Levenberg for SBA.
|
||||
// TODO: Note that g2o/RobustKernelDelta parameter could be
|
||||
// potentially tuned to avoid that error with GaussNewton.
|
||||
if(0)//optimizer_ == 1)
|
||||
{
|
||||
#ifdef RTABMAP_G2O_CPP11
|
||||
optimizer.setAlgorithm(new g2o::OptimizationAlgorithmGaussNewton(
|
||||
@@ -1573,8 +1522,23 @@ std::map<int, Transform> OptimizerG2O::optimizeBA(
|
||||
#endif
|
||||
}
|
||||
|
||||
// detect if there are gravity constraints
|
||||
bool hasGravityConstraints = false;
|
||||
if(!isSlam2d() && gravitySigma() > 0)
|
||||
{
|
||||
for(std::multimap<int, Link>::const_iterator iter=links.begin(); iter!=links.end(); ++iter)
|
||||
{
|
||||
if( iter->second.from() == iter->second.to() &&
|
||||
iter->second.type() == Link::kGravity)
|
||||
{
|
||||
hasGravityConstraints = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UDEBUG("fill poses to g2o...");
|
||||
|
||||
UDEBUG("fill %ld poses to g2o... (rootId=%d hasGravityConstraints=%d isSlam2d=%d)", poses.size(), rootId, hasGravityConstraints?1:0, isSlam2d()?1:0);
|
||||
for(std::map<int, Transform>::const_iterator iter=poses.begin(); iter!=poses.end(); ++iter)
|
||||
{
|
||||
if(iter->first > 0)
|
||||
@@ -1590,11 +1554,8 @@ std::map<int, Transform> OptimizerG2O::optimizeBA(
|
||||
|
||||
// Add node's pose
|
||||
UASSERT(!camPose.isNull());
|
||||
#ifdef RTABMAP_ORB_SLAM
|
||||
g2o::VertexSE3Expmap * vCam = new g2o::VertexSE3Expmap();
|
||||
#else
|
||||
g2o::VertexCam * vCam = new g2o::VertexCam();
|
||||
#endif
|
||||
|
||||
rtabmap::VertexCam * vCam = new rtabmap::VertexCam();
|
||||
|
||||
Eigen::Affine3d a = camPose.toEigen3d();
|
||||
#ifdef RTABMAP_ORB_SLAM
|
||||
@@ -1613,7 +1574,65 @@ std::map<int, Transform> OptimizerG2O::optimizeBA(
|
||||
vCam->setId(iter->first*MULTICAM_OFFSET + i);
|
||||
|
||||
// negative root means that all other poses should be fixed instead of the root
|
||||
vCam->setFixed((rootId >= 0 && iter->first == rootId) || (rootId < 0 && iter->first != -rootId));
|
||||
bool fixNode = (rootId >= 0 && iter->first == rootId) || (rootId < 0 && iter->first != -rootId);
|
||||
|
||||
UASSERT_MSG(optimizer.addVertex(vCam), uFormat("cannot insert cam vertex %d (pose=%d)!?", vCam->id(), iter->first).c_str());
|
||||
|
||||
if(this->isSlam2d())
|
||||
{
|
||||
if(fixNode)
|
||||
{
|
||||
UDEBUG("Set node %d fixed", iter->first);
|
||||
vCam->setFixed(true);
|
||||
}
|
||||
else if(i==0) // Only set prior on the first camera
|
||||
{
|
||||
// add a singleton constraint that locks the position of the robot on the plane
|
||||
EdgeSBACamPrior* planeConstraint = new EdgeSBACamPrior();
|
||||
Eigen::Matrix<double, 6, 6> pinfo = Eigen::Matrix<double, 6, 6>::Zero();
|
||||
pinfo(2, 2) = 1e9;
|
||||
planeConstraint->setInformation(pinfo);
|
||||
g2o::SE3Quat fixedZ = g2o::SE3Quat();
|
||||
fixedZ.setTranslation(Eigen::Vector3d(0,0,iter->second.z()));
|
||||
planeConstraint->setMeasurement(fixedZ);
|
||||
Eigen::Affine3d a = iterModel->second[i].localTransform().inverse().toEigen3d();
|
||||
planeConstraint->setCameraInvLocalTransform(g2o::SE3Quat(a.linear(), a.translation()));
|
||||
planeConstraint->vertices()[0] = vCam;
|
||||
optimizer.addEdge(planeConstraint);
|
||||
}
|
||||
}
|
||||
else if(fixNode)
|
||||
{
|
||||
if(rootId < 0 || !hasGravityConstraints)
|
||||
{
|
||||
UDEBUG("Set node %d fixed", iter->first);
|
||||
vCam->setFixed(true);
|
||||
}
|
||||
else if(hasGravityConstraints && i==0) // Only set prior on the first camera in case of multi-cam
|
||||
{
|
||||
// Setup root prior (fixed x,y,z,yaw)
|
||||
EdgeSBACamPrior * e = new EdgeSBACamPrior();
|
||||
e->vertices()[0] = vCam;
|
||||
Eigen::Affine3d a = iter->second.toEigen3d();
|
||||
e->setMeasurement(g2o::SE3Quat(a.linear(), a.translation()));
|
||||
a = iterModel->second[i].localTransform().inverse().toEigen3d();
|
||||
e->setCameraInvLocalTransform(g2o::SE3Quat(a.linear(), a.translation()));
|
||||
Eigen::Matrix<double, 6, 6> information = Eigen::Matrix<double, 6, 6>::Identity()*10e6;
|
||||
// pitch and roll not fixed
|
||||
information(3,3) = information(4,4) = 1;
|
||||
e->setInformation(information);
|
||||
if (!optimizer.addEdge(e))
|
||||
{
|
||||
delete e;
|
||||
UERROR("Map: Failed adding fixed constraint of node %d, set as fixed instead", iter->first);
|
||||
vCam->setFixed(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
UDEBUG("Set node %d fixed with prior (have gravity constraints)", iter->first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*UDEBUG("camPose %d (camid=%d) (fixed=%d) fx=%f fy=%f cx=%f cy=%f Tx=%f baseline=%f t=%s",
|
||||
iter->first,
|
||||
@@ -1626,8 +1645,6 @@ std::map<int, Transform> OptimizerG2O::optimizeBA(
|
||||
iterModel->second[i].Tx(),
|
||||
iterModel->second[i].Tx()<0.0?-iterModel->second[i].Tx()/iterModel->second[i].fx():baseline_,
|
||||
camPose.prettyPrint().c_str());*/
|
||||
|
||||
UASSERT_MSG(optimizer.addVertex(vCam), uFormat("cannot insert cam vertex %d (pose=%d)!?", vCam->id(), iter->first).c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1646,7 +1663,6 @@ std::map<int, Transform> OptimizerG2O::optimizeBA(
|
||||
|
||||
if(id1 == id2)
|
||||
{
|
||||
#ifndef RTABMAP_ORB_SLAM
|
||||
g2o::HyperGraph::Edge * edge = 0;
|
||||
if(gravitySigma() > 0 && iter->second.type() == Link::kGravity && poses.find(iter->first) != poses.end())
|
||||
{
|
||||
@@ -1660,7 +1676,7 @@ std::map<int, Transform> OptimizerG2O::optimizeBA(
|
||||
|
||||
Eigen::MatrixXd information = Eigen::MatrixXd::Identity(3, 3) * 1.0/(gravitySigma()*gravitySigma());
|
||||
|
||||
g2o::VertexCam* v1 = (g2o::VertexCam*)optimizer.vertex(id1*MULTICAM_OFFSET);
|
||||
rtabmap::VertexCam* v1 = (rtabmap::VertexCam*)optimizer.vertex(id1*MULTICAM_OFFSET);
|
||||
EdgeSBACamGravity* priorEdge(new EdgeSBACamGravity());
|
||||
std::map<int, std::vector<CameraModel> >::const_iterator iterModel = models.find(iter->first);
|
||||
// Gravity constraint added only to first camera of a pose
|
||||
@@ -1677,7 +1693,6 @@ std::map<int, Transform> OptimizerG2O::optimizeBA(
|
||||
UERROR("Map: Failed adding constraint between %d and %d, skipping", id1, id2);
|
||||
return optimizedPoses;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else if(id1>0 && id2>0) // not supporting landmarks
|
||||
{
|
||||
@@ -1833,14 +1848,14 @@ std::map<int, Transform> OptimizerG2O::optimizeBA(
|
||||
|
||||
g2o::OptimizableGraph::Edge * e;
|
||||
double baseline = 0.0;
|
||||
rtabmap::VertexCam* vcam = dynamic_cast<rtabmap::VertexCam*>(optimizer.vertex(camId));
|
||||
#ifdef RTABMAP_ORB_SLAM
|
||||
g2o::VertexSE3Expmap* vcam = dynamic_cast<g2o::VertexSE3Expmap*>(optimizer.vertex(camId));
|
||||
|
||||
std::map<int, std::vector<CameraModel> >::const_iterator iterModel = models.find(poseId);
|
||||
|
||||
UASSERT(iterModel != models.end() && camIndex<iterModel->second.size() && iterModel->second[camIndex].isValidForProjection());
|
||||
UASSERT(iterModel != models.end() && camIndex<(int)iterModel->second.size() && iterModel->second[camIndex].isValidForProjection());
|
||||
baseline = iterModel->second[camIndex].Tx()<0.0?-iterModel->second[camIndex].Tx()/iterModel->second[camIndex].fx():baseline_;
|
||||
#else
|
||||
g2o::VertexCam* vcam = dynamic_cast<g2o::VertexCam*>(optimizer.vertex(camId));
|
||||
baseline = vcam->estimate().baseline;
|
||||
#endif
|
||||
double variance = pixelVariance_;
|
||||
@@ -1938,7 +1953,8 @@ std::map<int, Transform> OptimizerG2O::optimizeBA(
|
||||
|
||||
if(uIsNan(chi2))
|
||||
{
|
||||
UERROR("Optimization generated NANs, aborting optimization! Try another g2o's optimizer (current=%d).", optimizer_);
|
||||
UERROR("Optimization generated NANs, aborting optimization! Try another g2o's optimizer (current %s=%d) or solver (current %s=%d).",
|
||||
Parameters::kg2oOptimizer().c_str(), optimizer_, Parameters::kg2oSolver().c_str(), solver_);
|
||||
return optimizedPoses;
|
||||
}
|
||||
UDEBUG("iteration %d: %d nodes, %d edges, chi2: %f", i, (int)optimizer.vertices().size(), (int)optimizer.edges().size(), chi2);
|
||||
@@ -1972,15 +1988,18 @@ std::map<int, Transform> OptimizerG2O::optimizeBA(
|
||||
//UDEBUG("Ignoring edge (%d<->%d) d=%f var=%f kernel=%f chi2=%f", (*iter)->vertex(0)->id()-stepVertexId, (*iter)->vertex(1)->id(), d, 1.0/((g2o::EdgeProjectP2SC*)(*iter))->information()(0,0), (*iter)->robustKernel()->delta(), (*iter)->chi2());
|
||||
#endif
|
||||
|
||||
cv::Point3f pt3d;
|
||||
int id=-1;
|
||||
if((*iter)->vertex(0)->id() > negVertexOffset)
|
||||
{
|
||||
pt3d = points3DMap.at(negVertexOffset - (*iter)->vertex(0)->id());
|
||||
id = negVertexOffset - (*iter)->vertex(0)->id();
|
||||
}
|
||||
else
|
||||
{
|
||||
pt3d = points3DMap.at((*iter)->vertex(0)->id()-stepVertexId);
|
||||
id = (*iter)->vertex(0)->id() - stepVertexId;
|
||||
}
|
||||
UASSERT_MSG(points3DMap.find(id) != points3DMap.end(), uFormat("word id=%d points3DMap=%ld vertex id=%d (negVertexOffset=%d stepVertexId=%d)",
|
||||
id, points3DMap.size(), (*iter)->vertex(0)->id(), negVertexOffset, stepVertexId).c_str());
|
||||
cv::Point3f pt3d = points3DMap.at(id);
|
||||
((g2o::VertexSBAPointXYZ*)(*iter)->vertex(0))->setEstimate(Eigen::Vector3d(pt3d.x, pt3d.y, pt3d.z));
|
||||
|
||||
if(outliers)
|
||||
@@ -2037,12 +2056,26 @@ std::map<int, Transform> OptimizerG2O::optimizeBA(
|
||||
return optimizedPoses;
|
||||
}
|
||||
|
||||
// FIXME: is there a way that we can add the 2D constraint directly in SBA?
|
||||
if(this->isSlam2d())
|
||||
{
|
||||
// get transform between old and new pose
|
||||
t = iter->second.inverse() * t;
|
||||
optimizedPoses.insert(std::pair<int, Transform>(iter->first, iter->second * t.to3DoF()));
|
||||
// The optimized poses should be already fixed to original height,
|
||||
// but it may have varied a little (not exaclty the same number).
|
||||
// Here we just put back the original z value.
|
||||
if(fabs(t.z() - iter->second.z()) < 0.001)
|
||||
{
|
||||
t.z() = iter->second.z();
|
||||
optimizedPoses.insert(std::pair<int, Transform>(iter->first, t));
|
||||
}
|
||||
else
|
||||
{
|
||||
UWARN("Planar constraints didn't work!? original pose (%d), pose %s -> %s. Falling back to old approach.",
|
||||
iter->first,
|
||||
iter->second.prettyPrint().c_str(),
|
||||
t.prettyPrint().c_str());
|
||||
// get transform between old and new pose
|
||||
t = iter->second.inverse() * t;
|
||||
optimizedPoses.insert(std::pair<int, Transform>(iter->first, iter->second * t.to3DoF()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2102,6 +2135,390 @@ std::map<int, Transform> OptimizerG2O::optimizeBA(
|
||||
return optimizedPoses;
|
||||
}
|
||||
|
||||
bool OptimizerG2O::loadGraph(
|
||||
const std::string & fileName,
|
||||
std::map<int, Transform> & poses,
|
||||
std::multimap<int, Link> & edgeConstraints)
|
||||
{
|
||||
FILE * file = 0;
|
||||
#ifdef _MSC_VER
|
||||
fopen_s(&file, fileName.c_str(), "r");
|
||||
#else
|
||||
file = fopen(fileName.c_str(), "r");
|
||||
#endif
|
||||
|
||||
if(!file)
|
||||
{
|
||||
UERROR("Cannot open file %s", fileName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// saveGraph() writes landmarks (originally negative ids, remapped to
|
||||
// landmarkOffset - id) first in DESCENDING file-id order, then regular
|
||||
// poses in ASCENDING file-id order. We recover landmarkOffset from this
|
||||
// order to restore the original negative landmark ids.
|
||||
struct VertexEntry {
|
||||
int fileId;
|
||||
Transform transform;
|
||||
bool definitelyLandmark; // VERTEX_XY / VERTEX_TRACKXYZ
|
||||
};
|
||||
struct EdgeEntry {
|
||||
int from;
|
||||
int to;
|
||||
Link::Type type;
|
||||
Transform transform;
|
||||
cv::Mat info;
|
||||
bool isPrior; // from==to, prior on a single vertex
|
||||
bool hasLandmarkEndpoint; // tag implies a landmark on one side
|
||||
};
|
||||
std::vector<VertexEntry> verticesList;
|
||||
std::vector<EdgeEntry> edgesList;
|
||||
|
||||
char line[2048];
|
||||
while(fgets(line, 2048, file) != NULL)
|
||||
{
|
||||
std::list<std::string> tokenList = uSplit(uReplaceChar(uReplaceChar(line, '\n', ' '), '\r', ' '), ' ');
|
||||
std::vector<std::string> v;
|
||||
v.reserve(tokenList.size());
|
||||
for(std::list<std::string>::const_iterator iter = tokenList.begin(); iter != tokenList.end(); ++iter)
|
||||
{
|
||||
if(!iter->empty())
|
||||
{
|
||||
v.push_back(*iter);
|
||||
}
|
||||
}
|
||||
if(v.empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const std::string & tag = v[0];
|
||||
|
||||
// Skip parameters, switch helpers and unrelated entries
|
||||
if(tag == "PARAMS_SE2OFFSET" || tag == "PARAMS_SE3OFFSET" ||
|
||||
tag == "VERTEX_SWITCH" || tag == "EDGE_SWITCH_PRIOR")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if(tag == "VERTEX_SE2" && v.size() == 5)
|
||||
{
|
||||
VertexEntry e;
|
||||
e.fileId = atoi(v[1].c_str());
|
||||
e.transform = Transform(uStr2Float(v[2]), uStr2Float(v[3]), uStr2Float(v[4]));
|
||||
e.definitelyLandmark = false;
|
||||
verticesList.push_back(e);
|
||||
}
|
||||
else if(tag == "VERTEX_XY" && v.size() == 4)
|
||||
{
|
||||
VertexEntry e;
|
||||
e.fileId = atoi(v[1].c_str());
|
||||
e.transform = Transform(uStr2Float(v[2]), uStr2Float(v[3]), 0);
|
||||
e.definitelyLandmark = true;
|
||||
verticesList.push_back(e);
|
||||
}
|
||||
else if(tag == "VERTEX_SE3:QUAT" && v.size() == 9)
|
||||
{
|
||||
VertexEntry e;
|
||||
e.fileId = atoi(v[1].c_str());
|
||||
e.transform = Transform(uStr2Float(v[2]), uStr2Float(v[3]), uStr2Float(v[4]),
|
||||
uStr2Float(v[5]), uStr2Float(v[6]), uStr2Float(v[7]), uStr2Float(v[8]));
|
||||
e.definitelyLandmark = false;
|
||||
verticesList.push_back(e);
|
||||
}
|
||||
else if(tag == "VERTEX_TRACKXYZ" && v.size() == 5)
|
||||
{
|
||||
VertexEntry e;
|
||||
e.fileId = atoi(v[1].c_str());
|
||||
e.transform = Transform(uStr2Float(v[2]), uStr2Float(v[3]), uStr2Float(v[4]), 0, 0, 0);
|
||||
e.definitelyLandmark = true;
|
||||
verticesList.push_back(e);
|
||||
}
|
||||
else if(tag == "EDGE_SE2" && v.size() == 12)
|
||||
{
|
||||
EdgeEntry e;
|
||||
e.from = atoi(v[1].c_str());
|
||||
e.to = atoi(v[2].c_str());
|
||||
e.transform = Transform(uStr2Float(v[3]), uStr2Float(v[4]), uStr2Float(v[5]));
|
||||
e.info = cv::Mat::eye(6, 6, CV_64FC1);
|
||||
e.info.at<double>(0, 0) = uStr2Double(v[6]);
|
||||
e.info.at<double>(0, 1) = e.info.at<double>(1, 0) = uStr2Double(v[7]);
|
||||
e.info.at<double>(0, 5) = e.info.at<double>(5, 0) = uStr2Double(v[8]);
|
||||
e.info.at<double>(1, 1) = uStr2Double(v[9]);
|
||||
e.info.at<double>(1, 5) = e.info.at<double>(5, 1) = uStr2Double(v[10]);
|
||||
e.info.at<double>(5, 5) = uStr2Double(v[11]);
|
||||
e.type = Link::kUndef; // disambiguated after we know landmarkOffset
|
||||
e.isPrior = false;
|
||||
e.hasLandmarkEndpoint = false;
|
||||
edgesList.push_back(e);
|
||||
}
|
||||
else if(tag == "EDGE_SE2_XY" && v.size() == 8)
|
||||
{
|
||||
EdgeEntry e;
|
||||
e.from = atoi(v[1].c_str());
|
||||
e.to = atoi(v[2].c_str());
|
||||
e.transform = Transform(uStr2Float(v[3]), uStr2Float(v[4]), 0);
|
||||
e.info = cv::Mat::eye(6, 6, CV_64FC1);
|
||||
e.info.at<double>(0, 0) = uStr2Double(v[5]);
|
||||
e.info.at<double>(0, 1) = e.info.at<double>(1, 0) = uStr2Double(v[6]);
|
||||
e.info.at<double>(1, 1) = uStr2Double(v[7]);
|
||||
e.type = Link::kLandmark;
|
||||
e.isPrior = false;
|
||||
e.hasLandmarkEndpoint = true;
|
||||
edgesList.push_back(e);
|
||||
}
|
||||
else if((tag == "EDGE_SE3:QUAT" || tag == "EDGE_SE3") && v.size() == 31)
|
||||
{
|
||||
EdgeEntry e;
|
||||
e.from = atoi(v[1].c_str());
|
||||
e.to = atoi(v[2].c_str());
|
||||
e.transform = Transform(uStr2Float(v[3]), uStr2Float(v[4]), uStr2Float(v[5]),
|
||||
uStr2Float(v[6]), uStr2Float(v[7]), uStr2Float(v[8]), uStr2Float(v[9]));
|
||||
e.info = cv::Mat::eye(6, 6, CV_64FC1);
|
||||
int idx = 10;
|
||||
for(int r = 0; r < 6; ++r)
|
||||
{
|
||||
for(int c = r; c < 6; ++c)
|
||||
{
|
||||
e.info.at<double>(r, c) = uStr2Double(v[idx++]);
|
||||
if(r != c) e.info.at<double>(c, r) = e.info.at<double>(r, c);
|
||||
}
|
||||
}
|
||||
// EDGE_SE3 (no :QUAT) is the landmark variant emitted by saveGraph
|
||||
bool landmarkTag = (tag == "EDGE_SE3");
|
||||
e.type = landmarkTag ? Link::kLandmark : Link::kUndef;
|
||||
e.isPrior = false;
|
||||
e.hasLandmarkEndpoint = landmarkTag;
|
||||
edgesList.push_back(e);
|
||||
}
|
||||
else if(tag == "EDGE_SE3_TRACKXYZ" && v.size() == 13)
|
||||
{
|
||||
EdgeEntry e;
|
||||
e.from = atoi(v[1].c_str());
|
||||
e.to = atoi(v[2].c_str());
|
||||
// v[3] = param_offset id, ignored
|
||||
e.transform = Transform(uStr2Float(v[4]), uStr2Float(v[5]), uStr2Float(v[6]), 0, 0, 0);
|
||||
e.info = cv::Mat::eye(6, 6, CV_64FC1);
|
||||
e.info.at<double>(0, 0) = uStr2Double(v[7]);
|
||||
e.info.at<double>(0, 1) = e.info.at<double>(1, 0) = uStr2Double(v[8]);
|
||||
e.info.at<double>(0, 2) = e.info.at<double>(2, 0) = uStr2Double(v[9]);
|
||||
e.info.at<double>(1, 1) = uStr2Double(v[10]);
|
||||
e.info.at<double>(1, 2) = e.info.at<double>(2, 1) = uStr2Double(v[11]);
|
||||
e.info.at<double>(2, 2) = uStr2Double(v[12]);
|
||||
e.type = Link::kLandmark;
|
||||
e.isPrior = false;
|
||||
e.hasLandmarkEndpoint = true;
|
||||
edgesList.push_back(e);
|
||||
}
|
||||
else if(tag == "EDGE_PRIOR_SE2" && v.size() == 11)
|
||||
{
|
||||
EdgeEntry e;
|
||||
e.from = atoi(v[1].c_str());
|
||||
e.to = e.from;
|
||||
e.transform = Transform(uStr2Float(v[2]), uStr2Float(v[3]), uStr2Float(v[4]));
|
||||
e.info = cv::Mat::eye(6, 6, CV_64FC1);
|
||||
e.info.at<double>(0, 0) = uStr2Double(v[5]);
|
||||
e.info.at<double>(0, 1) = e.info.at<double>(1, 0) = uStr2Double(v[6]);
|
||||
e.info.at<double>(0, 5) = e.info.at<double>(5, 0) = uStr2Double(v[7]);
|
||||
e.info.at<double>(1, 1) = uStr2Double(v[8]);
|
||||
e.info.at<double>(1, 5) = e.info.at<double>(5, 1) = uStr2Double(v[9]);
|
||||
e.info.at<double>(5, 5) = uStr2Double(v[10]);
|
||||
e.type = Link::kPosePrior;
|
||||
e.isPrior = true;
|
||||
e.hasLandmarkEndpoint = false;
|
||||
edgesList.push_back(e);
|
||||
}
|
||||
else if(tag == "EDGE_PRIOR_SE2_XY" && v.size() == 7)
|
||||
{
|
||||
EdgeEntry e;
|
||||
e.from = atoi(v[1].c_str());
|
||||
e.to = e.from;
|
||||
e.transform = Transform(uStr2Float(v[2]), uStr2Float(v[3]), 0);
|
||||
e.info = cv::Mat::eye(6, 6, CV_64FC1);
|
||||
e.info.at<double>(0, 0) = uStr2Double(v[4]);
|
||||
e.info.at<double>(0, 1) = e.info.at<double>(1, 0) = uStr2Double(v[5]);
|
||||
e.info.at<double>(1, 1) = uStr2Double(v[6]);
|
||||
// no orientation info on this prior
|
||||
e.info.at<double>(3, 3) = e.info.at<double>(4, 4) = e.info.at<double>(5, 5) = 1.0 / 9999.0;
|
||||
e.type = Link::kPosePrior;
|
||||
e.isPrior = true;
|
||||
e.hasLandmarkEndpoint = false;
|
||||
edgesList.push_back(e);
|
||||
}
|
||||
else if(tag == "EDGE_SE3_PRIOR" && v.size() == 31)
|
||||
{
|
||||
EdgeEntry e;
|
||||
e.from = atoi(v[1].c_str());
|
||||
e.to = e.from;
|
||||
// v[2] = param_offset id, ignored
|
||||
e.transform = Transform(uStr2Float(v[3]), uStr2Float(v[4]), uStr2Float(v[5]),
|
||||
uStr2Float(v[6]), uStr2Float(v[7]), uStr2Float(v[8]), uStr2Float(v[9]));
|
||||
e.info = cv::Mat::eye(6, 6, CV_64FC1);
|
||||
int idx = 10;
|
||||
for(int r = 0; r < 6; ++r)
|
||||
{
|
||||
for(int c = r; c < 6; ++c)
|
||||
{
|
||||
e.info.at<double>(r, c) = uStr2Double(v[idx++]);
|
||||
if(r != c) e.info.at<double>(c, r) = e.info.at<double>(r, c);
|
||||
}
|
||||
}
|
||||
e.type = Link::kPosePrior;
|
||||
e.isPrior = true;
|
||||
e.hasLandmarkEndpoint = false;
|
||||
edgesList.push_back(e);
|
||||
}
|
||||
else if(tag == "EDGE_POINTXYZ_PRIOR" && v.size() == 11)
|
||||
{
|
||||
EdgeEntry e;
|
||||
e.from = atoi(v[1].c_str());
|
||||
e.to = e.from;
|
||||
e.transform = Transform(uStr2Float(v[2]), uStr2Float(v[3]), uStr2Float(v[4]), 0, 0, 0);
|
||||
e.info = cv::Mat::eye(6, 6, CV_64FC1);
|
||||
e.info.at<double>(0, 0) = uStr2Double(v[5]);
|
||||
e.info.at<double>(0, 1) = e.info.at<double>(1, 0) = uStr2Double(v[6]);
|
||||
e.info.at<double>(0, 2) = e.info.at<double>(2, 0) = uStr2Double(v[7]);
|
||||
e.info.at<double>(1, 1) = uStr2Double(v[8]);
|
||||
e.info.at<double>(1, 2) = e.info.at<double>(2, 1) = uStr2Double(v[9]);
|
||||
e.info.at<double>(2, 2) = uStr2Double(v[10]);
|
||||
// no orientation info on this prior
|
||||
e.info.at<double>(3, 3) = e.info.at<double>(4, 4) = e.info.at<double>(5, 5) = 1.0 / 9999.0;
|
||||
e.type = Link::kPosePrior;
|
||||
e.isPrior = true;
|
||||
e.hasLandmarkEndpoint = false;
|
||||
edgesList.push_back(e);
|
||||
}
|
||||
else if(tag == "EDGE_SE2_SWITCHABLE" && v.size() == 13)
|
||||
{
|
||||
EdgeEntry e;
|
||||
e.from = atoi(v[1].c_str());
|
||||
e.to = atoi(v[2].c_str());
|
||||
// v[3] = switch vertex id, ignored
|
||||
e.transform = Transform(uStr2Float(v[4]), uStr2Float(v[5]), uStr2Float(v[6]));
|
||||
e.info = cv::Mat::eye(6, 6, CV_64FC1);
|
||||
e.info.at<double>(0, 0) = uStr2Double(v[7]);
|
||||
e.info.at<double>(0, 1) = e.info.at<double>(1, 0) = uStr2Double(v[8]);
|
||||
e.info.at<double>(0, 5) = e.info.at<double>(5, 0) = uStr2Double(v[9]);
|
||||
e.info.at<double>(1, 1) = uStr2Double(v[10]);
|
||||
e.info.at<double>(1, 5) = e.info.at<double>(5, 1) = uStr2Double(v[11]);
|
||||
e.info.at<double>(5, 5) = uStr2Double(v[12]);
|
||||
e.type = Link::kUndef;
|
||||
e.isPrior = false;
|
||||
e.hasLandmarkEndpoint = false;
|
||||
edgesList.push_back(e);
|
||||
}
|
||||
else if(tag == "EDGE_SE3_SWITCHABLE" && v.size() == 32)
|
||||
{
|
||||
EdgeEntry e;
|
||||
e.from = atoi(v[1].c_str());
|
||||
e.to = atoi(v[2].c_str());
|
||||
// v[3] = switch vertex id, ignored
|
||||
e.transform = Transform(uStr2Float(v[4]), uStr2Float(v[5]), uStr2Float(v[6]),
|
||||
uStr2Float(v[7]), uStr2Float(v[8]), uStr2Float(v[9]), uStr2Float(v[10]));
|
||||
e.info = cv::Mat::eye(6, 6, CV_64FC1);
|
||||
int idx = 11;
|
||||
for(int r = 0; r < 6; ++r)
|
||||
{
|
||||
for(int c = r; c < 6; ++c)
|
||||
{
|
||||
e.info.at<double>(r, c) = uStr2Double(v[idx++]);
|
||||
if(r != c) e.info.at<double>(c, r) = e.info.at<double>(r, c);
|
||||
}
|
||||
}
|
||||
e.type = Link::kUndef;
|
||||
e.isPrior = false;
|
||||
e.hasLandmarkEndpoint = false;
|
||||
edgesList.push_back(e);
|
||||
}
|
||||
else
|
||||
{
|
||||
UWARN("Unsupported or malformed g2o line: \"%s\" (tag=%s, tokens=%d)", line, tag.c_str(), (int)v.size());
|
||||
}
|
||||
}
|
||||
fclose(file);
|
||||
|
||||
// Recover landmarkOffset from vertex order:
|
||||
// file order = [landmarks with DESCENDING file_ids] + [regular poses with ASCENDING file_ids]
|
||||
// Walk backwards from the end and take the longest ascending suffix as the regular poses.
|
||||
// landmarkOffset = max regular pose id (= last fileId of that suffix).
|
||||
int landmarkOffset = 0;
|
||||
int firstRegularIdx = (int)verticesList.size();
|
||||
if(!verticesList.empty())
|
||||
{
|
||||
firstRegularIdx = (int)verticesList.size() - 1;
|
||||
while(firstRegularIdx > 0 &&
|
||||
verticesList[firstRegularIdx - 1].fileId < verticesList[firstRegularIdx].fileId)
|
||||
{
|
||||
--firstRegularIdx;
|
||||
}
|
||||
landmarkOffset = verticesList.back().fileId;
|
||||
|
||||
// If the alleged regular suffix actually starts on a definite landmark
|
||||
// (VERTEX_XY / VERTEX_TRACKXYZ), then there are no regular poses and
|
||||
// saveGraph used landmarkOffset = 0; restore that case.
|
||||
if(verticesList[firstRegularIdx].definitelyLandmark)
|
||||
{
|
||||
landmarkOffset = 0;
|
||||
firstRegularIdx = (int)verticesList.size();
|
||||
}
|
||||
}
|
||||
|
||||
// Insert vertices into poses, remapping landmark file ids back to negative.
|
||||
for(int i = 0; i < (int)verticesList.size(); ++i)
|
||||
{
|
||||
int originalId;
|
||||
if(i < firstRegularIdx)
|
||||
{
|
||||
originalId = landmarkOffset - verticesList[i].fileId; // negative
|
||||
}
|
||||
else
|
||||
{
|
||||
originalId = verticesList[i].fileId;
|
||||
}
|
||||
if(poses.find(originalId) == poses.end())
|
||||
{
|
||||
poses.insert(std::make_pair(originalId, verticesList[i].transform));
|
||||
}
|
||||
else
|
||||
{
|
||||
UWARN("Vertex %d (file id %d) already exists, ignoring duplicate", originalId, verticesList[i].fileId);
|
||||
}
|
||||
}
|
||||
|
||||
// Remap edge endpoints. Any file id > landmarkOffset (or, if landmarkOffset == 0
|
||||
// and there are any landmarks at all, any id present in the landmark prefix)
|
||||
// is a landmark and gets the negative id back.
|
||||
bool allLandmarks = (landmarkOffset == 0 && firstRegularIdx == (int)verticesList.size() && !verticesList.empty());
|
||||
auto remap = [&](int fileId) -> int {
|
||||
if(landmarkOffset > 0 && fileId > landmarkOffset)
|
||||
{
|
||||
return landmarkOffset - fileId; // negative
|
||||
}
|
||||
if(allLandmarks)
|
||||
{
|
||||
return -fileId;
|
||||
}
|
||||
return fileId;
|
||||
};
|
||||
|
||||
for(const EdgeEntry & e : edgesList)
|
||||
{
|
||||
int from = remap(e.from);
|
||||
int to = e.isPrior ? from : remap(e.to);
|
||||
Link::Type type = e.type;
|
||||
// Promote ambiguous edges (EDGE_SE2) to kLandmark when an endpoint
|
||||
// turns out to be a landmark after remapping.
|
||||
if(type == Link::kUndef && (from < 0 || to < 0))
|
||||
{
|
||||
type = Link::kLandmark;
|
||||
}
|
||||
edgeConstraints.insert(std::make_pair(from, Link(from, to, type, e.transform, e.info)));
|
||||
}
|
||||
|
||||
UINFO("Graph loaded from %s (%d poses, %d edges, landmarkOffset=%d)",
|
||||
fileName.c_str(), (int)poses.size(), (int)edgeConstraints.size(), landmarkOffset);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OptimizerG2O::saveGraph(
|
||||
const std::string & fileName,
|
||||
const std::map<int, Transform> & poses,
|
||||
|
||||
@@ -51,7 +51,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include <gtsam/nonlinear/NonlinearOptimizer.h>
|
||||
#include <gtsam/nonlinear/Marginals.h>
|
||||
#include <gtsam/nonlinear/Values.h>
|
||||
#include "gtsam/GravityFactor.h"
|
||||
#include <gtsam/navigation/AttitudeFactor.h>
|
||||
#include <optimizer/gtsam/XYFactor.h>
|
||||
#include <optimizer/gtsam/XYZFactor.h>
|
||||
#include <gtsam/nonlinear/ISAM2.h>
|
||||
@@ -90,6 +90,27 @@ bool OptimizerGTSAM::available()
|
||||
#endif
|
||||
}
|
||||
|
||||
std::size_t OptimizerGTSAM::getISAM2LiveFactorsCount() const
|
||||
{
|
||||
#ifdef RTABMAP_GTSAM
|
||||
if(isam2_ == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
// iSAM2 keeps removed factors as null entries to preserve factor indices,
|
||||
// so we have to skip nulls to get the actual live count.
|
||||
const gtsam::NonlinearFactorGraph & factors = isam2_->getFactorsUnsafe();
|
||||
std::size_t live = 0;
|
||||
for(const gtsam::NonlinearFactorGraph::sharedFactor & f : factors)
|
||||
{
|
||||
if(f) ++live;
|
||||
}
|
||||
return live;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void OptimizerGTSAM::parseParameters(const ParametersMap & parameters)
|
||||
{
|
||||
Optimizer::parseParameters(parameters);
|
||||
@@ -121,10 +142,10 @@ void OptimizerGTSAM::parseParameters(const ParametersMap & parameters)
|
||||
params.relinearizeThreshold = threshold;
|
||||
params.relinearizeSkip = skip;
|
||||
params.evaluateNonlinearError = true;
|
||||
isam2_ = new ISAM2(params);
|
||||
isam2_ = new gtsam::ISAM2(params);
|
||||
|
||||
addedPoses_.clear();
|
||||
lastAddedConstraints_.clear();
|
||||
trackedFactors_.clear();
|
||||
lastRootFactorIndex_.first = 0;
|
||||
lastSwitchId_ = 1000000000;
|
||||
}
|
||||
@@ -197,7 +218,21 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<ConstraintToFactor> addedPrior;
|
||||
// Ordered list of (from, to, type) entries added to the gtsam graph
|
||||
// during this optimize() call. The order matches the order of factors
|
||||
// pushed into iSAM2, so result.newFactorsIndices[j] gives the factor
|
||||
// index for addedConstraints[j]. Used post-update to populate
|
||||
// trackedFactors_. type holds Link::Type as int; type == -1 marks
|
||||
// the synthetic root prior, which is managed separately via
|
||||
// lastRootFactorIndex_ and stays out of trackedFactors_.
|
||||
struct ConstraintToFactor {
|
||||
ConstraintToFactor(int _from, int _to, int _type = -1) :
|
||||
from(_from), to(_to), type(_type) {}
|
||||
int from;
|
||||
int to;
|
||||
int type;
|
||||
};
|
||||
std::vector<ConstraintToFactor> addedConstraints;
|
||||
gtsam::FactorIndices removeFactorIndices;
|
||||
|
||||
//prior first pose
|
||||
@@ -211,7 +246,7 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
||||
{
|
||||
gtsam::noiseModel::Diagonal::shared_ptr priorNoise = gtsam::noiseModel::Diagonal::Variances(gtsam::Vector3(0.01, 0.01, hasGPSPrior?1e-2:1e-9));
|
||||
graph.add(gtsam::PriorFactor<gtsam::Pose2>(rootId, gtsam::Pose2(initialPose.x(), initialPose.y(), initialPose.theta()), priorNoise));
|
||||
addedPrior.push_back(ConstraintToFactor(rootId, rootId, -1));
|
||||
addedConstraints.push_back(ConstraintToFactor(rootId, rootId));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -221,7 +256,7 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
||||
(hasGPSPrior?2:1e-2), hasGPSPrior?2:1e-2, hasGPSPrior?2:1e-2 // xyz
|
||||
).finished());
|
||||
graph.add(gtsam::PriorFactor<gtsam::Pose3>(rootId, gtsam::Pose3(initialPose.toEigen4d()), priorNoise));
|
||||
addedPrior.push_back(ConstraintToFactor(rootId, rootId, -1));
|
||||
addedConstraints.push_back(ConstraintToFactor(rootId, rootId));
|
||||
}
|
||||
if(isam2_ && lastRootFactorIndex_.first!=0)
|
||||
{
|
||||
@@ -238,7 +273,8 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
||||
delete isam2_;
|
||||
isam2_ = new gtsam::ISAM2(params);
|
||||
addedPoses_.clear();
|
||||
lastAddedConstraints_.clear();
|
||||
trackedFactors_.clear();
|
||||
isLandmarkWithRotation_.clear();
|
||||
lastRootFactorIndex_.first = 0;
|
||||
lastSwitchId_ = 1000000000;
|
||||
}
|
||||
@@ -249,6 +285,16 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
||||
std::map<int, Transform> newPoses;
|
||||
std::multimap<int, Link> newEdgeConstraints;
|
||||
|
||||
// trackedFactors_ keys are (min(from,to), max(from,to), type) so that:
|
||||
// - (A,B) and (B,A) with the same type map to the same entry
|
||||
// (matches graph::findLink(checkBothWays=true) and the
|
||||
// "Input links should be unique!" invariant in Graph.cpp);
|
||||
// - the same pair with a *different* Link::Type counts as a
|
||||
// distinct constraint and gets its own factor index.
|
||||
auto linkKey = [](int from, int to, int type) {
|
||||
return std::make_tuple(std::min(from, to), std::max(from, to), type);
|
||||
};
|
||||
|
||||
if(isam2_)
|
||||
{
|
||||
UDEBUG("Add new poses...");
|
||||
@@ -263,31 +309,78 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
||||
}
|
||||
UDEBUG("Add new links...");
|
||||
// new links?
|
||||
// - self-referring (priors, gravity): add once per new pose, by
|
||||
// checking addedPoses_ (these are never removed/re-added).
|
||||
// - regular edges: add if (from,to,type) is not already a live
|
||||
// factor in iSAM2 (trackedFactors_) AND not already queued
|
||||
// earlier in this same call (queuedLinks). The latter dedupes
|
||||
// bidirectional duplicates of the same logical edge supplied
|
||||
// within a single optimize() call — only one factor goes into
|
||||
// iSAM2.
|
||||
// Self-ref constraints (priors, gravity) are tracked uniformly
|
||||
// with edges: when a node is transferred to LTM its prior is
|
||||
// dropped from the input, and we need to remove its factor from
|
||||
// iSAM2. Likewise a prior re-supplied after being dropped must
|
||||
// be re-added.
|
||||
std::set<std::tuple<int, int, int> > queuedLinks;
|
||||
for(std::multimap<int, Link>::const_iterator iter=edgeConstraints.begin(); iter!=edgeConstraints.end(); ++iter)
|
||||
{
|
||||
if(addedPoses_.find(iter->second.from()) == addedPoses_.end() ||
|
||||
addedPoses_.find(iter->second.to()) == addedPoses_.end())
|
||||
const int from = iter->second.from();
|
||||
const int to = iter->second.to();
|
||||
const std::tuple<int, int, int> key = linkKey(from, to, (int)iter->second.type());
|
||||
bool isNew;
|
||||
if(trackedFactors_.find(key) != trackedFactors_.end())
|
||||
{
|
||||
isNew = false;
|
||||
}
|
||||
else if(!queuedLinks.insert(key).second)
|
||||
{
|
||||
// Already queued earlier in this call (bidirectional
|
||||
// duplicate supplied as both A->B and B->A with the
|
||||
// same type). Skip the second copy.
|
||||
UDEBUG("Ignoring duplicate constraint %d (%d->%d type=%d)", iter->first, from, to, (int)iter->second.type());
|
||||
isNew = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
isNew = true;
|
||||
}
|
||||
if(isNew)
|
||||
{
|
||||
newEdgeConstraints.insert(*iter);
|
||||
UDEBUG("Adding constraint %d (%d->%d) to factor graph", iter->first, iter->second.from(), iter->second.to());
|
||||
UDEBUG("Adding constraint %d (%d->%d type=%d) to factor graph", iter->first, from, to, (int)iter->second.type());
|
||||
}
|
||||
}
|
||||
|
||||
if(!this->isRobust())
|
||||
{
|
||||
UDEBUG("Remove links...");
|
||||
// Remove constraints not there anymore in case the last loop closures were rejected.
|
||||
// As we don't track "switch" constraints, we don't support this if vertigo is used.
|
||||
for(size_t i=0; i<lastAddedConstraints_.size(); ++i)
|
||||
// Remove every tracked non-self-ref factor whose (from,to,type)
|
||||
// is no longer present in the input. Covers loop closures
|
||||
// rejected since the last call, links deleted by graph repair,
|
||||
// or any external deleteLink() applied to old edges. We don't
|
||||
// track "switch" constraints, so this is skipped when vertigo
|
||||
// is used.
|
||||
std::set<std::tuple<int, int, int> > inputLinks;
|
||||
for(std::multimap<int, Link>::const_iterator iter=edgeConstraints.begin(); iter!=edgeConstraints.end(); ++iter)
|
||||
{
|
||||
if(lastAddedConstraints_[i].from != lastAddedConstraints_[i].to &&
|
||||
graph::findLink(edgeConstraints, lastAddedConstraints_[i].from, lastAddedConstraints_[i].to) == edgeConstraints.end())
|
||||
inputLinks.insert(linkKey(iter->second.from(), iter->second.to(), (int)iter->second.type()));
|
||||
}
|
||||
for(std::map<std::tuple<int, int, int>, std::uint64_t>::iterator iter=trackedFactors_.begin(); iter!=trackedFactors_.end(); )
|
||||
{
|
||||
if(inputLinks.find(iter->first) == inputLinks.end())
|
||||
{
|
||||
removeFactorIndices.push_back(lastAddedConstraints_[i].factorIndice);
|
||||
UDEBUG("Removing constraint %d->%d (factor indice=%ld)",
|
||||
lastAddedConstraints_[i].from,
|
||||
lastAddedConstraints_[i].to,
|
||||
lastAddedConstraints_[i].factorIndice);
|
||||
removeFactorIndices.push_back(iter->second);
|
||||
UDEBUG("Removing constraint %d->%d type=%d (factor indice=%ld)",
|
||||
std::get<0>(iter->first),
|
||||
std::get<1>(iter->first),
|
||||
std::get<2>(iter->first),
|
||||
iter->second);
|
||||
iter = trackedFactors_.erase(iter);
|
||||
}
|
||||
else
|
||||
{
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -297,7 +390,7 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
||||
return optimizedPoses;
|
||||
}
|
||||
|
||||
lastAddedConstraints_ = addedPrior;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -308,7 +401,13 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
||||
UDEBUG("fill poses to gtsam... rootId=%d (priorsIgnored=%d landmarksIgnored=%d)",
|
||||
rootId, priorsIgnored()?1:0, landmarksIgnored()?1:0);
|
||||
gtsam::Values initialEstimate;
|
||||
std::map<int, bool> isLandmarkWithRotation;
|
||||
// In batch (non-iSAM2) mode each optimize() call is independent.
|
||||
// In iSAM2 mode the map persists so we can resolve landmarks added
|
||||
// in a previous incremental call but referenced by a new edge.
|
||||
if(!isam2_)
|
||||
{
|
||||
isLandmarkWithRotation_.clear();
|
||||
}
|
||||
for(std::map<int, Transform>::const_iterator iter = newPoses.begin(); iter!=newPoses.end(); ++iter)
|
||||
{
|
||||
UASSERT(!iter->second.isNull());
|
||||
@@ -328,12 +427,12 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
||||
if (1 / static_cast<double>(jter->second.infMatrix().at<double>(5,5)) >= 9999.0)
|
||||
{
|
||||
initialEstimate.insert(iter->first, gtsam::Point2(iter->second.x(), iter->second.y()));
|
||||
isLandmarkWithRotation.insert(std::make_pair(iter->first, false));
|
||||
isLandmarkWithRotation_.insert(std::make_pair(iter->first, false));
|
||||
}
|
||||
else
|
||||
{
|
||||
initialEstimate.insert(iter->first, gtsam::Pose2(iter->second.x(), iter->second.y(), iter->second.theta()));
|
||||
isLandmarkWithRotation.insert(std::make_pair(iter->first, true));
|
||||
isLandmarkWithRotation_.insert(std::make_pair(iter->first, true));
|
||||
}
|
||||
addedPoses_.insert(iter->first);
|
||||
}
|
||||
@@ -357,12 +456,12 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
||||
1 / static_cast<double>(jter->second.infMatrix().at<double>(5,5)) >= 9999.0)
|
||||
{
|
||||
initialEstimate.insert(iter->first, gtsam::Point3(iter->second.x(), iter->second.y(), iter->second.z()));
|
||||
isLandmarkWithRotation.insert(std::make_pair(iter->first, false));
|
||||
isLandmarkWithRotation_.insert(std::make_pair(iter->first, false));
|
||||
}
|
||||
else
|
||||
{
|
||||
initialEstimate.insert(iter->first, gtsam::Pose3(iter->second.toEigen4d()));
|
||||
isLandmarkWithRotation.insert(std::make_pair(iter->first, true));
|
||||
isLandmarkWithRotation_.insert(std::make_pair(iter->first, true));
|
||||
}
|
||||
addedPoses_.insert(iter->first);
|
||||
}
|
||||
@@ -379,8 +478,8 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
||||
int id1 = iter->second.from();
|
||||
int id2 = iter->second.to();
|
||||
|
||||
UASSERT_MSG(poses.find(id1)!=poses.end(), uFormat("id1=%d", id1).c_str());
|
||||
UASSERT_MSG(poses.find(id2)!=poses.end(), uFormat("id2=%d", id2).c_str());
|
||||
UASSERT_MSG(poses.find(id1)!=poses.end(), uFormat("id1=%d for constraint %d->%d (type=%d)", id1, id1, id2, iter->second.type()).c_str());
|
||||
UASSERT_MSG(poses.find(id2)!=poses.end(), uFormat("id2=%d for constraint %d->%d (type=%d)", id2, id1, id2, iter->second.type()).c_str());
|
||||
|
||||
UASSERT(!iter->second.transform().isNull());
|
||||
if(id1 == id2)
|
||||
@@ -390,21 +489,21 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
||||
{
|
||||
if(isSlam2d())
|
||||
{
|
||||
if(id1 < 0 && !isLandmarkWithRotation.at(id1))
|
||||
if(id1 < 0 && !isLandmarkWithRotation_.at(id1))
|
||||
{
|
||||
noiseModel::Diagonal::shared_ptr model = noiseModel::Diagonal::Variances(Vector2(
|
||||
gtsam::noiseModel::Diagonal::shared_ptr model = gtsam::noiseModel::Diagonal::Variances(gtsam::Vector2(
|
||||
1/iter->second.infMatrix().at<double>(0,0),
|
||||
1/iter->second.infMatrix().at<double>(1,1)));
|
||||
graph.add(XYFactor<gtsam::Point2>(id1, gtsam::Point2(iter->second.transform().x(), iter->second.transform().y()), model));
|
||||
lastAddedConstraints_.push_back(ConstraintToFactor(id1, id1, -1));
|
||||
addedConstraints.push_back(ConstraintToFactor(id1, id1, (int)iter->second.type()));
|
||||
}
|
||||
else if (1 / static_cast<double>(iter->second.infMatrix().at<double>(5,5)) >= 9999.0)
|
||||
{
|
||||
noiseModel::Diagonal::shared_ptr model = noiseModel::Diagonal::Variances(Vector2(
|
||||
gtsam::noiseModel::Diagonal::shared_ptr model = gtsam::noiseModel::Diagonal::Variances(gtsam::Vector2(
|
||||
1/iter->second.infMatrix().at<double>(0,0),
|
||||
1/iter->second.infMatrix().at<double>(1,1)));
|
||||
graph.add(XYFactor<gtsam::Pose2>(id1, gtsam::Point2(iter->second.transform().x(), iter->second.transform().y()), model));
|
||||
lastAddedConstraints_.push_back(ConstraintToFactor(id1, id1, -1));
|
||||
addedConstraints.push_back(ConstraintToFactor(id1, id1, (int)iter->second.type()));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -424,30 +523,30 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
||||
|
||||
gtsam::noiseModel::Gaussian::shared_ptr model = gtsam::noiseModel::Gaussian::Information(information);
|
||||
graph.add(gtsam::PriorFactor<gtsam::Pose2>(id1, gtsam::Pose2(iter->second.transform().x(), iter->second.transform().y(), iter->second.transform().theta()), model));
|
||||
lastAddedConstraints_.push_back(ConstraintToFactor(id1, id1, -1));
|
||||
addedConstraints.push_back(ConstraintToFactor(id1, id1, (int)iter->second.type()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(id1 < 0 && !isLandmarkWithRotation.at(id1))
|
||||
if(id1 < 0 && !isLandmarkWithRotation_.at(id1))
|
||||
{
|
||||
noiseModel::Diagonal::shared_ptr model = noiseModel::Diagonal::Precisions(Vector3(
|
||||
gtsam::noiseModel::Diagonal::shared_ptr model = gtsam::noiseModel::Diagonal::Precisions(gtsam::Vector3(
|
||||
iter->second.infMatrix().at<double>(0,0),
|
||||
iter->second.infMatrix().at<double>(1,1),
|
||||
iter->second.infMatrix().at<double>(2,2)));
|
||||
graph.add(XYZFactor<gtsam::Point3>(id1, gtsam::Point3(iter->second.transform().x(), iter->second.transform().y(), iter->second.transform().z()), model));
|
||||
lastAddedConstraints_.push_back(ConstraintToFactor(id1, id1, -1));
|
||||
addedConstraints.push_back(ConstraintToFactor(id1, id1, (int)iter->second.type()));
|
||||
}
|
||||
else if (1 / static_cast<double>(iter->second.infMatrix().at<double>(3,3)) >= 9999.0 ||
|
||||
1 / static_cast<double>(iter->second.infMatrix().at<double>(4,4)) >= 9999.0 ||
|
||||
1 / static_cast<double>(iter->second.infMatrix().at<double>(5,5)) >= 9999.0)
|
||||
{
|
||||
noiseModel::Diagonal::shared_ptr model = noiseModel::Diagonal::Precisions(Vector3(
|
||||
gtsam::noiseModel::Diagonal::shared_ptr model = gtsam::noiseModel::Diagonal::Precisions(gtsam::Vector3(
|
||||
iter->second.infMatrix().at<double>(0,0),
|
||||
iter->second.infMatrix().at<double>(1,1),
|
||||
iter->second.infMatrix().at<double>(2,2)));
|
||||
graph.add(XYZFactor<gtsam::Pose3>(id1, gtsam::Point3(iter->second.transform().x(), iter->second.transform().y(), iter->second.transform().z()), model));
|
||||
lastAddedConstraints_.push_back(ConstraintToFactor(id1, id1, -1));
|
||||
addedConstraints.push_back(ConstraintToFactor(id1, id1, (int)iter->second.type()));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -465,17 +564,24 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
||||
gtsam::SharedNoiseModel model = gtsam::noiseModel::Gaussian::Information(mgtsam);
|
||||
|
||||
graph.add(gtsam::PriorFactor<gtsam::Pose3>(id1, gtsam::Pose3(iter->second.transform().toEigen4d()), model));
|
||||
lastAddedConstraints_.push_back(ConstraintToFactor(id1, id1, -1));
|
||||
addedConstraints.push_back(ConstraintToFactor(id1, id1, (int)iter->second.type()));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(!isSlam2d() && gravitySigma() > 0 && iter->second.type() == Link::kGravity && newPoses.find(iter->first) != newPoses.end())
|
||||
{
|
||||
Vector3 r = gtsam::Pose3(iter->second.transform().toEigen4d()).rotation().xyz();
|
||||
gtsam::Unit3 nG = gtsam::Rot3::RzRyRx(r.x(), r.y(), 0).rotate(gtsam::Unit3(0,0,-1));
|
||||
gtsam::SharedNoiseModel model = gtsam::noiseModel::Isotropic::Sigmas(gtsam::Vector2(gravitySigma(), gravitySigma()));
|
||||
graph.add(Pose3GravityFactor(iter->first, nG, model, Unit3(0,0,1)));
|
||||
lastAddedConstraints_.push_back(ConstraintToFactor(iter->first, iter->first, -1));
|
||||
gtsam::Rot3 nRbMeas = gtsam::Pose3(iter->second.transform().toEigen4d()).rotation();
|
||||
gtsam::Unit3 nZ(0,0,1);
|
||||
gtsam::Unit3 bGMeas = nRbMeas.unrotate(nZ);
|
||||
gtsam::SharedNoiseModel model = gtsam::noiseModel::Isotropic::Sigma(2, gravitySigma());
|
||||
#if GTSAM_VERSION_NUMERIC <= 40300
|
||||
// Note: till 40301 is officially released, version 40300 with "4.3a1" would fail here.
|
||||
// Just replace "<=" above by "<" to use AttitudeFactor<Pose3> below.
|
||||
graph.add(gtsam::Pose3AttitudeFactor(iter->first, nZ, model, bGMeas));
|
||||
#else
|
||||
graph.add(gtsam::AttitudeFactor<gtsam::Pose3>(iter->first, nZ, model, bGMeas));
|
||||
#endif
|
||||
addedConstraints.push_back(ConstraintToFactor(iter->first, iter->first, (int)iter->second.type()));
|
||||
}
|
||||
}
|
||||
else if(id1<0 || id2 < 0)
|
||||
@@ -494,9 +600,9 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
||||
t = iter->second.transform().inverse();
|
||||
std::swap(id1, id2); // should be node -> landmark
|
||||
}
|
||||
|
||||
UASSERT(isLandmarkWithRotation_.find(id2) != isLandmarkWithRotation_.end());
|
||||
#ifdef RTABMAP_VERTIGO
|
||||
if(this->isRobust() && isLandmarkWithRotation.at(id2))
|
||||
if(this->isRobust() && isLandmarkWithRotation_.at(id2))
|
||||
{
|
||||
// create new switch variable
|
||||
// Sunderhauf IROS 2012:
|
||||
@@ -514,7 +620,7 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
||||
gtsam::noiseModel::Diagonal::shared_ptr switchPriorModel = gtsam::noiseModel::Diagonal::Sigmas(gtsam::Vector1(1.0));
|
||||
graph.add(gtsam::PriorFactor<vertigo::SwitchVariableLinear> (gtsam::Symbol('s',lastSwitchId_), vertigo::SwitchVariableLinear(prior), switchPriorModel));
|
||||
}
|
||||
else if(this->isRobust() && !isLandmarkWithRotation.at(id2))
|
||||
else if(this->isRobust() && !isLandmarkWithRotation_.at(id2))
|
||||
{
|
||||
UWARN("%s cannot be used for landmark constraints without orientation.", Parameters::kOptimizerRobust().c_str());
|
||||
}
|
||||
@@ -522,7 +628,7 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
||||
|
||||
if(isSlam2d())
|
||||
{
|
||||
if(isLandmarkWithRotation.at(id2))
|
||||
if(isLandmarkWithRotation_.at(id2))
|
||||
{
|
||||
Eigen::Matrix<double, 3, 3> information = Eigen::Matrix<double, 3, 3>::Identity();
|
||||
if(!isCovarianceIgnored())
|
||||
@@ -549,7 +655,7 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
||||
#endif
|
||||
{
|
||||
graph.add(gtsam::BetweenFactor<gtsam::Pose2>(id1, id2, gtsam::Pose2(t.x(), t.y(), t.theta()), model));
|
||||
lastAddedConstraints_.push_back(ConstraintToFactor(id1, id2, -1));
|
||||
addedConstraints.push_back(ConstraintToFactor(id1, id2, (int)iter->second.type()));
|
||||
}
|
||||
}
|
||||
else if(1 / static_cast<double>(iter->second.infMatrix().at<double>(1,1)) < 9999)
|
||||
@@ -565,7 +671,7 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
||||
gtsam::Point2 landmark(t.x(), t.y());
|
||||
gtsam::Pose2 p;
|
||||
graph.add(gtsam::BearingRangeFactor<gtsam::Pose2, gtsam::Point2>(id1, id2, p.bearing(landmark), p.range(landmark), model));
|
||||
lastAddedConstraints_.push_back(ConstraintToFactor(id1, id2, -1));
|
||||
addedConstraints.push_back(ConstraintToFactor(id1, id2, (int)iter->second.type()));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -580,12 +686,12 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
||||
gtsam::Point2 landmark(t.x(), t.y());
|
||||
gtsam::Pose2 p;
|
||||
graph.add(gtsam::BearingFactor<gtsam::Pose2, gtsam::Point2>(id1, id2, p.bearing(landmark), model));
|
||||
lastAddedConstraints_.push_back(ConstraintToFactor(id1, id2, -1));
|
||||
addedConstraints.push_back(ConstraintToFactor(id1, id2, (int)iter->second.type()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(isLandmarkWithRotation.at(id2))
|
||||
if(isLandmarkWithRotation_.at(id2))
|
||||
{
|
||||
Eigen::Matrix<double, 6, 6> information = Eigen::Matrix<double, 6, 6>::Identity();
|
||||
if(!isCovarianceIgnored())
|
||||
@@ -612,7 +718,7 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
||||
#endif
|
||||
{
|
||||
graph.add(gtsam::BetweenFactor<gtsam::Pose3>(id1, id2, gtsam::Pose3(t.toEigen4d()), model));
|
||||
lastAddedConstraints_.push_back(ConstraintToFactor(id1, id2, -1));
|
||||
addedConstraints.push_back(ConstraintToFactor(id1, id2, (int)iter->second.type()));
|
||||
}
|
||||
}
|
||||
else if(1 / static_cast<double>(iter->second.infMatrix().at<double>(2,2)) < 9999)
|
||||
@@ -629,7 +735,7 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
||||
gtsam::Point3 landmark(t.x(), t.y(), t.z());
|
||||
gtsam::Pose3 p;
|
||||
graph.add(gtsam::BearingRangeFactor<gtsam::Pose3, gtsam::Point3>(id1, id2, p.bearing(landmark), p.range(landmark), model));
|
||||
lastAddedConstraints_.push_back(ConstraintToFactor(id1, id2, -1));
|
||||
addedConstraints.push_back(ConstraintToFactor(id1, id2, (int)iter->second.type()));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -645,7 +751,7 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
||||
gtsam::Point3 landmark(t.x(), t.y(), t.z());
|
||||
gtsam::Pose3 p;
|
||||
graph.add(gtsam::BearingFactor<gtsam::Pose3, gtsam::Point3>(id1, id2, p.bearing(landmark), model));
|
||||
lastAddedConstraints_.push_back(ConstraintToFactor(id1, id2, -1));
|
||||
addedConstraints.push_back(ConstraintToFactor(id1, id2, (int)iter->second.type()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -703,7 +809,7 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
||||
#endif
|
||||
{
|
||||
graph.add(gtsam::BetweenFactor<gtsam::Pose2>(id1, id2, gtsam::Pose2(iter->second.transform().x(), iter->second.transform().y(), iter->second.transform().theta()), model));
|
||||
lastAddedConstraints_.push_back(ConstraintToFactor(id1, id2, -1));
|
||||
addedConstraints.push_back(ConstraintToFactor(id1, id2, (int)iter->second.type()));
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -733,7 +839,7 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
||||
#endif
|
||||
{
|
||||
graph.add(gtsam::BetweenFactor<gtsam::Pose3>(id1, id2, gtsam::Pose3(iter->second.transform().toEigen4d()), model));
|
||||
lastAddedConstraints_.push_back(ConstraintToFactor(id1, id2, -1));
|
||||
addedConstraints.push_back(ConstraintToFactor(id1, id2, (int)iter->second.type()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -783,7 +889,7 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
||||
{
|
||||
float x,y,z,roll,pitch,yaw;
|
||||
std::map<int, Transform> tmpPoses;
|
||||
const Values values = isam2_?isam2_->calculateEstimate():optimizer->values();
|
||||
const gtsam::Values values = isam2_?isam2_->calculateEstimate():optimizer->values();
|
||||
#if GTSAM_VERSION_NUMERIC >= 40200
|
||||
for(gtsam::Values::deref_iterator iter=values.begin(); iter!=values.end(); ++iter)
|
||||
#else
|
||||
@@ -800,9 +906,9 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
||||
gtsam::Pose2 p = iter->value.cast<gtsam::Pose2>();
|
||||
tmpPoses.insert(std::make_pair(key, Transform(p.x(), p.y(), p.theta())));
|
||||
}
|
||||
else if(!landmarksIgnored() && isLandmarkWithRotation.find(key)!=isLandmarkWithRotation.end())
|
||||
else if(!landmarksIgnored() && isLandmarkWithRotation_.find(key)!=isLandmarkWithRotation_.end())
|
||||
{
|
||||
if(isLandmarkWithRotation.at(key))
|
||||
if(isLandmarkWithRotation_.at(key))
|
||||
{
|
||||
newPoses.at(key).getTranslationAndEulerAngles(x,y,z,roll,pitch,yaw);
|
||||
gtsam::Pose2 p = iter->value.cast<gtsam::Pose2>();
|
||||
@@ -823,9 +929,9 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
||||
gtsam::Pose3 p = iter->value.cast<gtsam::Pose3>();
|
||||
tmpPoses.insert(std::make_pair(key, Transform::fromEigen4d(p.matrix())));
|
||||
}
|
||||
else if(!landmarksIgnored() && isLandmarkWithRotation.find(key)!=isLandmarkWithRotation.end())
|
||||
else if(!landmarksIgnored() && isLandmarkWithRotation_.find(key)!=isLandmarkWithRotation_.end())
|
||||
{
|
||||
if(isLandmarkWithRotation.at(key))
|
||||
if(isLandmarkWithRotation_.at(key))
|
||||
{
|
||||
gtsam::Pose3 p = iter->value.cast<gtsam::Pose3>();
|
||||
tmpPoses.insert(std::make_pair(key, Transform::fromEigen4d(p.matrix())));
|
||||
@@ -856,6 +962,7 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
||||
{
|
||||
UDEBUG("Update iSAM with the new factors");
|
||||
result = isam2_->update(graph, initialEstimate, removeFactorIndices);
|
||||
|
||||
#if BOOST_VERSION >= 106800
|
||||
UASSERT(result.errorBefore.has_value());
|
||||
UASSERT(result.errorAfter.has_value());
|
||||
@@ -868,12 +975,26 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
||||
error = result.errorAfter.value();
|
||||
if(!this->isRobust())
|
||||
{
|
||||
UASSERT_MSG(lastAddedConstraints_.size() == result.newFactorsIndices.size(),
|
||||
uFormat("%ld versus %ld", lastAddedConstraints_.size(), result.newFactorsIndices.size()).c_str());
|
||||
UASSERT_MSG(addedConstraints.size() == result.newFactorsIndices.size(),
|
||||
uFormat("%ld versus %ld", addedConstraints.size(), result.newFactorsIndices.size()).c_str());
|
||||
for(size_t j=0; j<result.newFactorsIndices.size(); ++j)
|
||||
{
|
||||
UDEBUG("New factor indice: %ld", result.newFactorsIndices[j]);
|
||||
lastAddedConstraints_[j].factorIndice = result.newFactorsIndices[j];
|
||||
// Persist all input-derived factors (including
|
||||
// self-ref priors / gravity) so later calls can
|
||||
// remove them when dropped from input or skip
|
||||
// re-adding when still present. type == -1 marks
|
||||
// the synthetic root prior, which is managed
|
||||
// separately via lastRootFactorIndex_ and must
|
||||
// stay out of trackedFactors_.
|
||||
if(addedConstraints[j].type != -1)
|
||||
{
|
||||
trackedFactors_[linkKey(
|
||||
addedConstraints[j].from,
|
||||
addedConstraints[j].to,
|
||||
addedConstraints[j].type)] =
|
||||
result.newFactorsIndices[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
if(rootId != 0 && lastRootFactorIndex_.first == 0)
|
||||
@@ -914,7 +1035,7 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
||||
delete isam2_;
|
||||
isam2_ = new gtsam::ISAM2(params);
|
||||
addedPoses_.clear();
|
||||
lastAddedConstraints_.clear();
|
||||
trackedFactors_.clear();
|
||||
lastRootFactorIndex_.first = 0;
|
||||
lastSwitchId_ = 1000000000;
|
||||
}
|
||||
@@ -993,9 +1114,9 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
||||
gtsam::Pose2 p = iter->value.cast<gtsam::Pose2>();
|
||||
optimizedPoses.insert(std::make_pair(key, Transform(p.x(), p.y(), z, roll, pitch, p.theta())));
|
||||
}
|
||||
else if(!landmarksIgnored() && isLandmarkWithRotation.find(key)!=isLandmarkWithRotation.end())
|
||||
else if(!landmarksIgnored() && isLandmarkWithRotation_.find(key)!=isLandmarkWithRotation_.end())
|
||||
{
|
||||
if(isLandmarkWithRotation.at(key))
|
||||
if(isLandmarkWithRotation_.at(key))
|
||||
{
|
||||
poses.at(key).getTranslationAndEulerAngles(x,y,z,roll,pitch,yaw);
|
||||
gtsam::Pose2 p = iter->value.cast<gtsam::Pose2>();
|
||||
@@ -1016,9 +1137,9 @@ std::map<int, Transform> OptimizerGTSAM::optimize(
|
||||
gtsam::Pose3 p = iter->value.cast<gtsam::Pose3>();
|
||||
optimizedPoses.insert(std::make_pair(key, Transform::fromEigen4d(p.matrix())));
|
||||
}
|
||||
else if(!landmarksIgnored() && isLandmarkWithRotation.find(key)!=isLandmarkWithRotation.end())
|
||||
else if(!landmarksIgnored() && isLandmarkWithRotation_.find(key)!=isLandmarkWithRotation_.end())
|
||||
{
|
||||
if(isLandmarkWithRotation.at(key))
|
||||
if(isLandmarkWithRotation_.at(key))
|
||||
{
|
||||
gtsam::Pose3 p = iter->value.cast<gtsam::Pose3>();
|
||||
optimizedPoses.insert(std::make_pair(key, Transform::fromEigen4d(p.matrix())));
|
||||
|
||||
@@ -543,7 +543,7 @@ bool OptimizerTORO::loadGraph(
|
||||
}
|
||||
else
|
||||
{
|
||||
UFATAL("Referred poses from the link not exist!");
|
||||
UERROR("Referred poses from the link (%d->%d) don't exist! Link ignored!", idFrom, idTo);
|
||||
}
|
||||
}
|
||||
else if(strList.size())
|
||||
|
||||
@@ -77,7 +77,7 @@ class AngleManifold {
|
||||
|
||||
#else
|
||||
|
||||
class AngleManfold {
|
||||
class AngleManifold {
|
||||
public:
|
||||
|
||||
template <typename T>
|
||||
@@ -90,7 +90,7 @@ class AngleManfold {
|
||||
}
|
||||
|
||||
static ceres::LocalParameterization* Create() {
|
||||
return (new ceres::AutoDiffLocalParameterization<AngleManfold, 1, 1>);
|
||||
return (new ceres::AutoDiffLocalParameterization<AngleManifold, 1, 1>);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -32,18 +32,23 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#ifndef RTAB_G2O_EDGE_SBACAM_GRAVITY_H_
|
||||
#define RTAB_G2O_EDGE_SBACAM_GRAVITY_H_
|
||||
|
||||
#ifdef RTABMAP_ORB_SLAM
|
||||
#include "g2o/types/types_six_dof_expmap.h"
|
||||
#else
|
||||
#include "g2o/types/sba/types_sba.h"
|
||||
#endif
|
||||
#include "g2o/core/base_unary_edge.h"
|
||||
namespace rtabmap {
|
||||
/**
|
||||
* \brief EdgeSBACamGravity
|
||||
* \brief g2o edge with gravity constraint
|
||||
*/
|
||||
class EdgeSBACamGravity : public g2o::BaseUnaryEdge<3, Eigen::Matrix<double, 6, 1>, g2o::VertexCam> {
|
||||
class EdgeSBACamGravity : public g2o::BaseUnaryEdge<3, Eigen::Matrix<double, 6, 1>, VertexCam> {
|
||||
public:
|
||||
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
|
||||
EdgeSBACamGravity(){
|
||||
information().setIdentity();
|
||||
cameraInvLocalTransform_.setIdentity();
|
||||
}
|
||||
virtual bool read(std::istream& is) {return false;} // not implemented
|
||||
virtual bool write(std::ostream& os) const {return false;} // not implemented
|
||||
@@ -55,30 +60,36 @@ class EdgeSBACamGravity : public g2o::BaseUnaryEdge<3, Eigen::Matrix<double, 6,
|
||||
|
||||
// return the error estimate as a 3-vector
|
||||
void computeError(){
|
||||
const g2o::VertexCam* v1 = static_cast<const g2o::VertexCam*>(_vertices[0]);
|
||||
const VertexCam* v = static_cast<const VertexCam*>(_vertices[0]);
|
||||
|
||||
Eigen::Vector3d direction = _measurement.head<3>();
|
||||
Eigen::Vector3d measurement = _measurement.tail<3>();
|
||||
Eigen::Vector3d direction = _measurement.head<3>();
|
||||
Eigen::Vector3d measurement = _measurement.tail<3>();
|
||||
|
||||
Eigen::Vector3d ea;
|
||||
g2o::SE3Quat estimate;
|
||||
#ifdef RTABMAP_ORB_SLAM
|
||||
estimate = v->estimate().inverse();
|
||||
#else
|
||||
estimate = v->estimate();
|
||||
#endif
|
||||
|
||||
// Transform pose from camera frame to world frame
|
||||
Eigen::Matrix3d t = v1->estimate().rotation().toRotationMatrix() * cameraInvLocalTransform_;
|
||||
ea[0] = atan2(t (2, 1), t (2, 2));
|
||||
ea[1] = asin(-t (2, 0));
|
||||
ea[2] = atan2(t (1, 0), t (0, 0));
|
||||
// Transform pose from camera frame to world frame
|
||||
Eigen::Matrix3d t = estimate.rotation().toRotationMatrix() * cameraInvLocalTransform_;
|
||||
Eigen::Vector3d ea;
|
||||
ea[0] = atan2(t (2, 1), t (2, 2));
|
||||
ea[1] = asin(-t (2, 0));
|
||||
ea[2] = atan2(t (1, 0), t (0, 0));
|
||||
|
||||
Eigen::Matrix3d rot =
|
||||
(Eigen::AngleAxisd(ea[1], Eigen::Vector3d::UnitY()) *
|
||||
Eigen::AngleAxisd(ea[0], Eigen::Vector3d::UnitX())).toRotationMatrix();
|
||||
Eigen::Matrix3d rot =
|
||||
(Eigen::AngleAxisd(ea[1], Eigen::Vector3d::UnitY()) *
|
||||
Eigen::AngleAxisd(ea[0], Eigen::Vector3d::UnitX())).toRotationMatrix();
|
||||
|
||||
Eigen::Vector3d estimate = rot * -direction;
|
||||
_error = estimate - measurement;
|
||||
Eigen::Vector3d newEstimate = rot * -direction;
|
||||
_error = newEstimate - measurement;
|
||||
|
||||
/*printf("%d : measured=%f %f %f est=%f %f %f error=%f %f %f\n", v1->id(),
|
||||
measurement[0], measurement[1], measurement[2],
|
||||
estimate[0], estimate[1], estimate[2],
|
||||
_error[0], _error[1], _error[2]);*/
|
||||
/*printf("%d : measured=%f %f %f est=%f %f %f error=%f %f %f\n", v1->id(),
|
||||
measurement[0], measurement[1], measurement[2],
|
||||
estimate[0], estimate[1], estimate[2],
|
||||
_error[0], _error[1], _error[2]);*/
|
||||
}
|
||||
|
||||
// 6 values:
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
Copyright (c) 2010-2019, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the Universite de Sherbrooke nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Adapted from EdgeSE3Prior
|
||||
*/
|
||||
|
||||
#ifndef RTAB_G2O_EDGE_SBACAM_PRIOR_H_
|
||||
#define RTAB_G2O_EDGE_SBACAM_PRIOR_H_
|
||||
|
||||
#ifdef RTABMAP_ORB_SLAM
|
||||
#include "g2o/types/types_six_dof_expmap.h"
|
||||
#else
|
||||
#include "g2o/types/sba/types_sba.h"
|
||||
#endif
|
||||
#include "g2o/core/base_unary_edge.h"
|
||||
namespace rtabmap {
|
||||
/**
|
||||
* \brief EdgeSBACamPrior
|
||||
* \brief g2o edge with gravity constraint
|
||||
*/
|
||||
class EdgeSBACamPrior : public g2o::BaseUnaryEdge<6, g2o::SE3Quat, VertexCam> {
|
||||
public:
|
||||
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
|
||||
EdgeSBACamPrior() {
|
||||
setMeasurement(g2o::SE3Quat());
|
||||
information().setIdentity();
|
||||
}
|
||||
|
||||
void setCameraInvLocalTransform(const g2o::SE3Quat & t)
|
||||
{
|
||||
_cameraInvLocalTransform = t;
|
||||
}
|
||||
|
||||
// return the error estimate as a 3-vector
|
||||
void computeError() {
|
||||
const VertexCam* v = static_cast<const VertexCam*>(_vertices[0]);
|
||||
g2o::SE3Quat estimate;
|
||||
#ifdef RTABMAP_ORB_SLAM
|
||||
estimate = v->estimate().inverse();
|
||||
#else
|
||||
estimate = v->estimate();
|
||||
#endif
|
||||
g2o::SE3Quat delta = _inverseMeasurement * estimate * _cameraInvLocalTransform;
|
||||
_error[0]=delta.translation().x();
|
||||
_error[1]=delta.translation().y();
|
||||
_error[2]=delta.translation().z();
|
||||
_error[3]=delta.rotation().x();
|
||||
_error[4]=delta.rotation().y();
|
||||
_error[5]=delta.rotation().z();
|
||||
}
|
||||
|
||||
// jacobian
|
||||
virtual void linearizeOplus() {
|
||||
_jacobianOplusXi = Eigen::Matrix<double, 6, 6>::Identity();
|
||||
}
|
||||
|
||||
virtual void setMeasurement(const g2o::SE3Quat& m){
|
||||
_measurement = m;
|
||||
_inverseMeasurement = m.inverse();
|
||||
}
|
||||
|
||||
virtual bool setMeasurementData(const double* d) override {
|
||||
Eigen::Map<const Eigen::Matrix<double, 7, 1, Eigen::ColMajor> > v(d);
|
||||
// SE3Quat expects [x, y, z, qx, qy, qz, qw]
|
||||
_measurement.fromVector(v);
|
||||
_inverseMeasurement = _measurement.inverse();
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool getMeasurementData(double* d) const override {
|
||||
Eigen::Map<Eigen::Matrix<double, 7, 1, Eigen::ColMajor> > v(d);
|
||||
// Returns [x, y, z, qx, qy, qz, qw]
|
||||
v = _measurement.toVector();
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual int measurementDimension() const {return 7;}
|
||||
|
||||
virtual double initialEstimatePossible(const g2o::OptimizableGraph::VertexSet& /*from*/,
|
||||
g2o::OptimizableGraph::Vertex* /*to*/) {
|
||||
return 1.;
|
||||
}
|
||||
|
||||
virtual void initialEstimate(const g2o::OptimizableGraph::VertexSet& from, g2o::OptimizableGraph::Vertex* to) {
|
||||
VertexCam *v = static_cast<VertexCam*>(_vertices[0]);
|
||||
assert(v && "Vertex for the Prior edge is not set");
|
||||
|
||||
#ifdef RTABMAP_ORB_SLAM
|
||||
g2o::SE3Quat newEstimate = _cameraInvLocalTransform * _inverseMeasurement;
|
||||
#else
|
||||
g2o::SE3Quat newEstimate = measurement()*_cameraInvLocalTransform.inverse();
|
||||
#endif
|
||||
if (_information.block<3,3>(0,0).array().abs().sum() == 0){ // do not set translation, as that part of the information is all zero
|
||||
newEstimate.setTranslation(v->estimate().translation());
|
||||
}
|
||||
if (_information.block<3,3>(3,3).array().abs().sum() == 0){ // do not set rotation, as that part of the information is all zero
|
||||
newEstimate.setRotation(v->estimate().rotation());
|
||||
}
|
||||
v->setEstimate(newEstimate);
|
||||
}
|
||||
|
||||
virtual bool read(std::istream& is) override { return true; }
|
||||
virtual bool write(std::ostream& os) const override { return true; }
|
||||
protected:
|
||||
g2o::SE3Quat _inverseMeasurement;
|
||||
g2o::SE3Quat _cameraInvLocalTransform;
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,74 @@
|
||||
#include "g2o/types/types_six_dof_expmap.h"
|
||||
|
||||
/**
|
||||
* \brief 3D edge between two SBAcam
|
||||
*/
|
||||
class EdgeSE3Expmap : public g2o::BaseBinaryEdge<6, g2o::SE3Quat, g2o::VertexSE3Expmap, g2o::VertexSE3Expmap>
|
||||
{
|
||||
public:
|
||||
EIGEN_MAKE_ALIGNED_OPERATOR_NEW;
|
||||
EdgeSE3Expmap(): BaseBinaryEdge<6, g2o::SE3Quat, g2o::VertexSE3Expmap, g2o::VertexSE3Expmap>(){}
|
||||
bool read(std::istream& is)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool write(std::ostream& os) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void computeError()
|
||||
{
|
||||
const g2o::VertexSE3Expmap* v1 = dynamic_cast<const g2o::VertexSE3Expmap*>(_vertices[0]);
|
||||
const g2o::VertexSE3Expmap* v2 = dynamic_cast<const g2o::VertexSE3Expmap*>(_vertices[1]);
|
||||
g2o::SE3Quat delta = _inverseMeasurement * (v1->estimate().inverse()*v2->estimate());
|
||||
_error[0]=delta.translation().x();
|
||||
_error[1]=delta.translation().y();
|
||||
_error[2]=delta.translation().z();
|
||||
_error[3]=delta.rotation().x();
|
||||
_error[4]=delta.rotation().y();
|
||||
_error[5]=delta.rotation().z();
|
||||
}
|
||||
|
||||
virtual void setMeasurement(const g2o::SE3Quat& meas){
|
||||
_measurement=meas;
|
||||
_inverseMeasurement=meas.inverse();
|
||||
}
|
||||
|
||||
virtual double initialEstimatePossible(const g2o::OptimizableGraph::VertexSet& , g2o::OptimizableGraph::Vertex* ) { return 1.;}
|
||||
virtual void initialEstimate(const g2o::OptimizableGraph::VertexSet& from_, g2o::OptimizableGraph::Vertex* ){
|
||||
g2o::VertexSE3Expmap* from = static_cast<g2o::VertexSE3Expmap*>(_vertices[0]);
|
||||
g2o::VertexSE3Expmap* to = static_cast<g2o::VertexSE3Expmap*>(_vertices[1]);
|
||||
if (from_.count(from) > 0)
|
||||
to->setEstimate((g2o::SE3Quat) from->estimate() * _measurement);
|
||||
else
|
||||
from->setEstimate((g2o::SE3Quat) to->estimate() * _inverseMeasurement);
|
||||
}
|
||||
|
||||
virtual bool setMeasurementData(const double* d){
|
||||
Eigen::Map<const g2o::Vector7d> v(d);
|
||||
_measurement.fromVector(v);
|
||||
_inverseMeasurement = _measurement.inverse();
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool getMeasurementData(double* d) const{
|
||||
Eigen::Map<g2o::Vector7d> v(d);
|
||||
v = _measurement.toVector();
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual int measurementDimension() const {return 7;}
|
||||
|
||||
virtual bool setMeasurementFromState() {
|
||||
const g2o::VertexSE3Expmap* v1 = dynamic_cast<const g2o::VertexSE3Expmap*>(_vertices[0]);
|
||||
const g2o::VertexSE3Expmap* v2 = dynamic_cast<const g2o::VertexSE3Expmap*>(_vertices[1]);
|
||||
_measurement = (v1->estimate().inverse()*v2->estimate());
|
||||
_inverseMeasurement = _measurement.inverse();
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
g2o::SE3Quat _inverseMeasurement;
|
||||
};
|
||||
@@ -1,91 +0,0 @@
|
||||
/* ----------------------------------------------------------------------------
|
||||
|
||||
* GTSAM Copyright 2010, Georgia Tech Research Corporation,
|
||||
* Atlanta, Georgia 30332-0415
|
||||
* All Rights Reserved
|
||||
* Authors: Frank Dellaert, et al. (see THANKS for the full author list)
|
||||
|
||||
* See LICENSE for the license information
|
||||
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Author: Mathieu Labbe
|
||||
* This file is a copy of AttitudeFactor.cpp of gtsam library but
|
||||
* with attitudeError() function overridden to ignore yaw errors.
|
||||
* For the noise model, use Sigmas(Vector2(0.1, 10)) (with second sigma high!)
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file GravityFactor.cpp
|
||||
* @author Frank Dellaert
|
||||
* @brief Implementation file for Attitude factor
|
||||
* @date January 28, 2014
|
||||
**/
|
||||
|
||||
#include "GravityFactor.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace rtabmap {
|
||||
|
||||
//***************************************************************************
|
||||
Vector GravityFactor::attitudeError(const Rot3& nRb,
|
||||
OptionalJacobian<2, 3> H) const {
|
||||
if (H) {
|
||||
Matrix23 D_nRef_R;
|
||||
Matrix22 D_e_nRef;
|
||||
Vector3 r = nRb.xyz();
|
||||
Unit3 nRef = Rot3::RzRyRx(r.x(), r.y(), 0).rotate(bRef_, D_nRef_R);
|
||||
Vector e = nZ_.error(nRef, D_e_nRef);
|
||||
(*H) = D_e_nRef * D_nRef_R;
|
||||
//printf("ref=%f %f %f grav=%f %f %f e= %f %f H=%f %f %f, %f %f %f\n",
|
||||
// nRef.point3().x(), nRef.point3().y(), nRef.point3().z(), nZ_.point3().x(), nZ_.point3().y(), nZ_.point3().z(), e(0), e(1),
|
||||
// (*H)(0,0), (*H)(0,1), (*H)(0,2), (*H)(1,0), (*H)(1,1), (*H)(1,2));
|
||||
return e;
|
||||
} else {
|
||||
Vector3 r = nRb.xyz();
|
||||
Unit3 nRef = Rot3::RzRyRx(r.x(), r.y(), 0) * bRef_;
|
||||
Vector e = nZ_.error(nRef);
|
||||
//printf("ref=%f %f %f grav=%f %f %f e= %f %f\n", nRef.point3().x(), nRef.point3().y(), nRef.point3().z(), nZ_.point3().x(), nZ_.point3().y(), nZ_.point3().z(), e(0), e(1));
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
//***************************************************************************
|
||||
void Rot3GravityFactor::print(const string& s,
|
||||
const KeyFormatter& keyFormatter) const {
|
||||
cout << s << "Rot3GravityFactor on " << keyFormatter(this->key()) << "\n";
|
||||
nZ_.print(" measured direction in nav frame: ");
|
||||
bRef_.print(" reference direction in body frame: ");
|
||||
this->noiseModel_->print(" noise model: ");
|
||||
}
|
||||
|
||||
//***************************************************************************
|
||||
bool Rot3GravityFactor::equals(const NonlinearFactor& expected,
|
||||
double tol) const {
|
||||
const This* e = dynamic_cast<const This*>(&expected);
|
||||
return e != NULL && Base::equals(*e, tol) && this->nZ_.equals(e->nZ_, tol)
|
||||
&& this->bRef_.equals(e->bRef_, tol);
|
||||
}
|
||||
|
||||
//***************************************************************************
|
||||
void Pose3GravityFactor::print(const string& s,
|
||||
const KeyFormatter& keyFormatter) const {
|
||||
cout << s << "Pose3GravityFactor on " << keyFormatter(this->key()) << "\n";
|
||||
nZ_.print(" measured direction in nav frame: ");
|
||||
bRef_.print(" reference direction in body frame: ");
|
||||
this->noiseModel_->print(" noise model: ");
|
||||
}
|
||||
|
||||
//***************************************************************************
|
||||
bool Pose3GravityFactor::equals(const NonlinearFactor& expected,
|
||||
double tol) const {
|
||||
const This* e = dynamic_cast<const This*>(&expected);
|
||||
return e != NULL && Base::equals(*e, tol) && this->nZ_.equals(e->nZ_, tol)
|
||||
&& this->bRef_.equals(e->bRef_, tol);
|
||||
}
|
||||
|
||||
//***************************************************************************
|
||||
|
||||
}/// namespace gtsam
|
||||
@@ -1,268 +0,0 @@
|
||||
/* ----------------------------------------------------------------------------
|
||||
|
||||
* GTSAM Copyright 2010, Georgia Tech Research Corporation,
|
||||
* Atlanta, Georgia 30332-0415
|
||||
* All Rights Reserved
|
||||
* Authors: Frank Dellaert, et al. (see THANKS for the full author list)
|
||||
|
||||
* See LICENSE for the license information
|
||||
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Author: Mathieu Labbe
|
||||
* This file is a copy of AttitudeFactor.h of gtsam library but
|
||||
* with attitudeError() function overridden to ignore yaw errors.
|
||||
* For the noise model, use Sigmas(Vector2(0.1, 10)) (with second sigma high!)
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file Pose3GravityFactor.h
|
||||
* @author Frank Dellaert
|
||||
* @brief Header file for Attitude factor
|
||||
* @date January 28, 2014
|
||||
**/
|
||||
#pragma once
|
||||
|
||||
#include <gtsam/nonlinear/NonlinearFactor.h>
|
||||
#include <gtsam/geometry/Pose3.h>
|
||||
#include <gtsam/geometry/Unit3.h>
|
||||
|
||||
using namespace gtsam;
|
||||
|
||||
namespace rtabmap {
|
||||
|
||||
/**
|
||||
* Base class for prior on gravity
|
||||
* Example:
|
||||
* - measurement is direction of gravity in navigation frame nG
|
||||
* - reference is direction of z axis in body frame bF
|
||||
* This factor will give zero error if nG is opposite direction of bF
|
||||
* @addtogroup Navigation
|
||||
*/
|
||||
class GravityFactor {
|
||||
|
||||
protected:
|
||||
|
||||
const Unit3 nZ_, bRef_; ///< Position measurement in
|
||||
|
||||
public:
|
||||
|
||||
/** default constructor - only use for serialization */
|
||||
GravityFactor() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Constructor
|
||||
* @param nZ measured direction in navigation frame
|
||||
* @param bRef reference direction in body frame (default Z-axis in NED frame, i.e., [0; 0; 1])
|
||||
*/
|
||||
GravityFactor(const Unit3& nZ, const Unit3& bRef = Unit3(0, 0, 1)) :
|
||||
nZ_(nZ), bRef_(bRef) {
|
||||
}
|
||||
|
||||
/** vector of errors */
|
||||
Vector attitudeError(const Rot3& p,
|
||||
#if GTSAM_VERSION_NUMERIC >= 40300
|
||||
OptionalJacobian<2,3> H = {}) const;
|
||||
#else
|
||||
OptionalJacobian<2,3> H = boost::none) const;
|
||||
#endif
|
||||
|
||||
/** Serialization function */
|
||||
#if defined(GTSAM_ENABLE_BOOST_SERIALIZATION) || GTSAM_VERSION_NUMERIC < 40300
|
||||
friend class boost::serialization::access;
|
||||
template<class ARCHIVE>
|
||||
void serialize(ARCHIVE & ar, const unsigned int /*version*/) {
|
||||
/*ar & boost::serialization::make_nvp("nZ_", const_cast<Unit3&>(nZ_));
|
||||
ar & boost::serialization::make_nvp("bRef_", const_cast<Unit3&>(bRef_));*/
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* Version of GravityFactor for Rot3
|
||||
* @addtogroup Navigation
|
||||
*/
|
||||
class Rot3GravityFactor: public NoiseModelFactor1<Rot3>, public GravityFactor {
|
||||
|
||||
typedef NoiseModelFactor1<Rot3> Base;
|
||||
|
||||
public:
|
||||
|
||||
/// shorthand for a smart pointer to a factor
|
||||
#if GTSAM_VERSION_NUMERIC >= 40300
|
||||
typedef std::shared_ptr<Rot3GravityFactor> shared_ptr;
|
||||
#else
|
||||
typedef boost::shared_ptr<Rot3GravityFactor> shared_ptr;
|
||||
#endif
|
||||
|
||||
/// Typedef to this class
|
||||
typedef Rot3GravityFactor This;
|
||||
|
||||
/** default constructor - only use for serialization */
|
||||
Rot3GravityFactor() {
|
||||
}
|
||||
|
||||
virtual ~Rot3GravityFactor() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Constructor
|
||||
* @param key of the Rot3 variable that will be constrained
|
||||
* @param nZ measured direction in navigation frame (remove yaw before rotating the gravity vector)
|
||||
* @param model Gaussian noise model
|
||||
* @param bRef reference direction in body frame (default Z-axis)
|
||||
*/
|
||||
Rot3GravityFactor(Key key, const Unit3& nZ, const SharedNoiseModel& model,
|
||||
const Unit3& bRef = Unit3(0, 0, 1)) :
|
||||
Base(model, key), GravityFactor(nZ, bRef) {
|
||||
}
|
||||
|
||||
/// @return a deep copy of this factor
|
||||
virtual gtsam::NonlinearFactor::shared_ptr clone() const {
|
||||
#if GTSAM_VERSION_NUMERIC >= 40300
|
||||
return std::static_pointer_cast<gtsam::NonlinearFactor>(
|
||||
#else
|
||||
return boost::static_pointer_cast<gtsam::NonlinearFactor>(
|
||||
#endif
|
||||
gtsam::NonlinearFactor::shared_ptr(new This(*this)));
|
||||
}
|
||||
|
||||
/** print */
|
||||
virtual void print(const std::string& s, const KeyFormatter& keyFormatter =
|
||||
DefaultKeyFormatter) const;
|
||||
|
||||
/** equals */
|
||||
virtual bool equals(const NonlinearFactor& expected, double tol = 1e-9) const;
|
||||
|
||||
/** vector of errors */
|
||||
virtual Vector evaluateError(const Rot3& nRb, //
|
||||
#if GTSAM_VERSION_NUMERIC >= 40300
|
||||
OptionalMatrixType H = OptionalNone) const {
|
||||
#else
|
||||
boost::optional<Matrix&> H = boost::none) const {
|
||||
#endif
|
||||
return attitudeError(nRb, H);
|
||||
}
|
||||
Unit3 nZ() const {
|
||||
return nZ_;
|
||||
}
|
||||
Unit3 bRef() const {
|
||||
return bRef_;
|
||||
}
|
||||
|
||||
private:
|
||||
#if defined(GTSAM_ENABLE_BOOST_SERIALIZATION) || GTSAM_VERSION_NUMERIC < 40300
|
||||
/** Serialization function */
|
||||
friend class boost::serialization::access;
|
||||
template<class ARCHIVE>
|
||||
void serialize(ARCHIVE & ar, const unsigned int /*version*/) {
|
||||
/*ar & boost::serialization::make_nvp("NoiseModelFactor1",
|
||||
boost::serialization::base_object<Base>(*this));
|
||||
ar & boost::serialization::make_nvp("GravityFactor",
|
||||
boost::serialization::base_object<GravityFactor>(*this));*/
|
||||
}
|
||||
#endif
|
||||
|
||||
public:
|
||||
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Version of GravityFactor for Pose3
|
||||
* @addtogroup Navigation
|
||||
*/
|
||||
class Pose3GravityFactor: public NoiseModelFactor1<Pose3>,
|
||||
public GravityFactor {
|
||||
|
||||
typedef NoiseModelFactor1<Pose3> Base;
|
||||
|
||||
public:
|
||||
|
||||
/// shorthand for a smart pointer to a factor
|
||||
#if GTSAM_VERSION_NUMERIC >= 40300
|
||||
typedef std::shared_ptr<Pose3GravityFactor> shared_ptr;
|
||||
#else
|
||||
typedef boost::shared_ptr<Pose3GravityFactor> shared_ptr;
|
||||
#endif
|
||||
/// Typedef to this class
|
||||
typedef Pose3GravityFactor This;
|
||||
|
||||
/** default constructor - only use for serialization */
|
||||
Pose3GravityFactor() {
|
||||
}
|
||||
|
||||
virtual ~Pose3GravityFactor() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Constructor
|
||||
* @param key of the Pose3 variable that will be constrained
|
||||
* @param nZ measured direction in navigation frame (remove yaw before rotating the gravity vector)
|
||||
* @param model Gaussian noise model
|
||||
* @param bRef reference direction in body frame (default Z-axis)
|
||||
*/
|
||||
Pose3GravityFactor(Key key, const Unit3& nZ, const SharedNoiseModel& model,
|
||||
const Unit3& bRef = Unit3(0, 0, 1)) :
|
||||
Base(model, key), GravityFactor(nZ, bRef) {
|
||||
}
|
||||
|
||||
/// @return a deep copy of this factor
|
||||
virtual gtsam::NonlinearFactor::shared_ptr clone() const {
|
||||
#if GTSAM_VERSION_NUMERIC >= 40300
|
||||
return std::static_pointer_cast<gtsam::NonlinearFactor>(
|
||||
#else
|
||||
return boost::static_pointer_cast<gtsam::NonlinearFactor>(
|
||||
#endif
|
||||
gtsam::NonlinearFactor::shared_ptr(new This(*this)));
|
||||
}
|
||||
|
||||
/** print */
|
||||
virtual void print(const std::string& s, const KeyFormatter& keyFormatter =
|
||||
DefaultKeyFormatter) const;
|
||||
|
||||
/** equals */
|
||||
virtual bool equals(const NonlinearFactor& expected, double tol = 1e-9) const;
|
||||
|
||||
/** vector of errors */
|
||||
virtual Vector evaluateError(const Pose3& nTb, //
|
||||
#if GTSAM_VERSION_NUMERIC >= 40300
|
||||
OptionalMatrixType H = OptionalNone) const {
|
||||
#else
|
||||
boost::optional<Matrix&> H = boost::none) const {
|
||||
#endif
|
||||
Vector e = attitudeError(nTb.rotation(), H);
|
||||
if (H) {
|
||||
Matrix H23 = *H;
|
||||
*H = Matrix::Zero(2,6);
|
||||
H->block<2,3>(0,0) = H23;
|
||||
}
|
||||
return e;
|
||||
}
|
||||
Unit3 nZ() const {
|
||||
return nZ_;
|
||||
}
|
||||
Unit3 bRef() const {
|
||||
return bRef_;
|
||||
}
|
||||
|
||||
private:
|
||||
#if defined(GTSAM_ENABLE_BOOST_SERIALIZATION) || GTSAM_VERSION_NUMERIC < 40300
|
||||
/** Serialization function */
|
||||
friend class boost::serialization::access;
|
||||
template<class ARCHIVE>
|
||||
void serialize(ARCHIVE & ar, const unsigned int /*version*/) {
|
||||
/*ar & boost::serialization::make_nvp("NoiseModelFactor1",
|
||||
boost::serialization::base_object<Base>(*this));
|
||||
ar & boost::serialization::make_nvp("GravityFactor",
|
||||
boost::serialization::base_object<GravityFactor>(*this));*/
|
||||
}
|
||||
#endif
|
||||
public:
|
||||
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
|
||||
};
|
||||
|
||||
} /// namespace gtsam
|
||||
|
||||
@@ -43,7 +43,7 @@ public:
|
||||
// @param H the optional Jacobian matrix, which use boost optional and has default null pointer
|
||||
gtsam::Vector evaluateError(const VALUE& p,
|
||||
#if GTSAM_VERSION_NUMERIC >= 40300
|
||||
OptionalMatrixType H = OptionalNone) const {
|
||||
gtsam::OptionalMatrixType H = OptionalNone) const {
|
||||
#else
|
||||
boost::optional<gtsam::Matrix&> H = boost::none) const {
|
||||
#endif
|
||||
@@ -59,5 +59,5 @@ public:
|
||||
|
||||
};
|
||||
|
||||
} // namespace gtsamexamples
|
||||
} // namespace rtabmap
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user