2025-08-22 16:57:29 +08:00
|
|
|
|
# CMVR-ES
|
|
|
|
|
|
|
2026-07-09 14:22:35 +08:00
|
|
|
|
## 简介
|
2025-08-22 16:57:29 +08:00
|
|
|
|
|
2026-07-09 14:22:35 +08:00
|
|
|
|
CMVR-ES 工程。
|
2025-08-22 16:57:29 +08:00
|
|
|
|
|
2026-07-09 14:22:35 +08:00
|
|
|
|
## 安装
|
|
|
|
|
|
|
|
|
|
|
|
### 1. 拉取 Git 子模块
|
2025-08-22 16:57:29 +08:00
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
git submodule update --init --recursive
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-07-09 14:22:35 +08:00
|
|
|
|
### 2. 安装系统依赖
|
2025-08-22 16:57:29 +08:00
|
|
|
|
|
|
|
|
|
|
```shell
|
2026-07-09 14:22:35 +08:00
|
|
|
|
# 基础工具
|
2026-02-05 17:31:18 +08:00
|
|
|
|
sudo apt-get update
|
2026-03-05 16:15:13 +08:00
|
|
|
|
sudo apt install -y build-essential cmake git pkg-config patchelf libboost-all-dev libssl-dev
|
|
|
|
|
|
|
2026-07-09 14:22:35 +08:00
|
|
|
|
# OpenCV
|
2026-02-05 17:31:18 +08:00
|
|
|
|
sudo apt install -y \
|
|
|
|
|
|
libjpeg-dev libpng-dev libtiff-dev \
|
|
|
|
|
|
libavcodec-dev libavformat-dev libswscale-dev \
|
|
|
|
|
|
libv4l-dev libxvidcore-dev libx264-dev \
|
2026-03-05 16:15:13 +08:00
|
|
|
|
libgtk-3-dev
|
2025-08-22 16:57:29 +08:00
|
|
|
|
|
2026-02-05 17:31:18 +08:00
|
|
|
|
# PulseAudio
|
|
|
|
|
|
sudo apt-get install libpulse-dev
|
2025-08-22 16:57:29 +08:00
|
|
|
|
|
2026-02-05 17:31:18 +08:00
|
|
|
|
# OpenGL
|
|
|
|
|
|
sudo apt-get install libgl1-mesa-dev
|
2025-08-22 16:57:29 +08:00
|
|
|
|
|
2026-02-05 17:31:18 +08:00
|
|
|
|
# GLFW
|
|
|
|
|
|
sudo apt-get install libglfw3-dev
|
2025-08-22 16:57:29 +08:00
|
|
|
|
|
2026-02-05 17:31:18 +08:00
|
|
|
|
# Assimp
|
|
|
|
|
|
sudo apt-get install libassimp-dev
|
2025-08-22 16:57:29 +08:00
|
|
|
|
|
2026-02-11 15:23:11 +08:00
|
|
|
|
# visp
|
2026-03-05 16:15:13 +08:00
|
|
|
|
sudo apt-get install -y libx11-dev liblapack-dev libzbar-dev libpthread-stubs0-dev libdc1394-dev nlohmann-json3-dev
|
2026-02-05 17:31:18 +08:00
|
|
|
|
|
2026-07-09 14:22:35 +08:00
|
|
|
|
# RealSense
|
2026-03-05 16:15:13 +08:00
|
|
|
|
sudo apt-get install -y \
|
|
|
|
|
|
libusb-1.0-0-dev libudev-dev \
|
|
|
|
|
|
libglu1-mesa-dev
|
2026-03-11 16:23:23 +08:00
|
|
|
|
|
|
|
|
|
|
# Matplot++
|
|
|
|
|
|
sudo apt install gnuplot-qt
|
2026-03-05 16:15:13 +08:00
|
|
|
|
```
|
2026-03-11 16:23:23 +08:00
|
|
|
|
|
2026-07-09 14:22:35 +08:00
|
|
|
|
### 3. 配置 IgH EtherCAT
|
|
|
|
|
|
|
|
|
|
|
|
工程已内置 IgH EtherCAT 1.7.0 的 userspace 文件:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
dependency/x86/third_party/ethercat/v1.7.0
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
先设置本机路径:
|
|
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
|
export CMVR_ES_ROOT=/path/to/cmvr-es
|
|
|
|
|
|
export IGH_ETHERCAT_ROOT=$CMVR_ES_ROOT/dependency/x86/third_party/ethercat/v1.7.0
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
安装当前内核的 header:
|
|
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
|
sudo apt-get update
|
|
|
|
|
|
sudo apt-get install -y linux-headers-$(uname -r)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
如果内置目录里已经有当前内核版本的 EtherCAT 内核模块,直接安装到系统:
|
|
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
|
sudo mkdir -p /lib/modules/$(uname -r)/ethercat
|
|
|
|
|
|
sudo cp -r $IGH_ETHERCAT_ROOT/lib/modules/$(uname -r)/ethercat/* /lib/modules/$(uname -r)/ethercat/
|
|
|
|
|
|
sudo depmod
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
内置内核模块只适用于相同内核版本。如果 `$IGH_ETHERCAT_ROOT/lib/modules/$(uname -r)` 不存在,说明这台机器的内核版本不匹配,需要在这台机器上重新编译安装 IgH EtherCAT:
|
|
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
|
sudo apt-get update
|
|
|
|
|
|
sudo apt-get install -y \
|
|
|
|
|
|
build-essential autoconf automake libtool pkg-config git \
|
|
|
|
|
|
linux-headers-$(uname -r)
|
|
|
|
|
|
|
|
|
|
|
|
cd /tmp
|
|
|
|
|
|
git clone --branch stable-1.7 --depth 1 https://gitlab.com/etherlab.org/ethercat.git ethercat-stable-1.7
|
|
|
|
|
|
cd ethercat-stable-1.7
|
|
|
|
|
|
|
|
|
|
|
|
./bootstrap
|
|
|
|
|
|
./configure \
|
|
|
|
|
|
--prefix=$IGH_ETHERCAT_ROOT \
|
|
|
|
|
|
--libdir=$IGH_ETHERCAT_ROOT/lib \
|
|
|
|
|
|
--includedir=$IGH_ETHERCAT_ROOT/include \
|
|
|
|
|
|
--sysconfdir=$IGH_ETHERCAT_ROOT/etc \
|
|
|
|
|
|
--with-systemdsystemunitdir=$IGH_ETHERCAT_ROOT/lib/systemd/system \
|
|
|
|
|
|
--enable-generic \
|
|
|
|
|
|
--with-linux-dir=/lib/modules/$(uname -r)/build
|
|
|
|
|
|
|
|
|
|
|
|
make -j$(nproc) all modules
|
|
|
|
|
|
make install
|
|
|
|
|
|
sudo make modules_install
|
|
|
|
|
|
sudo depmod
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
启动 EtherCAT。`eno1` 换成实际连接 EtherCAT 从站的网卡:
|
2026-03-11 16:23:23 +08:00
|
|
|
|
|
2026-07-09 14:22:35 +08:00
|
|
|
|
```shell
|
|
|
|
|
|
sudo script/ethercat/start_ethercat.sh eno1
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
脚本会写入内置 IgH 配置文件:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
$IGH_ETHERCAT_ROOT/etc/ethercat.conf
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
脚本会把该网卡的 MAC 写到 `MASTER0_DEVICE`,使用 `DEVICE_MODULES="generic"`,把网卡从 NetworkManager 断开,并通过 `ethercatctl -c` 启动 IgH master。
|
|
|
|
|
|
|
|
|
|
|
|
查看状态:
|
|
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
|
script/ethercat/status_ethercat.sh
|
|
|
|
|
|
|
|
|
|
|
|
$IGH_ETHERCAT_ROOT/bin/ethercat master
|
|
|
|
|
|
$IGH_ETHERCAT_ROOT/bin/ethercat slaves
|
|
|
|
|
|
$IGH_ETHERCAT_ROOT/bin/ethercat pdos
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
停止 EtherCAT:
|
|
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
|
sudo script/ethercat/stop_ethercat.sh eno1
|
|
|
|
|
|
sudo script/ethercat/stop_ethercat.sh eno1 --restore-network
|
|
|
|
|
|
```
|
2026-07-30 15:09:07 +08:00
|
|
|
|
|
|
|
|
|
|
## MotorService 与 Modbus TCP PLC
|
|
|
|
|
|
|
|
|
|
|
|
工程包含从 gRPC `MotorService`、`MotorManager`、`AbstractMotor` 到
|
|
|
|
|
|
`ModbusTcpMotorBusRuntime` 的 CMVR PLC v1 电机控制链,x86-64 的 libmodbus
|
|
|
|
|
|
3.1.11 已放在 `dependency/x86/third_party/modbus/3.1.11`。
|
|
|
|
|
|
|
|
|
|
|
|
PLC 对接时特别注意:
|
|
|
|
|
|
|
|
|
|
|
|
- `host` 必须配置为 IPv4 字面量,PLC boot ID 必须非零且每次重启变化;
|
|
|
|
|
|
- owner 决策、命令 ACK 都必须回显对应 session,重连不得执行旧 mailbox;
|
|
|
|
|
|
- PLC 在进程启动时可以离线;连接 supervisor 会继续退避重试,离线期间状态
|
|
|
|
|
|
返回不可用且运动命令不会写 mailbox;
|
|
|
|
|
|
- 状态区按 odd/even seqlock 发布,上位机使用
|
|
|
|
|
|
sequence-before → 64-word block → sequence-after 三段读取验证;
|
|
|
|
|
|
- 每次 `OpenCyclicPosition/Velocity` 创建新 stream epoch,PLC 必须原子清零
|
|
|
|
|
|
`last_applied_cyclic_sequence`、旧样本去重状态和 cyclic watchdog,确认
|
|
|
|
|
|
`StreamActive` 与正确 mode 后才 ACK;重开后的首样本序列从 `1` 开始并必须
|
|
|
|
|
|
重新应用;
|
|
|
|
|
|
- 活动 cyclic 流跨 `connection_epoch` 后不会自动重开;旧流的当前和后续
|
|
|
|
|
|
setpoint 均被拒绝并在 Quick Stop 后终止,客户端必须新建 gRPC 流。断链前
|
|
|
|
|
|
或断链期间 pending 的 setpoint 不会应用到新 session;
|
|
|
|
|
|
- 任何清理 Quick Stop 未确认时,MotorService 都会 fail-closed 锁存,并在
|
|
|
|
|
|
成功执行 `setEnabled(true)` 前拒绝新的运动命令;
|
|
|
|
|
|
- Modbus Quick Stop 只是功能性停止,不能替代硬接线急停或驱动器 STO。
|
|
|
|
|
|
- 当前 Modbus 后端只提供 x86-64 的 libmodbus 3.1.11;`dependency/arm`
|
|
|
|
|
|
尚无对应库,因此 ARM 构建暂不支持该后端。
|
|
|
|
|
|
|
|
|
|
|
|
完整 gRPC 语义、配置样例、寄存器表、TIA Portal 要求、构建测试和安全边界见
|
|
|
|
|
|
[`docs/motor_service_modbus_tcp.md`](docs/motor_service_modbus_tcp.md)。
|
|
|
|
|
|
|
|
|
|
|
|
## AUBO 控制柜 IO
|
|
|
|
|
|
|
|
|
|
|
|
`AuboArm` 通过通用的 `executeJsonCommand` 接口提供控制柜 Standard 数字 IO
|
|
|
|
|
|
读写。第一版支持以下命令:
|
|
|
|
|
|
|
|
|
|
|
|
| `operation` | 说明 | 必填字段 |
|
|
|
|
|
|
| --- | --- | --- |
|
|
|
|
|
|
| `get_di` | 读取控制柜数字输入 | `index` |
|
|
|
|
|
|
| `get_do` | 读取控制柜数字输出及其 runstate | `index` |
|
|
|
|
|
|
| `set_do` | 设置控制柜数字输出 | `index`、`value` |
|
|
|
|
|
|
|
|
|
|
|
|
JSON 命令示例:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
{"command":"cabinet_io","operation":"get_di","index":0}
|
|
|
|
|
|
{"command":"cabinet_io","operation":"get_do","index":0}
|
|
|
|
|
|
{"command":"cabinet_io","operation":"set_do","index":0,"value":true}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
其中 `index` 从 `0` 开始,运行时会根据控制器返回的 IO 数量检查范围。
|
|
|
|
|
|
`set_do.value` 必须是 JSON 布尔值 `true` 或 `false`,不接受 `0/1` 或字符串。
|
|
|
|
|
|
`set_do` 成功响应中的 `requested_value` 表示 SDK 已接受的请求值;需要确认控制器
|
|
|
|
|
|
当前输出状态时,再调用一次 `get_do` 读取实际值。
|
|
|
|
|
|
|
|
|
|
|
|
成功响应示例:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"success": true,
|
|
|
|
|
|
"command": "cabinet_io",
|
|
|
|
|
|
"operation": "get_di",
|
|
|
|
|
|
"index": 0,
|
|
|
|
|
|
"count": 16,
|
|
|
|
|
|
"value": false
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 通过 gRPC 调用
|
|
|
|
|
|
|
|
|
|
|
|
该功能复用 `cmvr.api.SystemService/ExecuteJsonCommand`。默认配置中的 gRPC
|
|
|
|
|
|
端口是 `50052`,读取 DI0:
|
|
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
|
grpcurl -plaintext \
|
|
|
|
|
|
-d '{
|
|
|
|
|
|
"header":{"deviceId":"aubo_arm"},
|
|
|
|
|
|
"requestJson":"{\"command\":\"cabinet_io\",\"operation\":\"get_di\",\"index\":0}"
|
|
|
|
|
|
}' \
|
|
|
|
|
|
127.0.0.1:50052 \
|
|
|
|
|
|
cmvr.api.SystemService/ExecuteJsonCommand
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
设置 DO0 为高电平:
|
|
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
|
grpcurl -plaintext \
|
|
|
|
|
|
-d '{
|
|
|
|
|
|
"header":{"deviceId":"aubo_arm"},
|
|
|
|
|
|
"requestJson":"{\"command\":\"cabinet_io\",\"operation\":\"set_do\",\"index\":0,\"value\":true}"
|
|
|
|
|
|
}' \
|
|
|
|
|
|
127.0.0.1:50052 \
|
|
|
|
|
|
cmvr.api.SystemService/ExecuteJsonCommand
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
使用源码树默认配置时,先在 `cmvr-es/config/manager/device_manager.pb.txt` 中把
|
|
|
|
|
|
`aubo_arm` 的 `enable` 改为 `true`,并在
|
|
|
|
|
|
`cmvr-es/config/devices/arm/aubo_arm.pb.txt` 中配置正确的控制器地址和登录信息,
|
|
|
|
|
|
然后重新安装配置并启动安装产物:
|
|
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
|
cmake --install build
|
|
|
|
|
|
./output/bin/cmvr_es
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
`output/bin/cmvr_es` 读取的是 `output/bin/config/`;如果进程使用显式配置路径,
|
|
|
|
|
|
应修改该配置根下的对应文件。
|
|
|
|
|
|
设备未启用或初始化失败时,gRPC 会返回 `Device not found: aubo_arm`。
|
|
|
|
|
|
|
|
|
|
|
|
### 安全约束
|
|
|
|
|
|
|
|
|
|
|
|
- 该接口只访问控制柜 Standard 数字 IO,不操作工具端 IO、可配置 IO 或安全 IO。
|
|
|
|
|
|
- `set_do` 不会修改控制器的输出 runstate。只有目标通道的 runstate 为
|
|
|
|
|
|
`StandardOutputRunState::None` 时才允许写入,否则返回
|
|
|
|
|
|
`output_managed_by_runstate`。
|
|
|
|
|
|
- 接口不会调用会重置全部输出配置的 `setDigitalOutputRunstateDefault()`。
|
|
|
|
|
|
- 模拟量 IO 涉及 domain、单位和量程,第一版暂不通过该 JSON 接口开放。
|