Caddy
A 2005 Roborodentia entry with vision and path planning capability
 All Data Structures Files Functions Variables Typedefs Macros Pages
line_tracking.c
1 /*
2  * This file is part of Caddy.
3  *
4  * Caddy is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * Caddy is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with Caddy. If not, see <http://www.gnu.org/licenses/>.
16  */
17 #include "line_tracking.h"
18 #include "motor_control.h"
19 #include "camera.h"
20 #include "tweak_data.h"
21 #include "tether_ui.h"
22 #include "lcd_16x2.h"
23 
24 // AVRLIB
25 #include "rprintf.h"
26 
27 // avr-libc
28 #include <stdint.h>
29 #include <stdbool.h>
30 
31 // Track BLACK line on WHITE background
32 #define LINE_RMIN 16
33 #define LINE_GMIN 16
34 #define LINE_BMIN 16
35 #define LINE_RMAX 50
36 #define LINE_GMAX 50
37 #define LINE_BMAX 40
38 
39 #define LINE_STAT_MASK 0Xf // Sends the mean, min, max, and pixel count
40 #define X_MEAN 0
41 #define X_MIN 1
42 #define X_MAX 2
43 #define LINE_COUNT 3
44 #define SCAN_WIDTH 4
45 
46 #define LINE_Y_MAX_NDX (LINE_STATS_ROWS - SCAN_WIDTH - 1)
47 #define JUNC_Y_MAX_NDX (LINE_STATS_ROWS - JUNC_SCAN_WIDTH - 1)
48 #define BALL_Y_MAX_NDX (LINE_STATS_ROWS - BALL_SCAN_WIDTH - 1)
49 
50 #define LINE_Y3 49
51 
52 #define JUNC_SCAN_WIDTH 1
53 #define BALL_SCAN_WIDTH 7
54 
55 // Global variables
56 volatile uint8_t lineStats[LINE_STATS_ROWS][LINE_STATS_COLS];
57 volatile bool lineStatsProcessed;
58 
59 int8_t junctionY;
60 static int16_t correction;
61 static int8_t possibleBallY;
62 
63 
64 void adjustPWM( void )
65 {
66  PWM_LEFT(MIN(MAX(l_base - correction, BASE_MIN), BASE_MAX));
67  PWM_RIGHT(MIN(MAX(r_base + correction, BASE_MIN), BASE_MAX));
68 
69 #if DEBUGGING
70  if (lcdMode == LINE_LCD_MODE)
71  {
72  lcdPrintHex(MIN(MAX(l_base - correction, BASE_MIN), BASE_MAX), 1, 11);
73  lcdPrintHex(MIN(MAX(r_base + correction, BASE_MIN), BASE_MAX), 1, 14);
74  }
75 #endif
76 }
77 
78 void trackLineInit(void)
79 {
80 #if DEBUGGING
81  // Clear LCD
82  if (lcdMode == LINE_LCD_MODE)
83  {
84  lcdWriteStr(" ", 0, 0);
85  lcdWriteStr(" ", 1, 0);
86  }
87 #endif
88 
89  // Assume we start on the center of the line and no junction in view
90  correction = 0;
91  junctionY = 0;
92 
93  lineStatsProcessed = true;
94 
95  // Initialize wheel speeds to forward, zero pwm
96  forward(BOTH_MOTORS, 0);
97  PWM_LEFT(l_base);
98  PWM_RIGHT(r_base);
99 
100  // Set Low Resolution
102  // Downsample the image
103  rprintf("DS %d %d\r", DS_X_LINE, DS_Y_LINE);
104  // set virtual window
105  rprintf("VW %d %d %d %d\r", VW_X1_LINE, VW_Y1_LINE, VW_X2_LINE, VW_Y2_LINE);
106 
107  // Line mode type 0, mode 2
108  // "per row statistics in the tracked region" p.41
109  rprintf("LM 0 2\r");
110  // Output mask hides everything but, mean p.45
111  rprintf("OM 5 %d\r", LINE_STAT_MASK);
112 
113  // Turn poll mode off so line packets can be streamed
114  rprintf("PM 0\r");
115 
116  // Start the tracking (Track Color)
117  rprintf("TC %d %d %d %d %d %d\r",
118  LINE_RMIN, LINE_RMAX, LINE_GMIN,
119  LINE_GMAX, LINE_BMIN, LINE_GMAX);
120 
121 }
122 
123 void restartLineMode( void )
124 {
125  rprintf("lm 0 2\r");
126  rprintf("PM 0\r");
127  rprintf("tc %d %d %d %d %d %d\r",
128  LINE_RMIN, LINE_RMAX, LINE_GMIN,
129  LINE_GMAX, LINE_BMIN, LINE_GMAX);
130 }
131 
132 
133 bool isGoodScan(uint8_t y)
134 {
135  uint8_t i = 0;
136 
137  for (i = y; i < y + SCAN_WIDTH; i++)
138  {
139  if (lineStats[i][X_MEAN] == 0 ||
140  lineStats[i][X_MIN] < VW_X1_LINE+5 ||
141  lineStats[i][X_MAX] > VW_X2_LINE-5 ||
142  lineStats[i][LINE_COUNT] > 9)
143  {
144  return false;
145  }
146  }
147  return true;
148 }
149 
150 bool isJunctionScan(uint8_t y)
151 {
152  uint8_t i = 0;
153 
154  for (i = y; i < y + JUNC_SCAN_WIDTH; i++)
155  {
156  if (lineStats[i][X_MEAN] == 0
157  ||
158  (lineStats[i][X_MIN] > VW_X1_LINE + 10 &&
159  lineStats[i][X_MAX] < VW_X2_LINE - 10)
160  ||
161  lineStats[i][LINE_COUNT] < 9)
162  {
163  return false;
164  }
165  }
166  return true;
167 }
168 
169 bool mayBeBall(uint8_t y)
170 {
171  uint8_t i = 0;
172 
173  for (i = y; i < y + BALL_SCAN_WIDTH; i++)
174  {
175  if (lineStats[i][X_MEAN] != 0)
176  {
177  return false;
178  }
179  }
180  return true;
181 }
182 
183 void analyzeLineStats(void)
184 {
185  uint8_t i;
186  uint8_t y;
187  uint16_t sum;
188 
189  uint8_t lineY1;
190  uint8_t lineX1;
191  uint8_t lineY2;
192  uint8_t lineX2;
193 
194  double m;
195  int8_t lineOffset;
196  static int16_t lineSlope = 0;
197  static int16_t lastSlope = 0;
198  int16_t damping;
199 
200  // find X1 and the row that corresponds to Y1
201  sum = 0;
202  y = 0;
203  while (y < LINE_Y_MAX_NDX && !isGoodScan(y))
204  {
205  y++;
206  }
207  lineY1 = y;
208  for (i = y; i < y + SCAN_WIDTH; i++)
209  {
210  sum += lineStats[i][X_MEAN];
211  }
212  lineX1 = sum / SCAN_WIDTH;
213 
214  // find X2 and the row that corresponds to Y2
215  sum = 0;
216  y = LINE_Y_MAX_NDX;
217  while (y > lineY1 && !isGoodScan(y))
218  {
219  y--;
220  }
221  lineY2 = y;
222  for (i = y; i < y + SCAN_WIDTH; i++)
223  {
224  sum += lineStats[i][X_MEAN];
225  }
226  lineX2 = sum / SCAN_WIDTH;
227 
228  // look for row where junction exists
229  y = 0;
230  while (y <= JUNC_Y_MAX_NDX && !isJunctionScan(y))
231  {
232  y++;
233  }
234  if (y > JUNC_Y_MAX_NDX)
235  {
236  junctionY = -VW_Y1_LINE;
237  }
238  else
239  {
240  junctionY = y;
241  }
242 
243  // look for row where ball may exist
244  y = 0;
245  while (y <= BALL_Y_MAX_NDX && !mayBeBall(y))
246  {
247  y++;
248  }
249  if (y > BALL_Y_MAX_NDX)
250  {
251  possibleBallY = -VW_Y1_LINE;
252  }
253  else
254  {
255  possibleBallY = y;
256  }
257 
258  // Correlate zero-based row numbers to Y values
259  lineY1 += VW_Y1_LINE;
260  lineY2 += VW_Y1_LINE;
261  junctionY += VW_Y1_LINE;
262  possibleBallY += VW_Y1_LINE;
263 
264  // calculate correction using line position and (if damping) rate of change
265  m = (double) (lineX2 - lineX1) / (double) (lineY2 - lineY1);
266  lastSlope = lineSlope;
267  lineSlope = (int16_t) (slopeCoef * m);
268  damping = lineSlope - lastSlope;
269  lineOffset = lineCenter - (m * (LINE_Y3 - lineY1) + lineX1);
270  correction = slopeCoef * m +
271  offCoef * lineOffset +
272  dampCoef * damping;
273 
274 #if DEBUGGING
275  if( lcdMode == LINE_LCD_MODE )
276  {
277  lcdPrintHex(lineY1, 0, 0);
278  lcdPrintHex(lineX1, 0, 3);
279  lcdPrintHex(lineY2, 1, 0);
280  lcdPrintHex(lineX2, 1, 3);
281  lcdPrintHex(damping, 0, 6);
282  lcdPrintHex(lineSlope>>8, 0, 9);
283  lcdPrintHex(lineSlope, 0, 11);
284  lcdPrintHex(lineOffset, 0, 14);
285 
286  lcdPrintHex(junctionY, 1, 6);
287  }
288 #endif
289 
290  lineStatsProcessed = true;
291 }