feat(nav2): load custom editor layouts in gazebo
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import json
|
||||
import os
|
||||
import tempfile
|
||||
import xml.etree.ElementTree as ET
|
||||
@@ -76,6 +77,59 @@ def create_flat_house_world(gazebo_dir):
|
||||
return flat_world_path
|
||||
|
||||
|
||||
def create_custom_layout_world():
|
||||
"""Build a Gazebo world from the editor's persisted 2D layout."""
|
||||
layout_path = os.environ.get('WORLD_LAYOUT_PATH', '/modules/world-layout.json')
|
||||
try:
|
||||
with open(layout_path, 'r', encoding='utf-8') as source:
|
||||
objects = json.load(source).get('objects', [])
|
||||
except (OSError, ValueError, AttributeError):
|
||||
objects = []
|
||||
|
||||
models = []
|
||||
for index, item in enumerate(objects):
|
||||
if not isinstance(item, dict):
|
||||
continue
|
||||
position = item.get('position') or [0, 0, 0]
|
||||
rotation = item.get('rotation') or [0, 0, 0]
|
||||
size = item.get('size') or [1, 1, 1]
|
||||
try:
|
||||
x, y, z = (float(position[0]), float(position[1]), float(position[2]))
|
||||
yaw = float(rotation[2])
|
||||
sx, sy, sz = (max(0.05, float(size[0])), max(0.05, float(size[1])), max(0.05, float(size[2])))
|
||||
except (IndexError, TypeError, ValueError):
|
||||
continue
|
||||
|
||||
name = f"custom_{item.get('type', 'box')}_{index}"
|
||||
if item.get('type') == 'pillar':
|
||||
geometry = f'<cylinder><radius>{sx / 2.0}</radius><length>{sz}</length></cylinder>'
|
||||
else:
|
||||
geometry = f'<box><size>{sx} {sy} {sz}</size></box>'
|
||||
models.append(
|
||||
f'''<model name="{name}">
|
||||
<static>true</static>
|
||||
<pose>{x} {y} {z} 0 0 {yaw}</pose>
|
||||
<link name="link">
|
||||
<collision name="collision"><geometry>{geometry}</geometry></collision>
|
||||
<visual name="visual"><geometry>{geometry}</geometry><material><ambient>0.34 0.58 0.82 1</ambient><diffuse>0.34 0.58 0.82 1</diffuse></material></visual>
|
||||
</link>
|
||||
</model>'''
|
||||
)
|
||||
|
||||
world_path = os.path.join(tempfile.gettempdir(), 'nav2_custom_layout.world')
|
||||
with open(world_path, 'w', encoding='utf-8') as destination:
|
||||
destination.write('''<?xml version="1.0"?>
|
||||
<sdf version="1.6">
|
||||
<world name="nav2_custom_layout">
|
||||
<include><uri>model://sun</uri></include>
|
||||
<include><uri>model://ground_plane</uri></include>
|
||||
<physics type="ode"><real_time_update_rate>1000</real_time_update_rate></physics>
|
||||
''')
|
||||
destination.write('\n'.join(models))
|
||||
destination.write('\n </world>\n</sdf>\n')
|
||||
return world_path
|
||||
|
||||
|
||||
def launch_gazebo(context):
|
||||
gazebo_dir = get_package_share_directory('turtlebot3_gazebo')
|
||||
simulator_dir = get_package_share_directory('nav2_turtlebot_simulator')
|
||||
@@ -86,7 +140,9 @@ def launch_gazebo(context):
|
||||
if render_mode not in {'flat', 'full'}:
|
||||
raise RuntimeError('render_mode must be either flat or full')
|
||||
|
||||
if world == 'turtlebot3_house.world' and render_mode == 'flat':
|
||||
if world == 'empty_world.world' and os.path.isfile(os.environ.get('WORLD_LAYOUT_PATH', '/modules/world-layout.json')):
|
||||
world_path = create_custom_layout_world()
|
||||
elif world == 'turtlebot3_house.world' and render_mode == 'flat':
|
||||
world_path = create_flat_house_world(gazebo_dir)
|
||||
else:
|
||||
world_path = os.path.join(
|
||||
|
||||
Reference in New Issue
Block a user