PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Probleme mit PID-Regelung (Drehzahl)



MechMac
25.08.2017, 19:25
Hallo,

ich benötige noch mal Hilfe.
Ich versuche gerade einen bürstenlosen Außenläufer mit Steller mittels einem Arduino zu regeln.
Ziel ist es zunächst, das eine Drehzahl gehalten wird.

Die Hardware ist ein Robbe Roxxy 2824-34. Steller ist ein BL-control 918.

Die Drehzahl wird mit einem Hallsensor gemessen.
Zum Regeln verwende ich die Lib "PID_v1".

Die Erfassung der Drehzahl erfolgt entweder alle 2 Sekunden oder alle 2 Ticks vom Hallsensor.

Die Neukalkulierung und setzen eines neuen Vorgabewertes erfolgt etwa zwei mal pro Sekunde.

Ich komme mit der Einstellerei nicht weiter.
Die Werte für I und D habe ich erst mal auf 0 gelassen.

Anbei vier Messwerte:

Werte von P,I und D
Solldrehzal->von der Regelung erreichte Drehzahl

Kp=0.01, Ki=0.00, Kd=0.00;
2200->2100
6000->3280
8000->4090


Kp=0.02, Ki=0.00, Kd=0.00;
2200->2100
6000->3840
8000->4950


Mit schwingen im anlauf, danach ruhig
Kp=0.03, Ki=0.00, Kd=0.00;
2200->2110
6000->4340
8000->5590


Mit ständigen schwingen
Kp=0.04, Ki=0.00, Kd=0.00;
2200->2125
6000->4600 (dann schwingen)
8000-> Werte zwischen 3600-7800 (erhebliches schwingen)


Warum bekomme ich es nicht hin das wenigstens die soll-Drehzahl erreicht wird?


Gruß, Andreas

Peter(TOO)
26.08.2017, 03:45
Hallo Andreas,

Warum bekomme ich es nicht hin das wenigstens die soll-Drehzahl erreicht wird?
Das ist das Grundlegende Problem des P-Reglers!
Die Stellgrösse des einfachen P-Reglers ist:
= Kp * (soll - Ist)
Deshalb hängt der P-Regler immer hinter dem Sollwert hinterher. Wird (Soll - Ist) gleich 0, dann wird auch die Stellgrösse 0.
Eine verbesserte Variante benutzt:
= (Kp * (Soll - Ist)) + (Kp2 * Soll)

Das Schwingen hängt von den Zeitkonstanten der Regelstrecke ab. Unbekannt ist noch wie sich die BL-control 918 zeitlich verhält.
Zudem spielt die Abtastrate des Istwerts auch noch mit rein.

Jetzt hast du das Problem, dass aus (Soll - Ist) die neue Stellgrösse berechnet wird. Bis zur nächsten Messung ist dann der Motor viel zu schnell geworden, also wird die Stellgrösse wieder verkleinert.
Möglicherweise musst du noch die Abtastrate erhöhen, der Motor reagiert recht schnell.

Praktisch muss du jetzt den D-Anteil erhöhen, bis die Geschichte nicht mehr schwingt. Dann kannst du den P-Anteil weiter erhöhen.
Dieses Spiel machst du, bis der Istwert eine minimale Abweichung hat und alles Stabil bleibt.

Dann kannst du mit dem I-Anteil die restliche Abweichung noch verkleinern.


MfG Peter(TOO)

HaWe
26.08.2017, 09:27
ja, das Tunen und Fine-Tunen ist ein echter PITA.
Ich hatte seinerzeit refresh-Intervalle von ca. 10ms für meine PID_v1-Regler. PID_v1 hat übrigens auch ein autotuning-Modul, das habe ich aber zugegebenermaßen selber nie verstanden.
Der Weg, den Peter-too aufgezeigt hat, ist aber schon richtig:
erst I und D auf Null und dann P optimal tunen,
dann I schrittweise erhöhen, bis das System anfängt sich einzuschwingen,
dann D schrittweise erhöhen, ohne dass es anfängt zu zittern oder zu schütteln, ggf. nochmal I und/oder P nachjustieren.

Es gibt fürs PID tuning mehrere Schemata im Netz (öfters erwähnt: Ziegler-Nichols), aber keines ist so richtig optimal für alle erdenklichen Anwendungszwecke.

Peter(TOO)
26.08.2017, 10:07
Hallo,

Es gibt fürs PID tuning mehrere Schemata im Netz (öfters erwähnt: Ziegler-Nichols), aber keines ist so richtig optimal für alle erdenklichen Anwendungszwecke.
Man kann die Sprungantwort der Regelstrecke messen und dann eine Menge rechnen.

Aber auch dann muss man von Hand nachtrimmen, da alle Konstanten variabel sind. :-)
Ich habe es in 40 Berufsjahren nie erlebt, dass berechnete Parameter wirklich gepasst haben, egal wer nach welcher Theorie gerechnet hat.

rov.cc
26.08.2017, 11:35
Die Werte für I und D habe ich erst mal auf 0 gelassen.

Das wird dann aber schwingen. Der D-Anteil sollte nun erhöht werden bis es nicht mehr schwingt. Der I-Anteil ist für die Minimierung der Regelabweichung notwendig.
Ohne I-Anteil wird die Solldrehzahl niemals erreicht & Ohne D-Anteil wirds Schwingen.

MechMac
26.08.2017, 11:47
Vielen Dank für die Infos!
Ich werde heute Abend dazu kommen das mal auszuprobieren.

Den Arduino zum Regeln, also der mit dem Hallsensor werde ich im folgetext ArduinoRH nennen.

Das Problem ist, das der Motor an einem Servocontroller hängt. Dieser ist via UART an dem Hauptcontroller angeschlossen. Und der ArduinoRH dann via I2C an dem Hauptcontroller.
Also ein laaaanger Weg.
Ich freunde mich schonmal mit dem Gedanken an, den Motor direkt an den ArduinoRH anschließen. Aber ich befürchte, das es trotzdem Timing probleme gibt.

Es macht doch keinen Sinn den PID-Algo alle 10ms aufzurufen, wenn es deutlich länger dauert eine Drehzahl zu ermitteln, oder?
Die I2C Versorgung auf dem ArduinoRH benötige ich in jedem Fall.
Nur wenn ich sende und empfange, kann ich in der Zeit keine Drehzahl messen.
Aufschieben kann ich die Antwort auch nicht, da der Request vom Hauptcontroller sofort beantwortet werden muss.

Peter(TOO)
26.08.2017, 12:48
Hallo MechMac,

Es macht doch keinen Sinn den PID-Algo alle 10ms aufzurufen, wenn es deutlich länger dauert eine Drehzahl zu ermitteln, oder?


Richtig erkannt!
Die kürzeste Zeit muss gleich der Abtastrate der Drehzahl sein. Alles was schneller ist bringt nichts, bzw. bringt den I-Anteil ins schleudern.

Ein wichtiger Faktor sind dann aber auch die Zeit-Schwankungen bei der Abtastung, diese sollten möglichst klein sein.

Sagen wir mal du hast alle 2 Sekunden eine neue Drehzahl und rufst aber alle 100 ms den Regler auf.
Dann hat sich bei 19 Aufrufen am Ist-Wert nichts geändert. Der Regler stellt aber bei jedem Aufruf fest, dass sich nichts an der Differenz (Soll - Ist) geändert hat und erhöht jedes mal den Stellwert.....
Das kann nichts werden!

Oft macht es aber auch noch Sinn zuerst mehrere Abtastwerte zu erfassen um dann den Mittelwert zu bilden. Damit kann man einen Teil der Störungen verringern.

Du solltest das Ganze so gestalten, dass du höchstens nach jeder Drehzahlmessung den Regler einmal aufrufst.
Ein Problem dabei ist, dass die Abtastrate direkt als Zeit-Parameter in die Formel eingeht.
Du hast dann eine Variable Konstante und das kann nicht stabil sein. :-(

Die Grundsätzliche Frage ist noch, wie schnell die Regelung sein muss?
Also wie schnell muss eine bestimmte Drehzahl erreicht werden und wie schnell müssen Lastwechsel ausgeglichen werden?
Hier stellt sich auch noch die Frage, wie gross und schnell die Lastwechsel überhaupt sind?
Über die Anwendung wissen wir bis jetzt noch gar nichts, also was da am Motor hängt?

Ich hatte viel mit Motor- und Heizreglern zu tun. Wenn du 4 m3 Wasser hast, macht es keinen Sinn da jede Sekunde zu messen, das System ist viel zu träge.

Bei Motoren spielen die Schwungmasse und die Belastung eine grosse Rolle. Die Schwungmasse verändert vor allem den D-Anteil und die Belastung ändert die Zeitkonstante bei der Sprungantwort.
Du hast dann eine weitere variable Konstante, weshalb die Berechnungen für einen PID-Regler eben nie stimmen....

MfG Peter(TOO)

HaWe
26.08.2017, 13:34
Hallo MechMac,


Richtig erkannt!
Die kürzeste Zeit muss gleich der Abtastrate der Drehzahl sein. Alles was schneller ist bringt nichts, bzw. bringt den I-Anteil ins schleudern.

Ein wichtiger Faktor sind dann aber auch die Zeit-Schwankungen bei der Abtastung, diese sollten möglichst klein sein.

Sagen wir mal du hast alle 2 Sekunden eine neue Drehzahl und rufst aber alle 100 ms den Regler auf.
Dann hat sich bei 19 Aufrufen am Ist-Wert nichts geändert. Der Regler stellt aber bei jedem Aufruf fest, dass sich nichts an der Differenz (Soll - Ist) geändert hat und erhöht jedes mal den Stellwert.....
Das kann nichts werden!

MfG Peter(TOO)

Ich habe tatsächlich die Frequenz von nur 2x/s überlesen, ich selber habe seinerzeit alle 200µs die Encoder ausgelesen, und eine deutlich langsamere Abtastung als 10 oder 100ms reicht dann bei dir natürlich auch aus.

MechMac
28.08.2017, 20:59
So, die tests werde ich in Kürze an dem "echten" Motor durchführen.
An dem Testmotor, den ich hier auch beschrieben hatte, bin ich zu folgendem Ergebnis gekommen:

Wenn ich I etwas erhöhe wird es besser. Aber D darf ich nicht größer als 0 machen. Dann dreht das Teil völlig am Rad und fängt wie wild an zu schwingen.
Ich hatte folgende Werte
P=0.03 I=0.04 D=0.00

Kann D eigentlich auch negativ sein?


Naja, der "echte" Motor ist viel größer und dreht deutlich langsamer. Da kann ich dann auch mal den Test unter Last machen.

Also irgendwie kommen mir die Werte so "klein" vor.

Aber nochwas:
Output, also das was von der PID-Regelung kommt, ist ein double. Ich übergebe dem Steller aber nur integer. Im Grunde fehlen mir die beiden nachkommastellen, die ja auch "feine" Korrekturen beinhalten.
Als nicht-Mathematik-Mensch fällt mir da nur ein: Den Wert nicht begrenzen von 1500.00-1800.00, sondern von 15.00-18.00 und dann das Ergebnis*100.
Macht das Sinn?
Muss ich mal drüber nächtigen....

Peter(TOO)
28.08.2017, 22:47
Naja, der "echte" Motor ist viel größer und dreht deutlich langsamer. Da kann ich dann auch mal den Test unter Last machen.

Also irgendwie kommen mir die Werte so "klein" vor.
Meine Frage ist immer noch die Abtastrate des Istwertes und die Aufrufrate des Reglers?

Wenn du den Regler öfters aufrufst, als gemessen wird, bekommt er zwischen den Messungen immer den selben Wert gefüttert.

Der berechnete P-Anteil bleibt also konstant, weil (Ist-Soll) auch konstant bleibt.

Der D-Anteil rechnet mit (IstALT - IstNEU) und kämpft gegen das Schwingen und zu schnelle Änderungen des Stellwerts. Wird er mit einem neuen Its-Wert aufgerufen rechnet er eine Gegenmassnahme. Bei den weiteren Aufrufen, wird sein Anteil dann 0.

Der I-Anteil integriert den Wert (Soll - Ist) Da diese Differenz zwischen den Abtastungen konstant bleibt, erhöht der P-teil bei jedem Aufruf die Stellgrösse, ohne zu bemerken, was die letzte Änderung des Stellwerts ausgerichtet hat.

Wenn du den Ist-Wert alle 2s abtastetest und den Regler alle 100ms aufrufst, hats du für 100ms einen vernünftigen Stellwert. Die nächsten 19 sind dann Schüsse in Blaue.
Das Doofe ist aber, dass der Motor sofort auf eine Änderung des Stellwertes reagiert .....

Die Abtastrate des Ist-Wertes muss also mindestens gleich gross, oder grösser, als die Aufrufzeit des Reglers sein.

Ein Problem ist noch die Konstanz der Abtastrate des Istwerts.
Etwas vereinfacht berechnet der Regler welche Drehzahlveränderung eine Stellwert-Veränderung um z.B. 10% in der Zeit tA bewirkt.
ist nun aber die Abtastzeit einmal 2s und einmal 1s ändert sich dieser Wert um den Faktor 2!
Alleine dies führt schon zum Schwingen.

Man muss also die Abtastzeit möglichst Konstanz halten oder die reale Zeit zwischen den Abtastungen mit erfassen und im Regler mit verarbeiten. Dadurch wird allerdings die Rechnerei im Regler um einiges aufwändiger. :-(




Aber nochwas:
Output, also das was von der PID-Regelung kommt, ist ein double. Ich übergebe dem Steller aber nur integer. Im Grunde fehlen mir die beiden nachkommastellen, die ja auch "feine" Korrekturen beinhalten.
Als nicht-Mathematik-Mensch fällt mir da nur ein: Den Wert nicht begrenzen von 1500.00-1800.00, sondern von 15.00-18.00 und dann das Ergebnis*100.
Du solltest natürlich schon die maximale Auflösung aus den Stellglied heraus holen!

Wenn dein Stellglied eine Auflösung von 1'000 hat und du skalierst das Ganze auf den Bereich von 0..10 (Integer), kann sich die Stellgrösse nur in 100er-Schritten ändern.
Skalierst du auf 0...1'000 kann sich der Ausgang in 1er-Schritten verstellen.

MfG Peter(TOO)

HaWe
29.08.2017, 09:49
Der P-Anteil integriert den Wert (Soll - Ist) Da diese Differenz zwischen den Abtastungen konstant bleibt, erhöht der P-teil bei jedem Aufruf die Stellgrösse, ohne zu bemerken, was die letzte Änderung des Stellwerts ausgerichtet hat.

hier meinst du sicher den I-Anteil (I=Integral=Summe aller bisherigen Fehler),

I vergisst zum Einen nichts (alte Werte werden als Teil der Fehler-Summe ewig mitgeschleppt, wenn sie nicht intern im Algorithmus gedämpft werden), und daher ist I auch sehr träge und reagiert folglich (als Teil aller PID-Parameter im Gesamtzusammenhang) vor allem bei relativem Stillstand der Regelung ("Verrecken kurz vor dem Ziel").
Da die Abtastfrequenz des OP sehr gering ist, wird daher I dermaßen langsam reagieren, dass sich erst nach -zig Sekunden oder gar Minuten der Stellwert ändert, und daher kann man eigentlich hier I von vornherein auf Null lassen: damit wird das Ganze zum PD-Regler, der auch sicher für diese Zwecke ausreicht.

Peter(TOO)
29.08.2017, 12:12
hier meinst du sicher den I-Anteil (I=Integral=Summe aller bisherigen Fehler),
Sorry, war ein Typo. Habe es korrigiert, also nicht wundern, weil das Zitat nicht mehr stimmt.

- - - Aktualisiert - - -

ich habe jetzt die Bibliothek nochmals genauer unter die Lupe genommen.

Der Regler rechnet stur alle, Default 200ms, alles durch. Im obigen Fall merkt er aber nicht, dass sich der Ist-Wert nur alle 2s ändert.
Der Regler rechnet also nach 200ms nochmals mit dem alten Ist-Wert und kommt zum Schluss, dass die letzte Änderung des Stellwertes nichts gebracht hat .....

MfG Peter(TOO)

Urinstein
29.08.2017, 14:39
Welche Einheiten haben denn deine Kp/Ki/Kd-Faktoren?
Deine Reglereingangsgröße kommt als Drehzahl, also in 1/min, 1/s etc. pp.
Was ist deine Stellgröße? Eine Spannung? Strom? PWM-Tastverhältnis?

Mach dir darüber erstmal Gedanken, dann kann man die Größenordnung der Reglerparameter für gewöhnlich schonmal durch reines Nachdenken abschätzen.
Aus deinem Startpost entnehme ich mal dass du willkürliche Werte für Kp gewählt und dann vervielfacht hast. Bist du bei den anderen Parametern genauso vorgegangen?



Also irgendwie kommen mir die Werte so "klein" vor.

Aber D darf ich nicht größer als 0 machen. Dann dreht das Teil völlig am Rad und fängt wie wild an zu schwingen.

Siehe oben. Ohne Information über die Größenordnung und Einheit der Ein- und Ausgangsgrößen wertlose Informationen.
Vielleicht liegen die idealen Reglerparameter für deinen Anwendungsfall ja bei Kp=0.00001, Ki=0.004, Kd=0.000000003?

oberallgeier
29.08.2017, 18:41
Hallo Andreas.
.. Ich versuche gerade einen bürstenlosen Außenläufer .. zu regeln. Ziel ist es zunächst, das eine Drehzahl gehalten wird ..Als ich vor dieser Aufgabe stand, hatte ich mal im RNWissen nachgesehen - da gibts eine sehr gute Abhandlung über Regelungstechnik (http://rn-wissen.de/wiki/index.php?title=Regelungstechnik). Das muss man nicht (aber kann es) durcharbeiten. Besonders für Dich wichtig ist der Abschnitt über digitale Regler (http://rn-wissen.de/wiki/index.php?title=Regelungstechnik#Digitaler_Regler) . Dort gibts dann im nachfolgenden Kapitel auch Ratschläge zur experimentellen, geschätzten oder regelrechten Bestimmung der Regelfaktoren.

Meine erste Notiz, damit auch der Beginn meiner Reglersoftwarekonstruktion, dazu war ein Aufzeichnen (m)eines Regelkreises

......https://dl.dropbox.com/s/ubfvgqsrk2wrfpx/regel-notiz.jpg?dl=0

mit den zugehörigen Werten (siehe auch hier (https://www.roboternetz.de/community/threads/64989-Drehzahlregelung-PID-mit-AVR?p=600032&viewfull=1#post600032)) - zu guten Teil stehen da meine Variablennamen.

Folge: das entsprechende Wägelchen (https://www.youtube.com/watch?v=jgm9DhS7vS4) fährt dann mit zwei UNABHÄNGIG von einander angesteuerten, getrennten Motoren bei Vorgabe gleicher WErte schnurgeradeaus *ggg*.

MechMac
29.08.2017, 19:35
Sorry, war ein Typo. Habe es korrigiert, also nicht wundern, weil das Zitat nicht mehr stimmt.

- - - Aktualisiert - - -

ich habe jetzt die Bibliothek nochmals genauer unter die Lupe genommen.

Der Regler rechnet stur alle, Default 200ms, alles durch. Im obigen Fall merkt er aber nicht, dass sich der Ist-Wert nur alle 2s ändert.
Der Regler rechnet also nach 200ms nochmals mit dem alten Ist-Wert und kommt zum Schluss, dass die letzte Änderung des Stellwertes nichts gebracht hat .....

MfG Peter(TOO)

Das verstehe ich nicht. Eigentlich rechnet er mit myPID.Compute();

Zu den Stellgrößen:
Da ich den Motor mittlerweile direkt an den Arduino-Regler angeschlossen habe und für die PWM die Lib "Servo" verwende, muss ich nun leider Wete von 0-180 angeben.
Wobei 90 die Mitte, also 1500µs darstellt.

ich lasse den PID-Regler für mich verständliche Werte in µs ausgeben:
myPID.SetOutputLimits(1538, 1800);
Die 1538µs sind kurz vor der langsamsten Drehzahl.

Dann mappe ich den ermittelten Wert auf das, was die Servo LIB benötigt:
myservo.write(map(servoval, 1000, 2000, 0, 180));

Welche Einheit oder Größe P, I, und D haben weiß ich nicht. Es sind für mich einheitslose Faktoren.

Mag ich gar nicht, da ich durch diese bekloppte 0-180 Grenze Genauigkeit verliere.
Ich könnte das auch selber machen ohne die Servo LIB, aber dann muss ich mit millis(); arbeiten.
Aber in der Vergangenheit hatte ich immer den Eindruck, das Millis(); und Micros(); abweichungen von mehreren 1/10 sek hat.
Wobei man bei den fertigen libs ja nie so genau weiß, ob da nicht irgend wo nen delay drin ist....Was das dann verursacht.
Ich weiß auch nicht wie das bei UART und I2C in Verbindung mit Millis(); und Micros(); aussieht...Aber ich schweife ab.

Ich hatte zunächst die Drehzahl entweder alle 200000 durchläufe oder alkle 2 Impulse berechnet.
Das hatte den Vorteil, das ich sowohl langsame, als auch schnelle Drehzahlen gut berechnen konnte.
Den PID-Regler habe ich unmittelbar nach der Messung gefüttert.
Das hatte zur folge, das, je schneller der Motor drehte, der PID auch öffter rechnete.

Da ihr aber meintet, zeitliche abtastung wäre besser, habe ich jetzt alle 1000 Zyklen festgelegt.
Damit kann ich drehzahlen unter circa 600 u/min nicht mehr erfassen. Aber ist nicht so schlimm.
Das hat jetzt auch den Nachteil, das ich mir die aktuelle Drehzahl nicht mehr via UART ausgeben kann, da UART nicht mehr funktioniert.
Später wird das mögl. weise noch ein Problem. Ob I2C noch funktioniert...keine Ahnung.

Wenn ich das aber alles ignoriere und so tue als ob die Kommunikation nur in eine Richtung laufen müsste hat es jetzt folgendes ergeben:
D darf ich immer noch nicht >0 setzen. Nach wie vor dreht das Teil komplett am Rad, wenn ich es nur wage.
Ohne I schwingt er etwas, mit I kaum.
In nur 1-2 Sekunden hat er die neue Drehzahl erreicht (welche das ist, k.a. da UART ja nicht mehr funktioniert)
Also im prinzip ist es jetzt wie vorher, nur das ich statt P=0.03, I=0.04 jetzt P=0.02, I=0.03 habe.

Was mir letztendlich komisch vorkommt, wieso darf ich bloß D nicht verwenden....
Ich habe gelesen das ein double auf dem Arduino auch nicht mehr wie 2 Nachkommastellen hat, oder?
Sonst hätte man es ja mit 0.001 versuchen können... Vielleicht ist 0.01 schon der Vorschlaghammer.



------Ergänzung-------
Der Motorsteller ist ein gewöhnlicher Steller aus dem Modellbaubereich.
Er erwartet ein PWM-Signal von etwa 1000-2000 µs
Wobei 1500µs +- ~30 den Totbereich um den Stillstand des Motors darstellen.
>1500 gilt als Vorwärtslauf, <1500 gilt als Rückwärtslauf

HaWe
29.08.2017, 21:53
double ist bei Arduino AVRs das selbe wie float, und float hat ca. 6-7 Dezimalstellen Genauigkeit, gemessen ab der 1. signifikanten Stelle ab der letzten führenden Null.
Sie werden bei Arduino Serial nur bei der Ausgabe nie alle angegeben, sondern immer nur 2 Nachkommastellen, es sei denn, du forderst diese Genauigkeit ausdrücklich.
Intern wird aber immer mit allen Stellen gerechnet.


ps,
du kannst die Sample Time definieren mit (wer hätte's gedacht) SetSampleTime(int milliseconds)
https://playground.arduino.cc/Code/PIDLibrarySetSampleTime

sie muss also nicht so wie per default bleiben.

Urinstein
30.08.2017, 18:44
Das verstehe ich nicht. Eigentlich rechnet er mit myPID.Compute();
Nein!
Deine PID Library rechnet, sofern möglich, mit der eingestellten Taktung. Default sind glaube ich alle 100ms, also 10Hz.
Bei jedem Ausführen der PID.Compute() fragt die Library ab, ob die Berechnung erfolgen soll oder nicht, also ob seit der letzten Berechnung eine Sampletime abgelaufen ist. Daher solltest du PID.Compute unabhängig von der eingestellten Sampletime so oft wie möglich ausführen.


Da ich den Motor mittlerweile direkt an den Arduino-Regler angeschlossen habe und für die PWM die Lib "Servo" verwende, muss ich nun leider Wete von 0-180 angeben.
Die Library hat auch einen Befehl um die Einschaltzeit des PWM-Signals auszugeben statt eines Winkels. Nutze ich immer so.
Der Befehl lautet servo.writeMicroseconds(1500).