; PicBot 2.05 ; ; approximate learning algorithm... ; while popping ; lastenvbad flag = badenv flag ; read senses into environment ; clear badenv flag ; if feelers touching or one dark bit set ; set badenv flag ; if action = lastmove ; if address <> environment ; boring = boring + 1 ; if boring >= boringthreshhold ; set badenv flag ; else ; boring = 0 ; if badenv or (not lastenvbad and reverse) ; if confidence > 0 ; decrement confidence ; memory(address) = action/confidence ; else ; if confidence < 3 ; increment confidence ; if confidence < 3 ; if direction = towards the light ; increment confidence ; memory(address) = action/confidence ; lastmove = action ; address = environment ; action/confidence = memory(address) ; if action not valid or confidence = 0 ; action = random ; confidence = 0 ; move robot according to action ; ; similar to David Heiserman's "beta class" machine ; (original algorithm used last move as part of the ; address, this version uses just the environment) ; ; Behavior... ; Rewards moves that result in good environment. ; Extra reward for movement towards light. ; Punishes moves that result in bad environment ; movement in reverse when good environment ; Environment broken down to six bits: 2 feelers, 2 bits ; to indicate greater light (never both on) and 2 bits ; to indicate darkness on that cell (also space-limited). ; Bad environment currently defined as either or both ; feelers touching or just one dark bit set, or same ; move made over and over in a changing environment ; (a convenient place to add extra conditions, setting ; the badenv flag triggers learning) ; ; Debugging... ; long flash = bad environment ; short flash = picking random move ; dumps memory at the end of pop cycle device PIC16C56, RC_OSC, WDT_ON, PROTECT_OFF reset startup org 000h pops = 15 ; pops per cycle popdur = 50 ; x ontime+offtime ontime = 1 ; duty cycle = ontime/(ontime+offtime) offtime = 1 optsleep = 00001111b ; during sleep - int rtc, max wdt optpause = 00001100b ; bits 0-2 control pause between pops maxsleep = 20 ; number of looks before waking in unchanging env. darkthresh = 30 ; when dark bits come on boringthresh = 6 ; max identical moves in changing environment ; port assignments... ; Ra0 - 24LC65 clock ; Ra1 - 24LC65 data ; Ra2 - 1381 output ; Ra3 - debug led ; Rb0 - Left Photocell ; Rb1 - Left Feeler ; Rb2 - Right Motor Forward (grounds blue when 1) ; Rb3 - Right Motor Reverse (grounds red when 1) ; Rb4 - Left Motor Forward (grounds red when 1) ; Rb5 - Left Motor Reverse (grounds blue when 1) ; Rb6 - Right Feeler ; Rb7 - Right Photocell powerpin = ra.2 leftfeelpin = rb.1 rightfeelpin = rb.6 forward = 01010100b ; predefined moves reverse = 01101000b ; bits 6,7 always "01" popleft = 01000100b ; bits 0,1 confidence=0 popright = 01010000b turnleft = 01100100b turnright = 01011000b backleft = 01100000b backright = 01001000b environment = 15h action = 16h popcnt = 17h flags = 18h badenv = flags.0 ; set if something bad in environment badmove = flags.1 ; set if something wrong with action sleeping = flags.2 ; set if robot was in deepsleep popping = flags.3 ; set if in a pop cycle lastenvbad = flags.4 ; last environment was bad temp = 19h temp1 = 1Ah LightL = 1Bh LightR = 1Ch lastmove = 1Dh boring = 1Eh statusLED = ra.3 portspeed = 25 ; memory dump bit delay ; EEPROM routines modified from Microchip application note AN558 ; bitin and bitout routines are coded in-line to permit write and ; read routines to be called without overflowing stack. statby = 03h eeport = 05h ; port A eeprom = 0Ah bycnt = 0Bh addr = 0Ch addr1 = 0Dh datai = 0Eh datao = 0Fh txbuf = 10h count = 11h bcount = 12h loops = 13h loops2 = 14h di = 3 do = 2 sdata = 1 sclk = 0 outmask = 11110100b inmask = 11110110b ; wait routine - waits approx loops milliseconds wait movlw 75 movwf loops2 wait2 nop nop nop decfsz loops2 goto wait2 decfsz loops goto wait clr wdt ; convenient ret ; generate start bit bstart bsf eeport, sdata movlw outmask tris eeport bcf eeport, sclk nop bsf eeport, sclk nop nop bcf eeport, sdata nop nop bcf eeport, sclk nop ret ; generate stop bit bstop bcf eeport, sdata movlw outmask tris eeport bcf eeport, sdata nop nop bsf eeport, sclk nop nop bsf eeport, sdata nop bcf eeport, sclk nop nop ret ; transmit byte in txbuf tx movlw 8 movwf count txlp bcf eeprom, do btfsc txbuf, 7 bsf eeprom, do movlw outmask tris eeport btfss eeprom, do goto txlp1 bsf eeport, sdata goto txlp2 txlp1 bcf eeport, sdata txlp2 bsf eeport, sclk nop nop nop bcf eeport, sclk rlf txbuf decfsz count goto txlp bsf eeprom, di movlw outmask tris eeport bsf eeport, sclk nop nop btfss eeport, sdata bcf eeprom, di bcf eeport, sclk ret ; receive byte into datai rx clrf datai movlw 8 movwf count bcf statby,0 rxlp rlf datai bsf eeprom, di movlw outmask tris eeport bsf eeport, sdata bsf eeport, sclk nop nop nop btfss eeport, sdata bcf eeprom, di bcf eeport, sclk btfsc eeprom, di bsf datai,0 decfsz count goto rxlp ret ; get byte from EEPROM into datai rbyte call bstart movlw 10100000b movwf txbuf call tx movf addr1,w ; high address movwf txbuf call tx movf addr,w ; low address movwf txbuf call tx call bstart movlw 10100001b movwf txbuf call tx call rx call bstop ret ; write byte in datao to EEPROM wbyte call bstart movlw 10100000b movwf txbuf call tx movf addr1,w movwf txbuf call tx movf addr,w movwf txbuf call tx movf datao,w movwf txbuf call tx call bstop movlw 10 movwf loops call wait ret ; end cryptic microchip instructions ; start parallax instructions ; get environment byte subroutine leftfeeler = environment.0 rightfeeler = environment.1 lightonleft = environment.2 lightonright = environment.3 darkonleft = environment.4 darkonright = environment.5 getenvironment clr environment ; start all bits clear sb leftfeelpin ; set feeler bits setb leftfeeler ; pins low=touching sb rightfeelpin ; env high=touching setb rightfeeler ; read photocells (hard coded to bits 0 and 7) mov lightL, #127 ; max light level mov !rb, #11000010b ; make L photobit an out clr rb ; short out cap mov loops, #10 ; for few ms call wait mov !rb, #11000011b ; make ins again ploopL jb rb.0, readR ; go on when input goes high djnz lightL, ploopL ; loop if still above 0 readR mov lightR, #127 mov !rb, #01000011b clr rb mov loops, #10 call wait mov !rb, #11000011b ploopR jb rb.7, readend djnz lightR, ploopR readend ; mask noise and lightL, #01111000b and lightR, #01111000b ; set environment bits csbe lightL, lightR ; if left > right setb lightonleft ; set lightonleft flag csbe lightR, lightL ; if right > left setb lightonright ; set lightonright flag csa lightL, #darkthresh ; if left <= darkthresh setb darkonleft ; set darkonleft flag csa lightR, #darkthresh ; if right <= darkthresh setb darkonright ; set darkonright flag ; set bad-environment flag clrb badenv mov w, environment and w, #00000011b ; isolate feelers sz ; if either touching setb badenv ; set badenv flag mov temp1, environment and temp1, #00110000b ; isolate dark bits jz gend ; skip if neither dark cse temp1, #00110000b ; skip if both dark setb badenv ; just one, set badenv flag gend ret ; sub to validate action byte and set badmove flag checkaction clrb badmove mov temp1, action and temp1, #11000000b cse temp1, #01000000b setb badmove ; invalid memory jnb action.2, ca1 jnb action.3, ca1 setb badmove ; smoke on right ca1 jnb action.4, ca2 jnb action.5, ca2 setb badmove ; smoke on left ca2 mov w, action and w, #00111100b snz setb badmove ; no movement ret ; table of valid moves getmove jmp pc+w retw forward retw reverse retw popleft retw popright retw turnleft retw turnright retw backleft retw backright ;debug subs shortflash setb ra.3 mov loops, #30 call wait clrb ra.3 mov loops, #220 call wait ret longflash setb ra.3 mov loops, #200 call wait clrb ra.3 mov loops, #50 call wait ret ;*********** main code here startup mov OPTION, #optpause ; short delay mov !ra, #00000111b ; 2=1381in 1=eedata 0=eeclk (set to hi-z) mov !rb, #11000011b ; 0,7=photo ins 1,6=feeler ins 2-5 motor outs clr ra ; clear led out (ra.3) clr rb ; clear motor outs mov addr1, #00011111b ; set to top of eeprom jnb statby.3, rest ; if not waking up from sleep jnb powerpin, rest ; and power not low call getenvironment ; initialize variables clrb lastenvbad clrb popping clr action clr addr clr lastmove clr boring rest jb popping, nextpop ; jump if in middle of a pop cycle mov popcnt, #maxsleep ; set up max delay before waking jb powerpin, watch ; become alert if enough power setb sleeping ; otherwise set deepsleep flag mov OPTION, #optsleep ; long duration (!OPTION for SPASM) sleep ; sleep and keep on charging watch ; charge until something changes or counter runs out jnb sleeping, wakeup ; if not in deepsleep, continue on mov temp, environment call getenvironment ; look around jb badenv, wakeup ; wake if bad environment cjne temp, environment, wakeup ; wake if change in env. decsz popcnt ; wake if count runs out sleep ; else sleep and charge wakeup mov popcnt, #pops ; set up pop counter clrb sleeping ; not sleeping anymore mov OPTION, #optpause ; shorten delay (!OPTION for SPASM) poploop ; learn about surroundings and store in eeprom ; eeprom memory layout... ; 7 6 5 4 3 2 1 0 a = motor actions m = set to "01" if ; m m a a a a c c c = confidence 0-3 a valid memory movb lastenvbad, badenv ; save bad flag call getenvironment ; look around snb badenv call longflash ; flash if bad environment ; boring detection mov temp, action and temp, #00111100b ; compare just action bits cjne lastmove, temp, rstbc ; if same move cje environment, addr, bdend ; if env. changed inc boring ; boring=boring+1 csb boring, #boringthresh ; if boring>=toomany setb badenv ; set badenv flag jmp bdend ; else rstbc clr boring ; boring=0 bdend ; update confidence of last move according to success jb badenv, decconf ; decrement last if bad environment jb lastenvbad, incconf ; increment if last environment bad mov w, action ; (but this one isn't) and w, #00101000b ; check for reverse bits jnz decconf ; decrement if backing up when good incconf mov temp, action ; good move, increment and temp, #00000011b cje temp, #00000011b, evaldone ; no change if max confidence inc action ; increment confidence mov temp, action ; learned photovore behavior and temp, #00000011b ; reward if going to light cje temp, #00000011b, wr2ee jnb lightonleft, lpvb2 jnb action.2, wr2ee jb action.5, wr2ee lpvb2 jnb lightonright, lpvb3 jnb action.4, wr2ee jb action.3, wr2ee lpvb3 inc action wr2ee mov datao, action ; write action call wbyte ; to eeprom (addr) jmp evaldone decconf mov w, action and w, #00000011b jz evaldone ; no change if conf already 0 dec action ; decrement confidence jmp wr2ee ; jmp to write evaldone mov lastmove, action ; save last move and lastmove, #00111100b ; minus extra data ; access memory from eeprom mov addr, environment call rbyte mov action, datai ; action = memory call checkaction jb badmove, rmove ; select random if bad move mov w, action and w, #00000011b jnz move ; confidence > 0, go with move ; select random move rmove call shortflash ; short flash when picking random ; variable delay based on light mov !rb, #11000010b ; discharge left photo input cap clr rb mov loops, #10 call wait mov !rb, #11000011b mov temp, #255 ; set max loops vdelay jb rb.0, loadrnd ; go if photo input high djnz temp, vdelay ; loop until temp = 0 loadrnd mov temp, rtcc ; who knows what and temp, #00000111b ; mask off all but bottom 3 mov w, temp ; convert 0-7 into call getmove ; valid predefined move mov action, w ; get move in action ; move robot with move in action variable ; (bits 2-5 to motors, rest ignored) move call checkaction ; validate action jb badmove, drstop ; stop if forbidden move mov temp, #popdur ; how much drloop mov rb, action mov loops, #ontime call wait clr rb mov loops, #offtime call wait djnz temp, drloop ; loop until done drstop setb popping ; tell startup we're in a move cycle sleep ; sleep between pops to save power nextpop ; here if wakes with popping set clrb popping ; end of move cycle djnz popcnt, poploop ; next pop mov OPTION, #optsleep ; long duration (!OPTION for SPASM) ; dump contents of memory to LED then go to sleep ; each byte ...1-0-m0-m1-m2-m3-m4-m5-m6-m7-1-0-0... mov temp, addr ; save current addr mov addr, #0 ; start at 0 dumplp call rbyte ; get byte setb statusLED mov loops,#50 ; '1' start bit Await mov loops2,#portspeed Await2 djnz loops2, Await2 djnz loops, Await clrb statusLED mov loops,#50 ; '0' start bit Bwait mov loops2,#portspeed Bwait2 djnz loops2, Bwait2 djnz loops, Bwait mov count, #8 ; 8 data bits LEDfla2 clrb statusLED snb datai.0 setb statusLED ; 1 if lsb set clr wdt ; don't bomb mov loops,#50 Cwait mov loops2,#portspeed Cwait2 djnz loops2, Cwait2 djnz loops, Cwait rr datai ; next bit djnz count, LEDfla2 setb statusLED mov loops,#50 ; '1' stop bit Dwait mov loops2,#portspeed Dwait2 djnz loops2, Dwait2 djnz loops, Dwait clrb statusLED mov loops, #100 ; 2 '0' stop bits Ewait mov loops2,#portspeed Ewait2 djnz loops2, Ewait2 djnz loops, Ewait inc addr ; next address jnb addr.6, dumplp ; loop if under 64 mov addr, temp ; restore old address sleep ; rest and charge ; end