# 纳博特 NexDroid 开放平台(二次开发文档) — 全量文档(中文)
> 本文件由构建脚本(scripts/generate-llms.mjs)自动生成,随站点更新;每页正文之间以分隔线隔开,标题下方给出原文 URL。
> 单页 Markdown 版本:将 .html 替换为 .md(例如 https://open.inexbot.com/zh/01.概述.html → 01.概述.md)。
> 文档索引:https://open.inexbot.com/llms.txt | 站点入口:https://open.inexbot.com/zh/
---
# 概述
> 原文:https://open.inexbot.com/zh/01.%E6%A6%82%E8%BF%B0.html
纳博特是国内领先的工业机器人控制器厂商,自主研发的开放型 **NexDroid** 软件平台分离了运控算法层与应用层,让合作伙伴只需关注行业需求,即可开发 3C 电子、医疗、核能、工程机械、风电等行业的专有自动化装备。
***
## 二次开发层级
纳博特软件平台提供六个层级的二次开发,用户可按需选择:
| 开发方式 | 说明 | 平台 | 语言 | 入口 |
| ------------- | ----------------------------- | --------------- | ----------------- | -------------------------------- |
| **上位机开发** | 脱离示教器,通过 API 直接控制机器人 | Windows / Linux | C++ / C# / Python | [上位机](./04.上位机/index.md) |
| **JSON 协议通信** | 遵循标准 JSON 协议操控控制器 | 跨平台 | 任意语言(TCP Socket) | [JSON 协议](./05.JSON-协议/index.md) |
| **控制器二次开发** | 专有工艺开发、特殊机型支持、插补算法替换 | Linux | C++ | [控制器](./06.控制器/index.md) |
| **示教器二次开发** | 定制专属 UI/UE,配合控制器实现专有功能 | Linux | Qt / C++ | [示教器](./07.示教器/index.md) |
| **主站开发** | 控制器退化为工控机,提供实时系统和 EtherCAT 主站 | Linux (RT) | C / C++ | [主站库](./09.主站库/) |
| **ROS 开发** | 控制器作为 ROS Node,按 ROS 协议控制 | Linux | C++ / Python | [ROS](./08.ROS/index.md) |
> 不确定选哪种?参见[入门指南](./02.入门指南.md)的快速决策表,一分钟确定方案。
***
## 目标受众
| 角色 | 典型需求 | 推荐开发方式 |
| -------- | ---------------- | ---------------- |
| **本体厂商** | 快速客制化控制系统,打造自有品牌 | 控制器 + 示教器二次开发 |
| **集成商** | 开发专有工艺,保护行业知识 | 上位机开发 + JSON 协议 |
| **科研用户** | 高精度运控算法研究,极限性能验证 | 控制器二次开发 + ROS |
| **教育用户** | 培养设计制造机器人的综合能力 | 上位机(Python)+ ROS |
***
## 系统架构
纳博特机器人控制系统由以下核心组件构成:
```text
┌──────────────┐ ┌──────────────┐
│ 上位机 │ │ 示教器 │
│ (客户软件) │ │ (人机交互) │
└──────┬───────┘ └──────┬───────┘
│ 通信协议/SDK │ 网线
▼ ▼
┌─────────────────────────────────────┐
│ 控制器 │
│ Linux 实时系统 · 工控机 │
│ 运动规划 · 伺服控制 · 程序执行 │
└──────┬──────────────┬───────────────┘
│ EtherCAT │
▼ ▼
┌──────────┐ ┌──────────────┐
│ 伺服驱动器 │ │ IO 模块 │
│ ↓ ↓ ↓ │ │ (继电器/夹爪) │
│ 电机(各轴) │ └──────────────┘
└──────────┘
```
| 组件 | 说明 |
| --------- | -------------------------------------------------------------- |
| **控制器** | 运行 Linux 实时系统的工控机,是整个系统的核心。负责运动规划、伺服控制、程序执行。通常安装在电气柜内,无独立显示界面。 |
| **示教器** | 人机交互设备,通过网线连接控制器。操作员在示教器上编写程序、控制机器人、查看状态。 |
| **上位机** | 客户自行开发的软件,通过 SDK 或通信接口控制控制器。与示教器的区别在于上位机可突破示教器功能局限,实现自定义逻辑。 |
| **伺服驱动器** | 接收控制器指令,驱动电机转动。不同轴因负载不同,所需功率不同。 |
| **IO 模块** | 输入/输出信号管理,控制继电器、夹爪、传感器等外围设备。 |
> **调试环境:** 开发测试时,可使用**虚拟伺服**和**虚拟 IO** 在无真实硬件的环境中模拟运行。
***
## 主要内容
### Demo 示例
纳博特 Demo 示例提供了多个基于纳博特控制器典型应用的 Demo,支持 C、C++、Qt、C#、Python、ROS 等环境,用户可以在常见的开发环境中进行快速演示。Demo 内容覆盖机械臂的各种基础操作和高级功能,包括坐标系操作、力控、抓取、IO 功能、外部轴控制、ModbusRTU 通信、样条曲线运动、角度透传、在线编程以及算法等,使用户可以快速掌握各种 API 接口的应用。
→ [浏览 Demo 示例](./03.Demo示例.md)
### 二次开发接口
纳博特二次开发接口(NexDroidAPI)将机械臂复杂功能进行接口化封装,用户无需底层开发即可使用机械臂的高级控制功能,降低了开发门槛。接口支持 C、C++、Qt、C#、Python、JSON、ROS 等开发环境,充分满足任意环境的部署需求。每份接口文档涵盖详细的接口说明、参数解释和示例代码,用户可以全面了解每个接口的功能和用法。
→ 入口:[上位机](./04.上位机/index.md) · [JSON 协议](./05.JSON-协议/index.md) · [控制器](./06.控制器/index.md) · [示教器](./07.示教器/index.md)
### 相关资源下载
软件二次开发资源包提供了 API 接口包、SDK 包、ROS 包等下载资源,含版本号、平台兼容矩阵和校验信息。基于软件包用户可以便捷地进行二次开发,快速进行应用部署。
→ [下载 SDK](./12.相关下载.md)
***
## 技术支持
- 遇到问题先查阅[常见问题](./11.常见问题.md)
- 官网:
- 视频教程:[纳博特运动控制 - 哔哩哔哩](https://space.bilibili.com/2230767)
- 技术支持邮箱:
- 关注微信公众号、微信小程序获取最新资讯和产品手册
扫码关注微信公众号 扫码体验微信小程序
---
# 入门指南
> 原文:https://open.inexbot.com/zh/02.%E5%85%A5%E9%97%A8%E6%8C%87%E5%8D%97.html
纳博特控制器提供多种二次开发方式。本指南帮你根据目标、技术栈和场景选择最合适的方案。
***
## 快速决策表
| 你的目标 | 推荐方案 | 语言/协议 | 平台 | 难度 | 文档入口 |
| ---------------- | --------- | -------------------- | --------------- | -- | ------------------------------------------------------------------------------------------------------------------------------- |
| 编写 PC 端控制软件 | 上位机开发 | C++, C#, Python | Windows / Linux | 低 | [C++](./04.上位机/01.C++/01.文档/index.md) · [C#](./04.上位机/02.CSharp/01.文档/01.环境搭建.md) · [Python](./04.上位机/03.Python/01.文档/index.md) |
| 通过 TCP 远程控制机器人 | JSON 协议通信 | 任意语言 (JSON over TCP) | 跨平台 | 中 | [RTL-22.07](./05.JSON-协议/01.RTL-22.07/index.md) · [RTL-24.03](./05.JSON-协议/02.RTL-24.03/index.md) |
| 定制控制器底层行为 | 控制器二次开发 | C++ | Linux | 高 | [控制器开发指南](./06.控制器/index.md) |
| 定制示教器界面 | 示教器二次开发 | C++ / Qt | Linux | 中高 | [示教器开发指南](./07.示教器/index.md) |
| 集成 ROS 生态 | ROS 开发 | C++, Python | Linux | 中高 | [ROS 集成指南](./08.ROS/index.md) |
| 搭建 EtherCAT 实时主站 | 主站开发 | C / C++ | Linux (RT) | 高 | [主站库说明](./09.主站库/) |
***
## 开发方式说明
### 1. 上位机开发
在 PC 上通过 API 直接控制机器人,摆脱示教器限制,适合自动化集成、数据采集、远程监控等场景。
**支持语言和平台:**
| 语言 | 平台 | 工具链 |
| ------ | --------------- | ------------ |
| C++ | Windows | MSVC / MinGW |
| C++ | Linux | GCC |
| C# | Windows | .NET |
| Python | Windows / Linux | Python 3.x |
**入门路径:** 选择语言 → 下载 SDK → 初始化项目 → 调用 API 连接控制器 → 运行示例
**适合用户:** 需要进行机器人高层控制、自动化操作或系统集成的开发人员。支持多语言,可根据项目需求灵活选择技术栈。
***
### 2. JSON 协议通信
通过标准 JSON 消息与控制器通信,适用于跨语言、跨平台的远程控制和数据交换。
**协议特点:**
- 基于 TCP 通信,端口 5000(文件传输)、6000/6001(JSON 文本命令通信)、7000(上位机服务功能)
- 与编程语言无关,任何支持 TCP Socket 的语言均可使用
> RTL-22.07 和 RTL-24.03 均为稳定版本,使用相同的端口体系。6000 端口一般用于示教器通信,6001 端口一般用于上位机通信。当前主流出货版本为 RTL-24.03。
**前提知识:**
- TCP Socket 编程基础
- JSON 数据格式
**入门路径:** 选择协议版本 → 查阅消息格式 → 建立 Socket 连接 → 发送指令
**适合用户:** 需要通过标准化协议与机器人进行远程控制或集成的开发人员。JSON 协议轻量、跨平台,适合保护自有工艺数据的集成商。
***
### 3. 控制器二次开发
在控制器 Linux 环境上开发底层算法,替换或扩展控制器的核心功能。
**应用场景:** 专有工艺开发、特殊机型支持、插补算法替换等。
**前提知识:**
- C++ 编程(面向对象、数据结构)
- Linux 系统编程(进程管理、内存管理)
- 机器人控制理论(运动学、动力学、插补算法)
**入门路径:** 安装交叉编译环境 → 编写定制内容 → 编译并部署到控制器 → 调试
**适合用户:** 控制器硬件和算法开发人员,具有较强的底层编程能力,需要定制插补算法、特殊机型支持或专有工艺。
***
### 4. 示教器二次开发
定制示教器用户界面和交互功能,实现与控制器的协同工作。
**应用场景:** 定制专属 UI/UE,配合控制器二次开发实现专有功能。
**前提知识:**
- C++ 编程
- Qt 框架(信号与槽、QML、UI 设计)
- Linux 系统基础操作
**入门路径:** 搭建 Qt 开发环境 → 创建示教器项目 → 设计 UI → 关联控制器功能
**适合用户:** UI/UE 开发人员,具备前端开发经验,希望为机器人系统提供自定义操作界面的工程师。
***
### 5. ROS 开发
将纳博特控制器集成到 ROS 网络中,实现机器人系统的实时监控和控制。
> ROS 开发使用 ROS 原生通信机制(话题/服务),不涉及 JSON 格式或 HTTP 协议。
**前提知识:**
- ROS 基本概念(节点、话题、服务、动作)
- C++ 或 Python 编程
- ROS 工具链(MoveIt、RViz 等)
**入门路径:** 安装 ROS → 配置 ROS 环境 → 启动纳博特 ROS 节点 → 在 ROS 网络中发布/订阅控制指令
**适合用户:** 科研用户和 ROS 开发者,需要将纳博特控制器集成到已有 ROS 系统中的工程师。ROS 生态提供丰富的工具库,适合复杂算法研究和多机器人协同。
***
### 6. 主站开发
将控制器退化为工控机,在其上运行 EtherCAT 实时主站,实现高度定制化的实时控制系统。
**应用场景:** 满足客户对实时控制的需求,可在该平台上开发实时控制系统和工业网络通信协议。
**前提知识:**
- C / C++ 编程
- EtherCAT 协议基础
- 实时操作系统(RTOS / Xenomai)
**入门路径:** 搭建交叉编译环境 → 初始化主站库 → 配置 EtherCAT 总线 → 编写实时任务
**适合用户:** 工业自动化和嵌入式系统开发人员,尤其是需要定制实时控制系统和网络通信协议的工程师。
***
## 按用户角色推荐
### 本体厂商
目标:快速客制化控制系统,打造自有机器人大脑,提高品牌辨识度。
推荐 **控制器二次开发 + 示教器二次开发**。本体厂商通常需要定制化的控制算法以适配其硬件,控制器二次开发允许对底层控制进行深度定制,调整运动控制、插补算法等。示教器二次开发则可打造专有用户界面,提升品牌辨识度。
**技术要求:** 对机器人有较深了解,熟悉 C++ 编程和控制算法,具备 Linux 系统开发能力。
### 集成商
目标:开发专有工艺,保护自有行业知识,打造产品护城河。
推荐 **上位机开发 + JSON 协议通信**。集成商需要集成自己的专有工艺和技术,上位机开发可通过 API 进行机器人控制,同时收集处理数据;JSON 协议作为轻量级通信方式,适合标准化数据传输,保护自有工艺数据。
**技术要求:** 熟悉网络协议和数据交换,能够进行系统集成和高效数据传输,掌握至少一种上位机开发语言。
### 科研用户
目标:专注高精度运控算法研究,深挖机器人极限性能。
推荐 **控制器二次开发 + ROS 开发**。控制器二次开发提供深度定制运动控制和算法的平台;ROS 支持多机器人协同工作,丰富的工具库帮助实现复杂算法研究和创新。
**技术要求:** 高度的技术研发能力,熟悉控制算法、ROS 开发和高精度运动控制,具备 C++ 和 Python 编程能力。
### 教育用户
目标:培养学生不仅会用机器人,更能设计和制造机器人。
推荐 **上位机开发(Python)+ ROS 开发**。Python 简单易用,适合快速上手和基础编程教学;ROS 有广泛社区支持和丰富学习资源,配合示例程序帮助学生理解机器人控制原理。
**技术要求:** 对机器人操作有基本了解,熟悉图形界面开发和基础编程语言,具备教学场景下的课程设计能力。
***
## 运动控制方式对比
机器人执行运动有三种方式,适用于不同场景。上位机 SDK 用户主要使用后两种。
| 方式 | 适用场景 | 特点 | 支持平台 |
| --------------- | --------- | ------------------------------ | --------- |
| **作业文件模式** | 固定任务、批量生产 | 预编程序列,可循环执行,支持完整逻辑控制(条件/循环/IO) | 示教器 + 上位机 |
| **队列模式(无作业文件)** | 上位机实时控制 | 不依赖作业文件,直接下发目标点,适合动态路径生成 | 上位机 SDK |
| **追加模式** | 视觉引导、动态避障 | 边运动边追加新指令,无需预编译,支持实时决策 | 仅上位机 SDK |
> **追加模式**无法通过示教器实现,必须通过 SDK 或通信协议调用。
## 入门小贴士
- **前提知识:** 对机器人操作有基本了解,熟悉图形界面开发和基础编程语言
- **先读文档:** 各开发方式都提供了详细的文档和示例代码,建议在开始之前先通读相关章节
- **灵活选择:** 初期阶段保持开发路径的开放性,有助于后期快速迭代
- **社区支持:** ROS 和 JSON 协议有广泛的社区支持,可通过在线论坛和教程获取帮助
## 不确定如何选择?
如果仍不清楚从何入手,按以下顺序尝试:
1. **先看** **[Demo](./03.Demo示例.md)** — 下载并运行对应语言的 Demo,直观感受工作流程
2. **从 Python 开始** — 环境最简单、调试最方便,适合快速验证想法
3. **需要更多功能再查 C++ / C#** — 性能和接口更全面
4. **需要跨语言通讯选 JSON** — 适用于已有 TCP 通信基础的系统集成场景
如果仍有疑问,请查阅[常见问题](./11.常见问题.md)、[版本与兼容性](./版本与兼容性.md)或发送邮件至 。
---
# Demo 示例
> 原文:https://open.inexbot.com/zh/03.Demo%E7%A4%BA%E4%BE%8B.html
纳博特提供多语言、多环境的 Demo 源码,覆盖机械臂基础操作到高级工艺功能,帮助开发者快速上手。
---
## 上位机开发 Demo
### C++ Demo
涵盖连接管理、伺服控制、运动控制、队列运动、错误回调、作业文件操作、传送带跟踪、焊接工艺等完整示例。
- **开发环境:** Visual Studio 2022 (MSVC) / Qt Creator (MinGW) / Linux GCC
- **核心文件:** `main.cpp`、`Api.cpp`、`Api.h`
- **头文件:** `nrc_interface.h`、`nrc_queue_operate.h`、`nrc_craft_weld.h`、`nrc_define.h`
### C# Demo
WinForms 完整上位机应用,包含连接管理、伺服状态监控、运动控制、IO 控制、DH 参数查看、作业文件管理、视觉工艺、传送带工艺、焊接工艺等模块的交互界面。
- **开发环境:** Visual Studio 2022,.NET Framework 4.7.2 / .NET 8.0
- **核心文件:** `Form1.cs`、`API.cs`、`NbtRobot.cs`
- **SDK 封装:** `Csharp_api/` 目录(SWIG 自动生成)
### Python Demo
PyQt5 图形化上位机,支持连接、伺服控制、队列运动(movJ/movL)、servo_move 跟踪运动、作业文件上传下载、位置监控等功能。
- **开发环境:** Python 3.8+,PyQt5
- **核心文件:** `main.py`、`Widget.py`、`nrc_interface.py`
- **本地库:** `_nrc_host.so`(随 SDK 包分发)
---
## 控制器二次开发 Demo
包含自定义指令的控制器侧实现示例。
---
## 示教器二次开发 Demo
包含自定义指令的示教器侧 UI 实现示例。
---
## JSON 协议二次开发 Demo
基于 JSON 文本协议直接与控制器通信的示例。
---
## HAL二次开发 Demo
---
## 相关下载
所有 Demo 源码及 SDK 包统一在[下载页](./12.相关下载.md)提供。
---
# 一、MinGW + Qt Creator(Windows)
> 原文:https://open.inexbot.com/zh/04.%E4%B8%8A%E4%BD%8D%E6%9C%BA/01.C++/01.%E6%96%87%E6%A1%A3/01.%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA/01.MinGW%20%E7%8E%AF%E5%A2%83.html
> **重要:** SDK 与编译器的 ABI 必须匹配。MinGW 编译的库与 MSVC 编译的库**不能混用**。下载 SDK 时请选择 MinGW 版本。
## 1.1 创建项目
打开 Qt Creator,创建一个 **Qt Widgets Application**,命名为 `qt_demo`。
## 1.2 导入 SDK
在项目根目录下新建 `libs` 文件夹,将 SDK 文件拷贝进去:
```text
项目根目录/
├── libs/
│ ├── include/
│ │ ├── c_interface/
│ │ ├── cpp_interface/
│ │ └── parameter/
│ ├── libnrc_host.dll.a # MinGW 导入库
│ └── nrc_host.dll # 运行时动态库
├── main.cpp
├── mainwindow.h
├── mainwindow.cpp
├── mainwindow.ui
└── qt_demo.pro
```
在 `qt_demo.pro` 末尾添加 SDK 引用:
```makefile
# 1. 指定头文件路径
INCLUDEPATH += $$PWD/libs/include
# 2. 指定库文件搜索路径
LIBS += -L$$PWD/libs
# 3. 链接库文件(注意名称格式)
LIBS += -lnrc_host
# 4. 生成后自动复制 DLL 到输出目录
win32 {
DLL_SOURCE = $$shell_path($${PWD}\\libs\\nrc_host.dll)
DLL_TARGET = $$shell_path($${OUT_PWD}\\\\)
QMAKE_POST_LINK += $$quote(cmd /c copy /Y $$quote($$DLL_SOURCE) $$quote($$DLL_TARGET))
}
```
## 1.3 编写最小连接代码
### (1) 修改 nrc_api.h 头文件包含路径
由于 SDK 的 `nrc_api.h` 中引用的头文件路径可能与实际目录结构不一致,需要修改 `libs/include/cpp_interface/nrc_api.h`,将其中引用的头文件路径改为直接使用文件名(因为它们与 `nrc_api.h` 位于同一 `cpp_interface/` 目录下):
```cpp
#ifndef INCLUDE_CPP_INTERFACE_NRC_API_H_
#define INCLUDE_CPP_INTERFACE_NRC_API_H_
#include "nrc_craft_conveyor_belt_track.h"
#include "nrc_craft_vision.h"
#include "nrc_craft_weld.h"
#include "nrc_interface.h"
#include "nrc_io.h"
#include "nrc_job_operate.h"
#include "nrc_modbus.h"
#include "nrc_queue_operate.h"
#include "nrc_track.h"
#include "nrc_dual_arm.h"
#endif /* INCLUDE_API_NRC_API_H_ */
```
### (2) 修改 cpp_interface/ 下头文件对 parameter/ 目录的引用路径
`nrc_api.h` 所包含的各个头文件(如 `nrc_craft_conveyor_belt_track.h` 等)内部还会引用 `parameter/` 目录下的头文件。由于这些头文件位于 `cpp_interface/` 目录下,需要使用相对路径 `../parameter/` 来定位 `parameter/` 目录。
以 `nrc_craft_conveyor_belt_track.h` 为例,将其内部的头文件引用修改为如下形式:
```cpp
#ifndef INCLUDE_CPP_INTERFACE_NRC_CRAFT_CONVEYOR_BELT_TRACK_H_
#define INCLUDE_CPP_INTERFACE_NRC_CRAFT_CONVEYOR_BELT_TRACK_H_
#include "../parameter/nrc_define.h"
#include "../parameter/nrc_craft_conveyor_belt_track_parameter.h"
......
```
> **提示:** 需要对 `cpp_interface/` 目录下**所有**头文件逐一检查,凡是引用了 `parameter/` 目录下头文件的地方,都要统一使用 `../parameter/xxx.h` 的相对路径格式。常见的被引用文件包括 `nrc_define.h`、各类 `*_parameter.h` 等。
### (3) 编写连接测试代码
在 `mainwindow.cpp` 中添加头文件和连接测试:
```cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "libs/include/cpp_interface/nrc_api.h" // SDK 头文件
#include
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
// 1. 连接控制器
SOCKETFD fd = connect_robot("192.168.1.15", "6001");
if (fd <= 0) {
ui->statusbar->showMessage("连接失败,请检查 IP 和端口");
return;
}
// 2. 等待连接就绪
while (get_connection_status(fd) != 0) {
QApplication::processEvents();
}
ui->statusbar->showMessage("连接成功!fd=" + QString::number(fd));
qDebug() << "SDK 版本:" << QString::fromStdString(get_library_version());
}
MainWindow::~MainWindow()
{
delete ui;
}
```
## 1.4 编译运行
选择 MinGW 编译套件,点击运行。如果状态栏显示"连接成功",说明 SDK 配置正确。
> **验证标准:** 不是"头文件能引入",而是**实际连接控制器成功**。如果编译报链接错误,请检查 `LIBS` 路径和库文件名是否匹配。
## 下一步
完成连接测试后,请浏览[接口示例](../../03.示例/index.md)了解更多 API 用法。
---
# 二、MSVC + Visual Studio(Windows)
> 原文:https://open.inexbot.com/zh/04.%E4%B8%8A%E4%BD%8D%E6%9C%BA/01.C++/01.%E6%96%87%E6%A1%A3/01.%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA/02.MSVC%20%E7%8E%AF%E5%A2%83.html
**环境要求:** Visual Studio 2019+,MSVC 编译工具链,x64 架构。
> **重要:** SDK 与编译器的 ABI 必须匹配。MSVC 编译的库与 MinGW 编译的库**不能混用**。下载 SDK 时请选择 MSVC 版本。
## 2.1 创建项目
选择 **C++ 控制台应用** 创建,命名为 `cpp_demo`。
## 2.2 导入 SDK
将 SDK 文件拷贝到项目目录:
```text
项目根目录/
├── libs/
│ ├── include/
│ │ ├── c_interface/
│ │ ├── cpp_interface/
│ │ └── parameter/
│ ├── nrc_host.lib # MSVC 导入库
│ └── nrc_host.dll # 运行时动态库
└── cpp_demo.cpp
└── cpp_demo.aps
└── cpp_demo.rc
└── cpp_demo.vcxproj
└── resource.h
```
## 2.3 配置 Visual Studio
右键项目 → **属性 (Properties)**,按以下步骤配置:
### (1) 添加头文件路径
**Configuration Properties → C/C++ → 常规 → 附加包含目录**
```text
$(ProjectDir)libs\include
```
### (2) 添加库路径
**Configuration Properties → 链接器 → 常规 → 附加库目录**
```text
$(ProjectDir)libs
```
### (3) 链接库文件
**Configuration Properties → 链接器 → 输入 → 附加依赖项**
```text
nrc_host.lib
```
### (4) 生成后自动复制 DLL
**Configuration Properties → 生成事件 → 生成后事件 → 命令行**
```text
copy "$(ProjectDir)libs\nrc_host.dll" "$(OutDir)"
```
## 2.4 编写最小连接代码
### (1) 修改 nrc_api.h 头文件包含路径
由于 SDK 默认的 `nrc_api.h` 中引用的头文件路径是相对于 `cpp_interface/` 目录的,而 Visual Studio 项目的附加包含目录配置为 `libs\include`,因此需要修改 `libs\include\cpp_interface\nrc_api.h` 文件的内容,将其中引用的头文件路径改为相对于 `cpp_interface/` 目录的形式:
```cpp
#ifndef INCLUDE_CPP_INTERFACE_NRC_API_H_
#define INCLUDE_CPP_INTERFACE_NRC_API_H_
#include "nrc_craft_conveyor_belt_track.h"
#include "nrc_craft_vision.h"
#include "nrc_craft_weld.h"
#include "nrc_interface.h"
#include "nrc_io.h"
#include "nrc_job_operate.h"
#include "nrc_modbus.h"
#include "nrc_queue_operate.h"
#include "nrc_track.h"
#include "nrc_dual_arm.h"
#endif /* INCLUDE_API_NRC_API_H_ */
```
> **提示:** 确保每个 `#include` 都直接使用文件名,不带额外的路径前缀,因为这些头文件都与 `nrc_api.h` 位于同一 `cpp_interface/` 目录下。
### (2) 修改 cpp_interface/ 下头文件对 parameter/ 目录的引用路径
`nrc_api.h` 所包含的各个头文件(如 `nrc_craft_conveyor_belt_track.h`、`nrc_interface.h` 等)内部还会引用 `parameter/` 目录下的头文件。由于这些头文件位于 `cpp_interface/` 目录下,需要使用相对路径 `../parameter/` 来定位 `parameter/` 目录。
以 `nrc_craft_conveyor_belt_track.h` 为例,将其内部的头文件引用修改为如下形式:
```cpp
#ifndef INCLUDE_CPP_INTERFACE_NRC_CRAFT_CONVEYOR_BELT_TRACK_H_
#define INCLUDE_CPP_INTERFACE_NRC_CRAFT_CONVEYOR_BELT_TRACK_H_
#include "../parameter/nrc_define.h"
#include "../parameter/nrc_craft_conveyor_belt_track_parameter.h"
......
```
> **提示:** 需要对 `cpp_interface/` 目录下**所有**头文件逐一检查,凡是引用了 `parameter/` 目录下头文件的地方,都要统一使用 `../parameter/xxx.h` 的相对路径格式。常见的被引用文件包括 `nrc_define.h`、各类 `*_parameter.h` 等。
### (3) 编写连接测试代码
```cpp
#include
#include
#include "libs/include/cpp_interface/nrc_api.h"
int main()
{
// 1. 连接控制器
SOCKETFD fd = connect_robot("192.168.1.15", "6001");
if (fd <= 0) {
std::cout << "连接失败,请检查 IP 和端口" << std::endl;
return 1;
}
// 2. 等待连接就绪
while (get_connection_status(fd) != 0) {
Sleep(200);
}
std::cout << "连接成功!fd = " << fd << std::endl;
std::cout << "SDK 版本: " << get_library_version() << std::endl;
// 3. 断开连接
disconnect_robot(fd);
return 0;
}
```
## 2.5 编译运行
选择 **Release x64** 配置,点击"本地 Windows 调试器"。控制台输出 "连接成功" 即表示配置正确。
> 如果使用 Debug 版本的库,需将配置切换为 Debug x64。
## 下一步
完成连接测试后,请浏览[接口示例](../../03.示例/index.md)了解更多 API 用法。
---
# 三、Linux + GCC
> 原文:https://open.inexbot.com/zh/04.%E4%B8%8A%E4%BD%8D%E6%9C%BA/01.C++/01.%E6%96%87%E6%A1%A3/01.%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA/03.Linux%20%E7%8E%AF%E5%A2%83.html
## 3.1 目录结构
```text
项目目录/
├── include/
│ ├── c_interface/ # C 接口头文件
│ ├── cpp_interface/ # C++ 接口主入口
│ └── parameter/ # 参数结构体头文件
├── lib/
│ └── libnrc_host.so # Linux 动态库
├── src/
│ └── main.cpp
└── Makefile
```
## 3.2 Makefile
```makefile
TARGET=demo
all:
g++ -o $(TARGET) src/*.cpp -I./include -L./lib -lnrc_host -lpthread -lm -ldl -lrt -lstdc++ -std=c++11 -fPIC
clean:
rm -f $(TARGET)
```
## 3.3 运行时动态库路径
编译成功后运行前,需要让系统能找到 `libnrc_host.so`:
```bash
# 方式一:设置 LD_LIBRARY_PATH
export LD_LIBRARY_PATH=$PWD/lib:$LD_LIBRARY_PATH
./demo
# 方式二:将库复制到系统目录
sudo cp lib/libnrc_host.so /usr/local/lib/
sudo ldconfig
./demo
```
## 3.4 头文件路径与最小连接代码
### 3.4.1 头文件包含路径
SDK 头文件之间存在两层 `#include` 关系,集成到自有工程时若目录结构与 3.1 节不同,需要同步调整这些路径,否则编译会报 `No such file or directory`。
**第一层:`nrc_api.h` 包含同目录下的其他接口头文件**
`nrc_api.h` 与下列头文件同位于 `include/cpp_interface/` 目录,直接用文件名引用即可:
```cpp
// include/cpp_interface/nrc_api.h
#include "nrc_craft_conveyor_belt_track.h"
#include "nrc_craft_vision.h"
#include "nrc_craft_weld.h"
#include "nrc_interface.h"
#include "nrc_io.h"
#include "nrc_job_operate.h"
#include "nrc_modbus.h"
#include "nrc_queue_operate.h"
#include "nrc_track.h"
#include "nrc_dual_arm.h"
```
**第二层:被包含的头文件再引用 `parameter/` 目录下的参数头文件**
由于 `cpp_interface/` 与 `parameter/` 是 `include/` 下的平级目录,被包含的头文件使用 `../parameter/` 相对路径引用参数定义。以 `nrc_craft_conveyor_belt_track.h` 为例:
```cpp
// include/cpp_interface/nrc_craft_conveyor_belt_track.h
#include "../parameter/nrc_define.h"
#include "../parameter/nrc_craft_conveyor_belt_track_parameter.h"
```
> 若你的工程把 `parameter/` 放到了其他位置(如 `cpp_interface/` 内部,或重命名了目录),需要同步修改这些 `#include` 路径。
### 3.4.2 最小连接代码
```cpp
#include
#include
#include "../include/cpp_interface/nrc_api.h"
int main() {
std::cout << "正在连接控制器..." << std::endl;
SOCKETFD fd = connect_robot("192.168.1.15", "6001");
if (fd <= 0) {
std::cout << "连接失败" << std::endl;
return 1;
}
// 等待连接就绪
while (get_connection_status(fd) != 0) {
usleep(200000);
}
std::cout << "连接成功: " << fd << std::endl;
std::cout << "SDK 版本: " << get_library_version() << std::endl;
std::cout << "连接状态: " << get_connection_status(fd) << std::endl;
// 断开连接
disconnect_robot(fd);
return 0;
}
```
## 3.5 编译运行
```bash
make
export LD_LIBRARY_PATH=$PWD/lib:$LD_LIBRARY_PATH
./demo
```
输出 `连接成功: 1` 即表示配置正确。
## 下一步
完成连接测试后,请浏览[接口示例](../../03.示例/index.md)了解更多 API 用法。
---
# 架构说明
> 原文:https://open.inexbot.com/zh/04.%E4%B8%8A%E4%BD%8D%E6%9C%BA/01.C++/01.%E6%96%87%E6%A1%A3/02.%E6%9E%B6%E6%9E%84%E8%AF%B4%E6%98%8E.html
`libnrc_host` 是基于 Socket API 通讯协议封装的 C++ 动态库,提供网络接口供上位机快速集成控制器功能。
## 系统架构
```text
┌──────────────────────┐
│ 上位机应用程序 │
│ (C++/C#/Python 通过 │
│ SDK 调用接口) │
└──────────┬───────────┘
│ TCP Socket (JSON 协议)
▼
┌──────────────────────┐
│ 纳博特控制器 │
│ Linux 实时系统 │
└──────────────────────┘
```
上位机通过 TCP Socket 连接控制器的指定端口,以 JSON 文本格式发送指令和接收响应。`libnrc_host` 封装了网络通信、JSON 编解码和接口调用,开发者无需关心底层通信细节。
## SDK 组成
| 组件 | 路径 | 说明 |
|------|------|------|
| `libnrc_host.dll` / `.so` | SDK 根目录 | 核心动态库,封装全部接口 |
| `include/cpp_interface/` | 头文件目录 | C++ 接口声明(按模块拆分) |
| `include/parameter/` | 参数头文件目录 | 数据结构与枚举定义 |
| `include/c_interface/` | C 接口目录 | 跨语言 ABI 兼容封装 |
## 头文件模块
| 头文件 | 模块 |
|--------|------|
| `nrc_api.h` | 主入口,聚合所有接口 |
| `nrc_interface.h` | 连接管理、伺服控制、运动控制、工具手、坐标系、标定 |
| `nrc_io.h` | 数字/模拟 IO、远程 IO、安全 IO |
| `nrc_modbus.h` | Modbus 主站(TCP/RTU) |
| `nrc_track.h` | 轨迹记录与回放 |
| `nrc_dual_arm.h` | 双臂机器人 |
| `nrc_vfd_ctr.h` | VFD 主轴变频器 |
| `nrc_job_operate.h` | 作业文件管理、运动/逻辑指令插入 |
| `nrc_queue_operate.h` | 队列运动模式 |
| `nrc_craft_weld.h` | 焊接工艺 |
| `nrc_craft_pallet.h` | 码垛工艺 |
| `nrc_craft_vision.h` | 视觉工艺 |
| `nrc_craft_laser_cutting.h` | 激光切割工艺 |
| `nrc_craft_conveyor_belt_track.h` | 传送带跟踪工艺 |
| `nrc_define.h` | 宏定义、枚举、基础数据结构 |
## 端口体系
| 端口 | 用途 |
|------|------|
| 6000 | 控制/查询:上下电、模式切换、状态查询(通常示教器使用) |
| 6001 | 上位机 SDK 命令端口:运动和程序控制(C++/C#/Python SDK 使用) |
| 7000 | 伺服跟踪数据:servo_move、点位运动控制、状态回调 |
| 5000 | 文件传输:作业文件上传/下载/备份 |
> **SDK 用户通常使用 6001 端口。** servo_move 等跟踪运动需要额外连接 7000 端口。
## ABI 兼容性
C++ SDK 分为两个 ABI 版本,**不能混用**:
| 编译器 | 平台 | 导入库 |
|--------|------|--------|
| MSVC (Visual Studio 2019+) | Windows x64 | `nrc_host.lib` |
| MinGW (GCC 8.3+) | Windows x64 | `libnrc_host.dll.a` |
| GCC (8.3+) | Linux x64 / ARM64 | `libnrc_host.so` |
## 通信模型
`libnrc_host` 采用 JSON 文本协议进行请求/响应交互,支持两种通信模式:
| 模式 | 说明 | 典型场景 |
|------|------|----------|
| 同步请求/响应 | 上位机发送指令,阻塞等待控制器回复 | 参数设置、状态查询、标定计算 |
| 异步回调 | 控制器主动推送数据,通过注册的回调函数接收 | 7000 端口伺服跟踪、状态变化通知 |
**请求流程:** 上位机构造 JSON 指令 → TCP 发送 → 控制器解析执行 → 返回 JSON 响应 → SDK 解析为返回值/输出参数。
**回调机制:** 调用 `send_message` / `recv_message` / `set_receive_error_or_warnning_message_callback` 等注册回调函数后,控制器事件到达时 SDK 在内部线程触发回调,开发者无需主动轮询。
## 多机器人支持
控制器支持 1~4 台机器人协同工作,SDK 提供两种调用方式:
| 方式 | 接口 | 说明 |
|------|------|------|
| 单机器人 | `robot_xxx(...)`(如 `robot_movej`) | 默认操作 1 号机器人 |
| 多机器人 | `robot_xxx_robot(fd, robotNum, ...)` | 通过 `robotNum` 参数指定机器人编号(1-4) |
> 多机器人并行需先调用 `set_robots_parallel` 开启并行模式;外部轴跟随主机器人编号管理。
## 工艺扩展架构
工艺类功能以独立头文件形式提供,通过工艺号(1-9)管理多套参数:
| 头文件 | 工艺 | 典型接口 |
|--------|------|----------|
| `nrc_craft_weld.h` | 焊接 | 焊接参数配置、送丝/退丝/送气、摆焊 |
| `nrc_craft_pallet.h` | 码垛 | 码垛参数设置、状态查询 |
| `nrc_craft_vision.h` | 视觉 | 视觉参数、标定、目标点计算 |
| `nrc_craft_laser_cutting.h` | 激光切割 | 全局/工艺/模拟量/IO 参数 |
| `nrc_craft_conveyor_belt_track.h` | 传送带跟踪 | 传送参数、编码器、PID 同步 |
工艺参数采用"设置/查询"成对接口,设置前需先指定工艺号,与示教器工艺配置保持数据一致。
## 数据流说明
| 端口 | 数据方向 | 内容 | 典型接口 |
|------|----------|------|----------|
| 6001 | 双向 | 命令流:控制指令与响应(JSON) | `robot_movel`、`set_servo_state` |
| 7000 | 双向 | 伺服数据流:点位跟踪、状态回调 | `servo_move`、`servo_point_position_motion_control` |
| 5000 | 双向 | 文件流:作业文件上传/下载/备份 | `file_upload` 等文件接口 |
> 6001 为 SDK 主命令端口;涉及伺服跟踪(如外部点移动、点位运动控制)需同时连接 7000 端口。
---
# 核心概念
> 原文:https://open.inexbot.com/zh/04.%E4%B8%8A%E4%BD%8D%E6%9C%BA/01.C++/01.%E6%96%87%E6%A1%A3/03.%E6%A0%B8%E5%BF%83%E6%A6%82%E5%BF%B5.html
使用 C++ SDK 前需要了解以下关键概念。
## 连接生命周期
```text
connect_robot() → 等待连接就绪 → 操作(伺服/运动/查询)
↓
disconnect_robot()
```
1. **连接:** `connect_robot(ip, port)` 返回 socket 文件描述符(SOCKETFD),失败返回 ≤0
2. **等待就绪:** 连接成功后需轮询 `get_connection_status()` 直到返回 `SUCCESS(0)`
3. **操作:** 通过 socketFd 调用所有接口
4. **断开:** `disconnect_robot(fd)` 主动断开
## 坐标系
| 坐标系 | coord 参数 | 说明 |
|--------|-----------|------|
| 关节坐标 | 0 | 各关节电机的角度位置 |
| 直角坐标 | 1 | 末端相对于机器人底座的世界位置 |
| 工具坐标 | 2 | 末端安装工具后的工具端点位置 |
| 用户坐标 | 3 | 在工作台上自定义的局部坐标系 |
## 伺服状态机
```text
停止(0) → 就绪(1) → 运行(3)
↑ ↓ ↓
←←←←← 报警(2) ←←←←←←
```
| 状态码 | 说明 |
|--------|------|
| 0 | 停止 — 未使能,可进行清错操作 |
| 1 | 就绪 — 已清错,等待上电 |
| 3 | 运行 — 已上电,可执行运动指令 |
> 上电前必须先设为就绪状态:`set_servo_state(fd, 1)` → `set_servo_poweron(fd)`
## 运动模式
| 模式 | 函数 | 特点 |
|------|------|------|
| MoveJ | `robot_movej` | 关节空间点到点,快速但路径不定 |
| MoveL | `robot_movel` | 笛卡尔空间直线运动 |
| Jog | `robot_start_jogging` / `robot_stop_jogging` | 手动点动 |
| Queue | `queue_motion_push_back_*` + `queue_motion_send_to_controller` | 无作业文件队列运动 |
| Servo | `servo_move` | 7000 端口伺服跟踪 |
## 返回值
| 返回值 | 含义 |
|--------|------|
| `SUCCESS` (0) | 调用成功 |
| 其他值 | 错误码,具体含义参见错误码表 |
> 异步接口返回值仅表示"指令已接收",不代表执行完毕。实际执行结果通过回调或状态查询获取。
## 线程安全
- 所有接口以 socketFd 区分连接,不同 socketFd 之间线程安全
- 同一 socketFd 的并发调用需自行加锁
- 回调函数内不应执行阻塞或耗时操作
## 坐标系深化
### 工具坐标系
工具坐标系定义末端工具的中心点(TCP)位置与姿态,默认工具坐标系原点为法兰盘中心,+Z 方向垂直法兰向外。
**工具手参数:**
| 参数 | 含义 | 单位 |
|------|------|------|
| X/Y/Z | 工具 TCP 相对法兰中心的偏移量 | mm |
| A/B/C | 工具相对法兰坐标系绕 X/Y/Z 轴的旋转角 | °/rad |
**标定方式选择:**
| 方式 | 适用场景 | 说明 |
|------|----------|------|
| 6点标定 | 已激光标定+焊枪 | 校准工具手尺寸+姿态,C轴精度较好 |
| 7点标定 | 6点标定 A/B 轴误差大 | 校准尺寸+姿态,A/B 轴精度较好 |
| 12点标定 | 未激光标定 | 校准零点+工具手尺寸 |
| 15点标定 | 高精度要求 | 校准零点+尺寸+姿态 |
| 20点标定 | 零点丢失 | 校准零点+工具手尺寸 |
### 用户坐标系
用户坐标系是工作台上自定义的局部坐标系,默认 User0 与直角坐标系重合,新用户坐标系基于 User0 变换得到。
**用户坐标参数:**
| 参数 | 含义 | 单位 |
|------|------|------|
| X/Y/Z | 用户坐标原点相对机器人基座原点的偏移 | mm |
| A/B/C | 用户坐标相对直角坐标系绕 X/Y/Z 轴的旋转角 | °/rad |
**标定步骤:**
1. 末梢移到期望的用户坐标原点位置,标定原点
2. 向期望 X 轴正方向移动任意距离,标定 X 轴
3. 向期望 Y 轴正方向移动任意距离,标定 Y 轴
4. 计算并保存,提示成功后标定完成
## 标定体系
| 标定类型 | 用途 | 相关接口 |
|----------|------|----------|
| 工具手标定(6/7/12/15/20点) | 确定 TCP 偏移与姿态 | `tool_hand_7_point_calibrate` 等 |
| 用户坐标标定 | 建立工作台局部坐标系 | `calibration_oxy`、`set_user_coordinate_data`、`calculate_user_coordinate` |
| 零点标定 | 设置/偏移电机单圈零点 | `set_axis_zero_position`、`set_zero_pos_deviation` |
| 4点标定(SCARA) | 计算杆长和轴零点偏移,填入 DH 参数 | `set_four_point_mark`、`four_point_calculation`、`set_result_for_DH` |
| 20点标定 | 高精度校准(支持 2/12/15/20/21 点法) | `tool_hand_2_or_20_point_calibrate` 等 |
> 标定前将法兰盘平行于水平面,标定过程中保持参考点固定;计算结果大于 1 需重新标定。
## 工艺系统
工艺号(craftID,1-9)用于管理同一工艺的多套参数,示教器与 SDK 共享同一套工艺数据。
| 工艺 | 工艺号 | 参数读写方式 |
|------|--------|--------------|
| 焊接 | 1-9 | `weld_set_config` / `weld_get_config` + 摆焊参数 |
| 码垛 | 1-9 | `pallet_set_running_state` / 状态查询 |
| 视觉 | 0-98 | `vision_set_basic_parameter` / 标定/触发 |
| 激光切割 | 1-9 | 全局/工艺/模拟量/IO 四类参数 |
| 传送带跟踪 | 1-9 | 传送参数 + 编码器 + PID |
> 使用前先设置当前工艺号,再读写参数,与示教器配置保持一致。
## 点位与变量
### 全局点位(GP/GE)
| 类型 | 说明 | posInfo 长度 |
|------|------|-------------|
| GP | 全局点位(机器人) | 14 |
| GE | 全局点位(含外部轴) | 21 |
**posInfo 格式(14 长度):**
| 索引 | 含义 |
|------|------|
| [0] | 坐标系:0=关节,1=直角,2=工具,3=用户 |
| [1] | 角度单位:0=角度制,1=弧度制 |
| [2] | 形态(SCARA:1=左手,2=右手) |
| [3] | 工具手坐标序号 |
| [4] | 用户坐标序号 |
| [5]-[6] | 备用 |
| [7]-[13] | 点位信息(关节角或 XYZ+姿态) |
### 全局变量
| 系列 | 类型 | 说明 |
|------|------|------|
| GI | int | 全局整数变量 |
| GD | double | 全局浮点变量 |
| GB | bool | 全局布尔变量 |
## 错误处理机制
### 返回值语义
| 返回值 | 含义 |
|--------|------|
| `SUCCESS` (0) | 调用成功 |
| 负数 | 通信/执行错误(-1 接收失败、-2 断开连接、-6 超时等) |
| 其他值 | 业务错误码,参见错误码表 |
### 异步接口
异步接口返回值仅表示"指令已接收",实际执行结果通过:
- **回调函数**:`recv_message`、`set_receive_error_or_warnning_message_callback` 等注册
- **状态查询**:`get_robot_running_state`、`get_servo_state` 等轮询
### 报警与清错
- 伺服报警后进入报警状态(状态码 2)
- 清错操作:`clear_error`,清错后需重新下电再上电
- 实时错误信息通过 `set_receive_error_or_warnning_message_callback` 回调接收
---
# C++ SDK 接口说明
> 原文:https://open.inexbot.com/zh/04.%E4%B8%8A%E4%BD%8D%E6%9C%BA/01.C++/02.%E6%8E%A5%E5%8F%A3/00.%E5%A4%B4%E6%96%87%E4%BB%B6%E4%B8%8E%E7%AB%AF%E5%8F%A3.html
## 头文件一览
| 头文件 | 路径 | 说明 |
| ------------------------------------------- | -------------------------- | ---------------------------------------------------------- |
| `nrc_api.h` | `interface/cpp/interface/` | 主入口头文件,聚合所有接口 |
| `nrc_interface.h` | `interface/cpp/interface/` | 基础连接、伺服控制、运动控制、工具手、用户坐标、全局变量等 |
| `nrc_io.h` | `interface/cpp/interface/` | 数字/模拟 IO 控制、远程 IO、安全 IO |
| `nrc_modbus.h` | `interface/cpp/interface/` | Modbus 主站通讯(TCP/RTU) |
| `nrc_track.h` | `interface/cpp/interface/` | 轨迹记录与回放 |
| `nrc_dual_arm.h` | `interface/cpp/interface/` | 双臂机器人用户坐标 |
| `nrc_vfd_ctr.h` | `interface/cpp/interface/` | VFD 主轴变频器控制 |
| `nrc_job_operate.h` | `interface/cpp/interface/` | 作业文件管理、运动/逻辑指令插入 |
| `nrc_queue_operate.h` | `interface/cpp/interface/` | 队列运动模式控制 |
| `nrc_craft_weld.h` | `interface/cpp/interface/` | 焊接工艺(弧焊、摆焊) |
| `nrc_craft_pallet.h` | `interface/cpp/interface/` | 码垛工艺 |
| `nrc_craft_vision.h` | `interface/cpp/interface/` | 视觉工艺(标定、参数配置) |
| `nrc_craft_laser_cutting.h` | `interface/cpp/interface/` | 激光切割工艺 |
| `nrc_craft_conveyor_belt_track.h` | `interface/cpp/interface/` | 传送带跟踪工艺 |
| `nrc_define.h` | `interface/cpp/parameter/` | 宏定义、枚举、基础数据结构 |
| `nrc_interface_parameter.h` | `interface/cpp/parameter/` | 接口相关结构体 |
| `nrc_io_parameter.h` | `interface/cpp/parameter/` | IO 相关结构体 |
| `nrc_modbus_parameter.h` | `interface/cpp/parameter/` | Modbus 参数结构体 |
| `nrc_craft_weld_parameter.h` | `interface/cpp/parameter/` | 焊接参数结构体 |
| `nrc_craft_vision_parameter.h` | `interface/cpp/parameter/` | 视觉参数结构体 |
| `nrc_craft_laser_cutting_parameter.h` | `interface/cpp/parameter/` | 激光切割参数结构体 |
| `nrc_craft_conveyor_belt_track_parameter.h` | `interface/cpp/parameter/` | 传送带跟踪参数结构体 |
---
## 端口说明
| 端口 | 用途 |
| ---- | ------------------------------------------------ |
| 6000 | 控制/查询:上下电、模式切换、状态查询 |
| 6001 | 示教编程:作业编辑和程序控制 |
| 7000 | 伺服跟踪数据:servo_move、点位运动控制、状态回调 |
---
---
# 一、基础连接与系统接口(nrc_interface.h)
> 原文:https://open.inexbot.com/zh/04.%E4%B8%8A%E4%BD%8D%E6%9C%BA/01.C++/02.%E6%8E%A5%E5%8F%A3/01.%E5%9F%BA%E7%A1%80%E8%BF%9E%E6%8E%A5%E4%B8%8E%E7%B3%BB%E7%BB%9F%E6%8E%A5%E5%8F%A3.html
## 1.1 连接管理
### get_library_version
获取库版本信息。
**函数签名:**
```cpp
std::string get_library_version();
```
**返回值:** 库版本相关信息字符串。
---
### connect_robot
连接机器人控制器。同步方式,函数会阻塞直到返回连接结果。
**函数签名:**
```cpp
SOCKETFD connect_robot(const std::string& ip, const std::string& port);
```
**参数说明:**
| 参数 | 类型 | 输入/输出 | 说明 |
|------|------|----------|------|
| ip | const std::string& | 输入 | 控制器 IP 地址,如 "192.168.1.13" |
| port | const std::string& | 输入 | 端口号,如 "6001" |
**返回值:** `SOCKETFD`(int)—— 连接成功返回 socket 文件描述符;-1 表示连接失败。
---
### disconnect_robot
断开与控制器连接。
**函数签名:**
```cpp
Result disconnect_robot(SOCKETFD socketFd);
```
---
### get_connection_status
获取控制器连接状态。
**函数签名:**
```cpp
Result get_connection_status(SOCKETFD socketFd);
```
---
### set_reconnect
设置是否开启断开后自动重连功能,默认关闭。
**函数签名:**
```cpp
Result set_reconnect(SOCKETFD socketFd, bool reconnect);
```
---
### set_reconnect_callback
设置重连成功后的回调函数。
**函数签名:**
```cpp
Result set_reconnect_callback(SOCKETFD socketFd, void(*function)());
```
---
## 1.2 消息通讯
### send_message
向控制器发送一条自定义消息。
**函数原型:**
```cpp
Result send_message(SOCKETFD socketFd, int messageID, const std::string& message);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| messageID | int | 消息 ID |
| message | const std::string& | 消息内容 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
**使用示例:**
```cpp
// 向控制器发送 ID=100 的消息
Result result = send_message(fd, 100, "hello controller");
```
---
### recv_message
注册消息接收回调,当收到控制器消息时触发。
**函数原型:**
```cpp
Result recv_message(SOCKETFD socketFd, void(*callback)(int messageID, const char* message));
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| callback | void(*)(int, const char*) | 回调函数,参数为消息 ID 和消息内容 |
> 回调函数内不能做耗时操作或阻塞。
**使用示例:**
```cpp
void on_message(int messageID, const char* message) {
printf("收到消息 ID=%d: %s\n", messageID, message);
}
Result result = recv_message(fd, on_message);
```
---
## 1.3 伺服控制
### set_axis_sdo
设置伺服命令字(SDO,Service Data Object)。用于直接向伺服驱动器写入 CANopen SDO 对象字典数据。
**函数原型:**
```cpp
Result set_axis_sdo(SOCKETFD socketFd, int axisNum, unsigned int index, unsigned int subindex, int cmdvalue, unsigned int size);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| axisNum | int | 机器人的轴编号 |
| index | unsigned int | 命令字编码(对象字典索引) |
| subindex | unsigned int | 命令字子编码(对象字典子索引) |
| cmdvalue | int | 要设置进去的值 |
| size | unsigned int | 命令字对应的值的字节数 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
**使用示例:**
```cpp
// 向 1 号轴写入对象字典 0x6060 子索引 0,值 8(CSP 模式),1 字节
Result result = set_axis_sdo(fd, 1, 0x6060, 0x00, 8, 1);
```
---
### set_robots_parallel
设置多机器人并行模式。
**函数原型:**
```cpp
Result set_robots_parallel(SOCKETFD socketFd, bool open);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| open | bool | true=开启并行,false=关闭 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### clear_error / clear_error_robot
伺服清错。
**函数原型:**
```cpp
Result clear_error(SOCKETFD socketFd);
Result clear_error_robot(SOCKETFD socketFd, int robotNum);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
::: warning 注意
出错前如果处于伺服运行状态,清错后需要手动进行下电操作,释放控制器的占用状态才可以继续上电(清错后不能直接上电,先下电再上电)。
:::
---
### set_servo_state / set_servo_state_robot
设置伺服状态(停止/就绪)。
**函数原型:**
```cpp
Result set_servo_state(SOCKETFD socketFd, int state);
Result set_servo_state_robot(SOCKETFD socketFd, int robotNum, int state);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| state | int | 0=停止,1=就绪 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
::: warning 注意
- 设置伺服就绪前应确保系统没有错误(先调用 `clear_error`)
- 该函数只有伺服状态为 0(停止)或 1(就绪)时调用生效,状态为 2(报警)或 3(运行)时不能直接设置
:::
---
### get_servo_state / get_servo_state_robot
获取伺服状态。
**函数原型:**
```cpp
Result get_servo_state(SOCKETFD socketFd, int& status);
Result get_servo_state_robot(SOCKETFD socketFd, int robotNum, int& status);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| status | int& | 输出参数:0=停止,1=就绪,2=报警,3=运行 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
**使用示例:**
```cpp
int status = 0;
Result result = get_servo_state(fd, status);
if (result == SUCCESS) {
printf("伺服状态: %d (0=停止 1=就绪 2=报警 3=运行)\n", status);
}
```
---
### set_servo_poweron / set_servo_poweron_robot
机器人上电。
**函数原型:**
```cpp
Result set_servo_poweron(SOCKETFD socketFd);
Result set_servo_poweron_robot(SOCKETFD socketFd, int robotNum);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
::: warning 注意
调用该函数前需要先调用 `set_servo_state(fd, 1)` 将伺服设置为就绪状态;上电成功后调用 `get_servo_state` 返回 3(运行状态)。该函数只有伺服状态为 1(就绪)时调用生效。
:::
---
### set_servo_poweroff / set_servo_poweroff_robot
机器人下电。
**函数原型:**
```cpp
Result set_servo_poweroff(SOCKETFD socketFd);
Result set_servo_poweroff_robot(SOCKETFD socketFd, int robotNum);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
::: warning 注意
下电成功后调用 `get_servo_state` 返回 1(就绪状态)。该函数只有伺服状态为 3(运行)时调用生效。
:::
---
## 1.4 位置与状态查询
### get_current_position / get_current_position_robot
获取机器人当前位置。
**函数原型:**
```cpp
Result get_current_position(SOCKETFD socketFd, int coord, std::vector& pos);
Result get_current_position_robot(SOCKETFD socketFd, int robotNum, int coord, std::vector& pos);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| coord | int | 坐标系:0=关节,1=直角,2=工具,3=用户 |
| pos | std::vector\& | 输出参数,存储点位数据,长度 7 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
**使用示例:**
```cpp
std::vector pos(7);
Result result = get_current_position(fd, 1, pos); // 直角坐标
if (result == SUCCESS) {
printf("当前位置: X=%.2f Y=%.2f Z=%.2f\n", pos[0], pos[1], pos[2]);
}
```
---
### get_joint_position / get_joint_position_robot
获取机器人指定关节的直角坐标(24.03 版本专用)。
**函数原型:**
```cpp
Result get_joint_position(SOCKETFD socketFd, int axisNum, std::vector& pos);
Result get_joint_position_robot(SOCKETFD socketFd, int robotNum, int axisNum, std::vector& pos);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| axisNum | int | 指定需要查询的关节 |
| pos | std::vector\& | 输出参数,存储点位数据,长度 7 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### get_current_extra_position / get_current_extra_position_robot
获取机器人外部轴当前位置。
**函数原型:**
```cpp
Result get_current_extra_position(SOCKETFD socketFd, std::vector& pos);
Result get_current_extra_position_robot(SOCKETFD socketFd, int robotNum, std::vector& pos);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| pos | std::vector\& | 输出参数,点位数组,长度 5 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### get_current_positon_and_extra_position / get_current_positon_and_extra_position_robot
获取机器人和外部轴的当前位置。
**函数原型:**
```cpp
Result get_current_positon_and_extra_position(SOCKETFD socketFd, int coord, std::vector& pos);
Result get_current_positon_and_extra_position_robot(SOCKETFD socketFd, int robotNum, int coord, std::vector& pos);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| coord | int | 坐标系:0=关节,1=直角,2=工具,3=用户 |
| pos | std::vector\& | 输出参数,点位数组,长度 12(前 7 位机器人,后 5 位外部轴) |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### get_robot_running_state / get_robot_running_state_robot
获取机器人运行状态。
**函数原型:**
```cpp
Result get_robot_running_state(SOCKETFD socketFd, int& status);
Result get_robot_running_state_robot(SOCKETFD socketFd, int robotNum, int& status);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| status | int& | 输出参数:0=停止,1=暂停,2=运行 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
## 1.5 速度与模式控制
### set_speed / set_speed_robot
设置当前模式的速度(示教/运行/远程三种模式)。
**函数原型:**
```cpp
Result set_speed(SOCKETFD socketFd, int speed);
Result set_speed_robot(SOCKETFD socketFd, int robotNum, int speed);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| speed | int | 速度,范围 0\& pos);
Result get_user_coord_para_robot(SOCKETFD socketFd, int robotNum, int userNum, std::vector& pos);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| userNum | int | 用户坐标编号 |
| pos | std::vector\& | 输出参数,用户坐标参数(X/Y/Z 偏移 + A/B/C 旋转角) |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### set_user_coordinate_data / set_user_coordinate_data_robot
标定用户坐标(写入坐标数据)。
**函数原型:**
```cpp
Result set_user_coordinate_data(SOCKETFD socketFd, int userNum, std::vector pos);
Result set_user_coordinate_data_robot(SOCKETFD socketFd, int robotNum, int userNum, std::vector pos);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| userNum | int | 用户坐标编号 |
| pos | std::vector\ | 坐标数据(X/Y/Z 偏移 + A/B/C 旋转角) |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### calibration_oxy / calibration_oxy_robot
标定 OXY(三点法:原点、X 轴方向点、Y 轴方向点)。
**函数原型:**
```cpp
Result calibration_oxy(SOCKETFD socketFd, int userNum, std::string xyo);
Result calibration_oxy_robot(SOCKETFD socketFd, int robotNum, int userNum, std::string xyo);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| userNum | int | 用户坐标编号 |
| xyo | std::string | 标定类型:'X'(X 轴方向点)、'Y'(Y 轴方向点)、'O'(原点) |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
**使用示例:**
```cpp
// 三点法标定用户坐标
calibration_oxy(fd, 1, "O"); // 1. 末梢移到原点位置
calibration_oxy(fd, 1, "X"); // 2. 向 X 轴正方向移动任意距离
calibration_oxy(fd, 1, "Y"); // 3. 向 Y 轴正方向移动任意距离
```
---
### calculate_user_coordinate / calculate_user_coordinate_robot
计算用户坐标(OXY 三点标定完成后计算最终结果)。
**函数原型:**
```cpp
Result calculate_user_coordinate(SOCKETFD socketFd, int userNumber);
Result calculate_user_coordinate_robot(SOCKETFD socketFd, int robotNum, int userNum);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| userNum / userNumber | int | 用户坐标编号 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
**使用示例:**
```cpp
// OXY 三点标定后计算用户坐标
Result result = calculate_user_coordinate(fd, 1);
if (result == SUCCESS) {
printf("用户坐标计算成功\n");
}
```
---
## 1.8 全局点位与变量
### set_global_position / set_global_position_robot
设置全局 GP 点位。
**函数原型:**
```cpp
Result set_global_position(SOCKETFD socketFd, std::string posName, std::vector posInfo);
Result set_global_position_robot(SOCKETFD socketFd, int robotNum, std::string posName, std::vector posInfo);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| posName | std::string | 全局位置名,如 "GP0001" |
| posInfo | std::vector\ | 点位数据,长度 14:`[0]`坐标系(0=关节,1=直角,2=工具,3=用户);`[1]`角度制(0)/弧度制(1);`[2]`形态;`[3]`工具手坐标序号;`[4]`用户坐标序号;`[5][6]`备用;`[7-13]`点位信息 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### get_global_position / get_global_position_robot
查询全局 GP 点位。
**函数原型:**
```cpp
Result get_global_position(SOCKETFD socketFd, std::string posName, std::vector& pos);
Result get_global_position_robot(SOCKETFD socketFd, int robotNum, std::string posName, std::vector& pos);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| posName | std::string | 全局位置名,如 "GP0001" |
| pos | std::vector\& | 输出参数,全局点位数组,长度 14(前 7 位为点位坐标/姿态信息,后 7 位为机器人位置) |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### set_global_sync_position / set_global_sync_position_robot
设置全局 GE 点位(含外部轴)。
**函数原型:**
```cpp
Result set_global_sync_position(SOCKETFD socketFd, const std::string& posName, std::vector posInfo);
Result set_global_sync_position_robot(SOCKETFD socketFd, int robotNum, const std::string& posName, std::vector posInfo);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| posName | const std::string& | 全局位置名,如 "GE0001" |
| posInfo | std::vector\ | 点位数据,长度 21:`[0-6]`坐标系/角度制/形态/工具/用户坐标序号/备用;`[7-13]`机器人本体点位;`[14-20]`外部轴点位 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### get_global_sync_position / get_global_sync_position_robot
查询全局 GE 点位。
**函数原型:**
```cpp
Result get_global_sync_position(SOCKETFD socketFd, const std::string& posName, std::vector& pos);
Result get_global_sync_position_robot(SOCKETFD socketFd, int robotNum, const std::string& posName, std::vector& pos);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| posName | const std::string& | 全局位置名,如 "GE0001" |
| pos | std::vector\& | 输出参数,全局点位数组,长度 21(前 7 位坐标/姿态,中间 7 位机器人位置,后 7 位外部轴位置) |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### set_global_variant / set_global_variant_robot
设置全局变量(GI/GD/GB 系列)。
**函数原型:**
```cpp
Result set_global_variant(SOCKETFD socketFd, const std::string& varName, double varValue);
Result set_global_variant_robot(SOCKETFD socketFd, int robotNum, const std::string& varName, double varValue);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| varName | const std::string& | 全局变量名,如 "GI001"、"GD001"、"GB001" |
| varValue | double | 变量值 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
**使用示例:**
```cpp
// 设置全局整数变量 GI001 为 100
Result result = set_global_variant(fd, "GI001", 100);
```
---
### get_global_variant / get_global_variant_robot
查询全局变量。
**函数原型:**
```cpp
Result get_global_variant(SOCKETFD socketFd, const std::string& varName, double& vaule);
Result get_global_variant_robot(SOCKETFD socketFd, int robotNum, const std::string& varName, double& vaule);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| varName | const std::string& | 全局变量名,如 "GI001"、"GD001"、"GB001" |
| vaule | double& | 输出参数,变量值 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
## 1.9 零点与标定
### set_axis_zero_position / set_axis_zero_position_robot
设置零点位置(将当前轴位置设为零点)。
**函数原型:**
```cpp
Result set_axis_zero_position(SOCKETFD socketFd, int axis);
Result set_axis_zero_position_robot(SOCKETFD socketFd, int robotNum, int axis);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| axis | int | 轴号 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### set_zero_pos_deviation / set_zero_pos_deviation_robot
设置零点偏移。
**函数原型:**
```cpp
Result set_zero_pos_deviation(SOCKETFD socketFd, int axis, double shift);
Result set_zero_pos_deviation_robot(SOCKETFD socketFd, int robotNum, int axis, double shift);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| axis | int | 需要偏移的轴号 |
| shift | double | 偏移量,范围 -360° \< shift \< 360° |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### get_single_cycle / get_single_cycle_robot
获取单圈值(编码器单圈位置)。
**函数原型:**
```cpp
Result get_single_cycle(SOCKETFD socketFd, std::vector& single_cycle);
Result get_single_cycle_robot(SOCKETFD socketFd, int robotNum, std::vector& single_cycle);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| single_cycle | std::vector\& | 输出参数,单圈值数组,长度 7 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### get_four_point / get_four_point_robot
查询 4 点标定(SCARA 杆长标定)。
**函数原型:**
```cpp
Result get_four_point(SOCKETFD socketFd, std::vector& result);
Result get_four_point_robot(SOCKETFD socketFd, int robotNum, std::vector& result);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| result | std::vector\& | 输出参数,4 点标定结果 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### set_four_point_mark / set_four_point_mark_robot
进行 4 点标记。
**函数原型:**
```cpp
Result set_four_point_mark(SOCKETFD socketFd, int point, int status);
Result set_four_point_mark_robot(SOCKETFD socketFd, int robotNum, int point, int status);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| point | int | 标记点位编号,范围 0-3 |
| status | int | 标记状态:0=取消标记,1=标记 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### four_point_calculation / four_point_calculation_robot
4 点标定计算。
**函数原型:**
```cpp
Result four_point_calculation(SOCKETFD socketFd, double L1, double L2, std::vector& result);
Result four_point_calculation_robot(SOCKETFD socketFd, int robotNum, double L1, double L2, std::vector& result);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| L1 | double | 大臂杆长 |
| L2 | double | 小臂杆长 |
| result | std::vector\& | 输出参数,标定计算结果 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### set_result_for_DH / set_result_for_DH_robot
将 4 点标定计算的结果写入机器人 DH 参数。
**函数原型:**
```cpp
Result set_result_for_DH(SOCKETFD socketFd, int& apply);
Result set_result_for_DH_robot(SOCKETFD socketFd, int robotNum, int& apply);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| apply | int& | 写入是否成功:成功/失败(1/0) |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### get_origin_coord_to_target_coord / get_origin_coord_to_target_coord_robot
坐标值坐标系转换(原坐标系转换为目标坐标系)。
**函数原型:**
```cpp
Result get_origin_coord_to_target_coord(SOCKETFD socketFd, int originCoord, std::vector originPos, int targetCoord, std::vector& targetPos);
Result get_origin_coord_to_target_coord_robot(SOCKETFD socketFd, int robotNum, int originCoord, std::vector originPos, int targetCoord, std::vector& targetPos);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| originCoord | int | 原坐标系:0=关节,1=直角,2=工具,3=用户 |
| originPos | std::vector\ | 要进行转换的坐标值,长度 7:关节取值范围 0-6[-10000,10000];直角/工具/用户取值范围 0-2[-10000,10000]、3-6[-3.1416,3.1416]rad |
| targetCoord | int | 目标坐标系:0=关节,1=直角,2=工具,3=用户 |
| targetPos | std::vector\& | 输出参数,转换后的坐标值 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
## 1.10 机器人切换与参数
### set_robot_switch
切换当前机器人。
**函数原型:**
```cpp
Result set_robot_switch(SOCKETFD socketFd, int robot);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robot | int | 切换到的机器人编号 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### get_robot_switch
获取当前机器人。
**函数原型:**
```cpp
Result get_robot_switch(SOCKETFD socketFd, int& robot);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robot | int& | 输出参数,当前机器人编号 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### get_robot_dh_param / get_robot_dh_param_robot
获取当前机器人 DH 参数。
**函数原型:**
```cpp
Result get_robot_dh_param(SOCKETFD socketFd, RobotDHParam& param);
Result get_robot_dh_param_robot(SOCKETFD socketFd, int robotNum, RobotDHParam& param);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| param | RobotDHParam& | 输出参数,DH 参数结构体 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
> DH 参数是机器人运动学建模的标准方法(Denavit-Hartenberg),包含连杆长度 a、连杆扭角 α、连杆偏距 d、关节角 θ,直接影响机器人运动学正逆解计算精度。
---
### get_robot_joint_param / get_robot_joint_param_robot
获取当前机器人关节参数。
**函数原型:**
```cpp
Result get_robot_joint_param(SOCKETFD socketFd, int id, RobotJointParam& param);
Result get_robot_joint_param_robot(SOCKETFD socketFd, int robotNum, int id, RobotJointParam& param);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| id | int | 关节编号,范围 [1,6] |
| param | RobotJointParam& | 输出参数,关节参数结构体 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### get_teachbox_connection_status / get_teachbox_connection_status_robot
查询示教盒连接状态。
**函数原型:**
```cpp
Result get_teachbox_connection_status(SOCKETFD socketFd, bool& connected);
Result get_teachbox_connection_status_robot(SOCKETFD socketFd, int robotNum, bool& connected);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| connected | bool& | 输出参数,示教盒连接状态 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### get_controller_id / get_controller_id_robot
获取控制器序列号 ID(`char*` 版本)。
**函数原型:**
```cpp
Result get_controller_id(SOCKETFD socketFd, char* id);
Result get_controller_id_robot(SOCKETFD socketFd, int robotNum, char* id);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| id | char* | 输出参数,控制器序列号 ID |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### get_controller_id_csharp / get_controller_id_csharp_robot
获取控制器序列号 ID(`std::vector` 版本,供 C# 使用)。
**函数原型:**
```cpp
Result get_controller_id_csharp(SOCKETFD socketFd, std::vector& id);
Result get_controller_id_csharp_robot(SOCKETFD socketFd, int robotNum, std::vector& id);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| id | std::vector\& | 输出参数,控制器序列号 ID |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
## 1.11 传感器与电机数据
### get_static_search_position / get_static_search_position_robot
获取静态寻位坐标。
**函数原型:**
```cpp
Result get_static_search_position(SOCKETFD socketFd, int fileid, int tableid, int delaytime, std::vector& pos);
Result get_static_search_position_robot(SOCKETFD socketFd, int robotNum, int fileid, int tableid, int delaytime, std::vector& pos);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| fileid | int | 寻位文件号 |
| tableid | int | 寻位参数表号 |
| delaytime | int | 参数表延时 |
| pos | std::vector\& | 输出参数,寻位坐标 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### get_curretn_motor_torque / get_curretn_motor_torque_robot
获取当前电机扭矩。
**函数原型:**
```cpp
Result get_curretn_motor_torque(SOCKETFD socketFd, std::vector& motorTorque, std::vector& motorTorqueSync);
Result get_curretn_motor_torque_robot(SOCKETFD socketFd, int robotNum, std::vector& motorTorque, std::vector& motorTorqueSync);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| motorTorque | std::vector\& | 输出参数,机器人扭矩,长度 7,单位 [%] |
| motorTorqueSync | std::vector\& | 输出参数,外部轴扭矩,长度 5,单位 [%] |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### get_curretn_motor_speed / get_curretn_motor_speed_robot
获取当前电机转速。
**函数原型:**
```cpp
Result get_curretn_motor_speed(SOCKETFD socketFd, std::vector& motorSpeed, std::vector& motorSpeedSync);
Result get_curretn_motor_speed_robot(SOCKETFD socketFd, int robotNum, std::vector& motorSpeed, std::vector& motorSpeedSync);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| motorSpeed | std::vector\& | 输出参数,机器人电机转速,长度 7,单位 [RPM] |
| motorSpeedSync | std::vector\& | 输出参数,外部轴电机转速,长度 5,单位 [RPM] |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### get_curretn_motor_payload / get_curretn_motor_payload_robot
获取当前电机负载。
**函数原型:**
```cpp
Result get_curretn_motor_payload(SOCKETFD socketFd, std::vector& motorPayload, std::vector& motorPayloadSync);
Result get_curretn_motor_payload_robot(SOCKETFD socketFd, int robotNum, std::vector& motorPayload, std::vector& motorPayloadSync);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| motorPayload | std::vector\& | 输出参数,机器人电机负载,长度 7,单位 [%] |
| motorPayloadSync | std::vector\& | 输出参数,外部轴电机负载,长度 5,单位 [%] |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### get_current_line_speed_and_joint_speed / get_current_line_speed_and_joint_speed_robot
获取当前末端线速度和轴速度。
**函数原型:**
```cpp
Result get_current_line_speed_and_joint_speed(SOCKETFD socketFd, double& lineSpeed, std::vector& jointSpeed, std::vector& jointSpeedSync);
Result get_current_line_speed_and_joint_speed_robot(SOCKETFD socketFd, int robotNum, double& lineSpeed, std::vector& jointSpeed, std::vector& jointSpeedSync);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| lineSpeed | double& | 输出参数,末端线速度,单位 [mm/s] |
| jointSpeed | std::vector\& | 输出参数,关节速度,长度 5,单位 [°/s] |
| jointSpeedSync | std::vector\& | 输出参数,外部轴关节速度,长度 5,单位 [°/s] |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
## 1.12 运动控制
### robot_start_jogging / robot_start_jogging_robot
开始点动。
**函数原型:**
```cpp
Result robot_start_jogging(SOCKETFD socketFd, int axis, bool dir);
Result robot_start_jogging_robot(SOCKETFD socketFd, int robotNum, int axis, bool dir);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| axis | int | 轴号 |
| dir | bool | 方向(true=正方向,false=反方向) |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### robot_stop_jogging / robot_stop_jogging_robot
停止点动。
**函数原型:**
```cpp
Result robot_stop_jogging(SOCKETFD socketFd, int axis);
Result robot_stop_jogging_robot(SOCKETFD socketFd, int robotNum, int axis);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| axis | int | 轴号 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### robot_go_to_reset_position / robot_go_to_reset_position_robot
回到设定的复位点。
**函数原型:**
```cpp
Result robot_go_to_reset_position(SOCKETFD socketFd);
Result robot_go_to_reset_position_robot(SOCKETFD socketFd, int robotNum);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
> 复位点需在示教器【设置-复位点设置】中配置,支持关节/直线插补到安全点,或使用复位程序指令自定义复位轨迹。
---
### robot_go_home / robot_go_home_robot
回到设定的零点。
**函数原型:**
```cpp
Result robot_go_home(SOCKETFD socketFd);
Result robot_go_home_robot(SOCKETFD socketFd, int robotNum);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### robot_movej / robot_movej_robot
关节运动(MoveJ)。关节空间点到点运动,速度快但路径不固定。
**函数原型:**
```cpp
Result robot_movej(SOCKETFD socketFd, MoveCmd moveCmd);
Result robot_movej_robot(SOCKETFD socketFd, int robotNum, MoveCmd moveCmd);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| moveCmd | MoveCmd | 运动指令参数 |
**MoveCmd 关键字段:**
| 字段 | 说明 |
|------|------|
| targetPosValue | 点位数组,n 个轴就赋值前 n 位数组,其余置 0 |
| velocity | 速度,范围 0\ 点位数组长度 14:前 7 位为机器人本体点位,后 7 位为外部轴点位,几轴就填几位,其余置 0,外部轴从 pos[7] 开始。速度范围 0\ 点位数组长度 14,结构同 `robot_extra_movej`。速度范围 1\ 本节接口需要额外连接 7000 端口:`SOCKETFD fd7000 = connect_robot("192.168.1.13", "7000");`
### get_robot_state / get_robot_state_robot
7000 端口查询状态。
**函数原型:**
```cpp
Result get_robot_state(SOCKETFD socketFd, RobotState param);
Result get_robot_state_robot(SOCKETFD socketFd, int robotNum, RobotState param);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 7000 端口连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| param | RobotState | 查询状态参数 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### robot_state_callback
7000 端口状态返回的回调函数。
**函数原型:**
```cpp
Result robot_state_callback(SOCKETFD socketFd, void(*function)(const char* message));
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 7000 端口连接句柄 |
| function | void(*)(const char*) | 状态回调函数 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### servo_move / servo_move_robot
外部点移动(伺服跟踪)。
**函数原型:**
```cpp
Result servo_move(SOCKETFD socketFd, ServoMovePara servoMove);
Result servo_move_robot(SOCKETFD socketFd, int robotNum, ServoMovePara servoMove);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 7000 端口连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| servoMove | ServoMovePara | 伺服运动参数 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
**使用示例:**
```cpp
SOCKETFD fd7000 = connect_robot("192.168.1.13", "7000");
ServoMovePara servoMove;
// 设置伺服运动参数...
Result result = servo_move(fd7000, servoMove);
```
---
### enable_servo_position_motion_control / enable_servo_position_motion_control_robot
开启/关闭伺服点位运动控制。
**函数原型:**
```cpp
Result enable_servo_position_motion_control(SOCKETFD socketFd, bool statue);
Result enable_servo_position_motion_control_robot(SOCKETFD socketFd, int robotNum, bool statue);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 7000 端口连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| statue | bool | 1=开启,0=关闭 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### servo_point_position_motion_control / servo_point_position_motion_control_robot
伺服点位运动控制。
**函数原型:**
```cpp
Result servo_point_position_motion_control(SOCKETFD socketFd, ServoPointMovePara servoMove);
Result servo_point_position_motion_control_robot(SOCKETFD socketFd, int robotNum, ServoPointMovePara servoMove);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 7000 端口连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| servoMove | ServoPointMovePara | 伺服点位运动参数 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
## 1.14 碰撞检测与拖拽
### set_collision_para / set_collision_para_robot
设置碰撞检测阈值。
**函数原型:**
```cpp
Result set_collision_para(SOCKETFD socketFd, CollisionPara collisionpara);
Result set_collision_para_robot(SOCKETFD socketFd, int robotNum, CollisionPara collisionpara);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| collisionpara | CollisionPara | 碰撞检测参数结构体(含指令位置响应时间、误差允许时间、碰撞检测阈值(点动)、碰撞检测阈值(指令)、机器人轴数) |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### set_darg_mode / set_darg_mode_robot
设置拖拽示教的拖拽方式。
**函数原型:**
```cpp
Result set_darg_mode(SOCKETFD socketFd, int mode);
Result set_darg_mode_robot(SOCKETFD socketFd, int robotNum, int mode);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| mode | int | 拖拽模式:0=无,1=3D 鼠标,2=力矩模式,3=位置 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### set_position_dragParams / set_position_dragParams_robot
设置位置拖动参数(笛卡尔空间线速度限制和关节空间速度限制)。
**函数原型:**
```cpp
Result set_position_dragParams(SOCKETFD socketFd, DragParam& param);
Result set_position_dragParams_robot(SOCKETFD socketFd, int robotNum, DragParam& param);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| param | DragParam& | 位置拖动参数结构体 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### get_drag_thread_is_end / get_drag_thread_is_end_robot
获取拖拽是否已结束。
**函数原型:**
```cpp
Result get_drag_thread_is_end(SOCKETFD socketFd, bool& endFlag);
Result get_drag_thread_is_end_robot(SOCKETFD socketFd, int robotNum, bool& endFlag);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| endFlag | bool& | 输出参数,拖拽结束标志位 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
## 1.15 独立轴控制
> 独立轴控制用于伺服轴独立于机器人本体运行,支持点到点、恒转速(PV)、恒转矩等模式。使用前需在示教器【设置-独立轴参数】中新建独立轴并配置关节参数(最多 10 个独立轴)。
### new_independent_axis_param / new_independent_axis_param_robot
新建独立轴参数。
**函数原型:**
```cpp
Result new_independent_axis_param(SOCKETFD socketFd, int axis_num);
Result new_independent_axis_param_robot(SOCKETFD socketFd, int robotNum, int axis_num);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| axis_num | int | 新建的独立轴数量 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### modify_independent_axis_param / modify_independent_axis_param_robot
修改独立轴参数(先新建再修改)。
**函数原型:**
```cpp
Result modify_independent_axis_param(SOCKETFD socketFd, IndependentAxisParam& param);
Result modify_independent_axis_param_robot(SOCKETFD socketFd, int robotNum, IndependentAxisParam& param);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| param | IndependentAxisParam& | 独立轴参数结构体 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### get_independent_axis_param / get_independent_axis_param_robot
获取独立轴参数。
**函数原型:**
```cpp
Result get_independent_axis_param(SOCKETFD socketFd, int num, IndependentAxisParam& param);
Result get_independent_axis_param_robot(SOCKETFD socketFd, int robotNum, int num, IndependentAxisParam& param);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| num | int | 独立轴编号 |
| param | IndependentAxisParam& | 输出参数,独立轴参数结构体 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### get_axis_sum / get_axis_sum_robot
获取独立轴总数。
**函数原型:**
```cpp
Result get_axis_sum(SOCKETFD socketFd, int& sum);
Result get_axis_sum_robot(SOCKETFD socketFd, int robotNum, int& sum);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| sum | int& | 输出参数,独立轴总数 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### delete_axis_sum / delete_axis_sum_robot
删除某个独立轴。
**函数原型:**
```cpp
Result delete_axis_sum(SOCKETFD socketFd, int& num);
Result delete_axis_sum_robot(SOCKETFD socketFd, int robotNum, int& num);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| num | int& | 将要删除的独立轴编号 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### get_axis_position / get_axis_position_robot
查询独立轴位置。
**函数原型:**
```cpp
Result get_axis_position(SOCKETFD socketFd, int& num, double& currentPos);
Result get_axis_position_robot(SOCKETFD socketFd, int robotNum, int& num, double& currentPos);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| num | int& | 要查询的独立轴 |
| currentPos | double& | 输出参数,独立轴当前位置 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### independent_axis_zero_calibration
独立轴零点标定。
**函数原型:**
```cpp
Result independent_axis_zero_calibration(SOCKETFD socketFd, int num);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| num | int | 要标定的独立轴 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### set_independent_axis_PV_run
独立控制轴 PV 运动(恒转速,仅支持外部轴)。
**函数原型:**
```cpp
Result set_independent_axis_PV_run(SOCKETFD socketFd, IndependentAxisRun param);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| param | IndependentAxisRun | 运动参数结构体(含速度、加减速等) |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### set_independent_axis_PV_stop
独立控制轴 PV 停止(仅支持外部轴)。
**函数原型:**
```cpp
Result set_independent_axis_PV_stop(SOCKETFD socketFd, int num);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| num | int | 轴编号 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### cancel_independent_axis_move
取消轴独立控制运动(仅支持外部轴)。
**函数原型:**
```cpp
Result cancel_independent_axis_move(SOCKETFD socketFd, int num);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| num | int | 轴编号 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### independent_axis_homing
独立轴回零。
**函数原型:**
```cpp
Result independent_axis_homing(SOCKETFD socketFd, int num);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| num | int | 轴编号 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
> 回零使用 CSP 模式(对象字典 607A),需配置 PDO 后正常使用。
---
### independent_axis_homing_stop
独立轴回零停止。
**函数原型:**
```cpp
Result independent_axis_homing_stop(SOCKETFD socketFd, int num);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| num | int | 轴编号 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### independent_axis_jog
独立轴点动。
**函数原型:**
```cpp
Result independent_axis_jog(SOCKETFD socketFd, IndependentAxisRun param);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| param | IndependentAxisRun | 点动参数结构体 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
> 点动使用 CSV 模式(对象字典 60FF),点动速度范围 [0.001,10000],加减速倍数范围 [1,5]。
---
## 1.16 机器人形态与可达性
### get_robot_configuration / get_robot_configuration_robot
获取 4 轴 SCARA 机器人的形态。SCARA 机器人在运动学正逆解时存在多解情况,需要通过形态参数指定机器人当前的臂型配置。
**函数签名:**
```cpp
Result get_robot_configuration(SOCKETFD socketFd, int& configuration);
Result get_robot_configuration_robot(SOCKETFD socketFd, int robotNum, int& configuration);
```
**参数说明:**
| 参数 | 类型 | 输入/输出 | 说明 |
|------|------|----------|------|
| socketFd | SOCKETFD | 输入 | 连接句柄 |
| robotNum | int | 输入 | 机器人编号(仅 `_robot` 版本) |
| configuration | int& | 输出 | 机器人形态 |
**形态值说明:**
| 值 | 含义 | 说明 |
|----|------|------|
| 1 | 左手形态(Lefty) | 机器人第 2 关节相对于第 1 关节与末端连线处于左侧 |
| 2 | 右手形态(Righty) | 机器人第 2 关节相对于第 1 关节与末端连线处于右侧 |
**返回值:**
- 0:成功
- 负数:失败
**注意事项:**
- 该接口仅适用于 4 轴 SCARA 机器人,其他机器人类型调用可能返回无效值。
- 形态值在点位数据结构 `posInfo[2]` 中同样使用(见 `set_global_position` 等接口的点位格式说明),运动规划时需确保形态一致。
- 在切换工具手坐标系或用户坐标系后,建议重新获取当前形态以确认配置正确。
**使用示例:**
```cpp
// 获取当前机器人形态
int config;
Result result = get_robot_configuration(fd, config);
if (result == 0) {
if (config == 1) {
printf("当前形态:左手(Lefty)\n");
} else if (config == 2) {
printf("当前形态:右手(Righty)\n");
}
}
```
---
### get_pos_reachable / get_pos_reachable_robot
判断指定点位在当前机器人工作空间内是否可达。该接口基于机器人运动学正逆解算法,验证目标点位是否存在有效的关节解。
**函数签名:**
```cpp
Result get_pos_reachable(SOCKETFD socketFd, std::vector pos, std::string movetype, bool &result);
Result get_pos_reachable_robot(SOCKETFD socketFd, int robotNum, std::vector pos, std::string movetype, bool &result);
```
**参数说明:**
| 参数 | 类型 | 输入/输出 | 说明 |
|------|------|----------|------|
| socketFd | SOCKETFD | 输入 | 连接句柄 |
| robotNum | int | 输入 | 机器人编号(仅 `_robot` 版本) |
| pos | `std::vector` | 输入 | 目标点位坐标数据,长度 14 |
| movetype | `std::string` | 输入 | 运动方式:`"MOVJ"`(关节运动)或 `"MOVL"`(直线运动) |
| result | bool& | 输出 | 点位是否可达 |
**pos 向量格式(长度 14):**
| 索引 | 含义 | 说明 |
|------|------|------|
| [0] | 坐标系 | 0=关节,1=直角,2=工具,3=用户 |
| [1] | 角度单位 | 0=角度制,1=弧度制 |
| [2] | 形态 | SCARA 机器人形态:1=左手,2=右手 |
| [3] | 工具手坐标序号 | 当前使用的工具手编号 |
| [4] | 用户坐标序号 | 当前使用的用户坐标系编号 |
| [5]-[6] | 备用 | 保留字段 |
| [7]-[13] | 点位信息 | 关节角(关节坐标系)或 XYZ+姿态(直角/工具/用户坐标系) |
**返回值:**
- 0:成功(查询操作本身成功)
- 负数:失败
::: warning 注意
`result` 参数才是点位可达性的判断结果,`true` 表示可达,`false` 表示不可达。函数返回值仅表示接口调用是否成功。
:::
**注意事项:**
- 同一目标点位在不同运动方式(MOVJ / MOVL)下的可达性判断结果可能不同:关节运动可能通过非直线路径到达,而直线运动受限于工作空间边界和奇异位形。
- 点位中的形态参数(pos[2])会影响逆解结果,需与实际机器人形态保持一致。
- 该接口内部调用运动学正逆解算法,对于存在关节限位、自碰撞或奇异位形的情况会返回不可达。
**使用示例:**
```cpp
// 构造目标点位(直角坐标系,角度制,形态=左手)
std::vector pos(14, 0);
pos[0] = 1; // 坐标系:直角
pos[1] = 0; // 角度制
pos[2] = 1; // 形态:左手
pos[3] = 1; // 工具手编号
pos[4] = 1; // 用户坐标编号
pos[7] = 400.0; // X (mm)
pos[8] = 0.0; // Y (mm)
pos[9] = 300.0; // Z (mm)
pos[10] = 180.0; // Rx (度)
pos[11] = 0.0; // Ry (度)
pos[12] = 0.0; // Rz (度)
// 判断 MOVL 直线运动是否可达
bool reachable = false;
Result ret = get_pos_reachable(fd, pos, "MOVL", reachable);
if (ret == 0) {
if (reachable) {
printf("目标点位通过 MOVL 可达\n");
} else {
printf("目标点位通过 MOVL 不可达,请尝试 MOVJ 或调整目标点位\n");
}
}
// 判断 MOVJ 关节运动是否可达
ret = get_pos_reachable(fd, pos, "MOVJ", reachable);
if (ret == 0 && reachable) {
printf("目标点位通过 MOVJ 可达\n");
}
```
---
---
# 二、IO 控制(nrc_io.h)
> 原文:https://open.inexbot.com/zh/04.%E4%B8%8A%E4%BD%8D%E6%9C%BA/01.C++/02.%E6%8E%A5%E5%8F%A3/02.IO%E6%8E%A7%E5%88%B6.html
## IO 基础概念
IO 控制接口用于读写机器人的数字量/模拟量输入输出端口,支持多块 IO 板级联,端口命名格式为 `板号-端口号`(如 `DIN1-1` 表示第 1 块 IO 板的第 1 路数字输入)。
| 类型 | 命名前缀 | 说明 | 典型用途 |
|------|----------|------|----------|
| 数字输入 | DIN | 接收外部开关/传感器信号(高/低电平) | 启动按钮、限位开关、传感器检测 |
| 数字输出 | DOUT | 控制外部设备通断,不接收反馈 | 继电器、指示灯、电磁阀 |
| 模拟输入 | AIN | 接收连续变化的电压/电流信号 | 模拟传感器、变送器 |
| 模拟输出 | AOUT | 输出连续变化的电压/电流信号,范围 [0,10] | 变频器给定、比例阀 |
> IO 板端口命名:`DIN1-1` 中 `DIN` 为数字输入、`1` 为 IO 板号、`1` 为端口号;模拟量同理为 `AIN1-1` / `AOUT1-1`。
## 2.1 IO 查询与设置
### get_io_type
IO 型号查询,获取当前连接的 IO 板数量、型号及各类型端口数量。
**函数原型:**
```cpp
Result get_io_type(SOCKETFD socketFd, IOtype& io_type);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| io_type | IOtype& | 输出参数,IO 型号和端口信息 |
**IOtype 结构体:**
| 字段 | 类型 | 说明 |
|------|------|------|
| num | int | IO 板数量 |
| type | vector\ | 各 IO 板型号 |
| io_port_sum | vector\\> | 各 IO 板端口数量,一维数组为 [数字输入数量, 数字输出数量, 模拟输入数量, 模拟输出数量] |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
**使用示例:**
```cpp
IOtype io_type;
Result result = get_io_type(fd, io_type);
if (result == SUCCESS) {
printf("IO板数量: %d\n", io_type.num);
for (int i = 0; i < io_type.num; i++) {
printf("IO板%d: %s, DIN:%d DOUT:%d AIN:%d AOUT:%d\n",
i + 1, io_type.type[i].c_str(),
io_type.io_port_sum[i][0], io_type.io_port_sum[i][1],
io_type.io_port_sum[i][2], io_type.io_port_sum[i][3]);
}
}
```
---
### set_digital_output
设置数字输出端口状态。
**函数原型:**
```cpp
Result set_digital_output(SOCKETFD socketFd, int port, int value);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| port | int | 端口号,范围 [1, 最大端口数] |
| value | int | 输出值,0 或 1(0=低电平,1=高电平) |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
**使用示例:**
```cpp
// 将第 1 块 IO 板的第 3 路数字输出置为高电平
Result result = set_digital_output(fd, 3, 1);
if (result == SUCCESS) {
printf("数字输出设置成功\n");
}
```
---
### get_digital_output
一次获取所有数字输出端口状态。
**函数原型:**
```cpp
Result get_digital_output(SOCKETFD socketFd, std::vector& out);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| out | std::vector\& | 输出参数,存储结果的数组,长度为 64 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
**使用示例:**
```cpp
std::vector out(64);
Result result = get_digital_output(fd, out);
if (result == SUCCESS) {
printf("DOUT1-1 = %d\n", out[0]);
}
```
---
### get_digital_input
一次获取所有数字输入端口状态。
**函数原型:**
```cpp
Result get_digital_input(SOCKETFD socketFd, std::vector& in);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| in | std::vector\& | 输出参数,存储结果的数组,长度为 64 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
**使用示例:**
```cpp
std::vector in(64);
Result result = get_digital_input(fd, in);
if (result == SUCCESS) {
printf("DIN1-1 = %d\n", in[0]); // 检测启动按钮状态
}
```
---
### set_analog_output
设置模拟输出端口数值。
**函数原型:**
```cpp
Result set_analog_output(SOCKETFD socketFd, int port, double value);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| port | int | 端口号 |
| value | double | 输出数值,范围 [0, 10] |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
**使用示例:**
```cpp
// 将 AOUT1-1 输出 5V
Result result = set_analog_output(fd, 1, 5.0);
if (result == SUCCESS) {
printf("模拟输出设置成功\n");
}
```
---
### get_analog_output
查询所有模拟输出端口数值。
**函数原型:**
```cpp
Result get_analog_output(SOCKETFD socketFd, std::vector& aout);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| aout | std::vector\& | 输出参数,模拟输出数组,最大长度 64 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### get_analog_input
查询所有模拟输入端口数值。
**函数原型:**
```cpp
Result get_analog_input(SOCKETFD socketFd, std::vector& ain);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| ain | std::vector\& | 输出参数,模拟输入数组,最大长度 64 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
**使用示例:**
```cpp
std::vector ain(64);
Result result = get_analog_input(fd, ain);
if (result == SUCCESS) {
printf("AIN1-1 = %.2f\n", ain[0]);
}
```
---
### set_force_digital_input
设置数字输入端口是否强制打开(强制置位),用于调试场景模拟输入信号。
**函数原型:**
```cpp
Result set_force_digital_input(SOCKETFD socketFd, int port, int force, int value);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| port | int | 输入端口号 |
| force | int | 是否强制,1=强制,0=非强制 |
| value | int | 强制状态,0 或 1 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
**使用示例:**
```cpp
// 强制将 DIN1-1 置为 1(模拟按钮按下)
Result result = set_force_digital_input(fd, 1, 1, 1);
```
---
### get_force_digital_input
获取当前已打开强制功能的输入端口及其状态。
**函数原型:**
```cpp
Result get_force_digital_input(SOCKETFD socketFd, std::vector& port, std::vector& status);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| port | std::vector\& | 输出参数,当前打开强制输入的端口 |
| status | std::vector\& | 输出参数,对应端口状态,下标与 port 一一对应 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
## 2.2 IO 复位与报警
### IO 复位功能说明
当程序运行停止或报错时,IO 复位功能能使 IO 输出端口恢复到初始状态。共分三种复位类型:
| 复位类型 | type 值 | 触发条件 |
|----------|---------|----------|
| 远程 IO 复位 | 1 | 远程模式给复位信号,机器人执行复位程序回到复位点时,将设置的 IO 端口复位到复位值;若复位程序中途停止则不复位 |
| 切模式停止 | 2 | 运行程序时切换到示教/远程模式导致程序停止,将设置的 IO 端口复位到复位值 |
| 程序报错 | 3 | 程序发生错误导致程序停止,将设置的 IO 端口复位到复位值(伺服报错、IO 设置的报错、系统运行中的报错) |
### IO 报警信息说明
可自定义 IO 输入输出端口触发报警时的提示信息,报警优先级高于其他类型 IO 报警。每条报警包含:
| 参数 | 说明 |
|------|------|
| 端口 | 绑定报警的 IO 端口 |
| 类型 | 消息(msgType=0)/ 警告(msgType=1)/ 错误(msgType=2) |
| 消息 | 触发报警后输出的内容 |
| 参数 | 0=端口为 0 时触发,1=端口为 1 时触发 |
| 使能 | 关闭后无论 IO 状态如何都不触发报警 |
**三种报警类型的表现:**
| 类型 | 界面表现 | 对机器人的影响 |
|------|----------|----------------|
| 消息 | 右下角白色提示条 | 无影响 |
| 警告 | 右下角黄色提示条 | 无影响 |
| 错误 | 右下角红色提示条 | 若机器人正在运行会强制下使能 |
---
### set_IO_reset_function
设置 IO 复位功能。
**函数原型:**
```cpp
Result set_IO_reset_function(SOCKETFD socketFd, int robotNum, int type, std::vector enable, std::vector value);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4) |
| type | int | 复位类型:1=远程 IO 复位,2=切模式停止,3=程序报错 |
| enable | std::vector\ | 是否复位容器,大小等于所有 IO 板输出端口总数 |
| value | std::vector\ | 复位值容器,大小等于所有 IO 板输出端口总数 |
::: warning 注意
每次设置需包含所有端口,否则会覆盖上一次的修改。多块 IO 板时端口顺序为上一块 IO 板末位端口的顺延。
:::
**使用示例(两块 IO 板,每块输出 16 路):**
```cpp
// 1-1 端口选择复位,复位值 1;2-1 端口选择复位,复位值 1
std::vector enable(32, 0);
std::vector value(32, 0);
enable[0] = 1; value[0] = 1; // 第 1 块 IO 板 1 端口
enable[16] = 1; value[16] = 1; // 第 2 块 IO 板 1 端口
Result result = set_IO_reset_function(fd, 1, 1, enable, value);
```
---
### get_IO_reset_function
获取 IO 复位相关参数。
**函数原型:**
```cpp
Result get_IO_reset_function(SOCKETFD socketFd, int robotNum, int type, std::vector& enable, std::vector& value);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4) |
| type | int | 复位类型:1=远程 IO 复位,2=切模式停止,3=程序报错 |
| enable | std::vector\& | 输出参数,是否复位容器 |
| value | std::vector\& | 输出参数,复位值容器 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### set_error_msg_of_digital_input / set_error_msg_of_digital_output
设置 IO 报警信息数字输入/输出端口报警功能。
**函数原型:**
```cpp
Result set_error_msg_of_digital_input(SOCKETFD socketFd, std::vector msg);
Result set_error_msg_of_digital_output(SOCKETFD socketFd, std::vector msg);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| msg | std::vector\ | 消息设置结构体数组,大小与 IO 板端口数一致 |
**AlarmdIO 结构体:**
| 字段 | 类型 | 说明 |
|------|------|------|
| msgType | int | 消息类型:0=普通消息,1=警告消息,2=错误消息 |
| value | int | IO 有效参数 (0/1),0=端口为 0 时触发,1=端口为 1 时触发 |
| enable | int | 使能设置 (0/1) |
| msg | std::string | 消息内容 |
::: warning 注意
msg 数组大小必须与 IO 板端口数一致,每次设置相应端口均不能忽略,否则会覆盖上一次的设置。
:::
**使用示例(两块 IO 板,每块输入输出各 16 位):**
```cpp
std::vector msg(32);
// IO 板1 输出端口 1:错误消息 "QQQ",类型 2,参数 1,使能 1
msg[0].msgType = 2; msg[0].enable = 1; msg[0].value = 1; msg[0].msg = "QQQ";
// 中间 15 个端口不设置
for (int i = 1; i < 16; i++) {
msg[i].msgType = 0; msg[i].enable = 0; msg[i].value = 0; msg[i].msg = "";
}
// IO 板2 输出端口 1:错误消息 "YYY"
msg[16].msgType = 2; msg[16].enable = 1; msg[16].value = 1; msg[16].msg = "YYY";
Result result = set_error_msg_of_digital_output(fd, msg);
```
---
### get_error_msg_of_digital_input / get_error_msg_of_digital_output
获取 IO 报警信息数字输入/输出端口报警设置。
**函数原型:**
```cpp
Result get_error_msg_of_digital_input(SOCKETFD socketFd, std::vector& msg);
Result get_error_msg_of_digital_output(SOCKETFD socketFd, std::vector& msg);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| msg | std::vector\& | 输出参数,消息设置结构体数组 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
## 2.3 远程 IO
### 远程模式信号说明
远程模式下可通过 IO 信号控制机器人启停及程序预约,标准信号功能如下:
**数字 IO 输入(远程控制信号):**
| 功能 | 触发方式 | 说明 |
|------|----------|------|
| 启动 | 上升沿 | 参数为 1 时,信号 0 变 1 有效 |
| 停止 | 持续有效 | 参数为 1 时,信号持续有效 |
| 暂停 | 持续有效 | 参数为 1 时,信号持续有效 |
| 清除报警 | 上升沿 | 参数为 1 时,信号 0 变 1 有效 |
| 预约即启动 | 无 | 打开时,预约成功即上电 |
| IO 远程程序 1-10 | 脉冲(周期 0.6s) | 参数为 1 时,信号 0-1-0 有效,程序预约成功至少需触发 0.6 秒以上 |
| 紧急停止 1/2 | 高电平 | 1ms 扫描一次,扫描到即触发 |
| 安全光幕 1/2 | 高电平 | 运行中触发机器人暂停 |
| 屏蔽紧急停止 1/2 | 配合紧急停止 | 按钮打开即屏蔽,设置时间到后重新检测 |
**数字 IO 输出(状态提示信号):**
| 功能 | 说明 |
|------|------|
| Robot1 运行 | 程序运行时输出高电平 |
| Robot1 暂停 | 程序暂停时输出高电平 |
| Robot1 停止 | 程序停止时输出高电平 |
| 报错提示 | 常亮输出高电平,闪烁输出脉冲(周期 1s) |
| 使能 | 输出高电平 |
| IO 远程程序 1-10 预约输出 | 预约中闪烁(周期 1.2s),运行中常亮 |
| 主程序首行 | 输出一个高电平参数为 1 的信号,程序光标跳至主程序首行 |
| 可继续执行 | 输出高电平,可运行暂停的程序 |
| 拔出示教盒 | 拔出示教盒后输出高/低电平 |
**远程模式状态说明:**
| 状态 | 说明 |
|------|------|
| 未预约 | 进入远程模式后未预约过程序,或预约后取消 |
| 预约中 | 预约成功显示预约中 |
| 运行中 | 程序正在运行 |
| 已预约 | 程序运行完成或被触发停止 |
> 远程模式不能修改速度,速度修改需提前在【设置-远程程序设置】中修改;触发对应程序的 IO 口即预约程序,取消需再次触发。
---
### set_remote_param
设置远程模式参数(速度、启动方式、屏蔽时间等)。
**函数原型:**
```cpp
Result set_remote_param(SOCKETFD socketFd, int robotNum, int speed, bool start, int time, int startTime, int num = 10);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4) |
| speed | int | 远程模式默认速度 [1,100] |
| start | bool | 是否自动启动 |
| time | int | IO 重复触发屏蔽时间,单位 ms |
| startTime | int | 启动确认时间 |
| num | int | 远程 IO 数量,默认 10 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
**使用示例:**
```cpp
// 设置远程参数:速度 50,不自动启动,屏蔽时间 500ms,启动确认 100ms,10 路远程 IO
Result result = set_remote_param(fd, 1, 50, false, 500, 100, 10);
```
---
### get_remote_param
获取远程参数设置数据。
**函数原型:**
```cpp
Result get_remote_param(SOCKETFD socketFd, int robotNum, int& speed, bool& start, int& time, int& startTime, int& num);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4) |
| speed | int& | 输出参数,远程模式默认速度 |
| start | bool& | 输出参数,是否自动启动 |
| time | int& | 输出参数,IO 重复触发屏蔽时间 (ms) |
| startTime | int& | 输出参数,启动确认时间 |
| num | int& | 输出参数,远程 IO 数量 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### set_remote_function
设置远程 IO 功能(通用控制信号 + 程序预约信号绑定)。
**函数原型:**
```cpp
Result set_remote_function(SOCKETFD socketFd, int robotNum, RemoteControl general, std::vector program, int num = 10);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4) |
| general | RemoteControl | 通用功能远程 IO 参数(启动/暂停/停止/清除报警等) |
| program | std::vector\ | 远程控制程序参数,program.size() 必须与 num 相等 |
| num | int | 远程 IO 数量(24.03 版本必须与 set_remote_param 中的 num 一致) |
**RemoteControl 结构体:**
| 字段 | 类型 | 说明 |
|------|------|------|
| clearStashPort / clearStashValue | int | 清除断电保持数据端口及有效参数 (0/1) |
| faultResetPort / faultResetValue | int | 清除报警端口及有效参数 (0/1) |
| pausePort / pauseValue | int | 暂停端口及有效参数 (0/1) |
| startPort / startValue | int | 启动端口及有效参数 (0/1) |
| stopPort / stopValue | int | 停止端口及有效参数 (0/1) |
| program | vector\ | 远程程序端口设置 |
**RemoteProgram 结构体:**
| 字段 | 类型 | 说明 |
|------|------|------|
| port | int | 远程程序端口绑定 |
| value | int | 使用远程 IO 功能时有效参数 (0/1);使用远程状态提示功能时有效参数 (0/1/2) |
**使用示例:**
```cpp
RemoteControl general;
general.startPort = 1; general.startValue = 1; // DIN1-1 启动
general.stopPort = 2; general.stopValue = 1; // DIN1-2 停止
general.pausePort = 3; general.pauseValue = 1; // DIN1-3 暂停
general.faultResetPort = 4; general.faultResetValue = 1; // DIN1-4 清除报警
std::vector program(10);
program[0].port = 5; program[0].value = 1; // DIN1-5 预约程序 1
Result result = set_remote_function(fd, 1, general, program, 10);
```
---
### get_remote_function
获取远程 IO 功能设置数据。
**函数原型:**
```cpp
Result get_remote_function(SOCKETFD socketFd, int robotNum, int& num, int& time, RemoteControl& general, std::vector& program);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4) |
| num | int& | 输出参数,远程 IO 数量 |
| time | int& | 输出参数,IO 重复触发屏蔽时间 (ms) |
| general | RemoteControl& | 输出参数,通用功能远程 IO 参数 |
| program | std::vector\& | 输出参数,远程控制程序参数 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### set_remote_status_tips
设置远程状态提示功能(运行/暂停/停止等状态输出端口)。
**函数原型:**
```cpp
Result set_remote_status_tips(SOCKETFD socketFd, int robotNum, int outagePort, int outageValue, std::vector program);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4) |
| outagePort | int | 断电保持数据恢复端口 |
| outageValue | int | 断电保持数据恢复端口有效值 (0/1/2) |
| program | std::vector\ | 远程状态提示参数(size 必须与 set_remote_param 的 num 相等) |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### get_remote_status_tips
获取远程状态提示功能数据。
**函数原型:**
```cpp
Result get_remote_status_tips(SOCKETFD socketFd, int robotNum, int& num, int& outagePort, int& outageValue, std::vector& program);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4) |
| num | int& | 输出参数,远程 IO 数量 |
| outagePort | int& | 输出参数,断电保持数据恢复端口 |
| outageValue | int& | 输出参数,断电保持数据恢复端口有效值 |
| program | std::vector\& | 输出参数,远程控制程序参数 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### set_remote_program
设置 IO 远程程序选择(预约哪个作业文件、运行次数)。
**函数原型:**
```cpp
Result set_remote_program(SOCKETFD socketFd, int robotNum, std::vector program);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4) |
| program | std::vector\ | 远程程序设置 |
**RemoteProgramSetting 结构体:**
| 字段 | 类型 | 说明 |
|------|------|------|
| job | std::string | 远程程序选择(作业文件名) |
| times | int | 远程程序运行次数 |
**使用示例:**
```cpp
std::vector program(10);
program[0].job = "main.job"; // 预约主程序
program[0].times = 1; // 运行 1 次
Result result = set_remote_program(fd, 1, program);
```
---
### get_remote_program
获取 IO 远程程序设置数据。
**函数原型:**
```cpp
Result get_remote_program(SOCKETFD socketFd, int robotNum, int& num, std::vector& program);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4) |
| num | int& | 输出参数,远程 IO 数量 |
| program | std::vector\& | 输出参数,远程程序设置 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
## 2.4 安全 IO
### 安全 IO 说明
安全 IO 用于紧急停止、安全光幕等安全功能的端口绑定与电平设置:
| 功能 | 说明 |
|------|------|
| 紧急停止 | 触发后机器人下电并切至伺服停止;解除后需先清错才可继续操作 |
| 安全光幕 | 触发后机器人暂停,再次按下启动按钮可继续运行 |
| 屏蔽紧急停止 | 打开后屏蔽时间内紧急停止信号被屏蔽 |
| 使能硬接线 | 使用使能硬接线示教盒时绑定 DIN 端口,上电使能由 IO 板输入信号控制 |
> 使能端口 1 为上电使能,使能端口 2 为下电使能;打开此功能后示教盒使能按钮失效。非使能硬接线示教盒请勿设置。
---
### set_hard_enable_port
设置是否使能硬接及相关端口。
**函数原型:**
```cpp
Result set_hard_enable_port(SOCKETFD socketFd, int enable, int port1, int port2);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| enable | int | 是否打开使能硬接 (0/1) |
| port1 | int | 使能硬接端口 1(上电使能) |
| port2 | int | 使能硬接端口 2(下电使能) |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
**使用示例:**
```cpp
// 使能硬接线:端口 1 绑定 DIN1-5,端口 2 绑定 DIN1-6
Result result = set_hard_enable_port(fd, 1, 5, 6);
```
---
### get_hard_enable_port
获取使能硬接开关是否打开及相关绑定端口。
**函数原型:**
```cpp
Result get_hard_enable_port(SOCKETFD socketFd, int& enable, int& port1, int& port2);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| enable | int& | 输出参数,是否打开使能硬接 |
| port1 | int& | 输出参数,使能硬接端口 1 |
| port2 | int& | 输出参数,使能硬接端口 2 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### set_safe_IO_function
设置 IO 安全设置参数(紧急停止、安全光幕、屏蔽时间等)。
**函数原型:**
```cpp
Result set_safe_IO_function(SOCKETFD socketFd, int robotNum, SafeIO safeIO);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4) |
| safeIO | SafeIO | IO 安全设置参数结构体 |
**SafeIO 结构体:**
| 字段 | 类型 | 说明 |
|------|------|------|
| quickStopPort1 / quickStopPort2 | int | 紧急停止端口 1/2 |
| quickStopValue1 / quickStopValue2 | int | 紧急停止参数 (0/1) |
| quickStopEnable | bool | 紧急停止使能 |
| quickStopShied1 / quickStopShied2 | bool | 屏蔽紧急停止 1/2 |
| quickStopTime | double | 快速停止时间,单位 ms,范围 [50,100] |
| quickStopShiedTime | int | 屏蔽紧急停止时间,单位 s |
| screenPort1 / screenPort2 | int | 安全光幕端口 1/2 |
| screenValue1 / screenValue2 | int | 安全光幕参数 (0/1) |
| screenEnable | bool | 安全光幕使能 |
**使用示例:**
```cpp
SafeIO safe;
safe.quickStopPort1 = 15; safe.quickStopValue1 = 1; safe.quickStopEnable = true;
safe.quickStopTime = 100; // 快速停止时间 100ms
safe.screenPort1 = 7; safe.screenValue1 = 1; safe.screenEnable = true;
Result result = set_safe_IO_function(fd, 1, safe);
```
---
### get_safe_IO_function
获取 IO 安全设置参数。
**函数原型:**
```cpp
Result get_safe_IO_function(SOCKETFD socketFd, int robotNum, SafeIO& safeIO);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4) |
| safeIO | SafeIO& | 输出参数,IO 安全设置参数 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
---
# 三、Modbus 通讯(nrc_modbus.h)
> 原文:https://open.inexbot.com/zh/04.%E4%B8%8A%E4%BD%8D%E6%9C%BA/01.C++/02.%E6%8E%A5%E5%8F%A3/03.Modbus%E9%80%9A%E8%AE%AF.html
## 数据结构
### ModbusTCPParameter
TCP 通讯参数。
| 成员 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| IP | string | `"192.168.1.13"` | Modbus TCP 服务器 IP 地址 |
| port | int | `503` | Modbus TCP 端口号 |
### ModbusRTUParameter
RTU 通讯参数。
| 成员 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| slaveId | int | - | 从站 ID,范围 [0, 65535] |
| port | int | `1` | 串口端口号 |
| baudrate | int | `115200` | 波特率 |
| checkBit | string | `"None"` | 奇偶校验位;`"None"`(无校验)/ `"Even"`(偶校验)/ `"Odd"`(奇校验) |
| dataBit | int | `8` | 数据位;可选 5、6、7、8 |
| stopBit | int | `1` | 停止位;可选 1、2 |
### ModbusMasterParameter
Modbus 主站参数。
| 成员 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| type | string | `"TCP"` | 主站类型;`"TCP"` 或 `"RTU"` |
| startAddress | bool | `false` | 起始地址偏移;`false` 为地址自动 -1(起始为 1),`true` 为地址不变(起始为 0) |
| TCP | ModbusTCPParameter | - | TCP 通讯参数(type 为 TCP 时有效) |
| RTU | ModbusRTUParameter | - | RTU 通讯参数(type 为 RTU 时有效) |
---
## 主站配置
### modbus_set_master_parameter
设置主站参数(支持 TCP/RTU,最多 9 个工艺号)。
```cpp
Result modbus_set_master_parameter(SOCKETFD socketFd, int id, const ModbusMasterParameter& param);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 套接字文件描述符,由 `connect_robot` 返回 |
| id | int | 工艺号,范围 [1, 9] |
| param | const ModbusMasterParameter& | 主站参数结构体 |
**返回值:** `Result` 结构体,`result` 为 `true` 时表示设置成功。
**使用示例:**
```cpp
ModbusMasterParameter masterParam;
masterParam.type = "TCP";
masterParam.startAddress = false;
masterParam.TCP.IP = "192.168.1.14";
masterParam.TCP.port = 503;
Result ret = modbus_set_master_parameter(socketFd, 1, masterParam);
if (ret.result) {
// 参数设置成功
}
```
---
### modbus_open_master
打开主站通讯连接。
```cpp
Result modbus_open_master(SOCKETFD socketFd, int id);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 套接字文件描述符 |
| id | int | 工艺号,范围 [1, 9] |
**返回值:** `Result` 结构体。
**说明:** 调用前需先通过 `modbus_set_master_parameter` 设置主站参数。
**使用示例:**
```cpp
Result ret = modbus_open_master(socketFd, 1);
```
---
### modbus_get_master_connection_status
获取主站连接状态。
```cpp
Result modbus_get_master_connection_status(SOCKETFD socketFd, int id, int& status);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 套接字文件描述符 |
| id | int | 工艺号,范围 [1, 9] |
| status | int& | 输出参数,连接状态;`0` 表示未连接,`1` 表示已连接 |
**返回值:** `Result` 结构体。
**使用示例:**
```cpp
int connectStatus = 0;
Result ret = modbus_get_master_connection_status(socketFd, 1, connectStatus);
if (ret.result && connectStatus == 1) {
// Modbus 已连接
}
```
---
## 功能码 01H - 读线圈
### modbus_read_coil_status
读取从站线圈状态(位操作,可读可写)。
```cpp
Result modbus_read_coil_status(SOCKETFD socketFd, int id, int address, int quantity, std::vector& data);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 套接字文件描述符 |
| id | int | 工艺号,范围 [1, 9] |
| address | int | 起始地址 |
| quantity | int | 读取数量;范围 [1, 2000] |
| data | `std::vector&` | 输出参数,读取到的线圈状态数组;每个元素为 `0` 或 `1` |
**返回值:** `Result` 结构体。
**使用示例:**
```cpp
std::vector coilData;
Result ret = modbus_read_coil_status(socketFd, 1, 0, 10, coilData);
if (ret.result) {
for (size_t i = 0; i < coilData.size(); i++) {
printf("Coil %zu: %d\n", i, coilData[i]);
}
}
```
---
## 功能码 02H - 读输入状态
### modbus_read_input_status
读取从站离散输入状态(位操作,只读)。
```cpp
Result modbus_read_input_status(SOCKETFD socketFd, int id, int address, int quantity, std::vector& data);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 套接字文件描述符 |
| id | int | 工艺号,范围 [1, 9] |
| address | int | 起始地址 |
| quantity | int | 读取数量;范围 [1, 2000] |
| data | `std::vector&` | 输出参数,读取到的输入状态数组;每个元素为 `0` 或 `1` |
**返回值:** `Result` 结构体。
**使用示例:**
```cpp
std::vector inputStatus;
Result ret = modbus_read_input_status(socketFd, 1, 100, 16, inputStatus);
```
---
## 功能码 03H - 读保持寄存器
### modbus_read_holding_registers
读取从站保持寄存器(16 位操作,可读可写)。
```cpp
Result modbus_read_holding_registers(SOCKETFD socketFd, int id, int address, int quantity, std::vector& data);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 套接字文件描述符 |
| id | int | 工艺号,范围 [1, 9] |
| address | int | 起始地址 |
| quantity | int | 读取数量;范围 [1, 125] |
| data | `std::vector&` | 输出参数,读取到的保持寄存器值数组;每个元素范围 [0, 65535] |
**返回值:** `Result` 结构体。
**使用示例:**
```cpp
std::vector holdingReg;
Result ret = modbus_read_holding_registers(socketFd, 1, 200, 5, holdingReg);
if (ret.result) {
for (size_t i = 0; i < holdingReg.size(); i++) {
printf("Register %zu: %d\n", i, holdingReg[i]);
}
}
```
---
## 功能码 04H - 读输入寄存器
### modbus_read_input_registers
读取从站输入寄存器(16 位操作,只读)。
```cpp
Result modbus_read_input_registers(SOCKETFD socketFd, int id, int address, int quantity, std::vector& data);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 套接字文件描述符 |
| id | int | 工艺号,范围 [1, 9] |
| address | int | 起始地址 |
| quantity | int | 读取数量;范围 [1, 125] |
| data | `std::vector&` | 输出参数,读取到的输入寄存器值数组;每个元素范围 [0, 65535] |
**返回值:** `Result` 结构体。
**使用示例:**
```cpp
std::vector inputReg;
Result ret = modbus_read_input_registers(socketFd, 1, 300, 8, inputReg);
```
---
## 功能码 05H - 写单个线圈
### modbus_write_signal_coil_status
写单个线圈状态。
```cpp
Result modbus_write_signal_coil_status(SOCKETFD socketFd, int id, int address, int data);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 套接字文件描述符 |
| id | int | 工艺号,范围 [1, 9] |
| address | int | 线圈地址 |
| data | int | 写入值;`0` 或 `1` |
**返回值:** `Result` 结构体。
**使用示例:**
```cpp
// 将地址 10 的线圈置为 ON
Result ret = modbus_write_signal_coil_status(socketFd, 1, 10, 1);
```
---
## 功能码 06H - 写单个保持寄存器
### modbus_write_signal_holding_registers
写单个保持寄存器。
```cpp
Result modbus_write_signal_holding_registers(SOCKETFD socketFd, int id, int address, int data);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 套接字文件描述符 |
| id | int | 工艺号,范围 [1, 9] |
| address | int | 寄存器地址 |
| data | int | 写入值;范围 [0, 65535] |
**返回值:** `Result` 结构体。
**使用示例:**
```cpp
// 将地址 200 的寄存器写入 9999
Result ret = modbus_write_signal_holding_registers(socketFd, 1, 200, 9999);
```
---
## 功能码 0FH - 写多个线圈
### modbus_write_multiple_coil_status
批量写入多个线圈状态。
```cpp
Result modbus_write_multiple_coil_status(SOCKETFD socketFd, int id, int address, const std::vector& data);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 套接字文件描述符 |
| id | int | 工艺号,范围 [1, 9] |
| address | int | 起始地址 |
| data | const `std::vector&` | 写入的线圈值数组;每个元素为 `0` 或 `1` |
**返回值:** `Result` 结构体。
**使用示例:**
```cpp
std::vector coilValues = {1, 0, 1, 0, 1, 1, 0, 0, 1, 0};
Result ret = modbus_write_multiple_coil_status(socketFd, 1, 20, coilValues);
```
---
## 功能码 10H - 写多个保持寄存器
### modbus_write_multiple_holding_registers
批量写入多个保持寄存器。
```cpp
Result modbus_write_multiple_holding_registers(SOCKETFD socketFd, int id, int address, const std::vector& data);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 套接字文件描述符 |
| id | int | 工艺号,范围 [1, 9] |
| address | int | 起始地址 |
| data | const `std::vector&` | 写入的寄存器值数组;每个元素范围 [0, 65535] |
**返回值:** `Result` 结构体。
**使用示例:**
```cpp
std::vector regValues = {100, 200, 300, 400, 500};
Result ret = modbus_write_multiple_holding_registers(socketFd, 1, 200, regValues);
```
---
## 完整使用示例
### TCP 主站读写完整流程
```cpp
#include "nrc_modbus.h"
void modbus_tcp_example(SOCKETFD socketFd) {
// 1. 设置主站参数(TCP 模式)
ModbusMasterParameter masterParam;
masterParam.type = "TCP";
masterParam.startAddress = false; // 起始地址为 1
masterParam.TCP.IP = "192.168.1.14";
masterParam.TCP.port = 503;
Result ret = modbus_set_master_parameter(socketFd, 1, masterParam);
if (!ret.result) {
printf("设置主站参数失败\n");
return;
}
// 2. 打开主站
ret = modbus_open_master(socketFd, 1);
if (!ret.result) {
printf("打开主站失败\n");
return;
}
// 3. 检查连接状态
int status = 0;
ret = modbus_get_master_connection_status(socketFd, 1, status);
if (ret.result && status == 1) {
printf("Modbus TCP 连接成功\n");
}
// 4. 读线圈(功能码 01H)
std::vector coilData;
ret = modbus_read_coil_status(socketFd, 1, 0, 10, coilData);
if (ret.result) {
printf("读取线圈成功: ");
for (int v : coilData) printf("%d ", v);
printf("\n");
}
// 5. 写单个线圈(功能码 05H)
ret = modbus_write_signal_coil_status(socketFd, 1, 3, 1);
// 6. 读保持寄存器(功能码 03H)
std::vector holdingReg;
ret = modbus_read_holding_registers(socketFd, 1, 200, 5, holdingReg);
if (ret.result) {
printf("读取保持寄存器成功: ");
for (int v : holdingReg) printf("%d ", v);
printf("\n");
}
// 7. 写多个保持寄存器(功能码 10H)
std::vector writeReg = {1000, 2000, 3000};
ret = modbus_write_multiple_holding_registers(socketFd, 1, 400, writeReg);
}
```
### RTU 主站配置示例
```cpp
ModbusMasterParameter rtuParam;
rtuParam.type = "RTU";
rtuParam.startAddress = false;
rtuParam.RTU.slaveId = 1;
rtuParam.RTU.port = 2;
rtuParam.RTU.baudrate = 115200;
rtuParam.RTU.checkBit = "Even";
rtuParam.RTU.dataBit = 8;
rtuParam.RTU.stopBit = 1;
Result ret = modbus_set_master_parameter(socketFd, 2, rtuParam);
```
---
## Modbus 功能码汇总
| 函数 | 功能码 | 操作类型 | 说明 |
|------|--------|----------|------|
| `modbus_read_coil_status` | 01H | 读 | 读取线圈状态(位,可读可写) |
| `modbus_read_input_status` | 02H | 读 | 读取输入状态(位,只读) |
| `modbus_read_holding_registers` | 03H | 读 | 读取保持寄存器(16 位,可读可写) |
| `modbus_read_input_registers` | 04H | 读 | 读取输入寄存器(16 位,只读) |
| `modbus_write_signal_coil_status` | 05H | 写 | 写单个线圈 |
| `modbus_write_signal_holding_registers` | 06H | 写 | 写单个保持寄存器 |
| `modbus_write_multiple_coil_status` | 0FH | 写 | 批量写多个线圈 |
| `modbus_write_multiple_holding_registers` | 10H | 写 | 批量写多个保持寄存器 |
## 注意事项
1. **工艺号范围**:`id` 参数范围为 [1, 9],最多支持 9 个独立的 Modbus 主站配置
2. **地址偏移**:`startAddress` 为 `false` 时,写入地址会自动减 1;为 `true` 时地址不变
3. **读取限制**:线圈/输入状态单次最多读取 2000 个,寄存器单次最多读取 125 个
4. **从站功能**:从站操作接口暂未开放
5. **错误处理**:所有函数返回 `Result` 结构体,需检查 `result` 字段确认操作是否成功
---
# 四、轨迹记录与回放(nrc_track.h)
> 原文:https://open.inexbot.com/zh/04.%E4%B8%8A%E4%BD%8D%E6%9C%BA/01.C++/02.%E6%8E%A5%E5%8F%A3/04.%E8%BD%A8%E8%BF%B9%E8%AE%B0%E5%BD%95%E4%B8%8E%E5%9B%9E%E6%94%BE.html
## 功能概述
轨迹记录与回放功能用于手动示教时记录机器人末端运动轨迹,并可按设定速度回放,适用于喷涂、点胶等需要复现人工轨迹的工艺场景。
**基本流程:** 开始记录 → 手动移动机器人 → 停止记录 → 保存轨迹 → 回放轨迹
| 步骤 | 接口 | 说明 |
|------|------|------|
| 1. 开始记录 | `track_record_start` | 设置采样点数与采样间隔 |
| 2. 手动示教 | 示教器/点动 | 移动机器人到目标轨迹 |
| 3. 停止记录 | `track_record_stop` | 结束轨迹采集 |
| 4. 保存 | `track_record_save` | 以名称保存轨迹数据 |
| 5. 回放 | `track_record_playback` | 按设定速度复现轨迹 |
> 所有接口均提供 `_robot` 后缀版本,用于指定机器人编号(1-4)。
---
### track_record_start / track_record_start_robot
轨迹记录开始。设置采样参数后开始采集机器人末端轨迹。
**函数原型:**
```cpp
Result track_record_start(SOCKETFD socketFd, double maxSamplingNum, double samplingInterval);
Result track_record_start_robot(SOCKETFD socketFd, int robotNum, double maxSamplingNum, double samplingInterval);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| maxSamplingNum | double | 最大采样点数,范围 [200, 12000] |
| samplingInterval | double | 采样间隔(秒),范围 [0.03, 1] |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
**使用示例:**
```cpp
// 以 5000 个采样点、0.05s 间隔开始轨迹记录
Result result = track_record_start(fd, 5000, 0.05);
if (result == SUCCESS) {
printf("轨迹记录已开始\n");
// 此时手动移动机器人进行示教...
}
```
---
### track_record_stop / track_record_stop_robot
轨迹记录关闭,结束轨迹数据采集。
**函数原型:**
```cpp
Result track_record_stop(SOCKETFD socketFd);
Result track_record_stop_robot(SOCKETFD socketFd, int robotNum);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
**使用示例:**
```cpp
// 示教完成后停止记录
Result result = track_record_stop(fd);
```
---
### get_track_record_status / get_track_record_status_robot
查询轨迹记录开启状态。
**函数原型:**
```cpp
Result get_track_record_status(SOCKETFD socketFd, bool& recordStart);
Result get_track_record_status_robot(SOCKETFD socketFd, int robotNum, bool& recordStart);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| recordStart | bool& | 输出参数,记录状态:true=开启,false=关闭 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
**使用示例:**
```cpp
bool isRecording = false;
Result result = get_track_record_status(fd, isRecording);
if (result == SUCCESS && isRecording) {
printf("轨迹正在记录中\n");
}
```
---
### track_record_save / track_record_save_robot
轨迹记录保存,将采集到的轨迹数据以指定名称保存。
**函数原型:**
```cpp
Result track_record_save(SOCKETFD socketFd, std::string trajName);
Result track_record_save_robot(SOCKETFD socketFd, int robotNum, std::string trajName);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| trajName | std::string | 保存的轨迹名称 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
**使用示例:**
```cpp
// 将记录的轨迹保存为 "paint_path"
Result result = track_record_save(fd, "paint_path");
if (result == SUCCESS) {
printf("轨迹保存成功\n");
}
```
---
### track_record_playback / track_record_playback_robot
轨迹回放,按设定速度复现已保存的轨迹。
**函数原型:**
```cpp
Result track_record_playback(SOCKETFD socketFd, int vel);
Result track_record_playback_robot(SOCKETFD socketFd, int robotNum, int vel);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| vel | int | 回放速度(百分比) |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
**使用示例:**
```cpp
// 以 80% 速度回放已保存轨迹
Result result = track_record_playback(fd, 80);
if (result == SUCCESS) {
printf("轨迹回放开始\n");
}
```
---
### track_record_delete / track_record_delete_robot
轨迹清除,删除当前保存的轨迹数据。
**函数原型:**
```cpp
Result track_record_delete(SOCKETFD socketFd);
Result track_record_delete_robot(SOCKETFD socketFd, int robotNum);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
**使用示例:**
```cpp
// 清除轨迹数据
Result result = track_record_delete(fd);
```
---
---
# 五、双臂机器人(nrc_dual_arm.h)
> 原文:https://open.inexbot.com/zh/04.%E4%B8%8A%E4%BD%8D%E6%9C%BA/01.C++/02.%E6%8E%A5%E5%8F%A3/05.%E5%8F%8C%E8%87%82%E6%9C%BA%E5%99%A8%E4%BA%BA.html
## 功能概述
双臂机器人接口用于双臂协同场景下的用户坐标系管理、单条运动指令完成回调及双臂 OXY 标定。
**双臂用户坐标系特点:**
| locationType | 说明 |
|--------------|------|
| 0 | 静态用户坐标系 |
| 1 | 联动坐标系(跟随另一机械臂运动) |
> 双臂用户坐标标定流程:设置坐标数据 → OXY 标定 → 计算坐标。
---
### set_one_movecomd_completion_callback
设置一条 move 指令执行完成时的回调函数。
**函数原型:**
```cpp
Result set_one_movecomd_completion_callback(SOCKETFD socketFd, void(*function)());
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| function | void(*)() | 回调函数指针,一条 move 指令执行完成时被调用 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
**使用示例:**
```cpp
void on_move_done() {
printf("一条 move 指令执行完成\n");
}
Result result = set_one_movecomd_completion_callback(fd, on_move_done);
```
---
### set_dualarm_user_coord_number / set_dualarm_user_coord_number_robot
设置双臂用户坐标编号。
**函数原型:**
```cpp
Result set_dualarm_user_coord_number(SOCKETFD socketFd, int userNum);
Result set_dualarm_user_coord_number_robot(SOCKETFD socketFd, int robotNum, int userNum);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| userNum | int | 用户坐标编号 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
**使用示例:**
```cpp
// 切换到用户坐标系 2
Result result = set_dualarm_user_coord_number(fd, 2);
```
---
### get_dualarm_user_coord_number / get_dualarm_user_coord_number_robot
获取当前使用的用户坐标编号。
**函数原型:**
```cpp
Result get_dualarm_user_coord_number(SOCKETFD socketFd, int& userNum);
Result get_dualarm_user_coord_number_robot(SOCKETFD socketFd, int robotNum, int& userNum);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| userNum | int& | 输出参数,当前用户坐标编号 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### get_dualarm_user_coord_para / get_dualarm_user_coord_para_robot
获取用户坐标参数(含坐标系类型和机械臂号)。
**函数原型:**
```cpp
Result get_dualarm_user_coord_para(SOCKETFD socketFd, int userNum, std::vector& pos, int& locationType, int& mechID);
Result get_dualarm_user_coord_para_robot(SOCKETFD socketFd, int robotNum, int userNum, std::vector& pos, int& locationType, int& mechID);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| userNum | int | 用户坐标编号 |
| pos | std::vector\& | 输出参数,用户坐标参数 |
| locationType | int& | 输出参数,坐标系类型:0=静态用户坐标系,1=联动坐标系 |
| mechID | int& | 输出参数,机器人号 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### set_dualarm_user_coordinate_data / set_dualarm_user_coordinate_data_robot
标定双臂用户坐标(写入坐标数据)。
**函数原型:**
```cpp
Result set_dualarm_user_coordinate_data(SOCKETFD socketFd, int userNum, std::vector pos, int locationType, int mechID);
Result set_dualarm_user_coordinate_data_robot(SOCKETFD socketFd, int robotNum, int userNum, std::vector pos, int locationType, int mechID);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| userNum | int | 用户坐标编号 |
| pos | std::vector\ | 坐标数据 |
| locationType | int | 坐标系类型:0=静态用户坐标系,1=联动坐标系 |
| mechID | int | 机器人号 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### dualarm_calibration_oxy / dualarm_calibration_oxy_robot
双臂 OXY 标定。
**函数原型:**
```cpp
Result dualarm_calibration_oxy(SOCKETFD socketFd, int userNum, std::string xyo);
Result dualarm_calibration_oxy_robot(SOCKETFD socketFd, int robotNum, int userNum, std::string xyo);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| userNum | int | 用户坐标编号 |
| xyo | std::string | 标定类型:'X'、'Y'、'O'(分别标定 X 轴方向点、Y 轴方向点、原点) |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
**使用示例:**
```cpp
// 依次标定原点、X 轴方向点、Y 轴方向点
dualarm_calibration_oxy(fd, 1, "O");
// 移动机器人到原点位置后...
dualarm_calibration_oxy(fd, 1, "X");
// 移动机器人到 X 轴方向点后...
dualarm_calibration_oxy(fd, 1, "Y");
```
---
### dualarm_calculate_user_coordinate / dualarm_calculate_user_coordinate_robot
计算双臂用户坐标(标定完成后计算最终结果)。
**函数原型:**
```cpp
Result dualarm_calculate_user_coordinate(SOCKETFD socketFd, int userNumber);
Result dualarm_calculate_user_coordinate_robot(SOCKETFD socketFd, int robotNum, int userNum);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| userNum / userNumber | int | 用户坐标编号 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
**使用示例:**
```cpp
// OXY 三点标定完成后计算用户坐标
Result result = dualarm_calculate_user_coordinate(fd, 1);
if (result == SUCCESS) {
printf("双臂用户坐标计算成功\n");
}
```
---
---
# 六、VFD 主轴变频器控制(nrc_vfd_ctr.h)
> 原文:https://open.inexbot.com/zh/04.%E4%B8%8A%E4%BD%8D%E6%9C%BA/01.C++/02.%E6%8E%A5%E5%8F%A3/06.VFD%E4%B8%BB%E8%BD%B4%E5%8F%98%E9%A2%91%E5%99%A8.html
### vfd_run
VFD 运行。
---
### vfd_shutdown
VFD 停机。
---
### vfd_spindle_positioning_stop
VFD 准停。
---
### get_vfd_vel
查询 VFD 电流、速度、角度。
---
---
# 七、作业文件操作(nrc_job_operate.h)
> 原文:https://open.inexbot.com/zh/04.%E4%B8%8A%E4%BD%8D%E6%9C%BA/01.C++/02.%E6%8E%A5%E5%8F%A3/07.%E4%BD%9C%E4%B8%9A%E6%96%87%E4%BB%B6%E6%93%8D%E4%BD%9C.html
## 功能概述
作业文件(.JBR)是控制器的核心程序文件,由多条指令组成。本模块提供作业文件的上传/下载/新建/删除/打开、运行控制(运行/单步/暂停/继续/停止)、内容读取与编辑(按行查询/删除/插入指令)等完整操作能力。
**作业文件生命周期:** 上传/新建 → 打开 → 编辑(插入/修改指令)→ 运行 → 暂停/继续 → 停止
## 7.1 作业文件管理
### job_get_all_jobfile_name
获取所有作业文件名。
**函数原型:**
```cpp
Result job_get_all_jobfile_name(SOCKETFD socketFd, std::vector>& robotsFile);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotsFile | std::vector\\>& | 输出参数,二维数组,一维长度 4,对应 4 个机器人的作业文件列表 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
**使用示例:**
```cpp
std::vector> files;
Result result = job_get_all_jobfile_name(fd, files);
if (result == SUCCESS) {
printf("机器人1的作业文件: \n");
for (const auto& name : files[0]) {
printf(" %s\n", name.c_str());
}
}
```
---
### job_upload_by_directory
根据文件夹上传一整个文件夹的作业文件。
**函数原型:**
```cpp
Result job_upload_by_directory(SOCKETFD socketFd, const std::string& directoryPath);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| directoryPath | const std::string& | 目录的完整路径 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
**使用示例:**
```cpp
Result result = job_upload_by_directory(fd, "C:/jobs/robot1");
```
---
### job_upload_by_file
根据文件名上传一个作业文件。
**函数原型:**
```cpp
Result job_upload_by_file(SOCKETFD socketFd, const std::string& filePath);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| filePath | const std::string& | 文件的完整路径 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
**使用示例:**
```cpp
Result result = job_upload_by_file(fd, "C:/jobs/main.JBR");
```
---
### job_sync_job_file
上传作业文件后同步刷新示教器文件列表。
**函数原型:**
```cpp
Result job_sync_job_file(SOCKETFD socketFd);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
> 调用 `job_upload_by_directory` / `job_upload_by_file` 后建议调用本接口刷新示教器显示。
---
### job_download_by_directory
下载所有作业文件到指定文件夹。
**函数原型:**
```cpp
Result job_download_by_directory(SOCKETFD socketFd, const std::string& directoryPath, bool isCover);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| directoryPath | const std::string& | 目录的完整路径 |
| isCover | bool | 是否覆盖已存在的文件 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### log_download_by_quantity
下载指定数量的日志文件到指定文件夹。
**函数原型:**
```cpp
Result log_download_by_quantity(SOCKETFD socketFd, int counts, const std::string& directoryPath);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| counts | int | 文件数量 |
| directoryPath | const std::string& | 目录的完整路径 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### backup_system
一键备份系统,保存至当前执行程序目录下。
**函数原型:**
```cpp
Result backup_system(SOCKETFD socketFd);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### job_create / job_create_robot
新建作业文件。
**函数原型:**
```cpp
Result job_create(SOCKETFD socketFd, const std::string& jobName);
Result job_create_robot(SOCKETFD socketFd, int robotNum, const std::string& jobName);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| jobName | const std::string& | 作业文件名,只允许字母开头,字母数字组合 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
**使用示例:**
```cpp
// 新建 QQQ.JBR
Result result = job_create(fd, "QQQ");
```
---
### job_delete / job_delete_robot
删除指定的作业文件。
**函数原型:**
```cpp
Result job_delete(SOCKETFD socketFd, const std::string& jobName);
Result job_delete_robot(SOCKETFD socketFd, int robotNum, const std::string& jobName);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| jobName | const std::string& | 作业文件名 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
**使用示例:**
```cpp
// 删除 QQQ.JBR
Result result = job_delete(fd, "QQQ");
```
---
### job_open / job_open_robot
打开指定的作业文件(打开后才能进行内容读取/编辑操作)。
**函数原型:**
```cpp
Result job_open(SOCKETFD socketFd, const std::string& jobName);
Result job_open_robot(SOCKETFD socketFd, int robotNum, const std::string& jobName);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| jobName | const std::string& | 作业文件名 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
**使用示例:**
```cpp
// 打开 QQQ.JBR
Result result = job_open(fd, "QQQ");
```
---
## 7.2 作业内容操作
### job_get_command_total_lines / job_get_command_total_lines_robot
获取当前打开作业文件的总行数。
**函数原型:**
```cpp
Result job_get_command_total_lines(SOCKETFD socketFd, int& totalLines);
Result job_get_command_total_lines_robot(SOCKETFD socketFd, int robotNum, int& totalLines);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| totalLines | int& | 输出参数,总行数 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
**使用示例:**
```cpp
int total = 0;
Result result = job_get_command_total_lines(fd, total);
printf("作业文件共 %d 行\n", total);
```
---
### job_get_command_content_by_line / job_get_command_content_by_line_robot
获取指定行号的作业文件内容。
**函数原型:**
```cpp
Result job_get_command_content_by_line(SOCKETFD socketFd, int line, int& commandType, std::string& jobContent);
Result job_get_command_content_by_line_robot(SOCKETFD socketFd, int robotNum, int line, int& commandType, std::string& jobContent);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| line | int | 行号 |
| commandType | int& | 输出参数,指令类型 |
| jobContent | std::string& | 输出参数,该行指令内容 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
**使用示例:**
```cpp
int cmdType = 0;
std::string content;
Result result = job_get_command_content_by_line(fd, 3, cmdType, content);
if (result == SUCCESS) {
printf("第3行: %s\n", content.c_str());
}
```
---
### job_delete_command_by_line / job_delete_command_by_line_robot
删除指定行号的指令。
**函数原型:**
```cpp
Result job_delete_command_by_line(SOCKETFD socketFd, int line);
Result job_delete_command_by_line_robot(SOCKETFD socketFd, int robotNum, int line);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| line | int | 要删除的行号 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
## 7.3 作业运行控制
### job_run / job_run_robot
运行指定的作业文件。
**函数原型:**
```cpp
Result job_run(SOCKETFD socketFd, const std::string& jobName);
Result job_run_robot(SOCKETFD socketFd, int robotNum, const std::string& jobName);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| jobName | const std::string& | 作业文件名 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
**使用示例:**
```cpp
// 运行 QQQ.JBR
Result result = job_run(fd, "QQQ");
```
---
### job_step / job_step_robot
单步运行指定的作业文件的某一行。
**函数原型:**
```cpp
Result job_step(SOCKETFD socketFd, const std::string& jobName, int line);
Result job_step_robot(SOCKETFD socketFd, int robotNum, const std::string& jobName, int line);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| jobName | const std::string& | 作业文件名 |
| line | int | 行号,范围 [1, 最大行号] |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
**使用示例:**
```cpp
// 单步运行 QQQ.JBR 的第一行
Result result = job_step(fd, "QQQ", 1);
```
---
### job_pause / job_pause_robot
暂停当前运行的作业文件。
**函数原型:**
```cpp
Result job_pause(SOCKETFD socketFd);
Result job_pause_robot(SOCKETFD socketFd, int robotNum);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### job_continue / job_continue_robot
继续运行暂停的作业文件。
**函数原型:**
```cpp
Result job_continue(SOCKETFD socketFd);
Result job_continue_robot(SOCKETFD socketFd, int robotNum);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
> 需要运行模式。
---
### job_stop / job_stop_robot
停止作业文件(不会下电)。
**函数原型:**
```cpp
Result job_stop(SOCKETFD socketFd);
Result job_stop_robot(SOCKETFD socketFd, int robotNum);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### job_run_times / job_run_times_robot
设置作业文件运行次数。
**函数原型:**
```cpp
Result job_run_times(SOCKETFD socketFd, int index);
Result job_run_times_robot(SOCKETFD socketFd, int robotNum, int index);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| index | int | 运行次数,0=无限次 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### job_break_point_run / job_break_point_run_robot
断点续跑指定的作业文件(从上次中断位置继续运行)。
**函数原型:**
```cpp
Result job_break_point_run(SOCKETFD socketFd, const std::string& jobName);
Result job_break_point_run_robot(SOCKETFD socketFd, int robotNum, const std::string& jobName);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| jobName | const std::string& | 作业文件名 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
> 需要运行模式。
---
### job_get_current_file / job_get_current_file_robot
获取当前打开的作业文件名称(`std::string` 版本)。
**函数原型:**
```cpp
Result job_get_current_file(SOCKETFD socketFd, std::string& jobName);
Result job_get_current_file_robot(SOCKETFD socketFd, int robotNum, std::string& jobName);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| jobName | std::string& | 输出参数,当前打开的作业文件名 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### job_get_current_file_csharp / job_get_current_file_csharp_robot
获取当前打开的作业文件名称(`std::vector` 版本,供 C# 使用)。
**函数原型:**
```cpp
Result job_get_current_file_csharp(SOCKETFD socketFd, std::vector& jobName);
Result job_get_current_file_csharp_robot(SOCKETFD socketFd, int robotNum, std::vector& jobName);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| jobName | std::vector\& | 输出参数,当前打开的作业文件名 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### job_get_current_line / job_get_current_line_robot
获取当前打开的作业文件运行到的行数。
**函数原型:**
```cpp
Result job_get_current_line(SOCKETFD socketFd, int& line);
Result job_get_current_line_robot(SOCKETFD socketFd, int robotNum, int& line);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| line | int& | 输出参数,运行到的行数 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
## 7.4 点位操作
### job_insert_local_position / job_insert_local_position_robot
作业文件插入一个局部点位。
**函数原型:**
```cpp
Result job_insert_local_position(SOCKETFD socketFd, PositionData posData);
Result job_insert_local_position_robot(SOCKETFD socketFd, int robotNum, PositionData posData);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| posData | PositionData | 位置数据 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### job_set_local_position / job_set_local_position_robot
根据点位名修改当前作业文件局部点位。
**函数原型:**
```cpp
Result job_set_local_position(SOCKETFD socketFd, const std::string& posName, std::vector posInfo);
Result job_set_local_position_robot(SOCKETFD socketFd, int robotNum, const std::string& posName, std::vector posInfo);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| posName | const std::string& | 需要修改的点位名 |
| posInfo | std::vector\ | 点位数据,长度 14:`[0]`坐标系(0=关节,1=直角,2=工具,3=用户);`[1]`角度制(0)/弧度制(1);`[2]`形态;`[3]`工具手坐标序号;`[4]`用户坐标序号;`[5][6]`备用;`[7-13]`点位信息 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
## 7.5 运动指令插入
向作业文件指定行号插入运动指令,所有接口均提供 `_robot` 版本。
| 接口 | 指令 | 说明 |
|------|------|------|
| `job_insert_moveJ` | MoveJ | 关节运动指令 |
| `job_insert_moveL` | MoveL | 直线运动指令 |
| `job_insert_moveC` | MoveC | 圆弧运动指令 |
| `job_insert_imove` | IMOVE | 增量运动指令(`moveCmd.targetPosType = 3`,`moveCmd.targetPosName = "RP0001"`) |
| `job_insert_moveComm` | moveComm | 外部点指令,`moveType` 为插补方式 `"MovJ"`/`"MovL"`,参数含速度/加速度/减速度/时间/平滑度 |
| `job_insert_samov_command` | SAMOV | 定点移动指令(点位数据用 PositionData) |
| `job_insert_EImoveL` | EImoveL | 外部轴直线运动 |
| `job_insert_EImoveC` | EImoveC | 外部轴圆弧运动 |
| `job_insert_EImoveCA` | EImoveCA | 外部轴圆弧绝对运动 |
**moveJ/moveL/moveC 函数原型:**
```cpp
Result job_insert_moveJ(SOCKETFD socketFd, int line, MoveCmd moveCmd);
Result job_insert_moveL(SOCKETFD socketFd, int line, MoveCmd moveCmd);
Result job_insert_moveC(SOCKETFD socketFd, int line, MoveCmd moveCmd);
```
**moveComm 函数原型:**
```cpp
Result job_insert_moveComm(SOCKETFD socketFd, int line, std::string moveType, double m_vel, double m_acc, double m_dec, int m_time, int m_pl);
```
**使用示例(插入 MoveJ 指令):**
```cpp
MoveCmd cmd;
cmd.coord = 0; // 关节坐标
cmd.velocity = 80; // 速度 80%
cmd.acc = 50;
cmd.dec = 50;
// 在第 5 行插入 MoveJ 指令
Result result = job_insert_moveJ(fd, 5, cmd);
```
---
## 7.6 延迟、IO、逻辑控制指令插入
| 接口 | 指令 | 说明 |
|------|------|------|
| `job_insert_timer_command` | TIMER | 延时指令,`time` 单位秒 |
| `job_insert_io_out_command` | IO_OUT | IO 输出指令,参数 `IOCommandParams` |
| `job_insert_until` | UNTIL | 直到循环开始(条件组 + 逻辑类型) |
| `job_insert_end_until` | END_UNTIL | 直到循环结束 |
| `job_insert_while` | WHILE | 条件循环开始 |
| `job_insert_end_while` | END_WHILE | 条件循环结束 |
| `job_insert_if` | IF | 条件判断开始 |
| `job_insert_end_if` | END_IF | 条件判断结束 |
| `job_insert_label` | LABEL | 标签指令,`label` 为标签名 |
| `job_insert_jump` | JUMP | 跳转指令(条件组 + 跳转条件标志 + 目标标签) |
**条件指令通用参数:**
| 参数 | 类型 | 说明 |
|------|------|------|
| conditionGroups | std::vector\\> | 条件组的二维向量 |
| logic | std::vector\ | 第一层逻辑类型,0=与,1=或 |
| logicGroup | std::vector\\> | 第二层逻辑类型,0=与,1=或 |
**使用示例(插入延时指令):**
```cpp
// 在第 3 行插入 2 秒延时
Result result = job_insert_timer_command(fd, 3, 2.0);
```
**使用示例(插入 IF 指令):**
```cpp
std::vector> conds; // 构造条件组...
std::vector logic = {0}; // 第一层"与"
std::vector> logicGroup = {{0}};
Result result = job_insert_if(fd, 5, conds, logic, logicGroup);
```
---
## 7.7 工艺指令插入
### 视觉工艺指令
| 接口 | 指令 | 说明 |
|------|------|------|
| `job_insert_vision_craft_start` | VISION_RUN | 开始视觉,参数 `id` 为工艺号 |
| `job_insert_vision_craft_get_pos` | VISION_POS | 获取视觉位置,`posName` 为存放位置变量名(如 GP0001) |
| `job_insert_vision_craft_trajectory` | VISION_TARCE | 获取视觉轨迹位置(24.03 可用),参数 `VisualTrajectoryPosition` |
| `job_insert_vision_craft_visual_trigger` | VISION_POS | 触发视觉,参数 `id` 为工艺号 |
| `job_insert_vision_craft_visual_end` | VISION_END | 视觉结束,参数 `id` 为工艺号 |
**使用示例:**
```cpp
// 在第 5 行插入视觉开始指令(工艺号 1)
Result result = job_insert_vision_craft_start(fd, 5, 1);
```
### 传送带工艺指令
| 接口 | 指令 | 说明 |
|------|------|------|
| `job_insert_conveyor_check_pos` | CONVEYOR_CHECKPOS | 传送带工件检测开始 |
| `job_insert_conveyor_check_end` | CONVEYOR_CHECKEND | 传送带工件检测结束 |
| `job_insert_conveyor_on` | CONVEYOR_ON | 传送带跟踪开始(`posTtype`:0=手动传点位,1=默认工件点;`vel` 范围 [2,2000]mm/s;`acc` 范围 [1,100]%) |
| `job_insert_conveyor_off` | CONVEYOR_OFF | 传送带跟踪结束 |
| `job_insert_conveyor_pos` | CONVEYOR_POS | 获取传送带跟踪位置,`posName` 为全局点位名 |
| `job_insert_conveyor_clear` | CONVEYOR_POS | 清除传送带跟踪目标(`removeType`:0=全部目标,1=本次目标) |
| `job_insert_conveyor_wait` | CONVEYOR_Wait | 传送带等料(24.03 可用),参数含超时时间/来料结果/来料位置 |
**CONVEYOR_ON 函数原型:**
```cpp
Result job_insert_conveyor_on(SOCKETFD socketFd, int line, int id, int postype, std::vector pos, int vel, int acc);
```
**使用示例:**
```cpp
// 在第 8 行插入传送带跟踪开始(工艺号 1,使用工件点,速度 500mm/s)
std::vector pos(14, 0);
Result result = job_insert_conveyor_on(fd, 8, 1, 1, pos, 500, 50);
```
### 相贯线与独立轴指令
| 接口 | 指令 | 说明 |
|------|------|------|
| `job_insert_cil` | CIL | 相贯线指令(MoveCmd + 工艺号) |
| `job_insert_axis_motion_position` | 轴点动 | 轴点到点运行控制(`AXISPostion`) |
| `job_insert_axis_motion_velocity` | 轴恒转速 | 轴恒转速控制(`AxisVelocity`) |
| `job_insert_axis_motion_torque` | 轴恒转矩 | 轴恒转矩控制(`AxisTorque`) |
| `job_insert_axis_motion_stop` | 轴停止 | 轴运动停止,`axisNum` 为轴号 |
| `job_insert_axis_motion_cancel_independent_control` | 取消独立控制 | 取消独立轴控制,`axisNum` 为轴号 |
| `job_insert_axis_motion_speed_judge` | 轴速度判断 | 轴运动速度判断(`Axisspeedjudge`) |
| `job_insert_axis_motion_position_stop` | 主轴准停 | 主轴准停指令,`angle` 为准停角度 |
---
## 7.8 多机协调指令插入
### job_insert_tasks / job_insert_tasks_rbobt
插入声明协调参数指令。
**函数原型:**
```cpp
Result job_insert_tasks(SOCKETFD socketFd, int line, int crafId, std::vector robotGroup);
Result job_insert_tasks_rbobt(SOCKETFD socketFd, int robotNum, int line, int crafId, std::vector robotGroup);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| line | int | 插入位置 |
| crafId | int | 协调任务号,范围 1-999 |
| robotGroup | std::vector\ | 需要同步的机器人组,传入需要同步的机器人编号 1-4 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
**使用示例:**
```cpp
// 声明协调任务 1,同步机器人 1 和 2
std::vector group = {1, 2};
Result result = job_insert_tasks(fd, 10, 1, group);
```
---
### job_insert_wait_sync_task / job_insert_wait_sync_task_rbobt
插入等待任务同步点指令。
**函数原型:**
```cpp
Result job_insert_wait_sync_task(SOCKETFD socketFd, int line, int crafId, std::string waitSignal);
Result job_insert_wait_sync_task_rbobt(SOCKETFD socketFd, int robotNum, int line, int crafId, std::string waitSignal);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| line | int | 插入位置 |
| crafId | int | 协调任务号,范围 1-999 |
| waitSignal | std::string | 同步信号量 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
**使用示例:**
```cpp
// 等待协调任务 1 的同步信号 "SYNC_1"
Result result = job_insert_wait_sync_task(fd, 12, 1, "SYNC_1");
```
---
---
# 八、队列运动模式(nrc_queue_operate.h)
> 原文:https://open.inexbot.com/zh/04.%E4%B8%8A%E4%BD%8D%E6%9C%BA/01.C++/02.%E6%8E%A5%E5%8F%A3/08.%E9%98%9F%E5%88%97%E8%BF%90%E5%8A%A8%E6%A8%A1%E5%BC%8F.html
## 8.1 队列模式控制
### queue_motion_set_status / queue_motion_set_status_robot
打开/关闭队列运动模式。打开或关闭都将清空远端队列。
---
### queue_motion_get_status / queue_motion_get_status_robot
查询队列运动模式状态。
---
### queue_motion_clear_Data / queue_motion_clear_Data_robot
清空缓存的运动队列数据。
---
### queue_motion_size / queue_motion_size_robot
查询队列长度。
---
### queue_motion_get_queuelen / queue_motion_get_queuelen_robot
查询队列剩余指令数。
---
### queue_motion_send_to_controller / queue_motion_send_to_controller_robot
发送队列数据到控制器。size: [0,31],0=全部发送。
---
### queue_motion_suspend / queue_motion_suspend_robot
暂停连续运动。
---
### queue_motion_restart / queue_motion_restart_robot
暂停后重启队列运动。
---
### queue_motion_stop / queue_motion_stop_robot
停止连续运动(已废弃,建议用 stop_not_power_off)。
---
### queue_motion_stop_not_power_off / queue_motion_stop_not_power_off_robot
停止连续运动(保持上电)。
---
## 8.2 队列运动指令插入
- `queue_motion_push_back_moveJ` / `queue_motion_push_back_moveJ_robot`
- `queue_motion_push_back_moveL` / `queue_motion_push_back_moveL_robot`
- `queue_motion_push_back_moveC` / `queue_motion_push_back_moveC_robot`
- `queue_motion_push_back_moveCA` / `queue_motion_push_back_moveCA_robot`
- `queue_motion_push_back_moveS` / `queue_motion_push_back_moveS_robot`
- `queue_motion_push_back_moveJ_extra` / `queue_motion_push_back_moveJ_extra_robot`
- `queue_motion_push_back_moveL_extra` / `queue_motion_push_back_moveL_extra_robot`
- `queue_motion_push_back_imove` / `queue_motion_push_back_imove_robot`
- `queue_motion_push_back_EImoveL` / `queue_motion_push_back_EImoveL_robot`
- `queue_motion_push_back_EImoveC` / `queue_motion_push_back_EImoveC_robot`
- `queue_motion_push_back_EImoveCA` / `queue_motion_push_back_EImoveCA_robot`
- `queue_motion_push_back_samov` / `queue_motion_push_back_samov_robot`
- `queue_motion_push_back_cil` / `queue_motion_push_back_cil_robot`
- `queue_motion_push_back_TOFFSETON` / `queue_motion_push_back_TOFFSETON_robot`
- `queue_motion_push_back_TOFFSETOFF` / `queue_motion_push_back_TOFFSETOFF_robot`
- `queue_motion_push_back_timer` / `queue_motion_push_back_timer_robot`
- `queue_motion_push_back_dout` / `queue_motion_push_back_dout_robot`
- `queue_motion_push_moveComm` / `queue_motion_push_moveComm_rbobt`
---
## 8.3 队列工艺指令插入
- `queue_motion_push_back_arc_on` / `queue_motion_push_back_arc_on_robot`
- `queue_motion_push_back_arc_off` / `queue_motion_push_back_arc_off_robot`
- `queue_motion_push_back_wave_on` / `queue_motion_push_back_wave_on_robot`
- `queue_motion_push_back_wave_off` / `queue_motion_push_back_wave_off_robot`
- `queue_motion_push_back_tigweld_on` / `queue_motion_push_back_tigweld_on_robot`
- `queue_motion_push_back_tigweld_off` / `queue_motion_push_back_tigweld_off_robot`
- `queue_motion_push_back_spot_weld` / `queue_motion_push_back_spot_weld_robot`
- `queue_motion_push_back_vision_craft_start` / `queue_motion_push_back_vision_craft_start_robot`
- `queue_motion_push_back_vision_craft_get_pos` / `queue_motion_push_back_vision_craft_get_pos_robot`
- `queue_motion_push_back_vision_craft_trajectory` / `queue_motion_push_back_vision_craft_trajectory_robot`
- `queue_motion_push_back_vision_craft_visual_trigger` / `queue_motion_push_back_vision_craft_visual_trigger_robot`
- `queue_motion_push_back_vision_craft_visual_end` / `queue_motion_push_back_vision_craft_visual_end_robot`
- `queue_motion_push_back_conveyor_check_pos` / `queue_motion_push_back_conveyor_check_pos_robot`
- `queue_motion_push_back_conveyor_check_end` / `queue_motion_push_back_conveyor_check_end_robot`
- `queue_motion_push_back_conveyor_on` / `queue_motion_push_back_conveyor_on_robot`
- `queue_motion_push_back_conveyor_off` / `queue_motion_push_back_conveyor_off_robot`
- `queue_motion_push_back_conveyor_pos` / `queue_motion_push_back_conveyor_pos_robot`
- `queue_motion_push_back_conveyor_clear` / `queue_motion_push_back_conveyor_clear_robot`
- `queue_motion_push_back_conveyor_wait` / `queue_motion_push_back_conveyor_wait_robot`
---
## 8.4 队列逻辑控制指令插入
- `queue_motion_push_back_until` / `queue_motion_push_back_until_robot`
- `queue_motion_push_back_end_until` / `queue_motion_push_back_end_until_robot`
- `queue_motion_push_back_while` / `queue_motion_push_back_while_robot`
- `queue_motion_push_back_end_while` / `queue_motion_push_back_end_while_robot`
- `queue_motion_push_back_if` / `queue_motion_push_back_if_robot`
- `queue_motion_push_back_end_if` / `queue_motion_push_back_end_if_robot`
- `queue_motion_push_back_label` / `queue_motion_push_back_label_robot`
- `queue_motion_push_back_jump` / `queue_motion_push_back_jump_robot`
---
## 8.5 队列轴控制指令插入
- `queue_motion_push_back_axis_motion_position` / `queue_motion_push_back_axis_motion_position_robot`
- `queue_motion_push_back_axis_motion_velocity` / `queue_motion_push_back_axis_motion_velocity_robot`
- `queue_motion_push_back_axis_motion_torque` / `queue_motion_push_back_axis_motion_torque_robot`
- `queue_motion_push_back_axis_motion_stop` / `queue_motion_push_back_axis_motion_stop_robot`
- `queue_motion_push_back_axis_motion_cancel_independent_control` / `queue_motion_push_back_axis_motion_cancel_independent_control_robot`
- `queue_motion_push_back_axis_motion_speed_judge` / `queue_motion_push_back_axis_motion_speed_judge_robot`
- `queue_motion_push_back_axis_motion_position_stop` / `queue_motion_push_back_axis_motion_position_stop_robot`
---
## 8.6 队列多机协调指令插入
- `queue_motion_push_back_tasks` / `queue_motion_push_back_tasks_robot`
- `queue_motion_push_back_wait_sync_task` / `queue_motion_push_back_wait_sync_task_robot`
---
---
# 九、焊接工艺(nrc_craft_weld.h)
> 原文:https://open.inexbot.com/zh/04.%E4%B8%8A%E4%BD%8D%E6%9C%BA/01.C++/02.%E6%8E%A5%E5%8F%A3/09.%E7%84%8A%E6%8E%A5%E5%B7%A5%E8%89%BA.html
焊接工艺接口提供焊接参数配置、送丝/退丝/送气控制、焊接使能、手动点焊、摆焊参数设置以及焊接状态监控等功能。支持单机器人和多机器人(`_robot` 后缀)两种调用方式。
## 数据结构
### ArcParam —— 焊接工艺参数
```cpp
struct ArcParam {
double weldVoltage; // 焊接电压 范围[-1000,1000]V
double weldCurrent; // 焊接电流 范围[0,1000]A
double arcOnCurrent; // 起弧电流 范围[0,1000]A
double arcOnVoltage; // 起弧电压 范围[-1000,1000]V
double arcOnTime; // 起弧时间 范围[0,5]S
bool arcOnRampEnable; // 起弧渐变 false:未使能 true:使能
int arcOnRampMode; // 起弧方式 时间渐变
double arcOnRampTime; // 起弧渐变时间 范围[0,100000]ms
double arcOffCurrent; // 收弧电流 范围[0,1000]A
double arcOffVoltage; // 收弧电压 范围[-1000,1000]V
double arcOffTime; // 收弧时间 范围[0,5]S
bool arcOffRampEnable; // 收弧渐变 false:未使能 true:使能
int arcOffRampMode; // 收弧方式 时间渐变
double arcOffRampTime; // 收弧渐变时间 范围[0,100000]ms
};
```
### WaveParam —— 摆焊参数
```cpp
struct WaveParam {
int type; // 摆法方式 0:正弦摆 1:Z字形 2:圆形摆 3:外部轴定点摆
double swingFreq; // 摆动频率
double swingAmplitude; // 摆动幅度
double radius; // 半径
double LTypeAngle; // L型角度
bool moveWhenEdgeStay; // 边缘停留
double leftStayTime; // 左停留时间
double rightStayTime; // 右停留时间
int initialDir; // 初始方向
double horizontalDeflection; // 水平偏转
double verticalDeflection; // 垂直偏转
};
```
### WeldState —— 焊接监控状态
```cpp
struct WeldState {
int pistolSwitch; // 焊枪开关 -1:未使能 0:错误 1:使能
int arcingSuccess; // 引弧成功 -1:未使能 0:错误 1:使能
int handWireFeed; // 手动送丝 -1:未使能 0:错误 1:使能
double weldCurrent; // 焊接电流
double weldVoltage; // 焊接电压
double weldTime; // 焊接时间
double weldPWM; // 焊接占空比
};
```
---
## 9.1 焊接参数配置
### weld_get_config / weld_get_config_robot
获取指定工艺号的焊接参数。
**函数签名:**
```cpp
Result weld_get_config(SOCKETFD socketFd, int index, ArcParam& param);
Result weld_get_config_robot(SOCKETFD socketFd, int robotNum, int index, ArcParam& param);
```
**参数说明:**
| 参数 | 类型 | 输入/输出 | 说明 |
|------|------|----------|------|
| socketFd | SOCKETFD | 输入 | 连接句柄 |
| robotNum | int | 输入 | 机器人编号(仅 `_robot` 版本) |
| index | int | 输入 | 工艺号 |
| param | ArcParam& | 输出 | 焊接工艺参数 |
**返回值:** `Result` 枚举,0 表示成功,负数表示失败(-1: 接收失败,-2: 断开连接,-6: 超时)。
**使用示例:**
```cpp
// 获取工艺号1的焊接参数
ArcParam param;
Result result = weld_get_config(fd, 1, param);
if (result == 0) {
printf("工艺号1的焊接参数:\n");
printf(" 焊接电压: %.2f V\n", param.weldVoltage);
printf(" 焊接电流: %.2f A\n", param.weldCurrent);
printf(" 起弧电流: %.2f A\n", param.arcOnCurrent);
printf(" 起弧时间: %.2f S\n", param.arcOnTime);
printf(" 收弧电流: %.2f A\n", param.arcOffCurrent);
printf(" 收弧时间: %.2f S\n", param.arcOffTime);
}
```
---
### weld_set_config / weld_set_config_robot
设置指定工艺号的焊接参数。
**函数签名:**
```cpp
Result weld_set_config(SOCKETFD socketFd, int index, ArcParam& param);
Result weld_set_config_robot(SOCKETFD socketFd, int robotNum, int index, ArcParam& param);
```
**参数说明:**
| 参数 | 类型 | 输入/输出 | 说明 |
|------|------|----------|------|
| socketFd | SOCKETFD | 输入 | 连接句柄 |
| robotNum | int | 输入 | 机器人编号(仅 `_robot` 版本) |
| index | int | 输入 | 工艺号 |
| param | ArcParam& | 输入 | 焊接工艺参数 |
**返回值:** `Result` 枚举,0 表示成功,负数表示失败。
**使用示例:**
```cpp
// 配置工艺号1的焊接参数
ArcParam param;
param.weldVoltage = 25.0; // 焊接电压 25V
param.weldCurrent = 180.0; // 焊接电流 180A
param.arcOnCurrent = 200.0; // 起弧电流 200A
param.arcOnVoltage = 28.0; // 起弧电压 28V
param.arcOnTime = 0.5; // 起弧时间 0.5秒
param.arcOnRampEnable = true; // 使能起弧渐变
param.arcOnRampMode = 0; // 起弧方式:时间渐变
param.arcOnRampTime = 200.0; // 起弧渐变时间 200ms
param.arcOffCurrent = 150.0; // 收弧电流 150A
param.arcOffVoltage = 22.0; // 收弧电压 22V
param.arcOffTime = 0.5; // 收弧时间 0.5秒
param.arcOffRampEnable = true; // 使能收弧渐变
param.arcOffRampMode = 0; // 收弧方式:时间渐变
param.arcOffRampTime = 200.0; // 收弧渐变时间 200ms
Result result = weld_set_config(fd, 1, param);
if (result == 0) {
printf("焊接参数设置成功\n");
}
```
---
## 9.2 送丝/退丝/送气控制
### weld_set_feed_wire / weld_set_feed_wire_robot
控制送丝功能。
**函数签名:**
```cpp
Result weld_set_feed_wire(SOCKETFD socketFd, int state);
Result weld_set_feed_wire_robot(SOCKETFD socketFd, int robotNum, int state);
```
**参数说明:**
| 参数 | 类型 | 输入/输出 | 说明 |
|------|------|----------|------|
| socketFd | SOCKETFD | 输入 | 连接句柄 |
| robotNum | int | 输入 | 机器人编号(仅 `_robot` 版本) |
| state | int | 输入 | 1: 开启送丝,0: 关闭送丝 |
**返回值:** `Result` 枚举,0 表示成功,负数表示失败。
**注意事项:** 送丝功能通常用于手动调试场景,在自动焊接过程中不建议单独调用。
**使用示例:**
```cpp
// 开启送丝
Result result = weld_set_feed_wire(fd, 1);
if (result == 0) {
printf("送丝已开启\n");
sleep(3); // 送丝3秒
// 关闭送丝
weld_set_feed_wire(fd, 0);
}
```
---
### weld_set_rewind_wire / weld_set_rewind_wire_robot
控制退丝功能。
**函数签名:**
```cpp
Result weld_set_rewind_wire(SOCKETFD socketFd, int state);
Result weld_set_rewind_wire_robot(SOCKETFD socketFd, int robotNum, int state);
```
**参数说明:**
| 参数 | 类型 | 输入/输出 | 说明 |
|------|------|----------|------|
| socketFd | SOCKETFD | 输入 | 连接句柄 |
| robotNum | int | 输入 | 机器人编号(仅 `_robot` 版本) |
| state | int | 输入 | 1: 开启退丝,0: 关闭退丝 |
**返回值:** `Result` 枚举,0 表示成功,负数表示失败。
**使用示例:**
```cpp
// 开启退丝
Result result = weld_set_rewind_wire(fd, 1);
if (result == 0) {
printf("退丝已开启\n");
sleep(2); // 退丝2秒
// 关闭退丝
weld_set_rewind_wire(fd, 0);
}
```
---
### weld_set_supply_gas / weld_set_supply_gas_robot
控制送气功能。
**函数签名:**
```cpp
Result weld_set_supply_gas(SOCKETFD socketFd, int state);
Result weld_set_supply_gas_robot(SOCKETFD socketFd, int robotNum, int state);
```
**参数说明:**
| 参数 | 类型 | 输入/输出 | 说明 |
|------|------|----------|------|
| socketFd | SOCKETFD | 输入 | 连接句柄 |
| robotNum | int | 输入 | 机器人编号(仅 `_robot` 版本) |
| state | int | 输入 | 1: 开启送气,0: 关闭送气 |
**返回值:** `Result` 枚举,0 表示成功,负数表示失败。
**使用示例:**
```cpp
// 焊前预送气
Result result = weld_set_supply_gas(fd, 1);
if (result == 0) {
printf("送气已开启,预送气中...\n");
sleep(1); // 预送气1秒
// 开始焊接...
// 焊接完成后关气
// weld_set_supply_gas(fd, 0);
}
```
---
## 9.3 焊接使能与手动点焊
### weld_set_enable / weld_set_enable_robot
控制焊接使能状态。
**函数签名:**
```cpp
Result weld_set_enable(SOCKETFD socketFd, int state);
Result weld_set_enable_robot(SOCKETFD socketFd, int robotNum, int state);
```
**参数说明:**
| 参数 | 类型 | 输入/输出 | 说明 |
|------|------|----------|------|
| socketFd | SOCKETFD | 输入 | 连接句柄 |
| robotNum | int | 输入 | 机器人编号(仅 `_robot` 版本) |
| state | int | 输入 | 1: 使能焊接,0: 关闭焊接使能 |
**返回值:** `Result` 枚举,0 表示成功,负数表示失败。
**注意事项:** 在自动焊接前需要先调用此接口使能焊接功能。关闭焊接使能后,焊接相关的 IO 输出将被禁用。
**使用示例:**
```cpp
// 使能焊接
Result result = weld_set_enable(fd, 1);
if (result == 0) {
printf("焊接使能已开启,可以开始焊接\n");
}
// 焊接完成后关闭使能
// weld_set_enable(fd, 0);
```
---
### weld_set_hand_spot / weld_set_hand_spot_robot
控制手动点焊功能。
**函数签名:**
```cpp
Result weld_set_hand_spot(SOCKETFD socketFd, int state);
Result weld_set_hand_spot_robot(SOCKETFD socketFd, int robotNum, int state);
```
**参数说明:**
| 参数 | 类型 | 输入/输出 | 说明 |
|------|------|----------|------|
| socketFd | SOCKETFD | 输入 | 连接句柄 |
| robotNum | int | 输入 | 机器人编号(仅 `_robot` 版本) |
| state | int | 输入 | 1: 开启手动点焊,0: 关闭手动点焊 |
**返回值:** `Result` 枚举,0 表示成功,负数表示失败。
**注意事项:** 手动点焊用于调试或临时焊接需求,开启后会立即执行点焊操作。请确保焊枪已到达目标位置后再调用。
**使用示例:**
```cpp
// 先移动到焊接位置
// robot_movel(fd, ...);
// 执行手动点焊
Result result = weld_set_hand_spot(fd, 1);
if (result == 0) {
printf("手动点焊完成\n");
}
```
---
## 9.4 状态获取
### weld_get_feed_wire_status / weld_get_feed_wire_status_robot
获取送丝、退丝、送气、焊接使能、手动点焊的状态。
**函数签名:**
```cpp
Result weld_get_feed_wire_status(SOCKETFD socketFd, std::vector& status);
Result weld_get_feed_wire_status_robot(SOCKETFD socketFd, int robotNum, std::vector& status);
```
**参数说明:**
| 参数 | 类型 | 输入/输出 | 说明 |
|------|------|----------|------|
| socketFd | SOCKETFD | 输入 | 连接句柄 |
| robotNum | int | 输入 | 机器人编号(仅 `_robot` 版本) |
| status | std::vector\& | 输出 | 状态数组,长度 5 |
**status 数组说明:**
| 索引 | 含义 | 取值 |
|------|------|------|
| status[0] | 送丝状态 | 0: 关闭,1: 开启 |
| status[1] | 退丝状态 | 0: 关闭,1: 开启 |
| status[2] | 送气状态 | 0: 关闭,1: 开启 |
| status[3] | 焊接使能 | 0: 关闭,1: 使能 |
| status[4] | 手动点焊 | 0: 关闭,1: 开启 |
**返回值:** `Result` 枚举,0 表示成功,负数表示失败。
**使用示例:**
```cpp
std::vector status;
Result result = weld_get_feed_wire_status(fd, status);
if (result == 0) {
printf("焊接状态:\n");
printf(" 送丝: %s\n", status[0] ? "开启" : "关闭");
printf(" 退丝: %s\n", status[1] ? "开启" : "关闭");
printf(" 送气: %s\n", status[2] ? "开启" : "关闭");
printf(" 焊接使能: %s\n", status[3] ? "使能" : "关闭");
printf(" 手动点焊: %s\n", status[4] ? "开启" : "关闭");
}
```
---
### weld_get_monitor_status / weld_get_monitor_status_robot
获取焊接实时监控状态,包括焊枪开关、引弧状态、电流电压等实时数据。
**函数签名:**
```cpp
Result weld_get_monitor_status(SOCKETFD socketFd, WeldState& status);
Result weld_get_monitor_status_robot(SOCKETFD socketFd, int robotNum, WeldState& status);
```
**参数说明:**
| 参数 | 类型 | 输入/输出 | 说明 |
|------|------|----------|------|
| socketFd | SOCKETFD | 输入 | 连接句柄 |
| robotNum | int | 输入 | 机器人编号(仅 `_robot` 版本) |
| status | WeldState& | 输出 | 焊接状态结构体 |
**WeldState 字段说明:**
| 字段 | 类型 | 说明 |
|------|------|------|
| pistolSwitch | int | 焊枪开关:-1=未使能,0=错误,1=使能 |
| arcingSuccess | int | 引弧成功:-1=未使能,0=错误,1=使能 |
| handWireFeed | int | 手动送丝:-1=未使能,0=错误,1=使能 |
| weldCurrent | double | 当前焊接电流(A) |
| weldVoltage | double | 当前焊接电压(V) |
| weldTime | double | 累计焊接时间(S) |
| weldPWM | double | 焊接占空比(%) |
**返回值:** `Result` 枚举,0 表示成功,负数表示失败。
**注意事项:** 该接口可用于焊接质量监控,建议在焊接过程中周期性调用(如每 100ms 一次),以实时获取焊接参数变化。
**使用示例:**
```cpp
// 焊接过程监控
WeldState state;
Result result = weld_get_monitor_status(fd, state);
if (result == 0) {
printf("焊接监控数据:\n");
printf(" 焊枪开关: %d\n", state.pistolSwitch);
printf(" 引弧成功: %d\n", state.arcingSuccess);
printf(" 焊接电流: %.2f A\n", state.weldCurrent);
printf(" 焊接电压: %.2f V\n", state.weldVoltage);
printf(" 焊接时间: %.2f S\n", state.weldTime);
printf(" 焊接占空比: %.2f%%\n", state.weldPWM);
}
```
---
## 9.5 摆焊参数
### weld_get_wave_weld_param / weld_get_wave_weld_param_robot
获取摆焊参数数据。控制器支持最多 9 组摆焊参数文件。
**函数签名:**
```cpp
Result weld_get_wave_weld_param(SOCKETFD socketFd, int num, WaveParam& param);
Result weld_get_wave_weld_param_robot(SOCKETFD socketFd, int robotNum, int num, WaveParam& param);
```
**参数说明:**
| 参数 | 类型 | 输入/输出 | 说明 |
|------|------|----------|------|
| socketFd | SOCKETFD | 输入 | 连接句柄 |
| robotNum | int | 输入 | 机器人编号(仅 `_robot` 版本) |
| num | int | 输入 | 摆焊文件编号,范围 [1, 9] |
| param | WaveParam& | 输出 | 摆焊参数 |
**返回值:** `Result` 枚举,0 表示成功,负数表示失败。
**使用示例:**
```cpp
// 获取第1组摆焊参数
WaveParam param;
Result result = weld_get_wave_weld_param(fd, 1, param);
if (result == 0) {
printf("摆焊参数(文件1):\n");
printf(" 摆动方式: %d (0:正弦摆 1:Z字形 2:圆形摆 3:外部轴定点摆)\n", param.type);
printf(" 摆动频率: %.2f Hz\n", param.swingFreq);
printf(" 摆动幅度: %.2f mm\n", param.swingAmplitude);
printf(" 半径: %.2f mm\n", param.radius);
printf(" 左停留时间: %.2f S\n", param.leftStayTime);
printf(" 右停留时间: %.2f S\n", param.rightStayTime);
}
```
---
### weld_set_wave_weld_param / weld_set_wave_weld_param_robot
设置摆焊参数。控制器支持最多 9 组摆焊参数文件。
**函数签名:**
```cpp
Result weld_set_wave_weld_param(SOCKETFD socketFd, int num, const WaveParam& param);
Result weld_set_wave_weld_param_robot(SOCKETFD socketFd, int robotNum, int num, const WaveParam& param);
```
**参数说明:**
| 参数 | 类型 | 输入/输出 | 说明 |
|------|------|----------|------|
| socketFd | SOCKETFD | 输入 | 连接句柄 |
| robotNum | int | 输入 | 机器人编号(仅 `_robot` 版本) |
| num | int | 输入 | 摆焊文件编号,范围 [1, 9] |
| param | const WaveParam& | 输入 | 摆焊参数 |
**WaveParam 字段说明:**
| 字段 | 类型 | 说明 |
|------|------|------|
| type | int | 摆动方式:0=正弦摆,1=Z字形摆,2=圆形摆,3=外部轴定点摆 |
| swingFreq | double | 摆动频率(Hz) |
| swingAmplitude | double | 摆动幅度(mm) |
| radius | double | 摆动半径(mm),圆形摆时有效 |
| LTypeAngle | double | L型角度,Z字形摆时有效 |
| moveWhenEdgeStay | bool | 边缘是否停留 |
| leftStayTime | double | 左侧边缘停留时间(S) |
| rightStayTime | double | 右侧边缘停留时间(S) |
| initialDir | int | 初始摆动方向 |
| horizontalDeflection | double | 水平方向偏转角度 |
| verticalDeflection | double | 垂直方向偏转角度 |
**返回值:** `Result` 枚举,0 表示成功,负数表示失败。
**注意事项:** 摆焊参数配置后,在焊接作业文件中引用对应的摆焊文件编号即可生效。支持 1~9 共 9 组独立的摆焊参数。
**使用示例:**
```cpp
// 配置正弦摆焊参数
WaveParam param;
param.type = 0; // 正弦摆
param.swingFreq = 2.5; // 摆动频率 2.5Hz
param.swingAmplitude = 5.0; // 摆动幅度 5mm
param.radius = 0.0; // 正弦摆不需要半径
param.LTypeAngle = 0.0;
param.moveWhenEdgeStay = true; // 边缘停留
param.leftStayTime = 0.1; // 左停留 0.1秒
param.rightStayTime = 0.1; // 右停留 0.1秒
param.initialDir = 0; // 初始方向
param.horizontalDeflection = 0.0;
param.verticalDeflection = 0.0;
Result result = weld_set_wave_weld_param(fd, 1, param);
if (result == 0) {
printf("摆焊参数(文件1)设置成功\n");
}
```
---
## 9.6 完整焊接流程示例
```cpp
#include "nrc_craft_weld.h"
#include
int main() {
// 1. 连接机器人(假设已完成)
SOCKETFD fd = connect_robot("192.168.1.15", "6001");
if (fd <= 0) {
printf("连接失败\n");
return -1;
}
// 2. 配置焊接参数
ArcParam arcParam;
arcParam.weldVoltage = 25.0;
arcParam.weldCurrent = 180.0;
arcParam.arcOnCurrent = 200.0;
arcParam.arcOnVoltage = 28.0;
arcParam.arcOnTime = 0.5;
arcParam.arcOffCurrent = 150.0;
arcParam.arcOffVoltage = 22.0;
arcParam.arcOffTime = 0.5;
Result ret = weld_set_config(fd, 1, arcParam);
if (ret != 0) {
printf("焊接参数配置失败\n");
return -1;
}
// 3. 配置摆焊参数(可选)
WaveParam waveParam;
waveParam.type = 0; // 正弦摆
waveParam.swingFreq = 2.5;
waveParam.swingAmplitude = 3.0;
ret = weld_set_wave_weld_param(fd, 1, waveParam);
if (ret != 0) {
printf("摆焊参数配置失败\n");
}
// 4. 预送气
weld_set_supply_gas(fd, 1);
sleep(1);
// 5. 使能焊接
weld_set_enable(fd, 1);
// 6. 启动焊接作业文件
// run_job_file(fd, "weld_job_001");
// 7. 监控焊接状态
for (int i = 0; i < 100; i++) {
WeldState state;
ret = weld_get_monitor_status(fd, state);
if (ret == 0) {
printf("焊接中: 电流=%.1fA 电压=%.1fV 时间=%.1fS\n",
state.weldCurrent, state.weldVoltage, state.weldTime);
}
usleep(100000); // 100ms
}
// 8. 焊接完成,关闭焊接使能和送气
weld_set_enable(fd, 0);
weld_set_supply_gas(fd, 0);
printf("焊接流程完成\n");
return 0;
}
```
---
# 十、码垛工艺(nrc_craft_pallet.h)
> 原文:https://open.inexbot.com/zh/04.%E4%B8%8A%E4%BD%8D%E6%9C%BA/01.C++/02.%E6%8E%A5%E5%8F%A3/10.%E7%A0%81%E5%9E%9B%E5%B7%A5%E8%89%BA.html
码垛工艺接口提供码垛运行状态设置与查询、当前运行码垛工艺号获取等功能。支持单机器人和多机器人(`_robot` 后缀)两种调用方式。
---
## 10.1 运行状态控制
### pallet_set_running_state / pallet_set_running_state_robot
设置码垛运行状态,指定当前码垛的工艺号、层数和工件数。调用后控制器将按照设定的参数执行码垛作业。
**函数签名:**
```cpp
Result pallet_set_running_state(SOCKETFD socketFd, int craftID, int layerNum, int workpiecesNum);
Result pallet_set_running_state_robot(SOCKETFD socketFd, int robotNum, int craftID, int layerNum, int workpiecesNum);
```
**参数说明:**
| 参数 | 类型 | 输入/输出 | 说明 |
|------|------|----------|------|
| socketFd | SOCKETFD | 输入 | 连接句柄 |
| robotNum | int | 输入 | 机器人编号(仅 `_robot` 版本) |
| craftID | int | 输入 | 码垛工艺号 |
| layerNum | int | 输入 | 总层数 |
| workpiecesNum | int | 输入 | 每层工件数 |
**返回值:** `Result` 枚举,0 表示成功,-1 表示等待消息失败,-2 表示找不到指定的客户端。
**注意事项:** 该接口用于启动或重置码垛运行状态,通常在码垛作业开始前调用。工艺号需与控制器中已配置的码垛工艺文件对应。
**使用示例:**
```cpp
// 设置码垛运行状态:工艺号1,共5层,每层4个工件
Result result = pallet_set_running_state(fd, 1, 5, 4);
if (result == 0) {
printf("码垛运行状态设置成功:工艺号=%d, 层数=%d, 每层工件数=%d\n", 1, 5, 4);
} else {
printf("码垛运行状态设置失败,错误码: %d\n", result);
}
```
---
### pallet_get_running_state / pallet_get_running_state_robot
获取码垛当前的运行状态,包括总工件数、总层数、当前层进度等详细信息。
**函数签名:**
```cpp
Result pallet_get_running_state(SOCKETFD socketFd, int craftID, int& totalWpNum, int& totalLayerNum, int& curLayerWpSum, int& curLayerNum, int& curPalletedWpSum, int& curLayerPalletedWpNum);
Result pallet_get_running_state_robot(SOCKETFD socketFd, int robotNum, int craftID, int& totalWpNum, int& totalLayerNum, int& curLayerWpSum, int& curLayerNum, int& curPalletedWpSum, int& curLayerPalletedWpNum);
```
**参数说明:**
| 参数 | 类型 | 输入/输出 | 说明 |
|------|------|----------|------|
| socketFd | SOCKETFD | 输入 | 连接句柄 |
| robotNum | int | 输入 | 机器人编号(仅 `_robot` 版本) |
| craftID | int | 输入 | 要查询的码垛工艺号 |
| totalWpNum | int& | 输出 | 总工件数 |
| totalLayerNum | int& | 输出 | 总层数 |
| curLayerWpSum | int& | 输出 | 当前层总工件数 |
| curLayerNum | int& | 输出 | 当前层数(正在执行的第几层) |
| curPalletedWpSum | int& | 输出 | 当前已码总工件数 |
| curLayerPalletedWpNum | int& | 输出 | 当前层已码工件数 |
**返回值:** `Result` 枚举,0 表示成功,-1 表示等待消息失败,-2 表示找不到指定的客户端。
**注意事项:** 该接口可用于实时监控码垛进度,适合在生产管理系统中周期性查询以更新码垛完成进度。
**使用示例:**
```cpp
// 查询工艺号1的码垛运行状态
int totalWpNum, totalLayerNum, curLayerWpSum;
int curLayerNum, curPalletedWpSum, curLayerPalletedWpNum;
Result result = pallet_get_running_state(fd, 1,
totalWpNum, totalLayerNum, curLayerWpSum,
curLayerNum, curPalletedWpSum, curLayerPalletedWpNum);
if (result == 0) {
printf("码垛运行状态(工艺号1):\n");
printf(" 总工件数: %d\n", totalWpNum);
printf(" 总层数: %d\n", totalLayerNum);
printf(" 当前层总工件数: %d\n", curLayerWpSum);
printf(" 当前层数: %d\n", curLayerNum);
printf(" 已码总工件数: %d\n", curPalletedWpSum);
printf(" 当前层已码工件数: %d\n", curLayerPalletedWpNum);
// 计算完成进度
if (totalWpNum > 0) {
float progress = (float)curPalletedWpSum / totalWpNum * 100;
printf(" 完成进度: %.1f%%\n", progress);
}
}
```
---
## 10.2 工艺号查询
### pallet_get_current_run_craftid / pallet_get_current_run_craftid_robot
获取当前运行的作业文件中所包含的所有码垛工艺号列表。
**函数签名:**
```cpp
Result pallet_get_current_run_craftid(SOCKETFD socketFd, std::vector& craftIds);
Result pallet_get_current_run_craftid_robot(SOCKETFD socketFd, int robotNum, std::vector& craftIds);
```
**参数说明:**
| 参数 | 类型 | 输入/输出 | 说明 |
|------|------|----------|------|
| socketFd | SOCKETFD | 输入 | 连接句柄 |
| robotNum | int | 输入 | 机器人编号(仅 `_robot` 版本) |
| craftIds | std::vector\& | 输出 | 码垛工艺号列表 |
**返回值:** `Result` 枚举,0 表示成功,-1 表示等待消息失败,-2 表示找不到指定的客户端。
**注意事项:** 一个作业文件中可能包含多个码垛工艺,该接口返回当前作业文件中所有已配置的码垛工艺号。可用于动态识别当前作业需要执行哪些码垛任务。
**使用示例:**
```cpp
// 获取当前运行的码垛工艺号列表
std::vector craftIds;
Result result = pallet_get_current_run_craftid(fd, craftIds);
if (result == 0) {
printf("当前作业包含 %zu 个码垛工艺:\n", craftIds.size());
for (size_t i = 0; i < craftIds.size(); i++) {
printf(" 工艺号 %d: %d\n", (int)(i + 1), craftIds[i]);
}
} else {
printf("获取码垛工艺号失败,错误码: %d\n", result);
}
```
---
## 10.3 完整码垛流程示例
```cpp
#include "nrc_craft_pallet.h"
#include "nrc_interface.h"
#include
int main() {
// 1. 连接机器人
SOCKETFD fd = connect_robot("192.168.1.15", "6001");
if (fd <= 0) {
printf("连接失败\n");
return -1;
}
// 2. 获取当前作业包含的码垛工艺号
std::vector craftIds;
Result ret = pallet_get_current_run_craftid(fd, craftIds);
if (ret == 0 && !craftIds.empty()) {
printf("检测到 %zu 个码垛工艺\n", craftIds.size());
}
// 3. 设置码垛运行状态
int craftID = 1; // 工艺号
int layers = 3; // 3层
int wpPerLayer = 6; // 每层6个工件
ret = pallet_set_running_state(fd, craftID, layers, wpPerLayer);
if (ret != 0) {
printf("码垛状态设置失败\n");
return -1;
}
printf("码垛任务启动:%d层 x %d件/层 = %d件\n", layers, wpPerLayer, layers * wpPerLayer);
// 4. 启动码垛作业文件
// run_job_file(fd, "pallet_job_001");
// 5. 监控码垛进度
bool running = true;
while (running) {
int totalWpNum, totalLayerNum, curLayerWpSum;
int curLayerNum, curPalletedWpSum, curLayerPalletedWpNum;
ret = pallet_get_running_state(fd, craftID,
totalWpNum, totalLayerNum, curLayerWpSum,
curLayerNum, curPalletedWpSum, curLayerPalletedWpNum);
if (ret == 0) {
printf("\r进度: 第%d/%d层, 已码%d/%d件",
curLayerNum, totalLayerNum,
curPalletedWpSum, totalWpNum);
fflush(stdout);
// 判断是否全部完成
if (curPalletedWpSum >= totalWpNum && totalWpNum > 0) {
running = false;
printf("\n码垛全部完成!\n");
}
}
usleep(500000); // 500ms 查询一次
}
printf("码垛流程结束\n");
return 0;
}
```
---
# 十一、视觉工艺(nrc_craft_vision.h)
> 原文:https://open.inexbot.com/zh/04.%E4%B8%8A%E4%BD%8D%E6%9C%BA/01.C++/02.%E6%8E%A5%E5%8F%A3/11.%E8%A7%86%E8%A7%89%E5%B7%A5%E8%89%BA.html
视觉工艺接口提供视觉系统的基本参数配置、视觉范围设置、坐标参数配置、视觉标定及标定数据查询、手眼标定计算等功能。支持单机器人和多机器人(`_robot` 后缀)两种调用方式。
## 数据结构
### VisionParam —— 视觉基本参数
```cpp
struct VisionParam {
CameraList cameraList; // 相机列表
Protocol protocol; // 通讯协议配置
Socket socket; // Socket 通讯配置
Trigger trigger; // 触发配置
int userCoordNum; // 用户坐标编号
};
```
**子结构体说明:**
**CameraList:**
| 字段 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| currentName | string | "customize" | 当前相机名称 |
| listNum | int | 0 | 相机列表数量 |
**Protocol(通讯协议):**
| 字段 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| addDataInitialPara | string | "GD001" | 附加数据初始参数 |
| addDataNum | int | 0 | 附加数据数量 |
| angleUnit | int | 1 | 角度单位 |
| endMark | string | "$" | 结束标志 |
| failFlag | string | "NG" | 失败标志 |
| frameHeader | string | "" | 帧头 |
| hasTCS | bool | false | 是否有TCS |
| hasUCS | bool | false | 是否有UCS |
| separator | string | "," | 分隔符 |
| singleTarget | bool | true | 单目标 |
| successFlag | string | "OK" | 成功标志 |
| timeOut | int | 30 | 超时时间 |
| type | int | 1 | 协议类型 |
**Socket(Socket 通讯配置):**
| 字段 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| IP | string | "192.168.1.120" | IP地址 |
| cameraDataType | int | 0 | 相机数据类型 |
| portNum | int | 1 | 端口号 |
| portOne | int | 5050 | 端口1 |
| portTwo | int | 5051 | 端口2 |
| server | bool | true | 是否为服务器 |
**Trigger(触发配置):**
| 字段 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| IOPort | int | 0 | IO端口 |
| duration | int | 1000 | 持续时间(ms) |
| intervals | int | 35 | 间隔时间(ms) |
| triggerMode | int | 2 | 触发模式:1=IO,2=EtherCAT |
| triggerOnce | bool | true | 单次触发 |
| triggerStr | string | "TRG" | 触发字符串 |
### VisionRange —— 视觉范围
```cpp
struct VisionRange {
std::string maxX; // 最大X坐标
std::string maxY; // 最大Y坐标
std::string maxZ; // 最大Z坐标
std::string minX; // 最小X坐标
std::string minY; // 最小Y坐标
std::string minZ; // 最小Z坐标
};
```
### VisionPositionParam —— 视觉坐标参数
```cpp
struct VisionPositionParam {
Position position; // 位置数据
int protocol; // 协议类型
};
```
**Position 子结构体包含:** 视角方向(angleDirection)、相机数据(cameraData)、相机点位(cameraPoint,长度14)、参考点位(datumPoint,长度14)、偏移量(Excursion:X/Y/Z 偏移 + 角度)、接收点类型(recvPointsType)、样本数据(sampleData)、比例(scale)。
### VisionCalibrationData —— 视觉标定数据
```cpp
struct VisionCalibrationData {
int visionNum; // 视觉编号
Calibration calibration; // 校准数据(含标定点列表,默认点数6,范围[6,30])
};
```
**Calibration 包含:** calibrated(是否已校准)、point(标定点列表)、point_num(点数量)。
**CalibrationPoint 包含:** pixel_pos(像素位置,长度7)、pixel_pos_deg(像素位置角度,长度7)、robot_pos(机器人位置,长度7)、robot_pos_deg(机器人位置角度,长度7)。
---
## 11.1 基本参数配置
### vision_set_basic_parameter / vision_set_basic_parameter_robot
设置视觉系统的基本参数,包括相机列表、通讯协议、Socket 配置和触发方式。
**函数签名:**
```cpp
Result vision_set_basic_parameter(SOCKETFD socketFd, int visionNum, VisionParam vsPamrm);
Result vision_set_basic_parameter_robot(SOCKETFD socketFd, int robotNum, int visionNum, VisionParam vsPamrm);
```
**参数说明:**
| 参数 | 类型 | 输入/输出 | 说明 |
|------|------|----------|------|
| socketFd | SOCKETFD | 输入 | 连接句柄 |
| robotNum | int | 输入 | 机器人编号(仅 `_robot` 版本) |
| visionNum | int | 输入 | 视觉ID编号 |
| vsPamrm | VisionParam | 输入 | 视觉基本参数结构体 |
**返回值:** `Result` 枚举,0 表示成功,-1 表示等待消息失败,-2 表示找不到指定的客户端。
**使用示例:**
```cpp
// 配置视觉系统基本参数
VisionParam param;
// 设置相机名称
param.cameraList.currentName = "camera_01";
param.cameraList.listNum = 1;
// 设置通讯协议
param.protocol.addDataInitialPara = "GD001";
param.protocol.endMark = "$";
param.protocol.failFlag = "NG";
param.protocol.successFlag = "OK";
param.protocol.separator = ",";
param.protocol.timeOut = 30;
param.protocol.type = 1;
// 设置Socket通讯
param.socket.IP = "192.168.1.120";
param.socket.portOne = 5050;
param.socket.portTwo = 5051;
param.socket.server = true;
// 设置触发方式
param.trigger.triggerMode = 2; // EtherCAT触发
param.trigger.duration = 1000;
param.trigger.intervals = 35;
param.trigger.triggerStr = "TRG";
// 设置用户坐标
param.userCoordNum = 1;
Result result = vision_set_basic_parameter(fd, 1, param);
if (result == 0) {
printf("视觉基本参数设置成功\n");
}
```
---
### vision_get_basic_parameter / vision_get_basic_parameter_robot
查询视觉系统已配置的基本参数。
**函数签名:**
```cpp
Result vision_get_basic_parameter(SOCKETFD socketFd, int visionNum, VisionParam& vsPamrm);
Result vision_get_basic_parameter_robot(SOCKETFD socketFd, int robotNum, int visionNum, VisionParam& vsPamrm);
```
**参数说明:**
| 参数 | 类型 | 输入/输出 | 说明 |
|------|------|----------|------|
| socketFd | SOCKETFD | 输入 | 连接句柄 |
| robotNum | int | 输入 | 机器人编号(仅 `_robot` 版本) |
| visionNum | int | 输入 | 视觉ID编号 |
| vsPamrm | VisionParam& | 输出 | 接收视觉基本参数 |
**返回值:** `Result` 枚举,0 表示成功,-1 表示等待消息失败,-2 表示找不到指定的客户端。
**使用示例:**
```cpp
VisionParam param;
Result result = vision_get_basic_parameter(fd, 1, param);
if (result == 0) {
printf("视觉基本参数:\n");
printf(" 相机名称: %s\n", param.cameraList.currentName.c_str());
printf(" IP地址: %s\n", param.socket.IP.c_str());
printf(" 端口1: %d\n", param.socket.portOne);
printf(" 触发模式: %d\n", param.trigger.triggerMode);
printf(" 用户坐标编号: %d\n", param.userCoordNum);
}
```
---
## 11.2 视觉范围配置
### vision_set_range / vision_set_range_robot
设置视觉识别的空间范围(包围盒),用于限定视觉检测的有效区域。
**函数签名:**
```cpp
Result vision_set_range(SOCKETFD socketFd, int visionNum, VisionRange vsPamrm);
Result vision_set_range_robot(SOCKETFD socketFd, int robotNum, int visionNum, VisionRange vsPamrm);
```
**参数说明:**
| 参数 | 类型 | 输入/输出 | 说明 |
|------|------|----------|------|
| socketFd | SOCKETFD | 输入 | 连接句柄 |
| robotNum | int | 输入 | 机器人编号(仅 `_robot` 版本) |
| visionNum | int | 输入 | 视觉ID编号 |
| vsPamrm | VisionRange | 输入 | 视觉范围参数(min/max XYZ) |
**返回值:** `Result` 枚举,0 表示成功,-1 表示等待消息失败,-2 表示找不到指定的客户端。
**使用示例:**
```cpp
// 设置视觉检测范围:X[0,500], Y[-200,200], Z[0,300]
VisionRange range;
range.minX = "0";
range.maxX = "500";
range.minY = "-200";
range.maxY = "200";
range.minZ = "0";
range.maxZ = "300";
Result result = vision_set_range(fd, 1, range);
if (result == 0) {
printf("视觉范围设置成功\n");
}
```
---
### vision_get_range / vision_get_range_robot
查询视觉已配置的空间范围参数。
**函数签名:**
```cpp
Result vision_get_range(SOCKETFD socketFd, int visionNum, VisionRange& vsPamrm);
Result vision_get_range_robot(SOCKETFD socketFd, int robotNum, int visionNum, VisionRange& vsPamrm);
```
**参数说明:**
| 参数 | 类型 | 输入/输出 | 说明 |
|------|------|----------|------|
| socketFd | SOCKETFD | 输入 | 连接句柄 |
| robotNum | int | 输入 | 机器人编号(仅 `_robot` 版本) |
| visionNum | int | 输入 | 视觉ID编号 |
| vsPamrm | VisionRange& | 输出 | 接收视觉范围参数 |
**返回值:** `Result` 枚举,0 表示成功,-1 表示等待消息失败,-2 表示找不到指定的客户端。
**使用示例:**
```cpp
VisionRange range;
Result result = vision_get_range(fd, 1, range);
if (result == 0) {
printf("视觉范围:\n");
printf(" X: [%s, %s]\n", range.minX.c_str(), range.maxX.c_str());
printf(" Y: [%s, %s]\n", range.minY.c_str(), range.maxY.c_str());
printf(" Z: [%s, %s]\n", range.minZ.c_str(), range.maxZ.c_str());
}
```
---
## 11.3 坐标参数配置
### vision_set_position_parameter / vision_set_position_parameter_robot
设置视觉坐标参数,包括拍照位置、参考点位、偏移量和样本数据格式等。
**函数签名:**
```cpp
Result vision_set_position_parameter(SOCKETFD socketFd, int visionNum, VisionPositionParam vsPamrm);
Result vision_set_position_parameter_robot(SOCKETFD socketFd, int robotNum, int visionNum, VisionPositionParam vsPamrm);
```
**参数说明:**
| 参数 | 类型 | 输入/输出 | 说明 |
|------|------|----------|------|
| socketFd | SOCKETFD | 输入 | 连接句柄 |
| robotNum | int | 输入 | 机器人编号(仅 `_robot` 版本) |
| visionNum | int | 输入 | 视觉ID编号 |
| vsPamrm | VisionPositionParam | 输入 | 视觉坐标参数 |
**返回值:** `Result` 枚举,0 表示成功,-1 表示等待消息失败,-2 表示找不到指定的客户端。
**使用示例:**
```cpp
VisionPositionParam posParam;
// 设置协议类型
posParam.protocol = 0;
// 设置位置数据
posParam.position.angleDirection = 0;
posParam.position.cameraData = "";
posParam.position.recvPointsType = 0;
posParam.position.sampleData = "x,y,Rz,h,$";
posParam.position.scale = 1.0;
// 设置偏移量
posParam.position.excursion.Xexcursion = 0.0;
posParam.position.excursion.Yexcursion = 0.0;
posParam.position.excursion.Zexcursion = 0.0;
posParam.position.excursion.angle = 0.0;
Result result = vision_set_position_parameter(fd, 1, posParam);
if (result == 0) {
printf("视觉坐标参数设置成功\n");
}
```
---
### vision_get_position_parameter / vision_get_position_parameter_robot
查询视觉已配置的坐标参数。
**函数签名:**
```cpp
Result vision_get_position_parameter(SOCKETFD socketFd, int visionId, VisionPositionParam& vsPamrm);
Result vision_get_position_parameter_robot(SOCKETFD socketFd, int robotNum, int visionId, VisionPositionParam& vsPamrm);
```
**参数说明:**
| 参数 | 类型 | 输入/输出 | 说明 |
|------|------|----------|------|
| socketFd | SOCKETFD | 输入 | 连接句柄 |
| robotNum | int | 输入 | 机器人编号(仅 `_robot` 版本) |
| visionId | int | 输入 | 视觉ID编号 |
| vsPamrm | VisionPositionParam& | 输出 | 接收视觉坐标参数 |
**返回值:** `Result` 枚举,0 表示成功,-1 表示等待消息失败,-2 表示找不到指定的客户端。
**使用示例:**
```cpp
VisionPositionParam posParam;
Result result = vision_get_position_parameter(fd, 1, posParam);
if (result == 0) {
printf("视觉坐标参数:\n");
printf(" 协议类型: %d\n", posParam.protocol);
printf(" 样本数据格式: %s\n", posParam.position.sampleData.c_str());
printf(" 偏移: X=%.2f Y=%.2f Z=%.2f Angle=%.2f\n",
posParam.position.excursion.Xexcursion,
posParam.position.excursion.Yexcursion,
posParam.position.excursion.Zexcursion,
posParam.position.excursion.angle);
}
```
---
## 11.4 视觉标定
### vision_calibrate / vision_calibrate_robot
执行视觉标定操作,设置标定相关的点位数据。
**函数签名:**
```cpp
Result vision_calibrate(SOCKETFD socketFd, int visionId, VisionCalibrationData vsPamrm);
Result vision_calibrate_robot(SOCKETFD socketFd, int robotNum, int visionId, VisionCalibrationData vsPamrm);
```
**参数说明:**
| 参数 | 类型 | 输入/输出 | 说明 |
|------|------|----------|------|
| socketFd | SOCKETFD | 输入 | 连接句柄 |
| robotNum | int | 输入 | 机器人编号(仅 `_robot` 版本) |
| visionId | int | 输入 | 视觉ID编号 |
| vsPamrm | VisionCalibrationData | 输入 | 标定数据,含像素位置和机器人位置 |
**返回值:** `Result` 枚举,0 表示成功,-1 表示等待消息失败,-2 表示找不到指定的客户端。
**注意事项:** 标定前需确保视觉基本参数和坐标参数已正确配置。标定点数默认为 6,范围 [6, 30],点数越多标定精度越高。
**使用示例:**
```cpp
// 创建标定数据(默认6个标定点)
VisionCalibrationData calibData(6);
calibData.visionNum = 1;
// 添加标定点(示例:第1个点)
CalibrationPoint point1;
// 像素位置(x, y, z, rx, ry, rz, 保留)
point1.pixel_pos = {100.0, 200.0, 0.0, 0.0, 0.0, 0.0, 0.0};
// 机器人实际位置
point1.robot_pos = {400.0, 150.0, 50.0, 180.0, 0.0, 0.0, 0.0};
calibData.calibration.addCalibrationPoint(point1);
// ... 继续添加其余5个标定点
// 执行标定
Result result = vision_calibrate(fd, 1, calibData);
if (result == 0) {
printf("视觉标定完成\n");
}
```
---
### vision_get_calibrate_data / vision_get_calibrate_data_robot
查询已保存的视觉标定数据。
**函数签名:**
```cpp
Result vision_get_calibrate_data(SOCKETFD socketFd, int visionId, VisionCalibrationData& vsPamrm);
Result vision_get_calibrate_data_robot(SOCKETFD socketFd, int robotNum, int visionId, VisionCalibrationData& vsPamrm);
```
**参数说明:**
| 参数 | 类型 | 输入/输出 | 说明 |
|------|------|----------|------|
| socketFd | SOCKETFD | 输入 | 连接句柄 |
| robotNum | int | 输入 | 机器人编号(仅 `_robot` 版本) |
| visionId | int | 输入 | 视觉ID编号 |
| vsPamrm | VisionCalibrationData& | 输出 | 接收标定数据 |
**返回值:** `Result` 枚举,0 表示成功,-1 表示等待消息失败,-2 表示找不到指定的客户端。
**使用示例:**
```cpp
VisionCalibrationData calibData;
Result result = vision_get_calibrate_data(fd, 1, calibData);
if (result == 0) {
printf("视觉标定数据(视觉编号=%d):\n", calibData.visionNum);
printf(" 已校准: %s\n", calibData.calibration.calibrated ? "是" : "否");
printf(" 标定点数: %d\n", calibData.calibration.point_num);
for (int i = 0; i < calibData.calibration.point_num; i++) {
auto pt = calibData.calibration.getPoint(i);
printf(" 点%d: 像素(%.1f,%.1f) -> 机器人(%.1f,%.1f,%.1f)\n",
i + 1,
pt.pixel_pos[0], pt.pixel_pos[1],
pt.robot_pos[0], pt.robot_pos[1], pt.robot_pos[2]);
}
}
```
---
### vision_hand_eye_calibration_calculation / vision_hand_eye_calibration_calculation_robot
执行手眼标定计算。在标定点数据全部录入后,调用此接口计算手眼关系矩阵。
**函数签名:**
```cpp
Result vision_hand_eye_calibration_calculation(SOCKETFD socketFd, int visionNum);
Result vision_hand_eye_calibration_calculation_robot(SOCKETFD socketFd, int robotNum, int visionNum);
```
**参数说明:**
| 参数 | 类型 | 输入/输出 | 说明 |
|------|------|----------|------|
| socketFd | SOCKETFD | 输入 | 连接句柄 |
| robotNum | int | 输入 | 机器人编号(仅 `_robot` 版本) |
| visionNum | int | 输入 | 视觉ID编号 |
**返回值:** `Result` 枚举,0 表示成功,-1 表示等待消息失败,-2 表示找不到指定的客户端。
**注意事项:** 手眼标定计算前需确保所有标定点(像素位置和对应的机器人位置)已通过 `vision_calibrate` 录入。计算完成后,视觉系统即可将相机识别到的像素坐标转换为机器人坐标系下的空间位置。
**使用示例:**
```cpp
// 标定点全部录入后,执行手眼标定计算
Result result = vision_hand_eye_calibration_calculation(fd, 1);
if (result == 0) {
printf("手眼标定计算完成,视觉系统已校准\n");
} else {
printf("手眼标定计算失败,请检查标定点数据\n");
}
```
---
## 11.5 完整视觉标定流程示例
```cpp
#include "nrc_craft_vision.h"
#include "nrc_interface.h"
#include
int main() {
SOCKETFD fd = connect_robot("192.168.1.15", "6001");
if (fd <= 0) {
printf("连接失败\n");
return -1;
}
// 1. 配置视觉基本参数
VisionParam visionParam;
visionParam.cameraList.currentName = "camera_01";
visionParam.cameraList.listNum = 1;
visionParam.socket.IP = "192.168.1.120";
visionParam.socket.portOne = 5050;
visionParam.socket.portTwo = 5051;
visionParam.socket.server = true;
visionParam.trigger.triggerMode = 2; // EtherCAT触发
visionParam.userCoordNum = 1;
Result ret = vision_set_basic_parameter(fd, 1, visionParam);
if (ret != 0) { printf("视觉基本参数配置失败\n"); return -1; }
// 2. 设置视觉范围
VisionRange range;
range.minX = "0"; range.maxX = "500";
range.minY = "-200"; range.maxY = "200";
range.minZ = "0"; range.maxZ = "300";
vision_set_range(fd, 1, range);
// 3. 设置坐标参数
VisionPositionParam posParam;
posParam.protocol = 0;
posParam.position.sampleData = "x,y,Rz,h,$";
posParam.position.scale = 1.0;
vision_set_position_parameter(fd, 1, posParam);
// 4. 执行6点标定
VisionCalibrationData calibData(6);
calibData.visionNum = 1;
// 标定点采集(此处为示意,实际需移动机器人到各标定点)
for (int i = 0; i < 6; i++) {
printf("请移动机器人到标定点%d...\n", i + 1);
// robot_movel(fd, ...); // 移动到目标位置
CalibrationPoint point;
// 从相机获取像素坐标
point.pixel_pos = {100.0 * i, 200.0, 0, 0, 0, 0, 0};
// 获取当前机器人位置
point.robot_pos = {400.0, 150.0 + i * 50, 50.0, 180.0, 0, 0, 0};
calibData.calibration.addCalibrationPoint(point);
printf("标定点%d已采集\n", i + 1);
sleep(1);
}
// 5. 提交标定数据
ret = vision_calibrate(fd, 1, calibData);
if (ret != 0) { printf("标定数据提交失败\n"); return -1; }
// 6. 执行手眼标定计算
ret = vision_hand_eye_calibration_calculation(fd, 1);
if (ret == 0) {
printf("手眼标定完成!\n");
} else {
printf("手眼标定计算失败\n");
return -1;
}
printf("视觉标定流程结束\n");
return 0;
}
```
---
# 十二、激光切割工艺(nrc_craft_laser_cutting.h)
> 原文:https://open.inexbot.com/zh/04.%E4%B8%8A%E4%BD%8D%E6%9C%BA/01.C++/02.%E6%8E%A5%E5%8F%A3/12.%E6%BF%80%E5%85%89%E5%88%87%E5%89%B2%E5%B7%A5%E8%89%BA.html
激光切割工艺接口提供切割全局参数、切割工艺参数、模拟量匹配参数和 IO 参数等配置与查询功能。支持单机器人和多机器人(`_robot` 后缀)两种调用方式。
## 数据结构
### LaserCuttingGlobalParam —— 切割全局参数
```cpp
struct LaserCuttingGlobalParam {
LaserCuttingEquipment equipment; // 设备参数
};
```
**LaserCuttingEquipment 子结构体:**
| 字段 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| RetreatDistance | double | 0.0 | 回退距离(mm) |
| arrivalOutLightMode | int | 0 | 到位出光模式 |
| collisionDistance | double | 10.0 | 碰撞检测距离(mm) |
| delAspiratedMode | int | 1 | 延时吸气模式 |
| delAspiratedTime | double | 0.0 | 延时吸气时间(S) |
| focusCompensation | bool | false | 焦点补偿使能 |
| focusCompensationConstant | double | 0.0 | 焦点补偿常数 |
| focusCompensationPower | double | 0.0 | 焦点补偿功率 |
| focusCompensationTime | double | 0.0 | 焦点补偿时间(S) |
| focusFormula | int | 0 | 焦点计算公式 |
| follow | int | 0 | 随动模式 |
| preAspiratedTime | double | 0.0 | 预吸气时间(S) |
| rePerforate | int | 1 | 重穿孔模式 |
| waitFollowTime | double | 1.0 | 等待随动时间(S) |
| waitLiftUpTime | double | 1.0 | 等待抬起时间(S) |
### LaserCuttingCraftParam —— 切割工艺参数
```cpp
struct LaserCuttingCraftParam {
int num; // 工艺号
LaserCuttingParam cut; // 切割参数
};
```
**LaserCuttingParam 子结构体:**
| 字段 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| dutyRatio | int | 0 | 占空比(%) |
| focusPosition | double | 0.0 | 焦点位置(mm) |
| followHeight | double | 0.10 | 随动高度(mm) |
| freq | int | 1 | 频率(Hz) |
| power | int | 1 | 激光功率(%) |
| pressure | double | 0.0 | 气压(bar) |
| note | string | "" | 备注信息 |
### LaserCuttingAnalogParam —— 模拟量匹配参数
```cpp
struct LaserCuttingAnalogParam {
LaserCuttingAnalogMatch analogMatch; // 模拟量匹配曲线
};
```
**LaserCuttingAnalogMatch 子结构体:**
| 字段 | 类型 | 说明 |
|------|------|------|
| laserPower | Curve | 激光功率匹配曲线(含level和x/y向量) |
| pressure | Curve | 气压匹配曲线(含level和x/y向量) |
**Curve 结构体:**
| 字段 | 类型 | 说明 |
|------|------|------|
| level | int | 曲线等级/层数 |
| x | vector\ | X轴数据(输入值) |
| y | vector\ | Y轴数据(输出值) |
### LaserCuttingIOParam —— IO 参数配置
```cpp
struct LaserCuttingIOParam {
IO io; // IO端口配置
Fault laser_fault; // 激光器故障输入端口
Fault pressure_fault; // 气压故障输入端口
Fault regulator_fault; // 调压器故障输入端口
Fault water_cooler_fault; // 水冷机故障输入端口
};
```
**IO 子结构体:**
| 字段 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| AO_laserPower | int | - | 激光功率模拟量输出端口 |
| AO_pressure | int | - | 气压模拟量输出端口 |
| DI_backMiddleArrival | int | - | 回中到位数字量输入端口 |
| DI_capacitance_ | int | - | 电容传感器数字量输入端口 |
| DI_followArrival | int | - | 随动到位数字量输入端口 |
| DI_liftUpArrival | int | - | 抬起到位数字量输入端口 |
| DI_perforateArrival | int | - | 穿孔到位数字量输入端口 |
| DO_aspiration | int | - | 吸气数字量输出端口 |
| DO_backMiddle | int | - | 回中数字量输出端口 |
| DO_capacitance_ | int | - | 电容传感器数字量输出端口 |
| DO_follow | int | - | 随动数字量输出端口 |
| DO_highPressgas | int | - | 高压气数字量输出端口 |
| DO_liftUp | int | - | 抬起数字量输出端口 |
| DO_lightGate | int | - | 光闸数字量输出端口 |
| DO_lowPressgas | int | - | 低压气数字量输出端口 |
| DO_redLight | int | - | 红光数字量输出端口 |
| pwm_port_ | int | - | PWM输出端口 |
**Fault 结构体:**
| 字段 | 类型 | 说明 |
|------|------|------|
| input_port | int | 故障信号输入端口号 |
---
## 12.1 全局参数
### laser_cutting_set_global_parameter / laser_cutting_set_global_parameter_robot
设置激光切割的全局参数,包括回退距离、碰撞检测、随动模式、穿孔模式等设备级配置。
**函数签名:**
```cpp
Result laser_cutting_set_global_parameter(SOCKETFD socketFd, LaserCuttingGlobalParam param);
Result laser_cutting_set_global_parameter_robot(SOCKETFD socketFd, int robotNum, LaserCuttingGlobalParam param);
```
**参数说明:**
| 参数 | 类型 | 输入/输出 | 说明 |
|------|------|----------|------|
| socketFd | SOCKETFD | 输入 | 连接句柄 |
| robotNum | int | 输入 | 机器人编号(仅 `_robot` 版本) |
| param | LaserCuttingGlobalParam | 输入 | 切割全局参数 |
**返回值:** `Result` 枚举,0 表示成功,-1 表示等待消息失败,-2 表示找不到指定的客户端。
**使用示例:**
```cpp
// 配置激光切割全局参数
LaserCuttingGlobalParam globalParam;
globalParam.equipment.RetreatDistance = 5.0; // 回退距离 5mm
globalParam.equipment.collisionDistance = 10.0; // 碰撞检测距离 10mm
globalParam.equipment.follow = 1; // 随动模式 1
globalParam.equipment.rePerforate = 1; // 重穿孔模式
globalParam.equipment.waitFollowTime = 1.0; // 等待随动 1秒
globalParam.equipment.waitLiftUpTime = 1.0; // 等待抬起 1秒
globalParam.equipment.focusCompensation = true; // 使能焦点补偿
globalParam.equipment.focusCompensationConstant = 0.5;
globalParam.equipment.focusCompensationPower = 80.0;
globalParam.equipment.focusCompensationTime = 0.3;
globalParam.equipment.delAspiratedMode = 1; // 延时吸气模式
globalParam.equipment.delAspiratedTime = 0.5; // 延时吸气 0.5秒
globalParam.equipment.preAspiratedTime = 1.0; // 预吸气 1秒
Result result = laser_cutting_set_global_parameter(fd, globalParam);
if (result == 0) {
printf("激光切割全局参数设置成功\n");
}
```
---
### laser_cutting_get_global_parameter / laser_cutting_get_global_parameter_robot
查询激光切割当前的全局参数配置。
**函数签名:**
```cpp
Result laser_cutting_get_global_parameter(SOCKETFD socketFd, LaserCuttingGlobalParam& param);
Result laser_cutting_get_global_parameter_robot(SOCKETFD socketFd, int robotNum, LaserCuttingGlobalParam& param);
```
**参数说明:**
| 参数 | 类型 | 输入/输出 | 说明 |
|------|------|----------|------|
| socketFd | SOCKETFD | 输入 | 连接句柄 |
| robotNum | int | 输入 | 机器人编号(仅 `_robot` 版本) |
| param | LaserCuttingGlobalParam& | 输出 | 接收全局参数 |
**返回值:** `Result` 枚举,0 表示成功,-1 表示等待消息失败,-2 表示找不到指定的客户端。
**使用示例:**
```cpp
LaserCuttingGlobalParam globalParam;
Result result = laser_cutting_get_global_parameter(fd, globalParam);
if (result == 0) {
printf("激光切割全局参数:\n");
printf(" 回退距离: %.2f mm\n", globalParam.equipment.RetreatDistance);
printf(" 碰撞检测距离: %.2f mm\n", globalParam.equipment.collisionDistance);
printf(" 随动模式: %d\n", globalParam.equipment.follow);
printf(" 焦点补偿: %s\n", globalParam.equipment.focusCompensation ? "使能" : "未使能");
printf(" 预吸气时间: %.2f S\n", globalParam.equipment.preAspiratedTime);
}
```
---
## 12.2 工艺参数
### laser_cutting_set_craft_parameter / laser_cutting_set_craft_parameter_robot
设置激光切割的工艺参数,包括指定工艺号的功率、频率、占空比、焦点位置、随动高度和气压等。
**函数签名:**
```cpp
Result laser_cutting_set_craft_parameter(SOCKETFD socketFd, LaserCuttingCraftParam param);
Result laser_cutting_set_craft_parameter_robot(SOCKETFD socketFd, int robotNum, LaserCuttingCraftParam param);
```
**参数说明:**
| 参数 | 类型 | 输入/输出 | 说明 |
|------|------|----------|------|
| socketFd | SOCKETFD | 输入 | 连接句柄 |
| robotNum | int | 输入 | 机器人编号(仅 `_robot` 版本) |
| param | LaserCuttingCraftParam | 输入 | 切割工艺参数,含工艺号和切割参数 |
**返回值:** `Result` 枚举,0 表示成功,-1 表示等待消息失败,-2 表示找不到指定的客户端。
**使用示例:**
```cpp
// 配置工艺号1的切割参数
LaserCuttingCraftParam craftParam;
craftParam.num = 1; // 工艺号1
craftParam.cut.power = 80; // 激光功率 80%
craftParam.cut.freq = 5000; // 频率 5000Hz
craftParam.cut.dutyRatio = 100; // 占空比 100%
craftParam.cut.focusPosition = -0.5; // 焦点位置 -0.5mm(板面下方)
craftParam.cut.followHeight = 1.0; // 随动高度 1.0mm
craftParam.cut.pressure = 8.0; // 气压 8.0bar
craftParam.cut.note = "3mm碳钢板切割"; // 备注
Result result = laser_cutting_set_craft_parameter(fd, craftParam);
if (result == 0) {
printf("工艺号%d切割参数设置成功\n", craftParam.num);
}
```
---
### laser_cutting_get_craft_parameter / laser_cutting_get_craft_parameter_robot
查询激光切割的工艺参数。
**函数签名:**
```cpp
Result laser_cutting_get_craft_parameter(SOCKETFD socketFd, LaserCuttingCraftParam& param);
Result laser_cutting_get_craft_parameter_robot(SOCKETFD socketFd, int robotNum, LaserCuttingCraftParam& param);
```
**参数说明:**
| 参数 | 类型 | 输入/输出 | 说明 |
|------|------|----------|------|
| socketFd | SOCKETFD | 输入 | 连接句柄 |
| robotNum | int | 输入 | 机器人编号(仅 `_robot` 版本) |
| param | LaserCuttingCraftParam& | 输出 | 接收工艺参数(需先设置 param.num 指定要查询的工艺号) |
**返回值:** `Result` 枚举,0 表示成功,-1 表示等待消息失败,-2 表示找不到指定的客户端。
**使用示例:**
```cpp
// 查询工艺号1的切割参数
LaserCuttingCraftParam craftParam;
craftParam.num = 1; // 需先指定要查询的工艺号
Result result = laser_cutting_get_craft_parameter(fd, craftParam);
if (result == 0) {
printf("工艺号%d切割参数:\n", craftParam.num);
printf(" 功率: %d%%\n", craftParam.cut.power);
printf(" 频率: %d Hz\n", craftParam.cut.freq);
printf(" 占空比: %d%%\n", craftParam.cut.dutyRatio);
printf(" 焦点位置: %.2f mm\n", craftParam.cut.focusPosition);
printf(" 随动高度: %.2f mm\n", craftParam.cut.followHeight);
printf(" 气压: %.2f bar\n", craftParam.cut.pressure);
if (!craftParam.cut.note.empty()) {
printf(" 备注: %s\n", craftParam.cut.note.c_str());
}
}
```
---
## 12.3 模拟量匹配参数
### laser_cutting_set_analog_parameter / laser_cutting_set_analog_parameter_robot
设置激光切割的模拟量匹配曲线参数,用于将激光功率百分比和气压值映射到实际的模拟量输出电压。
**函数签名:**
```cpp
Result laser_cutting_set_analog_parameter(SOCKETFD socketFd, LaserCuttingAnalogParam param);
Result laser_cutting_set_analog_parameter_robot(SOCKETFD socketFd, int robotNum, LaserCuttingAnalogParam param);
```
**参数说明:**
| 参数 | 类型 | 输入/输出 | 说明 |
|------|------|----------|------|
| socketFd | SOCKETFD | 输入 | 连接句柄 |
| robotNum | int | 输入 | 机器人编号(仅 `_robot` 版本) |
| param | LaserCuttingAnalogParam | 输入 | 模拟量匹配参数 |
**返回值:** `Result` 枚举,0 表示成功,-1 表示等待消息失败,-2 表示找不到指定的客户端。
**注意事项:** 模拟量匹配曲线用于校准不同激光器和比例阀的电压-输出特性。通常由设备供应商提供匹配曲线数据,需根据实际硬件特性进行配置。
**使用示例:**
```cpp
LaserCuttingAnalogParam analogParam;
// 配置激光功率匹配曲线(示例:线性映射 0-100% -> 0-10V)
analogParam.analogMatch.laserPower.level = 2; // 2点线性
analogParam.analogMatch.laserPower.x = {0.0, 100.0}; // 输入:功率百分比
analogParam.analogMatch.laserPower.y = {0.0, 10.0}; // 输出:电压值(V)
// 配置气压匹配曲线(示例:线性映射 0-16bar -> 0-10V)
analogParam.analogMatch.pressure.level = 2;
analogParam.analogMatch.pressure.x = {0.0, 16.0}; // 输入:气压值(bar)
analogParam.analogMatch.pressure.y = {0.0, 10.0}; // 输出:电压值(V)
Result result = laser_cutting_set_analog_parameter(fd, analogParam);
if (result == 0) {
printf("模拟量匹配参数设置成功\n");
}
```
---
### laser_cutting_get_analog_parameter / laser_cutting_get_analog_parameter_robot
查询激光切割当前的模拟量匹配曲线参数。
**函数签名:**
```cpp
Result laser_cutting_get_analog_parameter(SOCKETFD socketFd, LaserCuttingAnalogParam& param);
Result laser_cutting_get_analog_parameter_robot(SOCKETFD socketFd, int robotNum, LaserCuttingAnalogParam& param);
```
**参数说明:**
| 参数 | 类型 | 输入/输出 | 说明 |
|------|------|----------|------|
| socketFd | SOCKETFD | 输入 | 连接句柄 |
| robotNum | int | 输入 | 机器人编号(仅 `_robot` 版本) |
| param | LaserCuttingAnalogParam& | 输出 | 接收模拟量匹配参数 |
**返回值:** `Result` 枚举,0 表示成功,-1 表示等待消息失败,-2 表示找不到指定的客户端。
**使用示例:**
```cpp
LaserCuttingAnalogParam analogParam;
Result result = laser_cutting_get_analog_parameter(fd, analogParam);
if (result == 0) {
printf("激光功率匹配曲线点数: %d\n", analogParam.analogMatch.laserPower.level);
printf("气压匹配曲线点数: %d\n", analogParam.analogMatch.pressure.level);
}
```
---
## 12.4 IO 参数
### laser_cutting_set_io_parameter / laser_cutting_set_io_parameter_robot
设置激光切割的 IO 端口配置,包括模拟量输出、数字量输入输出以及各类故障检测端口。
**函数签名:**
```cpp
Result laser_cutting_set_io_parameter(SOCKETFD socketFd, LaserCuttingIOParam param);
Result laser_cutting_set_io_parameter_robot(SOCKETFD socketFd, int robotNum, LaserCuttingIOParam param);
```
**参数说明:**
| 参数 | 类型 | 输入/输出 | 说明 |
|------|------|----------|------|
| socketFd | SOCKETFD | 输入 | 连接句柄 |
| robotNum | int | 输入 | 机器人编号(仅 `_robot` 版本) |
| param | LaserCuttingIOParam | 输入 | IO参数配置(含IO端口和故障检测端口) |
**返回值:** `Result` 枚举,0 表示成功,-1 表示等待消息失败,-2 表示找不到指定的客户端。
**注意事项:** IO 端口配置需与实际的硬件接线一致。错误的端口配置会导致激光器无法正常控制或故障检测失效。
**使用示例:**
```cpp
LaserCuttingIOParam ioParam;
// 配置IO端口
ioParam.io.AO_laserPower = 1; // 激光功率模拟量输出端口1
ioParam.io.AO_pressure = 2; // 气压模拟量输出端口2
ioParam.io.DO_lightGate = 3; // 光闸输出端口3
ioParam.io.DO_redLight = 4; // 红光输出端口4
ioParam.io.DO_highPressgas = 5; // 高压气输出端口5
ioParam.io.DO_lowPressgas = 6; // 低压气输出端口6
ioParam.io.DO_aspiration = 7; // 吸气输出端口7
ioParam.io.DO_follow = 8; // 随动输出端口8
ioParam.io.DO_liftUp = 9; // 抬起输出端口9
ioParam.io.DO_backMiddle = 10; // 回中输出端口10
ioParam.io.DI_perforateArrival = 11; // 穿孔到位输入端口11
ioParam.io.DI_followArrival = 12; // 随动到位输入端口12
ioParam.io.DI_liftUpArrival = 13; // 抬起到位输入端口13
ioParam.io.DI_backMiddleArrival = 14;// 回中到位输入端口14
ioParam.io.pwm_port_ = 15; // PWM输出端口15
// 配置故障检测端口
ioParam.laser_fault.input_port = 20; // 激光器故障输入端口20
ioParam.pressure_fault.input_port = 21; // 气压故障输入端口21
ioParam.regulator_fault.input_port = 22; // 调压器故障输入端口22
ioParam.water_cooler_fault.input_port = 23;// 水冷机故障输入端口23
Result result = laser_cutting_set_io_parameter(fd, ioParam);
if (result == 0) {
printf("激光切割IO参数设置成功\n");
}
```
---
### laser_cutting_get_io_parameter / laser_cutting_get_io_parameter_robot
查询激光切割当前的 IO 端口配置。
**函数签名:**
```cpp
Result laser_cutting_get_io_parameter(SOCKETFD socketFd, LaserCuttingIOParam& param);
Result laser_cutting_get_io_parameter_robot(SOCKETFD socketFd, int robotNum, LaserCuttingIOParam& param);
```
**参数说明:**
| 参数 | 类型 | 输入/输出 | 说明 |
|------|------|----------|------|
| socketFd | SOCKETFD | 输入 | 连接句柄 |
| robotNum | int | 输入 | 机器人编号(仅 `_robot` 版本) |
| param | LaserCuttingIOParam& | 输出 | 接收IO参数配置 |
**返回值:** `Result` 枚举,0 表示成功,-1 表示等待消息失败,-2 表示找不到指定的客户端。
**使用示例:**
```cpp
LaserCuttingIOParam ioParam;
Result result = laser_cutting_get_io_parameter(fd, ioParam);
if (result == 0) {
printf("激光切割IO配置:\n");
printf(" 激光功率AO: 端口%d\n", ioParam.io.AO_laserPower);
printf(" 气压AO: 端口%d\n", ioParam.io.AO_pressure);
printf(" 光闸DO: 端口%d\n", ioParam.io.DO_lightGate);
printf(" 高压气DO: 端口%d\n", ioParam.io.DO_highPressgas);
printf(" 激光器故障DI: 端口%d\n", ioParam.laser_fault.input_port);
printf(" 水冷机故障DI: 端口%d\n", ioParam.water_cooler_fault.input_port);
}
```
---
## 12.5 完整激光切割配置流程示例
```cpp
#include "nrc_craft_laser_cutting.h"
#include "nrc_interface.h"
#include
int main() {
SOCKETFD fd = connect_robot("192.168.1.15", "6001");
if (fd <= 0) {
printf("连接失败\n");
return -1;
}
// 1. 配置全局参数
LaserCuttingGlobalParam globalParam;
globalParam.equipment.RetreatDistance = 5.0;
globalParam.equipment.collisionDistance = 10.0;
globalParam.equipment.follow = 1;
globalParam.equipment.waitFollowTime = 1.0;
globalParam.equipment.focusCompensation = true;
globalParam.equipment.focusCompensationConstant = 0.5;
globalParam.equipment.focusCompensationPower = 80.0;
globalParam.equipment.preAspiratedTime = 1.0;
globalParam.equipment.delAspiratedTime = 0.5;
Result ret = laser_cutting_set_global_parameter(fd, globalParam);
if (ret != 0) {
printf("全局参数设置失败\n");
return -1;
}
printf("全局参数配置完成\n");
// 2. 配置工艺号1的切割参数(3mm碳钢板)
LaserCuttingCraftParam craftParam;
craftParam.num = 1;
craftParam.cut.power = 80;
craftParam.cut.freq = 5000;
craftParam.cut.dutyRatio = 100;
craftParam.cut.focusPosition = -0.5;
craftParam.cut.followHeight = 1.0;
craftParam.cut.pressure = 8.0;
craftParam.cut.note = "3mm碳钢板";
ret = laser_cutting_set_craft_parameter(fd, craftParam);
if (ret != 0) {
printf("工艺参数设置失败\n");
return -1;
}
printf("工艺参数配置完成\n");
// 3. 配置模拟量匹配参数
LaserCuttingAnalogParam analogParam;
analogParam.analogMatch.laserPower.level = 2;
analogParam.analogMatch.laserPower.x = {0.0, 100.0};
analogParam.analogMatch.laserPower.y = {0.0, 10.0};
analogParam.analogMatch.pressure.level = 2;
analogParam.analogMatch.pressure.x = {0.0, 16.0};
analogParam.analogMatch.pressure.y = {0.0, 10.0};
laser_cutting_set_analog_parameter(fd, analogParam);
printf("模拟量匹配参数配置完成\n");
// 4. 配置IO参数
LaserCuttingIOParam ioParam;
ioParam.io.AO_laserPower = 1;
ioParam.io.AO_pressure = 2;
ioParam.io.DO_lightGate = 3;
ioParam.io.DO_redLight = 4;
ioParam.io.DO_highPressgas = 5;
ioParam.io.DO_aspiration = 7;
ioParam.io.DI_perforateArrival = 11;
ioParam.io.DI_followArrival = 12;
ioParam.laser_fault.input_port = 20;
ioParam.water_cooler_fault.input_port = 23;
ret = laser_cutting_set_io_parameter(fd, ioParam);
if (ret != 0) {
printf("IO参数设置失败\n");
return -1;
}
printf("IO参数配置完成\n");
// 5. 验证配置:读取所有参数
printf("\n========== 配置验证 ==========\n");
LaserCuttingGlobalParam readGlobal;
laser_cutting_get_global_parameter(fd, readGlobal);
printf("全局参数 - 回退距离: %.1fmm, 随动模式: %d\n",
readGlobal.equipment.RetreatDistance, readGlobal.equipment.follow);
LaserCuttingCraftParam readCraft;
readCraft.num = 1;
laser_cutting_get_craft_parameter(fd, readCraft);
printf("工艺参数 - 功率: %d%%, 频率: %dHz, 气压: %.1fbar\n",
readCraft.cut.power, readCraft.cut.freq, readCraft.cut.pressure);
printf("激光切割系统配置完成,准备开始切割作业\n");
return 0;
}
```
---
# 十三、传送带跟踪工艺(nrc_craft_conveyor_belt_track.h)
> 原文:https://open.inexbot.com/zh/04.%E4%B8%8A%E4%BD%8D%E6%9C%BA/01.C++/02.%E6%8E%A5%E5%8F%A3/13.%E4%BC%A0%E9%80%81%E5%B8%A6%E8%B7%9F%E8%B8%AA%E5%B7%A5%E8%89%BA.html
## 功能概述
传送带跟踪(Conveyor Belt Tracking)工艺用于机器人在运动中的传送带上动态抓取/放置工件。系统利用编码器实时获取传送带位置,结合用户输入的物料点位置,实时计算工件位置并通过运动追踪物料。
**核心参数分组:**
| 参数组 | 说明 | 对应结构体 |
|--------|------|-----------|
| 基本参数 | 编码器设置、传送带速度、用户坐标、跟踪高度 | `ConveyorBasicParams` |
| 识别参数 | 检测源(传感器/视觉)、触发方式 | `ConveyorIdentificationParams` |
| 传感器参数 | 传感器标定位置 | `ConveyorSensorParams` |
| 跟踪范围 | 跟踪窗口范围(X/Y/Z 最大最小) | `ConveyorTrackRangeParams` |
| 等待点 | 无工件等待位置 | `ConveyorWaitPointParams` |
> 使用前需在示教器【工艺】-【传送带跟踪工艺】中设置工艺号,每个工艺号(1-9)保存一组参数。
---
### conveyor_belt_tracking_set_basic_parameter / conveyor_belt_tracking_set_basic_parameter_robot
设置传送带跟踪的基本参数(编码器、速度、坐标等)。
**函数原型:**
```cpp
Result conveyor_belt_tracking_set_basic_parameter(SOCKETFD socketFd, int encoderVal, int time, int encoderDirection,
double encoderResolution, double maxEncoderVal, double minEncoderVal, int posRecordMode, double speed, int userCoord,
int conveyorID, int height, int trackOnRunModeWithTargetOverrun, int compensationEncoderVal);
Result conveyor_belt_tracking_set_basic_parameter_robot(SOCKETFD socketFd, int robotNum, int encoderVal, int time, int encoderDirection,
double encoderResolution, double maxEncoderVal, double minEncoderVal, int posRecordMode, double speed, int userCoord,
int conveyorID, int height, int trackOnRunModeWithTargetOverrun, int compensationEncoderVal);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| encoderVal | int | 编码器当前值,单位线 |
| time | int | 跟踪补偿时间,单位毫秒 |
| encoderDirection | int | 编码器方向,用于确定正反转 |
| encoderResolution | double | 编码器分辨率,每转或每单位的脉冲数 |
| maxEncoderVal / minEncoderVal | double | 编码器最大/最小值,限制或标定读数范围,单位线 |
| posRecordMode | int | 位置记录模式:编码器/恒速设置 |
| speed | double | 传送带速度,单位 mm/s(环形传送带为 °/s) |
| userCoord | int | 用户坐标系编号,指定操作在哪个坐标下进行 |
| conveyorID | int | 传送带 ID |
| height | int | 跟踪目标高度:传感器感知/跟踪指令示教 |
| trackOnRunModeWithTargetOverrun | int | 目标超限运行方式:等待下一个目标/跳行到追踪结束运行 |
| compensationEncoderVal | int | 跟踪补偿编码器值,单位线 |
**返回值:** `SUCCESS(0)` 表示成功,-1 表示等待消息失败,-2 表示找不到指定的客户端。
**使用示例:**
```cpp
// 设置传送带1的基本参数
Result result = conveyor_belt_tracking_set_basic_parameter(
fd, // 连接句柄
1000, // 编码器当前值
50, // 补偿时间 50ms
1, // 编码器正向
10.0, // 分辨率 10 线/mm
100000, 0, // 编码器范围
0, // 位置记录模式:编码器
500.0, // 速度 500mm/s
1, // 用户坐标系 1
1, // 传送带 ID 1
100, // 跟踪高度 100mm
0, // 目标超限:等待下一个目标
0 // 补偿编码器值
);
```
---
### conveyor_belt_tracking_set_identification_parameter / conveyor_belt_tracking_set_identification_parameter_robot
设置传送带参数识别的配置(检测源与识别方式)。
**函数原型:**
```cpp
Result conveyor_belt_tracking_set_identification_parameter(SOCKETFD socketFd, int conveyorID, int detectSrcType, int capturePos, int visionID, int visionIoFilterType, int visionLatchEncoderValueType, int communication, int sensorTrg, int type);
Result conveyor_belt_tracking_set_identification_parameter_robot(SOCKETFD socketFd, int robotNum, int conveyorID, int detectSrcType, int capturePos, int visionID, int visionIoFilterType, int visionLatchEncoderValueType, int communication, int sensorTrg, int type);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| conveyorID | int | 传送带 ID |
| detectSrcType | int | 检测源类型 |
| capturePos | int | 信号源参数(捕获位置 IO) |
| visionID | int | 视觉系统 ID |
| visionIoFilterType | int | 视觉输入输出过滤类型 |
| visionLatchEncoderValueType | int | 视觉锁存编码器值类型 |
| communication | int | 通讯方式 |
| sensorTrg | int | 传感器触发方式(0=下降沿,1=上升沿) |
| type | int | 识别类型 |
**返回值:** `SUCCESS(0)` 表示成功,-1 表示等待消息失败,-2 表示找不到指定的客户端。
---
### conveyor_belt_tracking_set_sensor_calibration / conveyor_belt_tracking_set_sensor_calibration_robot
设置传送带上的传感器标定参数。
**函数原型:**
```cpp
Result conveyor_belt_tracking_set_sensor_calibration(SOCKETFD socketFd, int conveyorID, const std::vector sensorPos);
Result conveyor_belt_tracking_set_sensor_calibration_robot(SOCKETFD socketFd, int robotNum, int conveyorID, const std::vector sensorPos);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| conveyorID | int | 传送带 ID |
| sensorPos | const std::vector\ | 传感器位置,长度 6 |
**返回值:** `SUCCESS(0)` 表示成功,-1 表示等待消息失败,-2 表示找不到指定的客户端。
---
### conveyor_belt_tracking_set_tracking_range / conveyor_belt_tracking_set_tracking_range_robot
设置传送带跟踪范围参数(跟踪窗口)。
**函数原型:**
```cpp
Result conveyor_belt_tracking_set_tracking_range(SOCKETFD socketFd, int conveyorID, double receLatestPos, double trackRangeXMax, double trackRangeYMax, double trackRangeYMin, double trackRangeZMax, double trackRangeZMin, double trackStartXPoint);
Result conveyor_belt_tracking_set_tracking_range_robot(SOCKETFD socketFd, int robotNum, int conveyorID, double receLatestPos, double trackRangeXMax, double trackRangeYMax, double trackRangeYMin, double trackRangeZMax, double trackRangeZMin, double trackStartXPoint);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| conveyorID | int | 传送带 ID |
| receLatestPos | double | 最新接收位置(最迟接收 X 值) |
| trackRangeXMax | double | X 轴最大跟踪范围 |
| trackRangeYMax / trackRangeYMin | double | Y 轴最大/最小跟踪范围 |
| trackRangeZMax / trackRangeZMin | double | Z 轴最大/最小跟踪范围 |
| trackStartXPoint | double | 跟踪起始 X 点 |
**返回值:** `SUCCESS(0)` 表示成功,-1 表示等待消息失败,-2 表示找不到指定的客户端。
> 工件进入跟踪范围后机器人开始同步;超出跟踪最大范围或未在最迟接收位置前收到数据,则放弃该工件。
---
### conveyor_belt_tracking_set_tracking_wait_point / conveyor_belt_tracking_set_tracking_wait_point_robot
标定传送带跟踪等待点的位置参数(无工件时等待位置)。
**函数原型:**
```cpp
Result conveyor_belt_tracking_set_tracking_wait_point(SOCKETFD socketFd, int conveyorID, bool isWait, double delayDetectTime, const std::vector pos);
Result conveyor_belt_tracking_set_tracking_wait_point_robot(SOCKETFD socketFd, int robotNum, int conveyorID, bool isWait, double delayDetectTime, const std::vector pos);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| conveyorID | int | 传送带 ID |
| isWait | bool | 无工件是否到指定点等待 |
| delayDetectTime | double | 等待延时时间 |
| pos | const std::vector\ | 机器人位置坐标,含 X/Y/Z 轴坐标及三个旋转轴角度(弧度制),长度 7 |
**返回值:** `SUCCESS(0)` 表示成功,-1 表示等待消息失败,-2 表示找不到指定的客户端。
---
### conveyor_belt_set_sensor_calibration / conveyor_belt_set_sensor_calibration_robot
标定获得传感器位置。
**函数原型:**
```cpp
Result conveyor_belt_set_sensor_calibration(SOCKETFD socketFd, int conveyorID);
Result conveyor_belt_set_sensor_calibration_robot(SOCKETFD socketFd, int robotNum, int conveyorID);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| conveyorID | int | 传送带 ID |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### conveyor_belt_calibrate_for_sensor_point / conveyor_belt_calibrate_for_sensor_point_robot
标定获得传感器位置(传感器标定流程第二步)。
**函数原型:**
```cpp
Result conveyor_belt_calibrate_for_sensor_point(SOCKETFD socketFd, int conveyorID);
Result conveyor_belt_calibrate_for_sensor_point_robot(SOCKETFD socketFd, int robotNum, int conveyorID);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| conveyorID | int | 传送带 ID |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### conveyor_belt_calculate_for_sensor_point / conveyor_belt_calculate_for_sensor_point_robot
计算传感器位置(传感器标定流程第三步,标定完成后计算)。
**函数原型:**
```cpp
Result conveyor_belt_calculate_for_sensor_point(SOCKETFD socketFd, int conveyorID);
Result conveyor_belt_calculate_for_sensor_point_robot(SOCKETFD socketFd, int robotNum, int conveyorID);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| conveyorID | int | 传送带 ID |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### conveyor_belt_get_basic_paramters / conveyor_belt_get_basic_paramters_robot
获取基本参数。
**函数原型:**
```cpp
Result conveyor_belt_get_basic_paramters(SOCKETFD socketFd, int conveyorID, ConveyorBasicParams& param);
Result conveyor_belt_get_basic_paramters_robot(SOCKETFD socketFd, int robotNum, int conveyorID, ConveyorBasicParams& param);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| conveyorID | int | 传送带 ID |
| param | ConveyorBasicParams& | 输出参数,基本参数结构体 |
**ConveyorBasicParams 结构体:**
| 字段 | 类型 | 说明 |
|------|------|------|
| conveyorID | int | 传送带 ID |
| compensation_encoderVal | double | 补偿编码器值 |
| compensation_time | double | 补偿时间 |
| conveyor_encoderDirection | int | 编码器方向 |
| conveyor_encoderResolution | double | 编码器分辨率 |
| conveyor_encoderValue | double | 编码器当前值 |
| conveyor_maxEncoderVal / conveyor_minEncoderVal | double | 编码器最大/最小值 |
| conveyor_posRecordMode | int | 位置记录模式 |
| conveyor_speed | double | 传送带速度 |
| conveyor_userCoord | int | 用户坐标系 |
| track_height | int | 轨道高度 |
| track_on_run_mode_with_target_overrun | int | 轨道运行模式与目标超限 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### conveyor_belt_get_identification_paramters / conveyor_belt_get_identification_paramters_robot
获取识别参数。
**函数原型:**
```cpp
Result conveyor_belt_get_identification_paramters(SOCKETFD socketFd, int conveyorID, ConveyorIdentificationParams& param);
Result conveyor_belt_get_identification_paramters_robot(SOCKETFD socketFd, int robotNum, int conveyorID, ConveyorIdentificationParams& param);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| conveyorID | int | 传送带 ID |
| param | ConveyorIdentificationParams& | 输出参数,识别参数结构体 |
**ConveyorIdentificationParams 结构体:**
| 字段 | 类型 | 说明 |
|------|------|------|
| conveyorID | int | 传送带 ID |
| detectSrc_DI_capturePos | int | 捕获位置(触发信号 IO) |
| detectSrc_globalVar | std::string | 全局变量 |
| detectSrc_type | int | 检测源类型(0=传感器,1=视觉,2=传感器+视觉等) |
| detectSrc_visionID | int | 视觉 ID |
| detectSrc_vision_io_filter_type | int | 视觉 IO 过滤类型 |
| detectSrc_vision_latch_encoder_value_type | int | 视觉锁存编码器值类型 |
| identification_communication | std::string | 识别通讯 |
| identification_sensorTrg | int | 识别传感器触发 |
| identification_type | int | 识别类型 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### conveyor_belt_get_sensor_paramters / conveyor_belt_get_sensor_paramters_robot
获取传感器参数。
**函数原型:**
```cpp
Result conveyor_belt_get_sensor_paramters(SOCKETFD socketFd, int conveyorID, ConveyorSensorParams& param);
Result conveyor_belt_get_sensor_paramters_robot(SOCKETFD socketFd, int robotNum, int conveyorID, ConveyorSensorParams& param);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| conveyorID | int | 传送带 ID |
| param | ConveyorSensorParams& | 输出参数,传感器参数结构体 |
**ConveyorSensorParams 结构体:**
| 字段 | 类型 | 说明 |
|------|------|------|
| conveyorID | int | 传送带 ID |
| sensorPosDeg_X / Y / Z | double | 传感器位置 X/Y/Z(角度) |
| sensorPosDeg_A / B / C | double | 传感器姿态 A/B/C(角度) |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### conveyor_belt_get_track_range_paramters / conveyor_belt_get_track_range_paramters_robot
获取跟踪范围参数。
**函数原型:**
```cpp
Result conveyor_belt_get_track_range_paramters(SOCKETFD socketFd, int conveyorID, ConveyorTrackRangeParams& param);
Result conveyor_belt_get_track_range_paramters_robot(SOCKETFD socketFd, int robotNum, int conveyorID, ConveyorTrackRangeParams& param);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| conveyorID | int | 传送带 ID |
| param | ConveyorTrackRangeParams& | 输出参数,跟踪范围结构体 |
**ConveyorTrackRangeParams 结构体:**
| 字段 | 类型 | 说明 |
|------|------|------|
| conveyorID | int | 传送带 ID |
| position_receLatestPos | double | 最迟到接收位置 |
| position_trackRangeXMax | double | 轨道范围 X 最大 |
| position_trackRangeYMax / YMin | double | 轨道范围 Y 最大/最小 |
| position_trackRangeZMax / ZMin | double | 轨道范围 Z 最大/最小 |
| position_trackStartXPoint | double | 轨道起始 X 点 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
### conveyor_belt_get_wait_point_paramters / conveyor_belt_get_wait_point_paramters_robot
获取等待点参数。
**函数原型:**
```cpp
Result conveyor_belt_get_wait_point_paramters(SOCKETFD socketFd, int conveyorID, ConveyorWaitPointParams& param);
Result conveyor_belt_get_wait_point_paramters_robot(SOCKETFD socketFd, int robotNum, int conveyorID, ConveyorWaitPointParams& param);
```
**参数说明:**
| 参数 | 类型 | 说明 |
|------|------|------|
| socketFd | SOCKETFD | 连接句柄 |
| robotNum | int | 机器人编号 (1-4),仅 `_robot` 版本 |
| conveyorID | int | 传送带 ID |
| param | ConveyorWaitPointParams& | 输出参数,等待点参数结构体 |
**ConveyorWaitPointParams 结构体:**
| 字段 | 类型 | 说明 |
|------|------|------|
| conveyorID | int | 传送带 ID |
| delayDetectTime | double | 延迟检测时间 |
| isWait | bool | 是否等待 |
| pos | std::vector\ | 位置数组 |
**返回值:** `SUCCESS(0)` 表示成功,负数表示失败。
---
---
# 快速开始
> 原文:https://open.inexbot.com/zh/04.%E4%B8%8A%E4%BD%8D%E6%9C%BA/01.C++/03.%E7%A4%BA%E4%BE%8B/01.%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B.html
最小可运行示例:连接控制器、获取版本、读取位置、断开。
## 代码
```cpp
#include
#include "cpp_interface/nrc_api.h"
int main()
{
// 1. 连接控制器
SOCKETFD fd = connect_robot("192.168.1.15", "6001");
if (fd <= 0) {
std::cout << "连接失败" << std::endl;
return 1;
}
// 2. 等待连接就绪
while (get_connection_status(fd) != 0) {
Sleep(200); // Linux: usleep(200000)
}
// 3. 获取版本
std::cout << "SDK 版本: " << get_library_version() << std::endl;
// 4. 读取当前位置(关节坐标)
std::vector pos(7);
get_current_position(fd, 0, pos);
std::cout << "关节位置: ";
for (auto v : pos) std::cout << v << " ";
std::cout << std::endl;
// 5. 断开
disconnect_robot(fd);
return 0;
}
```
## 预期输出
```text
SDK 版本: Inexbot API v2.0.4-24.03
关节位置: 0.0 0.0 0.0 0.0 0.0 0.0 0.0
```
## 下一步
- [MinGW + Qt Creator 环境搭建](../01.文档/01.环境搭建/01.MinGW%20环境.md)
- [MSVC + Visual Studio 环境搭建](../01.文档/01.环境搭建/02.MSVC%20环境.md)
- [Linux + GCC 环境搭建](../01.文档/01.环境搭建/03.Linux%20环境.md)
如需更复杂的运动控制,参见 [基础应用](./02.基础应用/index.md) 和 [进阶应用](./03.进阶应用/index.md)。
---
# 1. 获取不同坐标系的位置
> 原文:https://open.inexbot.com/zh/04.%E4%B8%8A%E4%BD%8D%E6%9C%BA/01.C++/03.%E7%A4%BA%E4%BE%8B/02.%E5%9F%BA%E7%A1%80%E5%BA%94%E7%94%A8/01.%E8%8E%B7%E5%8F%96%E4%B8%8D%E5%90%8C%E5%9D%90%E6%A0%87%E7%B3%BB%E7%9A%84%E4%BD%8D%E7%BD%AE.html
本期将介绍如何连接控制器,并获取机器人在不同坐标系下的实时位置。
机器人在运行和调试过程中,同一个位置可以使用不同的坐标系进行描述。程序首先使用 `connect_robot` 连接控制器,再通过返回的连接句柄 `fd` 调用 `get_current_position`,依次查询机器人在关节坐标、直角坐标、工具坐标和用户坐标下的当前位置。
`get_current_position` 接口中的 `coord` 参数用于指定需要查询的坐标系:
- `coord = 0`:关节坐标
- `coord = 1`:直角坐标
- `coord = 2`:工具坐标
- `coord = 3`:用户坐标
接口查询成功后,会将当前位置写入长度为 7 的 `pos` 容器。不同坐标系下各分量的含义不同,使用时应结合机器人型号、当前工具坐标系和用户坐标系配置进行确认。
::: warning ⚠️ 使用注意
本 Demo 只读取并输出机器人当前位置,不会发送运动指令。运行前仍需确认:
- **连接配置:** 控制器 IP 地址和 SDK 服务端口与实际配置一致
- **坐标类型:** `coord` 的取值位于 0~3 范围内
- **坐标配置:** 查询工具坐标或用户坐标前,确认控制器中对应坐标系已经正确设置
- **返回结果:** 实际项目中应检查 `get_current_position` 的返回值,接口调用成功后再使用 `pos` 中的数据
不同坐标系下的位置数据不能直接混用。将查询结果用于运动、偏移或坐标转换前,应先确认坐标系类型及各分量的单位和含义。
:::
```cpp
/*
* Demo # : 01
* 功能:获取机器人在关节、直角、工具和用户坐标系下的当前位置
* 依赖:nrc_api.h
*
* 说明:
* 1. coord = 0:关节坐标;coord = 1:直角坐标
* 2. coord = 2:工具坐标;coord = 3:用户坐标
* 3. 本 Demo 只查询并输出位置,不会向机器人发送运动指令
*/
#include
#include "cpp_interface/nrc_api.h" // 提供控制器连接和机器人位置查询接口
/**
* @brief Demo 程序入口:连接控制器,并依次输出四种坐标系下的机器人当前位置。
* @return 控制器连接失败或查询结束时返回 0。
*/
int main()
{
// 使用现场控制器的 IP 地址和 SDK 服务端口建立连接,并保存返回的连接句柄。
SOCKETFD fd = connect_robot("192.168.1.15", "6001");
if (fd <= 0)
{
std::cout << "连接失败" << std::endl;
return 0;
}
std::cout << "连接成功: "<< fd << std::endl;
std::vector pos(7); // 保存 SDK 返回的七维位置数据,具体含义随坐标系类型变化。
for (int coord = 0; coord < 4; coord++)
{
// 按 coord 指定的坐标系查询当前位置,查询结果由 SDK 写入 pos。
get_current_position(fd, coord, pos);
// 按接口返回顺序输出当前位置的七个分量。
for (auto v : pos)
{
std::cout << v << " ";
}
std::cout << std::endl;
}
return 0;
}
```
---
# 2. 上电流程
> 原文:https://open.inexbot.com/zh/04.%E4%B8%8A%E4%BD%8D%E6%9C%BA/01.C++/03.%E7%A4%BA%E4%BE%8B/02.%E5%9F%BA%E7%A1%80%E5%BA%94%E7%94%A8/02.%E4%B8%8A%E7%94%B5%E6%B5%81%E7%A8%8B.html
本期将介绍如何连接控制器,并根据当前状态安全地完成伺服上电和就绪确认。
通过 SDK 控制机器人之前,需要先连接控制器,再根据当前伺服状态执行清错、切换就绪状态和伺服上电等操作。
伺服状态的含义如下:
- 状态 0:伺服停止,需要先切换到就绪状态
- 状态 1:伺服就绪,可以直接执行上电
- 状态 2:伺服报警,需要先清除报警,再重新上电
- 状态 3:伺服已经上电并进入运行状态
::: danger ⚠️ 安全警告
伺服上电后,机器人将进入允许运动的状态。执行前必须确认:
- **人员安全:** 机器人工作空间内无人员或障碍物
- **急停可用:** 急停按钮处于可用状态,并在操作人员可触及范围内
- **控制器状态:** 控制器无未处理的安全报警,安全回路工作正常
- **程序检查:** 确认后续程序不会立即下发未经验证的运动指令
- **现场配置:** 控制器 IP 地址和 SDK 服务端口与实际配置一致
建议先在仿真环境中验证上电流程,确认状态切换符合预期后再连接真机。
:::
## 1、配置控制器连接和等待参数
首先配置控制器 IP 地址、SDK 服务端口、上电等待超时时间、状态轮询间隔和最大连续查询失败次数。
这些参数集中定义在程序开头,便于根据不同现场环境统一修改,避免相同参数分散在多个函数中,造成遗漏或配置不一致。
```cpp
/**
* @name 用户必须根据运行环境修改
* 以下配置决定 SDK 能否连接到实际控制器,运行前必须核对。
* @{
*/
const std::string robot_ip = "192.168.3.243"; ///< 用户实际控制器的 IP 地址
const std::string robot_port = "6001"; ///< 用户实际控制器的 SDK 服务端口,必须与控制器配置一致
/** @} */
/**
* @name 客户需根据本 Demo 修改
* 以下配置关系到上电等待是否符合当前机器人的实际耗时。
* @{
*/
constexpr int ready_timeout_seconds = 90; ///< 等待伺服完成上电的总超时时间
/** @} */
/**
* @name 建议客户修改
* 以下配置用于平衡状态反馈速度与控制器通信负载。
* @{
*/
constexpr int poll_interval_ms = 500; ///< 伺服状态轮询间隔,单位为毫秒
/** @} */
/**
* @name 可修改也可保留默认值
* 默认值适用于一般场景,仅在需要调整通信容错能力时修改。
* @{
*/
constexpr int max_query_failures = 3; ///< 状态查询允许的最大连续失败次数
/** @} */
```
## 2、封装 SDK 返回值检查函数
由于在上电过程中需要调用多个 SDK 接口,为了帮助判断接口是否调用成功。我们在这里封装 SDK 返回值检查函数,多数 SDK 控制接口都会返回 `Result`,只有返回 `SUCCESS` 时,才能确认本次调用成功。
```cpp
/**
* @brief 统一检查普通 SDK 接口的返回结果。
* @param[in] result SDK 接口返回的执行结果。
* @param[in] operation SDK 接口名称,用于输出错误信息。
* @retval true SDK 接口调用成功。
* @retval false SDK 接口调用失败。
*/
bool check_sdk_result(Result result, const char* operation)
{
if (result == SUCCESS)
{
return true; // SDK 调用成功。
}
std::cerr << operation << "调用失败,错误码:"
<< static_cast(result) << std::endl;
return false; // SDK 调用失败。
}
```
## 3、封装等待伺服就绪函数
`set_servo_poweron` 调用成功只表示上电命令已经成功发送,不代表伺服已经立即进入运行状态。控制器完成内部状态切换需要一定时间,因此程序还需要持续查询伺服状态,直到状态变为 3。
```cpp
/**
* @brief 等待伺服完成上电并进入运行状态。
* @param[in] fd 控制器连接句柄。
* @param[in] timeout 本次等待允许占用的最长时间。
* @retval true 已确认伺服进入运行状态。
* @retval false 查询连续失败、伺服报警或等待超时。
*/
bool wait_until_robot_ready(
SOCKETFD fd,
std::chrono::seconds timeout = std::chrono::seconds(ready_timeout_seconds))
{
using clock = std::chrono::steady_clock; // 使用单调时钟,避免系统时间变化影响超时判断。
const auto deadline = clock::now() + timeout; // 计算整个等待过程的绝对截止时间。
int state = 0;
int consecutiveFailures = 0; // 记录连续查询失败次数,用于处理短暂通信波动。
while (clock::now() < deadline)
{
const Result result = get_servo_state(fd, state); // 每轮只查询一次伺服状态。
if (result != SUCCESS)
{
++consecutiveFailures;
std::cerr << "获取伺服状态失败,第"
<< consecutiveFailures << "/" << max_query_failures
<< "次,错误码:" << static_cast(result) << std::endl;
if (consecutiveFailures >= max_query_failures)
{
std::cerr << "连续获取伺服状态失败,停止等待" << std::endl;
return false;
}
std::this_thread::sleep_for(
std::chrono::milliseconds(poll_interval_ms));
continue; // 本轮没有获得有效状态,直接开始下一轮查询。
}
consecutiveFailures = 0; // 查询恢复成功后,将连续失败次数清零。
if (state == 3)
{
std::cout << "伺服上电成功,机器人已经进入运行状态" << std::endl;
return true;
}
if (state == 2)
{
std::cerr << "伺服进入报警状态,上电失败" << std::endl;
return false;
}
// 状态为 0 或 1 时,等待一个轮询周期后再次查询。
std::this_thread::sleep_for(
std::chrono::milliseconds(poll_interval_ms));
}
std::cerr << "等待伺服上电超时,最后状态:" << state << std::endl;
return false;
}
```
其中使用 `std::chrono::steady_clock` 计算超时时间,是为了避免系统时间被人工修改或自动校时后影响等待结果。只有连续查询失败达到上限时才结束流程,能够容忍短暂的通信波动;任意一次查询成功后,连续失败计数都会清零。
## 4、封装伺服上电函数
机器人可能处于停止、就绪、报警或已经运行等不同状态。上电前必须先读取实际状态,并根据状态选择正确的处理路径。
```cpp
/**
* @brief 根据当前伺服状态执行上电流程。
* @param[in] fd 控制器连接句柄。
* @retval true 机器人已处于运行状态,或已完成上电并确认就绪。
* @retval false 状态查询、清错、状态切换、上电或就绪确认失败。
*/
bool power_on(int fd)
{
int state = 0;
if (!check_sdk_result(get_servo_state(fd, state), "get_servo_state"))
return false; // 状态未知时不允许继续发送控制命令。
switch (state)
{
case 0: // 停止状态:先切换到状态 1,再执行上电。
if (!check_sdk_result(set_servo_state(fd, 1), "set_servo_state"))
return false;
if (!check_sdk_result(set_servo_poweron(fd), "set_servo_poweron"))
return false;
break;
case 1: // 就绪状态:可以直接发送伺服上电命令。
if (!check_sdk_result(set_servo_poweron(fd), "set_servo_poweron"))
return false;
break;
case 2: // 报警状态:先清错,再重新设置就绪状态并执行上电。
if (!check_sdk_result(clear_error(fd), "clear_error"))
return false;
std::cerr << "伺服报警已清除,重新上电" << std::endl;
if (!check_sdk_result(set_servo_state(fd, 1), "set_servo_state"))
return false;
if (!check_sdk_result(set_servo_poweron(fd), "set_servo_poweron"))
return false;
break;
case 3: // 伺服已经处于运行状态,无需重复发送上电命令。
return true;
default: // 未定义状态不能安全处理,直接结束上电流程。
std::cerr << "未知伺服状态:" << state << std::endl;
return false;
}
// 上电命令发送成功后,继续等待状态 3,以确认伺服真正就绪。
return wait_until_robot_ready(fd);
}
```
各状态的处理逻辑如下:
1. 状态 0:调用 `set_servo_state` 切换到状态 1,再调用 `set_servo_poweron`。
2. 状态 1:控制器已经就绪,直接调用 `set_servo_poweron`。
3. 状态 2:先调用 `clear_error`,清错成功后重新切换到状态 1,再执行上电。
4. 状态 3:机器人已经处于运行状态,直接返回成功,避免重复上电。
5. 未知状态:无法确定安全处理方式,返回失败并停止后续操作。
## 5、主程序连接控制器并执行上电
主函数负责启用 UTF-8 控制台输出、连接控制器、调用封装好的上电函数,并根据结果决定程序是否继续。
上电失败时需要主动调用 `disconnect_robot` 释放已经建立的连接,避免异常路径遗留无效会话。
```cpp
#include
#include
#include
#include
#include "../demo_utils.h"
/**
* @brief Demo 程序入口。
* @return 连接失败时返回 0;上电失败时返回 1;成功时按默认规则返回 0。
*/
int main()
{
demo::enable_console_utf8(); // 启用 UTF-8 控制台输出。
// 同步连接控制器,并通过返回的连接句柄判断连接结果。
SOCKETFD fd = connect_robot(robot_ip, robot_port);
if (fd <= 0)
{
std::cout << "控制器连接失败" << std::endl;
return 0;
}
std::cout << "控制器连接成功" << std::endl;
// 只有确认伺服进入状态 3 后,才继续执行成功路径。
if (!power_on(fd))
{
std::cerr << "伺服未就绪,停止后续操作" << std::endl;
disconnect_robot(fd); // 上电失败时主动释放控制器连接。
return 1;
}
std::cout << "伺服已就绪,可以执行后续操作" << std::endl;
}
```
---
# 3. 伺服状态检测
> 原文:https://open.inexbot.com/zh/04.%E4%B8%8A%E4%BD%8D%E6%9C%BA/01.C++/03.%E7%A4%BA%E4%BE%8B/02.%E5%9F%BA%E7%A1%80%E5%BA%94%E7%94%A8/03.%E4%BC%BA%E6%9C%8D%E7%8A%B6%E6%80%81%E6%A3%80%E6%B5%8B.html
本期将介绍如何连接控制器,查询机器人当前的伺服状态,并根据状态码输出对应的检测结果。
在执行上电、运动或其他控制操作之前,通常需要先确认伺服当前所处的状态。程序通过 `get_servo_state` 获取状态码,再根据状态码判断机器人是未上电、已经就绪、正在运行,还是处于报警状态。
伺服状态码的含义如下:
- 状态 0:伺服未上电
- 状态 1:伺服就绪
- 状态 2:伺服报警
- 状态 3:伺服运行中
::: warning ⚠️ 使用注意
本 Demo 只查询和显示伺服状态,不会修改伺服状态,也不会向机器人发送运动指令。运行前仍需确认:
- **连接配置:** 控制器 IP 地址和 SDK 服务端口与实际配置一致
- **查询结果:** 只有 `get_servo_state` 返回 `SUCCESS` 时,才能使用查询得到的状态值
- **报警处理:** 状态为 2 时应停止后续控制操作,并检查控制器报警信息
- **未知状态:** SDK 返回未定义状态码时,应按异常处理,不能继续下发控制命令
- **现场安全:** 即使本 Demo 不控制机器人,也应确保急停和安全回路处于正常状态
伺服状态只能反映当前查询时刻的结果。实际项目在执行关键控制操作前,应根据业务需要重新查询状态,避免使用已经过期的状态值。
:::
## 1、配置控制器连接和查询参数
首先配置控制器 IP 地址、SDK 服务端口、查询失败后的重试间隔,以及允许的最大连续查询失败次数。
```cpp
/**
* @name 用户必须根据运行环境修改
* 以下配置决定 SDK 能否连接到实际控制器,运行前必须核对。
* @{
*/
const std::string robot_ip = "192.168.3.243"; ///< 用户实际控制器的 IP 地址。
const std::string robot_port = "6001"; ///< 用户实际控制器的 SDK 服务端口,必须与控制器配置一致。
/** @} */
/**
* @name 建议客户修改
* 以下配置应结合控制器通信质量和允许的查询响应时间进行调整。
* @{
*/
constexpr int query_retry_interval_ms = 500; ///< 查询失败后的重试间隔,单位为毫秒。
/** @} */
/**
* @name 可修改也可保留默认值
* 默认值与本 Demo 的失败提示一致,一般情况下可直接保留。
* @{
*/
constexpr int max_query_failures = 3; ///< 单次状态查询允许的最大连续失败次数。
/** @} */
```
`query_retry_interval_ms` 用于控制两次查询之间的等待时间。如果间隔过短,可能增加控制器的通信负载;如果间隔过长,则会延长状态检测的总响应时间。
`max_query_failures` 用于限制单次检测允许尝试的最大次数,避免控制器持续无响应时程序无限重试。
## 2、封装带重试的伺服状态查询函数
网络短暂波动或控制器临时繁忙时,单次调用 `get_servo_state` 可能失败。为了避免一次查询失败就直接结束检测,程序封装了 `get_servo_state_with_retry` 函数,在配置的次数范围内重复查询。
```cpp
/**
* @brief 查询当前伺服状态,并在查询失败时按配置进行重试。
* @param[in] fd 控制器连接句柄。
* @param[out] state 查询成功时保存当前伺服状态码。
* @retval true 在允许的重试次数内成功获取伺服状态。
* @retval false 所有查询均失败,此时调用方不应使用 @p state。
*/
bool get_servo_state_with_retry(SOCKETFD fd, int& state)
{
// 按固定次数处理临时网络波动,避免单次查询失败就结束检测。
for (int attempt = 1; attempt <= max_query_failures; ++attempt)
{
// SDK 将当前伺服状态写入 state,并返回本次调用结果。
const Result result = get_servo_state(fd, state);
if (result == SUCCESS)
{
return true; // 查询成功,此时 state 已保存当前状态值。
}
std::cerr << "获取伺服状态失败,第 "
<< attempt << "/" << max_query_failures
<< " 次,错误码:" << static_cast(result) << std::endl;
if (attempt < max_query_failures)
{
std::this_thread::sleep_for(
std::chrono::milliseconds(query_retry_interval_ms));
}
}
// 所有查询均失败,调用方不应再依据 state 执行后续判断。
std::cerr << "连续3次获取伺服状态失败" << std::endl;
return false;
}
```
函数的执行过程如下:
1. 从第 1 次查询开始调用 `get_servo_state`。
2. 如果接口返回 `SUCCESS`,立即返回 `true`,此时 `state` 保存有效状态码。
3. 如果查询失败,输出当前失败次数和 SDK 错误码。
4. 未达到最大次数时,等待 `query_retry_interval_ms` 毫秒后再次查询。
5. 所有查询均失败时返回 `false`,调用方不能继续解释 `state`。
## 3、封装伺服状态展示与判断函数
成功取得状态码后,还需要将数值状态转换为便于理解的文字,并判断该状态是否允许程序继续执行后续控制操作。
单独封装 `show_servo_state`,是为了将“从控制器读取状态”和“解释状态含义”分离。查询函数只负责保证数据有效,展示函数统一维护状态码与文字说明的对应关系,并通过布尔返回值告知主程序当前状态是否正常。以后增加新的状态码或修改异常处理方式时,只需要调整该函数。
```cpp
/**
* @brief 输出 SDK 状态码对应的伺服状态,并判断是否允许继续后续控制操作。
* @param[in] state SDK 返回的伺服状态码。
* @retval true 伺服处于未上电、就绪或运行中状态。
* @retval false 伺服处于报警状态,或 SDK 返回未知状态码。
*/
bool show_servo_state(int state)
{
switch (state)
{
case 0:
std::cout << "伺服状态:未上电" << std::endl;
return true;
case 1:
std::cout << "伺服状态:就绪" << std::endl;
return true;
case 2:
std::cerr << "伺服状态:报警" << std::endl;
std::cerr << "处理:停止后续控制操作,请检查控制器报警信息"
<< std::endl;
return false; // 报警未排除时停止后续控制,避免继续下发命令。
case 3:
std::cout << "伺服状态:运行中" << std::endl;
return true;
default:
// 未定义的状态码同样按异常处理,避免根据未知状态作出错误判断。
std::cerr << "伺服返回未知状态:" << state << std::endl;
std::cerr << "处理:停止后续控制操作" << std::endl;
return false;
}
}
```
各状态的处理逻辑如下:
1. 状态 0:输出“未上电”,状态值有效,函数返回 `true`。
2. 状态 1:输出“就绪”,状态值有效,函数返回 `true`。
3. 状态 2:输出“报警”和处理提示,函数返回 `false`,阻止后续控制操作。
4. 状态 3:输出“运行中”,状态值有效,函数返回 `true`。
5. 未知状态:无法确认机器人状态,按异常处理并返回 `false`。
这里返回 `true` 表示状态码已被识别且不属于报警或未知状态,并不表示伺服一定已经上电。例如状态 0 虽然返回 `true`,但它仍表示伺服当前处于未上电状态。
## 4、主程序连接控制器并检测伺服状态
主函数负责启用 UTF-8 控制台输出、连接控制器、获取伺服状态,并根据检测结果设置程序退出码。
只有 `get_servo_state_with_retry` 返回成功时,主函数才会调用 `show_servo_state`。这样可以避免查询失败后继续使用无效或过期的状态值。
```cpp
#include
#include
#include
#include
#include "../demo_utils.h"
/**
* @brief Demo 程序入口:连接控制器、查询伺服状态并输出检测结果。
* @return 状态正常时返回 0;查询失败、报警或未知状态时返回 1。
*/
int main()
{
demo::enable_console_utf8(); // 启用 UTF-8 控制台输出。
SOCKETFD fd = connect_robot(robot_ip, robot_port);
if (fd <= 0)
{
std::cout << "控制器连接失败" << std::endl;
return 0;
}
std::cout << "控制器连接成功" << std::endl;
int state = 0;
bool statusNormal = false; // 记录状态是否正常,用于确定程序最终退出码。
if (get_servo_state_with_retry(fd, state))
{
// 仅在查询成功时解释状态,避免使用无效或过期的状态值。
statusNormal = show_servo_state(state);
}
if (statusNormal) // 正常状态返回 0;查询失败、报警或未知状态返回非 0。
return 0;
else
return 1;
}
```
---
# 4. 直接运动指令
> 原文:https://open.inexbot.com/zh/04.%E4%B8%8A%E4%BD%8D%E6%9C%BA/01.C++/03.%E7%A4%BA%E4%BE%8B/02.%E5%9F%BA%E7%A1%80%E5%BA%94%E7%94%A8/04.%E7%9B%B4%E6%8E%A5%E8%BF%90%E5%8A%A8%E6%8C%87%E4%BB%A4.html
本期将介绍如何通过 SDK 依次发送 MOVJ 和 MOVL 直接运动指令,并在每段运动开始前执行目标点可达性预检,在指令发送后等待机器人运动完成。
MOVJ 和 MOVL 的运动方式不同:
- **MOVJ:** 以关节运动方式到达目标点,当前 Demo 使用关节坐标数据
- **MOVL:** 控制机器人末端沿直线路径到达目标点,当前 Demo 使用直角坐标数据
本 Demo 的执行顺序为:先检查目标点人工确认标志,再执行 MOVJ 可达性预检和运动;确认 MOVJ 完全结束后,重新执行 MOVL 可达性预检并发送 MOVL 指令,最后等待第二段运动完成。
::: danger ⚠️ 安全警告
以下代码会向控制器发送真实运动指令,并导致机器人实际运动。执行前必须确认:
- **人员安全:** 机器人工作空间及规划路径内无人员、工装干涉或障碍物
- **目标点来源:** MOVJ 和 MOVL 目标点均来自当前机器人实际示教,禁止直接使用其他机器人或其他现场的坐标
- **坐标配置:** 坐标类型、工具坐标系、用户坐标系和机器人构型与示教目标点一致
- **伺服与模式:** 伺服已经上电,控制器处于允许 SDK 下发运动指令的模式
- **速度限制:** 首次调试使用较低速度和较缓加减速度,并结合机器人负载逐步调整
- **急停可用:** 急停按钮处于可用状态,并在操作人员可触及范围内
- **路径安全:** 可达性预检只能判断运动学是否可达,不能代替碰撞检测和真实路径安全检查
建议先在仿真环境中验证目标点、坐标系和运动顺序,再在空载低速条件下进行真机测试。
:::
## 1、配置控制器、目标点和运动参数
首先配置控制器 IP 地址和 SDK 服务端口。程序只有连接到正确的控制器,才能对目标点执行预检并发送运动指令。
```cpp
/**
* @name 用户必须根据运行环境修改
* 以下配置决定 SDK 能否连接到实际控制器,运行前必须核对。
* @{
*/
const std::string robot_ip = "192.168.3.243"; ///< 实际控制器的 IP 地址。
const std::string robot_port = "6001"; ///< 实际控制器的 SDK 服务端口,必须与控制器配置一致。
/** @} */
```
MOVJ 和 MOVL 目标点必须来自当前机器人实际示教。源码默认使用全零数据作为占位值,并将 `target_positions_confirmed` 设置为 `false`,防止程序直接使用未经验证的目标点驱动机器人。
```cpp
/**
* @name 客户需根据本 Demo 修改
* 目标点必须来自当前机器人实际示教;确认点位和运行环境安全后,才可修改确认标志。
* @{
*/
const std::array movej_target = {
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0
}; ///< MOVJ 实际示教关节目标点,禁止使用未确认点位。
const std::array movel_target = {
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0
}; ///< MOVL 实际示教直角目标点,坐标格式必须匹配机器人型号。
constexpr bool target_positions_confirmed = false; ///< 两个目标点均完成示教和安全确认后才可设为 true。
/** @} */
```
运动速度、加速度和减速度应结合机器人型号、负载和现场条件设置。首次调试时应保留较低参数,再根据实际运行情况逐步调整。
```cpp
/**
* @name 建议客户修改
* 首次调试建议采用较低速度和较缓加减速度,并结合机器人负载及现场条件逐步调整。
* @{
*/
constexpr double movej_velocity = 10.0; ///< MOVJ 关节运动速度。
constexpr double movel_velocity = 20.0; ///< MOVL 直线运动速度。
constexpr double motion_acceleration = 20.0; ///< 运动加速度。
constexpr double motion_deceleration = 20.0; ///< 运动减速度。
/** @} */
```
等待参数用于限制单次运动的最长等待时间、控制运行状态查询频率,并处理短暂通信异常和极短运动状态难以捕获的问题。
```cpp
/**
* @name 可修改也可保留默认值
* 以下配置控制运动完成等待、状态轮询和通信容错,默认值适用于一般演示场景。
* @{
*/
constexpr int motion_timeout_seconds = 30; ///< 单次运动完成等待的总超时时间,单位为秒。
constexpr int poll_interval_ms = 100; ///< 机器人运行状态的轮询间隔,单位为毫秒。
constexpr int max_state_query_failures = 3; ///< 运行状态查询允许的最大连续失败次数。
constexpr int stopped_confirmation_count = 5; ///< 未观察到运行状态时,判定运动完成所需的连续停止状态次数。
/** @} */
```
## 2、封装运动指令构造函数
MOVJ 和 MOVL 都使用 `MoveCmd` 结构传递目标点、坐标类型、速度、加减速度、平滑参数和坐标系编号。两种指令的大部分公共参数相同,只有目标点、坐标类型和速度不同。
```cpp
/**
* @brief 使用统一的公共参数构造 MOVJ 或 MOVL 运动指令。
* @param[in] target 七维运动目标值。
* @param[in] coord 目标点坐标类型;当前 Demo 使用 0 表示关节坐标,1 表示直角坐标。
* @param[in] velocity 本次运动使用的速度。
* @return 填充完成的运动指令参数。
*/
MoveCmd make_move_command(
const std::array& target,
int coord,
double velocity)
{
MoveCmd command;
command.targetPosType = PosType::data;
std::copy(target.begin(), target.end(), command.targetPosValue.begin());
command.coord = coord;
command.velocity = velocity;
command.acc = motion_acceleration;
command.dec = motion_deceleration;
command.pl = 0; // 关闭与下一段轨迹的平滑过渡,便于准确判断本段结束。
command.toolNum = 0; // 必须与示教目标点使用的工具坐标系编号一致。
command.userNum = 0; // 必须与示教目标点使用的用户坐标系编号一致。
command.configuration = 0; // 必须按机器人型号填写目标点构型。
return command;
}
```
其中 `pl = 0` 表示本 Demo 不考虑与下一段运动之间的平滑过渡。程序会等待当前运动完全结束后再发送下一条指令,因此能够明确区分 MOVJ 和 MOVL 两段运动。
`toolNum`、`userNum` 和 `configuration` 不能仅因为示例值为 0 就直接保留。实际使用时,必须与目标点示教时采用的工具坐标系、用户坐标系和机器人构型一致。
## 3、封装可达性位置数据转换函数
运动指令使用 `MoveCmd` 保存目标数据,而 `get_pos_reachable` 接口要求传入长度为 14 的位置容器。程序需要将运动指令转换为可达性接口规定的数据格式。
```cpp
/**
* @brief 将运动指令转换为可达性接口要求的 14 位位置数据。
* @param[in] command 待执行的运动指令参数。
* @return 用于可达性预检的位置数据。
*
* @details
* 转换结果与实际下发指令使用同一目标数据,避免预检点位与真实运动点位不一致。
*/
std::vector make_reachability_position(const MoveCmd& command)
{
// 前 7 位描述坐标系、角度制和构型,后 7 位保存实际目标坐标。
std::vector position(14, 0.0);
position[0] = static_cast(command.coord);
position[1] = 0.0; // 当前目标使用角度制;使用弧度制时必须与实际数据保持一致。
position[2] = static_cast(command.configuration);
position[3] = static_cast(command.toolNum);
position[4] = static_cast(command.userNum);
std::copy_n(command.targetPosValue.begin(), 7, position.begin() + 7);
return position;
}
```
转换后的 14 位位置数据含义如下:
- 第 0 位:目标点坐标类型
- 第 1 位:角度制或弧度制标志,当前 Demo 使用角度制
- 第 2 位:机器人构型
- 第 3 位:工具坐标系编号
- 第 4 位:用户坐标系编号
- 第 5~6 位:备用字段,保持为 0
- 第 7~13 位:七维目标位置数据
## 4、封装目标点可达性预检函数
在向机器人发送真实运动指令之前,程序先调用 `get_pos_reachable` 检查目标点在运动学上是否可达。只有 SDK 调用成功且接口返回目标点可达时,才允许继续发送运动指令。
单独封装 `precheck_reachability`,是为了统一 MOVJ 和 MOVL 的预检流程、错误处理和结果输出。主程序不需要重复编写位置转换、SDK 返回值判断和可达结果判断,也能保证预检失败时立即取消对应运动。
```cpp
/**
* @brief 在发送真实运动指令前检查目标点是否可达。
* @param[in] fd 控制器连接句柄。
* @param[in] command 待预检的运动指令参数。
* @param[in] moveType 运动类型名称,用于调用接口和输出提示。
* @retval true SDK 调用成功且目标点可达。
* @retval false SDK 调用失败或目标点不可达。
*/
bool precheck_reachability(
SOCKETFD fd,
const MoveCmd& command,
const std::string& moveType)
{
bool reachable = false;
const Result result = get_pos_reachable(
fd,
make_reachability_position(command),
moveType,
reachable);
if (result != SUCCESS)
{
std::cerr << moveType << " 可达性预检调用失败,错误码:"
<< static_cast(result) << std::endl;
return false;
}
if (!reachable)
{
std::cerr << moveType << " 目标点不可达,已取消运动指令" << std::endl;
return false;
}
std::cout << moveType << " 目标点可达性预检通过" << std::endl;
return true;
}
```
需要特别注意,可达性预检通过只代表控制器认为目标点在运动学上可达,并不代表运动路径一定不会发生碰撞。工具、工件、外围设备、机器人本体和奇异位形等风险仍需通过仿真和现场安全验证进行确认。
## 5、封装等待运动完成函数
`robot_movej` 或 `robot_movel` 返回 `SUCCESS`,只表示运动指令已经成功发送,不代表机器人已经到达目标点。程序需要持续调用 `get_robot_running_state` 查询运行状态,直到确认本段运动结束。
运行状态的含义如下:
- 状态 0:机器人停止
- 状态 1:机器人暂停
- 状态 2:机器人正在运行
```cpp
/**
* @brief 等待当前运动完成,并统一处理暂停、查询失败、未知状态和超时。
* @param[in] fd 控制器连接句柄。
* @param[in] timeout 本次等待允许占用的最长时间。
* @retval true 已确认机器人运动完成。
* @retval false 状态查询连续失败、返回未知状态或等待超时。
*/
bool wait_for_motion_complete(
SOCKETFD fd,
std::chrono::seconds timeout = std::chrono::seconds(motion_timeout_seconds))
{
using clock = std::chrono::steady_clock;
const auto deadline = clock::now() + timeout; // 使用绝对截止时间限制整个等待过程。
bool runningObserved = false; // 是否观察到运行状态 2。
bool pauseReported = false; // 暂停期间只提示一次。
int stoppedCount = 0; // 连续停止状态的确认次数。
int consecutiveFailures = 0; // 运行状态连续查询失败次数。
while (clock::now() < deadline)
{
int runningState = 0;
const Result result = get_robot_running_state(fd, runningState);
if (result != SUCCESS)
{
++consecutiveFailures;
std::cerr << "获取机器人运行状态失败,第 "
<< consecutiveFailures << "/" << max_state_query_failures
<< " 次,错误码:" << static_cast(result) << std::endl;
if (consecutiveFailures >= max_state_query_failures)
{
std::cerr << "连续获取机器人运行状态失败,停止等待" << std::endl;
return false;
}
std::this_thread::sleep_for(
std::chrono::milliseconds(poll_interval_ms));
continue;
}
consecutiveFailures = 0; // 查询成功后清除连续失败计数。
switch (runningState)
{
case 2:
// 已确认运动开始,后续读取到停止状态即可判定本段运动结束。
runningObserved = true;
pauseReported = false;
stoppedCount = 0;
break;
case 1:
// 暂停不等于完成,继续等待恢复;总等待时间仍受 timeout 限制。
stoppedCount = 0;
if (!pauseReported)
{
std::cout << "机器人运动已暂停,继续等待恢复" << std::endl;
pauseReported = true;
}
break;
case 0:
// 已观察到运行时,状态 0 表示结束;否则连续确认以兼容极短运动。
++stoppedCount;
if (runningObserved || stoppedCount >= stopped_confirmation_count)
{
std::cout << "机器人运动完成" << std::endl;
return true;
}
break;
default:
std::cerr << "机器人返回未知运行状态:" << runningState << std::endl;
return false;
}
std::this_thread::sleep_for(
std::chrono::milliseconds(poll_interval_ms));
}
std::cerr << "等待机器人运动完成超时" << std::endl;
return false;
}
```
`runningObserved` 用于记录程序是否实际观察到机器人进入运行状态。对于正常运动,一旦先观察到状态 2,之后读取到状态 0 就可以确认运动结束。
某些运动时间很短,程序可能因为轮询间隔而没有捕获到状态 2。此时通过 `stopped_confirmation_count` 连续确认多次状态 0,避免程序一直等待,也降低单次瞬时状态造成误判的概率。
## 6、主程序依次执行 MOVJ 和 MOVL
主函数首先检查 `target_positions_confirmed`。确认标志为 `false` 时,程序会在连接控制器和发送运动指令之前直接退出,避免全零占位点或未经验证的目标点驱动机器人。
连接成功后,程序分别为 MOVJ 和 MOVL 构造运动指令。每条指令都必须先通过可达性预检,再检查运动接口的 SDK 返回值,并等待当前运动完全结束。
```cpp
#include
#include
#include
#include
#include
#include
#include
#include
#include "../demo_utils.h"
/**
* @brief Demo 程序入口:依次执行 MOVJ、MOVL,并等待每段运动完成。
* @return 全部预检、指令发送和运动等待均成功时返回 0,否则返回 1。
*/
int main()
{
demo::enable_console_utf8();
// 在连接和下发指令前检查人工确认标志,避免使用占位点或未经验证的点位。
if (!target_positions_confirmed)
{
std::cerr << "目标点尚未确认:请填写实际示教点,并将 "
<< "target_positions_confirmed 设置为 true" << std::endl;
return 1;
}
SOCKETFD fd = connect_robot(robot_ip, robot_port);
if (fd <= 0)
{
std::cerr << "控制器连接失败" << std::endl;
return 1;
}
std::cout << "控制器连接成功" << std::endl;
// MOVJ 使用关节坐标(coord = 0),仅在可达性预检通过后发送指令。
MoveCmd movejCommand = make_move_command(movej_target, 0, movej_velocity);
if (!precheck_reachability(fd, movejCommand, "MOVJ"))
{
return 1;
}
Result result = robot_movej(fd, movejCommand);
if (result != SUCCESS)
{
std::cerr << "发送 MOVJ 指令失败,错误码:"
<< static_cast(result) << std::endl;
return 1;
}
std::cout << "MOVJ 指令发送成功,等待运动完成" << std::endl;
// 等待 MOVJ 完全结束后再执行 MOVL,避免两条指令同时占用机器人。
if (!wait_for_motion_complete(fd))
{
return 1;
}
// MOVL 使用直角坐标(coord = 1),并基于当前实际位置重新执行预检。
MoveCmd movelCommand = make_move_command(movel_target, 1, movel_velocity);
if (!precheck_reachability(fd, movelCommand, "MOVL"))
{
return 1;
}
result = robot_movel(fd, movelCommand);
if (result != SUCCESS)
{
std::cerr << "发送 MOVL 指令失败,错误码:"
<< static_cast(result) << std::endl;
return 1;
}
std::cout << "MOVL 指令发送成功,等待运动完成" << std::endl;
// 将最后一次等待结果作为退出状态,便于外部脚本判断本次 Demo 是否成功。
return wait_for_motion_complete(fd) ? 0 : 1;
}
```
---
# 5. 断开连接
> 原文:https://open.inexbot.com/zh/04.%E4%B8%8A%E4%BD%8D%E6%9C%BA/01.C++/03.%E7%A4%BA%E4%BE%8B/02.%E5%9F%BA%E7%A1%80%E5%BA%94%E7%94%A8/05.%E6%96%AD%E5%BC%80%E8%BF%9E%E6%8E%A5.html
本期将介绍上位机如何主动断开与控制器建立的 SDK 连接,并分别演示正常退出和业务异常退出两种场景下的连接清理方式。
程序通过 `connect_robot` 成功连接控制器后,会获得一个连接句柄 `fd`。当业务执行结束或发生可捕获异常时,应在程序退出前显式调用 `disconnect_robot`,释放本次连接使用的通信资源。
本 Demo 支持以下两种运行方式:
```text
demo.exe 正常退出,在正常业务路径中显式断开连接
demo.exe --abnormal 模拟业务异常,在异常处理路径中显式断开连接
```
`--abnormal` 只用于模拟上层业务抛出异常,不表示 SDK 连接本身发生异常。两种场景都会真实连接控制器,也都会真实调用 `disconnect_robot`。
::: warning ⚠️ 使用注意
本 Demo 只演示建立和断开 SDK 连接,不会发送机器人运动指令。使用时仍需注意:
- **连接配置:** 控制器 IP 地址和 SDK 服务端口必须与实际配置一致
- **显式清理:** 正常路径和可捕获异常路径都应在退出前调用断开接口
- **返回结果:** 不能因为程序即将退出就忽略 `disconnect_robot` 的返回值
- **运动安全:** 断开 SDK 连接不等于急停、伺服下电或运动停止,不能将其作为安全停止手段
- **不可恢复场景:** 断电、进程被强制结束或操作系统异常终止时,程序无法继续执行,因此无法保证调用 SDK 断开接口
实际项目应将连接清理纳入统一的资源管理流程,同时通过急停、安全回路和控制器自身机制保障机器人运动安全。
:::
## 1、配置控制器连接参数
首先配置控制器 IP 地址和 SDK 服务端口。程序会使用这两个参数建立真实连接,因此运行前必须确认目标控制器无误。
```cpp
/**
* @name 用户必须根据运行环境修改
* 以下配置决定 SDK 能否连接到实际控制器,运行前必须核对。
* @{
*/
const std::string robot_ip = "192.168.3.243"; ///< 用户实际控制器的 IP 地址。
const std::string robot_port = "6001"; ///< 用户实际控制器的 SDK 服务端口,必须与控制器配置一致。
/** @} */
```
`connect_robot` 返回的是连接句柄,而不是普通的 `Result`。因此,程序通过判断句柄是否大于 0 来确认连接是否建立成功。只有获得有效句柄后,才能调用 `disconnect_robot`。
## 2、封装控制器连接断开函数
正常业务结束、标准异常和未知异常等退出路径都需要执行相同的断开操作。程序将断开逻辑封装为 `disconnect_controller`,统一调用 SDK 接口、检查返回值并输出当前退出路径的处理结果。
```cpp
/**
* @brief 主动断开控制器连接,并统一检查 SDK 返回结果。
* @param[in] fd 控制器连接句柄。
* @param[in] exitPath 当前退出路径的说明文字,用于输出处理结果。
* @retval true 控制器连接已成功断开。
* @retval false SDK 返回失败,或断开过程中捕获到异常。
*
* @details
* `noexcept` 保证清理函数不会继续传播异常,使正常和异常退出路径都能稳定结束。
*/
bool disconnect_controller(
SOCKETFD fd,
const std::string& exitPath) noexcept
{
try
{
// 真实调用 SDK 断开接口并检查返回值,不能因程序即将退出就假定连接已关闭。
const Result result = disconnect_robot(fd);
if (result != SUCCESS)
{
std::cerr << exitPath << ":关闭控制器连接失败,错误码:"
<< static_cast(result) << std::endl;
return false;
}
std::cout << exitPath << ":控制器连接已关闭" << std::endl;
return true;
}
catch (const std::exception& error)
{
// SDK 若抛出标准异常,在此转换为 false,由调用方统一返回失败退出码。
std::cerr << exitPath << ":disconnect_robot 抛出异常:"
<< error.what() << std::endl;
return false;
}
catch (...)
{
// 未知异常也在此截断,防止断开操作破坏原有异常处理流程。
std::cerr << exitPath << ":disconnect_robot 抛出未知异常" << std::endl;
return false;
}
}
```
函数使用 `exitPath` 区分“正常退出路径”和“异常退出路径”,便于从日志中判断本次断开操作发生在哪个阶段。
## 3、定义退出场景并封装参数解析函数
为了明确区分正常退出和异常退出,程序使用 `ExitScenario` 枚举表示本次需要运行的演示场景。
```cpp
/**
* @brief Demo 支持的退出场景。
*/
enum class ExitScenario
{
normal, ///< 正常业务路径主动断开连接。
abnormal ///< 模拟业务异常后主动断开连接。
};
```
使用枚举而不是普通整数或字符串,可以让场景含义更加清晰,并避免在后续业务函数中重复比较命令行文本。
命令行参数由 `parse_scenario` 统一解析:不传参数时选择正常场景,只传入 `--abnormal` 时选择异常场景,其他参数均视为无效。
单独封装参数解析函数,是为了在连接控制器之前完成输入校验。参数无效时程序可以直接退出,避免先建立真实控制器连接,再发现本次运行方式无法识别。
```cpp
/**
* @brief 解析命令行参数并确定本次 Demo 的退出场景。
* @param[in] argc 命令行参数数量。
* @param[in] argv 命令行参数数组。
* @param[out] scenario 解析成功后保存选择的退出场景。
* @retval true 参数有效,已确定退出场景。
* @retval false 参数无效,不应继续连接控制器。
*/
bool parse_scenario(
int argc,
char* argv[],
ExitScenario& scenario)
{
if (argc == 1)
{
// 未传入参数时,默认演示正常主动断开流程。
scenario = ExitScenario::normal;
return true;
}
if (argc == 2 && std::string(argv[1]) == "--abnormal")
{
// --abnormal 只选择异常测试路径,不表示 SDK 连接本身已经异常。
scenario = ExitScenario::abnormal;
return true;
}
std::cerr << "参数错误。用法:demo.exe [--abnormal]" << std::endl;
return false;
}
```
参数解析结果如下:
- `demo.exe`:选择 `ExitScenario::normal`
- `demo.exe --abnormal`:选择 `ExitScenario::abnormal`
- 其他参数:输出正确用法并返回失败,不连接控制器
## 4、封装正常和异常断开演示函数
`run_disconnect_demo` 根据选择的场景执行正常业务路径或模拟异常路径。
```cpp
/**
* @brief 执行选定的正常或异常断开演示场景。
* @param[in] fd 控制器连接句柄。
* @param[in] scenario 本次需要执行的退出场景。
* @return 正常路径成功断开连接时返回 0;正常路径断开失败时返回 1。
* @throws std::runtime_error 当选择异常场景时,抛出模拟业务异常。
*/
int run_disconnect_demo(SOCKETFD fd, ExitScenario scenario)
{
if (scenario == ExitScenario::abnormal)
{
// 故意抛出可捕获的业务异常,用于验证 catch 路径仍会关闭真实连接。
std::cout << "开始模拟业务异常" << std::endl;
throw std::runtime_error("模拟异常退出");
}
std::cout << "开始执行正常断开流程" << std::endl;
// 正常路径在返回前主动关闭连接,断开失败时使用非零退出码报告结果。
if (!disconnect_controller(fd, "正常退出路径"))
{
return 1;
}
return 0;
}
```
正常场景会直接调用 `disconnect_controller`,断开成功时返回 0,断开失败时返回 1。
异常场景不会在此函数内断开连接,而是故意抛出 `std::runtime_error`。异常会返回 `main` 中的 `catch` 路径,由异常处理代码统一调用 `disconnect_controller`,用于验证异常退出时的真实连接清理过程。
## 5、主程序连接控制器并处理退出路径
主函数首先解析命令行参数,参数有效时才连接控制器。连接成功后的场景执行代码全部放入 `try` 中,保证可捕获的标准异常和未知异常都能进入对应的清理路径。
```cpp
#include
#include
#include
#include
#include
#include "../demo_utils.h"
/**
* @brief Demo 程序入口:解析退出场景、连接控制器并验证显式断开流程。
* @param[in] argc 命令行参数数量。
* @param[in] argv 命令行参数数组。
* @return 正常退出且断开成功时返回 0;参数无效、连接失败、业务异常或断开失败时返回 1。
*/
int main(int argc, char* argv[])
{
demo::enable_console_utf8();
ExitScenario scenario = ExitScenario::normal;
if (!parse_scenario(argc, argv, scenario))
{
return 1;
}
// connect_robot 返回连接句柄而非 Result,因此通过句柄是否有效判断连接结果。
SOCKETFD fd = connect_robot(robot_ip, robot_port);
if (fd <= 0)
{
std::cerr << "控制器连接失败" << std::endl;
return 1;
}
std::cout << "控制器连接成功" << std::endl;
// 连接成功后的场景代码全部放入 try,确保可捕获异常都进入显式断开路径。
try
{
return run_disconnect_demo(fd, scenario);
}
catch (const std::exception& error)
{
std::cerr << "捕获业务异常:" << error.what() << std::endl;
// 异常路径在返回前显式关闭真实控制器连接。
if (!disconnect_controller(fd, "异常退出路径"))
{
return 1;
}
// 连接关闭成功不改变业务发生异常的事实,因此保持非零退出码。
return 1;
}
catch (...)
{
// 未知异常执行相同清理策略,避免新增异常类型时遗漏断开连接。
std::cerr << "捕获未知异常" << std::endl;
if (!disconnect_controller(fd, "异常退出路径"))
{
return 1;
}
return 1;
}
}
```
## 6、正常退出流程
不传入命令行参数时,程序执行正常主动断开流程:
1. `parse_scenario` 将场景设置为 `ExitScenario::normal`。
2. `connect_robot` 建立与控制器的真实连接。
3. `run_disconnect_demo` 进入正常业务路径。
4. `disconnect_controller` 调用 `disconnect_robot` 主动断开连接。
5. 断开成功时程序返回 0;断开失败时返回 1。
正常运行命令如下:
```text
demo.exe
```
正常路径预期输出类似:
```text
控制器连接成功
开始执行正常断开流程
正常退出路径:控制器连接已关闭
```
## 7、异常退出流程
传入 `--abnormal` 时,程序模拟业务执行过程中发生可捕获异常:
1. `parse_scenario` 将场景设置为 `ExitScenario::abnormal`。
2. 程序与控制器建立真实连接。
3. `run_disconnect_demo` 主动抛出 `std::runtime_error`。
4. `main` 中的标准异常分支捕获该异常。
5. 异常处理路径调用 `disconnect_controller` 断开真实连接。
6. 即使连接成功关闭,程序仍返回 1,以表示本次业务执行发生异常。
异常测试命令如下:
```text
demo.exe --abnormal
```
异常路径预期输出类似:
```text
控制器连接成功
开始模拟业务异常
捕获业务异常:模拟异常退出
异常退出路径:控制器连接已关闭
```
异常场景的非零退出码用于保留“业务执行失败”的事实,不能因为资源已经成功清理就将本次运行报告为成功。
需要注意,本 Demo 只能处理程序仍有机会执行代码的正常退出和可捕获异常。对于突然断电、进程被操作系统强制结束或不可恢复的进程崩溃,应用程序无法继续运行清理代码,因此不能依赖本示例保证这些情况下仍会调用 SDK 断开接口。
---
# 6. 运动队列的曲线运动
> 原文:https://open.inexbot.com/zh/04.%E4%B8%8A%E4%BD%8D%E6%9C%BA/01.C++/03.%E7%A4%BA%E4%BE%8B/02.%E5%9F%BA%E7%A1%80%E5%BA%94%E7%94%A8/06.%E8%BF%90%E5%8A%A8%E9%98%9F%E5%88%97%E7%9A%84%E6%9B%B2%E7%BA%BF%E8%BF%90%E5%8A%A8.html
## 1、封装设置伺服就绪函数
```cpp
/*
* 伺服就绪函数
*/
void servo_ready(int fd)
{
int state = 0;
get_servo_state(fd, state);
switch (state)
{
case 0:
set_servo_state(fd, 1); // 将伺服切换为就绪
break;
case 2:
clear_error(fd);
set_servo_state(fd, 1);
break;
}
get_servo_state(fd, state);
std::cout << "伺服状态: " << state << std::endl;
}
```
## 2、封装等待运动结束的函数
```cpp
/*
* 循环阻塞运动结束函数
*/
void wait_for_running_over(int fd)
{
// 等待运动完成
int running_state = 0;
get_robot_running_state(fd, running_state); // 查询机器人是否在运动 2-正在运动
while (running_state == 2)
{
std::this_thread::sleep_for(std::chrono::milliseconds(500)); // 阻塞500ms
get_robot_running_state(fd, running_state); // 再次查询
}
}
```
## 3.moves运动示例
```cpp
#include
#include