joy 及sensor_msgs

复制代码
void joyCallback(const sensor_msgs::msg::Joy::ConstSharedPtr& msg)
//               └────────────────┬────────────────┘
//                          消息类型
//                                    └─────────┬─────────┘
//                                              指针类型
//                                           └───┬───┘
//                                              引用
//                                    └─────┬─────┘
//                                         常量

1. sensor_msgs::msg::Joy

这是 ROS2 中手柄消息的标准数据类型 ,定义在 sensor_msgs 包中。

2. ConstSharedPtr

这是一个常量共享指针const std::shared_ptr<const Joy> 的别名),特点:

  • 只读:不能修改消息内容

  • 共享:多个地方可以同时持有

  • 安全:自动管理内存

3. &(引用)

传递引用避免拷贝,提高性能。


二、Joy 消息结构

cpp

复制代码
// sensor_msgs/msg/Joy.msg
Header header           // 时间戳和坐标系
float32[] axes          // 摇杆轴值数组(连续值)
int32[] buttons         // 按钮状态数组(离散值)

详细说明

cpp

复制代码
std_msgs/Header header  // 消息头
    uint32 seq          // 序列号(自动递增)
    time stamp          // 时间戳
    string frame_id     // 坐标系ID(通常为空)

float32[] axes          // 摇杆和扳机的值
    // 范围: -1.0 ~ 1.0 (摇杆)
    // 范围: 0.0 ~ 1.0 (扳机,默认在1.0位置)
    // 索引含义取决于手柄类型

int32[] buttons         // 按钮状态
    // 值: 0 (未按下) 或 1 (按下)
    // 索引含义取决于手柄类型

三、手柄数据映射(XBOX 手柄)

cpp

复制代码
// 摇杆轴索引
enum Axis {
  LEFT_STICK_X = 0,    // 左摇杆水平 (-1.0 ~ 1.0)
  LEFT_STICK_Y = 1,    // 左摇杆垂直 (-1.0 ~ 1.0)
  LEFT_TRIGGER = 2,    // 左扳机 (0.0 ~ 1.0)
  RIGHT_STICK_X = 3,   // 右摇杆水平 (-1.0 ~ 1.0)
  RIGHT_STICK_Y = 4,   // 右摇杆垂直 (-1.0 ~ 1.0)
  RIGHT_TRIGGER = 5,   // 右扳机 (0.0 ~ 1.0)
  D_PAD_X = 6,         // 方向键水平 (-1.0, 0.0, 1.0)
  D_PAD_Y = 7          // 方向键垂直 (-1.0, 0.0, 1.0)
};

// 按钮索引
enum Button {
  A = 0,               // A键
  B = 1,               // B键
  X = 2,               // X键
  Y = 3,               // Y键
  LEFT_BUMPER = 4,     // 左肩键 (LB)
  RIGHT_BUMPER = 5,    // 右肩键 (RB)
  CHANGE_VIEW = 6,     // 视图键 (Back)
  MENU = 7,            // 菜单键 (Start)
  HOME = 8,            // Xbox键
  LEFT_STICK_CLICK = 9,  // 左摇杆按下
  RIGHT_STICK_CLICK = 10 // 右摇杆按下
};

四、如何访问数据

cpp

复制代码
void joyCallback(const sensor_msgs::msg::Joy::ConstSharedPtr& msg)
{
  // ============================================================
  // 1. 访问摇杆数据 (axes数组)
  // ============================================================
  
  // 左摇杆
  float left_x = msg->axes[0];   // 左右
  float left_y = msg->axes[1];   // 前后
  
  // 右摇杆
  float right_x = msg->axes[3];  // 左右
  float right_y = msg->axes[4];  // 前后
  
  // 扳机
  float left_trigger = msg->axes[2];   // 0.0 ~ 1.0
  float right_trigger = msg->axes[5];  // 0.0 ~ 1.0
  
  // 方向键
  float d_pad_x = msg->axes[6];  // -1, 0, 1
  float d_pad_y = msg->axes[7];  // -1, 0, 1
  
  // ============================================================
  // 2. 访问按钮数据 (buttons数组)
  // ============================================================
  
  // 检查按钮是否按下 (0=未按下, 1=按下)
  bool a_pressed = msg->buttons[0] == 1;
  bool b_pressed = msg->buttons[1] == 1;
  bool x_pressed = msg->buttons[2] == 1;
  bool y_pressed = msg->buttons[3] == 1;
  
  bool lb_pressed = msg->buttons[4] == 1;  // 左肩键
  bool rb_pressed = msg->buttons[5] == 1;  // 右肩键
  
  // ============================================================
  // 3. 访问消息头
  // ============================================================
  
  rclcpp::Time stamp = msg->header.stamp;
  // 可以使用 stamp 进行时间同步
  
  // ============================================================
  // 4. 安全检查
  // ============================================================
  
  // 确保数组大小符合预期
  if (msg->axes.size() < 8 || msg->buttons.size() < 11) {
    RCLCPP_WARN(this->get_logger(), "Joy message size mismatch");
    return;
  }
}

五、实际应用示例

示例1:将手柄映射为 Twist 速度命令

cpp

复制代码
void joyCallback(const sensor_msgs::msg::Joy::ConstSharedPtr& msg)
{
  auto twist = geometry_msgs::msg::Twist();
  
  // 安全检查
  if (msg->axes.size() < 8) return;
  
  // 死区处理(防止摇杆漂移)
  double deadzone = 0.1;
  
  // 左摇杆 → 线性速度 (前后左右)
  twist.linear.x = (std::abs(msg->axes[1]) > deadzone) ? msg->axes[1] : 0.0;
  twist.linear.y = (std::abs(msg->axes[0]) > deadzone) ? msg->axes[0] : 0.0;
  
  // 右摇杆 → 角速度 (旋转)
  twist.angular.z = (std::abs(msg->axes[3]) > deadzone) ? msg->axes[3] : 0.0;
  
  // 速度缩放
  double speed_scale = 0.5;
  twist.linear.x *= speed_scale;
  twist.linear.y *= speed_scale;
  twist.angular.z *= speed_scale;
  
  // 发布
  cmd_pub_->publish(twist);
}

示例2:按钮控制(安全开关)

cpp

复制代码
void joyCallback(const sensor_msgs::msg::Joy::ConstSharedPtr& msg)
{
  if (msg->buttons.size() < 11) return;
  
  // A 键:启用控制(安全开关)
  if (msg->buttons[0] == 1) {
    // 启用控制
    enabled_ = true;
    RCLCPP_INFO_ONCE(this->get_logger(), "Control enabled (A pressed)");
  } 
  // B 键:禁用控制(紧急停止)
  else if (msg->buttons[1] == 1) {
    enabled_ = false;
    RCLCPP_WARN_ONCE(this->get_logger(), "Control disabled (B pressed)");
  }
  
  if (!enabled_) {
    // 发布零命令
    publishZeroCommand();
    return;
  }
  
  // ... 正常控制逻辑
}

示例3:组合键功能

cpp

复制代码
void joyCallback(const sensor_msgs::msg::Joy::ConstSharedPtr& msg)
{
  if (msg->buttons.size() < 11) return;
  
  // 切换坐标系:VIEW 键 + MENU 键
  if (msg->buttons[6] == 1 && msg->buttons[7] == 1) {
    if (frame_ == "base_link") {
      frame_ = "tool0";
    } else {
      frame_ = "base_link";
    }
    RCLCPP_INFO(this->get_logger(), "Switched to frame: %s", frame_.c_str());
  }
  
  // 速度调节:LB 减速,RB 加速
  if (msg->buttons[4] == 1) {
    speed_scale_ = std::max(0.1, speed_scale_ - 0.1);
  }
  if (msg->buttons[5] == 1) {
    speed_scale_ = std::min(1.0, speed_scale_ + 0.1);
  }
}

六、手柄类型适配

XBOX 手柄

cpp

复制代码
// XBOX 手柄映射(如上所示)
enum XBOX_Axis { LEFT_X=0, LEFT_Y=1, L_TRIGGER=2, RIGHT_X=3, RIGHT_Y=4, R_TRIGGER=5, DPAD_X=6, DPAD_Y=7 };
enum XBOX_Button { A=0, B=1, X=2, Y=3, LB=4, RB=5, VIEW=6, MENU=7, HOME=8, L_CLICK=9, R_CLICK=10 };

PlayStation 手柄(DualShock 4)

cpp

复制代码
enum PS4_Axis { LEFT_X=0, LEFT_Y=1, RIGHT_X=2, RIGHT_Y=3, L_TRIGGER=4, R_TRIGGER=5 };
enum PS4_Button { SQUARE=0, X=1, CIRCLE=2, TRIANGLE=3, L1=4, R1=5, L2=6, R2=7, SHARE=8, OPTIONS=9, L_CLICK=10, R_CLICK=11 };

通用 USB 手柄

cpp

复制代码
// 不同手柄映射可能不同,建议先打印查看
void joyCallback(const sensor_msgs::msg::Joy::ConstSharedPtr& msg)
{
  RCLCPP_INFO(this->get_logger(), 
    "Axes: [%f, %f, %f, %f, %f, %f, %f, %f]",
    msg->axes[0], msg->axes[1], msg->axes[2], msg->axes[3],
    msg->axes[4], msg->axes[5], msg->axes[6], msg->axes[7]);
}

七、调试技巧

打印所有数据

cpp

复制代码
void joyCallback(const sensor_msgs::msg::Joy::ConstSharedPtr& msg)
{
  std::string axes_str;
  for (size_t i = 0; i < msg->axes.size(); ++i) {
    axes_str += std::to_string(msg->axes[i]) + " ";
  }
  
  std::string buttons_str;
  for (size_t i = 0; i < msg->buttons.size(); ++i) {
    buttons_str += std::to_string(msg->buttons[i]) + " ";
  }
  
  RCLCPP_INFO(this->get_logger(), 
    "Axes: %s\nButtons: %s", 
    axes_str.c_str(), buttons_str.c_str());
}

查看手柄数据

bash

复制代码
# 在终端查看
ros2 topic echo /joy

# 查看发布频率
ros2 topic hz /joy

# 查看话题信息
ros2 topic info /joy

八、总结

组件 类型 范围 说明
msg->axes float32[] -1.0 ~ 1.0 摇杆/扳机值
msg->buttons int32[] 0 或 1 按钮状态
msg->header.stamp time - 时间戳
访问方式 -> - 共享指针使用箭头

关键点

  1. ConstSharedPtr 保证只读安全

  2. ✅ 使用前检查数组大小

  3. ✅ 添加死区防止摇杆漂移

  4. ✅ 速度缩放保证安全

  5. ✅ 添加安全开关(如 A 键启用)

相关推荐
Three_ST42 分钟前
沐神-动手学习深度学习-习题答案4.4模型选择,欠拟合,过拟合
人工智能·python·深度学习·学习·算法
CIO_Alliance44 分钟前
AI深度系列(2)| CNN卷积池化感受野原理:从局部感知到全局视野
人工智能·深度学习·线性代数·算法·计算机视觉·企业ai转型·企业cio联盟
探物 AI1 小时前
机器人清理墙角蜘蛛网,背后需要哪些算法?——从视觉分割到贴墙力控
算法·机器人
青 春 记 忆1 小时前
LeetCode 226. 翻转二叉树|Python 解法详解
python·算法·leetcode
民乐团扒谱机1 小时前
【微科普】压缩感知(CS):违反了奈奎斯特采样定理?不先采集也能还原信号?大白话讲透稀疏采样、L1重建与OMP,一文吃透附代码
python·神经网络·线性代数·算法·数学建模·压缩感知·奈奎斯特
88号技师1 小时前
2026年SCI-分数牛顿衍生优化算法Fractional Newton-derived optimizer-附Matlab免费代码
开发语言·算法·数学建模·matlab·优化算法
夏玉林的学习之路2 小时前
算法7.链式栈
算法
啊嘞嘞?2 小时前
力扣(回文链表)
算法·leetcode·链表
Awh-2 小时前
数据结构 第六章:哈希存储
数据结构·算法·哈希算法