MotionPlayer

MotionPlayer replays recorded motion from files on disk.
3.30–3.34

MotionPlayer replays recorded motion from files on disk. It reads a folder of profile files at startup, keeps them in memory, and streams one row at a time onto a fifty-wide output. Play, pause, stop and reload are commanded from the tree.

Use it to replay a taught motion, a test sequence or a recorded trajectory.

flowchart LR
    i1(["command"]) --> B["MotionPlayer"]
    i2(["timeScaleFactor"]) --> B
    p1(["profileID"]) --> B
    B --> o1(["output — 50 columns"])
    B --> o2(["state"])
    B --> o3(["error"])
    B --> o4(["activeProfileID"])
    B --> o5(["activeProfileTimestamp"])
    B --> o6(["activeProfileEndTime"])
    B --> o7(["activeProfileEnding"])
    B --> o8(["fractionComplete"])
    B --> o9(["numberOfProfiles"])

The output is never interpolated. It holds each file row until the next one is due, so the output is a staircase whose step size is set entirely by how finely the file was recorded. This is the single most important thing to know about the block.

Two behaviours will surprise you. Both are by design.

Stopping rewinds. Pausing holds. A stopped player drives its output back to the profile’s first row and keeps it there.

When a non-looping profile ends, the output jumps back to the first row, one cycle after it completes. If the file does not end where it starts, that is a step of the whole profile range on your output. Make the file end where it begins, or stop using its output before it finishes.

File format

Files live in a folder chosen when the controller is built, and are read once at startup.

  • The filename must begin with a number followed by an underscore — 00_home.csv, 12_pick.csv. That number is the profileID.
  • Lines beginning with # are comments.
  • A line reading loop = 1 makes the profile repeat; loop = 0, or no such line, plays it once.
  • Every other line is one row: the first number is the time in seconds, and the rest are the data columns, separated by spaces or tabs.
  • Times start at zero and must increase down the file. This is not checked — a file whose time column goes backwards will skip rows.
  • At most 50 data columns. Extra columns are dropped silently, and unused columns read 0.
  • Avoid blank lines. Each one adds an invisible all-zero row.

Signals

Inputs

Path Unit Range Description
command - 0 to 4 0 none, 1 play, 2 pause, 3 stop, 4 reload. Write the number; the block clears it back to 0 once acted on, so it is a pulse, not a level.
timeScaleFactor - above 0 Multiplies the playback speed. 1 is normal, 0.5 is half speed. Not checked. 0 freezes playback; a negative value stalls it rather than rewinding; a large value skips rows.

Outputs

Path Unit Description
output file unit The 50 columns of the row being played. Columns your file does not have read 0.
state - 0 stopped, 1 playing, 2 paused, 3 finished, 4 and 5 reloading.
error - 0 none, 1 no profile loaded, 2 the requested id does not exist.
activeProfileID - The profile actually loaded. -1 means none — check this first when nothing plays.
activeProfileTimestamp s The file timestamp of the current row, not the elapsed time. It advances in jumps, one per row.
activeProfileEndTime s The last timestamp in the loaded file — how long the profile runs at normal speed.
activeProfileEnding - 0 the profile plays once, 1 it loops. Read from the file’s loop = line.
fractionComplete - How far through the profile, 0 to 1. Reads 1.0 when no profile is loaded, so read activeProfileID alongside it.
numberOfProfiles - How many files loaded successfully. 0 means the folder was missing, empty, or every file was rejected.

Parameters

Path Unit Default Range Effect
profileID - 0 0 upward Which profile to play next. Changing it while a profile is playing does nothing until that profile ends — which is how profiles are sequenced back to back. If no file has this id, error reads 2 and activeProfileID reads -1.

profileID is persistent and survives a controller restart, so the player comes back with the same profile selected. No parameters exist below this block, and the folder the files are read from is not visible in the tree.

Setup

  1. Put your profile files in the player’s folder, named <number>_<name>.csv.

  2. Start the controller and read numberOfProfiles. It should match the number of files you provided. A 0 here means the folder was not found — nothing else in the tree will tell you that.

  3. Read activeProfileID. It should be 0 at startup, or -1 if no file has id 0.

  4. Read activeProfileEndTime and check it against the last timestamp in your file. A mismatch means the file did not parse as you expected.

  5. Set profileID to the profile you want and confirm activeProfileID follows it and error reads 0.

  6. Write 1 to command to play.

    Step 6 moves the machine, immediately and at whatever rate the file was recorded. Confirm the file’s first row matches where the machine already is, or the first output will be a step.

  7. Watch fractionComplete climb to 1 and state go to 0 or 3.

Tuning

  1. Check that the file’s first row matches the machine’s resting position. The player makes no attempt to blend into the profile.

  2. Check that a non-looping file ends where it starts, or arrange for its output to be ignored once fractionComplete reaches 1. Otherwise the snap back to row 0 is a step.

  3. Read activeProfileEndTime and divide it by the number of rows to get the average row interval. That interval is your output’s step size.

  4. If the machine is rough during playback, the file is too sparsely sampled. Re-record it more finely, or filter the output downstream.

  5. Use timeScaleFactor to slow a profile down while commissioning. Halving it halves the speed and doubles the duration.

  6. Sequence profiles by writing the next profileID while the current one is still playing. The switch happens exactly at the end, with no gap.

  7. After changing files on disk, write 4 to command to reload. Wait for state to leave 4 and 5 before playing.

    Reloading while a profile is selected is not safe in this version. Stop the player, set profileID to a different value and back again after the reload completes, so the profile is re-resolved.

The same motion recorded at three row spacings. Coarse rows produce visiblesteps in the output; fine rows produce a smooth curve. The block holds each rowand never interpolates between them.

Read your file’s step size off the height of the treads.

Symptom Cause Action
numberOfProfiles is 0 The folder is missing, empty, or every file was rejected Check the controller log — every rejected file is logged by name
A file was not loaded Its name does not start with a number and an underscore, or another file already claims that id Rename it. The log message mentions an “id: tag”, which is wrong — it is the filename that carries the id
activeProfileID reads -1 No file has the requested profileID Set profileID to an id that exists; error reads 2
fractionComplete reads 1.0 and nothing plays No profile is loaded Check activeProfileID — 1.0 here means “nothing”, not “finished”
The output stepped at the start of playback The file’s first row does not match where the machine is Re-record the file from the resting position
The output stepped at the end of playback Expected: a finished non-looping profile snaps back to row 0 End the file where it starts, or ignore the output past fractionComplete 1
Stopping caused a jump Expected: stop rewinds to row 0 Use pause, which holds
The output is a visible staircase The file’s rows are too far apart, and the block never interpolates Re-record more finely, or filter downstream
The motion is jerky at the loop point The file’s last row does not match its first Make them match
Playback froze timeScaleFactor is 0 or negative Set it above 0
Rows appear to be skipped timeScaleFactor is large, or the file’s time column is not increasing Check both. Non-monotonic times are not validated
Some columns read 0 The file has fewer than 50 columns Expected
Columns past the 50th are missing Only 50 are carried Split the profile across two players
A profileID change had no effect Expected: it is deferred until the current profile ends Stop the player first if you need it immediately
The player misbehaved after a reload Reloading while a profile is selected is not safe in this version Follow the callout in Tuning step 7
A command write disappeared Expected: the block clears it once acted on Read state to confirm the command took

A starting point:

profileID       = 0
timeScaleFactor = 1.0
command         = 1     (play)

Limits and errors

Limit Set by What happens Reported
Folder Fixed at build time Read once at startup. Not visible in the tree. A missing folder is logged and leaves nothing loaded numberOfProfiles reads 0
File names Fixed Must be <number>_<name>.csv. Others are logged and skipped Controller log only
Duplicate ids Checked The first file wins; the second is logged and skipped Controller log only
Data columns 50 Extra columns are dropped silently; missing ones read 0 Not reported
Row timestamps Nothing Must increase down the file. Not validated — a backwards step makes rows be skipped Not reported
Blank lines Nothing Each one adds an all-zero row Not reported
Interpolation Fixed None. Each row is held until the next is due Not reported
timeScaleFactor Nothing Not checked. 0 freezes; negative stalls; large skips rows Not reported
Profile switch Deferred A new profileID takes effect only when the current profile ends activeProfileID
End of a non-looping profile Fixed The output jumps back to row 0 one cycle after completion state, fractionComplete
Stop Fixed Rewinds the output to row 0 and holds it there state reads 0
Pause Fixed Holds the output where it is state reads 2
Reload command 4 Re-reads the folder on a background thread. Not safe while a profile is selected in this version state reads 4 then 5
Loading time The files The player reports reloading and cannot play until parsing finishes state

The block logs every file it loads, skips or fails to parse, at startup and on every reload. Nothing is logged during playback. Every other condition above shows as a value on a trace, or not at all.


Verified against motorcortex-control3 3.30.0 (bc348fd).