Project

General

Profile

Statistics
| Revision:

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

History | View | Annotate | Download (10.8 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] = {0};
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
    }
71

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

    
75

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

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

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

    
131

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

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

    
160

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

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

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

    
189

    
190

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

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

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

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

    
241

    
242

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

    
267

    
268

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

    
283

    
284

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

    
291

    
292

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

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

    
311
void addToBuckets(int curColor, int i)
312
{
313
    int oldest = lastReadings[i][lastReadingsPtr[i]];
314
    numLast[i][oldest]--;
315
    lastReadings[i][lastReadingsPtr[i]] = curColor;
316
    lastReadingsPtr[i] = (lastReadingsPtr[i]+1) % NUM_READINGS;
317
    numLast[i][curColor]++;
318

    
319
    if(DBG_LINEFOLLOW == 1)
320
    {
321
        usb_puts("LP[");
322
        usb_puti(i);
323
        usb_puts("]: ");
324
        usb_puti(lastReadingsPtr[i]);
325
        usb_puts(", oldest: ");
326
        usb_puti(oldest);
327
        usb_puts(", Totals: ");
328

    
329
        for(int j=0; j<=3; j++)
330
        {
331
            usb_puts("[");
332
            usb_puti(j);
333
            usb_puts("]: ");
334
            usb_puti(numLast[i][j]);
335
            usb_puts(" ");
336
        }
337
        usb_puts("\t ");
338
        usb_puts("\n");
339
    }
340
}
341

    
342
void updateBarCode()
343
{
344
    // USING THESE GLOBAL VARIABLES
345
    // global int duration = 0;
346
    // global int lastColor = 0;
347
    // global int barCodePosition = 0;
348
    // global char isReset = 0;
349

    
350
    // Just uses one sensor for now
351
    for(int i = /*RIGHT*/LEFT_SENSOR; i <= LEFT_SENSOR; i++)
352
    {
353
        // Add 6 to convert left (1) and right (0) to sensor 6 and 7
354
        int curReading = read_line(i + 6);
355
        int curColor;
356

    
357
        if(curReading > BLACK_THRESHOLD)
358
        {
359
            curColor = LBLACK;
360
        }
361
        else if(curReading < GREY_THRESHOLD)
362
        {
363
            curColor = LWHITE;
364
        }
365
        else
366
        {
367
            curColor = LGREY;
368
        }
369

    
370
        // Keep track of this reading
371
        addToBuckets(curColor, i);
372

    
373
        // Just an error check
374
        if(barCodePosition[i] > CODESIZE)
375
        {
376
            barCodePosition[i] = 0;
377
        }
378

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

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

    
415
            if(bitColor[i] == LGREY || bitColor[i] == LWHITE)
416
                bitColor[i] = curColor;
417

    
418
            // Only read a value if we have read 0 first (isReset == 1)
419
            if(isReset[i] && (curColor == LBLACK || curColor == LGREY) )
420
            {
421
                isReset[i] = 0;
422
                duration[i] = 0;
423
            }
424
            else if(curColor == LWHITE)
425
            {
426
                if(!isReset[i])
427
                {
428
                    barCode[i][barCodePosition[i]++] = 
429
                        (bitColor[i] == LBLACK) ? 1 : 0;
430
                    usb_puts("Reset. Read bit: ");
431
                    usb_puts(((bitColor[i] == LBLACK) ? "BLACK" : "GREY"));
432
                    usb_puts("\n");
433

    
434
                    bitColor[i] = LWHITE;
435
                }
436
                isReset[i] = 1;
437
                orb_set(0, 0, 0);
438
            }
439
        }
440

    
441
        if(curColor == LWHITE && duration[i] > TIMEOUT_DURATION 
442
            && barCodePosition[i] != 0)
443
        {
444
            usb_puts("TIMED OUT. BARCODE READER RESET.\n");
445
            usb_puts("Encoders: ");
446
            usb_puti(encoder_get_dx(LEFT));
447
            usb_puts(", ");
448
            usb_puti(encoder_get_dx(RIGHT));
449
            usb_puts("\n");
450
            barCodePosition[i] = 0;
451
            duration[i] = 0;
452
            isReset[i] = 1;
453
        }
454
    }
455
}
456

    
457

    
458
int min(int x, int y){return x>y ? y : x;}
459
int max(int x, int y){return x<y ? y : x;}
460

    
461
void motorLeft(int speed){
462
        ((speed-=127)>=0)?motor_l_set(FORWARD, 160+speed*95/128):motor_l_set(BACKWARD, 160-speed*95/127);
463
}
464

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