Skip to main content
Version: 1.1.1

Motion operating modes

Goal: learn the three cyclic control modes a CiA402 drive supports through the Axis facade — position, velocity, torque — and how to switch between them while the bus keeps running.

This tutorial builds directly on Facades and Device profiles: the modes here are a feature of the CiA402 profile, surfaced through the Axis you already know.

EtherCAT background: what "cyclic mode" means

A CiA402 servo can be controlled in several ways. The three you use for real-time motion are the cyclic synchronous modes: every EtherCAT cycle the master sends one fresh setpoint and the drive's internal loop chases it. The mode is selected by writing CANopen object 0x6060 ("modes of operation"); the drive echoes the active mode back in 0x6061.

ModeEnumYou commandDrive objectMeaning
CSPDriveOpMode::CspmoveTo(counts)0x607A target positionCyclic Synchronous Position — send a new position each cycle.
CSVDriveOpMode::CsvmoveAtVelocity(counts_per_sec)0x60FF target velocityCyclic Synchronous Velocity.
CSTDriveOpMode::CstapplyTorque(per_mille)0x6071 target torqueCyclic Synchronous Torque — torque in ‰ of rated.

Because a setpoint is consumed every cycle, your application must keep feeding the active command. The facade holds the last value you set, so if you stop updating, the drive simply holds the last setpoint.

Choosing the initial mode

You pick the starting mode during the PRE-OP window, before start():

for (lxmaster::Axis* ax : net.axes()) {
ax->setDriveMode(lxmaster::DriveOpMode::Csp);
ax->configure();
}

CSP is the standard choice for initial bring-up. setDriveMode() records the intent; configure() commits it as the drive walks to OPERATIONAL (LXMASTER writes 0x6060 for you).

Commanding each mode

Once running, command the axis with the method that matches its mode:

// CSP — absolute target position in encoder counts
drive->moveTo(home + 50000);

// CSV — target velocity in counts/second
drive->moveAtVelocity(100000);

// CST — target torque in per-mille of rated torque
drive->applyTorque(50);

Commanding the "wrong" method for the current mode has no effect — e.g. moveAtVelocity() is ignored unless the drive is in CSV. Read back what the drive is actually doing with modeDisplay() (the decoded 0x6061: 8 = CSP, 9 = CSV, 10 = CST).

Switching modes at runtime

You can change mode without leaving OPERATIONAL — no bus restart, no re-homing. Call setDriveMode() again while running:

drive->setDriveMode(lxmaster::DriveOpMode::Csv);
drive->moveAtVelocity(0); // give the new mode a valid setpoint immediately
Live switching needs 0x6060 in the RxPDO

A live mode switch only takes effect if the ENI maps the modes-of-operation object (0x6060) into the drive's RxPDO. lxmaster eni gen does this for CiA402 drives automatically, so ENIs generated by the CLI already support it. If you hand-craft an ENI, make sure 0x6060 is mapped.

Switch safely: zero and settle first

Switching modes abruptly can hand the new mode a large error the instant it takes over — a big position gap, or a sudden torque step — which can trip a drive fault. The safe pattern (used by the Servo Mode Sweep Demo) is to return to a neutral setpoint, let the drive settle for a moment, then switch:

// Leaving CSV, entering CST
drive->moveAtVelocity(0); // neutral setpoint
std::this_thread::sleep_for(std::chrono::milliseconds(500)); // settle
drive->setDriveMode(lxmaster::DriveOpMode::Cst);
drive->applyTorque(0); // valid setpoint for new mode

This zero-and-settle step is a safety practice, not an API requirement — but it will save you nuisance faults.

Worked example: a three-mode sweep

The Servo Mode Sweep Demo runs a drive through all three modes back-to-back. Its core is a helper that feeds a command lambda every cycle for a fixed duration:

static bool runPhase(lxmaster::EcNetwork& net, double duration,
const std::function<void(TimePoint, TimePoint)>& command) {
const auto period = std::chrono::nanoseconds(net.cycleTimeNs());
const auto start = std::chrono::steady_clock::now();
while (net.isRunning()) {
const auto now = std::chrono::steady_clock::now();
if (elapsedS(now, start) >= duration) break;
command(now, start); // e.g. drive->moveTo(...)
std::this_thread::sleep_for(period); // pace at the cycle time
}
return net.isRunning();
}

The three phases then read almost like prose:

// Phase 1 — CSP: sine position around home
drive->setDriveMode(lxmaster::DriveOpMode::Csp);
drive->moveTo(home);
runPhase(net, phaseS, [&](TimePoint now, TimePoint start) {
const double wt = kTwoPi * kFrequencyHz * elapsedS(now, start);
drive->moveTo(home + int32_t(0.5 * kPosAmp * (std::cos(wt) - 1.0)));
});

// Phase 2 — CSV: sine velocity (zero-and-settle before switching)
drive->moveTo(home);
settle(net, 0.5);
drive->setDriveMode(lxmaster::DriveOpMode::Csv);
drive->moveAtVelocity(0);
runPhase(net, phaseS, [&](TimePoint now, TimePoint start) {
const double wt = kTwoPi * kFrequencyHz * elapsedS(now, start);
drive->moveAtVelocity(int32_t(kVelAmp * std::sin(wt)));
});

// Phase 3 — CST: sine torque (zero-and-settle before switching)
drive->moveAtVelocity(0);
settle(net, 0.5);
drive->setDriveMode(lxmaster::DriveOpMode::Cst);
drive->applyTorque(0);
runPhase(net, phaseS, [&](TimePoint now, TimePoint start) {
const double wt = kTwoPi * kFrequencyHz * elapsedS(now, start);
drive->applyTorque(int32_t(kTorqueAmp * std::sin(wt)));
});

Notice the whole thing stays in OPERATIONAL — only the mode and the command method change between phases.

CST commands real torque

applyTorque() produces force at the motor shaft with no position limit of its own. Start with small per-mille values and make sure the axis is safe to move freely before running torque mode.

Reading motion feedback

Whatever the mode, you read the drive's state through the same facade:

ReadMethod
Actual position (counts)drive->actualPosition()
Actual velocity (counts/s)drive->actualVelocity()
Actual torque (‰ rated)drive->actualTorque()
Active mode (8/9/10)drive->modeDisplay()
Powered and holding?drive->isEnabled()

A value reads as 0 if the drive's profile does not map that object.

Recap

  • CiA402 offers three cyclic modes: CSP (moveTo), CSV (moveAtVelocity), CST (applyTorque).
  • Pick the initial mode with setDriveMode() + configure() before start().
  • Switch live with setDriveMode() — no restart — as long as 0x6060 is in the RxPDO (CLI-generated ENIs handle this).
  • Zero the command and settle before switching to avoid nuisance faults.

Next

Motion is only half the bus. Next you will drive discrete signals with the other main facade: Working with I/O.