Difference between revisions of "Line Following Car - Code"
From RoboWiki
Line 120: | Line 120: | ||
if __name__ == "__main__": | if __name__ == "__main__": | ||
main() | main() | ||
+ | |||
+ | </syntaxhighlight> |
Revision as of 14:15, 22 May 2024
Return back to project page: Line Following Car - Jakub Vojtek
Python code for the Line Following Car project:
import cv2
import numpy as np
import time
from buildhat import Motor
left_wheel = Motor('A')
right_wheel = Motor('B')
no_line_start_time = None
cam_width = 320
cam_height = 240
# black color range in HSV
lower_black = np.array([75, 30, 0])
upper_black = np.array([180, 255, 255])
camera = cv2.VideoCapture(0)
camera.set(cv2.CAP_PROP_FRAME_WIDTH, cam_width)
camera.set(cv2.CAP_PROP_FRAME_HEIGHT, cam_height)
def forward():
left_wheel.start(-10)
right_wheel.start(10)
def draw_arrow(frame, position, direction):
if direction == 'left':
cv2.arrowedLine(frame, position, (position[0] - 50, position[1]), (0, 255, 0), 2)
elif direction == 'right':
cv2.arrowedLine(frame, position, (position[0] + 50, position[1]), (0, 255, 0), 2)
elif direction == 'up':
cv2.arrowedLine(frame, position, (position[0], position[1] - 50), (0, 255, 0), 2)
def follow_line():
global no_line_start_time
while True:
ret, frame = camera.read()
if not ret:
break
hsv_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
# Threshold the HSV image to get only black colors
mask = cv2.inRange(hsv_frame, lower_black, upper_black)
# Bitwise-AND mask and original image
res = cv2.bitwise_and(frame, frame, mask=mask)
# Crop region of interest (ROI)
roi = res[cam_height//2:cam_height, :]
# Convert the ROI to grayscale
gray = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY)
# Find contours
contours, _ = cv2.findContours(gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if contours:
no_line_start_time = None
largest_contour = max(contours, key=cv2.contourArea)
# Get the centroid of the largest contour
moments = cv2.moments(largest_contour)
if moments["m00"] != 0:
centroid_x = int(moments["m10"] / moments["m00"])
centroid_y = int(moments["m01"] / moments["m00"])
cv2.circle(res, (centroid_x, centroid_y), 5, (0, 255, 0), -1) # Draw a green circle at the centroid
if centroid_x < cam_width // 2 - 10:
left_wheel.stop()
right_wheel.start(15)
draw_arrow(res, (centroid_x, centroid_y), 'left')
elif centroid_x > cam_width // 2 + 10:
left_wheel.start(-15)
right_wheel.stop()
draw_arrow(res, (centroid_x, centroid_y), 'right')
else:
forward()
draw_arrow(res, (centroid_x, centroid_y), 'up')
else:
if no_line_start_time is not None:
forward()
cv2.putText(res, "Waiting for line", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
current_time = time.time()
elapsed_time = current_time - no_line_start_time
if elapsed_time >= 2:
left_wheel.stop()
right_wheel.stop()
print("No line found")
break
else:
no_line_start_time = time.time()
cv2.imshow('mask', mask)
cv2.imshow('res', res)
# Exit if 'q' is pressed
if cv2.waitKey(1) & 0xFF == ord('q'):
break
def main():
if not camera.isOpened():
print("Error: Unable to open camera.")
return
forward()
follow_line()
camera.release()
cv2.destroyAllWindows()
if __name__ == "__main__":
main()