Forum Discussion
Altera_Forum
Honored Contributor
13 years agoPID controller problem
Hi ,
i am trying to build a pid controller , but for some reason using the vwf file to apply 00011111 for the led and the clk for period of 100 ns, the result of the PID_VAL and integral equation does not show as [x] .. any hep please library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.std_logic_signed.all; use ieee.numeric_std.all; entity PID is port( left_wheel:out integer; left_w : out std_logic_vector(0 to 1); right_wheel:out integer; right_w : out std_logic_vector(0 to 1); counter:out integer; led:in std_logic_vector(0 to 7); clk:in std_logic ); end PID; architecture Behavioral of PID is TYPE int_array IS ARRAY (0 to 7) OF integer; constant kp: integer :=80; constant kd: integer :=80; constant ki: integer :=50; constant desired: integer := 4; constant t_wait : time := 1000 ns; signal reset : std_logic := '0'; begin process (clk) is variable sum : integer:= 0; variable line_cur, line_prev : integer :=1; variable time_prev, time_cur : integer := 0; variable counter_a : integer := 0; variable pid_val : integer := 0; variable integral : integer := 0; variable led_val :int_array := (others => 0); begin counter_a := counter_a +1; counter <= counter_a; if( clk'event and clk='1') then pid_val := 0; integral := 0; sum := 0; line_prev := line_cur; line_cur :=0; time_prev := time_cur; for i in 0 to 7 loop if (led(i)='1')then led_val(i) := 1; else led_val(i) := 0; end if; time_cur := counter_a; sum := sum + led_val(i); line_cur := line_cur + ((i+1)*led_val(i)); end loop; line_cur := line_cur/sum; -- PID controller and drive integral := integral + ((desired - line_cur)/time_cur); pid_val := (kp*(desired - line_cur))+ (kd*((line_cur-line_prev)/(time_cur-time_prev)))+ (ki*integral); -- drive if ((100- pid_val) < 0) then left_wheel <= ((-1)*(100-pid_val)); left_w <="10"; else left_wheel <= (100-pid_val); left_w <="01"; end if; if ((100+pid_val) < 0 ) then right_wheel <= ((-1)*(100+pid_val)); right_w <="10"; else right_wheel <= (100+pid_val); left_w <="01"; end if; end if; end process; reset <='1'after 40 ns, '0' after 80 ns; end Behavioral;1 Reply
- Altera_Forum
Honored Contributor
First there are a few VHDL problems
Don't use those two libraries together, it can give very strange results. You'd better only use numeric_std, which is standard, and use its signed and unsigned types instead of std_logic_vector whenever you have a signal with a range of bits representing an integer value.use IEEE.std_logic_signed.all; use ieee.numeric_std.all;
Those two lines should be inside the if( clk'event and clk='1'). I'm not sure Quartus will know how to synthesize your code when it is written like this, and what it would attempt to synthesize would most probably not be what you want. A VHDL simulator will increase twice counter_a for each clock cycle, which will also be different from a synthesized version.counter_a := counter_a +1; counter <= counter_a;
You reinitialize integral to 0 on each clock cycle, so you no longer have an integral.if( clk'event and clk='1') then pid_val := 0; integral := 0;
I think this line is wrong. First you need to use the difference between the previous time value and the current one, instead of just the absolute time, and for an integral value you need to multiply by the time, not divide by it. Each time you write time_cur-time_prev you can just replace it by 1.integral := integral + ((desired - line_cur)/time_cur);
You don't use the reset signal anywhere in your code. Now for the simulation itself. Are you simulating from Quartus? If yes you should consider switching to Modelsim instead. It is a bit more difficult to get started, but it is much more powerful. The Quartus simulator isn't very good, and not supported any more. When you run the simulation, do you see the clock cycling? What kind of outputs do you see?reset <='1'after 40 ns, '0' after 80 ns;