Learning ROS
===
[Error collection](/FrJQS1G2RwWDJpW_Pdiijw)
[topic](/R65HFIz1ToatOZQeS1OURA)
# 3 Mode of ROS2 Communication
1. Pubilsher and Subscriber
2. Server and Client
3. Action
These three different communication mode can be included in a `node`.
They communicate in the same `topic`.
Action is the combination mode of the other two. In order to achieve some goal. Primarily focus on the change of state within the communication progress. (The *state* is some kind of measurable stuff.)
# CLI tools
Turtlesim is a lightweight simulator for learning ROS 2. And rqt is a graphical user interface (GUI) tool for ROS 2. Everything done in rqt can be done on the command line.
`$ ros2 run turtlesim turtlesim_node`
`$ ros2 run turtlesim turtle_teleop_key`
To see the nodes, and their associated topics, services, and actions, using the list subcommands of the respective commands:
`$ ros2 node list`
`$ ros2 topic list`
`$ ros2 service list`
`$ ros2 action list`
### :black_medium_small_square:Basically, we will run `ros2 node list` , first. To see the curret running node. And use the other three node to introspect the node we interest in.
```
# These are node name
---
/turtlesim
/teleop_turtle
/my_turtle
```
### :black_medium_small_square:We can use GUI tools`$ rqt_graph` to see the relationship between each running node.

### :black_medium_small_square:Now, we specify `/my_turtle`. We can use command`$ ros2 node info <node_name>` to see the node communication components.
> Three modes of ROS2 communciation(see above)
Run cmd
`$ ros2 node info /my_turtle`
```
/my_turtle
Subscribers:
/parameter_events: rcl_interfaces/msg/ParameterEvent
/turtle1/cmd_vel: geometry_msgs/msg/Twist
Publishers:
/parameter_events: rcl_interfaces/msg/ParameterEvent
/rosout: rcl_interfaces/msg/Log
/turtle1/color_sensor: turtlesim/msg/Color
/turtle1/pose: turtlesim/msg/Pose
Services:
/clear: std_srvs/srv/Empty
/kill: turtlesim/srv/Kill
/reset: std_srvs/srv/Empty
/spawn: turtlesim/srv/Spawn
/turtle1/set_pen: turtlesim/srv/SetPen
/turtle1/teleport_absolute: turtlesim/srv/TeleportAbsolute
/turtle1/teleport_relative: turtlesim/srv/TeleportRelative
/my_turtle/describe_parameters: rcl_interfaces/srv/DescribeParameters
/my_turtle/get_parameter_types: rcl_interfaces/srv/GetParameterTypes
/my_turtle/get_parameters: rcl_interfaces/srv/GetParameters
/my_turtle/list_parameters: rcl_interfaces/srv/ListParameters
/my_turtle/set_parameters: rcl_interfaces/srv/SetParameters
/my_turtle/set_parameters_atomically: rcl_interfaces/srv/SetParametersAtomically
Action Servers:
/turtle1/rotate_absolute: turtlesim/action/RotateAbsolute
Action Clients:
```
:black_medium_small_square:`$ ros2 topic list -t` will return the same list of topics`$ ros2 topic list`, this time with the **topic type appended in brackets**:
```
/parameter_events [rcl_interfaces/msg/ParameterEvent]
/rosout [rcl_interfaces/msg/Log]
/turtle1/cmd_vel [geometry_msgs/msg/Twist]
/turtle1/color_sensor [turtlesim/msg/Color]
/turtle1/pose [turtlesim/msg/Pose]
```
### :black_medium_small_square: To see the **data** being published on a topic, use:
`$ ros2 topic echo <topic_name>`
```
# use "echo" to introspect that topic
$ ros2 topic echo /turtle1/cmd_vel
```
```
linear:
x: 2.0
y: 0.0
z: 0.0
angular:
x: 0.0
y: 0.0
z: 0.0
---
```
### About topic
Topics don’t have to only be one-to-one communication; they can be
* one-to-many
* many-to-one
* many-to-many
Another way to look at running node information:
```
$ ros2 topic info /turtle1/cmd_vel
```
```
Type: geometry_msgs/msg/Twist
Publisher count: 1
Subscription count: 2
```
### ROS2 interface show
To know what message type is used on each topic. Recall that the cmd_vel topic has the type:
` geometry_msgs/msg/Twist`
This means that in the package geometry_msgs there is a msg called Twist.
> How to find the message type of node topic:
> 1. **$ rqt_graph**
> To see the relationship of the node.
> 2. **$ ros2 node list -t**
> Find the message type of the topic you want./
:black_medium_small_square: Run `ros2 interface show <msg type>` on this type to learn its details.
(Specifically, what structure of data the message expects!!!)
`$ ros2 interface show geometry_msgs/msg/Twist`
```
# This expresses velocity in free space broken into its linear and angular parts.
Vector3 linear
Vector3 angular
```
# ROS Beginner
### ament
Responsible for ROS2 package.
## colcon
The tool to structure the frame of ROS2.
And colcon is abstract concept of ament.
> colcon can service for the package of C/C++ and Python (Java, Rust, Go, etc)
> Notice!! colcon will ignore the directory which includes **"COLCON_IGNORE" file**
## Create a workspace
1. Create a workspace
`$ mkdir -p ~/ros2_ws/src`
`$ cd ~/ros2_ws`
2. Build the workspace
`$ colcon build --symlink-install`
> In the root of the workspace, run `colcon build`.
> Since build types such as `ament_cmake` do not support the concept of the `devel` space and require the package to be installed, `colcon` supports the option `--symlink-install`.
>This 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.
3. Run test
`$ colcon test`
4. Source the invironment
`$ source install/setup.bash`
> When *colcon* has completed building successfully, the output will be in the `install` directory.
>Before you can use any of the installed executables or libraries, you will need to add them to your path and library paths. *colcon* will have generated *bash/bat files* in the `install` directory to help set up the environment.
> These files will add all of the required elements to your path and library paths as well as provide any bash or shell commands exported by packages.
## package.xml
Information:
1. name
2. version
3. description
4. maintainer
5. license
...
Dependency:
> with extension name of `_depend`, like
1. buildtool_depend
2. test_depend
3. exec_depend
...
:red_circle: pass... (on going)
# ROS Intermediate
## Magaging Dependencies with rosdep
> Managing external dependencies using `rosdep`
> **`rosdep`** is ROS's dependency management utility that can work with ROS packages and external libraries.
> **`rosdep`** is a command-line utility for identifying and installing dependencies to build or install a package.
* Install packages to check the dependencies needed for it to execute
:seedling: pass...(on going)
## Creating an action
We can custom-define actions in our packages
```
#you can reuse existing workspace with this naming convention
mkdir -p ros2_ws/src
cd ros2_ws/src
ros2 pkg create action_tutorials_interfaces
```
### 1. Defining an action
```
# Request
---
# Result
---
# Feedback
```
> Actions are defined in `.action` files of the form
> An action definition is made up of three message definitions separated by `---`.
* A request message is sent from an action client to an action server initiating a new goal.
* A result message is sent from an action server to an action client when a goal is done.
* Feedback messages are periodically sent from an action server to an action client with updates about a goal.
Create an `action` directory in our ROS 2 package `action_tutorials_interfaces`:
```
cd action_tutorials_interfaces
mkdir action
```
Within the `action` directory, create a file called `Fibonacci.action` with the following contents:
```
# Request
int32 order
---
# Result
int32[] sequence
---
# Feedback
int32[] partial_sequence
```
> The goal request is the order of the Fibonacci sequence we want to compute, the result is the final sequence, and the feedback is the partial_sequence computed so far.
### 2. Building an action
:red_circle: pass...(on going)
## Writing an action server and client (Python)
### 1. Writing an action server
```
import rclpy
from rclpy.action import ActionServer
from rclpy.node import Node
from action_tutorials_interfaces.action import Fibonacci
# defines a class FibonacciActionServer that is a subclass of Node.
class FibonacciActionServer(Node):
def __init__(self):
```
1. The class is initialized by calling the Node constructor,
naming our node fibonacci_action_server:
```
super().__init__('fibonacci_action_server')
```
2. In the constructor we also instantiate a new action server.
(Instantiation: refers to the progress of creating functions
and variables inside a class in programming)
```
self._action_server = ActionServer(
self,
Fibonacci,
'fibonacci',
self.execute_callback)
# 3. This is the method that will be called to execute a goal once it is accepted.
def execute_callback(self, goal_handle):
self.get_logger().info('Executing goal...')
result = Fibonacci.Result()
return result
def main(args=None):
rclpy.init(args=args)
fibonacci_action_server = FibonacciActionServer()
rclpy.spin(fibonacci_action_server)
if __name__ == '__main__':
main()
```
### 2. Writing an action client
```
import rclpy
from rclpy.action import ActionClient
from rclpy.node import Node
from action_tutorials_interfaces.action import Fibonacci
class FibonacciActionClient(Node):
def __init__(self):
super().__init__('fibonacci_action_client')
self._action_client = ActionClient(self, Fibonacci, 'fibonacci')
def send_goal(self, order):
goal_msg = Fibonacci.Goal()
goal_msg.order = order
self._action_client.wait_for_server()
return self._action_client.send_goal_async(goal_msg)
def main(args=None):
rclpy.init(args=args)
action_client = FibonacciActionClient()
future = action_client.send_goal(10)
rclpy.spin_until_future_complete(action_client, future)
if __name__ == '__main__':
main()
```
## Launch
We don't need to use command line to run each of the node.
Launch files allow you to start up and configure a number of executables containing ROS 2 nodes simultaneously.
1. Creating a launch file.
> Learn how to create a launch file that will start up nodes and their configurations all at once.
2. Launching and monitoring multiple nodes.
> Get a more advanced overview of how launch files work.
3. Using substitutions.
> Use substitutions to provide more flexibility when describing reusable launch files.
4. Using event handlers.
> Use event handlers to monitor the state of processes or to define a complex set of rules that can be used to dynamically modify the launch file.
5. Managing large projects.
> Structure launch files for large projects so they may be reused as much as possible in different situations.
> See usage examples of different launch tools like parameters, YAML files, remappings, namespaces, default arguments, and RViz configs.
### :black_medium_small_square: Creating a launch file
Helping users describe the configuration of their system and then execute it as described.
#### Configuration of the system includes:
* Programs to run
* Where to run
* What arguments to pass
* ROS-specific conventions
> which make it easy to reuse components throughout the system by giving them each a different configuration.
> !!!Notice(other usage):
>Monitoring the state of the processes launched, and reporting and/or reacting to changes in the state of those processes.
#### 1. Setup
Create a new directory to store your launch files:
`$ mkdir launch`
#### 2. Write the launch file
```
# import statements pull in python "launch" modules
from launch import LaunchDescription
from launch_ros.actions import Node
# the launch description
def generate_launch_description():
return LaunchDescription([
Node(
package='turtlesim',
namespace='turtlesim1',
# The first two actions in the launch description launch the two turtlesim windows:
executable='turtlesim_node',
name='sim'
),
Node(
package='turtlesim',
namespace='turtlesim2',
# The final action launches the mimic node with the remaps:
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'),
]
)
]).
```
#### 3. ros2 launch
`$ cd launch`
`$ ros2 launch turtlesim_mimic_launch.py`
**Notice!!!**
For packages with launch files, it is a good idea to add an `exec_depend` dependency on the `ros2launch package` in your package’s `package.xml`:
>`<exec_depend>ros2launch</exec_depend>`
### :black_medium_small_square:Integrating launch files into ROS2 packages
#### 1. Create a package
#### 2. Creating the structure to hold launch files
#### 3. Writing the launch file
#### 4. Building and running the launch file
### :black_medium_small_square:Using substitutions
Problems:
> Launch files are used to start **nodes**, **services** and **execute processes**. This set of actions may have arguments, which affect their behavior.
Solution:
> Substitutions can be used in arguments to provide more flexibility when describing reusable launch files.
> Substitutions are variables:
> 1. Are only evaluated during execution of the launch description
> 2. Can be used to acquire specific information like a launch configuration, an environment variable, or to evaluate an arbitrary Python expression.
#### 1. Creat and setup package
#### 2. Parent launch file
#### 3. Substitutions example launch file
#### 4. Build the package
#### 5. Modifying launch arguments
### Using event handlers
Launch in ROS 2 is a system that executes and manages user-defined processes. It is responsible for monitoring the state of processes it launched, as well as reporting and reacting to changes in the state of those processes.
#### 1.
#### 2.
#### 3.
#### 4.
:red_circle: pass...(on going)
### Managing large projects
describes some tips for writing launch files for large projects. The focus is on how to structure launch files so they may be reused as much as possible in different situations.
Additionally, it covers usage examples of different ROS 2 launch tools, like:
* parameters*
* YAML files
* remappings
* namespaces
* default arguments
* RViz configs
#### 1. Top-level organization
#### 2. Parameters
#### 3. Namespaces
#### 4. Reusing nodes
#### 5. Parameter overrides
#### 6. Remapping
#### 7. Config files
#### 8. Environment Variables
### Running launch files
#### 1. Update setup.py
#### 2.Build and run
### Keep going
[Creating a launch file](https://docs.ros.org/en/foxy/Tutorials/Intermediate/Launch/Creating-Launch-Files.html)
[Using Python, XML, and YAML for ROS 2 Launch Files](https://docs.ros.org/en/foxy/How-To-Guides/Launch-file-different-formats.html)
## `tf2`
`tf2`is the transform library, which lets the user keep trak of multiple coordinate frames over time.
`tf2` maintains the relationship between coordinate frames in a tree structure buffered in time and lets the user transform points, vectors, etc.
### Writing a static broadcaster
#### 1. Create a package
`$ ros2 pkg create --build-type ament_python learning_tf2_py`
```
src.
|_learning_tf2_py # package
|_learning_tf2_py
```
```
# dependency
geometry_msgs
python3-numpy
rclpy
tf2_ros_py
turtlesim
```
```
import math
import sys
# provide the tamplete for the message (that we will publish to the transformation tree)
from geometry_msgs.msg import TransformStamped
import numpy as np
import rclpy
from rclpy.node import Node
from tf2_ros.static_transform_broadcaster import StaticTransformBroadcaster
def quaternion_from_euler(ai, aj, ak):
ai /= 2.0
aj /= 2.0
ak /= 2.0
ci = math.cos(ai)
si = math.sin(ai)
cj = math.cos(aj)
sj = math.sin(aj)
ck = math.cos(ak)
sk = math.sin(ak)
cc = ci*ck
cs = ci*sk
sc = si*ck
ss = si*sk
q = np.empty((4, ))
q[0] = cj*sc - sj*cs
q[1] = cj*ss + sj*cc
q[2] = cj*cs - sj*sc
q[3] = cj*cc + sj*ss
return q
class StaticFramePublisher(Node):
"""
(Notice!!!)Broadcast transforms that never change.
This example publishes transforms from `world` to a static turtle frame.
The transforms are only published once at startup, and are constant for all
time.
"""
def __init__(self, transformation):
super().__init__('static_turtle_tf2_broadcaster')
self.tf_static_broadcaster = StaticTransformBroadcaster(self)
# Publish static transforms once at startup
self.make_transforms(transformation)
def make_transforms(self, transformation):
```
The message we will send over once populated. Before passing the
actual transform values we need to give it the appropriate metadata.
```
t = TransformStamped() # create a TransformStamped object
```
1. To give the transform being published a timestamp with the current time
2. Set the name of the parent frame of the link we’re creating, in this case world
3. Set the name of the child frame of the link we’re creating
>>> frame_id: world
>>> child_id: mystaticturtle
```
t.header.stamp = self.get_clock().now().to_msg()
t.header.frame_id = 'world'
t.child_frame_id = transformation[1]
```
transform:
translation:
x:
y:
z:
rotation:
x:
y:
z:
w:
```
t.transform.translation.x = float(transformation[2])
t.transform.translation.y = float(transformation[3])
t.transform.translation.z = float(transformation[4])
# Transform euler to quaternion
quat = quaternion_from_euler(float(transformation[5]),
float(transformation[6]),
float(transformation[7]))
t.transform.rotation.x = quat[0]
t.transform.rotation.y = quat[1]
t.transform.rotation.z = quat[2]
t.transform.rotation.w = quat[3]
#use sendTransform() function to broadcast static transform
self.tf_static_broadcaster.sendTransform(t)
def main():
logger = rclpy.logging.get_logger('logger')
# obtain parameters from command line arguments
if len(sys.argv) != 8:
logger.info('Invalid number of parameters. Usage: \n'
'$ ros2 run learning_tf2_py static_turtle_tf2_broadcaster'
'child_frame_name x y z roll pitch yaw')
sys.exit(1)
if sys.argv[1] == 'world':
logger.info('Your static turtle name cannot be "world"')
sys.exit(2)
# pass parameters and initialize node
rclpy.init()
node = StaticFramePublisher(sys.argv)
try:
rclpy.spin(node)
except KeyboardInterrupt:
pass
rclpy.shutdown()
```
#### 2. Write the static broadcaster node
### Writing a broadcaster
### Writing a listener
### Adding a frame
### Using time
### Traveling in time
## Debuging `tf2`
### Quaternion fundametals
### Debuggin tf2 problems
## RUDF
### Building a visual robot model from scratch
### Building a movable robot model
### Adding physical and collision properties
### Using URDF with `robot_state_publisher`
# Publisher and Subsrciber
---
```
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
class MinimalPublisher(Node):
def __init__(self):
super().__init__('minimal_publisher')
self.publisher_ = self.create_publisher(String, 'topic', 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
self.publisher_.publish(msg)
self.get_logger().info('Publishing: "%s"' % msg.data)
self.i += 1
def main(args=None):
rclpy.init(args=args)
minimal_publisher = MinimalPublisher()
rclpy.spin(minimal_publisher)
# Destroy the node explicitly
# (optional - otherwise it will be done automatically
# when the garbage collector destroys the node object)
minimal_publisher.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
```
* *ROS 2 Client Library* that provides the API for invoking ROS 2 through Python
> rclpy
* main class which will be inherited here to instantiate our own node
> Node
* the library for standard messages that includes the `String` message type which we use in this node.This has to be declared as a dependency in `package.xml`, which we do next.
> std_msgs.msg
class MinimalPublisher(Node):
def __init__(self):
super().__init__('minimal_publisher')
self.publisher_ = self.create_publisher(String, 'topic', 10)
timer_period = 0.5 # seconds
self.timer = self.create_timer(timer_period, self.timer_callback)
self.i = 0
### 1. Node Initialization:
Begin by initializing the ROS2 node.
Create a Node instance to represent the publisher node.
### 2.Publisher Configuration:
Define the topic name that the publisher will use.
Specify the message type that the publisher will send.
### 3.Create a Publisher:
Instantiate a Publisher object with the defined topic name and message type.
The publisher is responsible for sending messages on the specified topic.
### 4.Configure Message Data:
Set up the data to be sent in the message.
This involves creating an instance of the message type and populating it with relevant data.
### 5.Publish Messages:
In a loop or as required, publish messages using the publisher's publish method.
The published message will be sent to the specified topic.
### 6.Shutdown the Node:
Ensure proper cleanup by shutting down the ROS2 node when done.