跳转到内容

集成与上层应用

SDK 底层通信基于自封装的 UDP Socket 层:

graph TB
A["Gamepad<KeysType>"] --> B["UdpReceiver"]
B --> C["Linux Socket API"]
  • UdpReceiver:封装 Socket 创建、端口绑定和数据接收
  • Gamepad<KeysType>:在独立线程中循环调用 UdpReceiver::ReceiveData(),将原始数据交给子类的 UpdateData() 解析
graph TB
A["UdpReceiver 阻塞等待 UDP 数据"] --> B["收到原始字节流"]
B --> C["Gamepad 调用子类 UpdateData"]
C --> D{"DataIsValid 三级校验"}
D -->|"通过"| E["解析按键和摇杆数据"]
E --> F["触发 updateCallback_"]
D -->|"失败"| A

SDK 使用独立的数据接收线程:

  • StartDataThread() 创建并 detach 一个后台线程
  • 线程内循环调用 ReceiveData()(阻塞),收到数据后通过 mutex_ 保护按键数据的读写
  • GetKeys() 通过同一 mutex_ 保证线程安全
  • StopDataThread() 设置 stop_thread_ 原子标志,线程在下一次循环退出

在项目的 CMakeLists.txt 中引用 SDK:

# 指定 gamepad SDK 路径
set(GAMEPAD_DIR "/path/to/gamepad")
# 引入头文件
include_directories(${GAMEPAD_DIR}/include)
# 链接静态库
link_directories(${GAMEPAD_DIR}/build)
target_link_libraries(your_target gamepad_receiver)
#include "retroid_gamepad.h"
#include "gamepad_keys.h"
// 创建手柄实例
RetroidGamepad gamepad(12121);
gamepad.StartDataThread();
// 方式一:回调方式获取数据
gamepad.SetUpdateCallback([](uint32_t count) {
// 数据已更新
});
// 方式二:轮询方式获取数据
while (running) {
RetroidKeys& keys = gamepad.GetKeys();
float vx = keys.left_axis_y; // 前进速度
float vy = keys.left_axis_x; // 横向速度
float omega = keys.right_axis_x; // 旋转速度
if (keys.Y) {
// Y 键按下 → 站立
}
if (keys.A) {
// A 键按下 → RL 控制
}
if (keys.left_axis_button && keys.right_axis_button) {
// 双摇杆按下 → 紧急阻尼
}
std::this_thread::sleep_for(std::chrono::milliseconds(5));
}

在 lite3_transfer 中,RetroidGamepadPublisher 封装了 Gamepad SDK 并定期将手柄数据发布到 ROS2 话题 /GAMEPAD_DATA:

auto gamepad_ptr = std::make_shared<RetroidGamepad>(12121);
gamepad_ptr->StartDataThread();
// 周期读取并发布
RetroidKeys rt_keys = gamepad_ptr->GetKeys();
drdds::msg::GamepadData msg;
msg.left_axis_x = rt_keys.left_axis_x;
msg.left_axis_y = rt_keys.left_axis_y;
msg.right_axis_x = rt_keys.right_axis_x;
msg.right_axis_y = rt_keys.right_axis_y;
msg.buttons = rt_keys.value;
  1. 确保网络隔离:手柄 App 通过 UDP 广播数据,确保开发网络与其他网络隔离,避免数据被干扰或截获。

  2. 防火墙配置:编译和运行前确保 UDP 端口已放行,否则无法接收手柄数据。

  3. 心跳检测:上层应用应实现手柄心跳检测机制,当长时间未收到手柄数据时自动进入安全模式(如阻尼状态),防止失控。

  4. 端口冲突:确保端口 12121 未被其他程序占用,可通过以下命令检查:

Terminal window
sudo lsof -i:12121