PID Control

This commit is contained in:
Arne van Iterson 2023-12-13 11:45:53 +01:00
parent 5e0ee34ed7
commit 5715322f78
3 changed files with 66 additions and 31 deletions

View File

@ -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

View File

@ -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

View File

@ -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):