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.

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

 

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.

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

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

.

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.

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.