Chief Delphi

Chief Delphi (http://www.chiefdelphi.com/forums/index.php)
-   C/C++ (http://www.chiefdelphi.com/forums/forumdisplay.php?f=183)
-   -   Unexpected results from Encoder::GetRate() (http://www.chiefdelphi.com/forums/showthread.php?t=82171)

mikets 06-04-2010 06:07

Re: Unexpected results from Encoder::GetRate()
 
Quote:

Originally Posted by vamfun (Post 949026)
We integrated dt = 1/(Update_Rate) and checked that the computed time matched real time so this led us to the double encoder GetRate() error which we verified independently using motor rpm measurements.

But what is Update_Rate? If it's derived from the period of some periodic loop, you need to verify the real interval of the periodic loop. How did you compute real time? I use the following code to measure my periodic intervals:
Code:

UINT32 timeCurr = GetFPGATime(); // in usec
float period = (float)(timeCurr - m_timestamp)/1000000.0;
m_timestamp = timeCurr;


vamfun 06-04-2010 16:17

Re: Unexpected results from Encoder::GetRate()
 
Quote:

Originally Posted by mikets (Post 949047)
But what is Update_Rate? If it's derived from the period of some periodic loop, you need to verify the real interval of the periodic loop. How did you compute real time?

Update_Rate is a define... say 100. hz.
dt = 1/Update_rate;

We use iterative class SetPeriod(dt);

Then we measure cumulative time rather than loop period:
We put this in the loop : time = time + dt :
and simply compare time print out with a stop watch. Since we were looking for a factor of 2 this was accurate enough. We easily checked within a few %.

mikets 06-04-2010 17:31

Re: Unexpected results from Encoder::GetRate()
 
I am sorry. I am probably not helping with your particular problem. I am merely trying to point out that in the iterative robot class, don't count on the period being accurate and use it as dt for differentiation or integration. You said your Update_Rate is 100 Hz. That makes the period 10 ms. If you put "time = time + 1/Update_Rate" in your loop, your time is really summing a constant 10 ms on every loop period, not real time. If you do this instead:
Code:

class MyRobot: public IterativeRobot
{
private:
    UINT32 m_timestamp;
    ....

public:
    void AutonomousInit()
    {
        m_timestamp = GetFPGATime();
        ....
    }
    void AutonomousPeriodic()
    {
        UINT32 timeCurr = GetFPGATime(); // in usec
        float period = (float)(timeCurr - m_timestamp)/1000000.0;
        m_timestamp = timeCurr;
        printf("loop period is %f\n", period);
        ...
    }
};

"period" will be the accurate loop interval that can be used as dt in your differentiation or integration. In theory period should print out 0.01s (10 ms), but in our case, it is way off. It could be because our loop execution took longer than the 10ms to execute that may cause a period to be skipped in some cases. Therefore, as a good practice, never use the period you set in any time critical calculations.

vamfun 06-04-2010 18:40

Re: Unexpected results from Encoder::GetRate()
 
Quote:

Originally Posted by mikets (Post 949348)
In theory period should print out 0.01s (10 ms), but in our case, it is way off. It could be because our loop execution took longer than the 10ms to execute that may cause a period to be skipped in some cases.

I've done a lot of real time control systems and never had a problem with interrupt driven loops because we always verify that execution cannot exceed the loop period. So I suspect you have a problem delay in your loop and indeed if you are using a debug console printf(...) in a 10 ms loop then this can easily cause you a problem since it requires about 1ms per char and once you exceed the buffer, this becomes a realized delay.



Quote:

Therefore, as a good practice, never use the period you set in any time critical calculations
... unless you have verified your computations can't exceed the loop time:)

Edit: Actually this is a design issue rather than good practice. Either you design it as a periodic loop or not. Often programmers don't understand the hardware fully and run into these types of problems and wind up adding the type of compensation programming that you are suggesting. In fact, IMHO, when things are time critical, the periodic loop is often preferred since it is more predictable than non periodic solutions.

vamfun 09-04-2010 00:53

Re: Unexpected results from Encoder::GetRate()
 
Ok, back to Encoder Problem:
I got hold of a CRIO finally and ran some encoder outputs while we bench drove the robot. I set the iterative loop rate to 10 hz and printed to the console :
printf( ..... ", GetDistance(), GetRate(), Derived rate) where derived rate was the avg rate over the 100ms period.

Ran full forward, full reverse, partial forward, partial reverse cases for 1x,2x,4x.

Data can be downloaded from
http://www.scribd.com/doc/29635300/5...er-4-7-09-Test
I recommend downloading and viewing the plots since they are cutoff on scrid site.

For some reason the encoder counts were about 1/5.7 of what they should have been... ie the avg derived rate was 1.2 fps rather than 6.8 fps. So the encoder might be broken but it did register linear GetDistance().
Aside from this, the GetRate() was consistent for 1x,2x,4x and showed plots with basically two interwoven levels. One close to the derived rate of 1.2 fps and the other around 12 fps when running max speed. So there is definitely something odd for all modes.

So, today, I tried some more direct debug of encoder.cpp. I disconnected the encoder inputs and replaced them with a periodic pulse on the A channel and open (pull up value) on the B channel. We figured we could manually cause the counters to increment by alternating between open and ground on the A input. Well, something really weird happened. The 0 input caused a 1 count and the 1 input caused the count to decrement to 0 count again. Huh?? So the GetRaw() just alternated between 1 and 0 rather than accumulate.

So must be some good explaination... right?

Maybe more on Monday.

Joe Ross 09-04-2010 01:02

Re: Unexpected results from Encoder::GetRate()
 
Quote:

Originally Posted by vamfun (Post 950605)
So, today, I tried some more direct debug of encoder.cpp. I disconnected the encoder inputs and replaced them with a periodic pulse on the A channel and open (pull up value) on the B channel. We figured we could manually cause the counters to increment by alternating between open and ground on the A input. Well, something really weird happened. The 0 input cause a 1 count and the 1 input caused the count to decrement rather than accumulate. Huh?? So the GetRaw() just alternated between 1 and 0.

That would be expected for quadrature decoding. With the B channel stuck at a value, it looks like it's constantly changing directions.

vamfun 09-04-2010 01:26

Re: Unexpected results from Encoder::GetRate()
 
Quote:

Originally Posted by Joe Ross (Post 950606)
That would be expected for quadrature decoding. With the B channel stuck at a value, it looks like it's constantly changing directions.

Joe, I would expect that for 2x and 4x but not 1x, since with 1x only A channel leading edge determines the count and the B channel is always the same value on the leading edge. Unless I'm missing something.

Jared Russell 09-04-2010 07:52

Re: Unexpected results from Encoder::GetRate()
 
Quote:

Originally Posted by vamfun (Post 950613)
Joe, I would expect that for 2x and 4x but not 1x, since with 1x only A channel leading edge determines the count and the B channel is always the same value on the leading edge. Unless I'm missing something.

There are many ways that one could implement a 1x quadrature decoder.

For a few reasons, a 1x decoder that requires a transition on both phases prior to incrementing the count is advantageous (namely, it can discriminate between the constantly changing direction vs. constant direction case).

vamfun 09-04-2010 17:12

Re: Unexpected results from Encoder::GetRate()
 
Quote:

Originally Posted by Jared341 (Post 950645)
There are many ways that one could implement a 1x quadrature decoder.

For a few reasons, a 1x decoder that requires a transition on both phases prior to incrementing the count is advantageous (namely, it can discriminate between the constantly changing direction vs. constant direction case).

Ok, lets get specific. When we write our own interrupt encoder routines, the B channel is just looked at when a TEU pulse occurs on the A chan to get the direction. However, the tcounters in CRIO are a bit of a mystery to me still and I need some more tutoring.

Here, the upSource is A chan and the downSource is B chan. This is called by encoder.cpp.

Counter::Counter(EncodingType encodingType, DigitalSource *upSource, DigitalSource *downSource, bool inverted)
{
wpi_assert(encodingType == k1X || encodingType == k2X);
InitCounter(kExternalDirection);
SetUpSource(upSource);
SetDownSource(downSource);

if (encodingType == k1X)
SetUpSourceEdge(true, false); // THis is clear to me
else
SetUpSourceEdge(true, true); // This is clear to me

SetDownSourceEdge(inverted, true); // This needs explanation
}


As I understand it, the tcounters have independent sources than can be set to count up or down. How does setting the B chan to always count down on a TED and down on TEU if inverted=true set up the counter to give the proper direction?

Bigcheese 10-04-2010 01:56

Re: Unexpected results from Encoder::GetRate()
 
Quote:

Originally Posted by vamfun (Post 950605)
For some reason the encoder counts were about 1/5.7 of what they should have been... ie the avg derived rate was 1.2 fps rather than 6.8 fps. So the encoder might be broken but it did register linear GetDistance().
Aside from this, the GetRate() was consistent for 1x,2x,4x and showed plots with basically two interwoven levels. One close to the derived rate of 1.2 fps and the other around 12 fps when running max speed. So there is definitely something odd for all modes.

That's definitely not what I get. How do you get a negative value when going full forward?

Here's the data I got for a bump test I did about a week ago. The setup is really weird because we were rushing to get it before we had to leave for a week (spring break). There are two different encoders on the left and right drive trains (I don't remember which is which at the moment). We set the robot on its butt and set the jags to output 0.6, waited for 2 seconds, and then set them back to 0.

The code is set to 2x decoding and a 0.005 period (200 HZ) between every sample.

https://docs.google.com/leaf?id=0B3I...NTFjM2Iz&hl=en

Again, I don't know exactly what value should be returned, but obviously there is a difference in what our GetRate()s are returning.

I should have access to the bot tomorrow, so I'll try adding a plot of the derived rate.

vamfun 10-04-2010 03:16

Re: Unexpected results from Encoder::GetRate()
 
Quote:

Originally Posted by Bigcheese (Post 951452)
That's definitely not what I get. How do you get a negative value when going full forward?

Here's the data I got for a bump test I did about a week ago.
Again, I don't know exactly what value should be returned, but obviously there is a difference in what our GetRate()s are returning.

I should have access to the bot tomorrow, so I'll try adding a plot of the derived rate.

Well, this data looks better... I suspect we had a bad encoder or maybe hardware problem.

Only thing clear is a factor of 2 difference in your data so far.

What are the units?
How did you acquire data?
Can you also print GetDistance or GetRaw?
Was SetDistancePerPulse checked for each encoder?
Can you run a 1x case if you have time?

Bigcheese 10-04-2010 11:07

Re: Unexpected results from Encoder::GetRate()
 
Quote:

Originally Posted by vamfun (Post 951461)
Only thing clear is a factor of 2 difference in your data so far.

The factor of two difference between the encoders is because one is about twice the pulses per rev as the other.

Quote:

Originally Posted by vamfun (Post 951461)
What are the units?
How did you acquire data?
Can you also print GetDistance or GetRaw?
Was SetDistancePerPulse checked for each encoder?
Can you run a 1x case if you have time?

  • The units are in feet per second.
  • Using a notifier that printed GetFPGATime(),GetJagOutput(),GetLeftRate(),GetRigh tRate() at 200Hz
  • Hopefully today.
  • The same value was used for both and was correct for one of them.
  • I think I have one, but I'm not sure which it was (we were rushing to see the data, didn't mark any of it). The data was almost identical except for a little less noise. I'll run it again though.

I will do this today if I can get into the school.

Bigcheese 11-04-2010 21:16

Re: Unexpected results from Encoder::GetRate()
 
Ok, I finally have good data. First, I edited Encoder.cpp, here's the diff.

Code:

Index: Encoder.cpp
===================================================================
--- Encoder.cpp        (revision 2130)
+++ Encoder.cpp        (working copy)
@@ -258,7 +258,7 @@
 {
        if (m_counter)
        {
-                return m_counter->GetPeriod() * DecodingScaleFactor();
+                return m_counter->GetPeriod() / DecodingScaleFactor();
        }
        else
        {
@@ -275,7 +275,7 @@
                        value = (double)output.Period / (double)output.Count;
                }
                wpi_assertCleanStatus(status);
-                return value * 1.0e-6  / (DecodingScaleFactor() * 4.0);
+                return value * 1.0e-6 / DecodingScaleFactor();
        }
 }
 
@@ -380,7 +380,7 @@
  */
 double Encoder::GetRate()
 {
-        return m_distancePerPulse / GetPeriod() * (GetDirection() ? 1.0 : -1.0);
+        return (m_distancePerPulse / GetPeriod() * (GetDirection() ? 1.0 : -1.0)) / 2;
 }
 
 /**

The following data is in 1x and 2x mode. I didn't do a 4x test.

https://spreadsheets.google.com/ccc?...0JZe UE&hl=en

Looks perfect to me :)

vamfun 12-04-2010 04:15

Re: Unexpected results from Encoder::GetRate()
 
Quote:

Originally Posted by Bigcheese (Post 952416)
The following data is in 1x and 2x mode. I didn't do a 4x test.

https://spreadsheets.google.com/ccc?...0JZe UE&hl=en

Looks perfect to me :)

Thanks for running a nice data set. So this is what I get from it:
1) Confirmed that the 1x is off by factor of 2.
2) Confirmed that 2x is off by same factor of 2 if corrected for inverted Decoding scale factor error. 2x rate exhibits more noise as expected.
3) We have not explained why the counter GetPeriod() is off by 2.
4)TBD for 4x. If the problem is in the counter GetPeriod() we might expect that the factor of 2 used in GetRate() will not be correct for 4x since it uses the FPGA. So this needs confirmation.

The next step is to find out where the counter period problem is. This is where the factor of 2 should be fixed rather than in the GetRate() function. In the counter.cpp
period = (double)output.Period / (double)output.Count;
So either output.Period is wrong or output.Count is defaulting to 2 instead of 1. I suspect the latter and will try to check this today.

Also still waiting for Joe or other expert to explain how B channel works to set count direction in WPI libs .
post http://www.chiefdelphi.com/forums/sh...3&postcount=24
Edit: Looking at the counter constructor for the quad encoder...seems that if
InitCounter(kExternalDirection);
is present then
SetDownSourceEdge(inverted, true);
has a special meaning. It sets direction externally with chan B rather than actually change the down count with chan B edges as the name implies. If this is the case, I really have the urge to yell at an overworked WPI programmer for this style of coding.

Mike: can you post a snippit of your notifier code and tell us how the printf data was sent to the console? We might try that tomorrow.

vamfun 12-04-2010 21:14

Re: Unexpected results from Encoder::GetRate()
 
Quote:

Originally Posted by vamfun (Post 952580)
Thanks for running a nice data set. So this is what
The next step is to find out where the counter period problem is. This is where the factor of 2 should be fixed rather than in the GetRate() function. In the counter.cpp
period = (double)output.Period / (double)output.Count;
So either output.Period is wrong or output.Count is defaulting to 2 instead of 1. I suspect the latter and will try to check this today.

Todays runs showed that output.Count = 1 as it should.... so output.Period is the remaining suspect.


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