0% found this document useful (0 votes)
7 views19 pages

Autonomous RC Car Line Following System

The document outlines the methodology for designing and implementing an autonomous RC car that follows a black line, detects parking spaces, and performs parking maneuvers while avoiding obstacles. It details the systems involved, including line following with IR sensors, obstacle avoidance using ultrasonic sensors, and parking space detection, all enhanced by PID control for precision. The code and control logic for the car's operation are also provided, ensuring reliable navigation and performance.

Uploaded by

omaromong3
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
7 views19 pages

Autonomous RC Car Line Following System

The document outlines the methodology for designing and implementing an autonomous RC car that follows a black line, detects parking spaces, and performs parking maneuvers while avoiding obstacles. It details the systems involved, including line following with IR sensors, obstacle avoidance using ultrasonic sensors, and parking space detection, all enhanced by PID control for precision. The code and control logic for the car's operation are also provided, ensuring reliable navigation and performance.

Uploaded by

omaromong3
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd

Project methodology

Objective:
Design and implement an RC car that autonomously follows a black line on a
white surface, detects parking spaces using ultrasonic sensors, and performs
a parking maneuver when a space is found. The car also avoids obstacles
and incorporates PID control for precise line following and
movement accuracy using encoders.

Line Following System

 Sensors Used: 3 IR sensors.

 Working Principle:
The IR sensors detect the contrast between the black line and the
white background. Based on the combination of sensor readings, the
car adjusts its motor speeds to stay centered on the line.

 Line Following Logic:

IR_L IR_C IR_R Action

Whit Blac Whit Go straight


e k e

Blac Blac Whit Slight turn


k k e left

Whit Blac Blac Slight turn


e k k right

Blac Whit Whit Sharp turn


k e e left

Whit Whit Blac Sharp turn


e e k right

Blac Blac Blac Stop


k k k

Whit Whit Whit Stop


e e e
 Control Enhancement:
A PID controller is applied to smooth motor control using the error
derived from the IR sensors, improving stability and precision.

Obstacle Avoidance System

 Sensor Used: Ultrasonic (Front).

 Working Principle:
Continuously monitors for obstacles ahead. If an object is detected
within a set distance (e.g., 20 cm), the car stops to avoid collision.

Parking Space Detection

 Sensors Used: Ultrasonic (Right priority; Left as backup).

 Working Principle:

 While following the line, the right ultrasonic sensor scans for spaces by
detecting a significant gap beside the car.

o If a space wider than a pre-defined threshold (e.g., >30 cm) is


found and long enough (measured via encoders), the parking
maneuver is triggered.

Parking Maneuver

 Procedure:

1. Stop when space is confirmed.

2. Align car position by moving slightly forward.

3. Reverse while turning hard right to enter the space.

4. Straighten and reverse fully into the parking space.

5. Optionally fine-tune (forward/reverse) for perfect alignment.

 Precision Control:

 The parking maneuver uses encoder feedback to ensure precise


distances and rotations during parking.

Control logic

1. Initialization:

 Set up motors, sensors, PID parameters.

2. Line Following Mode:

 Read IR sensors → Apply PID correction → Adjust motor speeds.

3. Obstacle Monitoring:

 Continuously read front ultrasonic sensor.

 If obstacle detected → Stop or wait.

4. Parking Space Scanning:

 Continuously read right ultrasonic sensor.

 If valid space detected (gap + length confirmed by encoders) → Switch


to parking mode.

5. Parking Execution:

 Perform the parking maneuver using encoder-based distance and angle


tracking.

6. Post-Parking:

 Remain parked
PID Controller

 Purpose:
Smooth out the control to avoid jerky movements during line following
and ensure precise motor control.

 Formula:

ini

Correction = Kp * Error + Ki * Integral + Kd * Derivative

 Error Calculation Example:

Pattern Error
Value

Left=1, Center=0, +2
Right=0

Left=1, Center=1, +1
Right=0

Left=0, Center=1, 0
Right=0

Left=0, Center=1, -1
Right=1

Left=0, Center=0, -2
Right=1

 Motor Adjustment:

o Left Motor: BaseSpeed - Correction

o Right Motor: BaseSpeed + Correction

 Encoders:
Wheel encoders are also integrated into the PID loop for more accurate
control of distance and speed, especially useful during parking and
obstacle handling.
Code
1. // Motor Pins
2. const int ENA = 5;
3. const int IN1 = 9;
4. const int IN2 = 8;
5. const int ENB = 10;
6. const int IN3 = 11;
7. const int IN4 = 12;
8.
9. // IR Sensor Pins
10. const int leftIRSensorPin = A1;
11. const int centerIRSensorPin = 6;
12. const int rightIRSensorPin = 4;
13.
14. // Ultrasonic Sensor Pins
15. const int leftUSTrigger = A2;
16. const int leftUSEcho = A3;
17. const int centerUSTrigger = A4;
18. const int centerUSEcho = A5;
19. const int rightUSTrigger = 7;
20. const int rightUSEcho = A0;
21.
22. // Encoder Pins (single output per encoder)
23. const int leftEncoderPin = 2;
24. const int rightEncoderPin = 3;
25.
26. // Constants
27. const int baseSpeed = 100;
28. const int turnSpeed = 80;
29. const int stopSpeed = 0;
30. const int parkingDistanceThreshold = 30;
31. const int obstacleDistanceThreshold = 20;
32. const float Kp = 0.5;
33. const float Kd = 0.5;
34. const long correctionDelay = 100;
35. const long parkingDelay = 1000;
36. const float wheelDiameter = 6.5;
37. const float pi = 3.14159265359;
38. const long encoderTicksPerRevolution = 360;
39. const float wheelCircumference = wheelDiameter * pi;
40. const float distancePerTick = wheelCircumference /
encoderTicksPerRevolution;
41.
42. // Variables
43. volatile long leftEncoderTicks = 0;
44. volatile long rightEncoderTicks = 0;
45. long leftEncoderCount = 0;
46. long rightEncoderCount = 0;
47. unsigned long lastEncoderUpdate = 0;
48. unsigned long previousTime = 0;
49. float previousError = 0;
50. bool foundParking = false;
51. int leftIRValue, centerIRValue, rightIRValue;
52.
53. void setup() {
54. [Link](9600);
55.
56. // Motor setup
57. pinMode(ENA, OUTPUT);
58. pinMode(IN1, OUTPUT);
59. pinMode(IN2, OUTPUT);
60. pinMode(ENB, OUTPUT);
61. pinMode(IN3, OUTPUT);
62. pinMode(IN4, OUTPUT);
63.
64. // IR sensor setup
65. pinMode(leftIRSensorPin, INPUT);
66. pinMode(centerIRSensorPin, INPUT);
67. pinMode(rightIRSensorPin, INPUT);
68.
69. // Ultrasonic setup
70. pinMode(leftUSTrigger, OUTPUT);
71. pinMode(leftUSEcho, INPUT);
72. pinMode(centerUSTrigger, OUTPUT);
73. pinMode(centerUSEcho, INPUT);
74. pinMode(rightUSTrigger, OUTPUT);
75. pinMode(rightUSEcho, INPUT);
76.
77. // Encoders
78. pinMode(leftEncoderPin, INPUT_PULLUP);
79. pinMode(rightEncoderPin, INPUT_PULLUP);
80. attachInterrupt(digitalPinToInterrupt(leftEncoderPin), []()
{ leftEncoderTicks++; }, RISING);
81. attachInterrupt(digitalPinToInterrupt(rightEncoderPin), []()
{ rightEncoderTicks++; }, RISING);
82.
83. stopMovement();
84. delay(2000);
85. }
86.
87. // Movement Functions
88. void moveForward() {
89. analogWrite(ENA, baseSpeed);
90. digitalWrite(IN1, HIGH);
91. digitalWrite(IN2, LOW);
92.
93. analogWrite(ENB, baseSpeed);
94. digitalWrite(IN3, HIGH);
95. digitalWrite(IN4, LOW);
96. }
97.
98. void moveBackward() {
99. analogWrite(ENA, baseSpeed);
100. digitalWrite(IN1, LOW);
101. digitalWrite(IN2, HIGH);
102.
103. analogWrite(ENB, baseSpeed);
104. digitalWrite(IN3, LOW);
105. digitalWrite(IN4, HIGH);
106. }
107.
108. void turnLeft() {
109. analogWrite(ENA, stopSpeed);
110. digitalWrite(IN1, LOW);
111. digitalWrite(IN2, LOW);
112.
113. analogWrite(ENB, turnSpeed);
114. digitalWrite(IN3, HIGH);
115. digitalWrite(IN4, LOW);
116. }
117.
118. void turnRight() {
119. analogWrite(ENA, turnSpeed);
120. digitalWrite(IN1, HIGH);
121. digitalWrite(IN2, LOW);
122.
123. analogWrite(ENB, stopSpeed);
124. digitalWrite(IN3, LOW);
125. digitalWrite(IN4, LOW);
126. }
127.
128. void stopMovement() {
129. analogWrite(ENA, stopSpeed);
130. digitalWrite(IN1, LOW);
131. digitalWrite(IN2, LOW);
132.
133. analogWrite(ENB, stopSpeed);
134. digitalWrite(IN3, LOW);
135. digitalWrite(IN4, LOW);
136. }
137.
138. // IR and Ultrasonic Reading
139. void readIRSensors() {
140. leftIRValue = digitalRead(leftIRSensorPin);
141. centerIRValue = digitalRead(centerIRSensorPin);
142. rightIRValue = digitalRead(rightIRSensorPin);
143. }
144.
145. int readUSDistance(int trigPin, int echoPin) {
146. digitalWrite(trigPin, LOW);
147. delayMicroseconds(2);
148. digitalWrite(trigPin, HIGH);
149. delayMicroseconds(10);
150. digitalWrite(trigPin, LOW);
151. long duration = pulseIn(echoPin, HIGH, 30000);
152. if (duration == 0) return 200;
153. return (duration / 2) / 29.1;
154. }
155.
156. bool checkFrontObstacle() {
157. int distance = readUSDistance(centerUSTrigger,
centerUSEcho);
158. [Link]("Front Distance: ");
159. [Link](distance);
160. return distance <= obstacleDistanceThreshold;
161. }
162.
163. void updateEncoders() {
164. noInterrupts();
165. leftEncoderCount += leftEncoderTicks;
166. rightEncoderCount += rightEncoderTicks;
167. leftEncoderTicks = 0;
168. rightEncoderTicks = 0;
169. interrupts();
170. }
171.
172. void correctPath() {
173. float error = (rightEncoderCount - leftEncoderCount) *
distancePerTick;
174. unsigned long currentTime = millis();
175. float dError = (error - previousError) / (currentTime -
previousTime + 1);
176.
177. float correction = Kp * error + Kd * dError;
178. int leftSpeed = baseSpeed - correction;
179. int rightSpeed = baseSpeed + correction;
180.
181. leftSpeed = constrain(leftSpeed, 0, 255);
182. rightSpeed = constrain(rightSpeed, 0, 255);
183.
184. analogWrite(ENA, leftSpeed);
185. analogWrite(ENB, rightSpeed);
186.
187. previousError = error;
188. previousTime = currentTime;
189. }
190.
191. void checkParkingSpace(int side) {
192. stopMovement();
193. delay(parkingDelay);
194. int distance = (side == 1)
195. ? readUSDistance(leftUSTrigger, leftUSEcho)
196. : readUSDistance(rightUSTrigger,
rightUSEcho);
197. [Link]("Side ");
198. [Link](side == 1 ? "Left" : "Right");
199. [Link](" Distance: ");
200. [Link](distance);
201.
202. if (distance > parkingDistanceThreshold) {
203. [Link]("Parking space is empty!");
204. park(side);
205. foundParking = true;
206. } else {
207. [Link]("Parking space is occupied!");
208. moveForward();
209. }
210. }
211.
212. void park(int side) {
213. stopMovement();
214. delay(500);
215. if (side == 1) {
216. turnLeft();
217. } else {
218. turnRight();
219. }
220. delay(750);
221. moveBackward();
222. delay(1000);
223. stopMovement();
224. }
225.
226. void loop() {
227. if (checkFrontObstacle()) {
228. stopMovement();
229. [Link]("Obstacle Detected! Stopping.");
230. while (true) delay(1000);
231. }
232.
233. readIRSensors();
234.
235. if (millis() - lastEncoderUpdate > 100) {
236. updateEncoders();
237. lastEncoderUpdate = millis();
238. }
239.
240. if (centerIRValue == LOW) {
241. moveForward();
242. correctPath();
243. } else if (leftIRValue == LOW) {
244. turnLeft();
245. } else if (rightIRValue == LOW) {
246. turnRight();
247. } else {
248. stopMovement();
249. delay(parkingDelay);
250. checkParkingSpace(1);
251. if (foundParking) return;
252. checkParkingSpace(2);
253. if (foundParking) return;
254. moveForward();
255. delay(parkingDelay / 2);
256. }
257. }

Conclusion

This methodology ensures that the RC car can autonomously navigate a path
by following a line, intelligently handle obstacles, detect parking spaces, and
perform parking maneuvers with precision. The integration of PID control and
encoders allows for highly precise movement and distance measurement,
enhancing the overall reliability and performance of the system.

Future Update to the code


1. // =======================
2. // Pin Configurations
3. // =======================
4.
5. // Motor Driver Pins
6. const int ENA = 5; // PWM pin for Motor A
7. const int IN1 = 9;
8. const int IN2 = 8;
9. const int ENB = 10; // PWM pin for Motor B
10. const int IN3 = 11;
11. const int IN4 = 12;
12.
13. // IR Sensor Pins
14. const int leftIRSensorPin = A1; // Digital read (NOT PWM)
15. const int centerIRSensorPin = 6;
16. const int rightIRSensorPin = 4;
17.
18. // Ultrasonic Sensor Pins
19. const int leftUSTrigger = A2;
20. const int leftUSEcho = A3;
21. const int centerUSTrigger = A4;
22. const int centerUSEcho = A5;
23. const int rightUSTrigger = 7;
24. const int rightUSEcho = A0;
25.
26. // Encoder Pins (using Uno's external interrupts)
27. const int leftEncoderPin = 2; // Interrupt 0
28. const int rightEncoderPin = 3; // Interrupt 1
29.
30. // =======================
31. // Constants
32. // =======================
33.
34. const int baseSpeed = 100;
35. const int turnSpeed = 80;
36. const int stopSpeed = 0;
37. const int parkingDistanceThreshold = 30; // cm
38. const int obstacleDistanceThreshold = 20; // cm
39.
40. // PID Constants
41. const float Kp = 0.5;
42. const float Kd = 0.5;
43.
44. // Wheel/Encoder Constants
45. const float wheelDiameter = 6.5; // cm
46. const float pi = 3.14159265359;
47. const long encoderTicksPerRevolution = 360;
48. const float wheelCircumference = wheelDiameter * pi;
49. const float distancePerTick = wheelCircumference /
encoderTicksPerRevolution;
50.
51. // Timing
52. const long correctionDelay = 100;
53. const long parkingDelay = 1000;
54.
55. // =======================
56. // Variables
57. // =======================
58.
59. volatile long leftEncoderTicks = 0;
60. volatile long rightEncoderTicks = 0;
61. unsigned long previousTime = 0;
62. float previousError = 0;
63. bool foundParking = false;
64.
65. int leftIRValue, centerIRValue, rightIRValue;
66.
67. // =======================
68. // Setup
69. // =======================
70.
71. void setup() {
72. [Link](9600);
73.
74. // Motor setup
75. pinMode(ENA, OUTPUT);
76. pinMode(IN1, OUTPUT);
77. pinMode(IN2, OUTPUT);
78. pinMode(ENB, OUTPUT);
79. pinMode(IN3, OUTPUT);
80. pinMode(IN4, OUTPUT);
81.
82. // IR sensors
83. pinMode(leftIRSensorPin, INPUT);
84. pinMode(centerIRSensorPin, INPUT);
85. pinMode(rightIRSensorPin, INPUT);
86.
87. // Ultrasonic sensors
88. pinMode(leftUSTrigger, OUTPUT);
89. pinMode(leftUSEcho, INPUT);
90. pinMode(centerUSTrigger, OUTPUT);
91. pinMode(centerUSEcho, INPUT);
92. pinMode(rightUSTrigger, OUTPUT);
93. pinMode(rightUSEcho, INPUT);
94.
95. // Encoder pins (attach interrupts)
96. pinMode(leftEncoderPin, INPUT_PULLUP);
97. pinMode(rightEncoderPin, INPUT_PULLUP);
98. attachInterrupt(digitalPinToInterrupt(leftEncoderPin),
leftEncoderISR, RISING);
99. attachInterrupt(digitalPinToInterrupt(rightEncoderPin),
rightEncoderISR, RISING);
100.
101. stopMovement();
102. delay(2000);
103. }
104.
105. // =======================
106. // Encoder ISRs
107. // =======================
108.
109. void leftEncoderISR() {
110. leftEncoderTicks++;
111. }
112.
113. void rightEncoderISR() {
114. rightEncoderTicks++;
115. }
116.
117. // =======================
118. // Movement Functions
119. // =======================
120.
121. void moveForward() {
122. analogWrite(ENA, baseSpeed);
123. digitalWrite(IN1, HIGH);
124. digitalWrite(IN2, LOW);
125.
126. analogWrite(ENB, baseSpeed);
127. digitalWrite(IN3, HIGH);
128. digitalWrite(IN4, LOW);
129. }
130.
131. void moveBackward() {
132. analogWrite(ENA, baseSpeed);
133. digitalWrite(IN1, LOW);
134. digitalWrite(IN2, HIGH);
135.
136. analogWrite(ENB, baseSpeed);
137. digitalWrite(IN3, LOW);
138. digitalWrite(IN4, HIGH);
139. }
140.
141. void turnLeft() {
142. analogWrite(ENA, stopSpeed);
143. digitalWrite(IN1, LOW);
144. digitalWrite(IN2, LOW);
145.
146. analogWrite(ENB, turnSpeed);
147. digitalWrite(IN3, HIGH);
148. digitalWrite(IN4, LOW);
149. }
150.
151. void turnRight() {
152. analogWrite(ENA, turnSpeed);
153. digitalWrite(IN1, HIGH);
154. digitalWrite(IN2, LOW);
155.
156. analogWrite(ENB, stopSpeed);
157. digitalWrite(IN3, LOW);
158. digitalWrite(IN4, LOW);
159. }
160.
161. void stopMovement() {
162. analogWrite(ENA, stopSpeed);
163. digitalWrite(IN1, LOW);
164. digitalWrite(IN2, LOW);
165.
166. analogWrite(ENB, stopSpeed);
167. digitalWrite(IN3, LOW);
168. digitalWrite(IN4, LOW);
169. }
170.
171. // =======================
172. // Sensor Reading
173. // =======================
174.
175. void readIRSensors() {
176. leftIRValue = digitalRead(leftIRSensorPin);
177. centerIRValue = digitalRead(centerIRSensorPin);
178. rightIRValue = digitalRead(rightIRSensorPin);
179. }
180.
181. int readUSDistance(int trigPin, int echoPin) {
182. digitalWrite(trigPin, LOW);
183. delayMicroseconds(2);
184. digitalWrite(trigPin, HIGH);
185. delayMicroseconds(10);
186. digitalWrite(trigPin, LOW);
187. long duration = pulseIn(echoPin, HIGH, 30000);
188. if (duration == 0) return 200; // fail-safe
189. return (duration / 2) / 29.1; // cm
190. }
191.
192. // =======================
193. // PID Correction
194. // =======================
195.
196. void correctPath() {
197. float error = (rightEncoderTicks - leftEncoderTicks) *
distancePerTick;
198. unsigned long currentTime = millis();
199. float dError = (error - previousError) / (currentTime -
previousTime + 1);
200.
201. float correction = Kp * error + Kd * dError;
202. int leftSpeed = baseSpeed - correction;
203. int rightSpeed = baseSpeed + correction;
204.
205. leftSpeed = constrain(leftSpeed, 0, 255);
206. rightSpeed = constrain(rightSpeed, 0, 255);
207.
208. analogWrite(ENA, leftSpeed);
209. analogWrite(ENB, rightSpeed);
210.
211. previousError = error;
212. previousTime = currentTime;
213. }
214.
215. // =======================
216. // Parking Check
217. // =======================
218.
219. void checkParkingSpace(int side) {
220. stopMovement();
221. delay(parkingDelay);
222. int distance = (side == 1)
223. ? readUSDistance(leftUSTrigger, leftUSEcho)
224. : readUSDistance(rightUSTrigger,
rightUSEcho);
225. [Link]("Side ");
226. [Link](side == 1 ? "Left" : "Right");
227. [Link](" Distance: ");
228. [Link](distance);
229.
230. if (distance > parkingDistanceThreshold) {
231. [Link]("Parking space is empty!");
232. park(side);
233. foundParking = true;
234. } else {
235. [Link]("Parking space is occupied!");
236. moveForward();
237. }
238. }
239.
240. void park(int side) {
241. stopMovement();
242. delay(500);
243. if (side == 1) {
244. turnLeft();
245. } else {
246. turnRight();
247. }
248. delay(750);
249. moveBackward();
250. delay(1000);
251. stopMovement();
252. }
253.
254. // =======================
255. // Main Loop
256. // =======================
257.
258. void loop() {
259. int frontDistance = readUSDistance(centerUSTrigger,
centerUSEcho);
260. [Link]("Front Distance: ");
261. [Link](frontDistance);
262.
263. if (frontDistance <= obstacleDistanceThreshold) {
264. stopMovement();
265. [Link]("Obstacle Detected! Stopping.");
266. while (true) delay(1000);
267. }
268.
269. readIRSensors();
270.
271. if (centerIRValue == LOW) {
272. moveForward();
273. correctPath();
274. } else if (leftIRValue == LOW) {
275. turnLeft();
276. } else if (rightIRValue == LOW) {
277. turnRight();
278. } else {
279. stopMovement();
280. delay(parkingDelay);
281. checkParkingSpace(1); // Check left side
282. if (foundParking) return;
283. checkParkingSpace(2); // Check right side
284. if (foundParking) return;
285. moveForward();
286. delay(parkingDelay / 2);
287. }
288. }
289.
Key Improvements and Explanations

1. PID-Based Path Correction

To enhance the robot's straight-line movement, a Proportional-Derivative


(PID) controller was implemented.

 Why? Without PID control, the robot may drift due to wheel imbalances
or uneven motor speeds.

 How? By continuously adjusting the motor speeds based on encoder


feedback, the robot can correct its path in real-time.

2. Encoder-Based Movement Control

Encoders were utilized to track wheel rotations, enabling precise control over
the robot's movement distance.

 Why? This allows the robot to move specific distances without relying
solely on time-based delays.

 How? By counting encoder ticks, the robot can determine when to stop
after traveling a set distance.

3. Obstacle Detection and Avoidance

Ultrasonic sensors were integrated to detect obstacles in the robot's path.

 Why? This ensures the robot can avoid collisions by stopping or


changing direction when an obstacle is detected.

 How? The checkFrontObstacle() function reads the distance from the


ultrasonic sensor and compares it to a threshold value.

4. Parking Space Detection


The robot can assess parking spaces using ultrasonic sensors to determine if
a space is available.

 Why? This allows the robot to autonomously decide whether to park or


continue searching for an empty space.

 How? The checkParkingSpace() function measures the distance to the


side walls and compares it to a threshold.5. Improved Parking
Maneuver

The parking logic was refined to ensure smoother and more reliable parking
operations.

 Why? To ensure the robot parks correctly without unnecessary


movements.

 How? The park() function was adjusted to handle different parking


scenarios more [Link] Forum+1Arduino Forum+1

You might also like