- 选型指南
- 概述
- 入门指南
- 机械臂参数
- 示教器使用
-
二次开发
-
API(C++)
- 快速开始
- 接口说明
- 7000端口接口说明
- 使用示例
- 返回值说明
-
API(Python)
- 快速开始
- 接口说明
- 7000端口接口说明
- 使用示例
- 返回值说明
- ROS2开发
-
API(C++)
- 相关下载
队列运动使用示例
1. 队列运动使用示例
本节为队列运动使用示例,完整源码见 examples/cpp/ex_queue_motion.cpp。该示例演示队列运动的完整工作流:开启队列模式、插入 MoveJ/MoveL 指令、发送队列到控制器、等待执行完成,以及暂停/恢复/停止保持上电等控制。完整代码如下:
C++
/**
* ex_queue_motion.cpp
* @brief 队列运动示例程序 —— 展示队列运动完整工作流
* @attention
* - 确保机械臂供电正常、网络通信正常
* - 可用命令行参数 -ip 指定控制器 IP(默认 192.168.1.13)
* - 队列运动模式下,所有指令先在本地缓存,调用 send_to_controller 后一次性下发执行
* - 演示三种队列运动:纯 MoveJ、纯 MoveL、MoveJ+MoveL 混合
* - 运行模式: 队列模式(在queue_motion_set_status(true)之后,即可进入队列模式,在此之前建议set_current_mode(sock, RobotMode::RUN)切换到运行模式)
* - 开始前需: connect_robot → 上电 → tl::queue_motion_set_status(true)
* - 退出前需: tl::queue_motion_set_status(false) → tl::set_current_mode(RobotMode::TEACH) → set_servo_poweroff → disconnect_robot
* @note 运行步骤
* 编译: cd build && cmake .. && make
* 运行: ./ex_queue_motion
*/
#include "tl_api.h"
#include
#include
#include
#include
#include
#include
#ifdef _WIN32
#include
#endif
// ============================================================
// 工具函数
// ============================================================
void print_result(const std::string& msg, int ret)
{
if (ret == tl::Result::SUCCESS) {
std::cout << " [成功] " << msg << std::endl;
} else {
std::cout << " [失败] " << msg << ",错误码: " << ret << std::endl;
}
}
void print_separator(const std::string& title)
{
std::cout << "\n========== " << title << " ==========" << std::endl;
}
/**
* @brief 构建 MoveCmd(关节坐标模式)
* @param joints 6 个关节角度值,单位 °(度),顺序 [J1, J2, J3, J4, J5, J6]
* @param vel 百分比 % (1,100],最大关节速度的百分比,实际受全局速度限制
* @param acc 百分比 % (1,100]
* @param dec 百分比 % (1,100]
* @param pl 平滑参数 [0,5]
* @note 实际运动速度 = 全局速度 × (vel / 100)
*/
tl::MoveCmd make_movej_cmd(const std::vector& joints,
double vel = 30, double acc = 50, double dec = 50, int pl = 0)
{
tl::MoveCmd cmd;
cmd.coord = tl::Coord::JOINT; // 关节坐标系
cmd.velocity = vel;
cmd.acc = acc;
cmd.dec = dec;
cmd.pl = pl;
cmd.targetPosValue.resize(14, 0.0);
for (size_t i = 0; i < joints.size() && i < 7; ++i) {
cmd.targetPosValue[i] = joints[i];
}
return cmd;
}
/**
* @brief 构建 MoveCmd(基坐标模式)
* @param pose 位姿 [X, Y, Z, Rx, Ry, Rz],位置单位 mm,姿态单位 °
* @param vel 百分比 % (1,1000],最大 TCP 线速度的百分比
* @param acc 百分比 % (1,100]
* @param dec 百分比 % (1,100]
* @param pl 平滑参数 [0,5]
* @note 实际运动速度 = 全局速度 × (vel / 100)
*/
tl::MoveCmd make_movel_cmd(const std::vector& pose,
double vel = 100, double acc = 50, double dec = 50, int pl = 0)
{
tl::MoveCmd cmd;
cmd.coord = tl::Coord::BASE; // 基坐标系,可切换到 tl::Coord::TOOL / tl::Coord::USER
cmd.velocity = vel;
cmd.acc = acc;
cmd.dec = dec;
cmd.pl = pl;
cmd.targetPosValue.resize(14, 0.0);
for (size_t i = 0; i < pose.size() && i < 7; ++i) {
cmd.targetPosValue[i] = pose[i];
}
return cmd;
}
// ============================================================
// 队列运动执行器
// ============================================================
/**
* @brief 执行一轮队列运动(通用执行器)
* @param sock 控制端口 socket
* @param name 演示名称
* @param fn_push 回调:向队列插入运动指令
* @note 该函数演示完整的队列运动流程:
* 1. queue_motion_set_status(true) 开启队列模式(会清空远端队列)
* 2. 通过 fn_push 回调插入运动指令
* 3. queue_motion_size 查询本地队列长度
* 4. queue_motion_send_to_controller(sock, 0, false) 发送全部指令到控制器
* 5. 轮询 get_robot_running_state 等待运动完成
* 6. queue_motion_set_status(false) 关闭队列模式
* @return true 成功 / false 失败
*/
bool run_queue_motion(tl::SOCKETFD sock, const std::string& name,
std::function fn_push)
{
print_separator("队列运动:" + name);
// 1. 打开队列运动模式(会清空远端队列)
tl::Result ret = tl::queue_motion_set_status(sock, true);
print_result("tl::queue_motion_set_status(true)", ret);
if (ret != tl::Result::SUCCESS) return false;
std::this_thread::sleep_for(std::chrono::milliseconds(200));
// 确认队列模式已开启
{
bool status = false;
tl::queue_motion_get_status(sock, status);
std::cout << " [信息] 队列模式状态: " << (status ? "已开启" : "已关闭") << std::endl;
}
// 2. 插入运动指令
std::cout << " [信息] 插入运动指令..." << std::endl;
if (!fn_push(sock)) {
std::cerr << "[ERROR] 插入运动指令失败" << std::endl;
tl::queue_motion_set_status(sock, false);
return false;
}
// 3. 查询本地队列长度
{
int size = 0;
ret = tl::queue_motion_size(sock, size);
if (ret == tl::Result::SUCCESS) {
std::cout << " [信息] 本地队列长度: " << size << " 条指令" << std::endl;
}
}
// 4. 发送队列到控制器并等待执行完成
std::cout << " [信息] 发送队列到控制器..." << std::endl;
ret = tl::queue_motion_send_to_controller(sock, 0, false);
print_result("send_to_controller", ret);
if (ret != tl::Result::SUCCESS) {
tl::queue_motion_set_status(sock, false);
return false;
}
std::cout << " [信息] 等待运动执行完成..." << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(50));
while (true) {
tl::RunState running_state;
ret = tl::get_robot_running_state(sock, running_state);
if (ret == tl::Result::SUCCESS && running_state == tl::RunState::STOP) break;
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
std::cout << " [信息] 队列运动执行完毕" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
// 5. 关闭队列运动模式
tl::queue_motion_set_status(sock, false);
std::this_thread::sleep_for(std::chrono::milliseconds(200));
return true;
}
// ============================================================
// 主函数
// ============================================================
int main(int argc, char* argv[])
{
#ifdef _WIN32
SetConsoleOutputCP(CP_UTF8);
#endif
// 解析命令行参数: ./ex_queue_motion [-ip 192.168.1.13],默认 192.168.1.13
std::string ip = "192.168.1.13";
for (int i = 1; i + 1 < argc; ++i) {
if (std::string(argv[i]) == "-ip" || std::string(argv[i]) == "--ip") {
ip = argv[i + 1];
break;
}
}
// ---- 获取 SDK 版本 ----
{
std::string version = tl::get_library_version();
std::cout << "[信息] SDK 版本: " << version << std::endl;
}
// ---- 连接控制器 ----
print_separator("连接控制器");
tl::SOCKETFD sock = tl::connect_robot(ip, "6001");
if (sock < 0) {
std::cerr << "[ERROR] 连接控制器失败" << std::endl;
return -1;
}
std::cout << "[信息] 连接成功 (socket = " << sock << ")" << std::endl;
// ---- 清错 ----
tl::clear_error(sock);
std::this_thread::sleep_for(std::chrono::milliseconds(300));
tl::set_servo_poweroff(sock);
std::this_thread::sleep_for(std::chrono::milliseconds(300));
tl::Result ret;
// ---- 检查模式并确保伺服运行 ----
{
// 先获取当前模式
tl::RobotMode current_mode;
tl::Result ret = tl::get_current_mode(sock, current_mode);
if (ret != tl::Result::SUCCESS) {
std::cerr << "[ERROR] 获取当前模式失败: " << ret << std::endl;
tl::disconnect_robot(sock);
return -1;
}
std::cout << " [信息] 当前模式: " << static_cast(current_mode) << " (RobotMode::TEACH/REMOTE/RUN)" << std::endl;
if (current_mode == tl::RobotMode::TEACH) {
// 示教模式 → 先确保伺服就绪上电,再切换为运行模式
tl::ServoState servo_state;
ret = tl::get_servo_state(sock, servo_state);
if (ret != tl::Result::SUCCESS) {
std::cerr << "[ERROR] 获取伺服状态失败: " << ret << std::endl;
tl::disconnect_robot(sock);
return -1;
}
std::cout << " [信息] 伺服状态: " << static_cast(servo_state) << " (ServoState::STOP/READY/ALARM/RUNNING)" << std::endl;
if (servo_state != tl::ServoState::RUNNING) {
// 伺服未运行 → 执行上电流程
std::cout << " [信息] 伺服未运行,执行上电流程..." << std::endl;
ret = tl::set_servo_state(sock, tl::ServoState::READY);
if (ret != tl::Result::SUCCESS && ret != tl::Result::OPERATION_NOT_ALLOWED) {
std::cerr << "[ERROR] 设置伺服就绪失败: " << ret << std::endl;
tl::disconnect_robot(sock);
return -1;
}
std::this_thread::sleep_for(std::chrono::milliseconds(500));
ret = tl::set_servo_poweron(sock);
if (ret != tl::Result::SUCCESS) {
std::cerr << "[ERROR] 上电失败: " << ret << std::endl;
tl::disconnect_robot(sock);
return -1;
}
std::cout << " [信息] 上电成功" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(2));
} else {
std::cout << " [信息] 伺服已在运行状态" << std::endl;
}
// 切换为运行模式
std::cout << " [信息] 切换为运行模式..." << std::endl;
ret = tl::set_current_mode(sock, tl::RobotMode::RUN);
if (ret != tl::Result::SUCCESS) {
std::cerr << "[ERROR] 切换运行模式失败: " << ret << std::endl;
tl::disconnect_robot(sock);
return -1;
}
std::cout << " [信息] 已切换为运行模式" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
} else if (current_mode == tl::RobotMode::RUN) {
// 已在运行模式,直接检查伺服状态
tl::ServoState servo_state;
ret = tl::get_servo_state(sock, servo_state);
if (ret != tl::Result::SUCCESS || servo_state != tl::ServoState::RUNNING) {
std::cerr << "[ERROR] 伺服未在运行状态 (servo_state=" << static_cast(servo_state) << ")" << std::endl;
tl::disconnect_robot(sock);
return -1;
}
std::cout << " [信息] 已在运行模式,伺服运行中 (servo_state=RUNNING)" << std::endl;
} else {
std::cerr << "[ERROR] 不支持的模式: " << static_cast(current_mode) << ",期望RobotMode::TEACH 或 RobotMode::RUN模式" << std::endl;
tl::disconnect_robot(sock);
return -1;
}
}
// 延时 2 秒,保证状态完全就绪
std::this_thread::sleep_for(std::chrono::seconds(2));
// ---- 设置全局速度 ----
// 控制器默认全局速度为 5%,设为 30% 提升运动效率
print_separator("设置全局速度");
ret = tl::set_speed(sock, 30);
print_result("tl::set_speed(30)", ret);
// ============================================================
// 演示 1: 纯 MoveJ 队列运动
// ============================================================
run_queue_motion(sock, "纯 MoveJ", [](tl::SOCKETFD sock) -> bool {
tl::Result r;
std::cout << " push_back_moveJ: J1=30°" << std::endl;
r = tl::queue_motion_push_back_moveJ(sock, make_movej_cmd({30, 0, 0, 0, 0, 0}, 30));
print_result("push_back_moveJ", r);
if (r != tl::Result::SUCCESS) return false;
std::cout << " push_back_moveJ: J1=30°, J2=20°" << std::endl;
r = tl::queue_motion_push_back_moveJ(sock, make_movej_cmd({30, 20, 0, 0, 0, 0}, 30));
print_result("push_back_moveJ", r);
if (r != tl::Result::SUCCESS) return false;
std::cout << " push_back_moveJ: J1=0°, J2=20°" << std::endl;
r = tl::queue_motion_push_back_moveJ(sock, make_movej_cmd({0, 20, 0, 0, 0, 0}, 30));
print_result("push_back_moveJ", r);
if (r != tl::Result::SUCCESS) return false;
std::cout << " push_back_moveJ: 回零" << std::endl;
r = tl::queue_motion_push_back_moveJ(sock, make_movej_cmd({0, 0, 0, 0, 0, 0}, 20));
print_result("push_back_moveJ", r);
if (r != tl::Result::SUCCESS) return false;
return true;
});
// ============================================================
// 演示 2: 纯 MoveL 队列运动
// ============================================================
run_queue_motion(sock, "纯 MoveL", [](tl::SOCKETFD sock) -> bool {
tl::Result r;
std::cout << " push_back_moveL: X=300, Y=0, Z=300" << std::endl;
r = tl::queue_motion_push_back_moveL(sock, make_movel_cmd({300, 0, 300, 3.141, 0, 0}, 30));
print_result("push_back_moveL", r);
if (r != tl::Result::SUCCESS) return false;
std::cout << " push_back_moveL: X=400, Y=100, Z=300" << std::endl;
r = tl::queue_motion_push_back_moveL(sock, make_movel_cmd({400, 100, 300, 3.141, 0, 0}, 30));
print_result("push_back_moveL", r);
if (r != tl::Result::SUCCESS) return false;
std::cout << " push_back_moveL: X=300, Y=200, Z=300" << std::endl;
r = tl::queue_motion_push_back_moveL(sock, make_movel_cmd({300, 200, 300, 3.141, 0, 0}, 30));
print_result("push_back_moveL", r);
if (r != tl::Result::SUCCESS) return false;
std::cout << " push_back_moveL: 回起始点" << std::endl;
r = tl::queue_motion_push_back_moveL(sock, make_movel_cmd({229.882, 0, 358.680, 3.141, 0, 0}, 20));
print_result("push_back_moveL", r);
if (r != tl::Result::SUCCESS) return false;
return true;
});
// ============================================================
// 演示 3: MoveJ + MoveL 混合队列运动
// ============================================================
run_queue_motion(sock, "MoveJ + MoveL 混合", [](tl::SOCKETFD sock) -> bool {
tl::Result r;
// 先用 MoveJ 调整姿态
std::cout << " push_back_moveJ: J1=45°(调整关节姿态)" << std::endl;
r = tl::queue_motion_push_back_moveJ(sock, make_movej_cmd({45, 0, 0, 0, 0, 0}, 30));
print_result("push_back_moveJ", r);
if (r != tl::Result::SUCCESS) return false;
// 再用 MoveL 走直线
std::cout << " push_back_moveL: X=400, Y=0, Z=300" << std::endl;
r = tl::queue_motion_push_back_moveL(sock, make_movel_cmd({400, 0, 300, 3.141, 0, 0}, 30));
print_result("push_back_moveL", r);
if (r != tl::Result::SUCCESS) return false;
// MoveJ 换向
std::cout << " push_back_moveJ: J1=-45°(换向)" << std::endl;
r = tl::queue_motion_push_back_moveJ(sock, make_movej_cmd({-45, 0, 0, 0, 0, 0}, 30));
print_result("push_back_moveJ", r);
if (r != tl::Result::SUCCESS) return false;
// MoveL 走另一条直线
std::cout << " push_back_moveL: X=300, Y=200, Z=300" << std::endl;
r = tl::queue_motion_push_back_moveL(sock, make_movel_cmd({300, 200, 300, 3.141, 0, 0}, 30));
print_result("push_back_moveL", r);
if (r != tl::Result::SUCCESS) return false;
// MoveJ 回零
std::cout << " push_back_moveJ: 回零" << std::endl;
r = tl::queue_motion_push_back_moveJ(sock, make_movej_cmd({0, 0, 0, 0, 0, 0}, 20));
print_result("push_back_moveJ", r);
if (r != tl::Result::SUCCESS) return false;
return true;
});
// ============================================================
// 演示 4: 队列运动控制(暂停/恢复/停止保持上电)
// ============================================================
{
print_separator("队列运动控制:暂停/恢复/停止");
// 辅助:等待队列剩余数减少到 target_len
auto wait_queue_len = [&](int target_len) {
int len = -1;
do {
std::this_thread::sleep_for(std::chrono::milliseconds(200));
tl::queue_motion_get_queuelen(sock, len);
} while (len > target_len && len != -1);
};
// 1. 开启队列模式
tl::Result r = tl::queue_motion_set_status(sock, true);
print_result("tl::queue_motion_set_status(true)", r);
if (r != tl::Result::SUCCESS) return -1;
std::this_thread::sleep_for(std::chrono::seconds(2));
// 2. 插入 4 条 J5 摆动指令(简单清晰,便于观察控制效果)
std::cout << " [信息] 插入 4 条 MoveJ 指令(J5 摆动)" << std::endl;
std::vector> targets = {
{0, 0, 0, 0, -30, 0, 0}, // J5 → -30°
{0, 0, 0, 0, 0, 0, 0}, // J5 → 0°
{0, 0, 0, 0, -30, 0, 0}, // J5 → -30°
{0, 0, 0, 0, 0, 0, 0}, // J5 → 0°
};
for (size_t i = 0; i < targets.size(); ++i) {
auto cmd = make_movej_cmd(targets[i], 30);
r = tl::queue_motion_push_back_moveJ(sock, cmd);
std::cout << " 第" << (i + 1) << "条: push_back_moveJ (J5→" << targets[i][4] << "°)"
<< (r == tl::Result::SUCCESS ? " 成功" : " 失败") << std::endl;
}
// 3. 发送队列到控制器
std::cout << " [信息] 发送队列到控制器..." << std::endl;
r = tl::queue_motion_send_to_controller(sock, 0);
print_result("send_to_controller", r);
if (r != tl::Result::SUCCESS) return -1;
// 4. 第 1 条执行中 → 暂停
std::cout << " [信息] 第 1 条执行中,等待 2 秒后暂停..." << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(2));
r = tl::queue_motion_suspend(sock);
print_result("暂停 (suspend)", r);
std::this_thread::sleep_for(std::chrono::seconds(2));
// 5. 恢复
r = tl::queue_motion_restart(sock);
print_result("恢复 (restart)", r);
std::cout << " [信息] 等待第 1 条完成..." << std::endl;
wait_queue_len(3);
std::cout << " [信息] 第 1 条完成" << std::endl;
// 6. 等待第 2 条完成
std::cout << " [信息] 等待第 2 条完成..." << std::endl;
wait_queue_len(2);
std::cout << " [信息] 第 2 条完成" << std::endl;
// 7. 第 3 条执行中 → 暂停
std::this_thread::sleep_for(std::chrono::seconds(1));
r = tl::queue_motion_suspend(sock);
print_result("再次暂停 (suspend)", r);
std::this_thread::sleep_for(std::chrono::seconds(2));
// 8. 恢复
r = tl::queue_motion_restart(sock);
print_result("再次恢复 (restart)", r);
std::cout << " [信息] 等待第 3 条完成..." << std::endl;
wait_queue_len(1);
std::cout << " [信息] 第 3 条完成" << std::endl;
// 9. 第 4 条执行中 → 停止(保持上电)
std::this_thread::sleep_for(std::chrono::seconds(2));
r = tl::queue_motion_stop_not_power_off(sock);
print_result("停止保持上电 (stop_not_power_off)", r);
std::this_thread::sleep_for(std::chrono::seconds(1));
// 10. 关闭队列模式
tl::queue_motion_set_status(sock, false);
std::cout << " [信息] 队列运动控制演示完毕" << std::endl;
}
// ---- 切回示教模式并断开连接 ----
print_separator("切回示教模式 + 断开连接");
ret = tl::set_current_mode(sock, tl::RobotMode::TEACH);
print_result("tl::set_current_mode(RobotMode::TEACH) 切回示教模式", ret);
std::this_thread::sleep_for(std::chrono::milliseconds(300));
tl::disconnect_robot(sock);
std::cout << "[信息] 已断开连接" << std::endl;
std::cout << "\n[信息] 队列运动示例程序运行完毕" << std::endl;
return 0;
}
