Control dc motor speed - PID controller

Discussion about programs, libraries and tools that work with MicroPython. Mostly these are provided by a third party.
Target audience: All users and developers of MicroPython.
Post Reply
Mohamed
Posts: 9
Joined: Thu Jun 11, 2020 6:54 pm

Control dc motor speed - PID controller

Post by Mohamed » Sun Jun 14, 2020 6:45 pm

Hello,
I am working in a robot that has 2 motors and the speed of each is not the same. So, I use fork barrier sensors to get the number of rotations of each motor, control the speed and then use it to make the robot move for specific distance. I tried PI controller (P =8, I=0.2); the error compensates after about 2 sec to a limit of about 4 to 7 difference between left motor encoder and right motor encoder 'robot still drifts'. I used PID controller (P=1.5, I=0.2, D=0.004) but I cannot reach a satisfying result with it also.
Kindly could you please help me solve this problem?
Thanks in advance

Code: Select all

class PIController:
    def __init__(self,pk=0,ik=0):
        self.proportional_constant = pk
        self.integral_constant = ik
        self.integral_sums = 0

    def get_value(self,error): ##PI Controller
        self.integral_sums += error
        p = self.self.proportional_constant*error
        i = self.integral_constant*self.integral_sums
 
        return p + i

Code: Select all

import utime
class PIDController:
    def __init__(self,pk=0,ik=0,dk=0):
        self.proportional_constant = pk
        self.integral_constant = ik
        self.derivative_constant = dk
        self.current_time = utime.ticks_ms()
        self.previous_time = 0
        self.integral_sums = 0
        self.last_error = 0
        self.cum_error = 0.0
  

    def get_value(self,error):  
        self.current_time = utime.ticks_ms()
        elapsed_time = (self.current_time - self.previous_time)/1000 #in sec
        error_rate = (error - self.last_error)/elapsed_time
        self.cum_error += (error * elapsed_time)

        p = self.proportional_constant * error
        d = self.derivative_constant * error_rate
        i = self.integral_constant * self.cum_error
        
        self.last_error = error
        self.previous_time = self.current_time
        return (p + i + d)

    def reset(self):
        self.current_time = 0
        self.previous_time = 0
        self.integral_sums = 0
        self.last_error = 0
        self.cum_error = 0.0
        self.sample_time = 0.00
Attachments
sampla data.png
sampla data.png (100.29 KiB) Viewed 737 times

User avatar
russ_h
Posts: 27
Joined: Thu Oct 03, 2019 2:26 am

Re: Control dc motor speed - PID controller

Post by russ_h » Sun Jun 14, 2020 8:54 pm

I'm fairly new to PID controllers myself, if you are new to PID controllers as well, play with this PID Simulator to get a feel for how the different terms will affect performance. It helped me get a better feel for PID tuning. I was working on something similar using an Arduino and saw the same kind of issue. To solve the issue, I increased my P term (gain) and compensated for the overshoot by increasing the D term value. In my case I ended up with P=16 and D= 0.7 before I got the results I liked. Here is short video showing my motors in action with these values. The graph shows a series of random set points and the encoder counter values for both motors.

Russ

Mohamed
Posts: 9
Joined: Thu Jun 11, 2020 6:54 pm

Re: Control dc motor speed - PID controller

Post by Mohamed » Mon Jun 15, 2020 11:30 am

Thank you for your reply!
I will try this

Daniel R
Posts: 2
Joined: Mon Nov 23, 2020 7:12 pm

Re: Control dc motor speed - PID controller

Post by Daniel R » Wed Nov 25, 2020 3:15 pm

Hi,

I recently wrote a PID, based on:

https://en.wikipedia.org/wiki/PID_controller#Pseudocode

Code: Select all

class PID:
 
    def __init__(self, Kp, Ki, Kd, dt, setpoint, measure_func, output_func, output_min, output_max):
       
        self.Kp = Kp
        self.Ki = Ki
        self.Kd = Kd
        self.dt = int(round(dt / 1000)) # Convert from ms
        self.setpoint = setpoint
        self.measure_func = measure_func
        self.output_func = output_func
        self.output_min = output_min
        self.output_max = output_max
       
        self.error = setpoint - measure_func()
        self.integral = 0
       
        self.timer = Timer(-1) # Virtual timer
        self.timer.init(mode=Timer.PERIODIC, period=dt, callback=self.control)
   
    def control(self, timer):
        
        error = self.setpoint - self.measure_func()
        proportional = self.Kp * error
        self.integral = self.integral + error * self.dt
        
        # Prevent integral windup
        if (proportional > self.output_max) or (proportional < self.output_min):
            self.integral = 0
        
        self.derivative = (error - self.error) / self.dt
        output = proportional + self.Ki * self.integral + self.Kd * self.derivative
        self.error = error

        output = max(self.output_min, output)
        output = min(self.output_max, output)
        self.output_func(output)
        
    def setpoint(value):
        self.setpoint = value

    def stop(self):
        
        self.timer.deinit()
As you can see, I use a Timer to continuously control the system. Also some code to prevent integral windup. Output min and max defines the controllable region.

Improvements or comments?

Daniel

Post Reply