机器人SDK通信架构设计----Half-Sync/Half-Asyn
·
机器人SDK通信架构设计:从TCP服务端到客户端
前言
在机器人SDK开发中,通信架构的设计直接影响系统的实时性、稳定性和可维护性。本文将分享一套经过实践验证的SDK通信架构设计方案,涵盖服务端(机器人控制器侧)与客户端(上位机侧)的完整设计。
整个系统的核心目标:
- 服务端:运行在机器人控制器上,通过TCP/UDP接收指令并调用底层SDK
- 客户端:运行在上位机,提供阻塞式的同步调用接口,隐藏网络通信细节
整体架构图
┌─────────────────────────────────────────────────────────────────────────┐
│ 上位机 (Client) │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 用户线程1 用户线程2 recv线程 │ │
│ │ moveJ() getState() 持续收包分发 │ │
│ │ send + wait send + wait id → promise │ │
│ └─────────────────────────────────────────────────────────────────┘ │
└───────────────────────────────┬─────────────────────────────────────────┘
│ TCP/UDP
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ 机器人控制器 (Server) │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 异步层 (epoll主线程) │ │
│ │ ┌──────────────────────────────────────────────────────────┐ │ │
│ │ │ 非阻塞收包 → 命令分类 │ │ │
│ │ │ ├── getState/estop/disable → 主线程立即处理返回 │ │ │
│ │ │ ├── moveJ/moveL/enable... → 入队 │ │ │
│ │ │ └── servoJ/servoDualJ → UDP直接执行 │ │ │
│ │ └──────────────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 同步层 (Worker线程) │ │
│ │ ┌──────────────────────────────────────────────────────────┐ │ │
│ │ │ 串行队列 → 取任务 → 调用SDK → 写回响应 │ │ │
│ │ └──────────────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 底层 SDK │ │
│ │ moveJ() / moveL() / servoJ() / getState() │ │
│ └─────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
服务端架构:SDK调用与网络解耦
为什么需要分层
底层SDK的运动控制接口是阻塞的(moveJ要等运动完成才返回),但网络收包不能阻塞。分层设计解决了这个矛盾:
网络层 → 快速收包(非阻塞)→ 队列缓冲 → SDK层 → 阻塞执行
命令分类
| 类型 | 命令 | 处理方式 |
|---|---|---|
| 快速命令 | getState, estop, disable | 主线程立即返回 |
| 阻塞命令 | moveJ, moveL, moveW, moveJDual, moveLDual, enable, setSpeed, setTool, setMode | Worker串行执行 |
| UDP命令 | servoJ, servoDualJ, servoWaist | 直接执行无需响应 |
Worker线程设计
为什么单Worker就够了?
├── servoJ走UDP → 本身并行
├── moveDualJ → SDK内部并行
├── 阻塞命令按顺序执行即可
└── 多Worker → 增加复杂度,收益几乎为零
客户端架构:把异步网络变成同步调用
核心设计目标
客户端的目标很明确:让调用者像调用本地函数一样调用远程SDK。
本地调用:moveJ(targets) → 等待返回 → 继续
远程调用:moveJ(targets) → 等待返回 → 继续(网络细节全部隐藏)
发收分离模型
┌─────────────────────────────────────────────────────────────────┐
│ 调用线程A 调用线程B recv线程 │
│ │ │ │ │
│ moveJ(id=1) getState(id=2) │ │
│ send(id=1) send(id=2) │ │
│ wait(id=1) wait(id=2) │ │
│ │ │ 持续recv │
│ │ │ 收到响应id=1 → 唤醒A │
│ 返回结果 │ 收到响应id=2 → 唤醒B │
│ │ 返回结果 │
└─────────────────────────────────────────────────────────────────┘
核心数据结构
class RobotClient {
private:
// 待响应的请求表
std::map<int, std::shared_ptr<std::promise<json>>> pending_;
std::atomic<int> next_id_{1};
public:
json sendCommand(const json& req) {
int id = ++next_id_;
auto promise = std::make_shared<std::promise<json>>();
pending_[id] = promise;
send(req_with_id); // 发送带id的请求
auto future = promise->get_future();
if (future.wait_for(60s) != ready) {
pending_.erase(id);
return 超时错误;
}
return future.get();
}
void recvLoop() {
while (true) {
json resp = recv();
int id = resp["id"];
auto it = pending_.find(id);
if (it != pending_.end()) {
it->second->set_value(resp);
pending_.erase(it);
}
}
}
};
阻塞风格 vs 回调风格
| 对比维度 | 回调风格 | 阻塞风格 |
|---|---|---|
| 代码形态 | 每来一个包调一次函数 | 发→等→处理,顺序执行 |
| 业务逻辑 | 被切碎,分散在各回调 | 完整连贯,像本地调用 |
| 错误处理 | 难统一,易遗漏 | try-catch集中处理 |
| 可维护性 | 差 | 好 |
通信协议
TCP协议格式
每条消息:JSON + “/f/b”
请求示例:
{"id":2, "cmd":"moveJ", "params":{"arm":0, "targets":[10,20,30,0,0,0,0]}}/f/b
响应示例:
{"cmd":"moveJ","id":2,"result":0}/f/b
UDP协议格式
{"cmd":"servoJ", "params":{"arm":0, "targets":[10,20,30,0,0,0,0]}}/f/b
核心设计决策
| 层面 | 设计决策 |
|---|---|
| 服务端 | Half-Sync/Half-Async,epoll + Worker串行队列 |
| 客户端 | 发收分离,Promise/Future异步转同步 |
| 命令分类 | 快速命令主线程处理,阻塞命令入队,UDP直接执行 |
| 编码风格 | 阻塞式顺序调用,优于回调式 |
| 并发模型 | 单Worker,不盲目增加复杂度 |
总结
这套架构兼顾了实时性、稳定性和开发效率。核心思想是:把网络通信的复杂性封装在SDK内部,对外提供像本地调用一样简单的接口。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐



所有评论(0)