diff --git a/README.md b/README.md
index 557b4b3..3763a95 100644
--- a/README.md
+++ b/README.md
@@ -55,21 +55,53 @@ Once the package is built and sourced, you can start a simulation.
_Note: You can use `--world_name` flag to indicate other [world](andino_gz/worlds/) to use. (For example: `depot.sdf`(default), `empty.sdf`)_
-If you'd like to work from ROS you can launch the ros bridge by adding the corresponding flag
+By default the ros bridge and rviz is initialized. In case you prefer to disable any of those you can do it via its flags:
```sh
- ros2 launch andino_gz andino_gz.launch.py ros_bridge:=true
+ ros2 launch andino_gz andino_gz.launch.py ros_bridge:=false rviz:=false
```
-(Optional) Or launching it separately via:
-
+To see a complete list of available arguments for the launch file do:
```sh
- ros2 launch andino_gz gz_ros_bridge.launch.py
+ ros2 launch andino_gz andino_gz.launch.py --show-args
```
Make sure to review the required topics using `ign topics` and `ros2 topic` CLI tools.
Also, consider using looking at the translation entries under `andino_gz/config/bridge_config.yaml`.
+### Multi robot simulation
+
+ This simulation also support multi robot simulation.
+
+ ```sh
+ ros2 launch andino_gz andino_gz.launch.py robots:="
+ andino1={x: 0.0, y: 0.0, z: 0.1, yaw: 0.};
+ andino2={x: -0.4, y: 0.1, z: 0.1, yaw: 0.};
+ andino3={x: -0.4, y: -0.1, z: 0.1, yaw: 0.};
+ andino4={x: -0.8, y: 0.2, z: 0.1, yaw: 0.};
+ andino5={x: -0.8, y: -0.2, z: 0.1, yaw: 0.};
+ andino6={x: -0.8, y: 0.0, z: 0.1, yaw: 0.};"
+ ```
+
+ _Note: You can add as many as you want_
+
+
+
+ The launch file is in charge of:
+ - Start Gazebo simulator with a defined world (See '--world_name' flag)
+ - Spawn as many robots as commanded.
+ - Launch ros bridge for each robot.
+ - Launch rviz visualization for each robot.
+
+ The simulation allows spawn as many robots as you want via the `--robots` flags.
+ For that you can pass the information of the robots in some sort of YAML format via ROS2 cli:
+ ```yaml
+ ={x: 0.0, y: 0.0, yaw: 0.0, roll: 0.0, pitch: 0.0, yaw: 0.0};
+ ```
+
+ Note a ROS Namespace is pushed for each robot so all the topics and nodes are called the same with a difference of a `` prefix.
+
+
### SLAM
@@ -77,7 +109,7 @@ Also, consider using looking at the translation entries under `andino_gz/config/
1. Run simulation with ros bridge and RViz.
```sh
- ros2 launch andino_gz andino_gz.launch.py ros_bridge:=true rviz:=true
+ ros2 launch andino_gz andino_gz.launch.py
```
2. Run slam toolbox
@@ -93,22 +125,6 @@ Also, consider using looking at the translation entries under `andino_gz/config/
3. Visualize in RViz: Add `map` panel to RViz and see how the map is being generated.
-### Spawn multiple Andinos
-
-Launch simulation as before:
- ```sh
- ros2 launch andino_gz andino_gz.launch.py
- ```
-
-This will spawn only one Andino in the simulation
-
-For spawning more Andinos you can use the `spawn_robot` launch file. Make sure a different `entity` name is passed as argument as well as initial positions.
- ```sh
- ros2 launch andino_gz spawn_robot.launch.py entity:=andino_n initial_pose_x:=1 initial_pose_y:=1
- ```
-
-_Note: Andino is spawned but no bridge to ROS2 bridge are spawned for those robots._
-
## :raised_hands: Contributing
Issues or PRs are always welcome! Please refer to [CONTRIBUTING](CONTRIBUTING.md) doc.
diff --git a/andino_gz/config/bridge_config.yaml b/andino_gz/config/bridge_config.yaml
index 7519d5d..7f1f3ed 100644
--- a/andino_gz/config/bridge_config.yaml
+++ b/andino_gz/config/bridge_config.yaml
@@ -1,46 +1,44 @@
-- ros_topic_name: "/clock"
- gz_topic_name: "/clock"
- ros_type_name: "rosgraph_msgs/msg/Clock"
- gz_type_name: "gz.msgs.Clock"
- direction: GZ_TO_ROS
-- ros_topic_name: "/odom"
- gz_topic_name: "/model/andino/odometry"
+# Placeholders to be rewritten by launch file:
+# - : Should be replaced by entity name: For example "andino", "andino_1", "andino_2".
+
+- ros_topic_name: "odom"
+ gz_topic_name: "/model//odometry"
ros_type_name: "nav_msgs/msg/Odometry"
gz_type_name: "gz.msgs.Odometry"
direction: GZ_TO_ROS
# odom <-> base_link transform
-- ros_topic_name: "/tf"
- gz_topic_name: "/model/andino/pose"
+- ros_topic_name: "tf"
+ gz_topic_name: "/model//pose"
ros_type_name: "tf2_msgs/msg/TFMessage"
gz_type_name: "gz.msgs.Pose_V"
direction: GZ_TO_ROS
-- ros_topic_name: "/joint_states"
- gz_topic_name: "/world/gazebo_world/model/andino/joint_state"
+- ros_topic_name: "joint_states"
+ gz_topic_name: "/world/gazebo_world/model//joint_state"
ros_type_name: "sensor_msgs/msg/JointState"
gz_type_name: "gz.msgs.Model"
direction: GZ_TO_ROS
-- ros_topic_name: "/camera_info"
- gz_topic_name: "/world/gazebo_world/model/andino/link/base_link/sensor/camera/camera_info"
+- ros_topic_name: "camera_info"
+ gz_topic_name: "/world/gazebo_world/model//link/base_link/sensor/camera/camera_info"
ros_type_name: "sensor_msgs/msg/CameraInfo"
gz_type_name: "gz.msgs.CameraInfo"
direction: GZ_TO_ROS
-- ros_topic_name: "/image_raw"
- gz_topic_name: "/world/gazebo_world/model/andino/link/base_link/sensor/camera/image"
+- ros_topic_name: "image_raw"
+ gz_topic_name: "/world/gazebo_world/model//link/base_link/sensor/camera/image"
ros_type_name: "sensor_msgs/msg/Image"
gz_type_name: "gz.msgs.Image"
direction: GZ_TO_ROS
-- ros_topic_name: "/scan"
- gz_topic_name: "/world/gazebo_world/model/andino/link/base_link/sensor/sensor_ray_front/scan"
+- ros_topic_name: "scan"
+ gz_topic_name: "/world/gazebo_world/model//link/base_link/sensor/sensor_ray_front/scan"
ros_type_name: "sensor_msgs/msg/LaserScan"
gz_type_name: "gz.msgs.LaserScan"
direction: GZ_TO_ROS
-- ros_topic_name: "/scan/points"
- gz_topic_name: "/world/gazebo_world/model/andino/link/base_link/sensor/sensor_ray_front/scan/points"
+- ros_topic_name: "scan/points"
+ gz_topic_name: "/world/gazebo_world/model//link/base_link/sensor/sensor_ray_front/scan/points"
ros_type_name: "sensor_msgs/msg/PointCloud2"
gz_type_name: "gz.msgs.PointCloudPacked"
direction: GZ_TO_ROS
-- ros_topic_name: "/cmd_vel"
- gz_topic_name: "/model/andino/cmd_vel"
+- ros_topic_name: "cmd_vel"
+ gz_topic_name: "/model//cmd_vel"
ros_type_name: "geometry_msgs/msg/Twist"
gz_type_name: "gz.msgs.Twist"
direction: ROS_TO_GZ
diff --git a/andino_gz/launch/andino_gz.launch.py b/andino_gz/launch/andino_gz.launch.py
index 8c44464..e6b77b9 100644
--- a/andino_gz/launch/andino_gz.launch.py
+++ b/andino_gz/launch/andino_gz.launch.py
@@ -3,75 +3,131 @@
import os
from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
-from launch.actions import DeclareLaunchArgument, ExecuteProcess, IncludeLaunchDescription
+from launch.actions import DeclareLaunchArgument, GroupAction, IncludeLaunchDescription, LogInfo
from launch.conditions import IfCondition
from launch.launch_description_sources import PythonLaunchDescriptionSource
-from launch.substitutions import LaunchConfiguration, PathJoinSubstitution
-from launch_ros.actions import Node
-import xacro
+from launch.substitutions import LaunchConfiguration, PathJoinSubstitution, PythonExpression, TextSubstitution
+from launch_ros.actions import Node, PushRosNamespace
+
+from nav2_common.launch import ParseMultiRobotPose
def generate_launch_description():
pkg_andino_gz = get_package_share_directory('andino_gz')
ros_bridge_arg = DeclareLaunchArgument(
- 'ros_bridge', default_value='false', description = 'Run ROS bridge node.')
- rviz_arg = DeclareLaunchArgument('rviz', default_value='false', description='Start RViz.')
- world_name_arg = DeclareLaunchArgument('world_name', default_value='depot.sdf', description='Name of the world to load.')
+ 'ros_bridge', default_value='true', description='Run ROS bridge node.')
+ rviz_arg = DeclareLaunchArgument('rviz', default_value='true', description='Start RViz.')
+ world_name_arg = DeclareLaunchArgument(
+ 'world_name', default_value='depot.sdf', description='Name of the world to load.')
+ robots_arg = DeclareLaunchArgument(
+ 'robots', default_value="andino={x: 0., y: 0., z: 0.1, yaw: 0.};",
+ description='Robots to spawn, multiple robots can be stated separated by a ; ')
+ # Variables of launch file.
+ rviz = LaunchConfiguration('rviz')
+ ros_bridge = LaunchConfiguration('ros_bridge')
world_name = LaunchConfiguration('world_name')
- world_path = PathJoinSubstitution([pkg_andino_gz, 'worlds', world_name])
- # Gazebo Sim
- gazebo = IncludeLaunchDescription(
- PythonLaunchDescriptionSource(
- os.path.join(get_package_share_directory('ros_gz_sim'), 'launch', 'gz_sim.launch.py')
- ),
- launch_arguments={'gz_args': world_path}.items(),
- )
-
- # Spawn the robot and the Robot State Publisher node.
- spawn_robot_and_rsp = IncludeLaunchDescription(
- PythonLaunchDescriptionSource(
- os.path.join(pkg_andino_gz, 'launch', 'spawn_robot.launch.py')
- ),
- launch_arguments={
- 'entity': 'andino',
- 'initial_pose_x': '0',
- 'initial_pose_y': '0',
- 'initial_pose_z': '0.1',
- 'initial_pose_yaw': '0',
- 'robot_description_topic': '/robot_description',
- 'use_sim_time': 'true',
- }.items(),
- )
+ # Obtains world path.
+ world_path = PathJoinSubstitution([pkg_andino_gz, 'worlds', world_name])
- # RViz
- rviz = Node(
- package='rviz2',
- executable='rviz2',
- arguments=['-d', os.path.join(pkg_andino_gz, 'rviz', 'andino_gz.rviz')],
- parameters=[{'use_sim_time': True}],
- condition=IfCondition(LaunchConfiguration('rviz'))
+ base_group = GroupAction(
+ scoped=True, forwarding=False,
+ launch_configurations={
+ 'ros_bridge': ros_bridge,
+ 'world_name': world_name
+ },
+ actions=[
+ # Gazebo Sim
+ IncludeLaunchDescription(
+ PythonLaunchDescriptionSource(
+ os.path.join(get_package_share_directory('ros_gz_sim'), 'launch', 'gz_sim.launch.py')
+ ),
+ launch_arguments={'gz_args': world_path}.items(),
+ ),
+ # ROS Bridge for generic Gazebo stuff
+ Node(
+ package='ros_gz_bridge',
+ executable='parameter_bridge',
+ arguments=['/clock@rosgraph_msgs/msg/Clock[ignition.msgs.Clock'],
+ output='screen',
+ namespace='andino_gz_sim',
+ condition=IfCondition(ros_bridge),
+ ),
+ ]
)
- # Run ros_gz bridge
- ros_bridge = IncludeLaunchDescription(
- PythonLaunchDescriptionSource(
- os.path.join(pkg_andino_gz, 'launch', 'gz_ros_bridge.launch.py')
- ),
- condition=IfCondition(LaunchConfiguration('ros_bridge'))
- )
+ robots_list = ParseMultiRobotPose('robots').value()
+ # When no robots are specified, spawn a single robot at the origin.
+ # The default value isn't getting parsed correctly, so we need to check for an empty dictionary.
+ if (robots_list == {}):
+ robots_list = {"andino": {"x": 0., "y": 0., "z": 0.1, "yaw": 0.}}
+ log_number_robots = LogInfo(msg="Robots to spawn: " + str(robots_list))
+ spawn_robots_group = []
+ for robot_name in robots_list:
+ init_pose = robots_list[robot_name]
+ # As it is scoped and not forwarding, the launch configuration in this context gets cleared.
+ group = GroupAction(
+ scoped=True, forwarding=False,
+ launch_configurations={
+ 'rviz': rviz,
+ 'ros_bridge': ros_bridge
+ },
+ actions=[
+ LogInfo(msg="Group for robot: " + robot_name),
+ PushRosNamespace(
+ condition=IfCondition(
+ PythonExpression([TextSubstitution(text=str(len(robots_list.keys()))), ' > 1'])),
+ namespace=robot_name),
+ # Spawn the robot and the Robot State Publisher node.
+ IncludeLaunchDescription(
+ PythonLaunchDescriptionSource(
+ os.path.join(pkg_andino_gz, 'launch', 'include', 'spawn_robot.launch.py')
+ ),
+ launch_arguments={
+ 'entity': robot_name,
+ 'initial_pose_x': str(init_pose['x']),
+ 'initial_pose_y': str(init_pose['y']),
+ 'initial_pose_z': str(init_pose['z']),
+ 'initial_pose_yaw': str(init_pose['yaw']),
+ 'robot_description_topic': 'robot_description',
+ 'use_sim_time': 'true',
+ }.items(),
+ ),
+ # RViz
+ Node(
+ condition=IfCondition(rviz),
+ package='rviz2',
+ executable='rviz2',
+ arguments=['-d', os.path.join(pkg_andino_gz, 'rviz', 'andino_gz.rviz')],
+ parameters=[{'use_sim_time': True}],
+ remappings=[
+ ('/tf', 'tf'),
+ ('/tf_static', 'tf_static'),
+ ],
+ ),
+ # Run ros_gz bridge
+ IncludeLaunchDescription(
+ PythonLaunchDescriptionSource(
+ os.path.join(pkg_andino_gz, 'launch', 'include', 'gz_ros_bridge.launch.py')
+ ),
+ launch_arguments={
+ 'entity': robot_name,
+ }.items(),
+ condition=IfCondition(LaunchConfiguration('ros_bridge')),
+ )
+ ]
+ )
+ spawn_robots_group.append(group)
- return LaunchDescription(
- [
- # Arguments and Nodes
- ros_bridge_arg,
- rviz_arg,
- world_name_arg,
- gazebo,
- ros_bridge,
- spawn_robot_and_rsp,
- rviz,
- ]
- )
+ ld = LaunchDescription()
+ ld.add_action(log_number_robots)
+ ld.add_action(ros_bridge_arg)
+ ld.add_action(rviz_arg)
+ ld.add_action(world_name_arg)
+ ld.add_action(robots_arg)
+ ld.add_action(base_group)
+ for group in spawn_robots_group:
+ ld.add_action(group)
+ return ld
diff --git a/andino_gz/launch/gz_ros_bridge.launch.py b/andino_gz/launch/gz_ros_bridge.launch.py
deleted file mode 100644
index 01aa0f5..0000000
--- a/andino_gz/launch/gz_ros_bridge.launch.py
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/usr/bin/env python3
-
-import os
-from ament_index_python.packages import get_package_share_directory
-from launch import LaunchDescription
-from launch.actions import ExecuteProcess
-
-def generate_launch_description():
- pkg_andino_gz = get_package_share_directory('andino_gz')
- bridge_config_file_path = os.path.join(pkg_andino_gz, 'config', 'bridge_config.yaml')
-
- bridge_process = ExecuteProcess(
- cmd=[
- 'ros2',
- 'run',
- 'ros_gz_bridge',
- 'parameter_bridge',
- '--ros-args',
- '-p',
- f'config_file:={bridge_config_file_path}'
- ],
- shell=True,
- output='screen',
- )
-
- return LaunchDescription([bridge_process])
diff --git a/andino_gz/launch/include/gz_ros_bridge.launch.py b/andino_gz/launch/include/gz_ros_bridge.launch.py
new file mode 100644
index 0000000..97fa3ae
--- /dev/null
+++ b/andino_gz/launch/include/gz_ros_bridge.launch.py
@@ -0,0 +1,38 @@
+#!/usr/bin/env python3
+
+import os
+from ament_index_python.packages import get_package_share_directory
+from launch import LaunchDescription
+from launch.actions import DeclareLaunchArgument
+from launch_ros.actions import Node
+from launch.substitutions import LaunchConfiguration
+
+from nav2_common.launch import ReplaceString
+
+
+def generate_launch_description():
+ pkg_andino_gz = get_package_share_directory('andino_gz')
+ bridge_config_file_path = os.path.join(pkg_andino_gz, 'config', 'bridge_config.yaml')
+
+ entity_arg = DeclareLaunchArgument(
+ 'entity', default_value='andino', description='Name of the entity to bridge with Gazebo.')
+
+ # A placeholder is used in the bridge config file to be replaced by the entity name.
+ bridge_config = ReplaceString(
+ source_file=bridge_config_file_path,
+ replacements={'': LaunchConfiguration('entity')},
+ )
+
+ bridge_node = Node(
+ package='ros_gz_bridge',
+ executable='parameter_bridge',
+ output='screen',
+ parameters=[{
+ 'config_file': bridge_config
+ }],
+ )
+
+ return LaunchDescription([
+ entity_arg,
+ bridge_node,
+ ])
diff --git a/andino_gz/launch/spawn_robot.launch.py b/andino_gz/launch/include/spawn_robot.launch.py
similarity index 96%
rename from andino_gz/launch/spawn_robot.launch.py
rename to andino_gz/launch/include/spawn_robot.launch.py
index fc52081..01b7829 100644
--- a/andino_gz/launch/spawn_robot.launch.py
+++ b/andino_gz/launch/include/spawn_robot.launch.py
@@ -5,15 +5,14 @@
from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
-from launch.conditions import IfCondition
from launch.substitutions import LaunchConfiguration
-from launch.substitutions import PythonExpression
from launch_ros.actions import Node
from xacro import process_file
PKG_ANDINO_DESCRIPTION = get_package_share_directory('andino_description')
PKG_ANDINO_GZ = get_package_share_directory('andino_gz')
+
def get_robot_description() -> str:
"""
Obtain the URDF from the xacro file.
@@ -78,7 +77,7 @@ def generate_launch_description():
)
robot_desc_argument = DeclareLaunchArgument(
'robot_description_topic',
- default_value='/robot_description',
+ default_value='robot_description',
description='Robot description topic.',
)
rsp_frequency_argument = DeclareLaunchArgument(
@@ -105,6 +104,10 @@ def generate_launch_description():
'robot_description': get_robot_description(),
}
],
+ remappings=[
+ ('/tf', 'tf'),
+ ('/tf_static', 'tf_static'),
+ ],
)
# Spawn the robot model.
diff --git a/andino_gz/package.xml b/andino_gz/package.xml
index e7d0663..409dabe 100644
--- a/andino_gz/package.xml
+++ b/andino_gz/package.xml
@@ -20,6 +20,7 @@
ros_gz_sim
ros2launch
+ nav2_common
rviz2
xacro
diff --git a/andino_gz/rviz/andino_gz.rviz b/andino_gz/rviz/andino_gz.rviz
index 9181d77..dbce4ae 100644
--- a/andino_gz/rviz/andino_gz.rviz
+++ b/andino_gz/rviz/andino_gz.rviz
@@ -9,7 +9,7 @@ Panels:
- /RobotModel1
- /RobotModel1/Description Topic1
- /TF1
- - /LaserScan1
+ - /Image1
Splitter Ratio: 0.5
Tree Height: 2329
- Class: rviz_common/Selection
@@ -61,7 +61,7 @@ Visualization Manager:
Durability Policy: Volatile
History Policy: Keep Last
Reliability Policy: Reliable
- Value: /robot_description
+ Value: robot_description
Enabled: true
Links:
All Links Enabled: true
@@ -228,10 +228,24 @@ Visualization Manager:
Filter size: 10
History Policy: Keep Last
Reliability Policy: Reliable
- Value: /scan
+ Value: scan
Use Fixed Frame: true
Use rainbow: true
Value: true
+ - Class: rviz_default_plugins/Image
+ Enabled: true
+ Max Value: 1
+ Median window: 5
+ Min Value: 0
+ Name: Image
+ Normalize Range: true
+ Topic:
+ Depth: 5
+ Durability Policy: Volatile
+ History Policy: Keep Last
+ Reliability Policy: Reliable
+ Value: image_raw
+ Value: true
Enabled: true
Global Options:
Background Color: 48; 48; 48
@@ -296,15 +310,17 @@ Visualization Manager:
Pitch: 0.35039839148521423
Target Frame:
Value: Orbit (rviz)
- Yaw: 1.0153923034667969
+ Yaw: 1.0203922986984253
Saved: ~
Window Geometry:
Displays:
- collapsed: false
- Height: 2806
- Hide Left Dock: false
+ collapsed: true
+ Height: 1113
+ Hide Left Dock: true
Hide Right Dock: false
- QMainWindow State: 000000ff00000000fd0000000400000000000001da00000a07fc0200000008fb0000001200530065006c0065006300740069006f006e00000001e10000009b000000ab00fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c006100790073010000006900000a070000017800fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261000000010000014d00000a07fc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a00560069006500770073010000006900000a070000012300fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e100000197000000030000137c00000051fc0100000002fb0000000800540069006d006501000000000000137c0000046000fffffffb0000000800540069006d006501000000000000045000000000000000000000103d00000a0700000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000
+ Image:
+ collapsed: false
+ QMainWindow State: 000000ff00000000fd0000000400000000000002eb00000a07fc020000000afb0000001200530065006c0065006300740069006f006e00000001e10000009b000000ab00fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c006100790073000000006900000a070000017800fffffffb0000000a0056006900650077007300000009410000012f0000012300fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261fb0000000c00430061006d006500720061010000028000000153000000000000000000000001000001ca0000036afc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fc000000690000036a0000004500fffffffa000000000100000002fb0000000a0049006d0061006700650100000000ffffffff0000008c00fffffffb0000000a0049006d0061006700650000000000ffffffff0000000000000000fb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e10000019700000003000005fc00000051fc0100000002fb0000000800540069006d00650100000000000005fc0000046000fffffffb0000000800540069006d00650100000000000004500000000000000000000004260000036a00000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000
Selection:
collapsed: false
Time:
@@ -313,6 +329,6 @@ Window Geometry:
collapsed: false
Views:
collapsed: false
- Width: 4988
- X: 132
- Y: 0
+ Width: 1532
+ X: 2945
+ Y: 441
diff --git a/andino_gz/urdf/andino_gz.urdf.xacro b/andino_gz/urdf/andino_gz.urdf.xacro
index e91c712..9ca2dea 100644
--- a/andino_gz/urdf/andino_gz.urdf.xacro
+++ b/andino_gz/urdf/andino_gz.urdf.xacro
@@ -80,6 +80,7 @@
+ camera_link
1.047
diff --git a/andino_gz/worlds/depot.sdf b/andino_gz/worlds/depot.sdf
index 9796086..dfd4719 100644
--- a/andino_gz/worlds/depot.sdf
+++ b/andino_gz/worlds/depot.sdf
@@ -34,7 +34,7 @@
scene
0.4 0.4 0.4
0.8 0.8 0.8
- -0.7 0.7 0.7 0 0.3 -0.3
+ -1.7 0.7 0.7 0 0.3 -0.3
diff --git a/docker/requirements.txt b/docker/requirements.txt
index 5594dda..2eb6eed 100644
--- a/docker/requirements.txt
+++ b/docker/requirements.txt
@@ -20,6 +20,8 @@ python3
python3-pip
python3-setuptools
ros-humble-andino-description
+ros-humble-andino-slam
+ros-humble-nav2-common
software-properties-common
sudo
tmux
diff --git a/docs/media/andino_gz_multi_robot.png b/docs/media/andino_gz_multi_robot.png
new file mode 100644
index 0000000..2d3165e
Binary files /dev/null and b/docs/media/andino_gz_multi_robot.png differ