LOADING...

加载过慢请开启缓存(浏览器默认开启)

loading

Bonbon

Go farther and see the brighter light

ros-calibration-results

2023/2/3

ROS标定结果

今天拿到一个数据集,双目是采用ROS标定的,

记录一下标定结果各个参数的意义

官方链接:http://wiki.ros.org/camera_calibration

双目标定官方链接:http://wiki.ros.org/camera_calibration/Tutorials/StereoCalibration

结果解释:https://docs.opencv.org/2.4/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html


只需要看最上面即可,下面是对各个参数解释

D表示distortion,畸变参数,D=(k1,k2,p1,p2[,k3[,k4,k5,k6]])D =(k_1,k_2,p_1,p_2[,k_3[,k_4,k_5,k_6]])

K表示camera matrix,相机内参,K=[fx0cx0fycy001]K=\begin{bmatrix}f_x & 0 & c_x \\0 & f_y & c_y \\ 0 & 0 & 1\end{bmatrix}

R表示rectification,矫正

P表示projection,投影

self.T表示外参平移

self.R表示外参旋转

阅读全文

linux-alias

2023/2/3

linux-alias

给已有命令起别名

格式:alias [name[=value]]

alias myls=ls # 起别名
unalias myls # 取消

自定义简单命令

常规创建文件夹build并进入的命令为

mkdir build ; cd build

我们可以自定义命令mkcd,让他实现上述功能

alias mkcd=' __mkcd(){ mkdir $1; cd $1; }; __mkcd'

为了每次启动命令行都能使用,把这句命令写进~/.bashrc


自定义复杂命令

直接将复杂命令写成mycommand.sh脚本或mycommand.py脚本

然后指定命令即可

alias mycommand='sh mycommand.sh'
alias mycommand='python3 mycommand.py'
阅读全文

Mat-vector

2022/12/29

Mat-vector

今天看ORB-SLAM3的代码中发现存储Mat的vector中存在一些坑/妙用

简单来讲就是以下这段代码,如果外部改变image,vector里的Mat也会被相应地改变

std::vector<cv::Mat> image_list;
cv::Mat image = cv::imread("../test.png");
image_list.push_back(image);

如果你不知道这个是浅拷贝,然后外部又不小心改变了image,那就是个坑

但是如果已经知道了这个规则,就可以直接操作外部变量进而来修改vector中的元素

特别是vector里的Mat只是外部image的一部分时,会更方便简单


平常使用为了保险起见,放入vector前clone一下就好

image_list.push_back(image.clone());
阅读全文

ORB-SLAM3

2022/12/11

ORBSLAM3

官方链接:https://github.com/UZ-SLAMLab/ORB_SLAM3

安装依赖

C++11 Compiler

需要用到C++11的特性

sudo apt install gcc g++ build-essential cmake

Pangolin

官方链接:https://github.com/stevenlovegrove/Pangolin

选择安装0.6版本,是怕新版本cmake版本不够

git clone -b v0.6 --depth=1 https://github.com/stevenlovegrove/Pangolin.git

旧版本没有scripts安装依赖,根据0.6版本的README安装依赖

# openGL
sudo apt install libgl1-mesa-dev
# Glew
sudo apt install libglew-dev
# Cmake
sudo apt install cmake
# python
sudo apt install libpython2.7-dev
# Wayland
sudo apt install pkg-config
# FFMPEG
sudo apt install ffmpeg libavcodec-dev libavutil-dev libavformat-dev libswscale-dev libavdevice-dev
# DC1394
sudo apt install libdc1394-22-dev libraw1394-dev
# libjpeg, libpng, libtiff, libopenexr
sudo apt install libjpeg-dev libpng-dev libtiff5-dev libopenexr-dev

编译测试

cd Pangolin/
mkdir build && cd build
cmake ..
cmake --build . # make应该也可以
sudo make install
./examples/HelloPangolin/HelloPangolin # 测试

OpenCV

官方链接:https://opencv.org,我安装的是3.4.0版本

git clone -b 3.4.0 --depth=1 https://github.com/opencv/opencv.git opencv-3.4.0
cd opencv-3.4.0/
mkdir build && cd build
cmake -D CMAKE_BUILD_TYPE=Release ..
make
sudo make install

# 测试
cd ../samples/cpp/example_cmake/
mkdir build && cd build
cmake .. && make
./opencv_example
cd .. && rm -r build # 测试完删掉

Eigen3

官方链接:https://eigen.tuxfamily.org/index.php?title=Main_Page

安装

sudo apt install libeigen3-dev 

检查版本,保证在3.1.0之前

whereis eigen3
cat /usr/include/eigen3/Eigen/src/Core/util/Macros.h | head -n 20

编译

最新版本要opencv大于4.4.0,编译0.4版本

git clone -b v0.4-beta --depth=1 https://github.com/UZ-SLAMLab/ORB_SLAM3.git
cd ORB_SLAM3

可以直接通过build.sh编译,这个文件实现了DBoW2、g2o的编译,字典的解压,以及ORB-SLAM3的编译

为了避免中途报错不好定位问题,我打算一步一步执行

编译DBoW2

cd Thirdparty/DBoW2
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
make
cd ../../../

编译g2o

cd Thirdparty/g2o
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
make
cd ../../../

解压字典

cd Vocabulary
tar -xf ORBvoc.txt.tar.gz
cd ..

编译ORB-SLAM3

mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
make

报错解决

报错:recipe for target ‘CMakeFiles/ORB_SLAM3.dir/src/LocalMapping.cc.o’ failed

ORB_SLAM3/include/CameraModels/KannalaBrandt8.h 添加以下代码

namespace cv 
{ 
	template<typename _Tp, int m, int n> static inline Matx<_Tp, m, n> operator / (const Matx<_Tp, m, n>& a, float alpha) 
	{
		return Matx<_Tp, m, n>(a, 1.f / alpha, Matx_ScaleOp()); 
	} 
}

测试

数据集链接:https://projects.asl.ethz.ch/datasets/doku.php?id=kmavvisualinertialdatasets

以MH_01_easy为例

cd ORB_SLAM3
mkdir dataset && cd dataset
wget http://robotics.ethz.ch/~asl-datasets/ijrr_euroc_mav_dataset/machine_hall/MH_01_easy/MH_01_easy.zip
unzip MH_01_easy.zip -d ./MH01 # 这里MH01是脚本里的默认名
cd ../Examples
gedit ./euroc_examples.sh # 修改数据集路径为 ../dataset/ 后保存
./euroc_examples.sh # 运行 画面关闭ctrl+c关闭就好了

评估

ORB-SLAM3提供了多种视觉SLAM方案

单目:ORB_SLAM3/Examples/Monocular

单目+IMU:ORB_SLAM3/Examples/Monocular-Inertial

双目:ORB_SLAM3/Examples/Stereo

双目+IMU:ORB_SLAM3/Examples/Stereo-Inertial


以单目+IMU为例,

代码在ORB_SLAM3/Examples/Monocular-Inertial/mono_inertial_euroc.cc

看得到运行命令为:

./mono_inertial_euroc \
path_to_vocabulary \
path_to_settings \
path_to_sequence_folder_1 path_to_times_file_1 \
(path_to_image_folder_2 path_to_times_file_2 ... path_to_image_folder_N path_to_times_file_N) 

运行

cd ORB_SLAM3

./Examples/Monocular-Inertial/mono_inertial_euroc \
./Vocabulary/ORBvoc.txt \
./Examples/Monocular-Inertial/EuRoC.yaml \
./dataset/MH01 \
./Examples/Monocular-Inertial/EuRoC_TimeStamps/MH01.txt 

轨迹文件保存在ORB_SLAM3/CameraTrajectory.txt

关键帧轨迹文件保存在ORB_SLAM3/KeyFrameTrajectory.txt

用evo转换GT文件为tum文件,然后写python脚本统一时间戳单位后就可以评估了

阅读全文

2d_list_transpose_in_python

2022/12/9

python二维列表转置

有时候一些空间点数据,我会采用python做可视化

但是一般保存到txt后都是一行一个xyz,但plot却需要xyz三个列表

一般我都定义三个列表,然后分别储存,今天突然想到其实可以直接转置

用numpy又感觉大材小用了,所以尝试用二维列表实现

import matplotlib.pyplot as plt

with open(r"./1.txt","r") as f:
    datas = f.readlines()

# xyz_list: [[x1,y1,z1],[x2,y2,z2,],...]
xyz_list = []
for d in datas:
    xyz_list.append(list(map(float,d.strip().split(" "))))

# xyz [[x1,x2,...],[y1,y2,...],[z1,z2,...]]
xyz = list(map(list,zip(*xyz_list)))

ax = plt.axes(projection='3d')  # 设置三维轴
ax.scatter3D(*xyz)  # 三个数组对应三个维度(三个数组中的数一一对应)
plt.show()
阅读全文

pack_dataset_to_rosbag

2022/12/9

打包数据集成rosbag

资料参考

代码参考kalibr:https://github.com/ethz-asl/kalibr/blob/master/aslam_offline_calibration/kalibr/python/kalibr_bagcreater

数据类型Image:https://docs.ros.org/en/melodic/api/sensor_msgs/html/msg/Image.html

数据类型Imu:http://docs.ros.org/en/melodic/api/sensor_msgs/html/msg/Imu.html


过程

创建功能包,用的ros的opencv

cd ~/catkin_ws/src
catkin_create_pkg pack_datasets std_msgs sensor_msgs OpenCV rosbag rospy roscpp
cd ..
catkin_make

编写代码

#include <ros/ros.h>
#include <rosbag/bag.h>
#include <sensor_msgs/Image.h>
#include <sensor_msgs/Imu.h>
#include <opencv2/opencv.hpp>
#include <dirent.h>
#include <fstream>

std::string remove_suffix(std::string name)
{
    return (name.substr(0, name.rfind(".")));
}

bool get_dir_sort_names(std::string dir, std::vector<std::string> &result)
{
    // C++17可以用filesystem
    DIR *dir_ptr = opendir(dir.c_str());
    if (dir_ptr == NULL)
        return false;

    struct dirent *dirp;
    std::vector<std::string> names;
    while (((dirp = readdir(dir_ptr)) != NULL))
    {
        if (dirp->d_name[0] == '.')
            continue; // 剔除掉. 和 ..

        names.push_back(dirp->d_name);
    }

    std::sort(names.begin(), names.end(), [](std::string a, std::string b)
              { return (std::stod(remove_suffix(a)) < std::stod(remove_suffix(b))); }); // 升序排序

    result.assign(names.begin(), names.end());

    return true;
}

bool write_images_to_bag_topic(rosbag::Bag &bag, std::string dir, std::vector<std::string> names, std::string frame_id, std::string topic_name)
{
    std::cout << "Write image data..." << std::endl;
    for (auto name : names)
    {
        std::string full_path = dir + name;
        cv::Mat image = cv::imread(full_path);
        if(image.empty())
            return false;

        // 分离秒和纳秒 获得时间戳
        size_t dot_index = name.find(".");
        std::string time_sec_string = name.substr(0, dot_index);
        std::string time_nsec_string = name.substr(dot_index + 1, name.size());
        ros::Time timestamp(std::stoi(time_sec_string), std::stoi(time_nsec_string)); // s ns

        sensor_msgs::Image image_msg;
        image_msg.header.stamp = timestamp;
        image_msg.header.frame_id = frame_id;
        image_msg.height = image.rows;
        image_msg.width = image.cols;
        image_msg.step = image.step;
        image_msg.encoding = "bgr8";
        image_msg.data = (std::vector<uchar>)(image.reshape(1, 1));

        bag.write(topic_name, timestamp, image_msg);
        // ROS_INFO("Write %s to topic: %s", full_path.c_str(), topic_name.c_str());
    }
    return true;
}

bool write_imu_txt_to_bag_topic(rosbag::Bag &bag, std::string txt_path, std::string frame_id, std::string topic_name)
{
    std::cout << "Write imu data..." << std::endl;
    std::ifstream imu_txt(txt_path);
    if(!imu_txt.is_open())
        return false;

    
    while (!imu_txt.eof())
    {
        // 时间戳
        std::string time_string;
        imu_txt >> time_string;
        if (time_string.size() == 0)
            break;

        size_t dot_index = time_string.find(".");
        std::string time_sec_string = time_string.substr(0, dot_index);
        std::string time_nsec_string = time_string.substr(dot_index + 1, time_string.size());
        ros::Time timestamp(std::stoi(time_sec_string), std::stoi(time_nsec_string)); // s ns

        // 旋转四元数
        double quaternion[4]; // qx qy qz qw
        for (auto &q : quaternion)
            imu_txt >> q;
        geometry_msgs::Quaternion quaternion_msg;
        quaternion_msg.x = quaternion[0];
        quaternion_msg.y = quaternion[1];
        quaternion_msg.z = quaternion[2];
        quaternion_msg.w = quaternion[3];

        // 角速度
        double angular_velocity[3];
        for (auto &w : angular_velocity)
            imu_txt >> w;
        geometry_msgs::Vector3 angular_velocity_msg;
        angular_velocity_msg.x = angular_velocity[0];
        angular_velocity_msg.y = angular_velocity[1];
        angular_velocity_msg.z = angular_velocity[2];

        // 加速度
        double linear_acceleration[3];
        for (auto &a : linear_acceleration)
            imu_txt >> a;
        geometry_msgs::Vector3 linear_acceleration_msg;
        linear_acceleration_msg.x = linear_acceleration[0];
        linear_acceleration_msg.y = linear_acceleration[1];
        linear_acceleration_msg.z = linear_acceleration[2];

        sensor_msgs::Imu imu_msg;
        imu_msg.header.stamp = timestamp;
        imu_msg.header.frame_id = frame_id;
        imu_msg.orientation = quaternion_msg;
        imu_msg.angular_velocity = angular_velocity_msg;
        imu_msg.linear_acceleration = linear_acceleration_msg;

        bag.write(topic_name, timestamp, imu_msg);

        /*
        ROS_INFO("Write %d.%d %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf to topic: %s",
                 (int)imu_msg.header.stamp.sec, (int)imu_msg.header.stamp.nsec,
                 imu_msg.orientation.x, imu_msg.orientation.y, imu_msg.orientation.z, imu_msg.orientation.w,
                 imu_msg.angular_velocity.x, imu_msg.angular_velocity.y, imu_msg.angular_velocity.z,
                 imu_msg.linear_acceleration.x, imu_msg.linear_acceleration.y, imu_msg.linear_acceleration.z,
                 topic_name.c_str());
        */
    }

    return true;
}

int main(int argc, char **argv)
{
    /*
        rosrun pack_datasets pack_datasets \
        _save_bag_path:=SAVE_PATH \
        _cam0_images_dir:=CAM0_DIR \
        _cam0_topic_name:=CAM0_TOPIC_NAME \
        _cam1_images_dir:=CAM1_DIR \
        _cam1_topic_name:=CAM1_TOPIC_NAME \
        _imu_txt_path:=IMU_PATH \
        _imu_topic_name:=IMU_TOPIC_NAME 
    */
   
    // 初始化ROS节点
    ros::init(argc, argv, "pack_datasets");
    ros::NodeHandle nh("~");

    // rosbag
    rosbag::Bag bag;
    std::string save_bag_path;
    nh.param<std::string>("save_bag_path", save_bag_path);
    nh.getParam("save_bag_path", save_bag_path);
    if(save_bag_path.size() == 0)
    {
        ROS_ERROR("Please specify the package save path by _save_bag_path:=SAVE_PATH");
        return -1;
    }

    // cam0
    std::string cam0_images_dir;
    std::string cam0_topic_name;
    nh.param<std::string>("cam0_images_dir", cam0_images_dir);
    nh.param<std::string>("cam0_topic_name", cam0_topic_name);
    nh.getParam("cam0_images_dir", cam0_images_dir);
    nh.getParam("cam0_topic_name", cam0_topic_name);
    if((cam0_images_dir.size() == 0) ^ (cam0_topic_name.size() == 0)) 
    {
        ROS_ERROR("Please specify the image path of cam0 and the topic name to save to by _cam0_images_dir:=CAM0_DIR _cam0_topic_name:=CAM0_TOPIC_NAME");
        return -1;
    }

    // cam1
    std::string cam1_images_dir;
    std::string cam1_topic_name;
    nh.param<std::string>("cam1_images_dir", cam1_images_dir);
    nh.param<std::string>("cam1_topic_name", cam1_topic_name);
    nh.getParam("cam1_images_dir", cam1_images_dir);
    nh.getParam("cam1_topic_name", cam1_topic_name);
    if((cam1_images_dir.size() == 0) ^ (cam1_topic_name.size() == 0)) 
    {
        ROS_ERROR("Please specify the image path of cam1 and the topic name to save to by _cam1_images_dir:=CAM1_DIR _cam1_topic_name:=CAM1_TOPIC_NAME");
        return -1;
    }

    // imu
    std::string imu_txt_path;
    std::string imu_topic_name;
    nh.param<std::string>("imu_txt_path", imu_txt_path);
    nh.param<std::string>("imu_topic_name", imu_topic_name);
    nh.getParam("imu_txt_path", imu_txt_path);
    nh.getParam("imu_topic_name", imu_topic_name);
    if((imu_txt_path.size() == 0) ^ (imu_topic_name.size() == 0)) 
    {
        ROS_ERROR("Please specify the txt path of imu and the topic name to save to by _imu_txt_path:=IMU_PATH _imu_topic_name:=IMU_TOPIC_NAME");
        return -1;
    }

    bag.open(save_bag_path, rosbag::bagmode::Write);
    if((cam0_images_dir.size() != 0) && (cam0_topic_name.size() != 0))
    {
        std::vector<std::string> cam0_image_names;
        get_dir_sort_names(cam0_images_dir, cam0_image_names);
        write_images_to_bag_topic(bag, cam0_images_dir, cam0_image_names, "cam0", cam0_topic_name);
    }
    if((cam1_images_dir.size() != 0) && (cam1_topic_name.size() != 0))
    {
        std::vector<std::string> cam1_image_names;
        get_dir_sort_names(cam1_images_dir, cam1_image_names);
        write_images_to_bag_topic(bag, cam1_images_dir, cam1_image_names, "cam1", cam1_topic_name);
    }
    if((imu_txt_path.size() != 0) && (imu_topic_name.size() != 0))
    {
        write_imu_txt_to_bag_topic(bag, imu_txt_path, "imu0", imu_topic_name);
    }
    bag.close();

}

cmake添加

add_executable(pack_images_imu src/pack_images_imu.cpp)
target_link_libraries(pack_images_imu ${catkin_LIBRARIES})

编译

cd ~/catkin_ws
catkin_make

运行

rosrun pack_datasets pack_images_imu \
_save_bag_path:=./src/pack_datasets/result.bag \
_cam0_images_dir:=./src/pack_datasets/dataset/left/ \
_cam0_topic_name:=/cam0/image_raw \
_cam1_images_dir:=./src/pack_datasets/dataset/right/ \
_cam1_topic_name:=/cam1/image_raw \
_imu_txt_path:=./src/pack_datasets/dataset/imu.txt \
_imu_topic_name:=/imu0 
阅读全文

VINS-Fusion-evaluation

2022/12/9

VINS-Fusion评估

记录一下评估VINS-Fusion的过程


EVO安装

evo是一款用于视觉里程计和slam问题的轨迹评估工具。

核心功能是能够绘制相机的轨迹,或评估估计轨迹与真值的误差。

链接:https://github.com/MichaelGrupp/evo


ubuntu18.04安装:

pip3 install evo --upgrade --no-binary evo

配置VINS-Fusion输出

以单目+IMU为例,配置文件在VINS-Fusion/config/euroc/euroc_mono_imu_config.yaml

其中的output_path就是结果csv文件保存路径,pose_graph_save_path就是位姿图保存路径

(tips: ①注意改为绝对路径,不要用带~根目录,不然后续保存不成功 ②路径最后要加/ ③ 提前创建好文件夹)

修改完重新运行代码,等待运行完,在运行loop_fusion的终端,按s后回车,等待保存

需要40s左右的时间,最后有显示保存时间即为保存成功,在对应路径就可以找到输出的文件

cd ~/catkin_ws
roslaunch vins vins_rviz.launch
rosrun vins vins_node ~/catkin_ws/src/VINS-Fusion/config/euroc/euroc_mono_imu_config.yaml 
rosrun loop_fusion loop_fusion_node ~/catkin_ws/src/VINS-Fusion/config/euroc/euroc_mono_imu_config.yaml
rosbag play ~/catkin_ws/src/VINS-Fusion/datasets/MH_01_easy.bag

改变VINS-Fusion输出格式

经过上述配置,我们获得了VINS-Fusion的输出vio.csv和vio_loop.csv

但是这两个文件并没办法直接使用,因为每一行的末尾都有一个逗号

有两种办法:① 修改输出代码 ② 直接对输出文件去逗号

修改代码

代码修改会相对比较麻烦,但是方便后续拓展

①修改vio.csv的保存

文件所在路径:VINS-Fusion/vins_estimator/src/utility/visualization.cpp

定位位置:搜索foutC

保存的数据格式为:时间戳*1e9,位置xyz,旋转四元数wxyz,速度xyz

我们需要把行末的逗号去掉

②修改vio_loop.csv的保存

文件所在路径:VINS-Fusion/loop_fusion/src/pose_graph.cpp

定位位置:搜索SAVE_LOOP_PATH

保存的数据格式为:时间戳*1e9,位置xyz,旋转四元数wxyz

我们需要把行末的逗号去掉(有两段一样的要改)

③重新编译运行后保存


直接对输出文件去逗号

这个命令代表全局搜索(g)将行末的逗号(,$)替换掉并保存

sed -i 's/,$//g' vio.csv
sed -i 's/,$//g' vio_loop.csv

如果想保存在新文件可以写成这样

sed -e 's/,$//g w vio_new.csv' vio.csv

评估

格式转换

经过上述步骤,获得了可以用于评估的vio.csv和vio_loop.csv

可以先转化成tum格式文件,方便后续评估没有报错

转化后数据会只剩下(时间戳*1e9,位置xyz,旋转四元数wxyz)

# 转换GT文件为tum
cd ~/catkin_ws/src/VINS-Fusion/datasets/MH_01_easy/mav0/state_groundtruth_estimate0
evo_traj euroc data.csv --save_as_tum

# 转换vio文件为tum
cd ~/catkin_ws/src/VINS-Fusion/output
evo_traj euroc vio.csv --save_as_tum

# 转换vio_loop文件为tum
cd ~/catkin_ws/src/VINS-Fusion/output
evo_traj euroc vio_loop.csv --save_as_tum

绘制轨迹

可以绘图显示单个结果文件

cd ~/catkin_ws/src/VINS-Fusion/
evo_traj tum ./output/vio.tum -p
evo_traj tum ./output/vio_loop.tum -p

(我这边提示没有tkinter,安装即可)

sudo apt-get install python3-tk

绝对误差评估

cd ~/catkin_ws/src/VINS-Fusion/
mkdir ./evolution

evo_ape tum \
./datasets/MH_01_easy/mav0/state_groundtruth_estimate0/data.tum \
./output/vio.tum \
-va \
-p --plot_mode xy \
--save_plot ./evolution/vio_plot \
--save_results ./evolution/vio_result.zip

evo_ape tum \
./datasets/MH_01_easy/mav0/state_groundtruth_estimate0/data.tum \
./output/vio_loop.tum \
-va \
-p --plot_mode xy \
--save_plot ./evolution/vio_loop_plot \
--save_results ./evolution/vio_loop_result.zip

保存图片的路径应该是字符串拼接的,可以给图片名加前缀,如果要保存在文件夹内最后要加上/

一定要-a轨迹对齐,-v表示详细输出


轨迹对比评估

差值评估还是没有轨迹图对比评估来得直观

evo_traj tum \
./datasets/MH_01_easy/mav0/state_groundtruth_estimate0/data.tum \
./output/vio.tum \
-a --ref ./datasets/MH_01_easy/mav0/state_groundtruth_estimate0/data.tum \
-v -p

对比评估

mkdir ./evolution/comparison

evo_res  ./evolution/*.zip \
-p --save_plot ./evolution/comparison/ \
--save_table ./evolution/comparison/table.csv
阅读全文

EuRoc_dataset

2022/12/9

EuRoc数据集

EUROC是用于室内导航的双目+IMU数据集

链接:https://projects.asl.ethz.ch/datasets/doku.php?id=kmavvisualinertialdatasets

数据集提供ASL数据以及bag包


ASL数据格式

以MH_01_easy为例,格式如下:

.
├── MH_01_easy
│   └── mav0
│       ├── body.yaml //飞行器机体参数文件
│       ├── cam0 //左相机 20Hz
│       │   ├── data //采集的图像
│       │   ├── data.csv //时间戳和图像名的对应 其实图像名就是时间戳
│       │   └── sensor.yaml //左相机参数文件 包括外参 频率 图像分辨率 相机模型 内参 畸变模型 畸变参数
│       ├── cam1 //右相机 同左相机
│       │   ├── data
│       │   ├── data.csv
│       │   └── sensor.yaml
│       ├── imu0 //IMU 200Hz
│       │   ├── data.csv //时间戳和三轴角速度、三轴加速度的对应
│       │   └── sensor.yaml //IMU参数文件 包括外参 频率 角速度噪声密度、随机游走 加速度噪声密度、随机游走
│       ├── leica0 //激光
│       │   ├── data.csv //时间戳和三轴位置的对应
│       │   └── sensor.yaml //参数文件 包括外参
│       └── state_groundtruth_estimate0 //GT
│           ├── data.csv //时间戳和三轴位置、旋转四元数、三轴速度、三轴角速度、三轴加速度的对应
│           └── sensor.yaml

rog bag

其实就是将上述ASL数据格式的数据发布为四个话题

topics:      /cam0/image_raw    3682 msgs    : sensor_msgs/Image         
             /cam1/image_raw    3682 msgs    : sensor_msgs/Image         
             /imu0             36820 msgs    : sensor_msgs/Imu           
             /leica/position    3099 msgs    : geometry_msgs/PointStamped
阅读全文

VINS-Fusion

2022/12/9

VINS-Fusion

港科大的空中机器人组开源了很多视觉惯导方案,

有用于单目视觉惯性系统的实时SLAM框架VINS Mono,

并有基于其开发的支持iOS移动端的VINS-Mobile,

有基于VINS Mono的拓展,支持双目/双目+IMU/单目+IMU的VINS-Fusion,

也有相应的鱼眼方案VINS-Fisheye。

打算从VINS-Fusion入手。


环境搭建

Github链接:https://github.com/HKUST-Aerial-Robotics/VINS-Fusion

所需环境:Ubuntu18.04、ROS Melodic、Ceres

Ubuntu18.04

虚拟机方便就方便在于可以直接新创一个系统

简单换源、更新、安装输入法、安装vim

ROS Melodic

安装参考一些教程以及官方网址:http://wiki.ros.org/melodic/Installation/Ubuntu

配置源

sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list'

安装秘钥

sudo apt-key adv --keyserver 'hkp://keyserver.ubuntu.com:80' --recv-key C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654

更新源、安装ROS Melodic完整版

sudo apt update
sudo apt install ros-melodic-desktop-full

写入环境变量

echo "source /opt/ros/melodic/setup.bash" >> ~/.bashrc
source ~/.bashrc

安装工具

sudo apt install python-rosdep python-rosinstall python-rosinstall-generator python-wstool build-essential

初始化rosdep

sudo rosdep init
rosdep update

(配置完/etc/hosts还不行的话,可以用鱼香ROS的rosdepc)

sudo apt install python3-pip
sudo pip3 install rosdepc
sudo rosdepc init # 现在已经自带了 rosdepc update

跑小海龟验证

rosversion -d
roscore
rosrun turtlesim turtlesim_node 
rosrun turtlesim turtle_teleop_key

Ceres

安装参考官网教程:http://ceres-solver.org/installation.html#linux

#依赖
# CMake
sudo apt-get install cmake
# google-glog + gflags
sudo apt-get install libgoogle-glog-dev libgflags-dev
# Use ATLAS for BLAS & LAPACK
sudo apt-get install libatlas-base-dev
# Eigen3
sudo apt-get install libeigen3-dev
# SuiteSparse (optional)
sudo apt-get install libsuitesparse-dev

#编译
# 若安装最新:(需要提高CMake版本) git clone https://github.com/ceres-solver/ceres-solver.git 
# LocalParameterization在2.2.0版本之后弃用了,改为了Manifold 所以要安装2.2.0之前的版本
wget http://ceres-solver.org/ceres-solver-2.1.0.tar.gz
tar zxf ceres-solver-2.1.0.tar.gz
cd ceres-solver-2.1.0/
mkdir ceres-bin && cd ceres-bin
cmake ..
make
make test
sudo make install

# 测试
bin/simple_bundle_adjuster ../data/problem-16-22106-pre.txt

编译

# 创建工作空间
mkdir -p ~/catkin_ws/src
cd ~/catkin_ws/src
catkin_init_workspace
cd .. && catkin_make

# 环境变量
echo "source devel/setup.bash" >> ~/.bashrc
source ~/.bashrc

# 安装
cd ~/catkin_ws/src
git clone https://github.com/HKUST-Aerial-Robotics/VINS-Fusion.git
cd .. && catkin_make

运行

数据集地址:https://projects.asl.ethz.ch/datasets/doku.php?id=kmavvisualinertialdatasets

# 下载数据集 
cd ~/catkin_ws/src/VINS-Fusion
mkdir datasets && cd datasets
wget http://robotics.ethz.ch/~asl-datasets/ijrr_euroc_mav_dataset/machine_hall/MH_01_easy/MH_01_easy.bag
cd ~/catkin_ws

# 单目+IMU 多个shell
roslaunch vins vins_rviz.launch
rosrun vins vins_node ~/catkin_ws/src/VINS-Fusion/config/euroc/euroc_mono_imu_config.yaml 
rosrun loop_fusion loop_fusion_node ~/catkin_ws/src/VINS-Fusion/config/euroc/euroc_mono_imu_config.yaml
rosbag play ~/catkin_ws/src/VINS-Fusion/datasets/MH_01_easy.bag

# 双目+IMU 多个shell
roslaunch vins vins_rviz.launch
rosrun vins vins_node ~/catkin_ws/src/VINS-Fusion/config/euroc/euroc_stereo_imu_config.yaml 
rosrun loop_fusion loop_fusion_node ~/catkin_ws/src/VINS-Fusion/config/euroc/euroc_stereo_imu_config.yaml 
rosbag play ~/catkin_ws/src/VINS-Fusion/datasets/MH_01_easy.bag

# 双目 多个shell
roslaunch vins vins_rviz.launch
rosrun vins vins_node ~/catkin_ws/src/VINS-Fusion/config/euroc/euroc_stereo_config.yaml 
rosrun loop_fusion loop_fusion_node ~/catkin_ws/src/VINS-Fusion/config/euroc/euroc_stereo_config.yaml 
rosbag play ~/catkin_ws/src/VINS-Fusion/datasets/MH_01_easy.bag

阅读全文

3D-Reconstruction

2022/10/22

三维重建

最近有个任务要进行三维重建,尝试一下


Colmap

colmap是一个可以很方便进行三维重建的开源项目。

安装

官方链接:https://colmap.github.io/install.html

官方编译命令是make -j,我16G电脑内存,8G虚拟机

编译到95%时会报错fatal error: Killed signal terminated program cc1plus,虚拟机内存不足

我重开虚拟机后重新进入build文件夹中,用make编译成功了


使用

下载数据集:https://colmap.github.io/datasets.html

我下载的是gerrard-hall数据集


运行图形化界面

colmap gui

点击Reconstruction > Automatic reconstruction

Workspace folder选择gerrard-hall,Image folder选择gerrard-hall/images,质量Quality选择Medium

其他默认,直接run就可以了


我的电脑是没有CUDA的,报了警告:Skipping patch match stereo because CUDA is not available.

看了官方的FAQ,不支持CUDA的GPU不可以使用密集重建,需要使用外部密集重建软件进行替代


安装稠密重建软件

浅试了一下用PMVS2,官方链接:https://www.di.ens.fr/pmvs/documentation.html

不知道是不是因为软件太老,很多动态链接库都下载不了

然后想尝试CMVS,发现VisualSFM集成了CMVS and PMVS,

不过也是比较老的了,2012年的,官方下载也是支持ubuntu12.04,官方链接:http://ccwu.me/vsfm/

需要下载和编译很多软件,官方也说是It’s really easy to use, but really hard to install.

感觉步骤多,比较旧bug多,费时不讨好,再找找其他的。


比较新的好像是openMVG(Open Multiple View Geometry)

另一个比较像的叫openMVS(open Multi-View Stereo reconstruction library)

我需要的应该是openMVS,但我走了弯路多安装了openMVG


安装openMVG

既然安装了openMVG,这里也记录一下安装过程,

参照BUILD.md 安装,官方链接:https://github.com/openMVG/openMVG/blob/develop/BUILD.md#linux

为了避免使用外部ceres,先将src下的CMakeLists.txt的find_package(Ceres QUIET HINTS ${CERES_DIR_HINTS})注释掉,使用内部的ceres

sudo apt-get install libpng-dev libjpeg-dev libtiff-dev libxxf86vm1 libxxf86vm-dev libxi-dev libxrandr-dev
sudo apt-get install graphviz
git clone --recursive https://github.com/openMVG/openMVG.git
mkdir openMVG_Build && cd openMVG_Build
cmake -DCMAKE_BUILD_TYPE=RELEASE -DOpenMVG_BUILD_TESTS=ON ../openMVG/src/
sudo cmake --build . --target install
make test
ctest --output-on-failure -j

安装openMVS

官方链接:https://github.com/cdcseacave/openMVS

参照BUILD.md安装,一堆warning

# 先注意依赖要求
# (Eigen 3.4 or higher) (Opencv 2.4 or higher) (Ceres 1.10 or higher) 
# (CGAL 4.2 or higher) (Boost 1.56 or higher) (VCG) (GLFW)

sudo apt-get update
sudo apt-get -y install git cmake libpng-dev libjpeg-dev libtiff-dev libglu1-mesa-dev
# 我电脑里有eigen 3.3.7 实测也可行 这里不能瞎改版本 因为eigen版本是跟ceres版本绑定的
sudo apt-get -y install libboost-iostreams-dev libboost-program-options-dev libboost-system-dev libboost-serialization-dev
# 我电脑里有opencv4 编译不过 报错undefined reference to symbol 换成opencv3就搞定了
sudo apt-get -y install libcgal-dev libcgal-qt5-dev
git clone https://github.com/cdcseacave/VCG.git vcglib
# 我电脑里有ceres 2.1.0了所以略过 
sudo apt-get -y install freeglut3-dev libglew-dev libglfw3-dev

git clone https://github.com/cdcseacave/openMVS.git openMVS
main_path=`pwd`
mkdir openMVS_build && cd openMVS_build
cmake . ../openMVS -DCMAKE_BUILD_TYPE=Release -DVCG_ROOT="$main_path/vcglib"
make -j2

sudo make install

三维重建

参考教程:https://blog.csdn.net/rdw1246010462/article/details/121044348


我使用的是gerrard-hall数据集,来自https://colmap.github.io/datasets.html

数据集自带了sparse文件夹,就不需要用colmap生成了

如果没有的话用colmap稀疏重建一下就行了,然后需要把对应的bin文件转为txt


由于openMVS只支持PINHOLE相机模型,我的数据集的相机模型是OpenCV,需要简单地近似一下

Colmap的相机模型可以参考:https://colmap.github.io/cameras.html#

PINHOLE相机模型为width height params[fx,fy,cx,cy]

OpenCV相机模型为width height params[fx,fy,cx,cy,k1,k2,k3,k4]

所以只需要打开camera.txt将OpenCV改成PINHOLE,并且把后四个数字删掉就可以近似了


以下是我的操作过程(很奇怪的是他的程序运行完内存不会释放,重启虚拟机才能释放)

openmvs_path=~/openMVS_build/bin #openmvs编译路径
cd ~/colmap_test/gerrard-hall #数据集路径
mkdir dense

# 格式转换
$openmvs_path/InterfaceCOLMAP -i . -o ./dense/colmap_model.mvs 

# 稠密重建 最终会生成ply
$openmvs_path/DensifyPointCloud -i ./dense/colmap_model.mvs -o ./dense/dense_model.mvs --archive-type -1

# 表面重建 最终会生成ply
$openmvs_path/ReconstructMesh -i ./dense/dense_model.mvs -o ./dense/dense_mesh_model.mvs 

# 网格精化 内存不够就把分辨率级别调小一点 数字越大级别越小 最终会生成ply
$openmvs_path/RefineMesh -i ./dense/dense_mesh_model.mvs -o ./dense/refine_dense_mesh_model.mvs --resolution-level 4

# 重开虚拟机一保证下个命令能运行完
# 纹理映射 需要挺大内存的 100张图片我虚拟机调成14G才能通过
$openmvs_path/TextureMesh -i ./dense/refine_dense_mesh_model.mvs -o ./dense/final_model.mvs

# 查看
$openmvs_path/Viewer ./dense/final_model.mvs
阅读全文