3. 伺服状态检测
本期将介绍如何连接控制器,查询机器人当前的伺服状态,并根据状态码输出对应的检测结果。
在执行上电、运动或其他控制操作之前,通常需要先确认伺服当前所处的状态。程序通过 get_servo_state 获取状态码,再根据状态码判断机器人是未上电、已经就绪、正在运行,还是处于报警状态。
伺服状态码的含义如下:
- 状态 0:伺服未上电
- 状态 1:伺服就绪
- 状态 2:伺服报警
- 状态 3:伺服运行中
⚠️ 使用注意
本 Demo 只查询和显示伺服状态,不会修改伺服状态,也不会向机器人发送运动指令。运行前仍需确认:
- 连接配置: 控制器 IP 地址和 SDK 服务端口与实际配置一致
- 查询结果: 只有
get_servo_state返回SUCCESS时,才能使用查询得到的状态值 - 报警处理: 状态为 2 时应停止后续控制操作,并检查控制器报警信息
- 未知状态: SDK 返回未定义状态码时,应按异常处理,不能继续下发控制命令
- 现场安全: 即使本 Demo 不控制机器人,也应确保急停和安全回路处于正常状态
伺服状态只能反映当前查询时刻的结果。实际项目在执行关键控制操作前,应根据业务需要重新查询状态,避免使用已经过期的状态值。
1、配置控制器连接和查询参数
首先配置控制器 IP 地址、SDK 服务端口、查询失败后的重试间隔,以及允许的最大连续查询失败次数。
/**
* @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 函数,在配置的次数范围内重复查询。
/**
* @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<int>(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 次查询开始调用
get_servo_state。 - 如果接口返回
SUCCESS,立即返回true,此时state保存有效状态码。 - 如果查询失败,输出当前失败次数和 SDK 错误码。
- 未达到最大次数时,等待
query_retry_interval_ms毫秒后再次查询。 - 所有查询均失败时返回
false,调用方不能继续解释state。
3、封装伺服状态展示与判断函数
成功取得状态码后,还需要将数值状态转换为便于理解的文字,并判断该状态是否允许程序继续执行后续控制操作。
单独封装 show_servo_state,是为了将“从控制器读取状态”和“解释状态含义”分离。查询函数只负责保证数据有效,展示函数统一维护状态码与文字说明的对应关系,并通过布尔返回值告知主程序当前状态是否正常。以后增加新的状态码或修改异常处理方式时,只需要调整该函数。
/**
* @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;
}
}各状态的处理逻辑如下:
- 状态 0:输出“未上电”,状态值有效,函数返回
true。 - 状态 1:输出“就绪”,状态值有效,函数返回
true。 - 状态 2:输出“报警”和处理提示,函数返回
false,阻止后续控制操作。 - 状态 3:输出“运行中”,状态值有效,函数返回
true。 - 未知状态:无法确认机器人状态,按异常处理并返回
false。
这里返回 true 表示状态码已被识别且不属于报警或未知状态,并不表示伺服一定已经上电。例如状态 0 虽然返回 true,但它仍表示伺服当前处于未上电状态。
4、主程序连接控制器并检测伺服状态
主函数负责启用 UTF-8 控制台输出、连接控制器、获取伺服状态,并根据检测结果设置程序退出码。
只有 get_servo_state_with_retry 返回成功时,主函数才会调用 show_servo_state。这样可以避免查询失败后继续使用无效或过期的状态值。
#include <iostream>
#include <thread>
#include <chrono>
#include <cpp_interface/nrc_interface.h>
#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;
}