Utilizzare una Singola Callback per Molteplici Subscriber – ROS Italian Tutorial

Utilizzare una Singola Callback per Molteplici Subscriber – ROS Italian Tutorial

This tutorial is created by Rosbotics Ambassador 012 Alessandro 

Rosbotics Ambassador Program https://www.theconstruct.ai/rosbotics-ambassador/)

In questo tutorial verrà mostrata un’implementazione di un nodo subscriber in grado di leggere messaggi di diverso tipo e provenienti da molteplici topic, gestiti attraverso una singola funzione di callback.

Risorse

Come utilizzare i Rosject

Nel caso non si volesse installare ROS Noetic sul proprio sistema e fare il set up dell’ambiente di sviluppo, è possibile utilizzare i
Rosject offerti da The Construct per poter testare i contenuti del tutorial o per seguirlo passo passo.
Per fare ciò sarà sufficiente seguire il link indicato nelle risorse e cliccare su “Run”. In questo modo si avrà accesso a una copia del
Rosject utilizzato all’interno del tutorial.

Aprire terminal e IDE

Per aprire un nuovo terminale e avere accesso all’IDE forniti dal Rosject, sarà possibile utilizzare la console in basso a destra.

Prerequisiti

Per poter seguire il tutorial passo passo sarò necessario avere il nodo publisher, che pubblica su diversi topic, attivo e creare un package dove poter inserire il codice per il subscriber.
Per iniziare eseguiamo il nodo publisher. Sarà sufficiente aprire il terminale dalla console (vedi Risorse) e inserire i seguenti comandi nelle rispettive schede del terminale:

Terminale 1:
roscore
Terminale 2:
cd catkin_ws
rosrun publisher publisher.py

Quindi è possibile verificare il corretto funzionamento del nodo aprendo una seconda finestra al’interno del terminale e osservando l’output del comando rostopic list.
L’output previsto è il seguente:

Terminale 3:
rostopic list

/chatter0
/chatter1
/chatter2
/rosout
/rosout_agg

Procediamo quindi a creare il package per il subscriber:

Terminale 3:
cd src
catkin_create_pkg multiple_subscriber rospy std_msgs
mkdir multiple_subscriber/script

Situazione di partenza

Al momento abbiamo eseguito il nodo publisher che pubblica su 3 topic messaggi di vario tipo. In particolare:

  • /chatter0 pubblica messaggi di tipo String
  • /chatter1 pubblica messaggi di tipo Float64
  • /chatter2 pubblica messaggi di tipo Bool

Vediamo quindi come possiamo gestire tutti e tre i topic, creando un subscriber in grado di leggere i messaggi dai chatter e di gestirli con una singola funzione di callback, differenziando il risultato di quest’ultima sfruttando l’uso di parametri.

Creazione del file multiple_sub.py

Proseguiamo creando il file necessario a definire il nostro subscriber e tutte le sue funzioni.

Terminale 3:
touch multiple_sub/script/multiple_subscriber.py # Crea il file
chmod +x multiple_sub/script/multiple_subscriber.py # Garantisce i permessi di esecuzione
cd ..
catkin_make

Implementazione del subscriber

Partiamo quindi dallo specificare un interprete per il nostro codice in Python, dall’importare i moduli necessari (rospy e in questo caso std_msgs/String, std_msgs/Float64, std_msgs/Bool) e dallo scrivere un main che inizializzi il nodo e invochi rospy.spin() per poter eseguire le callback del nostro subscriber.

main

multiple_subscriber.py
import rospy
from std_msgs.msg import String, Float64, Bool

if __name__=="__main__":

rospy.init_node("multiple_subscriber")

topics = {"chatter0": [String, "Argomento passato da chatter0"], "chatter1": [Float64, "Argomento passato da chatter1"], "chatter2": [Bool, "Argomento passato da chatter2"]}

multiple_subscribe()

rospy.spin()

All’interno della funzione abbiamo definito anche topics, un dizionario contenente le specifiche dei topic che vogliamo andare a leggere, seguendo la seguente struttura:
{"nome_topic": [Tipo, Argomento]}

Per ogni chiave topic abbiamo quindi definito una lista contente il tipo del messaggio inviato dal topic e un argomento (in questo caso una stringa che esplicita di essere l’argomento passato per lo specifico topic) .

multiple_subscribe

La funzione multiple_subscribe() sarà definita perché il nodo possa seguire i 3 topic e perché i messaggi pubblicati possano innescare la callback.

multiple_subscriber.py
def multiple_subscribe():

for t in topics:

rospy.Subscriber(t, topics[t][0], callback, callback_args=topics[t][1])

Il nodo segue quindi ciascuno dei topic definiti nel dizionario, aspettandosi messaggi del relativo tipo, invocando una funzione callback e passando il relativo argomento tramite il parametro callback_args.

callback

Vediamo una semplice implementazione della callback per poter gestire i tre topic.

multiple_subscriber.py
def callback(data, args):

rospy.loginfo(f"Ricevuto: {data.data}")

print(args)

print('\n')

In questo caso andiamo a stampare come info di ROS il contenuto del messaggio arrivato alla callback e stampiamo l’argomento passato corrispondente. Entrambi dipenderanno da quale topic ha innescato la callback e perciò a questo livello il messaggio può essere gestito a seconda del parametro ad esso associato, potendo implementare in qualche modo un’esecuzione “su misura” della funzione di callback.

Test del subscriber

Siamo pronti per poter eseguire il nostro subscriber!
Ciò che ci aspettiamo è che vengano stampati allo stesso tempo il messaggio e l’argomento relativi alla callback innescata dall’arrivo di un messaggio dal singolo topic. L’output dovrebbe quindi essere di questo tipo:

[INFO] [***.***]: Ricevuto: Questo pubblica una stringa
Argomento passato da chatter0

[INFO] [***.***]: Ricevuto: 0.42
Argomento passato da chatter1

[INFO] [***.***]: Ricevuto: True
Argomento passato da chatter2

Per fare ciò dobbiamo eseguire il nostro nodo:

Terminale 3:
rosrun multiple_sub multiple_subscriber.py

Possiamo osservare come gli output si alternano ogni secondo a rotazione, ma questo è dovuto alla singola implementazione del publisher (che è possibile trovare all’interno del Rosject, vedi Risorse).

In ogni caso per ogni topic viene ricevuto, gestito e stampato un messaggio specifico relativo ad esso, e l’elaborazione del relativo parametro, con diverso risultato nonostante l’utilizzo della medesima funzione di callback.

Video Tutorial

How to import custom Python3 modules in a ROS 2 package

How to import custom Python3 modules in a ROS 2 package

What we are going to learn:

  1. How to set up the package architecture to have custom Python modules inside the ROS2 package
  2. How to configure setup.py so that the modules can be imported
  3. How to import modules and use them in a Python node script

 

If you want to learn ROS 2 Python in a practical, hands-on way, check out the course ROS 2 Basics in 5 Days: https://app.theconstruct.ai/courses/132

In this course, you’ll cover the foundational concepts needed to start working with ROS 2, as well as more advanced topics, all while engaging in hands-on practice.

Opening the rosject

In order to follow this tutorial, we need to have ROS2 installed in our system, and ideally a ros2_ws (ROS2 Workspace). To make your life easier, we have already prepared a rosject for that: https://app.theconstructsim.com/l/5e6f9418/

Just by copying the rosject (clicking the link above), you will have a setup already prepared for you.

After the rosject has been successfully copied to your own area, you should see a Run button. Just click that button to launch the rosject ( see this example).

After pressing the Run button, you should have the rosject loaded. Now, let’s head to the next section to get some real practice.

In order to interact with ROS2, we need a terminal.

Let’s open a terminal by clicking the Open a new terminal button.

Open a new Terminal

 

Setup the package architecture

Create a standard ROS2 package using ament_python

With dependencies on rclpy:

cd ~/ros2_ws/src/
ros2 pkg create py_pkg –build-type ament_python –dependencies rclpy

Run the “tree . command to see the folder structure. If you don’t have the command installed, you can install it using:

sudo apt-get update
sudo apt-get install -y tree

You should be able to see this structure which you are already familiar with:

`– py_pkg
|– package.xml
|– py_pkg
| `– __init__.py
|– resource
| `– py_pkg
|– setup.cfg
|– setup.py
`– test
|– test_copyright.py
|– test_flake8.py
`– test_pep257.py

Add custom Python module(s) that you want to import

You will need to add module files under the directory with the same name as the package (in this case py_pkg):

cd py_pkg/py_pkg
touch module_to_import.py

For this tutorial for simplicity, we can just add this minimal function to the module_to_import.py file that just prints hello world:

def function_to_import():
print(“hello world”)


Create a scripts directory for your Python node(s)

Specify the directory as a Python package by creating an empty__init__.pyfile (essential for the folder to be recognized by Python as a package), then add a Python node:

cd ..
mkdir scripts
touch scripts/__init__.py
touch scripts/py_node.py
Here is a simple node implementation (to paste in py_node.py) that imports and uses the function from the module we just created:
import rclpy
from rclpy.node import Node
# Import a specific function/class from your module
from py_pkg.module_to_import import function_to_import
class MyPythonNode(Node):
    def __init__(self):
        super().__init__(“my_node_name”)
# Run the imported function
        function_to_import()
def main(args=None):
    # Initiate ROS communications
    rclpy.init(args=args)
    # Instantiate the node
    node = MyPythonNode()
    # Make the node spin
    rclpy.spin(node)
    # Destroy the node object
    node.destroy_node()
    # Shutdown ROS communications
    rclpy.shutdown()
if __name__ == ‘__main__’:
    main()

Final package architecture

Run the “tree. command to see the folder structure inside py_pkg:

|– package.xml
|– py_pkg
| |– __init__.py
| `– module_to_import.py
|– resource
| `– py_pkg
|– scripts
| |– __init__.py
| `– py_node.py
|– setup.cfg
|– setup.py
`– test
|– test_copyright.py
|– test_flake8.py
`– test_pep257.py

Configure the package and test

Modify setup.py

  1. Add scripts to the list of Python packages:

packages=[package_name, ‘scripts’],

This is so that later when you build with colcon, ROS2 knows to put the Python packages here ~/<workspace_name>/install/<package_name>/lib/<python_version>/site-packages/<package_name> (in our case <workspace_name> is ros2_ws, <python_version> ispython3.10, and <package_name> is py_pkg).

2. State the entry point:

entry_points={
‘console_scripts’: [
‘py_node_executable = scripts.py_node:main’
],

So your setup.py should look like this:

from setuptools import setup

package_name = ‘py_pkg’

setup(
name=package_name,
version=’0.0.0′,
packages=[package_name, ‘scripts’],
data_files=[
(‘share/ament_index/resource_index/packages’,
[‘resource/’ + package_name]),
(‘share/’ + package_name, [‘package.xml’]),
],
install_requires=[‘setuptools’],
zip_safe=True,
maintainer=’user’,
maintainer_email=’user@todo.todo’,
description=’TODO: Package description’,
license=’TODO: License declaration’,
tests_require=[‘pytest’],
entry_points={
‘console_scripts’: [
‘py_node_executable = scripts.py_node:main’
],
},
)

Compile the Package

cd ~/ros2_ws/
colcon build –packages-select py_pkg

There is a good chance that you will get a SetuptoolsDeprecationWarning:

Starting >>> py_pkg
— stderr: py_pkg
/usr/lib/python3/dist-packages/setuptools/command/install.py:34: SetuptoolsDeprecationWarning: setup.py install is deprecated. Use build and pip and other standards-based tools.
warnings.warn(

Finished <<< py_pkg [1.88s]

Summary: 1 package finished [2.21s]
1 package had stderr output: py_pkg

If there is no warning then that’s great, but don’t worry if you get it as it is just a warning (not an error) and you will still be able to build successfully.

As of the time of writing this tutorial, the developers hope to have it fixed for Jazzy in May 2024, but for now, there are workarounds to remove the warning. The original workaround was to downgrade the version of setuptools that is currently installed by doing pip install setuptools==58.2.0 in the terminal. However, that is not recommended. Instead you should use the PYTHONWARNINGS environment variable to suppress that particular warning by adding this to the ~/.bashrc file:

PYTHONWARNINGS=”ignore:setup.py install is deprecated::setuptools.command.install”; export PYTHONWARNINGS

You could do this by entering this into the terminal:

echo “PYTHONWARNINGS=\”ignore:setup.py install is deprecated::setuptools.command.install\”; export PYTHONWARNINGS” >> ~/.bashrc

Don’t forget to source the .bashrc in the terminal before compiling the package to register the changes:

source ~/.bashrc # . ~/.bashrc

You should now be able to compile the package without the warning.

cd ~/ros2_ws/
colcon build –packages-select py_pkg

Run the node

In the terminal, source the ROS2 environment and start the Python node:

source ~/ros2_ws/install/setup.bash
ros2 run py_pkg py_node_executable

If you get the following output, that means it was successful!

hello world

Congratulations. You now know how to import custom Python3 modules in a ROS 2 package.

To learn more about ROS 2, have a look at the course below:

We hope this post was really helpful to you.

This tutorial is created by Robotics Ambassador Ernest.

Video Tutorial

Real Time in ROS2 – Intro

Real Time in ROS2 – Intro

In this post, we will see how to implement Real Time in ROS2, using the ROS2 Pendulum Real Time demo.

Step 1: Grab a copy of the ROS Project containing the code

Click here to get your own copy of the project (PS: If you don’t have an account on the ROS Development Studio, you would need to create one. Once you create an account or log in, we will copy the project to your workspace).

Run ROS project

That done, open the project using the Run button. This might take a few moments, please be patient.

You should now see a notebook with detailed instructions. This post includes a summary of these instructions and some other tips.

Step 2: Start the Simulation

Start a web shell

Start a web shell from the bottom bar of the screen as shown above and run the following command:

cd ~/ros2_ws
source install/local_setup.bash
ros2 launch pendulum_bringup pendulum_bringup.launch.py rviz:=True

See the robot by clicking on the Graphical Tools icon on the bottom bar:

Open Graphical Tools

You should now see something like this (if you cannot see it, please ensure the ros2 launch command is still running and restart it if necessary):

ROS2 Pendulum

You might need to drag the window into focus and then maximize it.

Create new web shell

Move the robot by executing the following in a new terminal:

ros2 topic pub -1 /teleop pendulum2_msgs/msg/PendulumTeleop "cart_position: 5.0"

If you want to run the simulation on your local PC, then you need to run the commands given in the Setup section.

Step 3: Explore the source code using the IDE

Open Code Editor

Open the IDE by clicking on the icon as shown above. You should now see something similar to the image below:

View source code in the IDE

All the files used in the simulation are in the ros2_ws/src directory. Explore the files. Double-click to open a file in the editor. You will refer back to some of the files later on.

Step 4: Watch the video to understand how the simulation implemented Real Time in ROS2 with the Pendulum

Here you go:

Step 5: Consolidate your learning

  • Do you understand how to implement Real Time in ROS2 after watching the video? If not, have you gone over the video again?
  • Can you try to implement the simulation on your local PC?

Related Resources

Feedback

Did you like this post? Do you have any questions about the explanations? Whatever the case, please leave a comment on the comments section below, so we can interact and learn from each other.

If you want to learn about other ROS2 topics, please let us know in the comments area and we will do a video or post about it.

Gazebo Joints Control in ROS2

Gazebo Joints Control in ROS2

In this post, we will see how to implement Gazebo joints control in ROS2. The way this is done has changed a bit from ROS1.

Step 1: Grab a copy of the ROS Project containing the code

Click here to get your own copy of the project (PS: If you don’t have an account on the ROS Development Studio, you would need to create one. Once you create an account or log in, we will copy the project to your workspace).

Run ROS project

That done, open the project using the Run button. This might take a few moments, please be patient.

You should now see a notebook with detailed instructions. This post includes a summary of these instructions and some other tips.

Step 2: Start the Simulation and get the robot moving

Start a web shell

Start a web shell from the bottom bar of the screen as shown above and run the following command:

ros2 launch box_car_gazebo box_bot_launch.py

Create new web shell

Start another tab in the web shell, run the following command:

ros2 run teleop_twist_keyboard teleop_twist_keyboard --ros-args --remap cmd_vel:=/box_bot/cmd_vel

See the robot by clicking on the Gazebo icon on the bottom bar:

Open Gazebo

You should now see something like this (if you cannot see it, please ensure the ros2 launch command is still running and restart it if necessary):

Gazebo window with box car

Put the Gazebo window and the shell window with the teleop command side by side, focus on the shell and move the robot with the keyboard. You should see the robot run off. PS: use the z key to reduce the speed before moving the robot forward or backward, otherwise it runs off too quickly!

Step 3: Explore the source code using the IDE

Open the IDE

Open the IDE by clicking on the icon as shown above. You should now see something similar to the image below:

ROS2 workspace

All the files used in the simulation are in the ros2_ws/src directory. Explore the files. Double-click to open a file in the editor. You will refer back to some of the files later on.

Step 4: Watch the video to understand how we implemented gazebo joints control in ROS2

Here you go:

Step 5: Consolidate your learning

  • Do you understand how to implement gazebo joints control in ROS2 after watching the video? If not, have you gone over the video again?
  • If you are familiar with ROS1 control, can you see how it’s different from ROS control?

Related Resources

Feedback

Did you like this post? Do you have any questions about the explanations? Whatever the case, please leave a comment on the comments section below, so we can interact and learn from each other.

If you want to learn about other ROS2 topics, please let us know in the comments area and we will do a video or post about it.

[ROS Mini Challenge] #7 – make a robot follow another robot

[ROS Mini Challenge] #7 – make a robot follow another robot

In this post, we will see how to make a robot follow another robot. We’ll make the iRobot follow the big turtle all around the world when it moves, using ROS TF broadcaster and listener nodes.

PS: This ROS project is part of our ROS Mini Challenge series, which gives you an opportunity to win an amazing ROS Developers T-shirt! This challenge is already solved. For updates on future challenges, please stay tuned to our Twitter channel.

Step 1: Grab a copy of the ROS Project containing the code for the challenge

Click here to get your own copy of the project. If you don’t have an account on the ROS Development Studio, you will need to create one. Once you create an account or log in, we will copy the project to your workspace. That done, open your ROSject using the Open button. This might take a few moments, please be patient.

You should now see a notebook with detailed instructions about the challenge. This post includes a summary of these instructions as well as the solution to the challenge.

PS: Please ignore the Claim your Prize! section because…well…you are late the party 🙂

Step 2: Start the Simulation and get the robots moving

  1. Click on the Simulations menu and then Choose launch file . In the dialog that appears, select rotw7.launch under turtle_tf_3d package. Then click the Launch button. You should see a Gazebo window popup showing the simulation.
  2. Get the robots moving. Pick a Shell from the Tools menu and run the following commands:
user:~$ source ~/catkin_ws/devel/setup.bash
user:~$ roslaunch rotw7_pkg irobot_follow_turtle.launch

At this point, you should already see the iRobot moving towards the big turtle.

Nothing happened? Heck, we gotta fix this! Let’s do that in the next section.

Step 3: Let’s find the problem

So the robots didn’t move as we expected. And we had this error message:

[INFO] [1580892397.791963, 77.216000]: Retrieveing Model indexes
[INFO] [1580892397.860043, 77.241000]: Robot Name=irobot, is NOT in model_state, trying again

The error message above says it cannot find the model name specified in the code, so let’s check that up. Fire up the IDE from the Tools menu and browse to the directory catkin_ws/src/rotw7_pkg/scripts. We have two Python scripts in there:

  • turtle_tf_broadcaster.py
  • turtle_tf_listener.py

The robot model names are specified on line 19 of turtle_tf_broadcaster.py:, in the publisher_of_tf function:

robot_name_list = ["irobot","turtle"]

Let’s check if we can find these robots in the simulation, using a Gazebo service:

user:~$ rosservice call /gazebo/get_world_properties "{}"
sim_time: 424.862
model_names: [ground_plane, coke_can, turtle1, turtle2]
rendering_enabled: True
success: True
status_message: "GetWorldProperties: got properties"

So we see that the names we specified are not in the simulation! The robots we need are turtle2 and turtle1.

Also, on line 19 of turtle_tf_listener.py, the code is publishing the “cmd_vel” (the topic that moves the robot) of the following robot:

turtle_vel = rospy.Publisher('/cmd_vel', geometry_msgs.msg.Twist,queue_size=1)

But, which of the turtles is the follower, and what is the correct topic for its “cmd_vel”? We have a hint from the launch file irobot_follow_turtle.launch:

<?xml version="1.0" encoding="UTF-8"?>
<launch>
    <include file="$(find rotw7_pkg)/launch/run_turtle_tf_broadcaster.launch"/>
    <include file="$(find rotw7_pkg)/launch/run_turtle_tf_listener.launch">
        <arg name="model_to_be_followed_name" value="turtle1" />
        <arg name="follower_model_name" value="turtle2" />
    </include>
</launch>

So the follower is turtle2. Now, let’s check what it’s “cmd_vel” topic is. It’s specified as /cmd_vel in the code, but is this true? Let’s check the list of topics:

user:~$ rostopic list
#...
/turtle1/cmd_vel
/turtle2/cmd_vel

Probably, it’s /turtle2/cmd_vel. How do we know? Let’s publish to both /cmd_vel and /turtle2/cmd_vel and see which works.

ser:~$ rostopic pub /cmd_vel
Display all 152 possibilities? (y or n)
user:~$ rostopic pub /turtle2/cmd_vel geometry_msgs/Twist "linear:
  x: 0.2
  y: 0.0
  z: 0.0
angular:
  x: 0.0
  y: 0.0
  z: 0.0"
publishing and latching message. Press ctrl-C to terminate
^Cuser:~$ rostopic pub /turtle2/cmd_vel geometry_msgs/Twist "linr:r
  x: 0.0
  y: 0.0
  z: 0.0
angular:
  x: 0.0
  y: 0.0
  z: 0.0"
publishing and latching message. Press ctrl-C to terminate

Publishing to /turtle2/cmd_vel works. /cmd_vel didn’t work.

Step 4: Let’s fix the problem

We saw the problems in Step 3, now let’s implement the fix and test again.

On line 19 of turtle_tf_broadcaster.py, change the list to reflect the real turtle names:

robot_name_list = ["turtle1","turtle2"]

Also, on line 19 of turtle_tf_listener.py, change /cmd_velto /turtle2/cmd_vel:

turtle_vel = rospy.Publisher('/turtle2/cmd_vel', geometry_msgs.msg.Twist,queue_size=1)

Now rerun the commands to move the robots:

user:~$ source ~/catkin_ws/devel/setup.bash
user:~$ roslaunch rotw7_pkg irobot_follow_turtle.launch

You should now see the iRobot moving towards the big turtle. Now you can start moving the Turtle using the keyboard. Pick another Shell from the Tools menu and run the following command:

user:~$ roslaunch turtle_tf_3d turtle_keyboard_move.launch

Move the big turtle around with the keyboard, and you should see that the iRobot follows it. Done, that’s an example of how to make a robot follow another robot.

Extra: Video of this post

We made a video showing how we solved this challenge and made the iRobot follow another robot. If you prefer “sights and sounds” to “black and white”, here you go:

Related Resources

Feedback

Did you like this post? Do you have any questions about the explanations? Whatever the case, please leave a comment on the comments section below, so we can interact and learn from each other.

If you want to learn about other ROS or ROS2 topics, please let us know in the comments area and we will do a video or post about it.

Pin It on Pinterest