Skip to content

Commit 133634a

Browse files
pablogs9FranFin
andauthored
Ref #8285. Updates on micro-ROS Linux (#150)
* Updates on micro-ROS Linux * Fix * Update _docs/tutorials/core/first_application_linux/index.md Co-authored-by: FranFin <58737168+FranFin@users.noreply.github.com> * Update _docs/tutorials/core/first_application_linux/index.md Co-authored-by: FranFin <58737168+FranFin@users.noreply.github.com> * Updates * Update cf Co-authored-by: FranFin <58737168+FranFin@users.noreply.github.com>
1 parent 1b04519 commit 133634a

1 file changed

Lines changed: 53 additions & 183 deletions

File tree

  • _docs/tutorials/core/first_application_linux

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

Lines changed: 53 additions & 183 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,24 @@ permalink: /docs/tutorials/core/first_application_linux/
55

66
This tutorial teaches you how to create a first micro-ROS application on Linux for testing purposes. In the follow-up tutorial [*First micro-ROS application on an RTOS*](/docs/tutorials/core/first_application_rtos/), you'll learn how to build and bring this application on a microcontroller running the RTOS NuttX, FreeRTOS, or Zephyr.
77

8+
## Installing ROS 2 and the micro-ROS build system
89

9-
## Adding a new micro-ROS app
10+
First of all, let's take a Ubuntu 18.04 LTS computer and install **ROS 2 Dashing Diademata**:
1011

11-
First of all, make sure that you have a **ROS 2** installation.
12+
```
13+
sudo locale-gen en_US en_US.UTF-8
14+
sudo update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8
15+
export LANG=en_US.UTF-8
16+
17+
sudo apt update && sudo apt install curl gnupg2 lsb-release
18+
curl -s https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc | sudo apt-key add -
1219
13-
***TIP:** if you are familiar with Docker containers, this image may be useful: [ros:dashing](https://hub.docker.com/layers/ros/library/ros/dashing/images/sha256-b796c14ea663537129897769aa6c715a851ca08dffd4875ef2ecaa31a4dbd431?context=explore)*
20+
sudo sh -c 'echo "deb http://packages.ros.org/ros2/ubuntu `lsb_release -cs` main" > /etc/apt/sources.list.d/ros2-latest.list'
21+
sudo apt update
22+
sudo apt install ros-dashing-desktop
23+
```
1424

15-
On the **ROS 2** installation open a command line and follow these steps:
25+
Once you have a **ROS 2** installation in the computer, follow these steps to install the micro-ROS build system:
1626

1727
```bash
1828
# Source the ROS 2 installation
@@ -32,224 +42,84 @@ colcon build
3242
source install/local_setup.bash
3343
```
3444

35-
Now, let's create a firmware workspace that targets all the required code and tools:
45+
***TIP:** if you are familiar with Docker containers, this image may be useful: [micro-ros:base](https://github.com/micro-ROS/docker/blob/dashing/base/Dockerfile)*
46+
47+
These instructions will setup a workspace with a ready-to-use micro-ROS build system. This build system is in charge of downloading the required cross-compilation tools and building the apps for the required platforms.
48+
49+
The build system's workflow is a four-step procedure:
50+
51+
* **Create step:** This step is in charge of downloading all the required code repositories and cross-compilation toolchains for the specific hardware platform. Among these repositories, it will also download a collection of ready to use micro-ROS apps.
52+
* **Configure step:** In this step, the user can select which app is going to be cross-compiled by the toolchain. Some other options, such as transport, agent address or port will be also selected in this step.
53+
* **Build step:** Here is where the cross-compilation takes place and the platform-specific binaries are generated.
54+
* **Flash step:** The binaries generated in the previous step are flashed onto the hardware platform memory, in order to allow the execution of the micro-ROS app.
55+
Further information about micro-ROS build system can be found [here](https://github.com/micro-ROS/micro-ros-build/tree/dashing/micro_ros_setup).
56+
57+
## Creating a new micro-ROS app
58+
59+
Once the build system is installed, let's create a firmware workspace that targets all the required code and tools:
3660

3761
```bash
3862
# Create step
3963
ros2 run micro_ros_setup create_firmware_ws.sh host
4064
```
4165

42-
Now you have all the required tools to build micro-ROS. At this point, you must know that the micro-ROS build system is a four-step workflow:
43-
44-
<!-- TODO (pablogs9): Remove and link to build-system tutorial when done -->
45-
1. **Create**: retrieves all the required packages for a specific RTOS and hardware platform.
46-
2. **Configure**: configures the downloaded packages with options such as the micro-ROS application, the selected transport layer or the micro-ROS agent IP address (in network transports).
47-
3. **Build**: generates a binary file ready for being loaded in the hardware.
48-
4. **Flash**: load the micro-ROS software in the hardware.
49-
50-
micro-ROS apps for Linux are located at `src/uros/micro-ROS-demos/rcl/`. In order to create a new application, create a new folder containing two files: the app code and the CMake file.
66+
micro-ROS apps for Linux are located at `src/uros/micro-ROS-demos/rcl/`. In order to create a new application, create a new folder containing two files: the app code and the CMake file. You can check the complete content of these file [here](https://github.com/micro-ROS/micro-ROS-demos/tree/dashing/rcl/ping_pong).
5167

5268
```bash
5369
# Creating a new app
5470
pushd src/uros/micro-ROS-demos/rcl/
55-
mkdir my_brand_new_app
56-
cd my_brand_new_app
71+
mkdir ping_pong
72+
cd ping_pong
5773
touch app.c CMakeLists.txt
5874
popd
5975
```
6076

61-
For this example we are going to create a ping pong app where a node sends a ping package with a unique identifier using a publisher and the same package is received by a pong subscriber. The node will also answer to pings received from other nodes with a pong message:
62-
63-
![pingpong](http://www.plantuml.com/plantuml/png/ZOwnIWGn48RxFCNFzSkoUG2vqce5jHEHi1dtWZkPa6GByNntavZY10yknMJu-ORlFwPiOjvvK-d3-M2YOR1uMKvHc93ZJafvoMML07d7h1NAE-DPWblg_na8vnwEx9OeZmzFOt1-BK7AzetJciPxCfRYVw1S0SbRLBEg1IpXPIvpUWLCmZpXIm6BS3addt7uQpu0ZQlxT1MK2r0g-7sfqbsbRrVfMrMwgbev3CDTlsqJGtJhATUmSMrMg5TKwaZUxfcttuMt7m00)
64-
65-
To start creating this app, the `CMakeLists.txt` file should looks like:
66-
67-
```cmake
68-
cmake_minimum_required(VERSION 3.5)
69-
70-
project(my_brand_new_app LANGUAGES C)
71-
72-
find_package(ament_cmake REQUIRED)
73-
find_package(rcl REQUIRED)
74-
find_package(std_msgs REQUIRED)
75-
find_package(rmw_microxrcedds REQUIRED)
77+
Don't forget to register your app in `src/uros/micro-ROS-demos/rcl/CMakeLists.txt` by adding the following line:
7678

77-
add_executable(${PROJECT_NAME} app.c)
79+
```
80+
export_executable(ping_pong)
81+
```
7882

79-
ament_target_dependencies(${PROJECT_NAME}
80-
rcl
81-
std_msgs
82-
rmw_microxrcedds
83-
)
83+
For this example we are going to create a ping pong app where a node sends a ping package with a unique identifier using a publisher and the same package is received by a pong subscriber. The node will also answer to pings received from other nodes with a pong message:
8484

85-
install(TARGETS ${PROJECT_NAME}
86-
DESTINATION ${PROJECT_NAME}
87-
)
88-
```
85+
![pingpong](http://www.plantuml.com/plantuml/png/ZOwnIWGn48RxFCNFzSkoUG2vqce5jHEHi1dtWZkPa6GByNntavZY10yknMJu-ORlFwPiOjvvK-d3-M2YOR1uMKvHc93ZJafvoMML07d7h1NAE-DPWblg_na8vnwEx9OeZmzFOt1-BK7AzetJciPxCfRYVw1S0SbRLBEg1IpXPIvpUWLCmZpXIm6BS3addt7uQpu0ZQlxT1MK2r0g-7sfqbsbRrVfMrMwgbev3CDTlsqJGtJhATUmSMrMg5TKwaZUxfcttuMt7m00)
8986

90-
Meanwhile `app.c` should look like the following code:
87+
The app logic of this demo is contained in [`app.c`](https://github.com/micro-ROS/micro-ROS-demos/blob/dashing/rcl/ping_pong/main.c). A thorough review of this file can show how to create a micro-ROS publisher or subscriber, as shown below.
9188

89+
**For more in depth learning about RCL and RCLC programming [check this section](https://micro-ros.github.io/docs/tutorials/core/programming_rcl_rclc/).**
9290
```c
93-
#include <rcl/rcl.h>
94-
#include <rcl/error_handling.h>
95-
#include <std_msgs/msg/header.h>
96-
97-
#include <rmw_uros/options.h>
98-
99-
#include <stdio.h>
100-
#include <unistd.h>
101-
#include <pthread.h>
102-
103-
#define STRING_BUFFER_LEN 100
104-
105-
// Publishing ping guard condition thread
106-
void * trigger_guard_condition(void *args){
107-
rcl_guard_condition_t * guard_condition = (rcl_guard_condition_t *)args;
108-
109-
while(true){
110-
rcl_trigger_guard_condition(guard_condition);
111-
sleep(5);
112-
}
113-
}
114-
115-
int main(int argc, const char * const * argv)
116-
{
117-
//Init RCL options
118-
rcl_init_options_t options = rcl_get_zero_initialized_init_options();
119-
rcl_init_options_init(&options, rcl_get_default_allocator());
120-
121-
// Init RCL context
122-
rcl_context_t context = rcl_get_zero_initialized_context();
123-
rcl_init(0, NULL, &options, &context);
124-
125-
// Create a node
126-
rcl_node_options_t node_ops = rcl_node_get_default_options();
127-
rcl_node_t node = rcl_get_zero_initialized_node();
128-
rcl_node_init(&node, "pingpong_node", "", &context, &node_ops);
129-
91+
...
13092
// Create a reliable ping publisher
13193
rcl_publisher_options_t ping_publisher_ops = rcl_publisher_get_default_options();
13294
rcl_publisher_t ping_publisher = rcl_get_zero_initialized_publisher();
13395
rcl_publisher_init(&ping_publisher, &node, ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, Header), "/microROS/ping", &ping_publisher_ops);
96+
...
13497

135-
// Create a best effort pong publisher
136-
rcl_publisher_options_t pong_publisher_ops = rcl_publisher_get_default_options();
137-
pong_publisher_ops.qos.reliability = RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT;
138-
rcl_publisher_t pong_publisher = rcl_get_zero_initialized_publisher();
139-
rcl_publisher_init(&pong_publisher, &node, ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, Header), "/microROS/pong", &pong_publisher_ops);
140-
98+
...
14199
// Create a best effort pong subscriber
142100
rcl_subscription_options_t pong_subscription_ops = rcl_subscription_get_default_options();
143101
pong_subscription_ops.qos.reliability = RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT;
144102
rcl_subscription_t pong_subscription = rcl_get_zero_initialized_subscription();
145103
rcl_subscription_init(&pong_subscription, &node, ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, Header), "/microROS/pong", &pong_subscription_ops);
146-
147-
// Create a best effort ping subscriber
148-
rcl_subscription_options_t ping_subscription_ops = rcl_subscription_get_default_options();
149-
ping_subscription_ops.qos.reliability = RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT;
150-
rcl_subscription_t ping_subscription = rcl_get_zero_initialized_subscription();
151-
rcl_subscription_init(&ping_subscription, &node, ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, Header), "/microROS/ping", &ping_subscription_ops);
152-
153-
// Create guard condition
154-
rcl_guard_condition_t guard_condition = rcl_get_zero_initialized_guard_condition();
155-
rcl_guard_condition_options_t guard_condition_options = rcl_guard_condition_get_default_options();
156-
rcl_guard_condition_init(&guard_condition, &context, guard_condition_options);
157-
158-
pthread_t guard_condition_thread;
159-
pthread_create(&guard_condition_thread, NULL, trigger_guard_condition, &guard_condition);
160-
161-
// Create a wait set
162-
rcl_wait_set_t wait_set = rcl_get_zero_initialized_wait_set();
163-
rcl_wait_set_init(&wait_set, 2, 1, 0, 0, 0, 0, &context, rcl_get_default_allocator());
164-
165-
// Create and allocate the pingpong publication message
166-
std_msgs__msg__Header msg;
167-
char msg_buffer[STRING_BUFFER_LEN];
168-
msg.frame_id.data = msg_buffer;
169-
msg.frame_id.capacity = STRING_BUFFER_LEN;
170-
171-
// Create and allocate the pingpong subscription message
172-
std_msgs__msg__Header rcv_msg;
173-
char rcv_buffer[STRING_BUFFER_LEN];
174-
rcv_msg.frame_id.data = rcv_buffer;
175-
rcv_msg.frame_id.capacity = STRING_BUFFER_LEN;
176-
177-
// Set device id and sequence number;
178-
int device_id = rand();
179-
int seq_no;
180-
181-
int pong_count = 0;
182-
struct timespec ts;
183-
rcl_ret_t rc;
184-
185-
do {
186-
// Clear and set the waitset
187-
rcl_wait_set_clear(&wait_set);
188-
189-
size_t index_pong_subscription;
190-
rcl_wait_set_add_subscription(&wait_set, &pong_subscription, &index_pong_subscription);
191-
192-
size_t index_ping_subscription;
193-
rcl_wait_set_add_subscription(&wait_set, &ping_subscription, &index_ping_subscription);
194-
195-
size_t index_guardcondition;
196-
rcl_wait_set_add_guard_condition(&wait_set, &guard_condition, &index_guardcondition);
197-
198-
// Run session for 100 ms
199-
rcl_wait(&wait_set, RCL_MS_TO_NS(100));
200-
104+
...
105+
...
201106
// Check if some pong message is received
202107
if (wait_set.subscriptions[index_pong_subscription]) {
203108
rc = rcl_take(wait_set.subscriptions[index_pong_subscription], &rcv_msg, NULL, NULL);
204-
if(rc == RCL_RET_OK && strcmp(msg.frame_id.data,rcv_msg.frame_id.data) == 0) {
205-
pong_count++;
206-
printf("Pong for seq %s (%d)\n", rcv_msg.frame_id.data, pong_count);
207-
}
208-
}
209-
210-
// Check if some ping message is received and pong it
211-
if (wait_set.subscriptions[index_ping_subscription]) {
212-
rc = rcl_take(wait_set.subscriptions[index_ping_subscription], &rcv_msg, NULL, NULL);
213-
214-
// Dont pong my own pings
215-
if(rc == RCL_RET_OK && strcmp(msg.frame_id.data,rcv_msg.frame_id.data) != 0){
216-
printf("Ping received with seq %s. Answering.\n", rcv_msg.frame_id.data);
217-
rcl_publish(&pong_publisher, (const void*)&rcv_msg, NULL);
109+
if(rc == RCL_RET_OK) {
110+
// Subscription app logic
218111
}
219112
}
220113

221-
// Check if it is time to send a ping
222-
if (wait_set.guard_conditions[index_guardcondition]) {
223-
// Generate a new random sequence number
224-
seq_no = rand();
225-
sprintf(msg.frame_id.data, "%d_%d", seq_no, device_id);
226-
msg.frame_id.size = strlen(msg.frame_id.data);
227-
228-
// Fill the message timestamp
229-
clock_gettime(CLOCK_REALTIME, &ts);
230-
msg.stamp.sec = ts.tv_sec;
231-
msg.stamp.nanosec = ts.tv_nsec;
232-
233-
// Reset the pong count and publish the ping message
234-
pong_count = 0;
235-
rcl_publish(&ping_publisher, (const void*)&msg, NULL);
236-
printf("Ping send seq %s\n", msg.frame_id.data);
237-
}
238-
239-
usleep(10000);
240-
} while (true);
241-
}
242-
```
243-
244-
Don't forget to register your app in `src/uros/micro-ROS-demos/rcl/CMakeLists.txt` by adding the following line:
245-
246-
```
247-
export_executable(my_brand_new_app)
114+
...
115+
// Reset the pong count and publish the ping message
116+
rcl_publish(&ping_publisher, (const void*)&msg, NULL);
117+
...
248118
```
249119
250120
## Running the micro-ROS app
251121
252-
The micro-ROS app is ready to connect to a micro-ROS-Agent and start talking with the rest of the ROS 2 world.
122+
The micro-ROS app is ready to be built and connected to a micro-ROS-Agent to start talking with the rest of the ROS 2 world.
253123
254124
First of all, create a micro-ROS agent:
255125
@@ -280,7 +150,7 @@ source /opt/ros/$ROS_DISTRO/setup.bash
280150
source install/local_setup.bash
281151

282152
# Run a micro-ROS agent
283-
ros2 run micro_ros_demos_rcl my_brand_new_app
153+
ros2 run micro_ros_demos_rcl ping_pong
284154
```
285155

286156
And finally, let's check that everything is working in another command line. We are going to listen to ping topic to check whether the Ping Pong node is publishing its own pings:

0 commit comments

Comments
 (0)