added more docs

This commit is contained in:
locker98 2024-12-12 15:30:03 -05:00
parent cdea73fdd7
commit 9a6115be31
13 changed files with 952 additions and 39 deletions

View File

@ -1,28 +0,0 @@
# Message Types
## What is a Message Type
In ROS 2 (Robot Operating System 2), a `msg` type refers to a message definition that contains data structures used for communication between nodes. These messages are the building blocks for publishing and subscribing to topics, which enable nodes to exchange information with each other. Think of a message as a container that holds specific types and amounts of data, like a digital package being sent between nodes in a ROS 2 nodes. Each type of message corresponds to a particular set of data fields and their data types (e.g., `int8`, `float32`, `String`). When you define a message, you're specifying the structure of the data that will be sent or received by nodes. This allows for more organized and efficient communication between nodes.
## How to find them
When creating a publisher you have to figure out what data type it is going to publish. To find examples of the different data types in ROS 2 you can run a special command.
```bash { .annotate }
ros2 interface show example_interfaces/msg/String #(1)
```
1. In this case it was the `String` type that I used but it could have been any other type. To get a list use `Tab`before typing `String` for a autocomplete list of options.
## Including Message Types
Once you have found the message type you will need to make sure that it is imported. This can be pretty easily in both C++ and Python.
Below you can see that we use the `String` as the message type. This example works for other types as well of messages as well. You can just swap `String` for whatever other data type that is available.
=== "Python"
```python
from example_interfaces.msg import String
```
=== "C++"
``` c++
#include "example_interfaces/msg/string.hpp"
```

View File

@ -0,0 +1,112 @@
# ROS 2 Custom Interfaces
## Creating Custom Interface
In ROS 2, a **custom interface** defines unique message, service, or action types tailored to specific application needs. It is important because it enables precise communication between nodes, ensuring data is structured and interpreted correctly for specialized robotics tasks.
### Making Package
To create a custom interface it is best to make a dedicated package for all the interfaces that are needed in that project. Go to the workspace and navigate to the `src` folder.
```bash
ros2 pkg create {robot_name}_interfaces
```
Once the package is create open the package folder with visual studio code and delete the `include` directory and the `src` directory. Next open the `packages.xml` file and delete all the lines that have the `<depend>{somthing}</depend>`. Finally, add the following highlighted lines.
```xml title="packages.xml" linenums="1" hl_lines="12-14"
<?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_interfaces</name>
<version>0.0.0</version>
<description>TODO: Package description</description>
<maintainer email="ros-laptop1@todo.todo">ros-laptop1</maintainer>
<license>TODO: License declaration</license>
<buildtool_depend>ament_cmake</buildtool_depend>
<build_depend>rosidl_default_generators</build_depend>
<exec_depend>rosidl_default_runtime</exec_depend>
<member_of_group>rosidl_interface_packages</member_of_group>
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
<export>
<build_type>ament_cmake</build_type>
</export>
</package>
```
Now that the `packages.xml` is one the `CMakeLists.txt` file needs to be changed. Go through and delete everything so it looks like this and add the lines that are highlighted.
```cmake title="CMakeLists.txt" linenums="1" hl_lines="10 12-15 17"
cmake_minimum_required(VERSION 3.8)
project(my_robot_interfaces)
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
# find dependencies
find_package(ament_cmake REQUIRED)
find_package(rosidl_default_generators REQUIRED)
rosidl_generate_interfaces(${PROJECT_NAME}
"msg/{service_name}.msg"
"srv/{service_name}.srv"
)
ament_export_dependencies(rosidl_default_runtime)
ament_package()
```
Lines 13 and 14 are the custom interfaces that you created and want to build. Look at the next two sections to learn about how to create these files with the message and server configuration.
### Creating a Custom Message
To create a custom message navigate to the package that was created in the [[#Making Package]] section. Here we will create a folder in this package called `msg` and inside this folder we will put all of are message configs. For example lets create a message for a color sensor. This message might contain the the red, green, and blue value from the sensor.
```xml title="ColorSensor.msg" linenums="1"
# you can add comments to your message configs like this
int64 red # comments can be added here also
int64 green
int64 blue
```
Now all you have to do add it to the `CMakeLists.txt` file and run `colcon build`.
### Creating a Custom Service
To create a custom service navigate to the package that was created in the [[#Making Package]] section. Here we will create a folder in this package called `srv` and inside this folder we will put all of are service configs. For example lets create a service for a rectangular area calculator. This service might have a length and width for a request and as a response just an area.
```xml title="CalculateArea.srv" linenums="1"
# you can add comments to your message configs like this
# the --- seperates the request from the response
float64 length
float64 width
---
float64 area
```
Now all you have to do add it to the `CMakeLists.txt` file and run `colcon build`.
### Using a Custom Interface
To use a custom interface in a Python or C++ program import it or include it just like normal. If you have problems with vscode not recognizing it in the C++ programs you will have to edit the `.vscode/c_cpp_properties.json` file and add it to the include path.
```json title="c_cpp_properties.json" linenums="1" hl_lines="11"
{
"configurations": [
{
"browse": {
"databaseFilename": "${default}",
"limitSymbolsToIncludedHeaders": false
},
"includePath": [
"/opt/ros/humble/include/**",
"/usr/include/**",
"~/some/folder/{workspace_name}_ws/install/{robot_name}_interfaces/include/**"
],
"name": "ROS",
"intelliSenseMode": "gcc-x64",
"compilerPath": "/usr/bin/gcc",
"cStandard": "gnu11",
"cppStandard": "c++14"
}
],
"version": 4
}
```

View File

@ -0,0 +1,58 @@
# ROS 2 Interfaces
## What is a Message Type
In ROS 2 (Robot Operating System 2), a `msg` type refers to a message definition that contains data structures used for communication between nodes. These messages are the building blocks for publishing and subscribing to topics, which enable nodes to exchange information with each other. Think of a message as a container that holds specific types and amounts of data, like a digital package being sent between nodes in a ROS 2 nodes. Each type of message corresponds to a particular set of data fields and their data types (e.g., `int8`, `float32`, `String`). When you define a message, you're specifying the structure of the data that will be sent or received by nodes. This allows for more organized and efficient communication between nodes.
## How to find them
When creating a publisher you have to figure out what data type it is going to publish. To find examples of the different data types in ROS 2 you can run a special command.
```bash { .annotate }
ros2 interface show example_interfaces/msg/String #(1)
```
1. In this case it was the `String` type that I used but it could have been any other type. To get a list use `Tab`before typing `String` for a autocomplete list of options.
## Including Message Types
Once you have found the message type you will need to make sure that it is imported. This can be pretty easily in both C++ and Python.
Below you can see that we use the `String` as the message type. This example works for other types as well of messages as well. You can just swap `String` for whatever other data type that is available.
=== "Python"
```python
from example_interfaces.msg import String
```
=== "C++"
``` c++
#include "example_interfaces/msg/string.hpp"
```
## Variables in ROS 2 Interfaces
You can find more information at the [ROS 2 Interface Documentation](https://docs.ros.org/en/rolling/Concepts/Basic/About-Interfaces.html).
| Type name | [C++](https://design.ros2.org/articles/generated_interfaces_cpp.html) | [Python](https://design.ros2.org/articles/generated_interfaces_python.html) | [DDS type](https://design.ros2.org/articles/mapping_dds_types.html) |
| --------- | --------------------------------------------------------------------- | --------------------------------------------------------------------------- | ------------------------------------------------------------------- |
| bool | bool | builtins.bool | boolean |
| byte | uint8_t | builtins.bytes* | octet |
| char | char | builtins.int* | char |
| float32 | float | builtins.float* | float |
| float64 | double | builtins.float* | double |
| int8 | int8_t | builtins.int* | octet |
| uint8 | uint8_t | builtins.int* | octet |
| int16 | int16_t | builtins.int* | short |
| uint16 | uint16_t | builtins.int* | unsigned short |
| int32 | int32_t | builtins.int* | long |
| uint32 | uint32_t | builtins.int* | unsigned long |
| int64 | int64_t | builtins.int* | long long |
| uint64 | uint64_t | builtins.int* | unsigned long long |
| string | std::string | builtins.str | string |
| wstring | std::u16string | builtins.str | wstring |
_Every built-in-type can be used to define arrays:_
|Type name|[C++](https://design.ros2.org/articles/generated_interfaces_cpp.html)|[Python](https://design.ros2.org/articles/generated_interfaces_python.html)|[DDS type](https://design.ros2.org/articles/mapping_dds_types.html)|
|---|---|---|---|
|static array|std::array<T, N>|builtins.list*|T[N]|
|unbounded dynamic array|std::vector|builtins.list|sequence|
|bounded dynamic array|custom_class<T, N>|builtins.list*|sequence<T, N>|
|bounded string|std::string|builtins.str*|string|
All types that are more permissive than their ROS definition enforce the ROS constraints in range and length by software.

View File

@ -0,0 +1,148 @@
# ROS 2 Launch Files
**Launch files** are Python-based scripts that manage the startup of multiple nodes and configurations simultaneously. They allow users to define which nodes to launch, pass parameters, remap topics, and set arguments, streamlining the deployment and orchestration of complex robotics systems.
## Creating Launch File Package
When creating a launch file in ROS 2 you have to use the `ros2 pkg create package_name`. Once the package is created delete all the folders in the package folder and create a folder called `launch` inside this folder you can put all the launch files. Finally edit the `CMakeLists.txt` so that it looks like the one below.
```cmake title="CMakeLists.txt" linenums="1"
cmake_minimum_required(VERSION 3.8)
project(my_robot1_description)
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
# find dependencies
find_package(ament_cmake REQUIRED)
install(
DIRECTORY launch
DESTINATION share/${PROJECT_NAME}/
)
ament_package()
```
## Creating Python Launch File
Below is a example of a launch file.
=== "Python"
```python title="display.launch.py" linenums="1"
from launch import LaunchDescription
from launch_ros.parameter_descriptions import ParameterValue
from launch.substitutions import Command
from launch_ros.actions import Node
from ament_index_python import get_package_share_directory
import os
def generate_launch_description():
urdf_path = os.path.join(get_package_share_directory('my_robot1_description'), 'urdf', 'my_robot1.urdf')
robot_description = ParameterValue(Command(['xacro ', urdf_path]), value_type=str)
rviz_config_path = os.path.join(get_package_share_directory('my_robot1_description'), 'rviz', 'urdf_config.rviz')
robot_state_publisher_node = Node(
package="robot_state_publisher",
executable="robot_state_publisher",
parameters = [{"robot_description": robot_description}]
)
joint_state_publisher_gui_node = Node(
package="joint_state_publisher_gui",
executable="joint_state_publisher_gui",
)
rviz2_node = Node(
package="rviz2",
executable="rviz2",
arguments=['-d', rviz_config_path]
)
return LaunchDescription([
robot_state_publisher_node,
joint_state_publisher_gui_node,
rviz2_node
])
```
=== "XML"
```xml title="display.launch.xml" linenums="1"
<launch>
<let name="urdf_path" value="$(find-pkg-share my_robot1_description)/urdf/my_robot1.urdf"/>
<let name="rviz_config_path" value="$(find-pkg-share my_robot1_description)/rviz/urdf_config.rviz"/>
<node pkg="robot_state_publisher" exec="robot_state_publisher">
<param name="robot_description" value="$(command 'xacro $(var urdf_path)')"/>
</node>
<node pkg="joint_state_publisher_gui" exec="joint_state_publisher_gui" />
<node pkg="rviz2" exec="rviz2" output="screen" args="-d $(var rviz_config_path)" />
</launch>
```
## Adding configs and UFDRs in Launch File Args
When you need to add a file or a config like when creating a rviz node you can take a few quick steps. First create another folder in the same directory you have the launch file. Next navigate to the `CMakeList.txt` file and and add a space followed by the name of the folder you create. In this case I am creating an folder called `rviz` and I will add it to the end of line `12`.
```cmake title="CMakeLists.txt" linenums="1" hl_lines="12"
cmake_minimum_required(VERSION 3.8)
project(my_robot1_description)
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
# find dependencies
find_package(ament_cmake REQUIRED)
install(
DIRECTORY launch rviz
DESTINATION share/${PROJECT_NAME}/
)
ament_package()
```
Now once it is added you will need to reference it in you launch file. Doing this is slightly different in python and xml but it is not two hard.
=== "Python"
```python
from launch import LaunchDescription
from launch_ros.actions import Node
from ament_index_python import get_package_share_directory
import os
def generate_launch_description():
rviz_config_path = os.path.join(get_package_share_directory('my_robot1_description'), 'rviz', 'urdf_config.rviz')
rviz2_node = Node(
package="rviz2",
executable="rviz2",
arguments=['-d', rviz_config_path]
)
return LaunchDescription([rviz2_node])
```
=== "XML"
```xml
<launch>
<let name="rviz_config_path" value="$(find-pkg-share my_robot1_description)/rviz/urdf_config.rviz"/>
<node pkg="rviz2" exec="rviz2" output="screen" args="-d $(var rviz_config_path)" />
</launch>
```
## Using Launch File
To run a launch file use the `ros2 launch` command.
```bash
ros2 launch package_name file.launch.py
```

View File

@ -0,0 +1,105 @@
# ROS 2 Parameters
## Overview of ROS 2 Parameters
In ROS 2, a **node parameter** is a configurable value used to adjust a node's behavior without modifying its code. Parameters are important because they provide flexibility, allowing dynamic tuning and reusability of nodes across different scenarios. For instance say you had a ROS node that took data from a USB camera and published it to a topic. You could use parameters to set the frame rate, USB port, and color depth.
## ROS 2 Parameters CLI
The following command are help full when you are debugging or tracking down a problem.
Command to list parameters
```bash
ros2 param list /node_name
```
Command to get parameter value
```bash
ros2 param get /node_name param_name
```
## Adding ROS 2 Parameters
Adding Parameters to a ROS 2 program is not that hard. Below you can see the steps.
=== "Python"
```python title="number_publisher.py" linenums="1" hl_lines="11 12 14 15"
#!/usr/bin/env python3
import rclpy
from rclpy.node import Node
from example_interfaces.msg import Int64
class NumberPublisherNode(Node):
def __init__(self):
super().__init__("number_publisher")
self.declare_parameter("number_to_publish", 2)
self.declare_parameter("publish_frequency", 1.0)
self.number_ = self.get_parameter("number_to_publish").value
self.publish_frequency_ = self.get_parameter("publish_frequency").value
self.number_publisher_ = self.create_publisher(Int64, "number", 10)
self.number_timer_ = self.create_timer(1.0 / self.publish_frequency_, self.publish_number)
self.get_logger().info("Number publisher has been started.")
def publish_number(self):
msg = Int64()
msg.data = self.number_
self.number_publisher_.publish(msg)
def main(args=None):
rclpy.init(args=args)
node = NumberPublisherNode()
rclpy.spin(node)
rclpy.shutdown()
if __name__ == "__main__":
main()
```
=== "C++"
```cpp title="number_publisher.cpp" linenums="1" hl_lines="9 10 12 13"
#include "rclcpp/rclcpp.hpp"
#include "example_interfaces/msg/int64.hpp"
class NumberPublisherNode : public rclcpp::Node
{
public:
NumberPublisherNode() : Node("number_publisher")
{
this->declare_parameter("number_to_publish", 2);
this->declare_parameter("publish_frequency", 1.0);
number_ = this->get_parameter("number_to_publish").as_int();
double publish_frequency = this->get_parameter("publish_frequency").as_double();
number_publisher_ = this->create_publisher<example_interfaces::msg::Int64>("number", 10);
number_timer_ = this->create_wall_timer(std::chrono::milliseconds((int)(1000.0 / publish_frequency)),
std::bind(&NumberPublisherNode::publishNumber, this));
RCLCPP_INFO(this->get_logger(), "Number publisher has been started.");
}
private:
void publishNumber()
{
auto msg = example_interfaces::msg::Int64();
msg.data = number_;
number_publisher_->publish(msg);
}
int number_;
rclcpp::Publisher<example_interfaces::msg::Int64>::SharedPtr number_publisher_;
rclcpp::TimerBase::SharedPtr number_timer_;
};
int main(int argc, char **argv)
{
rclcpp::init(argc, argv);
auto node = std::make_shared<NumberPublisherNode>();
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}
```

View File

@ -1,4 +1,4 @@
# Publisher # ROS 2 Publisher
## Sample Code ## Sample Code
Here is the code for creating a node that publishes strings to `/robot_news` at 2Hz. Here is the code for creating a node that publishes strings to `/robot_news` at 2Hz.

View File

@ -0,0 +1,268 @@
# ROS 2 Services
## Remapping Service Topics
Remapping service names can come in handy when packages have duplicate service names or you want to run the same service twice.
```bash
ros2 run your_package your_node --ros-args -r old_name:=new_name
```
## Debugging a Service
Sometimes you do not know what type of message a service takes. To find the right type use the `ros2 service type` command:
```bash
ros2 service type /service_topic
```
## Programming a Service
ROS 2, **services** enable synchronous communication between nodes. A service consists of a **request** and **response** model: one node (client) sends a request, and another node (service server) processes it and returns a response.
Here is the code for making a simple ROS 2 Service
=== "Python"
```python title="add_two_ints_server.py" linenums="1"
#!/usr/bin/env python
import rclpy
from rclpy.node import Node
from example_interfaces.srv import AddTwoInts
class AddTwoIntsServerNode(Node):
def __init__(self):
super().__init__("add_two_ints_server")
self.server_ = self.create_service(AddTwoInts, "add_two_ints", self.callback_add_two_ints);
self.get_logger().info("Add Two Ints Server has been started.")
def callback_add_two_ints(self, request, response):
response.sum = request.a + request.b
self.get_logger().info(str(request.a) + " + " + str(request.b) + " = " + str(response.sum))
return response
def main (args=None):
rclpy.init(args=args)
node = AddTwoIntsServerNode()
rclpy.spin(node)
rclpy.shutdown()
if __name__ == '__main__':
main()
```
=== "C++"
```cpp title="add_two_ints_server.cpp" linenums="1"
#include "rclcpp/rclcpp.hpp"
#include "example_interfaces/srv/add_two_ints.hpp"
using std::placeholders::_1;
using std::placeholders::_2;
class AddTwoIntServerNode : public rclcpp::Node
{
public:
AddTwoIntServerNode() : Node("add_two_ints_server")
{
server_ = this->create_service<example_interfaces::srv::AddTwoInts>(
"add_two_ints",
std::bind(&AddTwoIntServerNode::callbackAddTwoInts, this, _1, _2));
RCLCPP_INFO(this->get_logger(), "Service server has been started.");
}
private:
void callbackAddTwoInts(const example_interfaces::srv::AddTwoInts::Request::SharedPtr request,
const example_interfaces::srv::AddTwoInts::Response::SharedPtr response)
{
response->sum = request->a + request->b;
RCLCPP_INFO(this->get_logger(), "%d + %d = %d", (int)request->a, (int)request->b, (int)response->sum);
}
rclcpp::Service<example_interfaces::srv::AddTwoInts>::SharedPtr server_;
};
int main(int argc, char **argv)
{
rclcpp::init(argc, argv);
auto node = std::make_shared<AddTwoIntServerNode>();
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}
```
## Programming a Client
ROS 2, **Clients** are nodes or components that initiate the service call, while the service itself is implemented by the server node. This interaction is commonly used for tasks requiring immediate feedback, such as retrieving or setting specific values.
### Manual Making A Service Call
Sometime it is handy to make a ROS 2 service call manually. To do this use the `ros2 service call` command:
```bash
ros2 service call /add_two_ints example_interfaces/srv/AddTwoInts "{a: 5, b: 1}"
```
### Without Object Orientated Programming
This program creates a client without using object orientated programming.
=== "Python"
```python title="add_two_ints_client_no_oop.py" linenums="1"
#!/usr/bin/env python3
import rclpy
from rclpy.node import Node
from example_interfaces.srv import AddTwoInts
def main(args=None):
rclpy.init(args=args)
node = Node("add_two_ints_client_no_oop")
client = node.create_client(AddTwoInts, "add_two_ints")
while not client.wait_for_service(1.0):
node.get_logger().warn("Waiting for Server Add Two Ints...")
request = AddTwoInts.Request()
request.a = 3
request.b = 8
future = client.call_async(request)
rclpy.spin_until_future_complete(node, future)
try:
response = future.result()
node.get_logger().info(str(request.a) + " + " + str(request.b) + " = " + str(response.sum))
except Exception as e:
node.get_Togger().error("Service call failed %r" % (e,))
rclpy.shutdown()
if __name__ == "__main__":
main()
```
=== "C++"
```cpp title="add_two_ints_client_no_oop.cpp" linenums="1"
#include "rclcpp/rclcpp.hpp"
#include "example_interfaces/srv/add_two_ints.hpp"
int main(int argc, char **argv)
{
rclcpp::init(argc, argv);
auto node = std::make_shared<rclcpp::Node>("add_two_ints_client_no_oop");
auto client = node->create_client<example_interfaces::srv::AddTwoInts>("add_two_ints");
while(!client->wait_for_service(std::chrono::seconds(1)))
{
RCLCPP_WARN(node->get_logger(), "Waiting for the server to be up");
}
auto request = std::make_shared<example_interfaces::srv::AddTwoInts::Request>();
request->a = 3;
request->b = 8;
auto future = client->async_send_request(request);
if(rclcpp::spin_until_future_complete(node, future) == rclcpp::FutureReturnCode::SUCCESS)
{
RCLCPP_INFO(node->get_logger(), "%d + %d = %d", (int)request->a, (int)request->b, (int)future.get()->sum);
}
else{
RCLCPP_ERROR(node->get_logger(), "Error while calling service");
}
rclcpp::shutdown();
return 0;
}
```
### With Object Orientated Programming
This program creates a client with object orientated programming.
=== "Python"
```python title="add_two_ints_client.py" linenums="1"
#!/usr/bin/env python3
import rclpy
from rclpy.node import Node
from functools import partial
from example_interfaces.srv import AddTwoInts
class AddTwoIntsClientNode(Node):
def __init__(self):
super().__init__("add_two_ints_client")
self.call_add_two_ints_server(6,7)
def call_add_two_ints_server(self, a, b):
client = self.create_client(AddTwoInts, "add_two_ints")
while not client.wait_for_service(1.0):
self.get_logger().warn("Waiting For Server Add Ints...")
request = AddTwoInts.Request()
request.a = a
request.b = b
future = client.call_async(request)
future.add_done_callback(partial(self.callback_call_add_two_ints, a=a, b=b))
def callback_call_add_two_ints(self, future, a, b):
try:
response = future.result()
self.get_logger().info(str(a) + " + " + str(b) + " = " + str(response.sum))
except Exception as e:
self.get_Togger().error("Service call failed %r" % (e,))
def main (args=None):
rclpy.init(args=args)
node = AddTwoIntsClientNode()
rclpy.spin(node)
rclpy.shutdown()
if __name__ == '__main__':
main()
```
=== "C++"
```cpp title="add_two_ints_client.cpp" linenums="1"
#include "rclcpp/rclcpp.hpp"
#include "example_interfaces/srv/add_two_ints.hpp"
class AddTwoIntsClientNode : public rclcpp::Node
{
public:
AddTwoIntsClientNode() : Node("add_two_ints_client")
{
thread1_ = std::thread(std::bind(&AddTwoIntsClientNode::callAddTwoIntsService, this, 10, 4));
}
void callAddTwoIntsService(int a, int b)
{
auto client = this->create_client<example_interfaces::srv::AddTwoInts>("add_two_ints");
while(!client->wait_for_service(std::chrono::seconds(1)))
{
RCLCPP_WARN(this->get_logger(), "Waiting for the server to be up");
}
auto request = std::make_shared<example_interfaces::srv::AddTwoInts::Request>();
request->a = a;
request->b = b;
auto future = client->async_send_request(request);
try
{
auto response = future.get();
RCLCPP_INFO(this->get_logger(), "%d + %d = %d", a, b, (int)response->sum);
}
catch(const std::exception &e)
{
RCLCPP_ERROR(this->get_logger(), "Service call failed");
}
}
private:
std::thread thread1_;
};
int main(int argc, char **argv)
{
rclcpp::init(argc, argv);
auto node = std::make_shared<AddTwoIntsClientNode>();
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}
```

View File

@ -1,4 +1,4 @@
# Subscriber # ROS 2 Subscriber
## Sample Code ## Sample Code
Here is the code for creating a python node that subscribes to `/robot_news` at 2Hz. Here is the code for creating a python node that subscribes to `/robot_news` at 2Hz.

View File

@ -6,7 +6,7 @@
docker run -it --rm -v /dev:/dev -v /dev/shm:/dev/shm --privileged --net=host microros/micro-ros-agent:$ROS_DISTRO serial --dev [YOUR BOARD PORT] -v6 docker run -it --rm -v /dev:/dev -v /dev/shm:/dev/shm --privileged --net=host microros/micro-ros-agent:$ROS_DISTRO serial --dev [YOUR BOARD PORT] -v6
``` ```
=== "docker compose" === "docker compose"
```yaml title="docker-compose.yaml" ```yaml title="docker-compose.yaml" linenums="1"
services: services:
 micro_ros_agent:  micro_ros_agent:
   image: microros/micro-ros-agent:humble    image: microros/micro-ros-agent:humble
@ -26,7 +26,7 @@
docker run -it --rm -v /dev:/dev -v /dev/shm:/dev/shm --privileged --net=host microros/micro-ros-agent:$ROS_DISTRO tcp4 --port 8888 -v6 docker run -it --rm -v /dev:/dev -v /dev/shm:/dev/shm --privileged --net=host microros/micro-ros-agent:$ROS_DISTRO tcp4 --port 8888 -v6
``` ```
=== "docker compose" === "docker compose"
```yaml title="docker-compose.yaml" ```yaml title="docker-compose.yaml" linenums="1"
services: services:
 micro_ros_agent:  micro_ros_agent:
   image: microros/micro-ros-agent:humble    image: microros/micro-ros-agent:humble
@ -46,7 +46,7 @@
docker run -it --rm -v /dev:/dev -v /dev/shm:/dev/shm --privileged --net=host microros/micro-ros-agent:$ROS_DISTRO udp4 --port 8888 -v6 docker run -it --rm -v /dev:/dev -v /dev/shm:/dev/shm --privileged --net=host microros/micro-ros-agent:$ROS_DISTRO udp4 --port 8888 -v6
``` ```
=== "docker compose" === "docker compose"
```yaml title="docker-compose.yaml" ```yaml title="docker-compose.yaml" linenums="1"
services: services:
 micro_ros_agent:  micro_ros_agent:
   image: microros/micro-ros-agent:humble    image: microros/micro-ros-agent:humble

View File

@ -0,0 +1,18 @@
# ROS 2 Rosbag
## What is ROS 2 Rosbag
**Rosbag** is a tool for recording and replaying topic data. It is important because it enables debugging, analysis, and testing by capturing live data streams and replaying them for offline evaluation or simulation.
### The command to record a topic
```bash
ros2 bag record /{topic to record} -o {filename}
```
### The command to play back a topic
```bash
ros2 bag play {filename}
```
### The command to get information about a recording
```
ros2 bag info {filename}
```

37
docs/General ROS 2/ROS 2 TF.md Executable file
View File

@ -0,0 +1,37 @@
# ROS 2 TF
## What is ROS 2 TF
**TF (Transform)** is a library used to keep track of coordinate frames and their relationships over time. It allows robots to understand where objects and parts of themselves are in 3D space, enabling tasks like localization, navigation, and manipulation. For example, it handles transformations between the robot base, sensors, and the world.
## TF Tools
If you want to visualize the relation of the different TF's in a file use the following command to generate a PDF with a mermaid graph.
```bash
ros2 run tf2_tools view_frames
```
```mermaid
graph TD;
base_link --> left_leg;
left_leg --> left_base;
left_base --> left_front_wheel;
left_base --> left_back_wheel;
base_link --> right_leg;
right_leg --> right_base;
right_base --> right_front_wheel;
right_base --> right_back_wheel;
base_link --> gripper_pole;
gripper_pole --> left_gripper;
left_gripper --> left_tip;
gripper_pole --> right_gripper;
right_gripper --> right_tip;
base_link --> head;
head --> box;
```
## Viewing TFs
To view a urdf file that has TFs in it use the following command.
```bash
ros2 launch urdf_tutorial display.launch.py model:=/opt/ros/humble/share/urdf_tutorial/urdf/08-macroed.urdf.xacro
```

195
docs/General ROS 2/URDF.md Executable file
View File

@ -0,0 +1,195 @@
# URDF
## What is a URDF
A **URDF (Unified Robot Description Format)** in ROS 2 is an XML file format used to describe a robot's structure. It defines the robot's physical properties, such as its links (rigid parts), joints (connections), dimensions, and more. URDFs are crucial for simulation, visualization, and robot control as they provide the framework for tools like RViz and Gazebo to understand the robot's design.
here is a sample:
```xml title="my_robot.udrf" linenums="1"
<?xml version="1.0"?>
<robot name="my_robot">
<material name="blue">
<color rgba="0 0 0.5 1"/>
</material>
<material name="gray">
<color rgba="0.5 0.5 0.5 1.0"/>
</material>
<link name="base_footprint">
<origin xyz="0 0 0" rpy="0 0 0"/>
</link>
<link name="base_link">
<visual>
<geometry>
<box size="0.6 0.4 0.2"/>
</geometry>
<origin xyz="0 0 0.1" rpy="0 0 0"/>
<material name="blue"/>
</visual>
</link>
<link name="left_wheel_link">
<visual>
<geometry>
<cylinder radius="0.1" length="0.05"/>
</geometry>
<origin xyz="0 0 0" rpy="1.57 0 0"/>
<material name="gray"/>
</visual>
</link>
<link name="right_wheel_link">
<visual>
<geometry>
<cylinder radius="0.1" length="0.05"/>
</geometry>
<origin xyz="0 0 0" rpy="1.57 0 0"/>
<material name="gray"/>
</visual>
</link>
<link name="caster_wheel_link">
<visual>
<geometry>
<sphere radius="0.05" />
</geometry>
<origin xyz="0 0 0" rpy="0 0 0"/>
<material name="gray"/>
</visual>
</link>
<joint name="base_joint" type="fixed">
<parent link="base_footprint" />
<child link="base_link" />
<origin xyz="0 0 0.1" rpy="0 0 0" />
</joint>
<joint name="base_left_wheel_joint" type="continuous">
<parent link="base_link" />
<child link="left_wheel_link" />
<origin xyz="-0.15 0.225 0" rpy="0 0 0" />
<axis xyz="0 1 0"/>
</joint>
<joint name="base_right_wheel_joint" type="continuous">
<parent link="base_link" />
<child link="right_wheel_link" />
<origin xyz="-0.15 -0.225 0" rpy="0 0 0" />
<axis xyz="0 1 0"/>
</joint>
<joint name="base_caster_wheel_joint" type="fixed">
<parent link="base_link" />
<child link="caster_wheel_link" />
<origin xyz="0.2 0 -0.05" rpy="0 0 0" />
</joint>
</robot>
```
### Materials
Materials allow color and other information to be added to a `<visual>`
```xml
<material name="green">
<color rgba='0 1 0 0'>
</material>
```
### Links
**Links** are fundamental building blocks in a URDF file that represent the robot's physical parts. They allow you to create the individual parts of the robot and they are made up of multiple parts.
#### Visual
1. **`origin`**:
- Specifies the position and orientation of the visual geometry relative to the link's frame.
- Example: `<origin xyz="0 0 0.5" rpy="0 0 0" />`.
2. **`geometry`**:
- Describes the shape of the link. Shapes can be:
- **Box**: `<box size="x y z" />` specifies dimensions.
- **Cylinder**: `<cylinder length="h" radius="r" />`.
- **Sphere**: `<sphere radius="r" />`.
- **Mesh**: `<mesh filename="path/to/file" scale="x y z" />` for complex shapes.
3. **`material`**:
- Specifies the color or texture of the link.
- **Color**: `<color rgba="r g b a" />` uses RGBA values (alpha for transparency).
- **Texture**: `<texture filename="path/to/texture" />` for applying an image.
```xml
<link name="base_link">
<visual>
<geometry>
<box size="0.6 0.4 0.2" />
</geometry>
<origin xyz="0 0 0.1" rpy="0 0 0"/>
<material name="green" />
</visual>
</link>
```
### Joints
Joints are what old different objects together. There are many different types of joints as shown below. There are also different parts to a joint. The `<parent>` link tells the join what it is attached to while the `<child>` link tells it what its child is. The `<origin>` section tells the joint where it is located with the `xyz`being the coordinates and the `rpy` being role, pitch, and yaw. The `<axis>` section indicates a 1 for allowed moment and a 0 for no movement. Finally the `<limit>` section gives a lower and upper bound.
#### revolute
A hinge joint that rotates along the axis and has a limited range specified by the upper and lower limits that are in radians.
```xml
<joint name="joint_name" type="revolute">
<parent link="parent_name"/>
<child link="child_name"/>
<origin xyz="0 0 0" rpy="0 0 0"/>
<axis xyz="0, 0, 1" />
<limit lower="0" upper="0.2" velocity="100" effort="100" />
</joint>
```
#### continuous
A continuous hinge joint that rotates around the axis and has no upper and lower limits.
```xml
<joint name="joint_name" type="continuous">
<parent link="parent_name"/>
<child link="child_name"/>
<origin xyz="0 1 0" rpy="0 0 0"/>
<axis xyz="0, 0, 0" />
<limit velocity="100" effort="100" />
</joint>
```
#### prismatic
A sliding joint that slides along the axis, and has a limited range specified by the upper and lower limits in meters.
```xml
<joint name="joint_name" type="prismatic">
<parent link="parent_name"/>
<child link="child_name"/>
<origin xyz="0 0 0" rpy="0 0 0"/>
<axis xyz="1, 0, 0" />
<limit lower="0" upper="0.2" velocity="100" effort="100" />
</joint>
```
#### fixed
This is not really a joint because it cannot move. All degrees of freedom are locked. This type of joint does not require the `<axis>`, `<calibration>`, `<dynamics>`, `<limits>` or `<safety_controller>`.
```xml
<joint name="joint_name" type="fixed">
<parent link="parent_name"/>
<child link="child_name"/>
<origin xyz="0 0 0" rpy="0 0 0"/>
</joint>
```
#### floating
This joint allows motion for all 6 degrees of freedom.
```xml
<joint name="joint_name" type="floating">
<parent link="parent_name"/>
<child link="child_name"/>
<origin xyz="0 0 0" rpy="0 0 0"/>
</joint>
```
#### planar
This joint allows motion in a plane perpendicular to the axis.
```xml
<joint name="joint_name" type="planar">
<parent link="parent_name"/>
<child link="child_name"/>
<origin xyz="0 0 0" rpy="0 0 0"/>
<axis xyz="0, 0, 0" />
<limit lower="0" upper="0.2" velocity="100" effort="100" />
</joint>
```
## Links For Help
Here are some links for help.
- [ROS.org](https://wiki.ros.org/urdf/XML)

View File

@ -1,12 +1,12 @@
# Welcome # Welcome
Welcome this is the documenation for the Rhodes State ROS 2 Robotics equitment. This information is a collection of useful tips gathered from [Clearpath Robot Docs](https://docs.clearpathrobotics.com/docs/robots/), [Clearpath ROS 2](https://docs.clearpathrobotics.com/docs/ros/), and [Turtlebot 4 Manuel](https://turtlebot.github.io/turtlebot4-user-manual/). It also has informtation from the following courses; [ROS2 For Beginners](https://www.udemy.com/course/ros2-for-beginners) Welcome this is the documenation for the Rhodes State ROS 2 Robotics equitment. This information is a collection of useful tips gathered from [Clearpath Robot Docs](https://docs.clearpathrobotics.com/docs/robots/), [Clearpath ROS 2](https://docs.clearpathrobotics.com/docs/ros/), and [Turtlebot 4 Manuel](https://turtlebot.github.io/turtlebot4-user-manual/). It also has informtation from the following courses; [ROS2 For Beginners](https://www.udemy.com/course/ros2-for-beginners)
# General ROS 2
This section covers general topics and tools that are useful for ROS 2 development.
## General ROS 2 # Husky 200
This section covers genral topics and tools that are useful for ROS 2 development. This section contains all the important information on the Husky 200, including how to program it and use ROS 2.
## Husky 200 # Turtlebot 4
This section has all the importent information on the Husky 200 and how to program it and using ROS 2. This section contains information on the Turtlebot 4, including how to program it and use ROS 2.
## Turtlebot 4
This section has information on the Turtlebot 4 and how to program it and using ROS 2.