PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Servos auch langsam drehen?



copcom-weber
30.08.2010, 13:04
Hallo

habe schon gesucht aber nichts gefunden.
kann ich Servos auch langsam drehen lassen?
momentan ist es so wenn ich dem impuls gebe rast er sehr schnell dahin.
geht das auch das er diese position auch langsam anfährt?

benutze die M32 hier mein code:


void servo1(int pos)
{
PORTC |= IO_PC4;
sleep(pos);
PORTC &= ~IO_PC4;
mSleep(20);
}

BastelWastel
30.08.2010, 13:18
Ein Servo hat je nach Typ eine bestimmte Geschwindigkeit mit der er die vorgegebene Position anfaehrt.
D.h. um langsamer an diese zu fahren darfst du diese in deinem Programm nicht sofort auf den Endwert setzen, sondern musst sie schrittweise dem Endwert annaehern...z.B. via Timer interrupt

MfG, BastelWastel

copcom-weber
30.08.2010, 13:27
also wenn von 10 nach 15 will nicht direkt 15 sonder die zwischenwerte auch anfahren zb 11, 12, 13 ...
aber das ist dann meist noch schlimmer immer dieses beschleunigen und abbremsen macht es auch nicht besser.
sonst keine andere möglichkeit?

radbruch
30.08.2010, 14:13
Hallo

Den Drehweg in mehrere Teilschritte zu zerlegen funktioniert recht gut:

http://i4.ytimg.com/vi/s1bOm9C7GAk/1.jpg (http://www.youtube.com/watch?v=s1bOm9C7GAk)
http://www.youtube.com/watch?v=s1bOm9C7GAk

Besser geht's leider nicht, weil der Kontroller keinerlei Positionsrückmeldung erhält. Es gibt zwar Servos die das können, die haben aber auch einen höheren Preis.

Eine Bastellösung wäre ein zusätzliches externes Poti (z.b. aus einem Gamecontroller) mit einer kleinen Regelung im M32.

Gruß

mic

Magelan1979
30.08.2010, 15:03
Als Beispiel:

uint16_t i ;
for(i = 0; i < (ANSCHLAG_OBEN-ANSCHLAG_UNTEN)*10; i++)
{
servo4_position = ANSCHLAG_UNTEN + i/10;
mSleep(10);
task_SERVO();
}

Sieht zwar komisch aus, läuft bei mir aber schön langsam und geschmeidig

copcom-weber
30.08.2010, 19:14
kannst du mir mehr von deinem code dazu schicken weil so sagt mir das nicht ganz so viel.
ich habe ja nur die zeilen s.o.
was müsste ich daran umbauen?

BastelWastel
30.08.2010, 20:05
Code kann ich dir keinen anbieten, da Bascom und recht komplex..
Aber vom selbst basteln bleibt eh mehr hängen ;)

Du kannst dir für dein 'pos' zwei variablen machen, pos_soll und pos_ist z.B.
Wenn du dem Servo eine neue Position gibst speicherst du diese in pos_soll.
Deine Funktion mit der du den Servo ansteuerst rufst du aber mit pos_ist auf.

Wenn du nun z.B. jede mS einen Timer Interrupt hast kannst du in dieser pos_soll und pos_ist vergleichen und solange diese ungleich sind pos_ist in jedem Interrupt annähern.
Dies kannst du einfach immer um einen fixen Wert, dann hast du eine gleichmäsige Geschwindigkeit. Oder du berechnest den Wert damit du die Strecke in einer bestimmten Zeit zurück legst.
Bei Bedarf kannst du dann auch ein 'Geschwindigkeits Profil' fahren, mit langsamen an und ablauf.

radbruch
30.08.2010, 21:08
Ich hab's nicht getestet:


// "Weiche" Servoansteuerung mit sleep() mic 30.8.10

#include "RP6ControlLib.h"

char servo1(int pos)
{
static char pos_temp=0; // static = Werte zwischen den Aufrufen speichern
static char stellzeit=0;// dito

if (!pos_temp) // pos_temp beim ersten Aufruf initialisieren
{
pos_temp=pos; // Schnell zur gewünschten Position fahren
stellzeit=50; // innerhalb einer Sekunde
}

PORTC |= IO_PC4; // Impuls für aktuelle Position senden
sleep(pos_temp);
PORTC &= ~IO_PC4; // Impulspause
sleep(200-pos_temp); // Edit: mSleep() wird zu sleep()

if(stellzeit) // solange Stellzeit aktiv werden gleichlange Impulse gesendet
{
stellzeit--;
}
else // Position weiterschalten
{
stellzeit=5; // Stellzeit für den nächsten Teilschritt 0,1 Sek.
if(pos_temp<pos) pos_temp++; // Position noch zu klein
else if(pos_temp>pos) pos_temp--; // Position noch zu groß
else return(0); // 0 bedeutet: "Ziel erreicht"
}
return(1); // 1 bedeutet: "Ziel noch nicht erreicht"
}

int main(void)
{
initRP6Control();
DDRC |= IO_PC4;
PORTC &= ~IO_PC4;
while(true)
{
while(servo1(10)); // Solange auf Position 10 fahren bis 0 zurückkommt
while(servo1(20)); // dito mit Position 20
}
return(0);
}

copcom-weber
02.09.2010, 14:55
funktionieren tut es danke radbruch.
langsam ist es aber ruckelig und das hilft mir auch nicht viel weiter da ich am besten eine langsame bewegung brauch.
was gibts denn sonst für möglichkeiten?
was für servos oder so was?

radbruch
02.09.2010, 18:11
Hallo

Hier kannst du das etwas entruckeln:

stellzeit=5;

Die 5 sind die Anzahl der Impulse die gesendet werden bevor die Position weitergerechnet wird. Je kleiner der Wert, umso schneller wird die Position geändert. Wenn die Zeit zu klein ist erreicht das Servo aber eventuell nicht die Zielposition!

Etwas schneller und flüssiger wird es, wenn du die Schrittweite der Einzelschritte erhöhst:

pos_temp++; bzw. pos_temp--;

Dann wird es aber etwas komplizierter, weil die Bewegung nur als beendet gemeldet wird, wenn pos_temp genau pos entspricht.

Einfacher Ansatz wäre hier, nur bestimmte Positionen zu verwenden, bzw. wenn die Ziele immer ein vielfaches der Schrittweite sind. Hier ein Beispiel dazu mit 3er-Schritten:


// "Weiche" Servoansteuerung mit sleep() mic 2.9.10

#include "RP6ControlLib.h"

char servo1(int pos)
{
static char pos_temp=0; // static = Werte zwischen den Aufrufen speichern
static char stellzeit=0;// dito

if (!pos_temp) // pos_temp beim ersten Aufruf initialisieren
{
pos_temp=pos; // Schnell zur gewünschten Position fahren
stellzeit=50; // innerhalb einer Sekunde
}

PORTC |= IO_PC4; // Impuls für aktuelle Position senden
sleep(pos_temp);
PORTC &= ~IO_PC4; // Impulspause
mSleep(200-pos_temp);

if(stellzeit) // solange Stellzeit aktiv werden gleichlange Impulse gesendet
{
stellzeit--;
}
else // Position weiterschalten
{
stellzeit=5; // Stellzeit für den nächsten Teilschritt 0,1 Sek.
if(pos_temp<pos) pos_temp+=3; // Position noch zu klein
else if(pos_temp>pos) pos_temp-=3; // Position noch zu groß
else return(0); // 0 bedeutet: "Ziel erreicht"
}
return(1); // 1 bedeutet: "Ziel noch nicht erreicht"
}

int main(void)
{
initRP6Control();
DDRC |= IO_PC4;
PORTC &= ~IO_PC4;
while(true)
{
while(servo1(9)); // Solange auf Position 9 fahren bis 0 zurückkommt
while(servo1(21)); // dito mit Position 21
}
return(0);
}
(ungetestet)

8-20 oder 10-19 würde auch funktionieren. Von den paar Codezeilen darf man natürlich keine Wunder erwarten. Wirklich gut geht es nur mit höherer Auflösung der Positionen. Das macht man dann aber normalerweise nicht mehr blockierend.

Wenn man sleep(position) durch eine Zählschleife ersetzt, kann man mehr unterschiedliche Positionen anfahren:


// "Weiche" Servoansteuerung mit Zählschleife mic 2.9.10

#include "RP6ControlLib.h"

char servo1(int pos)
{
static int pos_temp=0; // static = Werte zwischen den Aufrufen speichern
static int stellzeit=0;// dito
int pos_dummy, dummy; // Hilfsvariable für die Zählschleife

if (!pos_temp) // pos_temp beim ersten Aufruf initialisieren
{
pos_temp=pos; // Schnell zur gewünschten Position fahren
stellzeit=50; // innerhalb einer Sekunde
}

PORTC |= IO_PC4; // Impuls für aktuelle Position senden
//sleep(pos_temp);
pos_dummy=pos_temp; // Schleifenzähler laden
while(pos_dummy--) dummy^=pos_dummy; // Zeit verbummeln
PORTC &= ~IO_PC4; // Impulspause
mSleep(190);

if(stellzeit) // solange Stellzeit aktiv werden gleichlange Impulse gesendet
{
stellzeit--;
}
else // Position weiterschalten
{
stellzeit=5; // Stellzeit für den nächsten Teilschritt 0,1 Sek.
if(pos_temp<pos) pos_temp+=100; // Position noch zu klein
else if(pos_temp>pos) pos_temp-=100; // Position noch zu groß
else return(0); // 0 bedeutet: "Ziel erreicht"
}
return(1); // 1 bedeutet: "Ziel noch nicht erreicht"
}

int main(void)
{
initRP6Control();
DDRC |= IO_PC4;
PORTC &= ~IO_PC4;
while(true)
{
while(servo1(2000)); // Solange auf Position 2000 fahren bis 0 zurückkommt
while(servo1(4000)); // dito mit Position 4000
}
return(0);
}
(ebenfalls ungetestet)

Ob 2000 und 4000 passen kann ich grad nicht sagen, das sind nur "gefühlte" Werte ;)

Aber ehrlich, das wird irgendwann eine Sackgasse.

Gruß

mic