top of page
BallPalVideo.mov

BallPal

3721155466e74eb7a389950aed69ca46.mov
Screenshot 2026-03-02 004238.png
Screenshot 2026-03-02 004535.png
Code for Raspberry Pi 5

# Import necessary libraries

import cv2

import numpy as np

import lgpio

from time import time, sleep

 

# Initialize GPIO chip

h = lgpio.gpiochip_open(0)

 

# Define motor GPIO pins

IN1, IN2 = 22, 20  

IN3, IN4 = 12, 16  

motor_pins = [IN1, IN2, IN3, IN4]

 

# Define ultrasonic sensor pins

TRIG, ECHO = 4, 17  

 

# Configure motor pins as outputs

for pin in motor_pins:

    lgpio.gpio_claim_output(h, pin)

 

# Configure ultrasonic sensor pins

lgpio.gpio_claim_output(h, TRIG)

lgpio.gpio_claim_input(h, ECHO)

 

# Initialize camera

cap = cv2.VideoCapture(0)

cap.set(cv2.CAP_PROP_FRAME_WIDTH, 320)

cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 240)

 

# Define HSV color range for detecting orange

lower_orange = np.array([9, 130, 130])

upper_orange = np.array([22, 255, 255])

 

# Basic movement control functions for robot

def motor_forward(): #moves forward

    lgpio.gpio_write(h, IN1, 1)

    lgpio.gpio_write(h, IN2, 0)

    lgpio.gpio_write(h, IN3, 1)

    lgpio.gpio_write(h, IN4, 0)

 

def motor_left(): #moves left

    lgpio.gpio_write(h, IN1, 0)

    lgpio.gpio_write(h, IN2, 1)

    lgpio.gpio_write(h, IN3, 1)

    lgpio.gpio_write(h, IN4, 0)

 

def motor_right(): #moves right

    lgpio.gpio_write(h, IN1, 1)

    lgpio.gpio_write(h, IN2, 0)

    lgpio.gpio_write(h, IN3, 0)

    lgpio.gpio_write(h, IN4, 1)

 

def motor_reverse(): #moves backward

    lgpio.gpio_write(h, IN1, 0)

    lgpio.gpio_write(h, IN2, 1)

    lgpio.gpio_write(h, IN3, 0)

    lgpio.gpio_write(h, IN4, 1)

 

def motor_stop(): #stops moving

    for pin in motor_pins:

        lgpio.gpio_write(h, pin, 0)

 

# Brush VEX motor control setup

vex_IN3 = 19

vex_IN4 = 26

ENB = 18 #This pin is for power control of the motor

 

# Configure Brush VEX motor pins

lgpio.gpio_claim_output(h, vex_IN3)

lgpio.gpio_claim_output(h, vex_IN4)

lgpio.gpio_claim_output(h, ENB)

 

# Function to move brush VEX motor forward at given speed (0–100%)

def vex_motor_forward(speed_percent):

    lgpio.gpio_write(h, vex_IN3, 1)

    lgpio.gpio_write(h, vex_IN4, 0)

    lgpio.tx_pwm(h, ENB, 10000, speed_percent)

 

# Stop brush VEX motor

def vex_motor_stop():

    print("Stopping motor")

    lgpio.gpio_write(h, vex_IN3, 0)

    lgpio.gpio_write(h, vex_IN4, 0)

 

# Read distance from ultrasonic sensor

def get_distance():

    lgpio.gpio_write(h, TRIG, 1)

    sleep(0.00001)

    lgpio.gpio_write(h, TRIG, 0)

    pulse_start = time()

    while lgpio.gpio_read(h, ECHO) == 0:

        pulse_start = time()

    pulse_end = time()

    while lgpio.gpio_read(h, ECHO) == 1:

        pulse_end = time()

    duration = pulse_end - pulse_start

    distance = round(duration * 17150, 2)

    return distance

 

# Core behavior loop: zigzag, track ball, avoid obstacles

def move_zigzag():

    zigzag_state = 0  # Keeps track of zigzag direction

    while True:

        distance = get_distance()

        print(f"Distance: {distance} cm")

        # Read camera frame

        ret, frame = cap.read()

        if not ret:

            continue

        # Apply HSV mask for orange ball detection

        hsv_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)

        mask = cv2.inRange(hsv_frame, lower_orange, upper_orange)

        contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

        # Check if ball is detected

        ball_detected = False

        ball_x = None

        for cnt in contours:

            area = cv2.contourArea(cnt)

            if area > 300:

                ball_detected = True

                x, y, w, h = cv2.boundingRect(cnt)

                ball_x = x + w // 2

                break

        if ball_detected: #when ball is detected…

            print("Ball Detected - Tracking Faster!")

            frame_center = frame.shape[1] // 2

            reaction_time = 0.2

            # Turn left toward ball

            if ball_x < frame_center - 60:

                print("Adjusting Left for .2 s")

                motor_left()

                start_time = time()

                while time() - start_time < reaction_time: #this loop makes camera constantly update

 

                    ret, frame = cap.read()

                    if ret:

                        hsv_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)

                        mask = cv2.inRange(hsv_frame, lower_orange, upper_orange)

                        cv2.imshow("Camera Feed", frame)

                        cv2.imshow("Orange Mask", mask)

                        if cv2.waitKey(1) & 0xFF == ord('q'):

                            return

 

            # Turn right toward ball

            elif ball_x > frame_center + 60:

                print("Adjusting Right for .2 s")

                motor_right()

                start_time = time()

                while time() - start_time < reaction_time: #this loop makes camera constantly update

 

                    ret, frame = cap.read()

                    if ret:

                        hsv_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)

                        mask = cv2.inRange(hsv_frame, lower_orange, upper_orange)

                        cv2.imshow("Camera Feed", frame)

                        cv2.imshow("Orange Mask", mask)

                        if cv2.waitKey(1) & 0xFF == ord('q'):

                            return

 

            # Move forward toward ball

            else:

                print("Moving Forward Toward Ball for .2 s")

                motor_forward()

                start_time = time()

                while time() - start_time < 0.7: #this loop makes the camera constantly update

                    ret, frame = cap.read()

                    if ret:

                        hsv_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)

                        mask = cv2.inRange(hsv_frame, lower_orange, upper_orange)

                        cv2.imshow("Camera Feed", frame)

                        cv2.imshow("Orange Mask", mask)

                        if cv2.waitKey(1) & 0xFF == ord('q'):

                            return

                vex_motor_forward(70)

                sleep(1.25)

                vex_motor_forward(98)

                sleep(3)

 

            # Stop after interaction

            vex_motor_stop()

            motor_stop()

            start_time = time()

            while time() - start_time < 1.5:

                ret, frame = cap.read()

                if ret:

                    hsv_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)

                    mask = cv2.inRange(hsv_frame, lower_orange, upper_orange)

                    cv2.imshow("Camera Feed", frame)

                    cv2.imshow("Orange Mask", mask)

                    if cv2.waitKey(1) & 0xFF == ord('q'):

                        return

 

        # Obstacle avoidance

        elif distance < 75:

            print("Wall detected! Avoiding")

            motor_reverse()

            sleep(0.8)

            motor_stop()

            motor_right()

            sleep(0.6)

            motor_stop()

            start_time = time()

            while time() - start_time < 1.5: #this loop makes the camera constantly update

 

                distance = get_distance()

                ret, frame = cap.read()

                if ret:

                    cv2.imshow("Camera Feed", frame)

                    cv2.imshow("Orange Mask", mask)

                    if cv2.waitKey(1) & 0xFF == ord('q'):

                        return

 

        # Default zigzag movement

        else:

            print("Zigzagging...")

            if zigzag_state == 0:

                motor_left()

                start_time = time()

                while time() - start_time < 0.17: #this loop makes the camera constantly update

                    distance = get_distance()

                    ret, frame = cap.read()

                    if ret:

                        cv2.imshow("Camera Feed", frame)

                        cv2.imshow("Orange Mask", mask)

                        if cv2.waitKey(1) & 0xFF == ord('q'):

                            return

                motor_forward()

                zigzag_state = 1

            else:

                motor_right()

                start_time = time()

                while time() - start_time < 0.17:

                    distance = get_distance()

                    ret, frame = cap.read()

                    if ret:

                        cv2.imshow("Camera Feed", frame)

                        cv2.imshow("Orange Mask", mask)

                        if cv2.waitKey(1) & 0xFF == ord('q'):

                            return

                motor_forward()

                zigzag_state = 0

 

            start_time = time()

            while time() - start_time < 0.3: #this loop makes the camera constantly update

                distance = get_distance()

                ret, frame = cap.read()

                if ret:

                    cv2.imshow("Camera Feed", frame)

                    cv2.imshow("Orange Mask", mask)

                    if cv2.waitKey(1) & 0xFF == ord('q'):

                        return

 

            motor_stop()

            start_time = time()

            while time() - start_time < 1.5:

                distance = get_distance()

                ret, frame = cap.read()

                if ret:

                    cv2.imshow("Camera Feed", frame)

                    cv2.imshow("Orange Mask", mask)

                    if cv2.waitKey(1) & 0xFF == ord('q'):

                        return

 

# Start behavior loop

try:

    move_zigzag()

except KeyboardInterrupt: #makes the typical error exception 

    print("Stopping")

    motor_stop()

    lgpio.gpiochip_close(h)

    cap.release()

    cv2.destroyAllWindows()

bottom of page