Main Content

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.001234
Iteration: 2 - Time Elapsed: 1.000570
Iteration: 3 - Time Elapsed: 2.001055
Iteration: 4 - Time Elapsed: 3.000756
Iteration: 5 - Time Elapsed: 4.001403
Iteration: 6 - Time Elapsed: 5.000534
Iteration: 7 - Time Elapsed: 6.000781
Iteration: 8 - Time Elapsed: 7.000148
Iteration: 9 - Time Elapsed: 8.000565
Iteration: 10 - Time Elapsed: 9.001009

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.0013 0.9993 1.0002 1.1028 0.9988 1.0000 1.0004 1.1024 1.0003 0.9994 1.0004 0.9994 1.1021 0.9987 1.0003 1.0004 1.0004 0.9994 1.1015 0.9992]
           NumPeriods: 20
        AveragePeriod: 1.0203
    StandardDeviation: 0.0420
          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.0003 1.0008 1.0001 2.0022 0.9973 1.0000 2.0001 1.0004 0.9994 1.0004 2.0003 0.9994 1.0004 0.9994 1.0004 2.0003]
           NumPeriods: 16
        AveragePeriod: 1.2501
    StandardDeviation: 0.4476
          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

| (ROS Toolbox) |