The DCM stores in a buffer all timed commands of each Actuator. At each DCM cycle it will analyze the previous and next order (if there are some) based on the current time and compute the appropriate command to send using a linear interpolation.
Previous commands are deleted after their use.
Currently, there is a limit of 512 timed commands stored in the DCM for each Actuator.
For suitable Actuator and values (joints position, stiffness, leds ...), the DCM automatically generates a linear approximation between the previous command and the next one (if there is one). This approximation is computed every DCM cycle and sent to the appropriate device.
As the command time does not fit the update time of the DCM, values asked could not be reached precisely (see example below), but they will follow by the evolution.
If there is no other order, the last one is kept and sent again to the actuator all next DCM cycle.
For some Actuator, there is no interpolation possible. For them, the previous command is sent and then deleted. A future command has no effect until the time is reached.
The main interests of timed command are:
It’s also possible not to use the date and linear interpolation of the DCM The main interest is if you want to update actuators orders at the D.C.M cycle speed (it’s better with a real time synchronisation). Not to use the interpolation, just send order immediately (with date = current date). At the next DCM cycle, the order will be applied immediately.
Here is a description of the interpolation algorithm.
For each Actuator, the following data are kept in memory:
At each DCM cycle, for each Actuator, the following algorithm is run:
continue = True
while (continue is True and commands is not empty):
# get the next comment (they are sorted)
command = commands[0]
if (command.time >= currentTime):
# the command is right now or in the future
# after this command, we will not process the commands list further
continue = False
if (command.time =< currentTime):
# the command is in the past
# store the command data for applying it and/or for
# future interpolations
sendCommandDouble = command.value
lastCommandValue = command.value
lastCommandTime = command.time
# remove this timed command from the list
commands.pop(0)
else:
# the command is in the future
# choose the starting point for the interpolation
if (lastCycleTime < lastCommandTime):
tmpTime = lastCommandTime
tmpCommand = lastCommandValue
else:
tmpTime = lastCycleTime
tmpCommand = lastCommandDouble
# compute the interpolation
dT1 = currentTime - varLastTime
dT2 = command.time - varLastTime
sendCommandDouble = ((command.value - tmpCommand)*dT1)/dT2 + tmpCommand
sendCommandInt = round(lastCommandDouble)
lastCycleTime = currentTime
actuator.set(sendCommandInt) # send the command to the actuator
Warning
Sending 2 or more timed commands at exactly the same date (in ms) with different values will result in unpredictable result.
We suppose that there is a DCM cycle every 10ms.
The command 1 was sent to (10,10). (first number is time, second is value, like degrees for joints).
At t=30ms (or between 20 and 30) the DCM received another command at (80,40).
Here is the computation of the next cycles:
t (ms) | dT1 (ms) | dT2 (ms) | sendCommandDouble |
---|---|---|---|
30 | 30 - 20 = 10 | 80 - 20 = 60 | (40 -10)*10)/60 + 10 = 15 |
40 | 40 - 30 = 10 | 80 - 30 = 50 | ((40 -15)*10)/50 + 15 = 20 |
50 | 50 - 40 = 10 | 80 - 40 = 40 | ((40 -20)*10)/40 + 20 = 25 |
60 | 60 - 50 = 10 | 80 - 50 = 30 | ((40 -25)*10)/30 + 25 = 30 |
70 | 70 - 60 = 10 | 80 - 60 = 20 | ((40 -30)*10)/20 + 30 = 35 |
80 | 80 - 70 = 10 | 80 - 70 = 10 | ((40 -35)*10)/10 + 35 = 40 |
Then the value stays at 40 for the next cycles, until another command is sent.
We suppose that there is a DCM cycle every 10ms.
At t=10ms (or between 0 and 10), the DCM received 4 commands for this actuator: (15, 10) (25, 30) (45,20) and (65, 0).
Here is the computation of the next cycles:
t (ms) | dT1 (ms) | dT2 (ms) | sendCommandDouble |
---|---|---|---|
10 | 10 - 0 = 10 | 15 - 0 = 15 | ((10 -0)*10)/15 + 0 = 6.66 |
20 | 20 - 15 = 5 | 25 - 15 = 10 | ((30 - 10)*5)/10 + 10 = 20 |
30 | 30 - 25 = 5 | 45 - 25 = 20 | ((20 - 30)*5)/20 + 30 = 27.5 |
40 | 40 - 30 = 10 | 45 - 30 = 15 | ((20 - 27.5)*10)/15 + 27.5 = 22.5 |
50 | 50 - 45 = 5 | 65 - 45 = 20 | ((0 - 20)*5)/20 + 20 = 15 |
60 | 60 - 50 = 10 | 65 - 50 = 15 | ((0 - 15)*10)/15 + 15 = 5 |
70 and above | 0 |
Warning
These callbacks are dangerous. Use them with care and only if you are comfortable with real-time, multithread, and precise timing.
The DCM provides a simple way to synchronize to its real time thread.
You can define a callback function called by the DCM just after its sleep (PreProcess), or just before going back to sleep (PostProcess).
onPreProcess is useful because it’s called just before the computation of orders sent to the chestboard (USB). Sending commands at this level means that you have the shortest delay to your command.
onPostProcess is called just after all value are updated in the ALMemory, and so at this time you have the newest values of all sensors.
These two callbacks can’t send/receive information, you still need to use the DCM API to send commands, and read the ALMemory to get sensors values.
Being called by the DCM thread means that your function is now real time. To keep the real time efficient and to avoid delay in the DCM cycle, you must:
Note
You can find an example of callback in the fastgetsetexample folder in the sdk.