This post is about using the MMA8452Q acceleromter to balance multirotor motors. As there is alot of information in the net about the balancing procedure I am focusing on using this cheap accelerometer for that purpose.
The easier way would be to use some app in a 3g phone to get and idea of the vibrations at the motor, but I lost my phone a month ago and I was living happy without it and didn't wat to spend 300$ in a new one. So searching for cheap and easy to set up accelerometers, this is one of them: less than 10$, I2C interface, 12bit resolution and example code.
MMA8452Q hw & sw setup
The connections is quite straigth forward, just solder some wires and connect to and arduino for example. This is form the example code MMA8452Q_AdvancedExample at GitHub
Hardware setup:
MMA8452 Breakout ------------ Arduino
3.3V --------------------- 3.3V
SDA ----------------------- A4
SCL ----------------------- A5
INT2 ---------------------- D3
INT1 ---------------------- D2
GND ---------------------- GND
It is possible to interface directly from a 3.3V device to a 5V arduino because SDA&SCl pull-ups at Arduino are disabled . For this you must use the i2c.h file in the example instead of the standard Wire arduino library, that switch on the pull-ups (twi.c):
void twi_init(void)
{
// initialize state
twi_state = TWI_READY;
twi_sendStop = true; // default value
twi_inRepStart = false;
// activate internal pullups for twi.
digitalWrite(SDA, 1);
digitalWrite(SCL, 1);
// initialize twi prescaler and bit rate
cbi(TWSR, TWPS0);
cbi(TWSR, TWPS1);
TWBR = ((F_CPU / TWI_FREQ) - 16) / 2;
/* twi bit rate formula from atmega128 manual pg 204
SCL Frequency = CPU Clock Frequency / (16 + (2 * TWBR))
note: TWBR should be 10 or higher for master mode
It is 72 for a 16mhz Wiring board with 100kHz TWI */
// enable twi module, acks, and twi interrupt
TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA);
}
The reason why this works is well explained in a comment in the product page at sparkfun, it is due to the open drain design of the i2c data lines. From sparkfun they notice that even without disabling the arduino pull-ups it works, and some users confirm it, because the final high voltage at the sensor input sda and scl lines would be 3.79V as a combination of sparkfun board 10k pull-ups to 3.3v and arduino 25k pull-ups to 5v, but I prefer to stay in the datasheet ranges, so I disabled pull-ups at arduino.
I don't know why, but even reading in the sensor datasheet that a 4.7 ohms resistor is recommended as pull-up, sparkfun soldered 10k pull-ups in the board. Some users advice that seing signal at oscilloscope it appears quite rounded with 10k, and that it is better shaped with 4.7k, you can solder a paralell 10k to get 5k final resistante to pull-up, but I must say it works quite fine for me with the 10k resistors in the board.
Changing scale +/- 2g/4g/8g
It is neccesary to understand the Zero-g offset concept to understand the output values from the sensor. When stationary the output registers from the sensor will be 0x00 (ideally middle of dynamic range) , but in fact z g is 1g when laying flat and stationary. It is well explained in the application note AN4069.
At each scale each count from the ouput register means a different value of g. For example when the full-scale is set to 2g, the measurement range is -2g to +1.999g, and each count corresponds to 1g/1024
(1 mg) at 12-bits resolution, each count will mean double g value if scale is doubled to 4g, and same for 8g.
Then the value of counts stored in the offset registers are referred to a scale, and the value added to 0x00 output when flat and stationary to get 1g at z axis will only result in 1g when the scale is the same at which the calibration was done.
This can be confusing using the example code, if you change the default scale to 4g, you will get at flat and stationary : x=0g,y=0g and z=2g, and x=0g,y=0g and z=4g when scale is 8g. You need to recalibrate if you want to see a pretty 1g for z, or if not at least understand what that value means.
Regarding scale also take into account that the scale is limited to 4g if you use the low noise mode, that is actiuvated by default in the example code
Using accelerometer data to balance multirotor motor
The procedure to balance a motor is well documented online, the idea is to place a weigth on different parts of the motor and compare the vibrations on each point. I did it using a ziptie and moving the head of the ziptie around the perimeter of the motor. In an ideal motor the vibrations woould be the same on every point, but in an unbalanced motor the vibrations will be less in the point where it is lighter.
You can plot the data in a easy way using Processing for example, that is what I tried first, but it is quite difficult to distinguish the difference in vibration level between two points simply observing the graphic. So I found it better to calculate mean value over time and comparing that mean at each point. That is an advantage of using arduino instead of a graphical phone application. If you sum up the mean on each axis you can get an idea of the overall vibrations generated by the motor. That all-axis mean cannot be used to compare vibrations between different motors unless you place the sensor equally leveled on all the motors, as z axis g value depends on the sensor horizontal level. But that all-axis mean can be used to compare vibrations in one motor placing the weigth at different points:
Summarizing the method:
The procedure to balance a motor is well documented online, the idea is to place a weigth on different parts of the motor and compare the vibrations on each point. I did it using a ziptie and moving the head of the ziptie around the perimeter of the motor. In an ideal motor the vibrations woould be the same on every point, but in an unbalanced motor the vibrations will be less in the point where it is lighter.
You can plot the data in a easy way using Processing for example, that is what I tried first, but it is quite difficult to distinguish the difference in vibration level between two points simply observing the graphic. So I found it better to calculate mean value over time and comparing that mean at each point. That is an advantage of using arduino instead of a graphical phone application. If you sum up the mean on each axis you can get an idea of the overall vibrations generated by the motor. That all-axis mean cannot be used to compare vibrations between different motors unless you place the sensor equally leveled on all the motors, as z axis g value depends on the sensor horizontal level. But that all-axis mean can be used to compare vibrations in one motor placing the weigth at different points:
Summarizing the method:
- Place the sensor on one motor, measure mean gx,gy,gx (about 3000 samples) and calculate gx+gy+gz = overall_g
- Note values for (a) Motor alone (b) Ziptie at 4 positions separated 90 degrees
- Put some weigth ( tape for instance) on the position where overall_g was less using the ziptie
- Measure overall_g with the motor with that weigth and compare to value of motor alone in point 2, it should be less than alone.
- Add weigth on that point while the overall_g continues decreasing until it start to increase.
With this method I got between 0,2g and 0,6g less overall vibration placing tape at the motors. I did it with the motors attached to the multirotor and spinning one at a time.