Dennis Frie

- a couple of projects and pictures

DIY headtracker

This as an OpenSource project I developed some time ago. The project have been widely used and discussed at RCgroups:
Project page on RCGroups

A couple of fellow FPV-friends complained about their headtrackers - mainly drift problems and bad resolution. One showed a video with the problem, and my comment "that looks really bad, even I can make that a lot better", ended up costing me a bit of time.

I have choosen to use 3 axis- accelerometer, gyro and magnetometer, as that's the only way to account for drift in all directions. So basically, this project is a simplified kalman filter for Atmega328 witg 9-axis IMU and PPM output.
As the project is meant to be OpenSource and easy available, some cheap and easy available hardware have been chosen.

- Arduino Nano
- 9-axis IMU called GY-85 (ITG3205+ADXL345+HMC5883L)

The project is made as user-friendly as possible, and only little technical knowledge should be necessary. All necessary settings can be changed from a graphical user interface - making it easy for everyone to use.
The connections diagram is as simple as this:

Can not show picture

The hardware used is the Arduino Nano and the small 9-axis sensorboard

Can not show picture

Assembled hardware - just missing connections to RC-transmitter

Can not show picture

Early test-version. Plug-n-play with FF9 transmitter.

Can not show picture

The graphical user interface

Can not show picture

- with advanced settings

Can not show picture

and finally a calibration wizard

Can not show picture

Tests with PPM expansion PPM in from FF9 (red), PPM out from tracker (blue):

Can not show picture

8 channel PPM in, 12 channel PPM out

Can not show picture

Constant frame-rate (red: FF9 PPM in, blue headtracker PPM out)

Can not show picture

Headtracker sensor plots, raw, filtered etc:

Can not show picture Can not show picture

Short description of the more technical stuff

The sensors are sampled with 128 samples/sec. As we have 9 sensors, we get 128 * 9 = 1152 measurements/samples every second (more than plenty for a headtracker). The sampling-rate is controlled by timer0, that will set a flag high - indicating that the sensors should be read and new angles etc. calculated (timer0 running in CTC-mode, using compare-register A). While it should be possible to make all the calculations etc. within the interrupt, it's not recommended. A lot of heavy/slow calculations are done, and in case of timing-problems, we just want it to skip a sample and continue (should never happen, but just in case).
If you turn on DEBUG you will get a lot of extra information, be able to see timing-problems etc. (please note, using serial.print is time comsuming and will in mast cases give timing-problems. Rarely a real problem when debugging, but be aware that it will change the samplerate a bit.)

The accelerometer data is converted to G-force. Not really necessary, but more convenient to work with. The total G-vector is calculated, and the angle calculated by using the total vector-length and the G-force in each direction.

The gyro indicates the angular change in degrees/sec. All axes accounts for tilt etc and data is continously integrated, to give an estimation of the angle.

The magnetometer is compensated for tilt (x- y-direction), and the final x and y values used to calculate the magnetic heading.

The filter is a basic complementary filter, and an optional lowpass filter. The weight for gyro/accelerometer/magnetometer can be sat in the GUI, and lowpass filter changed.

Please note that only PPM-out is activated as default.
The PPM in/out is a bit tricky, as we can only do one thing at a time. Oh - and we only have one 16 bit timer available. Timer1 (16 bit timer) is used to generate the PPM output. It's set to change pin at interrupt to get an accurate hardware-timed pin-change. The compare-interrupt will set the next compare-match.

PPM-in is detected by pin-change interrupt (higher priority than timer1 compare interrupt). It will use the time from timer1, and detect if the timer have been reset.

A few notes:

Use of Serial output
The headtracker uses a lot of slow calculations with floating points, cos, sin atan etc. Furthermore the sampling frequency is pretty high. Using Serial.print is great for getting info, data etc. - but it requires time. As it's running "on the edge", you can only print a few characters without loosing a sample. It's programmed to just skip a sample in case of timing problems, but just be sure to note this when testing.

Updating variables
All variables stored in EEPROM have to be changed in the GUI or by changing this:
// If the device have just been programmed, write initial config/values to EEPROM:
if ( != 42) {
But as all settings saved in EEPROM can be changed from GUI I'll suggest that, as it's a lot easier.

Using the GUI:
The GUI have been changed quite a bit since first versions, and is hopefully pretty intuitive.

PPM channels
Used to set the PPM-channel pan/tilt and roll is assigned t0

The center position of the servo

Servo travel end.

The gain determines how much the servo should move for a given head-movement. Default is 170 which pretty much gives 1:1 (90 degrees head-movement = 90 degrees servo movement).

All the "advanced settings"
- - - -

LP filter at tilt/roll [%]
Lowpass-filter of the final tilt/roll output.
1 = max lowpass/time constant. Will give very smooth, but also very slow change.
100 = lowpass off.

LP filter at pan [%]
Lowpass-filter of the final pan output.
1 = max lowpass/time constant. Will give very smooth, but also very slow change.
100 = lowpass off.

Gyro weight on tilt/roll [%]
How much do we thrust the gyro compared to the accelerometer on the tilt/roll axis? Accelerometer is noisy and should only be used to slowly compensate for drift.
100 = only use gyro.
0 = only use accelerometer

Gyro weight on pan [%]
How much do we thrust the gyro compared to the magnetometer on the pan-axis? The gyro is a lot more accurate, so magnetometer should only be used to slowly compensate drift.
100 = only use gyro
0 = only use magnetometer

Servo pulse variation
In short: Servo travel adjustment.
Used to set the max variation of the pulse-width in the PPM signal. The unit is microseconds*2 - matching the Atmega timer

Servo center
In short: Servo neutral position
Used to set the center/default length of the pulse-width in the PPM signal. The unit is microseconds*2 - matching the Atmega timer

Tilt and roll gain
Gain of the servo. You decide if you want to turn the head 10 degrees or 360 degrees to get full travel.

Pan gain
Gain of the servo. You decide if you want to turn the head 10 degrees or 360 degrees to get full travel.

Reverse (Tilt, roll and Pan)
used to inverse the servo direction.


General questions:
Can I use other Arduino versions?
Sure, all Arduino boards should have the necessary pins available (as far as I know). I have choosen Arduino Nano as it's cheap, small, have FTDI onboard and the regulator can handle a bit higher current, compared to Arduino Pro etc. But pick whatever you prefer/have available.

Can I use other sensors?
It should be pretty easy to use other sensors, as long as it's I2C. Update the sensor-init, sensor-reading and the sensor-configuration and you should more or less be good to go. I'll maybe add support for another IMU later.

Can I buy a headtracker from you?
No, it's meant as a DIY project. It's easy to make, and it should be pretty straight forward. I like to develope, but care little about sale etc.

Can I sell this?
Feel free. I can imagine a few people would like to buy a nice ready-made unit, but it would be great to know if you use my software/work. But in general it's uploaded with no strings attached.

What about copyright/license etc?
It's uploaded as OpenSource, and I expect it to be used and abused - don't really see much sense in "protecting" my work. So feel free to use it for whatever purpose.