www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik PID Regelung mit Widerständen myAVR in C


Autor: Gabriel 85 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo!

Ich hatte zwischenzeitlich den PID Regler aufgebaut, und es hatte 
geklappt. Jetzt wollte ich einen Widerstandswert als Regelgröße einlesen 
und das klappt nicht...vielleicht weiss jemand rat.

Das Programm funktioniert wie folgt:
Nach den LCD_init funktionen beginnt das main Programm.

Mit der Funktion ReadChannel(..) rufe ich die einzelnen ADC Kanäle 
(0,1,2) auf. Kanal 0 liest meine eine Spannung ein, Kanal 1 die 
Sollwertspannung (über Poti) und Kanal 2 liest eine Spannung ein die 
linear zum Strom meines Sensors ist.

Aus 0 und 2 wird durch Division ein Widerstandswert (Istwert) ermittelt.
Aus 1 und 2 analog auch ein Widerstandswert (Sollwert).

Nun werden diese Werte in PID Berechnung zwecks Regelung eingegeben, und 
man erhält y , das in main einfach über PWM (B1) ausgegeben wird.

Die Berechnung der Regelabweichung stimmt auch soweit, da bei meinem 
Aktor der Widerstand mit zunehmenden Stellweg kleiner wird.

Hardwaremäßig sind B1 mit C2 verbunden, da der Regler einen Stromwert 
für einen Aktor vorgibt.

Mein Aktor hat einen eingebauten Sensor, der mit steigendem Strom seinen 
Widerstand ändert.


Der Regler an sich, die Display_inits und die Read_Channels 
funktionieren alle einwandfrei. Ich denke es ist irgendwo ein Bug im 
Wirkprinzip oder in main.

Könnte es sein dass ein Wert (y) der vom µC ausgegeben wird, nicht 
direkt in den Eingang C2 gespeist werden kann, da er zum selben 
augenblick verwendet wird? (Hat mir jemand erzählt)
Hier der Quellcode:
//---------------------------------------------------------------------------
// Title      : Regler mit LCD
// Date       : 27.10.2008
// Version    : 1.0
// Autor      : 
//---------------------------------------------------------------------------
#define    CLOCK  3686400
#include  <io.h>
#include  <avr/delay.h>
/////////////////////////////////////////////////////////////////////////////
// allgemeine-Funktionen
//---------------------------------------------------------------------------
//  wait_ms(..) - Wartet einige Millisekunden
//  Die Dauer ist nicht kalibriert.
//  PE:  miliSec=Anzahl der zu wartenden Millisekunden
//---------------------------------------------------------------------------
void wait_ms(int miliSec)
{
  _delay_loop_2( 1*(CLOCK/(1000/4)) * miliSec);  // 4 Zyklen warteschleife
}
//////////////////////////////////////////////////////////////////////////////
//  LCD-Funktionen für myAVR-Board + myAVR-LCD
//  4-BitModus an PortD Bit 4-7
//  PortD Bit 2 = RS, high=Daten, low=Kommando
//  PortD Bit 3 = E, high-Impuls für gültige Daten
//---------------------------------------------------------------------------
//  lcd_send(..) - sendet ein Byte an LCD im 4-Bit-Modus
//  RS muss vorher richtig gesetzt sein
//  PE:  data=zu sendendes Byte
//---------------------------------------------------------------------------
void lcd_send(char data)
{
  // aktuelles RS ermitteln
  char rs=PORTD;
  rs&=4;
  // High-Teil senden
  char tmp=data;
  tmp&=0xf0;
  tmp|=rs;
  PORTD=tmp;
  // Schreibsignal
  sbi(PORTD,3);
  cbi(PORTD,3);
  // Low-Teil senden
  tmp=data;
  tmp&=0x0f;
  tmp*=16;
  tmp|=rs;
  PORTD=tmp;
  // Schreibsignal
  sbi(PORTD,3);
  cbi(PORTD,3);
  // verarbeiten lassen
  wait_ms(1);
}
//---------------------------------------------------------------------------
//  lcd_cmd(..) - sendet ein Kommando an LCD
//  PE:  cmd=Kommando-Byte
//---------------------------------------------------------------------------
void lcd_cmd(char cmd)
{
  cbi(PORTD,2);    // RS löschen = Kommando
  lcd_send(cmd);    // senden
}
//---------------------------------------------------------------------------
//  lcd_write(..) - sendet ein Zeichen (Daten) an LCD
//  PE:  text=Zeichen
//---------------------------------------------------------------------------
void lcd_write(char text)
{
  sbi(PORTD,2);    // RS setzen = Daten
  lcd_send(text);    // senden
}
//---------------------------------------------------------------------------
//  lcd_write(..) - sendet eine Zeichenkette an LCD
//  Die Zeichenkette muss mit 0x00 abgeschlossen sein
//  PE:  pText=Zeiger auf Zeichenkette
//---------------------------------------------------------------------------
void lcd_write(char* pText)
{
  while(pText[0]!=0)
  {
    lcd_write(pText[0]);
    pText++;
  }
}
//---------------------------------------------------------------------------
//  lcd_write(..) - sendet eine Zeichenkette an LCD
//  PE:  pText=Zeiger auf Zeichenkette
//    count=Anzahl der zu sendenden Zeichen
//---------------------------------------------------------------------------
void lcd_write(char* pText, int count)
{
  while(count!=0)
  {
    lcd_write(pText[0]);
    pText++;
    count--;
  }
}
//---------------------------------------------------------------------------
//  lcd_home(..) - Cursor auf Position 1,1
//---------------------------------------------------------------------------
void lcd_home()
{
  lcd_cmd(0x02);
  wait_ms(2);      // warten
}
//---------------------------------------------------------------------------
//  lcd_clear(..) - löscht die Anzeige im LCD
//---------------------------------------------------------------------------
void lcd_clear()
{
  lcd_cmd(0x01);
  wait_ms(2);      // warten
}
//---------------------------------------------------------------------------
//  lcd_on(..) - schaltet das LCD an
//---------------------------------------------------------------------------
void lcd_on()
{
  lcd_cmd(0x0E);
}
//---------------------------------------------------------------------------
//  lcd_off(..) - schaltet das LCD aus
//---------------------------------------------------------------------------
void lcd_off()
{
  lcd_cmd(0x08);
}
//---------------------------------------------------------------------------
//  lcd_goto(..) - setzt die Cursorposition
//   PE:  row = Zeile 1..2
//    col = Spalte 1..16
//---------------------------------------------------------------------------
void lcd_goto(int row, int col)
{
  row--;        // Null-basierend
  row&=0x01;      // sicherheitshalber
  row*=0x40;      // Zeile nach Bit 6 bringen
  col--;        // Null-basierend
  col&=0x0f;      // sicherheitshalber
  char tmp=row|col;
  tmp|=0x80;      // Cursor setzen
  lcd_cmd(tmp);    // senden
}
//---------------------------------------------------------------------------
//  lcd_init(..) - Schaltet die Ports und Initialisiert das LCD
//---------------------------------------------------------------------------
void lcd_init()
{
  // Port D = Ausgang
  DDRD=0xff;
  PORTD=0;
  // warten bist LCD-Controller gebootet
  wait_ms(200);    
  // 4-BitModus einschalten
  PORTD=0x20;      
  // Schreibsignal
  sbi(PORTD,3);
  cbi(PORTD,3);
  wait_ms(5);      // Zeit zum Umschalten lassen
  // ab hier im 4-Bit-Modus
  lcd_cmd(0x28);    // Funktions-Set: 2 Zeilen, 5x7 Matrix, 4 Bit
  //lcd_off();
  lcd_cmd(0x06);    // Entry Mode
  lcd_on();
  lcd_clear();


}

//---------------------------------------------------------------------------
// Main-Funktion
//---------------------------------------------------------------------------
int main (void)
{
    uint16_t ReadChannel(uint8_t mux_adc_channel);         // Auslese-Funktion deklarieren
  unsigned int PID_Berechnung (signed int x, signed int w);  // REGLER deklarieren
  TCCR1A = (1<<WGM11) | (1<<WGM10) | (1<<COM1A1);       // 10Bit-PWM-Modus (Auflösung=1024!)
                                    //Nicht Invertierend
  TCCR1B = (1<<CS10);                     // Aktivieren mit Prescaler 1, also keine Frequenz-'Teilung'
   DDRB |= (1<<DDB1);                       // Nicht vergessen, da es sich hier um einen Ausgang handelt, 
                                 //müssen wir das dem Mikrocontroller auch mitteilen
  wait_ms(2);    
  lcd_init();
  while (true)           // Mainloop
  {
    uint16_t SollR;
    uint16_t SollR1;
    uint16_t Ieingang;
    uint16_t Ieingang1;
        uint16_t Ueingang;
        uint16_t Reingang;
      
        uint16_t adc;
    uint16_t adc2; 
    uint16_t Y1;
    
           
        Ueingang = ReadChannel(0);
        SollR = ReadChannel(1);
        Ieingang = ReadChannel(2);
        Ieingang1= Ieingang/2; // Umrechnung U --> I Toellner Laborgerät
        
        Reingang = Ueingang/Ieingang1;
        
        SollR1 = SollR/Ieingang1;
        
        adc2 = Reingang;
        adc2 = adc2/64;
    lcd_goto(1,1);
    //lcd_write("Sollposition (°)");
    int j;
    j=0;
    for (j=0;j<adc2;j++)  lcd_write(0xFF);
    for (j=j;j<16;j++)  lcd_write(' ');
    wait_ms(2);
    
    adc = SollR1;
    adc=adc/64;    //10 bit auf 4 bit reduzieren
    
    int i;
    i=0;
    lcd_goto(2,1);
    
    
    for (i=0;i<adc;i++)  lcd_write(0xFF);
    for (i=i;i<16;i++)  lcd_write(' ');
    wait_ms(2);

                    
    Y1 =PID_Berechnung(Reingang, SollR1);
    OCR1A = Y1; 
   

    
  }
return 0;
}
//---------------------------------------------------------------------------------------------------------
uint16_t ReadChannel(uint8_t mux_adc_channel) //Unsere Funktion zum ADC-Channel aus lesen
{
  uint8_t i;
  uint16_t result = 0;         //Initialisieren wichtig, da lokale Variablen
                               //nicht automatisch initialisiert werden und
                               //zufällige Werte haben. Sonst kann Quatsch rauskommen
  ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS0);    // Frequenzvorteiler auf 32 setzen und ADC aktivieren 
 
  ADMUX = mux_adc_channel;                      // übergebenen Kanal waehlen
  ADMUX |= (1<<REFS1) | (1<<REFS0); // interne Referenzspannung nutzen 
 
  /* nach Aktivieren des ADC wird ein "Dummy-Readout" empfohlen, man liest
     also einen Wert und verwirft diesen, um den ADC "warmlaufen zu lassen" */
  ADCSRA |= (1<<ADSC);              // eine ADC-Wandlung (Der ADC setzt dieses Bit ja wieder auf 0 nach dem 
                    // Wandeln)
  while ( ADCSRA & (1<<ADSC) ) {
     ;     // auf Abschluss der Wandlung warten 
  }
 
  // Eigentliche Messung - Mittelwert aus 4 aufeinanderfolgenden Wandlungen
  for(i=0;i<4;i++)
  {
    ADCSRA |= (1<<ADSC);            // eine Wandlung
    while ( ADCSRA & (1<<ADSC) ) {
      ;     // auf Abschluss der Wandlung warten 
    }
    result += ADCW;            // Wandlungsergebnisse aufaddieren
  }
  ADCSRA &= ~(1<<ADEN);             // ADC deaktivieren ("Enable-Bit" auf LOW setzen)
 
  result /= 4;                     // Summe durch vier teilen = arithm. Mittelwert
 
  return result;
}
//-----------------------------------------------------------------------------------------------------------

//-----------------------------------------------------------------------------------------------------------
// REGLER
// W, X, Y in INT  - Eingänge mit Vorzeichen
// interne Variablen sollten dennoch in double gehalten werden (Gleitkommadarstellung)

unsigned int PID_Berechnung (signed int x, signed int w)
{

double Kp;
double I;
double Ta;
double D;
unsigned int prop;
unsigned int integ;
unsigned int diff;
signed int ealt;
int16_t esum;
int16_t e;

uint16_t y; 




Kp = 0.5;
I = 0.1;
D= 0.05;
Ta = 0.1;





if (x > w)
{   
e = x - w;            // aktuelle Regelabweichung bestimmen
}

if (x < w)
{
  e = 0;
}

if ((integ <= 1023)&&(integ > 0))
 {
   esum+=e;        // Summe der Regelabweichung aktualisieren  
 }

prop = (Kp*e);   // Proportional Faktor
integ = (I*Ta*esum); // Integraler Anteil
diff = (D*((e-ealt))/Ta);   // Differenzieller Anteil
ealt = e;                   // Regelabweichung für nächste Abtastung merken
y = prop + integ + diff;    // Ausgangsstellgröße


//if (y >= 1023)               // Stellgröße auf 0..1023 begrenzen(10 bitPWM)
//{
  // y = 1023;
 //}
 if (y < 1)
  {
    y = 0;
  }
 

return (y+10);              // unsigned int weil y nur zwischen 0 und 1023
 }


Autor: Gabriel 85 (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Im Anhang hab ich noch ein Bild was das ganze eigentlich soll...die 
Berechnung des Widerstandes im Microcontroller aus y und Ua ist 
notwendig

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.