底层通信示例
通过 UDP 获取 X30 运行状态信息
Section titled “通过 UDP 获取 X30 运行状态信息”文档目的: 指导用户搭建状态监控程序获取X30通过UDP协议上报运行状态信息。
第一步:修改X30运动主机网络配置
Section titled “第一步:修改X30运动主机网络配置”-
登录X30运动主机
连接X30 WiFi,通过ssh进入运动主机的后台(192.168.1.103),登录账号ysc的密码是
'(英文单引号)Terminal window ssh ysc@192.168.1.103 -
进入配置文件目录
Terminal window cd /home/ysc/jy_exe/conf -
编辑network.toml配置文件
Terminal window vim network.toml -
在文件末尾添加以下配置
# UDP状态上报配置[udp_status]ip = '192.168.1.105'target_port = 43897local_port = 43893ips = ["192.168.1.105", "192.168.1.106","xxxxx"] # 接收状态消息的IP地址列表ports = [30001, 30001, xxxxx] # 对应的端口号列表配置说明:
ips: 接收UDP状态消息的客户端IP地址数组ports: 对应的端口号数组,顺序与IP地址一一对应- 请根据实际网络环境修改IP地址
第二步:重启运动服务
Section titled “第二步:重启运动服务”cd /home/ysc/jy_exe/scripts./restart.sh第三步:编译并运行状态监控程序
Section titled “第三步:编译并运行状态监控程序”g++ UDPtest.cpp -o UDPtest./UDPtest第四步:验证配置效果
Section titled “第四步:验证配置效果”程序正常运行后将显示:
开始监听机器人状态上报,端口: 30001当收到机器人状态数据时,显示类似以下信息:
========================================收到状态数据 [来源:192.168.1.103]----------------------------------------机器人名称: X30_Robot当前里程: 1500 cm总里程: 12500 cm当前运行时间: 3600 秒总运行时间: 86400 秒当前运动时间: 1800 秒总运动时间: 43200 秒摇杆数据: [0, 0.5, 0, 0]运行模式: 自动急停来源: 0错误状态: 0x0========================================
UDP 状态监控程序示例代码
Section titled “UDP 状态监控程序示例代码”#include <iostream>#include <cstring>#include <sys/socket.h>#include <arpa/inet.h>#include <unistd.h>#include <stdint.h>
#define PORT 30001
struct RcsData { char robot_name[15]; int32_t current_mileage; int32_t total_mileage; int32_t current_run_time; int32_t total_run_time; int32_t current_motion_time; int32_t total_motion_time; float joystick[4]; union { struct { uint8_t is_nav_mode; uint8_t emergency_source; uint8_t mode_reserved[8]; } rcs_state_list; uint8_t rcs_state[10]; }; union { struct { uint32_t imu_error : 1; uint32_t wifi_error : 1; uint32_t driver_heat_warn : 1; uint32_t driver_error : 1; uint32_t motor_heat_warn : 1; uint32_t battery_low_warn : 1; uint32_t battery_heat_warn : 1; uint32_t gpio_error : 1; uint32_t cpu_heat_warn : 1; uint32_t cpu_freq_warn : 1; uint32_t reserved : 22; } error_state_bit; uint32_t error_state; };};
int main() { int sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd < 0) { std::cout << "创建Socket失败" << std::endl; return -1; }
struct sockaddr_in server_addr; memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = INADDR_ANY; server_addr.sin_port = htons(PORT);
if (bind(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) { std::cout << "绑定端口失败" << std::endl; close(sockfd); return -1; }
std::cout << "开始监听机器人状态上报,端口: " << PORT << std::endl;
while (true) { RcsData robot_data; struct sockaddr_in client_addr; socklen_t client_len = sizeof(client_addr);
ssize_t recv_len = recvfrom(sockfd, &robot_data, sizeof(robot_data), 0, (struct sockaddr*)&client_addr, &client_len);
if (recv_len == sizeof(robot_data)) { char client_ip[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &(client_addr.sin_addr), client_ip, INET_ADDRSTRLEN);
std::cout << "========================================" << std::endl; std::cout << "收到状态数据 [来源:" << client_ip << "]" << std::endl; std::cout << "----------------------------------------" << std::endl; std::cout << "机器人名称: " << robot_data.robot_name << std::endl; std::cout << "当前里程: " << robot_data.current_mileage << " cm" << std::endl; std::cout << "总里程: " << robot_data.total_mileage << " cm" << std::endl; std::cout << "当前运行时间: " << robot_data.current_run_time << " 秒" << std::endl; std::cout << "总运行时间: " << robot_data.total_run_time << " 秒" << std::endl; std::cout << "当前运动时间: " << robot_data.current_motion_time << " 秒" << std::endl; std::cout << "总运动时间: " << robot_data.total_motion_time << " 秒" << std::endl;
std::cout << "摇杆数据: [" << robot_data.joystick[0] << ", " << robot_data.joystick[1] << ", " << robot_data.joystick[2] << ", " << robot_data.joystick[3] << "]" << std::endl;
std::cout << "运行模式: " << (robot_data.rcs_state_list.is_nav_mode ? "自动" : "手动") << std::endl; std::cout << "急停来源: " << static_cast<int>(robot_data.rcs_state_list.emergency_source) << std::endl;
std::cout << "错误状态: 0x" << std::hex << robot_data.error_state << std::dec << std::endl; if (robot_data.error_state_bit.imu_error) std::cout << "- IMU错误" << std::endl; if (robot_data.error_state_bit.wifi_error) std::cout << "- WiFi错误" << std::endl; if (robot_data.error_state_bit.driver_heat_warn) std::cout << "- 驱动器过热警告" << std::endl; if (robot_data.error_state_bit.driver_error) std::cout << "- 驱动器错误" << std::endl; if (robot_data.error_state_bit.motor_heat_warn) std::cout << "- 电机过热警告" << std::endl; if (robot_data.error_state_bit.battery_low_warn) std::cout << "- 电池电量低警告" << std::endl; if (robot_data.error_state_bit.battery_heat_warn) std::cout << "- 电池过热警告" << std::endl; if (robot_data.error_state_bit.gpio_error) std::cout << "- GPIO错误" << std::endl; if (robot_data.error_state_bit.cpu_heat_warn) std::cout << "- CPU过热警告" << std::endl; if (robot_data.error_state_bit.cpu_freq_warn) std::cout << "- CPU频率警告" << std::endl; std::cout << "========================================" << std::endl; } }
close(sockfd); return 0;}通过 TCP 获取 X30 实时状态信息
Section titled “通过 TCP 获取 X30 实时状态信息”文档目的: 指导用户通过TCP协议获取X30机器狗的实时基础状态信息。
第一步:确认网络连接
Section titled “第一步:确认网络连接”确保监控主机与X30导航主机在同一网络段。X30导航主机默认IP:192.168.1.106,TCP端口30000。
验证连通性:
nc -zv 192.168.1.106 30000第二步:编译并运行状态查询程序
Section titled “第二步:编译并运行状态查询程序”g++ TCPtest.cpp -o TCPtest./TCPtest第三步:验证配置效果
Section titled “第三步:验证配置效果”程序正常运行后将显示:
=== X30机器狗实时状态获取程序 ===连接到机器狗 192.168.1.106:30000...连接成功发送状态查询请求...发送成功: 175 字收到响应时显示:
=== 收到机器狗状态响应 ===响应长度: 689 字节响应内容:<?xml version="1.0" encoding="UTF-8"?><PatrolDevice> <Type>1002</Type> <Command>1</Command> <Time>2025-11-12 14:30:25</Time> <Items> <MotionState>2</MotionState> <PosX>1.5</PosX><PosY>2.3</PosY><PosZ>0.0</PosZ> <Roll>0.05</Roll><Pitch>-0.02</Pitch><Yaw>1.57</Yaw> <Speed>0.5</Speed> <CurOdom>0.15</CurOdom><SumOdom>12.5</SumOdom> <Electricity>85</Electricity> <Location>0</Location> <GaitState>0</GaitState> <MotorState>0</MotorState> <ChargeState>0</ChargeState> <ControlMode>1</ControlMode> <OnDockState>0</OnDockState> </Items></PatrolDevice>功能扩展说明
Section titled “功能扩展说明”如需实现其他功能(例如下发导航任务 Type=1003),只需修改代码中的 XML 数据构造部分和响应解析部分。具体参考 通信架构 中的各消息类型定义。
TCP 状态查询程序示例代码
Section titled “TCP 状态查询程序示例代码”#include <iostream>#include <cstring>#include <sys/socket.h>#include <arpa/inet.h>#include <unistd.h>#include <string>#include <ctime>
#define SERVER_IP "192.168.1.106" // 机器狗IP地址#define PORT 30000 // 机器狗TCP服务端口
// X30协议头部结构(固定16字节)struct x30ProtocolHeader { unsigned char sync1; // 同步字符1: 0xEB unsigned char sync2; // 同步字符2: 0x90 unsigned char sync3; // 同步字符3: 0xEB unsigned char sync4; // 同步字符4: 0x90 unsigned short dataLength; // XML数据长度(小端字节序) unsigned short messageID; // 报文ID(小端字节序) unsigned char reserved[8]; // 预留字段};
// 获取当前时间字符串std::string getCurrentTime() { time_t now = time(0); struct tm tstruct; char buf[80]; tstruct = *localtime(&now); strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &tstruct); return buf;}
int main() { std::cout << "=== X30机器狗实时状态获取程序 ===" << std::endl;
// 创建TCP套接字 int client_fd = socket(AF_INET, SOCK_STREAM, 0); if (client_fd < 0) { perror("创建Socket失败"); return -1; }
// 配置服务器地址 struct sockaddr_in server_addr; server_addr.sin_family = AF_INET; server_addr.sin_port = htons(PORT); inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr);
// 连接到机器狗 std::cout << "连接到机器狗 " << SERVER_IP << ":" << PORT << "..." << std::endl; if (connect(client_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) { perror("连接失败"); close(client_fd); return -1; } std::cout << "连接成功" << std::endl;
// 构造获取实时状态XML数据 // Type=1002: 获取实时基础状态 std::string xmlData = R"(<?xml version="1.0" encoding="UTF-8"?><PatrolDevice> <Type>1002</Type> <Command>1</Command> <Time>)" + getCurrentTime() + R"(</Time> <Items/></PatrolDevice>)";
// 构造完整协议报文:16字节头部 + XML数据 unsigned short xmlLength = xmlData.length(); char message[16 + xmlLength];
x30ProtocolHeader* header = (x30ProtocolHeader*)message; header->sync1 = 0xEB; header->sync2 = 0x90; header->sync3 = 0xEB; header->sync4 = 0x90; header->dataLength = xmlLength; header->messageID = 1; memset(header->reserved, 0x00, 8);
memcpy(message + 16, xmlData.c_str(), xmlLength);
// 发送状态查询指令 std::cout << "发送状态查询请求..." << std::endl; ssize_t send_len = send(client_fd, message, 16 + xmlLength, 0); if (send_len < 0) { perror("发送失败"); close(client_fd); return -1; }
std::cout << "发送成功: " << send_len << " 字节" << std::endl;
// 接收机器狗响应 char responseBuffer[4096]; ssize_t recv_len = recv(client_fd, responseBuffer, sizeof(responseBuffer) - 1, 0);
if (recv_len > 0) { responseBuffer[recv_len] = '\0'; std::cout << "\n=== 收到机器狗状态响应 ===" << std::endl; std::cout << "响应长度: " << recv_len << " 字节" << std::endl;
if(recv_len > 16) { std::string responseXml(responseBuffer + 16, recv_len - 16); std::cout << "响应内容:" << std::endl; std::cout << responseXml << std::endl;
auto parseField = [&](const std::string& field) -> std::string { size_t start = responseXml.find("<" + field + ">"); if (start == std::string::npos) return "未知"; start += field.length() + 2; size_t end = responseXml.find("</" + field + ">", start); if (end == std::string::npos) return "未知"; return responseXml.substr(start, end - start); };
std::cout << "\n=== 关键状态信息 ===" << std::endl; std::cout << "位置: (" << parseField("PosX") << ", " << parseField("PosY") << ", " << parseField("PosZ") << ")" << std::endl; std::cout << "姿态: (" << parseField("Roll") << ", " << parseField("Pitch") << ", " << parseField("Yaw") << ")" << std::endl; std::cout << "速度: " << parseField("Speed") << " m/s" << std::endl; std::cout << "电量: " << parseField("Electricity") << "%" << std::endl;
std::string motionState = parseField("MotionState"); std::cout << "运动状态: " << motionState; if (motionState == "0") std::cout << " (趴下)"; else if (motionState == "1") std::cout << " (正在起立)"; else if (motionState == "2") std::cout << " (初始站立)"; else if (motionState == "3") std::cout << " (力控站立)"; else if (motionState == "4") std::cout << " (踏步)"; else if (motionState == "5") std::cout << " (正在趴下)"; else if (motionState == "6") std::cout << " (软急停)"; else if (motionState == "7") std::cout << " (摔倒)"; std::cout << std::endl;
std::string controlMode = parseField("ControlMode"); std::cout << "控制模式: " << (controlMode == "0" ? "手动模式" : "自动模式") << std::endl; } } else { std::cout << "未收到响应" << std::endl; }
close(client_fd); std::cout << "\n连接已关闭" << std::endl; return 0;}- 网络连通性:确保X30与监控主机在同一网络段,确保TCP端口30000畅通
- 配置一致性:
network.toml中的端口号必须与监控程序监听端口一致,IP地址必须为监控主机的实际IP地址