Are there a way around this?
As you have found out the default A/D reference used by the arduino analogRead() command uses whatever voltage that is being supplied to the Vcc and Avcc pins to represent 100% or range (or 1023 counts). As the battery voltage lowers in use you won't be able to determine it's absolute value because of that. There are several methods of working around this. Using an external regulated voltage source wired to the AREF pin (must be lower then the lowest possible battery voltage) and using a voltage divider wired to the battery output is one method. Enabling the internal 1.1vdc voltage reference and again using a voltage divider wired to the battery is another. Both have limitations in that they effect the range measurement for all the analog input pins.
Coding Badly and I worked on a simple sketch that used some cleaver programming method where a sketch can actually measure the battery voltage and therefore come up with an method where one can have a on-the-run calibration value to compensate for falling battery voltage but still be able to measure analog input pins accuratly. The only limitiation is an electrical one of the AVR controller chip in that the voltage on a analog input pin (or even a digital input pin) must never exceed the voltage on the Vcc and Avcc pins.
It's just a demonstration sketch and you would have to work at intergrating it into your larger sketch:
// Function created to obtain chip's actual Vcc voltage value, using internal bandgap reference
// This demonstrates ability to read processors Vcc voltage and the ability to maintain A/D calibration with changing Vcc
// Now works for 168/328 and mega boards.
// Thanks to "Coding Badly" for direct register control for A/D mux
// 1/9/10 "retrolefty"
int battVolts; // made global for wider avaliblity throughout a sketch if needed, example a low voltage alarm, etc
Serial.print("volts X 100");
Serial.println( "\r\n\r\n" );
battVolts=getBandgap(); //Determins what actual Vcc is, (X 100), based on known bandgap voltage
Serial.print("Battery Vcc volts = ");
Serial.print("Analog pin 0 voltage = ");
Serial.println(map(analogRead(0), 0, 1023, 0, battVolts));
int getBandgap(void) // Returns actual value of Vcc (x 100)
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
// For mega boards
const long InternalReferenceVoltage = 1115L; // Adjust this value to your boards specific internal BG voltage x1000
// REFS1 REFS0 --> 0 1, AVcc internal ref. -Selects AVcc reference
// MUX4 MUX3 MUX2 MUX1 MUX0 --> 11110 1.1V (VBG) -Selects channel 30, bandgap voltage, to measure
ADMUX = (0<<REFS1) | (1<<REFS0) | (0<<ADLAR)| (0<<MUX5) | (1<<MUX4) | (1<<MUX3) | (1<<MUX2) | (1<<MUX1) | (0<<MUX0);
// For 168/328 boards
const long InternalReferenceVoltage = 1056L; // Adjust this value to your boards specific internal BG voltage x1000
// REFS1 REFS0 --> 0 1, AVcc internal ref. -Selects AVcc external reference
// MUX3 MUX2 MUX1 MUX0 --> 1110 1.1V (VBG) -Selects channel 14, bandgap voltage, to measure
ADMUX = (0<<REFS1) | (1<<REFS0) | (0<<ADLAR) | (1<<MUX3) | (1<<MUX2) | (1<<MUX1) | (0<<MUX0);
delay(50); // Let mux settle a little to get a more stable A/D conversion
// Start a conversion
ADCSRA |= _BV( ADSC );
// Wait for it to complete
while( ( (ADCSRA & (1<<ADSC)) != 0 ) );
// Scale the value
int results = (((InternalReferenceVoltage * 1023L) / ADC) + 5L) / 10L; // calculates for straight line value