Skip to content

Commit 24dabc4

Browse files
authored
[Demo][Electron] Load prebuilt binaries at runtime (#1546)
## Summary Make the Electron demos load the prebuilt `rclnodejs` native binary at runtime instead of recompiling it during `electron-forge` packaging, fix the `turtle_tf2` demo's stuck loading screen on GPU-less environments (WSL2/headless), shorten the demo READMEs, and harden the native-loader unit tests. ## Native loader (`lib/native_loader.js`) - Add `mirrorPrebuiltToBuildRelease()`: when an exact-match prebuilt binary is found, copy it into `build/Release/rclnodejs.node` so standard `bindings` resolution (and packaged Electron apps where `ROS_DISTRO` may be unset at runtime) can locate it. Non-destructive — never overwrites an existing build output, preserving local source builds. - Extract `buildFromSource()` and make it Electron-aware: when running under Electron, target Electron's headers/ABI via `npm_config_target`, `npm_config_dist_url`, and `npm_config_arch` so the compiled addon matches the runtime (the addon links libuv directly, so host runtime matters). - Replace the two duplicated inline `npm run rebuild` `execSync` calls (forced build + fallback build paths) with the shared `buildFromSource()` helper. ## Electron demos — load prebuilt at packaging time - `demo/electron/{car,manipulator,topics,turtle_tf2}/package.json`: Remove the `"rebuild": "electron-rebuild"` script; add `forge.rebuildConfig.ignoreModules: ["rclnodejs"]` so `electron-forge` does not recompile `rclnodejs`, using the shipped prebuilt binary instead. ## `turtle_tf2` demo — WebGL / loading-screen fixes - `main.js`: append `ignore-gpu-blocklist` and `enable-unsafe-swiftshader` command-line switches so WebGL works on systems without a usable GPU (WSL2, headless, VMs) where Chromium blocklists hardware WebGL. - `renderer.js`: register ROS2 status listeners before 3D scene setup so the loading screen always reflects initialization progress, and wrap `initializeScene()/setupEventListeners()/setupKeyboardControls()` in a try/catch that surfaces a clear user-facing message ("Failed to initialize 3D view (WebGL unavailable): …") if scene init fails. ## Documentation - Rewrite/shorten the four demo READMEs (`car`, `manipulator`, `topics`, `turtle_tf2`) — net ~1100 lines removed, kept accurate against each `package.json` (scripts, dependency versions, output directory names). ## Tests (`test/test-native-loader.js`) - Update the two "exact match" prebuild tests to locate the prebuild candidate via `existsSync.getCalls()` (filtering for the `prebuilds` + `rclnodejs.node` path) instead of relying on `existsSync.lastCall`, which now points at the new `build/Release` mirror probe. Adds a guard assertion for a clearer failure message and drops the now-redundant `rclnodejs.node` check. Fix: #1545
1 parent bc78bfa commit 24dabc4

12 files changed

Lines changed: 323 additions & 1108 deletions

File tree

demo/electron/car/README.md

Lines changed: 54 additions & 298 deletions
Original file line numberDiff line numberDiff line change
@@ -1,332 +1,88 @@
1-
# ROS2 Car Control Demo with Electron and rclnodejs
1+
# ROS 2 Car Control Demo (Electron + rclnodejs)
22

3-
This demo showcases how to use **rclnodejs** with **Electron** to create an interactive car control application. The demo features a virtual joystick that publishes ROS2 velocity commands and a car visualization that responds to those commands in real-time.
3+
Interactive car-control app: a virtual joystick publishes `geometry_msgs/Twist`
4+
velocity commands on `/cmd_vel`, and a visualization subscribes and moves a car
5+
in real time.
46

5-
## 🚗 Features
6-
7-
### Joystick Control Panel
8-
9-
- **Directional Controls**: Up/Down/Left/Right buttons for car movement
10-
- **Stop Button**: Emergency stop functionality
11-
- **Keyboard Support**: WASD and arrow keys for control
12-
- **Real-time Status**: Display current command and velocity values
13-
14-
### Car Visualization
15-
16-
- **Real-time Movement**: Car moves and rotates based on received commands
17-
- **Position Tracking**: Shows current position (North/South/East/West/Center)
18-
- **Visual Feedback**: Color-coded movement indicators
19-
- **Command Counter**: Tracks total number of commands received
20-
21-
### ROS2 Integration
22-
23-
- **Topic**: `cmd_vel` (geometry_msgs/Twist)
24-
- **Publisher**: Sends velocity commands
25-
- **Subscriber**: Receives and displays velocity commands
26-
- **Node Name**: `car_control_node`
27-
28-
## 🔧 Prerequisites
29-
30-
Before running this demo, ensure you have:
31-
32-
1. **ROS2 installed** (Humble, Jazzy, Kilted, Lyrical, or Rolling)
33-
2. **Node.js** (version 20.20.2 or higher)
34-
3. **rclnodejs built** and working
7+
![demo screenshot](./car-control-electron.gif)
358

36-
### ROS2 Setup
9+
## Features
3710

38-
```bash
39-
# Source your ROS2 installation
40-
source /opt/ros/<your-ros-distro>/setup.bash
11+
- Joystick plus WASD / arrow-key control with an emergency stop
12+
- Real-time car movement, position tracking, and command counter
13+
- ROS 2 node `car_control_node` publishing and subscribing on `cmd_vel`
4114

42-
# Verify ROS2 is working
43-
ros2 topic list
44-
```
15+
## Prerequisites
4516

46-
### Build rclnodejs
17+
- ROS 2 (Humble, Jazzy, Kilted, Lyrical, or Rolling), sourced
18+
- Node.js >= 20.20.2
19+
- Linux (prebuilt rclnodejs binaries are provided for Ubuntu)
4720

48-
From the main rclnodejs directory:
21+
## Install & Run
4922

5023
```bash
24+
cd demo/electron/car
25+
source /opt/ros/<distro>/setup.bash # required before install and launch
5126
npm install
52-
npm run build
53-
```
54-
55-
## 🚀 Installation & Running
56-
57-
1. **Navigate to the demo directory:**
58-
59-
```bash
60-
cd electron_car_demo
61-
```
62-
63-
2. **Install dependencies:**
64-
65-
```bash
66-
npm install
67-
```
68-
69-
3. **Rebuild native modules for Electron:**
70-
71-
```bash
72-
npm run rebuild
73-
```
74-
75-
4. **Start the demo:**
76-
```bash
77-
# Make sure ROS2 is sourced first
78-
source /opt/ros/<your-ros-distro>/setup.bash
79-
npm start
80-
```
81-
82-
## 📦 Packaging for Distribution
83-
84-
You can package the application into a standalone folder using **Electron Forge**.
85-
86-
### 1. Build the Package
87-
88-
Run the following command to create a distributable executable:
89-
90-
```bash
91-
npm run package
92-
```
93-
94-
The output will be located in the `out/` directory.
95-
96-
**Technical Note on ASAR:** We enable ASAR but configure it to **unpack** the `rclnodejs` module. `rclnodejs` (v1.8.1+) requires file system access to generated code and native bindings, so we use the `asar.unpack` configuration in `package.json` to keep `rclnodejs` files accessible on disk while packing the rest of the application.
97-
98-
```json
99-
"config": {
100-
"forge": {
101-
"packagerConfig": {
102-
"asar": {
103-
"unpack": "**/node_modules/rclnodejs/**"
104-
}
105-
}
106-
}
107-
}
108-
```
109-
110-
### 2. Create Installers (Optional)
111-
112-
To create a `.zip` file or other platform-specific installers (deb/rpm), run:
113-
114-
```bash
115-
npm run make
116-
```
117-
118-
**Note**: Creating DEB/RPM installers requires system tools like `dpkg` and `fakeroot`. For ZIP files, you need `zip`.
119-
120-
### 3. Running the Standalone Application
121-
122-
Even as a standalone application, **ROS 2 must be installed and sourced on the target machine** because `rclnodejs` links dynamically to the ROS 2 shared libraries.
123-
124-
```bash
125-
# Source ROS2 environment
126-
source /opt/ros/<your-ros-distro>/setup.bash
127-
128-
# Run the packaged executable
129-
./out/rclnodejs-electron-car-demo-linux-x64/rclnodejs-electron-car-demo
130-
```
131-
132-
![demo screenshot](./car-control-electron.gif)
133-
134-
## 🎮 How to Use
135-
136-
### Control Methods
137-
138-
#### Mouse Controls
139-
140-
- Click the directional buttons (↑↓←→) on the joystick
141-
- Click the red **STOP** button to halt movement
142-
143-
#### Keyboard Controls
144-
145-
- **W** or ****: Move forward
146-
- **S** or ****: Move backward
147-
- **A** or ****: Turn left
148-
- **D** or ****: Turn right
149-
- **Space** or **Esc**: Stop
150-
151-
### Understanding the Interface
152-
153-
#### Left Panel - Joystick Control
154-
155-
- **Command**: Shows the current command being sent
156-
- **Linear X**: Forward/backward velocity (m/s)
157-
- **Angular Z**: Rotation velocity (rad/s)
158-
- **Topic**: ROS2 topic name (`cmd_vel`)
159-
160-
#### Right Panel - Car Visualization
161-
162-
- **Received Commands**: Total count of commands received
163-
- **Last Command**: Most recent command processed
164-
- **Car Position**: Current position in the visualization area
165-
166-
## 🔍 Technical Details
167-
168-
### ROS2 Message Format
169-
170-
The demo uses `geometry_msgs/Twist` messages with the following structure:
171-
172-
```javascript
173-
{
174-
linear: {
175-
x: 1.0, // Forward/backward velocity (m/s)
176-
y: 0.0, // Left/right velocity (typically 0 for cars)
177-
z: 0.0 // Up/down velocity (typically 0 for ground vehicles)
178-
},
179-
angular: {
180-
x: 0.0, // Roll rate (typically 0 for cars)
181-
y: 0.0, // Pitch rate (typically 0 for cars)
182-
z: 1.0 // Yaw rate (turning left/right in rad/s)
183-
}
184-
}
27+
npm start
18528
```
18629

187-
### Command Mapping
30+
rclnodejs ships prebuilt Electron binaries and selects the matching one at
31+
runtime from `ROS_DISTRO` + Linux codename + architecture, so no compilation is
32+
needed. Do not run `electron-rebuild` against rclnodejs — it rebuilds from
33+
source and bypasses the prebuilt binary (the Forge `rebuildConfig` in
34+
`package.json` already excludes `rclnodejs`).
18835

189-
| Command | Linear X | Angular Z | Description |
190-
| -------- | -------- | --------- | ---------------------- |
191-
| Forward | +1.0 | 0.0 | Move forward at 1 m/s |
192-
| Backward | -1.0 | 0.0 | Move backward at 1 m/s |
193-
| Left | 0.0 | +1.0 | Turn left at 1 rad/s |
194-
| Right | 0.0 | -1.0 | Turn right at 1 rad/s |
195-
| Stop | 0.0 | 0.0 | Stop all movement |
36+
## Controls
19637

197-
### Architecture
38+
- Mouse: directional buttons (↑ ↓ ← →) and the red STOP button
39+
- Keyboard: W/S forward/back, A/D turn, Space or Esc to stop
19840

199-
```
200-
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
201-
│ Renderer │ │ Main Process │ │ ROS2 Network │
202-
│ (UI/Browser) │ │ (Node.js) │ │ │
203-
├─────────────────┤ ├──────────────────┤ ├─────────────────┤
204-
│ • Joystick UI │───▶│ • rclnodejs Node │───▶│ • cmd_vel Topic │
205-
│ • Car Display │◀───│ • Publisher │ │ • Other Nodes │
206-
│ • Status Panel │ │ • Subscriber │◀───│ • Robot/Sim │
207-
└─────────────────┘ └──────────────────┘ └─────────────────┘
208-
```
41+
## Message Format & Mapping
20942

210-
## 🧪 Testing with ROS2 Tools
43+
`geometry_msgs/Twist` on `/cmd_vel`:
21144

212-
You can test the demo using standard ROS2 command-line tools:
45+
| Command | linear.x | angular.z |
46+
| -------- | -------- | --------- |
47+
| Forward | +1.0 | 0.0 |
48+
| Backward | -1.0 | 0.0 |
49+
| Left | 0.0 | +1.0 |
50+
| Right | 0.0 | -1.0 |
51+
| Stop | 0.0 | 0.0 |
21352

214-
### Listen to Published Commands
53+
## Test from the CLI
21554

21655
```bash
217-
# In a new terminal (with ROS2 sourced)
21856
ros2 topic echo /cmd_vel
57+
ros2 topic pub /cmd_vel geometry_msgs/Twist "{linear: {x: 1.0}, angular: {z: 0.0}}"
21958
```
22059

221-
### Send Commands from Command Line
222-
223-
```bash
224-
# Send a forward command
225-
ros2 topic pub /cmd_vel geometry_msgs/Twist "linear: {x: 1.0, y: 0.0, z: 0.0}, angular: {x: 0.0, y: 0.0, z: 0.0}"
226-
227-
# Send a turn left command
228-
ros2 topic pub /cmd_vel geometry_msgs/Twist "linear: {x: 0.0, y: 0.0, z: 0.0}, angular: {x: 0.0, y: 0.0, z: 1.0}"
229-
230-
# Stop command
231-
ros2 topic pub /cmd_vel geometry_msgs/Twist "linear: {x: 0.0, y: 0.0, z: 0.0}, angular: {x: 0.0, y: 0.0, z: 0.0}"
232-
```
233-
234-
### Check Active Topics
60+
## Packaging
23561

23662
```bash
237-
ros2 topic list
238-
ros2 topic info /cmd_vel
239-
ros2 topic hz /cmd_vel
63+
npm run package # standalone app in out/
64+
npm run make # zip / deb / rpm installers (needs zip, dpkg, fakeroot)
24065
```
24166

242-
## 🤖 Integration with Real Robots
243-
244-
This demo can be easily connected to real robots or simulators:
245-
246-
### TurtleBot3 (Example)
67+
ASAR is enabled but `rclnodejs` is unpacked (`asar.unpack` in `package.json`)
68+
because it needs filesystem access to its generated code and native bindings.
69+
The target machine must still have ROS 2 installed and sourced — rclnodejs links
70+
dynamically to ROS 2 shared libraries:
24771

24872
```bash
249-
# Launch TurtleBot3 simulation
250-
ros2 launch turtlebot3_gazebo turtlebot3_world.launch.py
251-
252-
# The demo will automatically control the robot via cmd_vel topic
253-
```
254-
255-
### Custom Robot
256-
257-
Ensure your robot subscribes to the `/cmd_vel` topic with `geometry_msgs/Twist` messages.
258-
259-
## 🛠️ Customization
260-
261-
### Modify Velocity Values
262-
263-
Edit the `speed` and `turnSpeed` constants in `main.js`:
264-
265-
```javascript
266-
const speed = 1.0; // Linear velocity (m/s)
267-
const turnSpeed = 1.0; // Angular velocity (rad/s)
268-
```
269-
270-
### Change Topic Name
271-
272-
Modify the topic name in `main.js`:
273-
274-
```javascript
275-
// Change 'cmd_vel' to your desired topic name
276-
carControlPublisher = carControlNode.createPublisher(
277-
'geometry_msgs/msg/Twist',
278-
'your_topic_name'
279-
);
280-
```
281-
282-
### Add More Commands
283-
284-
Extend the joystick commands by modifying the switch statement in `main.js` and adding corresponding UI elements.
285-
286-
## 🐛 Troubleshooting
287-
288-
### Common Issues
289-
290-
1. **"Failed to initialize ROS2" Error**
291-
- Ensure ROS2 is properly sourced before running npm start
292-
- Check that rclnodejs is built correctly
293-
294-
2. **Native Module Rebuild Issues**
295-
296-
```bash
297-
npm run rebuild
298-
# or manually:
299-
./node_modules/.bin/electron-rebuild
300-
```
301-
302-
3. **Topic Not Appearing**
303-
- Verify ROS2 daemon is running: `ros2 daemon status`
304-
- Check topic list: `ros2 topic list`
305-
306-
4. **Car Not Moving in UI**
307-
- Check browser console for JavaScript errors
308-
- Verify IPC communication between main and renderer processes
309-
310-
### Debug Mode
311-
312-
Add debug logging by modifying `main.js`:
313-
314-
```javascript
315-
// Enable debug logging
316-
console.log('Publishing command:', command, twist);
73+
source /opt/ros/<distro>/setup.bash
74+
./out/rclnodejs-electron-car-demo-linux-x64/rclnodejs-electron-car-demo
31775
```
31876

319-
## 📚 Learning Resources
320-
321-
- [rclnodejs Documentation](https://github.com/RobotWebTools/rclnodejs)
322-
- [ROS2 Tutorials](https://docs.ros.org/en/lyrical/Tutorials.html)
323-
- [Electron Documentation](https://www.electronjs.org/docs)
324-
- [geometry_msgs/Twist Documentation](https://docs.ros.org/en/lyrical/p/geometry_msgs/interfaces/msg/Twist.html)
325-
326-
## 📄 License
77+
## Troubleshooting
32778

328-
This demo is licensed under the Apache License 2.0, same as the main rclnodejs project.
79+
- **"Failed to initialize ROS2"** — source ROS 2 before `npm start`.
80+
- **Native module fails to load** — ensure `ROS_DISTRO` is set so the prebuilt
81+
binary matches; as a last resort set `RCLNODEJS_FORCE_BUILD=1` (needs a
82+
compiler toolchain and network access).
83+
- **Topic missing** — check `ros2 daemon status` and `ros2 topic list`.
84+
- **Car not moving** — open the DevTools console and check for errors.
32985

330-
## 🤝 Contributing
86+
## License
33187

332-
Feel free to submit issues and enhancement requests! This demo serves as both a functional example and a starting point for more complex ROS2 Electron applications.
88+
Apache License 2.0, same as rclnodejs.

0 commit comments

Comments
 (0)