mikrocontroller.net

Forum: FPGA, VHDL & Co. PID-Regler so realisierbar?


Autor: Tobias W. (eagle2010)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

für ein Projekt im Rahmen des Studiums benötige ich u.a. einen 
PID-Regler, der von ein FPGA realisiert werden soll.
Da ich abgesehen von einigen Grundlagen in der Vorlesung mir VHDL 
größtenteils selbst beigebracht habe, möchte ich die erfahrenen Benutzer 
in diesem Forum mal zu ihrer Meinung fragen, ob der Code so 
funktionieren kann.
Die Simulation in Modelsim mit einer PT1-Strecke funktioniert soweit und 
der Code läßt sich auch synthetisieren ohne allzu viel Logik zu belegen. 
Da ich jedoch z.Zt. noch keine Möglichkeit habe, den Regler in einem 
realen FPGA zu testen und ich gehört habe, das eine funktionierende 
Simulation nicht zwangsläufig auch bedeutet, dass der Code im FPGA 
funktioniert, wollte ich mal eure Meinung hören. Realisiert werden soll 
das Ganze mit einem Spartan3E250.
Der Code basiert grundsätzlich auf einem PID-Regler in C, den ich für 
das FPGA entsprechend angepasst habe.
Gerechnet wird nur mit ganzen Zahlen, wobei anschließend durch 
verschiedene 2er-Potenzen dividiert wird. Dies wird mit den definierten 
Konstanten erledigt. Es wird ausschließlich mit Funktionen aus 
numeric_std gerechnet.
Für den jeweiligen P, I und D-Anteil gibt es Ober- und Untergrenzen um 
einen Überlauf zu vermeiden.
Insbesondere würde mich noch interessieren, ob die Anweisungen INNERHALB 
des Prozesses mit Sicherheit nacheinander abgearbeitet werden unabhängig 
von Gatterlaufzeiten o.ä.?

Hier also der Code:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
--use IEEE.STD_LOGIC_ARITH.ALL;
--use IEEE.STD_LOGIC_UNSIGNED.ALL;

use IEEE.NUMERIC_STD.ALL;

---- Uncomment the following library declaration if instantiating
---- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity PID_Regler is
    port (
         w : in  unsigned (7 downto 0) := (others => '0');
           x : in  signed (8 downto 0) := (others => '0');
           y : out  signed (15 downto 0) := (others => '0');
        relais : in STD_LOGIC;
        reset : in STD_LOGIC;
        clk10hz : in STD_LOGIC
      );
end PID_Regler;

architecture Behavioral of PID_Regler is

-- Ta = 0.1 s
-- Alle Werte sollten zwischen 0 und 200 liegen!
  constant MKp : integer := 2**8;
  constant KpM : integer := 102; -- Kp * MKp   Kp = 0.4
  constant MTn : integer := 2**10;
  constant TaTnM : integer := 41; -- (Ta / Tn) * MTn    Tn = 2.5
  constant MTv : integer := 2**2;
  constant TvTaM : integer := 25; -- (Tv / Ta) * MTv   Tv = 0.625
  
  signal e : signed(8 downto 0) := (others => '0');

begin

  Regler: process(clk10hz)
    variable yp : signed(15 downto 0) := (others => '0');
    variable yi : signed(15 downto 0) := (others => '0');
    variable yd : signed(31 downto 0) := (others => '0');
    variable ealt : signed(8 downto 0) := (others => '0');
  begin
    if rising_edge(clk10hz) then
     if relais = '0' or reset = '1' then
      yp := (others => '0');
      yi := (others => '0');
      yd := (others => '0');
      ealt := (others => '0');
      y <= (others => '0');
     else
      e <= resize(to_integer(w) - x, 9); -- e = w - x
      yp := resize(KpM * e, 16); -- P-Anteil berechnen
      if yp > 20000 then yp := to_signed(20000, 16); -- obere Grenze des P-Anteils
      elsif yp < -20000 then yp := to_signed(-20000, 16); -- untere Grenze des P-Anteils
      end if;
      yi := resize(yi + (KpM * TaTnM * e) / MTn, 16); -- I-Anteil berechnen
      if yi > 25000 then yi := to_signed(25000, 16); -- obere Grenze des I-Anteils
      elsif yi < -25000 then yi := to_signed(-25000, 16); -- untere Grenze des I-Anteils
      end if;
      yd := resize((KpM * TvTaM * (e - ealt)) / MTv, 32); -- D-Anteil berechnen
      if yd > 1000000 then yd := to_signed(1000000, 32); -- obere Grenze des D-Anteils
      elsif yi < -1000000 then yd := to_signed(-1000000, 32); -- untere Grenze des D-Anteils
      end if;
      y <= resize((yp + yi + yd) / MKp, 16); -- y = yp + yi + yd
      ealt := e;
     end if;
   end if;
  end process Regler;

end Behavioral;

Gruß
Tobias

Autor: Thomas Hertwig (mac4ever)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die Anweisungen werden dann "nacheinander" abgearbeitet, wenn Du das 
Ergebnis in einer anderen Operation wieder benutzt. Das "Nacheinander" 
ist, wie Du schon meintest, von den Laufzeiten abhängig. Du musst also 
schauen, ob dein Timing noch stimmt. Wenn Du zu viele Operationen 
hintereinander ausführst ist es möglich, dass bis zum nächsten Takt das 
eigentliche Ergebnis nicht bereitgestellt wird.

Zur Simulation:
Wenn Du eine Post-Map Simulation ausführst und diese das richtige 
Ergebnis liefert, dann sollte es auch auf dem FPGA funktionieren. Bei 
Post-Map sind Signal- und Gatterlaufzeiten eingerechnet und Du kannst 
Dir die "reale" Verzögerung anschauen ...

Autor: Martin Kohler (mkohler)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wenn das ganze System synchron arbeitet und alle ankommenden und 
abgehenden Signale abgecklockt werden, dann genügt es, die PERIOD 
constraint richtig zu setzen. Das Synthesetool rechnet die Gatter-Delays 
dann korrekt ein und liefert eine Warnung, wenn diese die PERIOD 
übersteigen.

Autor: Klaus Falser (kfalser)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dein PID-Regler stimmt zwar im Prinzip, aber ein 10 Hz Takt ist fast 
eine Beleidigung für ein FPGA.
Zwar kann man einen 10 Hz Takt durch Herunterteilen eines schnelleren 
Taktes herstellen, solche Signale sollte man aber nicht als Takt 
verwenden.
Auch die DLL's des Spartan-FPGA's eignen sich vermutlich (müsste man 
nachschauen) nicht, um so einen langsamen Takt zu erzeugen.

Falls man es wirklich so ähnlich machen will, sollte man ein 
Clock-Enable Signal erzeugen, das alle 100 ms für einen Taktpuls auf '1' 
geht und das Aktualisieren des Reglers auslöst.
Der Kode muss dabei aber umgeschrieben werden, weil in diesem Fall die 
Berechnung innerhalb eines Taktzyklus erfolgen muss.
Die Lösung dafür heißt entweder Pipelining, oder kompliziertere 
Constraints, die sich "Multicycle-Path" nennen.
Leider ist dies alles komplizierter als die Lösung, die Du jetzt hast, 
aber solch ein PID-Regler mit 10 Hz am FGPA ergibt doch sonst keinen 
Sinn, oder?

Autor: Tobias W. (eagle2010)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Martin:
Auf welchen Wert sollte die PERIOD constraint dann gesetzt werden?
Mir wird eine maximal mögliche Taktrate von ca. 33 MHz angegeben, hat 
das was damit zu tun?

@Klaus:
Es ist klar, dass ein FPGA normalerweise mit wesentlich schnelleren 
Taktraten arbeitet, den 10 Hz-Takt teile ich tatsächlich von 66 MHz 
herunter, können denn dadurch Probleme entstehen?
Der PID-Regler soll eine Heizung regeln, also ein sehr träges System, so 
dass eigentlich keine höheren Taktraten erforderlich wären, aber es wäre 
natürlich auch möglich die Taktrate zu erhöhen.

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ Tobias W. (eagle2010)

>Auf welchen Wert sollte die PERIOD constraint dann gesetzt werden?

Im einfachsten Fall auf die Frequenz des Taktes. Passt schon, auch wenn 
es theoretisch mit einem Multicycle-Constraints besser wäre.

>Mir wird eine maximal mögliche Taktrate von ca. 33 MHz angegeben, hat
>das was damit zu tun?

Ja. reicht doch für dich.

>herunter, können denn dadurch Probleme entstehen?

Nicht wenn man es richtig (tm ) macht.

>Der PID-Regler soll eine Heizung regeln, also ein sehr träges System, so
>dass eigentlich keine höheren Taktraten erforderlich wären, aber es wäre
>natürlich auch möglich die Taktrate zu erhöhen.

Nobel geht die Welt zu Grunde. Heizungsreglung mit FPGA. ;-)

MfG
Falk

Autor: Amania (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
hi Tobias , deine Code läuft nicht in Xilinx oder ?

Autor: Tobias W. (eagle2010)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Amania wrote:
> hi Tobias , deine Code läuft nicht in Xilinx oder ?

Doch, er lässt sich in der ISE sowohl synthetisieren als auch 
simulieren. Warum nicht?

Autor: Amania (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ich habe eifach deine code in ISE kopieren und synthetisieren lassen , 
es gab aber viele Fehler . Wie ist es bei dir ?

Autor: Amania (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
bild dazu

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ Amania (Gast)

>Dateianhang: Unbenannt.JPG (184,5 KB, 3 Downloads)

Bildformate!!!

Ausserdem ist es nicht wirklich schwer, die Fehlermeldungen als Text zu 
kopieren und zu posten. Eieieieieiei.

MFG
Falk

Autor: Tobias W. (eagle2010)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Amania wrote:
> ich habe eifach deine code in ISE kopieren und synthetisieren lassen ,
> es gab aber viele Fehler . Wie ist es bei dir ?

Hast Du die Libraries richtig eingebunden (IEEE.NUMERIC_STD.ALL 
eingebunden und IEEE.STD_LOGIC_ARITH.ALL sowie 
IEEE.STD_LOGIC_UNSIGNED.ALL deaktiviert)?

Autor: Mathi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Hast Du die Libraries richtig eingebunden (IEEE.NUMERIC_STD.ALL
>eingebunden und IEEE.STD_LOGIC_ARITH.ALL sowie
>IEEE.STD_LOGIC_UNSIGNED.ALL deaktiviert)?

Böse... IEEE.STD_LOGIC_ARITH und IEEE.STD_LOGIC_UNSIGNED sollte man 
nicht benutzen. Die sind teilweise Hersteller abhängig. Nur das 
IEEE.NUMERIC_STD sollte noch verwendet werden.

Autor: Amania (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@tobias: ich habe einfach deine Code koppiert und laufen lassen . wenn 
die
obere Code richtig , dh. ich muss nicht ändern , aber läuft bei mir 
nicht , kann ich das Ergebnis von Simulation bei dir sehen ?

Autor: Tobias W. (eagle2010)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mathi wrote:
>>Hast Du die Libraries richtig eingebunden (IEEE.NUMERIC_STD.ALL
>>eingebunden und IEEE.STD_LOGIC_ARITH.ALL sowie
>>IEEE.STD_LOGIC_UNSIGNED.ALL deaktiviert)?
>
> Böse... IEEE.STD_LOGIC_ARITH und IEEE.STD_LOGIC_UNSIGNED sollte man
> nicht benutzen. Die sind teilweise Hersteller abhängig. Nur das
> IEEE.NUMERIC_STD sollte noch verwendet werden.

Ja, richtig. Genau aus diesem Grund wurden ja IEEE.STD_LOGIC_ARITH und 
IEEE.STD_LOGIC_UNSIGNED NICHT eingebunden!

@Amania:
Lässt sich denn der Code jetzt synthetisieren?
Ich sollte vielleicht noch dazu sagen, dass man für eine erfolgreiche 
Simulation noch eine Regelstrecke simulieren muss (z.B. PT1).

Autor: Amania (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
tobias :
 - achso , natürlich brauch man noch PT1 und Rückführung , kannst du bei 
Gelegenheit , di code von PT1 Strecke hochladen , das wäre sehr nett .
 - und noch eine Frage , wie kannst du die Abtastzeit Ta berechnen ?

Autor: luppi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
richtig

Autor: Ronny (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi Tobias.

Kannst du mir erklären wie du die Konstanten MKp, MTn, MTv, sowie Tn, Tv 
festgelegt hast?

Sowie die Berechnungsformeln für yi und yd.

Habe die Berchnungs bzw. Realsisierungsvorschrift für einen PID 
vorliegen, kann mir aber deine nicht erklären. Vielleicht hast du ein 
paar Minuten und postest mal eine kurze Erklärung.

Danke.Ciao

Autor: Tobias W. (eagle2010)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Ronny,

es ist ja schon etwas länger her, dass ich den PID-Regler programmiert 
habe, aber ich versuche mal, mich zu erinnern...
Der Sinn der Konstanten MKp, MTn und MTv ist der, dass ich 
ausschließlich mit Ganzzahlen rechnen möchte, d.h. ich multipliziere 
erst z.B. Kp mit MKp und dividiere schließlich das Ergebnis der 
Berechnung wieder durch MKp. Um dividieren zu können muss MKp eine 
2er-Potenz (2^x) sein. (s. dazu 
http://webber.physik.uni-freiburg.de/~hon/vorlss02... 
- Abschnitt 4.2)
Dies bedeutet, ich habe z.B. den Faktor MTn so gewählt, dass die 
Konstante TaTnM = (Ta / Tn) * MTn (gerundet) ein hinreichend genaues 
Ergebnis gibt (dafür sollte MTn möglichst groß sein, aber auch nicht zu 
groß, da TaTnM nicht größer als 200 sein sollte, um in der weiteren 
Berechnung einen Überlauf zu vermeiden). Die Faktoren Mxx sollen also so 
gewählt werden, dass die damit berechneten Konstanten < 200 sind.
Die Regler-Parameter Kp, Tn und Tv werden nach den üblichen Verfahren 
zur Regelereinstellung an der realen Regelstrecke festgelegt (z.B. 
Ziegler-Nichols oder Chien, Hrones und Reswick).
Die Berechnungen von yi und yd entsprechen eigentlich den 
Berechnungsvorschriften für I- und D-Anteil bei nummerischer Rechnung.
Der I-Anteil berechnet sich ja z.B. in integraler Form wie folgt:
Das Integral lässt sich aber auch durch die Summe von
 nummerisch berechnen:
Daraus folgt, dass yi berechnet werden kann, wenn das vorheriger 
Ergebnis für yi bekannt ist:
Dies - in Verbindung mit den Faktoren für die Ganzzahlarithmetik - ist 
genau dass, was ich in meiner Routine für die Berechnung des I-Anteils 
mache.
Analog funktioniert eigentlich auch die Berechnung des D-Anteils, nur 
dass hier immer die Differenz zwischen e und dem vorherigen e (ealt) 
herangezogen wird:

Ich hoffe, die Erklärungen konnten weiterhelfen.

Gruß
Tobias

Autor: Ronny (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Tobias.

Danke für deine schnelle Antwort.
Ja stimmt nach langem rechnen habe ich das erkannt.
Und danke für den Link, der ist sehr hilfreich.
Ich habe den Regler jetzt mal ausprobiert. Allerdings, bekomme ich wie 
befürchtet die Zeitprobleme.

Siehe auch:
Der Code muss dabei aber umgeschrieben werden, weil in diesem Fall die
Berechnung innerhalb eines Taktzyklus erfolgen muss.
Die Lösung dafür heißt entweder Pipelining, oder kompliziertere
Constraints, die sich "Multicycle-Path" nennen.
Leider ist dies alles komplizierter als die Lösung, die Du jetzt hast,
aber solch ein PID-Regler mit 10 Hz am FGPA ergibt doch sonst keinen
Sinn, oder?

Hast du dich damit nochmal beschäftigt?

Ciao

Autor: Tobias W. (eagle2010)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

da ja bei mir wie gesagt die 10 Hz schon mehr als ausreichend waren, 
weil es sich um eine Temperaturregelung handelt, war die Zeit kein 
Problem. Ich habe mich also nicht mit Pipelining o.ä. beschäftigt.

Wenn ich das richtig gelesen habe, willst Du damit einen Motor regeln? 
Welche Frequenz benötigst Du denn für den Regler? Wenn ich mich nicht 
irre, war bei meinem Code die Maximalfrequenz, die Xilinx ISE angegeben 
hat, ca. 40 MHz.

Gruß
Tobias

Autor: Ronny (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ja es soll ein Motor angesteuert werden. Ich habe einen Lagesensor der 
mir einen Wert liefert. Diesen nutze ich um ein PWM zu erzeugen und 
steuere somit einen Motor an. Ja bei mir ist das alles sehr zeitkritsch, 
da die Ansteuerung sehr des Motors sehr schnell sein muss und der Sensor 
auf eine schnelle Lageänderung reagiert. Habe das Altera MaxII was einen 
BoardTakt von 66MHz hat.

Gruß Ronny

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]
  • [vhdl]VHDL-Code[/vhdl]
  • [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.