Hazal Mestci
Published

Make a Robot Follow a Ball with Color Detection

Learn to use basic computer vision to make a rover follow a colored object.

BeginnerFull instructions provided519

Things used in this project

Hardware components

SCUTTLE Robot v3.0
SCUTTLE® SCUTTLE Robot v3.0
A wheeled rover. I used SCUTTLE for this project but you can use a different rover with a webcam on the front.
×1
Raspberry Pi 4 Model B
Raspberry Pi 4 Model B
You could also use a different board that can run 64-bit Linux.
×1
Webcam, Logitech® HD Pro
Webcam, Logitech® HD Pro
Or any webcam, such as one that is already on your robot.
×1

Software apps and online services

Viam Platform
Viam Robotics Viam Platform

Story

Read more

Code

Colored object following code

Python
Uses the Viam vision service to get color detections and drive a wheeled rover toward the detected color
import asyncio

from viam.robot.client import RobotClient
from viam.rpc.dial import Credentials, DialOptions
from viam.services.vision import VisionServiceClient
from viam.services.vision import VisModelConfig, VisModelType, Detection
from viam.components.camera import Camera
from viam.components.base import Base


async def connect():
   creds = Credentials(
        type="robot-location-secret",
        payload="[PLEASE ADD YOUR SECRET HERE. YOU CAN FIND THIS ON THE CODE SAMPLE TAB]")
   opts = RobotClient.Options(
        refresh_interval=0,
        dial_options=DialOptions(credentials=creds)
    )
   return await RobotClient.at_address("[ADD YOUR ROBOT ADDRESS HERE. YOU CAN FIND THIS ON THE CODE SAMPLE TAB]", opts)

# Get largest detection box and see if it's center is in the left, center, or right third
def leftOrRight(detections, midpoint):
   largest_area = 0
   largest = Detection()
   if not detections:
      print("nothing detected :(")
      return -1
   for d in detections:
      a = (d.x_max - d.x_min) * (d.y_max-d.y_min)
      if a > largest_area:
         a = largest_area
         largest = d
   centerX = largest.x_min + largest.x_max/2
   if centerX < midpoint-midpoint/6:
      return 0 # on the left
   if centerX > midpoint+midpoint/6:
      return 2 # on the right
   else:
      return 1  # basically centered

async def main():
   spinNum = 10         # when turning, spin the motor this much
   straightNum = 300    # when going straight, spin motor this much
   numCycles = 200      # run the loop X times
   vel = 500            # go this fast when moving motor

   # Connect to robot client and set up components
   robot = await connect()
   base = Base.from_robot(robot, "base")
   camera = Camera.from_robot(robot, "<camera-name>")

   # Grab the vision service for the detector
   my_detector = VisionClient.from_robot(robot, "my_color_detector")

   # Main loop. Detect the ball, determine if it's on the left or right, and head that way
   # Repeat this for numCycles
   for i in range(numCycles):
      detections = await my_detector.get_detections_from_camera(camera)

      answer = leftOrRight(detections, frame.size[0]/2)
      if answer == 0:
         print("left")
         await base.spin(spinNum, vel)     # CCW is positive
         await base.move_straight(straightNum, vel)
      if answer == 1:
         print("center")
         await base.move_straight(straightNum, vel)
      if answer == 2:
         print("right")
         await base.spin(-spinNum, vel)
      # If nothing is detected, nothing moves

   await robot.close()

if __name__ == "__main__":
  print("Starting up... ")
  asyncio.run(main())
  print("Done.")

Credits

Hazal Mestci
2 projects • 3 followers
I'm a Developer Advocate at Viam, a robotics startup, and I'm passionate about helping developers build innovative robotics projects.

Comments