rosxbox

Subscriber to Joy Topic in ROS


I want to integrate my XBOX Gamepad in ROS.

The idea is to write a Subscriber to the topic "/joy" and get the data of each Button and each Axes so that I can use them in controlling my robot.

I followed this Tutorial : " http://wiki.ros.org/joy/Tutorials/WritingTeleopNode " and i wanted to edit it. But they took information only from the Axis. I want to get from the Buttons too.

In addition, I don't want to publish these messages as Twist Messages to the turtle node. I want to send them to a new topic.

Can i have some help or maybe a link to package already done for this type of problems ?


Solution

  • When you subscribe to the sensor_msgs/joy msg, you have a float32[] axes, and a int32[] buttons. In your callback for the topic in your node, you can access the members of the msg as such. Arrays are lists in python and vectors in C++. The classic two paradigms are either to publish your new msg from the callback, doing transformations in the callback, and/or store the data from the callback as a global variable, to be used in another msg callback or a timer/periodic callback.

    You still have to figure out which buttons map to which number element in the array, however. See the normal numberings in the joy_node docs or jstest/jstest-gtk for help on this; store the mappings as an enum somewhere relevant.

    Minimum example:

    // joy_to_something_node.cpp
    
    // ROS
    #include <ros/ros.h>
    #include <sensor_msgs/Joy.h>
    #include <std_msgs/Bool.h>
    #include <geometry_msgs/Vector3Stamped.h>
    
    // Std Libs
    #include <vector>
    #include <stdint.h>
    
    // Subscribers
    //   joy_sub (sensor_msgs/Joy): "joy"
    
    // Publishers
    //   estop_pub (std_msgs/Bool): "estop"
    //   btn_dir_pub (geometry_msgs/Vector3Stamped): "joy/btn_dir"
    
    // Parameters
    //   frequency (double): 10 Hz
    //   lag (double): 0.25 %
    //   // could have the mappings as params
    
    enum {
      JOY_BTN_A = 0,
      JOY_BTN_B = 1,
      JOY_BTN_X = 2,
      JOY_BTN_Y = 3,
      JOY_BTN_LB = 4,
      JOY_BTN_RB = 5,
    };
    
    // ROS Nodehandles and Publishers
    ros::NodeHandle *nh;
    ros::NodeHandle *pnh;
    ros::Publisher estop_pub;
    ros::Publisher btn_dir_pub;
    
    // ROS Callbacks
    void joy_cb(const sensor_msgs::Joy::ConstPtr& msg);
    void timer_cb(const ros::TimerEvent&);
    
    // ROS Params
    double frequency = 10.0;
    double lag = 0.25;
    
    // Globals (ex)
    std::vector<int32_t> btn_dir_data;
    geometry_msgs::Vector3Stamped btn_dir_msg;
    
    int main(int argc, char** argv){
      // Init
      ros::init(argc, argv, "joy_to_something_node");
      nh = new ros::NodeHandle();
      pnh = new ros::NodeHandle("~");
    
      pnh->getParam("frequency", frequency);
      pnh->getParam("lag", lag);
    
      btn_dir_msg.header.frame_id = "base_link";
    
      // Subs
      ros::Subscriber joy_sub = nh->subscribe("joy", 1, joy_cb);
      ros::Timer timer = nh->createTimer(ros::Duration(1.0/frequency), timer_cb);
    
      // Pubs
      estop_pub = nh->advertise<std_msgs::Bool>("estop", 1);
      btn_dir_pub = nh->advertise<geometry_msgs::Vector3Stamped>("joy/btn_dir", 1);
    
      // Exec
      ros::spin();
      return 0;
    }
    
    void joy_cb(const sensor_msgs::Joy::ConstPtr& msg){
      std_msgs::Bool estop_msg;
      estop_msg.data = msg->buttons[JOY_BTN_LB] > 0; // Should be 1 or zero
      estop_pub.publish(estop_msg);
    
      btn_dir_data = msg->buttons;
    }
    
    void timer_cb(const ros::TimerEvent&){
      btn_dir_msg.header.seq++;
      btn_dir_msg.header.stamp = ros::Time::now();
      // X is fwd, Y is right
      btn_dir_msg.vector.x *= lag;
      btn_dir_msg.vector.x += (1.0 - lag) * (btn_dir_data[JOY_BTN_Y] - btn_dir_data[JOY_BTN_A]);
      btn_dir_msg.vector.y *= lag;
      btn_dir_msg.vector.y += (1.0 - lag) * (btn_dir_data[JOY_BTN_B] - btn_dir_data[JOY_BTN_X]);
      btn_dir_pub.publish(btn_dir_msg);
    }
    

    I usually have my CMakeLists.txt have these:

    find_package(catkin REQUIRED
        roscpp
        cmake_modules
        std_msgs
        geometry_msgs
    )
    
    catkin_package(
      INCLUDE_DIRS include 
    #  LIBRARIES [custom libs]
      CATKIN_DEPENDS
        roscpp
        std_msgs
        nav_msgs
    #  DEPENDS [sys libs]
    )
    
    include_directories(
      include
      ${catkin_INCLUDE_DIRS}
    )
    
    add_executable(my_node src/my_node.cpp)
    target_link_libraries(my_node ${catkin_LIBRARIES})
    set_target_properties(my_node PROPERTIES COMPILE_FLAGS "${CPP_DEVEL_FLAGS}")
    

    Likewise, your package.xml depends can just be the ones you also put in CMakeLists.txt, and don't need multiple declarations if you're using the (modern) format=2.

    <buildtool_depend>catkin</buildtool_depend>
    <depend>roscpp</depend>
    <depend>geometry_msgs</depend>
    <depend>std_msgs</depend>
    

    Edit: Added clarifications & the cmake/package.xml.