From cf248b1e3d261d895278af5bda837e64635c7f69 Mon Sep 17 00:00:00 2001
From: Paul Winpenny
 <plw1g21@soton.ac.ukgit config --global user.email plw1g21@soton.ac.uk>
Date: Thu, 21 Nov 2024 15:31:00 +0000
Subject: [PATCH] Added scripts from motor controller pi

---
 Motor_controller_test_pi/Encoder/Encoder.py   |  60 +++++++
 .../Encoder/Test_encoder_pulse.py             |  42 +++++
 Motor_controller_test_pi/Encoder_testing.py   | 104 +++++++++++
 Motor_controller_test_pi/Encoder_testing_2.py |  83 +++++++++
 .../IMU /Boasch/New_IMU.py                    |  45 +++++
 .../IMU /Boasch/New_IMU_calibration.py        |  65 +++++++
 .../IMU /IMU_Calibration.py                   | 164 ++++++++++++++++++
 Motor_controller_test_pi/IMU /IMU_Library.py  |  47 +++++
 Motor_controller_test_pi/IMU /IMU_smbus.py    |  99 +++++++++++
 Motor_controller_test_pi/IMU /ahrs_library.py | 118 +++++++++++++
 .../Motor_control/Motor_control.py            | 132 ++++++++++++++
 .../Motor_control/Ros2_Motor_control.py       |  68 ++++++++
 .../Test_encoder_motor.py                     | 104 +++++++++++
 Motor_controller_test_pi/pigpio.py            | 135 ++++++++++++++
 14 files changed, 1266 insertions(+)
 create mode 100644 Motor_controller_test_pi/Encoder/Encoder.py
 create mode 100644 Motor_controller_test_pi/Encoder/Test_encoder_pulse.py
 create mode 100644 Motor_controller_test_pi/Encoder_testing.py
 create mode 100644 Motor_controller_test_pi/Encoder_testing_2.py
 create mode 100644 Motor_controller_test_pi/IMU /Boasch/New_IMU.py
 create mode 100644 Motor_controller_test_pi/IMU /Boasch/New_IMU_calibration.py
 create mode 100644 Motor_controller_test_pi/IMU /IMU_Calibration.py
 create mode 100644 Motor_controller_test_pi/IMU /IMU_Library.py
 create mode 100644 Motor_controller_test_pi/IMU /IMU_smbus.py
 create mode 100644 Motor_controller_test_pi/IMU /ahrs_library.py
 create mode 100644 Motor_controller_test_pi/Motor_control/Motor_control.py
 create mode 100644 Motor_controller_test_pi/Motor_control/Ros2_Motor_control.py
 create mode 100644 Motor_controller_test_pi/Test_encoder_motor.py
 create mode 100644 Motor_controller_test_pi/pigpio.py

diff --git a/Motor_controller_test_pi/Encoder/Encoder.py b/Motor_controller_test_pi/Encoder/Encoder.py
new file mode 100644
index 00000000..6c949677
--- /dev/null
+++ b/Motor_controller_test_pi/Encoder/Encoder.py
@@ -0,0 +1,60 @@
+import RPi.GPIO as GPIO
+import time
+
+# Pin configuration
+channel_a = 17  # GPIO pin for Channel A
+channel_b = 27  # GPIO pin for Channel B
+
+# Encoder specifications
+PPR = 5  # Pulses per revolution
+time_interval = 1.0  # Time interval for speed calculation in seconds
+
+# Variables
+pulse_count = 0
+direction = None
+last_time = time.time()
+
+def setup():
+    GPIO.setmode(GPIO.BCM)
+    GPIO.setup(channel_a, GPIO.IN, pull_up_down=GPIO.PUD_UP)
+    GPIO.setup(channel_b, GPIO.IN, pull_up_down=GPIO.PUD_UP)
+    GPIO.add_event_detect(channel_a, GPIO.RISING, callback=encoder_callback)
+
+def encoder_callback(channel):
+    global pulse_count, direction
+    channel_b_state = GPIO.input(channel_b)
+    if channel_b_state == 0:
+        direction = "Clockwise"
+        pulse_count += 1
+    else:
+        direction = "Counterclockwise"
+        pulse_count -= 1
+
+def calculate_speed():
+    global pulse_count, last_time
+    current_time = time.time()
+    elapsed_time = current_time - last_time
+    last_time = current_time
+
+    # Calculate revolutions
+    revolutions = pulse_count / PPR
+    pulse_count = 0  # Reset pulse count after calculation
+
+    # Calculate RPM
+    rpm = (revolutions / elapsed_time) * 60
+    return rpm
+
+def main():
+    try:
+        setup()
+        print("Reading encoder data and calculating speed...")
+        while True:
+            time.sleep(time_interval)  # Wait for the interval
+            speed = calculate_speed()
+            print(f"Speed: {speed:.2f} RPM, Direction: {direction}")
+    except KeyboardInterrupt:
+        print("\nExiting...")
+        GPIO.cleanup()
+
+if __name__ == "__main__":
+    main()
diff --git a/Motor_controller_test_pi/Encoder/Test_encoder_pulse.py b/Motor_controller_test_pi/Encoder/Test_encoder_pulse.py
new file mode 100644
index 00000000..a7b76224
--- /dev/null
+++ b/Motor_controller_test_pi/Encoder/Test_encoder_pulse.py
@@ -0,0 +1,42 @@
+from gpiozero import Button
+from time import sleep
+
+# Define pins for the Hall sensors
+MOTOR1_HALL_A = 17  # Update with actual GPIO pin for Motor 1, Channel A
+MOTOR1_HALL_B = 27  # Update with actual GPIO pin for Motor 1, Channel B
+MOTOR2_HALL_A = 22  # Update with actual GPIO pin for Motor 2, Channel A
+MOTOR2_HALL_B = 23  # Update with actual GPIO pin for Motor 2, Channel B
+
+# Create Button objects for sensors
+motor1_hall_a = Button(MOTOR1_HALL_A, pull_up=True)
+motor1_hall_b = Button(MOTOR1_HALL_B, pull_up=True)
+motor2_hall_a = Button(MOTOR2_HALL_A, pull_up=True)
+motor2_hall_b = Button(MOTOR2_HALL_B, pull_up=True)
+
+# Callback functions for state changes
+def motor1_channel_a_detected():
+    print("Motor 1: Channel A pulse detected")
+
+def motor1_channel_b_detected():
+    print("Motor 1: Channel B pulse detected")
+
+def motor2_channel_a_detected():
+    print("Motor 2: Channel A pulse detected")
+
+def motor2_channel_b_detected():
+    print("Motor 2: Channel B pulse detected")
+
+# Attach callbacks to each sensor
+motor1_hall_a.when_pressed = motor1_channel_a_detected
+motor1_hall_b.when_pressed = motor1_channel_b_detected
+motor2_hall_a.when_pressed = motor2_channel_a_detected
+motor2_hall_b.when_pressed = motor2_channel_b_detected
+
+# Main loop
+try:
+    print("Monitoring Hall sensors for both motors. Press Ctrl+C to exit.")
+    while True:
+        sleep(0.1)  # Prevent excessive CPU usage
+
+except KeyboardInterrupt:
+    print("\nExiting program...")
diff --git a/Motor_controller_test_pi/Encoder_testing.py b/Motor_controller_test_pi/Encoder_testing.py
new file mode 100644
index 00000000..8c071e48
--- /dev/null
+++ b/Motor_controller_test_pi/Encoder_testing.py
@@ -0,0 +1,104 @@
+from gpiozero import PWMOutputDevice, RotaryEncoder
+from time import sleep, time
+
+class Motor():
+    def __init__(self, EnaA, In1A, In2A, EnaB, In1B, In2B, encA_pins=None, encB_pins=None):
+        # Initialize motor control pins
+        self.pwmA = PWMOutputDevice(EnaA)
+        self.in1A = PWMOutputDevice(In1A)
+        self.in2A = PWMOutputDevice(In2A)
+        self.pwmB = PWMOutputDevice(EnaB)
+        self.in1B = PWMOutputDevice(In1B)
+        self.in2B = PWMOutputDevice(In2B)
+        
+        # Initialize encoders if pins are provided
+        if encA_pins:
+            self.encoderA = RotaryEncoder(a=encA_pins[0], b=encA_pins[1], max_steps=0)
+            self.encoderA.steps = 0  # Reset steps
+            self.encoderA.when_rotated = self.on_rotate_left
+        else:
+             self.encoderA = None
+            
+        if encB_pins:
+            self.encoderB = RotaryEncoder(a=encB_pins[0], b=encB_pins[1], max_steps=0)
+            self.encoderB.steps = 0  # Reset steps
+            self.encoderB.when_rotated = self.on_rotate_right
+        else:
+            self.encoderB = None
+    
+    # Callback for left encoder rotation
+    def on_rotate_left(self):
+        steps = self.encoderA.steps
+        print(f"Left Encoder Steps: {steps} trigger")
+    
+    # Callback for right encoder rotation
+    def on_rotate_right(self):
+        steps = self.encoderB.steps
+        #print(f"Right Encoder Steps: {steps}")
+        
+    def move(self, speed=0.5, turn=0, t=0):
+        # Ensure speed and turn are within bounds
+        speed = max(-1, min(1, speed))  # Clamp speed to [-1, 1]
+        turn = max(-1, min(1, turn))    # Clamp turn to [-1, 1]
+        
+        # Calculate speed for each motor
+        leftSpeed = speed - turn
+        rightSpeed = speed + turn
+        
+        # Clamp motor speeds to [-1, 1]
+        leftSpeed = max(-1, min(1, leftSpeed))
+        rightSpeed = max(-1, min(1, rightSpeed))
+        
+        # Set left motor
+        self.pwmA.value = abs(leftSpeed)
+        self.in1A.value = leftSpeed > 0
+        self.in2A.value = leftSpeed <= 0
+        
+        # Set right motor
+        self.pwmB.value = abs(rightSpeed)
+        self.in1B.value = rightSpeed > 0
+        self.in2B.value = rightSpeed <= 0
+        
+        # Print debugging info
+        print(f"Left Motor: Speed={leftSpeed}, Right Motor: Speed={rightSpeed}")
+        
+        # Run the motors for time 't' while monitoring encoder steps
+        start_time = time()
+        while time() - start_time < t:
+            # Optionally, read and print encoder values
+            if self.encoderA:
+                left_steps = self.encoderA.steps
+                print(f"Left Encoder Steps: {left_steps}")
+            if self.encoderB:
+                right_steps = self.encoderB.steps
+#                 print(f"Right Encoder Steps: {right_steps}")
+            sleep(0.1)  # Adjust sleep time as needed
+            
+    def stop(self, t=0):
+        self.pwmA.value = 0
+        self.pwmB.value = 0
+        sleep(t)
+
+def main():
+    # Define encoder GPIO pins for left and right encoders
+    # Replace these with your actual GPIO pins
+    left_encoder_pins = (5, 6)     # GPIO pins for left encoder channels A and B
+    right_encoder_pins = (13, 19)  # GPIO pins for right encoder channels A and B
+    
+    # Initialize motor with encoder pins
+    motor1 = Motor(2, 3, 4, 17, 22, 27, encA_pins=left_encoder_pins, encB_pins=right_encoder_pins)
+    
+    # Run motor commands
+    motor1.move(0.5, 0, 10)  # Move forward for 3 seconds
+    motor1.stop(1)
+    '''
+    motor1.move(-0.5, 0, 3)  # Move backward for 3 seconds
+    motor1.stop(1)
+    motor1.move(0.5, 0.5, 3)  # Turn right for 3 seconds
+    motor1.stop(1)
+    motor1.move(0.5, -0.5, 3)  # Turn left for 3 seconds
+    motor1.stop(1)
+    '''
+
+if __name__ == '__main__':
+    main()
diff --git a/Motor_controller_test_pi/Encoder_testing_2.py b/Motor_controller_test_pi/Encoder_testing_2.py
new file mode 100644
index 00000000..50f65f46
--- /dev/null
+++ b/Motor_controller_test_pi/Encoder_testing_2.py
@@ -0,0 +1,83 @@
+from gpiozero import RotaryEncoder, PWMOutputDevice
+from time import sleep
+
+class Motor():
+    def __init__(self, EnaA, In1A, In2A, EnaB, In1B, In2B):
+        self.pwmA = PWMOutputDevice(EnaA)
+        self.in1A = PWMOutputDevice(In1A)
+        self.in2A = PWMOutputDevice(In2A)
+        self.pwmB = PWMOutputDevice(EnaB)
+        self.in1B = PWMOutputDevice(In1B)
+        self.in2B = PWMOutputDevice(In2B)
+        
+    def move(self, speed=0.5, turn=0, t=0):
+        # Ensure speed and turn are within bounds
+        speed = max(-1, min(1, speed))  # Clamp speed to [-1, 1]
+        turn = max(-1, min(1, turn))    # Clamp turn to [-1, 1]
+        
+        # Calculate speed for each motor
+        leftSpeed = speed - turn
+        rightSpeed = speed + turn
+        
+        # Clamp motor speeds to [-1, 1]
+        leftSpeed = max(-1, min(1, leftSpeed))
+        rightSpeed = max(-1, min(1, rightSpeed))
+        
+        # Set left motor
+        self.pwmA.value = abs(leftSpeed)
+        self.in1A.value = leftSpeed > 0
+        self.in2A.value = leftSpeed <= 0
+        
+        # Set right motor
+        self.pwmB.value = abs(rightSpeed)
+        self.in1B.value = rightSpeed > 0
+        self.in2B.value = rightSpeed <= 0
+        
+        # Print debugging info
+        print(f"Left Motor: Speed={leftSpeed}, Right Motor: Speed={rightSpeed}")
+        
+        sleep(t)
+        
+    def stop(self, t=0):
+        self.pwmA.value = 0
+        self.pwmB.value = 0
+        sleep(t)
+
+
+# Encoder GPIO pins (updated to avoid conflicts)
+MOTOR1_HALL_A = 5
+MOTOR1_HALL_B = 6
+MOTOR2_HALL_A = 13
+MOTOR2_HALL_B = 19
+
+# Create RotaryEncoder objects for encoders
+motor1_encoder = RotaryEncoder(MOTOR1_HALL_A, MOTOR1_HALL_B, max_steps=0)
+motor2_encoder = RotaryEncoder(MOTOR2_HALL_A, MOTOR2_HALL_B, max_steps=0)
+
+# Callback functions for rotation events
+def motor1_rotated():
+    print(f"Motor 1: Steps={motor1_encoder.steps}, Direction={'Clockwise' if motor1_encoder.value > 0 else 'Counter-Clockwise'}")
+
+def motor2_rotated():
+    print(f"Motor 2: Steps={motor2_encoder.steps}, Direction={'Clockwise' if motor2_encoder.value > 0 else 'Counter-Clockwise'}")
+
+# Attach callbacks to each encoder
+motor1_encoder.when_rotated = motor1_rotated
+motor2_encoder.when_rotated = motor2_rotated
+
+def main():
+    print("Starting motor and monitoring encoder outputs...")
+    motor1.move(0.1, 0, 3)  # Move forward for 3 seconds
+    motor1.stop(1)
+    # You can add more motor movements here
+    print("Test complete.")
+    # Keep the program running to continue monitoring encoders
+    while True:
+        sleep(1)
+
+if __name__ == '__main__':
+    motor1 = Motor(2, 3, 4, 17, 22, 27)
+    try:
+        main()
+    except KeyboardInterrupt:
+        print("\nExiting program...")
diff --git a/Motor_controller_test_pi/IMU /Boasch/New_IMU.py b/Motor_controller_test_pi/IMU /Boasch/New_IMU.py
new file mode 100644
index 00000000..b31f56b3
--- /dev/null
+++ b/Motor_controller_test_pi/IMU /Boasch/New_IMU.py	
@@ -0,0 +1,45 @@
+import smbus
+import time
+
+class BNO055:
+    def __init__(self, i2c_bus=1, address=0x28):
+        self.bus = smbus.SMBus(i2c_bus)
+        self.address = address
+        self.init_bno055()
+
+    def write_register(self, register, value):
+        self.bus.write_byte_data(self.address, register, value)
+
+    def read_register(self, register, length=1):
+        if length == 1:
+            return self.bus.read_byte_data(self.address, register)
+        else:
+            return self.bus.read_i2c_block_data(self.address, register, length)
+
+    def init_bno055(self):
+        # Switch to CONFIG mode to configure sensor
+        self.write_register(0x3D, 0x00)
+        time.sleep(0.05)
+
+        # Set power mode to Normal
+        self.write_register(0x3E, 0x00)
+
+        # Set to NDOF mode (Full sensor fusion for orientation)
+        self.write_register(0x3D, 0x0C)
+        time.sleep(0.05)
+
+    def read_euler_angles(self):
+        # Euler angles (yaw, pitch, roll) registers
+        data = self.read_register(0x1A, 6)
+        yaw = (data[1] << 8 | data[0]) / 16.0
+        pitch = (data[3] << 8 | data[2]) / 16.0
+        roll = (data[5] << 8 | data[4]) / 16.0
+        return yaw, pitch, roll
+
+if __name__ == "__main__":
+    sensor = BNO055()
+    print("Starting BNO055 data collection...")
+    while True:
+        yaw, pitch, roll = sensor.read_euler_angles()
+        print(f"Yaw: {yaw:.2f}, Pitch: {pitch:.2f}, Roll: {roll:.2f}")
+        time.sleep(0.5)
diff --git a/Motor_controller_test_pi/IMU /Boasch/New_IMU_calibration.py b/Motor_controller_test_pi/IMU /Boasch/New_IMU_calibration.py
new file mode 100644
index 00000000..982c0a85
--- /dev/null
+++ b/Motor_controller_test_pi/IMU /Boasch/New_IMU_calibration.py	
@@ -0,0 +1,65 @@
+import smbus
+import time
+
+class BNO055:
+    def __init__(self, i2c_bus=1, address=0x28):
+        self.bus = smbus.SMBus(i2c_bus)
+        self.address = address
+        self.init_bno055()
+
+    def write_register(self, register, value):
+        self.bus.write_byte_data(self.address, register, value)
+
+    def read_register(self, register, length=1):
+        if length == 1:
+            return self.bus.read_byte_data(self.address, register)
+        else:
+            return self.bus.read_i2c_block_data(self.address, register, length)
+
+    def init_bno055(self):
+        # Switch to CONFIG mode to configure sensor
+        self.write_register(0x3D, 0x00)
+        time.sleep(0.05)
+
+        # Set power mode to Normal
+        self.write_register(0x3E, 0x00)
+
+        # Set to NDOF mode (Full sensor fusion for orientation)
+        self.write_register(0x3D, 0x0C)
+        time.sleep(0.05)
+
+    def read_calibration_status(self):
+        # Read the calibration status byte (CALIB_STAT register: 0x35)
+        calib = self.read_register(0x35)
+        sys = (calib >> 6) & 0x03  # System calibration status (bits 6-7)
+        gyro = (calib >> 4) & 0x03  # Gyroscope calibration status (bits 4-5)
+        accel = (calib >> 2) & 0x03  # Accelerometer calibration status (bits 2-3)
+        mag = calib & 0x03          # Magnetometer calibration status (bits 0-1)
+        return sys, gyro, accel, mag
+
+if __name__ == "__main__":
+    sensor = BNO055()
+    print("Starting BNO055 calibration process...")
+    
+    while True:
+        sys, gyro, accel, mag = sensor.read_calibration_status()
+        print(f"System: {sys}, Gyro: {gyro}, Accel: {accel}, Mag: {mag}")
+        
+        if sys == 3 and gyro == 3 and accel == 3 and mag == 3:
+            print("Calibration Complete!")
+            break
+        
+        print("Follow these steps to calibrate:")
+        print("- Keep sensor still to calibrate gyroscope.")
+        print("- Move sensor in all axes to calibrate accelerometer.")
+        print("- Perform figure-8 motion for magnetometer.")
+        
+        time.sleep(1)
+'''
+def read_calibration_data(self):
+    return self.read_register(0x55, 22)  # 22 bytes of calibration data
+
+def write_calibration_data(self, data):
+    for i in range(22):
+        self.write_register(0x55 + i, data[i])
+'''
\ No newline at end of file
diff --git a/Motor_controller_test_pi/IMU /IMU_Calibration.py b/Motor_controller_test_pi/IMU /IMU_Calibration.py
new file mode 100644
index 00000000..1f8bb91c
--- /dev/null
+++ b/Motor_controller_test_pi/IMU /IMU_Calibration.py	
@@ -0,0 +1,164 @@
+import smbus
+import time
+
+# MPU6050 Registers and Addresses
+DEVICE_ADDRESS = 0x68       # MPU6050 device address
+
+PWR_MGMT_1 = 0x6B
+SMPLRT_DIV = 0x19
+CONFIG = 0x1A
+GYRO_CONFIG = 0x1B
+ACCEL_CONFIG = 0x1C
+INT_ENABLE = 0x38
+
+ACCEL_XOUT_H = 0x3B
+ACCEL_YOUT_H = 0x3D
+ACCEL_ZOUT_H = 0x3F
+
+GYRO_XOUT_H = 0x43
+GYRO_YOUT_H = 0x45
+GYRO_ZOUT_H = 0x47
+
+def calibrate_gyroscope(samples=1000):
+    print("Calibrating gyroscope... Please keep the sensor stationary.")
+    gyro_x_offset = 0
+    gyro_y_offset = 0
+    gyro_z_offset = 0
+
+    for _ in range(samples):
+        gyro_x = read_raw_data(GYRO_XOUT_H)
+        gyro_y = read_raw_data(GYRO_YOUT_H)
+        gyro_z = read_raw_data(GYRO_ZOUT_H)
+
+        gyro_x_offset += gyro_x
+        gyro_y_offset += gyro_y
+        gyro_z_offset += gyro_z
+
+        time.sleep(0.00001)  # Adjust the delay as per your sampling rate
+
+    gyro_x_offset /= samples
+    gyro_y_offset /= samples
+    gyro_z_offset /= samples
+
+    print("Gyroscope calibration complete.")
+    return gyro_x_offset, gyro_y_offset, gyro_z_offset
+
+def calibrate_accelerometer(samples=1000):
+    print("Calibrating gyroscope... Please keep the sensor stationary.")
+    acc_x_offset = 0
+    acc_y_offset = 0
+    acc_z_offset = 0
+
+    for _ in range(samples):
+        acc_x = read_raw_data(ACCEL_XOUT_H)
+        acc_y = read_raw_data(ACCEL_YOUT_H)
+        acc_z = read_raw_data(ACCEL_ZOUT_H)
+
+        acc_x_offset += acc_x
+        acc_y_offset += acc_y
+        acc_z_offset += acc_z
+
+        time.sleep(0.00001)  # Adjust the delay as per your sampling rate
+
+    acc_x_offset /= samples
+    acc_y_offset /= samples
+    acc_z_offset /= samples
+    
+
+    print("Gyroscope calibration complete.")
+    return acc_x_offset, acc_y_offset, acc_z_offset
+
+
+
+
+
+def MPU_Init():
+    # Write to sample rate register
+    bus.write_byte_data(DEVICE_ADDRESS, SMPLRT_DIV, 7)
+    # Write to power management register to wake up the MPU6050
+    bus.write_byte_data(DEVICE_ADDRESS, PWR_MGMT_1, 1)
+    # Write to configuration register
+    bus.write_byte_data(DEVICE_ADDRESS, CONFIG, 0)
+    # Write to gyroscope configuration register
+    bus.write_byte_data(DEVICE_ADDRESS, GYRO_CONFIG, 24)
+    # Write to accelerometer configuration register
+    bus.write_byte_data(DEVICE_ADDRESS, ACCEL_CONFIG, 0)
+    # Enable interrupts
+    bus.write_byte_data(DEVICE_ADDRESS, INT_ENABLE, 1)
+    
+    
+    
+
+def read_raw_data(addr):
+    # Read two bytes of data from the given address
+    high = bus.read_byte_data(DEVICE_ADDRESS, addr)
+    low = bus.read_byte_data(DEVICE_ADDRESS, addr+1)
+    # Combine higher and lower bytes
+    value = (high << 8) | low
+    # Convert to signed integer
+    if value > 32768:
+        value = value - 65536
+    return value
+
+
+
+# Initialize the I2C bus
+bus = smbus.SMBus(1)  # Use bus number 1 for Raspberry Pi
+
+# Initialize MPU6050
+MPU_Init()
+
+print("Reading data from MPU6050...")
+
+gyro_x_offset, gyro_y_offset, gyro_z_offset = calibrate_gyroscope()
+acc_x_offset, acc_y_offset, acc_z_offset = calibrate_accelerometer()
+
+try:
+#     while True:
+        # Read accelerometer data
+        acc_x = read_raw_data(ACCEL_XOUT_H)
+        acc_y = read_raw_data(ACCEL_YOUT_H)
+        acc_z = read_raw_data(ACCEL_ZOUT_H)
+        
+        # Read gyroscope data
+        gyro_x = read_raw_data(GYRO_XOUT_H)
+        gyro_y = read_raw_data(GYRO_YOUT_H)
+        gyro_z = read_raw_data(GYRO_ZOUT_H)
+    
+        
+        # Convert raw data to meaningful units
+        Ax = acc_x / 16384.0  # Accelerometer sensitivity scale factor
+        Ay = acc_y / 16384.0
+        Az = acc_z / 16384.0
+        
+        Gx = gyro_x / 131.0   # Gyroscope sensitivity scale factor
+        Gy = gyro_y / 131.0
+        Gz = gyro_z / 131.0
+  
+          
+        Aox = acc_x_offset / 16384.0  # Accelerometer sensitivity scale factor
+        Aoy = acc_y_offset / 16384.0
+        Aoz = (acc_z_offset / 16384.0)-1
+        
+        
+        Gox = gyro_x_offset / 131.0
+        Goy = gyro_y_offset / 131.0
+        Goz = gyro_z_offset / 131.0
+
+        # Print the results
+        #print(f"Accelerometer (g): X={Ax:.8f}, Y={Ay:.8f}, Z={Az:.8f}")
+        #print(f"Gyroscope (°/s): X={Gx:.8f}, Y={Gy:.8f}, Z={Gz:.8f}")
+        print("-" * 50)
+        
+        # Print the offset
+        print(f"Accelerometer Offset (g): X={Aox:.8f}, Y={Aoy:.8f}, Z={Aoz:.8f}")
+        print(f"Gyroscope Offset (°/s): X={Gox:.8f}, Y={Goy:.8f}, Z={Goz:.8f}")
+        print("-" * 50)
+        
+        
+        # Delay for readability
+        # time.sleep(0.1)
+
+except KeyboardInterrupt:
+    print("\nTerminated by User")
+      
\ No newline at end of file
diff --git a/Motor_controller_test_pi/IMU /IMU_Library.py b/Motor_controller_test_pi/IMU /IMU_Library.py
new file mode 100644
index 00000000..db740111
--- /dev/null
+++ b/Motor_controller_test_pi/IMU /IMU_Library.py	
@@ -0,0 +1,47 @@
+import mpu6050
+import time
+
+# Create a new Mpu6050 object
+mpu6050 = mpu6050.mpu6050(0x68)
+
+# Define a function to read the sensor data
+def read_sensor_data():
+    # Read the accelerometer values
+    accelerometer_data = mpu6050.get_accel_data()
+
+    # Read the gyroscope values
+    gyroscope_data = mpu6050.get_gyro_data()
+
+    # Read temp
+    temperature = mpu6050.get_temp()
+
+    return accelerometer_data, gyroscope_data, temperature
+
+# Start a while loop to continuously read the sensor data
+while True:
+
+    # Read the sensor data
+    accelerometer_data, gyroscope_data, temperature = read_sensor_data()
+    
+    Ax = accelerometer_data['x']
+    Ay = accelerometer_data['y']
+    Az = accelerometer_data['z']
+    
+    Gx = gyroscope_data['x']
+    Gy = gyroscope_data['y']
+    Gz = gyroscope_data['z']
+    
+    # Print the results
+    print(f"Accelerometer (g): X={Ax:.8f}, Y={Ay:.8f}, Z={Az:.8f}")
+    print(f"Gyroscope (°/s): X={Gx:.8f}, Y={Gy:.8f}, Z={Gz:.8f}")
+    print("-" * 50)
+
+
+    # Print the sensor data
+#     print("Accelerometer data:", accelerometer_data)
+#     print("Gyroscope data:", gyroscope_data)
+#     print("--------------------")
+    #print("Temp:", temperature)
+
+    # Wait for 1 second
+    time.sleep(0.1)
\ No newline at end of file
diff --git a/Motor_controller_test_pi/IMU /IMU_smbus.py b/Motor_controller_test_pi/IMU /IMU_smbus.py
new file mode 100644
index 00000000..3aa441fe
--- /dev/null
+++ b/Motor_controller_test_pi/IMU /IMU_smbus.py	
@@ -0,0 +1,99 @@
+import smbus
+import time
+import math
+
+# MPU6050 Registers and Addresses
+DEVICE_ADDRESS = 0x68       # MPU6050 device address
+
+PWR_MGMT_1 = 0x6B
+SMPLRT_DIV = 0x19
+CONFIG = 0x1A
+GYRO_CONFIG = 0x1B
+ACCEL_CONFIG = 0x1C
+INT_ENABLE = 0x38
+
+ACCEL_XOUT_H = 0x3B
+ACCEL_YOUT_H = 0x3D
+ACCEL_ZOUT_H = 0x3F
+
+GYRO_XOUT_H = 0x43
+GYRO_YOUT_H = 0x45
+GYRO_ZOUT_H = 0x47
+
+def MPU_Init():
+    # Write to sample rate register
+    bus.write_byte_data(DEVICE_ADDRESS, SMPLRT_DIV, 7)
+    # Write to power management register to wake up the MPU6050
+    bus.write_byte_data(DEVICE_ADDRESS, PWR_MGMT_1, 1)
+    # Write to configuration register
+    bus.write_byte_data(DEVICE_ADDRESS, CONFIG, 0)
+    # Write to gyroscope configuration register
+    bus.write_byte_data(DEVICE_ADDRESS, GYRO_CONFIG, 24)
+    # Write to accelerometer configuration register
+    bus.write_byte_data(DEVICE_ADDRESS, ACCEL_CONFIG, 0)
+    # Enable interrupts
+    bus.write_byte_data(DEVICE_ADDRESS, INT_ENABLE, 1)
+
+def read_raw_data(addr):
+    # Read two bytes of data from the given address
+    high = bus.read_byte_data(DEVICE_ADDRESS, addr)
+    low = bus.read_byte_data(DEVICE_ADDRESS, addr+1)
+    # Combine higher and lower bytes
+    value = (high << 8) | low
+    # Convert to signed integer
+    if value > 32768:
+        value = value - 65536
+    return value
+
+# Initialize the I2C bus
+bus = smbus.SMBus(1)  # Use bus number 1 for Raspberry Pi
+
+# Initialize MPU6050
+MPU_Init()
+
+print("Reading data from MPU6050...")
+
+try:
+    while True:
+        # Read accelerometer data
+        acc_x = read_raw_data(ACCEL_XOUT_H)
+        acc_y = read_raw_data(ACCEL_YOUT_H)
+        acc_z = read_raw_data(ACCEL_ZOUT_H)
+        
+        # Read gyroscope data
+        gyro_x = read_raw_data(GYRO_XOUT_H)
+        gyro_y = read_raw_data(GYRO_YOUT_H)
+        gyro_z = read_raw_data(GYRO_ZOUT_H)
+        
+        # Convert raw data to meaningful units
+        Ax = (acc_x / 16384.0 - 0.01467432) # Accelerometer sensitivity scale factor
+        Ay = (acc_y / 16384.0 - -0.00603101)
+        Az = (acc_z / 16384.0 - 0.06171924)
+        
+        Ax_m = 9.81*Ax
+        Ay_m = 9.81*Ay
+        Az_m = 9.81*Az
+        
+        Gx = gyro_x / 131.0  --0.17447328# Gyroscope sensitivity scale factor
+        Gy = gyro_y / 131.0  -0.08720611
+        Gz = gyro_z / 131.0  -0.16423664
+        
+        Gx_r = Gx/180 *math.pi
+        Gy_r = Gy/180 *math.pi
+        Gz_r = Gz/180 *math.pi
+        
+        # Print the results
+        print(f"Accelerometer (g): X={Ax:.3f}, Y={Ay:.3f}, Z={Az:.3f}")
+        print(f"Gyroscope (°/s): X={Gx:.3f}, Y={Gy:.3f}, Z={Gz:.3f}")
+        
+        print(f"Accelerometer (m/s^2): X={Ax_m:.3f}, Y={Ay_m:.3f}, Z={Az_m:.3f}")
+        print(f"Gyroscope (rad/s): X={Gx_r:.3f}, Y={Gy_r:.3f}, Z={Gz_r:.3f}")
+        print("-" * 50)
+        
+        # Delay for readability
+        time.sleep(0.1)
+
+except KeyboardInterrupt:
+    print("\nTerminated by User")
+      
+
diff --git a/Motor_controller_test_pi/IMU /ahrs_library.py b/Motor_controller_test_pi/IMU /ahrs_library.py
new file mode 100644
index 00000000..9d98799e
--- /dev/null
+++ b/Motor_controller_test_pi/IMU /ahrs_library.py	
@@ -0,0 +1,118 @@
+import smbus
+import time
+import math
+from ahrs.filters import Madgwick
+
+class IMUReader:
+    def __init__(self):
+        self.bus = smbus.SMBus(1)  # I2C bus
+        self.DEVICE_ADDRESS = 0x68  # MPU6050 device address
+        self.mpu_init()
+
+        self.filter = Madgwick()
+        self.orientation = [0.0, 0.0, 0.0, 1.0]  # Initial quaternion
+
+    def mpu_init(self):
+        PWR_MGMT_1 = 0x6B
+        SMPLRT_DIV = 0x19
+        CONFIG = 0x1A
+        GYRO_CONFIG = 0x1B
+        ACCEL_CONFIG = 0x1C
+        INT_ENABLE = 0x38
+
+        # Write to sample rate register
+        self.bus.write_byte_data(self.DEVICE_ADDRESS, SMPLRT_DIV, 7)
+        # Write to power management register to wake up the MPU6050
+        self.bus.write_byte_data(self.DEVICE_ADDRESS, PWR_MGMT_1, 1)
+        # Write to configuration register
+        self.bus.write_byte_data(self.DEVICE_ADDRESS, CONFIG, 0)
+        # Write to gyroscope configuration register
+        self.bus.write_byte_data(self.DEVICE_ADDRESS, GYRO_CONFIG, 24)
+        # Write to accelerometer configuration register
+        self.bus.write_byte_data(self.DEVICE_ADDRESS, ACCEL_CONFIG, 0)
+        # Enable interrupts
+        self.bus.write_byte_data(self.DEVICE_ADDRESS, INT_ENABLE, 1)
+
+    def read_raw_data(self, addr):
+        # Read two bytes of data from the given address
+        high = self.bus.read_byte_data(self.DEVICE_ADDRESS, addr)
+        low = self.bus.read_byte_data(self.DEVICE_ADDRESS, addr + 1)
+        value = (high << 8) | low
+        if value > 32768:
+            value = value - 65536
+        return value
+
+    def read_data(self):
+        ACCEL_XOUT_H = 0x3B
+        ACCEL_YOUT_H = 0x3D
+        ACCEL_ZOUT_H = 0x3F
+        GYRO_XOUT_H = 0x43
+        GYRO_YOUT_H = 0x45
+        GYRO_ZOUT_H = 0x47
+
+        # Read accelerometer data
+        acc_x = self.read_raw_data(ACCEL_XOUT_H)
+        acc_y = self.read_raw_data(ACCEL_YOUT_H)
+        acc_z = self.read_raw_data(ACCEL_ZOUT_H)
+
+        # Read gyroscope data
+        gyro_x = self.read_raw_data(GYRO_XOUT_H)
+        gyro_y = self.read_raw_data(GYRO_YOUT_H)
+        gyro_z = self.read_raw_data(GYRO_ZOUT_H)
+
+        ax_offset = 0.01467432
+        ay_offset = -0.00603101
+        az_offset = 0.06171924
+
+        gx_offset = -0.17447328
+        gy_offset = 0.08720611
+        gz_offset = 0.16423664
+
+        Ax = (acc_x / 16384.0) - ax_offset
+        Ay = (acc_y / 16384.0) - ay_offset
+        Az = (acc_z / 16384.0) - az_offset
+        Ax = 9.81 * Ax
+        Ay = 9.81 * Ay
+        Az = 9.81 * Az
+
+        Gx = (gyro_x / 131.0) - gx_offset
+        Gy = (gyro_y / 131.0) - gy_offset
+        Gz = (gyro_z / 131.0) - gz_offset   
+
+        Gx = Gx / 180 * math.pi
+        Gy = Gy / 180 * math.pi
+        Gz = Gz / 180 * math.pi
+
+        self.orientation = self.filter.updateIMU(self.orientation,
+                                                 [Gx, Gy, Gz],
+                                                 [Ax / 9.81, Ay / 9.81, Az / 9.81])
+        return Ax, Ay, Az, Gx, Gy, Gz, self.orientation
+
+    def start(self):
+        print("Starting IMU data collection...")
+        while True:
+            Ax, Ay, Az, Gx, Gy, Gz, orientation = self.read_data()
+
+            # Debugging: Raw data
+            print(f"Raw Accelerometer: Ax={Ax:.2f}, Ay={Ay:.2f}, Az={Az:.2f}")
+            print(f"Raw Gyroscope: Gx={Gx:.2f}, Gy={Gy:.2f}, Gz={Gz:.2f}")
+
+            # Debugging: Orientation
+            print(f"Orientation (quaternion): x={orientation[0]:.2f}, y={orientation[1]:.2f}, "
+                  f"z={orientation[2]:.2f}, w={orientation[3]:.2f}")
+
+            # Test stability
+            print("Orientation (Euler angles):")
+            roll = math.atan2(2.0 * (orientation[3] * orientation[0] + orientation[1] * orientation[2]),
+                              1.0 - 2.0 * (orientation[0]**2 + orientation[1]**2))
+            pitch = math.asin(2.0 * (orientation[3] * orientation[1] - orientation[2] * orientation[0]))
+            yaw = math.atan2(2.0 * (orientation[3] * orientation[2] + orientation[0] * orientation[1]),
+                             1.0 - 2.0 * (orientation[1]**2 + orientation[2]**2))
+            print(f"Roll={roll:.2f}, Pitch={pitch:.2f}, Yaw={yaw:.2f}")
+
+            time.sleep(0.5)
+
+
+if __name__ == "__main__":
+    imu_reader = IMUReader()
+    imu_reader.start()
diff --git a/Motor_controller_test_pi/Motor_control/Motor_control.py b/Motor_controller_test_pi/Motor_control/Motor_control.py
new file mode 100644
index 00000000..425372b0
--- /dev/null
+++ b/Motor_controller_test_pi/Motor_control/Motor_control.py
@@ -0,0 +1,132 @@
+import RPi.GPIO as GPIO
+from time import sleep
+
+GPIO.setmode(GPIO.BCM)
+GPIO.setwarnings(False)
+
+class Motor():
+    def __init__(self, EnaA, In1A, In2A,EnaB, In1B, In2B):
+        self.EnaA = EnaA
+        self.In1A = In1A
+        self.In2A = In2A
+        self.EnaB = EnaB
+        self.In1B = In1B
+        self.In2B = In2B
+        print(EnaB,In1B,In2B)
+        GPIO.setup(EnaA, GPIO.OUT)
+        GPIO.setup(In1A, GPIO.OUT)
+        GPIO.setup(In2A, GPIO.OUT)
+        GPIO.setup(EnaB, GPIO.OUT)
+        GPIO.setup(In1B, GPIO.OUT)
+        GPIO.setup(In2B, GPIO.OUT)
+        self.pwmA = GPIO.PWM(EnaA, 100)
+        self.pwmA.start(0)
+        self.pwmB = GPIO.PWM(EnaB, 100)
+        self.pwmB.start(0)
+        
+    def move(self,speed=0.5,turn = 0,t = 0):
+        
+        if turn > 1:
+            turn = 1
+        elif turn < -1:
+            turn = -1
+            
+        if turn >0.2:
+            print("Turnning Left.. (",turn,")")
+        elif turn < -0.2:
+            print("Turnning Right.. (",turn,")")
+        else:
+            if speed > 0:
+                print("Going Straight")
+            else:
+                print("Going backward")
+            
+            
+        #scale 
+        speed *= 100
+        turn *= 100
+        
+        #turn : 1 -> right,  0 -> left (-100 to 200)
+        leftSpeed = speed - turn
+        rightSpeed = speed + turn
+        
+       
+        if leftSpeed > 100:
+            leftSpeed = 100
+        elif leftSpeed < -100:
+            leftSpeed = -100
+        if rightSpeed > 100:
+            rightSpeed = 100
+        elif rightSpeed < -100:
+            rightSpeed = -100
+            
+        self.pwmA.ChangeDutyCycle(abs(leftSpeed))
+        self.pwmB.ChangeDutyCycle(abs(rightSpeed))
+      
+            
+          
+        if leftSpeed>0:
+            GPIO.output(self.In1A,GPIO.HIGH) #Left
+            GPIO.output(self.In2A,GPIO.LOW) #Right
+        else:
+           # leftSpeed = 0
+            GPIO.output(self.In1A,GPIO.LOW)
+            GPIO.output(self.In2A,GPIO.HIGH)
+        
+        if rightSpeed>0:
+            GPIO.output(self.In1B,GPIO.HIGH)
+            GPIO.output(self.In2B,GPIO.LOW)
+        else:
+          # rifhtSpeed = 0
+           GPIO.output(self.In1B,GPIO.LOW)
+           GPIO.output(self.In2B,GPIO.HIGH)
+        
+        #print(f"Speed: {speed:.2f} ,Left: {leftSpeed:.2f} Right: {rightSpeed:.2f} TURN:{turn:.6f}")
+        
+
+        sleep(t)
+
+
+    def stop(self,t=0):
+        self.pwmA.ChangeDutyCycle(0)
+        self.pwmB.ChangeDutyCycle(0)
+        sleep(t)
+        
+        
+
+
+def main():
+    
+    motor1.stop(1)
+    motor1.move(0.5,0,10) #turn right
+    motor1.stop(1)
+    '''
+    motor1.move(-1,0,0.5) #turn right
+    motor1.stop(1)
+    motor1.move(1,1,0.5) #turn right
+    motor1.stop(1)
+    motor1.move(-1,-1,0.5) #turn right
+    motor1.stop(1)
+    motor1.move(1,-1,0.5) #turn right
+    motor1.stop(1)
+    motor1.move(-1,1,0.5) #turn right
+    motor1.stop(1)
+    
+    motor1.move(0.1 , 0,3) #turn right
+    motor1.stop(1)
+    motor1.move(-0.1,0,3) #turn right
+    motor1.stop(1)
+    '''
+    #motor1.move(0.33,0.8,5) #turn right
+    motor1.stop(1)
+    
+    
+import sys
+if __name__ == '__main__' :
+    print(sys.version)
+    
+    
+    #motor1 = Motor(2,3,4,17,22,27)
+    motor1 = Motor(14, 15, 18, 17, 22, 27)
+    main()
+    
\ No newline at end of file
diff --git a/Motor_controller_test_pi/Motor_control/Ros2_Motor_control.py b/Motor_controller_test_pi/Motor_control/Ros2_Motor_control.py
new file mode 100644
index 00000000..716afa3f
--- /dev/null
+++ b/Motor_controller_test_pi/Motor_control/Ros2_Motor_control.py
@@ -0,0 +1,68 @@
+from gpiozero import PWMOutputDevice
+from time import sleep
+
+class Motor():
+    def __init__(self, EnaA, In1A, In2A, EnaB, In1B, In2B):
+        self.pwmA = PWMOutputDevice(EnaA)
+        self.in1A = PWMOutputDevice(In1A)
+        self.in2A = PWMOutputDevice(In2A)
+        self.pwmB = PWMOutputDevice(EnaB)
+        self.in1B = PWMOutputDevice(In1B) 
+        self.in2B = PWMOutputDevice(In2B)
+        
+    def move(self, speed=0.5, turn=0, t=0):
+        # Ensure speed and turn are within bounds
+        speed = max(-1, min(1, speed))  # Clamp speed to [-1, 1]
+        turn = max(-1, min(1, turn))    # Clamp turn to [-1, 1]
+        
+        # Calculate speed for each motor
+        leftSpeed = speed - turn
+        rightSpeed = speed + turn
+        
+        wheel_base = 0
+        '''
+        left_speed = speed - (turn* wheel_base / 2)
+        right_speed = peed + (turn* wheel_base / 2)
+        '''
+        
+        # Clamp motor speeds to [-1, 1]
+        leftSpeed = max(-1, min(1, leftSpeed))
+        rightSpeed = max(-1, min(1, rightSpeed))
+        
+        # Set left motor
+        self.pwmA.value = abs(leftSpeed)
+        self.in1A.value = leftSpeed > 0
+        self.in2A.value = leftSpeed <= 0
+        
+        # Set right motor
+        self.pwmB.value = abs(rightSpeed)
+        self.in1B.value = rightSpeed > 0
+        self.in2B.value = rightSpeed <= 0
+        
+        # Print debugging info
+        print(f"Left Motor: Speed={leftSpeed}, Right Motor: Speed={rightSpeed}")
+        
+        sleep(t)
+        
+    def stop(self, t=0):
+        self.pwmA.value = 0
+        self.pwmB.value = 0
+        sleep(t)
+
+def main():
+    #motor1.move(0.5, 0, 0.865)  # Move forward
+    motor1.move(0.5, 0, 3)  # Move forward
+    motor1.stop(1)
+    '''
+    motor1.move(-0.5, 0, 3)  # Move backward
+    motor1.stop(1)
+    motor1.move(0.5, 0.5, 3)  # Turn right
+    motor1.stop(1)
+    motor1.move(0.5, -0.5, 3)  # Turn left
+    motor1.stop(1)
+    '''
+
+if __name__ == '__main__':
+    motor1 = Motor(14, 15, 18,17 ,22 ,27 )
+    main()
+                                                                                                                                                           
\ No newline at end of file
diff --git a/Motor_controller_test_pi/Test_encoder_motor.py b/Motor_controller_test_pi/Test_encoder_motor.py
new file mode 100644
index 00000000..7e658535
--- /dev/null
+++ b/Motor_controller_test_pi/Test_encoder_motor.py
@@ -0,0 +1,104 @@
+from gpiozero import PWMOutputDevice, Button
+from time import sleep
+
+class Motor():
+    def __init__(self, EnaA, In1A, In2A, EnaB, In1B, In2B, HallA_A, HallA_B, HallB_A, HallB_B):
+        # Motor control initialization
+        self.pwmA = PWMOutputDevice(EnaA)
+        self.in1A = PWMOutputDevice(In1A)
+        self.in2A = PWMOutputDevice(In2A)
+        self.pwmB = PWMOutputDevice(EnaB)
+        self.in1B = PWMOutputDevice(In1B)
+        self.in2B = PWMOutputDevice(In2B)
+        
+        # Encoder initialization
+        self.motor1_hall_a = Button(HallA_A, pull_up=True)
+        self.motor1_hall_b = Button(HallA_B, pull_up=True)
+        self.motor2_hall_a = Button(HallB_A, pull_up=True)
+        self.motor2_hall_b = Button(HallB_B, pull_up=True)
+        
+        # Attach callbacks
+        self.motor1_hall_a.when_pressed = self.motor1_channel_a_detected
+        self.motor1_hall_b.when_pressed = self.motor1_channel_b_detected
+        self.motor2_hall_a.when_pressed = self.motor2_channel_a_detected
+        self.motor2_hall_b.when_pressed = self.motor2_channel_b_detected
+
+        # Encoder counters
+        self.motor1_count = 0
+        self.motor2_count = 0
+        
+    def move(self, speed=0.5, turn=0, t=0):
+        # Ensure speed and turn are within bounds
+        speed = max(-1, min(1, speed))  # Clamp speed to [-1, 1]
+        turn = max(-1, min(1, turn))    # Clamp turn to [-1, 1]
+        
+        # Calculate speed for each motor
+        leftSpeed = speed - turn
+        rightSpeed = speed + turn
+        
+        # Clamp motor speeds to [-1, 1]
+        leftSpeed = max(-1, min(1, leftSpeed))
+        rightSpeed = max(-1, min(1, rightSpeed))
+        
+        # Set left motor
+        self.pwmA.value = abs(leftSpeed)
+        self.in1A.value = leftSpeed > 0
+        self.in2A.value = leftSpeed <= 0
+        
+        # Set right motor
+        self.pwmB.value = abs(rightSpeed)
+        self.in1B.value = rightSpeed > 0
+        self.in2B.value = rightSpeed <= 0
+        
+        # Print debugging info
+        print(f"Left Motor: Speed={leftSpeed}, Right Motor: Speed={rightSpeed}")
+        sleep(t)
+        
+
+            
+    def stop(self, t=0):
+        self.pwmA.value = 0
+        self.pwmB.value = 0
+        sleep(t)
+
+
+
+
+    # Encoder callback functions
+    def motor1_channel_a_detected(self):
+        self.motor1_count += 1
+        print(f"Motor 1: Channel A pulse detected, Count: {self.motor1_count}")
+
+    def motor1_channel_b_detected(self):
+        print(f"Motor 1: Channel B pulse detected")
+
+
+    def motor2_channel_a_detected(self):
+        self.motor2_count += 1
+        print(f"Motor 2: Channel A pulse detected, Count: {self.motor2_count}")
+
+    def motor2_channel_b_detected(self):
+        print(f"Motor 2: Channel B pulse detected")
+        
+
+
+    
+def main():
+    
+    try:
+        print("Starting motor movement and encoder monitoring. Press Ctrl+C to exit.")
+        
+        motor1.move(0.1, 0, 10)  # Move forward
+        motor1.stop(1)
+
+
+
+    except KeyboardInterrupt:
+        print("\nExiting program...")
+        
+
+
+if __name__ == '__main__':
+    motor1 = Motor(2, 3, 4, 17, 22, 27, 5,6,20,21 )
+    main()
+
diff --git a/Motor_controller_test_pi/pigpio.py b/Motor_controller_test_pi/pigpio.py
new file mode 100644
index 00000000..2e47c7b8
--- /dev/null
+++ b/Motor_controller_test_pi/pigpio.py
@@ -0,0 +1,135 @@
+import pigpio
+from gpiozero import PWMOutputDevice
+from time import sleep, time
+import threading
+
+# Motor control class
+class Motor:
+    def __init__(self, EnaA, In1A, In2A, EnaB, In1B, In2B):
+        # Initialize PWM and GPIO pins for both motors
+        self.pwmA = PWMOutputDevice(EnaA)
+        self.in1A = PWMOutputDevice(In1A)
+        self.in2A = PWMOutputDevice(In2A)
+        self.pwmB = PWMOutputDevice(EnaB)
+        self.in1B = PWMOutputDevice(In1B)
+        self.in2B = PWMOutputDevice(In2B)
+        
+    def move(self, speed=0.5, turn=0, t=0):
+        # Ensure speed and turn are within bounds
+        speed = max(-1, min(1, speed))  # Clamp speed to [-1, 1]
+        turn = max(-1, min(1, turn))    # Clamp turn to [-1, 1]
+        
+        # Calculate speed for each motor
+        leftSpeed = speed - turn
+        rightSpeed = speed + turn
+         
+        # Clamp motor speeds to [-1, 1]
+        leftSpeed = max(-1, min(1, leftSpeed))
+        rightSpeed = max(-1, min(1, rightSpeed))
+        
+        # Set left motor
+        self.pwmA.value = abs(leftSpeed)
+        self.in1A.value = leftSpeed > 0
+        self.in2A.value = leftSpeed <= 0
+        
+        # Set right motor
+        self.pwmB.value = abs(rightSpeed)
+        self.in1B.value = rightSpeed > 0
+        self.in2B.value = rightSpeed <= 0
+        
+        # Print debugging info
+        print(f"Left Motor: Speed={leftSpeed}, Right Motor: Speed={rightSpeed}")
+        
+        sleep(t)
+        
+    def stop(self, t=0):
+        self.pwmA.value = 0
+        self.pwmB.value = 0
+        sleep(t)
+
+# Encoder class using pigpio
+class Encoder:
+    def __init__(self, gpioA, gpioB):
+        self.pi = pigpio.pi()
+        if not self.pi.connected:
+            raise IOError("Could not connect to pigpio daemon!")
+        self.gpioA = gpioA
+        self.gpioB = gpioB
+        self.position = 0
+
+        self.pi.set_mode(gpioA, pigpio.INPUT)
+        self.pi.set_mode(gpioB, pigpio.INPUT)
+
+        self.pi.set_pull_up_down(gpioA, pigpio.PUD_UP)
+        self.pi.set_pull_up_down(gpioB, pigpio.PUD_UP)
+
+        self.lastGpio = None
+
+        self.callbackA = self.pi.callback(gpioA, pigpio.EITHER_EDGE, self._pulse)
+        self.callbackB = self.pi.callback(gpioB, pigpio.EITHER_EDGE, self._pulse)
+
+    def _pulse(self, gpio, level, tick):
+        if gpio == self.gpioA:
+            levelB = self.pi.read(self.gpioB)
+            if level == levelB:
+                self.position += 1
+            else:
+                self.position -= 1
+        else:
+            levelA = self.pi.read(self.gpioA)
+            if level == levelA:
+                self.position -= 1
+            else:
+                self.position += 1
+
+    def get_position(self):
+        return self.position
+
+    def cancel(self):
+        self.callbackA.cancel()
+        self.callbackB.cancel()
+        self.pi.stop()
+
+def main():
+    # Initialize motor and encoder
+    motor = Motor(2, 3, 4, 17, 22, 27)
+    encoder1 = Encoder(5, 6)
+    encoder2 = Encoder(13, 19)
+    
+    try:
+        # Start motor movement in a separate thread
+        def move_motor():
+            print("Starting motor...")
+            motor.move(0.5, 0)  # Start moving forward
+            sleep(3)            # Move for 3 seconds
+            motor.stop()
+            print("Motor stopped.")
+
+        motor_thread = threading.Thread(target=move_motor)
+        motor_thread.start()
+
+        # Read encoder values while motor is running
+        start_time = time()
+        while motor_thread.is_alive():
+            pos1 = encoder1.get_position()
+            pos2 = encoder2.get_position()
+            elapsed_time = time() - start_time
+            print(f"Time: {elapsed_time:.2f}s, Motor 1 Position: {pos1}, Motor 2 Position: {pos2}")
+            sleep(0.1)  # Adjust the sleep time as needed
+
+        # Final encoder positions
+        pos1 = encoder1.get_position()
+        pos2 = encoder2.get_position()
+        print(f"Final Positions - Motor 1: {pos1}, Motor 2: {pos2}")
+
+    except KeyboardInterrupt:
+        print("Program interrupted by user.")
+    finally:
+        # Clean up
+        encoder1.cancel()
+        encoder2.cancel()
+        motor.stop()
+        print("Program terminated.")
+
+if __name__ == '__main__':
+    main()
-- 
GitLab