Bacteriobot:
Modeling bacterial phototaxis with
LEGO Mindstorms Robotics Invention System

The first project of my own in robotics had to be something based on the sample creations supplied with RIS. I've chosen the roverbot and started building a model for phototaxis in bacteria [1]. I called my creation Bacteriobot... just because it models bacteria.

It consists of driving base with 4 wheels, light sensor, left and right touch sensors engaged by a wide bumper. The last is not characteristic for bacteria, but is necessary for the robot to avoid of to get stuck against obstacles.

 

A general view of Bacteriobot. Light sensor (A) detects light levels in range 0-100. Touch sensors (B) help the bot to get out of troubles when it hits obstacles.

The flagellae motor (C) does not add up to the functionality, but spices the creature with a look-alike bacterial motor, which allows bacteria to swim in the media. These two spines reverse their motion when the robot makes a random move, so a perfect illusion is created that the bot moves by the means of flagellae.
This caters well to the students' amusement.


The gearing scheme for Bacteriobot. This makes the standard Lego Roverbot more agile yet also increase the ground clearance.

The algorithm for Bacteriobot is based on Brownian motion with variable Hurst exponent [2, 3]. For completely random walk of a particle the displacement is proportional to t0.5 (t means time). However, there are special cases of Brownian motion when reversal of movement became less or more probable than 1/2. In such cases displacement of particle is proportional to tH. Where H is Hurst exponent ranging from 0 to 1. If H=0.5 we get standard random walk; if H<0.5 the reversal of movement is more likely to happen than continuation, and for H>0.5 the opposite is true.

Bacteria being very primitive organisms cannot spot the light or chemical source directly and go to it. Instead, they swim in the media and randomly change direction of movement by reversing rotation of flagellae. They feel change in light intensity or concentration of a substance by means of change in internal electron transport and adjust frequency of movement reversals accordingly. Roughly speaking, what is changed is the probability of those reversals and, therefore, Hurst exponent of the process.

This phenomenon has been exploited in Bacteriobot. As soon as light intensity (I) measured by LEGO light sensor ranges from 0 to 100, this value was used as percentage of continuation probability (i.e. probability of moving into the same direction) and 100-I is the probability of reversal. The reversal is modeled as random spinning followed by random forward movement.

Compared to standard robotic algorithms of light seeking, Bacteriobot looks sometimes pretty dumb when suddenly turns away from direct light source, but from the other side, it can find this light source being started anywhere in the room with a lot of obstacles, even such tricky as (semi)transparent ones (I used transparent inflatable chair to confuse the robot).

The primary tests showed that in an empty room 5X5 m with light source placed in a corner it takes at average 3 min for Bacteriobot to find the light, whereas randomly walking Bacteriobot, which has been run for about 30 min never even approached the lamp. Below are programs for Bacteriobot phototaxis written in Ada (1) and NQC (Not Quite C, 2). The last is less dumb because of employing of multitasking, which is still not available in Ada package (to my deep regret). Package for programming RIS in Ada can be obtained e.g. at http://www.dit.upm.es/~str/proyectos/mindstorms/2012/index.html


Useful references:


1. Taylor B. L., Zhulin I. B., Johnson M. S. Aerotaxis and other energy-sensing behavior in bacteria // Ann. Rev. Microbiol. - 1999. - V. 53. - P. 103-128. (+references therein).
2. Hurst H. E. The long-term storage capacity of reservoirs // Trans. Am. Soc. Civ. Eng. - 1951. - V. 116. (the original work introducing notion of Hurst exponent).
3. Borodin A. N., Salminen P. Handbook of Brownian Motion - Facts and Formulae.// Birkhauser Verlag. - 2002.- 672 PP.

------------Beginning of of phototaxis_new.adb Cut here-------------------------
WITH Lego;
USE Lego;

PROCEDURE Phototaxis IS
  
Left : CONSTANT Integer:=0;
   Right :
CONSTANT Integer:=1;
   Coast:
CONSTANT Boolean:=False;
   Brake:
CONSTANT Boolean:=True;

   Left_Wheel :
CONSTANT Output_Port := Output_A;
   Right_Wheel :
CONSTANT Output_Port := Output_C;
   Left_Bumper :
CONSTANT Sensor_Port := Sensor_1;
   Right_Bumper :
CONSTANT Sensor_Port := Sensor_3;
   Photo:
CONSTANT Sensor_Port:=Sensor_2;
   Crit_Time:
CONSTANT Integer:=10;

   Cnt: Integer;
   Lght:Integer;

  
PROCEDURE Go_Forward(Tenths_Of_A_Second : IN Integer; Mode: IN Boolean ) IS
   BEGIN
     
Output_On_Forward(Output => Right_Wheel);
      Output_On_Forward(Output => Left_Wheel);
      Wait(Hundredths_Of_A_Second => Tenths_Of_A_Second *
10);
     
IF Mode THEN
         
Output_Off(Output => Left_Wheel);
         Output_Off(Output => Right_Wheel);
     
ELSE
        
Output_Float(Output => Left_Wheel);
         Output_Float(Output => Right_Wheel);
     
END IF;
  
END Go_Forward;

  
PROCEDURE Go_Back (Tenths_Of_A_Second : IN     Integer ; Mode: IN Boolean ) IS
   BEGIN
     
Output_On_Reverse(Output => Left_Wheel);
      Output_On_Reverse(Output => Right_Wheel);
      Wait(Hundredths_Of_A_Second => Tenths_Of_A_Second *
10);
     
IF Mode THEN
        
Output_Off(Output => Left_Wheel);
         Output_Off(Output => Right_Wheel);
     
ELSE
        
Output_Float(Output => Left_Wheel);
         Output_Float(Output => Right_Wheel);
     
END IF;
  
END Go_Back;

  
PROCEDURE Turn (Tenths_Of_A_Second : IN     Integer; Direction : IN     Integer ; Mode: IN Boolean ) IS
   BEGIN
      IF
Direction = Left THEN
         IF
Mode THEN
           
Output_Off(Output => Left_Wheel);
        
ELSE
           
Output_Float(Output => Left_Wheel);
        
END IF;
         Output_On_Forward(Output => Right_Wheel);
         Wait(Hundredths_Of_A_Second => Tenths_Of_A_Second *
10);
        
IF Mode THEN
           
Output_Off(Output => Right_Wheel);
        
ELSE
           
Output_Float(Output => Right_Wheel);
        
END IF;
     
ELSE --right
        
IF Mode THEN
           
Output_Off(Output => Right_Wheel);
        
ELSE
           
Output_Float(Output => Right_Wheel);
        
END IF;
         Output_On_Forward(Output => Left_Wheel);
         Wait(Hundredths_Of_A_Second => Tenths_Of_A_Second *
10);
        
IF Mode THEN
           
Output_Off(Output => Left_Wheel);
        
ELSE
           
Output_Float(Output => Left_Wheel);
        
END IF;
     
END IF;
  
END Turn;

  
PROCEDURE Spin (Tenths_Of_A_Second : IN     Integer; Direction : IN     Integer ; Mode: IN Boolean ) IS
   BEGIN
      IF
Direction = Left THEN
        
Output_On_Reverse(Left_Wheel);
         Output_On_Forward(Output => Right_Wheel);
         Wait(Hundredths_Of_A_Second => Tenths_Of_A_Second *
10);
     
ELSE --right
        
Output_On_Reverse(Right_Wheel);
         Output_On_Forward(Output => Left_Wheel);
         Wait(Hundredths_Of_A_Second => Tenths_Of_A_Second *
10);
     
END IF;
     
IF Mode THEN
        
Output_Off(Output => Left_Wheel);
         Output_Off(Output => Right_Wheel);
     
ELSE
        
Output_Float(Output => Left_Wheel);
         Output_Float(Output => Right_Wheel);
     
END IF;

  
END Spin;

  
PROCEDURE Left_Bmpr IS
   BEGIN
      IF
Get_Timer(1)-Cnt<Crit_Time THEN
        
Play_Tone(1000,10);
         Go_Back(
10, Coast);
         Spin(
12,Right, Coast);
     
ELSE
        
Go_Back(6, Coast);
         Spin(
4,Right, Coast);
         Cnt:=Get_Timer(
1);
     
END IF;
  
END Left_Bmpr;

  
PROCEDURE Right_Bmpr IS
   BEGIN
      IF
Get_Timer(1)-Cnt<Crit_Time THEN
        
Play_Tone(500,10);
         Go_Back(
10, Coast);
         Spin(
12,Left, Coast);
     
ELSE
        
Go_Back(6, Coast);
         Spin(
4,Left, Coast);
         Cnt:=Get_Timer(
1);
     
END IF;
  
END Right_Bmpr;

  
PROCEDURE Rnd_Move IS
   BEGIN
     
Spin (Random(12), Random(1), Coast);
      Go_Forward (Random(
10), Coast);
  
END Rnd_Move;

  
PROCEDURE Initialize_Robot IS
   BEGIN
     
Config_Sensor(Sensor => Sensor_1, Config => Config_Touch);
      Config_Sensor(Sensor => Sensor_3, Config => Config_Touch);
      Config_Sensor(Sensor => Sensor_2, Config => Config_Light);
      Output_Power(Output => Left_Wheel,Power  => Power_High);
      Output_Power(Output => Right_Wheel,Power  => Power_High);
  
END Initialize_Robot;

BEGIN
  
Initialize_Robot;
  
LOOP
      IF
Get_Sensor_Value(Sensor => Left_Bumper) = 1 THEN
        
Left_Bmpr;
     
ELSIF Get_Sensor_Value(Sensor => Right_Bumper) = 1 THEN
        
Right_Bmpr;
     
END IF;

      Lght:=Get_Sensor_Value(Photo);
      Display_Value(Lght,
0);
     
IF Lght>95 THEN  --light found
        
Output_Float(Output => Left_Wheel);
         Output_Float(Output => Right_Wheel);
         Play_Tone(
349,4);
         Wait (
10);
         Play_Tone(
392,4);
         Wait (
10);
         Play_Tone(
440,4);
         Wait (
10);
         Play_Tone(
494,4);
         Wait (
10);
         Play_Tone(
494,4);
         Wait (
10);
         Play_Tone(
440,4);
         Wait (
10);
         Play_Tone(
392,4);
         Wait (
10);
         Play_Tone(
349,4);
         Wait (
200);
     
END IF;
     
IF Random(100)>=Lght THEN
        
Rnd_Move;
     
ELSE
        
Go_Forward(Random(25),Coast);
     
END IF;
  
END LOOP;
END Phototaxis;
-------------End of phototaxis_new.adb  Cut here--------------------------------

-----------Beginning of of phototaxis.nqc Cut here------------------------------
#define LEFT (0 )
#define RIGHT (1 )
#define COAST (false )
#define BRAKE (true )
#define LEFT_WHEEL (OUT_A)
#define RIGHT_WHEEL (OUT_C)
#define LEFT_BUMPER (SENSOR_1)
#define RIGHT_BUMPER (SENSOR_3)
#define PHOTO (SENSOR_2)
#define CRIT_TIME (10)
#define __NOTETIME 6
#define __WAITTIME 1

int LGHT ;
void GO_FORWARD ( int TENTHS_OF_A_SECOND , int MODE )
{
OnFwd( RIGHT_WHEEL ) ;
OnFwd( LEFT_WHEEL ) ;
Wait( TENTHS_OF_A_SECOND * 10 ) ;
if ( MODE )
{
Off( LEFT_WHEEL ) ;
Off( RIGHT_WHEEL ) ;
}
else
{
Float( LEFT_WHEEL ) ;
Float( RIGHT_WHEEL ) ;
}
}
void GO_BACK ( int TENTHS_OF_A_SECOND , int MODE )
{
OnRev( LEFT_WHEEL ) ;
OnRev( RIGHT_WHEEL ) ;
Wait( TENTHS_OF_A_SECOND * 10 ) ;
if ( MODE )
{
Off( LEFT_WHEEL ) ;
Off( RIGHT_WHEEL ) ;
}
else
{
Float( LEFT_WHEEL ) ;
Float( RIGHT_WHEEL ) ;
}
}
void TURN ( int TENTHS_OF_A_SECOND , int DIRECTION , int MODE )
{
if ( DIRECTION == LEFT )
{
if ( MODE )
{
Off( LEFT_WHEEL ) ;

}
else
{
Float( LEFT_WHEEL ) ;

}
OnFwd( RIGHT_WHEEL ) ;
Wait( TENTHS_OF_A_SECOND * 10 ) ;
if ( MODE )
{
Off( RIGHT_WHEEL ) ;

}
else
{
Float( RIGHT_WHEEL ) ;

}
}
else
{
if ( MODE )
{
Off( RIGHT_WHEEL ) ;

}
else
{
Float( RIGHT_WHEEL ) ;

}
OnFwd( LEFT_WHEEL ) ;
Wait( TENTHS_OF_A_SECOND * 10 ) ;
if ( MODE )
{
Off( LEFT_WHEEL ) ;

}
else
{
Float( LEFT_WHEEL ) ;

}
}
}
void SPIN ( int TENTHS_OF_A_SECOND , int DIRECTION , int MODE )
{
if ( DIRECTION == LEFT )
{
OnRev( LEFT_WHEEL ) ;
OnFwd( RIGHT_WHEEL ) ;
Wait( TENTHS_OF_A_SECOND * 10 ) ;
}
else
{
OnRev( RIGHT_WHEEL ) ;
OnFwd( LEFT_WHEEL ) ;
Wait( TENTHS_OF_A_SECOND * 10 ) ;
}
if ( MODE )
{
Off( LEFT_WHEEL ) ;
Off( RIGHT_WHEEL ) ;
}
else
{
Float( LEFT_WHEEL ) ;
Float( RIGHT_WHEEL ) ;
}
}
void LEFT_BMPR ()
{
if ( Timer( 1 ) < CRIT_TIME )
{
PlayTone( 1000 , 10 ) ;
GO_BACK ( 10 , COAST ) ;
SPIN ( 12 , RIGHT , COAST ) ;
}
else
{
GO_BACK ( 6 , COAST ) ;
SPIN ( 4 , RIGHT , COAST ) ;
ClearTimer( 1 ) ;
}
}
void RIGHT_BMPR ()
{
if ( Timer( 1 ) < CRIT_TIME )
{
PlayTone( 500 , 10 ) ;
GO_BACK ( 10 , COAST ) ;
SPIN ( 12 , LEFT , COAST ) ;
}
else
{
GO_BACK ( 6 , COAST ) ;
SPIN ( 4 , LEFT , COAST ) ;
ClearTimer( 1 ) ;
}
}
void RND_MOVE ()
{
Toggle(OUT_B);
SPIN ( Random( 12 ) , Random( 1 ) , COAST ) ;
GO_FORWARD ( Random( 10 ) , COAST ) ;
}
void INITIALIZE_ROBOT ()
{
SetSensor( SENSOR_1, SENSOR_TOUCH) ;
SetSensor( SENSOR_3, SENSOR_TOUCH) ;
SetSensor( SENSOR_2, SENSOR_LIGHT) ;
SetPower( LEFT_WHEEL , 7 ) ;
SetPower( RIGHT_WHEEL , 7 ) ;
}
task watch_obstacles() {while (true) {
if ( ( LEFT_BUMPER ) == 1 )
{stop watch_light;
LEFT_BMPR () ;
start watch_light;
}
else if ( ( RIGHT_BUMPER ) == 1 )
{stop watch_light;
RIGHT_BMPR () ;
start watch_light;
}}}

task watch_light(){
while (true)
{
if ( Random( 100 ) >= LGHT )
{
RND_MOVE () ;
}
else
{
GO_FORWARD ( Random( 25 ) , COAST ) ;
}
}
}

task set_light() { while(true){
LGHT = ( PHOTO );
SetUserDisplay( LGHT , 0 );
if ( LGHT > 95 )
{ Float(OUT_B);
Float( LEFT_WHEEL ) ;
Float( RIGHT_WHEEL ) ;
PlayTone(1318,4*__NOTETIME);
Wait(4*__WAITTIME);
PlayTone(1244,4*__NOTETIME);
Wait(4*__WAITTIME);
PlayTone(1318,4*__NOTETIME);
Wait(4*__WAITTIME);
PlayTone(1244,4*__NOTETIME);
Wait(4*__WAITTIME);
PlayTone(1318,4*__NOTETIME);
Wait(4*__WAITTIME);
PlayTone(988,4*__NOTETIME);
Wait(4*__WAITTIME);
PlayTone(1175,4*__NOTETIME);
Wait(4*__WAITTIME);
PlayTone(1046,4*__NOTETIME);
Wait(4*__WAITTIME);
PlayTone(880,4*__NOTETIME);
Wait(200*__WAITTIME);
On (OUT_B); }
}}

task main()
{
INITIALIZE_ROBOT () ;
start set_light;
On(OUT_B);
start watch_obstacles;
start watch_light;
}
-----------------End of phototaxis.nqc  Cut here---------------------------------

Contacts

 

Please send your questions to: