AnalogRead Accuracy

I am trying to do precise measurements of a 12V battery, two decimal places would be fine, three decimals would be great. I run a calibrated voltage divider to lower the input (for when I convert to a floating point, the graph is just the int readings) and do the reading in a for-loop and average it. This worked fine but after serial printing the data to Excel, it has far more variation than I would like. I tried adding a unity gain, low pass filter op amp but the variation was unchanged. Is this the limit of the ADC on the Arduino or are there more tricks to increase accuracy? I am not at all concerned about speed. The following graph shows individual readings, not averages.

The readings of the ADCs are always relative to the Vcc (usually 5V) if not configured for AREF. So if your Vcc varies your readings of the ADC will change appropriately. How stable is your Vcc? Do you use the standard voltage regulator of the Arduino or an external stabilised power supply?

BTW: your readings are more or less withing two decimal places (628-638) with two exceptions.

12V battery, two decimal places would be fine, three decimals would be great

10 bits ADC (1024 steps) could provide 2 decimal digit, 12V / 1024 = 0.01171875 V. To get third will require ~3 bits more, you still could achieve this using oversampling technics, that outlined in AVR121 App Note.
Accuracy could be affected by output impedance of the source and wiring, can you post a drawings of your hardware set-up?

Is the battery just sitting there or is it doing something? Have you compared the readings with a good multimeter?

Is there something else going on of a cyclic type as well or a relay or other Vcc load like lot of led's??? does this have it's own power supply and are the grounds good between PSU and computer?

Doc

you may smooth readings with a running average - http://arduino.cc/playground/Main/RunningAverage -

You can actually increase resolution and lower noise by oversampling and decimation.

2ˆ2n samples will give you n bits of extra resolution. So if you take 1024samples, you'd get 5 extra bits of resolution.

You do this by summing the ADC values (ints) in a long variable which you then bit-shift.

Here's a full explanation of the method: www.atmel.com/Images/doc8003.pdf
"It is important to remember that normal averaging does not increase the resolution of the conversion. Decimation, or Interpolation, is the averaging method, which combined with oversampling, which increases the resolution."

made sample code some time ago on that note -

//
//    FILE: analogReadN.pde
//  AUTHOR: Rob Tillaart
//    DATE: 2012-05-10
//
// PUPROSE: higher precission analogRead()
//
// http://www.atmel.com/Images/doc8003.pdf 
//

void setup()
{
  Serial.begin(115200);

  for (int b=10; b< 17; b++)
  {
    unsigned int val = analogReadN(A0, b);
    Serial.println(val, DEC); 
  }
}

void loop()
{
}

uint16_t analogReadN(uint8_t pin, uint8_t bits)
{
  bits = constrain(bits, 10, 16) -10;

  int samples = 1 << (bits << 1);

  uint32_t sum=0;
  for (int i=0; i< samples; i++) sum += analogRead(pin);     
  return sum >> bits;                             
}

uint16_t analogReadNreferenc(uint8_t pin, uint8_t bits)
{
  bits = constrain(bits, 10, 16) -10;

  int d = 1;
  for (int i=0; i<bits; i++) d *= 2;
  int samples = d*d;

  uint16_t sum=0;
  for (int i=0; i< samples; i++) sum += analogRead(pin);     
  return sum / d;   // rounding => return (sum + d/2) / d;                              
}

You'll not have a good accuracy using Vcc as the ADC reference, because it will vary a bit depending on several conditions like power supply load and temperature. You'll have to use internal 1.1V nominal reference.

The internal reference is stable but its value is slightly different on each individual chip (can be from 1V to 1.2V), and therefore you'll need to "calibrate" your application, meaning you'll have to read the real value of the internal voltage reference once and save it in EEPROM for later usage (or use directly on the code if you're going to make only 1 unit).

In fact, your scaling down resistors will also introduce an extra error because they are not 0% tolerance so, in fact, you should calibrate your entire system, by feeding it a known voltage in the range you want to measure and seeing what value you read from the ADC - from this you can "correctly" read any other voltage.

Other than this, make averages as already been suggested by others. Put a decoupling capacitor on AREF, which will provide extra stability to the internal voltage reference. Put a small capacitor at the ADC's input to stabilize the input voltage. and you can also enter a special "ADC reading" sleep mode that the AVR supports for low noise ADC reading.