ROS / URDF

The HEBI C++ API can be easily integrated into an existing ROS node, or used as the basis for a new ROS node.

Because of the flexibility of ROS, we have chosen not to create a single hardware interface node at this time.  Instead, we are creating a set of example nodes that demonstrate interoperability of HEBI hardware with ROS.  We are also creating a library of URDF xacro files that represent the various physical HEBI components.

Currently, our complete ROS examples and URDF macros are in a beta testing phase with select partners.  If you do require these capabilities and want to be part of our pre-release testing, please contact us.  In the meantime, the code snippets below show how HEBI modules can work with some existing ROS conventions.


Joint Commands

Below, we subscribe to the joint_commands channel and convert incoming messages to commands to send to HEBI actuators.

// Callback function to respond to joint commands
void commandCallback(
    const sensor_msgs::JointState::ConstPtr& message)
{
    hebi::GroupCommand cmd(group->size());
    for (int i = 0; i < message->position.size(); ++i)
        cmd[i].actuator().position().set(message->position[i]);
    for (int i = 0; i < message->velocity.size(); ++i)
        cmd[i].actuator().velocity().set(message->velocity[i]);
    for (int i = 0; i < message->effort.size(); ++i)
        cmd[i].actuator().torque().set(message->effort[i]);
    group->sendCommand(cmd);
}

...

ros::Subscriber sub =
    n.subscribe("joint_commands", 5, commandCallback);

Joint Feedback

Below, we convert feedback from a group of HEBI actuators to a JointState message that is sent on the joint_states channel.

ros::Publisher joint_publisher =
    n.advertise<sensor_msgs::JointState>("joint_states", 10);

// Add feedback handler to be called from the background thread (uses C++ 11 lambda functions)
group->addFeedbackHandler(
    [&joint_publisher, &joint_names]
    (const hebi::GroupFeedback* feedback)->void
    {
        sensor_msgs::JointState message;
        message.header.stamp = ros::Time::now();
        
        // Add feedback:
        for (int i = 0; i < feedback->size(); ++i)
        {
            message.name.push_back(joint_names[i].c_str());
            
            auto position = (*feedback)[i].actuator().position();
            if (position)
                message.position.push_back(position.get());
                
            auto velocity = (*feedback)[i].actuator().velocity();
            if (velocity)
                message.velocity.push_back(velocity.get());
                
            auto torque = (*feedback)[i].actuator().torque();
            if (torque)
                message.effort.push_back(torque.get());
        }
        
        joint_publisher.publish(message);
    }
);

group->setFeedbackFrequencyHz(25);

Transforms/Robot State Publisher/RViz

The example below adds to the previous example by broadcasting odometry messages to enable visualization in RViz (when coupled with the appropriate Robot State Publisher and URDF files).

ros::Publisher joint_publisher =
    n.advertise<sensor_msgs::JointState>("joint_states", 10);
tf::TransformBroadcaster broadcaster;

// Declare base odometry message for robot state publisher
geometry_msgs::TransformStamped odom_transform;
odom_transform.header.frame_id = "odom";
odom_transform.child_frame_id = "base";
odom_transform.transform.rotation =
    tf::createQuaternionMsgFromYaw(0);

// Add feedback handler to be called from the background thread (uses C++ 11 lambda functions)
group->addFeedbackHandler(
    [&joint_publisher, &broadcaster, &odom_transform, &joint_names] (const hebi::GroupFeedback* feedback)->void
    {
        sensor_msgs::JointState message;
        message.header.stamp = ros::Time::now();
        odom_transform.header.stamp = ros::Time::now();
        
        // Add feedback:
        for (int i = 0; i < feedback->size(); ++i)
        {
            message.name.push_back(joint_names[i].c_str());
            
            auto position = (*feedback)[i].actuator().position();
            if (position)
                message.position.push_back(position.get());
                
            auto velocity = (*feedback)[i].actuator().velocity();
            if (velocity)
                message.velocity.push_back(velocity.get());
                
            auto torque = (*feedback)[i].actuator().torque();
            if (torque)
                message.effort.push_back(torque.get());
        }
        
        joint_publisher.publish(message);
        broadcaster.sendTransform(odom_transform);
    }
);

group->setFeedbackFrequencyHz(25);

URDF

Creating URDF models using HEBI xacro elements is quick and intuitive.  The following snippet produces the 5-DOF arm seen in the HEBI Lifting video.

<xacro:x5 joint="base|arm" child="shoulder-bracket"/>
<xacro:x_shoulder_bracket name="shoulder-bracket" child="shoulder|arm"/>
<xacro:x5 joint="shoulder|arm" child="shoulder-elbow"/>
<xacro:x_tube_link name="shoulder-elbow" child="elbow|arm" length="0.3302" twist="0"/>
<xacro:x5 joint="elbow|arm" child="elbow-wrist"/>
<xacro:x_tube_link name="elbow-wrist" child="wrist|arm" length="0.2667" twist="0"/>
<xacro:x5 joint="wrist|arm" child="wrist-hand"/>
<xacro:x_wrist_bracket name="wrist-hand" child="hand|arm"/>
<xacro:x5 joint="hand|arm" child="end_effector"/>