Skip to main content
Version: 1.0.15

Servo Sin Vel Demo

Servo Sine Velocity Demo — CSV sine motion

Source: servo_sin_vel_demo/main.cpp

Moves a servo through a sinusoidal velocity profile using Cyclic Synchronous Velocity (CSV) mode.


1. Config & prepare

lxmaster::NetworkConfig cfg = lxmaster::NetworkConfig::defaults();
cfg.eni.eni_path = "/home/user/myenifolder/myenifile.xml";

lxmaster::EcNetwork net(cfg);
net.prepare();

NetworkConfig::defaults() reads the EtherCAT interface name from the environment (set by lxmaster host setup). The ENI file describes the bus topology, PDO layout, and cycle time — prepare() parses it and brings all slaves to PreOP.


2. Configure axes

for (lxmaster::Axis* ax : net.axes()) {
ax->setDriveMode(lxmaster::DriveOpMode::Csv);
ax->configure();
}
lxmaster::Axis* drive = net.axes().front();

Each axis is set to CSV mode and walked to OPERATIONAL with configure(). In CSV mode the drive's internal position loop is replaced by its own velocity controller — the application supplies a velocity setpoint each cycle.


3. Start

net.start();
std::this_thread::sleep_for(std::chrono::milliseconds(50));

drive->moveAtVelocity(0);
std::this_thread::sleep_for(period);

net.start() launches the real-time cyclic thread. Zero velocity is commanded before the timed loop begins so the drive starts from rest without a step.


4. Motion loop

while (net.isRunning() && !g_interrupted) {
const double t = std::chrono::duration<double>(now - start).count();
drive->moveAtVelocity(static_cast<int32_t>(
kAmplitudeCountsPerSec * std::sin(kTwoPi * kFrequencyHz * t)));
std::this_thread::sleep_for(period);
}

moveAtVelocity() sends a target velocity in encoder counts per second each cycle. The sine starts and ends at zero, so there is no abrupt velocity step at entry or exit.


5. Stop

drive->moveAtVelocity(0);
net.stop();

Velocity is zeroed before shutdown so the drive decelerates to rest before net.stop() tears down the bus.

Source: main.cpp

#include <atomic>
#include <chrono>
#include <cmath>
#include <csignal>
#include <cstdint>
#include <iostream>
#include <thread>
#include <vector>

#include <lxmaster/lxmaster.hpp>

static std::atomic<bool> g_interrupted{false};

constexpr int32_t kAmplitudeCountsPerSec = 100000;
constexpr double kFrequencyHz = 0.5;
constexpr int kCycles = 5;
constexpr double kTwoPi = 6.283185307179586;

int main() {
signal(SIGINT, [](int){ g_interrupted = true; });
signal(SIGTERM, [](int){ g_interrupted = true; });

// --- 1. Config & prepare ---
lxmaster::NetworkConfig cfg = lxmaster::NetworkConfig::defaults();
cfg.eni.eni_path = "/home/user/myenifolder/myenifile.xml";

lxmaster::EcNetwork net(cfg);
if (!net.prepare()) { std::cerr << "prepare failed: " << net.lastError() << "\n"; return 1; }

std::vector<lxmaster::Axis*> axes = net.axes();
if (axes.empty()) { std::cerr << "No motion axes in ENI.\n"; return 1; }

// --- 2. Configure axes ---
for (lxmaster::Axis* ax : axes) {
ax->setDriveMode(lxmaster::DriveOpMode::Csv);
ax->configure();
}
lxmaster::Axis* drive = axes.front();

// --- 3. Start ---
if (!net.start()) { std::cerr << "start failed: " << net.lastError() << "\n"; return 1; }
std::this_thread::sleep_for(std::chrono::milliseconds(50));

const auto period = std::chrono::nanoseconds(net.cycleTimeNs());
const auto start = std::chrono::steady_clock::now();
const auto deadline = start + std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::duration<double>(kCycles / kFrequencyHz));

drive->moveAtVelocity(0);
std::this_thread::sleep_for(period);

while (net.isRunning() && !g_interrupted) {
const auto now = std::chrono::steady_clock::now();
if (now >= deadline) break;

const double t = std::chrono::duration<double>(now - start).count();
drive->moveAtVelocity(static_cast<int32_t>(kAmplitudeCountsPerSec * std::sin(kTwoPi * kFrequencyHz * t)));
std::this_thread::sleep_for(period);
}

// --- 4. Stop ---
drive->moveAtVelocity(0);
net.stop();
return 0;
}