diff --git a/MIGRATION.md b/MIGRATION.md index b2d6977bc7..fb3ce83d47 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -3,6 +3,7 @@ API changes in MoveIt releases ## ROS Rolling +- [07/2026] Generated warehouse launch files now use SQLite instead of MongoDB. Use `moveit_warehouse_database_path` to select the SQLite database file. The MongoDB-specific `moveit_warehouse_host` and `moveit_warehouse_port` launch arguments were removed. By default, the database is created in `$ROS_HOME` (or `~/.ros`) with a config-package-specific filename. - [11/2024] All MoveIt 2 headers have been updated to use the .hpp extension. .h headers are now autogenerated with a deprecation warning, and may be removed in future releases. Please update your imports to use the .hpp headers. - [11/2024] Added flags to control padding to CollisionRequest. This change deprecates PlanningScene::checkCollisionUnpadded(..) functions. Please use PlanningScene::checkCollision(..) with a req.pad_environment_collisions = false; diff --git a/moveit_configs_utils/moveit_configs_utils/launches.py b/moveit_configs_utils/moveit_configs_utils/launches.py index d03d92467f..581927fa61 100644 --- a/moveit_configs_utils/moveit_configs_utils/launches.py +++ b/moveit_configs_utils/moveit_configs_utils/launches.py @@ -3,13 +3,14 @@ from launch import LaunchDescription from launch.actions import ( DeclareLaunchArgument, + GroupAction, IncludeLaunchDescription, ) from launch.conditions import IfCondition from launch.launch_description_sources import PythonLaunchDescriptionSource from launch.substitutions import LaunchConfiguration -from launch_ros.actions import Node +from launch_ros.actions import Node, SetParameter from launch_ros.parameter_descriptions import ParameterValue from srdfdom.srdf import SRDF @@ -134,52 +135,38 @@ def generate_spawn_controllers_launch(moveit_config): def generate_warehouse_db_launch(moveit_config): - """Launch file for warehouse database""" + """Launch file for SQLite warehouse database.""" ld = LaunchDescription() ld.add_action( DeclareLaunchArgument( "moveit_warehouse_database_path", - default_value=str( - moveit_config.package_path / "default_warehouse_mongo_db" + default_value=os.path.join( + os.environ.get("ROS_HOME", os.path.expanduser("~/.ros")), + f"{moveit_config.package_path.name}_warehouse.sqlite", ), ) ) ld.add_action(DeclareBooleanLaunchArg("reset", default_value=False)) - # The default DB port for moveit (not default MongoDB port to avoid potential conflicts) - ld.add_action(DeclareLaunchArgument("moveit_warehouse_port", default_value="33829")) - - # The default DB host for moveit ld.add_action( - DeclareLaunchArgument("moveit_warehouse_host", default_value="localhost") + SetParameter( + name="warehouse_plugin", + value="warehouse_ros_sqlite::DatabaseConnection", + ) ) - - # Load warehouse parameters - db_parameters = [ - { - "overwrite": False, - "database_path": LaunchConfiguration("moveit_warehouse_database_path"), - "warehouse_port": LaunchConfiguration("moveit_warehouse_port"), - "warehouse_host": LaunchConfiguration("moveit_warehouse_host"), - "warehouse_exec": "mongod", - "warehouse_plugin": "warehouse_ros_mongo::MongoDatabaseConnection", - }, - ] - # Run the DB server - db_node = Node( - package="warehouse_ros_mongo", - executable="mongo_wrapper_ros.py", - # TODO(dlu): Figure out if this needs to be run in a specific directory - # (ROS 1 version set cwd="ROS_HOME") - parameters=db_parameters, + ld.add_action( + SetParameter( + name="warehouse_host", + value=LaunchConfiguration("moveit_warehouse_database_path"), + ) ) - ld.add_action(db_node) - # If we want to reset the database, run this node + # If requested, reset the database and add the default scene and robot state. reset_node = Node( package="moveit_ros_warehouse", executable="moveit_init_demo_warehouse", output="screen", + parameters=[moveit_config.to_dict()], condition=IfCondition(LaunchConfiguration("reset")), ) ld.add_action(reset_node) @@ -277,7 +264,7 @@ def generate_demo_launch(moveit_config, launch_package_path=None): DeclareBooleanLaunchArg( "db", default_value=False, - description="By default, we do not start a database (it can be large)", + description="By default, we do not configure a warehouse database", ) ) ld.add_action( @@ -309,31 +296,29 @@ def generate_demo_launch(moveit_config, launch_package_path=None): ) ) + # Limit warehouse parameters to the nodes that consume them. ld.add_action( - IncludeLaunchDescription( - PythonLaunchDescriptionSource( - str(launch_package_path / "launch/move_group.launch.py") - ), - ) - ) - - # Run Rviz and load the default config to see the state of the move_group node - ld.add_action( - IncludeLaunchDescription( - PythonLaunchDescriptionSource( - str(launch_package_path / "launch/moveit_rviz.launch.py") - ), - condition=IfCondition(LaunchConfiguration("use_rviz")), - ) - ) - - # If database loading was enabled, start mongodb as well - ld.add_action( - IncludeLaunchDescription( - PythonLaunchDescriptionSource( - str(launch_package_path / "launch/warehouse_db.launch.py") - ), - condition=IfCondition(LaunchConfiguration("db")), + GroupAction( + actions=[ + IncludeLaunchDescription( + PythonLaunchDescriptionSource( + str(launch_package_path / "launch/warehouse_db.launch.py") + ), + condition=IfCondition(LaunchConfiguration("db")), + ), + IncludeLaunchDescription( + PythonLaunchDescriptionSource( + str(launch_package_path / "launch/move_group.launch.py") + ), + ), + # Run Rviz and load the default MoveIt configuration. + IncludeLaunchDescription( + PythonLaunchDescriptionSource( + str(launch_package_path / "launch/moveit_rviz.launch.py") + ), + condition=IfCondition(LaunchConfiguration("use_rviz")), + ), + ] ) ) diff --git a/moveit_configs_utils/test/test_warehouse_launch.py b/moveit_configs_utils/test/test_warehouse_launch.py new file mode 100644 index 0000000000..9b67dcafa2 --- /dev/null +++ b/moveit_configs_utils/test/test_warehouse_launch.py @@ -0,0 +1,148 @@ +from pathlib import Path +from types import SimpleNamespace + +from launch import LaunchContext +from launch.actions import ( + DeclareLaunchArgument, + GroupAction, + IncludeLaunchDescription, + LogInfo, +) +from launch.conditions import IfCondition +from launch_ros.actions import SetParameter + +from moveit_configs_utils import launches + + +def _execute_parameter_actions(actions, context): + for action in actions: + if isinstance(action, (DeclareLaunchArgument, SetParameter)): + action.execute(context) + + +def test_generate_warehouse_db_launch_uses_sqlite(monkeypatch, tmp_path): + node_calls = [] + ros_home = tmp_path / "ros_home" + config_package_path = tmp_path / "test_moveit_config" + monkeypatch.setenv("ROS_HOME", str(ros_home)) + + def capture_node(**kwargs): + node_calls.append(kwargs) + return LogInfo(msg="captured node") + + monkeypatch.setattr(launches, "Node", capture_node) + + moveit_parameters = { + "robot_description": "", + "robot_description_semantic": "", + } + moveit_config = SimpleNamespace( + package_path=config_package_path, + to_dict=lambda: moveit_parameters, + ) + + launch_description = launches.generate_warehouse_db_launch(moveit_config) + actions = launch_description.entities + + launch_arguments = { + action.name: action + for action in actions + if isinstance(action, DeclareLaunchArgument) + } + assert set(launch_arguments) == { + "moveit_warehouse_database_path", + "reset", + } + + set_parameters = [action for action in actions if isinstance(action, SetParameter)] + assert len(set_parameters) == 2 + + context = LaunchContext() + _execute_parameter_actions(actions, context) + + assert context.launch_configurations["moveit_warehouse_database_path"] == str( + ros_home / "test_moveit_config_warehouse.sqlite" + ) + + global_parameters = dict(context.launch_configurations["global_params"]) + assert global_parameters == { + "warehouse_plugin": "warehouse_ros_sqlite::DatabaseConnection", + "warehouse_host": str(ros_home / "test_moveit_config_warehouse.sqlite"), + } + + custom_database_path = str(tmp_path / "custom.sqlite") + custom_context = LaunchContext() + custom_context.launch_configurations["moveit_warehouse_database_path"] = ( + custom_database_path + ) + _execute_parameter_actions(actions, custom_context) + + custom_parameters = dict(custom_context.launch_configurations["global_params"]) + assert custom_parameters["warehouse_host"] == custom_database_path + + # SQLite requires no separate server, so only the reset node is generated. + assert len(node_calls) == 1 + + reset_node = node_calls[0] + assert reset_node["package"] == "moveit_ros_warehouse" + assert reset_node["executable"] == "moveit_init_demo_warehouse" + assert reset_node["output"] == "screen" + assert reset_node["parameters"] == [moveit_parameters] + assert isinstance(reset_node["condition"], IfCondition) + + context.launch_configurations["reset"] = "true" + assert reset_node["condition"].evaluate(context) + + context.launch_configurations["reset"] = "false" + assert not reset_node["condition"].evaluate(context) + + +def test_demo_launch_scopes_warehouse_before_consumers(tmp_path): + launch_directory = tmp_path / "launch" + launch_directory.mkdir() + + launch_file_contents = """\ +from launch import LaunchDescription + + +def generate_launch_description(): + return LaunchDescription() +""" + + for filename in ( + "rsp.launch.py", + "warehouse_db.launch.py", + "move_group.launch.py", + "moveit_rviz.launch.py", + "spawn_controllers.launch.py", + ): + (launch_directory / filename).write_text(launch_file_contents) + + moveit_config = SimpleNamespace(package_path=tmp_path) + launch_description = launches.generate_demo_launch(moveit_config) + + groups = [ + action + for action in launch_description.entities + if isinstance(action, GroupAction) + ] + assert len(groups) == 1 + + includes = [ + action + for action in groups[0].get_sub_entities() + if isinstance(action, IncludeLaunchDescription) + ] + + context = LaunchContext() + include_names = [] + for action in includes: + source = action.launch_description_source + source.get_launch_description(context) + include_names.append(Path(source.location).name) + + assert include_names == [ + "warehouse_db.launch.py", + "move_group.launch.py", + "moveit_rviz.launch.py", + ]