I am looking to understand how to implement a simple loop closing algorithm.
Here is my situation : I have x numbers of point clouds, and I have used a registration algorithm in 3d that has given me the pose of all these point clouds.
In the end, I more or less end up at the same point in my map, but with drift. I can use my registration algorithm to see where my actual final point cloud is located in regards to my initial one.
Knowing that, I would like to globally optimize the rest of my point clouds all the way to my initial one, based on the "drift" that I calculated.
I have managed to quickly code something in regards to the translation, which seems correct, but the rotation is problematic, as in the accuracy/superposition of features (walls etc) is reduced.
What I have been looking at : g2o, GTSAM, ISAM libraries, all looking to optimize, but I feel like there is a huge overhead to implementing those, they all require multiple constraints, setting huge number of parameters etc..
I am not even looking to detect loops automatically (I'll do that later), I would just like to do: These two point clouds represent a loop, propagate (correctly) the drift in translation and rotation(that I calculate) between them to all point clouds between the two.
Thanks in advance,
I was waiting for this question. In my master degree I developed exactly that, a global optimization function to refine rotations in loopclosure cases. The product of all rotations in a circuit should be the SO(3) identity, but because of errors (drift), it's something close to that.
The trick is to compose rotations using both ways of the circuit, this will give you two independent rotations for each pose. Then, just interpolate between them using a interval proportional to the error acumulated in that pose. To do the interpolation you'll need to map all rotation matrix to quaternions (use a quaternion library), and then use SLERP (Spherical Linear Interpolation) tecnique to interpolate between them.
I'm going to explain the intervals using the image above. Supose that you have a circuit of TLS (Terrestrial Laser Scanner) stations around a object, like in (a). It can be any other type of circuit, with any geometry, and don't matter the number of clouds. Chose any cloud as the global origin, here it is the station in E0. If you register the clouds sequentialy, the registration will return relative poses:
Txy means the transformation which transform the point cloud on the reference frame x to y, it is a relative transformation between two adjacent clouds. Now, take the rotation
R
from each relative transformationT
and multyply in this order:R04*R43*R32*R21*R10
. What you'll obtain? If the pairwise registration has occurred with no gross errors, you wi'll find a matrix close to the identity matrix, wich represent nule rotation. If you use quaternions, you'll find something close to the quaternion identity:q_error = (1,0,0,0)
.Figure (b) show relative rotations composed to the global origin in clockwise order:
Figure (c) shows relative rotations composed in anti-clockwise order:
The optimal rotations (R_xy_opt) will be the interpolated rotations between both absolute rotations above:
Here, R_x0 means the rotarion which transform the system i to the global origin E0. Pay attention to the interpolation intervals, they increase linearly: (1/n, 2/n, ..., n-1/n), it is because the error in last poses are bigger than the error in first ones. Doing the interpolation, you'll distribute and also decrease the total error along the circuit.
To do the interpolation you need to map rotations to quaternions:
do the same with rotations in anti-clockwise order:
Now, just use the SLERP technique to do the interpolation between them:
Here q_opt_x is the optimized absolute rotation in the form of a quaternion.
Next image is a circuit with 901 clouds, accumulated error in each pairwise registration makes two ends not coincide. After the global optimization this error is backpropagated and the ends coincide. These results were obtained using two global optimizations, one for rotations, which I explained above, and another for translations, as suggested in (LU & MILIOS, 1997). Only one closed circuit is being treated. There's no use of graphs here. When dealing with this kind of optimization you naturally think in how to use multiply sobrepositions, g2o library can do this using multiples constraints within a graph, but I don't understand how it works. I only know how to optimize one closed circuit, in a graph things get complicated.
Animation of my work in the NCLT (North Campus Long Term) LiDAR dataset: https://www.youtube.com/shorts/XAiHnahhgpg
Here is my GitHub repositore with all fuctions necessary to do loop closure in a circuit and also a lot of other things related to point clouds. Everething is in python and uses only Open3D and a quaternion library. All functions are in the scrip "minhas_funcoes.py"
https://github.com/RubensBenevides/Point-Cloud-Registration-with-Global-Refinement