Archiv verlassen und diese Seite im Standarddesign anzeigen : PWM Erzeugung 18* Simultan
WarChild
20.07.2009, 16:01
Hallo Leute, ich hab mal wieder das Thema PWM aufgegriffen und habe eine Idee, wie man 18 oder mehr Servo PWMs mit nur einem 16-Bit-Timer mit einer Auflösung von 15 Bit erreichen kann.
Angenommen der µC arbeitet bei 16MHz, dann kann der 16Bit Timer taksynchron maximal 4.096ms darstellen. Also kann man auch einen ganzen Puls (max. 2.5ms) vorladen, starten und beim Überlauf beenden.
wenn man jedoch 18 signale gleichzeitig erzeugen will, so startet man alle PWM gleichzeitig, lädt den timer mit der gerigsten PWM dauer vor und wenn der überläuft,dann wird der kürzeste PWMbeendet, der Timer mit einer differenzzeit bis zum nächsten zu beendenden Impuls vorgeladen und so weiter.
also wenn in einem Array x[18] die pulsdauern liegen, so muss man diese zunächst sortieren in s[18] und die reihenfolge der indizes liegt in i[18]. also der kleinste wert von x[] steht in s[0] und in i[] steht der index des kleinsten Wertes aus x[]. nun werden in t[18] die benötigten Zeitdifferenzen berechnet. also t[0]=s[0], t[1]=s[1]-s[0], t[2]=s[2]-s[3]...
jetzt werden alle ausgänge auf high gesetzt,k=0, der 16Bit-Timer mit 2^16-t[k] vorgeladen, und beim überlauf wird k erhöht, der timer neu vorgeladen und stop(i[k]) wird aufgerufen. stop[n] beendet mittels einer case(n) struktur den entsprechenden Ausgang.
Was haltet ihr von der Idee? man könnte natürlich für sehr kleine zeitdifferenzen gleich den nächsten impuls beenden, da die berechnungen wahrscheinlich länger brauchen als die differenzzeit andauert. Außerdem muss natürlich noch eine minimale korrekturzeit für die unterbrechung des ISR von den Zeitdifferenzen abgezogen werden.
Aber sonst sollte es so möglich sein die relevanten 1,5ms mit mehr als 14 Bit aufzulösen, was für mich eine erhebliche steigerung wäre. momentan habe ich eine auflösung von ungefähr 200 schritten
Bin offen für Kritik und anregungen...
mfg WarChild[/flash]
WarChild
20.07.2009, 16:08
___|"""""""""|_______________________t[0];stop(i[k]);k++
___|""""""""""""|_____________________+t[1];stop(i[k]);k++
___|"""""""""""""|____________________+t[2];stop(i[k]);k++
___|""""""""""""""""""|________________+t[3];stop(i[k]);k++
___|"""""""""""""""""""""|______________+t[4];stop(i[k]);k++
___|""""""""""""""""""""""""|____________+t[5];stop(i[k]);k++
___|""""""""""""""""""""""""|____________stop(i[k]);k++ //da t[k]zu klein
___|"""""""""""""""""""""""""""|__________+t[7]];stop(i[k]);k++
Deine Idee wird so in Realität leider nicht funktionieren, es wäre viel zu ungenau.
Überleg z.B. was passiert, wen 2 Servos gleichzeitig auf LOW gestellt werden müssten, oder gar noch mehr Servos. Das würde mit dieser Funktion nicht gehen, ohne Genauigkeit einzubüßen (d.h. keine 15bit-Genauigkeit mehr)
Außerdem würde der Interrupt viel zu viel Zeit in Anspruch nehmen. Allein die case-Struktur (die die Werte jedes mal neu aus dem SRAM laden muss) und der Funktionsaufruf würden die Zeit sprengen.
Solche Funktionen, die sehr zeitkritisch sind, kann man nicht in C schreiben, sondern muss das in ASM machen.
Wozu brauchst du denn eine so große Auflösung? 15bit können Servos doch überhaupt nicht darstellen. Eine Auflösung von 200 sollte doch reichen, oder?
Ich bin gerade dabei, so eine Funktion für 18 Servosin ASM zu schreiben, allerdings verwende ich eine Genauigkeit von "nur" 180 Schritten und einen 20MHz-AVR. Theoretisch sollte auch eine Auflösung von ca.270 Schritten möglich sein. Viel höher geht es aber nicht, wenn es die Möglichkeit gibt, dass mehrere Servos gleichzeitig auf LOW gesetzt werden müssen.
Wenn dir das nicht so wichtig ist, und im restlichen Programm darauf achtest, dass servos nie den gleichen Wert haben, dann kannst du mit ASM eine Genauigkeit von ca. 1500 Schritten erreichen (schätzungsweise). Mit C wirst du nie so hoch kommen.
Mit einem sehr ausgeklügelten Programm und einigen Zeiteinbußen könntest du sogar erreichen, dass einige Servos Parallel auf LOW gesetzt werden könnten (allerdings nur die, die am selben PORT hängen).
Insgesammt ist sowas immer sehr schwer abzuschätzen, denn bei solch Zeitkritischen Programmen zählt jeder Befehl. Man müsste halt anfangen so eine Funktion zu schreiben, sie dann zu optimieren und dann gucken, was dabei rauskommt.
18Servos deuten sehr stark auf einen Hexapoden hin, was bedeutet, dass servos sehr wohl öfters den Selben Winkel haben. Außerdem braucht man meißt keine riesenhohe Auflösung für sowas, da die Servos sowieso nicht so genau sind, wie man es gerne wollte. Einen Unterschied von 0,5° wird niemand Merken. Mit einer 8bit-Auflösung spart man sich außerdem eine Menge Rechenzeit.
Ich hoffe, ich hab dir ein wenig weiterhelfen können,
Gruß, Yaro
WarChild
20.07.2009, 17:43
naja es kommt mir mehr auf die Auflösung an, nicht auf die Abweichung.
Wenn mehrere Servos gleichzeitig beendet werden müssen, dann stehen die zu beendenden signale in einem array, wenn der timer wieder läuft hat eine andere Funktion kurz Zeit um die Signale tatsächlich zu beenden. dass dabei verzögerungen von bis zu 200 zyklen auftreten ist mir egal.
diese konstante zeitverzögerung kann man ja beim vorladen des timers berücksichtigen. Die absolute Abweichung in grad kann man ehh nicht feststellen, denn das geht im Lagerspiel unter.
Meine derzeitige Auflösung von 200 Schritten kann man deutlich sehen. Es kommt sprunghaft zu einem längeren PWM, der servo will handeln, beschleunigt, schießt über das Ziel hinaus und er muss zurück....
Je dichter die möglichen PWM längen beieeinander liegen, desto geringer ist die beschleunigung des Servos und desto flüssiger wirkt seine bewegung.
In manchen Stellungen gerät mein Hexapod nähmlich genau aus diesem Überagieren der Servos in eine art Eigenschwingung. Da sein 4Kg die schnell beschleunigt werden relativ träge sind.
Aber trotzem Dange. mfg WarChild
Das mit der Schwingung ist ein gutes Argument. Wird nicht genaz einfach sein, so eine Funktion zu schreiben, weil man sehr viel berücksichtigen muss, aber interessant ist es allemal.
Ich werde in nächster Zukunft vielleicht auch mal soeine schreiben, wird aber noch mindestens 1-2Monate dauern, habe im Moment sehr viel zutun.
Gruß, Yaro
Besserwessi
20.07.2009, 19:43
Ich sehe keinen wirklichen Grund Wieso die Servosignale alle Syncron starten müssen. Wenn man die Pulse hintereinander hat, sollten eine Auflösung bis auf etwa 3 Zyklen relativ einfach sein. Selbst Zyklusgenau sollte in ASM gehen, wenn da keiner zu lange interruts sperrt. Ist aber doch recht viel Aufwandt die letzten paar Zyklen auch noch zu syncronisieren.
WarChild
20.07.2009, 20:13
das problem ist dabei nur dass ein 16Bit Timer seriell maximal 9 Signale a 2.2ms mit 50Hz erzeugen kann. Es sind also mindestenz zwei 16-Bit-timer von nöten, oder man muss viel mit "überläufe von 8-Bit-Timer zählen und aufsummieren" herumhantieren.
Meine ersten versuche laufen jetzt, aber was ich eingestehen muss:
Die signale werden wirklich äußerst genau aufgelöst, aber wenn die signale der Servos zu dicht beieinander liegen, dann wird geht die genauigkeit flöten. mal sehen, ob ich das irgendwie korrigiert bekomme.
WarChild
20.07.2009, 21:43
Dem problem ist nicht beizukommen. Der ISR schlckt einfach 50 takte und wenn alle 18 verschiedenen signale inerhalb eines bereiches mit einem maximalen abstand von 50 takten liegen, dann pflanzt sich da eine unschärfe fort, die beim letzten servo gute 5 grad beträgt, also unannehmbar.
mal sehen, vlt kann ich dem 16Bit timer ja beibringen zwei signale gleichzeitig zu generieren, eines mit dem compare match interrupt und das zweite mit dem überlauf, dann muss man die nur so sortieren, dass immer ein kleiner wert und ein größerer wert gleichzeitig generiert wird.
mfg WarChild
SIGNAL (SIG_OVERFLOW2) // 1ms Interrrupt
{
TCNT2 = 256 - 250; //Timer2 mit 6 neu vorladen
timer2++;
timer3++;
if(timer2 == 20) //alle Impulse starten
{
timer2 = 0; //timer2 endet bei 20ms und startet ALLE 18 Signale
signal= 0;
servo1on;
servo2on;
servo3on;
servo4on;
servo5on;
servo6on;
servo7on;
servo8on;
servo9on;
servo10on;
servo11on;
servo12on;
servo13on;
servo14on;
servo15on;
servo16on;
servo17on;
servo18on;
cli();
TCNT1 = 65536-dtime[signal]; //Timer1 neu vorladen
sei();
TIFR |= (1 << TOV1); //alten Overflow löschen
TIMSK |= (1 << TOIE1); //Timer1 Overflow Interrupt freigegeben
}
}
//**************************************************//
//Wird von Timer1 freigegeben und beendet die durch Timer2 gestarteten Pulse
SIGNAL (SIG_OVERFLOW1) //
{
stoppulse(sortindex[signal]); //letzten Impuls beenden
delay=ISRdelay;
missing=0;
signal++; //nächstes Signal
while(dtime[signal]<delay && signal<18) //für kleine Zeitintervalle direkt den nächsten beenden
{
stoppulse(sortindex[signal]);
delay+=88; //neue korrekturzeit bestimmen
missing+=dtime[signal]; //ubersprungene Zeit merken
signal++;
}
if(signal<18) //solange noch Impulse zu beenden sind
{
cli();
TCNT1 = 65536-dtime[signal]-missing+delay; //Timer1 neu vorladen
sei();
}
else //sonst Timer1 beenden
TIMSK &= ~(1 << TOIE1);
}
//**************************************************//
radbruch
20.07.2009, 21:59
Hallo
Es gibt unzählige Möglichkeiten viele Servos anzusteuern, hier möchte ich euch nochmal meine letzte Version vorstellen. Sie ist sicher nicht die beste Möglichkeit und mit 18 Servos funktioniert sie auch noch nicht richtig. Sie zeigt aber einen möglichen Ansatz ;)
// Servos ansteuern mit 8MHz Mega32 und 8-Bit Timer2 Overflow-ISR 22.3.2009 mic
// Die Servosimpulse werden nacheinander erzeugt. Die Impulsdauer jedes Servos
// setzt sich aus einem Grundimpuls (der für alle Servos gleich ist) und seinem
// Positionswert zwischen 0 und 255 zusammen.
// In der ISR werden im Wechsel ein Grundimpuls und ein Positionswert erzeugt
// und zum jeweiligen Servo gesendet. Nach den Servoimpulsen wird eine
// Pause eingefügt um die 50Hz Wiederholfrequenz (20ms) zu erzeugen.
// Diese auf acht Servos aufgebohrte Version scheint zu funktionieren,
// ich habe es allerdings nur mit angeschlossenen Servos 1-4 ausprobiert.
#include <avr/io.h>
#include <avr/interrupt.h>
// Servoausgänge 1-8
#define servoinit {DDRB |= (1<<PB7); PORTB &= ~(1<<PB7); DDRC |= 0b01110000; PORTC &= ~0b01110000;}
#define servo1on PORTC |= (1<<PC4)
#define servo1off PORTC &= ~(1<<PC4)
#define servo2on PORTC |= (1<<PC5)
#define servo2off PORTC &= ~(1<<PC5)
#define servo3on PORTC |= (1<<PC6)
#define servo3off PORTC &= ~(1<<PC6)
#define servo4on PORTB |= (1<<PB7)
#define servo4off PORTB &= ~(1<<PB7)
#define servo5on PORTB |= (1<<PB0) // Dummyservos 4-8 an SL6
#define servo5off PORTB &= ~(1<<PB0)
#define servo6on PORTB |= (1<<PB0)
#define servo6off PORTB &= ~(1<<PB0)
#define servo7on PORTB |= (1<<PB0)
#define servo7off PORTB &= ~(1<<PB0)
#define servo8on PORTB |= (1<<PB0)
#define servo8off PORTB &= ~(1<<PB0)
uint8_t servo1, servo2, servo3, servo4, servo5, servo6, servo7, servo8;
int main(void)
{
servoinit; // Datenrichtung der Servopins einstellen
//Timer2 Initialisierung
// für 8MHz Takt:
TCCR2 = (0 << WGM21) | (0 << COM20) | (1 << CS22); // Normal Mode, prescaler /64
// für 16MHz Takt:
//TCCR2 = (0 << WGM21) | (0 << COM20) | (1 << CS22) | (1 << CS20); // /128
TIMSK |= (1 << TOIE2); // Timer2 Overflow-Interrupt erlauben -> Servos an
//TIMSK &= ~(1 << TOIE2); // Timer2 Overflow-Interrupt verbieten -> Servos aus
sei();
servo1=125; // Mittelposition, Drehbereich ist von 0-255!
servo2=125;
servo3=125;
servo4=125;
servo5=125;
servo6=125;
servo7=125;
servo8=125;
while(1) // Hauptschleife
{
}
return(0);
}
ISR (TIMER2_OVF_vect)
{
static uint8_t servo_nr=0, grundimpuls=0; // Gestartet wird am Ende der Pause
static uint16_t impulspause;
if(servo_nr)
{
// Endweder wird hier der Grundimpuls erzeugt (Länge 56 Einheiten)
if(grundimpuls++ & 1) { TCNT2=200; impulspause-=256-200; } else
// Oder der zur Servoposition gehörende Impuls (0-255, 0 ist der längste Impuls!)
{
if(servo_nr==1) {TCNT2=servo1; servo1on; impulspause-=servo1;}
if(servo_nr==2) {TCNT2=servo2; servo1off; servo2on; impulspause-=servo2;}
if(servo_nr==3) {TCNT2=servo3; servo2off; servo3on; impulspause-=servo3;}
if(servo_nr==4) {TCNT2=servo4; servo3off; servo4on; impulspause-=servo4;}
if(servo_nr==5) {TCNT2=servo5; servo4off; servo5on; impulspause-=servo5;}
if(servo_nr==6) {TCNT2=servo6; servo5off; servo6on; impulspause-=servo6;}
if(servo_nr==7) {TCNT2=servo7; servo6off; servo7on; impulspause-=servo7;}
if(servo_nr==8) {TCNT2=servo8; servo7off; servo8on; impulspause-=servo8;}
if(servo_nr==9) {servo8off; servo_nr=0;}
if(servo_nr) servo_nr++;
}
}
else
// Anschliessend wird die Impulspuse erzeugt. Sie ergibt sich aus der Startlänge-
// der Summe der einzelnen Impulslängen. Bei acht Servos errechnet sich der
// kleinste benötigte Startwert für Impulspause etwa so:
// 8*56 + 8*256 = 2496 (Summe der Grundimpulse + Summe der Positionsimpulse)
{
if(impulspause>256) impulspause-=256; // Gesamtpause in 256er-Schritten
else {TCNT2=-impulspause; servo_nr++; impulspause=3000;} // die Restpause
}
}
Davon habe ich nun noch eine Version mit 16Bit-Timer im CTC-Mode und zwei Kanälen (A/B) für 18 Servos:
// 18 Servos ansteuern mit 8MHz-Mega32 und 16-Bit Timer1 24.3.2009 mic
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdlib.h>
#define systemtakt 1 // 1 bei 8MHz, 2 bei 16MHz-Prozessortakt
#define grundimpuls 47 // grundimpuls + 125 sollte Servomitte sein
// Servoausgänge A 1-9
#define servoainit {DDRB |= (1<<PB7); PORTB &= ~(1<<PB7);}
#define servoa1on PORTB |= (1<<PB7)
#define servoa1off PORTB &= ~(1<<PB7)
#define servoa2on PORTB |= (1<<PB0)
#define servoa2off PORTB &= ~(1<<PB0)
#define servoa3on PORTB |= (1<<PB0)
#define servoa3off PORTB &= ~(1<<PB0)
#define servoa4on PORTB |= (1<<PB0)
#define servoa4off PORTB &= ~(1<<PB0)
#define servoa5on PORTB |= (1<<PB0) // Dummyservoas 4-9 an SL6
#define servoa5off PORTB &= ~(1<<PB0)
#define servoa6on PORTB |= (1<<PB0)
#define servoa6off PORTB &= ~(1<<PB0)
#define servoa7on PORTB |= (1<<PB0)
#define servoa7off PORTB &= ~(1<<PB0)
#define servoa8on PORTB |= (1<<PB0)
#define servoa8off PORTB &= ~(1<<PB0)
#define servoa9on PORTB |= (1<<PB0)
#define servoa9off PORTB &= ~(1<<PB0)
// Servoausgänge B 1-9
#define servobinit {DDRC |= 0b01110000; PORTC &= ~0b01110000;}
#define servob1on PORTC |= (1<<PC4)
#define servob1off PORTC &= ~(1<<PC4)
#define servob2on PORTC |= (1<<PC5)
#define servob2off PORTC &= ~(1<<PC5)
#define servob3on PORTC |= (1<<PC6)
#define servob3off PORTC &= ~(1<<PC6)
#define servob4on PORTB |= (1<<PB0)
#define servob4off PORTB &= ~(1<<PB0)
#define servob5on PORTB |= (1<<PB0) // Dummyservobs 4-9 an SL6
#define servob5off PORTB &= ~(1<<PB0)
#define servob6on PORTB |= (1<<PB0)
#define servob6off PORTB &= ~(1<<PB0)
#define servob7on PORTB |= (1<<PB0)
#define servob7off PORTB &= ~(1<<PB0)
#define servob8on PORTB |= (1<<PB0)
#define servob8off PORTB &= ~(1<<PB0)
#define servob9on PORTB |= (1<<PB0)
#define servob9off PORTB &= ~(1<<PB0)
volatile uint8_t p; // 20ms-Timer
uint16_t servoa1, servoa2, servoa3, servoa4, servoa5, servoa6, servoa7, servoa8, servoa9;
uint16_t servob1, servob2, servob3, servob4, servob5, servob6, servob7, servob8, servob9;
/************************* Ausgabe an Terminal ********************************/
void writeChar(char ch) {while (!(UCSRA & (1<<UDRE))); UDR = (uint8_t)ch;}
void writeString(char *string) {while(*string) writeChar(*string++);}
void writeInteger(int16_t number, uint8_t base)
{char buffer[17]; itoa(number, &buffer[0], base); writeString(&buffer[0]);}
/************************************************** ****************************/
int main(void)
{
/************************ UART-Setup für RP6 *******************************/
#define BAUD_LOW 38400 //Low speed - 38.4 kBaud
#define UBRR_BAUD_LOW ((F_CPU/(16*BAUD_LOW))-1)
UBRRH = UBRR_BAUD_LOW >> 8; // Baudrate is Low Speed
UBRRL = (uint8_t) UBRR_BAUD_LOW;
UCSRA = 0x00;
UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0);
UCSRB = (1 << TXEN) | (1 << RXEN) | (1 << RXCIE);
/************************************************** *************************/
servoa1=125; // Drehbereich ist ca. 10-245!
servoa2=125;
servoa3=125;
servoa4=125;
servoa5=125;
servoa6=125;
servoa7=125;
servoa8=125;
servoa9=125;
servob1=125;
servob2=125;
servob3=125;
servob4=125;
servob5=125;
servob6=125;
servob7=125;
servob8=125;
servob9=125;
//servoa1=servoa2=servoa3=servoa4=servoa5=servoa6=se rvoa7=servoa8=servoa9=60; // Test
//servob1=servob2=servob3=servob4=servob5=servob6=se rvob7=servob8=servob9=60; // Test
servoainit; // Datenrichtung der Servopins A einstellen
servobinit; // Datenrichtung der Servopins B einstellen
//Timer1 Initialisierung
TCCR1A = 0;
TCCR1B = (0<<CS12) | (1<<CS11) | (1<<CS10); // Prescaler /64
TCCR1B|= (1<<WGM12); // CTC-Mode
OCR1A=100; // 100*64 Takte bis zum ersten Interrupt
OCR1B=100;
TIMSK |= (1 << OCIE1A);
TIMSK |= (1 << OCIE1B);
sei(); // ... und los!
while(1) // Hauptschleife
{
writeChar('*');
writeChar('\n');
servoa1=115;
servob1=115;
p=50; while(p); // Das sollte ungefähr 50*20ms=1 Sekunde verzögern
servoa1=135;
servob1=135;
p=50; while(p);
}
return(0);
}
ISR (TIMER1_COMPA_vect)
{
uint16_t temp=grundimpuls;
static uint8_t servob_nr=1;
static uint16_t impulspause=3000;
if(servob_nr==1) {temp+=servob1; servob1on;}
if(servob_nr==2) {temp+=servob2; servob1off; servob2on;}
if(servob_nr==3) {temp+=servob3; servob2off; servob3on;}
if(servob_nr==4) {temp+=servob4; servob3off; servob4on;}
if(servob_nr==5) {temp+=servob5; servob4off; servob5on;}
if(servob_nr==6) {temp+=servob6; servob5off; servob6on;}
if(servob_nr==7) {temp+=servob7; servob6off; servob7on;}
if(servob_nr==8) {temp+=servob8; servob7off; servob8on;}
if(servob_nr==9) {temp+=servob9; servob8off; servob9on;}
if(servob_nr >9) {temp =impulspause; servob9off; servob_nr=0;}
OCR1A=temp*systemtakt;
if(servob_nr) impulspause-=temp; else impulspause=3000;
servob_nr++;
}
ISR (TIMER1_COMPB_vect)
{
uint16_t temp=grundimpuls;
static uint8_t servoa_nr=1;
static uint16_t impulspause=3000;
switch(servoa_nr)
{
case 1: temp+=servoa1; servoa1on; if(p) p--; break;
case 2: temp+=servoa2; servoa1off; servoa2on; break;
case 3: temp+=servoa3; servoa2off; servoa3on; break;
case 4: temp+=servoa4; servoa3off; servoa4on; break;
case 5: temp+=servoa5; servoa4off; servoa5on; break;
case 6: temp+=servoa6; servoa5off; servoa6on; break;
case 7: temp+=servoa7; servoa6off; servoa7on; break;
case 8: temp+=servoa8; servoa7off; servoa8on; break;
case 9: temp+=servoa9; servoa8off; servoa9on; break;
default:temp =impulspause; servoa9off; servoa_nr=0; break;
}
OCR1B=temp*systemtakt;
if(servoa_nr) impulspause-=temp; else impulspause=3000;
servoa_nr++;
}(Beide Quellcodes aus: https://www.roboternetz.de/phpBB2/zeigebeitrag.php?p=436733#436733)
Leider funktionieren in der 18er Version immer nur 9 Servos in einer ISR, die andere ISR wird scheinbar ignoriert. Warum das so ist habe ich noch nicht rausgefunden, vielleicht findet ihr den Fehler.
Gruß
mic
Besserwessi
20.07.2009, 22:04
Für mehr als 8 oder 9 Servos gehen die Pulse nicht mehr alle hintereinander. Man könnte dann aber auf 2 Parallele Ketten von Pulsen ausweichen. Die 16 Bit Timer haben ja 2 Compare units. Man programmiert einfach jedesmal die Compare Werte um, auf den nächsten Wert. Da können dan schlimmstenfalls 2 Flanken zusammenfallen. Mit einigem auswand ließe sich das aber auch noch vermeiden indem man vorher gut plant und ggf. extra Pausen einfügt.
Gerade für Interreupts ist ASM einiges schneller als GCC oder gar BASCOM. Man kommt oft mit weniger Registern aus und braucht entsprechend weniger zu sichern. Auch ist der C compiler nicht immer so gut wenn es um 8 Bit werte geht.
radbruch
20.07.2009, 22:45
Da können dann schlimmstenfalls 2 Flanken zusammenfallen.
Bei meiner 18er Version wird jede ISR pro 20ms-Zyklus 10 mal angesprungen. Wenn wirklich zufällig eine ISR warten müsste würde das nur ein paar Takte dauern. Das sollte sich am Servo nicht bemerkbar machen.
Geschwindigkeitsprobleme bei Servoansteuerungen hatte ich mit C bisher noch nicht.
Gruß
mic
WarChild
20.07.2009, 23:46
Es klappt jetzt. ich erzeuge jnun je drei signale parallel und im ungünstigsten fall hat der letzte der drei eine abweichung von 50 takten. kann ich mit leben. aber ich werde die signale natürlch so legen, dass dieser fall eigentlich nie eintritt.
WarChild
21.07.2009, 04:20
Hi, nochmal...
kan mir jemand sagen, warum mein sortieralgorithmus nicht funktioniert?
Ich habe schon alle hilfvariablen gelöscht, aber der compiler scheint die innere If funktione einfach weg zu kürzen, zumindest komme ich im simulator niemals bis in die innere zuweisung. ich habe schon die suchrichtung umgekehrt, aber es hat nicht geholfen...
for(int8_t j=0;j<18;j++) // Werte in sorted sortieren und Sortierung in indexbuf merken
{
sorted[j]=65535;
for(int8_t k=j;k<18;k++)
{
if(sorted[j]>=calibrate[k])
{
sorted[j]=calibrate[k];
indexbuf[j]=k;
}
}
}
mfg WarChild
nur so als idee: man hat ja zwischen den servopulsen mengen an zeit, in denen kann man vorberechnen:
struct {
uint8_t portb, portc, portd;
uint16_t next_intr;
} timings[18];
also für jeden interrupt-zeitpunkt bereits vorberechnet haben, welche bitmuster auf welchen ports ausgegeben gehören, und welcher wert als nächster in den timer gehört.
damit läßt sich der code in der ISR ziemlich reduzieren:
ISR(TIMER1_COMP_vect) {
PORTB = timings[cur_timer].portb;
PORTC = timings[cur_timer].portc;
PORTD = timings[cur_timer].portd;
OCR1 = timings[cur_timer++].next_intr;
}
da da keine conditionals drin sind, weiß man genau, wieviele zyklen die ISR braucht, und kann beim aufbauen der timer-tabelle berücksichtigen, ob genug zeitabstand zwischen 2 werten ist oder man die besser zusammenfasst.
WarChild
21.07.2009, 09:58
Gute Idee!
Aber weiß jemand eine Antwort auf mein sortierproblem?
das ist eigentlich ne ganz banale sache.
die außere schleife speichert den durch die innere schleife als minimum bestimmten wert an erster stelle des sort[] arrays. aber das wird einfach übergangen... vlt auch wegoptimiert.
Lösung: nachdem ich die hilfs arrays global angelegt habe, hat es funktioniert... Fragt mich nicht warum.
lemmings
21.07.2009, 19:06
18x PWM riecht nach Hexabot :-)
nimm doch pro Bein einen ATtiny25/45/85 Automotive - die sind 8-Pin Dip und stellen 4 PWM-Kanäle zur Verfügung (dann aber nur basierend auf 2 8 Bit Timern - was IMHO genug genau ist)
8 Pins:
Vcc
Gnd
Reset
I2C Data
I2C Clock
PWM-1
PWM-2
PWM-3
:-)
KISS - keep it simple and stupid
Dann hat der Hauptprozessor auch keine Arbeit mehr.
WarChild
21.07.2009, 20:54
ich bin jetzt mit meiner Lösung ganz zufrieden.
Da die werte sortiert sind und immer ein ganz kurzer, ein mittlerer und ein ganz langer impuls gleichzeitig erzeugt werden, kommt es nie zu dem problem, dass die differenzzeit zwischen je drei Impulsen zu gering ist, um den ISR zu durchlaufen. ich habe den Hexapoden jetzt 10 minuten "zufällig" bewegen lassen, und die Fälle, bei denen er vorzeitig beenden musste zählen lassen. ich kann nur soviel sagen... es kam zu keinem überlauf meiner 8-Bit Zählvariablen. bei einem Überlauf wäre ein konrollpin high gestzt worden.(LED). selsbt, wenn ich bei allen 18 Servos den gleichen Winkel eingestllt habe, kam es nicht dazu, da jeder servo anders kalibreirt ist und aufrgrund dieser abweichnungen gibt es anscheinend immer eine Möglichkeit die Signale mit mehr als 100 Takten differenz in dreierpärchen anzuordnen. Deshalb habe ich die sonderbehandlung von eng beieinanderliegenden Signalen herausgenommen. Dank des Simulators konnte ich die Korreturzeiten fürs Vorladen bis auf einen Takt genau bestimmen. Deshalb erreicht der Timer1 effektiv für die relevanten 1,5ms (180°) eine auflösung von über 14-Bit.(außer die signale liegen zu eng beienander, was aber nie vorkommt)
Ich möchte behaupten, dass die auslastung des controllers größer wäre, wenn er 5 Atinys mit je 8Byte+BLA+BLA 50mal pro sekunde über I²C zu versorgen. Mein I²C Bus soll am ende nur dafür da sein, dass ein master Die Umwelterkennung und die verhaltenssteuerung macht und dem slave, der jetzt die IK und die PWMs macht nur ein paar steuerparameter bekommt, und dann eigenständig arbeitet. dass immer nur wenn sich das verhalten ändern soll ein kurzer steuer befehlkommen muss und dann hat de master wieder ruhe. Durch den ISR gehen mir jetzt alle 20ms 5000 Takte verloren und das sortieren kostet auch nochmal 13000 Takte, aber dafür ist dann alles "On-Board" und ist ein selbstgänger in der ISR.
Falls jemand von euch auch eine Hexapoden hat.
Wie hoch löst ihr die Koordinatensysteme auf?
Ich arbeite jetzt im ganzahligen mm maß. Und Wie immer gibt es ein schwächstes Glied. Die Rotation des Torsos läuft wirklich bis auf 0.0078° genau aufgelöst, aber die translation erfolgt in mm schritten. Sollte ich mir auch dort die arbeit machen die auflösung zu erhöhen, oder sprengt so etwas den rahmen?.
mfg WarChild
//***************Pulserzeugung + Timer**************//
//**************************************************//
void startpulse(uint8_t number)
{
switch (number)
{
case (0): servo1on; break;
case (1): servo2on; break;
case (2): servo3on; break;
case (3): servo4on; break;
case (4): servo5on; break;
case (5): servo6on; break;
case (6): servo7on; break;
case (7): servo8on; break;
case (8): servo9on; break;
case (9): servo10on; break;
case (10): servo11on; break;
case (11): servo12on; break;
case (12): servo13on; break;
case (13): servo14on; break;
case (14): servo15on; break;
case (15): servo16on; break;
case (16): servo17on; break;
case (17): servo18on; break;
}
}
//**************************************************//
//**************************************************//
void stoppulse(uint8_t number)
{
switch (number)
{
case (0): servo1off; break;
case (1): servo2off; break;
case (2): servo3off; break;
case (3): servo4off; break;
case (4): servo5off; break;
case (5): servo6off; break;
case (6): servo7off; break;
case (7): servo8off; break;
case (8): servo9off; break;
case (9): servo10off; break;
case (10): servo11off; break;
case (11): servo12off; break;
case (12): servo13off; break;
case (13): servo14off; break;
case (14): servo15off; break;
case (15): servo16off; break;
case (16): servo17off; break;
case (17): servo18off; break;
}
}
//**************************************************//
//**************************************************//
SIGNAL (SIG_OVERFLOW2) // 1ms Interrrupt
{
TCNT2 = 256 - 250; //Timer2 mit 6 neu vorladen
timer2++;
timer3++;
if(timer2 == 20) //je in 3ms Abstand drei Impulse starten
{
timer2 = 0; //timer2 endet bei 20ms
signal = 0;
}
if(timer2 == 18)
{
pulsecalculator();
nextIK=1;
}
else if(timer2%3 == 0) //alle 3 ms außer 18 s.o. je drei Signale starten
{
uint8_t a=sortindex[signal],b=sortindex[signal+1],c=sortindex[signal+2];
startpulse(a);
startpulse(b);
startpulse(c);
cli();
TCNT1 = 65536-dtime[signal]+ISRdelay1; //Timer1 neu vorladen
sei();
TIFR |= (1 << TOV1); //alten Overflow löschen
TIMSK |= (1 << TOIE1);
}
}
//**************************************************//
//**************************************************//
SIGNAL (SIG_OVERFLOW1) //Wird von Timer1 freigegeben und beendet die durch Timer2 gestarteten Pulse
{
stoppulse(sortindex[signal]); //letzten Impuls beenden
signal++; //nächstes Signal
if(signal%3) //solange signal kein vielfaches von drei ist die nächste zeit vorladen
{
uint16_t time=dtime[signal];
if(time<ISRdelay2)
TCNT1 = 65536-time;
else
TCNT1 = 65536-time+ISRdelay2;
}
else //sonst Timer1 beenden
TIMSK &= ~(1 << TOIE1);
}
//**************************************************//
lemmings
21.07.2009, 21:42
Ich möchte behaupten, dass die auslastung des controllers größer wäre, wenn er 5 Atinys mit je 8Byte+BLA+BLA 50mal pro sekunde über I²C zu versorgen.
Wie oft du neue Positionen an die Motoren übermitteln musst (ich denke, dir geht es um fliessende Bewegungsabläufe) hängt davon ab, wie du es implementierst.
du kannst auch zum normalen laufen feste positionen für jedes bein definieren oder auch synchronisierte abläufe und die dann einfach anfordern. der lokale µC übernimmt dann die Beschleunigung, bzw. eine gleichmässige geschwindigkeit.
:-)
Sehr interessantes Programm! Wirklich schön geschrieben!
Das einzige, was nicht so schön ist, ist dass ständig irgendwelche Interrupts raushüpfen =) Das könnte störend sein, wenn man was über einen Bus übertragen will. Das lässt sich hier aber kaum vermeiden.
Ich habe gerade meine ASM-Routine fertiggeschrieben. Wenn ich mal Zeit habe und es brauche, werde ich einen ähnlichen Code wie du schreiben.
Gruß, Yaro
WarChild
21.07.2009, 22:36
@ Yaro
Mein Ziel ist es nicht nur primitive festgelegten Bewegungsabläufen zu folgen, sondern dynamisch die beine nachzusetzten, während eine fortlaufende translation und rotation die fußpunkte unter dem bot wegbewegt, und nebenbei kann auchnoch eine statische translation und rotation eingestellt werden. (dann kann Er beim laufen mit drei freiheitsgraden (Vor/Rück,Rechts/Links,Rotation Rechts/Links herum) und nebenbei den Körper frei über den Füßen bewegen und orientieren) Da diese Methoden nur im globalen Koordinatensystem funktionieren müssen alle berechnungen in einem Chip vollbracht werden. Die Datenmenge die da anfällt würde jeden Bus sprengen und dank der Festpunktarithmetik lässt sich das alles in nur 5.3ms bei 16MHz von einem ATMega32 ausrechnen. Die fortlaufenden translation und rotation sowie das dynamische Bein nachsetzen fehlen noch, aber ich denke das sollte ebenfalls in 5ms machbar sein.
WarChild
21.07.2009, 22:43
für Interessierte:
Hier meine selbst geschriebenen und auch selsbt ausgedachten (außer sinus) Festpunktfunktionen.
Alle Funktionen arbeiten im Gradmaß!!! mit 7 Festkommabits das erspart einem die umrechnung mit PI
int16_t arccos(long number); //Eingabe 14 Festkommabits Ausgabe 7 Festkommabits
int16_t arctan(long number); //Eingabe 10 Festkommabits Ausgabe 7 Festkommabits
int16_t arctan2(int16_t y,int16_t x); //Eingaben proportional Ausgabe 7 Festkommabits
int16_t cosinus(long x); //Eingabe 7 Festkommabits Ausgabe 14 Festkommabits
inline int8_t signf(float number); //liefert 1 für positive und -1 für negtive Zahlen des tüps float
inline int8_t signl(long number); //des typs long (32-Bit)
inline int8_t signi(int16_t number); //des typs int16_t
int16_t sinus(long x); //Eingabe 7 Festkommabits Ausgabe 14 Festkommabits
uint16_t sqroot(unsigned long number); //Eingabe positive ganzzahl
//**************************************************// 100-1200 Takte*
uint16_t sqroot(unsigned long number) // O(n)=log_2(n) Max Abweichung meistens 0 oder 1, selten 2 bzw. 0.1% für große zahlen
{
if(number==0) return 0; //triviale Fälle abfangen
if(number==1) return 1;
unsigned long r2;
uint16_t root=0,delta;
if(number>=64536) //falls die Zahl größer als eine 16 Bit Zahl ist
delta=32768; //maximal mögliche Wurzel versuchen (2^15+2^14+2^13...=65535)
else
delta=(number>>1); //sonst beginn bei der Zahl selbst druch 2
while (delta) //liefert binnen 15 Iterationen eine Lösung
{
r2=(unsigned long)root*root;
if(r2<number)
root+=delta;
else
root-=delta;
delta=(delta>>1);
}
return root;
}
//**************************************************//
//**************************************************// 762 Takte*
const long W=23040UL; //Winkel eines Halbkreises Skalierung 128 = 7 Festkommabits
const long W2=46080UL; //Winkel eines Vollkreises
const uint16_t Wd2=11520; //Winkel eines Viertelkreises häufige verwendung spart Rechenzeit
int16_t sinus(long x) //
{
if(x>W2) x%=W2; //Skalierung auf den bereich [0;W2] (Sinuswelle)
if(x>W) x=-x+W; //x wird auf das Intervall [-W;W] normiert (aufgrund der Achsensymmetrie um x=0)
if(x<-W) x=-x-W;
//Parabel
const long B = 182; //2^-13 //(4/W); //linearer Formfaktor der Parabel
const long C = -259;//2^-21 //(-4/(W*W)); //quadratischer Formfaktor der Parabel
long y=((B*x)>>6)+((((C*x)>>11)*x*signl(x))>>10); //2^-14 //Funktionswert der einfachen Parabel
//Parabel Korrektur
const long Q = 99; //2^-7 //0.775; //Linearfaktor der einfachen parabel
const long P = 29; //2^-7 //0.225; //Linearfaktor der quadrierten korektur Parabel
y=((Q*y)>>7)+((((P*y)>>7)*y*signl(y))>>14); //2^-14 //Endergebnis nach gewichteter Summenbildung
return y;
}
//**************************************************//
//**************************************************// 768 Takte*
int16_t cosinus(long x) //
{ //
return sinus((x+Wd2));
}
//**************************************************//
//**************************************************// 1800 Takte*
int16_t arccos(long number) //
{ //
if (number==0) return Wd2; // 0 Abfangen
else if (number>=16384) return 0; // Werte größer 1 und kleiner -1 abfangen
else if (-number>=16384) return W;
if (signl(number)==1) // number>=0
return ((((long)sqroot(16384-number)*151511)>>11)+(((long)(16384-number)*4101)>>15)); //Faktoren 2^-11 und 2^-8 Ergebnis 2^-7
else
return (W-((((long)sqroot(16384+number)*151511)>>11)+(((long)(16384+number)*4101)>>15)));
}
//**************************************************//
//**************************************************// 974 Takte*
int16_t arctan(long number) //
{ //
int8_t v;
unsigned long x;
if (number==0) return 0;
if (number>0) {x= number;v=1;} // Betrag und Vorzeichen von number
else {x=-number;v=-1;}
if (x> 8388608UL) return (v* Wd2); //für |x|>2^13 gilt +-90°
if (x<1024) //für x<1 gilt allg.: x/(1+0.28x)
return v*(((209539UL*x)>>3)/(((x*x)>>10)+3657)); //zähler/nenner= 2^-17/2^-10=2^-7
else if(x<60000UL) // sonst gilt allg.: PI/2-x/(x²+0.28)
return v*(Wd2-((58671UL*(x>>3))/(((x*x)>>10)+287))); //zähler/nenner= 2^-17/2^-10=2^-7
else
{
x=(x>>5); //anpassung zum Schutz vor Überläufen
return v*(Wd2-(((58671UL*x)>>8)/(((x*x)+287)>>10))); //zähler/nenner= 2^-7/2^0=2^-7
}
}
//**************************************************//
//**************************************************// 1729 Takte*
int16_t arctan2(int16_t y,int16_t x) //
{ //
if(x==0) //Vorzeichenorientierte Fallunterscheidung
{
if (y==0) return 0;
else return signi(y)*Wd2;
}
if(x>0) return arctan(((long)y<<10)/x);
else return arctan(((long)y<<10)/x)+signi(y)*W;
}
//**************************************************//
//**************************************************// 5 Takte*
inline int8_t signf(float number)
{
if (signbit(number))
return -1;
else
return 1;
}
inline int8_t signi(int16_t number)
{
if(number&(1<<15))
return -1;
else
return 1;
}
inline int8_t signl(long number)
{
if(number&(1<<31))
return -1;
else
return 1;
}
//**************************************************//
Sehr interessante Funktionen!
Festpunktarithmetik zu benutzen habe ich mir auch schon überlegt, mache das aber in nur wenigen Funktionen. (arccos habe ich z.B. so geschrieben, mit einem Tabellen lookup, das Ergebniss ist zuwar ganzzahlig, dafür dauert die Berechnung aber auch nur 30Takte. Eine Tabelle von 1024byte im Flash stört ja nicht).
Das mit der Datenflut ist nicht unbedingt notwendig. Ich z.B. mache das so, dass ich im globalen Systen (auf dem ersten Controller) eine Funktion berechne nach der sich der Fuß bewegen soll und dann diese dem anderen Controller übergebe. Dieser rechnet dann für die jeweiligen Zeiten die nötige Position aus und fährt sie an. So habe ich mehr Spielraum und kann es mir leisten, mit float zu rechnen. Außerdem kann man dann leichter Peripherie anschließen und auswerten.
Gerade bin ich aber immernoch dabei, die mathematischen Funktionen zu erfinden, mit denen sich der Hexa bewegen soll. ist nicht ganz einfach sowas...
Bin gerade dabei, das Kurvengehen um einen bestimmten Radius zu implementieren. Ist nicht ganz einfach (aber das kennst du ja bestimmt =) )
Gruß, Yaro
MeckPommER
22.07.2009, 13:57
Hallo allerseits,
ich stand seinerzeit auch vor dem Problem, 18 Servos gleichzeitig mit einem Controller anzusteuern. Realisiert habe ich es letztendlich mit einem Atmega128 und dessen beiden 16-Bit Timern. Jeder Timer steuert 9 Servos mit einer nur wenigen Takten langen ISR, damit sich beide Interrupts nicht merkbar in die Quere kommen. Die Servos werden nacheinander angesteuert, womit auch keine allzu starken Lastspitzen auftreten.
Die Servoansteuerung auf diese Art verursacht fast keine Controller-Last, sodaß der Controller noch die IK für alle 6 Beine mitberechnen konnte.
Gruß MeckPommER
WarChild
22.07.2009, 20:50
ja...
ich habe aber eine (aus meiner sicht) geniale idee.
ich habe ein array in dem ist die position der fußpunkte enthalten, wie er ganz normal stehen soll. das bleibt so konstant. Und nun wird dies einmalig in ein zweites array kopiert, mit dem gearbeitet wird. (aufstehprozess).
Nun kommt die 1. funktion.
Die kopierten kordinaten werden mit 50Hz um ds_x, ds_y verschoben und um dphi_z gedreht und im gleichen array gespeichert. dies führt zu einer kontinuierlichen verschiebung der fußpunkte. Mit diesen parametern lässt sich jede fortlaufende Bewegung realisieren. Die anderen Freiheitsgrade kommen später.
jetzt wird eine funktion aufgerufen. Mit der die Abweichung der koordinate jedes gedreht und verschobenen fußpunktes vom stillstehenden ausgangspunkt perechnet wird (natürlich wird da zwischen steigender und fallender abstand unterschieden). Der fuß mit maximalem abstand wird als erstes gesetzt.
Jetzt kommt die frage wohn? dies lässt sich ebenfalls anhand von ds_x, ds_y und dphi_z feststellen. in verbindung mit dem ausgangspunkt wird der neue fußpunkt bestimmt und angefahren. (vlt sinusförmig).
nun folgen die sonstigen statischen transformationen um x,y,z, und phix, phiy und phiz. fertig ist das laufen.
das komplizierteste daran ist das wohin. mit dem Fuß und welcher als nächstes. die eigentliche bewegung des torsos auf einer kurvenbahn ist dagegen banal.
mfg WarChild
WarChild
22.07.2009, 21:13
@ Meckpommer
Ich habe gerade nochaml druch deine HP gestöbert.
Dein Bot hat ja schon ein paar evolutionen mehr hinter sich als meiner, aber zu der form der bewegung deines beines: Wenn du dir die gedanken mit sanften bewegungen machst, solltest du dann nicht alle punkte deiner bewegung betrachten? das mit dem abheben und aufsetzen ist sehr sanft bei dir, aber der mathematische ausdruck für sanft oder flüssig ist Stetigkeit, d.h. eine funktion ohne Sprünge und Knicke. Was würdest du von einer Cosinusfunktion, umgedreht und verschoben ins positive halten? Also: 1-cos(x) (optinal normieren mit 1/2) von 0° bis 180° da hast du auch deinen sanften Anfang und Ende, aber dafür keinen Schlagartigen wechsel von heben zu senken. Ist vlt. auch schonender für die servos.
Aber sonst: großes Vorbild!
mfg WarChild
Hört sich sehr durchdacht an, auch wenn ich zugeben muss, dass ich es nicht wirklich durchblickt habe... =)
Gruß, Yaro
MeckPommER
23.07.2009, 10:06
Jo, Warchild,
der Gedanke kam mir auch schon. Hat nur den Nachteil, das das Bein dann zu lange in Bodennähe "herumschleift". Bei 0 bzw. 180 Grad bleibt der Cosinus lange in den jeweiligen Maximalbereichen, was bei dem Bein zur Folge hat, das der Bot das Bein schon als angehoben ansieht, während es in Wirklichkeit noch so dicht über dem Boden ist, das es noch schleift und die Bewegung des Bots stört.
Das mit der Schonung der Servos ist immer zu beachten, da hast du absolut recht. Deshalb haben die Beine in Marvin Version 4 auch keine Servos mehr ^^ ... aber dazu in 2-3 Monaten mehr, wenn ich meine Ideen umgesetzt habe, oder aus bastlerischer Verzweiflung dem Fenster gesprungen bin ;-)
Das mit dem Drehen und Schieben des Koordinatensystems birgt auch so seine Tücken. Wenn du Botposition und -winkel vom Ursprung aus bearbeitest, mußt du erst drehen und dann schieben. Ansonsten würde ein Bot, der sich einen Meter vom Ursprung entfernt hat, nicht um seine eigene Achse drehen, sondern um den Ursprung, und mathematisch betrachtet fliegen oder im Boden versinken.
Gruß MeckPommER
ikarus_177
23.07.2009, 10:38
Hi,
ich habe mir auch ein Konzept für eine Kinematik zurechtgelegt: Der Controller kennt die Position des Bots im raumfesten Koordinatensystem, die Position des Bodenpunktes (ebenfalls im raumfesten System), sowie die drei (Euler-)Winkel für die Drehung im Raum.
Nun wird zuerst der Ursprung des roboterfesten Systems in den Ursprung des raumfesten Koordinatensystems gelegt, der Bodenpunkt mitverschoben. Nun wird die Position des Bodenpunktes vom raumfesten ins roboterfeste Koordinatensystem umgerechnet (https://www.roboternetz.de/phpBB2/viewtopic.php?p=452170#452170), was die weitere Berechnung enorm vereinfacht. Zu guter Letzt wird noch der Befestigungspunkt des Beines (der ja bekannt ist) in den Ursprung verschoben, und die Winkel per Tangens und Cosinussatz ausgerechnet.
Nun frage ich mich, wo der Haken an der ganzen Sache sein könnte? Mir kommt das nämlich nach einigem Nachdenken zu einfach vor...
Könnt ihr was entdecken?
Viele Grüße
ikarus_177
WarChild
23.07.2009, 23:30
nein das siht ganz ok aus. Man sollte sich nur von dem gedanken verabschieden, die bot von einem ortsfesten punkt aus betrachten zu wollen. ich habe bei mir als ursprung des globalen Koordinatensystems den Schwerpunk des Bots genommen. wenn ich nun transformiere, dann laufe ich gefahr, dass meine transformierten fußpunkte irgendwann im nirvana landen, aber die fußnachsetzfunktion holt die fußpunkte immer wieder ran. Ich rate also davon ab irgendeinen punkt, der nicht auf dem Bot liegt und sich nicht mitbewegt als bezugspunkt zu wählen. Ich denke das erspart einem einiges an rechnerei.
mfg WarChild.
MeckPommER
24.07.2009, 18:55
Ich habe zwei Koordinatensysteme - ein Ortsfestes und ein Bot-orientiertes. Das ist für den Fall sinnvoll, das der Bot irgendwo z.B. ein Hindernis feststellt und dieses auf einer internen Karte abspeichert. Hat der Bot kein festes System, in das er seine Sensorwerte eintragen kann, so kann er Sensorwerte nicht ortsfest positionieren, sondern kann nur aktuelle Messwerte verarbeiten.
Gruß MeckPommER
WarChild
28.07.2009, 16:06
So...
Ich habe jetzt nochmals meine Impulserzeugung vom Simulator checken lassen. Nach einem Tag simulation (25 Minuten Echtzeit), und soziemlich jeder nur denkbarer erreichbarer translation und rotation im raum kam die Software nie in die verlegenheit, dass sich die impulse nicht so anordnen ließen, dass zwischen 6 mal je drei signalen mindestens 150 Takte differenz bestand. Der Abstand zwischen zwei signalen war eigentlich immer größer als 1500 takte.
Wenn man jedoch bedenkt, dass die korrekturwerte der einzelnen servos zwischen 6000 und 12000 Takten liegen, überrascht es mich allerdings nicht mehr ganz so sehr, dass niemals mehr als sechs Signale näher als 150 Takte zusammen liegen.
Also, erzeugung von 6 mal je drei Signalen gleichzeitig mit einem 16-Bit Timer : läuft :-)
Und stromspitzen werden so auch etwas gemindert.
mfg WarChild
PS: laufen zu lehren ist doch schwerer als erwartet. da versteht man warum so viele nur mit vorprogrammierten sequenzen arbeiten und das nicht dynamisch machen.
Powered by vBulletin® Version 4.2.5 Copyright ©2024 Adduco Digital e.K. und vBulletin Solutions, Inc. Alle Rechte vorbehalten.