Project

General

Profile

Statistics
| Revision:

root / trunk / code / projects / linefollowing / lineFollow.c @ 1985

History | View | Annotate | Download (10.9 KB)

1
/**
2
 * @file lineFollow.c
3
 * @defgroup lineFollwing Line Following
4
 *
5
 * Takes care of following a line. Running this program is done by calling the
6
 * init() function and then the lineFollow(speed) command.  However, direct use
7
 * of this class is discouraged as its behavior is used by lineDrive.c, which
8
 * extends this class to provide behavior functionality.
9
 *
10
 * @author Dan Jacobs and the Colony Project
11
 * @date 11-1-2010
12
 */
13

    
14
#include "lineFollow.h"
15

    
16
//! The number of bits expected in a barcode
17
#define CODESIZE 5 
18
#define LINE_COLOR 200
19

    
20
/**
21
 * Helps with debugging.
22
 * 0 - no debug output.
23
 * 1 - print out buckets
24
 * 2 - print out line sensor readings
25
 */
26
#define DBG_LINEFOLLOW 0
27

    
28
int countHi = 0;
29
int countLo = 0;
30
int maxAvg, avg;
31

    
32
// Everything has a dimension of 2 for left and right readings
33
int barCode[2][ CODESIZE ];
34
int barCodePosition[2]={0};
35

    
36
int duration[2] = {0};
37
int lastColor[2] = {0};
38
char isReset[2] = {1};
39
int lastReadings[2][ NUM_READINGS ] = {{0}};
40
int lastReadingsPtr[2] = {0};
41
int numLast[2][4] = { {0, 0, 0, NUM_READINGS}, {0, 0, 0, NUM_READINGS} };
42
int bitColor[2] = {0};
43

    
44
int turnDistance=0;
45
//! Counts the number of full line readings before we determine an intersection
46
int intersectionFilter=0;
47
int disableBarCode=0;
48

    
49
//! Keeps track of where the encoder of one motor started, for use in turns.
50
int encoderStart = -1;
51
int encoderReset = 0;   // 0 if encoderStart has no value set
52

    
53

    
54
void lineFollow_init()
55
{
56
    int i, j;
57

    
58
        analog_init(0);
59
    encoders_init();
60
        lost = 0;
61
        intersectionFilter=0;
62
        disableBarCode=0;
63

    
64
    for(i=0; i<2; i++)
65
    {
66
        for(j=0; j<NUM_READINGS; j++)
67
        {
68
            lastReadings[i][j] = BAD_READING;
69
        }
70
        isReset[i] = 1;
71
    }
72

    
73
    //numLast = { {0, 0, 0, NUM_READINGS}, {0, 0, 0, NUM_READINGS} };
74
}
75

    
76

    
77
/** 
78
 * Follows a line at the given speed.
79
 * @param speed The speed with which to follow the line.
80
 */
81
int lineFollow(int speed)
82
{
83
        int colors[5];
84
        int position;
85
        
86

    
87
        updateLine(colors);
88
        position = lineLocate(colors);         
89
        
90
        //not on line
91
        if(position == NOLINE)
92
        {
93
                if(lost++ > 20)
94
                {
95
                        orb2_set_color(GREEN);
96
                        motors_off();
97
                        return LINELOST;
98
                }
99
        }
100
        else if(position == FULL_LINE)
101
        {
102
                if(intersectionFilter++ > 4)
103
                {
104
                        orb2_set_color(RED);
105
                        barCodePosition[0]=0;
106
            barCodePosition[1]=0;
107
                        disableBarCode=50;
108
                }
109
        }
110
        //on line
111
        else
112
        {
113
                position*=30;
114
                orb2_set_color(ORB_OFF);
115
                motorLeft(min(speed+position, 255));
116
                motorRight(min(speed-position, 255));
117
                lost=0;
118
                intersectionFilter=0;
119
        }
120

    
121
    // If we're running over a line, stop reading barcodes for a sec
122
        if(disableBarCode-- > 0)
123
        {
124
        // Return intersection once we cross the line
125
                if(disableBarCode) return NOBARCODE;
126
                return INTERSECTION;
127
        }
128
        updateBarCode();
129
        return getBarCode();
130
}
131

    
132

    
133
/**
134
 * Implements the left merge, assuming a line exists to the left.  Works by
135
 * turning off the line at an increasing angle and waiting to hit another line
136
 * on the left.
137
 */
138
int mergeLeft()
139
{
140
        motor_l_set(FORWARD, 200);
141
        if(turnDistance!=21)motor_r_set(FORWARD, 230);
142
        else motor_r_set(FORWARD, 210);
143
        int colors[5];
144
        updateLine(colors);
145
        int position = lineLocate(colors);
146
        if(position>3 || position<-3)turnDistance++;
147

    
148
        if(turnDistance>20)
149
        {
150
        turnDistance=21;
151
        
152
                if(position<3 && position>-3)
153
                {
154
                        turnDistance = 0;
155
                        return 0;
156
                }        
157
        }
158
        return 1;
159
}
160

    
161

    
162
/**
163
 * Implements the right merge, assuming a line exists to the right.  Works by
164
 * turning off the line at an increasing angle and waiting to hit another line
165
 * on the right.
166
 */
167
int mergeRight()
168
{
169
        motor_r_set(FORWARD, 200);
170
        if(turnDistance!=21)motor_l_set(FORWARD, 230);
171
        else motor_l_set(FORWARD, 210);
172
        int colors[5];
173
        updateLine(colors);
174
        int position = lineLocate(colors);
175
        if(position>3 || position<-3)turnDistance++;
176

    
177
        if(turnDistance>20)
178
        {
179
        turnDistance=21;
180

    
181
                if(position<3 && position>-3)
182
                {
183
                        turnDistance = 0;
184
                        return 0;
185
                } 
186
        }
187
        return 1;
188
}
189

    
190

    
191

    
192
/**
193
 * Turns left at a cross of two lines.  Assumes that we are at lines in a cross
194
 * pattern, and turns until it sets straight on the new line.
195
 * @return 0 if turn finishes otherwise return 1
196
 */
197
int turnLeft()
198
{
199
        /*motor_l_set(BACKWARD, 200);
200
        motor_r_set(FORWARD, 200);
201
        int colors[5];
202
        updateLine(colors);
203
        int position = lineLocate(colors);
204
        if(position>2 || position<-2)turnDistance++;
205
        if(turnDistance>1)
206
        {
207
                if(position<3 && position>-3)
208
                {
209
                        turnDistance = 0;
210
                         return 0;
211
                }
212
        }
213
        return 1;*/
214

    
215
        motor_l_set(BACKWARD,200);
216
        motor_r_set(FORWARD,200);
217
        if(!encoderReset)
218
        {
219
            encoderStart = encoder_get_x(RIGHT);
220
            encoderReset = 1;
221
        }
222

    
223
        if(encoder_get_x(RIGHT) < encoderStart)
224
        {
225
            encoderStart = 0;
226
            // Temporary: display an "error message" in case of overflow.
227
            // Using this for debugging, take it out soon!
228
            motor_l_set(FORWARD,0);
229
            motor_r_set(FORWARD,0);
230
            //orb_set_color(WHITE);
231
            delay_ms(2000);
232
        }
233

    
234
        if(encoder_get_x(RIGHT) - encoderStart > 300)
235
        {
236
            encoderReset = 0;
237
            return 0;
238
        }
239
        return 1;
240
}
241

    
242

    
243

    
244
/**
245
 * Turns right at a cross of two lines.  Assumes that we are at lines in a cross
246
 * pattern, and turns until it sets straight on the new line.
247
 * @return 0 if the turn finishes otherwise return 1
248
 */
249
int turnRight()
250
{
251
        motor_r_set(BACKWARD, 200);
252
        motor_l_set(FORWARD, 200);
253
        int colors[5];
254
        updateLine(colors);
255
        int position = lineLocate(colors);
256
        if(position>2 || position<-2)turnDistance++;
257
        if(turnDistance>1) 
258
        {
259
                if(position<3 && position>-3)
260
                {
261
                        turnDistance = 0;
262
                         return 0;
263
                }
264
        }
265
        return 1;
266
}
267

    
268

    
269

    
270
int getBarCode()
271
{
272
        if(barCodePosition[1] != CODESIZE)
273
        return NOBARCODE ;
274
    else
275
    {
276
        int temp = 0;
277
        for(int i=0; i<CODESIZE; i++)
278
            temp += (barCode[1][i] << i);
279
        barCodePosition[1] = 0;
280
        return temp;
281
    }
282
}
283

    
284

    
285

    
286
void updateLine(int* values)
287
{        
288
        for(int i = 0; i<5; i++)
289
                values[i] = (read_line(4-i) < LINE_COLOR ? LWHITE : LBLACK);
290
}
291

    
292

    
293

    
294
int lineLocate(int* colors)
295
{
296
        int i;
297
        int wsum = 0;
298
        int count=0;
299

    
300
        for(i = 0; i<5; i++)
301
        {
302
                count += colors[i]/2;
303
                wsum += (i)*colors[i];
304
        }
305
        if(count==0)
306
                return NOLINE;        
307
        if(count==5)
308
                return FULL_LINE;
309
        return (wsum/count)-4; // Subtract 4 to center the index around the center.
310
}
311

    
312
void printBuckets(int i)
313
{
314
    int j;
315

    
316
    usb_puts("LP[");
317
    usb_puti(i);
318
    usb_puts("]: ");
319
    usb_puti(lastReadingsPtr[i]);
320
    usb_puts(", Totals: ");
321

    
322
    for(int j=0; j<=3; j++)
323
    {
324
        usb_puts("[");
325
        usb_puti(j);
326
        usb_puts("]: ");
327
        usb_puti(numLast[i][j]);
328
        usb_puts(" ");
329
    }
330
    usb_puts("\t ");
331
    usb_puts("\n");
332
}
333

    
334
void addToBuckets(int curColor, int i)
335
{
336
    int oldest = lastReadings[i][lastReadingsPtr[i]];
337
    numLast[i][oldest]--;
338
    lastReadings[i][lastReadingsPtr[i]] = curColor;
339
    lastReadingsPtr[i] = (lastReadingsPtr[i]+1) % NUM_READINGS;
340
    numLast[i][curColor]++;
341

    
342
    if(DBG_LINEFOLLOW == 1)
343
    {
344
        printBuckets(i);
345
    }
346
}
347

    
348
void updateBarCode()
349
{
350
    // USING THESE GLOBAL VARIABLES
351
    // global int duration = 0;
352
    // global int lastColor = 0;
353
    // global int barCodePosition = 0;
354
    // global char isReset = 0;
355

    
356
    // Just uses one sensor for now
357
    for(int i = /*RIGHT*/LEFT_SENSOR; i <= LEFT_SENSOR; i++)
358
    {
359
        // Add 6 to convert left (1) and right (0) to sensor 6 and 7
360
        int curReading = read_line(i + 6);
361
        int curColor;
362

    
363
        if(curReading > BLACK_THRESHOLD)
364
        {
365
            curColor = LBLACK;
366
        }
367
        else if(curReading < GREY_THRESHOLD)
368
        {
369
            curColor = LWHITE;
370
        }
371
        else
372
        {
373
            curColor = LGREY;
374
        }
375

    
376
        // Keep track of this reading
377
        addToBuckets(curColor, i);
378

    
379
        // Just an error check
380
        if(barCodePosition[i] > CODESIZE)
381
        {
382
            barCodePosition[i] = 0;
383
        }
384

    
385
        // We now edit curColor to use the majority of the last buckets.
386
        if(numLast[i][1] > NUM_READINGS / 2)
387
        {
388
            curColor = LGREY;
389
        }
390
        else if(numLast[i][2] > NUM_READINGS / 2)
391
        {
392
            curColor = LBLACK;
393
        }
394
        else if(numLast[i][0] > NUM_READINGS / 2)
395
        {
396
            curColor = LWHITE;
397
            duration[i]++;
398
        }
399
        else
400
        {
401
            curColor = BAD_READING;
402
        }
403

    
404
        // Print out the current reading and label, if in debug mode
405
        if(DBG_LINEFOLLOW == 2)
406
        {
407
            switch(curColor)
408
            {
409
                case LBLACK: usb_puts("LBLACK.\t"); break;
410
                case LGREY: usb_puts("LGREY.\t\t"); break;
411
                case LWHITE: usb_puts("LWHITE.\t\t\t"); break;
412
            }
413
            usb_puti(curReading);
414
            usb_puts("\n");
415
        }
416
    
417
        if(curColor != BAD_READING)
418
        {
419
            // Now we assume our reading is significant - a bit, or a white space
420

    
421
            if(bitColor[i] == LGREY || bitColor[i] == LWHITE)
422
                bitColor[i] = curColor;
423

    
424
            // Only read a value if we have read 0 first (isReset == 1)
425
            if(isReset[i] && (curColor == LBLACK || curColor == LGREY) )
426
            {
427
                isReset[i] = 0;
428
                duration[i] = 0;
429
            }
430
            else if(curColor == LWHITE)
431
            {
432
                if(!isReset[i])
433
                {
434
                    barCode[i][barCodePosition[i]++] = 
435
                        (bitColor[i] == LBLACK) ? 1 : 0;
436
                    usb_puts("Reset. Read bit: ");
437
                    usb_puts(((bitColor[i] == LBLACK) ? "BLACK" : "GREY"));
438
                    usb_puts("\t");
439
                    usb_puts(((curColor==LWHITE) ? "LWHITE" : "NOTWHITE"));
440
                    usb_puts("\n");
441

    
442
                    bitColor[i] = LWHITE;
443
                }
444
                isReset[i] = 1;
445
                orb_set(0, 0, 0);
446
            }
447
        }
448

    
449
        if(curColor == LWHITE && duration[i] > TIMEOUT_DURATION 
450
            && barCodePosition[i] != 0)
451
        {
452
            usb_puts("TIMED OUT. BARCODE READER RESET.\n");
453
            usb_puts("Encoders: ");
454
            usb_puti(encoder_get_dx(LEFT));
455
            usb_puts(", ");
456
            usb_puti(encoder_get_dx(RIGHT));
457
            usb_puts("\n");
458
            barCodePosition[i] = 0;
459
            duration[i] = 0;
460
            isReset[i] = 1;
461
        }
462
    }
463
}
464

    
465

    
466
int min(int x, int y){return x>y ? y : x;}
467
int max(int x, int y){return x<y ? y : x;}
468

    
469
void motorLeft(int speed){
470
        ((speed-=127)>=0)?motor_l_set(FORWARD, 160+speed*95/128):motor_l_set(BACKWARD, 160-speed*95/127);
471
}
472

    
473
void motorRight(int speed){
474
        ((speed-=127)>=0)?motor_r_set(FORWARD, 160+speed*95/128):motor_r_set(BACKWARD, 160-speed*95/127);
475
}