Let's Make Robots!

Reading light direction off a servo. Arduino C++

Hi there smiley 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)

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

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.

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?

 

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.

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 "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.

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 :)
}
---------------------------------------------------------------

// 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

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.

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

.

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);