Source file: /~heha/mb-iwp/Bergwerk/fba-fw-230825.zip/regler.cpp

// Ebene 5: PI-Regler für Geschwindigkeit und Hydraulikventile

#include "fba.h"

/*************
 * PI-Regler *
 *************/

int limitInt(long v) {
 return limit<long>(v,32000);
}

Regler regler[4];	// wird (offenbar) per Konstruktor initialisiert!??
// Index 0: Lenkung, 1: Tempo, 2: Hubarm, 3: Löffel

Regler::Log Regler::log;

// Input: Fehler in Hundertstel-Winkelgrad
// Bspw. bei Istlenkwinkel -39° und Solllenkwinkel +39° = 7800
// D.h. Kp=1 bedeutet 1 PWM-Schritt für 2,56° Regelabweichung
// Output: Stellung des Hydraulikventils (gespreizt)
// Nicht integrieren wenn Ausgang auf Anschlag
// (Hinweis von Dr. Johannes Rudolph)
// 230825: Für Unterscheidung ob Slip-Stick oder veränderter Sollwert
// ist (mindestens) ein weiterer Parameter erforderlich!
char Regler::neu_soll(int e) {
 auto li = log.current();	// Vier Integerwerte „zur freien Verfügung“
 li.e = e;
 bool plus = e>=0;
 Eedata::PID&pi=eedata.pid[this-regler];
 int min = pi.L[0+(plus<<1)]<<8, max = pi.L[1+(plus<<1)]<<8;
 li.Σ = limit(add_sat(eΣ,e),min,max);	// Überlaufsichere 16-Bit-Addition
 int eω = eΩ;
 li.Δ = add_sat(e,-eω);	// Differenz-Anteil
 if (!f.losgerissen && abs(li.Δ)>=pi.M) f.losgerissen=true;
 eΩ = eω = (long(eω)*3+e)>>2;	// Hier: Unendliche Impulsantwort, aber wenig filtern (schneller)
 long y= (long)pi.Kp[plus]*li.e	// Überlaufsichere 32-Bit-Addition
	+(long)pi.Ki[plus]*shr6r(li.Σ)	// 8 Bit rechtsschieben war für Fahrtregler ungeeignet. 17 Ergebnisbits.
	+(long)pi.Kd[plus]*li.Δ;	// zurzeit ungenutzt
 int r = li.r = limitInt(y>>8);	// ohne Spreizung aufzeichnen
 log.savecurrent();		// Schreibzeiger erhöhen
// Da die Ausgangswirkung servotypisch nicht bei ±1 einsetzt
// (was für einen Regler elender Mist ist, Slip-Stick-Effekt)
// wird das Ausgangssignal gespreizt.
// Die Spreizwerte (im EEPROM) wurden experimentell ermittelt:
// Controller, fba3, Spannung 7..8 V (war unabhängig), aufgebockt.
// Im abgelegten Spreizbereich erfolgt „gerade so keine“ Reaktion.
 if (r) {
  r =limit<int>(r,f.vmax);		// Geschwindigkeitsbegrenzung vor Spreizwert
  r+=pi.Z[(r>=0)+(f.losgerissen<<1)];	// Spreizwert (vzb.)
 }
 if (r>=125) r=125;		// Endgültig auf ±125 begrenzen
 else if (r<=-125) r=-125;
 else eΣ = li.Σ;		// tatsächlich integrieren
 return r;
}

void Regler::periodic() {
 unsigned t=tout;
 if (t) {
  if (--t) {
   tout=t;		// rückschreiben
   byte i=this-regler;	// 0..3
//   if (i!=1) {
//    int n=
    l2.set(i|L2::fRegler,
	neu_soll(so*100-l4.lin[i]));
	
	//-l2.pwms[i];
// TODO: Geschwindigkeit in Begrenzung der Regler-Summe einfließen lassen!
// Geschwindigkeit der PWM-Veränderung in Schritt/20ms
// TODO: Geschwindigkeit des Hydraulikzylinders einfließen lassen (D-Anteil)
//    n=limit<int>(n,sp>0?sp:125);
//    l2.set(i|L2::fAdd|L2::fRegler|1<<L2::bSlow,n);
//   }else{
//    char st = so*80/28;	// Ungefähre Vorsteuerung (Leerlaufgeschwindigkeit bereits bei Servostellung 80)
// probeweise rein integrierend, ohne Zeitbegrenzung, langsam
//    char e = so - idiv100r(l4.lin[1]);	// Regelabweichung
//    e = limit<char>(e,5);		// langsam
//    eΣ = limit<int>(eΣ+e,125);
//    l2.set(i|L2::fRegler/*|1<<L2::bSlow*/,limit<int>(st+eΣ,125));
//   }
  }else reset();	// NaN anzeigen wenn Timeout erreicht wurde
 }// sonst nichts tun
}

// <to> in Zehntelsekunden mit folgenden Sonderbedeutungen:
//	0: Regler beenden
//	0xFC: Zeit unendlich
//	0xFE: Zeit nicht beeinflussen
//	0xFF: Wie 0 aber ohne zugehörige PWM-Ausgänge zu beeinflussen
//		(zum Rekursion vermeiden bei Aufruf aus L2::set())
void Regler::reset(byte to,bool cont) {
 byte i=this-regler;	// 0..3
 if (!tout || !cont) {
  eΣ = 0;		// Das wichtigste zuerst: Integrierglied nullsetzen
  eΩ = so*100-l4.lin[i];	// Altwert mit ungefilterter Regelabweichung starten
  f.losgerissen = false;
  if (!to || to==0xFF) {
   if (to!=0xFF) {	// Bei Aufruf aus L2::set() keine Rekursion!
    if (i==0) {
// Katastrophal: Lenkregler auf Anschlag _und_ Fahrzeug in Fahrt:
// Hydraulikpumpe oder Steuerventil (oder sonstwas) kaputt! Nicht weiterfahren!
     if (!to && cabs(l2.pwms[0])>=100 && l2.pwms[1]) {
      l2.set(1,0);	// ggf. Fahrtregler sowie Scheduler abwürgen
      eedata.debug[11]++;
     }
    }
    if (i!=1) l2.set(i|L2::fRegler,0);	// Hydraulikventil schließen aber Fahrt beibehalten
   }
   so=cNaN;
   to=0;
  }
 }
 if (to==0xFD) tout=3*60*50;	// Unendlich? Nein: 3 Minuten (9000 Ticks)
 else if (to!=0xFE) tout=to*5;	// Timeout nicht setzen
}

// liefert true wenn Regelziel erreicht
bool Regler::set(byte i,char so, char vmax) {
 byte k = i&3;
 Regler&reg=regler[k];
 char b = reg.so;
// Falls Konstruktor nicht ausgeführt, hier nachholen
 if (!reg.tout) reg.so = b = cNaN;
// if (!reg.sp) reg.sp = 10;
 if (so==cNaN) {
  if (b!=cNaN) reg.reset();
  return true;		// NaN als „Regelziel erreicht“ durchlaufen lassen
 }
 if (b==cNaN) b=idiv100r(l4.lin[k]);	// Für Addition vom aktuellen Istwert (!!) ausgehen
 int c = so;
 if (i&fAdd) c+=b;
 char min=eedata.fz.sp[0], max=eedata.fz.sp[1];
 switch (k) {
  case 0: min=eedata.lenkung.min();max=eedata.lenkung.max();break;
  case 2: min=eedata.hubarm.min(); max=eedata.hubarm.max(); break;
  case 3: min=eedata.löffel.min(); max=eedata.löffel.max(); break;
 }
 byte Δ = max-min;	// großes Δ = ganzer Stellweg (in Winkelgrad oder cm/s)
 c = limit<int>(c,min,max);	// Niemals Ziel außerhalb Stellbereich
 byte δ = c>=b ? c-b : b-c;	// kleines δ = dieser Stellweg = erste Regelabweichung
 if (!δ) return true;	// Abkürzung: Regelung nicht erforderlich
 reg.so = c;		// Regler setzen oder ändern
 if (vmax>0) reg.f.vmax = vmax;	// Nur positive Werte akzeptieren, cNaN ist hier negativ
 byte t = eedata.pid[k].T;	// Maximale Regelzeit in Zehntelsekunden für maximalen Stellweg
 if (!t) {		// unendlich
  reg.reset(0xFD,true);	// Kode für unendlich
 }else{
// TODO: reg.f.vmax in die Zeitbegrenzung einfließen lassen (t wird größer)
  if (t>=10) t-=10;	// Mindestzeit 1 s vor Skalierung abziehen
  t = t*δ/Δ;		// skalieren (= wird kleiner, alles vorzeichenlos)
  t+=10;		// Mindestzeit 1 s
  if (t>0xFC) t=0xFC;	// Nicht in Geheimkodes hineinrutschen
  reg.reset(t,true);	// Endliche Zeit setzen
 }
 return false;
}

void Regler::on20ms() {	// Aufrufen wenn neue Messwerte in l4.lin vorliegen
 for (byte i=0; i<elemof(regler); i++) regler[i].periodic();
}
Detected encoding: UTF-80