-
Notifications
You must be signed in to change notification settings - Fork 209
Changes for hamilton quaternion convention. #34
Conversation
@jgoppert I want to keep the ecl EKF able to run as a standalone library without reliance on other PX4 libraries. I will have a look at the changes to conventions implemented in this PR and see how much work is involved to do this for all EKF operations. |
OK, my goal would be to have ecl using matrix for math like the rest of the px4 code base to reduce the firmware size and make sure we have independent unit testing for the math. Let me know if there is anything I can do on my end to make it easier for you to use or more portable. |
The adaption to an existing quaternion convention is very welcome to me as I had problems with this library's quaternion convention in the past. The multiplication is the most important piece of the adaption and I'm really glad to see it change but I hope you are aware that other calculations like for example the dcm conversion also need to be compliant to the convention to have the perfectly predictable behavior. I'm not sure if this is already the case and just wanted to mention. If you wish I would gladly take a look at all this soon. |
@MaEtUgR I believe we have covered all conversions in the unit tests. For instance if you look at the changes in this diff to unit testing you see the impact of the quaternion multplication changes. Basically, it just inverts the multiplication order. https://github.com/PX4/Matrix/pull/34/files#diff-f27a9c721212dea87f37cd226eb7bd04L324 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I checked multiplication and rotation matrix conversion with this paper: http://www.iri.upc.edu/people/jsola/JoanSola/objectes/notes/kinematics.pdf
@priseborough as it was already mentioned this should only imply opposite order of all quaternion multiplications... |
Now also closes #30. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as a suggestion you could also do:
invert() {
this = this.inversed();
}
to keep this logic in one place
matrix/Quaternion.hpp
Outdated
q(1) *= -1; | ||
q(2) *= -1; | ||
q(3) *= -1; | ||
Type n = q.dot(q); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
q.dot(q)
gives you the norm squared but you want to devide by the norm here.
I would just use the norm method that we already have here n = q.norm()
which would use this correct implementation: https://github.com/PX4/Matrix/blob/master/matrix/Vector.hpp#L73
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should have called the variable nsq to be clear. But for inverse believe it is conjugate over norm sq.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is over the norm (without square) because the definition of the inverse is:
q * q^-1 = q^-1 * q = q_I where the identity quaternion q_I = [1 0 0 0] has unit length.
You can infer from the quaternion multiplication that |q^-1| = 1/|q|.
The matlab implementation is over norm and you can also look up a definition somwhere else for example https://www.j3d.org/matrix_faq/matrfaq_latest.html for example says:
How do I calculate the inverse of a quaternion?
----------------------------------------------------
This is equivalent to calculating the conjugate of the quaternion,
if the quaternion is normalized (or a unit quaternion).
In all other cases, the magnitude of the inverse is 1/|q|.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here is a check using sympy with our quaternion product. You cannot assume unit length here. This works for unit length and non-unit length quaternions: https://github.com/jgoppert/iekf_analysis/blob/master/Quaternion%20Inverse%20Check.ipynb. I'm using non-unit quaternions for the IEFK estimator I'm developing, so it is important that it works for both.
Also, look again here: https://www.mathworks.com/help/aerotbx/ug/quatinv.html
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I'll have a look at it asap.
The part with not assuming unit length is clear, I only have to recheck the norm squared thing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I checked, you are right! Sorry, my fault.
matrix/Quaternion.hpp
Outdated
ret(1) = -q(1); | ||
ret(2) = -q(2); | ||
ret(3) = -q(3); | ||
Type n = q.dot(q); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same as last comment.
btw a test to identify this issue would be:
q = [.5 0 0 0]
q.inv() == [2 0 0 0]
matrix/Matrix.hpp
Outdated
@@ -290,8 +290,8 @@ class Matrix | |||
|
|||
void print() const | |||
{ | |||
char buf[200]; | |||
write_string(buf, 200); | |||
char buf[1000]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's pretty evil on an embedded system. We need to find a better way than this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's only for debug prints but nevertheless I agree. What would be your proposition? Dynamic allocation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I think dynamic allocation would be a good way to control it, make print take a length arg with a default of 200? We could also possibly do it intelligently with field width etc based on the size of matrix.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, but if dynamic then using the fields for the exact size. Otherwise we have two disadavantages.
@jgoppert @priseborough we currently have matrix cloned twice in PX4 which is a bit confusing. Could we just use the version that comes in through ecl?
|
@dagar I'm usually bumping matrix a lot as I work on the estimators. I don't think we want to make IEKF and LPE dependent on ecl, it would slow down my workflow. Currently it seems ekf2 is grabbing the px4 version of matrix anyways because it blows up when I pull in the hamilton quaternion patch. ecl should probably only have a copy of matrix lib for test purposes and use installed version or version in parent repo where possible which it seems to be doing anyways, but it is confusing having the other copy sitting there. I think this happens because we compile ecl as a px4 module and the default matrix lib path comes ahead of the local path in the includes. |
matrix/Matrix.hpp
Outdated
@@ -290,8 +290,8 @@ class Matrix | |||
|
|||
void print() const | |||
{ | |||
char buf[1000]; | |||
write_string(buf, 1000); | |||
char * buf = new char[15*N*M]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you really want to spend 15 characters for every number? Seems excessive to me... but maybe you have other applications.
matrix/Matrix.hpp
Outdated
@@ -499,8 +499,8 @@ bool isEqual(const Matrix<Type, M, N> &x, | |||
|
|||
|
|||
if (!equal) { | |||
char buf_x[100]; | |||
char buf_y[100]; | |||
char * buf_x = new char[100]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is it then not dynamic here? We already generate the char array on the heap for print.
The easiest solution could be something like:
printf("not equal:\n x:\n");
x.print();
printf("\ny:\n");
y.print();
I just compressed the commits so this is easier to review. |
It seems like coveralls stopped getting new builds. https://coveralls.io/github/PX4/Matrix |
88e4767
to
2c614c6
Compare
This is cleaned up again to the minimal diff. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I appreciate that you narrowed down the diff again to the original topic that I already checked and recommended some time ago.
The lines and issues reviewed are not part of this pr anymore
Is there any concern with merging this or why is it still pending? |
@MaEtUgR We would need to get ekf2 converted over to this convention. |
@priseborough Can you help me get this into ekf2 so we can close it. You should just have to modify your derivation script for the new quaternion multiplication definition. |
OK, let's set up a call and you can walk me through the changes. I do wish to keep the ecl EKF portable and usable outside the PX4 framework. |
@priseborough great, I will contact you via skype. |
Switching multiplications should not be too much of a hassle, let me know if I can help anywhere to speed things up 👍 |
@jgoppert @priseborough @LorenzMeier Can we take this in now please? I want to push the switch to the useful hamilton convention now. It's pending forever and if we are not switching soon the current way will also be used in a lot more interfaces (ask @julianoes and @mrivi ) and we will have the PX4 quaternion convention in future student text books. Sorry for making this so urgent but otherwise nothing will ever happen. If noone writes back I'll merge and start converting multiplication to make submodule updates possible... |
I will create a PR for the ecl library that uses the revised multiplication order and reference it to this PR. |
I've been through the ecl and have a branch with the required changes https://github.com/PX4/ecl/tree/pr-ekfQuatMultOrder I'm happy for this PR to be merged. |
Let's start testing and adapting in PRs for all the clients. |
* M_n = C_nb * M_b * C_nb^(-1) (matrix rotation) | ||
* | ||
* or similarly | ||
* v_b = q_nb^1 * [0;v_n] * q_nb |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A bit late to the party, but there is a -
missing, should be v_b = q_nb^-1 ...
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, there's another ^(-1) missing. Can you quickly review #48 please?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again late to the party, it's merged. Thanks for spotting 👍
It is about time we got this switched over. Can we get the necessary changes in to EKF2 @priseborough . Closes #29.
The changes to the unit test should show you all that will need to be changed in EKF2.