Skip to content

Commit e0e90e2

Browse files
committed
Provide tutorial about basic concepts of ROS2 (#1243)
This PR adds a comprehensive tutorial document that introduces the fundamental communication patterns in ROS 2, specifically focusing on Topics (publish/subscribe) and Services (request/response) patterns using the rclnodejs JavaScript library. - Provides detailed explanations and practical examples for ROS 2 Topics and Services - Includes complete JavaScript code examples for publishers, subscribers, service servers, and clients - Offers guidance on when to use Topics vs Services with practical use cases Fix: #1241
1 parent dc7c434 commit e0e90e2

File tree

1 file changed

+399
-0
lines changed

1 file changed

+399
-0
lines changed

tutorials/ros2-basic-concepts.md

Lines changed: 399 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,399 @@
1+
# ROS 2 Basic Concepts Tutorial
2+
3+
This tutorial introduces the fundamental communication patterns in ROS 2: **Topics** (publish/subscribe) and **Services** (request/response). These are essential building blocks for any ROS 2 application.
4+
5+
## Table of Contents
6+
7+
- [What are ROS 2 Communication Patterns?](#what-are-ros-2-communication-patterns)
8+
- [Topics (Publish/Subscribe)](#topics-publishsubscribe)
9+
- [Services (Request/Response)](#services-requestresponse)
10+
- [When to Use Topics vs Services](#when-to-use-topics-vs-services)
11+
- [Running the Examples](#running-the-examples)
12+
13+
## What are ROS 2 Communication Patterns?
14+
15+
ROS 2 provides three primary communication patterns:
16+
17+
- **📡 Topics** - Continuous data streams (pub/sub)
18+
- **🔧 Services** - Remote procedure calls (request/response)
19+
- **⚡ Actions** - Long-running tasks with feedback (covered in separate tutorial)
20+
21+
These patterns enable **distributed communication** between nodes in a robotics system, allowing for flexible, modular architectures.
22+
23+
## Topics (Publish/Subscribe)
24+
25+
### Concept Overview
26+
27+
**Topics** implement a **publish/subscribe** communication pattern where:
28+
29+
- **Publishers** produce data and send it to a named topic
30+
- **Subscribers** consume data from the same named topic
31+
- **Anonymous** - Subscribers don't know which publisher sent the data
32+
- **Many-to-many** - Multiple publishers and subscribers per topic
33+
- **Asynchronous** - Publishers don't wait for subscribers
34+
35+
Topics are ideal for **continuous data streams** like sensor readings, robot state, or camera images.
36+
37+
### Basic Publisher Example
38+
39+
```javascript
40+
const rclnodejs = require('rclnodejs');
41+
42+
async function createPublisher() {
43+
await rclnodejs.init();
44+
const node = rclnodejs.createNode('publisher_example_node');
45+
46+
// Create a publisher for String messages on 'topic'
47+
const publisher = node.createPublisher('std_msgs/msg/String', 'topic');
48+
49+
let counter = 0;
50+
setInterval(() => {
51+
const message = `Hello ROS ${counter}`;
52+
console.log(`Publishing message: ${message}`);
53+
publisher.publish(message);
54+
counter++;
55+
}, 1000);
56+
57+
rclnodejs.spin(node);
58+
}
59+
60+
createPublisher().catch(console.error);
61+
```
62+
63+
### Basic Subscriber Example
64+
65+
```javascript
66+
const rclnodejs = require('rclnodejs');
67+
68+
async function createSubscriber() {
69+
await rclnodejs.init();
70+
const node = rclnodejs.createNode('subscriber_example_node');
71+
72+
// Create a subscriber for String messages on 'topic'
73+
node.createSubscription('std_msgs/msg/String', 'topic', (msg) => {
74+
console.log(`Received message: ${typeof msg}`, msg);
75+
});
76+
77+
rclnodejs.spin(node);
78+
}
79+
80+
createSubscriber().catch(console.error);
81+
```
82+
83+
### Topic Features
84+
85+
- **Strongly Typed** - Messages have well-defined types (e.g., `std_msgs/msg/String`)
86+
- **Buffered** - Publishers can send data even if no subscribers exist
87+
- **Discoverable** - Use `ros2 topic list` to see available topics
88+
- **Quality of Service** - Configure reliability, durability, and latency
89+
90+
### Advanced Publisher with Custom Messages
91+
92+
```javascript
93+
const rclnodejs = require('rclnodejs');
94+
95+
async function publishSensorData() {
96+
await rclnodejs.init();
97+
const node = rclnodejs.createNode('sensor_publisher');
98+
99+
// Publisher for geometry messages
100+
const publisher = node.createPublisher('geometry_msgs/msg/Twist', 'cmd_vel');
101+
102+
setInterval(() => {
103+
// Create a Twist message for robot velocity
104+
const twist = {
105+
linear: { x: 1.0, y: 0.0, z: 0.0 },
106+
angular: { x: 0.0, y: 0.0, z: 0.5 },
107+
};
108+
109+
console.log('Publishing velocity command');
110+
publisher.publish(twist);
111+
}, 100); // 10 Hz
112+
113+
rclnodejs.spin(node);
114+
}
115+
116+
publishSensorData().catch(console.error);
117+
```
118+
119+
## Services (Request/Response)
120+
121+
### Concept Overview
122+
123+
**Services** implement a **request/response** communication pattern where:
124+
125+
- **Service Server** provides a computation/service
126+
- **Service Client** requests the service and waits for response
127+
- **Synchronous** - Client waits for server response
128+
- **One-to-one** - One server per service name, multiple clients allowed
129+
- **Short-lived** - Services should return quickly
130+
131+
Services are ideal for **remote procedure calls**, configuration requests, or triggering specific actions.
132+
133+
### Basic Service Server Example
134+
135+
```javascript
136+
const rclnodejs = require('rclnodejs');
137+
138+
async function createServiceServer() {
139+
await rclnodejs.init();
140+
const node = rclnodejs.createNode('service_example_node');
141+
142+
// Create a service that adds two integers
143+
const service = node.createService(
144+
'example_interfaces/srv/AddTwoInts',
145+
'add_two_ints',
146+
(request, response) => {
147+
console.log(`Request: ${request.a} + ${request.b}`);
148+
149+
// Compute the result
150+
const result = response.template;
151+
result.sum = request.a + request.b;
152+
153+
console.log(`Sending response: ${typeof result}`, result);
154+
response.send(result);
155+
}
156+
);
157+
158+
console.log('Service server ready');
159+
rclnodejs.spin(node);
160+
}
161+
162+
createServiceServer().catch(console.error);
163+
```
164+
165+
### Basic Service Client Example
166+
167+
```javascript
168+
const rclnodejs = require('rclnodejs');
169+
170+
async function createServiceClient() {
171+
await rclnodejs.init();
172+
const node = rclnodejs.createNode('client_example_node');
173+
174+
// Create a client for the add_two_ints service
175+
const client = node.createClient(
176+
'example_interfaces/srv/AddTwoInts',
177+
'add_two_ints'
178+
);
179+
180+
// Wait for service to become available
181+
const serviceAvailable = await client.waitForService(5000);
182+
if (!serviceAvailable) {
183+
console.log('Service not available');
184+
rclnodejs.shutdown();
185+
return;
186+
}
187+
188+
// Create request
189+
const request = {
190+
a: BigInt(10),
191+
b: BigInt(15),
192+
};
193+
194+
console.log(`Calling service with: ${request.a} + ${request.b}`);
195+
196+
// Send request with callback
197+
client.sendRequest(request, (response) => {
198+
console.log(`Result: ${typeof response}`, response);
199+
rclnodejs.shutdown();
200+
});
201+
202+
rclnodejs.spin(node);
203+
}
204+
205+
createServiceClient().catch(console.error);
206+
```
207+
208+
### Service Features
209+
210+
- **Request/Response Structure** - Services define both request and response message types
211+
- **Blocking** - Clients wait for server response
212+
- **Error Handling** - Services can fail and return errors
213+
- **Discoverability** - Use `ros2 service list` to see available services
214+
215+
### Practical Service Example: Robot Configuration
216+
217+
```javascript
218+
const rclnodejs = require('rclnodejs');
219+
220+
class RobotConfigurationService {
221+
constructor() {
222+
this.robotConfig = {
223+
maxSpeed: 2.0,
224+
safetyEnabled: true,
225+
operationMode: 'autonomous',
226+
};
227+
}
228+
229+
async start() {
230+
await rclnodejs.init();
231+
this.node = rclnodejs.createNode('robot_config_service');
232+
233+
// Service to get robot configuration
234+
this.node.createService(
235+
'example_interfaces/srv/Trigger',
236+
'get_robot_config',
237+
(request, response) => {
238+
const result = response.template;
239+
result.success = true;
240+
result.message = JSON.stringify(this.robotConfig);
241+
response.send(result);
242+
}
243+
);
244+
245+
// Service to set max speed
246+
this.node.createService(
247+
'example_interfaces/srv/SetBool',
248+
'set_safety_mode',
249+
(request, response) => {
250+
this.robotConfig.safetyEnabled = request.data;
251+
252+
const result = response.template;
253+
result.success = true;
254+
result.message = `Safety mode set to: ${request.data}`;
255+
response.send(result);
256+
}
257+
);
258+
259+
console.log('Robot configuration services ready');
260+
rclnodejs.spin(this.node);
261+
}
262+
}
263+
264+
const configService = new RobotConfigurationService();
265+
configService.start().catch(console.error);
266+
```
267+
268+
## When to Use Topics vs Services
269+
270+
### Use Topics When:
271+
272+
-**Continuous data** - Sensor readings, status updates
273+
-**Multiple consumers** - Many nodes need the same data
274+
-**Asynchronous** - Publisher doesn't need immediate response
275+
-**High frequency** - Data published regularly (> 1 Hz)
276+
-**Fire-and-forget** - Don't care if anyone receives the data
277+
278+
**Examples**: Camera images, laser scans, robot odometry, joint states
279+
280+
### Use Services When:
281+
282+
-**Request/response** - Need a specific computation or action
283+
-**Occasional use** - Triggered by events, not continuous
284+
-**Synchronous** - Need to wait for result before continuing
285+
-**Configuration** - Setting parameters or modes
286+
-**Validation** - Need confirmation that action succeeded
287+
288+
**Examples**: Calculating path, setting robot mode, triggering calibration, querying status
289+
290+
### Quick Comparison
291+
292+
| Aspect | Topics | Services |
293+
| ---------------------- | ----------------- | ------------------------ |
294+
| **Pattern** | Publish/Subscribe | Request/Response |
295+
| **Communication** | Asynchronous | Synchronous |
296+
| **Frequency** | Continuous/High | Occasional/On-demand |
297+
| **Response** | No response | Always responds |
298+
| **Multiple consumers** | Yes | One server, many clients |
299+
| **Use case** | Data streams | Remote procedure calls |
300+
301+
## Running the Examples
302+
303+
### Topic Examples
304+
305+
Run the publisher and subscriber in separate terminals:
306+
307+
```bash
308+
# Terminal 1 - Start subscriber
309+
cd /path/to/rclnodejs
310+
node example/topics/subscriber/subscription-example.js
311+
312+
# Terminal 2 - Start publisher
313+
cd /path/to/rclnodejs
314+
node example/topics/publisher/publisher-example.js
315+
```
316+
317+
**Expected Output:**
318+
319+
**Subscriber terminal:**
320+
321+
```
322+
Received message: string Hello ROS 0
323+
Received message: string Hello ROS 1
324+
Received message: string Hello ROS 2
325+
```
326+
327+
**Publisher terminal:**
328+
329+
```
330+
Publishing message: Hello ROS 0
331+
Publishing message: Hello ROS 1
332+
Publishing message: Hello ROS 2
333+
```
334+
335+
### Service Examples
336+
337+
Run the service server and client in separate terminals:
338+
339+
```bash
340+
# Terminal 1 - Start service server
341+
cd /path/to/rclnodejs
342+
node example/services/service/service-example.js
343+
344+
# Terminal 2 - Start service client
345+
cd /path/to/rclnodejs
346+
node example/services/client/client-example.js
347+
```
348+
349+
**Expected Output:**
350+
351+
**Service server terminal:**
352+
353+
```
354+
Incoming request: object { a: 45n, b: 67n }
355+
Sending response: object { sum: 112n }
356+
```
357+
358+
**Service client terminal:**
359+
360+
```
361+
Sending: object { a: 45n, b: 67n }
362+
Result: object { sum: 112n }
363+
```
364+
365+
### Using ROS 2 CLI Tools
366+
367+
Monitor your topics and services:
368+
369+
```bash
370+
# List all active topics
371+
ros2 topic list
372+
373+
# Listen to topic messages
374+
ros2 topic echo /topic
375+
376+
# Show topic information
377+
ros2 topic info /topic
378+
379+
# List all services
380+
ros2 service list
381+
382+
# Call a service manually
383+
ros2 service call /add_two_ints example_interfaces/srv/AddTwoInts "{a: 5, b: 10}"
384+
385+
# Show service type
386+
ros2 service type /add_two_ints
387+
```
388+
389+
### Additional Examples
390+
391+
Explore more patterns in the examples directory:
392+
393+
- **`example/topics/publisher/`** - Various publisher patterns
394+
- **`example/topics/subscriber/`** - Different subscription approaches
395+
- **`example/services/`** - Service implementation examples
396+
- **QoS Examples** - Quality of Service configuration
397+
- **Message Examples** - Working with complex message types
398+
399+
This tutorial covers the fundamental communication patterns that form the backbone of any ROS 2 application. Topics and services provide the foundation for building distributed, modular robotics systems with rclnodejs.

0 commit comments

Comments
 (0)