PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : TicTacToe/Reaktivlicht - Led als Sensor



KR-500
20.12.2009, 17:42
Hallo libes Forum,

ich wollte euch hier jetzt mal mein neustes fertiges Projekt vorstellen. Es ist ein TicTacToe geworden, das man bedienen kann, in dem man auf die LEDs "zeigt". Das beruht auf dem Prinzip eines Reaktivlichtes, dass mit einer LED funktioniert. http://reaktivlicht.de/kochbuch.pdf
http://blog.makezine.com/order.jpg
(Quelle:http://blog.makezine.com/archive/2006/06/led_touch_sensor.html)
Ich brauche also keine Phototransistoren oder LDRs. Als Computer-Gegner hab ich mir den NegaMax-Akgorithmus ausgesucht, den hab ich allerdings abgetippt und auf meine Bedürfnisse angepasst. Der Algorithmus geht systematisch alle Eventualitäten durch und sucht die Beste aus, deswegen kann man gegen ihn nicht gewinnen, das Beste was man schaffen kann ist Unentschieden.
Nach dem Einschalten kann man wählen ob man gegen den Computer oder einen anderen Menschen spielen will. Am besten schaut ihr euch das Video an.
http://www.youtube.com/watch?v=UcPQEy6P6zc
Ich benutze übrigens einen Atmega32 und Duo-LEDs von Pollin. Der Atmega ist in C programmiert. Ich freue mich auf eure Kritik, Anregungen und Verbesserungsvorschläge.
Bei Intresse kann ich euch weitere Links zum Thema "Led als Sensor" geben.

KR-500

asurofreak.
20.12.2009, 17:53
hi KR-500!

das schaut ma cool aus!
das wirkt wie son touchscreen, echt klever
das signal der led muss man dann aber verstärken, oder?, denn ich hab mir mal so ne schaltung gebaut, wenn man eine led unter ner lichtquelle hielt, fing die andere an zu leuchten, ich brauchte dafür 2 transistoren.

MfG

TomEdl
20.12.2009, 17:54
Hallo!

LED's als Sensoren zu nutzen, habe ich auch schon öfters gemacht. Ich habe mal eine Uhr gebaut, bei der die Status-LED neben der Funktion als Statusanzeige die Helligkeit misst und dementsprechend die Displayhelligkeit anpasst.

Momentan arbeite ich an einem TicTacToe auf einem Grafik-LCD. allerdings noch ohne KI.

Aber auf die Idee, TicTacToe und Reaktivlicht zu kombinieren, bin ich noch nicht gekommen. Hut ab, es ist wirklich ein tolles Projekt von dir geworden...

Grüße
Thomas

TomEdl
20.12.2009, 18:01
hi KR-500!

das schaut ma cool aus!
das wirkt wie son touchscreen, echt klever
das signal der led muss man dann aber verstärken, oder?, denn ich hab mir mal so ne schaltung gebaut, wenn man eine led unter ner lichtquelle hielt, fing die andere an zu leuchten, ich brauchte dafür 2 transistoren.


Es ist nicht notwendig, da irgendwas zu verstärken. Es wird lediglich für einige ms die Led "geladen" (wie ein Kondensator) und dann geprüft, wie schnell sie sich wieder entlädt.

Grüße
Thomas

asurofreak.
20.12.2009, 18:34
was man mit LED´s alles so machen kann...das sowas geht wusste ich nun noch net, les mir gerade die pdf-datei durch

MfG

Virus
20.12.2009, 18:37
feine sache, ich hatte auch an ein tictactoe auf meinem roboter gedacht, hatte aber tasten im blickfeld. duoleds wollte ich auch nutzen, hab das projekt aber bis jetzt nur hardware mäßig geplant, weil ich mir noch zuviele gedanken über den code gemacht habe, ich habbe selber und an einem wochenende mal tictactoe entschlüsselt, kann jetzt nicht mehr gegen freunde verliehren, wenn ich anfange verliehren die immer, jetzt will keiner gegen mich spielen :-(

kannst du deinen code mal als hilfestellung zur übersetztung von meiner strategie in ein C-programm posten?

ist eigendlich perfekt, der RP6 hat ja auch den Atmega32

KR-500
20.12.2009, 19:31
Hallo zusammen,

danke für die netten Kommentare. Die AI ist ja wie schon erwähnt ein NegaMax-Algorithmus. Da der Code ca. 1000 Zeilen umfasst kann ich ihn nicht ganz posten aber ich hab die wichtigsten Stellen gepostet. Wenn du oder jemand anderes Intresse am gesamten Code hat schicke ich ihm gerne eine PM. Es tut mir leid wenn der Code an einigen Stellen unleserlich ist, ich sollte ihn bei Gelegenheit noch mal überarbeiten, weil ich relativ unsauber programmiert habe. Der Algorithmus ist ja wie schon gesagt abgetippt. Der NegaMax-Algorithmus ist nur einer von vielen, den man verwenden kann, wikipedia liefert eine ganz gute Übersicht, du musst unten in den links gucken, d stehen halt noch die anderen:
http://de.wikipedia.org/wiki/Minimax-Algorithmus
Hier sind noch ein paar links die mir geholfen haben den Algorithmus zu verstehen:
http://www.aihorizon.com/essays/basiccs/trees/minimax.htm
http://en.literateprograms.org/Tic_Tac_Toe_%28C_Plus_Plus%29
http://www.experts-exchange.com/Programming/Game/AI_Physics/Q_21994259.html
http://www.gm.fh-koeln.de/~hk/lehre/ala/ws0506/Praktikum/Projekt/A_rot/060125_Proj_AlphaBetaPruning_Norvig_Russell_FINAL. pdf
http://www.osix.net/modules/article/?id=801
http://www.chessandpoker.com/tic_tac_toe_strategy.html
http://www.angelfire.com/wi/wickmann/cs002.html
Die sind zwar alle bis auf einen auf Englisch aber du wirst damit gut klargekommen. Allerdings beschäftigt sich nur der letzte Link explizit mit dem "NegaMax-Algorithmus", von dem hab ich auch abgetippt. Hier ist mein Code:

#include <avr/io.h>
#include "USART_Util.h"
#include <util/delay.h>
#include <avr/interrupt.h>
#include <stdlib.h>
unsigned char Board[3][3]={{2,2,2},
{2,2,2},
{2,2,2}};
int ply=0;
int toggle=1,win=0;
unsigned char a[9] = {0,0,0,0,0,0,0,0,0};
unsigned char led[9] = {0,0,0,0,0,0,0,0,0};
unsigned int c_flag,npc=2;
volatile unsigned char k=100;
unsigned char OtherPlayer(unsigned char inPlayer)
{
if(inPlayer==1)return 0;
return 1;
}

char doWin(unsigned char inBoard[3][3],unsigned char inPlayer)
{

if((inBoard[0][0]==inPlayer)&(inBoard[0][1]==inPlayer)&(inBoard[0][2]==inPlayer))
{
return 1;
}
if((inBoard[0][0]==inPlayer)&(inBoard[1][0]==inPlayer)&(inBoard[2][0]==inPlayer))
{
return 1;
}

if((inBoard[2][0]==inPlayer)&(inBoard[2][1]==inPlayer)&(inBoard[2][2]==inPlayer))
{
return 1;
}

if((inBoard[2][2]==inPlayer)&(inBoard[1][2]==inPlayer)&(inBoard[0][2]==inPlayer))
{
return 1;
}
if((inBoard[1][0]==inPlayer)&(inBoard[1][1]==inPlayer)&(inBoard[1][2]==inPlayer))
{
return 1;
}
if((inBoard[0][1]==inPlayer)&(inBoard[1][1]==inPlayer)&(inBoard[2][1]==inPlayer))
{
return 1;
}

if((inBoard[0][0]==inPlayer)&(inBoard[1][1]==inPlayer)&(inBoard[2][2]==inPlayer))
{
return 1;
}

if((inBoard[0][2]==inPlayer)&(inBoard[1][1]==inPlayer)&(inBoard[2][0]==inPlayer))
{
return 1;
}
return 0;
}
int NegaMax(unsigned char inBoard[3][3], unsigned char inPlayer)
{

if(doWin(inBoard,inPlayer)==1)
{
return 1;
}
if(doWin(inBoard,OtherPlayer(inPlayer))==1)
{
return -1;
}
int best=-2;
int i;
for(i=0;i<3;i++)
{
int j;
for(j=0;j<3;j++)
{
if(inBoard[i][j]==2)
{
inBoard[i][j]=inPlayer;
int value=-NegaMax(inBoard,OtherPlayer(inPlayer));
inBoard[i][j]=2;
if(value>best)
{
best=value;
}
}
}
}
if(best==-2)
{
return 0;
}

return best;

}
void moveComputer(unsigned char inBoard[3][3])
{
if(ply<4){
ply++;
int i;
int j;

int best=0;
unsigned char besti=0;
unsigned char bestj=0;
for(i=0;i<3;i++)
{
for(j=0;j<3;j++)
{
if(Board[i][j]==2)
{
Board[i][j]=1;
best=-NegaMax(inBoard,0);
Board[i][j]=2;

besti=i;
bestj=j;

}
}
}

for(i=0;i<3;i++)
{
for(j=0;j<3;j++)
{
if(inBoard[i][j]==2)
{
inBoard[i][j]=1;
int score=-NegaMax(inBoard,0);
if(score>best)
{
besti=i;
bestj=j;
best=score;
}
inBoard[i][j]=2;
}
}
}
Board[besti][bestj]=1;
}
}





void pc (void){
int q,u;
c_flag=1;
for(unsigned int e=0;e<5;e++){
while(c_flag){
if(led[0]==0){
PORTA |= (1<<PA0);
PORTB &=~ (1<<PB0);
}
if(led[1]==0){
PORTA |= (1<<PA1);
PORTB &=~ (1<<PB1);
}
if(led[2]==0){
PORTA |= (1<<PA2);
PORTB &=~ (1<<PB2);
}
if(led[3]==0){
PORTA |= (1<<PA3);
PORTB &=~ (1<<PB3);
}
if(led[4]==0){
PORTA |= (1<<PA4);
PORTB &=~ (1<<PB4);
}
if(led[5]==0){
PORTA |= (1<<PA5);
PORTB &=~ (1<<PB5);
}
if(led[6]==0){
PORTA |= (1<<PA6);
PORTB &=~ (1<<PB6);
}
if(led[7]==0){
PORTA |= (1<<PA7);
PORTB &=~ (1<<PB7);
}
if(led[8]==0){
PORTC |= (1<<PC7);
PORTD &=~ (1<<PD5);
}
_delay_us(10);
if(led[0]==0){
DDRA &=~ (1<<PA0);
PORTA &=~ (1<<PA0);
}
if(led[1]==0){
DDRA &=~ (1<<PA1);
PORTA &=~ (1<<PA1);
}
if(led[2]==0){
DDRA &=~ (1<<PA2);
PORTA &=~ (1<<PA2);
}
if(led[3]==0){
DDRA &=~ (1<<PA3);
PORTA &=~ (1<<PA3);
}
if(led[4]==0){
DDRA &=~ (1<<PA4);
PORTA &=~ (1<<PA4);
}
if(led[5]==0){
DDRA &=~ (1<<PA5);
PORTA &=~ (1<<PA5);
}
if(led[6]==0){
DDRA &=~ (1<<PA6);
PORTA &=~ (1<<PA6);
}
if(led[7]==0){
DDRA &=~ (1<<PA7);
PORTA &=~ (1<<PA7);
}
if(led[8]==0){
DDRC &=~ (1<<PC7);
PORTC &=~ (1<<PC7);
}
_delay_ms(k-75);
a[2] = (PINA & (1<<PINA2));
a[3] = (PINA & (1<<PINA3));
a[5] = (PINA & (1<<PINA5));
_delay_ms(10);
a[4] = (PINA & (1<<PINA4));
_delay_ms(65);
a[0] = (PINA & (1<<PINA0));
a[1] = (PINA & (1<<PINA1));
a[6] = (PINA & (1<<PINA6));
a[7] = (PINA & (1<<PINA7));
a[8] = (PINC & (1<<PINC7));

DDRA = 0xFF;
DDRC = 0xFF;
if((a[0]!=0)&&(led[0]==0)){
PORTB |= (1<<PB0);
c_flag = 0;
c[0]=0;
Board[0][0]=0;
led[0]=1;
_delay_ms(120);
}
if(a[1]!=0){
PORTB |= (1<<PB1);
c_flag = 0;
c[1]=0;
Board[0][1]=0;
led[1]=1;
_delay_ms(120);
}
if(a[2]!=0){
PORTB |= (1<<PB2);
c_flag = 0;
c[2]=0;
Board[0][2]=0;
led[2]=1;
_delay_ms(120);
}
if(a[3]!=0){
PORTB |= (1<<PB3);
c_flag = 0;
c[3]=0;
Board[1][0]=0;
led[3]=1;
_delay_ms(120);
}
if(a[4]!=0){
PORTB |= (1<<PB4);
c_flag = 0;
c[4]=0;
Board[1][1]=0;
led[4]=1;
_delay_ms(120);
}
if(a[5]!=0){
PORTB |= (1<<PB5);
c_flag = 0;
c[5]=0;
Board[1][2]=0;
led[5]=1;
_delay_ms(120);
}
if(a[6]!=0){
PORTB |= (1<<PB6);
c_flag = 0;
c[6]=0;
Board[2][0]=0;
led[6]=1;
_delay_ms(120);
}
if(a[7]!=0){
PORTB |= (1<<PB7);
c_flag = 0;
c[7]=0;
Board[2][1]=0;
led[7]=1;
_delay_ms(120);
}
if(a[8]!=0){
PORTD |= (1<<PD5);
c_flag = 0;
c[8]=0;
Board[2][2]=0;
led[8]=1;
_delay_ms(120);
}
}
c_flag=1;
moveComputer(Board);
led_2();
if_win();
if(win==1){
for(q=0;q<3;q++){
for(u=0;u<3;u++){
Board[q][u]=2;
}
}
ply = 0;
win=0;
break;
}
}
}
Die wichtigsten Funktionen sind OtherPLayer, doWin, NegaMax, moveComputer und pc. USART_Util.h ist eine selbst geschriebene Binliothek, die die wichtigsten USART-Funktionen beinhaltet.

KR-500

Virus
20.12.2009, 20:06
mein rp6 wird nur gegen menschen (nicht mensch, mensch) spielen.
interesse an der PN oder email an timok95@gmail.com hätte ich aber trotzdem.

Eimer22
21.12.2009, 16:38
hmm. Wie hast du 9 Duo-LEDs an einem AVR angeschlossen?
Das würde mich mal interessieren. Schieberegister sehe ich auf dem Bild nicht.

Virus
21.12.2009, 16:57
es gibt duo led, die eigendlich aus zwei leds bestehen, die in einem gehäuse vereint sind, und die mit ihren kathoden verbunden sind.
dann kann man sie einfach wie ein 6x3 ledfeld multiplexen.

KR-500
21.12.2009, 18:54
Hallo,

ich hatte geplant die LEDs zu multiplexen, es stellte sich aber heraus, dass die LEDs eine Entlade Zeit von bis zu 200 millisekunden benötigen. Deshalb blieb mir nichts anderes übrig als jede Kathode/Anode an einen Pin vom µc zu löten. Der ATmega32 hat 32 I/O Pins, und 9*3, weil jede LED (http://www.pollin.de/shop/dt/MjI3OTc4OTk-/Bauelemente/Aktiv/LEDs/Duo_LED.html) 3 Anschlüsse hat bleiben also sogar noch 5 Pins übrig. Vielleicht ist das ein bisschen mit Kanonen auf Spatzen geschossen einen ATmega32 für neun LEDs zu verwenden aber in diesem Fall ging es nicht anders.

KR-500

radbruch
22.12.2009, 10:08
Hallo

Über diesen helligkeitsempfindlichen Effekt von LEDs bin ich auch schon gestolpert. Hier misst mein RP6 die Helligkeit mit seinen ACS-IR-LEDs:
https://www.roboternetz.de/phpBB2/zeigebeitrag.php?t=39560

Gruß

mic

Ozzy
22.12.2009, 15:36
Moin,

mich würde noch einmal Dein Code interessieren, mit dem Du die LEDs abfragst... Kannst Du da vielleicht mal ein Fragment für z.B. eine LED reinstellen?

MfG und vielen Dank, Ozzy

TomEdl
22.12.2009, 20:00
Also ich besitze einen Code für BASCOM. Wenns jemanden interessiert, stell ich in rein.

Grüße
Thomas

KR-500
22.12.2009, 20:58
Hallo,

hier ist einmal ein Code mit dem man die LEDs abfragen kann:

while(1){
if(led[0]==0){
PORTA |= (1<<PA0);
PORTB &=~ (1<<PB0);
}
if(led[1]==0){
PORTA |= (1<<PA1);
PORTB &=~ (1<<PB1);
}
if(led[2]==0){
PORTA |= (1<<PA2);
PORTB &=~ (1<<PB2);
}
if(led[3]==0){
PORTA |= (1<<PA3);
PORTB &=~ (1<<PB3);
}
if(led[4]==0){
PORTA |= (1<<PA4);
PORTB &=~ (1<<PB4);
}
if(led[5]==0){
PORTA |= (1<<PA5);
PORTB &=~ (1<<PB5);
}
if(led[6]==0){
PORTA |= (1<<PA6);
PORTB &=~ (1<<PB6);
}
if(led[7]==0){
PORTA |= (1<<PA7);
PORTB &=~ (1<<PB7);
}
if(led[8]==0){
PORTC |= (1<<PC7);
PORTD &=~ (1<<PD5);
}
_delay_us(10);
if(led[0]==0){
DDRA &=~ (1<<PA0);
PORTA &=~ (1<<PA0);
}
if(led[1]==0){
DDRA &=~ (1<<PA1);
PORTA &=~ (1<<PA1);
}
if(led[2]==0){
DDRA &=~ (1<<PA2);
PORTA &=~ (1<<PA2);
}
if(led[3]==0){
DDRA &=~ (1<<PA3);
PORTA &=~ (1<<PA3);
}
if(led[4]==0){
DDRA &=~ (1<<PA4);
PORTA &=~ (1<<PA4);
}
if(led[5]==0){
DDRA &=~ (1<<PA5);
PORTA &=~ (1<<PA5);
}
if(led[6]==0){
DDRA &=~ (1<<PA6);
PORTA &=~ (1<<PA6);
}
if(led[7]==0){
DDRA &=~ (1<<PA7);
PORTA &=~ (1<<PA7);
}
if(led[8]==0){
DDRC &=~ (1<<PC7);
PORTC &=~ (1<<PC7);
}
_delay_ms(k-75);
a[2] = (PINA & (1<<PINA2));
a[3] = (PINA & (1<<PINA3));
a[5] = (PINA & (1<<PINA5));
_delay_ms(10);
a[4] = (PINA & (1<<PINA4));
_delay_ms(65);
a[0] = (PINA & (1<<PINA0));
a[1] = (PINA & (1<<PINA1));
a[6] = (PINA & (1<<PINA6));
a[7] = (PINA & (1<<PINA7));
a[8] = (PINC & (1<<PINC7));

DDRA = 0xFF;
DDRC = 0xFF;
if((a[0]!=0)){
if(c_flag){
PORTB |= (1<<PB0);
c_flag = 0;
c[0]=0;
}
else{
PORTC |= (1<<PC6);
c_flag = 1;
c[0]=1;
}
led[0]=1;
_delay_ms(120);
}
if(a[1]!=0){
if(c_flag){
PORTB |= (1<<PB1);
c_flag = 0;
c[1]=0;
}
else{
PORTC |= (1<<PC5);
c_flag = 1;
c[1]=1;
}
led[1]=1;
_delay_ms(120);
}
if(a[2]!=0){
if(c_flag){
PORTB |= (1<<PB2);
c_flag = 0;
c[2]=0;
}
else{
PORTC |= (1<<PC4);
c_flag = 1;
c[2]=1;
}
led[2]=1;
_delay_ms(120);
}
if(a[3]!=0){
if(c_flag){
PORTB |= (1<<PB3);
c_flag = 0;
c[3]=0;
}
else{
PORTC |= (1<<PC3);
c_flag = 1;
c[3]=1;
}
led[3]=1;
_delay_ms(120);
}
if(a[4]!=0){
if(c_flag){
PORTB |= (1<<PB4);
c_flag = 0;
c[4]=0;
}
else{
PORTC |= (1<<PC2);
c_flag = 1;
c[4]=1;
}
led[4]=1;
_delay_ms(120);
}
if(a[5]!=0){
if(c_flag){
PORTB |= (1<<PB5);
c_flag = 0;
c[5]=0;
}
else{
PORTC |= (1<<PC1);
c_flag = 1;
c[5]=1;
}
led[5]=1;
_delay_ms(120);
}
if(a[6]!=0){
if(c_flag){
PORTB |= (1<<PB6);
c_flag = 0;
c[6]=0;
}
else{
PORTC |= (1<<PC0);
c_flag = 1;
c[6]=1;
}
led[6]=1;
_delay_ms(120);
}
if(a[7]!=0){
if(c_flag){
PORTB |= (1<<PB7);
c_flag = 0;
c[7]=0;
}
else{
PORTD |= (1<<PD7);
c_flag = 1;
c[7]=1;
}
led[7]=1;
_delay_ms(120);
}
if(a[8]!=0){
if(c_flag){
PORTD |= (1<<PD5);
c_flag = 0;
c[8]=0;
}
else{
PORTD |= (1<<PD6);
c_flag = 1;
c[8]=1;
}
led[8]=1;
_delay_ms(120);
}
if_win();

}
}


man muss am Anfang noch led[9],c[9],a[9] und c_flag declarieren. Die ganze abfrage mit "if(led[0]==0){...} braucht man, damit bereits angetippte LEDs nicht mehr aus gehen. An PORTA liegen übrigens die Kathoden und an PORTB und PORTC die Anoden. if_win() ist eine Funktion die nachguckt ob schon jemand gewonnen hat.
Der erste Link in meinem ersten Posting enthält eine gute Erklärung in C, man muss die Abfrage nur noch umkehren und vielleicht mit einer kleinen Funktion die Umgebungshelligkeit abfragen, so wie hier zum Beispiel:

unsigned char led_abfrage (unsigned char zeit) {
PORTA |= (1<<PA1);
PORTB &=~ (1<<PB1);
_delay_us(10);
DDRA &=~ (1<<PA1);
PORTA &=~ (1<<PA1);
_delay_ms(zeit);
DDRA |= (1<<PA1);
return (PINA & (1<<PINA1));
}
void init (void){
while(led_abfrage(k))if(k++>=175)break;
k=k+80;
USART_Transmit(k);
}
Später addiere ich zu k nochmal 80, weil ich Festgestellt
Hier sind noch ein paar Links zum Thema "LED als Sensor", viele Leider auf Englisch:
http://forums.linear1.org/index.php?PHPSESSID=67b425adb819f492b8c262f54aeab6 ce&/topic,445.0.html
http://forums.linear1.org/index.php/topic,469.0.html
http://www.mikrocontroller.net/topic/56759
http://blog.makezine.com/archive/2006/06/led_touch_sensor.html
http://cs.nyu.edu/~jhan/ledtouch/index.html
http://www.merl.com/publications/TR2003-035/ <- Das ist der Technische Bericht
http://projects.dimension-x.net/technology-and-projects/ledsensors

@TomEdl

Find ich ne gut Idee, vielleicht mag ja auch noch jemand ein Beispiel Programm für eine LED in Assembler schreiben und hier posten.

KR-500

Ozzy
23.12.2009, 07:10
Warum addiertst Du die 80 drauf? Irgendwie fehlt da was in Deinem Text...

KR-500
23.12.2009, 13:06
Hallo

Sorry, passiert mir normalerweise nicht, dass ich Sätze nicht vollende. Was ich Festgestellt habe ist, dass die LEDs sehr empfindlich sind und wenn ich zu k nochmal 80 dazu addiere werden die LEDs unempfindlicher.

KR-500