I have a DC motor that is controlled by an FPGA and a L298N motor driver board. The FPGA generates a PWM signal based on feedback from a dual channel quadrature rotary encoder (1000ppr per channel).
I have the code at the bottom of this post showing the PID code used to produce the error (PWM) signal for the motor driver. The PID code takes the setpoint and actual speeds in RPM and calculates the error based on the PID constants. The PID scan rate is 16ms which I think is to slow for this application, but as fast as I can currently get and I would like the tune the PID loop for this code run time.
I have some information about the running with constant changes (kd and ki not used yet):
Setpoint = 3500rpm and kp = 4, motor runs at constant 2750rpm and 80% duty cycle.
Setpoint = 3500rpm and kp = 5, motor fluctuates between 2830rpm and 2920rpm and between 79% and 83% duty cycle.
Setpoint = 3500rpm and kp = 6, motor fluctuates between 2920rpm and 3050rpm and between 78% and 85% duty cycle.
Setpoint = 3500rpm and kp = 7, motor fluctuates between 2970rpm and 3130rpm and between 77% and 86% duty cycle.
Setpoint = 3500rpm and kp = 8, motor fluctuates between 3000rpm and 3280rpm and between 68% and 90% duty cycle.
Setpoint = 3500rpm and kp = 9, motor fluctuates between 3020rpm and 3330rpm and between 71% and 91% duty cycle.
Setpoint = 3500rpm and kp = 10, motor fluctuates between 3080rpm and 3390rpm and between 70% and 94% duty cycle.
Setpoint = 3500rpm and kp = 13, motor fluctuates between 3100rpm and 3600rpm and between 25% and 91% duty cycle changing rapidly.
You can see with kp = 9, the duty cycle is starting to oscillate with the rpm value also starting to oscillate with some steady error.
And with kp = 13, the oscillation is very bad.
How should I go about tuning this system from here?
--This code contains the logic for generating the PWM output signal for the motor, based --on the PID control loop. library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; use work.Data_Sizes_Package.ALL; entity PWM_Counter_and_Comparator is Generic (Max_Counter_Value : integer := 5000); Port (PWM_Comparison_Value : in std_logic_vector(27 downto 0); Clock : in std_logic; PID_Run_Command : in std_logic; Run_Reset : in std_logic; PWM_Output : out std_logic ); end PWM_Counter_and_Comparator; architecture Behavioral of PWM_Counter_and_Comparator is signal Setpoint_RPM : integer range 0 to 5000 := 0; signal Actual_RPM : integer range 0 to 5000 := 0; signal Count_Value : integer range 0 to 5000 := 0; signal PID_Comparison_Value : integer range 0 to 5000 := 0; signal PID_Comparison_Value_Buffer : integer range 0 to 5000 := 0; constant Kp : integer := 5; constant Ki : integer := 0; constant Kd : integer := 0; begin Setpoint_RPM <= conv_integer(unsigned(PWM_Comparison_Value(13 downto 0))); --Convert setpoint RPM from a vector to an integer Actual_RPM <= conv_integer(unsigned(PWM_Comparison_Value(27 downto 14))); --Convert actual RPM from a vector to an integer Process(Clock , Run_Reset) --This process generates a counter ranging from 0 to 5000 with a 100MHz clock. --Used to create a 20kHz PWM output frequency. begin if(Run_Reset = '0') then --When run command is low, reset counter. Count_Value <= 0; elsif(rising_edge(Clock)) then if(Count_Value = Max_Counter_Value) then Count_Value <= 0; --Resets counter back to 0 once it reaches 5000. PID_Comparison_Value <= PID_Comparison_Value_Buffer; --Updates the new counter comparison value from PID calculation only when counter = 0 elsif(Run_Reset = '1') then Count_Value <= Count_Value + 1; --Increments counter on clock rising edge. end if; end if; end Process; Process(Clock) variable Error_RPM : integer range -5000 to 5000 := 0; variable Last_Error_RPM : integer range -50000000 to 50000000 := 0; variable Error_Sum: integer range -50000000 to 5000000 := 0; variable Error_Change: integer range -50000000 to 50000000 := 0; variable PID_Temp: integer range -50000000 to 50000000 := 0; variable PID_Output: integer range 0 to 5000 := 0; begin if(Run_Reset = '0') then Error_RPM := 0; Last_Error_RPM := 0; Error_Sum := 0; Error_Change := 0; PID_Output := 0; elsif((falling_edge(Clock)) and Run_Reset = '1' and PID_Run_Command = '1') then --PID clock is 41.67Hz, perfoming this process every 16ms. Error_RPM := Setpoint_RPM - Actual_RPM; --Calculate error in rpm Error_Sum := Error_Sum + Error_RPM; --Integral not used yet if(Error_Sum > 20000) then --Setting max integral limit Error_Sum := 20000; elsif(Error_Sum < -20000) then Error_Sum := -20000; --Setting min integral limit end if; Error_Change := Error_RPM - Last_Error_RPM; --Derivative not used yet PID_Temp := ((Kp * Error_RPM) + (Ki * Error_Sum) + (Kd * Error_Change) / 100); --PID output signal calculation Last_Error_RPM := Error_RPM; if(PID_Temp > Max_Counter_Value) then --Max_Counter_Value = 5000 = 100% duty cycle PID_Temp := Max_Counter_Value; elsif(PID_Temp < -5000) then -- -5000 = 0% duty cycle PID_Temp := -5000; end if; end if; PID_Output := (PID_Temp + 5000) / 2; --This scales the PID_Temp calculation result from -5000 to 5000, into a --value between 0 and 5000 to match the counter value for duty cycle. PID_Comparison_Value_Buffer <= PID_Output; --Buffer used to hold the PID_Output until counter is equal to zero. end Process; Process(Clock , Run_Reset) begin if((Count_Value < PID_Comparison_Value) and (Run_Reset = '1')) then PWM_Output <= '1'; --This process sets the output pin high and low at a frequency of 20kHz. else --Compares the counter value with the PID calculation output. PWM_Output <= '0'; --The ratio between count value and comparison value is the duty cycle. --i.e. count value = 5000, comparison value = 2500, duty cycle = 5000/2500 = 50% end if; end Process; end Behavioral; ```