Initial spikey encoder results - rpm plotted against time |
The first thing I do is sit down and think of what might be causing these inaccuracies. I'm not convinced it's a sensor issue, and the Arduino is easily fast enough to keep up. After some pondering, the first few potential culprits I come up with are:
- If the sensors are not out of phase by exactly 1/4 of a wave length then I'd expect readings to alternate between slightly too fast, then slightly too slow every interrupt. They should however average out, making every 2nd interrupt (half a wave) or 4th interrupt (a whole wave) right on the money. Note: not worked out which one yet, but the key is, it'd be very very regular :).
- The wheel is a little wobbly, which means it's getting closer to and farther from the sensors each revolution. If the sensors are perfectly aligned this wouldn't be a problem, however if they were at a slight angle it could turn the square wave into a slightly sin-waveish thing. I'd expect to see fairly regular inaccuracies in a pattern that repeats once per revolution (every 32 interrupts) if this was the issue.
- It could be the time difference between when my interrupt triggers to tick the encoder counter, and when I actually do my RPM calculations (every x ms) in the main loop. If this were the issue I'd expect to see fairly random inaccuracies within a threshold of roughly 5ms (the time it takes for a single encoder tick at 360rpm)
To track down the culprit, I print out rpm calculations every 50ms, then every 150ms, then every 250ms, then every 1000ms and plot them against encoder position:
Longer gaps will inevitably give flatter lines as they allow more interrupts to pass in between readings, so the data will naturally be smoothed. However the difference in patterns between the graphs should still indicate which of the above problems (if any) are the issue. As you can see, regardless of interval time, we get extremely spikey results with no discernable pattern. Before getting clever it occurs to me that I'm measuring times in milliseconds, but a single encoder tick can take around 5ms - this gives me at best a 20% error margin. I make a very simple change so the code does calculations at a microsecond level of accuracy instead, and look what happens:
Wow! What a difference. I'm still getting spikey results, but there's clearly a median line for each interval time, and the errors that bounce either side of it are extremely regular.
Now I'm convinced I'm not losing data due to accuracy (as even the errors are predictable!) I switch to running at constant 150ms intervals, printing out the rpm using 4 different calculations:
I make 2 changes to achieve this. First, my interrupt function now records the time at which it took it's last reading:
Then I update the loop function as follows:
Printing out the results and plotting against time, I now get the following graph:
The purple line contains the fully compensated data and look how flat it is! I decide that given this is a prototype (in MmBot V2 I'll use motors with built in encoders) this is good enough for now. So, as one final step, I attach the encoder to the inside of MmBot as you can see here:
Encoder results at different intervals - still very spikey |
Encoder results using microsecond accuracy instead of millsecond |
Now I'm convinced I'm not losing data due to accuracy (as even the errors are predictable!) I switch to running at constant 150ms intervals, printing out the rpm using 4 different calculations:
- No error compensation
- Compensation for time difference between interrupt and calculation time
- Compensation for out of phase sensors
- Combined both settings above
I make 2 changes to achieve this. First, my interrupt function now records the time at which it took it's last reading:
void updateEncoder()
{
encoder_time = micros(); //record time in microseconds of reading
byte new_encoder = readEncoder();
int dir = QEM[current_encoder*4+new_encoder];
encoder_pos += dir;
current_encoder = new_encoder;
}
Then I update the loop function as follows:
//record current time, the current encoder position, and the time the last encoder reading occured
long new_time = micros();
long new_encoder_pos = encoder_pos;
long new_encoder_time = encoder_time;
//calculate rpm with no compensation
current_rpm_nocomp = calcrpm(last_time, new_time, last_encoder_pos, new_encoder_pos);
//calculate rpm using time compensation (i.e. we use the last encoder time rather than current time in calculations)
current_rpm_timecomp = calcrpm(last_encoder_time, new_encoder_time, last_encoder_pos, new_encoder_pos);
//calculate rpm, only updating if it's an even numbered reading
current_rpm_evencomp = (new_encoder_pos & 1) ? current_rpm_evencomp : calcrpm(last_time, new_time, last_encoder_pos, new_encoder_pos);
//calculate rpm if even numbered reading, using time compensation
current_rpm_allcomp = (new_encoder_pos & 1) ? current_rpm_allcomp : calcrpm(last_encoder_time, new_encoder_time, last_encoder_pos, new_encoder_pos);
//record last readings to use in next calculations
last_time = new_time;
last_encoder_pos = new_encoder_pos;
last_encoder_time = new_encoder_time;
Printing out the results and plotting against time, I now get the following graph:
Different approaches to error compensation for the encoder |
Black/white wheel disk on inside of wheel, and 2 IR reflectivity sensors facing it to make a quadrature encoder |
Encoder position plotted against time (in microseconds) with motor turning on/off at regular intervals |
That's it for now - next up I'll properly attach both encoders, connect them to the Arduino Mega inside MmBot, wire up my Sabre Tooth motor controller and get some real navigation going!
ULANG TAHUN BOLAVITA YANG KE 6
ReplyDeleteSehubungan dengan ulang tahun Bolavita yg ke-6
Bolavita sebagai Agen Taruhan Judi Online terbaik dan terpercaya akan menbagikan FREEBET & FREECHIP kepada member setia bola vita.
FREEBET & FREECHIP hingga 2.000.000 (Dua juta rupiah)
syarat dan ketentuan cek = https://bit.ly/2SsDinv
Gabung sekarang dan klaim bonusnya!
Info Lengkap :
WA: 0812-2222-995 !
BBM : BOLAVITA
WeChat : BOLAVITA
Line : cs_bolavita
Info Togel =>> PREDIKSI TOGEL TERPERCAYA
Agen Taruhan Judi Online