This is a short manual for all main commands and hint for ROS2. By Tabi43.
To create a package:
ros2 pkg create turtle_control --build-type ament_python --dependencies rclpy geometry_msgs std_msgs --node-name turtle controller
C++ package:
ros2 pkg create turtle_control --build-type ament_cmake --dependencies rclcpp geometry_msgs std_msgs --node-name turtle controller
Make sure you source the enviroment with:
source /opt/ros/humble/setup.bash
To add a node in a ROS2 package you can add a new file .py or .cpp inside your src folder and update the package.xml declaring the new file. Inside setup.py add for each new node inside:
entry_points={
'console_scripts': [
'<node_name> = <pkg_name>.<node_name>:main',
],
To compile a package use inside the ws folder:
colcon build
To compile only a specific package:
colcon build --packages-select <my_package>
Add this line inside setup.py :
data_files=[
('share/ament_index/resource_index/packages',
['resource/' + package_name]),
('share/' + package_name, ['package.xml']),
-----> (os.path.join("share", package_name, "launch"), glob("launch/*.launch.py")),
],
It should be the last element of the array data_files.
Make sure you have added in setup.py
import os
from glob import glob
The launch file is structured as:
from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
return LaunchDescription([
Node(
package='<pkg_name>',
executable='<node_file_name>',
name='<name>',
remappings=[ ('/turtlesim2/turtle1/pose', '/remapped/pose'),
('/turtlesim2/turtle1/cmd_vel', '/remapped/cmd_vel')]
),
Node(
package='<pkg_name>',
executable='<node_file_name>',
name='<name>',
parameters=[
{"<parameter_name>": <value>},
{"<parameter_name>": <value>}
]
)
])
Messages needs to be defined in c++ packages! So we make a package containing interfaces, the convention is to name this package as "<pkg_name>_interface". Once we have created the package and maked the srv directory, we can write inside our .srv file:
The service.srv file is structured as:
#request
int64 x
int64 y
---
#response
int64 sum
Before compile we need to add a line inside CMakeLists.txt :
find_package(rosidl_default_generators REQUIRED)
rosidl_generate_interfaces(${PROJECT_NAME}
"srv/<srv_file_name>.srv"
DEPENDENCIES std_msgs geometry_msgs <etc>
)
The add the dependencies in package.xml :
<buildtool_depend>rosidl_default_generators</buildtool_depend>
<exec_depend>rosidl_default_runtime</exec_depend>
<member_of_group>rosidl_interface_packages</member_of_group>
NOTE:
- Use explicit types in req and res in the callback function in the code!
- Use wait_for_service() to ensure to access to a ready and existing service !
- TIP : close and reopen VS Code to see correctly the new interface builded
We need to define the package interface describe above...
The first letter of the File.action must be uppercase!
The action.action is structured as:
#Request
---
#Result
std_msgs/Int64 blabla
---
#Feedback
Before compile we need to add a line inside CMakeLists.txt :
find_package(rosidl_default_generators REQUIRED)
rosidl_generate_interfaces(${PROJECT_NAME}
"action/<action_file_name>.action"
DEPENDENCIES std_msgs geometry_msgs <etc>
)
The add the dependencies in package.xml :
<buildtool_depend>rosidl_default_generators</buildtool_depend>
<depend>action_msgs</depend>
<member_of_group>rosidl_interface_packages</member_of_group>
To use a parameter always decleare it ! (in init)
self.decleare_parameter('<parameter_name', <value>)
To read a parameter:
my_param = self.get_parameter('my_parameter').get_parameter_value().string_value
OR
my_param = <type_cast>(self.get_parameter('my_parameter').value)
To decleare a parameter (seldom used):
my_new_param = rclpy.parameter.Parameter(
'my_parameter',
rclpy.Parameter.Type.STRING,
'world'
)
all_new_parameters = [my_new_param]
self.set_parameters(all_new_parameters)
Is an object whic you can store multipole nodes and run them in a multiple thread fation.
When you have potentially overlapping callbacks put:
from rclpy.callback_groups import MutuallyExclusiveCallbackGroup
.
.
.
self.ack_sub = self.create_subscription(
Bool,
"/ack",
self.on_ack,
10,
---> callback_group = MutuallyExclusiveCallbackGroup()
)
To avoid deadlock.