Madgwick filter - Quaternion Multiplication

80 views (last 30 days)
I am trying to replicate the Madgwick filter just to learn from it. I am stuck at the multiplication to become the objective function.
This is what I got:
% Let:
q = [q1 q2 q3 q4];
qstar = [q1 -q2 -q3 -q4]; % conjugate of q
a = [0 ax ay az];
g = [0 0 0 1];
% fg(q,a) = qstar ⦻ g ⦻ q - a
Answer:
fg(q,a) =
0
2*q2*q4 - 2*q1*q3 - ax
2*q1*q2 - ay + 2*q3*q4
q1^2 - q2^2 - q3^2 + q4^2 - az
This is the answer from the paper:
There are other variables that should not be there. Can someone tell me why this is?
Here is the full code:
syms q1 q2 q3 q4 gx gy gz ax ay az mx my mz bx bz by h hx hy dx dy dz sx sy sz
q = [q1 q2 q3 q4];
cong_q = [q1 -q2 -q3 -q4];
gyro = [0 gx gy gz];
a = [0 ax ay az];
b = [0 bx 0 bz];
d = [0 dx dy dz];
s = [0 sx sy sz];
m = [0 mx my mz];
wg = [0 0 0 1];
% Gyroscope
qdotg = quatmul(q,gyro);
% Function and Jacobian for Accelerometer
fg1 = quatmul(cong_q,wg);
fg = quatmul(fg1,q) - transpose(a);
fg(1) = [];
Jg = jacobian(fg, [q1, q2, q3, q4]);
% Function and Jacobian for Magnetometer
fb = quatmul(cong_q,b);
fb = quatmul(fb,q) - transpose(m);
fb(1) = [];
Jb = jacobian(fb, [q1, q2, q3, q4]);
% Total function
Jg_t_fg = transpose(Jg)*fg;
Jb_t_fb = transpose(Jb)*fb;
ftot = Jg_t_fg + Jb_t_fb;
fnorm = ftot * (1/sqrt(ftot(1)^2+ftot(2)^2+ftot(3)^2+ftot(4)^2));
function quatprod = quatmul(r, k)
% r * k
quatprod1 = r(1)*k(1) - r(2)*k(2) - r(3)*k(3) - r(4)*k(4);
quatprod2 = r(1)*k(2) + r(2)*k(1) + r(3)*k(4) - r(4)*k(3);
quatprod3 = r(1)*k(3) - r(2)*k(4) + r(3)*k(1) + r(4)*k(2);
quatprod4 = r(1)*k(4) + r(2)*k(3) - r(3)*k(2) + r(4)*k(1);
quatprod = [quatprod1;quatprod2;quatprod3;quatprod4];
end
A similar problem occurs also for the other functions.
If anything is missing, let me know and I will add it. Thank you.
  3 Comments
James Tursa
James Tursa on 1 Dec 2020
Not sure what you are asking. You want the Symbolic Toolbox to automatically simplify and/or select a preferred expression using the unity constraint? How will it know which one you prefer?
Neel Mehta
Neel Mehta on 1 Dec 2020
Hi James, I was trying to figure this one out and I still haven't really managed to do this till now. So I have thought of this: Lets create a function called quatsimp(q) that simplifies quaternions.
function simp = quatsimp(q)
end
In this case, if there is a line with 4 cubed quaternion components for example:
That we can say that, we shall implement the quaternion normalisation rule.
So in this case the function will take the case and convert it to:
and using substitution will give us
I really want to do this symbolic to prove a point.
I hope this makes sense.

Sign in to comment.

Accepted Answer

James Tursa
James Tursa on 1 Dec 2020
I guess you could generate the following three expressions symbolically:
original
original + 1 - ()
original + () - 1
and then have some logic to pick the "simplest" from the three outcomes. Maybe "simplest" is simply the shortest one when converted to a char string?
  12 Comments
James Tursa
James Tursa on 12 Dec 2020
OK, here is a version that attempts to discover the symbols used for the quaternion elements first before simplifying.
% Simplify symbolic expression containing quaternion elements by
% using the identity q1^2 + q2^2 + q3^2 + q4^2 = 1.
% This code is vectorized.
% Programmer: James Tursa
% Date: 2020-12-11
function Q = simplifyq(q)
% possible quaternion element variables
syms q0 q1 q2 q3 q4 Q0 Q1 Q2 Q3 Q4 qw qx qy Qw Qx Qy QW QX QY qW qX qY
qz = sym('qz'); % need to do qz a special way
qZ = sym('qZ'); % need to do qZ a special way
Qz = sym('Qz'); % need to do Qz a special way
QZ = sym('QZ'); % need to do QZ a special way
% figure out which symbols are present
Q = expand(simplify(q));
V = symvar(sum(Q(:))); % get the variables
q0_present = ismember(q0,V) || ismember(Q0,V);
q4_present = ismember(q4,V) || ismember(Q4,V);
qn_present = ismember(q0,V) || ismember(q1,V) || ismember(q2,V) || ismember(q3,V) || ismember(q4,V);
Qn_present = ismember(Q0,V) || ismember(Q1,V) || ismember(Q2,V) || ismember(Q3,V) || ismember(Q4,V);
qx_present = ismember(qw,V) || ismember(qx,V) || ismember(qy,V) || ismember(qz,V);
qX_present = ismember(qW,V) || ismember(qX,V) || ismember(qY,V) || ismember(qZ,V);
Qx_present = ismember(Qw,V) || ismember(Qx,V) || ismember(Qy,V) || ismember(Qz,V);
QX_present = ismember(QW,V) || ismember(QX,V) || ismember(QY,V) || ismember(QZ,V);
qsum = qn_present + Qn_present + qx_present + qX_present + Qx_present + QX_present;
if( (q0_present && q4_present) || qsum > 1 )
error('Ambiguous quaternion elements found');
elseif( qsum == 0 )
warning('No quaternion elements found');
return;
elseif( ismember(q0,V) )
P1 = q0; P2 = q1; P3 = q2; P4 = q3;
elseif( ismember(Q0,V) )
P1 = Q0; P2 = Q1; P3 = Q2; P4 = Q3;
elseif( qn_present )
P1 = q1; P2 = q2; P3 = q3; P4 = q4;
elseif( Qn_present )
P1 = Q1; P2 = Q2; P3 = Q3; P4 = Q4;
elseif( qx_present )
P1 = qw; P2 = qx; P3 = qy; P4 = qz;
elseif( qX_present )
P1 = qW; P2 = qX; P3 = qY; P4 = qZ;
elseif( Qx_present )
P1 = Qw; P2 = Qx; P3 = Qy; P4 = Qz;
elseif( QX_present )
P1 = QW; P2 = QX; P3 = QY; P4 = QZ;
else
error('Internal coding error ... contact author');
end
% first cut at substitutions
qsubs = {P1,P1;
P1^2,1-P2^2-P3^2-P4^2;
P2^2,1-P1^2-P3^2-P4^2;
P3^2,1-P1^2-P2^2-P4^2;
P4^2,1-P1^2-P2^2-P3^2};
isq = @(s)s==P1||s==P2||s==P3||s==P4; % helper function
% vectorization loop
for m=1:numel(Q)
q = Q(m);
% find the substitution with the shortest length
s = q;
sc = char(s); sc(sc==' ')=[];
sn = length(sc);
for k=1:size(qsubs,1)
t = simplify(subs(q,qsubs{k,1},qsubs{k,2})); % substitute from above list
tc = char(t); tc(tc==' ')=[];
tn = length(tc);
if( tn < sn )
s = t;
sn = tn;
end
end
% now look for factors that we can combine
v = symvar(s); % get the variables
[C,T] = coeffs(s); % get coefficients and terms
S = 0; % initialize our final answer
for k=1:numel(v) % for each variable
if( ~isq(v(k)) ) % if variable isn't a quaternion element
b = arrayfun(@(x)ismember(v(k),symvar(x)),T); % which terms contain v(k)
S = S + prod(factor(sum(C(b).*T(b)))); % rewrite as prod of factors
C(b) = []; T(b) = []; % eliminate these terms we already used
end
end
Q(m) = S + sum(C.*T); % add in anything left over
end
end
Neel Mehta
Neel Mehta on 12 Dec 2020
Wow! That's a great effort. You should upload it on the file exchange. I'm sure alot of people would be very thankful.

Sign in to comment.

More Answers (0)

Categories

Find more on Satellite Mission Analysis in Help Center and File Exchange

Products


Release

R2020b

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!