Turn Your Raspberry Pi Pico into a Robot That Obeys Lines Like a Pro

Found your Raspberry PI Pico in a cupboard that you opened 2 years before… Get along with this project to make a line follower robot using Raspberry Pi Pico and a few other Stuff


What is a Line Follower Robot?

A line follower is a type of autonomous robot that can detect and follow a line drawn on the floor, typically a black line.

Components Required

| Component | Quantity | Notes |
|—-|—-|—-|
| Raspberry Pi Pico | 1 | Use the Pico H for easier pin access |
| BFD-1000 IR sensor array / any 5 array IR sensor Module | 1 | To detect the Black Line |
| L298N motor driver | 1 | To drive motors precisely |
| BO Motors (3–12V) | 2 | Preferably 12V ones 🙂 |
| Bo motor Wheels | 2 | Wheels For Bo Motor |
| Caster wheel (free wheel) | 1 | To balance the front |
| LiPo Battery (12V) | 1 | Or any regulated 12V source |
| Wires, breadboard, etc. | As needed | For connections |
| Computer | 1 | For programming and debugging |


Cooking the Pi🍳


The Software Part

1. Installing Thonny IDE

  • Visit: thonny’s Website

    Thonny Website Page

  • Download it for your OS (Windows/Mac/Linux).

    Link to install the Thonny IDE for windows only

  • Navigate to the Downloads folder after the .exe file has been downloaded in File Explorer

    Download Folder

  • Then click on the .exe file (thonny-4.1.7.exe) to execute the application and click on next until you see that the thonny is being installed

    installation wizard

Flashing the pico with Micropython Firmware

:::info
If you have already flashed the micropython firmware the you can skip to next part

:::

Plug in your Pico while holding the BOOTSEL button.

  1. It appears as a USB drive.
  2. Go to micropython.uf2[ file]() download for Pico
  3. Download .uf2 the file and copy it to the Pico USB drive.
  4. Pico will reboot into MicroPython.

Set up Thonny

  • Open Thonny

  • Go to Run > Select Interpreter > MicroPython (Raspberry Pi Pico)

    Port Selection

  • Select the right port.

  • Paste the following MicroPython code into the script area

    Get code here or paste the code below

  from machine import Pin, PWM
  import time

  # Motor Pins
  in1 = Pin(2, Pin.OUT)
  in2 = Pin(3, Pin.OUT)
  in3 = Pin(4, Pin.OUT)
  in4 = Pin(5, Pin.OUT)

  ena = PWM(Pin(6))
  enb = PWM(Pin(7))
  ena.freq(1000)
  enb.freq(1000)

  # Sensor Pins
  sensors = [Pin(i, Pin.IN) for i in range(8, 13)]

  # Speed Settings
  BASE_SPEED = 35000  # Slow and safe for normal movement
  MAX_SPEED = 40000   # Max PWM limit
  TURN_SPEED = 25000  # Slower speed for turning

  # PID Settings
  Kp = 8000  # Proportional gain, tune this as per your bot
  Ki = 0     
  Kd = 0     

  # PID Variables
  previous_error = 0
  integral = 0

  # Motor control functions
  def set_motor_speed(left_speed, right_speed):
      left_speed = max(0, min(MAX_SPEED, left_speed))
      right_speed = max(0, min(MAX_SPEED, right_speed))

      if left_speed == 0:
          in1.low()
          in2.low()
      else:
          in1.high()
          in2.low()

      if right_speed == 0:
          in3.low()
          in4.low()
      else:
          in3.high()
          in4.low()

      ena.duty_u16(left_speed)
      enb.duty_u16(right_speed)

  def stop():
      in1.low()
      in2.low()
      in3.low()
      in4.low()
      ena.duty_u16(0)
      enb.duty_u16(0)

  # Read sensor values
  def read_sensors():
      return [s.value() for s in sensors]

  # Calculate position (PID error calculation)
  def calculate_error(sensor_values):
      weights = [-2, -1, 0, 1, 2]
      total = 0
      count = 0
      for i in range(5):
          if sensor_values[i] == 0:  # Line detected (assuming black line)
              total += weights[i]
              count += 1
      if count == 0:
          return None  # Line lost
      return total / count

  # PID controller
  def pid_control(error):
      global previous_error, integral

      if error is None:
          return 0, 0  # No correction needed if line is lost

      integral += error
      derivative = error - previous_error
      correction = int(Kp * error + Ki * integral + Kd * derivative)

      previous_error = error

      return correction, correction

  # Smart Search with PID
  def smart_search():
      global previous_error, integral  # Reset PID variables for search
      previous_error = 0
      integral = 0

      for attempt in range(5):
          print("Search attempt", attempt+1)

          # Turn left (using PID control)
          in1.low()
          in2.high()
          in3.high()
          in4.low()
          ena.duty_u16(TURN_SPEED)
          enb.duty_u16(TURN_SPEED)
          time.sleep(0.5)
          stop()
          time.sleep(0.1)
          sensor_values = read_sensors()
          error = calculate_error(sensor_values)
          left_correction, right_correction = pid_control(error)

          if 0 in sensor_values:
              print("Found line after left turn")
              return

          # Turn right (using PID control)
          in1.high()
          in2.low()
          in3.low()
          in4.high()
          ena.duty_u16(TURN_SPEED)
          enb.duty_u16(TURN_SPEED)
          time.sleep(1.0)
          stop()
          time.sleep(0.1)
          sensor_values = read_sensors()
          error = calculate_error(sensor_values)
          left_correction, right_correction = pid_control(error)

          if 0 in sensor_values:
              print("Found line after right turn")
              return

      print("Failed to find line after searching.")

  # Main loop
  while True:
      sensor_values = read_sensors()
      print("Sensors:", sensor_values)

      error = calculate_error(sensor_values)

      if error is None:
          print("Line lost, starting search")
          stop()
          smart_search()
      else:
          correction = int(Kp * error)

          left_speed = BASE_SPEED - correction
          right_speed = BASE_SPEED + correction

          set_motor_speed(left_speed, right_speed)

      time.sleep(0.01)

Script Area

Click on the save icon The SAVE icon

  • You will get a Prompt stating:-

    Where do you want to save

    1)Raspberry Pi Pico

    2)To this PC

  • Choose Pico and save the file as main.py otherwise it will not auto-run on when powered on


The Hardware part

Connections

Here is a link for the line follower connections:- Click Here

Wiring Diagram

Connection Table

L298N to Pico

| L298N Pin | Connects To |
|—-|—-|
| IN1 | Pico GP2 |
| IN2 | Pico GP3 |
| IN3 | Pico GP4 |
| IN4 | Pico GP5 |
| ENA | Pico GP6 (PWM) |
| ENB | Pico GP7 (PWM) |
| VCC | 12V from the battery |
| GND | Pico GND & battery GND |
| 5V | V_Sys pin |

BFD 1000 to Pico

| IR Sensor Pin | Connects To (Pico Pin) | Function |
|—-|—-|—-|
| OUT1 | GP8 | Left-most sensor |
| OUT2 | GP9 | Left sensor |
| OUT3 | GP10 | Center sensor |
| OUT4 | GP11 | Right sensor |
| OUT5 | GP12 | Right-most sensor |
| VCC | 5V | Power |
| GND | GND | Ground |


IR Sensor Calibration

  1. Run your line follower code in Thonny.

  2. You should see values being printed in the following format corresponding to each sensor:-

    Sensors: [x, x, x, x, x]

  3. Place the bot on a white surface. You should see: [1, 1, 1, 1, 1] (white reflects IR = HIGH).

  4. Move the centre sensor over the black line. You should see: [1, 1, 0, 1, 1] (black absorbs IR = LOW).

  5. Slide the bot side to side across the line. All sensors should detect black (0) when over a line.

  6. Adjust potentiometers (if needed) on the IR sensor for reliable 0/1 switching.


Test Run Video

Drive Link:-https://drive.google.com/file/d/1ODuU0T4gvLMk8YIl7x5UDorZBtHX7OeK/view?usp=sharing

That’s all, folks, for this project

Meet you in another tutorial like this

Thanks,

Shivank Dan

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.