Hallo,
bereits der zweite Post heute zur selben hardware, die ich mir
überwiegend gebaut habe, um daran weiterzulernen (um Fragen schon einmal
vorzubeugen ;))
Die Humanoide Roboterhand (im Bild) bekommt gerade eine kleine
Kalibrierung.
Ich benutze den ATMega16 und habe nun folgende zwei Fragen:
Ich möchte die Servos der Hand "kontinuierlich" ansteuern, das heißt die
bewegung soll halbwegs flüssig sein und nicht ruckhaft.
Wenn ich nun (auf meine anfängerweise) die pulsweite des servosignals
vom ist zum sollzustand in einer forschleife in kleinen (µs-) schritten
durch addition zum istzustandspuls verändere, (momentan mache ich die
pulsweite über delay routinen weil ich mit dem internen timer noch nicht
vertraut bin), frisst der Rechenakt mir 20% vom Speicher des µC weg.
Diese Methode ist also offensichtlich äußerst ineffizient - damit die
erste Frage:
Wie kann ich servos codeeffizient "kontinuierlich" ansteuern?
Dann, was eventuell mit an der ersten frage hängt: Wie sorgt ihr für
eine langsamere bewegung eurer servos? Wenn ich eine position vorgebe,
fährt der servo ja mit maximaler geschwindigkeit an seine position - wie
(geht das?) kann ich die geschwindigkeit verringern?
Beste Grüße
Alex
Alex schrieb:> Wenn ich eine position vorgebe,> fährt der servo ja mit maximaler geschwindigkeit an seine position - wie> (geht das?) kann ich die geschwindigkeit verringern?
Ich habe zwar keine große Erfahrung bei der Ansteuerung von Servos.
Die Geschwindigkeit selber kann man jedenfalls nicht verringern.
Jedoch musst Du ja nicht die entgültige Endposition angeben, sondern
Dich in kleinen Schritten dieser annähern. Aber das hast Du ja bereits
so gemacht.
Du solltest Dich aber dringend über Timer schlau machen. Vor allem das
Stichwort PWM ist hier wichtig. Denn damit macht der Prozessor das ganze
Timing ohne große Programmierung selber.
Mit PWM musst Du nur noch eine Variable von der Istposition zur
Sollposition durchzählen (eventuell mit einer gewissen Zeit zwischen den
Schritten).
Geschwindigkeit von Modellbauservos ändern geht, wie Christian auch
schon angemerkt hat, eigendlich nicht.
Was mich aber interessieren würde, wie steuerst du die Servos überhaupt
an?
Ich würde einen internen 16bit Timer mit Output Capture verwenden um die
PWM zu erzeugen.
Diese auf eine beliebige Anzahl kaskatierte 4017 Counter geben zum
demultiplexen. Jeder Counter kann dabei 10 Servos bedienen.
Dazu reichen 2 Pins am AVR.
Alex schrieb:> Wenn ich nun (auf meine anfängerweise) die pulsweite des servosignals> vom ist zum sollzustand in einer forschleife in kleinen (µs-) schritten> durch addition zum istzustandspuls verändere,
Wozu in µs-Schritten? Bei der üblichen Ansteuerung bekommt ein Servo
doch sowieso nur alle 20 ms eine neue Position.
> (momentan mache ich die pulsweite über delay routinen weil ich mit dem> internen timer noch nicht vertraut bin), frisst der Rechenakt mir 20%> vom Speicher des µC weg.> Diese Methode ist also offensichtlich äußerst ineffizient
Warum das so ist, kann man ohne Code kaum sagen. Ins Blaue getippt: Hast
du vielleicht die delay-Funktionen der avr-libc verwendet, ohne die Doku
zu lesen? Da kann man schnell was falsch machen, was die Codegröße
explodieren läßt.
> Wie kann ich servos codeeffizient "kontinuierlich" ansteuern?
Die angegebene Methode sollte sich eigentlich bezüglich Codegröße recht
effizient implementieren lassen. Bezüglich Prozessorauslastung sieht es
aber wegen der Delays ganz anders aus. Da helfen dann Timer.
> Dann, was eventuell mit an der ersten frage hängt: Wie sorgt ihr für> eine langsamere bewegung eurer servos? Wenn ich eine position vorgebe,> fährt der servo ja mit maximaler geschwindigkeit an seine position - wie> (geht das?) kann ich die geschwindigkeit verringern?
Wie du es getan hast, die Sollposition langsam verstellen. So macht das
ja auch der Modellpilot, wenn er langsam am Steuerknüppel rührt.
>Ich möchte die Servos der Hand "kontinuierlich" ansteuern, das heißt die>bewegung soll halbwegs flüssig sein und nicht ruckhaft.
Dazu müsste man alle 20ms einen neuen Servowert berechnen, da ja die
Impulse alle 20ms ausgegeben werden. Für eine flüssige Bewegung musst Du
interpolieren ( Stichwort Bahninterpolation )
>, frisst der Rechenakt mir 20% vom Speicher des µC weg.
Der Rechentakt frisst Speicher?
Alex schrieb:> durch addition zum istzustandspuls verändere, (momentan mache ich die> pulsweite über delay routinen weil ich mit dem internen timer noch nicht> vertraut bin)
Das muss sich ändern.
Die Pulsausgabe an die Servos muss losgelöst von allem anderen
funktionieren.
Hier mal Code, mit dem ich auf einem Mega128 mit 16Mhz 12 Servos
ansteuere.
Dass die Servos auf PortF und PortC verteilt sind, hat keinen tieferen
Grund, ausser dass auf meiner Platine die Anschlüsse günstig lagen.
Jeweils 6 Servos sind auf F und 6 Servos sind auf C.
1
#define F_CPU 16000000UL
2
3
#include<avr/io.h>
4
#include<avr/interrupt.h>
5
#include<util/delay.h>
6
7
#define SERVO_ANZAHL 12
8
9
#define CENTER 30
10
11
volatileuint8_tvalue[SERVO_ANZAHL]=
12
{CENTER,CENTER,CENTER,
13
CENTER,CENTER,CENTER,
14
15
CENTER,CENTER,CENTER,
16
CENTER,CENTER,CENTER
17
};
18
19
ISR(TIMER2_COMP_vect)
20
{
21
staticuint8_tServoId=SERVO_ANZAHL;
22
23
if(++ServoId>=SERVO_ANZAHL)
24
ServoId=0;
25
26
if(ServoId==0){
27
PORTF=0x01;
28
PORTC=0x00;
29
}
30
elseif(ServoId<6)
31
PORTF<<=1;
32
33
elseif(ServoId==6){
34
PORTF=0x00;
35
PORTC=0x01;
36
}
37
else
38
PORTC<<=1;
39
40
OCR2=57+value[ServoId];// die 57 sind der 'Zeitoffset' für ca. 1ms
41
}
42
43
44
voidInitServo()
45
{
46
TCCR2=(1<<WGM21)|(1<<CS22);//Prescale von 256 und CTC mode
47
OCR2=value[0];
48
TIMSK|=(1<<OCIE2);
49
}
50
51
intmain(void)
52
{
53
uint8_ti;
54
55
DDRF=0xFF;
56
DDRC=0xFF;
57
58
PORTF=0x00;
59
60
// TCCR0 =(1<<WGM01) |(1<<CS00)|(1<<CS01);
61
// OCR0=125;
62
// TIMSK|=(1<<OCIE0);
63
64
InitServo();
65
66
sei();
67
68
....
69
70
// ab hier reicht es an value[x] einen neuen Wert zuzuweisen und
71
// das jeweilige Servo Nr x. fährt in die Richtige Position
72
73
...
Die 12 Servos steuern die Beine eines Hexapods (daher auch 6+6 für die
Servos der Ober- und der Unterarme).
Um zb einen Schritt auszuführen benutze ich zb
1
for(i=0;i<10;++i){
2
// angehobene Beine nach vorne
3
4
value[0]=CENTER+i;
5
value[1]=CENTER-i;
6
value[2]=CENTER+i;
7
value[3]=CENTER+i;
8
value[4]=CENTER-i;
9
value[5]=CENTER+i;
10
_delay_ms(100);
11
}
mit dem delay stellt man dann die Geschwindigkeit ein. Die
Pulsgenerierung selbst im Interrupt braucht so gut wie keine Zeit.
@Karl Heinz:
Merkt man in der Praxis den Zeitversatz zwischen den einzelnen Servos?
Die bekommen ihr Steuersignal ja quasi nicht 'synchron', oder ist das
kein Problem? Bzw. würdest du ab einer bestimmten Anzahl Servos da etwas
in der Richtung vermuten?
@Alex und alle. Das ist ein interessanter Spielplatz! Gibt es
Bezugsquellen für derartige Hände? Denn sowohl eine humanoide Hand, als
auch ein vergleichsweise schnöder Hexabod wären rein mechanisch für mich
unerreichbar.
Michael L. schrieb:> @Karl Heinz:>> Merkt man in der Praxis den Zeitversatz zwischen den einzelnen Servos?
Bei mir spielt das keine Rolle.
Im schlimmsten Fall ist die Pause zwischen den Updates eines Servos ja
maximal
12 * 2ms = 24ms
gross. Also sogar relativ nahe an den 20ms die sonst so genommen werden.
Im besten Fall kriegt jedes Servo so alle 10ms einen neuen
Positionspuls.
> Die bekommen ihr Steuersignal ja quasi nicht 'synchron', oder ist das> kein Problem?
Macht ja nichts. In einer Fernsteuerung bekommen sie ja auch nicht alle
gleichzeitig ihren jeweils nächsten Puls sondern reihum eines nach dem
anderen. Genau daraus (und aus ein wenig Spielraum um den Anfang einer
Sequenz bestimmen zu können) resultieren ja die 20ms in der
Wiederholrate.
> Bzw. würdest du ab einer bestimmten Anzahl Servos da etwas> in der Richtung vermuten?
Da musst du schon sehr viele Servos haben
Dass dein Code riesig wird, dürfte daran liegen, dass du eine
_delay_us-() "Funktion" mit Nichtkonstanten aufrufst - hab ich Recht?
Das sind nämlich keine Funktionen, sondern Makros, die _always_inline_
in deinen Code eingebettet werden. Sprich: bei jeder Ausführung liegt
dieses Stückchen Code wieder im Programmspeicher.
Das bläst natürlich deine Schleife mächtig auf.
Umgehen kannst du das Ganze, indem du z.B. folgende Funktion baust:
1
voidwarte_10us(unsignedcharwartezeit)
2
{
3
while(wartezeit)
4
{
5
wartezeit--;
6
_delay_us(10);
7
}
8
}
Davon darfst du jetzt aber keine präzise 10us Zeitbasis erwarten, weil
neben der eigentlichen Wartezeit z.B. noch Sprünge zur
Schleifenabarbeitung "Rechenzeit" brauchen.
Hallo und schon einmal herzlichen Dank für die rege Beteiligung!
@Michael Wolf: Im Moment steuere ich die Servos über einfache schleifen
an, die ungefähr so aussehen:
1
for(inti=0;i<50;i++)
2
{
3
PORTA&=~(1<<T_1);
4
_delay_us(19400);
5
6
PORTA|=(1<<T_1);
7
_delay_us(600);
8
}
wobei T_1 das erste Glied des Daumens darstellt.
@Rolf Magnus: in 100er µs schritten, damit die servobewegung
"kontinuierlich" wird, und in der delay-schleife überhaupt kleinere
schritte als ms möglich sind. Womit (auch s.o.) deine Frage nach der
unrühmlichen Verwendung der delay funktion klar mit einem JA beantwortet
wäre ;-)
@Markus: Mit Rechenakt war die Rechenoperation innerhalb der
delay-funktion (also zB _delay_ms(10+i) gemeint. Dann ist der code
plötzlich nicht mehr 5 sonder 25% des vorhandenen flashes groß
@ Karl Heinz: Ich sehe es ein, ich komme wohl um den Timer nicht mehr
herum. Vielen Dank für dein Codebeispiel, mal wieder genau das, was ich
brauchte um mich reinzufriemeln! Kommt meinem Problem doch sehr nache
(2*5 Servos).
@Michael H.: Danke für die Erklärung, ich tus nie wieder! ;)
mal sehen, wann ich wieder mehr zeit habe zum fummeln.
Viele Grüße
Alex
Alex schrieb:> (also zB _delay_ms(10+i) gemeint. Dann ist der code> plötzlich nicht mehr 5 sonder 25% des vorhandenen flashes groß
Das ist genau das, was Michael H. schreibt:
Michael H. schrieb:> Dass dein Code riesig wird, dürfte daran liegen, dass du eine> _delay_us-() "Funktion" mit Nichtkonstanten aufrufst - hab ich Recht?
achso und @Michael L: Ich weiß nicht, obs da Bausätze gibt, die Hand
habe ich in stundenlanger arbeit am pc gemesht und dann aus plaast
nachgebaut. ist also ein unikat ;-)
Respekt!!!
Wie hast du die Plastikteile denn geformt? Hab gelesen das man das
Plaast bei 60° verformen kann. Hast du das alles Freihand gemacht? Oder
hattest du irgendeine Form?
Hej! Cooles Projekt! Muss schon sagen!
Wie lange beschäftigst du dich schon mit der Hand?
@funky:
Kunststoff (nicht Plastik, denn Plastik ist PVC ;)) ist erst bei höheren
Temperaturen verformbar.
PMMA (als Plexiglas/Acrylglas bekannt) ist ca ab 100-120°C verformbar..
also schon etwas "unangenehm" heißer :)
MfG
Philipp9494
hi, danke!
die teile habe ich in formen "gedrückt" (nicht gegossen, das zeug wird
hat ja eine recht hohe viskosität über 60°). Die Formen habe ich nach
meinem Computermesh als Vorlage aus Modellbauholz gebaut (beschichtet
mit TESA, das ist wirklich die beste oberfläche für plaast :D )
Das schöne am plaast ist, dass man es, wenn was schiefgegangen ist,
einfach wieder einschmelzen oder anschmelzen (und nur lokal verändern)
kann.
grüße
Alex
Michael Wolf schrieb:> Ich würde einen internen 16bit Timer mit Output Capture verwenden um die> PWM zu erzeugen.> Diese auf eine beliebige Anzahl kaskatierte 4017 Counter geben zum> demultiplexen. Jeder Counter kann dabei 10 Servos bedienen.> Dazu reichen 2 Pins am AVR.
Hallo, das interessiert mich sehr!
Ich moechte zwar keine Roboterhand steuern, aber 16 Servos gleichzeitig
und weich, also nicht ruckartig! Bislang dachte ich, dies sei ein Fall
fuer CPLDs.
Ich bin leider ein ziemlicher Anfaenger, die Methode mit den 4017ern
erscheint mir einfacher, vor allem, weil ich noch kein VHDL kann. Kannst
du das bitte naeher erlaeutern?
Ich habe Zweifel, ob man mit dieser Methode viele Servos gleichzeitig
"weich" steuern kann. Sieht fuer mich so aus, als bekaeme alle
nacheinander ihren Wert. Ist das in der Praxis dann gleichzeitig genug
oder eher recht abgehackt?
ServoInteressierter schrieb:
^
> Ich habe Zweifel, ob man mit dieser Methode viele Servos gleichzeitig> "weich" steuern kann. Sieht fuer mich so aus, als bekaeme alle> nacheinander ihren Wert. Ist das in der Praxis dann gleichzeitig genug
Ich würde sagen: Wenn du einen Versatz von 20 bis 30 Millisekunden in
den Sercobewegungen wahrnehmen kannst, dann bist du Superman.
Könnte das nicht doch auf Probleme rauslaufen bei z.B. einem recht
komplexen Bein wo wirklich für flüssig aussehende Bewegung einige Servos
drin sind.
Bei, ich sag mal ohne nachdenken 24, Servos addiert sich ein Versatz
dann irgendwie doch schon und ich könnte mir bei der Modellierung einer
Bewegung vorstellen, daß das mühsahm ausgeglichen werden muss?
Clemens M. schrieb:> Könnte das nicht doch auf Probleme rauslaufen bei z.B. einem recht> komplexen Bein wo wirklich für flüssig aussehende Bewegung einige Servos> drin sind.> Bei, ich sag mal ohne nachdenken 24, Servos addiert sich ein Versatz> dann irgendwie doch schon und ich könnte mir bei der Modellierung einer> Bewegung vorstellen, daß das mühsahm ausgeglichen werden muss?
Irgendwann ist natürlich Schluss, dass ist schon klar.
24 * 2 Millisekunden sind 48 Millisekunden, die im Extremfall
zusammenkommen. 48 Millisekunden sind aber immer noch erst 5 Hunderstel
Sekunden und das ist dann auch noch 'im schlimmsten Fall'.
Guten Tag,
ich bin soeben auf den von Ihnen verfasst Eintrag gestoßen.
Ursprünglich war ich auf der Suche nach Info´s jeglicher Art bzgl.
Roboterarme.
Mich würde sehr interessieren welches Ende dieses Projekt gefunden hab,
alleine schon deshalb weil ich ein Student für Biomedical Engineering
bin.
Würde mich über eine Rückmeldung freuen,
mfg
"Ein Interessent"
*arno.lainer@gmail.com