-
Notifications
You must be signed in to change notification settings - Fork 57
Fix VRPN, improve tracker layout #146
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Fix VRPN, improve tracker layout #146
Conversation
Thanks a lot for this! This is great, but I have a few suggestions to make this even better and probably avoid some additional work in the future. In the last few commits to Somebody will have to do that anyway at some point, so while you are working on the tracker API, why not change it to 3D and be done with it? We can of course keep some (or all?) trackers operating in 2D for now, but the API could (and IMHO should!) already be in 3D. To handle calculations with rotations, there is the There are automatic conversions in place to implicitly convert to and from I'm not quite sure about this, but it might make sense to use Some further questions and thoughts about the suggested
|
src/tracker.h
Outdated
// Azimuth value at calibration in degree | ||
std::atomic<double> azi_correction{0.0}; | ||
|
||
struct Tracker_data |
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.
This data type seems overkill to me. Why not just use ssr::quat
?
Fix 90 deg offset Fix rebase onto master and revert additional rotations Fix mouse handling listener Fix Head tracking rotation
0e59e5f
to
fde501c
Compare
Thanks for the feedback!!
I'm not sure if I used this correctly. It seems the gml functions can not be applied directly yet, because they return a gml type which can't be used within the SSR it seems.
I was not able to use this correctly.
I'll look into this again, but I think this was one way I could come up to use the VRPN callback.
I see what you mean. But is this really necessary? If we say that our reset function is just resetting whatever comes out of any tracker, then, this should be independent of the child classes.
Yes, that is great!
Yes you are absolutely right. |
Yes, you are using it as intended (but I'm not sure about the "inverse" stuff): Lines 60 to 61 in 5aca323
C++ doesn't auto-convert We could theoretically add conversion operators to the
I would hope that this gets optimized away, but it would nice to know for sure ...
Yeah, we should think about thread-safety. It's probably not a big deal with a single I think we could simply use the "global controller lock" for this. {
auto control = _controller.take_control();
// access "correction" value
control->reference_rotation_offset(corrected_value)
} This way, the "correction" value can never be accessed by two threads at the same time.
I'm not saying you shouldn't use instance variables.
Well nothing is really necessary. There are always multiple ways to do things. I think you should look at It really just needs to be able to do one thing, namely It needs no other member functions nor any member variables. Anything else you are adding is just for code re-use. It's not because it is needed in the interface. Therefore, I think it would make more sense to create an intermediate
But some trackers (like Intersense) have a built-in "reset" functionality. Therefore we would add an additional, unneeded "correction" method.
Well it just has to call
You don't really have to hide it from the child tracker. It can have an arbitrary signature in its "constructor" (whether this is the real constructor or some static function). But none of this has to be part of the
I don't understand, it looks like it inherits from Line 46 in 1068831
|
} | ||
}; | ||
|
||
// Converting from yaw (around Z), pitch (around Y), roll (around X) to quaternions. |
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.
This is ambiguous and also not true.
-
ambiguous: the rotations are around the local coordinate axes (after the axes themselves have been rotated). I'm not sure what's the best way to say this un-ambiguously.
I personally find it better understandable to describe it as three global rotations in a given order (first "roll" then "pitch" then "yaw"). -
not true: "pitch" is a rotation around a local X axis, because the default orientation is "north".
At least that's how I think it should be in the SSR, other systems will differ.
This stuff is complicated, so I might well be totally wrong about it.
I think it's best just not to create an "official" function for it, since it's a one-liner anyway.
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.
Oh, I see... I just ran into the same problem as in the GUI head-tracker PR earlier.
We have to document this somewhere. Really.
- I refer to yaw pitch roll as Tait-Bryan angles (I thought this is the most "common" definition).
More specifically, the aerospace norm/ nautical angles.
At least for me, an airplane first yaws on ground, then pitches during take-off and then rolls around its forward (nose) axis. - I'm not sure if I would understand "north" orientation just from reading it. I think less ambiguous is Y-forward, Z-up. That's at least how coordinate systems got communicated to me, when in doubt (always assuming right handed coordinate systems). Did I understand correctly and do you agree?
We should also make sure to use this convention of YPR consistently across all trackers and maybe even the SSR. I'm not sure if this is the case atm. I thought that at least the Razor uses X-forward, Z-up and the YPR definition above.
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.
We have to document this somewhere. Really.
I agree.
My first attempt is there: https://audioscenedescriptionformat.readthedocs.io/en/latest/position-orientation.html
But this can be for sure improved, and we should mention it in the SSR docs somewhere.
I refer to yaw pitch roll as Tait-Bryan angles (I thought this is the most "common" definition).
Yeah, I prefer those, too. But there are still 6 possible conventions (not considering left-handed systems and not even considering where x, y and z point to in the real world).
I don't really like the aerospace convention because the z axis points down. I don't think that's intuitive in our situation and for our community.
I'm not sure if I would understand "north" orientation just from reading it. I think less ambiguous is Y-forward, Z-up.
I would hope that neither is ambiguous. Do you think it is?
I agree that north/east/south/west is a secondary way of looking at it, but it might still be helpful for some people.
And "north" is definitely more concise than "positive y axis".
And there is definitely a tradition of naming those things after compass directions, e.g. "ENU" and "NED" (see https://en.wikipedia.org/wiki/Axes_conventions).
I think that both are equally valid ways of describing the same thing.
We should also make sure to use this convention of YPR consistently across all trackers
For those tracker APIs that provide quaternions, this whole problem doesn't matter (except for left/right-handedness I guess).
For those that provide angles, we just have to figure out the convention used in their API and correctly convert it to ssr::quat
or ssr::Rot
.
But each tracker might use their own convention, therefore I don't think it makes sense to provide a general "ypr2quat()" function.
I thought that at least the Razor uses X-forward, Z-up and the YPR definition above.
Maybe. But that doesn't mean we have to use that convention for anything else.
It should really be an implementation detail of each tracker, the important thing is that we get a quaternion out.
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.
In the meantime I've created a new page in the ASDF docs: https://audioscenedescriptionformat.readthedocs.io/en/latest/rotation-matrices.html
Please tell me your suggestions for improvements. I hope there are no ambiguities left.
At some point I'll probably make a similar page using quaternions, but I think the rotation matrix stuff might be easier to understand anyway.
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've added a page about quaternions: https://audioscenedescriptionformat.readthedocs.io/en/latest/quaternions.html
}; | ||
|
||
// Converting from yaw (around Z), pitch (around Y), roll (around X) to quaternions. | ||
// This a lot worse than the other way around and should be avoided. |
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.
Depending on your definition of "worse", this might be exactly wrong.
This way is a unique transformation (except for the fact that each rotation can be represented by exactly two quaternions), the other way around is not unique, since there are rotations that can have infinitely many yaw/pitch/roll representations.
Really, the other way around should be avoided!
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.
Ah, of course. This is just plain wrong.
I was trying to say stay in quaternions and don't convert back to YPR.
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 was trying to say stay in quaternions and don't convert back to YPR.
Yes, that should be the advice!
template <typename T> | ||
ssr::quat ypr2quaternion(T yaw, T pitch, T roll) | ||
{ | ||
return gml::qrotate(gml::vec3{roll, pitch, yaw}); |
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.
Another way to write this is:
return gml::qrotate(gml::radians(azimuth), {0.0f, 0.0f, 1.0f})
* gml::qrotate(gml::radians(elevation), {1.0f, 0.0f, 0.0f})
* gml::qrotate(gml::radians(roll), {0.0f, 1.0f, 0.0f});
I have no clue whether those two are equivalent, because the order of rotations and the order of axes is not visible in the single call to gml::qrotate()
.
I like having both orders visible.
I don't know which one of the alternatives is faster, would be interesting to benchmark this ...
(mouse_pos.orientation() - prev_mouse_pos.orientation())); | ||
_controller.take_control()->reference_rotation_offset(_scene.get_reference().orientation + | ||
_scene.get_reference_offset().orientation + | ||
(mouse_pos.orientation() - prev_mouse_pos.orientation())); |
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 don't think the mouse should affect the "rotation offset".
Using the right mouse button doesn't affect the "position offset" either.
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.
When I try rotating the head (using the binaural renderer), the orientation is jumping around eratically. Something is wrong there ...
// rotate according to reference offset position | ||
glRotatef(_scene.get_reference().orientation.azimuth + | ||
_scene.get_reference_offset().orientation.azimuth, | ||
0.0f, 0.0f, 1.0f); |
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 long as the "rotation" and the "rotation offset" are indistinguishable in the interface for the binaural renderer, I agree that adding both makes sense.
But then you should also add "position" and "position offset".
// Update SSR | ||
virtual void update() | ||
{ | ||
ssr::quat r = inverse(_correction_rot) * inverse(_current_rot); |
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 do you call inverse()
twice?
Shouldn't it be enough to call inverse()
once in reset()
and here only left-multiply with the already inversed value?
In the meantime, the Qt GUI has been changed to show "reference position/rotation" and "reference offset position/rotation" (hopefully) correctly (in the horizontal plane). The Polhemus tracker has been extended to 3D. This means that many of the changes in this PR are obsolete, but the parts about the VRPN and Razor trackers are still relevant. In the meantime, there is also a function Lines 78 to 85 in aa757c9
|
This includes #140.
This PR fixes the VRPN tracker.
Furthermore, proposing a more uniform tracker data handling/layout across each implementation.
This is up for discussion, there are some open points, e.g. handling of YPR vs quaternions (What if a library doesn't provide quaternions?).
The basic idea is to augment the Tracker class, from which all trackers inherit.
It forces some layout by pure virtual functions.
Additionally it defines the struct
Tracker_data
.The
inline static Tracker_data current_data
provides the exact same member variable to all child classes.This implementation is tested under Linux with the latest Razor tracker, and under Windows with the same Razor and VRPN Optitrack.