At the forefront of Artificial Intelligence
  Home Articles Reviews Interviews JDK Glossary Features Discussion Search
Home » Articles » Robotics » Machine Learning

Emerging behavior: the wave (4 x 12F675)

By Bert van Dam

If you look at people in a soccer stadium doing the wave it looks like a giant orchestrated phenomena. But of course it isn't. Every spectator follows a very simple set of rules:

  1. I see the person next to me standing up
  2. so I stand up too
  3. and raise my arms in the air for a moment
  4. and then I sit down again

This phenomena is called emerging behavior. A set of small simple rules for (small brained) individuals results in seemingly intelligent group behavior. Not everybody follows exactly the same rules, otherwise in the example above the wave would simply never start. Unless someone fancied a hotdog and got up to get one, accidentally setting off a wave. So maybe you need a fourth rule, something like

  1. every once in a while I stand up for no apparent reason

The same thing applies to ants, termites, and even shop distribution across a city. If you're interested in this 'Emergence, the connected lives of ants, brains, cities and software' by Steven Johnson might be a good choice for you (No. No link to Amazon, I don't get paid for this).

I suppose the software equivalent of a wave would be a running light. It would be rather easy to simply program a running light into a single microcontroller, but of course that wouldn't be any fun. So instead we'll use four separate controllers, each with their own set of rules, who look at each other and respond to each other. Like this:

The LED on a module is 'spotted' by the LDR on the next module. After a short delay this module flashes its LED, which alters the next module, and so on. Important is that each module runs completely independent (and completely 'unaware') of the other modules, yet when put together they form a running light.

  1. I see the person next to me standing up (I see the LED of my neigbor)
  2. so I stand up too
  3. and raise my arms in the air for a moment (I light my LED)
  4. and then I sit down again (and switch it off again)

Since I don't have an unlimited supply of parts I've limited myself to four modules, but it would be rather interesting to see what would happen with, say, 100 of these modules in one room.

Anyway, obviously all four units look exactly the same:

The units need to be placed in such a way that each unit's LDR can see the LED of the previous unit, like this:

Note that I have added several 0.1 uF capacitors to eliminate interference over the power lines. The long wires attached to the top right hand unit are from the Xwisp in-circuit programmer.

Special feature is the auto calibration. Since the modules are supposed to react to the LED only, and not to changing ambient light conditions recalibration needs to take place on a regular basis, for example with a variable resistor. This is of course no fun with four units, so the units calibrate themselves.

  -- take a sample 
  PIC_ADC_read_low_res (2,  RESIST)
  
  -- get a random value
  RANDOMVAL = random_byte 

  -- check if a light has been observed by the LDR
  -- and if so turn on the own light after a short delay
  if RESIST < TRESHOLD then
    -- see if you feel like participating
    if RANDOMVAL > 50 then
      SENDDATA
      delay_10ms( REACTIONTIME )
      LEDON
    end if
  end if
        
  -- calculate new threshold value
  THRESHOLD = RESIST / 10
  THRESHOLD = THRESHOLD * 9

The code segment above takes a measurement of the LDR. If this is below the threshold value (meaning the LED of the neighbor unit is on), the signal is processed. If that is not the case a new threshold value is calculated. Since the neighbor LED is off this new threshold value is based on the current ambient light conditions. The resistor network (with the two 4k7 resistors) has been selected to function optimally under low ambient light (because I only program at night).

-- turn the LED on and off
procedure LEDON is
  LED =  high
  LIGHT = 1    
  SENDDATA
  delay_10ms( ONTIME )
  LED = low
  LIGHT = 0
end procedure

Once the signal is recognized as being from a neighbor unit the unit's own LED is flashed. Two variables control this process:

REACTIONTIME is the time is takes for the unit to actually respond. The shorter this time, the faster the unit's LED will go on.

ONTIME is the time the LED remains on.

You can play with these to variables to make the wave go faster or slower, flash short or long. If the units would only react to their neighbors but never take any action themselves the wave would never start. So every once in a while (in fact in 2% of the loops) each unit flashes spontaneously:

  -- every once in a while decide to light the LED just because...
  if RANDOMVAL > 250 then 
    LEDON
  end if

By the same token if every unit kept doing the wave it would never stop, so in 20% of the loops a unit will simply not participate.

    if RANDOMVAL > 50 then
      SENDDATA
      delay_10ms( REACTIONTIME )
      LEDON
    end if

It is fascinating to watch the LED's. Sometimes they perform perfect waves, at other times it's completely quiet, or two of them start a wave at the same time. These wave chase each other until they catch up, or a unit stops.

Serial communications aren't really needed, but I did use if for debugging, so I thought I'd leave it in, and write a small Visual Basic application to display what's going on inside a unit:

This is rather straight forward, except that the routine to switch the Xwisp programmer to pass through mode is now incorporated, which is of course much easier:

       'enable communications

        MSComm1.PortOpen = True
        
        'switch Xwisp programmer to pass through mode

        'Set the Break condition for 0.1 second to switch the
        'programmer to attention mode
        MSComm1.Break = True
        Duration! = Timer + 0.1
        Do Until Timer > Duration!
            Dummy = DoEvents()
        Loop
        MSComm1.Break = False

       
        'switch to programmer speed
        MSComm1.Settings = "19200,n,8,1"
        'send pass through command
        MSComm1.Output = "0000p"
        'wait to make sure the command is processed by
        'the programmer
        Duration! = Timer + 0.1
        Do Until Timer > Duration!
            Dummy = DoEvents()
        Loop

        'switch to microcontroller speed
        MSComm1.Settings = "1200,n,8,1"

You can download the source here, but of course you'll need Visual Basic in order to actually run it.

This is the full JAL source. It needs to be downloaded to each and every unit:

-- jal 4.55
include JRTL
include f675_4i
include jascii

-- random library
include random3

-- AD converter settings
const PIC_ADC_Nchan           = 3                              
const PIC_ADC_NVref           = 0                             
const PIC_ADC_Rsource         = 10_000   
const PIC_ADC_high_resolution = false            

-- serial communications settings
const asynch_baudrate = 1200
const asynch_polarity = active_high
const asynch_parity   = parity_none
const asynch_stopbits = 2

-- serial variables
var volatile bit asynch_in_pin        is pin_a1
var volatile bit asynch_in_direction  is pin_a1_direction
var volatile bit asynch_out_pin       is pin_a0
var volatile bit asynch_out_direction is pin_a0_direction

-- serial library
include seriali

-- AD converter variables
var volatile byte ADCON0   at 0x1F
var volatile bit  GO       at ADCON0 : 1
var volatile byte ADRESH   at 0x1E
var volatile byte ADRESL   at 0x9E
var volatile byte ADCON1   at 0x9F

-- AD library
include pic_adc

-- define the pins
pin_a2_direction = input
pin_a4_direction = output

-- general variable
var byte RESIST,  REQUEST, LIGHT, THRESHOLD, REACTIONTIME, ONTIME,RANDOMVAL

-- define the variables used
var bit LED is pin_a4

-- initialize the AD converter
PIC_ADC_init

-- some initial values for the variables
THRESHOLD = 0        -- start with 0 threshold
LED = low           -- start with the LED off
LIGHT = 0           -- indicator that LED is off
REACTIONTIME = 20   -- reaction time in 10ms
ONTIME = 10         -- time that the lED is on in 10ms

-- send data to PC procedure
procedure SENDDATA is
  asynch_send( RESIST )
  asynch_send( LIGHT )
  asynch_send( THRESHOLD )
  asynch_send( REACTIONTIME )
  asynch_send( ONTIME )
  asynch_send( RANDOMVAL )
end procedure

-- turn the LED on and off
procedure LEDON is
  LED =  high
  LIGHT = 1    
  SENDDATA
  delay_10ms( ONTIME )
  LED = low
  LIGHT = 0
end procedure

forever loop

  -- take a sample 
  PIC_ADC_read_low_res (2,  RESIST)
  
  -- get a random value
  RANDOMVAL = random_byte 

  -- check if a light has been observed by the LDR
  -- and if so turn on the own light after a short delay
  if RESIST < THRESHOLD then
    -- see if you feel like participating
    if RANDOMVAL > 50 then
      SENDDATA
      delay_10ms( REACTIONTIME )
      LEDON
    end if
  end if
        
  -- calculate new threshold value
  THRESHOLD = RESIST / 10
  THRESHOLD = THRESHOLD * 9

  -- call procedure to send data to the PC
  SENDDATA  
  
  -- give the computer time to react
  delay_100ms
  
  -- every once in a while decide to light the LED just because...
  if RANDOMVAL > 250 then 
    LEDON
  end if

end loop

Submitted: 06/02/2005

Article content copyright © Bert van Dam, 2005.
 Article Toolbar
Print
BibTeX entry

Search

Latest News
- Generation5 10-year Anniversary (03/09/2008)
- New Generation5 Design! (09/04/2007)
- Happy New Year 2007 (02/01/2007)
- Where has Generation5 Gone?! (04/11/2005)
- NeuroEvolving Robotic Operatives (NERO) (25/06/2005)

What's New?
- Back-propagation using the Generation5 JDK (07/04/2008)
- Hough Transforms (02/01/2008)
- Kohonen-based Image Analysis using the Generation5 JDK (11/12/2007)
- Modelling Bacterium using the JDK (19/03/2007)
- Modelling Bacterium using the JDK (19/03/2007)


All content copyright © 1998-2007, Generation5 unless otherwise noted.
- Privacy Policy - Legal - Terms of Use -