SOEM

SOEMは有志が開発しているオープンソースのEherCAT Masterライブラリである. TwinCATとは異なりリアルタイム性は保証されない. そのため, 基本的にTwinCATを使用することを推奨する. SOEMを使用するのはやむを得ない理由があるか, 開発時のみに限定するべきである. 一方, SOEMはクロスプラットフォームで動作し, インストールも単純という利点がある.

Windowsの場合は, npcapを「WinPcap API compatible mode」でインストールしておくこと. Linux/macOSの場合は, 特に準備は必要ない.

Install

cargo add autd3-link-soem
if(WIN32)
  FetchContent_Declare(
    autd3-link-soem
    URL https://github.com/shinolab/autd3-cpp-link-soem/releases/download/v32.0.1/autd3-link-soem-v32.0.1-win-x64.zip
  )
elseif(APPLE)
  FetchContent_Declare(
    autd3-link-soem
    URL https://github.com/shinolab/autd3-cpp-link-soem/releases/download/v32.0.1/autd3-link-soem-v32.0.1-macos-aarch64.tar.gz
  )
else()
  FetchContent_Declare(
    autd3-link-soem
    URL https://github.com/shinolab/autd3-cpp-link-soem/releases/download/v32.0.1/autd3-link-soem-v32.0.1-linux-x64.tar.gz
  )
endif()
FetchContent_MakeAvailable(autd3-link-soem)
target_link_libraries(<TARGET> PRIVATE autd3::link::soem)
dotnet add package AUTD3Sharp.Link.SOEM

https://github.com/shinolab/AUTD3Sharp.Link.SOEM.git#upm/latestをUnity Package Managerで追加する.

pip install pyautd3_link_soem

APIs

第1引数にはエラーが起きたときのコールバック関数を, 第2引数にはオプションを指定する.

#[cfg(target_os = "windows")]
use autd3_link_soem::ProcessPriority;
use autd3_link_soem::{Status, SyncMode, ThreadPriority, TimerStrategy, SOEM, SOEMOption};
use std::num::NonZeroUsize;
use std::time::Duration;

fn main() {
let _ = 
SOEM::new(
    |slave, status| {
        eprintln!("slave [{}]: {}", slave, status);
        if status == Status::Lost {
            std::process::exit(-1);
        }
    },
    SOEMOption {
        buf_size: NonZeroUsize::new(32).unwrap(),
        timer_strategy: TimerStrategy::SpinSleep,
        sync_mode: SyncMode::DC,
        ifname: String::new(),
        state_check_interval: Duration::from_millis(100),
        sync0_cycle: Duration::from_millis(1),
        send_cycle: Duration::from_millis(1),
        thread_priority: ThreadPriority::Max,
        #[cfg(target_os = "windows")]
        process_priority: ProcessPriority::High,
        sync_tolerance: Duration::from_micros(1),
        sync_timeout: Duration::from_secs(10),
    },
);
}
#include <iostream>
#include <autd3_link_soem.hpp>

int main() {
using namespace autd3;
link::SOEM(
    [](const uint16_t slave, const link::Status status) {
      std::cout << "slave [" << slave << "]: " << status << std::endl;
      if (status == link::Status::Lost()) {
        exit(-1);
      }
    },
    link::SOEMOption{
        .buf_size = 32,
        .timer_strategy = link::TimerStrategy::SpinSleep,
        .sync_mode = link::SyncMode::DC,
        .ifname = "",
        .state_check_interval = std::chrono::milliseconds(100),
        .sync0_cycle = std::chrono::milliseconds(1),
        .send_cycle = std::chrono::milliseconds(1),
        .thread_priority = link::ThreadPriority::Max(),
        .process_priority = link::ProcessPriority::High,
        .sync_tolerance = std::chrono::microseconds(1),
        .sync_timeout = std::chrono::seconds(10),

    });
return 0; }
using AUTD3Sharp;
using AUTD3Sharp.Link;

using AUTD3Sharp.Utils;
new SOEM(
    errHandler: (slave, status) =>
    {
        Console.Error.WriteLine($"slave [{slave}]: {status}");
        if (status == Status.Lost)
            Environment.Exit(-1);
    },
    option: new SOEMOption
    {
        BufSize = 32,
        TimerStrategy = TimerStrategy.SpinSleep,
        SyncMode = SyncMode.DC,
        Ifname = "",
        StateCheckInterval = Duration.FromMillis(100),
        Sync0Cycle = Duration.FromMillis(1),
        SendCycle = Duration.FromMillis(1),
        ThreadPriority = AUTD3Sharp.Link.ThreadPriority.Max,
        ProcessPriority = ProcessPriority.High,
        SyncTolerance = Duration.FromMicros(1),
        SyncTimeout = Duration.FromSecs(10),
    }
);
import os
from pyautd3 import Duration
from pyautd3_link_soem import (
    SOEM,
    ProcessPriority,
    SOEMOption,
    Status,
    SyncMode,
    ThreadPriority,
    TimerStrategy,
)


def err_handler(slave: int, status: Status) -> None:
    print(f"slave [{slave}]: {status}")
    if status == Status.Lost():
        os._exit(-1)


SOEM(
    err_handler=err_handler,
    option=SOEMOption(
        buf_size=32,
        timer_strategy=TimerStrategy.SpinSleep,
        sync_mode=SyncMode.DC,
        ifname="",
        state_check_interval=Duration.from_millis(100),
        sync0_cycle=Duration.from_millis(1),
        send_cycle=Duration.from_millis(1),
        thread_priority=ThreadPriority.Max,
        process_priority=ProcessPriority.High,  # only available on Windows
        sync_tolerance=Duration.from_micros(1),
        sync_timeout=Duration.from_secs(10),
    ),
)

SOEMリンクで指定できるオプションは以下の通りである. デフォルト値は上記の通り.

  • buf_size: 送信キューバッファサイズ. 通常は変更する必要はない.
  • timer_strategy: タイマーの戦略
    • StdSleep : 標準ライブラリのsleepを用いる
    • SpinSleep : spin_sleep crateを用いる. OSネイティブのスリープ (Windowsの場合はWaitableTimer) とスピンループを組み合わせ.
    • SpinWait : スピンループを用いる. 高解像度だが, CPU負荷が高い.
  • sync_mode: 同期モード
  • ifname: ネットワークインタフェース名. 空白の場合はAUTD3デバイスが接続されているネットワークインタフェースを自動的に選択する.
  • state_check_interval: エラーが出ているかどうかを確認する間隔
  • sync0_cycle: 同期信号の周期
  • send_cycle: 送信サイクル
    • SOEMも大量のデバイスを接続すると挙動が不安定になる場合がある1. このときは, sync0_cyclesend_cycleの値を増やす. これら値はエラーが出ない中で, 可能な限り小さな値が望ましい. どの程度の値にすべきかは接続している台数に依存する. 例えば, 9台の場合は程度の値にしておけば動作するはずである.
  • thread_priority: スレッドの優先度
  • process_priority: (Windowsのみ) プロセスの優先度
  • sync_tolerance: 同期許容レベル. 初期化時, 各デバイスのシステム時間差がこの値以下になるまで待機する. 以下のタイムアウト時間が経過しても同期が完了しない場合はエラーとなる. この値を変更することは推奨されない.
  • sync_timeout: 同期タイムアウト. 上記のシステム時間差測定のタイムアウト時間.
1

TwinCATよりは緩く, 普通に動くこともある.