Programmierproblem mit PID-Objekten

hallo,
mit OOP stehe ich noch auf Kriegsfuss...

ich habe eine lib mit einem PID Objekt und mehren Instanzen:

PID PID0(&Input[0] &Output[0], &Setpoint[0],  2,5,1, DIRECT);
PID PID1(&Input[1] &Output[1], &Setpoint[1],  2,5,1, DIRECT);
PID PID2(&Input[2] &Output[2], &Setpoint[2],  2,5,1, DIRECT);

ich möchte jetzt eine Funktion schreiben, die mehrere Aktionen mit einer variabel übergegeben Instanz ausführt,
also wenn ich PID0 übergebe, soll mit Input[0] Output[0], Setpoint[0] gerechnet werden, und wenn ich
PID1 übergebe, soll mit Input[1] Output[1], Setpoint[1] gerechnet werden,
leider kann ich die PID0, PID1, PID2 nicht als PID[0] , PID[1] , PID[2] schreiben, möchte aber auch nicht für jede einzelne Instanz die ganze Leier von Befehlen spagetticodemäßig einzeln untereinander schreiben.

also suche ich etwas in der Art

float foo(PID myPID) {
  myPID.Input=...
  myPID.Output=...
  myPID.compute();
  return myPID.Output;
}

// ...
foo(PID0);
foo(PID1);
foo(PID2);
// ...

was ntl so nicht geht -
Gibt es trotzdem einen Trick?

Lerne den Unterschied zwischen call by value und call by reference. Das ist nicht nur für Objekte relevant, sondern auch für elementare Darentypen. Du übergibst da eine Kopie des Objekts. Das ist gar nicht gut

Du musst eine Referenz auf das Objekt übergeben:

float foo(PID &myPID)
{
}

Der Aufruf erfolgt dann ganz normal mit foo(PID1)

Alternativ gehen auch Zeiger, aber das ist unnötig wenn es auch Referenzen tun:

float foo(PID* myPID)
{
}

Dann musst du aber beim Zugriff -> statt . verwenden. Und anders als bei einer Referenz muss man beim Aufruf explizit die Adresse mit & übergeben.

Das ist auch der Grund weshalb du im Konstruktor die Adresse übergibst:

&Input[1] &Output[1], &Setpoint[1]

Damit nicht nur eine Kopie übergeben wird, sondern der Wert verändert werden kann

leider kann ich die PID0, PID1, PID2 nicht als PID[0] , PID[1] , PID[2] schreiben,

Das würde theoretisch schon gehen wenn man ein Array aus Objekten, oder aus Zeigern auf Objekte hätte. Aber das in einer Funktion zu kapseln ist schöner.

dankeschön,
das mit "by reference" kannte ich schon für einfache Datentypen, ich dachte nicht, dass es auch für Objekte funktioniert.

also lautet jetzt folgende Rechenfunktion so...?

float foo(PID &myPID) {
  myPID->Input=...
  myPID->Output=...
  myPID->compute();
  return myPID->Output;
}

ich habe ja nirgends Input, Output, Setpoint, compute() in der Routine vorher lokal (oder global) definiert - er weiß und vesteht dann, dass er dann z.B jeweils, je nach Übergabeadresse,
myPID->Input durch PID0.Input[0] , PID1.Input[1] , PID2.Input[2] , ersetzen muss ?

Natürlich geht das auch mit Objekten. Es hier sogar noch wichtiger (selbst wenn man das Objekt nicht verändern will), da man Objekte nie als Kopie übergeben sollte. Einfach weil das Speicher und Zeit kostet.

Das -> ist für Zeiger. Nicht für Referenzen. Das sind zwei ähnliche, aber unterschiedliche Dinge. Referenzen werden wie gewöhnliche Variablen verwendet. Man schreibt lediglich bei der Deklaration ein & dazu

Das geht allerdings nicht:

 myPID.Input=...
  myPID.Output=...

Soweit hatte ich vorhin nicht gedacht. Diese Variablen gehören nicht fest zum Objekt. Die sind in deinem Programm von dir deklariert. Das Objekt erhält lediglich einen Zeiger darauf. Und dieser ist private.

Du könntest in PID_v1.h die Zeiger public machen. *myInput, etc. Dann kannst du die Werte über das Objekt ändern. Das ist in ein paar Sekunden erledigt.

Oder du legst dir doch die Objekte in einem Array an. Geht glaube ich so:

PID pids[] =
{ 
   PID (&Input[0] &Output[0], &Setpoint[0],  2,5,1, DIRECT),
   PID (&Input[1] &Output[1], &Setpoint[1],  2,5,1, DIRECT),
   PID(&Input[2] &Output[2], &Setpoint[2],  2,5,1, DIRECT)
};

Dann kannst du über über den gleichen Index das Objekt, Input, Output und Setpoint ansprechen. Den Index kann man auch eine Funktion übergeben.

Sowas in der Art:

void update(int index, double newInput, double newOuput)
{
    Inputs[index] = newInput;
    ...
    pids[index].compute();
}

ja, jetzt ists klarer.
Das mit dem public in der library und Aufruf über Pointer scheint mir zwar die elegantere Möglichkeit zusein, ist aber für mich persönlich noch zu ungewohnt und nicht idiotensicher genug.
Die Array-Methode allerdings mit Index-Übergabe ist von der Logik für mich einfacher, das probier ich mal aus.

Danke erst einmal!

Noch eine schöne Option wäre ein struct aus dem Objekt (oder einem Zeiger darauf) und den zugehörigen Variablen. Dann kann man alles was jeweils zusammengehört über ein struct ansprechen. Und dann eine Referenz auf das entsprechende struct an an die Funktion übergeben.

ja, genau, ich dachte ursprünglich, dass PID bereits so eine Stuktur im Objekt qusai inegriert hätte (eben plus Methoden).
Aber das sin z.Zt alles noch böhmische Dörfer.

Arrays verstehe ich :wink:

so - Fehlermeldungen, die ich nicht verstehe:

#include <PID_v1.h>

#define MAXMOTORS  2

//Define Variables we'll be connecting to
double PIDsetpoint[MAXMOTORS], PIDinput[MAXMOTORS], PIDoutput[MAXMOTORS];

//Specify the links and initial tuning parameters

PID pids[] =
{ 
   PID (&PIDinput[0] &PIDoutput[0], &PIDsetpoint[0],  2,5,1, DIRECT),
   PID (&PIDinput[1] &PIDoutput[1], &PIDsetpoint[1],  2,5,1, DIRECT),
   PID(&PIDinput[2] &PIDoutput[2], &PIDsetpoint[2],  2,5,1, DIRECT)
};

pid1012:18: error: invalid operands of types 'double*' and 'double' to binary 'operator&'
pid1012:19: error: invalid operands of types 'double*' and 'double' to binary 'operator&'
pid1012:20: error: invalid operands of types 'double*' and 'double' to binary 'operator&'
pid1012.ino: In function 'void setup()':
pid1012:174: error: expected unqualified-id before '[' token
pid1012:179: error: expected unqualified-id before '[' token
pid1012:180: error: expected unqualified-id before '[' token
pid1012:181: error: expected unqualified-id before '[' token
pid1012:183: error: expected unqualified-id before '[' token
pid1012.ino: In function 'void loop()':
pid1012:198: error: expected primary-expression before '[' token
pid1012:204: error: expected unqualified-id before '[' token
pid1012:206: error: expected primary-expression before '[' token

was will er mir damit sagen?

Schau mal genau hin. Da fehlen Kommas nach dem ersten Paramter. Ich habe das aber nur von dir kopiert und da Fehlen sie auch.

Außerdem muss dein Array 3 groß sein. Nicht 2.

uuuhh.. ja klar, im Moment habe ich ja auch nur 2, also [ 0] und [1]

:blush:

geht weiter...

#include <PID_v1.h>
#define MAXMOTORS  2

double PIDsetpoint[MAXMOTORS], PIDinput[MAXMOTORS], PIDoutput[MAXMOTORS];

PID pids[] =
{ 
   PID (&PIDinput[0], &PIDoutput[0], &PIDsetpoint[0],  2,5,1, DIRECT),
   PID (&PIDinput[1], &PIDoutput[1], &PIDsetpoint[1],  2,5,1, DIRECT),
};


//...  *SNIP*
//...

void setup()
{
  
   pids[0].PIDinput = motenc[0];

   // set PID parameters
   pids[0].SetMode(AUTOMATIC);
   pids[0].SetOutputLimits(-255, 255);
   pids[0].SetSampleTime(PID_REGTIME_MS);
    
   pids[0].setpoint = 360;                 // set target,  
   runstate[0]=OUT_RUNSTATE_ACTIVE;   // switch the PID on to motor[0]

  //...
}

pid1012.ino: In function 'void setup()':
pid1012:175: error: 'class PID' has no member named 'PIDinput'
pid1012:182: error: 'class PID' has no member named 'setpoint'
pid1012.ino: In function 'void loop()':
pid1012:197: error: expected primary-expression before '[' token
pid1012:203: error: expected unqualified-id before '[' token
pid1012:205: error: expected primary-expression before '[' token
pid1012:205: error: expected )' before 'setpoint' pid1012:205: error: expected )' before ';' token
pid1012:208: error: expected unqualified-id before '[' token
pid1012:209: error: expected unqualified-id before '[' token
pid1012:211: error: expected unqualified-id before '[' token
pid1012:213: error: expected primary-expression before '[' token
pid1012:217: error: expected unqualified-id before '[' token
pid1012:218: error: expected unqualified-id before '[' token
pid1012:222: error: expected primary-expression before '[' token

Die Varianten
pids[ 0].PIDinput [ 0]
und
pids.PIDinput [ 0]

akzeptiert er aber genausowenig :frowning:

Erstens muss im pids Konstruktor das Komma am Ende der letzten Zeile weg.

Zweitens ist das hier völliger Unsinn:

pids[0].PIDinput = motenc[0];

Natürlich hat die Klasse keine entsprechende Member Variable. Wie auch?!

Das Array wird ganz normal für sich angesprochen:

PIDinput[0] = motenc[0];

ach so, ich dachte, das wäre ein array mit einer Struktur :grin:

struct und class sind sehr ähnlich, ausser dass bei einer class alles was nicht nach

** **public:** **

steht, eben nur zum internen Gebrauch ([b]private:[/b] ) gedacht ist.

Was soll übrigens die Zeile

PIDinput[0] = motenc[0];

in Setup ?
Das ändert nur einmaig den Wert von PIDInput[0].
Nachträglich einen anderen Zeiger zuweisen ist nicht vorgesehen, und geht so nicht.

stimmt mit dem setup, ist ein Relikt aus der allerersten Version :wink:

in der loop steht es dann so:

void loop()
{ 
//...  
   PIDinput[0] = motenc[0];
//...

Der komplette Code wird jetzt kompiliert,

//***********************************************************************************
// PID controller
// Reading motor encoder[0] to control PWM motor [0]
// ver 1.012
//************************************************************************************

#include <PID_v1.h>

#define MAXMOTORS  2

// PID Variables 
double PIDsetpoint[MAXMOTORS], PIDinput[MAXMOTORS], PIDoutput[MAXMOTORS];

// PID tuning parameters
PID PIDs[] =
{ 
   PID (&PIDinput[0], &PIDoutput[0], &PIDsetpoint[0],  2,5,1, DIRECT),
   PID (&PIDinput[1], &PIDoutput[1], &PIDsetpoint[1],  2,5,1, DIRECT)
};

#define  PID_REGTIME_MS       8
#define  PID_REG_PREC         2
#define  PID_REGSTATE_IDLE      0
#define  PID_REGSTATE_ACTIVE    1


//*************************************************************

#define sensortouch(pinHIGH) !digitalRead(pinHIGH)

#define startpin  12

// set motor pins
// motor 0
#define pinmenc0A  2  // encA yellow
#define pinmenc0B  3  // encB blue
#define pinmot0d1  4  // dir1
#define pinmot0d2  5  // dir2
#define pinmot0pwm 6  // enable
// motor 1
#define pinmenc1A  7  // encA yellow
#define pinmenc1B  8  // encB blue
#define pinmot1d1  9  // dir1
#define pinmot1d2  10 // dir2
#define pinmot1pwm 11 // enable


//************************************************************************************
// Encoder functions courtesy of / entnommen aus: http: //www.meinDUINO.de //
//
// Globale Variablen zur Auswertung in der Interrupt-Service-Routine (ISR)
//************************************************************************************
volatile int8_t altAB[MAXMOTORS]  = {0,0};
volatile long   motenc[MAXMOTORS] = {0,0},
                oldenc[MAXMOTORS] = {0,0};
volatile int  regstate[MAXMOTORS];                
                

// Die beiden Schritt-Tabellen für 1/1, 1/2 oder 1/4-Auflösung/resolution
// 1/1 Auflösung/resolution
//int8_t schrittTab[16] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0};

// 1/2 Auflösung/resolution
  int8_t schrittTab[16] = {0, 0,0,0,1,0,0,-1, 0,0,0,1,0,0,-1,0};

// 1/4 Auflösung/resolution
//int8_t schrittTab[16] = {0,0,0,0,0,0,0,-1,0,0,0,0,0,1,0,0};

//*************************************************************
// Interrupt Service Routine: wenn Interrupt ausgelöst wird
//*************************************************************
ISR(TIMER1_COMPA_vect) {
  altAB[0] <<= 2;
  altAB[0] &= B00001100;
  altAB[0] |= (digitalRead(pinmenc0A) << 1) | digitalRead(pinmenc0B);
  motenc[0] += schrittTab[altAB[0]];           //
 
  altAB[1] <<= 2;
  altAB[1] &= B00001100;
  altAB[1] |= (digitalRead(pinmenc1A) << 1) | digitalRead(pinmenc1B);
  motenc[1] += schrittTab[altAB[1]];           //
}

//************************************************************************************

byte pinmotdir[MAXMOTORS][2]={ {pinmot0d1, pinmot0d2},
                               {pinmot1d1, pinmot1d2} };
                                         
int  pinmotpwm[MAXMOTORS]={pinmot0pwm, pinmot1pwm};
 
 

//************************************************************************************
inline void motoron(byte motnr, int power) {
  if(power>0) {
     digitalWrite( pinmotdir[motnr][0],HIGH);
     digitalWrite( pinmotdir[motnr][1],LOW);
  }
  else
  if(power<0) {
     digitalWrite( pinmotdir[motnr][0],LOW);
     digitalWrite( pinmotdir[motnr][1],HIGH);
  }
  else
  if(power==0) {
     digitalWrite( pinmotdir[motnr][0],LOW);
     digitalWrite( pinmotdir[motnr][1],LOW);
  }
 
  power = abs(power);
  if(power>254) power=254;
  analogWrite(pinmotpwm[motnr],power);
}

//------------------------------------------------------------------------------------

inline void motorbrake(byte motnr) {  // to do list !!!
  motoron(motnr, 0);
}

//------------------------------------------------------------------------------------

inline void motorcoast(int motnr) {
  motoron( motnr, 0);
}

#define  motoroff    motorcoast

//************************************************************************************

void setup()
{
  
   Serial.begin(115200);
   
   pinMode(startpin,INPUT_PULLUP);   // btn to start
   
   pinMode(pinmot0d1,OUTPUT);        // dir1
   pinMode(pinmot0d2,OUTPUT);        // dir2
   pinMode(pinmenc0A,INPUT_PULLUP);  // encA
   pinMode(pinmenc0B,INPUT_PULLUP);  // encB
   pinMode(pinmot0pwm,OUTPUT);       // enable
   
   pinMode(pinmot1d1,OUTPUT);        // dir1
   pinMode(pinmot1d2,OUTPUT);        // dir2
   pinMode(pinmenc1A,INPUT_PULLUP);  // encA
   pinMode(pinmenc1B,INPUT_PULLUP);  // encB
   pinMode(pinmot1pwm,OUTPUT);       // enable
 
   // time interrupt for encoder readings
   noInterrupts(); // Jetzt keine Interrupts / disable
       TIMSK1 |= (1<<OCIE1A);  // Timer 1 PIDOutput Compare A Match Interrupt Enable
       TCCR1A = 0;             // "Normaler" Modus
       // WGM12: CTC-Modus einschalten (Clear Timer on Compare match)
       //        Stimmen OCR1A und Timer überein, wird der Interrupt ausgelöst
       // Bit CS12 und CS10 setzen
            
       // => Prescaler=8:
       TCCR1B = (1<<WGM12) | (1<<CS11);
       // Frequenz = 16,000,000 / 8 / 512 = rd. 4 kHz
       OCR1A =511;
       
   interrupts(); // Interrupts wieder erlauben / enable

   // set PID parameters
   PIDs[0].SetMode(AUTOMATIC);
   PIDs[0].SetOutputLimits(-255, 255);
   PIDs[0].SetSampleTime(PID_REGTIME_MS);
    
   PIDsetpoint[0] = 360;                 // set target,  
   regstate[0]=PID_REGSTATE_ACTIVE;   // switch the PID on to motor[0]
}

char started =0;
int  cnt=0;


//************************************************************************************

void loop()
{ 
   cnt++;
   if (!started) {
     Serial.println("enc - output"); 
     Serial.print(motenc[0]); Serial.print(" - "); Serial.println(PIDoutput[0]); 
     delay(500); 
     started=1; 
   }
   
   
   PIDinput[0] = motenc[0];
 
   float gap = abs(PIDsetpoint[0]-PIDinput[0]); //distance away from setpoint 
   
   if (regstate[0]==PID_REGSTATE_ACTIVE) {
      PIDs[0].Compute(); 
      PIDs[0].SetTunings(40, 50, 1);
   }
   else PIDoutput[0]=0;
   
   if ((gap>PID_REG_PREC)&&(regstate[0]==PID_REGSTATE_ACTIVE)) motoron(0, PIDoutput[0]);

   if(cnt>=9) {
     if( (gap<=PID_REG_PREC)&& abs(oldenc[0]-motenc[0])<1 ) {
       PIDs[0].SetMode(MANUAL);
       PIDs[0].SetTunings( 0,  0, 0); 
       regstate[0]=PID_REGSTATE_IDLE;
       motoroff(0);
     }
     Serial.print(motenc[0]); Serial.print(" - "); Serial.println(PIDoutput[0]); 
     cnt=0; 
   }
   
   oldenc[0]=motenc[0];
   delay(10);
 
}

jetzt muss ich nochmal die Läufe testen, und dann geht's an die Erweiterung mit der foo-Funktion für weitere Motoren...

Wo ist da eine Struktur?

Die Idee mit dem struct ist etwas komplizierter als es sich anhört, aber man könnte es vielleicht so ähnlich machen:

struct PIDConfig
{ 
    double input;
    double output;
    double setpoint;
    PID* pid;
};

PIDConfig config;
PID pid(&config.input, &config.output, &config.setpoint, 0, 0, 0, DIRECT);

void setup()
{ 
     config.pid = &pid;
}

Nicht auf dem Arduino getestet, sondern nur in Visual C++, ob es kompiliert und die Zeiger passen. Am einfachsten ist es wahrscheinlich man erstellt so das PID Objekt per Hand und setzt einmal den Zeiger in config darauf.

Dann kann man das PID Objekt über config->pid ansprechen und die Einstellungen z.B. mit config.setpoint

update:
klappt wunderbar!

Danke an alle Helfer!

Hier das Ergebnis für alle Interessierten - share and enjoy ! :slight_smile:

// PID controller
// to control 2  encoder motors  by PWM 
// ver 1.022


#include <PID_v1.h>

#define MAXMOTORS  2


// global variables for Interrupt-Service-Routine (ISR)
volatile int8_t ISRab[MAXMOTORS]     = {0,0};  
volatile long   motenc[MAXMOTORS]    = {0,0},
                oldenc[MAXMOTORS]    = {0,0};
                

// global PID variables
double  PIDsetpoint[MAXMOTORS], 
        PIDinput[MAXMOTORS], 
        PIDoutput[MAXMOTORS];
int     PIDregstate[MAXMOTORS];      
float   PIDencgap[MAXMOTORS];               


// PID tuning parameters
PID PIDs[] =
{ 
   PID (&PIDinput[0], &PIDoutput[0], &PIDsetpoint[0],  2,5,1, DIRECT),
   PID (&PIDinput[1], &PIDoutput[1], &PIDsetpoint[1],  2,5,1, DIRECT)
};

#define  PID_REGTIME_MS       5
#define  PID_REG_PREC         1
#define  PID_REGSTATE_IDLE      0
#define  PID_REGSTATE_ACTIVE    1
#define  PID_REGMAX      255
#define  PID_REGMIN     -255


//*************************************************************

#define sensortouch(pinHIGH) !digitalRead(pinHIGH)

#define startpin  12

// set motor pins
// motor 0
#define pinmenc0A  2  // encA yellow
#define pinmenc0B  3  // encB blue
#define pinmot0d1  4  // dir1
#define pinmot0d2  5  // dir2
#define pinmot0pwm 6  // enable
// motor 1
#define pinmenc1A  7  // encA yellow
#define pinmenc1B  8  // encB blue
#define pinmot1d1  9  // dir1
#define pinmot1d2  10 // dir2
#define pinmot1pwm 11 // enable



// Encoder functions courtesy of / entnommen aus: http: //www.meinDUINO.de //


// Die beiden Schritt-Tabellen für 1/1, 1/2 oder 1/4-Auflösung/resolution
// 1/1 Auflösung/resolution
//int8_t schrittTab[16] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0};

// 1/2 Auflösung/resolution
  int8_t schrittTab[16] = {0, 0,0,0,1,0,0,-1, 0,0,0,1,0,0,-1,0};

// 1/4 Auflösung/resolution
//int8_t schrittTab[16] = {0,0,0,0,0,0,0,-1,0,0,0,0,0,1,0,0};


// Interrupt Service Routine: wenn Interrupt ausgelöst wird
ISR(TIMER1_COMPA_vect) {
  ISRab[0] <<= 2;
  ISRab[0] &= B00001100;
  ISRab[0] |= (digitalRead(pinmenc0A) << 1) | digitalRead(pinmenc0B);
  motenc[0] += schrittTab[ISRab[0]];           //
 
  ISRab[1] <<= 2;
  ISRab[1] &= B00001100;
  ISRab[1] |= (digitalRead(pinmenc1A) << 1) | digitalRead(pinmenc1B);
  motenc[1] += schrittTab[ISRab[1]];           //
}


byte pinmotdir[MAXMOTORS][2]={ {pinmot0d1, pinmot0d2},
                               {pinmot1d1, pinmot1d2} };                                       
int  pinmotpwm[MAXMOTORS]={pinmot0pwm, pinmot1pwm}; 


inline void motoron(byte motnr, int power) {
  if(power>0) {
       digitalWrite( pinmotdir[motnr][0],HIGH);
       digitalWrite( pinmotdir[motnr][1],LOW);
  }  else  if(power<0) {
       digitalWrite( pinmotdir[motnr][0],LOW);
       digitalWrite( pinmotdir[motnr][1],HIGH);
  }  else  if(power==0) {
       digitalWrite( pinmotdir[motnr][0],LOW);
       digitalWrite( pinmotdir[motnr][1],LOW);
  }
  power = abs(power);
  if(power>254) power=254;
  analogWrite(pinmotpwm[motnr], power);
}



inline void motorbrake(byte motnr) {  // to do list !!!
  motoron(motnr, 0);
}



inline void motorcoast(int motnr) {
  motoron( motnr, 0);
}

#define  motoroff    motorcoast

//************************************************************************************

void setup()
{
  
   Serial.begin(115200);
   
   pinMode(startpin,INPUT_PULLUP);   // btn to start
   
   pinMode(pinmot0d1,OUTPUT);        // dir1
   pinMode(pinmot0d2,OUTPUT);        // dir2
   pinMode(pinmenc0A,INPUT_PULLUP);  // encA
   pinMode(pinmenc0B,INPUT_PULLUP);  // encB
   pinMode(pinmot0pwm,OUTPUT);       // enable
   
   pinMode(pinmot1d1,OUTPUT);        // dir1
   pinMode(pinmot1d2,OUTPUT);        // dir2
   pinMode(pinmenc1A,INPUT_PULLUP);  // encA
   pinMode(pinmenc1B,INPUT_PULLUP);  // encB
   pinMode(pinmot1pwm,OUTPUT);       // enable
 
   // time interrupt for encoder readings
   noInterrupts(); // Jetzt keine Interrupts / disable
   
       TIMSK1 |= (1<<OCIE1A);  // Timer 1 PIDOutput Compare A Match Interrupt Enable
       TCCR1A = 0;             // "Normaler" Modus
       // WGM12: CTC-Modus einschalten (Clear Timer on Compare match)
       //        Stimmen OCR1A und Timer überein, wird der Interrupt ausgelöst
       // Bit CS12 und CS10 setzen
            
       // => Prescaler=8:
       TCCR1B = (1<<WGM12) | (1<<CS11);
       // Frequenz = 16,000,000 / 8 / 512 = rd. 4 kHz
       OCR1A =511;
       
   interrupts(); // Interrupts wieder erlauben / enable

   // set PID parameters
   PIDs[ 0].SetMode(AUTOMATIC);
   PIDs[ 0].SetOutputLimits(PID_REGMIN, PID_REGMAX);
   PIDs[ 0].SetSampleTime(PID_REGTIME_MS);
   PIDsetpoint[0] = 360;                 // set target,  
   PIDregstate[0]=PID_REGSTATE_ACTIVE;   // switch the PID on to motor[0]
   
   PIDs[ 1].SetMode(AUTOMATIC);
   PIDs[ 1].SetOutputLimits(PID_REGMIN, PID_REGMAX);
   PIDs[ 1].SetSampleTime(PID_REGTIME_MS);
   PIDsetpoint[ 1] = 540;                 // set target,  
   PIDregstate[ 1]=PID_REGSTATE_ACTIVE;   // switch the PID on to motor[0]
   
}

//************************************************************************************

char  started =0;
int   mainloopcnt=0;

void  PIDupdate( byte nr) { 
   PIDinput[nr] = motenc[nr];
   PIDencgap[nr] = abs(PIDsetpoint[nr]-PIDinput[nr]); //distance away from setpoint 
   if (PIDregstate[nr]==PID_REGSTATE_ACTIVE) {
      PIDs[nr].Compute(); 
      PIDs[nr].SetTunings(30, 80, 1.2);
   }
   else PIDoutput[nr]=0;
   if ((PIDencgap[nr]>PID_REG_PREC)&&(PIDregstate[nr]==PID_REGSTATE_ACTIVE)) 
       { motoron(nr, PIDoutput[nr]);  }
}



void PIDcheckok (byte nr) {
   if(mainloopcnt>=9) {                                       // stopped after 9 loops delay ?
     if( (PIDencgap[nr]<=PID_REG_PREC)&& abs(oldenc[nr]-motenc[nr])<1  
       && abs(PIDoutput[nr]<PID_REGMAX/2 ) ) {
       PIDs[nr].SetMode(MANUAL);
       PIDs[nr].SetTunings( 0,  0, 0); 
       PIDregstate[nr]=PID_REGSTATE_IDLE;
       motoroff(nr);
     }
   }
}



void loop()
{ 
   mainloopcnt++;
   if (!started) {
     Serial.println(); 
     Serial.println("nr | enc - output   |   nr | enc - output"); 
     Serial.print(          "0: "); Serial.print(motenc[0]); Serial.print(" | "); Serial.print(PIDoutput[0]); 
     Serial.print("     |    1: "); Serial.print(motenc[1]); Serial.print(" | "); Serial.print(PIDoutput[1]);
     Serial.println();
     
     delay(500);  // wait before starting
     started=1; 
   }
   
   for(int i=0; i< MAXMOTORS ; ++i) PIDupdate( i);
   
   for(int i=0; i< MAXMOTORS ; ++i) PIDcheckok( i);
   
   if (mainloopcnt >=9) {
     Serial.print(          "0: "); Serial.print(motenc[0]); Serial.print(" | "); Serial.print(PIDoutput[0]); 
     Serial.print("     |    1: "); Serial.print(motenc[1]); Serial.print(" | "); Serial.print(PIDoutput[1]);
     Serial.println();
     
     mainloopcnt=0;
   }
   
   for(int i=0; i< MAXMOTORS ; ++i) oldenc[ i]=motenc[ i];
   
   delay( 5);
    
 
}

Ziel(0)=360, Ziel(1)=540, Genauigkeit +/-1

nr | enc - output   |   nr | enc - output
0: 0 | 0.00     |    1: 0 | 0.00
0: 6 | 255.00     |    1: 8 | 255.00
0: 27 | 255.00     |    1: 32 | 255.00
0: 54 | 255.00     |    1: 63 | 255.00
0: 85 | 255.00     |    1: 98 | 255.00
0: 118 | 255.00     |    1: 136 | 255.00
0: 151 | 255.00     |    1: 174 | 255.00
0: 185 | 255.00     |    1: 213 | 255.00
0: 219 | 255.00     |    1: 252 | 255.00
0: 254 | 255.00     |    1: 291 | 255.00
0: 288 | 255.00     |    1: 331 | 255.00
0: 323 | 255.00     |    1: 371 | 255.00
0: 352 | -225.00     |    1: 412 | 255.00
0: 361 | -15.40     |    1: 454 | 255.00
0: 363 | 153.40     |    1: 497 | 255.00
0: 363 | 142.60     |    1: 530 | 75.00
0: 363 | 131.80     |    1: 542 | -46.20
0: 363 | 121.00     |    1: 545 | -151.40
0: 363 | 110.20     |    1: 545 | -167.80
0: 363 | 99.40     |    1: 545 | -184.20
0: 362 | 255.00     |    1: 545 | -200.20
0: 362 | 111.80     |    1: 545 | -216.60
0: 362 | 104.60     |    1: 545 | 37.80
0: 362 | 97.40     |    1: 544 | 21.80
0: 361 | 123.80     |    1: 544 | -24.20
0: 361 | 0.00     |    1: 544 | -7.80
0: 361 | 0.00     |    1: 544 | -22.20
0: 361 | 0.00     |    1: 544 | -36.60
0: 361 | 0.00     |    1: 544 | -51.00
0: 361 | 0.00     |    1: 543 | -33.80
0: 361 | 0.00     |    1: 541 | 20.60
0: 361 | 0.00     |    1: 541 | 0.00
0: 361 | 0.00     |    1: 541 | 0.00
0: 361 | 0.00     |    1: 541 | 0.00
0: 361 | 0.00     |    1: 541 | 0.00
0: 361 | 0.00     |    1: 541 | 0.00

PID sicher noch nicht 100% fein getunt :slight_smile: