Forum: Mikrocontroller und Digitale Elektronik Servo langsam ansteuern


von Patrick A. (Gast)


Lesenswert?

Hallo,
habe mal wieder en kleines Problem und ich hoffe ihr könnt mir helfen!

Ich will einen kleinen Servo mit einem Atmega8 ansteuern, was ja auch 
kein Problem ist.
Nur fährt er die Sollposition viel zu schnell an, was für mein Projekt 
sehr ungünstig ist.
Hab auch schon probiert die Sollposition in mehreren Schritten 
anzunähern, doch entweder er macht garnichts oder fährt normal zur 
Sollposition.

Kann mir vielleicht jemand einen Tipp geben, wie man das richtig macht, 
also dass der Servo langsam eine Position anfährt.

Achso ich habe alles in C Programmiert und kenne mich mit den anderen 
Sprachen nicht sonderlich gut aus.

Vielen Dank schon mal an alle im Voraus!

Gruß Patrick

von Daniel F. (df311)


Lesenswert?

du musst die strecke von der ist- zur sollposition in ausreichend viele 
schritte aufteilen (je nach gewünschter geschwindigkeit) und die dann 
der reihe nach anfahren und an jeder position kurz warten...

von Karl H. (kbuchegg)


Lesenswert?

Patrick A. schrieb:

> Hab auch schon probiert die Sollposition in mehreren Schritten
> anzunähern, doch entweder er macht garnichts oder fährt normal zur
> Sollposition.
>
> Kann mir vielleicht jemand einen Tipp geben, wie man das richtig macht,
> also dass der Servo langsam eine Position anfährt.

Genau so, wie du das versucht hast. Indem man das Servo in kleinen 
Schritten an die Zielposition führt. Wenn das immer noch zu schnell ist, 
dann müssen da eben noch mehr Zwischenschritte rein oder die zeitliche 
Distanz zwischen den Schritten vergrößert werden.

von Patrick A. (Gast)


Lesenswert?

Hallo,
habe noch mal etwas rum probiert aber es funktioniert immer noch nicht.
Erst soll er von der Mittelstellung schnell(hier ist es ok wenn es 
schnell geht) nach links fahren und da bleiben.
Soweit so gut und dann kommt das Problem: Von dort soll er dann langsam 
wieder nach rechts fahren und zu einem späteren zeitpunkt wieder nach 
links.
Ich fasse mal kurz zusammen was er macht:

Von links nach rechts>> er fährt schnell und brummt bis das Signal zu 
Ende ist.
Von rechts nach links>> er brummt nur und bewegt sich garnicht.

Ich füge mal den Code hier ein. Vielleicht sieht ja jemand den Fehler.

  a = 50;       // Schnell nach links

  while (a>0)
  {
    PORTB |= (1<<PB2);
    _delay_us(2000);
    PORTB &=~(1<<PB2);

    _delay_ms(18);
    a--;
  }
.
.
  uint8_t b=60;        // Langsam nach rechts
  uint8_t c=2000;
  uint8_t d;
  while (b>0)
  {
    d=0;
    PORTB |= (1<<PB2);
    while(d<c)
    {
      _delay_us(1);
      d++;
    }
    PORTB &=~(1<<PB2);

    _delay_ms(18);

    PORTB |= (1<<PB2);
    d=0;
    while(d<c)
    {
      _delay_us(1);
      d++;
    }
    PORTB &=~(1<<PB2);

    _delay_ms(18);

    PORTB |= (1<<PB2);
    d=0;
    while(d<c)
    {
      _delay_us(1);
      d++;
    }
    PORTB &=~(1<<PB2);

    _delay_ms(18);

    PORTB |= (1<<PB2);
    d=0;
    while(d<c)
    {
      _delay_us(1);
      d++;
    }
    PORTB &=~(1<<PB2);

    _delay_ms(18);

    b--;
    a=20;
    while (a>0)
    {
      a--;
      c--;
    }
    //c=c-20;
.
.
.
  b=60;                   // Langsam wieder nach links
  c=800;
  while (b>0)
  {
    d=0;
    PORTB |= (1<<PB2);
    while(d<c)
    {
      _delay_us(1);
      d++;
    }
    PORTB &=~(1<<PB2);

    _delay_ms(18);

    PORTB |= (1<<PB2);
    d=0;
    while(d<c)
    {
      _delay_us(1);
      d++;
    }
    PORTB &=~(1<<PB2);

    _delay_ms(18);

    d=0;
    PORTB |= (1<<PB2);
    while(d<c)
    {
      _delay_us(1);
      d++;
    }
    PORTB &=~(1<<PB2);

    _delay_ms(18);

    d=0;
    PORTB |= (1<<PB2);
    while(d<c)
    {
      _delay_us(1);
      d++;
    }
    PORTB &=~(1<<PB2);

    _delay_ms(18);

    b--;
    a=20;
    while (a>0)
    {
      a--;
      c++;
    }
  }

Viellen Dank noch mal!!!

Gruß Patrick

von Holger W. (holgerw)


Lesenswert?

so wird das nix.
Mach dir mal eine Funktion die als Parameter die Microsekunden erwartet 
(integer).
Dort setzt du den Portpin, wartest die übergebene Zeit, schaltest den 
Pin aus und wartest die 18 Milisekunden.
Im Hauptprogramm rufst du diese Funktion mit den Werten von 1000 bis 
2000 auf.
Und dann überlegst du dir wie oft und mit welchen Werten du die Funktion 
aufrufst.
Besser wäre das mit Timer, aber soweit bist du noch nicht.

Holger

von Tim M. (tim_m)


Lesenswert?

Hallo

Holger W. schrieb:
> Besser wäre das mit Timer, aber soweit bist du noch nicht.

Der M8 hat einen 16 Bit timer, mit welchem du ganz easy einen Servo 
ansteuern kannst. Dafür reichen sogar 8 Bit.

Dazu ein PWM Signal erzeugen, hab das auch schon mit einem Tiny13 
gemacht. Ich würde den 1. Timer dazu verwenden alle viertel Sekunde den 
Servostand um x Grad zu verschieben, den 8 Bit Timer für die PWM.

Es gibt zig Beispiele im Netz dazu.
Nettes Pdf: http://mil.ufl.edu/5666/handouts/ATMPWM.pdf

Tm

von Patrick A. (Gast)


Lesenswert?

Hallo,

meintest du das mit der Funktion so oder habe ich da was falsch 
verstanden?

void servo(uint8_t warten)
{
  PORTB |=(1<<PB2);
  _delay_us(warten);
  PORTB &=~(1<<PB2);
  _delay_ms(18);
}

im Hauptprogramm dann:
Von 2000 nach 800

a=60;
b=2000;
while (a>0)
{
    servo(b);
    b=b-20;
    a--;
}

Gruß Patrick

von Holger W. (holgerw)


Lesenswert?

so in der Art, aber uint8_t ist nur 8 Bit, hier also einen 16 Bit 
verwenden.

Holger

von Patrick A. (Gast)


Lesenswert?

OK Vielen Dank

Hab nur noch das Problem, dass der Compiler merkert, dass bei 
_delay(warten) keine Variable verwendet werden könnte.

Wie kann ich das Umgehen?

Gruß Patrick

von Holger W. (holgerw)


Lesenswert?

Dann warte in einer Scheife den übergebenen Wert 1 Microsekunde lang.
Der Lösungsansatz insgesamt ist nicht sehr schön, aber um einen Servo zu 
bewegen und zu verstehen wie das geht, schon ok.

Holger

von Rolf M. (rmagnus)


Lesenswert?

Holger W. schrieb:
> Dann warte in einer Scheife den übergebenen Wert 1 Microsekunde lang.
> Der Lösungsansatz insgesamt ist nicht sehr schön, aber um einen Servo zu
> bewegen und zu verstehen wie das geht, schon ok.

Nicht wirklich, denn die Wartezeiten sind dann völlig daneben. Bei 1 Mhz 
Prozessortakt wäre eine Mikrosekunde genau einen Taktzyklus lang. Ich 
weiß jetzt nicht, ob _delay_us inzwischen überhaupt so eine kurze 
Wartezeit machen kann, aber die Schleife braucht für sich selbst noch 
zusätzlich mehrere Taktzyklen, also sind die tatsächlichen Wartezeiten 
ein Vielfaches von der angegebenen.

von Karl H. (kbuchegg)


Lesenswert?

Holger W. schrieb:

> Besser wäre das mit Timer, aber soweit bist du noch nicht.

@Patrick

Das wär die beste Lösung. Daher solltest du da reinbeissen.

Servoansteuerung, bei der man sowohl das Pulssignal als auch 
Verfahrzeiten im Auge behalten muss, machen keinen Spass.
Die Pulserzeugung muss von alleine ablaufen und von den Verfahrzeiten 
und Wegen gestrennt sein. Alles andere bedeutet Aufwand ohne Ende

von STK500-Besitzer (Gast)


Lesenswert?

Rolf Magnus schrieb:
> Holger W. schrieb:
>> Dann warte in einer Scheife den übergebenen Wert 1 Microsekunde lang.
>> Der Lösungsansatz insgesamt ist nicht sehr schön, aber um einen Servo zu
>> bewegen und zu verstehen wie das geht, schon ok.
>
> Nicht wirklich, denn die Wartezeiten sind dann völlig daneben. Bei 1 Mhz
> Prozessortakt wäre eine Mikrosekunde genau einen Taktzyklus lang. Ich
> weiß jetzt nicht, ob _delay_us inzwischen überhaupt so eine kurze
> Wartezeit machen kann, aber die Schleife braucht für sich selbst noch
> zusätzlich mehrere Taktzyklen, also sind die tatsächlichen Wartezeiten
> ein Vielfaches von der angegebenen.

Ein Modellservo mit einer Auflösung von 1µs anzusteuern ist schon 
gewagt.
Sowas geht natürlich wunderbar mit der o.g. Methode, dass ein Timer mit 
der OnCapture-Einheit verwendet wird, dem man alle paar ms einen neuen 
Vergleiswert vorgibt.
Wenn man unbedingt Delay-Konstrukte verwenden will, sollte eine 
Auflösung von 10µs ausreichen.
Dann kann man auch das ganz einfach in geschachtelten Schleifen 
realisieren:

Pseudocode:
setze Endmarke
setze Impulsmarke
wiederhole bis Impulsmarke = Endmarke:
{
  setze Pulsdauer auf 0
  wiederhole bis Pulsdauer = Impulsendmarke erreicht:
  {
   warte 10µs
   inkrementiere Pulsdauer
  }
  wiederhole bis Pulsdauer = 20ms erreicht:
  {
   warte 10µs
   inkremtieren Pulsdauer
  }
  setze Impulsmarke auf neuen Wert
}

Das Setzen der der Pulsdauer kann durch Inkrementieren oder Dekrementier 
erfolgen. Je nachdem, in welche Richtung gedreht werden soll.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.