Shop OBEX P1 Docs P2 Docs Learn Events
BoeBot using PID — Parallax Forums

BoeBot using PID

Bot LearnerBot Learner Posts: 9
edited 2007-05-15 21:27 in Robotics
Hi all.

I'm new to this stuff and I'm working with the Boe-Bot Robotics kit. I finished the exercises in the book and noticed the jittering with the·programs in chapters 7 & 8 for following another boe bot. I read the Better Boe-Bot IR Distance Measurements paper by Andy and incorporated the new hardware design and software. Still quite a lot of jitter, so I read the PID Control Intro with the Basic Stamp. I've tried to incorporate PID in the software using the new circuit, but I'm running out of variable space. Not being a math wizard, I may be doing more than necessary. Here's the code:

' BoeBotFollowingWithPidAlgorithm.bs2
' Demonstrates how a combination of proportional, integral, and
' derivative control influence error correction in a feedback loop.
·
' {$STAMP BS2}
' {$PBASIC 2.5}
·
'
[noparse][[/noparse] Constants ]

·
SetPoint······· CON···· 2···················· ' Set point
Kpl·············· CON···· -15·················' L & R proportionality constants
Kpr············· CON···· 15
Kil·········· ···· CON···· -15··················' L & R integral constants
Kir·············· CON···· 15
Kdl············· CON···· -15··················' L & R derivative constants
Kdr············· CON···· 15
Current········ CON···· 0···················· ' Array index - current error
Accumulator··CON···· 1···················· ' Array index - accumulated error
Previous······· CON···· 2···················· ' Array index - previous error
Delta············ CON···· 3···················· ' Array index - change in error
Offset··········· CON···· 750·················' Servo not moving
·
'
[noparse][[/noparse] Variables ]

·
irDetectLeft··· VAR···· Bit
irDetectRight· VAR···· Bit
distanceLeft·· VAR···· Nib
distanceRight· VAR···· Nib
errorLeft······· VAR···· Word(4)·············' Four different types of errors
errorRight····· VAR···· Word(4)
p·················· VAR···· Word··················' Proportional term
i··················· VAR···· Word··················' Integral term
d·················· VAR···· Word················· ' Derivative term
pulseLeft······ VAR···· Word················· ' Output
pulseRight·····VAR· ···Word
·
'
[noparse][[/noparse] Initialization ]

·
FREQOUT 1, 1000, 3000
·
'
[noparse][[/noparse] Main Routine ]

·
DO
·
· GOSUB Get_IR_Distances
·
· ' Calculate error.
· errorLeft(Current) = SetPoint - distanceLeft
· errorRight(Current) = SetPoint - distanceRight
·
· ' Calculate left proportional term.
· p = Kpl * errorLeft(Current)
·
· ' Calculate left integral term.
· errorLeft(Accumulator) = errorLeft(Accumulator) + errorLeft(Current)
· i = Kil * errorLeft(Accumulator)
·
· ' Calculate left derivative term.
· errorLeft(Delta) = errorLeft(Current) - errorLeft(Previous)
· d = Kdl * errorLeft(Delta)
·
· ' Calculate left output.
· pulseLeft =· p + i + d + Offset MIN 650 MAX 850
·
· ' Calculate right proportional term.
· p = Kpr * errorRight(Current)
·
· ' Calculate right integral term.
· errorRight(Accumulator) = errorRight(Accumulator) + errorRight(Current)
· i = Kir * errorRight(Accumulator)
·
· ' Calculate right derivative term.
· errorRight(Delta) = errorRight(Current) - errorRight(Previous)
· d = Kdr * errorRight(Delta)
·
· ' Calculate right output.
· pulseRight = p + i + d + Offset MIN 650 MAX 850
·
· ' Save current error to previous error before next iteration.
· errorLeft(Previous) = errorLeft(Current)
· errorRight(Previous) = errorRight(Current)
·
· GOSUB Send_Pulse
·
LOOP
·
'
[noparse][[/noparse] Subroutine - Get_IR_Distances ]

·
Get_IR_Distances:
·
· distanceLeft = 0
· distanceRight = 0
·
· FOR DIRB = 1 TO 7
·
··· FREQOUT 7,1,38500
··· irDetectLeft = IN8
··· distanceLeft = distanceLeft + irDetectLeft
·
·· ·FREQOUT 3,1,38500
··· irDetectRight = IN2
··· distanceRight = distanceRight + irDetectRight
·
· NEXT
·
· RETURN
·
'
[noparse][[/noparse] Subroutine - Send_Pulse ]

·
Send_Pulse:
·
· PULSOUT 13, pulseLeft
· PULSOUT 12, pulseRight
· PAUSE 5
· RETURN

Anyone have any comments/suggestions?

Thanks,

Bot Learner

Comments

  • edited 2007-05-09 00:39
    Eventually, you can crunch it down to where you're using all nibbles and bytes, except for the maybe the integral accumulator, and you might even be able to keep that within a byte. The trick will be to shift everything over so that your results are always positive, so you won't need Word values any more for negative results.

    Don't do that now though, because it'll only confuse things. The current goal is to get a working prototype. After that, you can start experimenting with how much memory you can retrive. For your working prototype, you only really need one word. The next suggestion will save you three.

    The first thing I'd do to retrieve enough RAM to test the prototype is get rid of P, I, and D. How? By adding everything into driveLeft and driveRight. P, I, and D are intermediate terms, and therefore disposeable. Each time throught the loop, start by setting pulseLeft and pulseRight to zero. Then, instead of p = kpl * errorLeft(Current), use pulseLeft kpl * errorLeft(current). Next, instead of i = kil * errorLeft(accumulator), use pulseLeft = pulseLeft + (kil * errorLeft(accumulator)). Instead of d = kdl * errorLeft(Delta), use pulseLeft = pulseLeft + (kdl * errorLeft(Delta)). At the end, you'll need pulseLeft = pulseLeft + Offset.

    See how that works? Even though you're not using the variables P, I, and D, you are still performing the P, I, and D calculateions and adding them up as you go.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Andy Lindsay

    Education Department
    Parallax, Inc.
  • edited 2007-05-09 01:11
    You're really close to a working prototype at this point. After you get through keeping running totals of P, I, and D, there's another thing you're going to need to do. Your kp, ki, and kd constants are going to have to be redefined in fractional terms. That way, kp, ki, and kd can be used to multiply by numbers with fractional values. Below are examples that show how to multiply by pi (~3.1416) and the reciprocal of the square root of two (~0.7071).· You will also need a way of performing the fractional operations as positive values and then setting the sign of the result at the end because the BASIC Stamp's fractional scaling operators are designed for positive calculations.· Also explained below...

    PBASIC has two kinds of fractional multiplications: */ and **. The */ operator multiples by a number of 256ths and ** multiplies by a number of 65536ths. For **, the fraction has to be smaller than 1. Lets say you want value = value X 3.1416. 3.1416 has to be redefined in terms of 256ths. Simply multiply 3.1416 by 256, and use the integer result in your */ operation. So the equivalent operation with */ is value = value */ 804. Let's say you want to multiply a value by 0.7071. You can use ** for a more accurate calculation since your multiplying by a value that's less than 1. In this case, you are multiplying by a number of 65536ths. So multiply 0.7071 by 65536, and use the integer portion of the result in your ** calculation. The equivalent operation is value = value ** 46341.

    You'll probably want to do this work in a subroutine that also deals with negative results. Reason being, */ and ** only work properly with positive integers. Start with a small program that allows you to enter an error. First, store the sign of each term by storing bit15 of that term. If the term is negative, bit15 will be 1; otherwise, it'll be 0. Try declaring two bit variables, signA and signB. Then, use the ABS operator to do positive calculations. You will then have a positive result and two bits. If exclusive-or of signA nd signB is is 1, then the result is negative. If it's 0 the result is positive. So IF (signA ^ signB) THEN result = - result.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Andy Lindsay

    Education Department
    Parallax, Inc.

    Post Edited (Andy Lindsay (Parallax)) : 5/9/2007 1:22:37 AM GMT
  • Bot LearnerBot Learner Posts: 9
    edited 2007-05-09 01:19
    Thanks Andy!

    I tried the suggestions in your first reply. That gets the word count to 10, which works. Now I just have to figure out why the boebot keeps going in a backwards circle regardless of the distance of the detected object.

    Bot Learner
  • edited 2007-05-09 01:24
    Part of that is going to be tuning. The values of kp, ki, and kd will have to be determined with some experimentation. ki and kd should initially have a minute effect. Then, gradually increase them and see what happens. Your ki will have to be larger than it should to filter some of the noise (a secondary effect of integral control). Certain values of kd will dynamically offset the whole thing to make it work better. But first, you'll have to set up those signed fractional calculations as discussed in the 6:11 PM (GMT -7) post.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Andy Lindsay

    Education Department
    Parallax, Inc.
  • Bot LearnerBot Learner Posts: 9
    edited 2007-05-11 00:36
    Hi Andy.

    Okay. I read your posts above and did some more reading on PID control. I also read the PID Control section of the Industrial Control Student Guide. I have a program that works, but is still herky/jerky. What I'm having the most trouble understanding is the comments you made about fractional calculations. How and why are fractional calculations incorporated?

    Here's the latest code. (It might seem familiar :-) )

    ' BoeBotFollowingWithPidAlgorithm2.bs2

    ' {$STAMP BS2}
    ' {$PBASIC 2.5}

    '
    [noparse][[/noparse] Constants ]

    SetPoint·········· CON···· 1················ ' Set point
    Kpl················· CON···· 10··············· ' L & R proportionality constants
    Kpr················· CON···· -10
    Kil·················· CON···· 2················ ' L & R integral constants
    Kir·················· CON···· -2
    Kdl················· CON···· 2················ ' L & R derivative constants
    Kdr················· CON···· -2
    Current··········· CON···· 0················ ' Array index - current error
    Accumulator·····CON···· 1················ ' Array index - accumulated error
    Previous·········· CON···· 2················ ' Array index - previous error
    Delta·············· CON···· 3················ ' Array index - change in error
    Offset············· CON···· 750·············' Servo not moving

    '
    [noparse][[/noparse] Variables ]

    irDetectLeft······ VAR···· Bit
    irDetectRight·····VAR···· Bit
    distanceLeft······VAR···· Nib
    distanceRight·····VAR···· Nib
    errorLeft·········· VAR···· Word(4)·········· ' Four different types of errors
    errorRight········ ·VAR···· Word(4)
    pulseLeft·········· VAR···· Word············· ' Output
    pulseRight········· VAR···· Word
    Sign················ VAR·····Word············· ' Used to hold sign for calculations

    '
    [noparse][[/noparse] Initialization ]

    FREQOUT 1, 1000, 3000

    '
    [noparse][[/noparse] Main loop ]

    DO
    · GOSUB Get_IR_Distances
    · GOSUB Calc_Drive
    · GOSUB Send_Pulse
    LOOP

    '
    [noparse][[/noparse] Calculate Drive ]

    Calc_Drive:
    · GOSUB ErrorCalc···························· ' Error Calcs
    · GOSUB PropCalc·····························' Perform proportional error calcs
    · GOSUB IntCalc······························ ' Perform Integral Calcs
    · GOSUB DerivCalc····························' Perform Derivative calcs
    · RETURN

    '
    [noparse][[/noparse] Calculate Error ]

    ErrorCalc:
    · ' Calculate error
    · errorLeft(Current) = SetPoint - distanceLeft
    ··errorRight(Current) = SetPoint - distanceRight
    ··RETURN

    '
    [noparse][[/noparse] Proportional Drive - Sign adjusted ]

    PropCalc:
    · Sign = errorLeft(Current)
    · GOSUB SetSign
    · pulseLeft = Kpl * ABS errorLeft(Current)
    · pulseLeft = pulseLeft * Sign
    · Sign = errorRight(Current)
    · GOSUB SetSign
    · pulseRight = Kpr * ABS errorRight(Current)
    · pulseRight = pulseRight * Sign
    · RETURN

    '
    [noparse][[/noparse] Integral Drive - Sign Adjusted ]

    IntCalc:
    · errorLeft(Accumulator) = errorLeft(Accumulator) + errorLeft(Current)
    · Sign = errorLeft(Accumulator)
    · GOSUB SetSign
    · errorLeft(Accumulator)· = ABS errorLeft(Accumulator) * Kil
    · errorLeft(Accumulator)· = errorLeft(Accumulator)· * Sign
    · errorRight(Accumulator) = errorRight(Accumulator) + errorRight(Current)
    · Sign = errorRight(Accumulator)
    · GOSUB SetSign
    · errorRight(Accumulator) = ABS errorRight(Accumulator)· * Kir
    · errorRight(Accumulator)· = errorRight(Accumulator)· * Sign
    · RETURN

    '
    [noparse][[/noparse] Derivative Drive ]

    DerivCalc:
    · errorLeft(Delta) = errorLeft(Current) - errorLeft(Previous)
    · pulseLeft = pulseLeft + (Kdl * errorLeft(Delta))
    · errorRight(Delta) = errorRight(Current) - errorRight(Previous)
    · pulseRight = pulseRight + (Kdr * errorRight(Delta))
    · RETURN

    '
    [noparse][[/noparse] Set sign of value ]

    SetSign:
    · IF Sign.BIT15 = 0 THEN SignPos············ 'If signbit is 1, then negative
    · Sign = -1
    · RETURN

    · SignPos:
    ··· Sign = 1
    · RETURN

    '
    [noparse][[/noparse] Subroutine - Get_IR_Distances ]

    Get_IR_Distances:

    · distanceLeft = 0
    · distanceRight = 0

    · FOR DIRB = 1 TO 7

    ··· FREQOUT 7,1,38500
    ··· irDetectLeft = IN8
    ··· distanceLeft = distanceLeft + irDetectLeft

    ··· FREQOUT 3,1,38500
    ··· irDetectRight = IN2
    ··· distanceRight = distanceRight + irDetectRight

    · NEXT

    · RETURN

    '
    [noparse][[/noparse] Subroutine - Send_Pulse ]

    Send_Pulse:

    · PULSOUT 13, pulseLeft
    · PULSOUT 12, pulseRight
    · PAUSE 5
    · RETURN

    Thanks foryour help.

    Bot Learner
  • edited 2007-05-11 04:07
    If you add this DEBUG command to the sendPulse command, you'll see that you've got some debugging to do.

    DEBUG DEC pulseLeft, " ", DEC pulseRight, CR

    The values are nowhere near the 650 to 850 range most of the time. Start by revisiting the PID Stamps in Class Mini Projects post. Then, insert DEBUG commands to find if each calculation is doing what it's supposed to do when you manually measured distances using the Debug Terminal.· You'll need to insert a bunch of DEBUG commands in your code to see the results of each calculation.· Once you get the calculations fixed, go back and comment all the DEBUG commands because they will take too much time and slow down your code.

    I'll answer your math question in a second post.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Andy Lindsay

    Education Department
    Parallax, Inc.
  • edited 2007-05-11 04:55
    Here's a little something that's slightly off topic.· Attached is a modified version of FollowingBoeBotWithNewDistanceCircuit.bs2 that runs the proportional speed corrections based on distance measurements through a ramping subroutine.· The result is significantly improved tracking when the object is held still.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Andy Lindsay

    Education Department
    Parallax, Inc.
  • edited 2007-05-11 07:09
    Attached·is a functional Boe-Bot PID control loop in need of tuning.· You'll need to experiment with Kp, Ki, Kd, and the limits on the integral accumulators.· With the limited range of input values, you may be right about not needing fractional multiply.· However, the code is using fractional multiply, and it is working.··The Ki, Kd, and Kp values are 256 times larger because all the multiplications are done in fractions of 256 with the */ operator.

    Although this has been a·lesson in implementing the PID algorithm, I suspect it won't do any better than the ramping code even when it's tuned.· Smoothing isn't really PID's main function, it's tracking, and noise should be pre filtered.·

    Along·the pre filtering·lines, combining averaging and maybe some extreme and/or delta exclusion code will help as a pre filter for either the ramping or PID code.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Andy Lindsay

    Education Department
    Parallax, Inc.

    Post Edited (Andy Lindsay (Parallax)) : 5/11/2007 7:15:04 AM GMT
  • Bot LearnerBot Learner Posts: 9
    edited 2007-05-11 18:50
    Thanks Andy!

    Both programs work quite well! The concepts will come in handy with other projects like the Crawler Kit and the Gripper Kit just to name two.

    I'll take your suggestion and insert DEBUG coimmands in my code to look at the steps its going through and make sure I get the results I expect from the calculations. I also revisited the PID Stamps in Class Mini Projects post again and I noticed that in the front end you state "In coming Chapters, you will use PID to smooth out the Boe-Bot’s following behavior, and improve its line following performance." Do these "coming Chapters" currently exist, or are they forthcoming?


    Thanks again for all the help!!

    Bot Learner
  • edited 2007-05-11 19:33
    Bot Learner,

    Actually, you are helping create those chapters!· jumpin.gif· This is the first working dual PID loop Boe-Bot code I know of, and it's got me pretty fired up to refine it and start writing.· It'll probably be a couple weeks before I find enough spare time though.· sad.gif

    The·DEBUG code is already in BoeBotFollowingWithPidAlgorithmsCorrected.bs2.· Just uncomment the DEBUG commands, and you'll be able to go through it by·hand and·verify that it is doing what it's supposed to.· I did that to make sure my own advice was correct.··Unfortunately, I ended up loosing track of the·changes I made and it was getting late, so I posted the working·prototype instead of suggestions about how to·get there.· Sorry about that.· For best results at this point, do a line by line comparison between your code and the·Corrected and challenge any mysterious changes.

    The next step will be to examine the IR measurement noise by datalogging a few distance detection sessions in EEPROM.· After examining the noise, a software filter will be written to pre-filter that type of noise.· It'll be the best smoothing solution, and it will significantly improve both the PID and ramping examples.·

    To get started, place the Boe-Bot at various distances (find the middle of a given measurement zone), and then write and run datalogging code and store·various series of IR distance measurements in a spreadsheet.· For an example datalogging and storing data in a spreadsheet, see Smart Sensors and Applications, Chapter 6, Activity #2 through #6:

    Smart Sensors & Applications
    Smart Sensors and Applications Text (.pdf)
    Smart Sensors Code and Spreadsheet (.zip)

    Andy

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Andy Lindsay

    Education Department
    Parallax, Inc.

    Post Edited (Andy Lindsay (Parallax)) : 5/11/2007 7:41:29 PM GMT
  • edited 2007-05-11 19:37
    P.S. The parts in Smart Sensors and Applications make great Boe-Bot add-ons.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Andy Lindsay

    Education Department
    Parallax, Inc.
  • Bot LearnerBot Learner Posts: 9
    edited 2007-05-11 22:42
    I like the look of the Smart Sensors and Applications kit, but I already have the kit for the Ping sensors so I will look into the other individual parts when I get to that point.

    I'll also look at the datalogging and try that. I've already started looking at filters and am looking at Tracy Allen's Stamp II math note #5 as a starting point. (http://www.emesystems.com/BS2math5.htm)

    I did the line by line comparison and the only question I have has to do with the Calc_Drive subroutine. Every time through the main loop this subroutine is performed. Then the Send_Pulse subroutine is done. However, within the Calc_Drive subroutine a call is made to the Send_Pulse subroutine. On first look it seems redundant, but it obviously works, since the control is a lot more herky/jerky without the extra call even if I add the PAUSE command to the Send_Pulse subroutine. Why is that?

    The rest of the code makes sense. I like the way you do the fractional calculations and sign adjustments in the same routine. Clean and simple.

    I hope others find the code samples you came up with as useful as I do.

    Bot Learner
  • edited 2007-05-11 22:53
    That's a good question. I added the second call to the Send_Pulse subroutine because the current incarnation of the code takes 68 ms to read the sensors, do the calculations, and send the pulses. Servos like to get control pulses every 20 ms, but Parallax Continuous Rotation Servos will still behave just fine with delays up to about 45 ms between pulses. By adding the second call to Send_Pulse along with the pause, it causes two pulses to be sent 40 ms apart,·with each iteration of the main loop taking 80 ms. Code that takes 1/3 to 1/4 of the time, variables, and program memory will be a reasonable goal.

    Hmmm, yes, Tracy's low pass filter code has potential.· Definitely worth a try.· Condencing the PID code to free up three words will be the first order of business.

    Fortunately, there's no real difference between the cost of the individual parts and the cost of the kit.· There are some cool Boe-Bot + Ping))) and Accelerometer projects posted in the Stamps in Class "Mini Projects" section.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Andy Lindsay

    Education Department
    Parallax, Inc.

    Post Edited (Andy Lindsay (Parallax)) : 5/11/2007 11:04:52 PM GMT
  • bietzbietz Posts: 22
    edited 2007-05-14 13:00
    Hi,
    I was reading the PID Control Intro with the BASIC Stamp thread in Stamps in Class, and wanted to try it on my Boe-Bot since I was VERY attracted by the PEKbot video you posted of that very cool Propeller-based robot... (BTW I think I'll get the Propeller Education Kit as soon as possible, and this should be in July, because I'm going in the US (I actually live in France and shipping is unfortunately very expensive cry.gif. Hopefully Parallax (or UPS) will reduce shipping price to Europe one day...?))

    Ok so I tried to make the program (the code is below) and the results don't look very good, but I believe the BS2 can't do as good as the Propeller... Here is a video: www.youtube.com/watch?v=hzv63xnls34

    Can I improve the program some way? Should I try better PID constants? Which ones?

    Here's the code
    ' -----[noparse][[/noparse] Title ]--------------------------------------------------------------
    ' Boe-Bot PID control. Object follower
    
    ' {$STAMP BS2}                               ' Stamp directive.
    ' {$PBASIC 2.5}                              ' PBASIC directive.
    
    DEBUG "Program Running!"
    
    ' -----[noparse][[/noparse] Constants ]----------------------------------------------------------
    Kpl            CON     -15
    Kpr            CON     15
    Kil            CON     -3
    Kir            CON     3
    Kdl            CON     -3
    Kdr            CON     3
    SetPoint       CON     4
    CenterPulse    CON     750
    
    Current        CON     0
    Accumulator    CON     1
    Previous       CON     2
    Delta          CON     3
    
    Left           CON     0
    Right          CON     1
    
    ' -----[noparse][[/noparse] Variables ]----------------------------------------------------------
    irDetectLeft   VAR     Bit
    irDetectRight  VAR     Bit
    distanceLeft   VAR     Nib
    distanceRight  VAR     Nib
    pulseLeft      VAR     Word
    pulseRight     VAR     Word
    
    errorLeft      VAR     Word(4)
    errorRight     VAR     Word(4)
    'p              VAR     Word(2)
    'i              VAR     Word(2)
    'd              VAR     Word(2)
    
    ' -----[noparse][[/noparse] Initialization ]-----------------------------------------------------
    FREQOUT 1, 2000, 3000
    errorLeft(Accumulator) = 0
    errorLeft(Previous) = 0
    errorRight(Accumulator) = 0
    errorRight(Previous) = 0
    
    ' -----[noparse][[/noparse] Main Routine ]-------------------------------------------------------
    DO
    
      GOSUB Get_Ir_Distances
    
      'Current error
      errorLeft(Current) = SetPoint - distanceLeft
      errorRight(Current) = SetPoint - distanceRight
    
      errorLeft(Accumulator) = errorLeft(Accumulator) + errorLeft(Current)
      errorRight(Accumulator) = errorRight(Accumulator) + errorRight(Current)
    
      errorLeft(Delta) = errorLeft(Current) - errorLeft(Previous)
      errorRight(Delta) = errorRight(Current) - errorRight(Previous)
    
      ' Calculate pid output.
      pulseLeft =  (errorLeft(Current) * Kpl) + (errorLeft(Accumulator) * Kil) 
                       + (errorLeft(Delta) * Kdl) + CenterPulse MIN 650 MAX 850
      pulseRight =  (errorRight(Current) * Kpr) + (errorRight(Accumulator) * Kir) 
                        + (errorRight(Delta) * Kdr) + CenterPulse MIN 650 MAX 850
    
      GOSUB Send_Pulse
    
      errorLeft(Previous) = errorLeft(Current)
      errorRight(Previous) = errorRight(Current)
    
    LOOP
    
    ' -----[noparse][[/noparse] Subroutine - Get IR Distances ]--------------------------------------
    Get_Ir_Distances:
    
      distanceLeft = 0
      distanceRight = 0
    
      FOR DIRB = 1 TO 7
    
        FREQOUT 7,1,38500
        irDetectLeft = IN8
        distanceLeft = distanceLeft + irDetectLeft
    
        FREQOUT 3,1,38500
        irDetectRight = IN2
        distanceRight = distanceRight + irDetectRight
    
      NEXT
    
      RETURN
    
    ' -----[noparse][[/noparse] Subroutine - Get Pulse ]---------------------------------------------
    Send_Pulse:
    
      PULSOUT 13,pulseLeft
      PULSOUT 12,pulseRight
      PAUSE 5
      RETURN
    



    Thanks in advance
  • edited 2007-05-14 17:11
    Hello bietz,

    Nice code. You can fix the fact that the Boe-Bot runs into the target by putting limits on your error(accumulator) variables. Right now, they are accumulating too far, and then taking too long to decay after the correction has been made. Take a look at the code attached to my 5/11/2007 12:09 AM (GMT -7) post for an approach that uses MIN and MAX to limit the accumulator variables.

    There are also some physical adjustments you can make to keep the Boe-Bot from wagging its tail so much. (1) Mount the servos on the outside of the chassis. This slightly dampens its turning tendency. (2) Move the IR detectors toward the middle by two rows on the breadboard, and mount the IR LEDs on the outermost left and right. They should still be pointed very slightly outward, but not as much as they are now.

    My thought was the same as yours, that the PID code on the Boe-Bot should work as well as the PEKbot code. It may still be possible, but it's going to take some additional code. The Boe-Bot and PEKbot are using two different distance detection algorithms, and I think there is less noise per measurement in the PEKbot's detection system. In other words, the PEKbot's IR detection signal to noise ratio (SNR) is higher than the Boe-Bot robot's.

    In the PEKbot example, the integral is having a secondary effect, filtering some of the nose. Integral isn’t really intended to be used that way. Integral is supposed to make the output home in on its set point value, regardless of external forces that overwhelm the proportional component. It "accumulates" until the output fully corrects the input. Well, actually there is some overshoot and maybe some ringing, but eventually, it makes the system's output home in on the correct set point.

    A more conventional (and correct) approach is to pre filter the IR detector noise. This should help further improve your Boe-Bot's PID performance. Even on the PEKbot, this would make it possible to reduce the integral and get it to settle more quickly on its set point distance. For more info, take a look at this thread's posts between 5/11/2007 12:33 PM (GMT -7) and 5/11/2007 3:42 PM (GMT -7).

    Yeah, the Propeller Education Kit (PE Kit) is great for robotics prototyping. It's multiple cogs (processors) make it possible to simultaneously control all the actuators, poll all the sensors, and communicate with numerous other robots and/or processors. Fortunately, July isn't too far away. Even so, our sales department may have some tips for reducing the shipping costs to France. I'll check and post a follow-up here.

    Andy

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Andy Lindsay

    Education Department
    Parallax, Inc.
  • bietzbietz Posts: 22
    edited 2007-05-14 18:02
    Thank you Andy!
    I'll try the modifications as soon as I can.
  • bietzbietz Posts: 22
    edited 2007-05-15 21:08
    I'm trying to add limits to the accumulator, so I'm debugging the values to see what I should pick (DEBUG DEC errorLeft(Accumulator), " ", DEC errorRight(Accumulator), CR), but when the object stays close to the robot (i.e. when the accumulator should become negative), I get values close to 65536: is that just the representation of negative values for the BS2?

    And what limits do you think I should choose?
    I don't get what the following code means (your code): do the MIN and MAX only apply to the following variable or to the hole expression? How do these actually work?
    errorLeft(accumulator) + IntErrorOffset MAX IntErrorMax MIN IntErrorMin - IntErrorOffset

    ps: I had a look at the Propeller manual: spin looks really cool!! object oriented is fantastic!! (I know C++, so I know what that means...) It's got to be very powerful!
  • bietzbietz Posts: 22
    edited 2007-05-15 21:12
    with these MIN and MAX, I always get '100 100' on DEBUG:
    errorLeft(Accumulator) = errorLeft(Accumulator) + errorLeft(Current)    MIN -100 MAX 100  
    errorRight(Accumulator) = errorRight(Accumulator) + errorRight(Current) MIN -100 MAX 100
    



    Why is this?
  • bietzbietz Posts: 22
    edited 2007-05-15 21:19
    OK I just looked at the Documentation... (I should have done that earlier blush.gif )
    Documentation said...
    MIN works in positive math only; its comparisons are not valid when used on two’s complement negative numbers, since the positive-integer representation of a number like -1 ($FFFF or 65535 in unsigned decimal) is larger than that of a number like 10 ($000A or 10 decimal). Use MIN only with unsigned integers. Because of the way fixed-size integers work, you should be careful when using an expression involving MIN 0. For example, 0 - 1 MIN 0 will result in 65535 because of the way fixed-size integers wrap around.

    So how can I do it?
  • bietzbietz Posts: 22
    edited 2007-05-15 21:25
    ok I got it...
    your offset was just for shifting and coming back...
    And I guess my limit values were actually too big!!

    Here's the new code
    errorLeft(Accumulator) = errorLeft(Accumulator) + errorLeft(Current)  + IntOffset  MIN 70 MAX 130 - IntOffset   
    errorRight(Accumulator) = errorRight(Accumulator) + errorRight(Current) + IntOffset  MIN 70 MAX 130 - IntOffset
    


    [noparse][[/noparse]70 and 130: I like it with a bit more integral, so that I see the cool little turning 'delay']

    Sorry for my previous posts...

    Post Edited (bietz) : 5/15/2007 9:35:51 PM GMT
  • edited 2007-05-15 21:25
    bietz said...
    what I should pick (DEBUG DEC errorLeft(Accumulator), " ", DEC errorRight(Accumulator), CR), but when the object stays close to the robot (i.e. when the accumulator should become negative), I get values close to 65536: is that just the representation of negative values for the BS2?
    Aach!!! Sorry.· You are getting the twos compliment·version of the negative numbers.· Use SDEC instead of DEC.·

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Andy Lindsay

    Education Department
    Parallax, Inc.
  • edited 2007-05-15 21:27
    bietz said...
    ok I got it...

    ...Sorry for my previous posts...
    No worries.· We all benefit from these questions and answers.

    Andy

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Andy Lindsay

    Education Department
    Parallax, Inc.
Sign In or Register to comment.