Using Positional Tracking with ROS 2
Position with RVIZ
In this tutorial, you will learn in detail how to configure your own RVIZ session to see only the position data information that you require.
RVIZ provides plugins for visualizing the camera’s pose and its path over time.
Camera pose
The Pose plugin provides a visualization of the position and orientation of the camera (geometry_msgs/PoseStamped) in the Map frame.
The Topic to be subscribed to is ~/pose.
![]()
Key parameters:
Topic: Selects the odometry topic/zed/zed_node/odomUnreliable: Check this to reduce the latency of the messagesPosition toleranceandAngle tolerance: set both to0to be sure to visualize all positional dataKeep: Set to the number of messages to visualize at a timeShape: You can visualize odometry information as an arrow pointing in the direction of theXaxis, or as a threeAxesframe. Both shapes can be resized to your preference
Camera path
The ZED wrapper provides two different paths for the camera position and orientation:
~/path_map: The history of the camera pose in Map frame~/path_odom: The history of the odometry of the camera in Map frame
![]()
Above you can see both the Pose (green) and the Odometry (red) paths.
The odometry pose is calculated with a pure “visual odometry” algorithm as the sum of the movement from one step to the next. It is therefore affected by drift.
The camera pose is instead continuously fixed using the StereoLabs tracking algorithm that combines visual information, space memory information and if using a “ZED-M” or a “ZED2”, inertial information.
The parameters to be configured are analogous to the parameters seen above for the Pose plugin.
Position info subscribing in C++
In this tutorial, you will learn how to write a simple C++ node that subscribes to messages of type geometry_msgs/PoseStamped and nav_msgs/Odometry to retrieve the position and the orientation of the ZED camera in the Map and in the Odometry frames.
Introduction
Open a new console and use this command to connect the camera to the ROS 2 network:
the old launch command ros2 launch zed_display_rviz2 display_<camera model>.launch.py is now obsolete and will be removed in a future release of the wrapper.
The ZED node will start to publish image data in the network only if there is another node that subscribes to the relative topic.
Running the tutorial
If you properly followed the ROS 2 Examples Installation Guide, the executable of this tutorial has been compiled and you can run the subscriber node using this command:
The tutorial node subscribes to generic pose and odom topics, so a remapping is required to connect to the correct topics published by the ZED node:
If the ZED node is running and a camera is connected or you have loaded an SVO file, you will receive the following stream of messages confirming that you are correctly subscribing to the ZED positional tracking topics:
The code
The source code of the subscriber node zed_tutorial_pos_tracking.cpp:
The code explained
The tutorial is written using the new concept of Component introduced in ROS 2 in order to take advantage of the node composition capabilities.
A component named MinimalPoseOdomSubscriber is created subclassing the ROS 2 object rclcpp::Node.
In the constructor, we initialize the parent class Node with the name of our node new zed_odom_pose_tutorial:
The constructor mainly defines mPoseSub and mOdomSub, a std::SharedPtr to two objects that create two subscribers in the node. The subscribers check for topics of type pose and odom and recall the callback functions MinimalPoseOdomSubscriber::poseCallback and MinimalPoseOdomSubscriber::odomCallback every time they receive one of them.
ROS 2 uses the new constructs available with C++11 and C++14, so to bind the callbacks (a class method in this case) to the mPoseSub and mOdomSub objects we use the form
std::bind(&MinimalDepthSubscriber::<callback_name>, this, _1).
It is important that the two subscriptions use a QOS profile compatible with the QOS profile of the publisher of the topics.
In this case, the QOS profile is configured to keep the last received 10 messages with “best effort” reliability and “volatile” durability. This configuration is highly compatible with many possible publisher configurations.
For more information about QoS compatibility, refer to the ZED node guide
Now we must define the callbacks to execute when one of the subscribed topics is received:
The two callbacks are very similar; the only difference is that poseCallback receives messages of type geometry_msgs/PoseStampedand odomCallback receives messages of type nav_msgs/Odometry. These are similar but not identical. However, the information extracted by the two topics is the same: camera position and camera orientation.
Extracting the position is straightforward since the data is stored in a vector of three floating-point elements.
Extracting the orientation is less straightforward as it is published as a quaternion vector. To convert the quaternion to a more readable form, we must first convert it to a 3x3 rotation matrix from which we can finally extract the three values for Roll, Pitch and Yaw in radians.
Finally, we can print the information received to the screen after converting the radian values to degrees with the timestamp of the message.
Finally, the main function is standard for a ROS 2 node that executes a single component:
The ROS 2 environment is initialized using the rclcpp::init command. Then we create a pos_track_node Component as a std::shared_ptr.
to the MinimalPoseOdomSubscriber class that we defined above.
Then the code of the node is executed in the main thread using the rclcpp::spin(pos_track_node); command. The node will run until the user presses Ctrl+C to stop it, shut it down, and exit.
Conclusions
The full source code of this tutorial is available on GitHub in the zed_tutorial_pos_tracking sub-package.
Along with the node source code, you can find the package.xml and CMakeLists.txt files that complete the tutorial package.

