Execute Code at a Fixed-Rate
Introduction
By executing code at constant intervals, you can accurately time and schedule tasks.
Using a rateControl
object allows you to control the rate
of your code execution. These examples show different applications for the
rateControl
object including its uses with ROS and sending
commands for robot control.
Run Loop at Fixed Rate
Create a rate object that runs at 1 Hz.
r = rateControl(1);
Start a loop using the rateControl
object inside to control the loop execution. Reset the object prior to the loop execution to reset timer. Print the iteration and time elapsed.
reset(r) for i = 1:10 time = r.TotalElapsedTime; fprintf('Iteration: %d - Time Elapsed: %f\n',i,time) waitfor(r); end
Iteration: 1 - Time Elapsed: 0.001646 Iteration: 2 - Time Elapsed: 1.001156 Iteration: 3 - Time Elapsed: 2.000720 Iteration: 4 - Time Elapsed: 3.000994 Iteration: 5 - Time Elapsed: 4.001643 Iteration: 6 - Time Elapsed: 5.001109 Iteration: 7 - Time Elapsed: 6.001165 Iteration: 8 - Time Elapsed: 7.000602 Iteration: 9 - Time Elapsed: 8.000919 Iteration: 10 - Time Elapsed: 9.000195
Each iteration executes at a 1-second interval.
Overrun Actions for Fixed Rate Execution
The rateControl
object uses the OverrunAction
property to decide how to handle code that takes longer than the desired period to operate. The options are 'slip'
(default) or 'drop'
. This example shows how the OverrunAction
affects code execution.
Setup desired rate and loop time. slowFrames
is an array of times when the loop should be stalled longer than the desired rate.
desiredRate = 1; loopTime = 20; slowFrames = [3 7 12 18];
Create the Rate
object and specify the OverrunAction
property. 'slip'
indicates that the waitfor
function will return immediately if the time for LastPeriod
is greater than the DesiredRate
property.
rate = rateControl(desiredRate);
rate.OverrunAction = 'slip';
Reset Rate
object and begin loop. This loop will execute at the desired rate until the loop time is reached. When the TotalElapsedTime
reaches a slow frame time, it will stall for longer than the desired period.
reset(rate); while rate.TotalElapsedTime < loopTime if ~isempty(find(slowFrames == floor(rate.TotalElapsedTime))) pause(desiredRate + 0.1) end waitfor(rate); end
View statistics on the Rate
object. Notice the number of periods.
stats = statistics(rate)
stats = struct with fields:
Periods: [1.0072 0.9929 1.0008 1.1024 0.9997 0.9992 1.0000 1.1024 1.0004 0.9994 1.0004 0.9994 1.1015 1.0003 0.9994 1.0005 0.9994 1.0004 1.1046 0.9963]
NumPeriods: 20
AveragePeriod: 1.0203
StandardDeviation: 0.0423
NumOverruns: 4
Change the OverrunAction
to 'drop'
. 'drop'
indicates that the waitfor
function will return at the next time step, even if the LastPeriod
is greater than the DesiredRate
property. This effectively drops the iteration that was missed by the slower code execution.
rate.OverrunAction = 'drop';
Reset Rate
object and begin loop.
reset(rate); while rate.TotalElapsedTime < loopTime if ~isempty(find(slowFrames == floor(rate.TotalElapsedTime))) pause(1.1) end waitfor(rate); end stats2 = statistics(rate)
stats2 = struct with fields:
Periods: [1.0011 0.9999 0.9999 2.0006 0.9990 1.0001 2.0002 0.9995 1.0005 0.9993 2.0013 0.9994 0.9994 1.0004 1.0004 2.0005]
NumPeriods: 16
AveragePeriod: 1.2501
StandardDeviation: 0.4475
NumOverruns: 4
Using the 'drop'
over run action resulted in 16 periods when the 'slip'
resulted in 20 periods. This difference is because the 'slip'
did not wait until the next interval based on the desired rate. Essentially, using 'slip'
tries to keep the AveragePeriod
property as close to the desired rate. Using 'drop'
ensures the code will execute at an even interval relative to DesiredRate
with some iterations being skipped.
See Also
rateControl
| rosrate
(ROS Toolbox) | waitfor