Skip to content

Commit 64a9f59

Browse files
authored
Fix so3 log (#40)
* fix so3 log * read g2o
1 parent af424ec commit 64a9f59

4 files changed

Lines changed: 57 additions & 11 deletions

File tree

Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "tiny-solver"
3-
version = "0.14.1"
3+
version = "0.14.2"
44
edition = "2021"
55
authors = ["Powei Lin <poweilin1994@gmail.com>"]
66
readme = "README.md"
@@ -43,7 +43,9 @@ python = ["num-dual/python", "numpy", "pyo3"]
4343
[dev-dependencies]
4444
env_logger = "0.11.6"
4545
itertools = "0.14.0"
46+
nalgebra = { version = "0.33.2", features = ["rand"] }
4647
plotters = "0.3.6"
48+
rand = "0.8.5"
4749

4850
[profile.dev.package.faer]
4951
opt-level = 3

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ Inspired by [ceres-solver](https://github.com/ceres-solver/ceres-solver), [tiny-
99

1010
This is a general optimizer written in Rust, including bindings for Python. If you're familiar with ceres-solver or factor-graph optimizers, you'll find it very easy to use.
1111

12+
#### Other great rust optimizers
13+
* [factrs](https://github.com/rpl-cmu/factrs)
14+
* [sophus-rs](https://github.com/sophus-vision/sophus-rs)
15+
1216
## Installation
1317
### rust
1418
```sh

src/manifold/so3.rs

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -66,16 +66,30 @@ impl<T: na::RealField> SO3<T> {
6666
}
6767

6868
pub fn log(&self) -> na::DVector<T> {
69-
let xi = na::dvector![self.qx.clone(), self.qy.clone(), self.qz.clone()];
70-
// Abs value in case we had a negative quaternion
71-
let w = self.qw.clone().abs();
69+
const EPS: f64 = 1e-6;
70+
let ivec = na::dvector![self.qx.clone(), self.qy.clone(), self.qz.clone()];
7271

73-
let norm_v = xi.norm();
74-
if norm_v < T::from_f64(1e-3).unwrap() {
75-
xi * T::from_f64(2.0).unwrap()
76-
} else {
77-
xi * norm_v.clone().atan2(w) * T::from_f64(2.0).unwrap() / norm_v
78-
}
72+
let squared_n = ivec.norm_squared();
73+
let w = self.qw.clone();
74+
75+
let near_zero = squared_n.le(&T::from_f64(EPS * EPS).unwrap());
76+
77+
let w_sq = w.clone() * w.clone();
78+
let t0 = T::from_f64(2.0).unwrap() / w.clone()
79+
- T::from_f64(2.0 / 3.0).unwrap() * squared_n.clone() / (w_sq * w.clone());
80+
81+
let n = squared_n.sqrt();
82+
83+
let sign = T::from_f64(-1.0)
84+
.unwrap()
85+
.select(w.le(&T::zero()), T::one());
86+
let atan_nbyw = sign.clone() * n.clone().atan2(sign * w);
87+
88+
let t = T::from_f64(2.0).unwrap() * atan_nbyw / n;
89+
90+
let two_atan_nbyd_by_n = t0.select(near_zero, t);
91+
92+
ivec * two_atan_nbyd_by_n
7993
}
8094

8195
pub fn hat(xi: na::VectorView3<T>) -> na::Matrix3<T> {
@@ -137,7 +151,7 @@ impl<T: na::RealField> SO3<T> {
137151
let qz = w0.clone() * z1.clone() + x0.clone() * y1.clone() - y0.clone() * x1.clone()
138152
+ z0.clone() * w1.clone();
139153
let qw = w0 * w1 - x0 * x1 - y0 * y1 - z0 * z1;
140-
154+
// println!("q {}", self.to_vec());
141155
SO3 { qx, qy, qz, qw }
142156
}
143157
}

tests/test_manifold.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#[cfg(test)]
2+
mod tests {
3+
use nalgebra as na;
4+
use std::f64::consts::PI;
5+
6+
use tiny_solver::manifold::so3::SO3;
7+
8+
fn equal_to_na(so3: &SO3<f64>) -> bool {
9+
let q = so3.to_vec();
10+
let na_so3 =
11+
na::UnitQuaternion::from_quaternion(na::Quaternion::new(q[3], q[0], q[1], q[2]));
12+
let diff = so3.log() - na_so3.scaled_axis();
13+
diff.norm() < 1e-6
14+
}
15+
16+
#[test]
17+
fn test_so3() {
18+
for _ in 0..10000 {
19+
let mut rvec = na::DVector::new_random(3);
20+
rvec /= rvec.norm();
21+
rvec *= rand::random::<f64>() * PI;
22+
let r = SO3::exp(rvec.as_view());
23+
assert!(equal_to_na(&r));
24+
}
25+
}
26+
}

0 commit comments

Comments
 (0)