Skip to content

Commit f2c7253

Browse files
committed
updated executor tutorial
1 parent cea2b83 commit f2c7253

1 file changed

Lines changed: 102 additions & 72 deletions

File tree

  • _docs/tutorials/core/programming_rcl_rclc

‎_docs/tutorials/core/programming_rcl_rclc/index.md‎

Lines changed: 102 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -215,11 +215,108 @@ if (rc != RCL_RET_OK) {
215215

216216
## <a name="rclc_executor"/>RCLC Executor
217217

218-
The rclc-package is a [ROS 2](http://www.ros2.org/) package, which provides convenience functions to create ROS Client Library(RCL) data types and an RCLC-Executor to manage the execution for applications written in the C programming language. A complete documentation of the API and examples can be found [rclc package](https://github.com/ros2/rclc/tree/master/rclc) and [rclc example package](https://github.com/ros2/rclc/tree/master/rclc_examples), respectivly.
219-
The convenience functions can be used to create RCL objects, like publishers, subscribers, timers and nodes in a simplified API: The convenience functions, aka the rclc-package does not introduce any additional types but uses the data types of RCL only.
220-
The rclc Executor provides an API to configure the communication objects like subscriptions and timers and a defined semantics to execute corresponding callbacks, like the rclcpp Executor for C++.
221-
As described in [CB2019](#CB2019), it is difficult to reason about end-to-end latencies because of the complex semantics of the rclcpp Executor.
222-
Therefore, the RCLC Executor provides a number of features for deterministic and real-time execution.
218+
The rclc Executor provides an C-API to manage the execution of communication objects, like subscriptions and timers, like the rclcpp Executor for C++.
219+
Due to the complex semantics of the rclcpp Executor, it is difficult to reason about end-to-end latencies and to give real-time guarantees.
220+
To resolve these issues, the rclc Executor provides additional features for deterministic execution.
221+
222+
### Minimal setup
223+
This example demonstrates a minimal setup of the rclc Executor.
224+
225+
First, you include the header files [rclc/rclc.h](https://github.com/micro-ROS/rclc/blob/master/rclc/include/rclc/rclc.h). and [rclc/executor.h](https://github.com/micro-ROS/rclc/blob/master/rclc/include/rclc/executor.h).. After defining the `executor` you need to call the function `rclc_executor_get_zero_initialized_executor()` which returns a properly zero-initialized executor object.
226+
227+
```c
228+
#include<rclc/rclc.h>
229+
#include<rclc/executor.h>
230+
231+
main(int argc, const char * argv[]) {
232+
rclc_executor_t executor;
233+
executor = rclc_executor_get_zero_initialized_executor();
234+
```
235+
In the next step, executor is initialized with the RCL `context`, the number of communication objects `num_handles` and an `allocator`. The number of communication objects defines the total number of times and subscriptions, the executor shall manage. In this example, the executor will be setup with one timer and one subscription.
236+
237+
The `context` can be created with the convenience function `rclc_support_init()`, which creates a `support` variable of the type `rclc_support_t`, containing also other rcl objects necessary for the executor. To dynamically create memory, the `rclc_support_init()` function also needs an `allocator`, which is defined first.
238+
239+
```c
240+
241+
rcl_allocator_t allocator = rcl_get_default_allocator();
242+
rclc_support_t support;
243+
rcl_ret_t rc;
244+
245+
// create init_options
246+
rc = rclc_support_init(&support, argc, argv, &allocator);
247+
if (rc != RCL_RET_OK) {
248+
printf("Error rclc_support_init.\n");
249+
return -1;
250+
}
251+
252+
// compute total number of subsribers and timers
253+
unsigned int num_handles = 1 + 1;
254+
rclc_executor_init(&executor, &support.context, num_handles, &allocator);
255+
```
256+
257+
To add a subscription the function `rclc_c_executor_add_subscription` is used. Assuming you have created a susbscription `my_sub` with its message `sub_msg`and its callback `my_subscriber_callback`, the code looks like this:
258+
259+
continue Here
260+
261+
and a timer `my_timer`, as described above, the following code snippet shows the configuration for the executor:
262+
263+
```c
264+
// add subscription to executor
265+
rc = rclc_executor_add_subscription(&executor, &my_sub, &sub_msg, &my_subscriber_callback,
266+
ON_NEW_DATA);
267+
if (rc != RCL_RET_OK) {
268+
printf("Error in rclc_executor_add_subscription. \n");
269+
}
270+
271+
rclc_executor_add_timer(&executor, &my_timer);
272+
if (rc != RCL_RET_OK) {
273+
printf("Error in rclc_executor_add_timer.\n");
274+
}
275+
```
276+
277+
```c
278+
// set timeout for rcl_wait()
279+
unsigned int rcl_wait_timeout = 1000; // in ms
280+
rc = rclc_executor_set_timeout(&executor, RCL_MS_TO_NS(rcl_wait_timeout));
281+
if (rc != RCL_RET_OK) {
282+
printf("Error in rclc_executor_set_timeout.");
283+
}
284+
```
285+
286+
287+
288+
289+
A key feature of the rclc-Executor is, that the order of these functions to add handles matters. It defines the processing order when multiple messages have arrived or timers are ready. This provides full control over the execution order to the user.
290+
291+
There are are several functions, how to start the executor, one is `rclc_executor_spin_some(...)`, which allows the user to specify a timeout in nanoseconds.
292+
293+
Finally the code snippet shows how to destroy the objects for the node, publisher, subscriber and the executor with the respective fini-methods.
294+
295+
```c
296+
297+
298+
299+
300+
301+
302+
for (;;) {
303+
// timeout specified in ns (here 1s)
304+
rclc_executor_spin_some(&executor, 1000 * (1000 * 1000));
305+
}
306+
307+
// clean up
308+
rc = rclc_executor_fini(&executor);
309+
rc += rcl_publisher_fini(&my_pub, &my_node);
310+
rc += rcl_timer_fini(&my_timer);
311+
rc += rcl_subscription_fini(&my_sub, &my_node);
312+
rc += rcl_node_fini(&my_node);
313+
rc += rclc_support_fini(&support);
314+
315+
if (rc != RCL_RET_OK) {
316+
printf("Error while cleaning up!\n");
317+
return -1;
318+
}
319+
```
223320
224321
### rclc Executor Features
225322
The rclc Executor is a ROS 2 Executor implemented based on and for the rcl API, for applications written in the C language.
@@ -366,73 +463,6 @@ The function `rlce_executor_fini` frees the dynamically allocated memory of the
366463
367464
We provide the relevant code snippets how to setup the rclc Executor for typical software design patterns in mobile robotics applications and for real-time embedded use-cases.
368465
369-
#### Minimal example of rclc Executor
370-
This example demonstrates a minimal setup of the rclc Executor.
371-
The Executor is configured and setup with the functions
372-
`rclc_executor_get_zero_initialized_executor(..)`, `rclc_executor_set_timeout(..)`, `rclc_executor_add_subscription(...)`, `rclc_executor_add_timer(...)`, `rclc_executor_spin_some(...)` and `rclc_executor_fini(...)` in [rclc/executor.h](https://github.com/micro-ROS/rclc/blob/master/rclc/include/rclc/executor.h).
373-
374-
Assuming that you have created a subscription and a timer, as described above, the following code snippet shows the configuration of the RCLC-executor.
375-
376-
The executor is configured with 2 handles, aka one subscription and one timer. The `support.context` is the ROS 2 context, as shown above with `rclc_support_init(...)`.
377-
378-
The timeout is the waiting time for new handles to get ready, for example new messages arrive or timers are ready. The unit of the timeout is milliseconds.
379-
380-
A subscription `my_sub`, a place-holder for it's message `sub_msg` and it's corresponding callback `my_subscriber_callback` is added to the executor with the function `rclc_executor_add_subscription(...)`
381-
382-
A timers is added to the executor with the function `rclc_executor_add_timer(...)`
383-
384-
A key feature of the rclc-Executor is, that the order of these functions to add handles matters. It defines the processing order when multiple messages have arrived or timers are ready. This provides full control over the execution order to the user.
385-
386-
There are are several functions, how to start the executor, one is `rclc_executor_spin_some(...)`, which allows the user to specify a timeout in nanoseconds.
387-
388-
Finally the code snippet shows how to destroy the objects for the node, publisher, subscriber and the executor with the respective fini-methods.
389-
390-
```c
391-
rclc_executor_t executor;
392-
393-
// compute total number of subsribers and timers
394-
unsigned int num_handles = 1 + 1;
395-
printf("Debug: number of DDS handles: %u\n", num_handles);
396-
executor = rclc_executor_get_zero_initialized_executor();
397-
rclc_executor_init(&executor, &support.context, num_handles, &allocator);
398-
399-
// set timeout for rcl_wait()
400-
unsigned int rcl_wait_timeout = 1000; // in ms
401-
rc = rclc_executor_set_timeout(&executor, RCL_MS_TO_NS(rcl_wait_timeout));
402-
if (rc != RCL_RET_OK) {
403-
printf("Error in rclc_executor_set_timeout.");
404-
}
405-
406-
// add subscription to executor
407-
rc = rclc_executor_add_subscription(&executor, &my_sub, &sub_msg, &my_subscriber_callback,
408-
ON_NEW_DATA);
409-
if (rc != RCL_RET_OK) {
410-
printf("Error in rclc_executor_add_subscription. \n");
411-
}
412-
413-
rclc_executor_add_timer(&executor, &my_timer);
414-
if (rc != RCL_RET_OK) {
415-
printf("Error in rclc_executor_add_timer.\n");
416-
}
417-
418-
for (;;) {
419-
// timeout specified in ns (here 1s)
420-
rclc_executor_spin_some(&executor, 1000 * (1000 * 1000));
421-
}
422-
423-
// clean up
424-
rc = rclc_executor_fini(&executor);
425-
rc += rcl_publisher_fini(&my_pub, &my_node);
426-
rc += rcl_timer_fini(&my_timer);
427-
rc += rcl_subscription_fini(&my_sub, &my_node);
428-
rc += rcl_node_fini(&my_node);
429-
rc += rclc_support_fini(&support);
430-
431-
if (rc != RCL_RET_OK) {
432-
printf("Error while cleaning up!\n");
433-
return -1;
434-
}
435-
```
436466
#### Example sense-plan-act pipeline in mobile robotics
437467
438468
A common design paradigm in mobile robotics is a control loop, consisting of several phases: A sensing phase to aquire sensor data, a plan phase for localization and path planning and an actuation-phase to steer the mobile robot.

0 commit comments

Comments
 (0)