PID Control
This commit is contained in:
parent
5e0ee34ed7
commit
5715322f78
@ -26,9 +26,12 @@ class Pendulum:
|
|||||||
Returns:
|
Returns:
|
||||||
None
|
None
|
||||||
"""
|
"""
|
||||||
|
## Game variables
|
||||||
self.vector = None # Vector2 object
|
self.vector = None # Vector2 object
|
||||||
|
self.fallen = False # Stop when pendulum falls over
|
||||||
|
|
||||||
self.index = 0
|
## Physics variables
|
||||||
|
self.index = 0 # Index helper for plotting graphs
|
||||||
self.theta = [theta] # Angle in radians
|
self.theta = [theta] # Angle in radians
|
||||||
self.a_ang = [0] # Angular acceleration
|
self.a_ang = [0] # Angular acceleration
|
||||||
self.v_ang = [0] # Angular velocity
|
self.v_ang = [0] # Angular velocity
|
||||||
@ -44,18 +47,46 @@ class Pendulum:
|
|||||||
self.mass = mass # Mass of pendulum for physics
|
self.mass = mass # Mass of pendulum for physics
|
||||||
self.color = color # Display color
|
self.color = color # Display color
|
||||||
|
|
||||||
self.pid = False
|
## PID variables
|
||||||
self.fallen = False
|
self.pid = True
|
||||||
|
self.kp = 1.3
|
||||||
|
self.ki = 0.0
|
||||||
|
self.kd = 0.1
|
||||||
|
|
||||||
def update(self, dt):
|
def update(self, dt):
|
||||||
self.doMath(dt)
|
self.doMath(dt)
|
||||||
self.vector = Vector2.from_polar(
|
self.vector = Vector2.from_polar(
|
||||||
((self.length * C_MTPRATIO), math.degrees(self.theta[self.index] - (0.5 * math.pi)))
|
((self.length * C_MTPRATIO), math.degrees(self.theta[self.index] - (0.5 * math.pi)))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if self.pid:
|
||||||
|
self.pidControl()
|
||||||
|
|
||||||
if abs(self.theta[self.index]) == C_FALL_ANG:
|
if abs(self.theta[self.index]) == C_FALL_ANG:
|
||||||
self.fallen = True
|
self.fallen = True
|
||||||
|
|
||||||
|
# def update(self, dt):
|
||||||
|
# """
|
||||||
|
# Update the pendulum's state based on the elapsed time.
|
||||||
|
|
||||||
|
# Parameters:
|
||||||
|
# - dt (float): The elapsed time in milliseconds.
|
||||||
|
|
||||||
|
# Returns:
|
||||||
|
# None
|
||||||
|
# """
|
||||||
|
# a_ang = (-(C_GRAVITY * math.sin(self.theta)) / (self.length)) - (self.r_factor * self.v_ang) # Angular acceleration
|
||||||
|
|
||||||
|
# v_ang = a_ang * (dt/1000) + self.v_ang # Integrate acceleration to get velocity
|
||||||
|
# s_ang = v_ang * (dt/1000) # Angular displacement
|
||||||
|
|
||||||
|
# self.theta += s_ang # Update value
|
||||||
|
|
||||||
|
# self.vector = Vector2.from_polar(((self.length * 150), math.degrees(self.theta + math.pi/2)))
|
||||||
|
|
||||||
|
# self.a_ang = a_ang # Update value
|
||||||
|
# self.v_ang = v_ang # Update value
|
||||||
|
|
||||||
def doMath(self, dt):
|
def doMath(self, dt):
|
||||||
### ANGLE ###
|
### ANGLE ###
|
||||||
ang_term1 = self.a_cart[self.index] * math.cos(self.theta[self.index])
|
ang_term1 = self.a_cart[self.index] * math.cos(self.theta[self.index])
|
||||||
@ -155,31 +186,14 @@ class Pendulum:
|
|||||||
|
|
||||||
axs[0,0].plot(self.s_cart)
|
axs[0,0].plot(self.s_cart)
|
||||||
axs[0,0].set_title('Position [m]')
|
axs[0,0].set_title('Position [m]')
|
||||||
axs[0,1].plot(self.v_ang)
|
axs[0,1].plot(self.v_cart)
|
||||||
axs[0,1].set_title('Speed [m/s]')
|
axs[0,1].set_title('Speed [m/s]')
|
||||||
axs[1,0].plot(self.a_ang)
|
axs[1,0].plot(self.a_cart)
|
||||||
axs[1,0].set_title('Acceleration [m/s^2]')
|
axs[1,0].set_title('Acceleration [m/s^2]')
|
||||||
|
|
||||||
plt.show()
|
plt.show()
|
||||||
|
|
||||||
# def update(self, dt):
|
def pidControl(self):
|
||||||
# """
|
error = self.theta[self.index]
|
||||||
# Update the pendulum's state based on the elapsed time.
|
result = (self.kp * error) + (self.ki * sum(self.theta)) + (self.kd * self.v_ang[self.index])
|
||||||
|
self.a_cart[self.index] = result * 10
|
||||||
# Parameters:
|
|
||||||
# - dt (float): The elapsed time in milliseconds.
|
|
||||||
|
|
||||||
# Returns:
|
|
||||||
# None
|
|
||||||
# """
|
|
||||||
# a_ang = (-(C_GRAVITY * math.sin(self.theta)) / (self.length)) - (self.r_factor * self.v_ang) # Angular acceleration
|
|
||||||
|
|
||||||
# v_ang = a_ang * (dt/1000) + self.v_ang # Integrate acceleration to get velocity
|
|
||||||
# s_ang = v_ang * (dt/1000) # Angular displacement
|
|
||||||
|
|
||||||
# self.theta += s_ang # Update value
|
|
||||||
|
|
||||||
# self.vector = Vector2.from_polar(((self.length * 150), math.degrees(self.theta + math.pi/2)))
|
|
||||||
|
|
||||||
# self.a_ang = a_ang # Update value
|
|
||||||
# self.v_ang = v_ang # Update value
|
|
@ -2,7 +2,9 @@
|
|||||||
# Arne van Iterson, 2023
|
# Arne van Iterson, 2023
|
||||||
|
|
||||||
# Imports
|
# Imports
|
||||||
|
import pygame_widgets
|
||||||
import pygame
|
import pygame
|
||||||
|
from pygame_widgets.slider import Slider
|
||||||
from pygame.math import Vector2
|
from pygame.math import Vector2
|
||||||
import math
|
import math
|
||||||
|
|
||||||
@ -28,6 +30,11 @@ pendulum.reset()
|
|||||||
dx = 0 # x offset
|
dx = 0 # x offset
|
||||||
dt = 1 # delta time
|
dt = 1 # delta time
|
||||||
|
|
||||||
|
# Sliders
|
||||||
|
slider_kp = Slider(screen, 910, 590, 320, 16, initial=pendulum.kp, min=0, max=2, step=0.001)
|
||||||
|
slider_ki = Slider(screen, 910, 620, 320, 16, initial=pendulum.ki, min=0, max=0.5, step=0.001)
|
||||||
|
slider_kd = Slider(screen, 910, 650, 320, 16, initial=pendulum.kd, min=0, max=0.5, step=0.001)
|
||||||
|
|
||||||
# Gametime
|
# Gametime
|
||||||
rt = 10 # run time
|
rt = 10 # run time
|
||||||
highscore = 0
|
highscore = 0
|
||||||
@ -39,15 +46,22 @@ def meta():
|
|||||||
ui.meta(pendulum.a_ang[pendulum.index], "Angular acceleration")
|
ui.meta(pendulum.a_ang[pendulum.index], "Angular acceleration")
|
||||||
ui.meta(pendulum.dx, "dx")
|
ui.meta(pendulum.dx, "dx")
|
||||||
ui.meta(pendulum.a_cart[pendulum.index], "Cart acceleration")
|
ui.meta(pendulum.a_cart[pendulum.index], "Cart acceleration")
|
||||||
|
|
||||||
ui.meta(pendulum.pid, "Control")
|
ui.meta(pendulum.pid, "Control")
|
||||||
|
|
||||||
|
ui.meta(pendulum.kd, "Kd")
|
||||||
|
ui.meta(pendulum.ki, "Ki")
|
||||||
|
ui.meta(pendulum.kp, "Kp")
|
||||||
|
|
||||||
ui.meta(not update, "Paused")
|
ui.meta(not update, "Paused")
|
||||||
ui.meta(rt / 1000, "Run time [s]")
|
ui.meta(rt / 1000, "Run time [s]")
|
||||||
ui.meta(highscore / 1000, "Highscore [s]")
|
ui.meta(highscore / 1000, "Highscore [s]")
|
||||||
|
|
||||||
|
|
||||||
while running:
|
while running:
|
||||||
|
events = pygame.event.get()
|
||||||
### User controls ###
|
### User controls ###
|
||||||
for event in pygame.event.get():
|
for event in events:
|
||||||
# Quit
|
# Quit
|
||||||
if event.type == pygame.QUIT:
|
if event.type == pygame.QUIT:
|
||||||
running = False
|
running = False
|
||||||
@ -57,7 +71,6 @@ while running:
|
|||||||
running = False
|
running = False
|
||||||
# Reset simulation
|
# Reset simulation
|
||||||
elif event.key == pygame.K_SPACE:
|
elif event.key == pygame.K_SPACE:
|
||||||
|
|
||||||
pendulum.reset()
|
pendulum.reset()
|
||||||
rt = 0
|
rt = 0
|
||||||
# Pause simulation
|
# Pause simulation
|
||||||
@ -67,11 +80,14 @@ while running:
|
|||||||
elif event.key == pygame.K_g:
|
elif event.key == pygame.K_g:
|
||||||
if pendulum.fallen:
|
if pendulum.fallen:
|
||||||
pendulum.plot()
|
pendulum.plot()
|
||||||
|
else:
|
||||||
|
update = False
|
||||||
|
pendulum.plot()
|
||||||
# Toggle PID controller
|
# Toggle PID controller
|
||||||
elif event.key == pygame.K_c:
|
elif event.key == pygame.K_c:
|
||||||
pendulum.pid = not pendulum.pid
|
pendulum.pid = not pendulum.pid
|
||||||
|
|
||||||
# Move cart
|
# Move pendulum
|
||||||
keys = pygame.key.get_pressed()
|
keys = pygame.key.get_pressed()
|
||||||
if keys[pygame.K_LEFT] or keys[pygame.K_a]:
|
if keys[pygame.K_LEFT] or keys[pygame.K_a]:
|
||||||
pendulum.a_cart[pendulum.index] -= 4
|
pendulum.a_cart[pendulum.index] -= 4
|
||||||
@ -81,6 +97,11 @@ while running:
|
|||||||
# Draw grid
|
# Draw grid
|
||||||
ui.grid(50, 0, 15)
|
ui.grid(50, 0, 15)
|
||||||
|
|
||||||
|
# Update PID values
|
||||||
|
pendulum.kp = slider_kp.getValue()
|
||||||
|
pendulum.ki = slider_ki.getValue()
|
||||||
|
pendulum.kd = slider_kd.getValue()
|
||||||
|
|
||||||
# Update pendulum
|
# Update pendulum
|
||||||
if not pendulum.fallen:
|
if not pendulum.fallen:
|
||||||
if update:
|
if update:
|
||||||
@ -103,6 +124,7 @@ while running:
|
|||||||
pygame.draw.circle(screen, "black", pole + dx, 15, 3)
|
pygame.draw.circle(screen, "black", pole + dx, 15, 3)
|
||||||
|
|
||||||
# Draw frame
|
# Draw frame
|
||||||
|
pygame_widgets.update(events)
|
||||||
pygame.display.flip()
|
pygame.display.flip()
|
||||||
dt = clock.tick(60) # limits FPS to 120
|
dt = clock.tick(60) # limits FPS to 120
|
||||||
|
|
||||||
|
@ -11,7 +11,6 @@ gridDark = pygame.Color(C_GRID_D_VALUE, C_GRID_D_VALUE, C_GRID_D_VALUE)
|
|||||||
font_h = pygame.font.SysFont(None, 28)
|
font_h = pygame.font.SysFont(None, 28)
|
||||||
font_m = pygame.font.SysFont(None, 16)
|
font_m = pygame.font.SysFont(None, 16)
|
||||||
|
|
||||||
|
|
||||||
# UI Class
|
# UI Class
|
||||||
class SimUI:
|
class SimUI:
|
||||||
def __init__(self, screen, pole):
|
def __init__(self, screen, pole):
|
||||||
@ -23,7 +22,7 @@ class SimUI:
|
|||||||
self.blink = False
|
self.blink = False
|
||||||
self.blinkTimer = 0
|
self.blinkTimer = 0
|
||||||
|
|
||||||
self.controlled = False
|
self.controlled = True
|
||||||
self.paused = False
|
self.paused = False
|
||||||
|
|
||||||
def meta(self, val, desc):
|
def meta(self, val, desc):
|
||||||
|
Loading…
Reference in New Issue
Block a user