Skip to content

Commit e7570f6

Browse files
committed
fix inconsitency in jacobian(::Type{RotMatrix}, ::QuatRotation)
addresses #290
1 parent 2662b4f commit e7570f6

File tree

2 files changed

+44
-41
lines changed

2 files changed

+44
-41
lines changed

src/derivatives.jl

+29-39
Original file line numberDiff line numberDiff line change
@@ -26,53 +26,43 @@ ith_partial{N}(X::SMatrix{9,N}, i) = @SMatrix([X[1,i] X[4,i] X[7,i];
2626
jacobian(::Type{output_param}, R::input_param)
2727
Returns the jacobian for transforming from the input rotation parameterization
2828
to the output parameterization, centered at the value of R.
29+
Note that
30+
* if `input_param <: QuatRotation`, a unit quaternion is assumed and
31+
the jacobian is with respect to the four parameters of a unit quaternion.
32+
* if `input_param <: Quaternion`, a general (with arbitrary norm)
33+
quaternion is assumed and the jacobian is with respect to a general quaternion
2934
3035
jacobian(R::rotation_type, X::AbstractVector)
3136
Returns the jacobian for rotating the vector X by R.
3237
"""
33-
function jacobian(::Type{RotMatrix}, q::QuatRotation)
34-
w = real(q.q)
35-
x, y, z = imag_part(q.q)
36-
37-
# let q = s * qhat where qhat is a unit quaternion and s is a scalar,
38-
# then R = RotMatrix(q) = RotMatrix(s * qhat) = s * RotMatrix(qhat)
39-
40-
# get R(q)
41-
# R = q[:] # FIXME: broken with StaticArrays 0.4.0 due to https://github.com/JuliaArrays/StaticArrays.jl/issues/128
42-
R = SVector(Tuple(q))
43-
44-
# solve d(s*R)/dQ (because its easy)
45-
dsRdQ = @SMatrix [ 2*w 2*x -2*y -2*z ;
46-
2*z 2*y 2*x 2*w ;
47-
-2*y 2*z -2*w 2*x ;
48-
49-
-2*z 2*y 2*x -2*w ;
50-
2*w -2*x 2*y -2*z ;
51-
2*x 2*w 2*z 2*y ;
52-
53-
2*y 2*z 2*w 2*x ;
54-
-2*x -2*w 2*z 2*y ;
55-
2*w -2*x -2*y 2*z ]
56-
57-
# get s and dsdQ
58-
# s = 1
59-
dsdQ = @SVector [2*w, 2*x, 2*y, 2*z]
60-
61-
# now R(q) = (s*R) / s
62-
# so dR/dQ = (s * d(s*R)/dQ - (s*R) * ds/dQ) / s^2
63-
# = (d(s*R)/dQ - R*ds/dQ) / s
38+
function jacobian(::Type{RotMatrix}, q::Quaternion)
39+
s = abs(q)
40+
w = real(q) / s
41+
x, y, z = imag_part(q) ./ s
42+
qhat = SVector{4}(w, x, y, z) # unit quaternion
43+
44+
dRdqhat = _jacobian_unit_quat(qhat)
45+
46+
dqhatdq = (I(4) - qhat * qhat') ./ s
47+
dRdqhat * dqhatdq
48+
end
6449

65-
# now R(q) = (R(s*q)) / s for scalar s, because RotMatrix(s * q) = s * RotMatrix(q)
66-
#
67-
# so dR/dQ = (dR(s*q)/dQ*s - R(s*q) * ds/dQ) / s^2
68-
# = (dR(s*q)/dQ*s - s*R(q) * ds/dQ) / s^2
69-
# = (dR(s*q)/dQ - R(q) * ds/dQ) / s
7050

71-
jac = dsRdQ - R * transpose(dsdQ)
51+
jacobian(::Type{RotMatrix}, q::QuatRotation) = _jacobian_unit_quat(params(q))
7252

73-
# now reformat for output. TODO: is the the best expression?
74-
# return Vec{4,Mat{3,3,T}}(ith_partial(jac, 1), ith_partial(jac, 2), ith_partial(jac, 3), ith_partial(jac, 4))
7553

54+
@inline function _jacobian_unit_quat(qhat::SVector{4})
55+
# jacobian of vec(RotMatrix(QuatRotation(qhat, false)))
56+
w, x, y, z = qhat
57+
return @SMatrix [ 2*w 2*x -2*y -2*z ;
58+
2*z 2*y 2*x 2*w ;
59+
-2*y 2*z -2*w 2*x ;
60+
-2*z 2*y 2*x -2*w ;
61+
2*w -2*x 2*y -2*z ;
62+
2*x 2*w 2*z 2*y ;
63+
2*y 2*z 2*w 2*x ;
64+
-2*x -2*w 2*z 2*y ;
65+
2*w -2*x -2*y 2*z ]
7666
end
7767

7868

test/derivative_tests.jl

+15-2
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,26 @@ using ForwardDiff
1111
@testset "Jacobian checks" begin
1212

1313
# Quaternion to rotation matrix
14+
@testset "Jacobian (Quaternion -> RotMatrix)" begin
15+
for i in 1:10 # do some repeats
16+
q = rand(QuaternionF64) # a random quaternion, **not** normalized
17+
18+
# test jacobian to a Rotation matrix
19+
R_jac = Rotations.jacobian(RotMatrix, q)
20+
FD_jac = ForwardDiff.jacobian(x -> SVector{9}(QuatRotation(x[1], x[2], x[3], x[4])), # normalize
21+
SVector(q.s, q.v1, q.v2, q.v3))
22+
23+
# compare
24+
@test FD_jac R_jac
25+
end
26+
end
1427
@testset "Jacobian (QuatRotation -> RotMatrix)" begin
1528
for i in 1:10 # do some repeats
16-
q = rand(QuatRotation) # a random quaternion
29+
q = rand(QuatRotation) # a random quaternion (normalized)
1730

1831
# test jacobian to a Rotation matrix
1932
R_jac = Rotations.jacobian(RotMatrix, q)
20-
FD_jac = ForwardDiff.jacobian(x -> SVector{9}(QuatRotation(x[1], x[2], x[3], x[4])),
33+
FD_jac = ForwardDiff.jacobian(x -> SVector{9}(QuatRotation(x[1], x[2], x[3], x[4], false)), # don't normalize
2134
Rotations.params(q))
2235

2336
# compare

0 commit comments

Comments
 (0)