PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : [ERLEDIGT] ADXRS620 Genauigkeit verbessern.. erster Versuch mit RN Controllerboard



Ritchie
04.03.2012, 10:33
Hallo Zusammen,

ich arbeite derzeit an einer Routine für die Verbesserung des Ausgabewertes des Gyro ADXRS620 (Breakout Board). Derzeit werden nur die Rohwerte betrachtet. Die errechnete Winkeländerung muss ich auch noch einbauen.

Hierfür verwende ich derzeit das RN COntrolboard und den folgenden Quellcode.

Die Funktion des Programmes ist einfach. Ich gebe den Betrag der Spannungsänderung auf die LED aus, um hier eine visuelle Rückmeldung zur Bewegung zu bekommen.

Trotz gleitenden Mittelwert muss ich feststellen, das Bit 0 nicht verwertet werden kann, da es ständig flackert.

Hat jemand die gleichen Erfahrungen gemacht ? Oder jemand eine Idee, wie man das auch noch aus dem Weg räumt.

Ich habe die Ref.-Spannung mit dem Voltmeter gemessen und hier 4.96Volt gemessen, daher der Mittelpunkt bei 528. Der Gyro gibt hier 2,56 Volt raus. Ungenauigkeiten meines Voltmeter habe ich nicht eingerechnet.



/************************************************** ***********************************************
* INCLUDES *
************************************************** ***********************************************/

# define F_CPU 8000000UL

#include <stdlib.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
#include <string.h>

#include "uart.h"

/************************************************** ************************************************
* General Settings *
************************************************** ************************************************/

#define SETBIT(ADDRESS,BIT) (ADDRESS |= (1<<BIT)) // Bit set
#define CLEARBIT(ADDRESS,BIT) (ADDRESS &= ~(1<<BIT)) // Bit clear
#define CHECKBIT(ADDRESS,BIT) (ADDRESS & (1<<BIT)) // Bit check


#define ARRAYSIZEVALUES 40
int ValueArray[ARRAYSIZEVALUES];
int NextArrayIndex=0;
int ZeroValue=0;

/************************************************** ************************************************** ***********
* Init of the array for the floating average calculation *
************************************************** ************************************************** ***********/
void initFilterArray(void)
{
int i;

NextArrayIndex=0;
for(i=0;i< ARRAYSIZEVALUES;i++)
{
ValueArray[i]=512; // set up 2.56 Volt
}
}

/************************************************** ************************************************** ***********
* return the floating calculated average value *
************************************************** ************************************************** ***********/

int getAverageValue(void)
{
int Value=0,i;

for(i=0;i<ARRAYSIZEVALUES;i++)
{
Value += ValueArray[i]; // Add the value for sum
}

Value /= ARRAYSIZEVALUES;

return Value;
}
/************************************************** ************************************************** ***********
* Init of the Board *
************************************************** ************************************************** ***********/
void Board_init(void)
{

/* Ports initialisieren */
DDRA = 0x00; // Port A: All as Analoh Input
DDRC = 0xFF; // Port C: Bit 0... 5, 6 and 7 as Output

PORTA = 0x0; // Port A: without Pull-Up
PORTC = 0xFF; // Port C: with Pull ups for the LED
}


int main(void)
{
int result; // Result of the converting
int Value;
unsigned char Buffer;
int ZeroValue;
int i=0;

Board_init();
initFilterArray();

// Set up the pre Scaler for the Input values
// Prescaler 64 (more is not possible with 8Mhz)
ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1);

// We are starting with channel zero
ADMUX = 0;
ADMUX |= (1<<REFS0); // REFS1 = 0 / REFS0 = 1 -> AREF -> 5 Volt
ADCSRA |= (1<<ADSC); // Start the measurement

ZeroValue=528; // 2.56 using 4,96 Reference Voltage
// Jumper UREF is set
while(1)
{

if (ADCSRA & (1<<ADIF)) // Do we have a new finished measurement of values
{ //
result = ADCW; // Get the value of the measurement
Value = (ZeroValue - result);

ValueArray[NextArrayIndex]= Value; // Store value in the next Position
NextArrayIndex++; // Prepare for the next position
if(NextArrayIndex >= ARRAYSIZEVALUES ) // Are we at the end of position
NextArrayIndex=0;

i++;
if( i > 200)
{
Value = getAverageValue();
Buffer = (unsigned char) abs(Value);
PORTC = ~(Buffer);
i=0;
}
ADMUX = 0; // Select the Channel
ADMUX |= (1<<REFS0); // REFS1 = 0 / REFS0 = 1 -> AREF -> 5 Volt
ADCSRA |= (1<<ADIF); // Clear the IF Flag
ADCSRA |= (1<<ADSC); // Start the measurement
}
}
}


Ich muss diese Routine später noch in den eigentlichen Roboter einbauen und hier noch sehen, ob ich zeitlich mir einen Mittelwert über 40 Messwerte erlauben kann.

Da meine Zielmaschine eine 16Mhz Quarz hat, kann ich mir hier einen Vorzähler von 128 erlauben ?
Meine Zielmaschine muss 8 Analogwerte verarbeiten!

Gruss R.

Besserwessi
04.03.2012, 13:09
Mit etwas Fluktuation im untersten Bit muss man bei AD schon rechnen. Das liegt ggf. auch am Layout, und muss nicht vom Sensor kommen. Auch ist etwas Rauschen ggf. gar nicht so schlimm und kann sogar helfen, wenn man oversampling nutzt

Man kann hier aber noch etwas verbessern, bei der Berechnung des Mittelwertes: Der Mittelwert von 40 Werten hat mehr Auflösung als die Einzelwerte. Es wäre also möglich nicht durch die Zahl der Summanden zu teilen, sondern z.B. nur durch ARRAYSIZEVALUES/4 (hier = 10). Damit hätte der Mittelwert 2 Bit mehr an Auflösung. Die andere Skalierung kann man in der Regel einfach berücksichtigen. Ob die 2 zusatzlichen Bits wirklich signifikant sind ist hängt vom Sensor ab, schaden tun sie in der Regel nicht und man bekommt wenigstens das Quantisierungsrauschen kleiner.

Bei 16 MHz Takt kann man gut einen Teiler von 128 nehmen. Das gäbe dann 125 kHz AD Takt. Man kann auch einen Teiler von 64 probieren, wenn man lieber eine schnellere Wandlung haben will, und dafür ggf. etwas mehr Fehler der Einzelwerte akzeptiert - der Mittelwert kann damit ggf. noch etwas besser werden.

Wenn das Rauschen vom Sensor kommt, kann man ggf. noch etwas mit einem Antialiasing-Filter (Tiefpass) gewinnen, um da ggf. höherfrequentes Rauschen zu entfernen.

Ritchie
04.03.2012, 15:57
Hi,

ich habe die Idee mit der höheren Auflösung aufgenommen.

Weitere Filter will ich derzeit noch nicht einsetzen, da ich an der Zielmaschine jetzt einen Vorteiler von 128 ausprobieren will und die Ergebnisse erst dann betrachten will.

Da ich ja eine Regelung damit machen will, habe ich auch nicht viel Zeit zum Filtern.

Danke für den Tip.

Gruss R.

Besserwessi
04.03.2012, 16:22
Der Anti-aliasing filter ist analog vor dem AD-wandler. Herausgefiltert werden da die Frequenzen die zu hoch sind für die Rate mit der der AD abgefragt wird. Wesentlich langsamer wird eine digitale Regelung davon nicht - die Verzögerung liegt mehr so im Bereich der Zeit zwischen den AD Samples.

Ritchie
04.03.2012, 17:08
Ach so,

ich dachte da eher an einer softwaremäßigen Bearbeitung der Daten. Das würde Zeit kosten. Sorry, falsch verstanden.
Gruss R.

Ritchie
05.03.2012, 21:27
Hallo Zusammen,

hier jetzt das kleine Testprogram mit Winkelanzeige via LED Anzeige in der Version 1.

Das Programm zeigt jetzt den Winkel in Grad an ( Nun ja, nur bis 255 Grad korrekt.)

Ich muss noch sehen, wie stark der Schleppfehler wird.

- Programm für RN Control mit 16Mhz



/************************************************** ***********************************************
* INCLUDES *
************************************************** ***********************************************/

# define F_CPU 16000000UL

#include <stdlib.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <string.h>
#include <math.h>

#include "uart.h"

/************************************************** ************************************************
* Generel setting *
************************************************** ************************************************/

#define SETBIT(ADDRESS,BIT) (ADDRESS |= (1<<BIT)) // Bit Set
#define CLEARBIT(ADDRESS,BIT) (ADDRESS &= ~(1<<BIT)) // Bit reset
#define CHECKBIT(ADDRESS,BIT) (ADDRESS & (1<<BIT)) // Bit Check


#define ARRAYSIZEVALUES 40
int ValueArray[ARRAYSIZEVALUES];
int NextArrayIndex=0;
int ZeroValue=0;
float Angle=0;

volatile unsigned int m_Status;
volatile char m_TimerCounter=0; // Time value 0...100 * 10ms-> 1 sec.


/************************************************** ************************************************** ***********
* Init of the array for the floating average calculation *
************************************************** ************************************************** ***********/
void initFilterArray(void)
{
int i;

NextArrayIndex=0;
for(i=0;i< ARRAYSIZEVALUES;i++)
{
ValueArray[i]=ZeroValue; // set up 2.56 Volt
}
}

/************************************************** ************************************************** ***********
* return the floating calculated average value *
************************************************** ************************************************** ***********/

int getAverageValue(void)
{
long Value=0,i;

for(i=0;i<ARRAYSIZEVALUES;i++)
{
Value += ValueArray[i]; // Add the value for sum
}

Value /= 4;

return (int) Value;
}
/************************************************** ************************************************** ***********
* Init of the Board *
************************************************** ************************************************** ***********/
void Board_init(void)
{

/* Ports initialisieren */
DDRA = 0x00; // Port A: All as Analoh Input
DDRC = 0xFF; // Port C: Bit 0... 5, 6 and 7 as Output

PORTA = 0x0; // Port A: without Pull-Up
PORTC = 0xFF; // Port C: with Pull ups for the LED

// set up the timer 0
TCCR0 = (1<<CS02) | (1<<CS00); // Pre-saler of 1024
// Set up the interrupt for timer 0, overflow
TIMSK |= (1<<TOIE0); // Activate the Interrupt "overflow"
}


int main(void)
{
int Value,LastDiff=0,Diff;
float AngleChangeInDegree,AngleChangeInRad;
float AngleChangeInMinutes;
unsigned char Buffer;

cli (); // Clear any interrupt
Board_init();
PORTC = 0xff; // Clear the Port
sei();
// Set up the pre Scaler for the Input values
// Prescaler 128
ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);

// We are starting with channel zero
ADMUX = 0;
ADMUX |= (1<<REFS0); // REFS1 = 0 / REFS0 = 1 -> AREF -> 5 Volt
ADCSRA |= (1<<ADSC); // Start the measurement

ZeroValue=5285; // 2.56 using 4,96 Reference Voltage ( + 5 -> rounding)
initFilterArray();

while(1)
{
if (ADCSRA & (1<<ADIF)) // Do we have a new finished measurement of values
{ //
ValueArray[NextArrayIndex]= ADCW; // Store value in the next Position
NextArrayIndex = (NextArrayIndex + 1) % ARRAYSIZEVALUES;
ADMUX = 0; // Select the Channel
ADCSRA |= (1<<ADIF); // Clear the IF Flag
ADCSRA |= (1<<ADSC); // Start the measurement
}

if((m_Status & 1)) // Do we have to control the motor speed
{
m_Status=0; // Clear the request of the calculation
Value = getAverageValue(); // get the difference value
Diff = ZeroValue - Value; // get the difference of the change
if( Diff > 20 || Diff < -20 ) // do we have a real change
{ // -> +-300° (5,23598775rad) -> per second
// -> +-30° (0,523598775rad) per 100ms
// -> +-180 angular minute per 10ms
AngleChangeInMinutes=((float)((Diff+LastDiff) / 20 ) * 0.3515625);
AngleChangeInDegree = AngleChangeInMinutes / 60.0;
AngleChangeInRad = (AngleChangeInDegree * M_PI) / 180.0;
Angle += (AngleChangeInDegree);

if( Angle < 0.0 ) // Limit Range (only 8 bits)
Angle=360.0;
if( Angle > 360.0 )
Angle=0.0;

LastDiff=Diff;
}
else
LastDiff=0; // No difference found

Buffer = (unsigned char) Angle; // Convert Angle to the LED format
PORTC = ~(Buffer); // Output inverted
}
}
}

SIGNAL (SIG_OVERFLOW0) // Interrupt Overflow Timer T0
{
m_TimerCounter++;
SETBIT(m_Status,0); // Calculate the angle speed
TCNT0 = 99; // restart the time again
}



Hier sind mit Sicherheit noch Rundsfehler (Aufaddieren der Fehler) enthalten. Bin für Ideen offen.

Gruss R.