domingo, 27 de abril de 2014

Balancing multirotor motors with MMA8452Q accelerometer


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:


  1. Place the sensor on one motor, measure mean gx,gy,gx (about 3000 samples) and calculate gx+gy+gz = overall_g
  2. Note values for (a) Motor alone (b) Ziptie at 4 positions separated 90 degrees
  3. Put some weigth ( tape for instance) on the position where overall_g was less using the ziptie
  4. 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.
  5. 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.







domingo, 6 de abril de 2014

CRIUS AIOP v2 - Altitude hold based on MS5611 barometer


Some notes about the barometer included in the CRIUS AIOP flight controller and the importance of covering it with dark foam. I read some times about foam covering and I interpreted it as a way of getting more precise measurements, I expected to have some variance (cms, or 1, 2 meters as much)  if  you didn't cover it, but in fact it is absolutely necessary for being able to trust altitude measurements. Altitude is used in almost all common flight modes but Stailize and Acro (Auto, Altitude Hold, RTL, Loiter... )

The barometer used in CRIUS AIOP v2 is the MS5611-01BA03. This is an update from the MS5611-01BA01 used in previous versions of CRIUS board, but apart from the change from ceramic to metal case and slightly better response to variations in supply voltage, the specifications are same.


Having a quick look at the datasheet you see that the pressure calculated value depends on temperature, as the sensitivity of the sensor varies with it. Indeed there is a different calculation method for pressure when the temperature is below 20 degrees. This should not worry much if you fly in a cold day as long as the tempreature at the sensor remains more or less constant, but it would worry me if the temperature at the sensor suffers a dramatic change, say from 20 to 15 degrees in a very short time (due to a fresh air flow from the props for instance). It could easily led you from an altitude value of 10m to 500m, with the corresponding reaction from the copter....This kind of issue is what you can suffer if the sensor is exposed to sunlight or propwash.

I learned it by the hard way, the first flight with the sensor completely exposed gave me altitude values that varied as much as 500m from one momment to the other, just imagine the reactions from the multicopter when I was on any auto mode. 

In this log it can be seen the variations of 500m while trying to do a RTL (throttle in blue represent the reactions to such an altitude change, red is altitude, green is desired altitude)


So I covered the sensor with foam and introduced all the electronics into a dark box. I tried before just covering the sensor, but the altitude values were jumpy sometimes due to sunligth, for example one day I suffered a crash when the weather changed from foggy to shiny while copter was in the air. Conclusion: both air flow from propwash and sunligth produce important variations in temperature at the sensor, and in measured pressure therefore, so keep it calm and dark.




after that changes the values from the sensor were quite precise and now I can enjoy of rock solid loiters and auto missions. Here an example of the last log, you can't even distinguish the line of the desired altitude behind the real altitude:


And here a video demonstration of the perfect Loiter performance:



Quadcopter data:

- CRIUS AIOP v2
- MPNG 3.0.1 R2 firmware
- NTM 800kv 300W motors
- Graupner 12x6 props
- Rctimer 30A ESC with Simonk.