Neste tutorial, veremos como criar e executar scripts Python no ROS2. Isto permitir-lhe-á criar os seus próprios nós e começar a desenvolver com o ROS.
Criar um espaço de trabalho
Uma boa prática para desenvolver sob o ROS2 é criar espaços de trabalho nos quais instalar os pacotes desejados, separados da instalação principal.
Instalação do Colcon
sudo apt install python3-colcon-common-extensions
Criar pasta
mkdir -p ~/tuto_ws/src cd ~/tuto_ws
Em seguida, copie um projeto de exemplo, como o tutorial que contém o turtlesim
git clone https://github.com/ros/ros_tutorials.git -b iron
Em seguida, construa o projeto utilizando o comando
colcon build
Para carregar o seu espaço de trabalho num novo terminal
source /opt/ros/iron/setup.bash cd ~/ros2_ws source install/local_setup.bash
N.B.: pode colocar os seguintes comandos num ros_profile para a fonte num único comando “source ros_profile”
Criar um pacote python
O ROS2 oferece uma ferramenta simples para criar a arquitetura de arquivos de um pacote Python ou C++. É necessário especificar pelo menos o nome do pacote (my_package). Também é possível dar o nome do nó.
No diretório ~/ros2_sw/src, introduza o seguinte comando
ros2 pkg create --build-type ament_python --license Apache-2.0 --node-name my_node my_package --license Apache-2.0
o script python está agora no diretório ~/ros2_ws/src/my_package/my_package
Uma vez criado o pacote, pode adicionar tantos nós quantos quiser a esta pasta.
Terá de atualizar os ficheiros setup.py e package.xml com as dependências e os pontos de entrada
Instalação de dependências
Antes de compilar o projeto, é aconselhável resolver as dependências.
rosdep install -i --from-path src --rosdistro iron -y
Compilar o projeto
É possível compilar todo o projeto ou selecionar um pacote específico
colcon build --packages_select my_package --symlink-install
N.B.: symlink-install significa que não tens de recompilar sempre que alteras o script Python.
Lançar o nó
Depois de o pacote ter sido compilado, pode iniciar o nó com o comando
ros2 run my_package my_node
O código básico para criar um nó
def main(): print('Hi from my_package.') if __name__ == '__main__': main()
Quando iniciado, este código apresenta simplesmente o texto da impressão
Criar um editor e um subscritor para o Turtlesim
Neste exemplo, veremos como criar um pacote com dois nós, um controlando a tartaruga e o outro lendo a posição.
Criação de pacotes
ros2 pkg create --build-type ament_python --license Apache-2.0 --node-name projects control_turtle --license Apache-2.0
Aqui está o ficheiro de arquitetura desejado
.
├── LICENSE
├── package.xml
├── projects
│ ├── control_turtle.py
│ └── read_turtle.py
├── setup.cfg
└── setup.py
Adicionar scripts Python
- Editor controlo_tartaruga.py
Importamos o tipo de mensagem a publicar Torcer para controlar a velocidade da tartaruga
Ligamo-nos ao tópico especificando o nome (‘/turtle1/cmd_vel’) e o tipo de dados (Twist)
Criamos um temporizador que nos permitirá publicar regularmente sobre o tema (create_timer)
Finalmente, publicamos a linha e a velocidade angular
#!/usr/bin/env python # -*- coding: utf-8 -*- # # control_turtle.py import rclpy from rclpy.node import Node from geometry_msgs.msg import Twist import os # Node should have the same name as the node file nodename= os.path.splitext(os.path.basename(__file__))[0] class TurtleController(Node): def __init__(self): super().__init__(nodename) self.get_logger().info(nodename+" started") self.publisher_ = self.create_publisher( Twist, '/turtle1/cmd_vel', 10) timer_period = 0.5 # seconds self.timer = self.create_timer(timer_period, self.timer_callback) self.i = 0 def timer_callback(self): #msg = String() #msg.data = 'Hello World: %d' % self.i msg = Twist() msg.linear.x = 1.0 msg.angular.z = 0.5 self.publisher_.publish(msg) self.get_logger().info('Publishing: "%s"' % msg) self.i += 1 def main(args=None): rclpy.init(args=args) turtle_controller = TurtleController() try: rclpy.spin(turtle_controller) turtle_controller.destroy_node() rclpy.shutdown() except: turtle_controller.get_logger().info(nodename+" stopped") if __name__ == '__main__': main()
- Assinante read_turtle.py
Importar o tipo de mensagem a publicar Pose
Ligamo-nos ao tópico especificando o nome (‘/turtle1/pose’) e o tipo de dados (Pose)
Escrevemos a função de escuta que é executada quando estão disponíveis novos dados sobre o tema
Criamos um temporizador que nos permitirá apresentar os valores com a frequência pretendida
Finalmente, publicamos a linha e a velocidade angular
#!/usr/bin/env python # -*- coding: utf-8 -*- # # control_turtle.py import rclpy #rospy from rclpy.node import Node from turtlesim.msg import Pose import os # Node should have the same name as the node file nodename= os.path.splitext(os.path.basename(__file__))[0] class TurtleReader(Node): def __init__(self): super().__init__(nodename) self.get_logger().info(nodename+" started") self.subscription = self.create_subscription( Pose, '/turtle1/pose', self.listener_callback, 10) self.subscription # prevent unused variable warning self.timer = self.create_timer(2, self.timer_callback) self.msg = None def listener_callback(self, msg): self.msg = msg def timer_callback(self): self.get_logger().info("I read %s" % self.msg ) def main(args=None): rclpy.init(args=args) turtle_reader = TurtleReader() try: rclpy.spin(turtle_reader) turtle_reader.destroy_node() rclpy.shutdown() except: turtle_reader.get_logger().info(nodename+" stopped") if __name__ == '__main__': main()
Modificar setup.py
Nos pontos de entrada do ficheiro setup.py, é necessário especificar os nomes dos nós e a função a executar (main)
entry_points={ 'console_scripts': [ 'control_turtle = projects.control_turtle:main', 'read_turtle = projects.read_turtle:main', ], },
Compilação do pacote
colcon build --packages_select projects --symlink-install
Lançamento de nós
Para lançar um nó num terminal, é necessário obter a fonte do projeto
source /opt/ros/iron/setup.bash cd ~/ros2_ws source install/local_setup.bash
Em três terminais diferentes, execute os seguintes comandos
ros2 run turtlesim turtlesim_node #terminal1 ros2 run projects read_turtle #terminal2 ros2 run projects control_turtle #terminal3
Resultados
Vê-se que a tartaruga descreve a forma de um círculo e a mudança de posição no centro do assinante.
Lançar um nó com argumentos
Os nós podem ser configurados em tempo de execução. Neste exemplo, vamos definir parâmetros de entrada para a velocidade linear e angular. Por isso, vamos modificar o código no editor acima
Na inicialização do nó, declaramos os parâmetros com os valores por defeito
self.declare_parameter('x', 1.0) self.declare_parameter('z', 0.5)
Podemos então obter o valor destes parâmetros e armazená-los em variáveis que utilizamos na função de retorno de chamada
self.linx = self.get_parameter('linx').get_parameter_value().double_value self.angz = self.get_parameter('angz').get_parameter_value().double_value
Agora é possível iniciar um nó com e sem o
ros2 run projects circle_turtle #default param ros2 run projects circle_turtle --ros-args -p linx:=0.5 -p angz:=1.0
Código completo
#!/usr/bin/env python # -*- coding: utf-8 -*- # # control_turtle.py import rclpy #rospy from rclpy.node import Node from std_msgs.msg import String from turtlesim.msg import Pose from geometry_msgs.msg import Twist import os nodename= os.path.splitext(os.path.basename(__file__))[0] class TurtleController(Node): def __init__(self): super().__init__(nodename) self.get_logger().info(nodename+" started") self.publisher_ = self.create_publisher( Twist, '/turtle1/cmd_vel', 10) self.declare_parameter('linx', 1.0) self.declare_parameter('angz', 0.5) self.linx = self.get_parameter('linx').get_parameter_value().double_value self.angz = self.get_parameter('angz').get_parameter_value().double_value timer_period = 0.5 # seconds self.timer = self.create_timer(timer_period, self.timer_callback) self.i = 0 def timer_callback(self): #msg = String() #msg.data = 'Hello World: %d' % self.i msg = Twist() msg.linear.x = self.linx msg.angular.z = self.angz self.publisher_.publish(msg) self.get_logger().info('Publishing: "%s"' % msg) self.i += 1 def main(args=None): #os.system('ros2 service call /reset std_srvs/srv/Empty') #reset pos rclpy.init(args=args) turtle_controller = TurtleController() try: rclpy.spin(turtle_controller) turtle_controller.destroy_node() rclpy.shutdown() except: turtle_controller.get_logger().info(nodename+" stopped") if __name__ == '__main__': main()
Abrir os vários terminais com um script
Para poupar tempo durante o desenvolvimento, pode abrir terminais e lançar nós a partir de um único script bash
#open terminal and run turtlesim gnome-terminal -- $SHELL -c "source ros_profile && ros2 run turtlesim turtlesim_node;exec $SHELL" gnome-terminal -- $SHELL -c "source ros_profile && ros2 run projects control_turtle;exec $SHELL"
Resolução de problemas
- Descontinuação do SetupTools
Verificar a versão do setuptools
python3 import setuptools setuptools.__version__
‘59.6.0’
Instalar o pip
sudo apt install python3-pip
depois instale a versão 58.2.0 do setuptools
python3 -m pip install setuptools==58.2.0