ROS Bridge Overview and Workflow

Both ROS and Isaac make use of message passing to handle communication between different parts of their respective systems. Communicating between ROS and Isaac requires creating a message translation layer between the two systems. The simplest method for doing so involves turning an Isaac codelet into a full fledged ROS node. The majority of ROS functionality is not required by Isaac, thus the included packages only offer support for the messages commonly installed with a default ROS install. In order to make use of custom ROS messages it is necessary to generate a custom package and point the bazel build system to such a package.

The third-party libraries required for the ROS Bridge are only supported on desktop and Jetson Xavier.

In order to use the ROS bridge you have to install ROS on your device. For example if you want to use the ROS bridge on Jetson you have to install ROS on Jetson. If you want to use ROS on the desktop you have to install ROS on the desktop.

The ROS version you install has to match the operating system of your device. For your desktop system or Jetson Xavier (both running Ubuntu 18.04) you must install ROS Melodic Morenia.

To install ROS follow the instructions from the ROS webpage.

The following files demonstrate how to create a simple ROS-Isaac bridge codelet. The code initializes or makes use of a ROS node during start, then on tick the ROS message queues are pumped and the messages on both sides are translated and rebroadcast. The following ROS packages are included by default in the shipped ROS package

Copy
Copied!
            

roscpp rospy actionlib_msgs control_msgs diagnostic_msgs geometry_msgs map_msgs nav_msgs pcl_msgs sensor_msgs shape_msgs std_msgs stereo_msgs tf2_geometry_msgs tf2_msgs trajectory_msgs visualization_msgs

Copy
Copied!
            

/* Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. NVIDIA CORPORATION and its licensors retain all intellectual property and proprietary rights in and to this software, related documentation and any modifications thereto. Any use, reproduction, disclosure or distribution of this software and related documentation without an express license agreement from NVIDIA CORPORATION is strictly prohibited. */ #pragma once #include <memory> #include <string> #include "engine/alice/alice.hpp" #include "messages/messages.hpp" namespace isaac { namespace rosbridge { // This codelet represents a basic bridge to ROS for communicating navigation events. // It receives Pose2D messages from ROS and emits goal protos // It will also check the current pose of the robot and publish Pose2D messages to ROS class NavigationRosBridge : public alice::Codelet { public: // Explicitly declare constructors and destructors // to get around forward declaration of RosNavigationData NavigationRosBridge(); virtual ~NavigationRosBridge(); void start() override; void tick() override; void stop() override; // Output goal for the robot ISAAC_PROTO_TX(Goal2Proto, goal); // Output pose where the estimated pose of the robot is written ISAAC_POSE2(world, robot); // ROS publisher queue depth ISAAC_PARAM(int, publisher_queue_size, 1000); // ROS subscriber queue depth ISAAC_PARAM(int, subscriber_queue_size, 1000); // ROS publisher channel. Used to broadcast messages to ROS ISAAC_PARAM(std::string, publisher_channel_name, "isaac_navigation2D_status"); // ROS subscriber channel. Used to receive messagse from ROS ISAAC_PARAM(std::string, subscriber_channel_name, "isaac_navigation2D_request"); private: // Hide the ROS implementation details struct RosNavigationData; std::unique_ptr<RosNavigationData> nav_data_; }; } // namespace rosbridge } // namespace isaac ISAAC_ALICE_REGISTER_CODELET(isaac::rosbridge::NavigationRosBridge);


Copy
Copied!
            

/* Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. NVIDIA CORPORATION and its licensors retain all intellectual property and proprietary rights in and to this software, related documentation and any modifications thereto. Any use, reproduction, disclosure or distribution of this software and related documentation without an express license agreement from NVIDIA CORPORATION is strictly prohibited. */ #include "NavigationRosBridge.hpp" #include <memory> #include "engine/core/assert.hpp" #include "geometry_msgs/Pose2D.h" #include "messages/math.hpp" #include "ros/callback_queue.h" #include "ros/ros.h" namespace isaac { namespace rosbridge { namespace { // Callback functor to avoid having a callback function in the bridge class class CallbackFunctor { public: explicit CallbackFunctor(NavigationRosBridge* bridge) { bridge_ = bridge; } CallbackFunctor(const CallbackFunctor&) = default; ~CallbackFunctor() = default; void operator()(const geometry_msgs::Pose2D::ConstPtr& msg) { const Pose2d pose = Pose2d::FromXYA(msg->x, msg->y, msg->theta); auto goal_proto = bridge_->tx_goal().initProto(); ToProto(pose, goal_proto.initGoal()); goal_proto.setGoalFrame("world"); bridge_->tx_goal().publish(); } private: NavigationRosBridge* bridge_; }; } // namespace // Internal struct for holding the ROS node handle and the publisher and subscriber channels // Note the callback queue. To avoid spinning other codelets it is necessary to generate // a separate callback queue per codelet. struct NavigationRosBridge::RosNavigationData { ros::NodeHandle node; ros::Subscriber sub; ros::Publisher pub; ros::CallbackQueue callbackQueue; }; void NavigationRosBridge::start() { // Setup the ros node and communication channels // Make sure to disable the Sigint handler and if any args are needed // add them to the args string. ros::M_string args; if (!ros::isInitialized()) { ros::init(args, "isaacRosBridge", ros::init_options::NoSigintHandler); } nav_data_ = std::make_unique<RosNavigationData>(); nav_data_->node.setCallbackQueue(&(nav_data_->callbackQueue)); nav_data_->pub = nav_data_->node.advertise<geometry_msgs::Pose2D>( get_publisher_channel_name(), get_publisher_queue_size()); nav_data_->sub = nav_data_->node.subscribe<geometry_msgs::Pose2D>( get_subscriber_channel_name(), get_subscriber_queue_size(), CallbackFunctor(this)); // Because ROS requires that the message queues be manually pumped we need to tick. tickPeriodically(); } void NavigationRosBridge::tick() { if (ros::ok()) { bool ok; const Pose2d world_T_robot = get_world_T_robot(getTickTime(), ok); if (ok) { geometry_msgs::Pose2D msg; msg.x = world_T_robot.translation(0); msg.y = world_T_robot.translation(1); msg.theta = world_T_robot.rotation.angle(); nav_data_->pub.publish(msg); } // Pump the queue for this codelet. nav_data_->callbackQueue.callAvailable(); } else { LOG_ERROR("An error has occurred within ROS."); } } void NavigationRosBridge::stop() { nav_data_->pub.shutdown(); nav_data_->sub.shutdown(); nav_data_ = nullptr; } NavigationRosBridge::~NavigationRosBridge() { } NavigationRosBridge::NavigationRosBridge() { } } // namespace rosbridge } // namespace isaac


In order to test a ROS Bridge codelet, it is necessary to create a ROS test node. The following code is a simple node used to test the NavigationRosBridge discussed previously.

Copy
Copied!
            

#include <ros/ros.h> // Include the appropriate message headers for your test case #include <geometry_msgs/Pose2D.h> // Sending random points for navigation testing #include <random> // A simple call back to acknowledge receiving a message void navigationCallback(const geometry_msgs::Pose2D::ConstPtr& msg) { ROS_INFO("Heard: Isaac is at X:%f, Y:%f, Theta: %f", msg->x, msg->y, msg->theta); } constexpr double pi = 3.14159265358979323846; int main(int argc, char **argv) { // Select a random point to send as a goal pose std::random_device rd; std::mt19937_64 eng(rd()); std::uniform_real_distribution<> dist_x(10, 30); std::uniform_real_distribution<> dist_y(10, 30); std::uniform_real_distribution<> dist_theta(-pi, pi); ros::init(argc, argv, "rosBridgeTest"); ros::NodeHandle rosBridgeTestNode; // Channel to publish navigation requests to. // Replace with desired message and channel name for your test case ros::Publisher nav_pub = rosBridgeTestNode.advertise<geometry_msgs::Pose2D>("isaac_navigation2D_request", 1000); // Listen for pose/navigation updates. // Replace with desired message and channel name for your test case ros::Subscriber nav_sub = rosBridgeTestNode.subscribe("isaac_navigation2D_status", 1000, navigationCallback); // Send a message and check updates every ten seconds ros::Rate loop_rate(10); while (ros::ok()) { // Generate a random goal location geometry_msgs::Pose2D pose_msg; pose_msg.x = dist_x(eng); pose_msg.y = dist_y(eng); pose_msg.theta = dist_theta(eng); ROS_INFO("Sent: Move Isaac to X:%f, Y:%f, Theta: %f", pose_msg.x, pose_msg.y, pose_msg.theta); nav_pub.publish(pose_msg); // Manually spin to check messages ros::spinOnce(); loop_rate.sleep(); } return 0; }

To use a custom ROS package, you must first generate a custom workspace which contains the required packages. Refer to engine/build/scripts/ros_package_generation.sh for guidelines on how to create a custom package with the proper path structure. Once such a package exists, modify third_party/ros.bzl to point to the new workspace.

Begin by commenting out the platform specific default package, which should be similar to the following:

Copy
Copied!
            

isaac_new_http_archive( name = "isaac_ros_bridge_x86_64", build_file = clean_dep("//third_party:ros.BUILD"), sha256 = "b2a6c2373fe2f02f3896586fec0c11eea83dff432e65787f4fbc0ef82100070a", url = "https://developer.nvidia.com/isaac/download/third_party/ros_kinetic_x86_64.tar.gz", )

Replace the platform-specific package with the following to point to the new workspace with the custom packages:

Copy
Copied!
            

isaac_new_local_repository( name='isaac_ros_bridge_x86_64', path='path to the workspace', build_file = clean_dep("//third_party:ros.BUILD"), )

The name and build_file fields in the new section must match those of the section being replaced.

© Copyright 2019, NVIDIA Corporation. Last updated on Feb 2, 2023.