It’s been a year or so since I talked about my Arduino head-tracking, and I think it is a topic worth revisiting. For one, the original sources have disappeared or become lost with forum directory changes, and I wasn’t particularly happy about my original coverage of it.
As a foreword, I want to thank “Kroda1” of arduino.cc for the base source code. Also newboerg for his original FaceTrackHAT guide which got me into this, and snowsky for his commands code addition; both of these latter folks where members of the Arma 3 community from around 2014 when I first came across this, and I’ve been using it fairly regularly since, especially with Elite: Dangerous.
This is made from a Generic Pro Micro Atmega32u4 (Arduino Leonardo), which looks like there are plenty of choices to pick from online and all pretty comparable. With an MPU-6050 accelerometer and gyro board addition, which is probably the more important part. There may also be other more advanced hardware with compass functionality included to improve accuracy, but this works ok to a degree, but with some extra work required.
Mine is a rather ridiculous looking electronic breadboard with the Arduino and MPU-6050 perched upon it, I’ve also found velcro to be beneficial and some normal tape over the breadboard’s paper bottom; which suffered at the hands of double-sided tape until I had the smart idea to use plastic tape wrapped around the edges onto the sturdier breadboard’s plastic.
There are numerous guides on wiring up the MPU-6050, depending upon whether you went with the above Arduino you may want to double check but mine is wired up thusly:
- MPU VCC to Arduino 5V
- MPU GND to Arduino GND
- MPU SDA to Arduino 2
- MPU SCL to Arduino 3
- MPU AD0 to Arduino GND
- And of course a micro USB to USB cable for the PC
Beyond this, I’m afraid I’m no hardware hacker or tinkerer, so I cannot be of much assistance. This setup at least allows for rotational control, and while the MPU-6050 is capable of translational control, I haven’t looked into it (and I suspect it would introduce a whole new kettle of fish).
You will also want to grab the Arduino IDE to compile and upload the code (please see second page for full source-code) and the head-tracking software. I originally suggested FaceTrackNoIR with the Hatire plugin, but now OpenTrack actually includes both in one package, but currently has a few small problems (high CPU usage being one).
Initially I used Kroda1’s original code with the command changes made by snowsky (allowing the software to send a command to stop/start the tracker), though I found that any combination of the commands or drift-correcting arduino code wasn’t quite right. It was either over-zealous counter-action or just a bit too inconvenient to keep hitting a key every 30 seconds (though this actually only centers in software, but that doesn’t really matter).
And then I had the “neck-snap” drift, I recorded a lot of debug output and saw large drifting action on the x and y axis; x in this case was yaw and y being roll. Roll would auto-correct in a short time (don’t ask me why or how, I have no idea) but the yaw would not, even minor anti-drift doesn’t help when you’re suddenly looking 90 degrees or more to the left/right. But recording the debug output let me know this was happening suddenly, in other words, a single moment/wrong reading from the MPU.
Note: I may not be correct about x and y above, they certainly improved things when altered but its been some time since I originally did this and didn’t document things well enough when I did.
The first problem, drift correction – because yaw is most often used I couldn’t just keep hitting a key when it didn’t quite return to center. So I have a simple drift correction for those moments when its not too frustrating and I just let it do it automatically:
//Drift correction, more correction further away
The downside to the above is that if I want to track a target that stays on my left or right for too long, the yaw will center regardless.
After I made the below changes I actually found I didn’t need this first adjustment, as such I will be commenting it out in source and I’ve added a little section at the bottom of this first page.
The second problem is much simpler in its solution, filtering out large erroneous spikes from the MPU (or perhaps arduino calculation). See the following code:
// Extreme-drift counter, used for glitchy behaviour
// Compares last angle and figures out if the rotation is
// "too much", otherwise uses last "valid" angle
if( abs(accel_angle_x) > abs( get_last_acc_x_angle() + 15 ) )
accel_angle_x = get_last_acc_x_angle();
// Figure out how to do the above but for Y (roll?)
if( abs(accel_angle_y) > abs( get_last_acc_y_angle() + 15 ) )
accel_angle_y = get_last_acc_y_angle();
This required some other modifications from the original source of course, get_last_acc_x_angle() (and y’s equivalent) didn’t exist; and we have to pass the current calculated angle acceleration to the function that saves the last angle’s acceleration.
Anyhow, essentially if the current calculated angle’s acceleration is greater than the last angle plus 15 degrees, then we’ll just use the last angle’s acceleration. Yes, this could introduce drift, but as we don’t expect this erroneous output that often (maybe once every 5 seconds or so, on a hardware-based timescale of nanoseconds at that), it shouldn’t have much impact for our planned use.
The logic is that we can’t actually turn out heads fast enough in a nanosecond (or whatever the actual timescale of the hardware’s clock is), we’d have to be doing ourselves some serious harm if that was the case.
I may actually refine these numbers at some point; and it may reduce the drift immensely that I don’t need the first solution, but we’ll see.
Warning! If you have this setup, then the solution above is/could be very specific to my hardware, your drift may be on different axis to mine and as such you may want to implement your own version of these changes. I have no drift in pitch or seemingly erroneous output so I don’t bother with it (the x axis in code).
This is a late addition to this article, I’ve done some experimenting after adding and improving the second fix and found it pretty much removed the worst drift; there can still be small amounts of drift of course, without a compass to correct there isn’t much chance of avoiding it.
I think though, this hardware can sometimes become uncalibrated when things warm up – so it can be worth taking the tracker (or head-set its mounted to) off, putting it somewhere stable (I hold mine on my desk or brace my headphones against my desk to stabilise) and hitting stop/start in OpenTrack or Reset in the Hatire dialogue.
I’ve been able to play for multiple hours without having to hit my center button that often.
Please see the second page for full source code.