extends Node3D var p:float = 0.4 var i:float = 0.0 var d:float = 1 var force:float = 200.0 var error:float = 0.0 var last_error = 0.0 var error_derivative:float = 0.0 var integral:float = 0.0 var target:float = 0.0 #0.0 -> 359.99 var p_input:SpinBox var i_input:SpinBox var d_input:SpinBox var force_input:SpinBox var target_input:SpinBox var spinner_node:Node3D var target_node:Node3D var error_lable:Label var speed_lable:Label var acceleration_label:Label var error_derivative_lable:Label var integral_lable:Label var spinner_speed:float = 0.0 var max_speed:float = 120 # deg/m -> 1/360/60s = 1rpm var acceleration:float func _ready() -> void: spinner_node = get_node("Spinner") target_node = get_node("Target") p_input = get_node("PID Menue/PanelContainer/VBoxContainer/Proportinal Term (P)/Input") p_input.value_changed.connect(_update_p_value) i_input = get_node("PID Menue/PanelContainer/VBoxContainer/Integral (I)/Input") i_input.value_changed.connect(_update_i_value) d_input = get_node("PID Menue/PanelContainer/VBoxContainer/Derivative (D)/Input") d_input.value_changed.connect(_update_d_value) force_input = get_node("PID Menue/PanelContainer/VBoxContainer/Force/Input") force_input.value_changed.connect(_update_force_value) target_input =get_node("PID Menue/PanelContainer/VBoxContainer/Target Rotation/Input") target_input.value_changed.connect(_update_target_rot) error_lable = get_node("PID Menue/PanelContainer/VBoxContainer/Error/Output") speed_lable = get_node("PID Menue/PanelContainer/VBoxContainer/Speed/Output") error_derivative_lable = get_node("PID Menue/PanelContainer/VBoxContainer/Error Derivative/Output") acceleration_label = get_node("PID Menue/PanelContainer/VBoxContainer/Acceleration/Output") integral_lable = get_node("PID Menue/PanelContainer/VBoxContainer/Integral/Output") # Set default values p_input.value = p i_input.value = i d_input.value = d force_input.value = force func _update_target_rot(value:float) -> void: target_node.rotation.x = deg_to_rad(value) func _update_p_value(value:float) -> void: p = value func _update_i_value(value:float) -> void: i = value func _update_d_value(value:float) -> void: d = value func _update_force_value(value:float) -> void: force = value func _process(delta: float) -> void: # PID Stuff target = rad_to_deg(target_node.rotation.x) error = target - rad_to_deg(spinner_node.rotation.x) error_derivative = (error - last_error) /delta integral += error * delta # Clamp acceleration between min and max force var pid_output := (p * error) + (i * integral) + (d * error_derivative) acceleration = clampf(pid_output, -force, force) # Set speed var next_speed = spinner_speed + acceleration * delta # Block acceleration that would exceed max speed if abs(spinner_speed) >= max_speed and sign(acceleration) == sign(spinner_speed): acceleration = 0.0 next_speed = spinner_speed spinner_speed = clamp(next_speed, -max_speed, max_speed) spinner_node.rotation_degrees.x += spinner_speed * delta last_error = error func _physics_process(delta: float) -> void: #UI Updats error_lable.text = str(error).pad_decimals(2).pad_zeros(3) + " deg" error_derivative_lable.text = str(error_derivative).pad_decimals(2).pad_zeros(3) integral_lable.text = str(integral).pad_decimals(2).pad_zeros(3) speed_lable.text = str(spinner_speed).pad_decimals(2).pad_zeros(3) + " deg/m" acceleration_label.text = str(acceleration/force*100).pad_decimals(2).pad_zeros(3) + " %"