精简的 Python PID 控制算法

如题,笔者写的一个(基于 python 3.9 及以上)可以用于 pid 控制的类。

可以改造后用于各种用途,直接执行可以看到效果。 

PID 算法的原理可以参考另一篇博客:PID控制算法学习_哲学旺仔的博客-优快云博客_pid算法学习

import time


class PIDController:

    def __init__(self, p_value: float = .0, i_value: float = .0, d_value: float = .0, /,
                 sample_period: float = .0, expected_value: float = .0):
        self._p_value = p_value
        self._i_value = i_value
        self._d_value = d_value
        self._sample_period = sample_period
        self._cur_time = time.time()
        self._last_time = self._cur_time
        self._expected_value = expected_value
        self._value = .0
        self._last_error = .0
        self._d_term = .0
        self._i_term = .0
        self._p_term = .0

    def reset(self):
        self._p_term = .0
        self._i_term = .0
        self._d_term = .0
        self._last_error = .0
        self._value = .0

    def update(self, feedback_val: float):
        # calculate error
        error = self.expected_value - feedback_val

        # time elapses
        self._cur_time = time.time()
        delta_time = self._cur_time - self._last_time

        # error difference, delta e
        delta_error = error - self._last_error

        # if sample period not reached, pass
        if delta_time < self._sample_period:
            return self._value

        self._p_term = self._p_value * error  # proportional term
        self._i_term += error * delta_time  # integration term

        self._d_term = .0
        if delta_time > 0:
            self._d_term = delta_error / delta_time  # differential term

        self._last_time = self._cur_time
        self._last_error = error
        self._value = self._p_term + (self._i_value * self._i_term) + (self._d_value * self._d_term)
        return self._value

    @property
    def value(self):
        return self._value

    @property
    def expected_value(self):
        return self._expected_value

    @expected_value.setter
    def expected_value(self, val: float):
        self._verify_input(val)
        self._expected_value = val

    @property
    def sample_period(self):
        return self._sample_period

    @sample_period.setter
    def sample_period(self, val: float):
        self._verify_input(val)
        self._sample_period = val

    @property
    def p_value(self):
        return self._p_value

    @p_value.setter
    def p_value(self, val: float):
        self._verify_input(val)
        self._p_value = val

    @property
    def i_value(self):
        return self._i_value

    @i_value.setter
    def i_value(self, val: float):
        self._verify_input(val)
        self._i_value = val

    @property
    def d_value(self):
        return self._d_value

    @d_value.setter
    def d_value(self, val: float):
        self._verify_input(val)
        self._d_value = val

    @staticmethod
    def _verify_input(val):
        if not isinstance(val, (float, int)) or val < 0:
            raise ValueError("input is expected to be a positive float/integer")


if __name__ == "__main__":

    EXPECT_VALUE = 1.1
    pid_controller = PIDController(1.2, 1, .001, sample_period=.01, expected_value=EXPECT_VALUE)
    sample_num = 20

    feed_back = 10
    feed_back_list = []
    time_list = []
    expected_value_list = []

    for i in range(sample_num):
        feed_back += pid_controller.update(feed_back)
        time.sleep(.1)
        feed_back_list.append(feed_back)
        expected_value_list.append(EXPECT_VALUE)
        time_list.append(i)

    import numpy as np
    import matplotlib.pyplot as plt
    from scipy.interpolate import make_interp_spline

    x_points = np.linspace(min(time_list), max(time_list), 300)
    y_points = make_interp_spline(time_list, feed_back_list)(x_points)

    plt.figure(0)
    plt.ion()
    plt.grid(True)
    plt.xlabel('time (s)')
    plt.ylabel('PID (PV)')
    plt.title('PythonTEST PID', fontsize=15)
    plt.xlim((0, sample_num))
    plt.ylim((min(feed_back_list) - .5, max(feed_back_list) + .5))
    plt.plot(time_list, expected_value_list, "r")
    for i in range(len(x_points)):
        plt.scatter(x_points[i], y_points[i], marker=".", c="b")
        plt.pause(.005)

    plt.ioff()
    plt.show()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值