Let's Make Robots!

While loop breaks unexpectedly

Hi guys,

This is my first robot. The microprocessor is a QIC board and micrrocontroller is the Microchip PIC 16F877. The code is written in PIC C, a C compiler.

The robot is to solve a maze which is black and white. The robot is to stay on the black line and I use two sensors in the front of rover called resistor1 and resistor 2. There are two sensors for turning: resistor3 and resistor4.

The problem is that my code works but it shouldn't! I will only post a sample of my code below:

while(1){

....

....

while( resistor1 <100 ) {

if(resistor3>60) {
turn_left();
//output_low(pin_c3);output_low(pin_c0);set_pwm1_duty(830);set_pwm2_duty(785);
}

set_pwm2_duty(600);

set_adc_channel(0);
delay_us(10);
resistor1=read_adc();

set_adc_channel(3);
delay_us(10);
resistor3=read_adc();

.....


....

}

void turn_left() {

for(counter=0; counter<=150; counter++) {
//Motor 1 (output high)
output_high(pin_c3);
set_pwm1_duty(700);

//Motor 2
output_low(pin_c0);
set_pwm2_duty(1000);

                }

}

OK so logically, the resistor 3 detects a left line and the rover starts turning until the counter counts down to 150. However, what actually happens is that the rover starts turning and it stops suddenly when it senses another black line. It's as if (I think) the rover broke the while loop as soon as resistor3 was less than 60.

This is contrary to my experiece in C. The compiler reads the program line by line. I mean for example If I had this:

#include <stdio.h>
#include <stdlib.h>
int main() {
int i=5, j=100, counter;
while(i<10) {
if(j>50) {

for(counter=0; counter<=10; counter++) {
               printf("%d", counter);
               if(counter==8) {i=15;}             }
          }
        
         }
        
system("pause");
return 0;
}

The program won't break the for loop as soon as counter=8. It will finish the for loop and then stop compiling.

Thank you for reading...

 

Comment viewing options

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

Counters are pretty unreliable when used for determining how far a robot turns. Apart from having to trial a number of values until you get the right one. The value will change as the battery goes low. Or surface changes.

With 5 sensors you should be able to do your turns smoothly by reacting to what they read.

I don't know how they are spread on your bot. But I'd picture a layout like this:

middle sensor3 stays over black line should read black always and if not we've gone off the end of the line and need to rotate 180 to get back on.

then inner left2 and right4 should read half black half white to indicate edges of straight line.

then outer left1 and right5 should always read white and if they don't it indicates the line turns to the left or right.

just implement a switch case statement in main and have it loop through sensor readings and this.

You need to know what the sensor reads over a black surface and a white. And be consistent with those values in your case switches.

as in:

init

main() {

while(1) {

read sensor1:read sensor2:read sensor3:read sensor4:read sensor5

switch

case sensor1 = white & sensor2 = white & sensor3 = black & sensor4 = white & sensor5 = white

 mov_forward()

 break

case sensor1 = white & sensor2 = black & sensor3 = black & sensor4 = white & sensor5 = white

 mov_left()

 break

case sensor1 = white & sensor2 = white & sensor3 = black & sensor4 = black & sensor5 = white

 mov_right()

 break 

 }

}

just add  a couple or more cases to implement when sensor 1 or 5 indicate black to implement 90 degree turns. Being that I picture the sensors arrayed from left to right = 1 to 5.  I say a couple or more cases for the final states because you do need to distinguish between just straying a bit from the line or a true turn left or right.

I hope this makes sense as its all in my head. The only line follower I've built was a kit. But I'm pretty sure it's right.

 

Hello all,

Oh yes those turn_right() calls should be turn_left() calls. I think the problem was with using a counter of 150 which I originally thought was like 2 seconds but is actually only like a few milliseconds. So the program kept jumping back to main and executed this function like a 100 times when it should only execute it once.

Yup... I'm still not sure what would be the best method to make it turn. I'm thinking of making a delay to throw the rover off the black line and then make it turn until it senses a black line again.

then I am amazed it does a left turn at all. I don't see a call to the turn_left() function in main. You have only called turn_right()  for 3 different sets of sensor readings values. Should one of those calls been turn_left() ?

Hi,


OK. I've gone through my code and I can barely work on it any longer due to this problem. If anyone can point out what's wrong it would be great. Thank you.

// Use Microprocessor as a switch

//Motor1-left Motor2-right
//resistor1- left resistor2-right  (for correction)
//resistor3- left resistor4- right (for turning)



#include <qic.h>
#include <qicextio.h>

void initialize(){
set_tris_b(0b00000000);
set_tris_c(0b00000000);
set_tris_d(0b11111111);
setup_ccp1(CCP_PWM);
setup_ccp2(CCP_PWM);
setup_timer_2(T2_DIV_BY_1, 255, 1);
setup_adc_ports(ALL_ANALOG);
setup_adc(ADC_CLOCK_DIV_32);
port_b_pullups(TRUE);
}


void turn_left();
void turn_right();
void rotate();

long int a=1;



void main(){

long int resistor1, resistor2, microswitch, motor1, motor2, resistor3, resistor4, resistor5, counter;
initialize();


//initial value

while(1){

output_low(pin_c3);
output_low(pin_c0);
set_pwm1_duty(750);
set_pwm2_duty(750);
// 830 785


set_adc_channel(0);
delay_us(10);
resistor1=read_adc();

set_adc_channel(1);
delay_us(10);
resistor2=read_adc();

set_adc_channel(3);
delay_us(10);
resistor3=read_adc();

set_adc_channel(4);
delay_us(10);
resistor4=read_adc();

//if(resistor1<90 && resistor2<38 && resistor3<40 && resistor4<28) {
//rotate();
//}

set_adc_channel(2);
delay_us(10);
resistor5=read_adc();

/*
if(resistor5<36) {
rotate();
}
*/

if(resistor4>50) {
turn_right();
}




//CORRECTION FACTOR
//Goes to left

while(resistor1<48) {

/*
if(resistor5<36) {
rotate();
}
*/

if(resistor4>60) {
turn_right();
}

set_pwm1_duty(830);

set_adc_channel(0);
delay_us(10);
resistor1=read_adc();

set_adc_channel(4);
delay_us(10);
resistor4=read_adc();

set_adc_channel(2);
delay_us(10);
resistor5=read_adc();

}


//Goes to right
while( resistor2<20  ) {

/*
if(resistor5<31) {
rotate();
}
*/

if(resistor4>60) {
turn_right();
}

set_pwm2_duty(830);


set_adc_channel(1);
delay_us(10);
resistor2=read_adc();

set_adc_channel(4);
delay_us(10);
resistor4=read_adc();

set_adc_channel(2);
delay_us(10);
resistor5=read_adc();
}

    } //while

}




void turn_left() {
//counter runs in background (delay?) and as soon resistor1>100 it stops at which point resistor r3>33.
//It starts turning when r1<100 and r3>33 and soon as r1>100 it stops. *Position of sensor 3 in front is crucial.
long int counter, resistor1;
// resistor 2 was ok


for(counter=0; counter<=150; counter++) {
//Motor 1 (output high)
output_high(pin_c3);
set_pwm1_duty(700);

//Motor 2
output_low(pin_c0);
set_pwm2_duty(1000);

}}

void rotate() {
long int counter, resistor1;
// resistor 2 was ok


for(counter=0; counter<=300; counter++) {
//Motor 1 (output high)
output_high(pin_c3);
set_pwm1_duty(1000);

//Motor 2
output_low(pin_c0);
set_pwm2_duty(1000);

}}

void turn_right() {
long int counter, resistor1;

for(counter=0; counter<=50; counter++) {
//Motor 1 (output high)
output_low(pin_c3);
set_pwm1_duty(1000);

//Motor 2
output_high(pin_c0);
set_pwm2_duty(700);
}}



I think where you have said compile you meant to say execute. The c compiles once and is downloaded to the pic as machine code. If the compiler stops before its done compiling it will be because of an error or errors in the code which should be visible in the output window.

As for the unexpected behaviour. In my limited experience with c/c++ I have found most times unexpected behaviour is caused by a missing brace or brace in the wrong place. As you have not supplied a complete lisiting it is hard to tell if this is the case here.

The code looks ok from what you posted. Have you thought of doing away with the counter and just turning left until a change of state with the sensor. Which is what it sounds like its doing anyway. It's trying to tell you this is the way I want to do do it. Heheh.

 

 

If you would like more help, attach the program in its entirety to to this post.

Well, I'm not using interrupts or multi-tasking. Infact I just heard of them now. It seems that turn_left() runs in the backgound until the conditions are true i.e. if(resistor3>60).

Is there such a thing is aluto multitasking where the compiler makes decisions based on the time required to execute a function?

I guess I'll try debugging it more. It's just annoying. I have the code and I know what it does but can't explain why.

...

It does process sequentially unless your doing multi-threaded stuff or possibly using interrupts - which I doubt you are.

If you have a question about a piece of code, try debugging it and removing as much code as you can from around it.  Simplify it to only the problem and you might discover the real issue.

Is there any debugging or simulator you can use?