diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml deleted file mode 100644 index 347f4c11f..000000000 --- a/.github/workflows/documentation.yml +++ /dev/null @@ -1,45 +0,0 @@ -name: Documentation - -on: - push: - branches: - - master - -jobs: - generate_references: - runs-on: ubuntu-20.04 - steps: - - uses: actions/checkout@v1 - - name: Generate documentation - run: | - # Install dependencies - sudo apt-get install git python3-setuptools - pip3 install wheel - pip3 install sphinx-markdown-builder sphinx - export PATH=${PATH}:${HOME}/.local/bin - - # Generate docs - cd docs - make markdown - - # Initialize Git - NAME=$(curl https://api.github.com/users/${GITHUB_ACTOR} | jq -r .name) - ID=$(curl https://api.github.com/users/${GITHUB_ACTOR} | jq -r .id) - git config --global user.name "${NAME}" - git config --global user.email "${ID}+${GITHUB_ACTOR}@users.noreply.github.com" - git reset --hard - git fetch - - # Clone GitHub Wiki - git clone https://github.com/${GITHUB_REPOSITORY}.wiki.git webots_ros2_wiki - [ -d 'webots_ros2_wiki/API' ] && rm -rf webots_ros2_wiki/API - mkdir webots_ros2_wiki/API - cp -r build/markdown/* webots_ros2_wiki/API - - # Publish docs to GitHub Wiki - cd webots_ros2_wiki - if [ ! -z "$(git diff)" ]; then - git add -A - git commit -m "API docs updated" - git push "https://$GITHUB_ACTOR:${{secrets.GITHUB_TOKEN}}@github.com/${GITHUB_REPOSITORY}.wiki.git" - fi diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 01e59dc5d..da70a6750 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -16,7 +16,7 @@ jobs: fail-fast: false matrix: ROS_REPO: [main, testing] - ROS_DISTRO: [foxy, galactic, humble, rolling] + ROS_DISTRO: [foxy, humble, rolling] runs-on: ubuntu-latest env: AFTER_INIT: ./scripts/ci_after_init.bash ${ROS_DISTRO} ${ROS_REPO} diff --git a/.github/workflows/test_develop.yml b/.github/workflows/test_develop.yml index 523fcdd09..67b333146 100644 --- a/.github/workflows/test_develop.yml +++ b/.github/workflows/test_develop.yml @@ -14,7 +14,7 @@ jobs: fail-fast: false matrix: ROS_REPO: [main, testing] - ROS_DISTRO: [foxy, galactic, humble, rolling] + ROS_DISTRO: [foxy, humble, rolling] runs-on: ubuntu-latest env: AFTER_INIT: ./scripts/ci_after_init.bash ${ROS_DISTRO} ${ROS_REPO} diff --git a/scripts/ci_after_init.bash b/scripts/ci_after_init.bash index 523f9d808..f9933376c 100755 --- a/scripts/ci_after_init.bash +++ b/scripts/ci_after_init.bash @@ -26,9 +26,8 @@ if [[ $(lsb_release -rs) == "22.04" && ${WEBOTS_RELEASE_VERSION} == "2022a" ]]; mv /tmp/openssl-1.1/* /usr/local/webots/lib/webots/ fi -# The following packages are only available in the ROS 2 Foxy/Galactic distributions. Therefore, we cannot include them in the package.xml, but we have to install them manually here. - -if [[ "${ROS_DISTRO}" == "foxy" || "${ROS_DISTRO}" == "galactic" ]]; then +# The following packages are only available in the ROS 2 Foxy distribution. Therefore, we cannot include them in the package.xml, but we have to install them manually here. +if [[ "${ROS_DISTRO}" == "foxy" ]]; then apt install -y ros-${ROS_DISTRO}-turtlebot3-cartographer ros-${ROS_DISTRO}-turtlebot3-navigation2 fi diff --git a/scripts/sync_controller_lib.sh b/scripts/sync_controller_lib.sh index b14e37f04..6d8ea2ad8 100755 --- a/scripts/sync_controller_lib.sh +++ b/scripts/sync_controller_lib.sh @@ -44,6 +44,7 @@ mkdir -p projects/default/libraries/vehicle/java cp -r ${WEBOTS_HOME}/projects/default/libraries/vehicle/c/* projects/default/libraries/vehicle/c cp -r ${WEBOTS_HOME}/projects/default/libraries/vehicle/cpp/* projects/default/libraries/vehicle/cpp cp -r ${WEBOTS_HOME}/projects/default/libraries/vehicle/java/* projects/default/libraries/vehicle/java +cp ${WEBOTS_HOME}/projects/default/libraries/vehicle/java/.gitignore projects/default/libraries/vehicle/java cp ${WEBOTS_HOME}/projects/default/libraries/vehicle/Makefile projects/default/libraries/vehicle rm -rf resources diff --git a/webots_ros2/CHANGELOG.rst b/webots_ros2/CHANGELOG.rst index 5efa9ad24..59dfee7a9 100644 --- a/webots_ros2/CHANGELOG.rst +++ b/webots_ros2/CHANGELOG.rst @@ -2,16 +2,26 @@ Changelog for package webots_ros2 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -2023.0.0 (2022-11-29) +2023.0.2 (2023-XX-XX) ------------------ -* Add support for the new Python API of Webots R2023a +* Drop support for Galactic. + +2023.0.1 (2023-01-05) +------------------ +* Fixed relative assets in WSL. +* Fixed broken controller connection in Rats life example. + +2023.0.0 (2022-11-30) +------------------ +* Added support for the new Python API of Webots R2023a * Convert C++ controller API functions to C * Replace libController submodule by commited source files * Removed 'webots_ros2_core' package (deprecated). +* Allow custom motor-encoder pair. 2022.1.4 (2022-11-18) ------------------ -* Fix the camera focal length in the CameraInfo topic. +* Fixed the camera focal length in the CameraInfo topic. * Upgraded to urdf2webots 2.0.3 * Update the calculation of CameraRecognitionObject messages to the RDF convention of R2022b. diff --git a/webots_ros2/package.xml b/webots_ros2/package.xml index 6cc4c43ca..0a3dd8903 100644 --- a/webots_ros2/package.xml +++ b/webots_ros2/package.xml @@ -2,7 +2,7 @@ webots_ros2 - 2022.1.4 + 2023.0.1 Interface between Webots and ROS2 Cyberbotics diff --git a/webots_ros2/setup.py b/webots_ros2/setup.py index bb52ef24f..4a9ac3315 100644 --- a/webots_ros2/setup.py +++ b/webots_ros2/setup.py @@ -6,7 +6,7 @@ setup( name=package_name, - version='2022.1.4', + version='2023.0.1', packages=[package_name], data_files=[ ('share/' + package_name, ['package.xml']), diff --git a/webots_ros2_control/CHANGELOG.rst b/webots_ros2_control/CHANGELOG.rst index 813c02195..490a9dd8a 100644 --- a/webots_ros2_control/CHANGELOG.rst +++ b/webots_ros2_control/CHANGELOG.rst @@ -2,9 +2,10 @@ Changelog for package webots_ros2_control ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -2023.0.0 (2022-XX-XX) +2023.0.0 (2022-11-30) ------------------ * Convert C++ controller API functions to C +* Allow custom motor-encoder pair. 1.2.3 (2022-05-30) ------------------ diff --git a/webots_ros2_control/CMakeLists.txt b/webots_ros2_control/CMakeLists.txt index 80b632310..e79aedb14 100644 --- a/webots_ros2_control/CMakeLists.txt +++ b/webots_ros2_control/CMakeLists.txt @@ -4,8 +4,6 @@ project(webots_ros2_control) # Check which ROS distribution is used, ros2control depends of that if($ENV{ROS_DISTRO} MATCHES "foxy") add_compile_definitions(FOXY) -elseif($ENV{ROS_DISTRO} MATCHES "galactic") - add_compile_definitions(GALACTIC) elseif($ENV{ROS_DISTRO} MATCHES "humble") add_compile_definitions(HUMBLE) elseif($ENV{ROS_DISTRO} MATCHES "rolling") @@ -35,6 +33,9 @@ find_package(rclcpp REQUIRED) find_package(rclcpp_lifecycle REQUIRED) find_package(webots_ros2_driver REQUIRED) +add_compile_definitions(HARDWARE_INTERFACE_VERSION_MAJOR=${hardware_interface_VERSION_MAJOR}) +add_compile_definitions(HARDWARE_INTERFACE_VERSION_MINOR=${hardware_interface_VERSION_MINOR}) + if (MSVC OR MSYS OR MINGW OR WIN32) set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) endif() diff --git a/webots_ros2_control/include/webots_ros2_control/Ros2ControlSystem.hpp b/webots_ros2_control/include/webots_ros2_control/Ros2ControlSystem.hpp index 5816b67cc..e924d94da 100644 --- a/webots_ros2_control/include/webots_ros2_control/Ros2ControlSystem.hpp +++ b/webots_ros2_control/include/webots_ros2_control/Ros2ControlSystem.hpp @@ -70,7 +70,7 @@ namespace webots_ros2_control std::vector export_state_interfaces() override; std::vector export_command_interfaces() override; -#if FOXY || GALACTIC +#if FOXY hardware_interface::return_type read() override; hardware_interface::return_type write() override; #else // HUMBLE, ROLLING diff --git a/webots_ros2_control/package.xml b/webots_ros2_control/package.xml index 16b97a1fc..092031d3e 100644 --- a/webots_ros2_control/package.xml +++ b/webots_ros2_control/package.xml @@ -2,7 +2,7 @@ webots_ros2_control - 2022.1.4 + 2023.0.1 ros2_control plugin for Webots Cyberbotics http://wiki.ros.org/webots_ros2 diff --git a/webots_ros2_control/src/Ros2Control.cpp b/webots_ros2_control/src/Ros2Control.cpp index 9ecd904dd..466627d8f 100644 --- a/webots_ros2_control/src/Ros2Control.cpp +++ b/webots_ros2_control/src/Ros2Control.cpp @@ -42,7 +42,7 @@ namespace webots_ros2_control const rclcpp::Duration dt = rclcpp::Duration::from_seconds(mControlPeriodMs / 1000.0); if (periodMs >= mControlPeriodMs) { -#if FOXY || GALACTIC +#if FOXY mControllerManager->read(); #else mControllerManager->read(mNode->get_clock()->now(), dt); @@ -55,7 +55,7 @@ namespace webots_ros2_control mLastControlUpdateMs = nowMs; #endif -#if FOXY || GALACTIC +#if FOXY mControllerManager->write(); #else // HUMBLE, ROLLING mControllerManager->write(mNode->get_clock()->now(), dt); @@ -95,9 +95,17 @@ namespace webots_ros2_control } for (unsigned int i = 0; i < controlHardware.size(); i++) { + +// Necessary hotfix for renamed variables present in "hardware_interface" package for versions above 3.5 (#590) +#if HARDWARE_INTERFACE_VERSION_MAJOR >= 3 && HARDWARE_INTERFACE_VERSION_MINOR >= 5 + const std::string pluginName = controlHardware[i].hardware_plugin_name; + auto webotsSystem = std::unique_ptr( + mHardwareLoader->createUnmanagedInstance(pluginName)); +#else const std::string hardwareType = controlHardware[i].hardware_class_type; auto webotsSystem = std::unique_ptr( mHardwareLoader->createUnmanagedInstance(hardwareType)); +#endif webotsSystem->init(mNode, controlHardware[i]); #if FOXY resourceManager->import_component(std::move(webotsSystem)); diff --git a/webots_ros2_control/src/Ros2ControlSystem.cpp b/webots_ros2_control/src/Ros2ControlSystem.cpp index c0aedb0f4..415466066 100644 --- a/webots_ros2_control/src/Ros2ControlSystem.cpp +++ b/webots_ros2_control/src/Ros2ControlSystem.cpp @@ -40,7 +40,12 @@ namespace webots_ros2_control WbDeviceTag device = wb_robot_get_device(joint.name.c_str()); WbNodeType type = wb_device_get_node_type(device); joint.motor = (type == WB_NODE_LINEAR_MOTOR || type == WB_NODE_ROTATIONAL_MOTOR) ? device : wb_position_sensor_get_motor(device); + device = (component.parameters.count("sensor") == 0) ? + wb_robot_get_device(joint.name.c_str()) : + wb_robot_get_device(component.parameters.at("sensor").c_str()); + type = wb_device_get_node_type(device); joint.sensor = (type == WB_NODE_POSITION_SENSOR) ? device : wb_motor_get_position_sensor(device); + if (joint.sensor) wb_position_sensor_enable(joint.sensor, wb_robot_get_basic_time_step()); if (!joint.sensor && !joint.motor) @@ -153,7 +158,7 @@ namespace webots_ros2_control } #endif -#if FOXY || GALACTIC +#if FOXY hardware_interface::return_type Ros2ControlSystem::read() #else // HUMBLE, ROLLING hardware_interface::return_type Ros2ControlSystem::read(const rclcpp::Time &/*time*/, const rclcpp::Duration &/*period*/) @@ -180,7 +185,7 @@ namespace webots_ros2_control return hardware_interface::return_type::OK; } -#if FOXY || GALACTIC +#if FOXY hardware_interface::return_type Ros2ControlSystem::write() #else // HUMBLE, ROLLING hardware_interface::return_type Ros2ControlSystem::write(const rclcpp::Time &/*time*/, const rclcpp::Duration &/*period*/) diff --git a/webots_ros2_driver/CHANGELOG.rst b/webots_ros2_driver/CHANGELOG.rst index 6374bf427..3e24b53b3 100644 --- a/webots_ros2_driver/CHANGELOG.rst +++ b/webots_ros2_driver/CHANGELOG.rst @@ -2,7 +2,11 @@ Changelog for package webots_ros2_driver ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -2023.0.0 (2022-11-29) +2023.0.1 (2023-01-05) +------------------ +* Fix relative assets in WSL. + +2023.0.0 (2022-11-30) ------------------ * Add support for the new Python API of Webots R2023a * Convert C++ controller API functions to C diff --git a/webots_ros2_driver/CMakeLists.txt b/webots_ros2_driver/CMakeLists.txt index c9bf997dd..f3dbd9f40 100644 --- a/webots_ros2_driver/CMakeLists.txt +++ b/webots_ros2_driver/CMakeLists.txt @@ -6,18 +6,13 @@ if($ENV{ROS_DISTRO} MATCHES "foxy") add_compile_definitions(FOXY) endif() -if($ENV{ROS_DISTRO} MATCHES "galactic") - add_compile_definitions(GALACTIC) -endif() - set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_BUILD_WITH_INSTALL_RPATH ON) # Check which ROS distribution is used, vision_msgs depends of that if($ENV{ROS_DISTRO} MATCHES "foxy") add_compile_definitions(FOXY) -elseif($ENV{ROS_DISTRO} MATCHES "galactic") - add_compile_definitions(GALACTIC) elseif($ENV{ROS_DISTRO} MATCHES "humble") add_compile_definitions(HUMBLE) elseif($ENV{ROS_DISTRO} MATCHES "rolling") @@ -39,7 +34,7 @@ find_package(webots_ros2_msgs REQUIRED) find_package(tinyxml2_vendor REQUIRED) find_package(TinyXML2 REQUIRED) -if($ENV{ROS_DISTRO} MATCHES "foxy" OR $ENV{ROS_DISTRO} MATCHES "galactic") +if($ENV{ROS_DISTRO} MATCHES "foxy") find_package(PythonLibs 3.8 EXACT REQUIRED) else() find_package(PythonLibs 3.10 EXACT REQUIRED) @@ -85,6 +80,8 @@ ament_python_install_package(vehicle ament_python_install_package(${PROJECT_NAME} PACKAGE_DIR ${PROJECT_NAME}) +# Driver +set(CMAKE_INSTALL_RPATH "$ORIGIN/../controller") add_executable(driver src/Driver.cpp src/WebotsNode.cpp @@ -100,8 +97,6 @@ add_executable(driver src/utils/Math.cpp src/utils/Utils.cpp ) - -# Driver ament_target_dependencies(driver rosgraph_msgs rclcpp @@ -131,6 +126,7 @@ install(TARGETS driver ) # Dynamic IMU +set(CMAKE_INSTALL_RPATH "$ORIGIN/controller") add_library( ${PROJECT_NAME}_imu SHARED @@ -179,18 +175,6 @@ install( PATTERN "*CppDriver*" ) -# Create symlinks of libController -macro(lib_symlink filename) - install(CODE "execute_process(COMMAND ln -sf ${CMAKE_INSTALL_PREFIX}/lib/controller/${filename} ${CMAKE_INSTALL_PREFIX}/lib/${filename})") -endmacro(lib_symlink) - -lib_symlink(libController.so) -lib_symlink(libcar.so) -lib_symlink(libCppCar.so) -lib_symlink(libCppController.so) -lib_symlink(libCppDriver.so) -lib_symlink(libdriver.so) - # Prevent pluginlib from using boost target_compile_definitions(driver PUBLIC "PLUGINLIB__DISABLE_BOOST_FUNCTIONS") target_compile_definitions(${PROJECT_NAME}_imu PUBLIC "PLUGINLIB__DISABLE_BOOST_FUNCTIONS") @@ -208,6 +192,15 @@ install( ) # Ament export +set(WEBOTS_LIB_PATH + controller/${CMAKE_SHARED_LIBRARY_PREFIX}Controller${CMAKE_SHARED_LIBRARY_SUFFIX} + controller/${CMAKE_SHARED_LIBRARY_PREFIX}CppController${CMAKE_SHARED_LIBRARY_SUFFIX} + controller/${CMAKE_SHARED_LIBRARY_PREFIX}driver${CMAKE_SHARED_LIBRARY_SUFFIX} + controller/${CMAKE_SHARED_LIBRARY_PREFIX}CppDriver${CMAKE_SHARED_LIBRARY_SUFFIX} + controller/${CMAKE_SHARED_LIBRARY_PREFIX}car${CMAKE_SHARED_LIBRARY_SUFFIX} + controller/${CMAKE_SHARED_LIBRARY_PREFIX}CppCar${CMAKE_SHARED_LIBRARY_SUFFIX} +) + ament_export_include_directories( include include/webots/c @@ -227,6 +220,6 @@ ament_export_dependencies( ) ament_export_libraries( ${PROJECT_NAME}_imu - ${WEBOTS_LIB} + ${WEBOTS_LIB_PATH} ) ament_package() diff --git a/webots_ros2_driver/include/webots_ros2_driver/plugins/static/Ros2Camera.hpp b/webots_ros2_driver/include/webots_ros2_driver/plugins/static/Ros2Camera.hpp index 193f05a45..e679c3797 100644 --- a/webots_ros2_driver/include/webots_ros2_driver/plugins/static/Ros2Camera.hpp +++ b/webots_ros2_driver/include/webots_ros2_driver/plugins/static/Ros2Camera.hpp @@ -22,7 +22,12 @@ #include #include #include +#if defined (HUMBLE) || defined (ROLLING) +#include +#else +// Deprecated in Humble and Rolling #include +#endif #include #include #include diff --git a/webots_ros2_driver/package.xml b/webots_ros2_driver/package.xml index e9ed36786..b8b5d7279 100644 --- a/webots_ros2_driver/package.xml +++ b/webots_ros2_driver/package.xml @@ -1,7 +1,7 @@ webots_ros2_driver - 2022.1.4 + 2023.0.1 Implementation of the Webots - ROS 2 interface Cyberbotics Apache License 2.0 diff --git a/webots_ros2_driver/scripts/webots_tcp_client.py b/webots_ros2_driver/scripts/webots_tcp_client.py index eed7f2d97..bbe64c01e 100644 --- a/webots_ros2_driver/scripts/webots_tcp_client.py +++ b/webots_ros2_driver/scripts/webots_tcp_client.py @@ -18,15 +18,35 @@ import os import socket +import subprocess import sys import time -HOST = 'host.docker.internal' # Connect to host of the container + +def get_host_ip(): + try: + output = subprocess.run(['ip', 'route'], check=True, stdout=subprocess.PIPE, universal_newlines=True) + for line in output.stdout.split('\n'): + fields = line.split() + if fields and fields[0] == 'default': + return fields[2] + sys.exit('Unable to get host IP address.') + except subprocess.CalledProcessError: + sys.exit('Unable to get host IP address. \'ip route\' could not be executed.') + + +HOST = get_host_ip() # Connect to host of the VM PORT = 2000 # Port to connect to tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) -launch_arguments = sys.argv[1] -world_name = sys.argv[2] +launch_arguments = '' +for arg in sys.argv: + if arg.endswith('.py') or arg == '': + continue + elif arg.endswith('.wbt'): + world_name = arg + else: + launch_arguments = launch_arguments + ' ' + arg def host_shared_folder(): diff --git a/webots_ros2_driver/src/Driver.cpp b/webots_ros2_driver/src/Driver.cpp index 184abd955..65f367ed5 100644 --- a/webots_ros2_driver/src/Driver.cpp +++ b/webots_ros2_driver/src/Driver.cpp @@ -29,7 +29,7 @@ int main(int argc, char **argv) { webots_ros2_driver::WebotsNode::handleSignals(); rclcpp::InitOptions options{}; -#if FOXY || GALACTIC +#if FOXY options.shutdown_on_sigint = false; #else options.shutdown_on_signal = false; diff --git a/webots_ros2_driver/src/plugins/static/Ros2Camera.cpp b/webots_ros2_driver/src/plugins/static/Ros2Camera.cpp index 9fd90d7fb..b136899d0 100644 --- a/webots_ros2_driver/src/plugins/static/Ros2Camera.cpp +++ b/webots_ros2_driver/src/plugins/static/Ros2Camera.cpp @@ -158,7 +158,7 @@ namespace webots_ros2_driver vision_msgs::msg::ObjectHypothesisWithPose hypothesis; hypothesis.pose.pose = pose.pose; detection.results.push_back(hypothesis); - #if FOXY || GALACTIC + #if FOXY detection.bbox.center.x = objects[i].position_on_image[0]; detection.bbox.center.y = objects[i].position_on_image[1]; #else @@ -174,7 +174,7 @@ namespace webots_ros2_driver recognitionWebotsObject.id = objects[i].id; recognitionWebotsObject.model = std::string(objects[i].model); recognitionWebotsObject.pose = pose; - #if FOXY || GALACTIC + #if FOXY recognitionWebotsObject.bbox.center.x = objects[i].position_on_image[0]; recognitionWebotsObject.bbox.center.y = objects[i].position_on_image[1]; #else diff --git a/webots_ros2_driver/webots/projects/default/libraries/vehicle/java/.gitignore b/webots_ros2_driver/webots/projects/default/libraries/vehicle/java/.gitignore new file mode 100644 index 000000000..748e52f50 --- /dev/null +++ b/webots_ros2_driver/webots/projects/default/libraries/vehicle/java/.gitignore @@ -0,0 +1,4 @@ +/vehicle.cpp +/output +/*jnilib + diff --git a/webots_ros2_driver/webots_ros2_driver/ros2_supervisor.py b/webots_ros2_driver/webots_ros2_driver/ros2_supervisor.py old mode 100644 new mode 100755 index 73c562fcd..2cb140105 --- a/webots_ros2_driver/webots_ros2_driver/ros2_supervisor.py +++ b/webots_ros2_driver/webots_ros2_driver/ros2_supervisor.py @@ -32,7 +32,8 @@ from std_msgs.msg import String sys.path.insert(1, os.path.join(os.path.dirname(webots_ros2_importer.__file__), 'urdf2webots')) from urdf2webots.importer import convertUrdfFile, convertUrdfContent -from webots_ros2_msgs.srv import SpawnUrdfRobot +from webots_ros2_msgs.srv import SpawnUrdfRobot, SpawnNodeFromString +import re # As Ros2Supervisor needs the controller library, we extend the path here # to avoid to load another library named "controller" or "vehicle". @@ -52,12 +53,19 @@ def __init__(self): self.create_timer(1 / 1000, self.__supervisor_step_callback) self.__clock_publisher = self.create_publisher(Clock, 'clock', 10) - # Spawn URDF robots + # Spawn Nodes (URDF robots or Webots objects) root_node = self.__robot.getRoot() - self.__insertion_robot_place = root_node.getField('children') - self.__urdf_robots_list=[] + self.__insertion_node_place = root_node.getField('children') + self.__node_list=[] + + + # Services self.create_service(SpawnUrdfRobot, 'spawn_urdf_robot', self.__spawn_urdf_robot_callback) - self.create_subscription(String, 'remove_urdf_robot', self.__remove_urdf_robot_callback, qos_profile_services_default) + self.create_service(SpawnNodeFromString, 'spawn_node_from_string', self.__spawn_node_from_string_callback) + # Subscriptions + self.create_subscription(String, 'remove_node', self.__remove_imported_node_callback, qos_profile_services_default) + + def __spawn_urdf_robot_callback(self, request, response): robot = request.robot @@ -68,7 +76,7 @@ def __spawn_urdf_robot_callback(self, request, response): self.get_logger().info('Ros2Supervisor cannot import an unnamed URDF robot. Please specifiy it with name="" in the URDFSpawner object.') response.success = False return response - if robot_name in self.__urdf_robots_list: + if robot_name in self.__node_list: self.get_logger().info('The URDF robot name "' + str(robot_name) + '" is already used by another robot! Please specifiy a unique name.') response.success = False return response @@ -93,33 +101,77 @@ def __spawn_urdf_robot_callback(self, request, response): self.get_logger().info('Ros2Supervisor can not import a URDF file without a specified "urdf_path" or "robot_description" in the URDFSpawner object.') response.success = False return response - - self.__insertion_robot_place.importMFNodeFromString(-1, robot_string) + self.__insertion_node_place.importMFNodeFromString(-1, robot_string) self.get_logger().info('Ros2Supervisor has imported the URDF robot named "' + str(robot_name) + '".') - self.__urdf_robots_list.append(robot_name) + self.__node_list.append(robot_name) + response.success = True + return response + + + def __spawn_node_from_string_callback(self, request, response): + object_string = request.data + if(object_string == ''): + self.get_logger().info('Ros2Supervisor cannot import an empty string.') + response.success = False + return response + # Extract Webots node name from string. + name_match = re.search('name "[a-z0-9_]*"', object_string) + object_name = name_match.group().replace('name ', '') + object_name = object_name.replace('"', '') + # Check that the name is not an empty string. + if object_name == '': + self.get_logger().info('Ros2Supervisor cannot import an unnamed node.') + response.success = False + return response + # Check that the name is unique. + if object_name in self.__node_list: + self.get_logger().info('Ros2Supervisor has found a duplicate node in the world named "' + str(object_name) + '". Please specifiy a unique name.') + response.success = False + return response + # Insert the object. + self.__node_list.append(object_name) + self.__insertion_node_place.importMFNodeFromString(-1, object_string) + + # Check if the object has been imported into the world + node = None + node_imported_successfully = False + for id_node in range(self.__insertion_node_place.getCount()): + node = self.__insertion_node_place.getMFNode(id_node) + node_name_field = node.getField('name') + if node_name_field and node_name_field.getSFString() == object_name: + node_imported_successfully = True + break + if not node_imported_successfully: + self.__node_list.remove(object_name) + self.get_logger().info('Ros2Supervisor could not import the node named "' + str(object_name) + '".') + response.success = False + return response + + self.get_logger().info('Ros2Supervisor has imported the node named "' + str(object_name) + '".') response.success = True return response - def __remove_urdf_robot_callback(self, message): - robotName = message.data + # Allows to remove any imported node (urdf robots / VRML Nodes) by name. + def __remove_imported_node_callback(self, message): + name = message.data - if robotName in self.__urdf_robots_list: - robot_node = None + if name in self.__node_list: + node = None - for id_node in range(self.__insertion_robot_place.getCount()): - node = self.__insertion_robot_place.getMFNode(id_node) + for id_node in range(self.__insertion_node_place.getCount()): + node = self.__insertion_node_place.getMFNode(id_node) node_name_field = node.getField('name') - if node_name_field and node_name_field.getSFString() == robotName: - robot_node = node + if node_name_field and node_name_field.getSFString() == name: + node = node break - if robot_node: - robot_node.remove() - self.__urdf_robots_list.remove(robotName) - self.get_logger().info('Ros2Supervisor has removed the URDF robot named "' + str(robotName) + '".') + if node: + node.remove() + self.__node_list.remove(name) + self.get_logger().info('Ros2Supervisor has removed the node named "' + str(name) + '".') else: - self.get_logger().info('Ros2Supervisor wanted to remove the URDF robot named "' + str(robotName) + - '" but this robot has not been found in the simulation world.') + self.get_logger().info('Ros2Supervisor wanted to remove the node named "' + str(name) + + '" but this node has not been found in the simulation world.') def __supervisor_step_callback(self): if self.__robot.step(self.__timestep) < 0: diff --git a/webots_ros2_driver/webots_ros2_driver/utils.py b/webots_ros2_driver/webots_ros2_driver/utils.py index 729c4eeab..ebcbf02da 100644 --- a/webots_ros2_driver/webots_ros2_driver/utils.py +++ b/webots_ros2_driver/webots_ros2_driver/utils.py @@ -125,9 +125,21 @@ def container_shared_folder(): return shared_folder_list[1] +def get_host_ip(): + try: + output = subprocess.run(['ip', 'route'], check=True, stdout=subprocess.PIPE, universal_newlines=True) + for line in output.stdout.split('\n'): + fields = line.split() + if fields and fields[0] == 'default': + return fields[2] + sys.exit('Unable to get host IP address.') + except subprocess.CalledProcessError: + sys.exit('Unable to get host IP address. \'ip route\' could not be executed.') + + def controller_url_prefix(): if has_shared_folder() or is_wsl(): - return 'tcp://' + ('host.docker.internal' if has_shared_folder() else get_wsl_ip_address()) + ':1234/' + return 'tcp://' + (get_host_ip() if has_shared_folder() else get_wsl_ip_address()) + ':1234/' else: return '' diff --git a/webots_ros2_driver/webots_ros2_driver/webots_launcher.py b/webots_ros2_driver/webots_ros2_driver/webots_launcher.py index 60306348e..3e7b1accc 100644 --- a/webots_ros2_driver/webots_ros2_driver/webots_launcher.py +++ b/webots_ros2_driver/webots_ros2_driver/webots_launcher.py @@ -72,7 +72,6 @@ def __init__(self, output='screen', world=None, gui=True, mode='realtime', strea else: webots_path = '' - mode_string = mode mode = mode if isinstance(mode, Substitution) else TextSubstitution(text=mode) self.__world_copy = tempfile.NamedTemporaryFile(mode='w+', suffix='_world_with_URDF_robot.wbt', delete=False) @@ -96,21 +95,21 @@ def __init__(self, output='screen', world=None, gui=True, mode='realtime', strea xvfb_run_prefix.append('--auto-servernum') no_rendering = '--no-rendering' - # Create arguments file and initialize command to start Webots remotely through TCP + # Initialize command to start Webots remotely through TCP if self.__has_shared_folder: - launch_arguments = ['--batch', ' --mode=' + mode_string] - if not gui: - launch_arguments.extend([' --no-rendering', ' --stdout', ' --stderr', ' --minimize']) - if stream: - launch_arguments.append(' --stream') - webots_tcp_client = (os.path.join(get_package_share_directory('webots_ros2_driver'), 'scripts', 'webots_tcp_client.py')) super().__init__( output=output, cmd=[ 'python3', webots_tcp_client, - launch_arguments, + stream_argument, + no_rendering, + stdout, + stderr, + minimize, + '--batch', + ['--mode=', mode], os.path.basename(self.__world_copy.name), ], name='webots_tcp_client', @@ -157,7 +156,10 @@ def execute(self, context: LaunchContext): if os.path.isabs(url_path) or url_path.startswith('webots://') or url_path.startswith('http://') or url_path.startswith('https://'): continue - new_url_path = '"' + os.path.split(world_path)[0] + '/' + url_path + '"' + new_url_path = os.path.split(world_path)[0] + '/' + url_path + if self.__is_wsl: + new_url_path = subprocess.check_output(['wslpath', '-w', new_url_path]).strip().decode('utf-8').replace('\\', '/') + new_url_path = '"' + new_url_path + '"' url_path = '"' + url_path + '"' content = content.replace(url_path, new_url_path) diff --git a/webots_ros2_epuck/CHANGELOG.rst b/webots_ros2_epuck/CHANGELOG.rst index d9a06edd1..d626749d2 100644 --- a/webots_ros2_epuck/CHANGELOG.rst +++ b/webots_ros2_epuck/CHANGELOG.rst @@ -2,6 +2,10 @@ Changelog for package webots_ros2_epuck ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +2023.0.1 (2023-01-05) +------------------ +* Fixed broken controller connection in Rats life example. + 2022.1.3 (2022-11-02) ------------------ * Added macOS support. diff --git a/webots_ros2_epuck/launch/robot_launch.py b/webots_ros2_epuck/launch/robot_launch.py index 886b0da1a..bf82345f0 100644 --- a/webots_ros2_epuck/launch/robot_launch.py +++ b/webots_ros2_epuck/launch/robot_launch.py @@ -69,7 +69,7 @@ def get_ros2_nodes(*args): package='webots_ros2_driver', executable='driver', output='screen', - additional_env={'WEBOTS_CONTROLLER_URL': controller_url_prefix() + 'epuck'}, + additional_env={'WEBOTS_CONTROLLER_URL': controller_url_prefix() + 'e-puck'}, parameters=[ {'robot_description': robot_description, 'use_sim_time': use_sim_time, diff --git a/webots_ros2_epuck/package.xml b/webots_ros2_epuck/package.xml index 7dc27958c..e4ee096e1 100644 --- a/webots_ros2_epuck/package.xml +++ b/webots_ros2_epuck/package.xml @@ -2,7 +2,7 @@ webots_ros2_epuck - 2022.1.4 + 2023.0.1 E-puck2 driver for Webots simulated robot Cyberbotics diff --git a/webots_ros2_epuck/setup.py b/webots_ros2_epuck/setup.py index fab280105..24978f6c6 100644 --- a/webots_ros2_epuck/setup.py +++ b/webots_ros2_epuck/setup.py @@ -43,7 +43,7 @@ setup( name=package_name, - version='2022.1.4', + version='2023.0.1', packages=[package_name], data_files=data_files, install_requires=['setuptools', 'launch'], diff --git a/webots_ros2_epuck/worlds/epuck_world.wbt b/webots_ros2_epuck/worlds/epuck_world.wbt index 244420208..04881bf1f 100644 --- a/webots_ros2_epuck/worlds/epuck_world.wbt +++ b/webots_ros2_epuck/worlds/epuck_world.wbt @@ -52,7 +52,6 @@ WoodenBox { E-puck { translation 0.0530459 0.00806403 0.001 rotation 0 0 1 3.1415 - name "epuck" controller "" version "2" camera_width 640 diff --git a/webots_ros2_importer/package.xml b/webots_ros2_importer/package.xml index 6a9da58d7..f22b80d56 100644 --- a/webots_ros2_importer/package.xml +++ b/webots_ros2_importer/package.xml @@ -2,7 +2,7 @@ webots_ros2_importer - 2022.1.4 + 2023.0.1 This package allows to convert URDF and XACRO files into Webots PROTO files. Cyberbotics diff --git a/webots_ros2_importer/setup.py b/webots_ros2_importer/setup.py index b9423251b..334126d67 100644 --- a/webots_ros2_importer/setup.py +++ b/webots_ros2_importer/setup.py @@ -10,7 +10,7 @@ setup( name=package_name, - version='2022.1.4', + version='2023.0.1', packages=[package_name, package_name + '.urdf2webots.urdf2webots'], data_files=data_files, install_requires=[ diff --git a/webots_ros2_mavic/package.xml b/webots_ros2_mavic/package.xml index 90de6a117..4e579af52 100644 --- a/webots_ros2_mavic/package.xml +++ b/webots_ros2_mavic/package.xml @@ -2,7 +2,7 @@ webots_ros2_mavic - 2022.1.4 + 2023.0.1 Mavic 2 Pro robot ROS2 interface for Webots. Cyberbotics diff --git a/webots_ros2_mavic/setup.py b/webots_ros2_mavic/setup.py index 9b515c62e..4bfbbaf6e 100644 --- a/webots_ros2_mavic/setup.py +++ b/webots_ros2_mavic/setup.py @@ -18,7 +18,7 @@ setup( name=package_name, - version='2022.1.4', + version='2023.0.1', packages=[package_name], data_files=data_files, install_requires=['setuptools', 'launch'], diff --git a/webots_ros2_msgs/CMakeLists.txt b/webots_ros2_msgs/CMakeLists.txt index 6762f94ad..853124bf7 100644 --- a/webots_ros2_msgs/CMakeLists.txt +++ b/webots_ros2_msgs/CMakeLists.txt @@ -30,6 +30,7 @@ set(msg_files set(srv_files "srv/SetInt.srv" "srv/SpawnUrdfRobot.srv" + "srv/SpawnNodeFromString.srv" ) rosidl_generate_interfaces(${PROJECT_NAME} diff --git a/webots_ros2_msgs/package.xml b/webots_ros2_msgs/package.xml index 5c23aaa26..c0824ed01 100644 --- a/webots_ros2_msgs/package.xml +++ b/webots_ros2_msgs/package.xml @@ -2,7 +2,7 @@ webots_ros2_msgs - 2022.1.4 + 2023.0.1 Services and Messages of the webots_ros2 packages. Cyberbotics diff --git a/webots_ros2_msgs/srv/SpawnNodeFromString.srv b/webots_ros2_msgs/srv/SpawnNodeFromString.srv new file mode 100644 index 000000000..faf61bb0e --- /dev/null +++ b/webots_ros2_msgs/srv/SpawnNodeFromString.srv @@ -0,0 +1,3 @@ +string data +--- +bool success diff --git a/webots_ros2_tesla/package.xml b/webots_ros2_tesla/package.xml index 73d0ce1bf..d9b5ac1e6 100644 --- a/webots_ros2_tesla/package.xml +++ b/webots_ros2_tesla/package.xml @@ -2,7 +2,7 @@ webots_ros2_tesla - 2022.1.4 + 2023.0.1 Tesla ROS2 interface for Webots. Cyberbotics diff --git a/webots_ros2_tesla/setup.py b/webots_ros2_tesla/setup.py index 63f555894..33a48a51c 100644 --- a/webots_ros2_tesla/setup.py +++ b/webots_ros2_tesla/setup.py @@ -18,7 +18,7 @@ setup( name=package_name, - version='2022.1.4', + version='2023.0.1', packages=[package_name], data_files=data_files, install_requires=['setuptools', 'launch'], diff --git a/webots_ros2_tests/package.xml b/webots_ros2_tests/package.xml index dd65cf2a8..9ef5d3e85 100644 --- a/webots_ros2_tests/package.xml +++ b/webots_ros2_tests/package.xml @@ -2,7 +2,7 @@ webots_ros2_tests - 2022.1.4 + 2023.0.1 System tests for `webots_ros2` packages. Cyberbotics diff --git a/webots_ros2_tests/setup.py b/webots_ros2_tests/setup.py index 1781f6ad9..34932c92f 100644 --- a/webots_ros2_tests/setup.py +++ b/webots_ros2_tests/setup.py @@ -11,7 +11,7 @@ setup( name=package_name, - version='2022.1.4', + version='2023.0.1', packages=[package_name], data_files=data_files, install_requires=['setuptools', 'launch'], diff --git a/webots_ros2_tests/test/test_system_tiago.py b/webots_ros2_tests/test/test_system_tiago.py index 850e3e2f7..8058f5ba9 100644 --- a/webots_ros2_tests/test/test_system_tiago.py +++ b/webots_ros2_tests/test/test_system_tiago.py @@ -36,7 +36,7 @@ def generate_test_description(): initialize_webots_test() # If ROS_DISTRO is rolling or humble, skip the test as some required packages are missing (cf. ci_after_init.bash) - if 'ROS_DISTRO' in os.environ and os.environ['ROS_DISTRO'] != 'foxy' and os.environ['ROS_DISTRO'] != 'galactic': + if 'ROS_DISTRO' in os.environ and os.environ['ROS_DISTRO'] != 'foxy': pytest.skip('ROS_DISTRO is rolling or humble, skipping this test') tiago_webots = IncludeLaunchDescription( diff --git a/webots_ros2_tests/test/test_system_turtlebot_tutorial_navigation.py b/webots_ros2_tests/test/test_system_turtlebot_tutorial_navigation.py index 8ae2aca3b..02acd38aa 100644 --- a/webots_ros2_tests/test/test_system_turtlebot_tutorial_navigation.py +++ b/webots_ros2_tests/test/test_system_turtlebot_tutorial_navigation.py @@ -43,7 +43,7 @@ def generate_test_description(): pytest.skip('RViz might crash in CI, skipping this test') # If ROS_DISTRO is rolling or humble, skip the test as some required packages are missing (cf. ci_after_init.bash) - if 'ROS_DISTRO' in os.environ and os.environ['ROS_DISTRO'] != 'foxy' and os.environ['ROS_DISTRO'] != 'galactic': + if 'ROS_DISTRO' in os.environ and os.environ['ROS_DISTRO'] != 'foxy': pytest.skip('ROS_DISTRO is rolling or humble, skipping this test') # Webots diff --git a/webots_ros2_tests/test/test_system_turtlebot_tutorial_slam.py b/webots_ros2_tests/test/test_system_turtlebot_tutorial_slam.py index 175206f15..e5b56cd93 100644 --- a/webots_ros2_tests/test/test_system_turtlebot_tutorial_slam.py +++ b/webots_ros2_tests/test/test_system_turtlebot_tutorial_slam.py @@ -37,7 +37,7 @@ def generate_test_description(): pytest.skip('RViz might crash in CI, skipping this test') # If ROS_DISTRO is rolling or humble, skip the test as some required packages are missing (cf. ci_after_init.bash) - if 'ROS_DISTRO' in os.environ and os.environ['ROS_DISTRO'] != 'foxy' and os.environ['ROS_DISTRO'] != 'galactic': + if 'ROS_DISTRO' in os.environ and os.environ['ROS_DISTRO'] != 'foxy': pytest.skip('ROS_DISTRO is rolling or humble, skipping this test') # Webots diff --git a/webots_ros2_tiago/package.xml b/webots_ros2_tiago/package.xml index 1a5d883ce..b09f1f284 100644 --- a/webots_ros2_tiago/package.xml +++ b/webots_ros2_tiago/package.xml @@ -2,7 +2,7 @@ webots_ros2_tiago - 2022.1.4 + 2023.0.1 TIAGo robots ROS2 interface for Webots. Cyberbotics diff --git a/webots_ros2_tiago/setup.py b/webots_ros2_tiago/setup.py index a57e2d285..d86b8c0cb 100644 --- a/webots_ros2_tiago/setup.py +++ b/webots_ros2_tiago/setup.py @@ -17,7 +17,7 @@ setup( name=package_name, - version='2022.1.4', + version='2023.0.1', packages=[], data_files=data_files, install_requires=['setuptools', 'launch'], diff --git a/webots_ros2_turtlebot/package.xml b/webots_ros2_turtlebot/package.xml index 5213c2b38..221f88458 100644 --- a/webots_ros2_turtlebot/package.xml +++ b/webots_ros2_turtlebot/package.xml @@ -2,7 +2,7 @@ webots_ros2_turtlebot - 2022.1.4 + 2023.0.1 TurtleBot3 Burger robot ROS2 interface for Webots. Cyberbotics diff --git a/webots_ros2_turtlebot/setup.py b/webots_ros2_turtlebot/setup.py index cf43842d0..c5157ef70 100644 --- a/webots_ros2_turtlebot/setup.py +++ b/webots_ros2_turtlebot/setup.py @@ -22,7 +22,7 @@ setup( name=package_name, - version='2022.1.4', + version='2023.0.1', packages=[package_name], data_files=data_files, install_requires=['setuptools', 'launch'], diff --git a/webots_ros2_universal_robot/package.xml b/webots_ros2_universal_robot/package.xml index d1d0fb2e8..8ecbe3cbe 100644 --- a/webots_ros2_universal_robot/package.xml +++ b/webots_ros2_universal_robot/package.xml @@ -2,7 +2,7 @@ webots_ros2_universal_robot - 2022.1.4 + 2023.0.1 Universal Robot ROS2 interface for Webots. Cyberbotics diff --git a/webots_ros2_universal_robot/setup.py b/webots_ros2_universal_robot/setup.py index af1c6a40a..d74aa7278 100644 --- a/webots_ros2_universal_robot/setup.py +++ b/webots_ros2_universal_robot/setup.py @@ -30,7 +30,7 @@ setup( name=package_name, - version='2022.1.4', + version='2023.0.1', packages=['webots_ros2_universal_robot'], data_files=data_files, install_requires=['setuptools', 'launch'],