Reading light direction off a servo. Arduino C++

Hi there  I am fiddling with my second light follower robot on my 2WD chassis
the first one was the classic "3 photo resistors" model.
Now iI have fitted a servo onto the chassis, and have mounted a single photo resistor onto the servo.



https://fbcdn-sphotos-e-a.akamaihd.net/hphotos-ak-ash3/178416_637341408647_239375050_o.jpg

The servo scans from 40 degrees, to 130 degrees (90 is the centre, both of these values are the extents possible without fouling the photo resistor on the bodywork)

I want the photo resistor to take a reading for each increment of servo movement, compare it to the previous reading, and once the light level starts to drop down again (as in once the photo resistor has scanned past the light, and starts to scan away from the light source), it notes the position of the servo, and logs it as a "direction" to the light source.

currently 130 is left, 90 is straight forward, and 40 is right. I would build "windows" into the factors, as in "if the light is between 80 and 100, straight forward, else steer, depending on value)

hello

you might wanna make an array and store the readings in. i am not familliar with c++ but i guess you would wanna do something like this

int where=0;

int lightarray[90]; // store the rading on each degree sort of

for(HoriPos = 40; HoriPos < 130; HoriPos += 1) 
  { 

    lightarray[where] = lightlevel;

         where++;

         if (where > 4){

         if (lightarray[where]<lightarray[where-1] AND lightarray[where-1]<lightarray[where-2]){

         //insert what to to after finding the strongest lightsource

         }

         }


    Hori.write(HoriPos);             
    delay(15);                      
  } 

        where=90;
  for(HoriPos = 130; HoriPos>=40; HoriPos-=1)    
  { 

         lightarray[where] = lightlevel;        

         where–;

         if (where < 86){

         if (lightarray[where]<lightarray[where+1] AND lightarray[where+1]<lightarray[where+2]){   //comparing the last three values

         //insert what to to after finding the strongest lightsource

         }

         }
    Hori.write(HoriPos);             
    delay(15);       

 

A quick way to make the code smaller is,

instead of where = 0; ,and later, where = 90; you could just replace where with HoriPos - 90, or, inside the for loop set where = HoriPos - 90. Either way you save yourself a little bit. Keeping the where and just using HoriPos - 90 to set where is probably the best option.

damnit

As always you are correct Bird. Well almost anyway…

you would want to set where=horipos-40 to make the array range from 90-0 and 0-90 respectively

i hope you get the idea olly. Post you code here when you have worked on it a bit more. ill be happy to help out as much as i can :slight_smile:

I think this is properly

I think this is properly indented now.


I have got so far, I want it to update once per sweep. I have added a count function, and have put a “print” request within this, so that it should only print once per sweep too.

I am currently getting the prints at the end of each sweep, and it is printing the value of the extremes only. (40, 130, 40, 130 etc…)

(once i have got a simple sweep back and forth going, i will be able to include the vertical axis, and identify a position, before making a movement decision based on the information.)

I think it is the voidloop where i have fallen down, but here is everything:


Code:
#include <AFMotor.h>
#include <VarSpeedServo.h>

AF_DCMotor motorL(3, MOTOR12_1KHZ); 
AF_DCMotor motorR(2, MOTOR12_1KHZ);
VarSpeedServo Hori;
VarSpeedServo Vert;

const int PR = A1; // connect PR to A1
const int RLED = 13; // indicator LED: 13 and 2 are the only properly pins on the motorshield.
const int LLED = 2; // indicator LED

int HoriPos = 90;      // straight forward,
int VertPos = 90;      // 30 degrees up from horizontal

int LightLevel = 0;
int PrevLightLevel = 0;
int Direction = 0;
byte count = 0;

void setup() {

  Serial.begin(9600);  // Serial at 9600 bps

  Hori.attach(9);  // Hori Servo on Pin 9 (Servo2 on the Motorshield)
  Vert.attach(10); // Vert Servo on Pin 10 (Ser1 on the Motorshield)

  constrain(HoriPos, 40, 130); // 90 is center, straightforward, 40 is Left, 130 is full right. Doesnt foul when vert is = 120, would foul if vert is < 120
  constrain(VertPos, 1, 170); // 0 is tilted back approx 30,  170 is max down without fouling on body. 30 is vertical, 120 is horizontal

  motorL.setSpeed(150);      // set the speed of motors: 0 is stop, 255 is full speed:
  motorR.setSpeed(150);

  pinMode (RLED,OUTPUT);
  pinMode (LLED,OUTPUT);

}

void loop() {





  for(HoriPos = 40; HoriPos <= 130; HoriPos += 1)
  {   

    Hori.write(HoriPos);
    PrevLightLevel = LightLevel;
    LightLevel = analogRead(PR);   

    if (LightLevel > PrevLightLevel && count == 0) {
      Direction = HoriPos;
      Serial.println(Direction);
      count = 1;
    }

    if (LightLevel > PrevLightLevel && count == 1) {
    }

    if (LightLevel < PrevLightLevel) {
    }
    if (HoriPos == 130){
      count = 0;
    }
    delay(50);                         


  }


  for(HoriPos = 130; HoriPos >= 40; HoriPos-=1)   
  {

   


Thanks for looking.

.

.

hmmm

it looks like you are storing the last read value all the time instead of the highest one
and let me just point yout to you if you where to use an array to store all the readings
in you could also estimate how far from the light source you are since you would have
more high reading in a row the closer to the ligth source you are.
Anyway here goes.

 
PrevLight_level=0;
for(HoriPos = 40; HoriPos <= 130; HoriPos += 1)
  {   

    Hori.write(HoriPos);
   
    LightLevel = analogRead(PR);   

    if (LightLevel > PrevLightLevel) {

PrevLightLevel = LightLevel; //with this put here it should work alot better…
      Direction = HoriPos;
    }

    }
    delay(50);                         


  }
Serial.println(Direction)

//here you can do the same for the the sweep going back.

now you read all of the values and always store the bigger value in prevLightLevel
then when you are done with the sweep you print the highest value which is Prev…

I have not come across the

I have not come across the “array” before. I will have a look into it. I do understand your idea (i think).

Although, To clarify how I THINK my sketch SHOULD work, that is to say, what i think ive done, not what i think is best. I’m always open to suggestions.

It sweeps the servo, as the light goes up, the PR value drops. for each cycle (new servo position), it first moves the existing lightlevel to the previouslightlevel, and then populates a new light level reading. It is comparing the prev, with the new light level. As soon as the light level starts to drop (PR value goes up) in comparison to the previous level, it updates the “direction” value, with the HoriPos value, but only once.

as I see it, i would sweep the servo back and forth once. Take the value, and then make a movement decision based on the value. I have a vertical axis as well, which would give range (assuming a celing mounted light) A sweep with vertical, then horizontal, would provide enough information for a move, before doing it again to adjust.

I will look into arrays anyway, It seems it will populate a whole list of numbers on a single sweep without manually defining each degree? How i then retrive them and “ask for thier position” i will have to look into. Thanks for sticking with me.

Link to the 'classic “3 photo resistors” model’

I am kinda curious about this ‘classic “3 photo resistors” model’.

I did some searching and it seems it may not be as “classic” as you think. I have found tons of 2-sensor examples, but not so much on the 3-sensor style of which you speak.

Can I get a link? I may help me in a related project.

From looking at your code

The first time into your first loop both lightvalue and previousvalue are = 0. As soon as you take a reading, lightvalue is larger AND count == 0. So, direction = horipos. That is the extent of direction getting set. I can not tell you any more as you have not included the rest of your code in the post above.

hi again

well the way i wrote it above was pretty much the way you wanted it but i can give you a more complete snippet and explain better how it works. a “enhanced” version is also found here which i wrote for you. http://pastebin.com/NRRhUuHk

 

PrevLightLevel=analogRead(PR); //i just set this to a read value for comparison
for(HoriPos = 40; HoriPos <= 130; HoriPos += 1)
  {   

    Hori.write(HoriPos);
   
    LightLevel = analogRead(PR);    // your reading is stored in LightLevel

    if (LightLevel < PrevLightLevel) { // i dont now if you get a high reading when theres much light and a low if less light just change
// this if its the other way around. now when you get a lower value than the previous one(more light)
PrevLightLevel = LightLevel; // we store the new highest value in PrevLightLevel.

      Direction = HoriPos; // and the direction where the light was strong
    }

    }
    delay(50);                         


  }
//since it’s done with the sweep (one way) we dont print it yet.if you want to you could make a
//CCW_Direction and a CW_Direction integer to save the sweeps in.(CCW=counter clockwise sweep and , yeah you get the rest…)


//here you do the same for the the sweep going back.but without setting previous to 0.



for(HoriPos = 130; HoriPos > 40; HoriPos -= 1)
  {   

    Hori.write(HoriPos);
   
    LightLevel = analogRead(PR);    // your reading is stored in LightLevel

    if (LightLevel < PrevLightLevel) { // i dont now if you get a high reading when theres much light and a low if less light just change
// this if its the other way around. now when you get a lower value than the previous one(more light)
PrevLightLevel = LightLevel; // we store the new highest value in PrevLightLevel.

      Direction = HoriPos; // and the direction where the light was strong
    }

    }
    delay(50);                         


  }
Serial.println(Direction)

//now you sweep both ways and the strongest lightvalue is stored in previousLightLevel and position of the servo is now in
// Direction.

I also came to think of that this way of scanning might take a while so i just wanna make a suggestion
to scan in increments of maybe 10 steps on the servo and then in the strongest areas of the sweep you
might wanna do a 1 step scan.

like this-------------------------------------------------
PrevLightLevel=analogRead(PR);
for(HoriPos = 40; HoriPos <= 130; HoriPos += 10)
  {   

    Hori.write(HoriPos);
   
    LightLevel = analogRead(PR);   

    if (LightLevel < PrevLightLevel) {

PrevLightLevel = LightLevel;

      Direction = HoriPos;
    }

    }
    delay(50);                         


  }
PrevLightLevel=analogRead(PR);
int Temp = Direction;
for(Horipos=Temp-5;Horipos>=Temp+5;Horipos+=1){
Hori.write(HoriPos);
   
    LightLevel = analogRead(PR);   
    if (LightLevel < PrevLightLevel) {

PrevLightLevel = LightLevel;

      Direction = HoriPos; // and the direction where the light was strongest is stored here :slight_smile:
}
---------------------------------------------------------------

// this way if the light is strongest at lets say Direction 70 you would only
 have to scan 9+10 times, 19 times instead of 90.
which means you can make faster scans.
Good luck

well

I do set PrevLightLevel to 0 because i thought 0 was the lowest value it could have. and if that was the case it would work because the new higher value would always overwrite the old one and wont print it till the for loop is done but now i see the Prevlight must be changed in ALL OF THE CODE i have written. i think he will get the idea though set PrevLightLevel to the lowest value or highest depending on how your lighthingy works. mail me if you have any questions anyway or just post it here.

So to make it short. if a strong light give you a low value set PrevLightLevel to a high value and change the if so it makes sense with this value.

 

Edit----

In the code above birdmuns comment it should now be fixed. In the pastebin code it is also fixed. Good luck

Hey, So i looked into arrays

Hey, So i looked into arrays following your advice here. You are correct, it seems to be much more useful, and less clumsy

I actually built an array code from a bit of research (its all new to me, and im only doing it to learn, not to get an end product) and ended up with a code very much like yours.

through checkin with the serial output, I have successfully populated the array, and put the “if” function in, but when i put “Direction = Horizontal servo position” as far as i can tell, it populates the result with the “light level” if the light level is high, or if it is low, it populates with the extremes of the servo (either 0 or 130)

as far as i can tell

Any clues as to where i have gone wrong?

Cheers!

 

/* Cyclops V2. rather than move to three discrete servo positions, scan, and populate PR readings on the move

 try and use “array” to define strongest light HoriDir

 */

 

 

#include <AFMotor.h>

#include <VarSpeedServo.h> 

 

AF_DCMotor motorL(3, MOTOR12_1KHZ);  

AF_DCMotor motorR(2, MOTOR12_1KHZ);

VarSpeedServo Hori;

VarSpeedServo Vert;

 

const int PR = A1; // connect PR to A1

const int RLED = 13; // indicator LED: 13 and 2 are the only properly unused digital pins on the motorshield.

const int LLED = 2; // indicator LED

 

int HoriPos = 0;      // straight forward, 

int VertPos = 90;      // 30 degrees up from horizontal

 

int HoriArray[130];

int CWHoriDir = 0;

int CCWHoriDir = 0;

 

void setup() {

 

  Serial.begin(9600);  // Serial at 9600 bps

 

  Hori.attach(9);  // Hori Servo on Pin 9 (Servo2 on the Motorshield)

  Vert.attach(10); // Vert Servo on Pin 10 (Ser1 on the Motorshield)

 

  motorL.setSpeed(150);      // set the speed of motors: 0 is stop, 255 is full speed:

  motorR.setSpeed(150);

 

  pinMode (RLED,OUTPUT);

  pinMode (LLED,OUTPUT);

 

}

 

 

void CWSweep (){

 

 

  for(HoriPos = 0; HoriPos <= 130; HoriPos += 1)

  {    

    HoriArray[HoriPos] = analogRead(PR);

    Hori.write(HoriPos);

    delay (10) ;       

  }

 

  if ((HoriArray[HoriPos] < HoriArray[HoriPos-1]) && (HoriArray[HoriPos-1] < HoriArray[HoriPos-2])){

    CWHoriDir = HoriPos;

  }

}

 

void CCWSweep (){

 

  for(HoriPos = 130; HoriPos >= 0; HoriPos-=1)   

  {  

    HoriArray[HoriPos] = analogRead(PR);

 

    Hori.write(HoriPos);

    delay (10) ;  

  }

  if ((HoriArray[HoriPos] < HoriArray[HoriPos+1]) && (HoriArray[HoriPos+1] < HoriArray[HoriPos+2])){

    CCWHoriDir = HoriPos;

  }

 

}

 

void loop() {

 

  CWSweep();

  Serial.println(CWHoriDir);

  CCWSweep();

  Serial.println(CCWHoriDir);

}

P.S. I like your focused second sweep idea, but want to get this basic version working first, and make sure i understand it.
Cheers
Ol

I would have thought it was

I would have thought it was quite common!

 

my setup was 3 PRs, each with its own Analogue read pin, and pointed indifferent directions (Left sightly, Central, Right Slightly)

The sketch compared the three values, and then depending on what was strongest ran the motors. Left ran the right motor only, central ran both, and Right ran left motor only.

I suppose if you had two, you would have it run forward if they were equal?

or just let it bounce between left and right, so it would waddle in the right direction, rather than run both motors at the same time?

 

ok

i have only had time to have a quick peek at your code, but one thing i noticed was this…

void CWSweep (){

 

 

  for(HoriPos = 0; HoriPos <= 130; HoriPos += 1)

  {    

    HoriArray[HoriPos] = analogRead(PR);

    Hori.write(HoriPos);

    delay (10) ;       

  }

 

  if ((HoriArray[HoriPos] < HoriArray[HoriPos-1]) && (HoriArray[HoriPos-1] < HoriArray[HoriPos-2])){

    CWHoriDir = HoriPos;

  }

}

 

you should put the if inside the for loop instead. and you only need to check if it is less once. like this…

 

 

void CWSweep (){

 

 

  for(HoriPos = 40; HoriPos <= 130; HoriPos += 1) //wasnt it from 40?

  {    

    HoriArray[HoriPos-40] = analogRead(PR);

    Hori.write(HoriPos);

   
    if ((HoriArray[HoriPos-40] < HoriArray[HoriPos-41]) && (HoriPos>0))// if it is at horipos 0 it doesnt need to compare values.

    CWHoriDir = HoriPos;

  }

    delay (10) ;       

  }

}

 

and the you need to change the CCW one aswell.