JeVois Tutorials  1.20
JeVois Smart Embedded Machine Vision Tutorials
Share this page:
JeVoisRobotCar2.C
Go to the documentation of this file.
1 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2 //
3 // JeVois Smart Embedded Machine Vision Toolkit - Copyright (C) 2017 by Laurent Itti, the University of Southern
4 // California (USC), and iLab at USC. See http://iLab.usc.edu and http://jevois.org for information about this project.
5 //
6 // This file is part of the JeVois Smart Embedded Machine Vision Toolkit. This program is free software; you can
7 // redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software
8 // Foundation, version 2. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
9 // without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
10 // License for more details. You should have received a copy of the GNU General Public License along with this program;
11 // if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
12 //
13 // Contact information: Laurent Itti - 3641 Watt Way, HNB-07A - Los Angeles, CA 90089-2520 - USA.
14 // Tel: +1 213 740 3527 - itti@pollux.usc.edu - http://iLab.usc.edu - http://jevois.org
15 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
16 
17 // You need to install the Arduino PID Library by Brett Beauregard
18 #include <PID_v1.h>
19 #include <Servo.h>
20 
21 // Pins setup: Use RXLED(17) on Arduino pro micro 32u4 which does not have LED(13)
22 #define LPWMPIN 3
23 #define LIN1PIN 2
24 #define LIN2PIN 21
25 #define RPWMPIN 5
26 #define RIN1PIN 20
27 #define RIN2PIN 4
28 #define LEDPIN 17
29 #define PANPIN 9
30 #define TILTPIN 10
31 
32 // Serial port to use for JeVois:
33 // On chips with USB (e.g., 32u4), that usually is Serial1. On chips without USB, use Serial.
34 #define SERIAL Serial1
35 
36 // Serial port for debugging (optional):
37 #define DBGSERIAL Serial
38 
39 // Buffer for received serial port bytes:
40 #define INLEN 128
41 char instr[INLEN + 1];
42 
43 // Gain values to compensate for unbalanced motors: Do not exceed 655. Give a lower value to the motor that is faster.
44 // E.g., if your car vires to the left when both motors are 100% forward, your right motor is a bit faster, so decrease
45 // rightgain to compensate.
46 long leftgain = 655;
47 long rightgain = 655;
48 
49 // Desired target width in standardized JeVois coordinates (a value of 2000 would occupy the whole field of view):
50 int TARGW = 300;
51 
52 // Create a PID for target width, and one for steering angle:
53 double wset, win, wout, aset, ain, aout;
54 PID wpid(&win, &wout, &wset, 1.0, 0.01, 0.01, DIRECT);
55 PID apid(&ain, &aout, &aset, 0.5, 0.05, 0.02, DIRECT);
56 
57 // current speed and steering:
58 double speed = 0;
59 double steer = 0;
60 int steerdelay = 0; // Delay in steering when re-aquiring a target to avoid steering jerk
61 
62 // Pan and tilt servos zero values and +/- angular range, in degrees:
63 #define PANZERO 90
64 #define PANRANGE 60
65 #define TILTZERO 70
66 #define TILTRANGE 40
67 
68 // Handoff between several targets:
69 int currid = -1, currw = 0, currt = 0, nextid = -1, nextw = 0, nextt = 0, doneid = -1;
70 #define HANDOFFW ((TARGW * 5) / 10)
71 int t = 0;
72 
73 // ###################################################################################################
74 // Simple PD servo controller
75 class ServoPD
76 {
77 public:
78  ServoPD(long Kp, long Kd, long zero, long range, long scalebits = 8) :
79  itsKp(Kp), itsKd(Kd), itsPos(zero << scalebits),
80  itsPrevTarget(zero << scalebits), itsZero(zero << scalebits),
81  itsRange(range << scalebits), itsScaleBits(scalebits)
82  { }
83 
84  void attach(int pin, int pos)
85  {
86  itsServo.attach(pin);
87  itsPos = (pos << itsScaleBits);
88  itsServo.write(pos);
89  }
90 
91  long get() const
92  {
93  return (itsPos >> itsScaleBits);
94  }
95 
96  void update(long targetpos)
97  {
98  targetpos <<= itsScaleBits;
99  long diff = itsKp * targetpos + itsKd * (targetpos - itsPrevTarget);
100  itsPos += (diff >> 16);
101  itsPos = constrain(itsPos, itsZero - itsRange, itsZero + itsRange);
102  itsServo.write(itsPos >> itsScaleBits);
103  itsPrevTarget = targetpos;
104  }
105 
106  void reset(long targetpos)
107  {
108  targetpos <<= itsScaleBits;
109  itsPos = constrain(itsPos, itsZero - itsRange, itsZero + itsRange);
110  itsPrevTarget = targetpos;
111  }
112 
113  long rawget()
114  {
115  return itsPos >> itsScaleBits;
116  }
117 
118  void rawset(long rawval)
119  {
120  itsPos = rawval << itsScaleBits;
121  itsServo.write(rawval);
122  }
123 
124  private:
125  Servo itsServo;
126  long itsKp, itsKd, itsPos, itsPrevTarget, itsZero, itsRange, itsScaleBits;
127 };
128 
129 // Create one servo PD controler for camera pan and another for camera tilt:
130 //ServoPD panservo(400, 200, PANZERO, PANRANGE);
131 ServoPD panservo(100, 50, PANZERO, PANRANGE);
132 ServoPD tiltservo(300, 100, TILTZERO, TILTRANGE);
133 
134 // ###################################################################################################
135 void setup()
136 {
137  SERIAL.setTimeout(50);
138  SERIAL.begin(115200);
139  SERIAL.setTimeout(50);
140  pinMode(LPWMPIN, OUTPUT);
141  pinMode(LIN1PIN, OUTPUT);
142  pinMode(LIN2PIN, OUTPUT);
143  pinMode(RPWMPIN, OUTPUT);
144  pinMode(RIN1PIN, OUTPUT);
145  pinMode(RIN2PIN, OUTPUT);
146  pinMode(LEDPIN, OUTPUT);
147  pinMode(PANPIN, OUTPUT);
148  pinMode(TILTPIN, OUTPUT);
149  motor(0, 0);
150 
153 
154  win = TARGW; wset = TARGW; wout = 0;
155  wpid.SetMode(AUTOMATIC);
156  wpid.SetSampleTime(10);
157  wpid.SetOutputLimits(-120, 120);
158 
159  ain = 0; aset = 0; aout = 0;
160  apid.SetMode(AUTOMATIC);
161  apid.SetSampleTime(10);
162  apid.SetOutputLimits(-50, 50);
163 }
164 
165 // ###################################################################################################
166 void loop()
167 {
168  byte len = SERIAL.readBytesUntil('\n', instr, INLEN);
169  instr[len] = 0;
170 
171  char * tok = strtok(instr, " \r\n");
172  int state = 0; int id, targx, targy, targw, targh;
173 
174  while (tok)
175  {
176  // State machine:
177  // 0: start parsing
178  // 1: N2 command, parse id
179  // 2: N2 command, parse targx
180  // 3: N2 command, parse targy
181  // 4: N2 command, parse targw
182  // 5: N2 command, parse targh
183  // 6: N2 command complete
184  // 1000: unknown command
185  switch (state)
186  {
187  case 0: if (strcmp(tok, "N2") == 0) state = 1; else state = 1000; break;
188  case 1: id = atoi(&tok[1]); state = 2; break; // ignore prefix
189  case 2: targx = atoi(tok); state = 3; break;
190  case 3: targy = atoi(tok); state = 4; break;
191  case 4: targw = atoi(tok); state = 5; break;
192  case 5: targh = atoi(tok); state = 6; break;
193  default: break; // Skip any additional tokens
194  }
195  tok = strtok(0, " \r\n");
196  }
197 
198  // If a complete new N2 command was received, act:
199  if (state == 6)
200  {
201  // Update our steering delay after re-aquisition of previously lost target:
202  if (steerdelay) steerdelay -= 1;
203 
204  // Decide whether we should track this target or another (larger) one:
205  int trackit = 0;
206 
207  // If we are not tracking anything, track this one:
208  if (currid == -1)
209  { currid = id; currw = targw; currt = t; trackit = 1; } // New target, track it
210  else
211  {
212  if (id == doneid)
213  { trackit = 0; } // Ignore this one as we have already tracked it all the way
214  else if (id == currid)
215  { currw = targw; currt = t; trackit = 1; } // Keep tracking current target
216  else if (nextid == -1 && targw < currw)
217  { nextid = id; nextw = targw; nextt = t; trackit = 0; } // Next target detected, do not track it yet
218  else if (id == nextid && currw > HANDOFFW)
219  { doneid = currid; currid = nextid; nextid = -1; currw = targw; currt = t; trackit = 1; } // handoff
220  }
221 
222  if (trackit)
223  {
224  // Actuate the pan/tilt head to track the target:
225  panservo.update(-targx);
226  tiltservo.update(targy);
227 
228  // Move the car forward/backward to track the derired target width:
229  win = targw; wpid.Compute(); speed = wout;
230 
231  // Steer the car to zero out the camera's pan angle:
232  ain = panservo.get() - PANZERO; apid.Compute(); steer = aout * (1.0 + fabs(speed) * 0.05);
233  }
234  }
235  else
236  {
237  // Slow down if we lost track:
238  speed *= 0.95;
239  steer *= 0.8;
240  steerdelay = 1;
241  }
242 
243  // Actuate the motors:
244  if (steerdelay) steer = 0.0; // delay steering a bit after target re-acquisition to avoid steering jerk
245  motor(speed + steer, speed - steer);
246 
247  // Invalidate previously detected targets that we have not seen in a while:
248  if (t - currt > 30) { currid = -1; nextid = -1; doneid = -1; }
249  if (t - nextt > 30) nextid = -1;
250 
251  // Blink the LED on every loop:
252  digitalWrite(LEDPIN, t & 1);
253  t += 1;
254 }
255 
256 // ###################################################################################################
257 // Actuate the motors: Values in [-100 .. 100]; positive = forward, negative = backward, 0 = brake
258 void motor(long left, long right)
259 {
260  if (left > 100) left = 100; else if (left < -100) left = -100;
261  if (right > 100) right = 100; else if (right < -100) right = -100;
262 
263  long motleft = (left * leftgain) >> 8;
264  long motright = (right * rightgain) >> 8;
265 
266  if (motleft > 0) {
267  digitalWrite(LIN1PIN, LOW);
268  digitalWrite(LIN2PIN, HIGH);
269  analogWrite(LPWMPIN, motleft);
270  } else if (motleft < 0) {
271  digitalWrite(LIN1PIN, HIGH);
272  digitalWrite(LIN2PIN, LOW);
273  analogWrite(LPWMPIN, -motleft);
274  } else {
275  digitalWrite(LIN1PIN, LOW);
276  digitalWrite(LIN2PIN, LOW);
277  }
278 
279  if (motright > 0) {
280  digitalWrite(RIN1PIN, LOW);
281  digitalWrite(RIN2PIN, HIGH);
282  analogWrite(RPWMPIN, motright);
283  } else if (motright < 0) {
284  digitalWrite(RIN1PIN, HIGH);
285  digitalWrite(RIN2PIN, LOW);
286  analogWrite(RPWMPIN, -motright);
287  } else {
288  digitalWrite(RIN1PIN, LOW);
289  digitalWrite(RIN2PIN, LOW);
290  }
291 }
292 
TILTRANGE
#define TILTRANGE
Definition: JeVoisRobotCar2.C:66
tiltservo
ServoPD tiltservo(300, 100, TILTZERO, TILTRANGE)
LEDPIN
#define LEDPIN
Definition: JeVoisRobotCar2.C:28
HANDOFFW
#define HANDOFFW
Definition: JeVoisRobotCar2.C:70
ServoPD::reset
void reset(long targetpos)
Definition: JeVoisRobotCar2.C:106
wout
double wout
Definition: JeVoisRobotCar2.C:53
LPWMPIN
#define LPWMPIN
Definition: JeVoisRobotCar2.C:22
TILTZERO
#define TILTZERO
Definition: JeVoisRobotCar2.C:65
steer
double steer
Definition: JeVoisRobotCar2.C:59
PANZERO
#define PANZERO
Definition: JeVoisRobotCar2.C:63
TARGW
int TARGW
Definition: JeVoisRobotCar2.C:50
motor
void motor(long left, long right)
Definition: JeVoisRobotCar2.C:258
aout
double aout
Definition: JeVoisRobotCar2.C:53
ServoPD::ServoPD
ServoPD(long Kp, long Kd, long zero, long range, long scalebits=8)
Definition: JeVoisRobotCar2.C:78
ain
double ain
Definition: JeVoisRobotCar2.C:53
nextw
int nextw
Definition: JeVoisRobotCar2.C:69
nextid
int nextid
Definition: JeVoisRobotCar2.C:69
currw
int currw
Definition: JeVoisRobotCar2.C:69
RIN2PIN
#define RIN2PIN
Definition: JeVoisRobotCar2.C:27
INLEN
#define INLEN
Definition: JeVoisRobotCar2.C:40
RPWMPIN
#define RPWMPIN
Definition: JeVoisRobotCar2.C:25
ServoPD::attach
void attach(int pin, int pos)
Definition: JeVoisRobotCar2.C:84
TILTPIN
#define TILTPIN
Definition: JeVoisRobotCar2.C:30
LIN2PIN
#define LIN2PIN
Definition: JeVoisRobotCar2.C:24
ServoPD
Definition: JeVoisRobotCar1.C:71
win
double win
Definition: JeVoisRobotCar2.C:53
panservo
ServoPD panservo(100, 50, PANZERO, PANRANGE)
nextt
int nextt
Definition: JeVoisRobotCar2.C:69
leftgain
long leftgain
Definition: JeVoisRobotCar2.C:46
ServoPD::get
long get() const
Definition: JeVoisRobotCar2.C:91
loop
void loop()
Definition: JeVoisRobotCar2.C:166
speed
double speed
Definition: JeVoisRobotCar2.C:58
setup
void setup()
Definition: JeVoisRobotCar2.C:135
doneid
int doneid
Definition: JeVoisRobotCar2.C:69
aset
double aset
Definition: JeVoisRobotCar2.C:53
currt
int currt
Definition: JeVoisRobotCar2.C:69
rightgain
long rightgain
Definition: JeVoisRobotCar2.C:47
PANPIN
#define PANPIN
Definition: JeVoisRobotCar2.C:29
currid
int currid
Definition: JeVoisRobotCar2.C:69
RIN1PIN
#define RIN1PIN
Definition: JeVoisRobotCar2.C:26
LIN1PIN
#define LIN1PIN
Definition: JeVoisRobotCar2.C:23
steerdelay
int steerdelay
Definition: JeVoisRobotCar2.C:60
t
int t
Definition: JeVoisRobotCar2.C:71
wset
double wset
Definition: JeVoisRobotCar2.C:53
SERIAL
#define SERIAL
Definition: JeVoisRobotCar2.C:34
ServoPD::rawset
void rawset(long rawval)
Definition: JeVoisRobotCar2.C:118
PANRANGE
#define PANRANGE
Definition: JeVoisRobotCar2.C:64
instr
char instr[INLEN+1]
Definition: JeVoisRobotCar2.C:41
ServoPD::rawget
long rawget()
Definition: JeVoisRobotCar2.C:113
ServoPD::update
void update(long targetpos)
Definition: JeVoisRobotCar2.C:96