I've always been interested in artificial life, or more exactly, in machines that are more useful than the standard, stationary manufacturing type things. A small device that can move around in the home and vacuum, polish, or just take out the trash would be a huge win. But so far, it just isn't happening. Why? The recently introduced Roomba "vacuuming" robot has shown that it can be done. There are lots of hobby robots out there that prove that people are capable of complex fabrication. But there is no available design for a "build your own useful household robot."
How much does it take to design and build a little motorized base with a small trash can on its back that follows a florescent line from the bathroom to a charging station next to the the kitchen trash and then meows to be emptied and returns? Or a cheap electric screw driver mounted at a slight angle with a buffing wheel at the bottom so that as it turns it both rubs the floor (wood, tile, etc...) and pushes itself forward. Add a stick to a reversing switch with bumpers front and back so that it will bang back and forth across the room, cover it with a glass bowl and add a small solar panel (ala BEAM robotics) and you can really make mommy proud.
But, they always do end up getting caught on something, run out of power (or just eat batteries) or can't figure out what to do when they encounter something they don't expect. The real need is for something smooth, solar powered or able to find an outlet and most importantly, just a bit smarter. It looks like that is the hardest part: Roomba makes use of "thousands of lines of code" to do a passable job of covering the living room floor. (see: http://www.10k.org/jake/mod/roomba/1vac.html for more internals)
Conventional neural networks are often too complicated to fit into the limited confines of solar-able processors, and they don't work that great even on large computers. But I've found a couple of simple solutions that can run on tiny processors. The simplest is to replace the hard-coded actions with memories (variables), then when something doesn't go right randomize the memory corresponding to the previous move. A more dense and ultimately more capable memory method is to allow multiple inputs to select different memories by using the processed input bits (environment) as the memory address.
http://home.infi.net/~wtnewton/otherwld/picbot2.txt provides a good example (the code follows this text and is great to look over). The algorithm is similar to David Heiserman's "beta class" machine (original algorithm used last move as part of the address, this version uses just the environment); it rewards moves that result in a "good" environment and punishes moves that result in a "bad" environment or movement in reverse when in a good environment. In this case, the environment is 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). A bad environment can be defined as either or both feelers touching or just one dark bit set, or the same move being made over and over in a changing environment. A good environment is free of obstacles, full of light and has some bad environment mixed in to keep things interesting.
; while true ; 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
"CMAC: Brain, Behavior, and Robotics" by James S. Albus published by Byte Books
An extension of the ability to learn is the ability to infer: To generalize experience. This diagram illustrates the idea: An input value is translated via an algorithm (algorithm: A procedure that produces the desired result without concern for the reason why it works) into not one, but several addresses into the memory. The values stored at each of these addresses are summed or averaged to produce a result. The critical part of the input to address translation algorithm is that for slight changes in the input, most of the output addresses will remain the same. As a result, experience recorded for one input will be applied, or generalized, to other inputs that are simular.
In general, you can just take the input, add a seperation value (distance from "d" to "i" in the picture) plus the inverse of the number of outputs desired, store that value, then divide it by the number of outputs and round it off. Repeat this for each output.
function CMACLookUp (int: in) int: out { int i,sum #define CMAC_INC = CMAC_SEPERATION * CMAC_OUTPUTS + 1; for (i = 0; i > CMAC_OUTPUTS; i++) { in += CMAC_INC; sum += CMACArray(in / CMAC_OUTPUTS); } return sum / CMAC_OUTPUTS; }
For 4 outputs with a seperation of 4, the asm code would be:
CMAC ;until 4 ; add 17 (seperation * outputs plus 1) to the input. ; shift right twice and use as address. ; lookup output and average into result ; loop :Lookup call :NextAddr call :ArrayLookup mov out,w call :NextAddr call :ArrayLookup add out,w ;combine the first 2 outputs rr out ;and average (recovers C) call :NextAddr call :ArrayLookup mov out2,w call :NextAddr call :ArrayLookup add out2,w ;combine second 2 outputs mov w, >> out2 ;average second 2 add out, w ;combine the averages rr out ;average the two averages ret :NextAddr add in, #17 mov w,>>in ;recover "in.8" aka carry while /2 IFDEF WREG ;wreg is FR1 when it shows W and not RTCC clc ;clear carry rr wreg ;divide W by 2 ELSE clr temp add temp,w ;clears carry mov w,>>temp ENDIF ret
Training is basically the same except that the correction (random or computed) value is spread over the memory addresses as a change. The potential magnitude of the change is based on how good or bad the current environment is, or on the direction and magnatide of the change of fortunes. Better outcomes result in smaller changes, going from bad to worse may produce a larger change. This provides the same sort of "confidence" system employed in our first example, but allows for small "fine tuning" adjustments even during "good times" so that "even better times" might be found. Rather than a single bit for good or bad environment, the amount of change needs to be related to a range of good to bad situations. A situational report is prepared before the training and is saved to look for upward or downward trends.
function CMACtrain (int: in, oldSitRep, newSitRep) void { int i,training #define CMAC_INC = CMAC_SEPERATION * CMAC_OUTPUTS + 1; training = ( rnd(0)*newSitRep + rnd(0)*(newSitRep - oldSitRep) ) / CMAC_OUTPUTS for (i = 0; i > CMAC_OUTPUTS; i++) { in += CMAC_INC; CMACArray(in / CMAC_OUTPUTS) += training; } return; }
For a system of outputs with a spread of 4:
;training is random * sitRep + random * statusChange shifted right twice ;until 4 ; add 17 (seperation * outputs plus 1) to the input. ; shift right twice and use as address. ; change array value by training ; loop
It should also be said that the CMAC is probably the upper limit of what can be implemented in a microcontroller like the SX or PIC. In most CMAC systems, there are not one, but many CMACs and most have more than one input or diminsion. In a minimal system with only one CMAC, the input must be a combination of different values rather than just one sensor per CMAC diminsion. If the input is broken into individual bits, then we must realize that there is a difference in the "importance" or "priority" of the bits. and assign the positions of the bits in a usefull way. Low order bits will have little effect on the operation of the unit. High order bit changes in the input will drastically change the experiences the unit draws upon. For example, for a vaccume cleaning bot, the lowest bit could be assigned to a dirt sensor; it doesn't really signal a need to change operation, but it provide some hope that it will learn to find more dirt. The highest bit might be assigned to the status of the battery since the operation of the unit when charged is completly different than its operation when it needs to find a recharge.
See also:
Much more can be said and done on this subject and more informed and much more brilliant writers have and will. But I hope to have inspired a vew hobby robot fans to look into adding a bit more brain, and more AI especially, to their projects.
Best wishes! And thanks for your support.
; PicBot 2.05 ; ; 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 wak ng 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 mov W, #75 mov loops2, W wait2 nop nop nop decsz loops2 jmp wait2 decsz loops jmp wait clr wdt ; convenient ret ; generate start bit bstart setb eeport.sdata mov W, #outmask ;*** WARNING: TRIS expanded in two instructions. Check if previous instruction is a skip instruction. ; tris eeport clrb eeport.sclk nop setb eeport.sclk nop nop clrb eeport.sdata nop nop clrb eeport.sclk nop ret ; generate stop bit bstop clrb eeport.sdata mov W, #outmask ;*** WARNING: TRIS expanded in two instructions. Check if previous instruction is a skip instruction. ; tris eeport clrb eeport.sdata nop nop setb eeport.sclk nop nop setb eeport.sdata nop clrb eeport.sclk nop nop ret ; transmit byte in txbuf tx mov W, #8 mov count, W txlp clrb eeprom.do snb txbuf.7 setb eeprom.do mov W, #outmask ;*** WARNING: TRIS expanded in two instructions. Check if previous instruction is a skip instruction. ; tris eeport sb eeprom.do jmp txlp1 setb eeport.sdata jmp txlp2 txlp1 clrb eeport.sdata txlp2 setb eeport.sclk nop nop nop clrb eeport.sclk rl txbuf decsz count jmp txlp setb eeprom.di mov W, #outmask ;*** WARNING: TRIS expanded in two instructions. Check if previous instruction is a skip instruction. ; tris eeport setb eeport.sclk nop nop sb eeport.sdata clrb eeprom.di clrb eeport.sclk ret ; receive byte into datai rx clr datai mov W, #8 mov count, W clrb statby.0 rxlp rl datai setb eeprom.di mov W, #outmask ;*** WARNING: TRIS expanded in two instructions. Check if previous instruction is a skip instruction. ; tris eeport setb eeport.sdata setb eeport.sclk nop nop nop sb eeport.sdata clrb eeprom.di clrb eeport.sclk snb eeprom.di setb datai.0 decsz count jmp rxlp ret ; get byte from EEPROM into datai rbyte call bstart mov W, #10100000b mov txbuf, W call tx mov W, addr1 ; high address mov txbuf, W call tx mov W, addr ; low address mov txbuf, W call tx call bstart mov W, #10100001b mov txbuf, W call tx call rx call bstop ret ; write byte in datao to EEPROM wbyte call bstart mov W, #10100000b mov txbuf, W call tx mov W, addr1 mov txbuf, W call tx mov W, addr mov txbuf, W call tx mov W, datao mov txbuf, W call tx call bstop mov W, #10 mov loops, W 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 /* CMAC - Cerebellum Model Articulation Controller as described in AI Expert, June 1992, pp. 32-41 and Transactions of ASME, Sept. 1975, pp. 220-227 __________________ \ | |---|**** |----------| \ | |---|AND *--| x Weight |--+ \ >---| |---|**** |----------| | X Input1-* X | | | X >---| | | X Input2-* X | | : | / >---| | | / | | | / | | : | | Interconnect | | \ | Matrix | | \ | | : | |-----------| \ >---| | +-+ | Y Input1-* X | |---|**** |----------| | | Output X >---| |---|AND *--| x Weight |----+ Output +-------> Y Input2-* X | |---|**** |----------| | Summation | / >---| | +-+ | / | | : | |-----------| / | | | | | | \ | | : | \ | | | \ >---| | | Z Input1-* X | | | X >---| | : | Z Input2-* X | | | / >---| | | / | |---|**** |----------| | / | |---|AND *--| x Weight |--+ | |---|**** |----------| ------------------ ^ trainable weight vector ^ AND gates (eg. dimension=3) ^connection matrix (connects inputs to AND gates) ^overlapping input sensors (eg. width=2 inputs/sensor) ^inputs (eg. quant=2 bits/dimension) ^dimension (eg. 3) Notes: 1. Only one input per dimension can be active (= 1) at any time. Input values must be quantized into 1 of "quant" values. 2. Input sensors overlap and cover to "1 to width" number of inputs. Width can vary between 1 to "quant". Low numbers usually work best. 3. Interconnect matrix is such that each input vector (eg. X,Y,Z) activates exactly "width" number of AND gates. 4. Each AND gate has "dimension" number of inputs. 5. Output is a summation of weights corresponding to activated AND gates. 6. Weights are trained using the Delta rule. 7. CMAC converges very rapidly. Three bit parity example can be solved to an accuracy of .001 in about 20 training passes. 8. All nonlinearity comes from input mapping instead, of sigmoid function like FFNNs. 9. Visualize input vectors as locations in an N-dimensional space. The Output is then the value of the function at that location. 10. Interpolation between multiple outputs can be added to reduce effect of quantization. 11. Once the weights have been trained, the compute_output routine can be used alone to determine output quickly. 12. CMAC can be used to find approximations to N-dimensional nonlinear functions like sqrt or distance calculations quickly. 13. CMAC has been used successfully to linearize transducers or to form the inverse function of unknown plant dynamics. */ #include <stdio.h> #include <math.h> /* User selectable values */ #define dimension 3 /* input dimensions */ #define quant 2 /* input quantization per dimension */ #define width 2 /* width of input sensors */ #define max_gates 10 /* ==> set to (quant**dimension)+width) */ #define beta 0.4 /* learning rate for weight training */ #define err_limit 0.001 /* maximum error for any input */ #define k1 (quant+width-1) /* intermediate calculation */ #define max(a,b) ((a>=b)?a:b) /* max macro */ #define min(a,b) ((a<=b)?a:b) /* min macro */ struct list_of_conns{ int n; /* number of gates in list */ int gate_ptr[(max_gates)]; /* indices of gates */ } conn[quant][dimension], /* connections between inputs and gates */ activated, /* list of activated gates */ *c; /* pointer to list_of_conns */ int n_inputs; /* number of possible input vectors */ int n_sensors; /* number of possible input sensors */ int input[dimension]; /* input vector */ int nbr_gates = 0; /* num of gates used */ int gate[max_gates]; /* AND gates */ double weight[max_gates]; /* weight matrix */ double output, desired, error, total_error; void form_interconnects(); void compute_output(); void train(); int main() { register int i, j, k; int pass=1; double max_error=err_limit; printf("\n\n CMAC \n\n"); n_inputs=(int)pow((double)quant,(double)dimension); n_sensors=(int)pow((double)(quant+width-1),(double)dimension); form_interconnects(); printf("cmac: finished interconnects, beginning training.\n"); while(max_error>=err_limit){ /* for each pass */ total_error=0.0; max_error=0.0; for(i=0;i<n_inputs;i++){ /* for each possible input */ for(k=1,j=0;j<dimension;k*=quant,j++) /* form input vector */ input[j]=(i/k)%quant; compute_output(); for(desired=0,j=0;j<dimension;j++) /* compute desired output */ desired=(double)(((int)desired) ^ input[j]); /* parity bit example */ train(); total_error+=fabs(error); max_error=max(fabs(error),max_error); } printf("Pass=%d \tmax_error=%g\tAvg Err=%g\n", pass++,max_error,total_error/n_inputs); } printf("cmac: finished training.\n\n"); for(i=0;i<nbr_gates;i++) /* display weights */ printf("W[%d]=%f\n",i,weight[i]); } void form_interconnects() /* generate interconnect lists */ { register int i, j, k, m, n, p, found; for(c = &conn[0][0],i=0; i<quant*dimension; c++,i++) /* initialization */ c->n=0; for (k=0; k<width; k++){ for(i=0;i<n_sensors;i++){ /* for each combination of input sensors */ found=1; for(m=1,j=0; j<dimension; m*=k1,j++) /* for each dimension */ if((((i/m)%k1)%width)!=k){ /* check acceptance criteria */ found=0; /* rejected, try another */ break; } if(found==1){ /* if acceptable */ for(m=1,j=0; j<dimension; m*=k1,j++){ /* for each dimension */ n=(i/m)%k1; /* n=input sensor */ for(p=max(0,n-width+1); p<=min(n,(quant-1)); p++){ c = &conn[p][j]; /* p=input connected to sensor n */ c->gate_ptr[(c->n)++]=nbr_gates; } } nbr_gates++; if(nbr_gates > max_gates){ printf("cmac: error, maximum number of gates exceeded!\n"); exit(3); /* increase #define max_gates */ } } } } } void compute_output() /* usable during and after training */ { register int g, i, j; activated.n=0; /* initialization */ output=0; for(g=0;g<nbr_gates;g++) gate[g]=0; for(i=0;i<dimension;i++){ /* for all dimensions */ c = &conn[input[i]][i]; for(j=0;j<c->n;j++){ /*increment all gates in list */ g=c->gate_ptr[j]; gate[g]++; if(((i+1)==dimension)&&(gate[g]==dimension)){ /* if activated */ /* generate list of activated gates */ activated.gate_ptr[activated.n++]=g; output += weight[g]; /* update output */ } } } } void train() /* compute error and modify weights */ { register int i; error=desired-output; for(i=0;i<activated.n;i++) /* using list of activated gates */ weight[activated.gate_ptr[i]]+=beta*error; }
+
file: /Techref/new/letter/news0307.htm, 35KB, , updated: 2016/2/24 14:06, local time: 2024/11/21 10:44,
18.191.178.16:LOG IN
|
©2024 These pages are served without commercial sponsorship. (No popup ads, etc...).Bandwidth abuse increases hosting cost forcing sponsorship or shutdown. This server aggressively defends against automated copying for any reason including offline viewing, duplication, etc... Please respect this requirement and DO NOT RIP THIS SITE. Questions? <A HREF="http://massmind.org/techref/new/letter/news0307.htm"> July 2003 SXList.com newsletter - SX Learning Robotics / CMAC</A> |
Did you find what you needed? |
Welcome to massmind.org! |
Welcome to massmind.org! |
.