Project

General

Profile

Statistics
| Revision:

root / trunk / code / projects / activities_fair_demo / lineFollow.c @ 2001

History | View | Annotate | Download (12.2 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 2
27

    
28
//! Anything lower than this value is white
29
int GREY_THRESHOLD = 300;
30
//! Anything higher than this value is black
31
int BLACK_THRESHOLD = 750;
32

    
33
int countHi = 0;
34
int countLo = 0;
35
int maxAvg, avg;
36

    
37
// Everything has a dimension of 2 for left and right readings
38
int barCode[2][ CODESIZE ];
39
int barCodePosition[2]={0};
40

    
41
int duration[2] = {0};
42
int lastColor[2] = {0};
43
char isReset[2] = {1};
44
int lastReadings[2][ NUM_READINGS ] = {{0}};
45
int lastReadingsPtr[2] = {0};
46
int numLast[2][4] = { {0, 0, 0, NUM_READINGS}, {0, 0, 0, NUM_READINGS} };
47
int bitColor[2] = {0};
48

    
49
int turnDistance=0;
50
//! Counts the number of full line readings before we determine an intersection
51
int intersectionFilter=0;
52
int disableBarCode=0;
53

    
54
//! Keeps track of where the encoder of one motor started, for use in turns.
55
int encoderStart = -1;
56
int encoderReset = 0;   // 0 if encoderStart has no value set
57

    
58

    
59
void lineFollow_init()
60
{
61
    int i, j, curReading;
62
    int lowGrey = 1000, highGrey = 0, lowBlack = 1000,
63
        highBlack = 0;
64

    
65
        analog_init(0);
66
    encoders_init();
67
        lost = 0;
68
        intersectionFilter=0;
69
        disableBarCode=0;
70

    
71
    for(i=0; i<2; i++)
72
    {
73
        for(j=0; j<NUM_READINGS; j++)
74
        {
75
            lastReadings[i][j] = BAD_READING;
76
        }
77
        isReset[i] = 1;
78
    }
79
    
80
    // Calibrate thresholds
81
    orb_set_color(YELLOW);
82
    delay_ms(2000);
83

    
84
    orb_set_color(BLUE);
85

    
86
    for(i=0; i<100; i++)
87
    {
88
        curReading = read_line(LEFT_SENSOR + 6);
89
        if(curReading < lowGrey)
90
            lowGrey = curReading;
91
        if(curReading > highGrey)
92
            highGrey = curReading;
93

    
94
        delay_ms(20);
95
    }
96

    
97
    orb_set_color(YELLOW);
98
    delay_ms(2000);
99

    
100
    orb_set_color(GREEN);
101

    
102
    for(i=0; i<100; i++)
103
    {
104
        curReading = read_line(LEFT_SENSOR + 6);
105
        if(curReading < lowBlack)
106
            lowBlack = curReading;
107
        if(curReading > highBlack)
108
            highBlack = curReading;
109

    
110
        delay_ms(20);
111
    }
112

    
113
    orbs_set(0,0,0,0,0,0);
114

    
115
    GREY_THRESHOLD = (lowGrey + highGrey) / 2;
116
    BLACK_THRESHOLD = (highBlack + lowBlack) / 2;
117

    
118
    usb_puts("Grey: ");
119
    usb_puti(lowGrey);
120
    usb_puts(", ");
121
    usb_puti(highGrey);
122
    usb_puts("\nBlack: ");
123
    usb_puti(lowBlack);
124
    usb_puts(", ");
125
    usb_puti(highBlack);
126
    usb_puts("\nThresholds: ");
127
    usb_puti(GREY_THRESHOLD);
128
    usb_puts(", ");
129
    usb_puti(BLACK_THRESHOLD);
130
    usb_puts("\n\n");
131

    
132
    delay_ms(1500);
133

    
134
    //numLast = { {0, 0, 0, NUM_READINGS}, {0, 0, 0, NUM_READINGS} };
135
}
136

    
137

    
138
/** 
139
 * Follows a line at the given speed.
140
 * @param speed The speed with which to follow the line.
141
 */
142
int lineFollow(int speed)
143
{
144
        int colors[5];
145
        int position;
146
        
147

    
148
        updateLine(colors);
149
        position = lineLocate(colors);         
150
        
151
        //not on line
152
        if(position == NOLINE)
153
        {
154
                if(lost++ > 20)
155
                {
156
                        orb2_set_color(GREEN);
157
                        motors_off();
158
                        return LINELOST;
159
                }
160
        }
161
        else if(position == FULL_LINE)
162
        {
163
                if(intersectionFilter++ > 4)
164
                {
165
                        orb2_set_color(RED);
166
                        barCodePosition[0]=0;
167
            barCodePosition[1]=0;
168
                        disableBarCode=50;
169
                }
170
        }
171
        //on line
172
        else
173
        {
174
                position*=30;
175
                orb2_set_color(ORB_OFF);
176
                motorLeft(min(speed+position, 255));
177
                motorRight(min(speed-position, 255));
178
                lost=0;
179
                intersectionFilter=0;
180
        }
181

    
182
    // If we're running over a line, stop reading barcodes for a sec
183
        if(disableBarCode-- > 0)
184
        {
185
        // Return intersection once we cross the line
186
                if(disableBarCode) return NOBARCODE;
187
                return INTERSECTION;
188
        }
189
        updateBarCode();
190
        return getBarCode();
191
}
192

    
193

    
194
/**
195
 * Implements the left merge, assuming a line exists to the left.  Works by
196
 * turning off the line at an increasing angle and waiting to hit another line
197
 * on the left.
198
 */
199
int mergeLeft()
200
{
201
        motor_l_set(FORWARD, 200);
202
        if(turnDistance!=21)motor_r_set(FORWARD, 230);
203
        else motor_r_set(FORWARD, 210);
204
        int colors[5];
205
        updateLine(colors);
206
        int position = lineLocate(colors);
207
        if(position>3 || position<-3)turnDistance++;
208

    
209
        if(turnDistance>20)
210
        {
211
        turnDistance=21;
212
        
213
                if(position<3 && position>-3)
214
                {
215
                        turnDistance = 0;
216
                        return 0;
217
                }        
218
        }
219
        return 1;
220
}
221

    
222

    
223
/**
224
 * Implements the right merge, assuming a line exists to the right.  Works by
225
 * turning off the line at an increasing angle and waiting to hit another line
226
 * on the right.
227
 */
228
int mergeRight()
229
{
230
        motor_r_set(FORWARD, 200);
231
        if(turnDistance!=21)motor_l_set(FORWARD, 230);
232
        else motor_l_set(FORWARD, 210);
233
        int colors[5];
234
        updateLine(colors);
235
        int position = lineLocate(colors);
236
        if(position>3 || position<-3)turnDistance++;
237

    
238
        if(turnDistance>20)
239
        {
240
        turnDistance=21;
241

    
242
                if(position<3 && position>-3)
243
                {
244
                        turnDistance = 0;
245
                        return 0;
246
                } 
247
        }
248
        return 1;
249
}
250

    
251

    
252

    
253
/**
254
 * Turns left at a cross of two lines.  Assumes that we are at lines in a cross
255
 * pattern, and turns until it sets straight on the new line.
256
 * @return 0 if turn finishes otherwise return 1
257
 */
258
int turnLeft()
259
{
260
        /*motor_l_set(BACKWARD, 200);
261
        motor_r_set(FORWARD, 200);
262
        int colors[5];
263
        updateLine(colors);
264
        int position = lineLocate(colors);
265
        if(position>2 || position<-2)turnDistance++;
266
        if(turnDistance>1)
267
        {
268
                if(position<3 && position>-3)
269
                {
270
                        turnDistance = 0;
271
                         return 0;
272
                }
273
        }
274
        return 1;*/
275

    
276
        motor_l_set(BACKWARD,200);
277
        motor_r_set(FORWARD,200);
278
        if(!encoderReset)
279
        {
280
            encoderStart = encoder_get_x(RIGHT);
281
            encoderReset = 1;
282
        }
283

    
284
        if(encoder_get_x(RIGHT) < encoderStart)
285
        {
286
            encoderStart = 0;
287
            // Temporary: display an "error message" in case of overflow.
288
            // Using this for debugging, take it out soon!
289
            motor_l_set(FORWARD,0);
290
            motor_r_set(FORWARD,0);
291
            //orb_set_color(WHITE);
292
            delay_ms(2000);
293
        }
294

    
295
        if(encoder_get_x(RIGHT) - encoderStart > 300)
296
        {
297
            encoderReset = 0;
298
            return 0;
299
        }
300
        return 1;
301
}
302

    
303

    
304

    
305
/**
306
 * Turns right at a cross of two lines.  Assumes that we are at lines in a cross
307
 * pattern, and turns until it sets straight on the new line.
308
 * @return 0 if the turn finishes otherwise return 1
309
 */
310
int turnRight()
311
{
312
        motor_r_set(BACKWARD, 200);
313
        motor_l_set(FORWARD, 200);
314
        int colors[5];
315
        updateLine(colors);
316
        int position = lineLocate(colors);
317
        if(position>2 || position<-2)turnDistance++;
318
        if(turnDistance>1) 
319
        {
320
                if(position<3 && position>-3)
321
                {
322
                        turnDistance = 0;
323
                         return 0;
324
                }
325
        }
326
        return 1;
327
}
328

    
329

    
330

    
331
int getBarCode()
332
{
333
        if(barCodePosition[1] != CODESIZE)
334
        return NOBARCODE ;
335
    else
336
    {
337
        int temp = 0;
338
        for(int i=0; i<CODESIZE; i++)
339
            temp += (barCode[1][i] << i);
340
        barCodePosition[1] = 0;
341
        return temp;
342
    }
343
}
344

    
345

    
346

    
347
void updateLine(int* values)
348
{        
349
        for(int i = 0; i<5; i++)
350
                values[i] = (read_line(4-i) < LINE_COLOR ? LWHITE : LBLACK);
351
}
352

    
353

    
354

    
355
int lineLocate(int* colors)
356
{
357
        int i;
358
        int wsum = 0;
359
        int count=0;
360

    
361
        for(i = 0; i<5; i++)
362
        {
363
                count += colors[i]/2;
364
                wsum += (i)*colors[i];
365
        }
366
        if(count==0)
367
                return NOLINE;        
368
        if(count==5)
369
                return FULL_LINE;
370
        return (wsum/count)-4; // Subtract 4 to center the index around the center.
371
}
372

    
373
void printBuckets(int i)
374
{
375
    int j;
376

    
377
    usb_puts("LP[");
378
    usb_puti(i);
379
    usb_puts("]: ");
380
    usb_puti(lastReadingsPtr[i]);
381
    usb_puts(", Totals: ");
382

    
383
    for(int j=0; j<=3; j++)
384
    {
385
        usb_puts("[");
386
        usb_puti(j);
387
        usb_puts("]: ");
388
        usb_puti(numLast[i][j]);
389
        usb_puts(" ");
390
    }
391
    usb_puts("\t ");
392
    usb_puts("\n");
393
}
394

    
395
void addToBuckets(int curColor, int i)
396
{
397
    int oldest = lastReadings[i][lastReadingsPtr[i]];
398
    numLast[i][oldest]--;
399
    lastReadings[i][lastReadingsPtr[i]] = curColor;
400
    lastReadingsPtr[i] = (lastReadingsPtr[i]+1) % NUM_READINGS;
401
    numLast[i][curColor]++;
402

    
403
    if(DBG_LINEFOLLOW == 1)
404
    {
405
        printBuckets(i);
406
    }
407
}
408

    
409
void updateBarCode()
410
{
411
    // USING THESE GLOBAL VARIABLES
412
    // global int duration = 0;
413
    // global int lastColor = 0;
414
    // global int barCodePosition = 0;
415
    // global char isReset = 0;
416

    
417
    // Just uses one sensor for now
418
    for(int i = /*RIGHT*/LEFT_SENSOR; i <= LEFT_SENSOR; i++)
419
    {
420
        // Add 6 to convert left (1) and right (0) to sensor 6 and 7
421
        int curReading = read_line(i + 6);
422
        int curColor;
423

    
424
        if(curReading > BLACK_THRESHOLD)
425
        {
426
            curColor = LBLACK;
427
        }
428
        else if(curReading < GREY_THRESHOLD)
429
        {
430
            curColor = LWHITE;
431
        }
432
        else
433
        {
434
            curColor = LGREY;
435
        }
436

    
437
        // Keep track of this reading
438
        addToBuckets(curColor, i);
439

    
440
        // Just an error check
441
        if(barCodePosition[i] > CODESIZE)
442
        {
443
            barCodePosition[i] = 0;
444
        }
445

    
446
        // We now edit curColor to use the majority of the last buckets.
447
        if(numLast[i][1] > NUM_READINGS / 2)
448
        {
449
            curColor = LGREY;
450
        }
451
        else if(numLast[i][2] > NUM_READINGS / 2)
452
        {
453
            curColor = LBLACK;
454
        }
455
        else if(numLast[i][0] > NUM_READINGS / 2)
456
        {
457
            curColor = LWHITE;
458
            duration[i]++;
459
        }
460
        else
461
        {
462
            curColor = BAD_READING;
463
        }
464

    
465
        // Print out the current reading and label, if in debug mode
466
        if(DBG_LINEFOLLOW == 2)
467
        {
468
            switch(curColor)
469
            {
470
                case LBLACK: usb_puts("LBLACK.\t"); break;
471
                case LGREY: usb_puts("LGREY.\t\t"); break;
472
                case LWHITE: usb_puts("LWHITE.\t\t\t"); break;
473
            }
474
            usb_puti(curReading);
475
            usb_puts("\n");
476
        }
477
    
478
        if(curColor != BAD_READING)
479
        {
480
            // Now we assume our reading is significant - a bit, or a white space
481

    
482
            if(bitColor[i] == LGREY || bitColor[i] == LWHITE)
483
                bitColor[i] = curColor;
484

    
485
            // Only read a value if we have read 0 first (isReset == 1)
486
            if(isReset[i] && (curColor == LBLACK || curColor == LGREY) )
487
            {
488
                isReset[i] = 0;
489
                duration[i] = 0;
490
            }
491
            else if(curColor == LWHITE)
492
            {
493
                if(!isReset[i])
494
                {
495
                    barCode[i][barCodePosition[i]++] = 
496
                        (bitColor[i] == LBLACK) ? 1 : 0;
497
                    usb_puts("Reset. Read bit: ");
498
                    usb_puts(((bitColor[i] == LBLACK) ? "BLACK" : "GREY"));
499
                    usb_puts("\t");
500
                    usb_puts(((curColor==LWHITE) ? "LWHITE" : "NOTWHITE"));
501
                    usb_puts("\n");
502

    
503
                    bitColor[i] = LWHITE;
504
                }
505
                isReset[i] = 1;
506
                orb_set(0, 0, 0);
507
            }
508
        }
509

    
510
        if(curColor == LWHITE && duration[i] > TIMEOUT_DURATION 
511
            && barCodePosition[i] != 0)
512
        {
513
            usb_puts("TIMED OUT. BARCODE READER RESET.\n");
514
            usb_puts("Encoders: ");
515
            usb_puti(encoder_get_dx(LEFT));
516
            usb_puts(", ");
517
            usb_puti(encoder_get_dx(RIGHT));
518
            usb_puts("\n");
519
            barCodePosition[i] = 0;
520
            duration[i] = 0;
521
            isReset[i] = 1;
522
        }
523
    }
524
}
525

    
526

    
527
int min(int x, int y){return x>y ? y : x;}
528
int max(int x, int y){return x<y ? y : x;}
529

    
530
void motorLeft(int speed){
531
        ((speed-=127)>=0)?motor_l_set(FORWARD, 160+speed*95/128):motor_l_set(BACKWARD, 160-speed*95/127);
532
}
533

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