PID boost control code
#1
Evolved Member
Thread Starter
PID boost control code
To avoid polluting Tephra's V5 thread. To discuss V6/PID boost control. Code below is from my 2002 EBC project using Atmel AVR microcontroller and BASCOM-AVR. It was for a slowbaru hence only 20PSI at the time... Discuss...
The throttle position and MAP sensor are read from ADC channels. So are the gains for P, I and D.
The throttle position and MAP sensor are read from ADC channels. So are the gains for P, I and D.
Code:
Dim Setpoint As Byte ' Setpoint Dim Tps As Byte 'throttle position Dim Map As Byte ' MAP Dim Cv As Integer ' output Dim Pterm As Integer Dim Dterm As Integer Dim Iterm As Integer Dim Kp As Integer ' gain Dim Kd As Integer Dim Ki As Integer Dim Duty As Byte ' target duty cycle if no error Dim X As Word 'temporary unsigned variable for ADC Dim Error As Integer ' Difference between Setpoint and Map Dim Lastmap As Byte Dim Integrate As Byte Dim Sum_error As Integer 'still space for 8 more integers at least Const Psi0 = 116 'ADC input for 0 PSI Const Psi1 = 121 Const Psi2 = 126 Const Psi3 = 131 Const Psi17 = 200 Const Psi18 = 205 Const Psi19 = 210 Const Psi20 = 215 'ADC input for 20 PSI Const Mindutycycle = 0 Const Maxdutycycle = 238 'dutycycle %*2.55 Const Tpstarget = 170 'throttle position reference Const Mintps = 100 Config Adc = Single , Prescaler = Auto 'start analog/digital converter Start Adc 'hardware configure 10 bits 0-5V Config Portb = Output Config Timer1 = Pwm , Pwm = 8 , Compare A Pwm = Clear Down , Prescale = 1024 'start PWM output 15.3Hz Enable Interrupts 'each PWM cycle run loop Enable Timer1 On Timer1 Calculate Nosave 'no registers to save Setpoint = Psi18 Do 'endless loop until interrupt Loop Calculate: X = Getadc(0) '10 bits unsigned Shift X , Right , 2 'convert to 8 bit Tps = X 'throttle position X = Getadc(1) '10 bits unsigned Shift X , Right , 2 'convert to 8 bit Duty = X 'target duty cycle if no error X = Getadc(5) Shift X , Right , 2 Ki = X 'integral gain X = Getadc(3) 'MAP sensor Shift X , Right , 2 Map = X X = Getadc(2) Shift X , Right , 2 Kp = X 'proportional gain X = Getadc(4) Shift X , Right , 2 Kd = X 'differential gain Error = Setpoint - Map Portb = 63 'flash 4 LEDS to show how near to target If Error > 0 Then Reset Portb.5 If Error < 3 Then Reset Portb.4 End If If Error < 0 Then Reset Portb.2 If Error > -3 Then Reset Portb.3 End If Dterm = Map - Lastmap 'caluclate differential part Lastmap = Map Dterm = Dterm * Kd Dterm = Dterm / 128 Pterm = Kp * Error 'calculate proportional part Pterm = Pterm / 128 'kp is in range 0-255 'representing 0-2 therefore divide by 128 'quick and dirty FP math using integers 'not using shifts because signed variable! If Integrate = 1 Then Sum_error = Sum_error + Error 'calculate integral part with antiwindup Else Sum_error = 0 End If Iterm = Ki * Sum_error Iterm = Iterm / 128 Integrate = 1 'set to integrate next time unless cancelled by conditions Cv = Pterm + Dterm 'add all three together (can only add 2 variables at once) Cv = Cv + Iterm If Tps < Tpstarget Then Cv = 0 'use static target at partial throttle Integrate = 0 End If Cv = Cv + Duty 'error adjust target duty cycle If Map < Psi1 Then Cv = Mindutycycle 'if no boost shut up/rest solenoid If Tps < Mintps Then Cv = Mindutycycle If Map > Psi20 Then Cv = Mindutycycle 'overboost cutout for momentary 20PSI If Cv <= Mindutycycle Then Cv = Mindutycycle Integrate = 0 End If If Cv >= Maxdutycycle Then Cv = Maxdutycycle Integrate = 0 End If Pwm1a = Cv 'adjust duty cycle output Return
Last edited by jcsbanks; May 16, 2008 at 05:03 AM.
#2
Evolved Member
Thread Starter
Useful info on PID control http://newton.ex.ac.uk/teaching/CDHW/Feedback/
Includes Excel spreadsheet that plots graphs with different gains for PID for an over controller! You can see the oscillations, over/underdamping etc from incorrect gains. Useful to experiment in a simulator before on the car unless you've tuned PID systems before. Certainly helped me with this project.
Includes Excel spreadsheet that plots graphs with different gains for PID for an over controller! You can see the oscillations, over/underdamping etc from incorrect gains. Useful to experiment in a simulator before on the car unless you've tuned PID systems before. Certainly helped me with this project.
#4
Evolved Member
Thread Starter
Post what you've got it you like...
Gains were unitless as I did not convert pressures or duty cycles into real values but left them as integers as the microcontroller I used had no floating point.
Issues were of setting the gains, or the range available to something sensible, required a lot of experimentation. Also issues of sorting out integral wind up and small bugs in code that produce odd results.
I did find that it most definitely needed a starting duty cycle to work with, it wouldn't just find it without. So I got the duty cycle to approx that required to run the boost level then started to add proportional gain. This made a nice controller. I then added a small amount of integral gain, and in the end I think I left out derivative/differential as it was difficult to setup and I was juggling too many variables. When I got into the Subaru ECU ROM I abandoned the project, but I had intended to make it RPM aware, this is easy in our case because we have the variable sitting right there in the ECU
Gains were unitless as I did not convert pressures or duty cycles into real values but left them as integers as the microcontroller I used had no floating point.
Issues were of setting the gains, or the range available to something sensible, required a lot of experimentation. Also issues of sorting out integral wind up and small bugs in code that produce odd results.
I did find that it most definitely needed a starting duty cycle to work with, it wouldn't just find it without. So I got the duty cycle to approx that required to run the boost level then started to add proportional gain. This made a nice controller. I then added a small amount of integral gain, and in the end I think I left out derivative/differential as it was difficult to setup and I was juggling too many variables. When I got into the Subaru ECU ROM I abandoned the project, but I had intended to make it RPM aware, this is easy in our case because we have the variable sitting right there in the ECU
#7
Account Disabled
iTrader: (3)
Join Date: Apr 2006
Location: USA
Posts: 1,029
Likes: 0
Received 0 Likes
on
0 Posts
I've tuned a lot of PID systems in the past. Actually the stock BCS code pseudo acts like a PID system if you look at it in a certain way. Anyways if you get code up and running I'll for sure give it a shot.
Trending Topics
#9
Evolved Member
Thread Starter
Just looking through the code again, the reason I maybe wasn't impressed with the derivative control is that I think the sign is wrong. By using map-lastmap then if the boost is rising then a positive differential gain would add further to the duty cycle, which is the opposite to what we require. I also read that the usual method is to differentiate the error, so again we would use setpoint-present value. So as we increase boost towards the target, the error would be quickly reducing which would reduce the derivative term and dampen the overshoot. Once we actually overshot then the error would then become negative and the differential term would take the inertia out of it, and as dexmix pointed out in the other thread effectively smooth it out.
#10
Account Disabled
iTrader: (3)
Join Date: Apr 2006
Location: USA
Posts: 1,029
Likes: 0
Received 0 Likes
on
0 Posts
Derivative (as I've been taught) is how far away you are from your target. I've always been cautioned to use it sparingly or not at all if I can. The reason being is that it tends to make systems unstable. Too much derivative on a fast control system like boost and you risk overshooting a lot.
Integral (grows bigger with being off of target for a long time) may be difficult. It might have to be triggered by TPS or something because this value could grow very large if just driving around normally for an hour. Or maybe it should be triggered by a certian psi of boost and untriggered when dropping below this boost. Anyways integral tends to make systems more stable.
Integral (grows bigger with being off of target for a long time) may be difficult. It might have to be triggered by TPS or something because this value could grow very large if just driving around normally for an hour. Or maybe it should be triggered by a certian psi of boost and untriggered when dropping below this boost. Anyways integral tends to make systems more stable.
#12
Evolved Member
Thread Starter
... so in that case the derivative gain would be negative to damp the rapidly rising boost.
If Map < Psi1 Then Cv = Mindutycycle 'if no boost shut up/rest solenoid
If Tps < Mintps Then Cv = Mindutycycle
If Map > Psi20 Then Cv = Mindutycycle 'overboost cutout for momentary 20PSI
If Cv <= Mindutycycle Then
Cv = Mindutycycle
Integrate = 0
End If
If Cv >= Maxdutycycle Then
Cv = Maxdutycycle
Integrate = 0
End If
The above bits of code were trying to stop the problem with integral wind up you mention dan l. So it has a TPS trigger and boost level only above which it will allow integration. It also turns it off if it has wound up to either end of the duty cycle range - I think this may be undesirable?
If Map < Psi1 Then Cv = Mindutycycle 'if no boost shut up/rest solenoid
If Tps < Mintps Then Cv = Mindutycycle
If Map > Psi20 Then Cv = Mindutycycle 'overboost cutout for momentary 20PSI
If Cv <= Mindutycycle Then
Cv = Mindutycycle
Integrate = 0
End If
If Cv >= Maxdutycycle Then
Cv = Maxdutycycle
Integrate = 0
End If
The above bits of code were trying to stop the problem with integral wind up you mention dan l. So it has a TPS trigger and boost level only above which it will allow integration. It also turns it off if it has wound up to either end of the duty cycle range - I think this may be undesirable?
#14
EvoM Community Team
iTrader: (15)
For the rest of us who haven't been introduced, the everpresent wiki helps bring the understanding level up a bit also:
http://en.wikipedia.org/wiki/PID_controller
http://en.wikipedia.org/wiki/PID_controller
#15
EvoM Guru
iTrader: (6)
right so I was thinking about this this morning.
The problem with PID is that it is purely reactive - it can't see into the future so to speak.
What are you guys thoughts on trying to implement a Futures-PID, ie we guess what boost we will be at in the future by what's happened in the past. That should hopefully reduce the amount of overboost spike we will see @ spoolup.
Anyways this is my code so far
edit - just updated the code with better Integral Windup, before I was limiting the output of IGAIN*I, now I am limiting I itself... Also added in code so I can flip between PID boost and Stock Boost... temporary for the timebeing but allows me to drive without fear of exploding something
The problem with PID is that it is purely reactive - it can't see into the future so to speak.
What are you guys thoughts on trying to implement a Futures-PID, ie we guess what boost we will be at in the future by what's happened in the past. That should hopefully reduce the amount of overboost spike we will see @ spoolup.
Anyways this is my code so far
Code:
sts.l pr, @-r15 mov.l r0, @-r15 mov.l r1, @-r15 mov.l r2, @-r15 mov.l r3, @-r15 mov.l r4, @-r15 mov.l r5, @-r15 mov.l r10, @-r15 ! PSEUDO CODE ! MY_ERROR_PSI = target_boost - ECU_MAP_SENSOR ! OUTPUT = PGAIN*MY_ERROR_PSI + IGAIN2*MY_TOTAL_ERROR_PSI - DGAIN2*(MY_ERROR_PSI-MY_LAST_ERROR_PSI) ! MY_LAST_ERROR_PSI = MY_ERROR_PSI mov.l (MY_BDEL_map_pointer), r0 mov.l @r0, r0 mov.l (alternate_bdel_map), r1 cmp/eq r0, r1 bf not_on_altmaps mov.l (ECU_RPM_5), r0 mov.w @r0, r0 mov.w (min_rpm_for_boost_control), r1 cmp/hs r0, r1 bt rpm_for_boost_control_not_yet_met mov.l (MY_DAVES_COUNTER), r0 mov.w @r0, r0 tst r0, r0 bf interval_countdown_not_zero mov.w (interval), r1 mov.l (MY_DAVES_COUNTER), r0 mov.w r1, @r0 mov.l (axis_rpm_boost), r4 mov.l (Table_Lookup_Axis), r10 jsr @r10 nop mov.l (MY_BDEL_map_pointer), r4 mov.l (Query_3D_Table), r10 jsr @r10 nop mov r0, r1 mov.l (boost_control_load_offset), r0 mov.w @r0, r0 add r0, r1 mov.l (ECU_MAP_SENSOR), r0 mov.w @r0, r0 sub r0, r1 ! MY_ERROR_PSI = target_boost - ECU_MAP_SENSOR mov.l (MY_ERROR_PSI), r0 mov.w r1, @r0 mov.w (p_gain_x10), r0 mul.l r1, r0 sts macl, r2 ! r2 = PGAIN*MY_ERROR_PSI mov.l (MY_TOTAL_ERROR_PSI), r0 mov.w @r0, r0 add r0, r1 mov.w (min_i), r3 cmp/hs r1, r3 bt output_less_than_min_i mov.w (max_i), r3 cmp/hs r3, r1 bf output_less_than_max_i mov.w (max_i), r1 output_less_than_max_i: bra output_i_range_check_done nop output_less_than_min_i: mov.w (min_i), r1 output_i_range_check_done: mov.l (MY_TOTAL_ERROR_PSI), r0 mov.w r1, @r0 mov.w (i_gain_x10), r0 mul.l r1, r0 sts macl, r3 ! r3 = IGAIN*MY_TOTAL_ERROR_PSI mov.l (MY_LAST_ERROR_PSI), r0 mov.w @r0, r0 mov.l (MY_ERROR_PSI), r1 mov.w @r1, r1 sub r0, r1 mov.w (d_gain_x10), r0 mul.l r1, r0 sts macl, r4 ! r4 = DGAIN*(MY_ERROR_PSI-MY_LAST_ERROR_PSI) add r3, r2 sub r4, r2 mov r2, r4 mov #0x0a, r5 mov.l (R4_DIV_R5_Into_R0), r10 jsr @r10 nop mov.w (min_wgdc), r1 cmp/hs r0, r1 bt output_less_than_min_wgdc mov.w (max_wgdc), r1 cmp/hs r1, r0 bf output_less_than_max_wgdc mov.w (max_wgdc), r0 output_less_than_max_wgdc: bra output_range_check_done nop output_less_than_min_wgdc: mov.w (min_wgdc), r0 output_range_check_done: mov #0x02, r1 mul.l r1, r0 sts macl, r0 mov.l (ECU_FINAL_WASTEGATE_DUTY), r1 mov.w r0, @r1 mov.l (MY_ERROR_PSI), r0 mov.w @r0, r0 mov.l (MY_LAST_ERROR_PSI), r1 mov.w r0, @r1 bra boost_control_done nop interval_countdown_not_zero: mov.l (MY_DAVES_COUNTER), r0 mov.w @r0, r1 add #-0x1, r1 mov.w r1, @r0 bra boost_control_done nop rpm_for_boost_control_not_yet_met: mov #0x0, r0 mov.l (MY_DAVES_COUNTER), r1 mov.w r0, @r1 mov.l (MY_LAST_ERROR_PSI), r1 mov.w r0, @r1 mov.l (MY_TOTAL_ERROR_PSI), r1 mov.w r0, @r1 mov.l (ECU_FINAL_WASTEGATE_DUTY), r1 mov.w r0, @r1 bra boost_control_done nop not_on_altmaps: mov.l (Turbo_Control_Calcs), r10 jsr @r10 nop boost_control_done: mov.l @r15+, r10 mov.l @r15+, r5 mov.l @r15+, r4 mov.l @r15+, r3 mov.l @r15+, r2 mov.l @r15+, r1 mov.l @r15+, r0 lds.l @r15+, pr rts nop .align 4 version: .float 1.0 ECU_MAP_SENSOR: .long 0xFFFF6ACC ECU_FINAL_WASTEGATE_DUTY: .long 0xFFFF6EC6 ECU_RPM_5: .long 0xFFFF6AFE MY_DAVES_COUNTER: .long 0xFFFF8440 MY_ERROR_PSI: .long 0xFFFF8442 MY_LAST_ERROR_PSI: .long 0xFFFF8444 MY_TOTAL_ERROR_PSI: .long 0xFFFF8446 MY_BDEL_map_pointer: .long 0xFFFF8438 R4_DIV_R5_Into_R0: .long 0x9FA Query_3D_Table: .long 0xDE0 Table_Lookup_Axis: .long 0xCC6 boost_control_load_offset: .long 0x1670 ! OR Atmo base pressure :) axis_rpm_boost: .long 0x732C Turbo_Control_Calcs: .long 0x40110 alternate_bdel_map: .long 0x381e2 p_gain_x10: .word 2 i_gain_x10: .word 2 d_gain_x10: .word 1 min_i: .word 0 max_i: .word 1000 min_wgdc: .word 0 max_wgdc: .word 100 min_rpm_for_boost_control: .word 0x200 interval: .word 0x5
Last edited by tephra; May 18, 2008 at 11:58 PM.