Notices
ECU Flash

PID boost control code

Thread Tools
 
Search this Thread
 
Old May 16, 2008, 05:00 AM
  #1  
Evolved Member
Thread Starter
 
jcsbanks's Avatar
 
Join Date: May 2006
Location: UK
Posts: 2,399
Likes: 0
Received 5 Likes on 4 Posts
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.

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.
Old May 16, 2008, 05:28 AM
  #2  
Evolved Member
Thread Starter
 
jcsbanks's Avatar
 
Join Date: May 2006
Location: UK
Posts: 2,399
Likes: 0
Received 5 Likes on 4 Posts
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.
Old May 16, 2008, 06:02 AM
  #3  
EvoM Guru
iTrader: (6)
 
tephra's Avatar
 
Join Date: Feb 2007
Location: Melbourne, Australia
Posts: 9,486
Received 66 Likes on 42 Posts
ive already written the basics in asm...

i just need to debug it

what sort of issues did you run into?

what gains did you use for P I and D?
Old May 16, 2008, 06:07 AM
  #4  
Evolved Member
Thread Starter
 
jcsbanks's Avatar
 
Join Date: May 2006
Location: UK
Posts: 2,399
Likes: 0
Received 5 Likes on 4 Posts
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
Old May 16, 2008, 07:12 AM
  #5  
EvoM Guru
iTrader: (6)
 
tephra's Avatar
 
Join Date: Feb 2007
Location: Melbourne, Australia
Posts: 9,486
Received 66 Likes on 42 Posts
next week ill debug what code I have so it "runs" sensibly... then I can post it.

basically its going to use the current BDEL table to resolve what WGDC it needs...

If ppl like i can make a 3d map tps vs rpm vs psi but thats all detail we can worry about later
Old May 16, 2008, 08:38 AM
  #6  
Evolved Member
iTrader: (23)
 
honki24's Avatar
 
Join Date: Apr 2003
Location: Houston, TX
Posts: 1,579
Received 0 Likes on 0 Posts
lol a few of you need your own forum. Geniusforums.com
Old May 16, 2008, 02:17 PM
  #7  
Account Disabled
iTrader: (3)
 
dan l's Avatar
 
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.
Old May 16, 2008, 02:26 PM
  #8  
Evolved Member
 
EvoBroMA's Avatar
 
Join Date: Feb 2006
Location: MA
Posts: 1,345
Likes: 0
Received 1 Like on 1 Post
Atmel AVRs FTW!!!
Old May 16, 2008, 03:39 PM
  #9  
Evolved Member
Thread Starter
 
jcsbanks's Avatar
 
Join Date: May 2006
Location: UK
Posts: 2,399
Likes: 0
Received 5 Likes on 4 Posts
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.
Old May 16, 2008, 03:57 PM
  #10  
Account Disabled
iTrader: (3)
 
dan l's Avatar
 
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.
Old May 16, 2008, 11:07 PM
  #11  
EvoM Guru
iTrader: (6)
 
tephra's Avatar
 
Join Date: Feb 2007
Location: Melbourne, Australia
Posts: 9,486
Received 66 Likes on 42 Posts
I thought D was the difference between the error this time and the error lasttime

if if your target is 20psi, and last time you got 17psi, and this time you got 19psi, then D would be 2psi ((20-17)-(20-19))..
Old May 17, 2008, 04:07 AM
  #12  
Evolved Member
Thread Starter
 
jcsbanks's Avatar
 
Join Date: May 2006
Location: UK
Posts: 2,399
Likes: 0
Received 5 Likes on 4 Posts
... 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?
Old May 17, 2008, 06:25 AM
  #13  
Account Disabled
iTrader: (3)
 
dan l's Avatar
 
Join Date: Apr 2006
Location: USA
Posts: 1,029
Likes: 0
Received 0 Likes on 0 Posts
Originally Posted by tephra
I thought D was the difference between the error this time and the error lasttime

if if your target is 20psi, and last time you got 17psi, and this time you got 19psi, then D would be 2psi ((20-17)-(20-19))..
My bad I think that you are right.
Old May 18, 2008, 10:31 PM
  #14  
EvoM Community Team
iTrader: (15)
 
fostytou's Avatar
 
Join Date: Sep 2006
Location: Aurora, IL
Posts: 3,143
Received 7 Likes on 7 Posts
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
Old May 18, 2008, 10:48 PM
  #15  
EvoM Guru
iTrader: (6)
 
tephra's Avatar
 
Join Date: Feb 2007
Location: Melbourne, Australia
Posts: 9,486
Received 66 Likes on 42 Posts
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
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
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

Last edited by tephra; May 18, 2008 at 11:58 PM.


Quick Reply: PID boost control code



All times are GMT -7. The time now is 09:04 AM.