Skip to content

Commit d585e69

Browse files
committed
fix(nav2): update to Jazzy plugin syntax and simplify Docker build
- Fix Nav2 plugin names (/ -> ::) and add enable_stamped_cmd_vel - Shallow clone ros2_medkit from GitHub, local COPY for demo - Move gateway to /diagnostics namespace for Area demo - Add send-nav-goal.sh helper and update README
1 parent 1cad284 commit d585e69

File tree

7 files changed

+217
-129
lines changed

7 files changed

+217
-129
lines changed

demos/turtlebot3_integration/Dockerfile

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,13 @@ RUN apt-get update && apt-get install -y \
4040
curl \
4141
&& rm -rf /var/lib/apt/lists/*
4242

43-
# Create workspace and clone ros2_medkit
44-
# TODO: Replace with proper ROS 2 package dependency once ros2_medkit is released
43+
# Clone ros2_medkit from GitHub
4544
WORKDIR ${COLCON_WS}/src
46-
RUN git clone https://github.com/selfpatch/ros2_medkit.git
45+
RUN git clone --depth 1 https://github.com/selfpatch/ros2_medkit.git && \
46+
mv ros2_medkit/src/ros2_medkit_gateway . && \
47+
rm -rf ros2_medkit
4748

48-
# Copy demo package
49+
# Copy demo package from local context (this repo)
4950
COPY package.xml CMakeLists.txt ${COLCON_WS}/src/turtlebot3_medkit_demo/
5051
COPY config/ ${COLCON_WS}/src/turtlebot3_medkit_demo/config/
5152
COPY launch/ ${COLCON_WS}/src/turtlebot3_medkit_demo/launch/

demos/turtlebot3_integration/README.md

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,18 @@ curl -X POST http://localhost:8080/api/v1/topics/publish ...
148148

149149
## What You'll See
150150

151-
When TurtleBot3 simulation starts with Nav2, ros2_medkit will discover nodes such as:
151+
When TurtleBot3 simulation starts with Nav2, ros2_medkit will discover nodes organized into **areas** based on their ROS 2 namespaces:
152+
153+
### Areas (Namespaces)
154+
155+
| Area | Namespace | Description |
156+
|------|-----------|-------------|
157+
| `root` | `/` | TurtleBot3, Nav2, and Gazebo nodes |
158+
| `diagnostics` | `/diagnostics` | ros2_medkit gateway |
159+
160+
### Components
161+
162+
**Root** (`/`) - Main robot system:
152163

153164
- `turtlebot3_node` - Main robot interface
154165
- `robot_state_publisher` - TF tree publisher
@@ -157,9 +168,15 @@ When TurtleBot3 simulation starts with Nav2, ros2_medkit will discover nodes suc
157168
- `bt_navigator` - Behavior Tree Navigator
158169
- `controller_server` - Path following controller
159170
- `planner_server` - Global path planner
160-
- Various sensor and lifecycle nodes
171+
- `velocity_smoother` - Velocity command smoother
172+
- Various lifecycle and manager nodes
173+
174+
**Diagnostics** (`/diagnostics`):
175+
176+
- `ros2_medkit_gateway` - REST API gateway
161177

162-
These appear as **components** in the ros2_medkit REST API, organized into **areas** based on their ROS 2 namespaces.
178+
This demonstrates ros2_medkit's ability to discover ROS 2 nodes and organize them into areas.
179+
For a more hierarchical demo with multiple areas, see the [ros2_medkit demo nodes](https://github.com/selfpatch/ros2_medkit/tree/main/src/ros2_medkit_gateway#demo-nodes) which use namespaces like `/powertrain`, `/chassis`, and `/body`.
163180

164181
## Architecture
165182

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,20 @@
11
# ros2_medkit gateway configuration for TurtleBot3 demo
2-
ros2_medkit_gateway:
3-
ros__parameters:
4-
server.host: "0.0.0.0"
5-
server.port: 8080
6-
refresh_interval_ms: 2000
7-
cors.allowed_origins: ["*"]
8-
cors.allowed_methods: ["GET", "PUT", "POST", "OPTIONS"]
9-
cors.allowed_headers: ["Content-Type", "Accept"]
10-
cors.allow_credentials: false
11-
cors.max_age_seconds: 86400
2+
# Node runs under /diagnostics namespace, so we need to match that here
3+
diagnostics:
4+
ros2_medkit_gateway:
5+
ros__parameters:
6+
server:
7+
# Bind to all interfaces for Docker networking
8+
host: "0.0.0.0"
9+
port: 8080
10+
11+
refresh_interval_ms: 2000
12+
13+
cors:
14+
allowed_origins: ["*"]
15+
allowed_methods: ["GET", "PUT", "POST", "OPTIONS"]
16+
allowed_headers: ["Content-Type", "Accept"]
17+
allow_credentials: false
18+
max_age_seconds: 86400
19+
20+
max_parallel_topic_samples: 10

demos/turtlebot3_integration/config/nav2_params.yaml

Lines changed: 47 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,22 @@
11
# Nav2 parameters for TurtleBot3 + ros2_medkit demo
22
# Based on default nav2_bringup parameters for TurtleBot3 burger
33

4+
# Lifecycle manager configuration - exclude docking_server which we don't use
5+
lifecycle_manager_navigation:
6+
ros__parameters:
7+
use_sim_time: True
8+
autostart: True
9+
node_names:
10+
- controller_server
11+
- smoother_server
12+
- planner_server
13+
- behavior_server
14+
- bt_navigator
15+
- waypoint_follower
16+
- velocity_smoother
17+
- collision_monitor
18+
# Note: docking_server and route_server removed - not configured for this demo
19+
420
amcl:
521
ros__parameters:
622
use_sim_time: True
@@ -60,65 +76,15 @@ bt_navigator:
6076
action_server_result_timeout: 900.0
6177
navigators: ["navigate_to_pose", "navigate_through_poses"]
6278
navigate_to_pose:
63-
plugin: "nav2_bt_navigator/NavigateToPoseNavigator"
79+
plugin: "nav2_bt_navigator::NavigateToPoseNavigator"
6480
navigate_through_poses:
65-
plugin: "nav2_bt_navigator/NavigateThroughPosesNavigator"
66-
plugin_lib_names:
67-
- nav2_compute_path_to_pose_action_bt_node
68-
- nav2_compute_path_through_poses_action_bt_node
69-
- nav2_smooth_path_action_bt_node
70-
- nav2_follow_path_action_bt_node
71-
- nav2_spin_action_bt_node
72-
- nav2_wait_action_bt_node
73-
- nav2_assisted_teleop_action_bt_node
74-
- nav2_back_up_action_bt_node
75-
- nav2_drive_on_heading_bt_node
76-
- nav2_clear_costmap_service_bt_node
77-
- nav2_is_stuck_condition_bt_node
78-
- nav2_goal_reached_condition_bt_node
79-
- nav2_goal_updated_condition_bt_node
80-
- nav2_globally_updated_goal_condition_bt_node
81-
- nav2_is_path_valid_condition_bt_node
82-
- nav2_are_error_codes_active_condition_bt_node
83-
- nav2_would_a_controller_recovery_help_condition_bt_node
84-
- nav2_would_a_planner_recovery_help_condition_bt_node
85-
- nav2_would_a_smoother_recovery_help_condition_bt_node
86-
- nav2_initial_pose_received_condition_bt_node
87-
- nav2_reinitialize_global_localization_service_bt_node
88-
- nav2_rate_controller_bt_node
89-
- nav2_distance_controller_bt_node
90-
- nav2_speed_controller_bt_node
91-
- nav2_truncate_path_action_bt_node
92-
- nav2_truncate_path_local_action_bt_node
93-
- nav2_goal_updater_node_bt_node
94-
- nav2_recovery_node_bt_node
95-
- nav2_pipeline_sequence_bt_node
96-
- nav2_round_robin_node_bt_node
97-
- nav2_transform_available_condition_bt_node
98-
- nav2_time_expired_condition_bt_node
99-
- nav2_path_expiring_timer_condition
100-
- nav2_distance_traveled_condition_bt_node
101-
- nav2_single_trigger_bt_node
102-
- nav2_goal_updated_controller_bt_node
103-
- nav2_is_battery_low_condition_bt_node
104-
- nav2_navigate_to_pose_action_bt_node
105-
- nav2_navigate_through_poses_action_bt_node
106-
- nav2_remove_passed_goals_action_bt_node
107-
- nav2_planner_selector_bt_node
108-
- nav2_controller_selector_bt_node
109-
- nav2_goal_checker_selector_bt_node
110-
- nav2_controller_cancel_bt_node
111-
- nav2_path_longer_on_approach_bt_node
112-
- nav2_wait_cancel_bt_node
113-
- nav2_spin_cancel_bt_node
114-
- nav2_back_up_cancel_bt_node
115-
- nav2_assisted_teleop_cancel_bt_node
116-
- nav2_drive_on_heading_cancel_bt_node
117-
- nav2_is_battery_charging_condition_bt_node
81+
plugin: "nav2_bt_navigator::NavigateThroughPosesNavigator"
82+
# Note: plugin_lib_names is no longer needed in Jazzy - plugins are auto-loaded
11883

11984
controller_server:
12085
ros__parameters:
12186
use_sim_time: True
87+
enable_stamped_cmd_vel: True
12288
controller_frequency: 20.0
12389
min_x_velocity_threshold: 0.001
12490
min_y_velocity_threshold: 0.5
@@ -281,22 +247,23 @@ smoother_server:
281247

282248
behavior_server:
283249
ros__parameters:
250+
enable_stamped_cmd_vel: True
284251
local_costmap_topic: local_costmap/costmap_raw
285252
global_costmap_topic: global_costmap/costmap_raw
286253
local_footprint_topic: local_costmap/published_footprint
287254
global_footprint_topic: global_costmap/published_footprint
288255
cycle_frequency: 10.0
289256
behavior_plugins: ["spin", "backup", "drive_on_heading", "assisted_teleop", "wait"]
290257
spin:
291-
plugin: "nav2_behaviors/Spin"
258+
plugin: "nav2_behaviors::Spin"
292259
backup:
293-
plugin: "nav2_behaviors/BackUp"
260+
plugin: "nav2_behaviors::BackUp"
294261
drive_on_heading:
295-
plugin: "nav2_behaviors/DriveOnHeading"
262+
plugin: "nav2_behaviors::DriveOnHeading"
296263
wait:
297-
plugin: "nav2_behaviors/Wait"
264+
plugin: "nav2_behaviors::Wait"
298265
assisted_teleop:
299-
plugin: "nav2_behaviors/AssistedTeleop"
266+
plugin: "nav2_behaviors::AssistedTeleop"
300267
local_frame: odom
301268
global_frame: map
302269
robot_base_frame: base_link
@@ -322,6 +289,7 @@ waypoint_follower:
322289
velocity_smoother:
323290
ros__parameters:
324291
use_sim_time: True
292+
enable_stamped_cmd_vel: True
325293
smoothing_frequency: 20.0
326294
scale_velocities: False
327295
feedback: "OPEN_LOOP"
@@ -337,6 +305,7 @@ velocity_smoother:
337305
collision_monitor:
338306
ros__parameters:
339307
use_sim_time: True
308+
enable_stamped_cmd_vel: True
340309
base_frame_id: "base_link"
341310
odom_frame_id: "odom"
342311
cmd_vel_in_topic: "cmd_vel_smoothed"
@@ -363,3 +332,22 @@ collision_monitor:
363332
min_height: 0.15
364333
max_height: 2.0
365334
enabled: True
335+
336+
# Docking server - minimal config to satisfy lifecycle manager
337+
# We don't actually use docking in this demo
338+
docking_server:
339+
ros__parameters:
340+
use_sim_time: True
341+
enable_stamped_cmd_vel: True
342+
dock_plugins: ["simple_charging_dock"]
343+
simple_charging_dock:
344+
plugin: "opennav_docking::SimpleChargingDock"
345+
use_external_detection_pose: false
346+
docking_threshold: 0.02
347+
staging_x_offset: -0.5
348+
staging_yaw_offset: 0.0
349+
350+
# Route server - minimal config
351+
route_server:
352+
ros__parameters:
353+
use_sim_time: True

demos/turtlebot3_integration/docker-compose.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,6 @@ services:
5858
ros2 launch turtlebot3_medkit_demo demo.launch.py"
5959
6060
sovd-web-ui:
61-
# TODO: Replace with Docker Hub image once sovd_web_ui is published
62-
# For now, we clone and build from GitHub
6361
build:
6462
context: https://github.com/selfpatch/sovd_web_ui.git
6563
dockerfile: Dockerfile
Lines changed: 65 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,84 @@
1-
"""Launch TurtleBot3 simulation with Nav2 and ros2_medkit gateway for discovery demo."""
1+
"""Launch TurtleBot3 simulation with Nav2 and ros2_medkit gateway for discovery demo.
2+
3+
This launch file demonstrates ros2_medkit's hierarchical discovery by:
4+
- Running TurtleBot3 + Nav2 (in root namespace)
5+
- Adding ros2_medkit gateway under /diagnostics namespace
6+
- Showing how nodes are organized into Areas based on namespaces
7+
"""
28

39
import os
410

511
from ament_index_python.packages import get_package_share_directory
612
from launch import LaunchDescription
7-
from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription, SetEnvironmentVariable
13+
from launch.actions import (
14+
DeclareLaunchArgument,
15+
IncludeLaunchDescription,
16+
SetEnvironmentVariable,
17+
)
818
from launch.launch_description_sources import PythonLaunchDescriptionSource
919
from launch.substitutions import LaunchConfiguration
1020
from launch_ros.actions import Node
1121

1222

1323
def generate_launch_description():
1424
# Get package directories
15-
turtlebot3_gazebo_dir = get_package_share_directory('turtlebot3_gazebo')
16-
nav2_bringup_dir = get_package_share_directory('nav2_bringup')
17-
demo_pkg_dir = get_package_share_directory('turtlebot3_medkit_demo')
25+
turtlebot3_gazebo_dir = get_package_share_directory("turtlebot3_gazebo")
26+
nav2_bringup_dir = get_package_share_directory("nav2_bringup")
27+
demo_pkg_dir = get_package_share_directory("turtlebot3_medkit_demo")
1828

1929
# Path to config files from installed package
20-
medkit_params_file = os.path.join(demo_pkg_dir, 'config', 'medkit_params.yaml')
21-
nav2_params_file = os.path.join(demo_pkg_dir, 'config', 'nav2_params.yaml')
22-
map_file = os.path.join(demo_pkg_dir, 'config', 'turtlebot3_world.yaml')
30+
medkit_params_file = os.path.join(demo_pkg_dir, "config", "medkit_params.yaml")
31+
nav2_params_file = os.path.join(demo_pkg_dir, "config", "nav2_params.yaml")
32+
map_file = os.path.join(demo_pkg_dir, "config", "turtlebot3_world.yaml")
2333

2434
# Launch configuration variables
25-
use_sim_time = LaunchConfiguration('use_sim_time', default='True')
26-
27-
return LaunchDescription([
28-
# Declare launch arguments
29-
DeclareLaunchArgument(
30-
'use_sim_time',
31-
default_value='True',
32-
description='Use simulation (Gazebo) clock if true'
33-
),
35+
use_sim_time = LaunchConfiguration("use_sim_time", default="True")
3436

35-
# Set TurtleBot3 model (can be overridden by environment variable)
36-
SetEnvironmentVariable(
37-
name='TURTLEBOT3_MODEL',
38-
value=os.environ.get('TURTLEBOT3_MODEL', 'burger')
39-
),
40-
41-
# Launch TurtleBot3 Gazebo simulation (turtlebot3_world)
42-
IncludeLaunchDescription(
43-
PythonLaunchDescriptionSource(
44-
os.path.join(turtlebot3_gazebo_dir, 'launch', 'turtlebot3_world.launch.py')
37+
return LaunchDescription(
38+
[
39+
# Declare launch arguments
40+
DeclareLaunchArgument(
41+
"use_sim_time",
42+
default_value="True",
43+
description="Use simulation (Gazebo) clock if true",
4544
),
46-
launch_arguments={'use_sim_time': use_sim_time}.items()
47-
),
48-
49-
# Launch Nav2 navigation stack
50-
IncludeLaunchDescription(
51-
PythonLaunchDescriptionSource(
52-
os.path.join(nav2_bringup_dir, 'launch', 'bringup_launch.py')
45+
# Set TurtleBot3 model (can be overridden by environment variable)
46+
SetEnvironmentVariable(
47+
name="TURTLEBOT3_MODEL",
48+
value=os.environ.get("TURTLEBOT3_MODEL", "burger"),
5349
),
54-
launch_arguments={
55-
'map': map_file,
56-
'params_file': nav2_params_file,
57-
'use_sim_time': use_sim_time,
58-
'autostart': 'True',
59-
}.items()
60-
),
61-
62-
# Launch ros2_medkit gateway
63-
Node(
64-
package='ros2_medkit_gateway',
65-
executable='gateway_node',
66-
name='ros2_medkit_gateway',
67-
output='screen',
68-
parameters=[medkit_params_file, {'use_sim_time': use_sim_time}],
69-
),
70-
])
50+
# Launch TurtleBot3 Gazebo simulation (turtlebot3_world)
51+
# Runs in root namespace to publish standard topics (/scan, /odom, /cmd_vel)
52+
IncludeLaunchDescription(
53+
PythonLaunchDescriptionSource(
54+
os.path.join(
55+
turtlebot3_gazebo_dir, "launch", "turtlebot3_world.launch.py"
56+
)
57+
),
58+
launch_arguments={"use_sim_time": use_sim_time}.items(),
59+
),
60+
# Launch Nav2 navigation stack
61+
# Runs in root namespace to subscribe to robot topics
62+
IncludeLaunchDescription(
63+
PythonLaunchDescriptionSource(
64+
os.path.join(nav2_bringup_dir, "launch", "bringup_launch.py")
65+
),
66+
launch_arguments={
67+
"map": map_file,
68+
"params_file": nav2_params_file,
69+
"use_sim_time": use_sim_time,
70+
"autostart": "True",
71+
}.items(),
72+
),
73+
# Launch ros2_medkit gateway under /diagnostics namespace
74+
# This demonstrates namespace-based Area organization in discovery
75+
Node(
76+
package="ros2_medkit_gateway",
77+
executable="gateway_node",
78+
name="ros2_medkit_gateway",
79+
namespace="diagnostics",
80+
output="screen",
81+
parameters=[medkit_params_file, {"use_sim_time": use_sim_time}],
82+
),
83+
]
84+
)

0 commit comments

Comments
 (0)