Skip to content

Commit

Permalink
Add README's for action_tutorials. (#576)
Browse files Browse the repository at this point in the history
Co-authored-by: Audrow Nash <audrow@hey.com>
  • Loading branch information
kagibson and paudrow committed Dec 7, 2022
1 parent dfc8d86 commit c212741
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 0 deletions.
21 changes: 21 additions & 0 deletions action_tutorials/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# ROS2 Action Tutorials

This tutorial demonstrates implementing ROS action servers and action clients.

The action server in this case takes in an integer value between 0 and 45 named *order* and returns the Fibonacci sequence, a sequence with the form:
$$F_0 = 0$$
$$F_1 = 1$$
$$F_{order}=F_{order-1} + F_{order-2}$$

The action server calculates each number in the sequence one at a time and returns a partial sequence as feedback at each iteration.
If the action is cancelled before the entire sequence is calculated, the server stops calculating the sequence and no result is returned.
The action client in this tutorial sends a goal to the action server with an order of 10.
It logs each partial sequence returned as feedback.
Once the action is finished executing, the action client logs the resulting sequence.

## Packages

- [action_tutorials_cpp](./action_tutorials_cpp) implements the described action server and client using the rclcpp library in C++.
- [action_tutorials_py](./action_tutorials_py) implements the described action server and client using the rclpy library in Python.
- [action_tutorials_interfaces](./action_tutorials_interfaces) defines the interface for the Fibonacci action.
This interface takes an *order* as a goal, returns a *partial sequence* as feedback and a *sequence* as a result.
67 changes: 67 additions & 0 deletions action_tutorials/action_tutorials_cpp/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Action Server

In the constructor for `FibonacciActionServer`, an action server is created with callbacks that are called when a goal is received, when the goal is cancelled, and when the goal is accepted:

```cpp
this->action_server_ = rclcpp_action::create_server<Fibonacci>(
...
"fibonacci",
std::bind(&FibonacciActionServer::handle_goal, this, _1, _2),
std::bind(&FibonacciActionServer::handle_cancel, this, _1),
std::bind(&FibonacciActionServer::handle_accepted, this, _1));
```
The `handle_goal` callback is called whenever a goal is sent to the action server by an action client.
In the example code, the goal is accepted as long as the order is less than or equal to 46, otherwise it is rejected.
This is to prevent potential [integer overflow](https://en.wikipedia.org/wiki/Integer_overflow):
```cpp
if (goal->order > 46) {
return rclcpp_action::GoalResponse::REJECT;
}
return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE;
```

The `handle_cancelled` callback is called whenever an action client requests to cancel the goal being executed.
In this case, the goal cancel request is always accepted.

The `handle_accepted` callback is called following the action server's acceptance of a goal. In this example, a thread is started to execute the goal:
```cpp
std::thread{std::bind(&FibonacciActionServer::execute, this, _1), goal_handle}.detach();
```
The execution thread calculates the Fibonacci sequence up to *order* and publishes partial sequences as feedback as each item is added to the sequence.
A `rclcpp::Rate` object is used to sleep between the calculation of each item in order to represent a long-running task.
When execution is complete, the full sequence is returned to the action client.
If the goal is cancelled during execution, no result is returned, however the caller may have received partial sequences as feedback up until cancelling.
# Action Client
In the constructor for `FibonacciActionClient`, and action client for the `fibonacci` action is created:
```cpp
this->client_ptr_ = rclcpp_action::create_client<Fibonacci>(
...
"fibonacci");
```

A goal of type `Fibonacci` is created with order 10.
The goal is sent asynchronously with callbacks registered for the goal response, the feedback, and the goal result:

```cpp
auto send_goal_options = rclcpp_action::Client<Fibonacci>::SendGoalOptions();
send_goal_options.goal_response_callback =
std::bind(&FibonacciActionClient::goal_response_callback, this, _1);
send_goal_options.feedback_callback =
std::bind(&FibonacciActionClient::feedback_callback, this, _1, _2);
send_goal_options.result_callback =
std::bind(&FibonacciActionClient::result_callback, this, _1);
this->client_ptr_->async_send_goal(goal_msg, send_goal_options);
```
There are three types of callback functions:
- The `goal_response_callback` is called when the action server accepts or rejects the goal.
- The `feedback_callback` is called whenever the action server sends goal execution feedback.
- The `goal_result_callback` is called when the action server is finished executing the goal and returns the result of the goal which is the full or partial Fibonacci sequence.
10 changes: 10 additions & 0 deletions action_tutorials/action_tutorials_interfaces/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Action Tutorials ROS2 Interface

This tutorial defines the Fibonacci action for use with the action tutorials.
There are three parts of the action:

- The goal contains an *order* field which determines the length of the returned Fibonacci sequence.
For example, order 2 should return sequence [0, 1] and order 5 should return sequence [0, 1, 1, 2, 3].
- The feedback consists of a partial sequence that is returned as the Fibonacci sequence is calculated.
For example, for order 5 at some point the partial sequence [0, 1, 1] will be returned.
- The result consists of the complete Fibonacci sequence (ex. [0, 1, 1, 2, ..., 165580141]).
51 changes: 51 additions & 0 deletions action_tutorials/action_tutorials_py/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Action Server

In the constructor for `FibonacciActionServer`, an action server is created with a callback that is called when a goal is accepted.
```python
self._action_server = ActionServer(
self,
Fibonacci,
'fibonacci',
self.execute_callback)
```

There are three types of callbacks:

- A `goal_callback` can optionally be added to conditionally accept or reject the goal, however, by default the goal is accepted.
- A `cancel_callback` can also optionally be added to conditionally accept or reject the cancel goal request, however, by default the cancel goal request is accepted.
- The `execute_callback` calculates the Fibonacci sequence up to *order* and publishes partial sequences as feedback as each item is added to the sequence.

The thread sleeps for 1 second between the calculation of each item in order to represent a long-running task.
When execution is complete, the full sequence is returned to the action client.

# Action Client

In the constructor for `FibonacciActionClient`, an action client for the `fibonacci` action is created:

```python
self._action_client = ActionClient(self, Fibonacci, 'fibonacci')
```

A goal of type `Fibonacci` is created with order 10.
The goal is sent asynchronously with callbacks registered for the goal response and the feedback:

```python
self._send_goal_future = self._action_client.send_goal_async(
goal_msg,
feedback_callback=self.feedback_callback)

self._send_goal_future.add_done_callback(self.goal_response_callback)
```

Within the `goal_response_callback`, if the goal is accepted, the goal result is requested asynchronously.
A callback is registered for receiving the result:
```python
self._get_result_future = goal_handle.get_result_async()

self._get_result_future.add_done_callback(self.get_result_callback)
```

There are two types of callbacks:

- The `feedback_callback` logs the partial sequences as they are received.
- The `get_result_callback` logs the full Fibonacci sequence.

0 comments on commit c212741

Please sign in to comment.