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 键启用)

相关推荐
Q264336502314 小时前
【有源码】基于机器学习的电信网络诈骗话术语义识别与可视化分析系统 XGBoost算法+Hadoop+Spark
大数据·hadoop·算法·机器学习·spark·毕业设计·课程设计
不会就选b14 小时前
算法日常・每日刷题--贪心<11>
算法·leetcode·职场和发展
千里码aicood14 小时前
面向电商冷启动与数据稀疏性的协同过滤算法优化研究
大数据·算法·协同过滤
xxxiugou12314 小时前
双指针解题秘籍:从入门到精通
c语言·数据结构·c++·算法
能源革命14 小时前
TimesFM(Time Series Foundation Model)介绍
算法·能源
猎嘤一号14 小时前
学术训练(一)文献检索与阅读
人工智能·算法·机器学习·科研入门
Doubbbbbbble云14 小时前
多路归并算法在外部排序中的实现与优化
算法
沉淀的.晴天14 小时前
FreeRTOS信号量
数据结构·算法
青山木15 小时前
Hot 100 --- 打家劫舍
java·数据结构·算法·leetcode·动态规划
机器学习之心15 小时前
均值、中值、圆周期:三种 InSAR 干涉相位滤波的实测对比与参数取舍
算法·均值算法