Binary packages are precompiled versions of the software that can be installed directly using a package manager (e.g. apt). nstalling from packages is the recommended method, as it installs necessary dependencies automatically and also updates alongside regular system updates.
Building from source
When you install from source, you download the raw code and compile it on your system. Building from source is recommended for platforms where binaries arenβt available or supported. Building from source also gives you the option to install the absolute latest version of the software.
The ROS 2 development environment needs to be correctly configured before use. This can be done in two ways: either sourcing the setup files in every new shell you open, or adding the source command to your startup script.
ROS 2 relies on the notion of combining workspaces using the shell environment. βWorkspaceβ is a ROS term for the location on your system where youβre developing with ROS 2. The core ROS 2 workspace is called the underlay. Subsequent local workspaces are called overlays. When developing with ROS 2, you will typically have several workspaces active concurrently.
Option 1: Source the setup files in each shell manually
# Replace ".bash" with your shell if you're not using bash# Possible values are: setup.bash, setup.sh, setup.zshsource /opt/ros/humble/setup.bash
You will need to run this command in every new terminal session to access ROS 2 commands, unless you automate the sourcing.
Option 2 (Recommended): Add sourcing to your shell startup script
A ROS workspace is a directory with a particular structure. Commonly there is a src subdirectory. Inside that subdirectory is where the source code of ROS packages will be located. Typically the directory starts otherwise empty.
It is important that we have sourced the environment for an existing ROS 2 installation that will provide our workspace with the necessary build dependencies for the example packages. This is achieved by sourcing the setup script provided by a binary installation or a source installation, i.e., another colcon workspace (see Installation). We call this environment an underlay.
Create directory
mkdir -p ros2_ws/srccd ros2_ws/src
Clone the ros_tutorialsrepo, which contains turtlesim package, into the src folder
build/: Contains the build files generated by colcon build.
install/: Contains the installed packages and setup files necessary for running the workspace.
log/: Contains logs of the build process for debugging and analysis.
src/: The source directory where all ROS2 packages are placed. In this case, it contains the cloned ros_tutorials repository, which includes the turtlesim package.
Open a new terminal and source the setup file for your main ROS 2 installation.
Next, source the local_setup file from the training_ws overlay to enable access to the packages within the training_ws workspace.
source /opt/ros/humble/setup.bash# cd into ros2_wssource install/local_setup.bash
Build the workspace by running colcon build in the root of the workspace
--symlink-install allows the installed files to be changed by changing the files in the source space (e.g. Python files or other non-compiled resources) for faster iteration
you will create nodes that pass information in the form of string messages to each other over a topic. The example used here is a simple βtalkerβ and βlistenerβ system; one node publishes data and the other subscribes to the topic so it can receive that data.
The code used in these examples can be found here.
Create a package
cd ros2_ws/srcros2 pkg create --build-type ament_python --license Apache-2.0 py_pubsub
Write the publisher node
cd py_pubsub/py_pubsubwget https://raw.githubusercontent.com/ros2/examples/humble/rclpy/topics/minimal_publisher/examples_rclpy_minimal_publisher/publisher_member_function.py
In this step, you will declare that your package depends on rclpy and std_msgs to run.
cd .. # Navigate to the parent directory where your package.xml is locatednano package.xml # Open the file in nano editor# Add the following lines<exec_depend>rclpy</exec_depend><exec_depend>std_msgs</exec_depend>
Add an entry point
Add 'talker = py_pubsub.publisher_member_function:main' within the console_scripts brackets
The subscriberβs callback gets called as soon as it receives a message
cd py_pubsubwget https://raw.githubusercontent.com/ros2/examples/humble/rclpy/topics/minimal_subscriber/examples_rclpy_minimal_subscriber/subscriber_member_function.py
subscriber_member_function.py
import rclpyfrom rclpy.node import Nodefrom std_msgs.msg import Stringclass MinimalSubscriber(Node): def __init__(self): super().__init__('minimal_subscriber') self.subscription = self.create_subscription( String, 'topic', self.listener_callback, 10) self.subscription # prevent unused variable warning def listener_callback(self, msg): self.get_logger().info('I heard: "%s"' % msg.data)def main(args=None): rclpy.init(args=args) minimal_subscriber = MinimalSubscriber() rclpy.spin(minimal_subscriber) # Destroy the node explicitly # (optional - otherwise it will be done automatically # when the garbage collector destroys the node object) minimal_subscriber.destroy_node() rclpy.shutdown()if __name__ == '__main__': main()
Add the entry point for the subscriber node below the publisherβs entry point
Turtlesim is a lightweight simulator for learning ROS 2. It illustrates what ROS 2 does at the most basic level to give you an idea of what you will do with a real robot or a robot simulation later on.
This tutorial touches upon core ROS 2 concepts, like nodes, topics, and services. All of these concepts will be elaborated on in later tutorials; for now, you will simply set up the tools and get a feel for them
start turtlesim
ros2 run turtlesim turtlesim_node
[INFO] [1729587409.041614825] [turtlesim]: Starting turtlesim with node name /turtlesim
[INFO] [1729587409.072164425] [turtlesim]: Spawning turtle [turtle1] at x=[5.544445], y=[5.544445], theta=[0.000000]
Use rqt_graph to see the running node and topics itβs subscribed to
Click refresh if window is blank
rqt_graph
use turtlesim
Open another terminal to run a new node to control the turtle
ros2 run turtlesim turtle_teleop_key
Refresh rqt_graph to see how teleop node is interacting w/ the turtlesim node
In this setup, the /teleop_turtle node publishes velocity commands to /turtle1/cmd_vel, which is consumed by /turtlesim to move the turtle. Additionally, action-related topics handle feedback and status for rotating the turtle to specific angles. The rqt_graph visually represents the connections between nodes and the topics they communicate over.
Show the details of a message- the structure of data it expects
ros2 interface show geometry_msgs/msg/Twist
This same structure was shown with the echo command earlier
# This expresses velocity in free space broken into its linear and angular parts.
Vector3 linear
float64 x
float64 y
float64 z
Vector3 angular
float64 x
float64 y
float64 z
Pubish data direclty to a topic from the command line
ros2 topic pub <topic_name> <msg_type> '<args>'
Example using YAML syntax
With no command-line options, ros2 topic pub publishes the command in a steady stream at 1 Hz.
A package is an organizational unit for your ROS 2 code. If you want to be able to install your code or share it with others, then youβll need it organized in a package. With packages, you can release your ROS 2 work and allow others to build and use it easily.
Package creation in ROS 2 uses ament as its build system and colcon as its build tool. You can create a package using either CMake or Python, which are officially supported, though other build types do exist.
ROS2 Python packages
my_package/
βββ package.xml # File containing meta-information about the package
βββ resource/
β βββ my_package # Marker file for the package.
βββ setup.cfg # Required when a package has executables, so `ros2 run` can locate them.
βββ setup.py # Contains instructions for how to install the package.
βββ my_package/ # A directory with the same name as your package, used by ROS 2 tools to locate your package; it
# contains `__init__.py`.
ROS2 CMake Packages
my_package/
βββ CMakeLists.txt # Describes how to build the code within the package
βββ include/
β βββ my_package/ # Directory containing the public headers for the package
βββ package.xml # File containing meta-information about the package
βββ src/ # Directory containing the source code for the package
Use tf2_echo to debug or analyze transformations in a ROS system by displaying the relative pose (position and orientation) between two coordinate frames
ros2 run tf2_ros tf2_echo [source_frame] [target_frame]
Show the transfrom of turtle2 frame with respect to turtle1 frame
ros2 run tf2_ros tf2_echo turtle2 turtle1
Use rviz2, a visualization tool, to examine the tf2 frames
ros2 run rviz2 rviz2 -d $(ros2 pkg prefix --share turtle_tf2_py)/rviz/turtle_rviz.rviz
In the context of the ROS tf (transform) framework, the transform data provides the relationship between the two frames: the turtle1 frame (reference) and the turtle2 frame (follower). This includes:
Components of the Transform
Translation (Position Difference):
The difference in position between turtle1 and turtle2, usually expressed as a 2D vector ((dx), (dy)) in the turtle1 frame. This tells you where turtle2 is relative to turtle1βs position.
The difference in orientation between turtle1 and turtle2, often expressed as a single angular value ((yaw)) in 2D (since turtlesim operates in a plane). This tells you how much turtle2 needs to rotate to face the direction of turtle1.
yaw=ΞΈturtle1ββΞΈturtle2β
Transform in Action
The listener node continuously receives the transformation data (translation and rotation) between turtle1 and turtle2.
The control logic uses this transform to:
Compute the angular difference (needed to rotate turtle2).
Compute the linear distance (needed to move turtle2 forward).
These calculated differences are used to set the velocities for turtle2:
Angular Velocity aligns turtle2 with turtle1.
Linear Velocity moves turtle2 toward turtle1.
Example
Given Information:
turtle1 is at the origin: ((0, 0)).
turtle2 starts at ((1.544, 3.544)) and faces 0 degrees (along the positive x-axis).
Relative Translation:
The position vector from turtle2 to turtle1 is:
dx=0β1.544=β1.544dy=0β3.544=β3.544
Distance Between Turtles:
The Euclidean distance between the two turtles is:
distance=dx2+dy2β
Substituting the values:
distance=(β1.544)2+(β3.544)2β=3.847units
Angle to Face Turtle1:
The angle turtle2 needs to rotate to face turtle1 is given by:
ΞΈ=arctan2(dy,dx)
Substituting:
ΞΈ=arctan2(β3.544,β1.544)
This evaluates to:
ΞΈββ1.977radians(orΒ β113.3β)
Action Plan:
Angular Velocity:
Rotate turtle2 by ( \theta ) until it aligns with the vector pointing to turtle1.
Linear Velocity:
Move turtle2 forward along this vector to close the distance.
Code with Motion Strategy
The ROS implementation will first align turtle2 with the direction of turtle1 and then move it forward:
#!/usr/bin/env python3import rospyfrom geometry_msgs.msg import Twistfrom turtlesim.msg import Poseimport mathdef calculate_angle_and_distance(turtle1_pose, turtle2_pose): # Relative position dx = turtle1_pose.x - turtle2_pose.x dy = turtle1_pose.y - turtle2_pose.y # Distance between turtles distance = math.sqrt(dx**2 + dy**2) # Angle that turtle2 needs to face desired_angle = math.atan2(dy, dx) return desired_angle, distancedef move_turtle2(): rospy.init_node('turtle2_follow', anonymous=True) # Publishers and subscribers pub = rospy.Publisher('/turtle2/cmd_vel', Twist, queue_size=10) turtle2_pose = rospy.wait_for_message('/turtle2/pose', Pose) turtle1_pose = rospy.wait_for_message('/turtle1/pose', Pose) rate = rospy.Rate(10) # 10 Hz while not rospy.is_shutdown(): # Calculate required angle and distance desired_angle, distance = calculate_angle_and_distance(turtle1_pose, turtle2_pose) # Angle difference (rotation needed for turtle2) angle_difference = desired_angle - turtle2_pose.theta # Normalize the angle difference to [-pi, pi] angle_difference = math.atan2(math.sin(angle_difference), math.cos(angle_difference)) # Velocity commands twist = Twist() # Rotate to align with turtle1 if abs(angle_difference) > 0.01: # Allow small tolerance twist.angular.z = 2.0 * angle_difference # Proportional control twist.linear.x = 0.0 # No forward motion until aligned else: # Move forward when aligned twist.angular.z = 0.0 twist.linear.x = 1.5 * distance # Proportional control # Publish velocity pub.publish(twist) # Update current pose turtle2_pose = rospy.wait_for_message('/turtle2/pose', Pose) rate.sleep()if __name__ == '__main__': try: move_turtle2() except rospy.ROSInterruptException: pass
Expected Behavior
turtle2 computes the angle difference (\theta) and rotates to face turtle1.
Once aligned, it moves forward along the computed direction vector, closing the distance between the two turtles.
This ensures turtle2 correctly rotates to face turtle1 and moves toward it, respecting the given scenario.