mikrocontroller.net

Forum: FPGA, VHDL & Co. Digitaler PID Regler in VHDL


Autor: Ali Zafar (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo allerseits,

Ich hab mal ein kleines Problem. Ich habe eine Aufgabe bekommen, einen 
PID Regler in VHDL zu programmieren, das eine kommende Amplitude sowohl 
eine Eigenfrequenz eines Systems regelt. Kann mir einer ne Idee geben 
wie ich so ein Problem angehe, was sollte ich bei der Programmierung 
beachten.

Wäre sehr dankbar für euere Hilfe.

MfG

Autor: Joe G. (feinmechaniker) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sind die zu regelnde Amplitude und die Eigenfrequenz unabhängig?
Wenn nicht, wie hängen sie zusammen?
Wie wird die Eigenfrequenz beeinflußt?

Autor: Ali Zafar (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,
Also, ich muss einen generischen Digitalen regler programmieren, die 
Eigenfrequenz und Amplitude sind unabhängig voneinander, und können 
entweder vorgegeben werden oder werden von einem System in den Regler 
eingeschleust. Für den Amplitudenregler wird eine Aplituden Rampe 
eingeschleust und fürden EF-Regler eine Impedance Phase Ramp.
ich weiß es gibt zwei Methoden einen Regler zu entwerfen, 1. man 
entwirft analog einen und diskretisiert ihn oder man entwirft gleich 
einen diskreten Regler. Danke.

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

Bewertung
0 lesenswert
nicht lesenswert
Hi,

vielleicht hilft dir diese Arbeit. Sie beschreibt wie du mit Hilfe von 
Matlab/Simulink einen Regler entwirfst und dann in VHDL exportieren 
kannst.

Ansonsten schlage ich vor du gehst nach deiner zweiten Methode vor. Am 
besten fängst du mit einem einfachen P-Regler an. Der läst sich ganz 
einfach implementieren.

Autor: Ali Zafar (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,
danke für deine Hilfe, hilft mir wirklich sehr, hatte auch schon daran 
gedacht, erst in matlab zu programmieren und dnn in HDl umzuwandeln, da 
geht es einfacher, frag mich aber trotzdem wie ich direkt in VHDL 
implementieren soll.

Hab nun einen dieskreten PID Regler aufgestellt, was ist nun der nächste 
Schritt, wie beginne ich mit der Programmierung in VHDL.Danke.

MfG

Autor: Sebastian (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Diese Seite beschreibt sehr gut die Umsetzung von analogen und digitalen 
Reglern (auch Quellcodebeispeile) Diese muss man dann in VHDL umsetzen

http://www.rn-wissen.de/index.php/Regelungstechnik...

Autor: Ali Zafar (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
hallo,
vielen dank, diese seite kenne ich bereits, hab meine anfänglichen 
versuche unternommen um einen pid regler zu programmien, wie siehts aus, 
ist es einigermaßen in Ordnung.Danke.
 
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;


entity PID_Regler is
   generic(
    data_width: integer := 14;
    intern_ data_width  := 28;
      

     );
   port (
    w : in std_logic_vector(data_width -1 downto 0) --:= (others => '0');
    x : in std_logic_vector(data_width -1 downto 0) --:= (others => '0');
    y : out std_logic_vector(data_width -1 downto 0) --:= (others => '0');
    k_p  : in std_logic_vector(intern_data_width-1 downto 0);
    k_i  : in std_logic_vector(intern_data_width-1 downto 0);
    k_d  : in std_logic_vector(intern_data_width-1 downto 0);
    clk_i : in  std_logic;
    rst_i : in  std_logic;
);
end PID_Regler;

architecture Behavioral of PID_Regler is

signal e : std_logic_vector(daten_breite-1 downto 0) := (others => '0');

begin

  Regler: process(clk_i)

variable yp: std_logic_vector(intern_data_width-1 downto 0);
variable yi: std_logic_vector(intern_data_width-1 downto 0);
variable yd: std_logic_vector(intern_data_width-1 downto 0);
 
if rst_i = '1' then

      yp := (others => '0');
      yi := (others => '0');
      yd := (others => '0');
      ealt := (others => '0');
      y <= (others => '0');
else
      e  <= std_logic_vector(signed(w) - signed(x));  
      yp := (k_p*e);
      yi := (yi_alt+(k_i*e*T)); - yi_alt=yi
      yd := (k_d*((e-ealt)/T)); -- ealt=e

      y<= (yp+yi+yd);
ealt := e;
yi_alt := yi;
end if;
end process Regler;
end Behavioral;
 


MfG

Autor: mki (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi,

ich finde das sieht fürs erste mal ganz gut aus. Was noch fehlt (glaub 
ich) ist sowas wie "if(rising_edge(clk_i))". Ansonsten führt er den 
Process auch bei fallender Flanke aus oder macht sogar was ganz 
undefiniertes.

Was mir noch aufgefallen ist: Du hast bei
 yi := (yi_alt+(k_i*e*T));
kein T definiert. Und du brauchst das auch nicht wirklich. Deine Zeit 
ist ja in clk_i mit enthalten und dass der Process mit jedem Zeittakt 
neu ausgeführt wird. Das gleiche gilt auch für yd.

Autor: mac4ever (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Falls dein Problem nur simuliert werden soll, kannst du es so lassen.

Ansonsten würde ich die Variablen durch Signale ersetzen. Bei diesen 
verschachtelten Anweisungen bekommst du sonst sicher Probleme mit der 
Laufzeit bzw. maximalen Taktrate.
Signale generieren dir eine Pipeline wodurch das Resultat zwar um einen 
Takt verzögert am Ausgang erscheint, du allerdings den Systemtakt stark 
erhöhen kannst. Das wiederum kann der Bandbreite des Reglers nur 
dienlich sein.

Btw.: Die Divisionsoperation ist nicht so einfach zu synthetisieren. Das 
wird dir jedes Tool um die Ohren hauen. Ich persönlich löse soetwas gern 
durch Multiplikation mit dem Reziprokwert aus einem ROM. Das geht 
schnell und braucht nicht viel Ressourcen. Dafür ist man aber auf eine 
max. Anzahl von Werte für T beschränkt.

Autor: Ali Zafar (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,
danke für deine Antwort, kannt du mir erklären wie das mit dem 
reziprokwert funktioniert, das habe ich so nicht verstanden.Danke.

MfG

Autor: Hans (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
1/5 : division!
1/5 = 1 * (1/5) = 1 * 0.2 : multiplikation!

0.2 ist der reziproke wert zu 5, das muss in ner tabelle liegen.

Autor: mac4ever (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sagen wir, du möchtest mit Werten von 1-500 dividieren. Also nimmst du 
ein BlockRAM und hinterlegst an den Adressen 1-500 die entsprechenden 
(vorberechneten) Reziprokwerte. Jetzt "wählst" du dir den gewünschten 
Divisor durch anlegen der entsprechenden Adresse (also dem 
Divisionswert) an den RAM. Im nächsten Takt liegt der Reziprokwert am 
Ausgang des BlockRAMs und wird einem Multiplizierer zugeführt. Schwupps, 
Division erledigt.

Nachteil dieser Methode: Bei zu geringer Bitbreite des Divisors wird die 
Genauigkeit schlechter.

Autor: Ali Zafar (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,
Ich hab die Variable yp durch signal ersetzt, gibt dann fehlermeldung

Signal "yp" cannot be target of variable assignment statement.
Signal declaration 'yp' not allowed in this region

dieser Code fonktioniert einwandfrei mit variable bei dem Obigen hatte 
ich paar Fehler drin:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use ieee.math_real.all;


entity PID_Regler is
   generic(
    data_width: integer := 16
    --intern_data_width: integer  := 28;
    );
    
   port(
    w : in signed(data_width-1 downto 0); --:= (others => '0');
    x : in signed(data_width-1 downto 0); --:= (others => '0');
    y : out signed(data_width-1 downto 0); --:= (others => '0');
    --k_p  : in std_logic_vector(data_width-1 downto 0);
    --k_i  : in std_logic_vector(data_width-1 downto 0);
    --k_d  : in std_logic_vector(data_width-1 downto 0);
    clk_i : in  std_logic;
    rst_i : in  std_logic
);
end PID_Regler;

architecture Behavioral of PID_Regler is

constant k_p : integer := 10;
constant k_i : integer := 100;
constant k_d : integer := 1;
--constant T: real := 0.1;

signal e : signed(data_width-1 downto 0) := (others => '0');

begin

Regler: process(clk_i)

signal yp: signed(data_width-1 downto 0) := (others => '0');
variable yi: signed(data_width-1 downto 0) := (others => '0');
variable yd: signed(data_width-1 downto 0) := (others => '0');
variable ealt : signed(data_width-1 downto 0) := (others => '0');
variable yi_alt : signed(data_width-1 downto 0) := (others => '0');
 
begin
if rising_edge(clk_i) then
  if rst_i = '1' then
    yp := (others => '0');
    yi := (others => '0');
    yd := (others => '0');
    ealt := (others => '0');
    y <= (others => '0');
    else
    e  <= (signed(w) - signed(x));  
    yp := (k_p*e);
    yi := (yi_alt+(k_i*e/10)); --yi_alt=yi
    yd := (k_d*((e-ealt)*10)); -- ealt=e

    y <= (yp+yi+yd);
    --y <= resize((yp + yi + yd));
    ealt := e;
    yi_alt := yi;
    end if;
end if;
end process Regler;
end Behavioral;


Autor: Mathi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ein Signal darf nicht innerhalb eines Prozesses deklariert werden.
Schreibe mal die Deklaration zwischen architecture und begin.

Außerdem muss es
yp <= (others => '0');
bzw.
yp <= (k_p*e);

Autor: Der Profi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Ich persönlich löse soetwas gern
>durch Multiplikation mit dem Reziprokwert aus einem ROM.

Ja, habe ich auch immer gemacht. Bei höheren Genauigkeiten ist das aber 
untauglich. Es ist zweckmäßiger, die Division auf Binärteilung zu 
normieren und entsprechend zu multiplizieren, also die Restrechung zu 
skalieren.

>Also nimmst du ein BlockRAM und hinterlegst an den Adressen 1-500
> die entsprechenden (vorberechneten) Reziprokwerte.

Ja ist klar. Ein Blockram mit 512 Speicherzellen. In diese Architektur 
bekommt man aber auch ohne große Schwierigkeiten die Terme für eine 
binäre Division hinein.

>Nachteil dieser Methode: Bei zu geringer Bitbreite des Divisors wird die
>Genauigkeit schlechter.

Genau. Denn wenn man genauer werden will, muss man manuell dividieren, 
also den Rest nochmal prozessieren. Das sind dann 2x(2+1) Takt = 6 Takte 
+ handling drum herum.

Ein Core im Cyclone 2 macht das auch in 8-10 Takten voll gepipelined. 
Der nutzt dazu die sog. Softmultiplier, die er ins RAM stopft. Und er 
ist zudem auch noch "restwertfähig".

Als Beispiel habe ich eine Division über 20bit/10 Bit mit nochmaliger 
Restwertberechung Rest*10Bit/10Bit und bekomme ein 20Bit breites Ergbnis 
nach nur 20 Takten. Die Rechenpipeline ist so aufgezogen, daß alle 
Divisionen (durchaus ähnlich einem Regler mit " /T") einzeln vorliegen. 
Dann packt der Core 8 Kanäle und hat einen Durchsatz von 1/(80 clks) je 
Kanal -> 1 MHz. (immer noch "analog klein" gegen das Wandlereinlesen und 
-auslese z.B.).

Autor: Ali (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,
Habs soweit einen konkreten PID regler aufgestellt, nun will ich ihn 
soweit erweitern, die eigentliche Aufgabe ist ja, dass der Regler extern 
Werte in Form Amplitude und Frequenz einer Spannung abbekommt, die 
werden dann durch einen A/D Wandler umgewandelt, die Auflösung des A/D 
wandlers ist 14 bit groß, das heißt doch, dasss ich mein w (Sollwert) 
und x(Istwert)
als std_logic_vector (13 downto 0) deklarieren muss.
Anschließend wird wieder D/A umgewandelt und die Daten an ein System 
gesendet.
Nochwas, die PID Anteile sind nicht konkret festgelegt, sondern sollen 
in ein Register festgelegt werden wobei die dann abgerufen werden, dass 
ist für mich der schwierige Teil.

Also muss ich doch alle signal und variablen usw. als std_logic_vector 
deklarieren oder.

So sieht mein gedankengang aus, hoffe ihr könnt mir weiterhelfen.Danke.
 
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use ieee.math_real.all;


entity PID_Regler is
   generic(
    data_width: integer := 14
    intern_data_width: integer  := 18;
    );
 
port(
      clk_i              :  in  std_logic;
      rst_i              :  in  std_logic;
      k_p          : in std_logic_vector(intern_data_width-1 downto 0);
      k_i          : in std_logic_vector(intern_data_width-1 downto 0);
      k_d          : in std_logic_vector(intern_data_width-1 downto 0);
      data_w_i        :  in std_logic_vector(data_width-1 downto 0);
      data_x_i        :  in std_logic_vector(data_width-1 downto 0);
      data_y_o        :  out std_logic_vector(data_width-1 downto 0)
  );
end PID_Regler

architecture Behavioral of PID_Regler is

signal  w    :  std_logic_vector(data_width-1 downto 0);
signal  x    :  std_logic_vector(data_width-1 downto 0);
signal  e    :   std_logic_vector(data_width-1 downto 0);

signal  y  : std_logic_vector(data_width-1 downto 0);
signal yp: std_logic_vector(intern_data_width-1 downto 0) := (others => '0');
signal yi: std_logic_vector(intern_data_width-1 downto 0) := (others => '0');
signal yd: std_logic_vector(intern_data_width-1 downto 0) := (others => '0');
signal ealt : std_logic_vector(intern_data_width-1 downto 0) := (others => '0');
signal yi_alt : std_logic_vector(intern_data_width-1 downto 0) := (others => '0');

begin
Regler: process(clk_i)
begin
--Mappen der Eingeänge
w  <= data_w_i;
x  <= data_x_i;
  
--Regeldifferenz
e  <= std_logic_vector(signed(w) - signed(x));  


if rising_edge(clk_i) then
  if rst_i = '1' then
    yp <= (others => '0');
    yi <= (others => '0');
    yd <= (others => '0');
    ealt <= (others => '0');
    y <= (others => '0');
    else
    e  <= (w-x);
    yp <= (k_p*e);
    yi <= (yi_alt+(k_i*e/10)); --yi_alt=yi
    yd <= (k_d*((e-ealt)*10)); -- ealt=e

    y <= (yp+yi+yd);
    --y <= resize((yp + yi + yd));
    ealt <= e;
    yi_alt <= yi;
    end if;
end if;
end process Regler;
data_y_o  <= y;
end Behavioral;

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ali schrieb:
> use ieee.math_real.all;
Wofür?

> Also muss ich doch alle signal und variablen usw. als std_logic_vector
> deklarieren oder.
Wenn ich eine rechenlastige Aufgabe habe, dann werden die Signale an den 
Ports als std_logic_vector übergeben und in meinem Modul 
schnellstmöglich nach signed oder unsigned oder integer gewandelt. Es 
ist unsinnig, mit nackten Vektoren zu rechnen. Denn was gibt z.B. "111" 
+ "111"?

Autor: mac4ever (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Der Profi schrieb:
> Ein Core im Cyclone 2 macht das auch in 8-10 Takten voll gepipelined.
> Der nutzt dazu die sog. Softmultiplier, die er ins RAM stopft. Und er
> ist zudem auch noch "restwertfähig".

Ich habe auch nie davon abgeraten einen IP-Core zu nutzen. Was du unter 
"restwertfähig" verstehst würde mich brennend interessieren. Die von mir 
vorgeschlagene Variante braucht keinen expliziten Rest, da das Ergebnis 
automatisch zu einer Festkommazahl mit x Nachkommastellen wird.

Der Profi schrieb:
> Ja ist klar. Ein Blockram mit 512 Speicherzellen.

Was willst du mir damit sagen? Also mehrere RAM-Blöcke zu verknoten, um 
512 32-Bit Werte zu speichern, sollte doch kein Problem darstellen.


@Ali
std_logic_vector ist die richtige Wahl. Für mathematische Operationen 
bieten sich aber auch unsinged/signed aus ieee.numeric_std an. 
Geschmacksache ...
Mit was ist das FPGA denn verbunden? Mikrocontroller oder PC? Dann würde 
ich auf I2C oder RS-232 zurückgreifen, um die Parameter zu übertragen. 
Dafür gibt es genügend Cores, etwa bei opencores.org

Autor: Ali (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,
danke für deine Antwort, es ist mit einem Microcontroller verbunden, 
aber die Daten können über eine RS232 Schnittstelle auch mit dem 
computer verbunden werden, und über diese Schnittstelle kann man auf die 
Register zugreifen. Hauptsächlich werden die Parameter mit RS232 
übetragen.

opencores.org: was genau enthält diese website, was ist für mich 
brauchbar, hab diese seite noch nie besucht.
Danke.

Autor: Ali (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,
Danke für deine Antwort,
wie genau wandle ich die Daten in signed um etwa so
e  <= std_logic_vector(signed(w) - signed(x));  

Danke
MfG

Autor: mac4ever (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die Umwandlung ist so i.O. wenn e,w und x vom Type std_logic_vector 
sind.

Auf opencores.org gibt es verschiedenste IP-Cores. So z.B. 
http://opencores.org/project,mmuart ... eine RS-232 UART für die 
Verbindung von FPGA und PC/Mikrocontroller. Erspart einen die Arbeit das 
Rad neu zu erfinden :)

Autor: Ali (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,
Vielen Dank nochmal.
Muss ich die Umwandlung auch für yp yi.... vornehmen, oder kann ich die 
so stehen lassen.Danke.

Mfg

Autor: mac4ever (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sinnvoll wäre es mit Sicherheit wenn Du die gesamte Rechnung über signed 
laufen lässt. Dafür kannst du bei der Signaldefinition anstatt 
std_logic_vector(x downto 0) -> signed(x downto 0) schreiben. Allerdings 
müssen k_(p|i|d) über einen Cast zu signed konvertiert werden.

Autor: A. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,
da bin ich wieder,
ich muss die Einstellungen für den P- I- und D-Anteil in einem Register 
ablegen wie genau realisiere ich das.Danke.


Hab mein Code soweit umgeändert:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use ieee.math_real.all;


entity PID_Regler is
   generic(
    data_width: integer := 14;
    intern_data_width: integer := 18
    );
 
port(
      clk_i              :  in  std_logic;
      rst_i              :  in  std_logic;
      k_p          : in std_logic_vector(intern_data_width-1 downto 0);
      k_i          : in std_logic_vector(intern_data_width-1 downto 0);
      k_d          : in std_logic_vector(intern_data_width-1 downto 0);
      data_w_i        :  in std_logic_vector(data_width-1 downto 0);
      data_x_i        :  in std_logic_vector(data_width-1 downto 0);
      data_y_o        :  out std_logic_vector(data_width-1 downto 0)
  );
end PID_Regler;

architecture Behavioral of PID_Regler is

signal  w    :  std_logic_vector(data_width-1 downto 0);
signal  x    :  std_logic_vector(data_width-1 downto 0);
signal  e    :   std_logic_vector(data_width-1 downto 0);

signal  y  : std_logic_vector(data_width-1 downto 0);
signal yp: std_logic_vector(intern_data_width-1 downto 0) := (others => '0');
signal yi: std_logic_vector(intern_data_width-1 downto 0) := (others => '0');
signal yd: std_logic_vector(intern_data_width-1 downto 0) := (others => '0');
signal ealt : std_logic_vector(intern_data_width-1 downto 0) := (others => '0');
signal yi_alt : std_logic_vector(intern_data_width-1 downto 0) := (others => '0');

begin
Regler: process(clk_i)
begin
--Mappen der Eingeänge
w  <= data_w_i;
x  <= data_x_i;
  
--Regeldifferenz
e  <= std_logic_vector(signed(w) - signed(x));  


if rising_edge(clk_i) then
  if rst_i = '1' then
    yp <= (others => '0');
    yi <= (others => '0');
    yd <= (others => '0');
    ealt <= (others => '0');
    y <= (others => '0');
    else
    --e  <= std_logic_vector(signed(w) - signed(x));
    yp <= std_logic_vector(signed(k_p)*signed(e));
    yi <= std_logic_vector(signed(yi_alt)+(signed(k_i)*signed(e)/10)); --yi_alt=yi
    yd <= std_logic_vector(signed(k_d)*((signed(e)-signed(ealt))*10)); -- ealt=e

    y <= std_logic_vector(signed(yp)+signed(yi)+signed(yd));
    --y <= resize((yp + yi + yd));
    ealt <= e;
    yi_alt <= yi;
    end if;
end if;
end process Regler;
data_y_o  <= y;
end Behavioral;


Autor: Dudhat Manish (Firma: student) (dudhat01)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
thank you guys for aour support in PID controller design!

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.