source /opt/ros/humble/setup.bash
nano ~./bashrc
then add below command in the last line and save
source /opt/ros/humble/setup.bash
ros2 run demo_nodes_py listener
Demo Node to Publish:
ros2 run demo_nodes_cpp talker
ros2 node list
ros2 node info <node_name>
ros2 topic list
ros2 topic list -t
ros2 topic info /chatter
Example Output:
Type: std_msgs/msg/String
Publisher count: 1
Subscription count: 0
ros2 interface show std_msgs/msg/String
Example Output:
#This was originally provided as an example message.
#It is deprecated as of Foxy
#It is recommended to create your own semantically meaningful message.
#However if you would like to continue using this please use the equivalent in example_msgs.
string data
ros2 topic echo /chatter
ros2 topic pub -r 9 /chatter std_msgs/msg/String "{data: 'Nabil'}"
where, -r is the rate, is a topic publising by node
ros2 topic hz /chatter
ros2 topic bw /chatter
rqt_graph
Start Turtlesim Graphical Tool
ros2 run turtlesim turtlesim_node
start turtle teleop keyboard to be able to control the turtle
ros2 run turtlesim turtle_teleop_key
here turtlesim is a package and turtlesim_node and turtle_teleop_key are the node.
sudo apt install python3-colcon-common-extensions
enable colcon autocompletion
cd /usr/share/colcon_argcomplete/hook/
use command ls and you should see colcon-argcomplete.bash colcon-argcomplete.zsh Now we have to add this in bashrc file
nano ~/.bashrc
add below line and save
source /usr/share/colcon_argcomplete/hook/colcon-argcomplete.bash
go to home directory using command
cd
then
mkdir ros2_ws
enter ros2_ws folder/directory using
cd ros2_ws
create a folder/directory src using
mkdir src
then from ros2_ws directory type
colcon build
source ~/ros2_ws/install/setup.bash
Useful Video: https://youtu.be/iBGZ8LEvkCY
go to src directory using this command
cd ~/ros2_ws/src/
then create package using this command
ros2 pkg create my_robot_controller --build-type ament_python --dependencies rclpy
here, Package Name: my_robot_controller Build Tool : Colcon Build System : ament_python for Python package or ament_cmake for C++ package rclpy : python client library for ROS2 rclcpp : C++ client library for ROS2
download visual studio code using the following command
sudo snap install code --classic
Open Visual Studio in the same directory just type
code .
visual studio will be open and click trust
colcon build
if you find any error you may need to downgrade python package setuptools to 58.2.0
If pip3 is not installed
sudo apt install python3-pip
then
pip3 install setuptools==58.2.0
Now build using
colcon build
Go to the directory ros2_ws > src > my_robot_controller > my_robot_controller
cd ~/ros2_ws/src/my_robot_controller/my_robot_controller
create my_first_node.py file
touch my_first_node.py
Now change permission to make it executable
chmod +x my_first_node.py
Now open visual studio in the same directory (i.e. ros2_ws > src > my_robot_controller > my_robot_controller)
code .
and add following code and save the my_first_node.py
file
#! /usr/bin/env python3
import rclpy
from rclpy.node import Node
class MyNode(Node):
def __init__(self):
super().__init__("nabil_first_node")
# node name : first_node but this file name my_first_node
# remember executable name may be different in setup.py file
self.counter_ = 1
# This node first print the following line
self.get_logger().info("Hello from nabil_first_node")
# This timer will call timer_callback function in every 0.5 Second
self.create_timer(0.5, self.timer_callbac)
def timer_callbac(self):
self.get_logger().info("www.nabilbd.com " + "Counter: " + str(self.counter_))
self.counter_ = self.counter_ + 1
def main(args = None):
# Start the node
rclpy.init(args = args)
node = MyNode()
# spin will keep the node running
rclpy.spin(node)
# Finally Shutdown the node
rclpy.shutdown()
if __name__ == '__main__':
main()
Now open setup.py file and add code in console_scripts. Actually it's the instruction to create executables.
We are going to add "test_node = my_robot_controller.my_first_node:main"
Where,
Executable Name = test_node
Package Name = my_robot_controller
Python File Name = my_first_node
main function = main
Full code setup.py
from setuptools import setup
package_name = 'my_robot_controller'
setup(
name=package_name,
version='0.0.0',
packages=[package_name],
data_files=[
('share/ament_index/resource_index/packages',
['resource/' + package_name]),
('share/' + package_name, ['package.xml']),
],
install_requires=['setuptools'],
zip_safe=True,
maintainer='nabil',
maintainer_email='www.nabilbd.com',
description='TODO: Package description',
license='TODO: License declaration',
tests_require=['pytest'],
entry_points={
'console_scripts': [
"test_node = my_robot_controller.my_first_node:main"
# Executable Name = test_node
# Package Name = my_robot_controller
# Python File Name = my_first_node
# main function = main
],
},
)
From ~/ros2_ws
workspace build the update that we have made
colcon build
Now run the node
ros2 run my_robot_controller test_node
Everytime we made a change in our code we have to build. But Python is an interpreted lanugae. An interpreted language is a programming language which are generally interpreted, without compiling a program into machine instructions.
to avoid build every time we can do following thing
in ros2_ws
workspace
colcon build --symlink-install
then source bashrc
source ~/.bashrc
Warning: If package not found error popup then
cd ~/ros2_ws
colcon build
source install/setup.bash
From now you can just change the python code and after saving change will be reflected without building.
Now run the node and see visually using rqt_graph
You can see node info while node is running
ros2 node list
ros2 node info /nabil_first_node
List of All the Packages
ros2 pkg list
Search or Filter package
ros2 pkg list | grep PACKAGE_NAME
See executables under certain package
ros2 pkg executables turtlesim
See package location ros2 pkg prefix --share PACKAGE_NAME
ros2 pkg prefix --share turtlesim
Runs executables provided by a package
ros2 run <package_name> <executable_file>
lets create another node name draw_circle
as like test_node
follow previous node creation process and also update setup.py
and package.xml
draw_circle.py Code:
#! /usr/bin/env python3
import rclpy
from rclpy.node import Node
from geometry_msgs.msg import Twist
class DrawCircleNode(Node):
def __init__(self):
super().__init__("draw_circle")
self.cmd_vel_publisher_ = self.create_publisher(Twist, "/turtle1/cmd_vel",10)
self.timer_ = self.create_timer(0.5, self.send_velocity_command)
self.get_logger().info("Draw Circle Node has been Started")
def send_velocity_command(self):
msg = Twist()
msg.linear.x = 2.0
msg.angular.z = 1.0
self.cmd_vel_publisher_.publish(msg)
def main(args = None):
rclpy.init(args = args)
node = DrawCircleNode()
rclpy.spin(node)
rclpy.shutdown()
setup.py code:
from setuptools import setup
package_name = 'my_robot_controller'
setup(
name=package_name,
version='0.0.0',
packages=[package_name],
data_files=[
('share/ament_index/resource_index/packages',
['resource/' + package_name]),
('share/' + package_name, ['package.xml']),
],
install_requires=['setuptools'],
zip_safe=True,
maintainer='nabil',
maintainer_email='www.nabilbd.com',
description='TODO: Package description',
license='TODO: License declaration',
tests_require=['pytest'],
entry_points={
'console_scripts': [
"test_node = my_robot_controller.my_first_node:main",
# Executable Name = test_node
# Package Name = my_robot_controller
# Python File Name = my_first_node
# main function = main
"draw_circle = my_robot_controller.draw_circle:main"
],
},
)
package.xml file:
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>my_robot_controller</name>
<version>0.0.0</version>
<description>TODO: Package description</description>
<maintainer email="[email protected]">nabil</maintainer>
<license>TODO: License declaration</license>
<depend>rclpy</depend>
<depend>geometry_msgs</depend>
<depend>turtlesim</depend>
<test_depend>ament_copyright</test_depend>
<test_depend>ament_flake8</test_depend>
<test_depend>ament_pep257</test_depend>
<test_depend>python3-pytest</test_depend>
<export>
<build_type>ament_python</build_type>
</export>
</package>
#! /usr/bin/env python3
import rclpy
from rclpy.node import Node
from turtlesim.msg import Pose
class PoseSubscriberNode(Node):
def __init__(self):
super().__init__("pose_subscriber")
self.get_logger().info("Pose Subscriber Node has been started")
self.create_subscription(Pose,"/turtle1/pose", self.pose_callback, 10)
def pose_callback(self, msg: Pose):
self.get_logger().info(str(msg))
def main(args = None):
rclpy.init(args=args)
node = PoseSubscriberNode()
rclpy.spin(node)
rclpy.shutdown()
Step 1: Run TurtleSim Node
ros2 run turtlesim turtlesim_node
Step 2: See all ros2 topic
ros2 topic list
Step 3: We are interested in /turtle1/cmd_vel
topic. So we need more info
ros2 topic info /turtle1/cmd_vel
Step 4: Now we will see the Type:
ros2 interface show geometry_msgs/msg/Twist
Step 5: We have to Publish Twist message in /turtle1/cmd_vel
topic command format is
ros2 topic pub -t 1 <topic> <message_type> <message>
Where, -t is how many times we want to send the message
Tip: Type ros2 topic pub -t 1 /turtle1/cmd_vel geometry_msgs/msg/Twist "lin
and press tab
and then fill the message
So we are sending
ros2 topic pub -t 1 /turtle1/cmd_vel geometry_msgs/msg/Twist "linear:
x: 2.0
y: 0.0
z: 0.0
angular:
x: 0.0
y: 0.0
z: 0.0"
You will see that Turtle is moving towards x direction.
Services are another method of communication for nodes in the ROS graph. Services are based on a call-and-response model, versus topics’ publisher-subscriber model. While topics allow nodes to subscribe to data streams and get continual updates, services only provide data when they are specifically called by a client.
https://docs.ros.org/en/foxy/Tutorials/Beginner-CLI-Tools/Understanding-ROS2-Services/Understanding-ROS2-Services.html
Run a demo service
ros2 run demo_nodes_cpp add_two_ints_server
Our goal is to send two number and this service will give us back the summation result.
See service list
ros2 service list
To see in details
ros2 service list -t
ros2 service type /add_two_ints
ros2 interface show example_interfaces/srv/AddTwoInts
Call a Service
ros2 service call <service_name> <service_type> <arguments>
Now we will send two number to the service
ros2 service call /add_two_ints example_interfaces/srv/AddTwoInts "{'a':2,'b':3}"
https://docs.ros.org/en/foxy/Tutorials/Beginner-CLI-Tools/Understanding-ROS2-Parameters/Understanding-ROS2-Parameters.html
First, start TurtleSim Node
ros2 run turtlesim turtle_node
More Turtle:
ros2 service call /spawn turtlesim/srv/Spawn "{'x': 5.0, 'y':5.0, 'theta':90.0, 'name':'commando'}"
Clear Trace:
ros2 service call /clear std_srvs/srv/Empty
Credit: https://youtu.be/vCTbUgw6k8U
#!/usr/bin/env python3
# Code Credit: https://youtu.be/vCTbUgw6k8U
import rclpy
from rclpy.node import Node
from turtlesim.msg import Pose
from geometry_msgs.msg import Twist
from turtlesim.srv import SetPen
from functools import partial
class TurtleControllerNode(Node):
def __init__(self):
super().__init__("turtle_controller")
self.previous_x_ = 0
self.cmd_vel_publisher_ = self.create_publisher(Twist, "/turtle1/cmd_vel", 10)
self.pose_subscriber_ = self.create_subscription(Pose, "/turtle1/pose", self.pose_callback, 10)
self.get_logger().info("Turtle Controller with Servcie")
def pose_callback(self, pose: Pose):
cmd = Twist()
if pose.x > 9.0 or pose.x < 2.0 or pose.y > 9.0 or pose.y < 2.0:
cmd.linear.x = 1.0
cmd.angular.z = 0.9
else:
cmd.linear.x = 5.0
cmd.angular.z = 0.0
self.cmd_vel_publisher_.publish(cmd)
if pose.x > 5.5 and self.previous_x_ <= 5.5:
self.previous_x_ = pose.x
self.get_logger().info("Set color to red")
self.call_set_pen_service(255, 0, 0, 3, 0)
elif pose.x <= 5.5 and self.previous_x_ > 5.5:
self.previous_x_ = pose.x
self.get_logger().info("Set color to red")
self.call_set_pen_service(0, 255 ,0, 3, 0)
def call_set_pen_service(self, r, g, b, width, off):
client = self.create_client(SetPen, "/turtle1/set_pen")
while not client.wait_for_service(1.0): # 1.0 is timeout in second
self.get_logger().warn("Waiting for service ...")
request = SetPen.Request()
request.r = r
request.g = g
request.b = b
request.width = width
request.off = off
future = client.call_async(request)
future.add_done_callback(partial(self.callback_set_pen))
def callback_set_pen(self, future):
try:
response = future.result()
except Exception as e:
self.get_logger().error("Service call failed: %r" % (e,))
def main(args = None):
rclpy.init(args = args)
node = TurtleControllerNode()
rclpy.spin(node)
rclpy.shutdown()
setup.py
file:
from setuptools import setup
package_name = 'my_robot_controller'
setup(
name=package_name,
version='0.0.0',
packages=[package_name],
data_files=[
('share/ament_index/resource_index/packages',
['resource/' + package_name]),
('share/' + package_name, ['package.xml']),
],
install_requires=['setuptools'],
zip_safe=True,
maintainer='nabil',
maintainer_email='www.nabilbd.com',
description='TODO: Package description',
license='TODO: License declaration',
tests_require=['pytest'],
entry_points={
'console_scripts': [
"test_node = my_robot_controller.my_first_node:main",
# Executable Name = test_node
# Package Name = my_robot_controller
# Python File Name = my_first_node
# main function = main
"draw_circle = my_robot_controller.draw_circle:main",
"pose_subscriber = my_robot_controller.pose_subscriber:main",
"turtle_controller = my_robot_controller.turtle_controller:main"
],
},
)
The launch system in ROS 2 is responsible for helping the user describe the configuration of their system and then execute it as described. The configuration of the system includes what programs to run, where to run them, what arguments to pass them, and ROS-specific conventions which make it easy to reuse components throughout the system by giving them each a different configuration. It is also responsible for monitoring the state of the processes launched, and reporting and/or reacting to changes in the state of those processes.
Launch files written in Python, XML, or YAML can start and stop different nodes as well as trigger and act on various events. See Using Python, XML, and YAML for ROS 2 Launch Files for a description of the different formats. The package providing this framework is launch_ros, which uses the non-ROS-specific launch framework underneath.
Official Tutorial: https://docs.ros.org/en/humble/Tutorials/Intermediate/Launch/Creating-Launch-Files.html
Step 1: Create a new directory to store your launch files:
mkdir launch
Step 2: Write the launch file
from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
return LaunchDescription([
Node(
package='turtlesim',
namespace='turtlesim1',
executable='turtlesim_node',
name='sim'
),
Node(
package='turtlesim',
namespace='turtlesim2',
executable='turtlesim_node',
name='sim'
),
Node(
package='turtlesim',
executable='mimic',
name='mimic',
remappings=[
('/input/pose', '/turtlesim1/turtle1/pose'),
('/output/cmd_vel', '/turtlesim2/turtle1/cmd_vel'),
]
)
])
Step 3: ROS2 Launch
cd launch
ros2 launch turtlesim_mimic_launch.py