![]() |
Re: Unexpected results from Encoder::GetRate()
Here's 4x decoding data with the same changes to Encoder.cpp as listed above.
http://spreadsheets.google.com/ccc?k...E1zX 2c&hl=en And here's the relevant code for getting the data. Code:
void updateCSV(void* stream) |
Re: Unexpected results from Encoder::GetRate()
Quote:
Allow me to do that here... If you select "ExternalDirection" mode, then the following overloads apply: "DownRisingEdge" specifies invert the direction or not. "DownFallingEdge => true" enables quadrature decoding mode. In quadrature decoding mode, "UpFallingEdge" selects 1x or 2x decoding and "UpRisingEdge" must be true... more literally, what these two mean in quadrature mode is: "UpRisingEdge => count A if B is high" "UpFallingEdge => count A if B is low" Obvious, right? Again sorry for the confusion (I realize you probably aren't any less confused at this point). Quote:
Count = (ARising | AFalling) & ((BHigh & UpRisingEdge) | (BLow & UpFallingEdge)) CountUp = Count & !(BHigh ^ DownRisingEdge ^ ARising) CountDown = Count & (BHigh ^ DownRisingEdge ^ ARising) With this, there will hopefully be no more mystery. :) See you in Atlanta! -Joe |
Re: Unexpected results from Encoder::GetRate()
Quote:
Question: What encoders did you use on left and right and what are the corresponding distances per pulse? The right encoder has a little less noise than the left. I did a statistical analysis of your data and will post it soon. If you still have the same set up and a little extra time, I'd love to see the 2x and 4x cases with the get rate averaged over 2 and 4 pulses respectively rather than the default single pulse. Please use the corresponding JAG speeds as in the original data sets. In case you aren't familiar with this: The 2x is set in the counter.cpp InitCounter() line : m_counter->writeTimerConfig_AverageSize(1, &status); by changing to m_counter->writeTimerConfig_AverageSize(2, &status); Similarly, the 4x is set in the encoder.cpp InitEncoder() line: m_encoder->writeTimerConfig_AverageSize(1, &status); by changing to m_encoder->writeTimerConfig_AverageSize(4, &status); This would help mitigate the rate noise if people insist on using 2x and 4x encoder modes. I would like this to be the default configurations. If you run the cases Ill add it to the statistical analysis and post it also. It would be a nice reference for future First teams making this decision. If you can't do it, I certainly understand. Thanks By the way, last year Joe H and I had a thread on moving average : http://forums.usfirst.org/showthread...8143#post28143. Our team added a function to the Encoder.cpp that allows you to do this in your robot init code similar to SetDistancePerPulse(). Eventually, I hope the WPI guys incorporate this capability. Code:
void SetMovingAveragePulseCount(int max_count) |
Re: Unexpected results from Encoder::GetRate()
Quote:
Quote:
Quote:
|
Re: Unexpected results from Encoder::GetRate()
Quote:
If you log in with a generic FIRST Forge user account, you can post bug reports. -Joe |
Re: Unexpected results from Encoder::GetRate()
Michael, I posted the rate statistics for segments of your data here.
http://www.chiefdelphi.com/forums/sh...519#post953519 Joe: Thanks for the info and good luck in Atlanta. Our factor of 2 GetPeriod() problem awaits your attention. |
Re: Unexpected results from Encoder::GetRate()
Quote:
Quote:
-Joe |
Re: Unexpected results from Encoder::GetRate()
Quote:
"DownRisingEdge => countup A if B is high" "DownFallingEdge => countdown A if B is low" Quote:
Lets try an example for 1x: UpRisingEdge = true , DownRisingEdge=false Assume B is static high so Bhigh=true Now A channel gets a rising pulse so ARising = true , AFalling= false Evaluate 1) Count = true; (BHigh ^ DownRisingEdge ^ ARising)=(true^false^true) = false; so CountUp = true & !(false) = true CountDown = true&false = false Hence we count up .... Now A channel gets a falling pulse so Arising = false, AFalling = true Evaluate 2) Count = true: (BHigh ^ DownRisingEdge ^ ARising)=(true^false^false) = true; so CountUp= true &!(true)=false CountDown = true&true = true Hence we count down .... This repeats so with a stuck B channel, it oscillates. In the case where B is not static , it inhibits the count when AFalling = true because Bhigh= false during that transition. So I guess I'm with you. Since this logic requires a transition to count 1x properly, it seems the code should throw a flag when the B is static since this is an invalid input. Otherwise why require it. |
Re: Unexpected results from Encoder::GetRate()
Quote:
Quote:
We shouldn't single out B being static... it's perfectly valid for the encoder to measure a change in direction as well as many changes in direction one after another. -Joe |
Re: Unexpected results from Encoder::GetRate()
Quote:
Quote:
To create a valid 1x decoder, you must be sensitive to a different edge of A based on the value of B, not always be sensitive to one edge of A and choose direction based on B. Naturally the correct way to implement this is not simple on a microcontroller because interrupt configuration cannot be tied to a digital input, so the mistake is often made because it is seen as the only option. The fact that it almost works only reinforces to the misunderstanding. In some cases is it a concious decision to use this optimization (which has half the interrupt traffic that would be required to implement it correctly) with the acceptance and acknowledgement that it will generate invalid counts any time the direction changes. -Joe |
Re: Unexpected results from Encoder::GetRate()
Quote:
Quote:
] |
Re: Unexpected results from Encoder::GetRate()
Quote:
That exact situation happened on our "Triple Play" robot. We had quadrature encoders on miniature omniwheel-ish rollers to track our motion on the carpet. We could watch the wheels not spin appreciably while the code told us they were making several revolutions. I fixed it by implementing a true quadrature decoder. Something almost exactly like my code was independently developed by Kevin Watson and incorporated into his Encoder.c library for the IFI controller. |
Re: Unexpected results from Encoder::GetRate()
Quote:
You make a good point Alan, regarding the sensitive nature of my scheme to vibration that can cause oscillation about an edge. It will certainly do what you said... but it is the price that must be paid if you want to use fewer interrupts. If this is not a constraint, then clearly the use of both rising edge and falling edges will help as you discovered. These are the mappings as I see them: my 1x scheme 1x , 1 interrupt per cycle, sensitive to edge oscillation, rate sensitive to rising edge phase errors of A channel A (rising) B (low) increment A (rising) B (high) decrement 2x , 2 interrupts per cycle, not sensitive to oscillations rate sensitive to rising and falling edge phase errors of A channel A (rising) B (low) increment A (falling) B (high) increment A (rising) B (high) decrement A (falling) B (low) decrement 4x , 4 interrupts per cycle, not sensitive to oscillations rate sensitive to rising and falling edge phase errors of both A and B channels A (rising) B (low) increment B (rising) A (high) increment A (falling) B (high) increment B (falling) A (low) increment A (rising) B (high) decrement B (rising) A (low) decrement A (falling) B (low) decrement B (falling) A (high) decrement Making a 1x insensitive to oscillations could be done a number of ways: One simple mapping is to enable count when B (low) and count when B(high) This requires B transitions for counting. So.. 1x, 2 interrupts per cycle, not sensitive to oscillations rate sensitive to rising edge phase errors of A channel A (rising) B (high) increment , reset enable A (falling) B (high) decrement, reset enable A (rising) B (low) enable count A (falling) B (low) enable count Ill have to go back and review how Kevin did his. Its been a while. |
Re: Unexpected results from Encoder::GetRate()
Quote:
Congrats 67/177/294 In the same mapping style, it translates to: A(falling) B(high)...................set Backward state A(falling) B(low) ...................set Forward state A(rising) B(low) Backward........decrement A(rising) B(high) Forward.........increment This works well to prevent edge oscillation error counts since it requires a B transition to count also. Kevin used the single interrupt scheme on the first two encoder channels and this two interrupt scheme on the three and four channels. He had the foresight to know that sometimes you need fewer interrupts with high velocity encoders and more interrupts with high precision distance encoders. Note to Joe H: For your 1x, I don't like your algorithm because the count oscillates when stuck with an oscillating A edge over a BHigh or Blow state. I believe this can really aggravate the GetRate() noise. Code:
Count = (ARising | AFalling) & ((BHigh & UpRisingEdge) | (BLow & UpFallingEdge))CountUp = BHigh&ARising CountDown = Bhigh&!Arising --> Bhigh&Afalling if counting. You count up or down on the A edge transition if Bhigh so you oscillate between +1 and -1 increments. My 1x non-oscillating scheme and Kevin's will not do this. Mine will allow a single proper count in the normal direction and no change on the oscillatory reversal. Kevin's will not allow any count until B changes sign. |
Re: Unexpected results from Encoder::GetRate()
Quote:
Because I'm using an FPGA, I can afford to implement the decoder correctly without regard for shortcuts in interrupt handling. The simple fact that your interrupt handler can't check the state of the B line at the exact instance that the A line edge is detected means that you have a race condition to count the wrong way anyway... and it's a pretty big hole with most interrupt handlers, especially on the PIC18F where you don't get hardware context saving and it's just generally a very slow processor. Without hardware support, you can't really trust the decoding in the face of noise. -Joe |
| All times are GMT -5. The time now is 12:29. |
Powered by vBulletin® Version 3.6.4
Copyright ©2000 - 2017, Jelsoft Enterprises Ltd.
Copyright © Chief Delphi