Archiv verlassen und diese Seite im Standarddesign anzeigen : zufallszahlen "auf" asuro erzeugen
malediction
24.01.2009, 16:01
hi...
ich würde gern anständige zufallszahlen auf asuro erzeugen.
jetzt hab ich zunächst mal gegooglet, wie man das am besten in c anstellt und bin zu folgendem code gekommen:
:
#include <time.h>
:
int zufall(int min, int max)
{
srand((unsigned)time(NULL));
return min+(rand()%(max-min+1));
}
:
dabei erzeugt die rand()-funktion die zufallszahlen und der term hinter dem return grenzt mittels modulo die folge zwischen minimalwert und maximalwert ein. leider arbeitet diese rand() nach einem gewissen schema, sodass nur pseudozufallszaheln entstehen (also immer wieder die gleichen). dazu initialisiert man die rand()-funktion mittels srand() (man weist der rand() sozusagen einen startwert zu). um das möglichst zweckmäßig zu machen und hier nicht immer dei gleichen werte zu liefern, benutzt man dazu die aktuelle zeit. (daher auch das #include time.h)
nun hab ich 2 probleme:
1. winavr hat die time.h-lib nicht und ich weiß auch nicht wie ich sie einbinden kann.
2. da der asuro ja nach dem flashen autonom arbeitet, bin ich mir nicht sicher, ob das überhaupt funktioniert, weil ich nicht weiß, ob es auf dem asuro sowas wie eine "uhrzeit" gibt, die ich auslesen kann.
kann mir da jemand helfen?
oder gibt es sogar eine elegantere lösung um zufallszahlen zu erzeugen?
danke schon mal.
malediction.
radbruch
24.01.2009, 18:04
Hallo
kann mir da jemand helfen?
oder gibt es sogar eine elegantere lösung um zufallszahlen zu erzeugen?
Eine Suche hier im RN-Forum nach "zufallszahlen AND asuro" findet genau drei Threads. Deinen hier, einen mit einer netten kleinen Zufallsfunktion:
https://www.roboternetz.de/phpBB2/zeigebeitrag.php?t=31712&highlight=zufallszahlen+asuro
Der dritte Thread erklärt, wie man sowas anstellen kann und wie ich es auch machen würde:
https://www.roboternetz.de/phpBB2/zeigebeitrag.php?t=9458&highlight=zufallszahlen+asuro
Alle zur Verfügung stehenden ADC-Werte verknüpfen:
#include "asuro.h"
#define versuche 5000 // Anzahl der Versuche
#define max 50 // Werte von 0 bis max-1 erzeugen
#define balken 0 // 1 für Anzeige mit Balken
unsigned char j, zahlen[max];
unsigned int i;
unsigned int rnd(unsigned char bereich)
{
static unsigned int alter_zufall;
unsigned int zufall, odo[2], line[2], bat, ports[2];
OdometrieData(odo);
LineData(line);
bat=Batterie();
ports[0]=256*PINB+PINC;
ports[1]=256*PINC+PIND;
zufall=~alter_zufall^odo[0]^odo[1]^line[0]^line[1]^bat^ports[0]^ports[1]^TCNT2;
alter_zufall=zufall;
while(zufall>=bereich) zufall-=bereich;
return(zufall);
}
int main(void)
{
Init();
for(i=0; i<max; i++) zahlen[i]=0;
SerWrite("\n\rZufallszahlen mit dem asuro\n\n\r", 32);
while(1)
{
i=versuche;
while(i-- && ++zahlen[rnd(max)]); // Zufallswerte einlesen (0 bei Überlauf!)
SerWrite("\n\rVerteilung nach ", 18);
PrintInt(versuche);
SerWrite(" Versuchen mit Maxwert ", 23);
PrintInt(max);
SerWrite(":\n\n\r", 4);
for(i=0; i<max; i++) // Verteilung der Werte ausgeben
{
PrintInt(i);
if(balken==1) for(j=0; j<zahlen[i]; j++) SerWrite(".",1);
SerWrite(" ", 1);
PrintInt(zahlen[i]);
SerWrite("\n\r", 2);
}
while(1);
}
return(0);
}
Es funktioniert leidlich ;)
Gruß
mic
Besserwessi
24.01.2009, 20:13
Nur die AD werte sollte man nicht nehmen. Da sollte man schon eine Pseudozufallszahl und eine echte zufallszahl kombinieren. Wenn die zwei unabhängig sind (das sollte bei AD Wandler Wert und Pseudozufall der Fall sein) kann das Ergebnis eigentlich nicht schlechter, also eher besser werden als jedes Verfahren für sich.
malediction
24.01.2009, 20:39
ich hab es jetzt so gelöst:
/*Funktion, die eine Zufallszahl zwischen min und max liefert*/
uint8_t zufall(int min, int max)
{
static uint16_t startwert=0x0AA;
uint16_t temp;
uint8_t n;
for(n=1;n<8;n++)
{
temp=startwert;
startwert=startwert << 1;
temp ^= startwert;
if ((temp & 0x4000)==0x4000)
{
startwert |= 1;
}
}
return min+(startwert%(max-min+1));
}
das ist die funktion von M1.R aus diesem (https://www.roboternetz.de/phpBB2/zeigebeitrag.php?t=31712) thread.
ich hab sie nur dahingehend verändert, dass ich sie auf einen wählbaren bereich zwinge.
funktioniert hinreichend gut ;) auch wenn ich nciht zu 100% verstehe, was da gemacht wird, aber hey :)
danke euch trotzdem für die antworten.
male...
Besserwessi
24.01.2009, 22:25
Das Programm sieht auch nicht sonderlich gut aus. Die Folge der Pseudozufallszahlen wird sich nach spätestens rund 16000 Elementen wiederhohlen. Das ist relativ wenig. Da sollte schon GCC (edit: natürlich nicgt gcc selber sondern libc: in <stdlib.h>) eine wesenlich bessere Routine mitbringen.
Die Zahlen sind auch nicht mal gut gleichverteilt, denn kleine Reste kommen von der Tendens ein kleine wenig häufiger vor. Einen ähnlichen Fehler haben die Deutschen Banken aber immerhin bei der ersten Generation EC karten auch gemacht.
Besser ist es da den Rest, der sich nicht mehr Teilen läßt ganz wegfallen zu lassen und dann lieber die nächste "Zufallszahl" zu nehmen.
am praxistauglichsten ist meiner meinung nach die vorgehensweise, einen pseudozufallsgenerator mit einer echten zufallszahl zu initialisieren und nach in diesem fall, laut besserwessi, 16000 zyklen (je nach generator) neu zu initialisieren.
dies kann man am einfachsten mit einem ADC-eingang, der "in der luft" hängt, machen. Der ADC hat soweit ich weiß eine Entropie von 2 Bit, würde aber nur mit einem, sprich dem niederwertigsten arbeiten, dieses 8 (oder 16 etc, je nachdem) mal samplen und das dem Generator geben.
in der c't war jetzt ein sehr informativer artikel über (Pseudo-) Zufallszahlen.
radbruch
25.01.2009, 02:12
Dann habe ich ja schon einiges richtig gemacht:
zufall=~alter_zufall^odo[0]^odo[1]^line[0]^line[1]^bat^ports[0]^ports[1]^TCNT2;
TCNT2 läuft zwar nur von 25 nach 0, aber mit dem hier wird zusätzlich die Ausführungszeit variabel:
while(zufall>=bereich) zufall-=bereich
Modula mit aktiven Interrupts, viel zufälliger gehts wohl nimmer... Aber ich bin eher Zufalls-Laie..
Zufällig ist nicht gleichverteilt, auch 6 Sechser nacheinander sind Zufall.
Wie zufällig muss es denn sein? Manchmal ist es auch sinnvoll ein Programm mit immer den selben zufälligen Werten zu testen. Wie ich das mit meiner "Formel" nachvollziehen sollte ist mir schleierhaft *lol*
malediction
25.01.2009, 11:47
ok ok...
ich komm grad nicht mehr so ganz mit :D
also die idee mit der psudozufallszahl und einer initialisierung mit einer echten zufallszahl habe ich ja oben schon gepostet.
hier war eben mein problem, weil asuro das mit der funktion time() nicht hinbekommt (weil er wohl keine zeit kennt, denke ich mal).
die pseudozufallszahl liefert die funktion rand(). (edit: initialisieren kann man das ganze mittels srand(unsigned int seed), wobei seed der neue ausgangswert für rand() ist.) das hätten wir also. die frage ist jetzt nur, wie ich aus dem ADC so eine "echte" zufallszahl bekomme. die variante von netzmann verstehe ich ehrlich gesagt nicht.
Wie zufällig muss es denn sein?
ansich war das o.g. beispiel schon ausreichend. ok, die meisten zahlen waren oft sehr groß, oder sehr klein, aber das war nicht weiter schlimm. eigentlich wollte ich nur ein kleines testprogramm schreiben, in dem asuro mittels ir hindernisse erkennt und dann "zufällig" in eine richtung (also "zufällige" kurve rechts oder links mit "zufälliger" gradzahl) ausweicht.
hätte ich geahnt was auf mich zukommt, hätte ich es anders implementiert... aber nu isses zu spät ;)
vielleicht ist es aber auch ganz gut so. so könnte man eine anständige, funktionstüchtige, ECHTE zufallszahl auf asuro erzeugen. und wenn die funktion gut ist, könnte man diese in die lib einbauen.
also um aufs problem zurückzukommen: woher die echte zufallszahl aus dem ADC bekommen?!
Besserwessi
25.01.2009, 12:44
Wenn man den ADC Ausgang offen läßt, fängt der sich eine Menge Störungen ein. Im wesenlichen wohl 50 Hz. Das ist dann zwar noch keine wirklich zufällige Spannung aber doch relativ ungregelmäßig. Wenn man es etwas besser haben will gibt es schaltungen die einfach eine elektronisches Rauchen (z.B. Widerstand oder Zenderdiode) genügend stark verstärken und so gute echte Zufallsspannungen erzeugen. Die Kunst dabei ist es dann Einkopplungen der 50 Hz / 100 Hz zu vermeiden.
Das mit der Verknüpfung verschiedener Zufallszahlen funktioniert nur gut wenn die wirklich unabhängig sind. Aufeinanderfolgen Werte eines Pseudzufallsgenerators sind da relativ schlecht. Selbst bei mehreren verschiedenen Pseudozufallszahlen kann das daneben gehen. Aber die Verknüpfung von einer Pseudozufallszahl und einer wenn auch schlechten echten Zufallszahl wie dem AD-wert hilft.
malediction
25.01.2009, 14:08
ok. das klingt ja ganz gut.
die frage ist jetzt, wie ich in c die werte aus dem ADC auslesen und in einer variablen speichern kann, sodass ich sie für die srand() funktion benutzen kann. geht das überhaupt?!
wenn man nur das niederwerigste bit vom ADC-Sampling hernimmt, hat man "echte" zufallszahlen, egal ob mit 50/100Hz Rauschen oder nicht. das ergibt ziemlich genau ein weisses rauschen.
Allerdings ist die datenrate nicht besonders hoch (eben 1Bit/Sample), eignet sich also nicht wirklich zur direkten zufallszahlenerzeugung, wohl aber als Seed für einen Pseudogenerator.
für einen 8-Bit generator also einfach 8xADC-Sample einlesen, jeweils das niederwertigste bit nehmen, dann jeweils auf die richtige Position shiften und addieren. heraus kommt eine wunderschöne, 8-Bit Zufallszahl.
Pseudocode:
RandomNr = 0
FOR SampleNr = 0 TO 7
ADC_Val = GetSample()
ADC_Val = ADC_Val And &B00000001
ShiftLeft ADC_Val, SampleNr
RandomNr = RandomNr + ADC_Val
NEXT
malediction
25.01.2009, 14:58
super.
das klingt wunderbar und dein programm ist auch logisch und einfach zu verstehen. allerdigns weiß ich nicht, wie ich das a) in c umsetzt und b) den wert des ADC vom asuro bekomme...
/*Funktion, die eine Zufallszahl zwischen min und max liefert*/
int zufall(int min, int max)
{
static uint8_t RandomNr=0;
uint8_t ADC_Val;
int n;
for(n=0;n<8;n++)
{
/*hier müsste jetzt der Wert aus dem ADC geholt werden, aber wie?!*/
ADC_Val=GetWert();
ADC_Val&=0x01;
ADC_Val=ADC_Val<<n;
RandomNr=RandomNr+ADC_Val;
}
return min+(RandomNr%(max-min+1));
}
kann man das so machen?! und wenn ja, mit welcher funktion oder wie auch immer, kann ich an den wert aus dem ADC rankommen?!
und dann gleich noch ne frage: geht das auch mit 16bit?! müsste doch ansich, oder?
z.b. so:
/*Funktion, die eine Zufallszahl zwischen min und max liefert*/
int zufall(int min, int max)
{
static uint16_t RandomNr=0;
uint8_t ADC_Val;
int n;
for(n=0;n<16;n++)
{
/*hier müsste jetzt der Wert aus dem ADC geholt werden, aber wie?!*/
ADC_Val=GetWert();
ADC_Val&=0x01;
ADC_Val=ADC_Val<<n;
RandomNr=RandomNr+ADC_Val;
}
return min+(RandomNr%(max-min+1));
}
?!
(edit: ich hab natürlich in meinem eifer das eigentliche erzeugen der zufallszahl vergessen. :D soll heißen: das prgramm liefert uns jetzt den seed, mit dem wir dann die rand() initialisieren müssen... nicht wundern! ;) )
radbruch
25.01.2009, 15:05
Hallo
Die sechs ADCs des asuros kann man mit seiner Library bequem einlesen:
ADC0/1: OdometrieData()
ADC2/3: LineData()
ADC4: PollSwitch()
ADC5: Batterie()
Das sind zwar keine freien ADC-Eingänge, aber besser als nichts.
Gruß
mic
malediction
25.01.2009, 15:31
ach mehr isses nicht?! ich dachte wunder, was ich da jetzt machen muss ;)
ok... also sollte es so gehen: (oder?!)
/*Funktion, die eine Zufallszahl zwischen min und max liefert*/
int zufall(int min, int max)
{
static uint16_t RandomNr=0;
uint8_t ADC_Val;
int n;
for(n=0;n<16;n++)
{
/*hier müsste jetzt der Wert aus dem ADC geholt werden, aber wie?!*/
ADC_Val=(ReadADC(WHEEL_LEFT,0)+ReadADC(WHEEL_RIGHT ,0)+ReadADC(IR_LEFT,0)+ReadADC(IR_RIGHT,0)+ReadADC (BATTERIE,0))/5;
ADC_Val&=0x01;
ADC_Val=ADC_Val<<n;
RandomNr=RandomNr+ADC_Val;
}
srand(RandomNr);
return min+(rand()%(max-min+1));
}
ich hab jetzt hier die funktion ReadADC() benutzt, da diese in der aktuellen lib schon so existiert. somit kann man direkt darauf zugreifen.
ist die maskierung ADC_Val&=0x01; so richtig?
wenn ja, sollte ja alles klappen...
malediction
25.01.2009, 17:54
zusammenfassend lässt sich sagen: ;)
/*Funktion, die eine Zufallszahl zwischen min und max liefert*/
int Zufall(int min, int max)
{
static uint16_t zahl=0;
uint8_t adc;
int n;
for(n=0;n<16;n++)
{
adc=(ReadADC(WHEEL_LEFT,0)+ReadADC(WHEEL_RIGHT,0)+ ReadADC(IR_LEFT,0)+ReadADC(IR_RIGHT,0)+ReadADC(BAT TERIE,0))/5;
adc&=0x01;
adc=adc<<n;
zahl=zahl+adc;
}
srand(zahl);
return min+(rand()%(max-min+1));
}
so funktioniert die zufallsfunktion sehr gut. ich denke die art der umsetzung ist auch recht sauber. ein test mit folgendem programm:
int main(void)
{
Init();
EncoderInit();
while(1)
{
if (Taster()!=0)
{
Msleep(1000);
while(1)
{
int i=0;
int n;
SerPrint("Zufallszahlen zw. 0 und 1");
SerPrint("\n\r");
for (n=0;n<10;n++)
{
i=Zufall(0,1);
PrintInt(i);
SerPrint(" - ");
Msleep(500);
}
Msleep(1500);
SerPrint("\n\r");
SerPrint("Zufallszahlen zw. 0 und 50");
SerPrint("\n\r");
for (n=0;n<10;n++)
{
i=Zufall(0,50);
PrintInt(i);
SerPrint(" - ");
Msleep(500);
}
SerPrint("\n\r");
SerPrint("Zufallszahlen zw. 80 und 500");
SerPrint("\n\r");
Msleep(1500);
for (n=0;n<10;n++)
{
i=Zufall(80,500);
PrintInt(i);
SerPrint(" - ");
Msleep(500);
}
SerPrint("\n\r");
SerPrint("Zufallszahlen zw. 0 und 60000");
SerPrint("\n\r");
Msleep(1500);
for (n=0;n<10;n++)
{
i=Zufall(0,60000);
PrintInt(i);
SerPrint(" - ");
Msleep(500);
}
SerPrint("\n\r");
SerPrint("Zufallszahlen zw. 0 und 50");
SerPrint("\n\r");
Msleep(1500);
for (n=0;n<10;n++)
{
i=Zufall(0,50);
PrintInt(i);
SerPrint(" - ");
Msleep(500);
}
Msleep(1500);
}
}
else Blinken('S');
}
while (1);
return 0;
}
lieferte bei mir dieses ergebnis:
Zufallszahlen zw. 0 und 1
0 - 1 - 1 - 0 - 0 - 0 - 1 - 1 - 0 - 0 -
Zufallszahlen zw. 0 und 50
22 - 22 - 1 - 17 - 12 - 28 - 50 - 33 - 14 - 28 -
Zufallszahlen zw. 80 und 500
332 - 141 - 201 - 246 - 427 - 361 - 365 - 174 - 371 - 155 -
Zufallszahlen zw. 0 und 60000
656 - 4886 - 947 - 4516 - 577 - 1505 - 2142 - 4125 - 3851 - 90 -
Zufallszahlen zw. 0 und 50
50 - 24 - 26 - 16 - 37 - 8 - 24 - 5 - 4 - 3 -
Zufallszahlen zw. 0 und 1
0 - 1 - 1 - 1 - 1 - 1 - 0 - 1 - 0 - 0 -
Zufallszahlen zw. 0 und 50
11 - 45 - 19 - 11 - 39 - 29 - 39 - 28 - 14 - 23 -
Zufallszahlen zw. 80 und 500
216 - 303 - 348 - 488 - 244 - 320 - 250 - 339 - 431 - 108 -
Zufallszahlen zw. 0 und 60000
933 - 1135 - 2625 - 2625 - 2625 - 2005 - 491 - 2289 - 0967 - 3539 -
Zufallszahlen zw. 0 und 50
38 - 33 - 35 - 4 - 42 - 23 - 11 - 18 - 40 - 19 -
daran sieht man, dass die zahlen doch sehr zufällig vorkommen und vor allem, dass 2 aufrufe der funktion mit den gleichen min- und maxwerten nicht zu den gleichen (und damit zu keinen pseudozufallszahlen) führen.
einzig verwunderliche ist, dass bei der zufallszahl zw. 0 und 60000 kein wert größer ist als 5000. leigt das vielleicht am wertebereich des int?!
Besserwessi
25.01.2009, 21:34
Durch den Wertebereich von Int, sollten keine Werte über rund 32000 vorkommen. Durch das berechenen mit rand() % (max-min) kommen die kleinen Werte eventuell schon mal doppelt so oft vor wie die großen. Der Rest kann also einfach nur Pech sein.
Die häufiger vorkommenden kleinen Werte kann man aber vermeiden.
Da ist aber eventuell auch ein Problem bei der binär-> dez. Wandlung: die 0967 sieht komisch aus.
Durch die vielen AD-Wandlungen ist diese Routine aber wirklich langsam.
malediction
26.01.2009, 09:04
ja stimmt. di 0967 ist mir noch gar nicht aufgefallen. hab eher darauf geachtet, dass sich nichts verdoppelt.
die geschwindigkeit ist ok, wenn asuro fliegen könnte, wär es vielleicht etwas langsam, aber für die bodenanwendungen reichts in der regel. man könnt das ganze sicher beschleunigen, indem man nur einen ADC ausliest. ich dachte mir jedoch, dass beim auslesen und mittelwertbilden von 5 ADCs ein schönerer zufallswert zustande kommt.
danke euch allen für eure hilfe. :)
malediction.
hallo leute, vielleicht kann mir von euch jemand helfen.
und zwar brauche ich eine methode für den asuro die mir zufallszahlen im wertebereich -45 bis 45 liefert!
hintergrund ist, ich will meinen asuro radom mäßig durch einen raum navigieren lassen (random walk).
die move methode etc ist alles schon fertig...
das einzige problem im moment sind noch die zufallszahlen :(
danke schonmal für die hilfe
malediction
23.05.2010, 14:50
Nimm doch einfach die obige Routine; als Grenzen 0 und 91 und dann "addiere" am Ende einen Offset von -45 dazu. Schon hast Du den gesuchten Bereich...
ich kanns grad leider nicht testen, weil ein freund grad den asuro hat.
das problem ist, ich hatte bei den letzten versuchen immer probleme mit dem datentyp uint8_t....den kennt mein compiler nich...
malediction
23.05.2010, 16:17
Dann probier einfach mal den normalen int aus ohne den ganzen Schnickschnack. Ich hab baer schon lang nichts mehr programmiert... Bin bisl raus aus der Materie.
"Try and error" heißt das Stichwort! ;)
Viel Erfolg!
>das problem ist, ich hatte bei den letzten versuchen immer probleme mit dem datentyp
>uint8_t....den kennt mein compiler nich...
Den kennt er schon, wenn Du an den Anfang
#include <inttypes.h>
setzt.
Vielen Dank für das tolle Programm und die schnellen Antworten. Jetzt funzt alles O:)
Powered by vBulletin® Version 4.2.5 Copyright ©2024 Adduco Digital e.K. und vBulletin Solutions, Inc. Alle Rechte vorbehalten.