Don Burnette

On the sub, all of our behavior services ran a syncrhonous loop acting as a state machine, moving from one state to the next. I wanted to avoid using timers for various reasons, so I went for a solution using a timeoutport as follows:

Code Snippet

_state.maxFrequency = 15;

...

public IEnumerator<ITask> RunBehavior(sub_arb.Run run)

{

if (_state.behaviorActive)

{

_state.isRunning = true;

/* Do behavior stuff */

/* Determine Behavior Frequency */

timer.Stop();

double frequency = 1 / timer.Duration;

/* Determine if we ran too fast and need to delay */

if (frequency > _state.maxFrequency)

{

int delay = (int)(1000 * (1 / _state.maxFrequency - 1 / frequency));

Activate(

Arbiter.Receive(false, TimeoutPort(delay),

delegate(DateTime time)

{

timer.Stop();

//Console.WriteLine("Refresh Frequency: " + 1 / timer.Duration);

timer.Start();

_mainPort.Run(new sub_arb.RunRequest());

}

)

);

}

else

{

timer.Stop();

//Console.WriteLine("Refresh Frequency1: " + 1 / timer.Duration);

timer.Start();

_mainPort.Run(new sub_arb.RunRequest());

}

}

else

{

_state.isRunning = false;

}

yield break;

}

So if the loop runs too fast, I calculate the number of msec I would have to wait in order to meet the desired run speed, and set a timeoutport for that many msec, and the loop is then restarted.

This works ok for values around 5Hz or 10Hz, at 20Hz it works sometimes. But if I want to run a synchronous loop around 40Hz, it's very inconsistent, sometimes I will get a perfect 25ms loop time, but it jumps around sometimes taking up to 35 or 40ms.

It seems that when the loop slows down, it's because the Timeoutport is waiting for some other code to execute before it gets the priority, thus making it slow. Also, is there some overhead time associated with setting up the Arbiter.Receive for this case

I'm more concerned with consistency than with accuracy. If I specificy 40Hz and I only get 30Hz, that's not as bad as getting a range from 35-45Hz at random.

Advice

Thanks,

Don



Re: Microsoft Robotics - Decentralized Software Services (DSS) How To Create A Timed Loop

George Chrysanthakopoulos

The TimeoutPort helper just uses the DispatcherQueue.EnqueueTimer api of the CCR. That in turn, uses the System.Timer object to sequence things and post a DateTime on a port, when the timer fires.

1) The underlying CLR timer is not very accurate. 15ms at best is what you get, worse depending on thread usage overall

2) the ccr adding and removing receivers is only a few hundred nano seconds so thats not the issue

3) if you share dispatcher with all other services however (which is the default), this might be causing the delay, due to the roundrobin behavior we have at the CCR level between all services

to eliminate 3, use the [ActivationSettings] attribute on your service, use the named parameters (Sharing = false, ExecutionUnits = 0 or 1 if you want one thread per core, or just 1)

This will guarantee your tasks never get queued/compete with other service tasks. This usually is ok if the load on the system is avg/small but on some robots, you really do need your own dispatcher for some high cycle rate services (we can do up to 50K msgs per sec, between services, but that is if they are all well behaved)

g