├── .gitignore
├── .vscode
├── c_cpp_properties.json
└── settings.json
├── LICENSE
├── README.md
├── asset
└── overview.png
├── calibration_pattern_creator
├── marker_3x6.bmp
├── marker_3x6.pdf
└── paint.m
├── circle_detector
├── CMakeLists.txt
├── include
│ ├── circle_detector.h
│ └── cylindroid_factor.h
├── launch
│ └── circle_detector.launch
├── package.xml
└── src
│ └── circle_detector.cpp
├── circle_msgs
├── CMakeLists.txt
├── msg
│ ├── circle.msg
│ └── circleArray.msg
└── package.xml
├── config
└── setup.yaml
├── corner_detector
├── CMakeLists.txt
├── include
│ └── corner_detector.h
├── launch
│ └── corner_detector.launch
├── package.xml
└── src
│ └── corner_detector.cpp
├── corner_msgs
├── CMakeLists.txt
├── msg
│ ├── corner.msg
│ └── cornerArray.msg
└── package.xml
├── estimator
├── CMakeLists.txt
├── include
│ ├── calibration_board.h
│ ├── camera.h
│ ├── estimator_manager.h
│ ├── factor
│ │ ├── analytic_diff
│ │ │ ├── image_feature_factor.h
│ │ │ ├── rd_spline_view.h
│ │ │ ├── so3_spline_view.h
│ │ │ ├── split_spline_view.h
│ │ │ └── trajectory_value_factor.h
│ │ └── ceres_local_param.h
│ ├── initialization.h
│ ├── trajectory_estimator.h
│ ├── trajectory_manager.h
│ └── trajectory_viewer.h
├── launch
│ └── estimator.launch
├── package.xml
├── rviz
│ └── estimator.rviz
└── src
│ ├── estimator
│ ├── estimator_manager.cpp
│ ├── initialization.cpp
│ ├── trajectory_estimator.cpp
│ └── trajectory_manager.cpp
│ ├── estimator_node.cpp
│ ├── sophus_lib
│ ├── average.hpp
│ ├── common.hpp
│ ├── geometry.hpp
│ ├── interpolate.hpp
│ ├── interpolate_details.hpp
│ ├── num_diff.hpp
│ ├── rotation_matrix.hpp
│ ├── rxso2.hpp
│ ├── rxso3.hpp
│ ├── se2.hpp
│ ├── se3.hpp
│ ├── sim2.hpp
│ ├── sim3.hpp
│ ├── sim_details.hpp
│ ├── so2.hpp
│ ├── so3.hpp
│ ├── types.hpp
│ └── velocities.hpp
│ ├── spline
│ ├── assert.h
│ ├── ceres_spline_helper.h
│ ├── ceres_spline_helper_jet.h
│ ├── rd_spline.h
│ ├── se3_spline.h
│ ├── so3_spline.h
│ ├── spline_common.h
│ ├── spline_segment.h
│ ├── trajectory.cpp
│ └── trajectory.h
│ └── utils
│ ├── eigen_utils.hpp
│ ├── math_utils.h
│ ├── mypcl_cloud_type.h
│ ├── parameter_struct.h
│ ├── sophus_utils.hpp
│ └── yaml_utils.h
├── eventmap_generator
├── CMakeLists.txt
├── include
│ └── generator.h
├── launch
│ └── generator.launch
├── msg
│ └── eventmap.msg
├── package.xml
└── src
│ ├── generator.cpp
│ └── generator_node.cpp
├── record_bag
├── circle_info.bag
└── corner_info.bag
└── run.sh
/.gitignore:
--------------------------------------------------------------------------------
1 | devel/
2 | logs/
3 | build/
4 | bin/
5 | lib/
6 | msg_gen/
7 | srv_gen/
8 | msg/*Action.msg
9 | msg/*ActionFeedback.msg
10 | msg/*ActionGoal.msg
11 | msg/*ActionResult.msg
12 | msg/*Feedback.msg
13 | msg/*Goal.msg
14 | msg/*Result.msg
15 | msg/_*.py
16 | build_isolated/
17 | devel_isolated/
18 |
19 | # Generated by dynamic reconfigure
20 | *.cfgc
21 | /cfg/cpp/
22 | /cfg/*.py
23 |
24 | # Ignore generated docs
25 | *.dox
26 | *.wikidoc
27 |
28 | # eclipse stuff
29 | .project
30 | .cproject
31 |
32 | # qcreator stuff
33 | CMakeLists.txt.user
34 |
35 | srv/_*.py
36 | *.pcd
37 | *.pyc
38 | qtcreator-*
39 | *.user
40 |
41 | /planning/cfg
42 | /planning/docs
43 | /planning/src
44 |
45 | *~
46 |
47 | # Emacs
48 | .#*
49 |
50 | # Catkin custom files
51 | CATKIN_IGNORE
52 | record_bag/trial_A_3.bag
53 | .vscode/
--------------------------------------------------------------------------------
/.vscode/c_cpp_properties.json:
--------------------------------------------------------------------------------
1 | {
2 | "configurations": [
3 | {
4 | "browse": {
5 | "databaseFilename": "${default}",
6 | "limitSymbolsToIncludedHeaders": false
7 | },
8 | "includePath": [
9 | "/data/catkin_calib/devel/include/**",
10 | "/opt/ros/melodic/include/**",
11 | "/data/catkin_calib/src/EF-Calib/circle_detector/include/**",
12 | "/data/catkin_calib/src/EF-Calib/corner_detector/include/**",
13 | "/data/catkin_calib/src/EF-Calib/rpg_dvs_ros/davis_ros_driver/include/**",
14 | "/data/catkin_calib/src/EF-Calib/rpg_dvs_ros/dvs_calibration/include/**",
15 | "/data/catkin_calib/src/EF-Calib/rpg_dvs_ros/dvs_renderer/include/**",
16 | "/data/catkin_calib/src/EF-Calib/rpg_dvs_ros/dvs_ros_driver/include/**",
17 | "/data/catkin_calib/src/EF-Calib/rpg_dvs_ros/dvxplorer_ros_driver/include/**",
18 | "/data/catkin_calib/src/EF-Calib/estimator/include/**",
19 | "/data/catkin_calib/src/EF-Calib/eventmap_generator/include/**",
20 | "/usr/include/**"
21 | ],
22 | "name": "ROS",
23 | "intelliSenseMode": "gcc-x64",
24 | "compilerPath": "/usr/bin/gcc",
25 | "cStandard": "gnu11",
26 | "cppStandard": "c++14"
27 | }
28 | ],
29 | "version": 4
30 | }
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "python.autoComplete.extraPaths": [
3 | "/data/catkin_calib/devel/lib/python2.7/dist-packages",
4 | "/opt/ros/melodic/lib/python2.7/dist-packages"
5 | ],
6 | "python.analysis.extraPaths": [
7 | "/data/catkin_calib/devel/lib/python2.7/dist-packages",
8 | "/opt/ros/melodic/lib/python2.7/dist-packages"
9 | ],
10 | "files.associations": {
11 | "ostream": "cpp",
12 | "cctype": "cpp",
13 | "clocale": "cpp",
14 | "cmath": "cpp",
15 | "csignal": "cpp",
16 | "cstdarg": "cpp",
17 | "cstddef": "cpp",
18 | "cstdio": "cpp",
19 | "cstdlib": "cpp",
20 | "cstring": "cpp",
21 | "ctime": "cpp",
22 | "cwchar": "cpp",
23 | "cwctype": "cpp",
24 | "array": "cpp",
25 | "atomic": "cpp",
26 | "strstream": "cpp",
27 | "bit": "cpp",
28 | "*.tcc": "cpp",
29 | "bitset": "cpp",
30 | "chrono": "cpp",
31 | "cinttypes": "cpp",
32 | "compare": "cpp",
33 | "complex": "cpp",
34 | "concepts": "cpp",
35 | "condition_variable": "cpp",
36 | "cstdint": "cpp",
37 | "deque": "cpp",
38 | "list": "cpp",
39 | "map": "cpp",
40 | "set": "cpp",
41 | "unordered_map": "cpp",
42 | "unordered_set": "cpp",
43 | "vector": "cpp",
44 | "exception": "cpp",
45 | "algorithm": "cpp",
46 | "functional": "cpp",
47 | "iterator": "cpp",
48 | "memory": "cpp",
49 | "memory_resource": "cpp",
50 | "numeric": "cpp",
51 | "optional": "cpp",
52 | "random": "cpp",
53 | "ratio": "cpp",
54 | "string": "cpp",
55 | "string_view": "cpp",
56 | "system_error": "cpp",
57 | "tuple": "cpp",
58 | "type_traits": "cpp",
59 | "utility": "cpp",
60 | "hash_map": "cpp",
61 | "hash_set": "cpp",
62 | "fstream": "cpp",
63 | "initializer_list": "cpp",
64 | "iomanip": "cpp",
65 | "iosfwd": "cpp",
66 | "iostream": "cpp",
67 | "istream": "cpp",
68 | "limits": "cpp",
69 | "mutex": "cpp",
70 | "new": "cpp",
71 | "ranges": "cpp",
72 | "sstream": "cpp",
73 | "stdexcept": "cpp",
74 | "stop_token": "cpp",
75 | "streambuf": "cpp",
76 | "thread": "cpp",
77 | "cfenv": "cpp",
78 | "typeindex": "cpp",
79 | "typeinfo": "cpp",
80 | "valarray": "cpp"
81 | }
82 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Shaoan Wang
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # EF-Calib: Spatiotemporal Calibration of Event- and Frame-Based Cameras Using Continuous-Time Trajectories
2 |
3 |
4 |

5 |
6 |
7 | # Installation
8 |
9 | We have tested EF-Calib on machines with the following configurations:
10 | * Ubuntu 20.04 LTS
11 | * ROS Noetic
12 | * OpenCV 4+
13 | * Ceres 1.13.0
14 |
15 | ## Driver Installation
16 |
17 | To work with event cameras, especially for the Dynamic Vision Sensors (DVS/DAVIS), you need to install some drivers. Please follow the instructions at [rpg_dvs_ros](https://github.com/uzh-rpg/rpg_dvs_ros) before moving on to the next step. Note that you need to replace the name of the ROS distribution with the one installed on your computer.
18 |
19 | We use catkin tools to build the code. You should have it installed during the driver installation.
20 |
21 | ## Dependencies Installation
22 |
23 | ### Ceres-solver
24 |
25 | Please follow this [link](http://ceres-solver.org/installation.html) to install Ceres-solver. Note that versions above 2.0.0 might encounter issues due to some recent changes.
26 |
27 | ### OpenCV 4
28 |
29 | Please follow this [link](https://opencv.org/get-started/) to install OpenCV 4.
30 |
31 | ## EF-Calib Installation
32 |
33 | You should have created a catkin workspace in Driver Installation. If not, please go back and create one.
34 |
35 | Clone this repository into the `src` folder of your catkin workspace.
36 |
37 | $ cd ~/catkin_ws/src
38 | $ git clone https://github.com/wsakobe/EF-Calib.git
39 |
40 | Build all the packages of EF-Calib.
41 |
42 | $ catkin build
43 |
44 |
45 | # Usage
46 |
47 | First, record the image data and event streams synchronized according to the method described in the paper. Alternatively, you can download an example rosbag from this [link](https://drive.google.com/file/d/1UXtpTBYqeFjvbvVHNQ-meKYFzR9RP7e2/view?usp=sharing)
48 |
49 | Configure parameters in the `config/setup.yaml` file.
50 |
51 | - `BagName`: File path of the recorded rosbag
52 | - `square_size`: Adjacent feature spacing on the calibration board
53 | - `init_window_size`: Minimum number of frames required for initialization
54 | - `board_width`: Calibration board size (number of features in the width direction)
55 | - `board_height`: Calibration board size (number of features in the height direction)
56 | - `knot_distance`: B-spline knot spacing
57 | - `continue_number`: Minimum number of consecutive B-spline knots
58 | - `opt_time`: Total duration of the rosbag
59 |
60 | Next, run the following commands to extract features from frames and events into `corner_info.bag` and `circle_info.bag`, respectively.
61 |
62 | $ source ../devel/setup.bash
63 | $ cd EF-Calib
64 | $ ./run.sh
65 |
66 | Once valid `corner_info.bag` and `circle_info.bag` files are obtained, run the estimator node to complete the calibration process. If successful, the calibration results will be displayed in the Terminal.
67 |
68 | $ roslaunch estimator estimator.launch
69 |
70 | # Credits
71 |
72 | This code was developed by [Shaoan Wang](https://wsakobe.github.io/) from Peking University.
73 |
74 | For researchers that have leveraged or compared to this work, please cite the following:
75 |
76 | ```latex
77 | @ARTICLE{10705060,
78 | author={Wang, Shaoan and Xin, Zhanhua and Hu, Yaoqing and Li, Dongyue and Zhu, Mingzhu and Yu, Junzhi},
79 | journal={IEEE Robotics and Automation Letters},
80 | title={EF-Calib: Spatiotemporal Calibration of Event- and Frame-Based Cameras Using Continuous-Time Trajectories},
81 | year={2024},
82 | volume={9},
83 | number={11},
84 | pages={10280-10287},
85 | keywords={Cameras;Calibration;Trajectory;Splines (mathematics);Robot vision systems;Accuracy;Lighting;Spatiotemporal phenomena;Synchronization;Pattern recognition;Calibration and identification;sensor fusion;event camera;spatiotemporal calibration},
86 | doi={10.1109/LRA.2024.3474475}
87 | }
88 | ```
89 |
90 | # Acknowledgement
91 |
92 | The B-spline module is adapted from [basalt](https://gitlab.com/VladyslavUsenko/basalt-headers). Thanks for the excellent job!
93 |
--------------------------------------------------------------------------------
/asset/overview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wsakobe/EF-Calib/0e218c9640a237769aebffc63c615c44b1c2e4e0/asset/overview.png
--------------------------------------------------------------------------------
/calibration_pattern_creator/marker_3x6.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wsakobe/EF-Calib/0e218c9640a237769aebffc63c615c44b1c2e4e0/calibration_pattern_creator/marker_3x6.bmp
--------------------------------------------------------------------------------
/calibration_pattern_creator/marker_3x6.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wsakobe/EF-Calib/0e218c9640a237769aebffc63c615c44b1c2e4e0/calibration_pattern_creator/marker_3x6.pdf
--------------------------------------------------------------------------------
/calibration_pattern_creator/paint.m:
--------------------------------------------------------------------------------
1 | marker_size = [3, 6]; % [row, col]
2 | desired_mm = 500; % distance between two crosspoints
3 |
4 | [temp_1, temp_2] = generateCellTemp();
5 |
6 | marker_row1 = [];
7 | marker_row2 = [];
8 | for i=1:marker_size(2)
9 | if mod(i,2)
10 | marker_row1 = [marker_row1 temp_2];
11 | marker_row2 = [marker_row2 temp_1];
12 | else
13 | marker_row1 = [marker_row1 temp_1];
14 | marker_row2 = [marker_row2 temp_2];
15 | end
16 | end
17 | marker_img=[];
18 | for i=1:marker_size(1)
19 | if mod(i,2)
20 | marker_img = [marker_img; marker_row1];
21 | else
22 | marker_img = [marker_img; marker_row2];
23 | end
24 | end
25 |
26 | desired_width_inches = desired_mm / 25.4;
27 | desired_height_inches = desired_mm / 25.4;
28 |
29 | imshow(marker_img);
30 | imwrite(marker_img,['marker_', num2str(marker_size(1)), 'x', num2str(marker_size(2)), '.bmp']);
31 |
32 | screen_dpi = 96;
33 | desired_width_pixels = desired_width_inches * screen_dpi;
34 | desired_height_pixels = desired_height_inches * screen_dpi;
35 | set(gcf, 'Position', [100, 100, desired_width_pixels, desired_height_pixels]);
36 | set(gcf, 'PaperUnits', 'inches');
37 | set(gcf, 'PaperSize', [desired_width_inches, desired_height_inches]);
38 | exportgraphics(gcf,['marker_', num2str(marker_size(1)), 'x', num2str(marker_size(2)), '.pdf'],'ContentType','vector');
39 |
40 | function [temp, temp_inv] = generateCellTemp()
41 | temp = ones(900,900);
42 |
43 | center_x = 450;
44 | center_y = 450;
45 | theta_circ = linspace(0, pi*2, 500);
46 | r_circ = ones(size(theta_circ)) * 300;
47 | x = r_circ .* cos(theta_circ) + center_x;
48 | y = r_circ .* sin(theta_circ) + center_y;
49 | temp = insertShape(temp,'FilledPolygon',[[center_x;x';center_x],[center_y;y';center_y]],'Color','black','Opacity',1,'SmoothEdges', false);
50 |
51 | % 定义扇形的参数
52 | theta = linspace(0, pi/2, 500); % 扇形的角度范围
53 | r = ones(size(theta)) * 150; % 扇形的半径
54 | x = r .* cos(theta) + center_x + 1; % 扇形在x轴的坐标
55 | y = r .* sin(theta) + center_y + 1; % 扇形在y轴的坐标
56 | temp = insertShape(temp,'FilledPolygon',[[center_x+1;x';center_x+1],[center_y+1;y';center_y+1]],'Color','white','Opacity',1,'SmoothEdges', false);
57 |
58 | theta = linspace(-pi, -pi/2, 500); % 扇形的角度范围
59 | r = ones(size(theta)) * 150; % 扇形的半径
60 | x = r .* cos(theta) + center_x; % 扇形在x轴的坐标
61 | y = r .* sin(theta) + center_y; % 扇形在y轴的坐标
62 | temp = insertShape(temp,'FilledPolygon',[[center_x;x';center_x],[center_y;y';center_y]],'Color','white','Opacity',1,'SmoothEdges', false);
63 |
64 | temp_inv = imrotate(temp, 90);
65 | end
66 |
--------------------------------------------------------------------------------
/circle_detector/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.0.2)
2 | project(circle_detector)
3 |
4 | set(CMAKE_CXX_STANDARD 17)
5 | set(CMAKE_CXX_FLAGS_RELEASE "-O3 -g -pthread")
6 |
7 | find_package(catkin REQUIRED COMPONENTS
8 | circle_msgs
9 | cv_bridge
10 | image_transport
11 | roscpp
12 | rosbag
13 | std_msgs
14 | dvs_msgs
15 | eventmap_generator
16 | )
17 |
18 | find_package(OpenCV REQUIRED)
19 | find_package(Ceres REQUIRED)
20 |
21 | ###################################
22 | ## catkin specific configuration ##
23 | ###################################
24 | ## The catkin_package macro generates cmake config files for your package
25 | ## Declare things to be passed to dependent projects
26 | ## INCLUDE_DIRS: uncomment this if your package contains header files
27 | ## LIBRARIES: libraries you create in this project that dependent projects also need
28 | ## CATKIN_DEPENDS: catkin_packages dependent projects also need
29 | ## DEPENDS: system dependencies of this project that dependent projects also need
30 | catkin_package(
31 | # INCLUDE_DIRS include
32 | # LIBRARIES circle_detector
33 | # CATKIN_DEPENDS circle_msgs cv_bridge image_transport opencv3 roscpp std_msgs
34 | # DEPENDS system_lib
35 | )
36 |
37 | include_directories(
38 | include
39 | ${catkin_INCLUDE_DIRS}
40 | ${OpenCV_INCLUDE_DIRS}
41 | ${CERES_INCLUDE_DIRS}
42 | )
43 |
44 | add_executable(circle_detector
45 | src/circle_detector.cpp)
46 |
47 | target_link_libraries(circle_detector
48 | ${catkin_LIBRARIES}
49 | ${OpenCV_LIBRARIES}
50 | ${CERES_LIBRARIES}
51 | -lglog
52 | )
53 |
54 | add_dependencies(circle_detector circle_msgs_generate_messages_cpp)
55 |
56 |
--------------------------------------------------------------------------------
/circle_detector/include/cylindroid_factor.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "ceres/ceres.h"
4 | #include "glog/logging.h"
5 | #include
6 | #include
7 | #include
8 |
9 | namespace circle_detector{
10 |
11 | struct CylindroidResidual{
12 |
13 | CylindroidResidual(cv::Point3i point, int time)
14 | : point_(point), time_(time) {}
15 |
16 | template
17 | bool operator()(const T* const coeffs, T* residual) const {
18 | T x_rot = ceres::cos(coeffs[2] / 180.0 * 3.14) * ((T)point_.x - (coeffs[0] + coeffs[5] * (T)(point_.z - time_))) + ceres::sin(coeffs[2] / 180.0 * 3.14) * ((T)point_.y - (coeffs[1] + coeffs[6] * (T)(point_.z - time_)));
19 | T y_rot = ceres::sin(coeffs[2] / 180.0 * 3.14) * ((T)point_.x - (coeffs[0] + coeffs[5] * (T)(point_.z - time_))) - ceres::cos(coeffs[2] / 180.0 * 3.14) * ((T)point_.y - (coeffs[1] + coeffs[6] * (T)(point_.z - time_)));
20 |
21 | residual[0] = ceres::sqrt((x_rot * x_rot * 4.0) / (coeffs[3] * coeffs[3]) + (y_rot * y_rot * 4.0) / (coeffs[4] * coeffs[4])) - T(1);
22 |
23 | return true;
24 | }
25 |
26 | private:
27 | const cv::Point3i point_;
28 | const int time_;
29 | };
30 |
31 | } // namespace circle_detector
--------------------------------------------------------------------------------
/circle_detector/launch/circle_detector.launch:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/circle_detector/package.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | circle_detector
4 | 0.0.0
5 | The circle_detector package
6 |
7 |
8 |
9 |
10 | root
11 |
12 |
13 |
14 |
15 |
16 | TODO
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | catkin
52 | circle_msgs
53 | cv_bridge
54 | image_transport
55 | opencv3
56 | roscpp
57 | rosbag
58 | std_msgs
59 | dvs_msgs
60 | eventmap_generator
61 | circle_msgs
62 | cv_bridge
63 | image_transport
64 | opencv3
65 | roscpp
66 | rosbag
67 | std_msgs
68 | dvs_msgs
69 | circle_msgs
70 | cv_bridge
71 | image_transport
72 | opencv3
73 | roscpp
74 | rosbag
75 | std_msgs
76 | dvs_msgs
77 | eventmap_generator
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
--------------------------------------------------------------------------------
/circle_detector/src/circle_detector.cpp:
--------------------------------------------------------------------------------
1 | #include "circle_detector.h"
2 |
3 | using namespace circle_detector;
4 |
5 | int main(int argc, char** argv) {
6 | google::InitGoogleLogging(argv[0]);
7 |
8 | ros::init(argc, argv, "circle_detector");
9 | ros::NodeHandle nh;
10 |
11 | circle_detector::CircleDetector cd(nh);
12 |
13 | ros::spin();
14 |
15 | ros::shutdown();
16 |
17 | return 0;
18 | }
19 |
--------------------------------------------------------------------------------
/circle_msgs/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.0.2)
2 | project(circle_msgs)
3 |
4 | # search for all msg files
5 | FILE(GLOB messages_to_build RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}/msg"
6 | "${CMAKE_CURRENT_SOURCE_DIR}/msg/*.msg")
7 |
8 | find_package(catkin REQUIRED COMPONENTS
9 | roscpp
10 | std_msgs
11 | message_generation
12 | message_runtime
13 | )
14 |
15 | add_message_files(
16 | FILES
17 | ${messages_to_build}
18 | )
19 |
20 | generate_messages(
21 | DEPENDENCIES
22 | std_msgs
23 | )
24 |
25 | catkin_package(
26 | # INCLUDE_DIRS include
27 | # LIBRARIES corner_detector
28 | # CATKIN_DEPENDS message_generation message_runtime cv_bridge image_transport opencv3 roscpp sensor_msgs std_msgs
29 | # DEPENDS system_lib
30 | CATKIN_DEPENDS message_generation message_runtime roscpp std_msgs
31 | )
32 |
33 | include_directories(
34 | include
35 | ${catkin_INCLUDE_DIRS}
36 | )
37 |
--------------------------------------------------------------------------------
/circle_msgs/msg/circle.msg:
--------------------------------------------------------------------------------
1 | Header header
2 | float32 x
3 | float32 y
4 | int32 x_grid
5 | int32 y_grid
6 | float32 velo_x
7 | float32 velo_y
8 | time timestamp
9 | time start_ts
10 | time end_ts
--------------------------------------------------------------------------------
/circle_msgs/msg/circleArray.msg:
--------------------------------------------------------------------------------
1 | Header header
2 | circle[] circles
--------------------------------------------------------------------------------
/circle_msgs/package.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | circle_msgs
4 | 1.0.0
5 | The circle_msgs package
6 |
7 |
8 |
9 |
10 | root
11 |
12 |
13 |
14 |
15 |
16 | GPLv3
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | catkin
52 | roscpp
53 | std_msgs
54 | roscpp
55 | std_msgs
56 | roscpp
57 | std_msgs
58 | message_generation
59 | message_generation
60 | message_runtime
61 | message_runtime
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
--------------------------------------------------------------------------------
/config/setup.yaml:
--------------------------------------------------------------------------------
1 | ## Board size
2 | BoardWidth: 6
3 | BoardHeight: 3
4 | square_size: 40 # Distance between two neighboring crosspoints (unit:mm)
5 |
6 | ## Bag config
7 | BagName: "/data/catkin_calib/src/EF-Calib/record_bag/trial_A_3.bag"
8 | StorePath: "/data/catkin_calib/src/EF-Calib/record_bag/"
9 | cornerBagPath: "/data/catkin_calib/src/EF-Calib/record_bag/corner_info.bag"
10 | circleBagPath: "/data/catkin_calib/src/EF-Calib/record_bag/circle_info.bag"
11 |
12 | ## Estimator config
13 | log_path: "/data/catkin_calib/logs"
14 | init_window_size: 8
15 |
16 | conv_cam_width: 1280
17 | conv_cam_height: 1024
18 | ev_cam_width: 346
19 | ev_cam_height: 260
20 |
21 | board_width: 6
22 | board_height: 3
23 |
24 | knot_distance: 0.035
25 | continue_number: 15
26 |
27 | opt_time: 40
--------------------------------------------------------------------------------
/corner_detector/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.0.2)
2 | project(corner_detector)
3 |
4 | # search for all msg files
5 | FILE(GLOB messages_to_build RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}/msg"
6 | "${CMAKE_CURRENT_SOURCE_DIR}/msg/*.msg")
7 |
8 | find_package(OpenCV 3 REQUIRED)
9 | find_package(catkin REQUIRED COMPONENTS
10 | cv_bridge
11 | image_transport
12 | roscpp
13 | rosbag
14 | sensor_msgs
15 | std_msgs
16 | corner_msgs
17 | )
18 |
19 | catkin_package(
20 | # INCLUDE_DIRS include
21 | # LIBRARIES corner_detector
22 | # CATKIN_DEPENDS message_generation message_runtime cv_bridge image_transport opencv3 roscpp sensor_msgs std_msgs
23 | # DEPENDS system_lib
24 | )
25 |
26 | include_directories(
27 | include
28 | ${catkin_INCLUDE_DIRS}
29 | ${OpenCV_INCLUDE_DIRS}
30 | )
31 |
32 | add_executable(corner_detector
33 | src/corner_detector.cpp)
34 |
35 | target_link_libraries(corner_detector
36 | ${catkin_LIBRARIES}
37 | ${OpenCV_LIBRARIES}
38 | )
39 |
40 | add_dependencies(corner_detector corner_msgs_generate_messages_cpp)
41 |
--------------------------------------------------------------------------------
/corner_detector/launch/corner_detector.launch:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/corner_detector/package.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | corner_detector
4 | 1.0.0
5 | The corner_detector package
6 |
7 |
8 |
9 |
10 | root
11 |
12 |
13 |
14 |
15 |
16 | GPLv3
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | catkin
52 | cv_bridge
53 | image_transport
54 | opencv3
55 | roscpp
56 | rosbag
57 | sensor_msgs
58 | std_msgs
59 | cv_bridge
60 | image_transport
61 | opencv3
62 | roscpp
63 | rosbag
64 | sensor_msgs
65 | std_msgs
66 | cv_bridge
67 | image_transport
68 | opencv3
69 | roscpp
70 | rosbag
71 | sensor_msgs
72 | std_msgs
73 | corner_msgs
74 | corner_msgs
75 | corner_msgs
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
--------------------------------------------------------------------------------
/corner_detector/src/corner_detector.cpp:
--------------------------------------------------------------------------------
1 | #include "corner_detector.h"
2 |
3 | int main(int argc, char** argv) {
4 | ros::init(argc, argv, "image_processor");
5 | ros::NodeHandle nh;
6 | corner_detector::ImageProcessor* ip = new corner_detector::ImageProcessor(nh);
7 |
8 | ros::spin();
9 |
10 | return 0;
11 | }
12 |
--------------------------------------------------------------------------------
/corner_msgs/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.0.2)
2 | project(corner_msgs)
3 |
4 | # search for all msg files
5 | FILE(GLOB messages_to_build RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}/msg"
6 | "${CMAKE_CURRENT_SOURCE_DIR}/msg/*.msg")
7 |
8 | find_package(catkin REQUIRED COMPONENTS
9 | roscpp
10 | std_msgs
11 | message_generation
12 | message_runtime
13 | )
14 |
15 | add_message_files(
16 | FILES
17 | ${messages_to_build}
18 | )
19 |
20 | generate_messages(
21 | DEPENDENCIES
22 | std_msgs
23 | )
24 |
25 | catkin_package(
26 | # INCLUDE_DIRS include
27 | # LIBRARIES corner_detector
28 | # CATKIN_DEPENDS message_generation message_runtime cv_bridge image_transport opencv3 roscpp sensor_msgs std_msgs
29 | # DEPENDS system_lib
30 | CATKIN_DEPENDS message_generation message_runtime roscpp std_msgs
31 | )
32 |
33 | include_directories(
34 | include
35 | ${catkin_INCLUDE_DIRS}
36 | )
37 |
--------------------------------------------------------------------------------
/corner_msgs/msg/corner.msg:
--------------------------------------------------------------------------------
1 | float32 x
2 | float32 y
3 | int32 x_grid
4 | int32 y_grid
--------------------------------------------------------------------------------
/corner_msgs/msg/cornerArray.msg:
--------------------------------------------------------------------------------
1 | Header header
2 | corner[] corners
3 | time timestamp
--------------------------------------------------------------------------------
/corner_msgs/package.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | corner_msgs
4 | 1.0.0
5 | The corner_msgs package
6 |
7 |
8 |
9 |
10 | root
11 |
12 |
13 |
14 |
15 |
16 | GPLv3
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | catkin
52 | roscpp
53 | std_msgs
54 | roscpp
55 | std_msgs
56 | roscpp
57 | std_msgs
58 | message_generation
59 | message_generation
60 | message_runtime
61 | message_runtime
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
--------------------------------------------------------------------------------
/estimator/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.0.2)
2 | project(estimator)
3 |
4 | set(CMAKE_CXX_STANDARD 17)
5 | set(CMAKE_CXX_FLAGS_RELEASE "-O3")
6 |
7 | find_package(catkin REQUIRED COMPONENTS
8 | roscpp
9 | rosbag
10 | circle_msgs
11 | corner_msgs
12 | std_msgs
13 | geometry_msgs
14 | nav_msgs
15 | pcl_conversions
16 | pcl_ros
17 | visualization_msgs
18 | eigen_conversions
19 | )
20 |
21 | find_package(Eigen3 REQUIRED)
22 | find_package(Ceres REQUIRED)
23 | # find_package(Sophus REQUIRED)
24 | find_package(OpenCV 3 REQUIRED)
25 |
26 | #find_package(yaml-cpp REQUIRED)
27 | find_package(PkgConfig REQUIRED)
28 | pkg_check_modules(YAML_CPP REQUIRED yaml-cpp>=0.5)
29 |
30 | catkin_package(
31 | #INCLUDE_DIRS src
32 | )
33 |
34 | ###########
35 | ## Build ##
36 | ###########
37 | include_directories(
38 | include
39 | src
40 | ${catkin_INCLUDE_DIRS}
41 | ${YAML_INCLUDE_DIRS}
42 | ${CERES_INCLUDE_DIRS}
43 | # ${Sophus_INCLUDE_DIRS}
44 | ${EIGEN3_INCLUDE_DIR}
45 | )
46 |
47 | list(APPEND thirdparty_libraries
48 | ${YAML_CPP_LIBRARIES}
49 | ${Boost_LIBRARIES}
50 | ${catkin_LIBRARIES}
51 | ${OpenCV_LIBS}
52 | ${PCL_LIBRARIES}
53 | ${CERES_LIBRARIES}
54 | )
55 |
56 | ######################
57 | ### Estimator
58 | ######################
59 |
60 | add_library(spline_lib src/spline/trajectory.cpp)
61 | target_link_libraries(spline_lib ${thirdparty_libraries})
62 |
63 | add_executable(estimator
64 | src/estimator_node.cpp
65 | src/estimator/estimator_manager.cpp
66 | src/estimator/initialization.cpp
67 | src/estimator/trajectory_manager.cpp
68 | src/estimator/trajectory_estimator.cpp
69 | )
70 | target_link_libraries(estimator
71 | spline_lib
72 | ${thirdparty_libraries}
73 | )
--------------------------------------------------------------------------------
/estimator/include/calibration_board.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include "ros/ros.h"
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | namespace estimator{
11 | class CalibBoard{
12 | public:
13 | typedef std::shared_ptr Ptr;
14 |
15 | CalibBoard(cv::Size board_size, double squarelen)
16 | : board_size_(board_size), square_len(squarelen)
17 | {
18 | }
19 |
20 | Eigen::Vector3d getBoardPointInWorld(int x, int y){
21 | Eigen::Vector3d point_world;
22 | point_world = Eigen::Vector3d(x * square_len, y * square_len, 0);
23 |
24 | return point_world;
25 | }
26 |
27 | double square_len;
28 |
29 | private:
30 | cv::Size board_size_;
31 |
32 | };
33 | } //namespace estimator
34 |
--------------------------------------------------------------------------------
/estimator/include/camera.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include "ros/ros.h"
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | namespace estimator{
11 |
12 | class Camera{
13 | public:
14 | typedef std::shared_ptr Ptr;
15 | Camera(cv::Size image_size)
16 | : image_size_(image_size)
17 | {
18 | image_width_ = image_size_.width;
19 | image_height_ = image_size_.height;
20 | }
21 |
22 | double intrinsicParams[4]; //fx_, fy_, cx_, cy_
23 | double distortParams[5]; //k1_, k2_, d1_, d2_, k3_
24 |
25 | int image_width_, image_height_;
26 | cv::Size image_size_;
27 |
28 | Sophus::SO3 R_C2W;
29 | Eigen::Matrix t_C2W;
30 |
31 | double time_delay = 0;
32 |
33 | cv::Mat getIntrinsicMatrixOC(){
34 | cv::Mat K = (cv::Mat_(3, 3) << intrinsicParams[0], 0, intrinsicParams[2], 0, intrinsicParams[1], intrinsicParams[3], 0, 0, 1);
35 | return K;
36 | }
37 |
38 | cv::Mat getDistortionMatrixOC(){
39 | cv::Mat K = (cv::Mat_(5, 1) << distortParams[0], distortParams[1], distortParams[2], distortParams[3], distortParams[4]);
40 | return K;
41 | }
42 |
43 | Eigen::Matrix getIntrinsicMatrix(){
44 | Eigen::Matrix K;
45 | K(0, 0) = intrinsicParams[0];
46 | K(1, 1) = intrinsicParams[1];
47 | K(0, 2) = intrinsicParams[2];
48 | K(1, 2) = intrinsicParams[3];
49 | K(2, 2) = 1;
50 |
51 | return K;
52 | }
53 |
54 | Eigen::Matrix getDistortionMatrix(){
55 | Eigen::Matrix K;
56 | K(0, 0) = distortParams[0];
57 | K(1, 0) = distortParams[1];
58 | K(2, 0) = distortParams[2];
59 | K(3, 0) = distortParams[3];
60 | K(4, 0) = distortParams[4];
61 | return K;
62 | }
63 |
64 | Eigen::Matrix getExtrinsicMatrix(){
65 | Eigen::Matrix T;
66 | T.block<3, 3>(0, 0) = R_C2W.matrix();
67 | T.block<3, 1>(0, 3) = t_C2W;
68 | T.block<1, 4>(3, 0) << 0, 0, 0, 1;
69 | return T;
70 | }
71 |
72 | void updateIntrinsic(cv::Mat& intrinsic, cv::Mat& distortion){
73 | intrinsicParams[0] = intrinsic.at(0, 0);
74 | intrinsicParams[1] = intrinsic.at(1, 1);
75 | intrinsicParams[2] = intrinsic.at(0, 2);
76 | intrinsicParams[3] = intrinsic.at(1, 2);
77 |
78 | distortParams[0] = distortion.at(0, 0);
79 | distortParams[1] = distortion.at(0, 1);
80 | distortParams[2] = distortion.at(0, 2);
81 | distortParams[3] = distortion.at(0, 3);
82 | distortParams[4] = distortion.at(0, 4);
83 | }
84 |
85 | void updateExtrinsic(cv::Mat& Transformation){
86 | Eigen::Matrix4d eigenT;
87 | cv::cv2eigen(Transformation, eigenT);
88 | SE3d se3(eigenT);
89 | R_C2W = se3.rotationMatrix();
90 | t_C2W = se3.translation();
91 | }
92 |
93 | Eigen::Vector2d projectIntoImage(Eigen::Vector3d input_point){
94 | Eigen::Vector2d normalized_input_point(input_point.x() / input_point.z(), input_point.y() / input_point.z());
95 |
96 | // Apply distortion correction
97 | double r2 = normalized_input_point.squaredNorm();
98 | double radial_distortion = 1.0 + distortParams[0] * r2 + distortParams[1] * r2 * r2 + distortParams[4] * r2 * r2 * r2;
99 |
100 | double x_corr = normalized_input_point.x() * radial_distortion +
101 | 2.0 * distortParams[2] * normalized_input_point.x() * normalized_input_point.y() +
102 | distortParams[3] * (r2 + 2.0 * normalized_input_point.x() * normalized_input_point.x());
103 | double y_corr = normalized_input_point.y() * radial_distortion +
104 | 2.0 * distortParams[3] * normalized_input_point.x() * normalized_input_point.y() +
105 | distortParams[2] * (r2 + 2.0 * normalized_input_point.y() * normalized_input_point.y());
106 |
107 | // Construct undistorted coordinates
108 | Eigen::Vector3d undistorted_point(x_corr, y_corr, 1);
109 | //Eigen::Vector3d undistorted_point1(normalized_input_point.x(), normalized_input_point.y(), 1);
110 |
111 | Eigen::Vector3d projected_point;
112 | projected_point = getIntrinsicMatrix() * undistorted_point;
113 |
114 | // Normalize the projected point
115 | Eigen::Vector2d normalized_point(projected_point.x() / projected_point.z(), projected_point.y() / projected_point.z());
116 |
117 | //std::cout << "input:\n" << input_point << "\nproject:\n" << undistorted_point << "\nnormalized:\n" << normalized_point << "x:" << x_corr << " y:" << y_corr << std::endl;
118 |
119 | return normalized_point;
120 | }
121 |
122 | private:
123 | };
124 |
125 | } // namespace estimator
126 |
--------------------------------------------------------------------------------
/estimator/include/estimator_manager.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | #include "initialization.h"
7 | #include "trajectory_manager.h"
8 | #include "trajectory_viewer.h"
9 |
10 | #include
11 |
12 | namespace estimator{
13 |
14 | class EstimatorManager{
15 | public:
16 | EstimatorManager(const YAML::Node& yaml_node, ros::NodeHandle& nh);
17 | ~EstimatorManager();
18 |
19 | void cornerArrayCallback(const corner_msgs::cornerArray& msg);
20 | void circleArrayCallback(const circle_msgs::circleArray& msg);
21 |
22 | void initialization();
23 | void optimization();
24 |
25 | private:
26 | void readBags(std::string corner_bag_path, std::string circle_bag_path);
27 | void performEstimator();
28 | void setInitialState();
29 | void updateTrajectoryInitial();
30 | void calculateRepError(int CAM_TYPE, int tag = 0);
31 | void clearPreStatus();
32 |
33 | ros::NodeHandle nh_;
34 | ros::Subscriber corner_sub_;
35 | ros::Subscriber circle_sub_;
36 | rosbag::Bag read_bag_corner, read_bag_circle;
37 |
38 | Initializer::Ptr est_initializer;
39 | TrajectoryManager::Ptr trajectory_manager_;
40 | Trajectory::Ptr trajectory_;
41 | Camera::Ptr event_camera_, convent_camera_;
42 | CalibBoard::Ptr calib_board_;
43 |
44 | TrajectoryViewer traj_viewer;
45 |
46 | std::mutex init_lc;
47 |
48 | double knot_distance;
49 | double opt_time;
50 | std::vector rep_err_conv, rep_err_ev, rep_err_ev_st, rep_err_ev_ed;
51 | ceres::Solver::Summary summary;
52 | int num_disp_pose = 0, continue_num = 10;
53 | int64_t current_start_time, current_time, current_end_time, prev_time;
54 | cv::Mat T;
55 | Eigen::Matrix4d eigenT;
56 | SE3d se3;
57 |
58 | std::vector corner_used, circle_used;
59 | };
60 |
61 | } // namespace estimator
62 |
--------------------------------------------------------------------------------
/estimator/include/factor/analytic_diff/rd_spline_view.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | #include
7 | #include
8 |
9 | #include
10 |
11 | namespace estimator
12 | {
13 |
14 | class RdSplineView
15 | {
16 | public:
17 | static constexpr int N = SplineOrder; ///< Order of the spline.
18 | static constexpr int DIM = 3; ///< Dimension of euclidean vector space.
19 |
20 | using MatN = Eigen::Matrix;
21 | using VecN = Eigen::Matrix;
22 | using VecD = Eigen::Matrix;
23 |
24 | /// @brief Struct to store the Jacobian of the spline
25 | struct JacobianStruct
26 | {
27 | size_t
28 | start_idx; ///< Start index of the non-zero elements of the Jacobian.
29 | std::array d_val_d_knot; ///< Value of nonzero Jacobians.
30 | };
31 |
32 | RdSplineView() {}
33 |
34 | /// @brief Evaluate value or derivative of the spline
35 | ///
36 | /// @param Derivative derivative to evaluate (0 for value)
37 | /// @param[in] time_ns time for evaluating of the spline
38 | /// @param[out] J if not nullptr, return the Jacobian of the value with
39 | /// respect to knots
40 | /// @return value of the spline or derivative. Euclidean vector of dimention
41 | /// DIM.
42 | template
43 | static VecD evaluate(const int64_t time_ns,
44 | const SplineSegmentMeta &splne_meta,
45 | double const *const *knots,
46 | JacobianStruct *J = nullptr)
47 | {
48 | std::pair ui = splne_meta.computeTIndexNs(time_ns);
49 | size_t s = ui.second + splne_meta.n;
50 | double u = ui.first;
51 |
52 | VecN p;
53 | baseCoeffsWithTimeR3(p, u);
54 |
55 | VecN coeff = splne_meta.pow_inv_dt[Derivative] * (blending_matrix_ * p);
56 |
57 | VecD res;
58 | res.setZero();
59 |
60 | for (int i = 0; i < N; i++)
61 | {
62 | Eigen::Map p(knots[s + i]);
63 | res += coeff[i] * p;
64 | if (J)
65 | J->d_val_d_knot[i] = coeff[i];
66 | }
67 |
68 | if (J)
69 | J->start_idx = s;
70 |
71 | return res;
72 | }
73 |
74 | /// @brief Alias for first derivative of spline. See \ref evaluate.
75 | static VecD velocity(const int64_t time_ns,
76 | const SplineSegmentMeta &splne_meta,
77 | double const *const *knots,
78 | JacobianStruct *J = nullptr)
79 | {
80 | return evaluate<1>(time_ns, splne_meta, knots, J);
81 | }
82 |
83 | /// @brief Alias for second derivative of spline. See \ref evaluate.
84 | static VecD acceleration(const int64_t time_ns,
85 | const SplineSegmentMeta &splne_meta,
86 | double const *const *knots,
87 | JacobianStruct *J = nullptr)
88 | {
89 | return evaluate<2>(time_ns, splne_meta, knots, J);
90 | }
91 |
92 | // protected:
93 | /// @brief Vector of derivatives of time polynomial.
94 | ///
95 | /// Computes a derivative of \f$ \begin{bmatrix}1 & t & t^2 & \dots &
96 | /// t^{N-1}\end{bmatrix} \f$ with repect to time. For example, the first
97 | /// derivative would be \f$ \begin{bmatrix}0 & 1 & 2 t & \dots & (N-1)
98 | /// t^{N-2}\end{bmatrix} \f$.
99 | /// @param Derivative derivative to evaluate
100 | /// @param[out] res_const vector to store the result
101 | /// @param[in] t
102 | template
103 | static void baseCoeffsWithTimeR3(const Eigen::MatrixBase &res_const,
104 | double t)
105 | {
106 | EIGEN_STATIC_ASSERT_VECTOR_SPECIFIC_SIZE(Derived, N);
107 | Eigen::MatrixBase &res =
108 | const_cast &>(res_const);
109 |
110 | res.setZero();
111 |
112 | if (Derivative < N)
113 | {
114 | res[Derivative] = base_coefficients_(Derivative, Derivative);
115 |
116 | double _t = t;
117 | for (int j = Derivative + 1; j < N; j++)
118 | {
119 | res[j] = base_coefficients_(Derivative, j) * _t;
120 | _t = _t * t;
121 | }
122 | }
123 | }
124 |
125 | ///< Base coefficients matrix. See \ref computeBaseCoefficients.
126 | static inline MatN base_coefficients_ = computeBaseCoefficients();
127 |
128 | ///< Blending matrix. See \ref computeBlendingMatrix.
129 | static inline MatN blending_matrix_ = computeBlendingMatrix();
130 | };
131 |
132 | // typename RdSplineView::MatN RdSplineView::base_coefficients_ =
133 | // estimator::computeBaseCoefficients();
134 |
135 | // typename RdSplineView::MatN RdSplineView::blending_matrix_ =
136 | // estimator::computeBlendingMatrix();
137 |
138 | } // namespace estimator
139 |
--------------------------------------------------------------------------------
/estimator/include/factor/analytic_diff/split_spline_view.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | namespace estimator
7 | {
8 | struct SplitSpineView : public So3SplineView, public RdSplineView
9 | {
10 | static constexpr int N = SplineOrder; // Order of the spline.
11 | static constexpr int DEG = N - 1; // Degree of the spline.
12 | static constexpr int DIM = 3; // Dimension of euclidean vector space.
13 |
14 | using MatN = Eigen::Matrix;
15 | using VecN = Eigen::Matrix;
16 | using Vec3 = Eigen::Matrix;
17 | using Mat3 = Eigen::Matrix;
18 |
19 | using VecD = Eigen::Matrix;
20 | using MatD = Eigen::Matrix;
21 |
22 | using SO3 = Sophus::SO3;
23 |
24 | using SO3View = So3SplineView;
25 | using R3View = RdSplineView;
26 |
27 | // struct SplineIMUData
28 | // {
29 | // double timestamp;
30 | // Eigen::Vector3d gyro;
31 | // VecD accel;
32 | // SO3 R_inv;
33 | // };
34 | struct SplineIMUData
35 | {
36 | int64_t time_ns;
37 | Eigen::Vector3d gyro;
38 | VecD accel;
39 | SO3 R_inv;
40 | size_t start_idx;
41 | };
42 |
43 | SplitSpineView() {}
44 |
45 | static SplineIMUData Evaluate(
46 | const int64_t time_ns, const SplineSegmentMeta &splne_meta,
47 | double const *const *knots, const Vec3 &gravity,
48 | typename SO3View::JacobianStruct *J_rot_w = nullptr,
49 | typename SO3View::JacobianStruct *J_rot_a = nullptr,
50 | typename R3View::JacobianStruct *J_pos = nullptr)
51 | {
52 | SplineIMUData spline_imu_data;
53 | spline_imu_data.time_ns = time_ns;
54 |
55 | std::pair ui = splne_meta.computeTIndexNs(time_ns);
56 | size_t s = ui.second;
57 | double u = ui.first;
58 |
59 | size_t R_knot_offset = s;
60 | size_t P_knot_offset = s + splne_meta.NumParameters();
61 | spline_imu_data.start_idx = s;
62 |
63 | VecN Up, lambda_a;
64 | R3View::template baseCoeffsWithTimeR3<2>(Up, u);
65 | lambda_a = splne_meta.pow_inv_dt[2] * R3View::blending_matrix_ * Up;
66 |
67 | VecN Ur, lambda_R;
68 | SO3View::template baseCoeffsWithTime<0>(Ur, u);
69 | lambda_R = SO3View::blending_matrix_ * Ur;
70 |
71 | VecN Uw, lambda_w;
72 | SO3View::template baseCoeffsWithTime<1>(Uw, u);
73 | lambda_w = splne_meta.pow_inv_dt[1] * SO3View::blending_matrix_ * Uw;
74 |
75 | VecD accelerate;
76 | accelerate.setZero();
77 |
78 | if (J_pos)
79 | J_pos->start_idx = s;
80 | for (int i = 0; i < N; i++)
81 | {
82 | Eigen::Map p(knots[P_knot_offset + i]);
83 | accelerate += lambda_a[i] * p;
84 |
85 | if (J_pos)
86 | J_pos->d_val_d_knot[i] = lambda_a[i];
87 | }
88 |
89 | Vec3 d_vec[DEG]; // d_1, d_2, d_3
90 | SO3 A_rot_inv[DEG]; // A_1_inv, A_2_inv, A_3_inv
91 | SO3 A_accum_inv; // A_3_inv * A2_inv * A1_inv
92 | Mat3 A_post_inv[N]; // A_3_inv*A2_inv*A1_inv, A_3_inv*A2_inv, A_3_inv, I
93 | Mat3 Jr_dvec_inv[DEG]; // Jr_inv(d1), Jr_inv(d2), Jr_inv(d3)
94 | Mat3 Jr_kdelta[DEG]; // Jr(-kd1), Jr(-kd2), Jr(-kd3)
95 |
96 | A_post_inv[N - 1] = A_accum_inv.matrix(); // Identity Matrix
97 | /// 2 1 0
98 | for (int i = DEG - 1; i >= 0; i--)
99 | {
100 | Eigen::Map R0(knots[R_knot_offset + i]);
101 | Eigen::Map R1(knots[R_knot_offset + i + 1]);
102 |
103 | d_vec[i] = (R0.inverse() * R1).log();
104 |
105 | Vec3 k_delta = lambda_R[i + 1] * d_vec[i];
106 | A_rot_inv[i] = Sophus::SO3d::exp(-k_delta);
107 | A_accum_inv *= A_rot_inv[i];
108 |
109 | if (J_rot_w || J_rot_a)
110 | {
111 | A_post_inv[i] = A_accum_inv.matrix();
112 |
113 | Sophus::rightJacobianInvSO3(d_vec[i], Jr_dvec_inv[i]);
114 | Sophus::rightJacobianSO3(-k_delta, Jr_kdelta[i]);
115 | }
116 | }
117 |
118 | /// Omega(j)
119 | Vec3 omega[N]; // w(1), w(2), w(3), w(4)
120 | {
121 | omega[0] = Vec3::Zero();
122 | for (int i = 0; i < DEG; i++)
123 | {
124 | omega[i + 1] = A_rot_inv[i] * omega[i] + lambda_w[i + 1] * d_vec[i];
125 | }
126 | spline_imu_data.gyro = omega[3];
127 |
128 | Eigen::Map Ri(knots[R_knot_offset]);
129 | SO3 R_inv = A_accum_inv * Ri.inverse();
130 |
131 | spline_imu_data.accel = R_inv * (accelerate + gravity);
132 | spline_imu_data.R_inv = R_inv;
133 | }
134 |
135 | if (J_rot_w)
136 | {
137 | J_rot_w->start_idx = s;
138 | for (int i = 0; i < N; i++)
139 | {
140 | J_rot_w->d_val_d_knot[i].setZero();
141 | }
142 |
143 | // d(omega) / d(d_j)
144 | Mat3 d_omega_d_delta[DEG]; // w(4)/d1, w(4)/d2, w(4)/d3
145 | d_omega_d_delta[0] = lambda_w[1] * A_post_inv[1];
146 | for (int i = 1; i < DEG; i++)
147 | {
148 | d_omega_d_delta[i] = lambda_R[i + 1] * A_post_inv[i] *
149 | SO3::hat(omega[i]) * Jr_kdelta[i] +
150 | lambda_w[i + 1] * A_post_inv[i + 1];
151 | }
152 |
153 | for (int i = 0; i < DEG; i++)
154 | {
155 | J_rot_w->d_val_d_knot[i] -=
156 | d_omega_d_delta[i] * Jr_dvec_inv[i].transpose();
157 | J_rot_w->d_val_d_knot[i + 1] += d_omega_d_delta[i] * Jr_dvec_inv[i];
158 | }
159 | }
160 |
161 | if (J_rot_a)
162 | {
163 | // for accelerate jacobian
164 | Mat3 R_accum[DEG - 1]; // R_i, R_i*A_1, R_i*A_1*A_2
165 | Eigen::Map R0(knots[R_knot_offset]);
166 | R_accum[0] = R0.matrix();
167 | /// 1 2
168 | for (int i = 1; i < DEG; i++)
169 | {
170 | R_accum[i] = R_accum[i - 1] * A_rot_inv[i - 1].matrix().transpose();
171 | }
172 |
173 | J_rot_a->start_idx = s;
174 | for (int i = 0; i < N; i++)
175 | {
176 | J_rot_a->d_val_d_knot[i].setZero();
177 | }
178 |
179 | Mat3 lhs =
180 | spline_imu_data.R_inv.matrix() * SO3::hat(accelerate + gravity);
181 | J_rot_a->d_val_d_knot[0] += lhs * R_accum[0];
182 | for (int i = 0; i < DEG; i++)
183 | {
184 | Mat3 d_a_d_delta = lambda_R[i + 1] * lhs * R_accum[i] * Jr_kdelta[i];
185 |
186 | J_rot_a->d_val_d_knot[i] -= d_a_d_delta * Jr_dvec_inv[i].transpose();
187 | J_rot_a->d_val_d_knot[i + 1] += d_a_d_delta * Jr_dvec_inv[i];
188 | }
189 | }
190 |
191 | return spline_imu_data;
192 | }
193 | };
194 |
195 | } // namespace estimator
--------------------------------------------------------------------------------
/estimator/include/factor/ceres_local_param.h:
--------------------------------------------------------------------------------
1 | /**
2 | BSD 3-Clause License
3 |
4 | This file is part of the Basalt project.
5 | https://gitlab.com/VladyslavUsenko/basalt-headers.git
6 |
7 | Copyright (c) 2019, Vladyslav Usenko and Nikolaus Demmel.
8 | All rights reserved.
9 |
10 | Redistribution and use in source and binary forms, with or without
11 | modification, are permitted provided that the following conditions are met:
12 |
13 | * Redistributions of source code must retain the above copyright notice, this
14 | list of conditions and the following disclaimer.
15 |
16 | * Redistributions in binary form must reproduce the above copyright notice,
17 | this list of conditions and the following disclaimer in the documentation
18 | and/or other materials provided with the distribution.
19 |
20 | * Neither the name of the copyright holder nor the names of its
21 | contributors may be used to endorse or promote products derived from
22 | this software without specific prior written permission.
23 |
24 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
28 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 |
35 | @file
36 | @brief Generic local parametrization for Sophus Lie group types to be used with
37 | ceres.
38 | */
39 |
40 | /**
41 | File adapted from Sophus
42 |
43 | Copyright 2011-2017 Hauke Strasdat
44 | 2012-2017 Steven Lovegrove
45 |
46 | Permission is hereby granted, free of charge, to any person obtaining a copy
47 | of this software and associated documentation files (the "Software"), to
48 | deal in the Software without restriction, including without limitation the
49 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
50 | sell copies of the Software, and to permit persons to whom the Software is
51 | furnished to do so, subject to the following conditions:
52 |
53 | The above copyright notice and this permission notice shall be included in
54 | all copies or substantial portions of the Software.
55 |
56 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
57 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
58 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
59 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
60 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
61 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
62 | IN THE SOFTWARE.
63 | */
64 |
65 | #pragma once
66 |
67 | #include
68 | #include
69 |
70 | namespace estimator
71 | {
72 |
73 | /// @brief Local parametrization for ceres that can be used with Sophus Lie
74 | /// group implementations.
75 | template
76 | class LieLocalParameterization : public ceres::LocalParameterization
77 | {
78 | public:
79 | virtual ~LieLocalParameterization() {}
80 |
81 | using Tangentd = typename Groupd::Tangent;
82 |
83 | /// @brief plus operation for Ceres
84 | ///
85 | /// T * exp(x)
86 | ///
87 | virtual bool Plus(double const *T_raw, double const *delta_raw,
88 | double *T_plus_delta_raw) const
89 | {
90 | Eigen::Map const T(T_raw);
91 | Eigen::Map const delta(delta_raw);
92 | Eigen::Map T_plus_delta(T_plus_delta_raw);
93 | T_plus_delta = T * Groupd::exp(delta);
94 | return true;
95 | }
96 |
97 | ///@brief Jacobian of plus operation for Ceres
98 | ///
99 | /// Dx T * exp(x) with x=0
100 | /// w -z y
101 | /// z w -x
102 | /// -y x w
103 | /// -x -y -z
104 | /// 4*3
105 | virtual bool ComputeJacobian(double const *T_raw,
106 | double *jacobian_raw) const
107 | {
108 | Eigen::Map T(T_raw);
109 | // TODO
110 | Eigen::Map>
112 | // Eigen::Map>
113 | jacobian(jacobian_raw);
114 | jacobian = T.Dx_this_mul_exp_x_at_0();
115 | return true;
116 | }
117 |
118 | ///@brief Global size
119 | virtual int GlobalSize() const { return Groupd::num_parameters; }
120 |
121 | ///@brief Local size
122 | virtual int LocalSize() const { return Groupd::DoF; }
123 | };
124 |
125 | template
126 | class LieAnalyticLocalParameterization : public ceres::LocalParameterization
127 | {
128 | public:
129 | virtual ~LieAnalyticLocalParameterization() {}
130 |
131 | using Tangentd = typename Groupd::Tangent;
132 |
133 | /// @brief plus operation for Ceres
134 | ///
135 | /// T * exp(x)
136 | ///
137 | virtual bool Plus(double const *T_raw, double const *delta_raw,
138 | double *T_plus_delta_raw) const
139 | {
140 | Eigen::Map const T(T_raw);
141 | Eigen::Map const delta(delta_raw);
142 | Eigen::Map T_plus_delta(T_plus_delta_raw);
143 | T_plus_delta = T * Groupd::exp(delta);
144 | return true;
145 | }
146 |
147 | virtual bool ComputeJacobian(double const *T_raw,
148 | double *jacobian_raw) const
149 | {
150 | Eigen::Map T(T_raw);
151 | Eigen::Map>
153 | jacobian(jacobian_raw);
154 | jacobian.setZero();
155 | jacobian(0, 0) = 1;
156 | jacobian(1, 1) = 1;
157 | jacobian(2, 2) = 1;
158 | return true;
159 | }
160 |
161 | ///@brief Global size
162 | virtual int GlobalSize() const { return Groupd::num_parameters; }
163 |
164 | ///@brief Local size
165 | virtual int LocalSize() const { return Groupd::DoF; }
166 | };
167 |
168 | } // namespace estimator
169 |
--------------------------------------------------------------------------------
/estimator/include/initialization.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include "ros/ros.h"
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 |
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include
19 |
20 | #include
21 | #include
22 | #include
23 |
24 | #define CONV_CAM 0
25 | #define EV_CAM 1
26 | #define BOTH 2
27 |
28 | namespace estimator{
29 |
30 | class Initializer{
31 | public:
32 | typedef std::shared_ptr Ptr;
33 |
34 | Initializer(Camera::Ptr event_camera, Camera::Ptr convent_camera, CalibBoard::Ptr calib_board)
35 | : event_camera_(event_camera), convent_camera_(convent_camera), calib_board_(calib_board)
36 | {
37 | b_conv_initialized = false;
38 | b_ev_initialized = false;
39 | b_both_initialized = false;
40 | };
41 |
42 | bool convInitialSucc(){ return b_conv_initialized;}
43 | bool evInitialSucc(){ return b_ev_initialized;}
44 | bool judgeBufferStatus(int cam_type){
45 | if (cam_type == 0){
46 | if (getCornerBufferNum() < initial_window_size){
47 | ROS_INFO("Conv cam no sufficient frames, now: %d, %d", getCornerBufferNum(), initial_window_size);
48 | return false;
49 | }
50 | return true;
51 | }
52 | else{
53 | if (getCircleBufferNum() < initial_window_size){
54 | ROS_INFO("EV cam no sufficient frames, now: %d", getCircleBufferNum());
55 | return false;
56 | }
57 | return true;
58 | }
59 | }
60 |
61 | void processConv();
62 | void processEv();
63 | void estimateInitialExtrinsic();
64 |
65 | std::vector corner_buffer_, corner_buffer_selected;
66 | std::deque circle_buffer_, circle_buffer_selected;
67 | int initial_window_size = 10;
68 | float square_size;
69 | cv::Size conv_cam_size, ev_cam_size;
70 | double epoch_time;
71 |
72 | private:
73 | double computeReprojectionError(const cv::Mat& H, const std::vector& point_1, const std::vector& point_2) {
74 | double total_error = 0.0;
75 | int point_count = static_cast(point_1.size());
76 |
77 | for (int i = 0; i < point_count; ++i) {
78 | cv::Point2f p1 = point_1[i];
79 | cv::Point3d p1_homogeneous((double)p1.x, (double)p1.y, 1.0);
80 | cv::Mat p1_transformed = H * cv::Mat(p1_homogeneous);
81 | p1_transformed /= p1_transformed.at(2);
82 |
83 | cv::Point2f p2 = point_2[i];
84 | double error = cv::norm(cv::Point2f(p1_transformed.at(0), p1_transformed.at(1)) - p2);
85 | total_error += error;
86 | }
87 |
88 | return total_error / point_count;
89 | }
90 | int getCornerBufferNum(){
91 | corner_buffer_selected.clear();
92 | legal_corner_size = 0;
93 | if (corner_buffer_.empty()) return 0;
94 | cv::Mat w, u, vt, rotationMatrix, rvec;
95 | for (auto c:corner_buffer_){
96 | if ((c.timestamp - corner_buffer_.front().timestamp).toSec() > epoch_time) break;
97 | static bool fisrt_judge = true;
98 | if (fisrt_judge){
99 | fisrt_judge = false;
100 | last_corner = c;
101 | corner_buffer_selected.emplace_back(c);
102 | }
103 | float dist = 0;
104 | std::vector point_1, point_2;
105 | for (auto corner_last:last_corner.corners){
106 | for (auto corner_now:c.corners){
107 | if (corner_last.x_grid == corner_now.x_grid && corner_last.y_grid == corner_now.y_grid){
108 | dist += std::sqrt((corner_last.x - corner_now.x) * (corner_last.x - corner_now.x) + (corner_last.y - corner_now.y) * (corner_last.y - corner_now.y));
109 | point_1.emplace_back(cv::Point2f(corner_last.x, corner_last.y));
110 | point_2.emplace_back(cv::Point2f(corner_now.x, corner_now.y));
111 | }
112 | }
113 | }
114 | cv::Mat H = cv::findHomography(point_1, point_2);
115 | cv::Mat h1 = H.col(0);
116 | cv::Mat h2 = H.col(1);
117 | cv::Mat h3 = H.col(2);
118 |
119 | double lambda = 1.0 / cv::norm(h1);
120 |
121 | cv::Mat r1 = lambda * h1;
122 | cv::Mat r2 = lambda * h2;
123 | cv::Mat r3 = r1.cross(r2);
124 |
125 | rotationMatrix = (cv::Mat_(3, 3) << r1.at(0), r2.at(0), r3.at(0),
126 | r1.at(1), r2.at(1), r3.at(1),
127 | r1.at(2), r2.at(2), r3.at(2));
128 |
129 | cv::SVDecomp(rotationMatrix, w, u, vt);
130 | rotationMatrix = u * vt;
131 | cv::Rodrigues(rotationMatrix, rvec);
132 |
133 | double reprojection_error = computeReprojectionError(H, point_1, point_2);
134 | dist /= last_circle.circles.size();
135 | if (dist > convent_camera_->image_height_ * 0.1 && reprojection_error < 1 && cv::norm(rvec) > 0.03){
136 | legal_corner_size++;
137 | last_corner = c;
138 | corner_buffer_selected.emplace_back(c);
139 | }
140 | }
141 | std::cout << "corner num:" << legal_corner_size << "\n";
142 | return legal_corner_size;
143 | }
144 | int getCircleBufferNum(){
145 | circle_buffer_selected.clear();
146 | legal_circle_size = 0;
147 | if (circle_buffer_.empty()) return 0;
148 | cv::Mat w, u, vt, rotationMatrix, rvec;
149 | for (auto cir:circle_buffer_){
150 | if ((cir.circles[0].timestamp - circle_buffer_.front().circles[0].timestamp).toSec() > epoch_time) break;
151 | static bool first_inside = true;
152 | if (first_inside){
153 | first_inside = false;
154 | last_circle = cir;
155 | circle_buffer_selected.emplace_back(cir);
156 | }
157 | float dist = 0;
158 | std::vector point_1, point_2;
159 | for (auto circle_last:last_circle.circles){
160 | for (auto circle_now:cir.circles){
161 | if (circle_last.x_grid == circle_now.x_grid && circle_last.y_grid == circle_now.y_grid){
162 | dist += std::sqrt((circle_last.x - circle_now.x) * (circle_last.x - circle_now.x) + (circle_last.y - circle_now.y) * (circle_last.y - circle_now.y));
163 | point_1.emplace_back(cv::Point2f(circle_last.x, circle_last.y));
164 | point_2.emplace_back(cv::Point2f(circle_now.x, circle_now.y));
165 | }
166 | }
167 | }
168 | cv::Mat H = cv::findHomography(point_1, point_2);
169 | cv::Mat h1 = H.col(0);
170 | cv::Mat h2 = H.col(1);
171 | cv::Mat h3 = H.col(2);
172 |
173 | double lambda = 1.0 / cv::norm(h1);
174 |
175 | cv::Mat r1 = lambda * h1;
176 | cv::Mat r2 = lambda * h2;
177 | cv::Mat r3 = r1.cross(r2);
178 |
179 | rotationMatrix = (cv::Mat_(3, 3) << r1.at(0), r2.at(0), r3.at(0),
180 | r1.at(1), r2.at(1), r3.at(1),
181 | r1.at(2), r2.at(2), r3.at(2));
182 |
183 | cv::SVDecomp(rotationMatrix, w, u, vt);
184 | rotationMatrix = u * vt;
185 | cv::Rodrigues(rotationMatrix, rvec);
186 |
187 | double reprojection_error = computeReprojectionError(H, point_1, point_2);
188 | dist /= last_circle.circles.size();
189 | if (dist > event_camera_->image_height_ * 0.2 && reprojection_error < 0.5 && cv::norm(rvec) > 0.03){
190 | legal_circle_size++;
191 | last_circle = cir;
192 | circle_buffer_selected.emplace_back(cir);
193 | }
194 | }
195 | std::cout << "circle num:" << legal_circle_size << "\n";
196 | return legal_circle_size;
197 | }
198 |
199 | bool solveRelativePose(const corner_msgs::cornerArray& corners, Camera::Ptr cam, cv::Mat& Transformation);
200 | bool solveRelativePose(const circle_msgs::circleArray& circles, Camera::Ptr cam, cv::Mat& Transformation);
201 | void refinePose(const corner_msgs::cornerArray& corners, Camera::Ptr cam, cv::Mat& Transformation);
202 | void buildProblem(ceres::Problem* problem, std::vector imagePoints, std::vector worldPoints, Camera::Ptr cam, cv::Mat& Transformation);
203 |
204 | std::vector> corner_image_cluster, circle_image_cluster, circle_image_cluster_random;
205 | std::vector> corner_world_cluster, circle_world_cluster, circle_world_cluster_random;
206 |
207 | bool b_conv_initialized, b_ev_initialized, b_both_initialized;
208 | double rms_conv, rms_ev;
209 | corner_msgs::cornerArray last_corner;
210 | circle_msgs::circleArray last_circle;
211 | int legal_corner_size, legal_circle_size;
212 |
213 | Trajectory::Ptr trajectory_;
214 | Camera::Ptr event_camera_, convent_camera_;
215 | CalibBoard::Ptr calib_board_;
216 |
217 | cv::Mat convCameraMatrix, convDistCoeffs;
218 | std::vector convRvecs, convTvecs;
219 | cv::Mat evCameraMatrix, evDistCoeffs;
220 | std::vector evRvecs, evTvecs;
221 |
222 | struct SnavelyReprojectionError;
223 | ceres::Problem problem;
224 | double rot[3], trans[3];
225 |
226 | friend class EstimatorManager;
227 | };
228 |
229 | };
--------------------------------------------------------------------------------
/estimator/include/trajectory_estimator.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include "factor/ceres_local_param.h"
6 | #include
7 | #include
8 | #include
9 |
10 | #include
11 | #include
12 | #include
13 | #include
14 |
15 | namespace estimator{
16 | class TrajectoryEstimator{
17 | static ceres::Problem::Options DefaultProblemOptions()
18 | {
19 | ceres::Problem::Options options;
20 | options.loss_function_ownership = ceres::TAKE_OWNERSHIP;
21 | options.local_parameterization_ownership = ceres::DO_NOT_TAKE_OWNERSHIP;
22 | return options;
23 | }
24 |
25 | public:
26 | EIGEN_MAKE_ALIGNED_OPERATOR_NEW
27 |
28 | typedef std::shared_ptr Ptr;
29 |
30 | TrajectoryEstimator(Trajectory::Ptr trajectory, Camera::Ptr event_camera, Camera::Ptr convent_camera, CalibBoard::Ptr calib_board);
31 |
32 | ~TrajectoryEstimator(){
33 | delete analytic_local_parameterization_;
34 | }
35 |
36 | void addConvFeature(const corner_msgs::corner &corner, ros::Time timestamp);
37 |
38 | void addEventFeature(const circle_msgs::circle &circle);
39 |
40 | void addControlPoints(const SplineMeta &spline_meta, std::vector &vec, std::shared_ptr problem, bool addPosKnot = false, bool isEvent = false);
41 |
42 | ceres::Solver::Summary solve(int max_iterations, bool progress, int num_threads);
43 | ceres::Solver::Summary solve1(int max_iterations, bool progress, int num_threads);
44 |
45 | class UpdateTrajectoryCallback : public ceres::IterationCallback {
46 | public:
47 | UpdateTrajectoryCallback(TrajectoryEstimator* estimator) : estimator_(estimator) {}
48 |
49 | virtual ceres::CallbackReturnType operator()(const ceres::IterationSummary& summary) {
50 | return ceres::SOLVER_CONTINUE;
51 | }
52 |
53 | private:
54 | TrajectoryEstimator* estimator_;
55 | };
56 |
57 | std::shared_ptr problem_, problem_1;
58 | std::vector> trajectory_info_;
59 |
60 | double max_time_delay = 0.05; // unit: second
61 |
62 | private:
63 | Trajectory::Ptr trajectory_;
64 |
65 | Camera::Ptr event_camera_, convent_camera_;
66 | CalibBoard::Ptr calib_board_;
67 |
68 | // std::shared_ptr problem_;
69 | ceres::LocalParameterization *analytic_local_parameterization_;
70 |
71 | friend class UpdateTrajectoryCallback;
72 | };
73 |
74 | } //namespace estimator
--------------------------------------------------------------------------------
/estimator/include/trajectory_manager.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include "trajectory_estimator.h"
3 | #include
4 |
5 | namespace estimator
6 | {
7 |
8 | class TrajectoryManager
9 | {
10 | public:
11 | EIGEN_MAKE_ALIGNED_OPERATOR_NEW
12 |
13 | typedef std::shared_ptr Ptr;
14 |
15 | TrajectoryManager(Trajectory::Ptr trajectory);
16 |
17 | void extendTrajectory(int64_t max_time, SE3d now_knot);
18 | void setOriginalPose();
19 |
20 | private:
21 | Trajectory::Ptr trajectory_;
22 |
23 | int64_t max_bef_ns;
24 | int max_bef_idx;
25 | int max_aft_idx;
26 | };
27 |
28 | } //namespace estimator
29 |
--------------------------------------------------------------------------------
/estimator/include/trajectory_viewer.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 | #include
12 |
13 | #include
14 | #include
15 |
16 | namespace estimator
17 | {
18 | class TrajectoryViewer
19 | {
20 | private:
21 | ros::Publisher pub_cam_pose_;
22 |
23 | // PublishSplineTrajectory
24 | ros::Publisher pub_spline_trajectory_;
25 | ros::Publisher pub_spline_trajectory_before_opt_;
26 |
27 | public:
28 | void SetPublisher(ros::NodeHandle &nh)
29 | {
30 | pub_cam_pose_ = nh.advertise("/camera/pose", 10);
31 |
32 | /// spline trajectory
33 | pub_spline_trajectory_ = nh.advertise("/spline/trajectory", 10);
34 | pub_spline_trajectory_before_opt_ = nh.advertise("/spline/trajectory_bef_opt", 10);
35 | std::cout << "[SetPublisher] init done.\n";
36 | }
37 |
38 | void PublishPose(SE3d pose, int cam_type, int id, bool refresh = false)
39 | {
40 | ros::Time time_now = ros::Time::now();
41 | visualization_msgs::MarkerArray marker_array;
42 |
43 | if (refresh){
44 | visualization_msgs::Marker marker;
45 | marker.action = visualization_msgs::Marker::DELETEALL;
46 | marker_array.markers.push_back(marker);
47 |
48 | // Publish the MarkerArray message
49 | pub_cam_pose_.publish(marker_array);
50 |
51 | return;
52 | }
53 |
54 | geometry_msgs::PoseStamped poseIinG;
55 | poseIinG.header.stamp = time_now;
56 | poseIinG.header.frame_id = "/map";
57 | tf::pointEigenToMsg(pose.translation(), poseIinG.pose.position);
58 | tf::quaternionEigenToMsg(pose.unit_quaternion(),
59 | poseIinG.pose.orientation);
60 |
61 | visualization_msgs::Marker marker;
62 | marker.header.frame_id = "/map";
63 | marker.header.stamp = time_now;
64 | marker.ns = "pose_markers";
65 | marker.id = id;
66 | marker.type = visualization_msgs::Marker::SPHERE;
67 | marker.action = visualization_msgs::Marker::ADD;
68 | marker.pose = poseIinG.pose;
69 |
70 | if (cam_type == 1){
71 | marker.scale.x = 2;
72 | marker.scale.y = 2;
73 | marker.scale.z = 2;
74 | marker.color.r = 1.0;
75 | marker.color.g = 0.0;
76 | marker.color.b = 0.0;
77 | marker.color.a = 0.8;
78 | }else if (cam_type == 0){
79 | marker.scale.x = 5;
80 | marker.scale.y = 5;
81 | marker.scale.z = 5;
82 | marker.color.r = 0.3;
83 | marker.color.g = 0.3;
84 | marker.color.b = 0.9;
85 | marker.color.a = 0.9;
86 | }else if (cam_type == 2){
87 | marker.scale.x = 2;
88 | marker.scale.y = 2;
89 | marker.scale.z = 2;
90 | marker.color.r = 0.0;
91 | marker.color.g = 1.0;
92 | marker.color.b = 0.0;
93 | marker.color.a = 0.9;
94 | }else if (cam_type == 3){
95 | marker.scale.x = 5;
96 | marker.scale.y = 5;
97 | marker.scale.z = 5;
98 | marker.color.r = 0.5;
99 | marker.color.g = 0.5;
100 | marker.color.b = 0.5;
101 | marker.color.a = 0.9;
102 | }
103 |
104 | marker_array.markers.push_back(marker);
105 | pub_cam_pose_.publish(marker_array);
106 | }
107 |
108 | void PublishSplineTrajectory(Trajectory::Ptr trajectory, std::vector> trajectory_info, int64_t min_time, int continue_num,
109 | int64_t max_time, int64_t dt, bool stage)
110 | {
111 | ros::Time time_now = ros::Time::now();
112 | ros::Time t_temp;
113 |
114 | if (stage){
115 | if (pub_spline_trajectory_.getNumSubscribers() != 0)
116 | {
117 | std::vector poses_geo;
118 | int num = -1;
119 | for (auto traj:trajectory_info){
120 | if ((traj.second - traj.first) >= continue_num){
121 | PublishPose(trajectory->poseNs(std::max(0, (traj.first - trajectory->DEG + 1)) * trajectory->getDtNs()), 3, num--);
122 | for (int64_t t = std::max(0, (traj.first - trajectory->DEG + 1)) * trajectory->getDtNs(); t < std::min(max_time, (traj.second - 3) * trajectory->getDtNs()); t += dt){
123 | SE3d pose = trajectory->poseNs(t);
124 | geometry_msgs::PoseStamped poseIinG;
125 | poseIinG.header.stamp = t_temp.fromSec(t * NS_TO_S);
126 | poseIinG.header.frame_id = "/map";
127 | tf::pointEigenToMsg(pose.translation(), poseIinG.pose.position);
128 | tf::quaternionEigenToMsg(pose.unit_quaternion(),
129 | poseIinG.pose.orientation);
130 | poses_geo.push_back(poseIinG);
131 | }
132 | PublishPose(trajectory->poseNs(std::min(max_time, (traj.second - 3) * trajectory->getDtNs()) - 0.0001 * S_TO_NS), 0, num--);
133 | }
134 | }
135 |
136 | nav_msgs::Path traj_path;
137 | traj_path.header.stamp = time_now;
138 | traj_path.header.frame_id = "/map";
139 | traj_path.poses = poses_geo;
140 |
141 | pub_spline_trajectory_.publish(traj_path);
142 | }
143 | }
144 | else{
145 | if (pub_spline_trajectory_before_opt_.getNumSubscribers() != 0)
146 | {
147 | std::vector poses_geo;
148 | for (int64_t t = min_time; t < max_time; t += dt)
149 | {
150 | int start_cp_idx = (long long)(t) / trajectory->getDtNs();
151 | for (auto traj:trajectory_info){
152 | if ((traj.second - traj.first) >= continue_num && start_cp_idx >= traj.first && start_cp_idx <= traj.second){
153 | SE3d pose = trajectory->poseNs(t);
154 | geometry_msgs::PoseStamped poseIinG;
155 | poseIinG.header.stamp = t_temp.fromSec(t * NS_TO_S);
156 | poseIinG.header.frame_id = "/map";
157 | tf::pointEigenToMsg(pose.translation(), poseIinG.pose.position);
158 | tf::quaternionEigenToMsg(pose.unit_quaternion(),
159 | poseIinG.pose.orientation);
160 | poses_geo.push_back(poseIinG);
161 | }
162 | }
163 | }
164 |
165 | nav_msgs::Path traj_path;
166 | traj_path.header.stamp = time_now;
167 | traj_path.header.frame_id = "/map";
168 | traj_path.poses = poses_geo;
169 |
170 | pub_spline_trajectory_before_opt_.publish(traj_path);
171 | }
172 | }
173 | }
174 | };
175 |
176 | } // namespace estimator
--------------------------------------------------------------------------------
/estimator/launch/estimator.launch:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/estimator/package.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | estimator
4 | 0.0.0
5 | The estimator package
6 |
7 |
8 |
9 |
10 | root
11 |
12 |
13 |
14 |
15 |
16 | TODO
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | catkin
52 | circle_msgs
53 | corner_msgs
54 | opencv3
55 | roscpp
56 | rosbag
57 | circle_msgs
58 | corner_msgs
59 | opencv3
60 | roscpp
61 | rosbag
62 | circle_msgs
63 | corner_msgs
64 | opencv3
65 | roscpp
66 | rosbag
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/estimator/rviz/estimator.rviz:
--------------------------------------------------------------------------------
1 | Panels:
2 | - Class: rviz/Displays
3 | Help Height: 104
4 | Name: Displays
5 | Property Tree Widget:
6 | Expanded:
7 | - /Global Options1
8 | - /Status1
9 | - /Grid1
10 | - /Path1
11 | - /MarkerArray1
12 | - /Path2
13 | Splitter Ratio: 0.5
14 | Tree Height: 470
15 | - Class: rviz/Selection
16 | Name: Selection
17 | - Class: rviz/Tool Properties
18 | Expanded:
19 | - /2D Pose Estimate1
20 | - /2D Nav Goal1
21 | - /Publish Point1
22 | Name: Tool Properties
23 | Splitter Ratio: 0.5886790156364441
24 | - Class: rviz/Views
25 | Expanded:
26 | - /Current View1
27 | Name: Views
28 | Splitter Ratio: 0.5
29 | - Class: rviz/Time
30 | Experimental: false
31 | Name: Time
32 | SyncMode: 0
33 | SyncSource: ""
34 | Preferences:
35 | PromptSaveOnExit: true
36 | Toolbars:
37 | toolButtonStyle: 2
38 | Visualization Manager:
39 | Class: ""
40 | Displays:
41 | - Alpha: 0.5
42 | Cell Size: 50
43 | Class: rviz/Grid
44 | Color: 160; 160; 164
45 | Enabled: true
46 | Line Style:
47 | Line Width: 0.029999999329447746
48 | Value: Lines
49 | Name: Grid
50 | Normal Cell Count: 0
51 | Offset:
52 | X: 0
53 | Y: 0
54 | Z: 0
55 | Plane: XY
56 | Plane Cell Count: 10
57 | Reference Frame:
58 | Value: true
59 | - Alpha: 1
60 | Buffer Length: 1
61 | Class: rviz/Path
62 | Color: 25; 255; 0
63 | Enabled: true
64 | Head Diameter: 0.30000001192092896
65 | Head Length: 0.20000000298023224
66 | Length: 0.30000001192092896
67 | Line Style: Lines
68 | Line Width: 0.029999999329447746
69 | Name: Path
70 | Offset:
71 | X: 0
72 | Y: 0
73 | Z: 0
74 | Pose Color: 255; 85; 255
75 | Pose Style: None
76 | Radius: 0.029999999329447746
77 | Shaft Diameter: 0.10000000149011612
78 | Shaft Length: 0.10000000149011612
79 | Topic: /spline/trajectory
80 | Unreliable: false
81 | Value: true
82 | - Class: rviz/MarkerArray
83 | Enabled: true
84 | Marker Topic: /camera/pose
85 | Name: MarkerArray
86 | Namespaces:
87 | {}
88 | Queue Size: 100
89 | Value: true
90 | - Alpha: 1
91 | Buffer Length: 1
92 | Class: rviz/Path
93 | Color: 114; 159; 207
94 | Enabled: true
95 | Head Diameter: 0.30000001192092896
96 | Head Length: 0.20000000298023224
97 | Length: 0.30000001192092896
98 | Line Style: Lines
99 | Line Width: 0.029999999329447746
100 | Name: Path
101 | Offset:
102 | X: 0
103 | Y: 0
104 | Z: 0
105 | Pose Color: 255; 85; 255
106 | Pose Style: None
107 | Radius: 0.029999999329447746
108 | Shaft Diameter: 0.10000000149011612
109 | Shaft Length: 0.10000000149011612
110 | Topic: /spline/trajectory_bef_opt
111 | Unreliable: false
112 | Value: true
113 | Enabled: true
114 | Global Options:
115 | Background Color: 48; 48; 48
116 | Default Light: true
117 | Fixed Frame: map
118 | Frame Rate: 30
119 | Name: root
120 | Tools:
121 | - Class: rviz/Interact
122 | Hide Inactive Objects: true
123 | - Class: rviz/MoveCamera
124 | - Class: rviz/Select
125 | - Class: rviz/FocusCamera
126 | - Class: rviz/Measure
127 | - Class: rviz/SetInitialPose
128 | Theta std deviation: 0.2617993950843811
129 | Topic: /initialpose
130 | X std deviation: 0.5
131 | Y std deviation: 0.5
132 | - Class: rviz/SetGoal
133 | Topic: /move_base_simple/goal
134 | - Class: rviz/PublishPoint
135 | Single click: true
136 | Topic: /clicked_point
137 | Value: true
138 | Views:
139 | Current:
140 | Class: rviz/Orbit
141 | Distance: 758.266845703125
142 | Enable Stereo Rendering:
143 | Stereo Eye Separation: 0.05999999865889549
144 | Stereo Focal Distance: 1
145 | Swap Stereo Eyes: false
146 | Value: false
147 | Focal Point:
148 | X: -229.94384765625
149 | Y: -173.66639709472656
150 | Z: 72.33602905273438
151 | Focal Shape Fixed Size: true
152 | Focal Shape Size: 0.05000000074505806
153 | Invert Z Axis: false
154 | Name: Current View
155 | Near Clip Distance: 0.009999999776482582
156 | Pitch: 1.2997965812683105
157 | Target Frame:
158 | Value: Orbit (rviz)
159 | Yaw: 0.9103978872299194
160 | Saved: ~
161 | Window Geometry:
162 | Displays:
163 | collapsed: false
164 | Height: 846
165 | Hide Left Dock: false
166 | Hide Right Dock: false
167 | QMainWindow State: 000000ff00000000fd00000004000000000000017b0000028cfc0200000008fb0000001200530065006c0065006300740069006f006e00000001e10000009b0000008200fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c00610079007301000000520000028c0000011e00fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c0000026100000001000001110000028cfc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a0056006900650077007301000000520000028c000000e000fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e10000019700000003000005da00000045fc0100000002fb0000000800540069006d00650100000000000005da0000034f00fffffffb0000000800540069006d006501000000000000045000000000000000000000033c0000028c00000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000
168 | Selection:
169 | collapsed: false
170 | Time:
171 | collapsed: false
172 | Tool Properties:
173 | collapsed: false
174 | Views:
175 | collapsed: false
176 | Width: 1498
177 | X: 3505
178 | Y: 647
179 |
--------------------------------------------------------------------------------
/estimator/src/estimator/trajectory_estimator.cpp:
--------------------------------------------------------------------------------
1 | #include "trajectory_estimator.h"
2 |
3 | namespace estimator{
4 |
5 | TrajectoryEstimator::TrajectoryEstimator(Trajectory::Ptr trajectory, Camera::Ptr event_camera, Camera::Ptr convent_camera, CalibBoard::Ptr calib_board)
6 | :trajectory_(trajectory), event_camera_(event_camera), convent_camera_(convent_camera), calib_board_(calib_board)
7 | {
8 | problem_ = std::make_shared(DefaultProblemOptions());
9 | problem_1 = std::make_shared(DefaultProblemOptions());
10 | analytic_local_parameterization_ = new LieAnalyticLocalParameterization();
11 | }
12 |
13 | void TrajectoryEstimator::addControlPoints(
14 | const SplineMeta &spline_meta, std::vector &vec, std::shared_ptr problem,
15 | bool addPosKnot, bool fixed){
16 | for (auto const &seg : spline_meta.segments)
17 | {
18 | size_t start_idx = trajectory_->computeTIndexNs(seg.t0_ns).second;
19 | for (size_t i = start_idx; i < (start_idx + seg.NumParameters()); ++i){
20 | if (addPosKnot){
21 | vec.emplace_back(trajectory_->getKnotPos(i).data());
22 | problem->AddParameterBlock(vec.back(), 3);
23 | if (fixed){
24 | problem->SetParameterBlockConstant(vec.back());
25 | }
26 | }
27 | else{
28 | vec.emplace_back(trajectory_->getKnotSO3(i).data());
29 | problem->AddParameterBlock(vec.back(), 4, analytic_local_parameterization_);
30 | if (fixed){
31 | problem->SetParameterBlockConstant(vec.back());
32 | }
33 | }
34 | }
35 | }
36 | }
37 |
38 | void TrajectoryEstimator::addConvFeature(const corner_msgs::corner &corner, ros::Time timestamp){
39 | SplineMeta spline_meta;
40 | int64_t data_start_time = trajectory_->getDataStartTime();
41 | trajectory_->CaculateSplineMeta({{timestamp.toSec() * S_TO_NS - data_start_time - max_time_delay * S_TO_NS, timestamp.toSec() * S_TO_NS - data_start_time + max_time_delay * S_TO_NS}}, spline_meta);
42 | ceres::CostFunction *cost_function_conv = new ConventFactor(corner, timestamp, spline_meta.segments.at(0), convent_camera_, calib_board_, data_start_time);
43 |
44 | std::vector vec;
45 | addControlPoints(spline_meta, vec, problem_1, false, true); // 4 * 4
46 | addControlPoints(spline_meta, vec, problem_1, true, true); // 4 * 4 + 4 * 3
47 |
48 | // add intrinsic & distortion
49 | vec.emplace_back(convent_camera_->intrinsicParams);
50 | problem_1->AddParameterBlock(convent_camera_->intrinsicParams, 4);
51 | vec.emplace_back(convent_camera_->distortParams);
52 | problem_1->AddParameterBlock(convent_camera_->distortParams, 5);
53 |
54 | // add extrinsic
55 | vec.emplace_back(convent_camera_->R_C2W.data()); // 4 * 7 + 9 + 4
56 | problem_1->AddParameterBlock(vec.back(), 4, analytic_local_parameterization_);
57 | vec.emplace_back(convent_camera_->t_C2W.data()); // 4 * 7 + 9 + 7
58 | problem_1->AddParameterBlock(vec.back(), 3);
59 |
60 | // add time delay
61 | vec.emplace_back(&convent_camera_->time_delay); // 4 * 7 + 9 + 7 + 1 = 45 dims
62 | problem_1->AddParameterBlock(vec.back(), 1);
63 | problem_1->SetParameterLowerBound(&convent_camera_->time_delay, 0, -max_time_delay);
64 | problem_1->SetParameterUpperBound(&convent_camera_->time_delay, 0, max_time_delay);
65 |
66 | problem_1->AddResidualBlock(cost_function_conv, new ceres::HuberLoss(2.0), vec);
67 | }
68 |
69 | void TrajectoryEstimator::addEventFeature(const circle_msgs::circle &circle){
70 | SplineMeta spline_meta;
71 | int64_t data_start_time = trajectory_->getDataStartTime();
72 |
73 | trajectory_->CaculateSplineMeta({{circle.timestamp.toSec() * S_TO_NS - data_start_time, circle.timestamp.toSec() * S_TO_NS - data_start_time}}, spline_meta);
74 | ceres::CostFunction *cost_function_ev = new EventFactor(circle, spline_meta.segments.at(0), event_camera_, calib_board_, data_start_time);
75 |
76 | std::vector vec;
77 | addControlPoints(spline_meta, vec, problem_, false); // 4 * 4
78 | addControlPoints(spline_meta, vec, problem_, true); // 4 * 4 + 4 * 3
79 |
80 | // add intrinsic & distortion
81 | vec.emplace_back(event_camera_->intrinsicParams); // 4 * 7 + 4
82 | problem_->AddParameterBlock(vec.back(), 4);
83 | vec.emplace_back(event_camera_->distortParams); // 4 * 7 + 4 + 5
84 | problem_->AddParameterBlock(vec.back(), 5);
85 |
86 | problem_->AddResidualBlock(cost_function_ev, NULL, vec);
87 |
88 | for (float ratio_st = 0.3; ratio_st < 1.0; ratio_st += 0.5){
89 | SplineMeta spline_meta1;
90 | trajectory_->CaculateSplineMeta({{(circle.start_ts.toSec() + (circle.timestamp - circle.start_ts).toSec() * ratio_st) * S_TO_NS - data_start_time, (circle.start_ts.toSec() + (circle.timestamp - circle.start_ts).toSec() * ratio_st) * S_TO_NS - data_start_time}}, spline_meta1);
91 | ceres::CostFunction *cost_function_ev1 = new EventFactor(circle, spline_meta1.segments.at(0), event_camera_, calib_board_, data_start_time, 1, ratio_st);
92 |
93 | std::vector vec1;
94 | addControlPoints(spline_meta1, vec1, problem_, false); // 4 * 4
95 | addControlPoints(spline_meta1, vec1, problem_, true); // 4 * 4 + 4 * 3
96 |
97 | // add intrinsic & distortion
98 | vec1.emplace_back(event_camera_->intrinsicParams); // 4 * 7 + 4
99 | problem_->AddParameterBlock(vec1.back(), 4);
100 | vec1.emplace_back(event_camera_->distortParams); // 4 * 7 + 4 + 5
101 | problem_->AddParameterBlock(vec1.back(), 5);
102 |
103 | problem_->AddResidualBlock(cost_function_ev1, NULL, vec1);
104 | }
105 |
106 | for (float ratio_end = 0.3; ratio_end < 1.0; ratio_end += 0.5){
107 | SplineMeta spline_meta2;
108 |
109 | trajectory_->CaculateSplineMeta({{(circle.end_ts.toSec() + (circle.timestamp - circle.end_ts).toSec() * ratio_end) * S_TO_NS - data_start_time, (circle.end_ts.toSec() + (circle.timestamp - circle.end_ts).toSec() * ratio_end) * S_TO_NS - data_start_time}}, spline_meta2);
110 | ceres::CostFunction *cost_function_ev2 = new EventFactor(circle, spline_meta2.segments.at(0), event_camera_, calib_board_, data_start_time, 2, ratio_end);
111 |
112 | std::vector vec2;
113 | addControlPoints(spline_meta2, vec2, problem_, false); // 4 * 4
114 | addControlPoints(spline_meta2, vec2, problem_, true); // 4 * 4 + 4 * 3
115 |
116 | // add intrinsic & distortion
117 | vec2.emplace_back(event_camera_->intrinsicParams); // 4 * 7 + 4
118 | problem_->AddParameterBlock(vec2.back(), 4);
119 | vec2.emplace_back(event_camera_->distortParams); // 4 * 7 + 4 + 5
120 | problem_->AddParameterBlock(vec2.back(), 5);
121 |
122 | problem_->AddResidualBlock(cost_function_ev2, NULL, vec2);
123 | }
124 | }
125 |
126 | ceres::Solver::Summary TrajectoryEstimator::solve(int max_iterations = 50, bool progress = false, int num_threads = 1){
127 | ceres::Solver::Options options;
128 |
129 | options.minimizer_type = ceres::TRUST_REGION;
130 | options.trust_region_strategy_type = ceres::LEVENBERG_MARQUARDT;
131 | options.linear_solver_type = ceres::SPARSE_NORMAL_CHOLESKY;
132 | options.minimizer_progress_to_stdout = progress;
133 | options.max_num_iterations = num_threads;
134 | options.logging_type = ceres::PER_MINIMIZER_ITERATION;
135 | options.update_state_every_iteration = true;
136 | UpdateTrajectoryCallback callback(this);
137 | options.callbacks.push_back(&callback);
138 |
139 | if (num_threads < 1){
140 | num_threads = 1; // std::thread::hardware_concurrency(); // mine is 8
141 | }
142 | options.num_threads = num_threads;
143 | options.max_num_iterations = max_iterations;
144 |
145 | ceres::Solver::Summary summary;
146 | ceres::Solve(options, problem_.get(), &summary);
147 |
148 | std::cout << summary.BriefReport() << std::endl;
149 |
150 | return summary;
151 | }
152 |
153 | ceres::Solver::Summary TrajectoryEstimator::solve1(int max_iterations = 50, bool progress = false, int num_threads = 1){
154 | ceres::Solver::Options options;
155 |
156 | options.minimizer_type = ceres::TRUST_REGION;
157 | options.trust_region_strategy_type = ceres::LEVENBERG_MARQUARDT;
158 | options.linear_solver_type = ceres::DENSE_NORMAL_CHOLESKY;
159 | options.minimizer_progress_to_stdout = progress;
160 | options.max_num_iterations = num_threads;
161 | options.logging_type = ceres::PER_MINIMIZER_ITERATION;
162 | options.parameter_tolerance = 1e-12;
163 |
164 | if (num_threads < 1){
165 | num_threads = 1; // std::thread::hardware_concurrency(); // mine is 8
166 | }
167 | options.num_threads = num_threads;
168 | options.max_num_iterations = max_iterations;
169 |
170 | ceres::Solver::Summary summary;
171 | ceres::Solve(options, problem_1.get(), &summary); //old
172 |
173 | std::cout << summary.BriefReport() << std::endl;
174 |
175 | return summary;
176 | }
177 |
178 |
179 |
180 | } // namespace estimator
--------------------------------------------------------------------------------
/estimator/src/estimator/trajectory_manager.cpp:
--------------------------------------------------------------------------------
1 | #include "trajectory_manager.h"
2 |
3 | namespace estimator{
4 |
5 | TrajectoryManager::TrajectoryManager(Trajectory::Ptr trajectory)
6 | : trajectory_(trajectory)
7 | {
8 |
9 | }
10 |
11 | void TrajectoryManager::extendTrajectory(int64_t max_time, SE3d now_knot){
12 | double max_bef = trajectory_->maxTimeNs() * NS_TO_S;
13 | max_bef_ns = trajectory_->maxTimeNs();
14 | max_bef_idx = trajectory_->cpnum() - 1;
15 |
16 | trajectory_->extendKnotsTo(max_time, now_knot); // maxTime>=max_time
17 | double max_aft = trajectory_->maxTimeNs() * NS_TO_S;
18 | max_aft_idx = trajectory_->cpnum() - 1;
19 | }
20 |
21 | } //namespace estimator
--------------------------------------------------------------------------------
/estimator/src/estimator_node.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * EF-Calib
3 | * Copyright (C) 2023 Shaoan Wang
4 | *
5 | * This program is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License as published by
7 | * the Free Software Foundation, either version 3 of the License, or
8 | * (at your option) any later version.
9 | *
10 | * This program is distributed in the hope that it will be useful,
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with this program. If not, see .
17 | */
18 |
19 | #include "estimator_manager.h"
20 |
21 | #include
22 | #include
23 |
24 | using namespace estimator;
25 |
26 | int main(int argc, char **argv)
27 | {
28 | google::InitGoogleLogging(argv[0]);
29 |
30 | ros::init(argc, argv, "estimator");
31 | ros::NodeHandle nh("~");
32 |
33 | std::string config_path;
34 | nh.param("config_path", config_path, "../config/setup.yaml");
35 | ROS_INFO("Estimator load %s.", config_path.c_str());
36 | YAML::Node config_node = YAML::LoadFile(config_path);
37 | std::string log_path = config_node["log_path"].as();
38 | FLAGS_log_dir = log_path;
39 | FLAGS_colorlogtostderr = true;
40 | LOG(INFO) << "Start Estimator";
41 |
42 | EstimatorManager estimator_manager(config_node, nh);
43 |
44 | ros::spin();
45 |
46 | return 0;
47 | }
48 |
49 |
--------------------------------------------------------------------------------
/estimator/src/sophus_lib/average.hpp:
--------------------------------------------------------------------------------
1 | #ifndef SOPHUS_AVERAGE_HPP
2 | #define SOPHUS_AVERAGE_HPP
3 |
4 | #include "common.hpp"
5 | #include "rxso2.hpp"
6 | #include "rxso3.hpp"
7 | #include "se2.hpp"
8 | #include "se3.hpp"
9 | #include "sim2.hpp"
10 | #include "sim3.hpp"
11 | #include "so2.hpp"
12 | #include "so3.hpp"
13 |
14 | namespace Sophus {
15 |
16 | template
17 | optional iterativeMean(
18 | SequenceContainer const& foo_Ts_bar, int max_num_iterations) {
19 | size_t N = foo_Ts_bar.size();
20 | SOPHUS_ENSURE(N >= 1, "N must be >= 1.");
21 |
22 | using Group = typename SequenceContainer::value_type;
23 | using Scalar = typename Group::Scalar;
24 | using Tangent = typename Group::Tangent;
25 |
26 | // This implements the algorithm in the beginning of Sec. 4.2 in
27 | // ftp://ftp-sop.inria.fr/epidaure/Publications/Arsigny/arsigny_rr_biinvariant_average.pdf.
28 | Group foo_T_average = foo_Ts_bar.front();
29 | Scalar w = Scalar(1. / N);
30 | for (int i = 0; i < max_num_iterations; ++i) {
31 | Tangent average;
32 | setToZero(average);
33 | for (Group const& foo_T_bar : foo_Ts_bar) {
34 | average += w * (foo_T_average.inverse() * foo_T_bar).log();
35 | }
36 | Group foo_T_newaverage = foo_T_average * Group::exp(average);
37 | if (squaredNorm(
38 | (foo_T_newaverage.inverse() * foo_T_average).log()) <
39 | Constants::epsilon()) {
40 | return foo_T_newaverage;
41 | }
42 |
43 | foo_T_average = foo_T_newaverage;
44 | }
45 | // LCOV_EXCL_START
46 | return nullopt;
47 | // LCOV_EXCL_STOP
48 | }
49 |
50 | // Mean implementation for SO(2).
51 | template
53 | enable_if_t<
54 | std::is_same>::value,
55 | optional>
56 | average(SequenceContainer const& foo_Ts_bar) {
57 | // This implements rotational part of Proposition 12 from Sec. 6.2 of
58 | // ftp://ftp-sop.inria.fr/epidaure/Publications/Arsigny/arsigny_rr_biinvariant_average.pdf.
59 | size_t N = std::distance(std::begin(foo_Ts_bar), std::end(foo_Ts_bar));
60 | SOPHUS_ENSURE(N >= 1, "N must be >= 1.");
61 | SO2 foo_T_average = foo_Ts_bar.front();
62 | Scalar w = Scalar(1. / N);
63 |
64 | Scalar average(0);
65 | for (SO2 const& foo_T_bar : foo_Ts_bar) {
66 | average += w * (foo_T_average.inverse() * foo_T_bar).log();
67 | }
68 | return foo_T_average * SO2::exp(average);
69 | }
70 |
71 | // Mean implementation for RxSO(2).
72 | template
74 | enable_if_t<
75 | std::is_same>::value,
76 | optional>
77 | average(SequenceContainer const& foo_Ts_bar) {
78 | size_t N = std::distance(std::begin(foo_Ts_bar), std::end(foo_Ts_bar));
79 | SOPHUS_ENSURE(N >= 1, "N must be >= 1.");
80 | RxSO2 foo_T_average = foo_Ts_bar.front();
81 | Scalar w = Scalar(1. / N);
82 |
83 | Vector2 average(Scalar(0), Scalar(0));
84 | for (RxSO2 const& foo_T_bar : foo_Ts_bar) {
85 | average += w * (foo_T_average.inverse() * foo_T_bar).log();
86 | }
87 | return foo_T_average * RxSO2::exp(average);
88 | }
89 |
90 | namespace details {
91 | template
92 | void getQuaternion(T const&);
93 |
94 | template
95 | Eigen::Quaternion getUnitQuaternion(SO3 const& R) {
96 | return R.unit_quaternion();
97 | }
98 |
99 | template
100 | Eigen::Quaternion getUnitQuaternion(RxSO3 const& sR) {
101 | return sR.so3().unit_quaternion();
102 | }
103 |
104 | template
106 | Eigen::Quaternion averageUnitQuaternion(
107 | SequenceContainer const& foo_Ts_bar) {
108 | // This: http://stackoverflow.com/a/27410865/1221742
109 | size_t N = std::distance(std::begin(foo_Ts_bar), std::end(foo_Ts_bar));
110 | SOPHUS_ENSURE(N >= 1, "N must be >= 1.");
111 | Eigen::Matrix Q(4, N);
112 | int i = 0;
113 | Scalar w = Scalar(1. / N);
114 | for (auto const& foo_T_bar : foo_Ts_bar) {
115 | Q.col(i) = w * details::getUnitQuaternion(foo_T_bar).coeffs();
116 | ++i;
117 | }
118 |
119 | Eigen::Matrix QQt = Q * Q.transpose();
120 | // TODO: Figure out why we can't use SelfAdjointEigenSolver here.
121 | Eigen::EigenSolver> es(QQt);
122 |
123 | std::complex max_eigenvalue = es.eigenvalues()[0];
124 | Eigen::Matrix, 4, 1> max_eigenvector =
125 | es.eigenvectors().col(0);
126 |
127 | for (int i = 1; i < 4; i++) {
128 | if (std::norm(es.eigenvalues()[i]) > std::norm(max_eigenvalue)) {
129 | max_eigenvalue = es.eigenvalues()[i];
130 | max_eigenvector = es.eigenvectors().col(i);
131 | }
132 | }
133 | Eigen::Quaternion quat;
134 | quat.coeffs() << //
135 | max_eigenvector[0].real(), //
136 | max_eigenvector[1].real(), //
137 | max_eigenvector[2].real(), //
138 | max_eigenvector[3].real();
139 | return quat;
140 | }
141 | } // namespace details
142 |
143 | // Mean implementation for SO(3).
144 | //
145 | // TODO: Detect degenerated cases and return nullopt.
146 | template
148 | enable_if_t<
149 | std::is_same>::value,
150 | optional>
151 | average(SequenceContainer const& foo_Ts_bar) {
152 | return SO3(details::averageUnitQuaternion(foo_Ts_bar));
153 | }
154 |
155 | // Mean implementation for R x SO(3).
156 | template
158 | enable_if_t<
159 | std::is_same>::value,
160 | optional>
161 | average(SequenceContainer const& foo_Ts_bar) {
162 | size_t N = std::distance(std::begin(foo_Ts_bar), std::end(foo_Ts_bar));
163 |
164 | SOPHUS_ENSURE(N >= 1, "N must be >= 1.");
165 | Scalar scale_sum = Scalar(0);
166 | using std::exp;
167 | using std::log;
168 | for (RxSO3 const& foo_T_bar : foo_Ts_bar) {
169 | scale_sum += log(foo_T_bar.scale());
170 | }
171 | return RxSO3(exp(scale_sum / Scalar(N)),
172 | SO3(details::averageUnitQuaternion(foo_Ts_bar)));
173 | }
174 |
175 | template
177 | enable_if_t<
178 | std::is_same>::value,
179 | optional>
180 | average(SequenceContainer const& foo_Ts_bar, int max_num_iterations = 20) {
181 | // TODO: Implement Proposition 12 from Sec. 6.2 of
182 | // ftp://ftp-sop.inria.fr/epidaure/Publications/Arsigny/arsigny_rr_biinvariant_average.pdf.
183 | return iterativeMean(foo_Ts_bar, max_num_iterations);
184 | }
185 |
186 | template
188 | enable_if_t<
189 | std::is_same