Du willst ein Objekt der Klasse A erstellen, welches beim Aufruf von Methode B einen Task mit PID-Regler startet?
mfg
hallo,
ist es möglich, einen pthread-Task als Methode eines C++ Objekts zu implementieren, um mehrere solcher Tasks über mehrere Instanzen des Objekts unabhängig und pseudo-parallel starten zu können ?
Es geht um kompliziertere PID-Regler für Motoren mit Quadraturencodern, jeder Motor soll über eigene PID-Task-Instanzen (endlos oder selbstterminierend) gesteuert werden. Der Aufruf der Motor-PID-Tasks wird sehr häufig gebraucht und nötig sein, und zwar für jede einzelne Motorstellung, die die einzelnen Motoren jeweils einzeln und unabhängig / asynchron immer wieder neu und mit veränderten Zielwerten und Abbruchbedingungen anfahren müssen (Zielstellung anfahren, dann coasten oder dann bremsen, oder dann dauerhaft gegen Widerstand oder passive externe Verstellkräfte bis zum gesonderten Abbruchbefehl approximiert halten), teilweise werden auch 2 Motoren paarweise miteinander per PID z.B für "perfekten" Geradeauslauf zu synchronisieren sein.
Als Einzel-Tasks mit Einzel-Routinen gibt es bereits eine Lösung, ich möchte nur nicht alle Methoden für 8 oder 10 Motoren 8 oder 10x bandwurmartig immer einzeln neu schreiben.
Du willst ein Objekt der Klasse A erstellen, welches beim Aufruf von Methode B einen Task mit PID-Regler startet?
mfg
hm, vlt doch nicht ganz so.... ich glaube soooo könnte man es vlt ausdrücken....:
ich habe mir vorgestellt, ein Objekt PID zu entwerfen mit verschiedenen Variablen (für kP, kI, kD u.a.m.) und einer Methode
PID.startPIDtask();
dieser soll dann einen spezifischen pthread task starten
pthread_t threadID;
pthread_create(&threadID, NULL, threadName, NULL);
jede Instanz
new PID PID_A;
new PID PID_B;
new PID PID_C;
...
soll dann in der Lage sein, ihren eigenen task zu starten und nach dessen Beendigung wieder zu "joinen",
pthread_join(threadID, NULL);
um ihn bei Bedarf jederzeit erneut starten zu können.
Um das Konzepz etwas konkreter zu zeigen, hier wäre ein Setup ohne diese Objekte, wo für jeden Motor eine eigene globale statische Task-Methode programmiert werden musste (mit "NXC" für lego NXT, mangels OOP und Funktionspointer nicht anders möglich):
http://www.mindstormsforum.de/viewto...p=61935#p61930
ich habe 3 feste (Motor-spezifische) task Routinen plus eine (Motor-spezifische) PID-Struktur, für jeden Motor eine eigene,die über reichlich komplizierte und umständliche Hilfs-Funktions-Aufrufe immerhin selektiv asynchron gestartet und beendet werden können (start task / stop task).
So kompliziert und "ungelenk" wollte ich es jetzt für 8 -10 Motoren nicht wieder machen müssen, daher die Idee mit dem PID-Objekt statt der PID-Struktur.
Ich hoffe, das könnte das Problem vlt ein wenig erhellen..... :-/
Ich habs auch noch nicht ganz verstanden.
Hier nur mal ein Beispiel in normalem C++ (11 oder 14), nur erstmal um zu verstehen, wie die Klasse aussehen soll
Da passiert nicht viel, es gibt nur sechs mal "Controlling ..." aus.Code:#include <cstdio> #include <thread> class PidClass { public: void Control(unsigned int n) { for (unsigned int i = 0; i < n; i++) { printf("Controlling ...\n"); } } std::thread MakeTask() { return std::thread([this] { Control(3); }); } }; int main() { PidClass pid; auto t1 = pid.MakeTask(); // ... t1.join(); auto t2 = pid.MakeTask(); // ... t2.join(); return 0; }
Ist das so gemeint ? Die Klasse hat eine Methode die was tut (mit Parametern) und eine zweite, die einen Thread erzeugt der die erste irgendwie verwendet ?
---
Achso vergessen.
Ja, kann man auch zu Fuss machen, wird aber unschön
http://stackoverflow.com/questions/1...n-from-a-class
Geändert von Mxt (19.09.2016 um 16:56 Uhr) Grund: Link hinzugefügt
die Methode soll im Prinzip diesen Task als pthread-Task als "Muster" beinhalten - allerdings müssen später die Instanzen des Objekts für die pthread-Tasks natürlich eigene IDs und Namen bekommen, wenn sie von den Instanzen "erschaffen" und gestartet werden. C11-Tasks will ich nicht verwenden, sondern nur pthread Tasks:
aufgerufen werden soll das Objekt bzw. seine Instanz mit seinem Task von so einer FunktionCode:// "Pseudocode"-Vorlage aus NXC: task task_PID_A() { float aspeed, damp, PWMpwr, readold, errorold, tprop; long readstart, cmax, cmin; // for monitoring long starttime, runtime, clock, dtime; // timer char regloop; PID_A.runstate = 0x10; // reg state: RAMPUP PID_A.read = (MotorRotationCount(OUT_A)); // get current encoder reading PID_A.err = PID_A.target - PID_A.read; // error to target readstart = PID_A.read; regloop = 1; damp=0; // damp the integral memory starttime= CurrentTick(); // appoach target _Astart: PID_A.runstate = 0x10; // run state: RUNNING do { dtime = CurrentTick() - clock; clock = CurrentTick(); runtime = clock - starttime; tprop = dtime/20.0; if ((PID_A.err==errorold)&& (abs(PID_A.err)>PID_A.precis)) damp=1; // stalling else damp=PID_A.damp; PID_A.integr = (damp * PID_A.integr) + PID_A.err; if((PID_A.integr) > 3*PID_A.maxout) PID_A.integr = 3*PID_A.maxout; // cut away else if((PID_A.integr) <-3*PID_A.maxout) PID_A.integr =-3*PID_A.maxout; PWMpwr= (PID_A.P*PID_A.err) + (PID_A.I*PID_A.integr)*tprop + (PID_A.D*(PID_A.err-errorold))/tprop; if(PWMpwr > PID_A.maxout) PWMpwr= PID_A.maxout; // forward maxout else if(PWMpwr < -PID_A.maxout) PWMpwr= -PID_A.maxout; // reverse maxout PID_A.speed= (PID_A.read-readold)*100/dtime; // rotat speed [degrees/100ms] aspeed = abs(PID_A.speed) ; if (aspeed > PID_A.tarpwm) { PWMpwr = sign(PWMpwr)*PID_A.tarpwm; } PID_A.outp = round(PWMpwr); //************************************************************************** // PID regulation ! OnFwd(OUT_A, (PID_A.outp)); // action ! Wait(PID_A.regtime); // wait regulation time //************************************************************************** readold = PID_A.read; // save old sensor errorold = PID_A.err; // save old error PID_A.read = (MotorRotationCount(OUT_A)); // get new encoder value PID_A.err = PID_A.target-PID_A.read; // new error to target if (PID_A.read>cmax) cmax=PID_A.read; // monitor overshooting else if (PID_A.read<cmin) cmin=PID_A.read; // monitor overshooting if ((PID_A.cont)&& (abs(PID_A.err)<=PID_A.precis)) PID_A.runstate = 0x60; else PID_A.runstate = 0x20; if (PID_A.cont) continue; if (abs(PID_A.err)<=PID_A.precis) { regloop +=1 ; PID_A.runstate = 0x40; } } while ((abs(PID_A.err)>=PID_A.precis) && (regloop<=5)); // target reached Off(OUT_A); // finished - brake motor PID_A.runstate = 0x40; // run state: RAMPDOWN PID_A.outp=0; Wait(50); PID_A.read = MotorRotationCount(OUT_A); regloop=1; if (PID_A.read>cmax) cmax=PID_A.read; // detect overshooting if (PID_A.read<cmin) cmin=PID_A.read; // detect overshooting PID_A.err = PID_A.target-PID_A.read; if ((abs(PID_A.err)>PID_A.precis)) {goto _Astart;} PID_A.runstate=0; Wait(1); //runstate = IDLE }
(port == "Motornummer" oder auch A, B, C, D, E,.... ):
Code:void RotatePIDtoTarget (char port, long Target, float RotatSpeed); // approach absolute target once void RotatePIDdegrees (char port, long Target, float RotatSpeed); // turn relative degrees void RotatePIDcontinue (char port, long Target, float RotatSpeed); // approach target continuously void StopPIDcontrol (char port); // stop PIDummer):
Das Objekt soll wie diese Struktur aussehen plus alle notwendigen Hilfs-Funktionen und pthread-tasks enthalten, damit sie von allen Instanzen benutzt werden können (kann eigentlich alles public sein):
- - - Aktualisiert - - -Code:struct PIDstruct { // custom target values long target; // set target int tarpwm; // motor target speed // custom regulation parameters float P; // P: proportional to error float I; // I: integral: avoid perish float D; // D: derivative: avoid oscillating float precis; // error precision to target int regtime; // PID loop time float damp; // damp the integral memory char cont; // target: continue or hit once // internal control variables char runstate; // monitors runstate int outp; // PID control output value int maxout; // max output (max motor pwr) long read; // current sensor reading float err; // current error float integr; // integral of errors float speed; // current speed } ;
edit:
ich muss auch sagen, die Erklärungen in http://stackoverflow.com/questions/1...n-from-a-class sind mir erheblich zu schwierig - ich habe bisher nie selber Klassen erstellt - mein Code ist fast immer nur ANSI C. Wahrscheinlich muss ich das aufgeben, wenn es wirklich so kompliziert sein muss.
Gibt es einen Grund warum es pthread direkt sein muss und das C++ 11 frontend nicht? (Btw. du redest über Tasks meinst aber Threads. Das ist ein ziemlicher Unterschied)
Mit C++ 11 ist es super einfach einen Thread in einer klasse laufen zulassen (Siehe Post von Mxt - Wobei der Code meiner Ansicht nach so nicht funktionieren wird
Ich finde das Beispiel auf Stackoverflow ganz gut: https://stackoverflow.com/questions/...ethod-of-class
Wenn du unbedingt direkt auf die pthread API programmieren willst wirst du um eine kompliziertere Lösung wie hier ( https://stackoverflow.com/questions/...start-function ) nicht drumherumkommen.
der Grund für pthread ist, dass ich mich mittlerweile ENDLICH einigermaßen damit zurechtfinde, dass ich die thread priority sinnvoll justieren kann, dass auch wiringPi mit pthread arbeitet (auch mit vereinfachten Mutexen) und ich auch sonst immer nur pthread einsetze. C++ ist mir eigentlich ein Greuel, so wie auch andere OOP Sprachen (Java, C#) und ich nutze es nur, wenn zufällig ein paar libs ihre API Funktionen eben so zur Verfügung stellen (Arduino automatisch, beim Raspi v.a. wenn zwingend iostream gebraucht wird). Auch Gordon Henderson mit seinem wiringPi teilt übrigens meine Abneigung gegen C++, auch er nutzt (wie er mir mal schrieb) "aus Prinzip" nur ANSI C.
Ich habe aber keinen Schimmer, was (wie bei stackoverflow) in diesem Zusammenhang vektoren, this, eine static class method, eine plain ordinary function, to bootstrap the class, virtual, protected, public, private, static void * InternalThreadEntryFunc(void * This) {((MyThreadClass *)This)->InternalThreadEntry(); return NULL;}, boost::thread, ein pointer to "context", ein pointer to an instance, C++ construct - 'friend' function und all dieses Gedöns ist und den Unterschied zwischen :: und -> werde ich mit Sicherheit auch nie kapieren.
Ich gebe ntl zu, dass OOP hier und da Vorteile hat (wie bei Arduino Serial und TWI) und hatte vermutet, dass ich dieses schnell mal so auch ganz einfach bei meinem PID Controller nutzen kann, wenn ich nur ein PID Muster erstelle und dieses dann per new PID... samt seiner dann miterschaffenen Tasks einfach "vervielfachen" könnte. Leider ist mir der ganze OOP Rattenschwanz, der offenbar zwangsweise mitgeschleppt werden muss und der mir regelrechte Hirnkonvulsionen verursacht mit progressivem Gefühl mich unstillbar übergeben zu müssen, über alle Maßen bei weitem zu wirr, zu umständlich und zu aufwändig.
Ich wollte in mein Objekt nur irgendwie ganz einfach reinschreiben
pthread_t threadID;
pthread_create(&threadID, NULL, threadName, NULL);
und hatte gehofft, alle Instanzen würden dann auch ganz einfach so ihren eigenen pthread task starten, laufen lassen und beenden können, ohne dass es Probleme bei 8 oder 10 gleichzeitig oder nacheinander aufgerufenen Instanzen gegeben hätte.
Da ist es 1000x einfacher, wenn ich meine statischen Task Funktionen 10x untereinander per copy + paste schreibe als
void * PID_A
void * PID_B
void * PID_C
usw.
wie ich es in NXC gemacht habe - und bleibe beim guten alten ANSI C .
Also std::thread ist nur eine dünne Verpackung um entweder pthread oder Windows threads. Man komm auch noch an den verpackten "nativen" Thread heran. Muss man auch, z.B. zum Setzen von Prioritäten.
Da sind die Regeln ganz einfach: In C-Funktionen kann man nur statische Memberfunktionen von Objekten als Parameter übergeben. Objektinstanzen werden als void* übergeben.
ich weiß gar nicht was statische Funktionen oder statische Memberfunktionen von Objekten sind und was nicht.... und was das für mein PID Problem jetzt eigentlich bedeutet....
http://en.cppreference.com/w/cpp/language/static
- - - Aktualisiert - - -
Auf einem Pi habe ich ihn nicht getestet, aber in Visual Studio läuft er.
Lesezeichen