import numpy as np
import matplotlib.pyplot as plt
# 自適應 PID 結構模擬
class AdaptivePID:
def __init__(self, Kp, Ki, Kd, output_limit, integral_limit, max_error, max_derivative):
self.Kp_base = Kp
self.Ki_base = Ki
self.Kd_base = Kd
self.output_limit = output_limit
self.integral_limit = integral_limit
self.max_error = max_error
self.max_derivative = max_derivative
self.integral = 0.0
self.prev_error = 0.0
def compute(self, error, dt):
if dt <= 0.0001:
dt = 0.001
derivative = (error - self.prev_error) / dt
error_ratio = min(1.0, abs(error) / self.max_error)
derivative_ratio = min(1.0, abs(derivative) / self.max_derivative)
Kp = self.Kp_base * error_ratio
Kd = self.Kd_base * derivative_ratio
Ki = self.Ki_base if abs(error) < (self.max_error * 0.3) else 0.0
self.integral += error * dt
self.integral = max(min(self.integral, self.integral_limit), -self.integral_limit)
output = Kp * error + Ki * self.integral + Kd * derivative
output = max(min(output, self.output_limit), -self.output_limit)
self.prev_error = error
return output
# 初始化 PID
pid = AdaptivePID(Kp=1.0, Ki=0.02, Kd=0.1, output_limit=120.0,
integral_limit=50.0, max_error=12.0, max_derivative=5.0)
# 模擬範圍 -12 ~ 12 mm 誤差
errors = np.linspace(-12, 12, 200)
dt = 0.02 # 模擬每筆的時間差
outputs = [pid.compute(e, dt) for e in errors]
# 畫圖
plt.figure(figsize=(10, 5))
plt.plot(errors, outputs, label="RPM Output")
plt.axhline(120, color='red', linestyle='--', linewidth=0.5, label="RPM Limit (+120)")
plt.axhline(-120, color='blue', linestyle='--', linewidth=0.5, label="RPM Limit (-120)")
plt.title("Adaptive PID: Error vs RPM Output")
plt.xlabel("Position Error (mm)")
plt.ylabel("RPM Output")
plt.grid(True)
plt.legend()
plt.tight_layout()
plt.show()
aW1wb3J0IG51bXB5IGFzIG5wCmltcG9ydCBtYXRwbG90bGliLnB5cGxvdCBhcyBwbHQKCiMg6Ieq6YGp5oeJIFBJRCDntZDmp4vmqKHmk6wKY2xhc3MgQWRhcHRpdmVQSUQ6CiAgICBkZWYgX19pbml0X18oc2VsZiwgS3AsIEtpLCBLZCwgb3V0cHV0X2xpbWl0LCBpbnRlZ3JhbF9saW1pdCwgbWF4X2Vycm9yLCBtYXhfZGVyaXZhdGl2ZSk6CiAgICAgICAgc2VsZi5LcF9iYXNlID0gS3AKICAgICAgICBzZWxmLktpX2Jhc2UgPSBLaQogICAgICAgIHNlbGYuS2RfYmFzZSA9IEtkCiAgICAgICAgc2VsZi5vdXRwdXRfbGltaXQgPSBvdXRwdXRfbGltaXQKICAgICAgICBzZWxmLmludGVncmFsX2xpbWl0ID0gaW50ZWdyYWxfbGltaXQKICAgICAgICBzZWxmLm1heF9lcnJvciA9IG1heF9lcnJvcgogICAgICAgIHNlbGYubWF4X2Rlcml2YXRpdmUgPSBtYXhfZGVyaXZhdGl2ZQogICAgICAgIHNlbGYuaW50ZWdyYWwgPSAwLjAKICAgICAgICBzZWxmLnByZXZfZXJyb3IgPSAwLjAKCiAgICBkZWYgY29tcHV0ZShzZWxmLCBlcnJvciwgZHQpOgogICAgICAgIGlmIGR0IDw9IDAuMDAwMToKICAgICAgICAgICAgZHQgPSAwLjAwMQoKICAgICAgICBkZXJpdmF0aXZlID0gKGVycm9yIC0gc2VsZi5wcmV2X2Vycm9yKSAvIGR0CiAgICAgICAgZXJyb3JfcmF0aW8gPSBtaW4oMS4wLCBhYnMoZXJyb3IpIC8gc2VsZi5tYXhfZXJyb3IpCiAgICAgICAgZGVyaXZhdGl2ZV9yYXRpbyA9IG1pbigxLjAsIGFicyhkZXJpdmF0aXZlKSAvIHNlbGYubWF4X2Rlcml2YXRpdmUpCgogICAgICAgIEtwID0gc2VsZi5LcF9iYXNlICogZXJyb3JfcmF0aW8KICAgICAgICBLZCA9IHNlbGYuS2RfYmFzZSAqIGRlcml2YXRpdmVfcmF0aW8KICAgICAgICBLaSA9IHNlbGYuS2lfYmFzZSBpZiBhYnMoZXJyb3IpIDwgKHNlbGYubWF4X2Vycm9yICogMC4zKSBlbHNlIDAuMAoKICAgICAgICBzZWxmLmludGVncmFsICs9IGVycm9yICogZHQKICAgICAgICBzZWxmLmludGVncmFsID0gbWF4KG1pbihzZWxmLmludGVncmFsLCBzZWxmLmludGVncmFsX2xpbWl0KSwgLXNlbGYuaW50ZWdyYWxfbGltaXQpCgogICAgICAgIG91dHB1dCA9IEtwICogZXJyb3IgKyBLaSAqIHNlbGYuaW50ZWdyYWwgKyBLZCAqIGRlcml2YXRpdmUKICAgICAgICBvdXRwdXQgPSBtYXgobWluKG91dHB1dCwgc2VsZi5vdXRwdXRfbGltaXQpLCAtc2VsZi5vdXRwdXRfbGltaXQpCgogICAgICAgIHNlbGYucHJldl9lcnJvciA9IGVycm9yCiAgICAgICAgcmV0dXJuIG91dHB1dAoKIyDliJ3lp4vljJYgUElECnBpZCA9IEFkYXB0aXZlUElEKEtwPTEuMCwgS2k9MC4wMiwgS2Q9MC4xLCBvdXRwdXRfbGltaXQ9MTIwLjAsCiAgICAgICAgICAgICAgICAgIGludGVncmFsX2xpbWl0PTUwLjAsIG1heF9lcnJvcj0xMi4wLCBtYXhfZGVyaXZhdGl2ZT01LjApCgojIOaooeaTrOevhOWcjSAtMTIgfiAxMiBtbSDoqqTlt64KZXJyb3JzID0gbnAubGluc3BhY2UoLTEyLCAxMiwgMjAwKQpkdCA9IDAuMDIgICMg5qih5pOs5q+P562G55qE5pmC6ZaT5beuCm91dHB1dHMgPSBbcGlkLmNvbXB1dGUoZSwgZHQpIGZvciBlIGluIGVycm9yc10KCiMg55Wr5ZyWCnBsdC5maWd1cmUoZmlnc2l6ZT0oMTAsIDUpKQpwbHQucGxvdChlcnJvcnMsIG91dHB1dHMsIGxhYmVsPSJSUE0gT3V0cHV0IikKcGx0LmF4aGxpbmUoMTIwLCBjb2xvcj0ncmVkJywgbGluZXN0eWxlPSctLScsIGxpbmV3aWR0aD0wLjUsIGxhYmVsPSJSUE0gTGltaXQgKCsxMjApIikKcGx0LmF4aGxpbmUoLTEyMCwgY29sb3I9J2JsdWUnLCBsaW5lc3R5bGU9Jy0tJywgbGluZXdpZHRoPTAuNSwgbGFiZWw9IlJQTSBMaW1pdCAoLTEyMCkiKQpwbHQudGl0bGUoIkFkYXB0aXZlIFBJRDogRXJyb3IgdnMgUlBNIE91dHB1dCIpCnBsdC54bGFiZWwoIlBvc2l0aW9uIEVycm9yIChtbSkiKQpwbHQueWxhYmVsKCJSUE0gT3V0cHV0IikKcGx0LmdyaWQoVHJ1ZSkKcGx0LmxlZ2VuZCgpCnBsdC50aWdodF9sYXlvdXQoKQpwbHQuc2hvdygpCg==