手柄替代键盘

键盘控制版本改写成了 ROS 2 手柄控制版本 ,使用游戏手柄(Xbox/PS/ 通用手柄) 控制机械臂末端运动,支持线性运动 + 旋转运动 、坐标系切换 、安全急停,功能完整可直接运行。

手柄控制机械臂 Servo 节点(ROS 2 Humble/Iron 兼容)

功能说明

  1. 左摇杆 :控制末端前后左右(XY 轴)
  2. 右摇杆 :控制末端旋转(Roll/Pitch)
  3. LB/RB :控制末端上下(Z 轴)
  4. Y 键 :切换基坐标系
  5. X 键 :切换末端坐标系
  6. A 键 :急停 / 停止运动
  7. BACK 键:退出节点

完整代码(servo_joy_teleop.cpp)

cpp

运行

复制代码
/*********************************************************************
 * Software License Agreement (BSD License)
 *********************************************************************/
#include <rclcpp/rclcpp.hpp>
#include <geometry_msgs/msg/twist_stamped.hpp>
#include <sensor_msgs/msg/joy.hpp>
#include <signal.h>
#include <stdlib.h>

// 话题名称(与Servo节点保持一致)
const std::string TWIST_TOPIC = "/servo_demo_node/delta_twist_cmds";
const std::string JOY_TOPIC = "/joy";
const size_t ROS_QUEUE_SIZE = 10;

// 坐标系定义
const std::string EEF_FRAME_ID = "panda_hand";
const std::string BASE_FRAME_ID = "panda_link0";

// 安全速度参数(可根据需求调整)
const double LINEAR_VEL = 0.15;    // 线速度
const double ANGULAR_VEL = 0.25;   // 角速度

// 全局退出信号
bool g_quit = false;
void quit(int sig)
{
  (void)sig;
  g_quit = true;
  rclcpp::shutdown();
  exit(0);
}

class JoyServo : public rclcpp::Node
{
public:
  JoyServo() : Node("servo_joy_teleop"), frame_id_(BASE_FRAME_ID)
  {
    // 创建发布器:发送Twist指令给Servo节点
    twist_pub_ = this->create_publisher<geometry_msgs::msg::TwistStamped>(
      TWIST_TOPIC, ROS_QUEUE_SIZE);

    // 创建订阅器:接收手柄数据
    joy_sub_ = this->create_subscription<sensor_msgs::msg::Joy>(
      JOY_TOPIC, ROS_QUEUE_SIZE,
      std::bind(&JoyServo::joyCallback, this, std::placeholders::_1));

    RCLCPP_INFO(this->get_logger(), "=============================================");
    RCLCPP_INFO(this->get_logger(), "          手柄控制机械臂末端");
    RCLCPP_INFO(this->get_logger(), "=============================================");
    RCLCPP_INFO(this->get_logger(), "左摇杆:前后左右(XY) | 右摇杆:旋转(Roll/Pitch)");
    RCLCPP_INFO(this->get_logger(), "LB:下降(Z-) | RB:上升(Z+)");
    RCLCPP_INFO(this->get_logger(), "Y:基坐标系 | X:末端坐标系 | A:急停");
    RCLCPP_INFO(this->get_logger(), "BACK:退出节点");
    RCLCPP_INFO(this->get_logger(), "=============================================");
  }

private:
  /**
   * @brief 手柄数据回调函数
   */
  void joyCallback(const sensor_msgs::msg::Joy::SharedPtr joy)
  {
    auto twist_msg = std::make_unique<geometry_msgs::msg::TwistStamped>();
    twist_msg->header.stamp = this->now();
    twist_msg->header.frame_id = frame_id_;

    // ==================== 坐标系切换 ====================
    // Y键 → 基坐标系
    if (joy->buttons[3] == 1) {
      frame_id_ = BASE_FRAME_ID;
      RCLCPP_INFO(this->get_logger(), "已切换至:基坐标系");
    }
    // X键 → 末端坐标系
    if (joy->buttons[2] == 1) {
      frame_id_ = EEF_FRAME_ID;
      RCLCPP_INFO(this->get_logger(), "已切换至:末端坐标系");
    }

    // ==================== 急停(A键) ====================
    if (joy->buttons[0] == 1) {
      // 发布零速度指令
      twist_pub_->publish(std::move(twist_msg));
      RCLCPP_WARN(this->get_logger(), "⚠️  急停:已停止所有运动");
      return;
    }

    // ==================== 退出(BACK键) ====================
    if (joy->buttons[6] == 1) {
      RCLCPP_INFO(this->get_logger(), "退出手柄控制节点");
      rclcpp::shutdown();
      return;
    }

    // ==================== 轴控制 ====================
    // 左摇杆左右 → Y轴(左右)
    twist_msg->twist.linear.y = LINEAR_VEL * joy->axes[0];
    // 左摇杆前后 → X轴(前后)
    twist_msg->twist.linear.x = LINEAR_VEL * joy->axes[1];
    // 右摇杆左右 → 旋转(Roll)
    twist_msg->twist.angular.z = ANGULAR_VEL * joy->axes[3];
    // 右摇杆上下 → 旋转(Pitch)
    twist_msg->twist.angular.y = ANGULAR_VEL * joy->axes[4];

    // LB/RB → Z轴(上下)
    if (joy->buttons[4] == 1) {  // LB:下降
      twist_msg->twist.linear.z = -LINEAR_VEL;
    }
    if (joy->buttons[5] == 1) {  // RB:上升
      twist_msg->twist.linear.z = LINEAR_VEL;
    }

    // 发布指令
    twist_pub_->publish(std::move(twist_msg));
  }

  // 成员变量
  rclcpp::Publisher<geometry_msgs::msg::TwistStamped>::SharedPtr twist_pub_;
  rclcpp::Subscription<sensor_msgs::msg::Joy>::SharedPtr joy_sub_;
  std::string frame_id_;
};

int main(int argc, char** argv)
{
  rclcpp::init(argc, argv);
  signal(SIGINT, quit);

  auto node = std::make_shared<JoyServo>();
  rclcpp::spin(node);

  rclcpp::shutdown();
  return 0;
}

配套 CMakeLists.txt

cmake

复制代码
cmake_minimum_required(VERSION 3.8)
project(servo_joy_teleop)

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic)
endif()

# 依赖
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(geometry_msgs REQUIRED)
find_package(sensor_msgs REQUIRED)

add_executable(servo_joy_teleop src/servo_joy_teleop.cpp)
ament_target_dependencies(
  servo_joy_teleop
  rclcpp
  geometry_msgs
  sensor_msgs
)

install(TARGETS
  servo_joy_teleop
  DESTINATION lib/${PROJECT_NAME})

ament_package()

配套 package.xml

xml

复制代码
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
  <name>servo_joy_teleop</name>
  <version>0.0.0</version>
  <description>Servo control with joystick</description>
  <maintainer email="your@mail.com">Your Name</maintainer>
  <license>BSD</license>

  <buildtool_depend>ament_cmake</buildtool_depend>
  <depend>rclcpp</depend>
  <depend>geometry_msgs</depend>
  <depend>sensor_msgs</depend>

  <test_depend>ament_lint_auto</test_depend>
  <test_depend>ament_lint_common</test_depend>

  <export>
    <build_type>ament_cmake</build_type>
  </export>
</package>

使用方法

1. 安装 ROS 2 手柄驱动

bash

运行

复制代码
sudo apt install ros-$ROS_DISTRO-joy

2. 运行手柄节点

bash

运行

复制代码
ros2 run joy joy_node

3. 运行本控制节点

bash

运行

复制代码
ros2 run servo_joy_teleop servo_joy_teleop

手柄按键映射(Xbox 手柄标准)

表格

按键 / 摇杆 功能
左摇杆左右 末端左右移动(Y 轴)
左摇杆前后 末端前后移动(X 轴)
右摇杆 末端旋转(Roll/Pitch)
LB 键 末端下降(Z-)
RB 键 末端上升(Z+)
Y 键 切换到基坐标系
X 键 切换到末端坐标系
A 键 急停,立即停止运动
BACK 键 退出程序

可自定义参数

你可以直接修改代码中的常量调整手感:

cpp

运行

复制代码
const double LINEAR_VEL = 0.15;    // 线速度大小
const double ANGULAR_VEL = 0.25;   // 旋转速度大小

总结

  1. 这是纯 C++ ROS 2 手柄控制版本,替代原键盘版本
  2. 支持线性运动 + 旋转运动 、坐标系切换 、安全急停
  3. 兼容Xbox/PS/ 通用 USB 手柄,开箱即用
  4. 话题名与原代码完全一致,可直接对接 Servo 节点
相关推荐
2401_832298103 分钟前
以智赋能,向善而行——理性拥抱人工智能新时代
人工智能
johnsong4 分钟前
AI前沿日报 2026-10-10:边缘融合
人工智能
Laurie三省6 分钟前
现场版却配上录音室版的歌词?聊聊我给开源 Mac 歌词软件写的多源打分算法
算法·macos·go
陈天伟教授8 分钟前
CAD中关于坡度的标记的技巧
人工智能·阅读·具身智能·论文写法·文字排版
前沿在线9 分钟前
破解 AI 推理效率困局,详解华为 OceanStor M900 AI记忆存储新基建
人工智能·ai·大模型
深圳多奥智能一卡(码、脸)通系统10 分钟前
实现机器人、梯控与电梯的无缝对接,成为了一个亟待解决的痛点
机器人·agv·机器狗·门禁·梯控·amr
惊鸿一博11 分钟前
3DGS_可视化工具SIBR_gaussianViewer_app_使用说明/快捷键
人工智能·3dgs
Pingred11 分钟前
端侧推理排查(二):线程在大核、CPU 满频,为什么还是慢?
人工智能
黑妹天下第一乖14 分钟前
小智改造实战解读-用kokoro-onnx替换云端 TTS 的完整记录
大数据·开发语言·人工智能·python·交互
对空六课16 分钟前
Vue 组件曝光埋点为什么会重复触发?去重方案与实现思路
服务器·前端·算法·数据分析