Acrome-SMD Docs
All Acrome ProductsReferencesBlogCase StudiesContact Us
  • ACROME SMD
  • Electronics
    • 🔴SMD Red
      • Coding Guide
      • Robot with Raspberry Pi Setup Guide
      • Robot with Arduino Setup Guide
      • Troubleshooting Guide
    • 🔵SMD Blue
    • 🟢SMD Green
    • Gateway Modules
      • Arduino Gateway Module
      • USB Gateway Module
    • Electrical Motors
      • Brushed DC Motors (BDC)
      • Stepper DC Motors (SDC)
      • Brushless DC Motor (BLDC)
      • Linear Actuator with Feedback
    • Add-on Modules
      • Ambient Light Sensor Module
      • Button Module
      • Buzzer Module
      • IMU Module
      • Joystick Module
      • Potentiometer Module
      • Reflectance Sensor Module
      • RGB LED Module
      • Servo Module
      • Ultrasonic Distance Sensor Module
  • SMD Kits
    • Starter Kit
      • What You Can Build
    • Education Kit
      • What You Can Build
    • Motion Kit
      • What You Can Build
  • Software
    • Libraries
      • Python Library
      • Arduino Library
      • Java Library
      • Matlab Library
    • SMD UI
    • SMD Blockly
      • Introducing Customized Blockly Blocks
  • SMD Applications
    • Basics
      • Blink
      • Action - Reaction
      • Autonomous Lighting
      • Smart Doorbell
      • Security System
      • Distance Buzzer Warning
      • Distance Auto Stop
      • Smart Light Control
    • Interactive
      • Automatic Trash Bin
      • Radar
      • Chrome Dino Game Player
      • Play Chrome Dino Game With Joystick
      • Snake Game With Joystick
      • Pan-Tilt with Joystick Module
      • Joystick Mouse Control
      • Rev Up the Engine
      • Motor Rotation Based on Turn Input Value
      • Basic Motor Speed Control Application
      • Basic Motor Control Application Using PWM Input
      • Basic Motor Position Control Application
      • Basic Motor Torque Control Application
      • Motor Rotation Based on Joystick Counting
    • Robotics
      • Differential Robot Projects
      • Mouse Cursor Tracker Motion Robot
      • Waypoint tracker robot
      • Braitenberg Robot
      • Line-Follower Robot
      • Teleoperation Robot
      • Obstacle Avoidance Robot
      • ESP32 Wireless Controlled Mobile Robot
  • AI
    • Object Tracking Robot
    • Groq Chatbot-Controlled Robot
  • ROS
    • Teleoperation Robot with ROS
  • Mechanics
    • Building Set
      • Plates
        • 2x2 Plate Package
        • 2x3 120° Plate Package
        • 3x3 Plate Package
        • 3x5 Plate Package
        • 3x9 Plate Package
        • 11x19 Plate
        • 9x19 Plate
        • 5x19 Plate
        • 3x19 Plate
        • 9x11 Plate
        • 5x13 Plate
      • Joints
        • 60° Joint Package
        • 90° Joint Package
        • 120° Joint Package
        • Slot Joint M2 Package
        • Slot Joint M3 Package
        • U Joint Package
      • Mounts
        • Add-on Mount Package
        • Motor L Mount Package
        • Pan-Tilt Package
      • Wheels
        • Ball Wheel Package
        • Caster Wheel Package
        • Wheel Package
      • Cables
        • Power Cable 10 cm Package
        • Power Cable 20 cm Package
        • Power Cable 35 cm Package
        • RJ-11 Cable 7.5 cm Package
        • RJ-11 Cable 20 cm Package
        • RJ-11 Cable 35 cm Package
        • RJ-45 Cable 7.5 cm Package
        • RJ-45 Cable 20 cm Package
        • RJ-45 Cable 35 cm Package
      • Fasteners
        • M2x5 Allen Hex Screw Package
        • M3x6 Allen Hex Screw Package
        • M3x8 Allen Hex Screw Package
        • M3 Hex Nut Package
  • Help
    • Manual
    • Shops
    • Reach Us
Powered by GitBook
On this page
  1. SMD Applications
  2. Robotics
  3. Differential Robot Projects

Line-Follower Robot

The Line Follower Robot is an intelligent robotic system designed to autonomously follow a predefined path using sensor feedback. This project demonstrates fundamental concepts in robot navigation, sensor fusion, and control systems, making it an ideal platform for learning about autonomous robotics.

Key Features:

  • Infrared (IR) or Optical Sensors: Detects and follows lines with high accuracy.

  • Smooth & Precise Movement: Uses real-time feedback to maintain stability and avoid deviations.

  • Hands-on Learning: Explores control algorithms, such as PID control, for optimized navigation.

  • Real-World Applications: Commonly used in automated transport, warehouse robots, and industrial automation.

Discover More About the Line Follower Robot

Codes

from smd.red import *
import time
import tkinter as tk
import math
from serial.tools.list_ports import comports
from platform import system

class LineFollower:
    def __init__(self, port):
        # Initialize master and motors
        self.m = Master(port)
        self.redLeft = self.m.attach(Red(1))
        self.redRight = self.m.attach(Red(0))
        
        # PID parameters
        self.kp = 0.13
        self.kd = 0.38
        self.ki = 0.0
        
        # Control variables
        self.error = 0
        self.prev_error = 0
        self.integral = 0
        self.main_speed = 50
        
        # Create GUI
        self.root = tk.Tk()
        self.root.title("Robot Movement")
        
        # Create canvas
        self.canvas = tk.Canvas(self.root, width=1200, height=800, bg='white')
        self.canvas.pack(pady=20)
        
        # Robot position
        self.robot_x = 600
        self.robot_y = 400
        self.robot_angle = 0
        self.path_points = []
        
        # Robot size
        self.robot_size = 15
        
        # Robot shape
        self.robot = self.canvas.create_rectangle(
            self.robot_x - self.robot_size,
            self.robot_y - self.robot_size,
            self.robot_x + self.robot_size,
            self.robot_y + self.robot_size,
            fill='red'
        )
        
        # Direction indicator
        self.direction = self.canvas.create_line(
            self.robot_x,
            self.robot_y,
            self.robot_x + self.robot_size * math.cos(self.robot_angle),
            self.robot_y - self.robot_size * math.sin(self.robot_angle),
            fill='black',
            width=2
        )
        
        # Control buttons
        self.button_frame = tk.Frame(self.root)
        self.button_frame.pack(pady=10)
        
        self.start_button = tk.Button(self.button_frame, text="Start", command=self.start)
        self.start_button.pack(side=tk.LEFT, padx=5)
        
        self.stop_button = tk.Button(self.button_frame, text="Stop", command=self.stop)
        self.stop_button.pack(side=tk.LEFT, padx=5)
        
        self.clear_button = tk.Button(self.button_frame, text="Clear", command=self.clear_path)
        self.clear_button.pack(side=tk.LEFT, padx=5)
        
        self.running = False

    def setup(self):
        """Initial robot setup"""
        try:
            # Set to PWM mode
            self.m.set_operation_mode(1, OperationMode.PWM)
            self.m.set_operation_mode(0, OperationMode.PWM)
            
            # Enable motors
            self.m.enable_torque(1, True)
            self.m.enable_torque(0, True)
            return True
        except Exception as e:
            print(f"Error: {e}")
            return False

    def read_line(self):
        """Read line position from QTR sensors"""
        try:
            qtr_values = self.m.get_qtr(0, 1)  # Get QTR values
            
            if qtr_values is not None:
                left = qtr_values[0]
                middle = qtr_values[1]
                right = qtr_values[2]
                
                # Calculate weighted sum
                weighted_sum = (left * 0) + (middle * 1000) + (right * 2000)
                sensor_sum = left + middle + right
                
                if sensor_sum > 0:
                    return weighted_sum / sensor_sum
            return None
        except Exception as e:
            print(f"Sensor read error: {e}")
            return None

    def calculate_pid(self, position):
        """PID calculation"""
        self.error = position - 1000  # Middle point is 1000
        self.integral += self.error
        derivative = self.error - self.prev_error
        
        # Limit integral
        self.integral = max(min(self.integral, 500), -500)
        
        # Calculate PID output
        output = (self.kp * self.error) + \
                (self.ki * self.integral) + \
                (self.kd * derivative)
                
        self.prev_error = self.error
        return output

    def update_robot_position(self, left_speed, right_speed):
        """Update robot position"""
        # Normalize speeds (-100 to 100 -> -1 to 1)
        left_speed = left_speed / 100.0
        right_speed = right_speed / 100.0
        
        # Calculate angular velocity
        angular_velocity = (right_speed - left_speed) * 0.1
        self.robot_angle += angular_velocity
        
        # Calculate forward speed
        forward_speed = (right_speed + left_speed) * 0.5
        
        # Calculate new position
        self.robot_x += forward_speed * math.cos(self.robot_angle) * 5
        self.robot_y -= forward_speed * math.sin(self.robot_angle) * 5
        
        # Update robot shape
        self.canvas.coords(
            self.robot,
            self.robot_x - self.robot_size,
            self.robot_y - self.robot_size,
            self.robot_x + self.robot_size,
            self.robot_y + self.robot_size
        )
        
        # Update direction line
        self.canvas.coords(
            self.direction,
            self.robot_x,
            self.robot_y,
            self.robot_x + self.robot_size * math.cos(self.robot_angle),
            self.robot_y - self.robot_size * math.sin(self.robot_angle)
        )
        
        # Draw path
        self.path_points.append((self.robot_x, self.robot_y))
        if len(self.path_points) > 1:
            self.canvas.create_line(
                self.path_points[-2][0],
                self.path_points[-2][1],
                self.path_points[-1][0],
                self.path_points[-1][1],
                fill='blue',
                width=2
            )
        
        # Update canvas
        self.root.update()

    def set_motors(self, pid):
        """Set motor speeds"""
        base_speed = self.main_speed
        error_abs = abs(self.error)
        
        if error_abs > 400:
            base_speed = self.main_speed * 0.3
        elif error_abs > 200:
            base_speed = self.main_speed * 0.5
        elif error_abs > 100:
            base_speed = self.main_speed * 0.7
            
        if pid < 0:
            right_speed = base_speed + abs(pid)
            left_speed = base_speed * (1 - abs(pid)/100)
        else:
            right_speed = base_speed * (1 - abs(pid)/100)
            left_speed = base_speed + abs(pid)
        
        right_speed = min(100, max(-100, right_speed))
        left_speed = min(100, max(-100, left_speed))
        
        self.m.set_duty_cycle(0, -left_speed)
        self.m.set_duty_cycle(1, right_speed)
        
        # Update GUI
        self.update_robot_position(left_speed, right_speed)

    def clear_path(self):
        """Clear path"""
        self.canvas.delete("all")
        self.path_points = []
        self.robot_x = 600
        self.robot_y = 400
        self.robot_angle = 0
        
        # Redraw robot and direction line
        self.robot = self.canvas.create_rectangle(
            self.robot_x - self.robot_size,
            self.robot_y - self.robot_size,
            self.robot_x + self.robot_size,
            self.robot_y + self.robot_size,
            fill='red'
        )
        self.direction = self.canvas.create_line(
            self.robot_x,
            self.robot_y,
            self.robot_x + self.robot_size * math.cos(self.robot_angle),
            self.robot_y - self.robot_size * math.sin(self.robot_angle),
            fill='black',
            width=2
        )

    def start(self):
        """Start the robot"""
        self.running = True
        self.run()

    def stop(self):
        """Stop the robot"""
        self.running = False
        self.m.set_duty_cycle(0, 0)
        self.m.set_duty_cycle(1, 0)

    def run(self):
        """Main operation loop"""
        if not self.setup():
            print("Setup failed!")
            return
            
        print("Starting robot...")
        try:
            while self.running:
                position = self.read_line()
                if position is not None:
                    pid = self.calculate_pid(position)
                    self.set_motors(pid)
                self.root.update()
                time.sleep(0.001)
                
        except KeyboardInterrupt:
            print("\nStopping program...")
        finally:
            self.m.set_duty_cycle(0, 0)
            self.m.set_duty_cycle(1, 0)

def USB_Port():
	ports = list(comports())
	usb_names = {
		"Windows": ["USB Serial Port"],
		"Linux": ["/dev/ttyUSB"],
		"Darwin": [
			"/dev/tty.usbserial",
			"/dev/tty.usbmodem",
			"/dev/tty.SLAB_USBtoUART",
			"/dev/tty.wchusbserial",
			"/dev/cu.usbserial",
            "/dev/cu.usbmodem",
			"/dev/cu.SLAB_USBtoUART",
			"/dev/cu.wchusbserial",
		]
	}
	
	os_name = system()
	if ports:
		for port, desc, hwid in sorted(ports):
			if any(name in port or name in desc for name in usb_names.get(os_name, [])):
				print("Connect")
				return port
		# Eğer uygun port bulunmazsa mevcut portları yazdır
		print("Mevcut portlar:")
		for port, desc, hwid in ports:
			print(f"Port: {port}, Açıklama: {desc}, HWID: {hwid}")
	else:
		print("Port not found!")
	return None

def main():
    port = USB_Port()
    if port:
        print(f"Port found: {port}")
        robot = LineFollower(port)
        robot.root.mainloop()
    else:
        print("Port not found!")

if __name__ == "__main__":
    main()
#include <Acrome-SMD.h>

#define BAUDRATE    115200


Red redLeft(1, Serial, BAUDRATE);
Red redRight(0, Serial, BAUDRATE); 

uint8_t Sensor[1]={iQTR_1};
int16_t X[3];
int Error,DeltaError,IterationP,IterationD,PrevError,PD;

float kp=0.13;
float kd=0.38;
short MainSpeed=100;


void setup() {
  redLeft.begin();
  redRight.begin();
  redLeft.setOperationMode(PWMControl);
  redRight.setOperationMode(PWMControl);
  redLeft.torqueEnable(1);
  redRight.torqueEnable(1);
  redRight.setConnectedModules(Sensor, 1);
}


void loop() {
  LinePos();
  PD2PWM();
}


void LinePos() {
  QTRValues qtrValues = redRight.getQTR(1);

  uint32_t WeightedSum=0,SensorSum=0;
  float PosValue=0;

  WeightedSum=(int32_t)(qtrValues.MiddleValue)*1000+(int32_t)(qtrValues.RightValue)*2000;
  SensorSum= qtrValues.LeftValue+qtrValues.MiddleValue+qtrValues.RightValue;
  PosValue=(float)(WeightedSum)/(float)(SensorSum);

  fPD(PosValue);
}


void fPD(int32_t PosValue) {
  Error= PosValue-1000;

  DeltaError=Error-PrevError;

  IterationP=kp*Error;
  IterationD=kd*DeltaError;

  PD=IterationP+IterationD;

  PrevError=Error;
}


void PD2PWM() {
  float RightPWM=(PD<0)?MainSpeed+PD:MainSpeed;
  float LeftPWM=(PD<0)?MainSpeed:MainSpeed-PD;
  
  RightPWM=(RightPWM>100)?100:RightPWM; RightPWM=(RightPWM<-MainSpeed)?-MainSpeed:RightPWM;
  LeftPWM=(LeftPWM>100)?100:LeftPWM; LeftPWM=(LeftPWM<-MainSpeed)?-MainSpeed:LeftPWM;
  
  redRight.setpoint(0,RightPWM);
  redLeft.setpoint(0,(-1)*LeftPWM);
}

Last updated 2 months ago