diff --git a/.github/ci/after_make.sh b/.github/ci/after_make.sh index 2e1e59fce2..7e3bdeb02c 100755 --- a/.github/ci/after_make.sh +++ b/.github/ci/after_make.sh @@ -6,12 +6,3 @@ set -e # Install (needed for some tests) make install export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/lib - -# For gz-tools -export GZ_CONFIG_PATH=/usr/local/share/gz - -# For rendering / window tests -Xvfb :1 -screen 0 1280x1024x24 & -export DISPLAY=:1.0 -export RENDER_ENGINE_VALUES=ogre2 -export MESA_GL_VERSION_OVERRIDE=3.3 diff --git a/.github/ci/before_cmake.sh b/.github/ci/before_cmake.sh new file mode 100755 index 0000000000..308074bc46 --- /dev/null +++ b/.github/ci/before_cmake.sh @@ -0,0 +1,9 @@ +#!/bin/sh -l + +set -x + +# For rendering / window tests +Xvfb :1 -screen 0 1280x1024x24 & +export DISPLAY=:1.0 +export RENDER_ENGINE_VALUES=ogre2 +export MESA_GL_VERSION_OVERRIDE=3.3 diff --git a/.github/ci/packages.apt b/.github/ci/packages.apt index 77df164d6e..efde86b28b 100644 --- a/.github/ci/packages.apt +++ b/.github/ci/packages.apt @@ -2,35 +2,34 @@ freeglut3-dev libbenchmark-dev libfreeimage-dev libglew-dev -libgz-cmake3-dev -libgz-common5-dev -libgz-fuel-tools9-dev -libgz-gui8-dev -libgz-math7-eigen3-dev -libgz-msgs10-dev -libgz-physics7-dev -libgz-plugin2-dev -libgz-rendering8-dev -libgz-sensors8-dev +libgz-cmake4-dev +libgz-common6-dev +libgz-fuel-tools10-dev +libgz-gui9-dev +libgz-math8-eigen3-dev +libgz-msgs11-dev +libgz-physics8-dev +libgz-plugin3-dev +libgz-rendering9-dev +libgz-sensors9-dev libgz-tools2-dev -libgz-transport13-dev -libgz-utils2-cli-dev +libgz-transport14-dev +libgz-utils3-cli-dev libogre-1.9-dev libogre-next-2.3-dev libprotobuf-dev libprotoc-dev -libsdformat14-dev +libsdformat15-dev libtinyxml2-dev libxi-dev libxmu-dev libpython3-dev -python3-distutils -python3-gz-math7 -python3-gz-msgs10 -python3-gz-transport13 +python3-gz-math8 +python3-gz-msgs11 +python3-gz-transport14 python3-pybind11 python3-pytest -python3-sdformat14 +python3-sdformat15 qml-module-qt-labs-folderlistmodel qml-module-qt-labs-settings qml-module-qtgraphicaleffects @@ -45,3 +44,5 @@ qtdeclarative5-dev qtquickcontrols2-5-dev uuid-dev xvfb +x11-utils +mesa-utils diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a0ce61b450..a011e2abda 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,6 +7,8 @@ on: - 'ign-gazebo[0-9]' - 'gz-sim[0-9]?' - 'main' + +# Every time you make a push to your PR, it cancel immediately the previous checks, # and start a new one. The other runner will be available more quickly to your PR. concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} @@ -28,7 +30,23 @@ jobs: uses: gazebo-tooling/action-gz-ci@jammy with: # per bug https://github.com/gazebosim/gz-sim/issues/1409 - cmake-args: '-DBUILD_DOCS=OFF' + cmake-args: '-DCMAKE_INSTALL_PREFIX=/usr -DBUILD_DOCS=OFF' codecov-enabled: true cppcheck-enabled: true cpplint-enabled: true + noble-ci: + runs-on: ubuntu-latest + name: Ubuntu Noble CI + steps: + - name: Checkout + uses: actions/checkout@v4 + - uses: actions/setup-python@v3 + - uses: pre-commit/action@v3.0.0 + with: + extra_args: --all-files + - name: Compile and test + id: ci + uses: gazebo-tooling/action-gz-ci@noble + with: + # per bug https://github.com/gazebosim/gz-sim/issues/1409 + cmake-args: '-DCMAKE_INSTALL_PREFIX=/usr -DBUILD_DOCS=OFF' diff --git a/.github/workflows/package_xml.yml b/.github/workflows/package_xml.yml new file mode 100644 index 0000000000..4bd4a9aa0b --- /dev/null +++ b/.github/workflows/package_xml.yml @@ -0,0 +1,11 @@ +name: Validate package.xml + +on: + pull_request: + +jobs: + package-xml: + runs-on: ubuntu-latest + name: Validate package.xml + steps: + - uses: gazebo-tooling/action-gz-ci/validate_package_xml@jammy diff --git a/CMakeLists.txt b/CMakeLists.txt index 503f447c1b..9f309499c8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,16 +1,16 @@ -cmake_minimum_required(VERSION 3.10.2 FATAL_ERROR) +cmake_minimum_required(VERSION 3.22.1 FATAL_ERROR) #============================================================================ # Initialize the project #============================================================================ -project(gz-sim8 VERSION 8.3.0) -set (GZ_DISTRIBUTION "Harmonic") +project(gz-sim9 VERSION 9.0.0) +set (GZ_DISTRIBUTION "Ionic") #============================================================================ # Find gz-cmake #============================================================================ # If you get an error at this line, you need to install gz-cmake -find_package(gz-cmake3 REQUIRED) +find_package(gz-cmake4 REQUIRED) #============================================================================ # Configure the project @@ -66,7 +66,7 @@ cmake_dependent_option(USE_DIST_PACKAGES_FOR_PYTHON #============================================================================ # Setting this policy enables using the protobuf_MODULE_COMPATIBLE -# set command in CMake versions older than 13.13 +# set command when cmake_minimum_required is less than 3.13 set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) # This option is needed to use the PROTOBUF_GENERATE_CPP # in case protobuf is found with the CMake config files @@ -74,28 +74,28 @@ set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) # as protobuf could be find transitively by any dependency set(protobuf_MODULE_COMPATIBLE TRUE) -gz_find_package(sdformat14 REQUIRED) -set(SDF_VER ${sdformat14_VERSION_MAJOR}) +gz_find_package(sdformat15 REQUIRED) +set(SDF_VER ${sdformat15_VERSION_MAJOR}) #-------------------------------------- # Find gz-plugin -gz_find_package(gz-plugin2 REQUIRED COMPONENTS loader register) -set(GZ_PLUGIN_VER ${gz-plugin2_VERSION_MAJOR}) +gz_find_package(gz-plugin3 REQUIRED COMPONENTS loader register) +set(GZ_PLUGIN_VER ${gz-plugin3_VERSION_MAJOR}) #-------------------------------------- # Find gz-transport -gz_find_package(gz-transport13 REQUIRED COMPONENTS log parameters) -set(GZ_TRANSPORT_VER ${gz-transport13_VERSION_MAJOR}) +gz_find_package(gz-transport14 REQUIRED COMPONENTS log parameters) +set(GZ_TRANSPORT_VER ${gz-transport14_VERSION_MAJOR}) #-------------------------------------- # Find gz-msgs -gz_find_package(gz-msgs10 REQUIRED) -set(GZ_MSGS_VER ${gz-msgs10_VERSION_MAJOR}) +gz_find_package(gz-msgs11 REQUIRED) +set(GZ_MSGS_VER ${gz-msgs11_VERSION_MAJOR}) #-------------------------------------- # Find gz-common # Always use the profiler component to get the headers, regardless of status. -gz_find_package(gz-common5 +gz_find_package(gz-common6 COMPONENTS av events @@ -104,17 +104,17 @@ gz_find_package(gz-common5 profiler REQUIRED ) -set(GZ_COMMON_VER ${gz-common5_VERSION_MAJOR}) +set(GZ_COMMON_VER ${gz-common6_VERSION_MAJOR}) #-------------------------------------- # Find gz-fuel_tools -gz_find_package(gz-fuel_tools9 REQUIRED) -set(GZ_FUEL_TOOLS_VER ${gz-fuel_tools9_VERSION_MAJOR}) +gz_find_package(gz-fuel_tools10 REQUIRED) +set(GZ_FUEL_TOOLS_VER ${gz-fuel_tools10_VERSION_MAJOR}) #-------------------------------------- # Find gz-gui -gz_find_package(gz-gui8 REQUIRED) -set(GZ_GUI_VER ${gz-gui8_VERSION_MAJOR}) +gz_find_package(gz-gui9 REQUIRED) +set(GZ_GUI_VER ${gz-gui9_VERSION_MAJOR}) gz_find_package (Qt5 COMPONENTS Core @@ -125,18 +125,18 @@ gz_find_package (Qt5 #-------------------------------------- # Find gz-physics -gz_find_package(gz-physics7 +gz_find_package(gz-physics8 COMPONENTS heightmap mesh sdf REQUIRED ) -set(GZ_PHYSICS_VER ${gz-physics7_VERSION_MAJOR}) +set(GZ_PHYSICS_VER ${gz-physics8_VERSION_MAJOR}) #-------------------------------------- # Find gz-sensors -gz_find_package(gz-sensors8 REQUIRED +gz_find_package(gz-sensors9 REQUIRED # component order is important COMPONENTS # non-rendering @@ -164,17 +164,17 @@ gz_find_package(gz-sensors8 REQUIRED thermal_camera wide_angle_camera ) -set(GZ_SENSORS_VER ${gz-sensors8_VERSION_MAJOR}) +set(GZ_SENSORS_VER ${gz-sensors9_VERSION_MAJOR}) #-------------------------------------- # Find gz-rendering -gz_find_package(gz-rendering8 REQUIRED) -set(GZ_RENDERING_VER ${gz-rendering8_VERSION_MAJOR}) +gz_find_package(gz-rendering9 REQUIRED) +set(GZ_RENDERING_VER ${gz-rendering9_VERSION_MAJOR}) #-------------------------------------- # Find gz-math -gz_find_package(gz-math7 REQUIRED COMPONENTS eigen3) -set(GZ_MATH_VER ${gz-math7_VERSION_MAJOR}) +gz_find_package(gz-math8 REQUIRED COMPONENTS eigen3) +set(GZ_MATH_VER ${gz-math8_VERSION_MAJOR}) #-------------------------------------- # Find if gz command is available @@ -190,8 +190,8 @@ set(GZ_TOOLS_VER 2) #-------------------------------------- # Find gz-utils -gz_find_package(gz-utils2 REQUIRED COMPONENTS cli) -set(GZ_UTILS_VER ${gz-utils2_VERSION_MAJOR}) +gz_find_package(gz-utils3 REQUIRED COMPONENTS cli) +set(GZ_UTILS_VER ${gz-utils3_VERSION_MAJOR}) #-------------------------------------- # Find protobuf @@ -199,7 +199,7 @@ gz_find_package(GzProtobuf REQUIRED COMPONENTS all PRETTY Protobuf) -set(Protobuf_IMPORT_DIRS ${gz-msgs10_INCLUDE_DIRS}) +set(Protobuf_IMPORT_DIRS ${gz-msgs11_INCLUDE_DIRS}) #-------------------------------------- # Find python diff --git a/Changelog.md b/Changelog.md index cb821fd2cc..620f3f922a 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,366 @@ +## Gazebo Sim 9.x + +### Gazebo Sim 9.0.0 (2024-09-25) + +1. **Baseline:** this includes all changes from 8.6.0 and earlier. + +1. Miscellaneous documentation fixes + * [Pull request #2634](https://github.com/gazebosim/gz-sim/pull/2634) + * [Pull request #2632](https://github.com/gazebosim/gz-sim/pull/2632) + * [Pull request #2628](https://github.com/gazebosim/gz-sim/pull/2628) + * [Pull request #2631](https://github.com/gazebosim/gz-sim/pull/2631) + * [Pull request #2627](https://github.com/gazebosim/gz-sim/pull/2627) + * [Pull request #2625](https://github.com/gazebosim/gz-sim/pull/2625) + * [Pull request #2622](https://github.com/gazebosim/gz-sim/pull/2622) + * [Pull request #2607](https://github.com/gazebosim/gz-sim/pull/2607) + * [Pull request #2606](https://github.com/gazebosim/gz-sim/pull/2606) + * [Pull request #2602](https://github.com/gazebosim/gz-sim/pull/2602) + * [Pull request #2601](https://github.com/gazebosim/gz-sim/pull/2601) + * [Pull request #2603](https://github.com/gazebosim/gz-sim/pull/2603) + * [Pull request #2578](https://github.com/gazebosim/gz-sim/pull/2578) + * [Pull request #2592](https://github.com/gazebosim/gz-sim/pull/2592) + * [Pull request #2582](https://github.com/gazebosim/gz-sim/pull/2582) + * [Pull request #2585](https://github.com/gazebosim/gz-sim/pull/2585) + * [Pull request #2576](https://github.com/gazebosim/gz-sim/pull/2576) + * [Pull request #2573](https://github.com/gazebosim/gz-sim/pull/2573) + * [Pull request #2571](https://github.com/gazebosim/gz-sim/pull/2571) + * [Pull request #2574](https://github.com/gazebosim/gz-sim/pull/2574) + * [Pull request #2564](https://github.com/gazebosim/gz-sim/pull/2564) + * [Pull request #2563](https://github.com/gazebosim/gz-sim/pull/2563) + * [Pull request #2562](https://github.com/gazebosim/gz-sim/pull/2562) + * [Pull request #2553](https://github.com/gazebosim/gz-sim/pull/2553) + +1. Fix log playback GUI display + * [Pull request #2611](https://github.com/gazebosim/gz-sim/pull/2611) + * [Pull request #2619](https://github.com/gazebosim/gz-sim/pull/2619) + +1. Add tutorial + example SDF for shadow texture size + * [Pull request #2597](https://github.com/gazebosim/gz-sim/pull/2597) + +1. Fix making breadcrumb static if it's a nested model + * [Pull request #2593](https://github.com/gazebosim/gz-sim/pull/2593) + +1. Update physics system error msg when plugin can not be loaded + * [Pull request #2604](https://github.com/gazebosim/gz-sim/pull/2604) + +1. Fix configuring global illumination GUI plugin parameters + * [Pull request #2594](https://github.com/gazebosim/gz-sim/pull/2594) + +1. Fix particle emitter color range image path warning + * [Pull request #2560](https://github.com/gazebosim/gz-sim/pull/2560) + +1. Fix empty gui world file + * [Pull request #2591](https://github.com/gazebosim/gz-sim/pull/2591) + +1. Fix crash on windows due to invalid log directory path + * [Pull request #2589](https://github.com/gazebosim/gz-sim/pull/2589) + +1. Use ogre2 for DEM worlds + * [Pull request #2586](https://github.com/gazebosim/gz-sim/pull/2586) + +1. Fix crash when running the optical tactile sensor world + * [Pull request #2561](https://github.com/gazebosim/gz-sim/pull/2561) + +1. Prevent follow actor plugin from crashing when actor is removed + * [Pull request #2577](https://github.com/gazebosim/gz-sim/pull/2577) + * [Pull request #2584](https://github.com/gazebosim/gz-sim/pull/2584) + +1. Fix hydrodynamics deprecation warning. + * [Pull request #2579](https://github.com/gazebosim/gz-sim/pull/2579) + +1. Removed actor population world due to bad merge + * [Pull request #2581](https://github.com/gazebosim/gz-sim/pull/2581) + +1. Fixed warning joint trayectory sdf + * [Pull request #2580](https://github.com/gazebosim/gz-sim/pull/2580) + +1. Fix looking up camera name in camera lens system + * [Pull request #2559](https://github.com/gazebosim/gz-sim/pull/2559) + +1. Add a flexible mechanism to combine user and default plugins + * [Pull request #2497](https://github.com/gazebosim/gz-sim/pull/2497) + +1. Fix crash at exit due to a race condition with new signal handler + * [Pull request #2545](https://github.com/gazebosim/gz-sim/pull/2545) + +1. Remove from test worlds + * [Pull request #2551](https://github.com/gazebosim/gz-sim/pull/2551) + +1. Consolidate entity creation. + * [Pull request #2452](https://github.com/gazebosim/gz-sim/pull/2452) + * [Pull request #2527](https://github.com/gazebosim/gz-sim/pull/2527) + * [Pull request #2546](https://github.com/gazebosim/gz-sim/pull/2546) + +1. Add cmake install prefix + * [Pull request #2539](https://github.com/gazebosim/gz-sim/pull/2539) + +1. Fix UNIT_Server_TEST failure caused by change in behavior of `gz::common::SignalHandler` + * [Pull request #2537](https://github.com/gazebosim/gz-sim/pull/2537) + +1. Fix SphericalCoordinates deprecation warnings + * [Pull request #2535](https://github.com/gazebosim/gz-sim/pull/2535) + +1. Revert behavior change introduced in #2452 + +1. Specify System::PreUpdate, Update execution order + * [Pull request #2487](https://github.com/gazebosim/gz-sim/pull/2487) + +1. Add System interface to set default priority + * [Pull request #2500](https://github.com/gazebosim/gz-sim/pull/2500) + +1. Force Qt to use xcb plugin on Wayland + * [Pull request #2526](https://github.com/gazebosim/gz-sim/pull/2526) + +1. Physics: set link velocity from VelocityReset components + * [Pull request #2489](https://github.com/gazebosim/gz-sim/pull/2489) + +1. ForceTorque system: write WrenchMeasured to ECM + * [Pull request #2494](https://github.com/gazebosim/gz-sim/pull/2494) + +1. Remove unused var + * [Pull request #2524](https://github.com/gazebosim/gz-sim/pull/2524) + +1. Deprecate use of added mass via hydrodynamics + * [Pull request #2493](https://github.com/gazebosim/gz-sim/pull/2493) + +1. Make sure steering joints exist before updating velocity / odometry in AckermannSteering plugin + * [Pull request #2521](https://github.com/gazebosim/gz-sim/pull/2521) + +1. Fix ResourceSpawner + * [Pull request #2490](https://github.com/gazebosim/gz-sim/pull/2490) + +1. gui_system_plugin: clarify description in README + * [Pull request #2253](https://github.com/gazebosim/gz-sim/pull/2253) + +1. Fix adding system to non-existent entity + * [Pull request #2516](https://github.com/gazebosim/gz-sim/pull/2516) + +1. Remove ignition related deprecations + * [Pull request #2505](https://github.com/gazebosim/gz-sim/pull/2505) + +1. Fix #2458 - Checking linkEnity is empty + * [Pull request #2509](https://github.com/gazebosim/gz-sim/pull/2509) + +1. Specify System::PreUpdate, Update execution order + * [Pull request #2487](https://github.com/gazebosim/gz-sim/pull/2487) + +1. Improve signal handling + * [Pull request #2501](https://github.com/gazebosim/gz-sim/pull/2501) + +1. Initialize threadsNeedCleanUp + * [Pull request #2503](https://github.com/gazebosim/gz-sim/pull/2503) + +1. Added support for spacecraft thrusters + * [Pull request #2431](https://github.com/gazebosim/gz-sim/pull/2431) + +1. Remove systems if their parent entity is removed + * [Pull request #2232](https://github.com/gazebosim/gz-sim/pull/2232) + +1. Disable rendering tests that are failing on github actions + * [Pull request #2480](https://github.com/gazebosim/gz-sim/pull/2480) + +1. Fix warnings generated by NetworkConfigTest + * [Pull request #2469](https://github.com/gazebosim/gz-sim/pull/2469) + +1. Support visualizing mesh collisions with convex decomposition + * [Pull request #2352](https://github.com/gazebosim/gz-sim/pull/2352) + +1. Remove python3-distutils from package.xml + * [Pull request #2450](https://github.com/gazebosim/gz-sim/pull/2450) + +1. shapes.sdf example: bump to 1.12, add cone shape + * [Pull request #2448](https://github.com/gazebosim/gz-sim/pull/2448) + +1. Adding cone primitives. + * [Pull request #2410](https://github.com/gazebosim/gz-sim/pull/2410) + * [Pull request #2449](https://github.com/gazebosim/gz-sim/pull/2449) + +1. Enable 24.04 CI, require cmake 3.22.1 + * [Pull request #2420](https://github.com/gazebosim/gz-sim/pull/2420) + +1. Parse and set bullet solver iterations + * [Pull request #2351](https://github.com/gazebosim/gz-sim/pull/2351) + +1. ForceTorque system: improve readability + * [Pull request #2403](https://github.com/gazebosim/gz-sim/pull/2403) + +1. Fix warn unused variable in test + * [Pull request #2388](https://github.com/gazebosim/gz-sim/pull/2388) + +1. Physics: remove VelocityCmd at each time step + * [Pull request #2228](https://github.com/gazebosim/gz-sim/pull/2228) + +1. Regroup tutorials into four categories + * [Pull request #2109](https://github.com/gazebosim/gz-sim/pull/2109) + +1. Remove HIDE_SYMBOLS_BY_DEFAULT: replace by a default configuration in gz-cmake. + * [Pull request #2283](https://github.com/gazebosim/gz-sim/pull/2283) + +1. Enable HIDE_SYMBOLS_BY_DEFAULT + linux patches + * [Pull request #2248](https://github.com/gazebosim/gz-sim/pull/2248) + +1. Use sdf FindElement API to avoid const_cast + * [Pull request #2231](https://github.com/gazebosim/gz-sim/pull/2231) + +1. Bumps in Ionic: gz-sim9 + * [Pull request #2205](https://github.com/gazebosim/gz-sim/pull/2205) + * [Pull request #2211](https://github.com/gazebosim/gz-sim/pull/2211) + ## Gazebo Sim 8.x +### Gazebo Sim 8.6.0 (2024-07-25) + +1. Fix error resolving gazebo classic material when loading world + * [Pull request #2492](https://github.com/gazebosim/gz-sim/pull/2492) + +1. Remove systems if their parent entity is removed + * [Pull request #2232](https://github.com/gazebosim/gz-sim/pull/2232) + +1. Fix warnings generated by NetworkConfigTest + * [Pull request #2469](https://github.com/gazebosim/gz-sim/pull/2469) + +1. Fix lidar visualization when `gz_frame_id` is specified + * [Pull request #2481](https://github.com/gazebosim/gz-sim/pull/2481) + +1. Backport convex decomposition visualization + * [Pull request #2454](https://github.com/gazebosim/gz-sim/pull/2454) + +1. Add UserCommands plugin to GPU lidar sensor example + * [Pull request #2479](https://github.com/gazebosim/gz-sim/pull/2479) + +1. Check if any entity actually has a ContactSensorData component before calling GetContactsFromLastStep + * [Pull request #2474](https://github.com/gazebosim/gz-sim/pull/2474) + +1. Enable tests on macOS + * [Pull request #2468](https://github.com/gazebosim/gz-sim/pull/2468) + +1. Update description of reset_sensors test + * [Pull request #2467](https://github.com/gazebosim/gz-sim/pull/2467) + +1. Magnetometer: correct field calculation + * [Pull request #2460](https://github.com/gazebosim/gz-sim/pull/2460) + +1. Address a couple of todos in Conversion.cc + * [Pull request #2461](https://github.com/gazebosim/gz-sim/pull/2461) + +1. Correct name of sensor in warning message + * [Pull request #2457](https://github.com/gazebosim/gz-sim/pull/2457) + +1. Set max contacts for collision pairs + * [Pull request #2270](https://github.com/gazebosim/gz-sim/pull/2270) + +1. Add GravityEnabled boolean component + * [Pull request #2451](https://github.com/gazebosim/gz-sim/pull/2451) + +1. Add support for no gravity link + * [Pull request #2398](https://github.com/gazebosim/gz-sim/pull/2398) + +1. Handle sdf::Geometry::EMPTY in conversions + * [Pull request #2430](https://github.com/gazebosim/gz-sim/pull/2430) + +1. Use topicFromScopedName in a few systems + * [Pull request #2427](https://github.com/gazebosim/gz-sim/pull/2427) + +1. Fix typo in a comment + * [Pull request #2429](https://github.com/gazebosim/gz-sim/pull/2429) + +### Gazebo Sim 8.5.0 (2024-06-26) + +1. Backport: Adding cone primitives + * [Pull request #2404](https://github.com/gazebosim/gz-sim/pull/2404) + +1. Permit to run gz sim -g on Windows + * [Pull request #2382](https://github.com/gazebosim/gz-sim/pull/2382) + +1. Parse voxel resolution SDF param when decomposing meshes + * [Pull request #2445](https://github.com/gazebosim/gz-sim/pull/2445) + +1. Fix model command api test + * [Pull request #2444](https://github.com/gazebosim/gz-sim/pull/2444) + +1. Add tutorial for using the Pose component + * [Pull request #2219](https://github.com/gazebosim/gz-sim/pull/2219) + +1. Do not update sensors if it a triggered sensor + * [Pull request #2443](https://github.com/gazebosim/gz-sim/pull/2443) + +### Gazebo Sim 8.4.0 (2024-06-12) + +1. Add pause run tutorial + * [Pull request #2383](https://github.com/gazebosim/gz-sim/pull/2383) + +1. Fix warning message to show precise jump back in time duration + * [Pull request #2435](https://github.com/gazebosim/gz-sim/pull/2435) + +1. Optimize rendering sensor pose updates + * [Pull request #2425](https://github.com/gazebosim/gz-sim/pull/2425) + +1. Remove a few extra zeros from some sdf files + * [Pull request #2426](https://github.com/gazebosim/gz-sim/pull/2426) + +1. Use VERSION_GREATER_EQUAL in cmake logic + * [Pull request #2418](https://github.com/gazebosim/gz-sim/pull/2418) + +1. Support mesh optimization when using AttachMeshShapeFeature + * [Pull request #2417](https://github.com/gazebosim/gz-sim/pull/2417) + +1. Rephrase cmake comment about CMP0077 + * [Pull request #2419](https://github.com/gazebosim/gz-sim/pull/2419) + +1. Fix CMake warnings in Noble + * [Pull request #2397](https://github.com/gazebosim/gz-sim/pull/2397) + +1. Update sensors with pending trigger immediately in Sensors system + * [Pull request #2408](https://github.com/gazebosim/gz-sim/pull/2408) + +1. Add missing algorithm include + * [Pull request #2414](https://github.com/gazebosim/gz-sim/pull/2414) + +1. Add Track and Follow options in gui EntityContextMenu + * [Pull request #2402](https://github.com/gazebosim/gz-sim/pull/2402) + +1. ForceTorque system: improve readability + * [Pull request #2403](https://github.com/gazebosim/gz-sim/pull/2403) + +1. LTA Dynamics System + * [Pull request #2241](https://github.com/gazebosim/gz-sim/pull/2241) + +1. Remove Empty Test File + * [Pull request #2396](https://github.com/gazebosim/gz-sim/pull/2396) + +1. Fix GCC/CMake warnings for Noble + * [Pull request #2375](https://github.com/gazebosim/gz-sim/pull/2375) + +1. Fix warn unused variable in test + * [Pull request #2388](https://github.com/gazebosim/gz-sim/pull/2388) + +1. Fix name of gz-fuel_tools in package.xml + * [Pull request #2386](https://github.com/gazebosim/gz-sim/pull/2386) + +1. Add package.xml + * [Pull request #2337](https://github.com/gazebosim/gz-sim/pull/2337) + +1. Fix namespace and class links in documentation references that use namespace `gz` + * [Pull request #2385](https://github.com/gazebosim/gz-sim/pull/2385) + +1. Fix ModelPhotoShootTest test failures + * [Pull request #2294](https://github.com/gazebosim/gz-sim/pull/2294) + +1. Enable StoreResolvedURIs when loading SDF + * [Pull request #2349](https://github.com/gazebosim/gz-sim/pull/2349) + +1. Drop python3-disttutils from apt packages files + * [Pull request #2374](https://github.com/gazebosim/gz-sim/pull/2374) + +1. Added example world for `DopplerVelocityLogSystem` + * [Pull request #2373](https://github.com/gazebosim/gz-sim/pull/2373) + +1. Fix Gazebo/White and refactored MaterialParser + * [Pull request #2302](https://github.com/gazebosim/gz-sim/pull/2302) + +1. Support for Gazebo materials + * [Pull request #2269](https://github.com/gazebosim/gz-sim/pull/2269) + ### Gazebo Sim 8.3.0 (2024-04-11) 1. Use relative install paths for plugin shared libraries and gz-tools data @@ -310,6 +671,65 @@ ## Gazebo Sim 7.x +### Gazebo Sim 7.8.0 (2024-07-22) + +1. Added support for spacecraft thrusters + * [Pull request #2431](https://github.com/gazebosim/gz-sim/pull/2431) + +1. Disable rendering tests that are failing on github actions + * [Pull request #2480](https://github.com/gazebosim/gz-sim/pull/2480) + +1. Consolidate entity creation. + * [Pull request #2452](https://github.com/gazebosim/gz-sim/pull/2452) + +1. Set max contacts for collision pairs + * [Pull request #2270](https://github.com/gazebosim/gz-sim/pull/2270) + +1. Add GravityEnabled boolean component + * [Pull request #2451](https://github.com/gazebosim/gz-sim/pull/2451) + +1. Add support for no gravity link + * [Pull request #2398](https://github.com/gazebosim/gz-sim/pull/2398) + +1. Handle sdf::Geometry::EMPTY in conversions + * [Pull request #2430](https://github.com/gazebosim/gz-sim/pull/2430) + +1. Use topicFromScopedName in a few systems + * [Pull request #2427](https://github.com/gazebosim/gz-sim/pull/2427) + +1. Fix typo in a comment + * [Pull request #2429](https://github.com/gazebosim/gz-sim/pull/2429) + +1. Remove a few extra zeros from some sdf files + * [Pull request #2426](https://github.com/gazebosim/gz-sim/pull/2426) + +1. Use VERSION_GREATER_EQUAL in cmake logic + * [Pull request #2418](https://github.com/gazebosim/gz-sim/pull/2418) + +1. Rephrase cmake comment about CMP0077 + * [Pull request #2419](https://github.com/gazebosim/gz-sim/pull/2419) + +1. ForceTorque system: improve readability + * [Pull request #2403](https://github.com/gazebosim/gz-sim/pull/2403) + +1. LTA Dynamics System + * [Pull request #2241](https://github.com/gazebosim/gz-sim/pull/2241) + +1. Fix namespace and class links in documentation references that use namespace `gz` + * [Pull request #2385](https://github.com/gazebosim/gz-sim/pull/2385) + +1. Fix ModelPhotoShootTest test failures + * [Pull request #2294](https://github.com/gazebosim/gz-sim/pull/2294) + +1. update sdf version + * [Pull request #2313](https://github.com/gazebosim/gz-sim/pull/2313) + +1. Fix Gazebo/White and refactored MaterialParser + * [Pull request #2302](https://github.com/gazebosim/gz-sim/pull/2302) + +1. Support for Gazebo materials + * [Pull request #2269](https://github.com/gazebosim/gz-sim/pull/2269) + ### Gazebo Sim 7.7.0 (2024-01-17) 1. Allow using plugin file names and environment variables compatible with Garden and later @@ -3713,6 +4133,36 @@ ## Gazebo Sim 3.x +### Gazebo Sim 3.15.1 (2024-01-05) + +1. Update github action workflows + * [Pull request #2237](https://github.com/gazebosim/gz-sim/pull/2237) + * [Pull request #1988](https://github.com/gazebosim/gz-sim/pull/1988) + +1. Fix macOS test failures by registering components in the core library + * [Pull request #2220](https://github.com/gazebosim/gz-sim/pull/2220) + +1. Bump Fuel model version in test + * [Pull request #2190](https://github.com/gazebosim/gz-sim/pull/2190) + +1. Fix a minor issue in the documentation of the server API + * [Pull request #2067](https://github.com/gazebosim/gz-sim/pull/2067) + +1. Use sdf::Element::FindElement instead of GetElement in ApplyLinkWrench + * [Pull request #2052](https://github.com/gazebosim/gz-sim/pull/2052) + +1. Adds a warning if the `Server` method of a `TestFixture` is called before `Finalize` + * [Pull request #2047](https://github.com/gazebosim/gz-sim/pull/2047) + +1. Protobuf: Do not require version 3 do support Protobuf 4.23.2 (23.2) + * [Pull request #2006](https://github.com/gazebosim/gz-sim/pull/2006) + +1. Print an error message when trying to load SDF files that don't contain a `` + * [Pull request #1998](https://github.com/gazebosim/gz-sim/pull/1998) + +1. Enable GzWeb visualization of markers by republishing service requests on a topic + * [Pull request #1994](https://github.com/gazebosim/gz-sim/pull/1994) + ### Gazebo Sim 3.15.0 (2023-05-08) 1. Speed up Resource Spawner load time by fetching model list asynchronously diff --git a/Migration.md b/Migration.md index a1df392c5c..4f6632b474 100644 --- a/Migration.md +++ b/Migration.md @@ -5,6 +5,25 @@ Deprecated code produces compile-time warnings. These warning serve as notification to users that their code should be upgraded. The next major release will remove the deprecated code. +## Gazebo Sim 8.x to 9.0 + + * **Modified**: + + In the Physics system, all `*VelocityCmd` components are now deleted after + each time step, whereas previously the component values were set to `0` + after each time step. Persistent velocity commands should be reapplied at + each time step. + + Default priority values are specified for the `Physics` and `UserCommands` + systems to ensure that `Physics::Update` and `UserCommands::PreUpdate` + execute before systems with default execution priority. Several constants + for system priority are defined in `gz/sim/System.hh`. + + The ForceTorque system has been changed from updating sensor data during + the parallelized `PostUpdate` phase to use the sequential `Update` phase + and writing directly to the ECM if a sensor entity has a `WrenchMeasured` + component. The ForceTorque system priority is specified to use the + `gz::sim::systems::kPostPhysicsSensorPriority` constant to ensure that its + `Update` phase executes after `Physics::Update` and before systems with + default priority. + ## Gazebo Sim 7.x to 8.0 * **Deprecated** + `gz::sim::components::Factory::Register(const std::string &_type, ComponentDescriptorBase *_compDesc)` and diff --git a/README.md b/README.md index 4a0440eaa9..695107209d 100644 --- a/README.md +++ b/README.md @@ -9,10 +9,10 @@ Build | Status -- | -- -Test coverage | [![codecov](https://codecov.io/gh/gazebosim/gz-sim/tree/gz-sim8/graph/badge.svg)](https://codecov.io/gh/gazebosim/gz-sim/tree/gz-sim8) -Ubuntu Jammy | [![Build Status](https://build.osrfoundation.org/buildStatus/icon?job=gz_sim-ci-gz-sim8-jammy-amd64)](https://build.osrfoundation.org/job/gz_sim-ci-gz-sim8-jammy-amd64) -Homebrew | [![Build Status](https://build.osrfoundation.org/buildStatus/icon?job=gz_sim-ci-gz-sim8-homebrew-amd64)](https://build.osrfoundation.org/job/gz_sim-ci-gz-sim8-homebrew-amd64) -Windows | [![Build Status](https://build.osrfoundation.org/job/gz_sim-8-win/badge/icon)](https://build.osrfoundation.org/job/gz_sim-8-win/) +Test coverage | [![codecov](https://codecov.io/gh/gazebosim/gz-sim/tree/gz-sim9/graph/badge.svg)](https://codecov.io/gh/gazebosim/gz-sim/tree/gz-sim9) +Ubuntu Noble | [![Build Status](https://build.osrfoundation.org/buildStatus/icon?job=gz_sim-ci-gz-sim9-noble-amd64)](https://build.osrfoundation.org/job/gz_sim-ci-gz-sim9-noble-amd64) +Homebrew | [![Build Status](https://build.osrfoundation.org/buildStatus/icon?job=gz_sim-ci-gz-sim9-homebrew-amd64)](https://build.osrfoundation.org/job/gz_sim-ci-gz-sim9-homebrew-amd64) +Windows | [![Build Status](https://build.osrfoundation.org/job/gz_sim-9-win/badge/icon)](https://build.osrfoundation.org/job/gz_sim-9-win/) Gazebo Sim is an open source robotics simulator. Through Gazebo Sim, users have access to high fidelity physics, rendering, and sensor models. Additionally, users and developers have multiple points of entry to simulation including a graphical user interface, plugins, and asynchronous message passing and services. @@ -32,10 +32,10 @@ Gazebo Sim is derived from [Gazebo Classic](http://classic.gazebosim.org) and re [Folder Structure](#folder-structure) -[Code of Conduct](#code-of-conduct) - [Contributing](#contributing) +[Code of Conduct](#code-of-conduct) + [Versioning](#versioning) [License](#license) @@ -78,7 +78,7 @@ introspection and control. # Install -See the [installation tutorial](https://gazebosim.org/api/sim/8/install.html). +See the [installation tutorial](https://gazebosim.org/api/sim/9/install.html). # Usage @@ -99,39 +99,30 @@ gz sim -h In the event that the installation is a mix of Debian and from source, command line tools from `gz-tools` may not work correctly. -A workaround for a single package is to define the environment variable -`GZ_CONFIG_PATH` to point to the location of the Gazebo library installation, -where the YAML file for the package is found, such as +A workaround is to define the environment variable +`GZ_CONFIG_PATH` to point to the different locations of the Gazebo libraries installations, +where the YAML files for the packages are found, such as ``` -export GZ_CONFIG_PATH=/usr/local/share/gz +export GZ_CONFIG_PATH=/usr/local/share/gz:$HOME/ws/install/share/gz ``` -However, that environment variable only takes a single path, which means if the -installations from source are in different locations, only one can be specified. +where `$HOME/ws` is an example colcon workspace used to build Gazebo. -Another workaround for working with multiple Gazebo libraries on the command -line is using symbolic links to each library's YAML file. -``` -mkdir ~/.gz/tools/configs -p -cd ~/.gz/tools/configs/ -ln -s /usr/local/share/gz/fuel8.yaml . -ln -s /usr/local/share/gz/transport13.yaml . -ln -s /usr/local/share/gz/transportlog13.yaml . -... -export GZ_CONFIG_PATH=$HOME/.gz/tools/configs -``` - -This issue is tracked [here](https://github.com/gazebosim/gz-tools/issues/8). +On Windows, `gz sim` (i.e. running both server and GUI in one command) doesn't yet work. +To run Gazebo Sim on Windows, you need to run the server in one terminal (`gz sim -s `) +and the GUI in another terminal (`gz sim -g `). Remember this when reading through +all Gazebo Sim tutorials. Also remember that Conda and `install\setup.bat` need to be sourced +in both terminals (as well as any changes to `GZ_PARTITION` and other environment variables). # Documentation -See the [installation tutorial](https://gazebosim.org/api/sim/8/install.html). +See the [installation tutorial](https://gazebosim.org/api/sim/9/install.html). # Testing -See the [installation tutorial](https://gazebosim.org/api/sim/8/install.html). +See the [installation tutorial](https://gazebosim.org/api/sim/9/install.html). -See the [Writing Tests section of the contributor guide](https://github.com/gazebosim/gz-sim/blob/main/CONTRIBUTING.md#writing-tests) for help creating or modifying tests. +See the [Writing Tests section of the contributor guide](https://gazebosim.org/docs/all/contributing/#writing-tests) for help creating or modifying tests. # Folder Structure @@ -154,7 +145,7 @@ gz-sim │   ├── performance Performance tests. │   ├── plugins Plugins used in tests. │   ├── regression Regression tests. -│   └── tutorials Tutorials, written in markdown. +├── tutorials Tutorials, written in markdown. ├── Changelog.md Changelog. ├── CMakeLists.txt CMake build script. ├── Migration.md Migration guide. @@ -163,8 +154,8 @@ gz-sim # Contributing -Please see -[CONTRIBUTING.md](https://github.com/gazebosim/gz-sim/blob/main/CONTRIBUTING.md). +Please see the +[contribution guide](https://gazebosim.org/docs/all/contributing/). # Code of Conduct diff --git a/docker/Dockerfile.nightly b/docker/Dockerfile.nightly index 6fb0d6bf7b..4dee43a1d5 100644 --- a/docker/Dockerfile.nightly +++ b/docker/Dockerfile.nightly @@ -11,19 +11,19 @@ RUN scripts/enable_nightly.sh RUN apt-get update \ && apt-get install -y \ - libgz-cmake3-dev \ - libgz-common5-dev \ - libgz-fuel-tools9-dev \ - libgz-math7-eigen3-dev \ - libgz-plugin2-dev \ - libgz-physics7-dev \ - libgz-rendering8-dev \ + libgz-cmake4-dev \ + libgz-common6-dev \ + libgz-fuel-tools10-dev \ + libgz-math8-eigen3-dev \ + libgz-plugin3-dev \ + libgz-physics8-dev \ + libgz-rendering9-dev \ libgz-tools2-dev \ - libgz-transport13-dev \ - libgz-gui8-dev \ - libgz-msgs10-dev \ - libgz-sensors8-dev \ - libsdformat14-dev + libgz-transport14-dev \ + libgz-gui9-dev \ + libgz-msgs11-dev \ + libgz-sensors9-dev \ + libsdformat15-dev COPY . gz-sim RUN cd gz-sim \ diff --git a/docker/scripts/build_gz.sh b/docker/scripts/build_gz.sh index 914705f60f..d1ed98317e 100755 --- a/docker/scripts/build_gz.sh +++ b/docker/scripts/build_gz.sh @@ -2,7 +2,7 @@ # Command line parameters: # 1 - github organization name. For example gazebosim or osrf. # 2 - the name of the Gazebo repository. For example gz-math. -# 3 - the name of the branch. For example gz-math7 +# 3 - the name of the branch. For example gz-math8 set -o errexit set -o verbose diff --git a/docker/scripts/upload_json_benchmark.sh b/docker/scripts/upload_json_benchmark.sh index 1cbe1e8cef..10fb0a4349 100755 --- a/docker/scripts/upload_json_benchmark.sh +++ b/docker/scripts/upload_json_benchmark.sh @@ -2,7 +2,7 @@ # Command line parameters: # 1 - github organization name. For example gazebosim or osrf. # 2 - the name of the Gazebo repository. For example gz-math. -# 3 - the name of the branch. For example gz-math7 +# 3 - the name of the branch. For example gz-math8 set -o errexit set -o verbose diff --git a/examples/plugin/command_actor/CMakeLists.txt b/examples/plugin/command_actor/CMakeLists.txt index a01caa9607..be6f6918ff 100644 --- a/examples/plugin/command_actor/CMakeLists.txt +++ b/examples/plugin/command_actor/CMakeLists.txt @@ -1,15 +1,15 @@ -cmake_minimum_required(VERSION 3.10.2 FATAL_ERROR) +cmake_minimum_required(VERSION 3.22.1 FATAL_ERROR) -find_package(gz-cmake3 REQUIRED) +find_package(gz-cmake4 REQUIRED) project(CommandActor) -find_package(gz-plugin2 REQUIRED COMPONENTS register) -set(GZ_PLUGIN_VER ${gz-plugin2_VERSION_MAJOR}) +find_package(gz-plugin3 REQUIRED COMPONENTS register) +set(GZ_PLUGIN_VER ${gz-plugin3_VERSION_MAJOR}) -find_package(gz-sim8 REQUIRED) +find_package(gz-sim9 REQUIRED) add_library(CommandActor SHARED CommandActor.cc) set_property(TARGET CommandActor PROPERTY CXX_STANDARD 17) target_link_libraries(CommandActor PRIVATE gz-plugin${GZ_PLUGIN_VER}::gz-plugin${GZ_PLUGIN_VER} - PRIVATE gz-sim8::gz-sim8) + PRIVATE gz-sim9::gz-sim9) diff --git a/examples/plugin/custom_component/CMakeLists.txt b/examples/plugin/custom_component/CMakeLists.txt index d7f6a23ae6..7e2c0b1478 100644 --- a/examples/plugin/custom_component/CMakeLists.txt +++ b/examples/plugin/custom_component/CMakeLists.txt @@ -1,17 +1,17 @@ -cmake_minimum_required(VERSION 3.10.2 FATAL_ERROR) +cmake_minimum_required(VERSION 3.22.1 FATAL_ERROR) -find_package(gz-cmake3 REQUIRED) +find_package(gz-cmake4 REQUIRED) project(CustomComponentPlugin) -find_package(gz-plugin2 REQUIRED COMPONENTS register) -set(GZ_PLUGIN_VER ${gz-plugin2_VERSION_MAJOR}) +find_package(gz-plugin3 REQUIRED COMPONENTS register) +set(GZ_PLUGIN_VER ${gz-plugin3_VERSION_MAJOR}) -find_package(gz-sim8 REQUIRED) +find_package(gz-sim9 REQUIRED) add_library(CustomComponentPlugin SHARED CustomComponentPlugin.cc ) set_property(TARGET CustomComponentPlugin PROPERTY CXX_STANDARD 17) target_link_libraries(CustomComponentPlugin PRIVATE gz-plugin${GZ_PLUGIN_VER}::gz-plugin${GZ_PLUGIN_VER} - PRIVATE gz-sim8::gz-sim8) + PRIVATE gz-sim9::gz-sim9) diff --git a/examples/plugin/custom_sensor_system/CMakeLists.txt b/examples/plugin/custom_sensor_system/CMakeLists.txt index 32bb92dac3..6aef7ce712 100644 --- a/examples/plugin/custom_sensor_system/CMakeLists.txt +++ b/examples/plugin/custom_sensor_system/CMakeLists.txt @@ -1,17 +1,17 @@ -cmake_minimum_required(VERSION 3.11.0 FATAL_ERROR) +cmake_minimum_required(VERSION 3.22.1 FATAL_ERROR) -find_package(gz-cmake3 REQUIRED) +find_package(gz-cmake4 REQUIRED) project(OdometerSystem) -gz_find_package(gz-plugin2 REQUIRED COMPONENTS register) -set(GZ_PLUGIN_VER ${gz-plugin2_VERSION_MAJOR}) +gz_find_package(gz-plugin3 REQUIRED COMPONENTS register) +set(GZ_PLUGIN_VER ${gz-plugin3_VERSION_MAJOR}) -gz_find_package(gz-sim8 REQUIRED) -set(GZ_SIM_VER ${gz-sim8_VERSION_MAJOR}) +gz_find_package(gz-sim9 REQUIRED) +set(GZ_SIM_VER ${gz-sim9_VERSION_MAJOR}) -find_package(gz-sensors8 REQUIRED) -set(GZ_SENSORS_VER ${gz-sensors8_VERSION_MAJOR}) +find_package(gz-sensors9 REQUIRED) +set(GZ_SENSORS_VER ${gz-sensors9_VERSION_MAJOR}) # Fetch the custom sensor example from gz-sensors # Users won't commonly use this to fetch their sensors. The sensor may be part @@ -20,17 +20,18 @@ include(FetchContent) FetchContent_Declare( sensors_clone GIT_REPOSITORY https://github.com/gazebosim/gz-sensors - GIT_TAG gz-sensors8 + GIT_TAG gz-sensors${GZ_SENSORS_VER} ) FetchContent_Populate(sensors_clone) add_subdirectory(${sensors_clone_SOURCE_DIR}/examples/custom_sensor ${sensors_clone_BINARY_DIR}) add_library(${PROJECT_NAME} SHARED ${PROJECT_NAME}.cc) target_link_libraries(${PROJECT_NAME} - PRIVATE gz-plugin${GZ_PLUGIN_VER}::gz-plugin${GZ_PLUGIN_VER} - PRIVATE gz-sim${GZ_SIM_VER}::gz-sim${GZ_SIM_VER} - PRIVATE gz-sensors${GZ_SENSORS_VER}::gz-sensors${GZ_SENSORS_VER} - PRIVATE odometer + PRIVATE + gz-plugin${GZ_PLUGIN_VER}::gz-plugin${GZ_PLUGIN_VER} + gz-sim${GZ_SIM_VER}::gz-sim${GZ_SIM_VER} + gz-sensors${GZ_SENSORS_VER}::gz-sensors${GZ_SENSORS_VER} + odometer ) target_include_directories(${PROJECT_NAME} PUBLIC ${sensors_clone_SOURCE_DIR}/examples/custom_sensor) diff --git a/examples/plugin/custom_sensor_system/README.md b/examples/plugin/custom_sensor_system/README.md index 47700bc7fe..7d47ddb04b 100644 --- a/examples/plugin/custom_sensor_system/README.md +++ b/examples/plugin/custom_sensor_system/README.md @@ -10,7 +10,7 @@ It uses the odometer created on this example: From the root of the `gz-sim` repository, do the following to build the example: ~~~ -cd examples/plugins/custom_sensor_system +cd examples/plugin/custom_sensor_system mkdir build cd build cmake .. @@ -27,7 +27,7 @@ the `odometer.sdf` file that's going to be loaded. Before starting Gazebo, we must make sure it can find the plugin by doing: ~~~ -cd examples/plugins/custom_sensor_system +cd examples/plugin/custom_sensor_system export GZ_SIM_SYSTEM_PLUGIN_PATH=`pwd`/build ~~~ diff --git a/examples/plugin/gui_system_plugin/CMakeLists.txt b/examples/plugin/gui_system_plugin/CMakeLists.txt index 3a682f11ff..d2da6a83d9 100644 --- a/examples/plugin/gui_system_plugin/CMakeLists.txt +++ b/examples/plugin/gui_system_plugin/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.10.2 FATAL_ERROR) +cmake_minimum_required(VERSION 3.22.1 FATAL_ERROR) if(POLICY CMP0100) cmake_policy(SET CMP0100 NEW) @@ -8,7 +8,7 @@ project(GuiSystemPlugin) set(CMAKE_AUTOMOC ON) -find_package(gz-sim8 REQUIRED COMPONENTS gui) +find_package(gz-sim9 REQUIRED COMPONENTS gui) QT5_ADD_RESOURCES(resources_RCC ${PROJECT_NAME}.qrc) @@ -17,5 +17,5 @@ add_library(${PROJECT_NAME} SHARED ${resources_RCC} ) target_link_libraries(${PROJECT_NAME} - PRIVATE gz-sim8::gui + PRIVATE gz-sim9::gui ) diff --git a/examples/plugin/gui_system_plugin/README.md b/examples/plugin/gui_system_plugin/README.md index ac8e21d9da..e85a0bb769 100644 --- a/examples/plugin/gui_system_plugin/README.md +++ b/examples/plugin/gui_system_plugin/README.md @@ -2,10 +2,10 @@ This example shows how to create a GUI system plugin. -Gazebo supports any kind of Gazebo GUI plugin -(`gz::gui::Plugin`). Gazebo GUI plugins are a special type of Gazebo -GUI plugin which also have access to entity and component updates coming from -the server. +Gazebo supports any kind of GUI plugin +(`gz-gui` library: `gz::gui::Plugin`). However, GuiSystem plugins +(`gz-sim` library: `gz::sim::GuiSystem`) are a special type of GUI plugin, +which also have access to entity and component updates coming from the server. See `GuiSystemPluginPlugin.hh` for more information. diff --git a/examples/plugin/hello_world/CMakeLists.txt b/examples/plugin/hello_world/CMakeLists.txt index 135d6288ce..d1a6adc2fd 100644 --- a/examples/plugin/hello_world/CMakeLists.txt +++ b/examples/plugin/hello_world/CMakeLists.txt @@ -1,14 +1,14 @@ -cmake_minimum_required(VERSION 3.10.2 FATAL_ERROR) +cmake_minimum_required(VERSION 3.22.1 FATAL_ERROR) -find_package(gz-cmake3 REQUIRED) +find_package(gz-cmake4 REQUIRED) project(Hello_world) -gz_find_package(gz-plugin2 REQUIRED COMPONENTS register) -set(GZ_PLUGIN_VER ${gz-plugin2_VERSION_MAJOR}) +gz_find_package(gz-plugin3 REQUIRED COMPONENTS register) +set(GZ_PLUGIN_VER ${gz-plugin3_VERSION_MAJOR}) -gz_find_package(gz-sim8 REQUIRED) -set(GZ_SIM_VER ${gz-sim8_VERSION_MAJOR}) +gz_find_package(gz-sim9 REQUIRED) +set(GZ_SIM_VER ${gz-sim9_VERSION_MAJOR}) add_library(HelloWorld SHARED HelloWorld.cc) set_property(TARGET HelloWorld PROPERTY CXX_STANDARD 17) diff --git a/examples/plugin/priority_printer_plugin/CMakeLists.txt b/examples/plugin/priority_printer_plugin/CMakeLists.txt new file mode 100644 index 0000000000..e5497abb73 --- /dev/null +++ b/examples/plugin/priority_printer_plugin/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.22.1 FATAL_ERROR) + +find_package(gz-cmake4 REQUIRED) + +project(Priority_printer) + +gz_find_package(gz-plugin3 REQUIRED COMPONENTS register) +set(GZ_PLUGIN_VER ${gz-plugin3_VERSION_MAJOR}) + +gz_find_package(gz-sim9 REQUIRED) +set(GZ_SIM_VER ${gz-sim9_VERSION_MAJOR}) + +add_library(PriorityPrinter SHARED PriorityPrinter.cc) +set_property(TARGET PriorityPrinter PROPERTY CXX_STANDARD 17) +target_link_libraries(PriorityPrinter + PRIVATE gz-plugin${GZ_PLUGIN_VER}::gz-plugin${GZ_PLUGIN_VER} + PRIVATE gz-sim${GZ_SIM_VER}::gz-sim${GZ_SIM_VER}) diff --git a/examples/plugin/priority_printer_plugin/PriorityPrinter.cc b/examples/plugin/priority_printer_plugin/PriorityPrinter.cc new file mode 100644 index 0000000000..164596d968 --- /dev/null +++ b/examples/plugin/priority_printer_plugin/PriorityPrinter.cc @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2024 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +// We'll use a string and the gzmsg command below for a brief example. +// Remove these includes if your plugin doesn't need them. +#include +#include +#include + +// This header is required to register plugins. It's good practice to place it +// in the cc file, like it's done here. +#include + +// Don't forget to include the plugin's header. +#include "PriorityPrinter.hh" + +// This is required to register the plugin. Make sure the interfaces match +// what's in the header. +GZ_ADD_PLUGIN( + priority_printer::PriorityPrinter, + gz::sim::System, + priority_printer::PriorityPrinter::ISystemConfigure, + priority_printer::PriorityPrinter::ISystemPreUpdate, + priority_printer::PriorityPrinter::ISystemUpdate) + +using namespace priority_printer; + +void PriorityPrinter::Configure( + const gz::sim::Entity &_entity, + const std::shared_ptr &_sdf, + gz::sim::EntityComponentManager &_ecm, + gz::sim::EventManager &_eventMgr) +{ + // Parse priority value as a string for printing + const std::string priorityElementName {gz::sim::System::kPriorityElementName}; + if (_sdf && _sdf->HasElement(priorityElementName)) + { + this->systemPriority = _sdf->Get(priorityElementName); + } + + const std::string labelElementName {"label"}; + if (_sdf && _sdf->HasElement(labelElementName)) + { + this->systemLabel = _sdf->Get(labelElementName); + } +} + +void PriorityPrinter::PreUpdate(const gz::sim::UpdateInfo &_info, + gz::sim::EntityComponentManager &/*_ecm*/) +{ + gzmsg << "PreUpdate: " + << "Iteration " << _info.iterations + << ", system priority " << this->systemPriority + << ", system label " << this->systemLabel + << '\n'; +} + +void PriorityPrinter::Update(const gz::sim::UpdateInfo &_info, + gz::sim::EntityComponentManager &/*_ecm*/) +{ + gzmsg << "Update: " + << "Iteration " << _info.iterations + << ", system priority " << this->systemPriority + << ", system label " << this->systemLabel + << '\n'; +} diff --git a/examples/plugin/priority_printer_plugin/PriorityPrinter.hh b/examples/plugin/priority_printer_plugin/PriorityPrinter.hh new file mode 100644 index 0000000000..741c2f596c --- /dev/null +++ b/examples/plugin/priority_printer_plugin/PriorityPrinter.hh @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2024 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef EXAMPLE_PLUGIN_PRIORITYPRINTER_HH_ +#define EXAMPLE_PLUGIN_PRIORITYPRINTER_HH_ + +#include +#include +#include + +namespace priority_printer +{ + // This plugin prints the number of elapsed simulation iterations, + // this system's priority value from the XML configuration, + // and a custom label from the XML configuration during the Update callback. + class PriorityPrinter: + public gz::sim::System, + public gz::sim::ISystemConfigure, + public gz::sim::ISystemPreUpdate, + public gz::sim::ISystemUpdate + { + public: void Configure(const gz::sim::Entity &_entity, + const std::shared_ptr &_sdf, + gz::sim::EntityComponentManager &_ecm, + gz::sim::EventManager &_eventMgr) override; + + public: void PreUpdate(const gz::sim::UpdateInfo &_info, + gz::sim::EntityComponentManager &_ecm) override; + + public: void Update(const gz::sim::UpdateInfo &_info, + gz::sim::EntityComponentManager &_ecm) override; + + public: std::string systemPriority{"unset"}; + public: std::string systemLabel{"unset"}; + }; +} +#endif diff --git a/examples/plugin/priority_printer_plugin/README.md b/examples/plugin/priority_printer_plugin/README.md new file mode 100644 index 0000000000..670c269979 --- /dev/null +++ b/examples/plugin/priority_printer_plugin/README.md @@ -0,0 +1,128 @@ +# Priority Printer + +This example illustrates how to control the order of execution of System +PreUpdate and Update callbacks. As documented in +[gz/sim/System.hh](https://github.com/gazebosim/gz-sim/tree/main/include/gz/sim/System.hh), +the PreUpdate and Update phases are executed sequentially in the same +thread, and the order of execution of these phases can be +controlled by specifying a signed integer priority value for the System +in its XML configuration. The default priority value is zero, and +smaller values are executed earlier. Systems with the same priority +value are executed in the order in which they are loaded. + +## Build + +From the root of the `gz-sim` repository, do the following to build the example: + +~~~ +cd gz-sim/examples/plugins/priority_printer +mkdir build +cd build +cmake .. +make +~~~ + +This will generate the `PriorityPrinter` library under `build`. + +## Run + +Multiple instances of the `PriorityPrinter` plugin are added to the +[priority\_printer\_plugin.sdf](priority_printer_plugin.sdf) world file +with various priority values and unique labels corresponding to the order +in which the plugins are specified ("first" for the first plugin and so on). +Without priority values, the systems would be executed in the order they are +specified in XML ("first", then "second", etc.). +With the priority values specified, the systems with smallest integer priority +values are executed first. For systems with the same priority value, the +system that is specified earlier in the XML file will be executed first. + +Before starting Gazebo, we must make sure it can find the plugin by doing: + +~~~ +cd gz-sim/examples/plugins/priority_printer +export GZ_SIM_SYSTEM_PLUGIN_PATH=`pwd`/build +~~~ + +Then load the example world and run for 5 iterations: + + gz sim -v 3 priority_printer_plugin.sdf -s -r --iterations 5 + +You should see green messages on the terminal like those given below. +Note that the system with priority `-100` was executed first, despite being +the fifth system in the XML ordering. There are two instances of systems with +the same priority value: the fourth and sixth systems with priority 0 (with +"unset" defaulting to 0) and the first and seventh systems with priority 100. +In each case, the system declared earlier in XML executed first. + +``` +[Msg] PreUpdate: Iteration 1, system priority -100, system label fifth +[Msg] PreUpdate: Iteration 1, system priority -10, system label third +[Msg] PreUpdate: Iteration 1, system priority unset, system label fourth +[Msg] PreUpdate: Iteration 1, system priority 0, system label sixth +[Msg] PreUpdate: Iteration 1, system priority 10, system label second +[Msg] PreUpdate: Iteration 1, system priority 100, system label first +[Msg] PreUpdate: Iteration 1, system priority 100, system label seventh +[Msg] Update: Iteration 1, system priority -100, system label fifth +[Msg] Update: Iteration 1, system priority -10, system label third +[Msg] Update: Iteration 1, system priority unset, system label fourth +[Msg] Update: Iteration 1, system priority 0, system label sixth +[Msg] Update: Iteration 1, system priority 10, system label second +[Msg] Update: Iteration 1, system priority 100, system label first +[Msg] Update: Iteration 1, system priority 100, system label seventh +[Msg] PreUpdate: Iteration 2, system priority -100, system label fifth +[Msg] PreUpdate: Iteration 2, system priority -10, system label third +[Msg] PreUpdate: Iteration 2, system priority unset, system label fourth +[Msg] PreUpdate: Iteration 2, system priority 0, system label sixth +[Msg] PreUpdate: Iteration 2, system priority 10, system label second +[Msg] PreUpdate: Iteration 2, system priority 100, system label first +[Msg] PreUpdate: Iteration 2, system priority 100, system label seventh +[Msg] Update: Iteration 2, system priority -100, system label fifth +[Msg] Update: Iteration 2, system priority -10, system label third +[Msg] Update: Iteration 2, system priority unset, system label fourth +[Msg] Update: Iteration 2, system priority 0, system label sixth +[Msg] Update: Iteration 2, system priority 10, system label second +[Msg] Update: Iteration 2, system priority 100, system label first +[Msg] Update: Iteration 2, system priority 100, system label seventh +[Msg] PreUpdate: Iteration 3, system priority -100, system label fifth +[Msg] PreUpdate: Iteration 3, system priority -10, system label third +[Msg] PreUpdate: Iteration 3, system priority unset, system label fourth +[Msg] PreUpdate: Iteration 3, system priority 0, system label sixth +[Msg] PreUpdate: Iteration 3, system priority 10, system label second +[Msg] PreUpdate: Iteration 3, system priority 100, system label first +[Msg] PreUpdate: Iteration 3, system priority 100, system label seventh +[Msg] Update: Iteration 3, system priority -100, system label fifth +[Msg] Update: Iteration 3, system priority -10, system label third +[Msg] Update: Iteration 3, system priority unset, system label fourth +[Msg] Update: Iteration 3, system priority 0, system label sixth +[Msg] Update: Iteration 3, system priority 10, system label second +[Msg] Update: Iteration 3, system priority 100, system label first +[Msg] Update: Iteration 3, system priority 100, system label seventh +[Msg] PreUpdate: Iteration 4, system priority -100, system label fifth +[Msg] PreUpdate: Iteration 4, system priority -10, system label third +[Msg] PreUpdate: Iteration 4, system priority unset, system label fourth +[Msg] PreUpdate: Iteration 4, system priority 0, system label sixth +[Msg] PreUpdate: Iteration 4, system priority 10, system label second +[Msg] PreUpdate: Iteration 4, system priority 100, system label first +[Msg] PreUpdate: Iteration 4, system priority 100, system label seventh +[Msg] Update: Iteration 4, system priority -100, system label fifth +[Msg] Update: Iteration 4, system priority -10, system label third +[Msg] Update: Iteration 4, system priority unset, system label fourth +[Msg] Update: Iteration 4, system priority 0, system label sixth +[Msg] Update: Iteration 4, system priority 10, system label second +[Msg] Update: Iteration 4, system priority 100, system label first +[Msg] Update: Iteration 4, system priority 100, system label seventh +[Msg] PreUpdate: Iteration 5, system priority -100, system label fifth +[Msg] PreUpdate: Iteration 5, system priority -10, system label third +[Msg] PreUpdate: Iteration 5, system priority unset, system label fourth +[Msg] PreUpdate: Iteration 5, system priority 0, system label sixth +[Msg] PreUpdate: Iteration 5, system priority 10, system label second +[Msg] PreUpdate: Iteration 5, system priority 100, system label first +[Msg] PreUpdate: Iteration 5, system priority 100, system label seventh +[Msg] Update: Iteration 5, system priority -100, system label fifth +[Msg] Update: Iteration 5, system priority -10, system label third +[Msg] Update: Iteration 5, system priority unset, system label fourth +[Msg] Update: Iteration 5, system priority 0, system label sixth +[Msg] Update: Iteration 5, system priority 10, system label second +[Msg] Update: Iteration 5, system priority 100, system label first +[Msg] Update: Iteration 5, system priority 100, system label seventh +``` diff --git a/examples/plugin/priority_printer_plugin/priority_printer_plugin.sdf b/examples/plugin/priority_printer_plugin/priority_printer_plugin.sdf new file mode 100644 index 0000000000..e60d75a497 --- /dev/null +++ b/examples/plugin/priority_printer_plugin/priority_printer_plugin.sdf @@ -0,0 +1,36 @@ + + + + + + 100 + + + + 10 + + + + -10 + + + + + + + -100 + + + + 0 + + + + 100 + + + + diff --git a/examples/plugin/rendering_plugins/CMakeLists.txt b/examples/plugin/rendering_plugins/CMakeLists.txt index 8668676cf0..983cd746a1 100644 --- a/examples/plugin/rendering_plugins/CMakeLists.txt +++ b/examples/plugin/rendering_plugins/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.10.2 FATAL_ERROR) +cmake_minimum_required(VERSION 3.22.1 FATAL_ERROR) if(POLICY CMP0100) cmake_policy(SET CMP0100 NEW) @@ -7,14 +7,14 @@ endif() project(RenderingPlugins) # Common to both plugins -find_package(gz-rendering8 REQUIRED) +find_package(gz-rendering9 REQUIRED) # GUI plugin set(GUI_PLUGIN RenderingGuiPlugin) set(CMAKE_AUTOMOC ON) -find_package(gz-gui8 REQUIRED) +find_package(gz-gui9 REQUIRED) QT5_ADD_RESOURCES(resources_RCC ${GUI_PLUGIN}.qrc) @@ -24,21 +24,21 @@ add_library(${GUI_PLUGIN} SHARED ) target_link_libraries(${GUI_PLUGIN} PRIVATE - gz-gui8::gz-gui8 - gz-rendering8::gz-rendering8 + gz-gui9::gz-gui9 + gz-rendering9::gz-rendering9 ) # Server plugin set(SERVER_PLUGIN RenderingServerPlugin) -find_package(gz-plugin2 REQUIRED COMPONENTS register) -find_package(gz-sim8 REQUIRED) +find_package(gz-plugin3 REQUIRED COMPONENTS register) +find_package(gz-sim9 REQUIRED) add_library(${SERVER_PLUGIN} SHARED ${SERVER_PLUGIN}.cc) set_property(TARGET ${SERVER_PLUGIN} PROPERTY CXX_STANDARD 17) target_link_libraries(${SERVER_PLUGIN} PRIVATE - gz-plugin2::gz-plugin2 - gz-sim8::gz-sim8 - gz-rendering8::gz-rendering8 + gz-plugin3::gz-plugin3 + gz-sim9::gz-sim9 + gz-rendering9::gz-rendering9 ) diff --git a/examples/plugin/reset_plugin/CMakeLists.txt b/examples/plugin/reset_plugin/CMakeLists.txt index 824d84176a..3a9e97329a 100644 --- a/examples/plugin/reset_plugin/CMakeLists.txt +++ b/examples/plugin/reset_plugin/CMakeLists.txt @@ -1,12 +1,12 @@ -cmake_minimum_required(VERSION 3.10.2 FATAL_ERROR) +cmake_minimum_required(VERSION 3.22.1 FATAL_ERROR) project(ResetPlugins) -find_package(gz-plugin2 REQUIRED COMPONENTS register) -set(GZ_PLUGIN_VER ${gz-plugin2_VERSION_MAJOR}) +find_package(gz-plugin3 REQUIRED COMPONENTS register) +set(GZ_PLUGIN_VER ${gz-plugin3_VERSION_MAJOR}) -find_package(gz-sim8 REQUIRED) -set(GZ_SIM_VER ${gz-sim8_VERSION_MAJOR}) +find_package(gz-sim9 REQUIRED) +set(GZ_SIM_VER ${gz-sim9_VERSION_MAJOR}) add_library(JointPositionRandomizer SHARED JointPositionRandomizer.cc) target_link_libraries(JointPositionRandomizer diff --git a/examples/plugin/system_plugin/CMakeLists.txt b/examples/plugin/system_plugin/CMakeLists.txt index 0a0e5ba789..1a651f6bca 100644 --- a/examples/plugin/system_plugin/CMakeLists.txt +++ b/examples/plugin/system_plugin/CMakeLists.txt @@ -1,15 +1,15 @@ -cmake_minimum_required(VERSION 3.10.2 FATAL_ERROR) +cmake_minimum_required(VERSION 3.22.1 FATAL_ERROR) -find_package(gz-cmake3 REQUIRED) +find_package(gz-cmake4 REQUIRED) project(SampleSystem) -find_package(gz-plugin2 REQUIRED COMPONENTS register) -set(GZ_PLUGIN_VER ${gz-plugin2_VERSION_MAJOR}) +find_package(gz-plugin3 REQUIRED COMPONENTS register) +set(GZ_PLUGIN_VER ${gz-plugin3_VERSION_MAJOR}) -find_package(gz-sim8 REQUIRED) +find_package(gz-sim9 REQUIRED) add_library(SampleSystem SHARED SampleSystem.cc SampleSystem2.cc) set_property(TARGET SampleSystem PROPERTY CXX_STANDARD 17) target_link_libraries(SampleSystem PRIVATE gz-plugin${GZ_PLUGIN_VER}::gz-plugin${GZ_PLUGIN_VER} - PRIVATE gz-sim8::gz-sim8) + PRIVATE gz-sim9::gz-sim9) diff --git a/examples/scripts/python_api/systems/test_system.py b/examples/scripts/python_api/systems/test_system.py index b5d03347b0..a10aef1fec 100644 --- a/examples/scripts/python_api/systems/test_system.py +++ b/examples/scripts/python_api/systems/test_system.py @@ -12,8 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -from gz.math7 import Vector3d -from gz.sim8 import Model, Link +from gz.math8 import Vector3d +from gz.sim9 import Model, Link import random diff --git a/examples/scripts/python_api/testFixture.py b/examples/scripts/python_api/testFixture.py index 5be3afa6dc..820d281dd3 100755 --- a/examples/scripts/python_api/testFixture.py +++ b/examples/scripts/python_api/testFixture.py @@ -24,9 +24,9 @@ import os -from gz.common5 import set_verbosity -from gz.sim8 import TestFixture, World, world_entity -from gz.math7 import Vector3d +from gz.common6 import set_verbosity +from gz.sim9 import TestFixture, World, world_entity +from gz.math8 import Vector3d set_verbosity(4) diff --git a/examples/standalone/acoustic_comms_demo/CMakeLists.txt b/examples/standalone/acoustic_comms_demo/CMakeLists.txt index 2129e946e1..73a7dddde9 100644 --- a/examples/standalone/acoustic_comms_demo/CMakeLists.txt +++ b/examples/standalone/acoustic_comms_demo/CMakeLists.txt @@ -1,12 +1,12 @@ -cmake_minimum_required(VERSION 3.10.2 FATAL_ERROR) +cmake_minimum_required(VERSION 3.22.1 FATAL_ERROR) project(gz-sim-acoustic-comms-demo) -find_package(gz-transport13 QUIET REQUIRED OPTIONAL_COMPONENTS log) -set(GZ_TRANSPORT_VER ${gz-transport13_VERSION_MAJOR}) +find_package(gz-transport14 QUIET REQUIRED OPTIONAL_COMPONENTS log) +set(GZ_TRANSPORT_VER ${gz-transport14_VERSION_MAJOR}) -find_package(gz-sim8 REQUIRED) -set(GZ_SIM_VER ${gz-sim8_VERSION_MAJOR}) +find_package(gz-sim9 REQUIRED) +set(GZ_SIM_VER ${gz-sim9_VERSION_MAJOR}) add_executable(acoustic_comms_demo acoustic_comms_demo.cc) target_link_libraries(acoustic_comms_demo diff --git a/examples/standalone/comms/CMakeLists.txt b/examples/standalone/comms/CMakeLists.txt index 19d11f99f6..a1c6e7c55b 100644 --- a/examples/standalone/comms/CMakeLists.txt +++ b/examples/standalone/comms/CMakeLists.txt @@ -1,9 +1,9 @@ -cmake_minimum_required(VERSION 3.10.2 FATAL_ERROR) +cmake_minimum_required(VERSION 3.22.1 FATAL_ERROR) project(gz-sim-comms) -find_package(gz-transport13 QUIET REQUIRED) -set(GZ_TRANSPORT_VER ${gz-transport13_VERSION_MAJOR}) +find_package(gz-transport14 QUIET REQUIRED) +set(GZ_TRANSPORT_VER ${gz-transport14_VERSION_MAJOR}) add_executable(publisher publisher.cc) target_link_libraries(publisher diff --git a/examples/standalone/custom_server/CMakeLists.txt b/examples/standalone/custom_server/CMakeLists.txt index cf1e917e89..2fbbc64fdb 100644 --- a/examples/standalone/custom_server/CMakeLists.txt +++ b/examples/standalone/custom_server/CMakeLists.txt @@ -1,10 +1,10 @@ -cmake_minimum_required(VERSION 3.10.2 FATAL_ERROR) +cmake_minimum_required(VERSION 3.22.1 FATAL_ERROR) project(gz-sim-custom-server) if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") - find_package(gz-sim8 REQUIRED) - set(GZ_SIM_VER ${gz-sim8_VERSION_MAJOR}) + find_package(gz-sim9 REQUIRED) + set(GZ_SIM_VER ${gz-sim9_VERSION_MAJOR}) add_executable(custom_server custom_server.cc) target_link_libraries(custom_server diff --git a/examples/standalone/each_performance/CMakeLists.txt b/examples/standalone/each_performance/CMakeLists.txt index 6f756f12e5..98b94029eb 100644 --- a/examples/standalone/each_performance/CMakeLists.txt +++ b/examples/standalone/each_performance/CMakeLists.txt @@ -1,9 +1,9 @@ -cmake_minimum_required(VERSION 3.10.2 FATAL_ERROR) +cmake_minimum_required(VERSION 3.22.1 FATAL_ERROR) project(gz-sim-each-performance) -find_package(gz-sim8 QUIET REQUIRED) +find_package(gz-sim9 QUIET REQUIRED) add_executable(each each.cc) target_link_libraries(each - gz-sim8::core) + gz-sim9::core) diff --git a/examples/standalone/entity_creation/CMakeLists.txt b/examples/standalone/entity_creation/CMakeLists.txt index d41479eafa..445f7eab69 100644 --- a/examples/standalone/entity_creation/CMakeLists.txt +++ b/examples/standalone/entity_creation/CMakeLists.txt @@ -1,9 +1,9 @@ -cmake_minimum_required(VERSION 3.10.2 FATAL_ERROR) +cmake_minimum_required(VERSION 3.22.1 FATAL_ERROR) project(gz-sim-entity-creation) -find_package(gz-transport13 QUIET REQUIRED OPTIONAL_COMPONENTS log) -set(GZ_TRANSPORT_VER ${gz-transport13_VERSION_MAJOR}) +find_package(gz-transport14 QUIET REQUIRED OPTIONAL_COMPONENTS log) +set(GZ_TRANSPORT_VER ${gz-transport14_VERSION_MAJOR}) add_executable(entity_creation entity_creation.cc) target_link_libraries(entity_creation diff --git a/examples/standalone/external_ecm/CMakeLists.txt b/examples/standalone/external_ecm/CMakeLists.txt index d06850d284..24f8d78594 100644 --- a/examples/standalone/external_ecm/CMakeLists.txt +++ b/examples/standalone/external_ecm/CMakeLists.txt @@ -1,9 +1,9 @@ -cmake_minimum_required(VERSION 3.10.2 FATAL_ERROR) +cmake_minimum_required(VERSION 3.22.1 FATAL_ERROR) project(gz-sim-external-ecm) -find_package(gz-sim8 REQUIRED) +find_package(gz-sim9 REQUIRED) add_executable(external_ecm external_ecm.cc) target_link_libraries(external_ecm - gz-sim8::core) + gz-sim9::core) diff --git a/examples/standalone/gtest_setup/CMakeLists.txt b/examples/standalone/gtest_setup/CMakeLists.txt index 376497eeaf..99a24e3fb7 100644 --- a/examples/standalone/gtest_setup/CMakeLists.txt +++ b/examples/standalone/gtest_setup/CMakeLists.txt @@ -1,22 +1,23 @@ -cmake_minimum_required(VERSION 3.11.0 FATAL_ERROR) +cmake_minimum_required(VERSION 3.22.1 FATAL_ERROR) project(GTestSetup) # Find Gazebo -find_package(gz-sim8 REQUIRED) -set(GZ_SIM_VER ${gz-sim8_VERSION_MAJOR}) +find_package(gz-sim9 REQUIRED) +set(GZ_SIM_VER ${gz-sim9_VERSION_MAJOR}) # Fetch and configure GTest include(FetchContent) FetchContent_Declare( googletest - URL https://github.com/google/googletest/archive/609281088cfefc76f9d0ce82e1ff6c30cc3591e5.zip + DOWNLOAD_EXTRACT_TIMESTAMP TRUE + # Version 1.14. Use commit hash to prevent tag relocation + URL https://github.com/google/googletest/archive/f8d7d77c06936315286eb55f8de22cd23c188571.zip ) set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) FetchContent_MakeAvailable(googletest) enable_testing() -include(Dart) # Generate tests foreach(TEST_TARGET diff --git a/examples/standalone/joy_to_twist/CMakeLists.txt b/examples/standalone/joy_to_twist/CMakeLists.txt index 2344583176..c2ea401431 100644 --- a/examples/standalone/joy_to_twist/CMakeLists.txt +++ b/examples/standalone/joy_to_twist/CMakeLists.txt @@ -1,12 +1,12 @@ -cmake_minimum_required(VERSION 3.10.2 FATAL_ERROR) +cmake_minimum_required(VERSION 3.22.1 FATAL_ERROR) project(gz-sim-joy-to-twist) -find_package(gz-transport13 QUIET REQUIRED OPTIONAL_COMPONENTS log) -set(GZ_TRANSPORT_VER ${gz-transport13_VERSION_MAJOR}) +find_package(gz-transport14 QUIET REQUIRED OPTIONAL_COMPONENTS log) +set(GZ_TRANSPORT_VER ${gz-transport14_VERSION_MAJOR}) -find_package(sdformat14 REQUIRED) -set(SDF_VER ${sdformat14_VERSION_MAJOR}) +find_package(sdformat15 REQUIRED) +set(SDF_VER ${sdformat15_VERSION_MAJOR}) add_executable(joy_to_twist joy_to_twist.cc) target_link_libraries(joy_to_twist diff --git a/examples/standalone/joystick/CMakeLists.txt b/examples/standalone/joystick/CMakeLists.txt index 2a4f593633..2a066a8e89 100644 --- a/examples/standalone/joystick/CMakeLists.txt +++ b/examples/standalone/joystick/CMakeLists.txt @@ -1,14 +1,14 @@ -cmake_minimum_required(VERSION 3.10.2 FATAL_ERROR) +cmake_minimum_required(VERSION 3.22.1 FATAL_ERROR) # joystick currently works only on linux project(gz-sim-joystick) if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") - find_package(gz-transport13 QUIET REQUIRED OPTIONAL_COMPONENTS log) - set(GZ_TRANSPORT_VER ${gz-transport13_VERSION_MAJOR}) + find_package(gz-transport14 QUIET REQUIRED OPTIONAL_COMPONENTS log) + set(GZ_TRANSPORT_VER ${gz-transport14_VERSION_MAJOR}) - find_package(sdformat14 REQUIRED) - set(SDF_VER ${sdformat14_VERSION_MAJOR}) + find_package(sdformat15 REQUIRED) + set(SDF_VER ${sdformat15_VERSION_MAJOR}) add_executable(joystick joystick.cc) target_link_libraries(joystick diff --git a/examples/standalone/keyboard/CMakeLists.txt b/examples/standalone/keyboard/CMakeLists.txt index 04227710c7..9d96cb7ef4 100644 --- a/examples/standalone/keyboard/CMakeLists.txt +++ b/examples/standalone/keyboard/CMakeLists.txt @@ -1,19 +1,19 @@ -cmake_minimum_required(VERSION 3.10.2 FATAL_ERROR) +cmake_minimum_required(VERSION 3.22.1 FATAL_ERROR) project(gz-sim-keyboard) if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") - find_package(gz-transport13 QUIET REQUIRED OPTIONAL_COMPONENTS log) - set(GZ_TRANSPORT_VER ${gz-transport13_VERSION_MAJOR}) + find_package(gz-transport14 QUIET REQUIRED OPTIONAL_COMPONENTS log) + set(GZ_TRANSPORT_VER ${gz-transport14_VERSION_MAJOR}) - find_package(sdformat14 REQUIRED) - set(SDF_VER ${sdformat14_VERSION_MAJOR}) + find_package(sdformat15 REQUIRED) + set(SDF_VER ${sdformat15_VERSION_MAJOR}) - find_package(gz-msgs10 REQUIRED) - set(GZ_MSGS_VER ${gz-msgs10_VERSION_MAJOR}) + find_package(gz-msgs11 REQUIRED) + set(GZ_MSGS_VER ${gz-msgs11_VERSION_MAJOR}) - find_package(gz-common5 REQUIRED) - set(GZ_COMMON_VER ${gz-common5_VERSION_MAJOR}) + find_package(gz-common6 REQUIRED) + set(GZ_COMMON_VER ${gz-common6_VERSION_MAJOR}) add_executable(keyboard keyboard.cc) target_link_libraries(keyboard diff --git a/examples/standalone/light_control/CMakeLists.txt b/examples/standalone/light_control/CMakeLists.txt index 0f3de2e3e7..9f3ee16e4c 100644 --- a/examples/standalone/light_control/CMakeLists.txt +++ b/examples/standalone/light_control/CMakeLists.txt @@ -1,12 +1,12 @@ -cmake_minimum_required(VERSION 3.10.2 FATAL_ERROR) +cmake_minimum_required(VERSION 3.22.1 FATAL_ERROR) project(gz-sim-light-control) -find_package(gz-transport13 QUIET REQUIRED OPTIONAL_COMPONENTS log) -set(GZ_TRANSPORT_VER ${gz-transport13_VERSION_MAJOR}) +find_package(gz-transport14 QUIET REQUIRED OPTIONAL_COMPONENTS log) +set(GZ_TRANSPORT_VER ${gz-transport14_VERSION_MAJOR}) -find_package(gz-sim8 REQUIRED) -set(GZ_SIM_VER ${gz-sim8_VERSION_MAJOR}) +find_package(gz-sim9 REQUIRED) +set(GZ_SIM_VER ${gz-sim9_VERSION_MAJOR}) add_executable(light_control light_control.cc) target_link_libraries(light_control diff --git a/examples/standalone/lrauv_control/CMakeLists.txt b/examples/standalone/lrauv_control/CMakeLists.txt index f0cbc94a03..6c075de8dd 100644 --- a/examples/standalone/lrauv_control/CMakeLists.txt +++ b/examples/standalone/lrauv_control/CMakeLists.txt @@ -1,12 +1,12 @@ -cmake_minimum_required(VERSION 3.10.2 FATAL_ERROR) +cmake_minimum_required(VERSION 3.22.1 FATAL_ERROR) project(gz-sim-lrauv-control) -find_package(gz-transport13 QUIET REQUIRED OPTIONAL_COMPONENTS log) -set(GZ_TRANSPORT_VER ${gz-transport13_VERSION_MAJOR}) +find_package(gz-transport14 QUIET REQUIRED OPTIONAL_COMPONENTS log) +set(GZ_TRANSPORT_VER ${gz-transport14_VERSION_MAJOR}) -find_package(gz-sim8 REQUIRED) -set(GZ_SIM_VER ${gz-sim8_VERSION_MAJOR}) +find_package(gz-sim9 REQUIRED) +set(GZ_SIM_VER ${gz-sim9_VERSION_MAJOR}) add_executable(lrauv_control lrauv_control.cc) target_link_libraries(lrauv_control diff --git a/examples/standalone/lrauv_control/lrauv_control.py b/examples/standalone/lrauv_control/lrauv_control.py index 8f7ad2cf21..56eb0088aa 100644 --- a/examples/standalone/lrauv_control/lrauv_control.py +++ b/examples/standalone/lrauv_control/lrauv_control.py @@ -26,10 +26,10 @@ # $ python3 lrauv_control.py 0.5 0.78 0.174 # -from gz.msgs10.double_pb2 import Double -from gz.msgs10.odometry_pb2 import Odometry -from gz.math7 import Quaterniond, Vector3d -from gz.transport13 import Node +from gz.msgs11.double_pb2 import Double +from gz.msgs11.odometry_pb2 import Odometry +from gz.math8 import Quaterniond, Vector3d +from gz.transport14 import Node from threading import Lock diff --git a/examples/standalone/marker/CMakeLists.txt b/examples/standalone/marker/CMakeLists.txt index bbca4e481d..f3bd3b655b 100644 --- a/examples/standalone/marker/CMakeLists.txt +++ b/examples/standalone/marker/CMakeLists.txt @@ -1,16 +1,16 @@ -cmake_minimum_required(VERSION 3.10.2 FATAL_ERROR) +cmake_minimum_required(VERSION 3.22.1 FATAL_ERROR) project(gz-sim-marker) if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") - find_package(gz-transport13 QUIET REQUIRED) - set(GZ_TRANSPORT_VER ${gz-transport13_VERSION_MAJOR}) + find_package(gz-transport14 QUIET REQUIRED) + set(GZ_TRANSPORT_VER ${gz-transport14_VERSION_MAJOR}) - find_package(gz-common5 REQUIRED) - set(GZ_COMMON_VER ${gz-common5_VERSION_MAJOR}) + find_package(gz-common6 REQUIRED) + set(GZ_COMMON_VER ${gz-common6_VERSION_MAJOR}) - find_package(gz-msgs10 REQUIRED) - set(GZ_MSGS_VER ${gz-msgs10_VERSION_MAJOR}) + find_package(gz-msgs11 REQUIRED) + set(GZ_MSGS_VER ${gz-msgs11_VERSION_MAJOR}) add_executable(marker marker.cc) target_link_libraries(marker diff --git a/examples/standalone/multi_lrauv_race/CMakeLists.txt b/examples/standalone/multi_lrauv_race/CMakeLists.txt index 6bffdef142..69049f2ad0 100644 --- a/examples/standalone/multi_lrauv_race/CMakeLists.txt +++ b/examples/standalone/multi_lrauv_race/CMakeLists.txt @@ -1,12 +1,12 @@ -cmake_minimum_required(VERSION 3.10.2 FATAL_ERROR) +cmake_minimum_required(VERSION 3.22.1 FATAL_ERROR) project(gz-sim-multi-lrauv-race) -find_package(gz-transport13 QUIET REQUIRED OPTIONAL_COMPONENTS log) -set(GZ_TRANSPORT_VER ${gz-transport13_VERSION_MAJOR}) +find_package(gz-transport14 QUIET REQUIRED OPTIONAL_COMPONENTS log) +set(GZ_TRANSPORT_VER ${gz-transport14_VERSION_MAJOR}) -find_package(gz-sim8 REQUIRED) -set(GZ_SIM_VER ${gz-sim8_VERSION_MAJOR}) +find_package(gz-sim9 REQUIRED) +set(GZ_SIM_VER ${gz-sim9_VERSION_MAJOR}) add_executable(multi_lrauv_race multi_lrauv_race.cc) target_link_libraries(multi_lrauv_race diff --git a/examples/standalone/multi_lrauv_race/multi_lrauv_race.py b/examples/standalone/multi_lrauv_race/multi_lrauv_race.py index fa565feed1..aff979c158 100644 --- a/examples/standalone/multi_lrauv_race/multi_lrauv_race.py +++ b/examples/standalone/multi_lrauv_race/multi_lrauv_race.py @@ -26,8 +26,8 @@ # before for other python examples. You can use then following: # $ export PYTHONPATH=$PYTHONPATH:/install/lib/python -from gz.msgs10.double_pb2 import Double -from gz.transport13 import Node +from gz.msgs11.double_pb2 import Double +from gz.transport14 import Node import random import time diff --git a/examples/standalone/scene_requester/CMakeLists.txt b/examples/standalone/scene_requester/CMakeLists.txt index e47e8eac87..049877d3db 100644 --- a/examples/standalone/scene_requester/CMakeLists.txt +++ b/examples/standalone/scene_requester/CMakeLists.txt @@ -1,9 +1,9 @@ -cmake_minimum_required(VERSION 3.10.2 FATAL_ERROR) +cmake_minimum_required(VERSION 3.22.1 FATAL_ERROR) project(gz-sim-scene-requester) -find_package(gz-transport13 QUIET REQUIRED OPTIONAL_COMPONENTS log) -set(GZ_TRANSPORT_VER ${gz-transport13_VERSION_MAJOR}) +find_package(gz-transport14 QUIET REQUIRED OPTIONAL_COMPONENTS log) +set(GZ_TRANSPORT_VER ${gz-transport14_VERSION_MAJOR}) add_executable(scene_requester scene_requester.cc) target_link_libraries(scene_requester diff --git a/examples/worlds/actors_population.sdf b/examples/worlds/actors_population.sdf deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/examples/worlds/camera_sensor.sdf b/examples/worlds/camera_sensor.sdf index 3d967d701e..0cee711b47 100644 --- a/examples/worlds/camera_sensor.sdf +++ b/examples/worlds/camera_sensor.sdf @@ -8,23 +8,11 @@ 0.001 1.0 - - ogre2 - - - - 1.0 1.0 1.0 @@ -32,103 +20,22 @@ true + + true + - 3D View false docked - ogre2 scene 0.4 0.4 0.4 0.8 0.8 0.8 - -6 0 6 0 0.5 0 - - - - - - floating - 5 - 5 - false - - - - - false - 5 - 5 - floating - false - - - - - false - 5 - 5 - floating - false - - - - - false - 5 - 5 - floating - false - - - - - - World control - false - false - 72 - 1 - - floating - - - - - - - true - true - true - true - - - - - - - World stats - false - false - 110 - 290 - 1 - - floating - - - - - - - true - true - true - true + -3 0 3 0 0.5 0 @@ -137,20 +44,6 @@ camera - - - - - docked - - - - - - - docked - - diff --git a/examples/worlds/contact_sensor.sdf b/examples/worlds/contact_sensor.sdf index c37f7f259b..0714c34bf7 100644 --- a/examples/worlds/contact_sensor.sdf +++ b/examples/worlds/contact_sensor.sdf @@ -9,22 +9,7 @@ Run the following to print out contacts, --> - - - - - - - - + false diff --git a/examples/worlds/dem_monterey_bay.sdf b/examples/worlds/dem_monterey_bay.sdf index 7f2d170121..39539ca1f2 100644 --- a/examples/worlds/dem_monterey_bay.sdf +++ b/examples/worlds/dem_monterey_bay.sdf @@ -22,7 +22,7 @@ docked - ogre + ogre2 scene 0 0 0 0.8 0.8 0.8 diff --git a/examples/worlds/dem_moon.sdf b/examples/worlds/dem_moon.sdf index a1de71ad5e..047607d3f1 100644 --- a/examples/worlds/dem_moon.sdf +++ b/examples/worlds/dem_moon.sdf @@ -29,7 +29,7 @@ docked - ogre + ogre2 scene 0 0 0 0.8 0.8 0.8 diff --git a/examples/worlds/dem_volcano.sdf b/examples/worlds/dem_volcano.sdf index f48b2779a4..f2f766b869 100644 --- a/examples/worlds/dem_volcano.sdf +++ b/examples/worlds/dem_volcano.sdf @@ -21,7 +21,7 @@ docked - ogre + ogre2 scene 0 0 0 0.8 0.8 0.8 diff --git a/examples/worlds/dvl_world.sdf b/examples/worlds/dvl_world.sdf new file mode 100644 index 0000000000..0b599a3cb4 --- /dev/null +++ b/examples/worlds/dvl_world.sdf @@ -0,0 +1,291 @@ + + + + + + + 0.0 1.0 1.0 + + 0.0 0.7 0.8 + + false + + + + 0.001 + 1.0 + + + + + + + + + 1000 + + + + + + + + + + true + 0 0 10 0 0 0 + 1 1 1 1 + 0.5 0.5 0.5 1 + + 1000 + 0.9 + 0.01 + 0.001 + + -0.5 0.1 -0.9 + + + + + true + + + + + 0 0 1 + + 300000 300000 + + + 1.0 + + + + + + true + 0 0 -100 0 0 0 + + + + + 0 0 1 + + 300000 300000 + + + + + + 0.5 0.5 0.5 + 0.5 0.5 0.5 + + + + 0 0 1 + + 300000 300000 + + + + + + + + 0 0 -80 0 0 1.57 + https://fuel.gazebosim.org/1.0/accurrent/models/MBARI Tethys LRAUV + + + + -0.60 0 -0.16 0 0 180 + 1 + 1 + /dvl/velocity + + phased_array + + + 2 + 45 + 30 + + + 2 + 135 + 30 + + + 2 + -45 + 30 + + + 2 + -135 + 30 + + + + + best + + + 0.002 + + false + + + + 0.01 + 100. + 0.1 + + 0 0 0 0 0 -1.570796 + + + + + + + + + + + horizontal_fins_joint + 0.1 + + + + vertical_fins_joint + 0.1 + + + + tethys + 0 + propeller_joint + 0.004422 + 1000 + 0.2 + + + + + + + 1000 + 4.13 + -1.1 + 0.2 + 0.03 + 0.17 + 0 + 0.0244 + 0 1 0 + 1 0 0 + vertical_fins + 0 0 0 + + + + + 1000 + 4.13 + -1.1 + 0.2 + 0.03 + 0.17 + 0 + 0.0244 + 0 0 1 + 1 0 0 + horizontal_fins + 0 0 0 + + + + + base_link + -4.876161 + -126.324739 + -126.324739 + 0 + -33.46 + -33.46 + -6.2282 + 0 + -601.27 + 0 + -601.27 + 0 + -0.1916 + 0 + -632.698957 + 0 + -632.698957 + 0 + + + + diff --git a/examples/worlds/empty_gui.sdf b/examples/worlds/empty_gui.sdf index 0975a4f142..54df8c2e31 100644 --- a/examples/worlds/empty_gui.sdf +++ b/examples/worlds/empty_gui.sdf @@ -37,6 +37,10 @@ This example helps illustrate the interaction of the MinimalScene with other GUI + + false + + diff --git a/examples/worlds/gpu_lidar_sensor.sdf b/examples/worlds/gpu_lidar_sensor.sdf index 1e1a69cfd9..756de3eb34 100644 --- a/examples/worlds/gpu_lidar_sensor.sdf +++ b/examples/worlds/gpu_lidar_sensor.sdf @@ -22,6 +22,10 @@ filename="gz-sim-scene-broadcaster-system" name="gz::sim::systems::SceneBroadcaster"> + + true diff --git a/examples/worlds/ground_spacecraft_testbed.sdf b/examples/worlds/ground_spacecraft_testbed.sdf new file mode 100644 index 0000000000..9d061f2fdd --- /dev/null +++ b/examples/worlds/ground_spacecraft_testbed.sdf @@ -0,0 +1,77 @@ + + + + + 0 0 -9.8066 + + 0.001 + 1.0 + + + + + + + + + ogre2 + + + true + 0 0 10 0 0 0 + 0.8 0.8 0.8 1 + 0.2 0.2 0.2 1 + + 1000 + 0.9 + 0.01 + 0.001 + + -0.5 0.1 -0.9 + + + true + + + + + 0 0 1 + 100 100 + + + + + + + 0 0 1 + 100 100 + + + + 0.8 0.8 0.8 1 + 0.8 0.8 0.8 1 + 0.8 0.8 0.8 1 + + + + + + 0 0 0 0 0 0 + https://fuel.gazebosim.org/1.0/proque/models/kth_freeflyer + + + diff --git a/examples/worlds/joint_trajectory_controller.sdf b/examples/worlds/joint_trajectory_controller.sdf index 2f9508bd49..d6b62a5b8c 100644 --- a/examples/worlds/joint_trajectory_controller.sdf +++ b/examples/worlds/joint_trajectory_controller.sdf @@ -479,10 +479,10 @@ RR_velocity_control_link1 1 0 0 + + 0.02 + - - 0.02 - 0 0 0.1 0 0 0 @@ -490,10 +490,10 @@ RR_velocity_control_link2 1 0 0 + + 0.01 + - - 0.01 - + + + + + 0.001 + + 1 + + + 0 0 -9.81 + + + + + + + + + + 1.097 + + + + true + 0 0 10 0 0 0 + 1 1 1 1 + 0.5 0.5 0.5 1 + + 1000 + 0.9 + 0.01 + 0.001 + + -0.5 0.1 -0.9 + + + + true + + + + + 0 0 1 + 100 100 + + + + + + + 0 0 1 + 100 100 + + + + 0.8 0.8 0.8 1 + 0.8 0.8 0.8 1 + 0.8 0.8 0.8 1 + + + + + + + https://fuel.gazebosim.org/1.0/hkotze/models/airship + + + + diff --git a/examples/worlds/mimic_fast_slow_pendulums_world.sdf b/examples/worlds/mimic_fast_slow_pendulums_world.sdf index b42c8bc73a..7386bd3dac 100644 --- a/examples/worlds/mimic_fast_slow_pendulums_world.sdf +++ b/examples/worlds/mimic_fast_slow_pendulums_world.sdf @@ -16,6 +16,10 @@ 1 + + gz-physics-bullet-featherstone-plugin + + true 0 0 10 0 0 0 diff --git a/examples/worlds/quadcopter.sdf b/examples/worlds/quadcopter.sdf index 4b00c86c15..1ec3eaa18e 100644 --- a/examples/worlds/quadcopter.sdf +++ b/examples/worlds/quadcopter.sdf @@ -17,18 +17,6 @@ 0.001 1.0 - - - - - - diff --git a/examples/worlds/sensors.sdf b/examples/worlds/sensors.sdf index 9717bb11b3..8ca7b63789 100644 --- a/examples/worlds/sensors.sdf +++ b/examples/worlds/sensors.sdf @@ -38,6 +38,7 @@ + 10 + + + + 0.1 0.1 0.1 + 0.0 0.0 0.0 + + + + + + ogre2 + -10 0 7 0 0.5 0 + + 8192 + + + + + + + + + + 0 0 8 0 0 0 + 1 1 1 1 + 0 0 0 0 + + 50 + 0 + 0 + 0 + + true + -2 2 -1.5 + 1.0 + + + + + 0 0 0 0 0 -1.57 + + https://fuel.gazebosim.org/1.0/OpenRobotics/models/Garden Mascot + + + + + -5 0 -0.5 0 0 0 + true + + + + + 15 15 1 + + + + + + + 15 15 1 + + + + 0.8 0.8 0.8 1 + 0.8 0.8 0.8 1 + 0.8 0.8 0.8 1 + + + + + + diff --git a/examples/worlds/shapes.sdf b/examples/worlds/shapes.sdf index 1d75661b49..5d020a8c32 100644 --- a/examples/worlds/shapes.sdf +++ b/examples/worlds/shapes.sdf @@ -1,12 +1,13 @@ - - + + + 1.0 1.0 1.0 @@ -240,5 +241,36 @@ Try moving a model: + + + 0 4.5 0.5 0 0 0 + + + 1 + + + + + 0.5 + 1.0 + + + + + + + + 0.5 + 1.0 + + + + 1 0.47 0 1 + 1 0.47 0 1 + 1 0.47 0 1 + + + + diff --git a/examples/worlds/spacecraft.sdf b/examples/worlds/spacecraft.sdf new file mode 100644 index 0000000000..b28090c283 --- /dev/null +++ b/examples/worlds/spacecraft.sdf @@ -0,0 +1,51 @@ + + + + + 0 0 0 + + 0.001 + 1.0 + + + + + + + + + ogre2 + + + true + 0 0 10 0 0 0 + 0.8 0.8 0.8 1 + 0.2 0.2 0.2 1 + + 1000 + 0.9 + 0.01 + 0.001 + + -0.5 0.1 -0.9 + + + 0 0 0 0 0 0 + https://fuel.gazebosim.org/1.0/proque/models/dart/7 + + + diff --git a/src/System_TEST.cc b/include/gz/sim/Constants.hh similarity index 61% rename from src/System_TEST.cc rename to include/gz/sim/Constants.hh index e238456085..96d281ccc7 100644 --- a/src/System_TEST.cc +++ b/include/gz/sim/Constants.hh @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 Open Source Robotics Foundation + * Copyright (C) 2024 Open Source Robotics Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,15 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. * -*/ + */ -#include +#ifndef GZ_SIM_CONSTANTS_HH_ +#define GZ_SIM_CONSTANTS_HH_ -#include "gz/sim/System.hh" +#include "gz/sim/config.hh" +#include -using namespace gz; - -///////////////////////////////////////////////// -TEST(System, Constructor) +namespace gz::sim +{ +// Inline bracket to help doxygen filtering. +inline namespace GZ_SIM_VERSION_NAMESPACE { + constexpr std::string_view kPoliciesTag{"gz:policies"}; } +} // namespace gz::sim + +#endif diff --git a/include/gz/sim/EntityComponentManager.hh b/include/gz/sim/EntityComponentManager.hh index b87ada778c..2a20d159fc 100644 --- a/include/gz/sim/EntityComponentManager.hh +++ b/include/gz/sim/EntityComponentManager.hh @@ -675,6 +675,14 @@ namespace gz /// \return True if there are components marked for removal. public: bool HasRemovedComponents() const; + /// \brief Get an Entity based on a name component that is associated + /// with the entity. + /// \param[in] _name Name associated with the Entity + /// \return The Entity, if an Entity with the given name exists, + /// otherwise return std::nullopt. + public: std::optional EntityByName( + const std::string &_name) const; + /// \brief Clear the list of newly added entities so that a call to /// EachAdded after this will have no entities to iterate. This function /// is protected to facilitate testing. @@ -828,6 +836,9 @@ namespace gz friend class GuiRunner; friend class SimulationRunner; + // Make SystemManager friend so it has access to removals + friend class SystemManager; + // Make network managers friends so they have control over component // states. Like the runners, the managers are internal. friend class NetworkManagerPrimary; diff --git a/include/gz/sim/Link.hh b/include/gz/sim/Link.hh index 842e287025..18037896a7 100644 --- a/include/gz/sim/Link.hh +++ b/include/gz/sim/Link.hh @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -277,6 +278,14 @@ namespace gz public: std::optional WorldInertiaMatrix( const EntityComponentManager &_ecm) const; + /// \brief Get the fluid added mass matrix in the world frame. + /// \param[in] _ecm Entity-component manager. + /// \return Fluide added matrix in world frame, returns nullopt if link + /// does not have components components::Inertial and + /// components::WorldPose. + public: std::optional WorldFluidAddedMassMatrix( + const EntityComponentManager &_ecm) const; + /// \brief Get the rotational and translational kinetic energy of the /// link with respect to the world frame. /// \param[in] _ecm Entity-component manager. diff --git a/include/gz/sim/Model.hh b/include/gz/sim/Model.hh index 9de49ea0a1..ddb0e8130d 100644 --- a/include/gz/sim/Model.hh +++ b/include/gz/sim/Model.hh @@ -137,6 +137,14 @@ namespace gz public: sim::Entity LinkByName(const EntityComponentManager &_ecm, const std::string &_name) const; + /// \brief Get the ID of a nested model entity which is an immediate + /// child of this model. + /// \param[in] _ecm Entity-component manager. + /// \param[in] _name Nested model name. + /// \return Nested model entity. + public: sim::Entity ModelByName(const EntityComponentManager &_ecm, + const std::string &_name) const; + /// \brief Get all joints which are immediate children of this model. /// \param[in] _ecm Entity-component manager. /// \return All joints in this model. @@ -167,6 +175,12 @@ namespace gz /// \return Number of links in this model. public: uint64_t LinkCount(const EntityComponentManager &_ecm) const; + /// \brief Get the number of nested models which are immediate children + /// of this model. + /// \param[in] _ecm Entity-component manager. + /// \return Number of nested models in this model. + public: uint64_t ModelCount(const EntityComponentManager &_ecm) const; + /// \brief Set a command to change the model's pose. /// \param[in] _ecm Entity-component manager. /// \param[in] _pose New model pose. diff --git a/include/gz/sim/Primitives.hh b/include/gz/sim/Primitives.hh index 84bc8344f5..b90b9e7553 100644 --- a/include/gz/sim/Primitives.hh +++ b/include/gz/sim/Primitives.hh @@ -35,6 +35,7 @@ namespace gz { kBox, kCapsule, + kCone, kCylinder, kEllipsoid, kSphere, @@ -67,8 +68,8 @@ namespace gz /// \brief Return an SDF string of one of the available primitive shape or /// light types. /// \param[in] _typeName Type name of the of shape or light to retrieve. - /// Must be one of: box, sphere, cylinder, capsule, ellipsoid, directional, - /// point, or spot. + /// Must be one of: box, sphere, cylinder, cone, capsule, ellipsoid, + /// directional, point, or spot. /// \return String containing SDF description of primitive shape or light. /// Empty string if the _typeName is invalid. std::string GZ_SIM_VISIBLE diff --git a/include/gz/sim/SdfEntityCreator.hh b/include/gz/sim/SdfEntityCreator.hh index 8e5292ef10..be96451b8a 100644 --- a/include/gz/sim/SdfEntityCreator.hh +++ b/include/gz/sim/SdfEntityCreator.hh @@ -93,6 +93,13 @@ namespace gz /// \return World entity. public: Entity CreateEntities(const sdf::World *_world); + /// \brief Create all entities that exist in the sdf::World object and + /// load their plugins. + /// \param[in] _world SDF world object. + /// \param[in] _worldEntity The world entity object. + public: void CreateEntities(const sdf::World *_world, + Entity _worldEntity); + /// \brief Create all entities that exist in the sdf::Model object and /// load their plugins. Also loads plugins of child sensors. /// \param[in] _model SDF model object. @@ -186,6 +193,9 @@ namespace gz private: Entity CreateEntities(const sdf::Model *_model, bool _staticParent); + /// \brief Load plugins for all models + private: void LoadModelPlugins(); + /// \brief Pointer to private data. private: std::unique_ptr dataPtr; }; diff --git a/include/gz/sim/ServerConfig.hh b/include/gz/sim/ServerConfig.hh index bfb0e3e362..d32e0c4fcb 100644 --- a/include/gz/sim/ServerConfig.hh +++ b/include/gz/sim/ServerConfig.hh @@ -60,6 +60,14 @@ namespace gz kSdfString, }; + /// \brief SDF error behavior + public: enum class SdfErrorBehavior + { + /// \brief Exit the server immediately + EXIT_IMMEDIATELY, + /// \brief Continue loading the server if possible + CONTINUE_LOADING + }; class PluginInfoPrivate; /// \brief Information about a plugin that should be loaded by the @@ -386,7 +394,17 @@ namespace gz const std::string &_apiBackend); /// \return Api backend for gui. See SetRenderEngineGuiApiBackend() - const std::string &RenderEngineGuiApiBackend() const; + public: const std::string &RenderEngineGuiApiBackend() const; + + /// \brief Set the server behavior when SDF errors are encountered while + //// loading the server. + /// \param[in] _behavior Server behavior when SDF errors are encounted. + public: void SetBehaviorOnSdfErrors(SdfErrorBehavior _behavior); + + /// \brief Get the behavior when SDF errors are encountered while + //// loading the server. + /// \return Server behavior when SDF errors are encounted. + public: SdfErrorBehavior BehaviorOnSdfErrors() const; /// \brief Instruct simulation to attach a plugin to a specific /// entity when simulation starts. diff --git a/include/gz/sim/System.hh b/include/gz/sim/System.hh index cc0139161e..327f9bddbe 100644 --- a/include/gz/sim/System.hh +++ b/include/gz/sim/System.hh @@ -17,6 +17,7 @@ #ifndef GZ_SIM_SYSTEM_HH_ #define GZ_SIM_SYSTEM_HH_ +#include #include #include @@ -64,6 +65,14 @@ namespace gz /// * Used to read out results at the end of a simulation step to be used /// for sensor or controller updates. /// + /// The PreUpdate and Update phases are executed sequentially in the same + /// thread, while the PostUpdate phase is executed in parallel in multiple + /// threads. The order of execution of PreUpdate and Update phases can be + /// controlled by specifying a signed integer Priority value for the System + /// in its XML configuration. The default Priority value is zero, and + /// smaller values are executed earlier. Systems with the same Priority + /// value are executed in the order in which they are loaded. + /// /// It's important to note that UpdateInfo::simTime does not refer to the /// current time, but the time reached after the PreUpdate and Update calls /// have finished. So, if any of the *Update functions are called with @@ -74,6 +83,19 @@ namespace gz /// simulation is started un-paused. class System { + /// \brief Signed integer type used for specifying priority of the + /// execution order of PreUpdate and Update phases. + public: using PriorityType = int32_t; + + /// \brief Default priority value for execution order of the PreUpdate + /// and Update phases. + public: constexpr static PriorityType kDefaultPriority = {0}; + + /// \brief Name of the XML element from which the priority value will be + /// parsed. + public: constexpr static std::string_view kPriorityElementName = + {"gz:system_priority"}; + /// \brief Constructor public: System() = default; @@ -81,6 +103,31 @@ namespace gz public: virtual ~System() = default; }; + /// \brief Define constant priority values for specific systems. + namespace systems + { + /// \brief Default priority value for the UserCommands system, with a very + /// negative value to indicate that it should run before most systems in + /// order to modify the ECM in response to user commands received over + /// gz-transport. This is especially important for user commands that add + /// objects to the world. + constexpr System::PriorityType kUserCommandsPriority = -16384; + + /// \brief A suggested priority value for a system that should execute + /// before the Physics system. + constexpr System::PriorityType kPrePhysicsPriority = -128; + + /// \brief Default priority value for the Physics system, with a negative + /// value ensuring that it will run before systems with priority + /// System::kDefaultPriority. + constexpr System::PriorityType kPhysicsPriority = -64; + + /// \brief A suggested priority value for sensor systems that should + /// execute after the Physics system but before the systems with priority + /// System::kDefaultPriority. + constexpr System::PriorityType kPostPhysicsSensorPriority = -32; + } + /// \class ISystemConfigure ISystem.hh gz/sim/System.hh /// \brief Interface for a system that implements optional configuration /// @@ -102,6 +149,20 @@ namespace gz EventManager &_eventMgr) = 0; }; + /// \class ISystemConfigure ISystem.hh gz/sim/System.hh + /// \brief Interface for a system that implements optional configuration + /// of the default priority value. + /// + /// ConfigurePriority is called before the system is instantiated to + /// override System::kDefaultPriority. It can still be overridden by the + /// XML priority element. + class ISystemConfigurePriority { + /// \brief Configure the default priority of the system, which can still + /// be overridden by the XML priority element. + /// \return The default priority for the system. + public: virtual System::PriorityType ConfigurePriority() = 0; + }; + /// \class ISystemConfigureParameters ISystem.hh gz/sim/System.hh /// \brief Interface for a system that declares parameters. /// diff --git a/include/gz/sim/Util.hh b/include/gz/sim/Util.hh index e8e4bed0e0..de0585e5c0 100644 --- a/include/gz/sim/Util.hh +++ b/include/gz/sim/Util.hh @@ -316,6 +316,13 @@ namespace gz /// \return The loaded mesh or null if the mesh can not be loaded. GZ_SIM_VISIBLE const common::Mesh *loadMesh(const sdf::Mesh &_meshSdf); + /// \brief Optimize input mesh. + /// \param[in] _meshSdf Mesh SDF DOM with mesh optimization parameters + /// \param[in] _mesh Input mesh to optimize. + /// \return The optimized mesh or null if the mesh can not be optimized. + GZ_SIM_VISIBLE const common::Mesh *optimizeMesh(const sdf::Mesh &_meshSdf, + const common::Mesh &_mesh); + /// \brief Environment variable holding resource paths. const std::string kResourcePathEnv{"GZ_SIM_RESOURCE_PATH"}; diff --git a/include/gz/sim/components/AngularVelocityReset.hh b/include/gz/sim/components/AngularVelocityReset.hh new file mode 100644 index 0000000000..11292852ed --- /dev/null +++ b/include/gz/sim/components/AngularVelocityReset.hh @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2024 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#ifndef GZ_SIM_COMPONENTS_WORLDANGULARVELOCITYRESET_HH_ +#define GZ_SIM_COMPONENTS_WORLDANGULARVELOCITYRESET_HH_ + +#include +#include +#include +#include +#include + +namespace gz +{ +namespace sim +{ +// Inline bracket to help doxygen filtering. +inline namespace GZ_SIM_VERSION_NAMESPACE { +namespace components +{ + /// \brief Angular velocity of an entity, in its own frame + /// and in SI units (rad/s). The angular velocity is + // represented by gz::math::Vector3d. + using AngularVelocityReset = Component; + GZ_SIM_REGISTER_COMPONENT( + "gz_sim_components.AngularVelocityReset", AngularVelocityReset) + + /// \brief Angular velocity of an entity in the world frame + /// and in SI units (rad/s). The angular velocity is + // represented by gz::math::Vector3d. + using WorldAngularVelocityReset = Component; + GZ_SIM_REGISTER_COMPONENT( + "gz_sim_components.WorldAngularVelocityReset", WorldAngularVelocityReset) +} +} +} +} + +#endif diff --git a/include/gz/sim/components/Factory.hh b/include/gz/sim/components/Factory.hh index 393c770a9d..afe139f33d 100644 --- a/include/gz/sim/components/Factory.hh +++ b/include/gz/sim/components/Factory.hh @@ -17,6 +17,7 @@ #ifndef GZ_SIM_COMPONENTS_FACTORY_HH_ #define GZ_SIM_COMPONENTS_FACTORY_HH_ +#include #include #include #include @@ -282,17 +283,6 @@ namespace components std::string debugEnv; gz::common::env("GZ_DEBUG_COMPONENT_FACTORY", debugEnv); - if (debugEnv != "true") - { - gz::common::env("IGN_DEBUG_COMPONENT_FACTORY", debugEnv); - if (debugEnv == "true") - { - std::cerr << "Environment variable [IGN_DEBUG_COMPONENT_FACTORY] " - << "is deprecated! Please use [GZ_DEBUG_COMPONENT_FACTORY]" - << "instead." << std::endl; - } - } - if (debugEnv == "true") { std::cout << "Registering [" << ComponentTypeT::typeName << "]" diff --git a/include/gz/sim/components/Gravity.hh b/include/gz/sim/components/Gravity.hh index 82ba3d036a..a4b8ac352d 100644 --- a/include/gz/sim/components/Gravity.hh +++ b/include/gz/sim/components/Gravity.hh @@ -36,6 +36,11 @@ namespace components /// \brief Store the gravity acceleration. using Gravity = Component; GZ_SIM_REGISTER_COMPONENT("gz_sim_components.Gravity", Gravity) + + /// \brief Store the gravity acceleration. + using GravityEnabled = Component; + GZ_SIM_REGISTER_COMPONENT( + "gz_sim_components.GravityEnabled", GravityEnabled) } } } diff --git a/include/gz/sim/components/LinearVelocityReset.hh b/include/gz/sim/components/LinearVelocityReset.hh new file mode 100644 index 0000000000..c7343105f3 --- /dev/null +++ b/include/gz/sim/components/LinearVelocityReset.hh @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2024 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#ifndef GZ_SIM_COMPONENTS_WORLDLINEARVELOCITYRESET_HH_ +#define GZ_SIM_COMPONENTS_WORLDLINEARVELOCITYRESET_HH_ + +#include +#include +#include +#include +#include + +namespace gz +{ +namespace sim +{ +// Inline bracket to help doxygen filtering. +inline namespace GZ_SIM_VERSION_NAMESPACE { +namespace components +{ + /// \brief Linear velocity of an entity in its own frame + /// and in SI units (m/s). The linear velocity is + /// represented by gz::math::Vector3d. + using LinearVelocityReset = Component; + GZ_SIM_REGISTER_COMPONENT( + "gz_sim_components.LinearVelocityReset", LinearVelocityReset) + + /// \brief Linear velocity of an entity in the world frame + /// and in SI units (m/s). The linear velocity is + /// represented by gz::math::Vector3d. + using WorldLinearVelocityReset = Component; + GZ_SIM_REGISTER_COMPONENT( + "gz_sim_components.WorldLinearVelocityReset", WorldLinearVelocityReset) +} +} +} +} + +#endif diff --git a/include/gz/sim/components/Performer.hh b/include/gz/sim/components/Performer.hh index 2323b67275..58c0092047 100644 --- a/include/gz/sim/components/Performer.hh +++ b/include/gz/sim/components/Performer.hh @@ -17,6 +17,7 @@ #ifndef GZ_SIM_COMPONENTS_PERFORMER_HH_ #define GZ_SIM_COMPONENTS_PERFORMER_HH_ +#include #include #include @@ -34,6 +35,11 @@ namespace components /// \brief This component identifies an entity as being a performer. using Performer = Component; GZ_SIM_REGISTER_COMPONENT("gz_sim_components.Performer", Performer) + + /// \brief This component contains the performer reference name. + using PerformerRef = Component; + GZ_SIM_REGISTER_COMPONENT("gz_sim_components.PerformerRef", PerformerRef) } } } diff --git a/include/gz/sim/components/Physics.hh b/include/gz/sim/components/Physics.hh index e486c0d0b9..22f6b3f849 100644 --- a/include/gz/sim/components/Physics.hh +++ b/include/gz/sim/components/Physics.hh @@ -17,6 +17,7 @@ #ifndef GZ_SIM_COMPONENTS_PHYSICS_HH_ #define GZ_SIM_COMPONENTS_PHYSICS_HH_ +#include #include #include @@ -65,6 +66,12 @@ namespace components class PhysicsSolverTag, serializers::StringSerializer>; GZ_SIM_REGISTER_COMPONENT("gz_sim_components.PhysicsSolver", PhysicsSolver) + + /// \brief The number of solver iterations for each step. + using PhysicsSolverIterations = Component; + GZ_SIM_REGISTER_COMPONENT("gz_sim_components.PhysicsSolverIterations", + PhysicsSolverIterations) } } } diff --git a/include/gz/sim/components/WrenchMeasured.hh b/include/gz/sim/components/WrenchMeasured.hh new file mode 100644 index 0000000000..74c350514b --- /dev/null +++ b/include/gz/sim/components/WrenchMeasured.hh @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2024 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#ifndef GZ_SIM_COMPONENTS_WRENCHMEASURED_HH_ +#define GZ_SIM_COMPONENTS_WRENCHMEASURED_HH_ + +#include + +#include +#include +#include +#include + +namespace gz +{ +namespace sim +{ +// Inline bracket to help doxygen filtering. +inline namespace GZ_SIM_VERSION_NAMESPACE { + +namespace components +{ +/// \brief Wrench measured by a ForceTorqueSensor in SI units (Nm for torque, +/// N for force). +/// The wrench is expressed in the Sensor frame and the force component is +/// applied at the sensor origin. +/// \note The term Wrench is used here to mean a pair of 3D vectors representing +/// torque and force quantities expressed in a given frame and where the force +/// is applied at the origin of the frame. This is different from the Wrench +/// used in screw theory. +/// \note The value of force_offset in msgs::Wrench is ignored for this +/// component. The force is assumed to be applied at the origin of the sensor +/// frame. +using WrenchMeasured = + Component; +GZ_SIM_REGISTER_COMPONENT("gz_sim_components.WrenchMeasured", + WrenchMeasured) +} // namespace components +} +} +} + +#endif diff --git a/package.xml b/package.xml new file mode 100644 index 0000000000..507b36f440 --- /dev/null +++ b/package.xml @@ -0,0 +1,54 @@ + + + + gz-sim9 + 9.0.0 + Gazebo Sim : A Robotic Simulator + Michael Carroll + Apache License 2.0 + https://github.com/gazebosim/gz-sim + + cmake + + benchmark + glut + gz-cmake4 + gz-common6 + gz-fuel_tools10 + gz-gui9 + gz-math8 + gz-msgs11 + gz-physics8 + gz-plugin3 + gz-rendering9 + gz-sensors9 + gz-tools2 + gz-transport14 + gz-utils3 + libfreeimage-dev + libglew-dev + libxi-dev + libxmu-dev + protobuf-dev + pybind11-dev + qml-module-qt-labs-folderlistmodel + qml-module-qt-labs-settings + qml-module-qtgraphicaleffects + qml-module-qtquick-controls2 + qml-module-qtquick-controls + qml-module-qtquick-dialogs + qml-module-qtquick-layouts + qml-module-qtquick2 + qtbase5-dev + qtdeclarative5-dev + sdformat15 + tinyxml2 + uuid + + xvfb + python3-pytest + + + cmake + + diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 3069e98752..98bbe66650 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -1,22 +1,6 @@ -if(WIN32 AND CMAKE_BUILD_TYPE STREQUAL "Debug") - # pybind11 logic for setting up a debug build when both a debug and release - # python interpreter are present in the system seems to be pretty much broken. - # This works around the issue. - set(PYTHON_LIBRARIES "${PYTHON_DEBUG_LIBRARIES}") -endif() - - if(USE_SYSTEM_PATHS_FOR_PYTHON_INSTALLATION) - if(${CMAKE_VERSION} VERSION_LESS "3.12.0") - execute_process( - COMMAND "${PYTHON_EXECUTABLE}" -c "if True: - from distutils import sysconfig as sc - print(sc.get_python_lib(plat_specific=True))" - OUTPUT_VARIABLE Python3_SITEARCH - OUTPUT_STRIP_TRAILING_WHITESPACE) - else() - # Get install variable from Python3 module - # Python3_SITEARCH is available from 3.12 on, workaround if needed: + if(NOT Python3_SITEARCH) + # Get variable from Python3 module find_package(Python3 COMPONENTS Interpreter) endif() @@ -31,7 +15,6 @@ else() set(GZ_PYTHON_INSTALL_PATH ${GZ_LIB_INSTALL_DIR}/python) endif() -set(GZ_PYTHON_EXECUTABLE ${Python3_EXECUTABLE}) set(GZ_PYTHON_INSTALL_PATH "${GZ_PYTHON_INSTALL_PATH}/gz") # Set the build location and install location for a CPython extension @@ -101,7 +84,7 @@ if (BUILD_TESTING) world_TEST ) - execute_process(COMMAND "${GZ_PYTHON_EXECUTABLE}" -m pytest --version + execute_process(COMMAND "${Python3_EXECUTABLE}" -m pytest --version OUTPUT_VARIABLE PYTEST_output ERROR_VARIABLE PYTEST_error RESULT_VARIABLE PYTEST_result) @@ -115,10 +98,10 @@ if (BUILD_TESTING) foreach (test ${python_tests}) if (pytest_FOUND) add_test(NAME ${test} COMMAND - "${GZ_PYTHON_EXECUTABLE}" -m pytest "${CMAKE_SOURCE_DIR}/python/test/${test}.py" --junitxml "${CMAKE_BINARY_DIR}/test_results/UNIT_${test}.xml") + "${Python3_EXECUTABLE}" -m pytest "${CMAKE_SOURCE_DIR}/python/test/${test}.py" --junitxml "${CMAKE_BINARY_DIR}/test_results/UNIT_${test}.xml") else() add_test(NAME ${test} COMMAND - "${GZ_PYTHON_EXECUTABLE}" "${CMAKE_SOURCE_DIR}/python/test/${test}.py") + "${Python3_EXECUTABLE}" "${CMAKE_SOURCE_DIR}/python/test/${test}.py") endif() set(_env_vars) diff --git a/python/src/gz/sim/Joint.cc b/python/src/gz/sim/Joint.cc index 307289ec44..328e4d51cf 100644 --- a/python/src/gz/sim/Joint.cc +++ b/python/src/gz/sim/Joint.cc @@ -94,7 +94,22 @@ void defineSimJoint(py::object module) py::arg("ecm"), py::arg("limits"), "Set the effort limits on a joint axis.") - .def("set_position_imits", &gz::sim::Joint::SetPositionLimits, + .def("set_position_imits", + [](pybind11::object &self, EntityComponentManager &_ecm, + const std::vector &_limits) + { + auto warnings = pybind11::module::import("warnings"); + auto builtins = pybind11::module::import("builtins"); + warnings.attr("warn")( + "set_position_imits() is deprecated, use set_position_limits() instead.", + builtins.attr("DeprecationWarning")); + + return self.attr("set_position_limits")(_ecm, _limits); + }, + py::arg("ecm"), + py::arg("limits"), + "Set the position limits on a joint axis.") + .def("set_position_limits", &gz::sim::Joint::SetPositionLimits, py::arg("ecm"), py::arg("limits"), "Set the position limits on a joint axis.") diff --git a/python/test/gz_test_deps/common.py b/python/test/gz_test_deps/common.py index d28e66e5e5..60f802749d 100644 --- a/python/test/gz_test_deps/common.py +++ b/python/test/gz_test_deps/common.py @@ -1 +1 @@ -from gz.common5 import * +from gz.common6 import * diff --git a/python/test/gz_test_deps/math.py b/python/test/gz_test_deps/math.py index cb2860c798..d84d0a4777 100644 --- a/python/test/gz_test_deps/math.py +++ b/python/test/gz_test_deps/math.py @@ -1 +1 @@ -from gz.math7 import * +from gz.math8 import * diff --git a/python/test/gz_test_deps/msgs.py b/python/test/gz_test_deps/msgs.py index e91bef7942..c28bb81ef3 100644 --- a/python/test/gz_test_deps/msgs.py +++ b/python/test/gz_test_deps/msgs.py @@ -1,3 +1,3 @@ import sys -import gz.msgs10 -sys.modules["gz_test_deps.msgs"] = gz.msgs10 +import gz.msgs11 +sys.modules["gz_test_deps.msgs"] = gz.msgs11 diff --git a/python/test/gz_test_deps/sdformat.py b/python/test/gz_test_deps/sdformat.py index 51cec2889b..5d34df703e 100644 --- a/python/test/gz_test_deps/sdformat.py +++ b/python/test/gz_test_deps/sdformat.py @@ -1 +1 @@ -from sdformat14 import * +from sdformat15 import * diff --git a/python/test/gz_test_deps/sim.py b/python/test/gz_test_deps/sim.py index 38c3164e14..c41dc0aff9 100644 --- a/python/test/gz_test_deps/sim.py +++ b/python/test/gz_test_deps/sim.py @@ -1 +1 @@ -from gz.sim8 import * +from gz.sim9 import * diff --git a/python/test/gz_test_deps/transport.py b/python/test/gz_test_deps/transport.py index 242eeb064a..0c95691f87 100644 --- a/python/test/gz_test_deps/transport.py +++ b/python/test/gz_test_deps/transport.py @@ -1 +1 @@ -from gz.transport13 import * +from gz.transport14 import * diff --git a/python/test/joint_test.sdf b/python/test/joint_test.sdf index 4c9d79345c..078f3974b7 100644 --- a/python/test/joint_test.sdf +++ b/python/test/joint_test.sdf @@ -1,8 +1,8 @@ - - + + diff --git a/python/test/sensor_TEST.py b/python/test/sensor_TEST.py index aba4c61ca9..61068c3fa3 100755 --- a/python/test/sensor_TEST.py +++ b/python/test/sensor_TEST.py @@ -33,11 +33,11 @@ def test_model(self): file_path = os.path.dirname(os.path.realpath(__file__)) fixture = TestFixture(os.path.join(file_path, 'joint_test.sdf')) - def on_post_udpate_cb(_info, _ecm): + def on_post_update_cb(_info, _ecm): self.post_iterations += 1 - def on_pre_udpate_cb(_info, _ecm): - self.pre_iterations += 1 + def on_update_cb(_info, _ecm): + self.iterations += 1 world_e = world_entity(_ecm) self.assertNotEqual(K_NULL_ENTITY, world_e) w = World(world_e) @@ -53,19 +53,19 @@ def on_pre_udpate_cb(_info, _ecm): # Pose Test self.assertEqual(Pose3d(0, 1, 0, 0, 0, 0), sensor.pose(_ecm)) # Topic Test - if self.pre_iterations <= 1: + if self.iterations <= 1: self.assertEqual(None, sensor.topic(_ecm)) else: self.assertEqual('sensor_topic_test', sensor.topic(_ecm)) # Parent Test self.assertEqual(j.entity(), sensor.parent(_ecm)) - def on_udpate_cb(_info, _ecm): - self.iterations += 1 + def on_pre_update_cb(_info, _ecm): + self.pre_iterations += 1 - fixture.on_post_update(on_post_udpate_cb) - fixture.on_update(on_udpate_cb) - fixture.on_pre_update(on_pre_udpate_cb) + fixture.on_post_update(on_post_update_cb) + fixture.on_update(on_update_cb) + fixture.on_pre_update(on_pre_update_cb) fixture.finalize() server = fixture.server() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e23c1a866a..d4a5589b19 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -11,11 +11,11 @@ get_target_property(msgs_desc_file gz_msgs_generate_messages_impl( MSGS_GEN_SCRIPT - ${gz-msgs10_PROTO_GENERATOR_SCRIPT} + ${gz-msgs11_PROTO_GENERATOR_SCRIPT} FACTORY_GEN_SCRIPT - ${gz-msgs10_FACTORY_GENERATOR_SCRIPT} + ${gz-msgs11_FACTORY_GENERATOR_SCRIPT} GZ_PROTOC_PLUGIN - ${gz-msgs10_PROTO_GENERATOR_PLUGIN} + ${gz-msgs11_PROTO_GENERATOR_PLUGIN} INPUT_PROTOS ${gz_msgs_proto_files} PROTO_PACKAGE @@ -124,7 +124,6 @@ set (gtest_sources SimulationRunner_TEST.cc SystemLoader_TEST.cc SystemManager_TEST.cc - System_TEST.cc TestFixture_TEST.cc Util_TEST.cc World_TEST.cc @@ -307,10 +306,8 @@ foreach(CMD_TEST # and executables in a common CMAKE_RUNTIME_OUTPUT_DIRECTORY, so that the .dll are found # as they are in the same directory where the executable is loaded. For tests that are # launched via Ruby, this does not work, so we need to manually add CMAKE_RUNTIME_OUTPUT_DIRECTORY - # to the PATH. This is done via the ENVIRONMENT_MODIFICATION that is only available - # since CMake 3.22. However, if an older CMake is used another trick to install the libraries - # beforehand - if (WIN32 AND CMAKE_VERSION STRGREATER "3.22") + # to the PATH. This is done via the ENVIRONMENT_MODIFICATION that was added in CMake 3.22. + if (WIN32) set_tests_properties(${CMD_TEST} PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}") endif() diff --git a/src/Conversions.cc b/src/Conversions.cc index fbe7587141..d5c054ada9 100644 --- a/src/Conversions.cc +++ b/src/Conversions.cc @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -53,6 +54,7 @@ #include #include #include +#include #include #include #include @@ -176,6 +178,12 @@ msgs::Geometry gz::sim::convert(const sdf::Geometry &_in) out.mutable_capsule()->set_radius(_in.CapsuleShape()->Radius()); out.mutable_capsule()->set_length(_in.CapsuleShape()->Length()); } + else if (_in.Type() == sdf::GeometryType::CONE && _in.ConeShape()) + { + out.set_type(msgs::Geometry::CONE); + out.mutable_cone()->set_radius(_in.ConeShape()->Radius()); + out.mutable_cone()->set_length(_in.ConeShape()->Length()); + } else if (_in.Type() == sdf::GeometryType::CYLINDER && _in.CylinderShape()) { out.set_type(msgs::Geometry::CYLINDER); @@ -212,6 +220,24 @@ msgs::Geometry gz::sim::convert(const sdf::Geometry &_in) meshMsg->set_filename(asFullPath(meshSdf->Uri(), meshSdf->FilePath())); meshMsg->set_submesh(meshSdf->Submesh()); meshMsg->set_center_submesh(meshSdf->CenterSubmesh()); + + if (!meshSdf->OptimizationStr().empty()) + { + auto header = out.mutable_header()->add_data(); + header->set_key("optimization"); + header->add_value(meshSdf->OptimizationStr()); + } + if (meshSdf->ConvexDecomposition()) + { + auto header = out.mutable_header()->add_data(); + header->set_key("max_convex_hulls"); + header->add_value(std::to_string( + meshSdf->ConvexDecomposition()->MaxConvexHulls())); + header = out.mutable_header()->add_data(); + header->set_key("voxel_resolution"); + header->add_value(std::to_string( + meshSdf->ConvexDecomposition()->VoxelResolution())); + } } else if (_in.Type() == sdf::GeometryType::HEIGHTMAP && _in.HeightmapShape()) { @@ -260,6 +286,10 @@ msgs::Geometry gz::sim::convert(const sdf::Geometry &_in) } } } + else if (_in.Type() == sdf::GeometryType::EMPTY) + { + out.set_type(msgs::Geometry::EMPTY); + } else { gzerr << "Geometry type [" << static_cast(_in.Type()) @@ -293,6 +323,16 @@ sdf::Geometry gz::sim::convert(const msgs::Geometry &_in) out.SetCapsuleShape(capsuleShape); } + else if (_in.type() == msgs::Geometry::CONE && _in.has_cone()) + { + out.SetType(sdf::GeometryType::CONE); + + sdf::Cone coneShape; + coneShape.SetRadius(_in.cone().radius()); + coneShape.SetLength(_in.cone().length()); + + out.SetConeShape(coneShape); + } else if (_in.type() == msgs::Geometry::CYLINDER && _in.has_cylinder()) { out.SetType(sdf::GeometryType::CYLINDER); @@ -341,6 +381,24 @@ sdf::Geometry gz::sim::convert(const msgs::Geometry &_in) meshShape.SetSubmesh(_in.mesh().submesh()); meshShape.SetCenterSubmesh(_in.mesh().center_submesh()); + sdf::ConvexDecomposition convexDecomp; + for (int i = 0; i < _in.header().data_size(); ++i) + { + auto data = _in.header().data(i); + if (data.key() == "optimization" && data.value_size() > 0) + { + meshShape.SetOptimization(data.value(0)); + } + if (data.key() == "max_convex_hulls" && data.value_size() > 0) + { + convexDecomp.SetMaxConvexHulls(std::stoul(data.value(0))); + } + if (data.key() == "voxel_resolution" && data.value_size() > 0) + { + convexDecomp.SetVoxelResolution(std::stoul(data.value(0))); + } + } + meshShape.SetConvexDecomposition(convexDecomp); out.SetMeshShape(meshShape); } else if (_in.type() == msgs::Geometry::HEIGHTMAP && _in.has_heightmap()) @@ -848,7 +906,7 @@ msgs::Atmosphere gz::sim::convert(const sdf::Atmosphere &_in) out.set_type(msgs::Atmosphere::ADIABATIC); } // todo(anyone) add mass density to sdf::Atmosphere? - // out.set_mass_density(_in.MassDensity());k + // out.set_mass_density(_in.MassDensity()); return out; } @@ -1681,15 +1739,7 @@ msgs::ParticleEmitter gz::sim::convert(const sdf::ParticleEmitter &_in) } } - /// \todo(nkoenig) Modify the particle_emitter.proto file to - /// have a topic field. - if (!_in.Topic().empty()) - { - auto header = out.mutable_header()->add_data(); - header->set_key("topic"); - header->add_value(_in.Topic()); - } - + out.mutable_topic()->set_data(_in.Topic()); out.mutable_particle_scatter_ratio()->set_data(_in.ScatterRatio()); return out; } @@ -1744,15 +1794,8 @@ sdf::ParticleEmitter gz::sim::convert(const msgs::ParticleEmitter &_in) out.SetColorRangeImage(_in.color_range_image().data()); if (_in.has_particle_scatter_ratio()) out.SetScatterRatio(_in.particle_scatter_ratio().data()); - - for (int i = 0; i < _in.header().data_size(); ++i) - { - auto data = _in.header().data(i); - if (data.key() == "topic" && data.value_size() > 0) - { - out.SetTopic(data.value(0)); - } - } + if (_in.has_topic()) + out.SetTopic(_in.topic().data()); return out; } @@ -1770,10 +1813,7 @@ msgs::Projector gz::sim::convert(const sdf::Projector &_in) out.set_fov(_in.HorizontalFov().Radian()); out.set_texture(_in.Texture().empty() ? "" : asFullPath(_in.Texture(), _in.FilePath())); - - auto header = out.mutable_header()->add_data(); - header->set_key("visibility_flags"); - header->add_value(std::to_string(_in.VisibilityFlags())); + out.set_visibility_flags(_in.VisibilityFlags()); return out; } @@ -1790,26 +1830,7 @@ sdf::Projector gz::sim::convert(const msgs::Projector &_in) out.SetHorizontalFov(math::Angle(_in.fov())); out.SetTexture(_in.texture()); out.SetRawPose(msgs::Convert(_in.pose())); - - /// \todo(anyone) add "visibility_flags" field to projector.proto - for (int i = 0; i < _in.header().data_size(); ++i) - { - auto data = _in.header().data(i); - if (data.key() == "visibility_flags" && data.value_size() > 0) - { - try - { - out.SetVisibilityFlags(std::stoul(data.value(0))); - } - catch (...) - { - gzerr << "Failed to parse projector : " - << data.value(0) << ". Using default value: 0xFFFFFFFF." - << std::endl; - out.SetVisibilityFlags(0xFFFFFFFF); - } - } - } + out.SetVisibilityFlags(_in.visibility_flags()); return out; } diff --git a/src/Conversions_TEST.cc b/src/Conversions_TEST.cc index 08ac3e1afd..5d0f5345ba 100644 --- a/src/Conversions_TEST.cc +++ b/src/Conversions_TEST.cc @@ -463,6 +463,11 @@ TEST(Conversions, GeometryMesh) meshShape.SetUri("file://watermelon"); meshShape.SetSubmesh("grape"); meshShape.SetCenterSubmesh(true); + meshShape.SetOptimization("convex_decomposition"); + sdf::ConvexDecomposition convexDecomp; + convexDecomp.SetMaxConvexHulls(4); + convexDecomp.SetVoxelResolution(10000); + meshShape.SetConvexDecomposition(convexDecomp); geometry.SetMeshShape(meshShape); auto geometryMsg = convert(geometry); @@ -473,6 +478,15 @@ TEST(Conversions, GeometryMesh) EXPECT_EQ("file://watermelon", geometryMsg.mesh().filename()); EXPECT_EQ("grape", geometryMsg.mesh().submesh()); EXPECT_TRUE(geometryMsg.mesh().center_submesh()); + auto header = geometryMsg.header().data(0); + EXPECT_EQ("optimization", header.key()); + EXPECT_EQ("convex_decomposition", header.value(0)); + header = geometryMsg.header().data(1); + EXPECT_EQ("max_convex_hulls", header.key()); + EXPECT_EQ("4", header.value(0)); + header = geometryMsg.header().data(2); + EXPECT_EQ("voxel_resolution", header.key()); + EXPECT_EQ("10000", header.value(0)); auto newGeometry = convert(geometryMsg); EXPECT_EQ(sdf::GeometryType::MESH, newGeometry.Type()); @@ -481,6 +495,11 @@ TEST(Conversions, GeometryMesh) EXPECT_EQ("file://watermelon", newGeometry.MeshShape()->Uri()); EXPECT_EQ("grape", newGeometry.MeshShape()->Submesh()); EXPECT_TRUE(newGeometry.MeshShape()->CenterSubmesh()); + EXPECT_EQ("convex_decomposition", newGeometry.MeshShape()->OptimizationStr()); + auto newConvexDecomp = newGeometry.MeshShape()->ConvexDecomposition(); + ASSERT_NE(nullptr, newConvexDecomp); + EXPECT_EQ(4, newConvexDecomp->MaxConvexHulls()); + EXPECT_EQ(10000, newConvexDecomp->VoxelResolution()); } ///////////////////////////////////////////////// @@ -1041,10 +1060,7 @@ TEST(Conversions, ParticleEmitter) EXPECT_EQ(math::Color(0.4f, 0.5f, 0.6f), msgs::Convert(emitterMsg.color_end())); EXPECT_EQ("range_image", emitterMsg.color_range_image().data()); - - auto header = emitterMsg.header().data(0); - EXPECT_EQ("topic", header.key()); - EXPECT_EQ("my_topic", header.value(0)); + EXPECT_EQ("my_topic", emitterMsg.topic().data()); EXPECT_FLOAT_EQ(0.9f, emitterMsg.particle_scatter_ratio().data()); @@ -1094,10 +1110,7 @@ TEST(Conversions, Projector) EXPECT_NEAR(30, projectorMsg.far_clip(), 1e-3); EXPECT_NEAR(0.4, projectorMsg.fov(), 1e-3); EXPECT_EQ("projector.png", projectorMsg.texture()); - - auto header = projectorMsg.header().data(0); - EXPECT_EQ("visibility_flags", header.key()); - EXPECT_EQ(0xFF, std::stoul(header.value(0))); + EXPECT_EQ(0xFF, projectorMsg.visibility_flags()); // Convert the message back to SDF. sdf::Projector projector2 = convert(projectorMsg); @@ -1203,3 +1216,13 @@ TEST(Conversions, MsgsPluginToSdf) EXPECT_EQ(innerXml, sdfPlugins[1].Contents()[0]->ToString("")); EXPECT_EQ(innerXml2, sdfPlugins[1].Contents()[1]->ToString("")); } + +///////////////////////////////////////////////// +TEST(Conversions, GeometryEmpty) +{ + sdf::Geometry geometry; + geometry.SetType(sdf::GeometryType::EMPTY); + + auto geometryMsg = convert(geometry); + EXPECT_EQ(msgs::Geometry::EMPTY, geometryMsg.type()); +} diff --git a/src/EntityComponentManager.cc b/src/EntityComponentManager.cc index 5ab9b0a635..2fd4e03c6e 100644 --- a/src/EntityComponentManager.cc +++ b/src/EntityComponentManager.cc @@ -2314,3 +2314,15 @@ void EntityComponentManager::ResetTo(const EntityComponentManager &_other) tmpCopy.ApplyEntityDiff(*this, ecmDiff); this->CopyFrom(tmpCopy); } + +///////////////////////////////////////////////// +std::optional EntityComponentManager::EntityByName( + const std::string &_name) const +{ + std::optional entity; + Entity entByName = EntityByComponents(components::Name(_name)); + if (entByName != kNullEntity) + entity = entByName; + + return entity; +} diff --git a/src/EntityComponentManager_TEST.cc b/src/EntityComponentManager_TEST.cc index bc7f03ff99..f7a6c11e7e 100644 --- a/src/EntityComponentManager_TEST.cc +++ b/src/EntityComponentManager_TEST.cc @@ -3421,6 +3421,23 @@ TEST_P(EntityComponentManagerFixture, EXPECT_EQ(321, comp->Data()); } +////////////////////////////////////////////////// +TEST_P(EntityComponentManagerFixture, EntityByName) +{ + // Create an entity, and give it a name + Entity entity = manager.CreateEntity(); + manager.CreateComponent(entity, components::Name("entity_name_a")); + + // Try to get an entity that doesn't exist + std::optional entityByName = manager.EntityByName("a_bad_name"); + EXPECT_FALSE(entityByName); + + entityByName = manager.EntityByName("entity_name_a"); + EXPECT_TRUE(entityByName); + CompareEntityComponents(manager, entity, + *entityByName, true); +} + // Run multiple times. We want to make sure that static globals don't cause // problems. INSTANTIATE_TEST_SUITE_P(EntityComponentManagerRepeat, diff --git a/src/LevelManager.cc b/src/LevelManager.cc index 0513fb4f30..ab950bc378 100644 --- a/src/LevelManager.cc +++ b/src/LevelManager.cc @@ -18,6 +18,7 @@ #include "LevelManager.hh" #include +#include #include #include @@ -42,7 +43,6 @@ #include "gz/sim/components/LevelEntityNames.hh" #include "gz/sim/components/Light.hh" #include "gz/sim/components/LinearVelocity.hh" -#include "gz/sim/components/LinearVelocitySeed.hh" #include "gz/sim/components/MagneticField.hh" #include "gz/sim/components/Model.hh" #include "gz/sim/components/Name.hh" @@ -50,15 +50,9 @@ #include "gz/sim/components/Performer.hh" #include "gz/sim/components/PerformerLevels.hh" #include "gz/sim/components/Physics.hh" -#include "gz/sim/components/PhysicsEnginePlugin.hh" #include "gz/sim/components/Pose.hh" -#include "gz/sim/components/RenderEngineGuiPlugin.hh" -#include "gz/sim/components/RenderEngineServerApiBackend.hh" -#include "gz/sim/components/RenderEngineServerHeadless.hh" -#include "gz/sim/components/RenderEngineServerPlugin.hh" #include "gz/sim/components/Scene.hh" #include "gz/sim/components/SphericalCoordinates.hh" -#include "gz/sim/components/Wind.hh" #include "gz/sim/components/World.hh" #include "SimulationRunner.hh" @@ -80,182 +74,57 @@ LevelManager::LevelManager(SimulationRunner *_runner, const bool _useLevels) this->runner->entityCompMgr, this->runner->eventMgr); - this->ReadLevelPerformerInfo(); - this->CreatePerformers(); - std::string service = transport::TopicUtils::AsValidTopic("/world/" + - this->runner->sdfWorld->Name() + "/level/set_performer"); + this->runner->sdfWorld.Name() + "/level/set_performer"); if (service.empty()) { gzerr << "Failed to generate set_performer topic for world [" - << this->runner->sdfWorld->Name() << "]" << std::endl; + << this->runner->sdfWorld.Name() << "]" << std::endl; return; } this->node.Advertise(service, &LevelManager::OnSetPerformer, this); } ///////////////////////////////////////////////// -void LevelManager::ReadLevelPerformerInfo() +void LevelManager::ReadLevelPerformerInfo(const sdf::World &_world) { - // \todo(anyone) Use SdfEntityCreator to avoid duplication - this->worldEntity = this->runner->entityCompMgr.CreateEntity(); - - // World components - this->runner->entityCompMgr.CreateComponent(this->worldEntity, - components::World()); - this->runner->entityCompMgr.CreateComponent( - this->worldEntity, components::Name(this->runner->sdfWorld->Name())); - - this->runner->entityCompMgr.CreateComponent(this->worldEntity, - components::Gravity(this->runner->sdfWorld->Gravity())); - - auto physics = this->runner->sdfWorld->PhysicsByIndex(0); - if (!physics) - { - physics = this->runner->sdfWorld->PhysicsDefault(); - } - this->runner->entityCompMgr.CreateComponent(this->worldEntity, - components::Physics(*physics)); - - // Populate physics options that aren't accessible outside the Element() - // See https://github.com/osrf/sdformat/issues/508 - if (physics->Element() && physics->Element()->HasElement("dart")) - { - auto dartElem = physics->Element()->GetElement("dart"); - - if (dartElem->HasElement("collision_detector")) - { - auto collisionDetector = - dartElem->Get("collision_detector"); - - this->runner->entityCompMgr.CreateComponent(worldEntity, - components::PhysicsCollisionDetector(collisionDetector)); - } - if (dartElem->HasElement("solver") && - dartElem->GetElement("solver")->HasElement("solver_type")) - { - auto solver = - dartElem->GetElement("solver")->Get("solver_type"); - - this->runner->entityCompMgr.CreateComponent(worldEntity, - components::PhysicsSolver(solver)); - } - } - - this->runner->entityCompMgr.CreateComponent(this->worldEntity, - components::MagneticField(this->runner->sdfWorld->MagneticField())); - - this->runner->entityCompMgr.CreateComponent(this->worldEntity, - components::PhysicsEnginePlugin( - this->runner->serverConfig.PhysicsEngine())); - - this->runner->entityCompMgr.CreateComponent(this->worldEntity, - components::RenderEngineServerPlugin( - this->runner->serverConfig.RenderEngineServer())); - - this->runner->entityCompMgr.CreateComponent(this->worldEntity, - components::RenderEngineServerApiBackend( - this->runner->serverConfig.RenderEngineServerApiBackend())); - - this->runner->entityCompMgr.CreateComponent(this->worldEntity, - components::RenderEngineServerHeadless( - this->runner->serverConfig.HeadlessRendering())); - - this->runner->entityCompMgr.CreateComponent(this->worldEntity, - components::RenderEngineGuiPlugin( - this->runner->serverConfig.RenderEngineGui())); - - auto worldElem = this->runner->sdfWorld->Element(); - - // Create Wind - auto windEntity = this->runner->entityCompMgr.CreateEntity(); - this->runner->entityCompMgr.CreateComponent(windEntity, components::Wind()); - this->runner->entityCompMgr.CreateComponent( - windEntity, components::WorldLinearVelocity( - this->runner->sdfWorld->WindLinearVelocity())); - // Initially the wind linear velocity is used as the seed velocity - this->runner->entityCompMgr.CreateComponent( - windEntity, components::WorldLinearVelocitySeed( - this->runner->sdfWorld->WindLinearVelocity())); - - this->entityCreator->SetParent(windEntity, this->worldEntity); - - // scene - if (this->runner->sdfWorld->Scene()) - { - this->runner->entityCompMgr.CreateComponent(this->worldEntity, - components::Scene(*this->runner->sdfWorld->Scene())); - } - - // atmosphere - if (this->runner->sdfWorld->Atmosphere()) - { - this->runner->entityCompMgr.CreateComponent(this->worldEntity, - components::Atmosphere(*this->runner->sdfWorld->Atmosphere())); - } - - // spherical coordinates - if (this->runner->sdfWorld->SphericalCoordinates()) - { - this->runner->entityCompMgr.CreateComponent(this->worldEntity, - components::SphericalCoordinates( - *this->runner->sdfWorld->SphericalCoordinates())); - } - // TODO(anyone) This should probably go somewhere else as it is a global // constant. const std::string kPluginName{"gz::sim"}; - sdf::ElementPtr pluginElem; - // Get the gz::sim plugin element - for (auto plugin = worldElem->FindElement("plugin"); plugin; - plugin = plugin->GetNextElement("plugin")) + bool found = false; + for (const sdf::Plugin &plugin : _world.Plugins()) { - if (plugin->Get("name") == kPluginName) + if (plugin.Name() == kPluginName) { - pluginElem = plugin; + this->ReadPerformers(plugin); + if (this->useLevels) + this->ReadLevels(plugin); + found = true; break; } } - if (pluginElem == nullptr) - { - if (this->useLevels) - { - gzerr << "Could not find a plugin tag with name " << kPluginName - << ". Levels and distributed simulation will not work.\n"; - } - } - else + if (!found && this->useLevels) { - this->ReadPerformers(pluginElem); - if (this->useLevels) - this->ReadLevels(pluginElem); + gzerr << "Could not find a plugin tag with name " << kPluginName + << ". Levels and distributed simulation will not work.\n"; } - - this->ConfigureDefaultLevel(); - - // Load world plugins. - this->runner->EventMgr().Emit(this->worldEntity, - this->runner->sdfWorld->Plugins()); - - // Store the world's SDF DOM to be used when saving the world to file - this->runner->entityCompMgr.CreateComponent( - worldEntity, components::WorldSdf(*this->runner->sdfWorld)); } ///////////////////////////////////////////////// -void LevelManager::ReadPerformers(const sdf::ElementPtr &_sdf) +void LevelManager::ReadPerformers(const sdf::Plugin &_plugin) { GZ_PROFILE("LevelManager::ReadPerformers"); - if (_sdf == nullptr) + sdf::ElementPtr sdf = _plugin.ToElement(); + if (sdf == nullptr) return; - if (_sdf->HasElement("performer")) + if (sdf->HasElement("performer")) { gzdbg << "Reading performer info\n"; - for (auto performer = _sdf->GetElement("performer"); performer; + for (auto performer = sdf->GetElement("performer"); performer; performer = performer->GetNextElement("performer")) { auto name = performer->Get("name"); @@ -263,6 +132,7 @@ void LevelManager::ReadPerformers(const sdf::ElementPtr &_sdf) Entity performerEntity = this->runner->entityCompMgr.CreateEntity(); // We use the ref to create a parent entity component later on std::string ref = performer->GetElement("ref")->GetValue()->GetAsString(); + if (this->performerMap.find(ref) == this->performerMap.end()) { this->performerMap[ref] = performerEntity; @@ -281,6 +151,8 @@ void LevelManager::ReadPerformers(const sdf::ElementPtr &_sdf) geometry.Load(performer->GetElement("geometry")); this->runner->entityCompMgr.CreateComponent(performerEntity, components::Performer()); + this->runner->entityCompMgr.CreateComponent(performerEntity, + components::PerformerRef(ref)); this->runner->entityCompMgr.CreateComponent(performerEntity, components::PerformerLevels()); this->runner->entityCompMgr.CreateComponent(performerEntity, @@ -288,8 +160,9 @@ void LevelManager::ReadPerformers(const sdf::ElementPtr &_sdf) this->runner->entityCompMgr.CreateComponent(performerEntity, components::Geometry(geometry)); - gzmsg << "Created performer [" << performerEntity << " / " << name << "]" - << std::endl; + gzmsg << "Created performer. EntityId[" << performerEntity + << "] EntityName[" << name << "] Ref[" << ref << "]" + << std::endl; } } @@ -380,16 +253,20 @@ bool LevelManager::OnSetPerformer(const msgs::StringMsg &_req, } ///////////////////////////////////////////////// -void LevelManager::ReadLevels(const sdf::ElementPtr &_sdf) +void LevelManager::ReadLevels(const sdf::Plugin &_plugin) { GZ_PROFILE("LevelManager::ReadLevels"); gzdbg << "Reading levels info\n"; - if (_sdf == nullptr) + sdf::ElementPtr sdf = _plugin.ToElement(); + if (sdf == nullptr) + return; + + if (!sdf->HasElement("level")) return; - for (auto level = _sdf->GetElement("level"); level; + for (auto level = sdf->GetElement("level"); level; level = level->GetNextElement("level")) { auto name = level->Get("name"); @@ -440,7 +317,15 @@ void LevelManager::ReadLevels(const sdf::ElementPtr &_sdf) this->runner->entityCompMgr.CreateComponent( levelEntity, components::LevelBuffer(buffer)); - this->entityCreator->SetParent(levelEntity, this->worldEntity); + auto worldEntity = + this->runner->entityCompMgr.EntityByComponents(components::World()); + + // All levels start inactive and unloaded. + this->UnloadLevel(levelEntity); + this->entityCreator->SetParent(levelEntity, worldEntity); + + gzdbg << "Created level with name[" << name << "] and pose[" + << pose << "]\n"; } } @@ -460,11 +345,17 @@ void LevelManager::ConfigureDefaultLevel() // Models for (uint64_t modelIndex = 0; - modelIndex < this->runner->sdfWorld->ModelCount(); ++modelIndex) + modelIndex < this->runner->sdfWorld.ModelCount(); ++modelIndex) { // There is no sdf::World::ModelByName so we have to iterate by index and // check if the model is in this level - auto model = this->runner->sdfWorld->ModelByIndex(modelIndex); + auto model = this->runner->sdfWorld.ModelByIndex(modelIndex); + if (!this->useLevels) + { + entityNamesInDefault.insert(model->Name()); + continue; + } + // If model is a performer, it will be handled separately if (this->performerMap.find(model->Name()) != this->performerMap.end()) { @@ -480,11 +371,18 @@ void LevelManager::ConfigureDefaultLevel() // Actors for (uint64_t actorIndex = 0; - actorIndex < this->runner->sdfWorld->ActorCount(); ++actorIndex) + actorIndex < this->runner->sdfWorld.ActorCount(); ++actorIndex) { // There is no sdf::World::ActorByName so we have to iterate by index and // check if the actor is in this level - auto actor = this->runner->sdfWorld->ActorByIndex(actorIndex); + auto actor = this->runner->sdfWorld.ActorByIndex(actorIndex); + + if (!this->useLevels) + { + entityNamesInDefault.insert(actor->Name()); + continue; + } + // If actor is a performer, it will be handled separately if (this->performerMap.find(actor->Name()) != this->performerMap.end()) { @@ -501,9 +399,9 @@ void LevelManager::ConfigureDefaultLevel() // Lights // We assume no performers are lights for (uint64_t lightIndex = 0; - lightIndex < this->runner->sdfWorld->LightCount(); ++lightIndex) + lightIndex < this->runner->sdfWorld.LightCount(); ++lightIndex) { - auto light = this->runner->sdfWorld->LightByIndex(lightIndex); + auto light = this->runner->sdfWorld.LightByIndex(lightIndex); if (this->entityNamesInLevels.find(light->Name()) == this->entityNamesInLevels.end()) { @@ -514,11 +412,12 @@ void LevelManager::ConfigureDefaultLevel() // Joints // We assume no performers are joints for (uint64_t jointIndex = 0; - jointIndex < this->runner->sdfWorld->JointCount(); ++jointIndex) + jointIndex < this->runner->sdfWorld.JointCount(); ++jointIndex) { - auto joint = this->runner->sdfWorld->JointByIndex(jointIndex); + auto joint = this->runner->sdfWorld.JointByIndex(jointIndex); - if (this->entityNamesInLevels.find(joint->Name()) == + if ( + this->entityNamesInLevels.find(joint->Name()) == this->entityNamesInLevels.end()) { entityNamesInDefault.insert(joint->Name()); @@ -530,57 +429,15 @@ void LevelManager::ConfigureDefaultLevel() defaultLevel, components::Level()); this->runner->entityCompMgr.CreateComponent( defaultLevel, components::DefaultLevel()); + this->runner->entityCompMgr.CreateComponent( + defaultLevel, components::Name("default")); this->runner->entityCompMgr.CreateComponent( defaultLevel, components::LevelEntityNames(entityNamesInDefault)); - this->entityCreator->SetParent(defaultLevel, this->worldEntity); -} - -///////////////////////////////////////////////// -void LevelManager::CreatePerformers() -{ - GZ_PROFILE("LevelManager::CreatePerformers"); - - if (this->worldEntity == kNullEntity) - { - gzerr << "Could not find the world entity while creating performers\n"; - return; - } - // Models - for (uint64_t modelIndex = 0; - modelIndex < this->runner->sdfWorld->ModelCount(); ++modelIndex) - { - auto model = this->runner->sdfWorld->ModelByIndex(modelIndex); - if (this->performerMap.find(model->Name()) != this->performerMap.end()) - { - Entity modelEntity = this->entityCreator->CreateEntities(model); - - // Make the model a parent of this performer - this->entityCreator->SetParent(this->performerMap[model->Name()], - modelEntity); - - // Add parent world to the model - this->entityCreator->SetParent(modelEntity, this->worldEntity); - } - } - - // Actors - for (uint64_t actorIndex = 0; - actorIndex < this->runner->sdfWorld->ActorCount(); ++actorIndex) - { - auto actor = this->runner->sdfWorld->ActorByIndex(actorIndex); - if (this->performerMap.find(actor->Name()) != this->performerMap.end()) - { - Entity actorEntity = this->entityCreator->CreateEntities(actor); - - // Make the actor a parent of this performer - this->entityCreator->SetParent(this->performerMap[actor->Name()], - actorEntity); + auto worldEntity = + this->runner->entityCompMgr.EntityByComponents(components::World()); - // Add parent world to the actor - this->entityCreator->SetParent(actorEntity, this->worldEntity); - } - } + this->entityCreator->SetParent(defaultLevel, worldEntity); } ///////////////////////////////////////////////// @@ -764,50 +621,27 @@ void LevelManager::UpdateLevelsState() // Make a list of entity names to unload making sure to leave out the ones // that have been marked to be loaded above - std::set entityNamesToUnload; - for (const auto &toUnload : levelsToUnload) + for (const Entity &toUnload : levelsToUnload) { - auto entityNames = this->runner->entityCompMgr - .Component(toUnload) - ->Data(); - - for (const auto &name : entityNames) - { - if (entityNamesMarked.find(name) == entityNamesMarked.end()) - { - entityNamesToUnload.insert(name); - } - } + this->UnloadLevel(toUnload, entityNamesMarked); } - // Load and unload the entities + // Load the entities if (entityNamesToLoad.size() > 0) - { this->LoadActiveEntities(entityNamesToLoad); - } - if (entityNamesToUnload.size() > 0) - { - this->UnloadInactiveEntities(entityNamesToUnload); - } - // Finally, upadte the list of active levels + // Finally, update the list of active levels for (const auto &level : levelsToLoad) { if (!this->IsLevelActive(level)) { - gzmsg << "Loaded level [" << level << "]" << std::endl; - this->activeLevels.push_back(level); - } - } + const components::Name *lvlName = + this->runner->entityCompMgr.Component(level); - auto pendingEnd = this->activeLevels.end(); - for (const auto &toUnload : levelsToUnload) - { - gzmsg << "Unloaded level [" << toUnload << "]" << std::endl; - pendingEnd = std::remove(this->activeLevels.begin(), pendingEnd, toUnload); + gzmsg << "Loaded level [" << lvlName->Data() << "]" << std::endl; + this->activeLevels.insert(level); + } } - // Erase from vector - this->activeLevels.erase(pendingEnd, this->activeLevels.end()); } ///////////////////////////////////////////////// @@ -815,7 +649,10 @@ void LevelManager::LoadActiveEntities(const std::set &_namesToLoad) { GZ_PROFILE("LevelManager::LoadActiveEntities"); - if (this->worldEntity == kNullEntity) + auto worldEntity = + this->runner->entityCompMgr.EntityByComponents(components::World()); + + if (worldEntity == kNullEntity) { gzerr << "Could not find the world entity while loading levels\n"; return; @@ -823,57 +660,60 @@ void LevelManager::LoadActiveEntities(const std::set &_namesToLoad) // Models for (uint64_t modelIndex = 0; - modelIndex < this->runner->sdfWorld->ModelCount(); ++modelIndex) + modelIndex < this->runner->sdfWorld.ModelCount(); ++modelIndex) { // There is no sdf::World::ModelByName so we have to iterate by index and // check if the model is in this level - auto model = this->runner->sdfWorld->ModelByIndex(modelIndex); - if (_namesToLoad.find(model->Name()) != _namesToLoad.end()) + auto model = this->runner->sdfWorld.ModelByIndex(modelIndex); + if (_namesToLoad.find(model->Name()) != _namesToLoad.end() && + this->runner->EntityByName(model->Name()) == std::nullopt) { Entity modelEntity = this->entityCreator->CreateEntities(model); - this->entityCreator->SetParent(modelEntity, this->worldEntity); + this->entityCreator->SetParent(modelEntity, worldEntity); } } // Actors for (uint64_t actorIndex = 0; - actorIndex < this->runner->sdfWorld->ActorCount(); ++actorIndex) + actorIndex < this->runner->sdfWorld.ActorCount(); ++actorIndex) { // There is no sdf::World::ActorByName so we have to iterate by index and // check if the actor is in this level - auto actor = this->runner->sdfWorld->ActorByIndex(actorIndex); - if (_namesToLoad.find(actor->Name()) != _namesToLoad.end()) + auto actor = this->runner->sdfWorld.ActorByIndex(actorIndex); + if (_namesToLoad.find(actor->Name()) != _namesToLoad.end() && + this->runner->EntityByName(actor->Name()) == std::nullopt) { Entity actorEntity = this->entityCreator->CreateEntities(actor); - this->entityCreator->SetParent(actorEntity, this->worldEntity); + this->entityCreator->SetParent(actorEntity, worldEntity); } } // Lights for (uint64_t lightIndex = 0; - lightIndex < this->runner->sdfWorld->LightCount(); ++lightIndex) + lightIndex < this->runner->sdfWorld.LightCount(); ++lightIndex) { - auto light = this->runner->sdfWorld->LightByIndex(lightIndex); - if (_namesToLoad.find(light->Name()) != _namesToLoad.end()) + auto light = this->runner->sdfWorld.LightByIndex(lightIndex); + if (_namesToLoad.find(light->Name()) != _namesToLoad.end() && + this->runner->EntityByName(light->Name()) == std::nullopt) { Entity lightEntity = this->entityCreator->CreateEntities(light); - this->entityCreator->SetParent(lightEntity, this->worldEntity); + this->entityCreator->SetParent(lightEntity, worldEntity); } } // Joints for (uint64_t jointIndex = 0; - jointIndex < this->runner->sdfWorld->JointCount(); ++jointIndex) + jointIndex < this->runner->sdfWorld.JointCount(); ++jointIndex) { - auto joint = this->runner->sdfWorld->JointByIndex(jointIndex); + auto joint = this->runner->sdfWorld.JointByIndex(jointIndex); if (_namesToLoad.find(joint->Name()) != _namesToLoad.end()) { Entity jointEntity = this->entityCreator->CreateEntities(joint); - this->entityCreator->SetParent(jointEntity, this->worldEntity); + this->entityCreator->SetParent(jointEntity, worldEntity); } } @@ -938,8 +778,7 @@ void LevelManager::UnloadInactiveEntities( ///////////////////////////////////////////////// bool LevelManager::IsLevelActive(const Entity _entity) const { - return std::find(this->activeLevels.begin(), this->activeLevels.end(), - _entity) != this->activeLevels.end(); + return this->activeLevels.find(_entity) != this->activeLevels.end(); } ///////////////////////////////////////////////// @@ -982,3 +821,31 @@ int LevelManager::CreatePerformerEntity(const std::string &_name, this->entityCreator->SetParent(this->performerMap[_name], modelEntity); return 0; } + +////////////////////////////////////////////////// +void LevelManager::UnloadLevel(const Entity &_entity, + const std::set &_entityNamesMarked) +{ + auto entityNames = this->runner->entityCompMgr + .Component(_entity) + ->Data(); + + std::set entityNamesToUnload; + for (const auto &name : entityNames) + { + if (_entityNamesMarked.find(name) == _entityNamesMarked.end()) + { + entityNamesToUnload.insert(name); + } + } + + if (entityNamesToUnload.size() > 0) + { + this->UnloadInactiveEntities(entityNamesToUnload); + } + this->activeLevels.erase(_entity); + const components::Name *lvlName = + this->runner->entityCompMgr.Component(_entity); + + gzmsg << "Unloaded level [" << lvlName->Data() << "]" << std::endl; +} diff --git a/src/LevelManager.hh b/src/LevelManager.hh index 63e0466533..69e398bd90 100644 --- a/src/LevelManager.hh +++ b/src/LevelManager.hh @@ -88,6 +88,15 @@ namespace gz /// every update cycle public: void UpdateLevelsState(); + /// \brief Read level and performer information from the sdf::World + /// object + /// \param[in] _world The SDF world + public: void ReadLevelPerformerInfo(const sdf::World &_world); + + /// \brief Determine which entities belong to the default level and + /// schedule them to be loaded + public: void ConfigureDefaultLevel(); + /// \brief Load entities that have been marked for loading. /// \param[in] _namesToLoad List of of entity names to load private: void LoadActiveEntities( @@ -98,27 +107,15 @@ namespace gz private: void UnloadInactiveEntities( const std::set &_namesToUnload); - /// \brief Read level and performer information from the sdf::World - /// object - private: void ReadLevelPerformerInfo(); - - /// \brief Create performers - /// Assuming that a simulation runner is performer-centered - private: void CreatePerformers(); - /// \brief Read information about performers from the sdf Element and /// create performer entities - /// \param[in] _sdf sdf::ElementPtr of the gz::sim plugin tag - private: void ReadPerformers(const sdf::ElementPtr &_sdf); + /// \param[in] _plugin sdf::Plugin of the gz::sim plugin tag + private: void ReadPerformers(const sdf::Plugin &_plugin); /// \brief Read information about levels from the sdf Element and /// create level entities - /// \param[in] _sdf sdf::ElementPtr of the gz::sim plugin tag - private: void ReadLevels(const sdf::ElementPtr &_sdf); - - /// \brief Determine which entities belong to the default level and - /// schedule them to be loaded - private: void ConfigureDefaultLevel(); + /// \param[in] _plugin sdf::Plugin of the gz::sim plugin tag + private: void ReadLevels(const sdf::Plugin &_plugin); /// \brief Determine if a level is active /// \param[in] _entity Entity of level to be checked @@ -145,8 +142,11 @@ namespace gz private: int CreatePerformerEntity(const std::string &_name, const sdf::Geometry &_geom); + private: void UnloadLevel(const Entity &_entity, + const std::set &_entityNamesMarked = {}); + /// \brief List of currently active levels - private: std::vector activeLevels; + private: std::set activeLevels; /// \brief Names of entities that are currently active (loaded). private: std::set activeEntityNames; @@ -161,9 +161,6 @@ namespace gz /// \brief Names of all entities that have assigned levels private: std::set entityNamesInLevels; - /// \brief Entity of the world. - private: Entity worldEntity{kNullEntity}; - /// \brief Flag whether to use levels or not. private: bool useLevels{false}; diff --git a/src/Link.cc b/src/Link.cc index 4fef211baf..348923a44a 100644 --- a/src/Link.cc +++ b/src/Link.cc @@ -356,6 +356,18 @@ std::optional Link::WorldInertiaMatrix( math::Inertiald(inertial->Data().MassMatrix(), comWorldPose).Moi()); } +std::optional Link::WorldFluidAddedMassMatrix( + const EntityComponentManager &_ecm) const +{ + auto inertial = _ecm.Component(this->dataPtr->id); + auto worldPose = _ecm.Component(this->dataPtr->id); + + if (!worldPose || !inertial) + return std::nullopt; + + return inertial->Data().FluidAddedMass(); +} + ////////////////////////////////////////////////// std::optional Link::WorldKineticEnergy( const EntityComponentManager &_ecm) const diff --git a/src/Model.cc b/src/Model.cc index a23779981f..01f4538fb7 100644 --- a/src/Model.cc +++ b/src/Model.cc @@ -148,6 +148,16 @@ Entity Model::LinkByName(const EntityComponentManager &_ecm, components::Link()); } +////////////////////////////////////////////////// +Entity Model::ModelByName(const EntityComponentManager &_ecm, + const std::string &_name) const +{ + return _ecm.EntityByComponents( + components::ParentEntity(this->dataPtr->id), + components::Name(_name), + components::Model()); +} + ////////////////////////////////////////////////// std::vector Model::Joints(const EntityComponentManager &_ecm) const { @@ -184,6 +194,12 @@ uint64_t Model::LinkCount(const EntityComponentManager &_ecm) const return this->Links(_ecm).size(); } +////////////////////////////////////////////////// +uint64_t Model::ModelCount(const EntityComponentManager &_ecm) const +{ + return this->Models(_ecm).size(); +} + ////////////////////////////////////////////////// void Model::SetWorldPoseCmd(EntityComponentManager &_ecm, const math::Pose3d &_pose) diff --git a/src/ModelCommandAPI_TEST.cc b/src/ModelCommandAPI_TEST.cc index 95f5b8b4a4..36e088680a 100644 --- a/src/ModelCommandAPI_TEST.cc +++ b/src/ModelCommandAPI_TEST.cc @@ -608,7 +608,7 @@ TEST(ModelCommandAPI, GZ_UTILS_TEST_DISABLED_ON_MAC(RgbdCameraSensor)) // Run without blocking. server.Run(false, 0, false); - // Tested command: gz model -m altimeter_mode -l link -s altimeter_sensor + // Tested command: gz model -m rgbd_camera -l rgbd_camera_link -s rgbd_camera { const std::string cmd = kGzModelCommand + "-m rgbd_camera -l rgbd_camera_link -s rgbd_camera"; @@ -657,7 +657,7 @@ TEST(ModelCommandAPI, GZ_UTILS_TEST_DISABLED_ON_MAC(RgbdCameraSensor)) " - Lens intrinsics Fy: 277\n" " - Lens intrinsics Cx: 160\n" " - Lens intrinsics Cy: 120\n" - " - Lens intrinsics skew: 1\n" + " - Lens intrinsics skew: 0\n" " - Visibility mask: 4294967295\n"; EXPECT_EQ(expectedOutput, output); } diff --git a/src/Primitives.cc b/src/Primitives.cc index 4fa93cea54..fcb7f65942 100644 --- a/src/Primitives.cc +++ b/src/Primitives.cc @@ -104,6 +104,49 @@ constexpr const char * kSphereSdf = R"( )"; +///////////////////////////////////////////////// +constexpr const char * kConeSdf = R"( + + + 0 0 0.5 0 0 0 + + + + 0.075 + 0 + 0 + 0.075 + 0 + 0.075 + + 1.0 + + + + + 0.5 + 1.0 + + + + + + + 0.5 + 1.0 + + + + 0.3 0.3 0.3 1 + 0.7 0.7 0.7 1 + 1 1 1 1 + + + + + +)"; + ///////////////////////////////////////////////// constexpr const char * kCylinderSdf = R"( @@ -301,6 +344,8 @@ std::string gz::sim::getPrimitiveShape(const PrimitiveShape &_type) return kBoxSdf; case PrimitiveShape::kSphere: return kSphereSdf; + case PrimitiveShape::kCone: + return kConeSdf; case PrimitiveShape::kCylinder: return kCylinderSdf; case PrimitiveShape::kCapsule: @@ -339,6 +384,8 @@ std::string gz::sim::getPrimitive(const std::string &_typeName) return getPrimitiveShape(PrimitiveShape::kSphere); else if (type == "cylinder") return getPrimitiveShape(PrimitiveShape::kCylinder); + else if (type == "cone") + return getPrimitiveShape(PrimitiveShape::kCone); else if (type == "capsule") return getPrimitiveShape(PrimitiveShape::kCapsule); else if (type == "ellipsoid") @@ -354,6 +401,7 @@ std::string gz::sim::getPrimitive(const std::string &_typeName) gzwarn << "The valid options are:\n"; gzwarn << " - box\n"; gzwarn << " - sphere\n"; + gzwarn << " - cone\n"; gzwarn << " - cylinder\n"; gzwarn << " - capsule\n"; gzwarn << " - ellipsoid\n"; diff --git a/src/SdfEntityCreator.cc b/src/SdfEntityCreator.cc index f4b693533b..2c258c1ad8 100644 --- a/src/SdfEntityCreator.cc +++ b/src/SdfEntityCreator.cc @@ -15,12 +15,15 @@ * */ +#include + #include #include #include #include "gz/sim/Events.hh" #include "gz/sim/SdfEntityCreator.hh" +#include "gz/sim/Util.hh" #include "gz/sim/components/Actor.hh" #include "gz/sim/components/AirPressureSensor.hh" @@ -47,11 +50,14 @@ #include "gz/sim/components/JointAxis.hh" #include "gz/sim/components/JointType.hh" #include "gz/sim/components/LaserRetro.hh" +#include "gz/sim/components/Level.hh" +#include "gz/sim/components/LevelEntityNames.hh" #include "gz/sim/components/Lidar.hh" #include "gz/sim/components/Light.hh" #include "gz/sim/components/LightType.hh" #include "gz/sim/components/LinearAcceleration.hh" #include "gz/sim/components/LinearVelocity.hh" +#include "gz/sim/components/LinearVelocitySeed.hh" #include "gz/sim/components/Link.hh" #include "gz/sim/components/LogicalCamera.hh" #include "gz/sim/components/MagneticField.hh" @@ -62,8 +68,10 @@ #include "gz/sim/components/NavSat.hh" #include "gz/sim/components/ParentEntity.hh" #include "gz/sim/components/ParentLinkName.hh" -#include +#include "gz/sim/components/ParticleEmitter.hh" +#include "gz/sim/components/Performer.hh" #include "gz/sim/components/Physics.hh" +#include "gz/sim/components/PhysicsEnginePlugin.hh" #include "gz/sim/components/Pose.hh" #include #include "gz/sim/components/RgbdCamera.hh" @@ -81,10 +89,12 @@ #include "gz/sim/components/Visibility.hh" #include "gz/sim/components/Visual.hh" #include "gz/sim/components/WideAngleCamera.hh" +#include "gz/sim/components/Wind.hh" #include "gz/sim/components/WindMode.hh" #include "gz/sim/components/World.hh" #include "rendering/MaterialParser/MaterialParser.hh" +#include "ServerPrivate.hh" class gz::sim::SdfEntityCreatorPrivate { @@ -228,34 +238,85 @@ SdfEntityCreator &SdfEntityCreator::operator=(SdfEntityCreator &&_creator) ////////////////////////////////////////////////// Entity SdfEntityCreator::CreateEntities(const sdf::World *_world) { - GZ_PROFILE("SdfEntityCreator::CreateEntities(sdf::World)"); - // World entity Entity worldEntity = this->dataPtr->ecm->CreateEntity(); - // World components - this->dataPtr->ecm->CreateComponent(worldEntity, components::World()); - this->dataPtr->ecm->CreateComponent(worldEntity, + this->CreateEntities(_world, worldEntity); + return worldEntity; +} + +////////////////////////////////////////////////// +void SdfEntityCreator::CreateEntities(const sdf::World *_world, + Entity _worldEntity) +{ + GZ_PROFILE("SdfEntityCreator::CreateEntities(sdf::World)"); + + if (!this->dataPtr->ecm->EntityHasComponentType( + _worldEntity, components::World::typeId)) + { + this->dataPtr->ecm->CreateComponent(_worldEntity, components::World()); + } + + this->dataPtr->ecm->CreateComponent(_worldEntity, components::Name(_world->Name())); + // Gravity + this->dataPtr->ecm->CreateComponent(_worldEntity, + components::Gravity(_world->Gravity())); + + // MagneticField + this->dataPtr->ecm->CreateComponent(_worldEntity, + components::MagneticField(_world->MagneticField())); + + // Create Wind + auto windEntity = this->dataPtr->ecm->CreateEntity(); + this->SetParent(windEntity, _worldEntity); + this->dataPtr->ecm->CreateComponent(windEntity, components::Wind()); + this->dataPtr->ecm->CreateComponent(windEntity, + components::WorldLinearVelocity(_world->WindLinearVelocity())); + // Initially the wind linear velocity is used as the seed velocity + this->dataPtr->ecm->CreateComponent(windEntity, + components::WorldLinearVelocitySeed(_world->WindLinearVelocity())); + + // Set the parent of each level to the world + this->dataPtr->ecm->Each([&]( + const Entity &_entity, + const components::Level *) -> bool + { + this->SetParent(_entity, _worldEntity); + return true; + }); + + // Get the entities that should be loaded based on level information. + std::set levelEntityNames; + this->dataPtr->ecm->Each ([&]( + const Entity &, + const components::DefaultLevel *, + const components::LevelEntityNames *_names) -> bool + { + levelEntityNames = _names->Data(); + return true; + }); + // scene if (_world->Scene()) { - this->dataPtr->ecm->CreateComponent(worldEntity, + this->dataPtr->ecm->CreateComponent(_worldEntity, components::Scene(*_world->Scene())); } // atmosphere if (_world->Atmosphere()) { - this->dataPtr->ecm->CreateComponent(worldEntity, + this->dataPtr->ecm->CreateComponent(_worldEntity, components::Atmosphere(*_world->Atmosphere())); } // spherical coordinates if (_world->SphericalCoordinates()) { - this->dataPtr->ecm->CreateComponent(worldEntity, + this->dataPtr->ecm->CreateComponent(_worldEntity, components::SphericalCoordinates(*_world->SphericalCoordinates())); } @@ -263,35 +324,84 @@ Entity SdfEntityCreator::CreateEntities(const sdf::World *_world) for (uint64_t modelIndex = 0; modelIndex < _world->ModelCount(); ++modelIndex) { - auto model = _world->ModelByIndex(modelIndex); - auto modelEntity = this->CreateEntities(model); + const sdf::Model *model = _world->ModelByIndex(modelIndex); + if (levelEntityNames.empty() || + levelEntityNames.find(model->Name()) != levelEntityNames.end()) - this->SetParent(modelEntity, worldEntity); + { + Entity modelEntity = this->CreateEntities(model, false); + + this->SetParent(modelEntity, _worldEntity); + } } // Actors for (uint64_t actorIndex = 0; actorIndex < _world->ActorCount(); ++actorIndex) { - auto actor = _world->ActorByIndex(actorIndex); - auto actorEntity = this->CreateEntities(actor); - - this->SetParent(actorEntity, worldEntity); + const sdf::Actor *actor = _world->ActorByIndex(actorIndex); + if (levelEntityNames.empty() || + levelEntityNames.find(actor->Name()) != levelEntityNames.end()) + { + Entity actorEntity = this->CreateEntities(actor); + this->SetParent(actorEntity, _worldEntity); + } } // Lights for (uint64_t lightIndex = 0; lightIndex < _world->LightCount(); ++lightIndex) { - auto light = _world->LightByIndex(lightIndex); - auto lightEntity = this->CreateEntities(light); + const sdf::Light *light = _world->LightByIndex(lightIndex); + if (levelEntityNames.empty() || + levelEntityNames.find(light->Name()) != levelEntityNames.end()) + { + Entity lightEntity = this->CreateEntities(light); - this->SetParent(lightEntity, worldEntity); + this->SetParent(lightEntity, _worldEntity); + } } - // Gravity - this->dataPtr->ecm->CreateComponent(worldEntity, - components::Gravity(_world->Gravity())); + // Attach performers to their parent entity + this->dataPtr->ecm->Each< + components::Performer, + components::PerformerRef>([&]( + const Entity &_entity, + const components::Performer *, + const components::PerformerRef *_ref) -> bool + { + std::optional parentEntity = + this->dataPtr->ecm->EntityByName(_ref->Data()); + if (!parentEntity) + { + // Performers have not been created yet. Try to create the model + // or actor and attach the peformer. + if (_world->ModelNameExists(_ref->Data())) + { + const sdf::Model *model = _world->ModelByName(_ref->Data()); + Entity modelEntity = this->CreateEntities(model, false); + this->SetParent(modelEntity, _worldEntity); + this->SetParent(_entity, modelEntity); + } + else if (_world->ActorNameExists(_ref->Data())) + { + const sdf::Actor *actor = _world->ActorByName(_ref->Data()); + Entity actorEntity = this->CreateEntities(actor); + this->SetParent(actorEntity, _worldEntity); + this->SetParent(_entity, actorEntity); + } + else + { + gzerr << "Unable to find performer parent entity with name[" << + _ref->Data() << "]. This performer will not adhere to levels.\n"; + } + } + else + { + this->SetParent(_entity, *parentEntity); + } + return true; + }); // Physics // \todo(anyone) Support picking a specific physics profile @@ -300,46 +410,56 @@ Entity SdfEntityCreator::CreateEntities(const sdf::World *_world) { physics = _world->PhysicsDefault(); } - this->dataPtr->ecm->CreateComponent(worldEntity, + this->dataPtr->ecm->CreateComponent(_worldEntity, components::Physics(*physics)); // Populate physics options that aren't accessible outside the Element() // See https://github.com/osrf/sdformat/issues/508 - if (physics->Element() && physics->Element()->HasElement("dart")) + if (physics->Element()) { - auto dartElem = physics->Element()->GetElement("dart"); - - if (dartElem->HasElement("collision_detector")) + if (auto dartElem = physics->Element()->FindElement("dart")) { - auto collisionDetector = - dartElem->Get("collision_detector"); + if (dartElem->HasElement("collision_detector")) + { + auto collisionDetector = + dartElem->Get("collision_detector"); - this->dataPtr->ecm->CreateComponent(worldEntity, - components::PhysicsCollisionDetector(collisionDetector)); + this->dataPtr->ecm->CreateComponent(_worldEntity, + components::PhysicsCollisionDetector(collisionDetector)); + } + if (auto solverElem = dartElem->FindElement("solver")) + { + if (solverElem->HasElement("solver_type")) + { + auto solver = solverElem->Get("solver_type"); + this->dataPtr->ecm->CreateComponent(_worldEntity, + components::PhysicsSolver(solver)); + } + } } - if (dartElem->HasElement("solver") && - dartElem->GetElement("solver")->HasElement("solver_type")) + if (auto bulletElem = physics->Element()->FindElement("bullet")) { - auto solver = - dartElem->GetElement("solver")->Get("solver_type"); - - this->dataPtr->ecm->CreateComponent(worldEntity, - components::PhysicsSolver(solver)); + if (auto solverElem = bulletElem->FindElement("solver")) + { + if (solverElem->HasElement("iters")) + { + uint32_t solverIterations = solverElem->Get("iters"); + this->dataPtr->ecm->CreateComponent(_worldEntity, + components::PhysicsSolverIterations(solverIterations)); + } + } } } - // MagneticField - this->dataPtr->ecm->CreateComponent(worldEntity, - components::MagneticField(_world->MagneticField())); - - this->dataPtr->eventManager->Emit(worldEntity, - _world->Plugins()); - // Store the world's SDF DOM to be used when saving the world to file this->dataPtr->ecm->CreateComponent( - worldEntity, components::WorldSdf(*_world)); + _worldEntity, components::WorldSdf(*_world)); - return worldEntity; + this->dataPtr->eventManager->Emit(_worldEntity, + _world->Plugins()); + + // Load model plugins after the world plugin. + this->LoadModelPlugins(); } ////////////////////////////////////////////////// @@ -350,6 +470,14 @@ Entity SdfEntityCreator::CreateEntities(const sdf::Model *_model) auto ent = this->CreateEntities(_model, false); // Load all model plugins afterwards, so we get scoped name for nested models. + this->LoadModelPlugins(); + + return ent; +} + +////////////////////////////////////////////////// +void SdfEntityCreator::LoadModelPlugins() +{ for (const auto &[entity, plugins] : this->dataPtr->newModels) { this->dataPtr->eventManager->Emit(entity, plugins); @@ -369,8 +497,6 @@ Entity SdfEntityCreator::CreateEntities(const sdf::Model *_model) this->dataPtr->eventManager->Emit(entity, plugins); } this->dataPtr->newVisuals.clear(); - - return ent; } ////////////////////////////////////////////////// @@ -572,6 +698,13 @@ Entity SdfEntityCreator::CreateEntities(const sdf::Link *_link) linkEntity, components::WindMode(_link->EnableWind())); } + if (!_link->EnableGravity()) + { + // If disable gravity, create a GravityEnabled component to the entity + this->dataPtr->ecm->CreateComponent( + linkEntity, components::GravityEnabled(false)); + } + // Visuals for (uint64_t visualIndex = 0; visualIndex < _link->VisualCount(); ++visualIndex) @@ -801,7 +934,8 @@ Entity SdfEntityCreator::CreateEntities(const sdf::Visual *_visual) "https://gazebosim.org/api/sim/8/migrationsdf.html#:~:text=Materials " << "for details." << std::endl; std::string scriptUri = visualMaterial.ScriptUri(); - if (scriptUri != "file://media/materials/scripts/gazebo.material") { + if (scriptUri != ServerPrivate::kClassicMaterialScriptUri) + { gzwarn << "Custom material scripts are not supported." << std::endl; } @@ -871,9 +1005,27 @@ Entity SdfEntityCreator::CreateEntities(const sdf::ParticleEmitter *_emitter) // Entity Entity emitterEntity = this->dataPtr->ecm->CreateEntity(); + auto particleEmitterMsg = convert(*_emitter); + + // Update image path + // Ideally this is done by gz/sim/src/SceneManager.cc when creating + // the particle emitter. However the component stores a msg instead of + // an sdf so the sdf FilePath information is lost and rendering is not + // able to construct the full path of the image. + // \todo(iche033) Consider changing the ParticleEmitter component to + // store an sdf::ParticleEmitter object instead of msgs::ParticleEmitter. + std::string imagePath = _emitter->ColorRangeImage(); + if (!imagePath.empty()) + { + std::string path = common::findFile(asFullPath(imagePath, + _emitter->Element()->FilePath())); + path = path.empty() ? imagePath : path; + particleEmitterMsg.mutable_color_range_image()->set_data(path); + } + // Components this->dataPtr->ecm->CreateComponent(emitterEntity, - components::ParticleEmitter(convert(*_emitter))); + components::ParticleEmitter(particleEmitterMsg)); this->dataPtr->ecm->CreateComponent(emitterEntity, components::Pose(ResolveSdfPose(_emitter->SemanticPose()))); this->dataPtr->ecm->CreateComponent(emitterEntity, diff --git a/src/SdfEntityCreator_TEST.cc b/src/SdfEntityCreator_TEST.cc index d4cad99d84..e7f7527dc4 100644 --- a/src/SdfEntityCreator_TEST.cc +++ b/src/SdfEntityCreator_TEST.cc @@ -107,9 +107,9 @@ TEST_F(SdfEntityCreatorTest, CreateEntities) EXPECT_TRUE(this->ecm.HasComponentType(components::LaserRetro::typeId)); // Check entities - // 1 x world + 5 x model + 5 x link + 5 x collision + 5 x visual + + // 1 x world + 1 wind + 5 x model + 5 x link + 5 x collision + 5 x visual + // 1 x light (light + visual) - EXPECT_EQ(23u, this->ecm.EntityCount()); + EXPECT_EQ(24u, this->ecm.EntityCount()); // Check worlds unsigned int worldCount{0}; @@ -685,8 +685,9 @@ TEST_F(SdfEntityCreatorTest, CreateLights) creator.CreateEntities(root.WorldByIndex(0)); // Check entities - // 1 x world + 1 x model + 1 x link + 1 x visual + 4 x light (light + visual) - EXPECT_EQ(12u, this->ecm.EntityCount()); + // 1 x world + 1 wind + 1 x model + 1 x link + 1 x visual + + // 4 x light (light + visual) + EXPECT_EQ(13u, this->ecm.EntityCount()); // Check worlds unsigned int worldCount{0}; @@ -1107,9 +1108,9 @@ TEST_F(SdfEntityCreatorTest, RemoveEntities) creator.CreateEntities(root.WorldByIndex(0)); // Check entities - // 1 x world + 4 x model + 4 x link + 4 x collision + 4 x visual + // 1 x world + 1 wind + 4 x model + 4 x link + 4 x collision + 4 x visual // + 1 x light (light + visual) - EXPECT_EQ(23u, this->ecm.EntityCount()); + EXPECT_EQ(24u, this->ecm.EntityCount()); auto world = this->ecm.EntityByComponents(components::World()); EXPECT_NE(kNullEntity, world); @@ -1135,7 +1136,7 @@ TEST_F(SdfEntityCreatorTest, RemoveEntities) creator.RequestRemoveEntity(models.front()); this->ecm.ProcessEntityRemovals(); - EXPECT_EQ(19u, this->ecm.EntityCount()); + EXPECT_EQ(20u, this->ecm.EntityCount()); models = this->ecm.ChildrenByComponents(world, components::Model()); ASSERT_EQ(4u, models.size()); @@ -1158,7 +1159,7 @@ TEST_F(SdfEntityCreatorTest, RemoveEntities) creator.RequestRemoveEntity(models.front(), false); this->ecm.ProcessEntityRemovals(); - EXPECT_EQ(18u, this->ecm.EntityCount()); + EXPECT_EQ(19u, this->ecm.EntityCount()); // There's only 1 model left models = this->ecm.ChildrenByComponents(world, components::Model()); diff --git a/src/Server.cc b/src/Server.cc index 7aca6f33f3..712f273467 100644 --- a/src/Server.cc +++ b/src/Server.cc @@ -121,6 +121,7 @@ Server::Server(const ServerConfig &_config) } gzmsg << msg; sdf::ParserConfig sdfParserConfig = sdf::ParserConfig::GlobalConfig(); + sdfParserConfig.SetStoreResolvedURIs(true); sdfParserConfig.SetCalculateInertialConfiguration( sdf::ConfigureResolveAutoInertials::SKIP_CALCULATION_IN_LOAD); errors = this->dataPtr->sdfRoot.LoadSdfString( @@ -145,6 +146,7 @@ Server::Server(const ServerConfig &_config) sdf::Root sdfRoot; sdf::ParserConfig sdfParserConfig = sdf::ParserConfig::GlobalConfig(); + sdfParserConfig.SetStoreResolvedURIs(true); sdfParserConfig.SetCalculateInertialConfiguration( sdf::ConfigureResolveAutoInertials::SKIP_CALCULATION_IN_LOAD); @@ -156,7 +158,9 @@ Server::Server(const ServerConfig &_config) // a black screen (search for "Async resource download" in // 'src/gui_main.cc'. errors = sdfRoot.Load(filePath, sdfParserConfig); - if (errors.empty()) { + if (errors.empty() || _config.BehaviorOnSdfErrors() != + ServerConfig::SdfErrorBehavior::EXIT_IMMEDIATELY) + { if (sdfRoot.Model() == nullptr) { this->dataPtr->sdfRoot = std::move(sdfRoot); } @@ -171,7 +175,9 @@ Server::Server(const ServerConfig &_config) return; } world->AddModel(*sdfRoot.Model()); - if (errors.empty()) { + if (errors.empty() || _config.BehaviorOnSdfErrors() != + ServerConfig::SdfErrorBehavior::EXIT_IMMEDIATELY) + { errors = this->dataPtr->sdfRoot.UpdateGraphs(); } } @@ -196,7 +202,11 @@ Server::Server(const ServerConfig &_config) { for (auto &err : errors) gzerr << err << "\n"; - return; + if (_config.BehaviorOnSdfErrors() == + ServerConfig::SdfErrorBehavior::EXIT_IMMEDIATELY) + { + return; + } } // Add record plugin @@ -205,6 +215,11 @@ Server::Server(const ServerConfig &_config) this->dataPtr->AddRecordPlugin(_config); } + // If we've received a signal before we create entities, the Stop event + // won't be propagated to them. Instead, we just quit early here. + if (this->dataPtr->signalReceived) + return; + this->dataPtr->CreateEntities(); // Set the desired update period, this will override the desired RTF given in diff --git a/src/ServerConfig.cc b/src/ServerConfig.cc index b8a33fd965..c3562a77fb 100644 --- a/src/ServerConfig.cc +++ b/src/ServerConfig.cc @@ -168,9 +168,13 @@ class gz::sim::ServerConfigPrivate this->timestamp = GZ_SYSTEM_TIME(); + std::string timeInIso = common::timeToIso(this->timestamp); + #ifdef _WIN32 + std::replace(timeInIso.begin(), timeInIso.end(), ':', '-'); + #endif // Set a default log record path this->logRecordPath = common::joinPaths(home, - ".gz", "sim", "log", common::timeToIso(this->timestamp)); + ".gz", "sim", "log", timeInIso); // If directory already exists, do not overwrite. This could potentially // happen if multiple simulation instances are started in rapid @@ -208,7 +212,8 @@ class gz::sim::ServerConfigPrivate seed(_cfg->seed), logRecordTopics(_cfg->logRecordTopics), isHeadlessRendering(_cfg->isHeadlessRendering), - source(_cfg->source){ } + source(_cfg->source), + behaviorOnSdfErrors(_cfg->behaviorOnSdfErrors){ } // \brief The SDF file that the server should load public: std::string sdfFile = ""; @@ -292,6 +297,10 @@ class gz::sim::ServerConfigPrivate /// \brief Type of source used. public: ServerConfig::SourceType source{ServerConfig::SourceType::kNone}; + + /// \brief Server loading behavior in presence of SDF errors. + public: ServerConfig::SdfErrorBehavior behaviorOnSdfErrors{ + ServerConfig::SdfErrorBehavior::EXIT_IMMEDIATELY}; }; ////////////////////////////////////////////////// @@ -598,6 +607,19 @@ const std::string &ServerConfig::RenderEngineGuiApiBackend() const return this->dataPtr->renderEngineGuiApiBackend; } +////////////////////////////////////////////////// +void ServerConfig::SetBehaviorOnSdfErrors( + ServerConfig::SdfErrorBehavior _behavior) +{ + this->dataPtr->behaviorOnSdfErrors = _behavior; +} + +////////////////////////////////////////////////// +ServerConfig::SdfErrorBehavior ServerConfig::BehaviorOnSdfErrors() const +{ + return this->dataPtr->behaviorOnSdfErrors; +} + ///////////////////////////////////////////////// void ServerConfig::AddPlugin(const ServerConfig::PluginInfo &_info) { @@ -906,19 +928,6 @@ sim::loadPluginInfo(bool _isPlayback) envConfig, true); - if (!configSet) - { - configSet = common::env("IGN_GAZEBO_SERVER_CONFIG_PATH", - envConfig, - true); - if (configSet) - { - gzwarn << "Config path found using deprecated environment variable " - << "[IGN_GAZEBO_SERVER_CONFIG_PATH]. Please use " - << "[GZ_SIM_SERVER_CONFIG_PATH] instead" << std::endl; - } - } - if (configSet) { if (common::exists(envConfig)) @@ -933,7 +942,7 @@ sim::loadPluginInfo(bool _isPlayback) gzwarn << kServerConfigPathEnv << " set but no plugins found\n"; } - gzdbg << "Loaded (" << ret.size() << ") plugins from file " << + gzdbg << "Loading (" << ret.size() << ") plugins from file " << "[" << envConfig << "]\n"; return ret; @@ -1013,7 +1022,7 @@ sim::loadPluginInfo(bool _isPlayback) << "], but no plugins found\n"; } - gzdbg << "Loaded (" << ret.size() << ") plugins from file " << + gzdbg << "Loading (" << ret.size() << ") plugins from file " << "[" << defaultConfig << "]\n"; return ret; diff --git a/src/ServerPrivate.cc b/src/ServerPrivate.cc index 80df07819e..83b97363e6 100644 --- a/src/ServerPrivate.cc +++ b/src/ServerPrivate.cc @@ -37,6 +37,9 @@ using namespace gz; using namespace sim; +const char ServerPrivate::kClassicMaterialScriptUri[] = + "file://media/materials/scripts/gazebo.material"; + /// \brief This struct provides access to the record plugin SDF string struct LoggingPlugin { @@ -112,13 +115,26 @@ ServerPrivate::~ServerPrivate() ////////////////////////////////////////////////// void ServerPrivate::OnSignal(int _sig) { - gzdbg << "Server received signal[" << _sig << "]\n"; - this->Stop(); + // There's a good chance that objects are being destructed from the previous + // signal, so it's not safe to call Stop if we've done it already. + if (!this->signalReceived) + { + this->signalReceived = true; + gzdbg << "Server received signal[" << _sig << "]\n"; + this->Stop(); + } } ///////////////////////////////////////////////// void ServerPrivate::Stop() { + // Stop might be called from the signal handler thread (new in Ionic) instead + // of the main thread, so we need to ensure that we keep `ServerPrivate` alive + // while the signal handler is still active. We do that by synchronizing on + // the `runMutex` here in ServerPrivate::Run right after the call + // SimulationRunner::Run returns. That way, `ServerPrivate::Run` cannot return + // before the signal handler is finished. + std::lock_guard lock(this->runMutex); this->running = false; for (std::unique_ptr &runner : this->simRunners) { @@ -130,6 +146,13 @@ void ServerPrivate::Stop() bool ServerPrivate::Run(const uint64_t _iterations, std::optional _cond) { + // Return early if we've received a signal right before. + // The ServerPrivate signal handler would set `running=false`, + // but we immediately would set it to true here, which will essentially ignore + // the signal. Since we can't reliably use the `running` variable, we return + // if `signalReceived` is true + if (this->signalReceived) + return false; this->runMutex.lock(); this->running = true; if (_cond) @@ -185,6 +208,8 @@ bool ServerPrivate::Run(const uint64_t _iterations, result = this->workerPool.WaitForResults(); } + // See comments ServerPrivate::Stop() for why we lock this mutex here. + std::lock_guard lock(this->runMutex); this->running = false; return result; } @@ -286,14 +311,14 @@ void ServerPrivate::CreateEntities() for (uint64_t worldIndex = 0; worldIndex < this->sdfRoot.WorldCount(); ++worldIndex) { - auto world = this->sdfRoot.WorldByIndex(worldIndex); + sdf::World *world = this->sdfRoot.WorldByIndex(worldIndex); { std::lock_guard lock(this->worldsMutex); this->worldNames.push_back(world->Name()); } auto runner = std::make_unique( - world, this->systemLoader, this->config); + *world, this->systemLoader, this->config); runner->SetFuelUriMap(this->fuelUriMap); this->simRunners.push_back(std::move(runner)); } @@ -546,6 +571,12 @@ bool ServerPrivate::ResourcePathsResolveService( ////////////////////////////////////////////////// std::string ServerPrivate::FetchResource(const std::string &_uri) { + // Handle gazebo classic material URIs. + // Return original URI string as the SdfEntityCreator checks for this URI + if (_uri == kClassicMaterialScriptUri) + return _uri; + + // Fetch resource from fuel auto path = fuel_tools::fetchResourceWithClient(_uri, *this->fuelClient.get()); diff --git a/src/ServerPrivate.hh b/src/ServerPrivate.hh index 03128c56e8..7a2ad69344 100644 --- a/src/ServerPrivate.hh +++ b/src/ServerPrivate.hh @@ -169,6 +169,10 @@ namespace gz /// \brief Our signal handler. public: gz::common::SignalHandler sigHandler; + /// \brief Set to true from signal handler. This will be used to + /// terminate the server where checking `running` is not sufficient. + public: std::atomic signalReceived{false}; + /// \brief Our system loader. public: SystemLoaderPtr systemLoader; @@ -187,6 +191,11 @@ namespace gz /// Server. It is used in the SDFormat world generator when saving worlds public: std::unordered_map fuelUriMap; + /// \brief Gazebo classic material URI string + /// A URI matching this string indicates that it is a gazebo classic + /// material. + public: static const char kClassicMaterialScriptUri[]; + /// \brief List of names for all worlds loaded in this server. private: std::vector worldNames; diff --git a/src/Server_Rendering_TEST.cc b/src/Server_Rendering_TEST.cc index b82b0690ee..bad8c2d323 100644 --- a/src/Server_Rendering_TEST.cc +++ b/src/Server_Rendering_TEST.cc @@ -130,7 +130,10 @@ TEST_F(ServerFixture, GZ_UTILS_TEST_DISABLED_ON_MAC(LoadSdfModelRelativeUri)) }; gz::sim::ServerConfig serverConfig; - + serverConfig.SetBehaviorOnSdfErrors( + ServerConfig::SdfErrorBehavior::CONTINUE_LOADING); + EXPECT_EQ(ServerConfig::SdfErrorBehavior::CONTINUE_LOADING, + serverConfig.BehaviorOnSdfErrors()); serverConfig.SetSdfFile(common::joinPaths(PROJECT_SOURCE_PATH, "test", "worlds", "models", "relative_resource_uri", "model2.sdf")); @@ -162,4 +165,16 @@ TEST_F(ServerFixture, GZ_UTILS_TEST_DISABLED_ON_MAC(LoadSdfModelRelativeUri)) } ASSERT_TRUE(server.RunOnce()); ASSERT_TRUE(server.RunOnce(false)); + + // Tell server to stop loading if there are SDF errors + // Server should not load because V2's visual mesh URI can not be resolved + serverConfig.SetBehaviorOnSdfErrors( + ServerConfig::SdfErrorBehavior::EXIT_IMMEDIATELY); + EXPECT_EQ(ServerConfig::SdfErrorBehavior::EXIT_IMMEDIATELY, + serverConfig.BehaviorOnSdfErrors()); + sim::Server server2 = Server(serverConfig); + EXPECT_FALSE(server2.HasEntity("relative_resource_uri")); + EXPECT_FALSE(server2.HasEntity("L1")); + EXPECT_FALSE(server2.HasEntity("V1")); + EXPECT_FALSE(server2.HasEntity("V2")); } diff --git a/src/Server_TEST.cc b/src/Server_TEST.cc index e2fba5e054..5f7697eeab 100644 --- a/src/Server_TEST.cc +++ b/src/Server_TEST.cc @@ -481,6 +481,7 @@ TEST_P(ServerFixture, sim::Server server(serverConfig); EXPECT_EQ(0u, *server.IterationCount()); EXPECT_EQ(3u, *server.EntityCount()); + EXPECT_EQ(4u, *server.SystemCount()); } @@ -776,6 +777,7 @@ TEST_P(ServerFixture, SigInt) EXPECT_TRUE(*server.Running(0)); std::raise(SIGTERM); + GZ_SLEEP_MS(20); EXPECT_FALSE(server.Running()); EXPECT_FALSE(*server.Running(0)); @@ -880,6 +882,7 @@ TEST_P(ServerFixture, GZ_UTILS_TEST_DISABLED_ON_WIN32(AddSystemWhileRunning)) // Stop the server std::raise(SIGTERM); + GZ_SLEEP_MS(20); EXPECT_FALSE(server.Running()); EXPECT_FALSE(*server.Running(0)); @@ -1012,8 +1015,11 @@ TEST_P(ServerFixture, GZ_UTILS_TEST_DISABLED_ON_WIN32(ResourcePath)) if (mesh) { - EXPECT_EQ("model://scheme_resource_uri/meshes/box.dae", - mesh->Uri()); + // StoreResolvedURIs is set to true so expect full path + EXPECT_NE(std::string::npos, + mesh->Uri().find("scheme_resource_uri/meshes/box.dae")); + EXPECT_FALSE(common::isRelativePath(mesh->Uri())); + EXPECT_TRUE(common::isFile(mesh->Uri())); } eachCount++; diff --git a/src/SimulationRunner.cc b/src/SimulationRunner.cc index a5ba51e96c..baa2712adc 100644 --- a/src/SimulationRunner.cc +++ b/src/SimulationRunner.cc @@ -18,6 +18,7 @@ #include "SimulationRunner.hh" #include +#include #ifdef HAVE_PYBIND11 #include #endif @@ -33,8 +34,10 @@ #include #include +#include #include "gz/common/Profiler.hh" +#include "gz/sim/Constants.hh" #include "gz/sim/components/Model.hh" #include "gz/sim/components/Name.hh" #include "gz/sim/components/Sensor.hh" @@ -43,8 +46,13 @@ #include "gz/sim/components/ParentEntity.hh" #include "gz/sim/components/Physics.hh" #include "gz/sim/components/PhysicsCmd.hh" +#include "gz/sim/components/PhysicsEnginePlugin.hh" #include "gz/sim/components/Recreate.hh" +#include "gz/sim/components/RenderEngineGuiPlugin.hh" +#include "gz/sim/components/RenderEngineServerHeadless.hh" +#include "gz/sim/components/RenderEngineServerPlugin.hh" #include "gz/sim/Events.hh" +#include "gz/sim/ServerConfig.hh" #include "gz/sim/SdfEntityCreator.hh" #include "gz/sim/Util.hh" #include "gz/transport/TopicUtils.hh" @@ -89,27 +97,18 @@ struct MaybeGilScopedRelease ////////////////////////////////////////////////// -SimulationRunner::SimulationRunner(const sdf::World *_world, +SimulationRunner::SimulationRunner(const sdf::World &_world, const SystemLoaderPtr &_systemLoader, const ServerConfig &_config) - // \todo(nkoenig) Either copy the world, or add copy constructor to the - // World and other elements. - : sdfWorld(_world), serverConfig(_config) + : sdfWorld(_world), serverConfig(_config) { - if (nullptr == _world) - { - gzerr << "Can't start simulation runner with null world." << std::endl; - return; - } - // Keep world name - this->worldName = transport::TopicUtils::AsValidTopic(_world->Name()); + this->worldName = transport::TopicUtils::AsValidTopic(_world.Name()); - auto validWorldName = transport::TopicUtils::AsValidTopic(worldName); if (this->worldName.empty()) { gzerr << "Can't start simulation runner with this world name [" - << worldName << "]." << std::endl; + << _world.Name() << "]." << std::endl; return; } @@ -119,10 +118,10 @@ SimulationRunner::SimulationRunner(const sdf::World *_world, // Get the physics profile // TODO(luca): remove duplicated logic in SdfEntityCreator and LevelManager - auto physics = _world->PhysicsByIndex(0); + const sdf::Physics *physics = _world.PhysicsByIndex(0); if (!physics) { - physics = _world->PhysicsDefault(); + physics = _world.PhysicsDefault(); } // Step size @@ -208,9 +207,6 @@ SimulationRunner::SimulationRunner(const sdf::World *_world, std::bind(&SimulationRunner::LoadPlugins, this, std::placeholders::_1, std::placeholders::_2)); - // Create the level manager - this->levelMgr = std::make_unique(this, _config.UseLevels()); - // Check if this is going to be a distributed runner // Attempt to create the manager based on environment variables. // If the configuration is invalid, then networkMgr will be `nullptr`. @@ -248,27 +244,11 @@ SimulationRunner::SimulationRunner(const sdf::World *_world, } } - // Load the active levels - this->levelMgr->UpdateLevelsState(); - - // Store the initial state of the ECM; - this->initialEntityCompMgr.CopyFrom(this->entityCompMgr); - - // Load any additional plugins from the Server Configuration - this->LoadServerPlugins(this->serverConfig.Plugins()); - - // If we have reached this point and no world systems have been loaded, then - // load a default set of systems. - if (this->systemMgr->TotalByEntity( - worldEntity(this->entityCompMgr)).empty()) - { - gzmsg << "No systems loaded from SDF, loading defaults" << std::endl; - bool isPlayback = !this->serverConfig.LogPlaybackPath().empty(); - auto plugins = sim::loadPluginInfo(isPlayback); - this->LoadServerPlugins(plugins); - } + // Create the level manager + this->levelMgr = std::make_unique(this, + this->serverConfig.UseLevels()); - this->LoadLoggingPlugins(this->serverConfig); + this->CreateEntities(_world); // TODO(louise) Combine both messages into one. this->node->Advertise("control", &SimulationRunner::OnWorldControl, this); @@ -283,9 +263,21 @@ SimulationRunner::SimulationRunner(const sdf::World *_world, // Publish empty GUI messages for worlds that have no GUI in the beginning. // In the future, support modifying GUI from the server at runtime. - if (_world->Gui()) + if (_world.Gui()) { - this->guiMsg = convert(*_world->Gui()); + this->guiMsg = convert(*_world.Gui()); + + auto worldElem = this->sdfWorld.Element(); + if (worldElem) + { + auto policies = worldElem->FindElement("gz:policies"); + if (policies) + { + auto headerData = this->guiMsg.mutable_header()->add_data(); + headerData->set_key("gz:policies"); + headerData->add_value(policies->ToString("")); + } + } } std::string infoService{"gui/info"}; @@ -294,7 +286,7 @@ SimulationRunner::SimulationRunner(const sdf::World *_world, gzmsg << "Serving GUI information on [" << opts.NameSpace() << "/" << infoService << "]" << std::endl; - gzmsg << "World [" << _world->Name() << "] initialized with [" + gzmsg << "World [" << this->worldName << "] initialized with [" << physics->Name() << "] physics profile." << std::endl; std::string genWorldSdfService{"generate_world_sdf"}; @@ -533,12 +525,15 @@ void SimulationRunner::ProcessSystemQueue() { auto pending = this->systemMgr->PendingCount(); - if (0 == pending) + if (0 == pending && !this->threadsNeedCleanUp) return; - // If additional systems are to be added, stop the existing threads. + // If additional systems are to be added or have been removed, + // stop the existing threads. this->StopWorkerThreads(); + this->threadsNeedCleanUp = false; + this->systemMgr->ActivatePendingSystems(); unsigned int threadCount = @@ -597,14 +592,24 @@ void SimulationRunner::UpdateSystems() { GZ_PROFILE("PreUpdate"); - for (auto& system : this->systemMgr->SystemsPreUpdate()) - system->PreUpdate(this->currentInfo, this->entityCompMgr); + for (auto& [priority, systems] : this->systemMgr->SystemsPreUpdate()) + { + for (auto& system : systems) + { + system->PreUpdate(this->currentInfo, this->entityCompMgr); + } + } } { GZ_PROFILE("Update"); - for (auto& system : this->systemMgr->SystemsUpdate()) - system->Update(this->currentInfo, this->entityCompMgr); + for (auto& [priority, systems] : this->systemMgr->SystemsUpdate()) + { + for (auto& system : systems) + { + system->Update(this->currentInfo, this->entityCompMgr); + } + } } { @@ -922,6 +927,8 @@ void SimulationRunner::Step(const UpdateInfo &_info) this->ProcessRecreateEntitiesCreate(); // Process entity removals. + this->systemMgr->ProcessRemovedEntities(this->entityCompMgr, + this->threadsNeedCleanUp); this->entityCompMgr.ProcessRemoveEntityRequests(); // Process components removals @@ -1467,19 +1474,7 @@ bool SimulationRunner::RequestRemoveEntity(const std::string &_name, std::optional SimulationRunner::EntityByName( const std::string &_name) const { - std::optional entity; - this->entityCompMgr.Each([&](const Entity _entity, - const components::Name *_entityName)->bool - { - if (_entityName->Data() == _name) - { - entity = _entity; - return false; - } - return true; - }); - - return entity; + return this->entityCompMgr.EntityByName(_name); } ///////////////////////////////////////////////// @@ -1547,3 +1542,122 @@ void SimulationRunner::SetNextStepAsBlockingPaused(const bool value) { this->blockingPausedStepPending = value; } + +////////////////////////////////////////////////// +void SimulationRunner::CreateEntities(const sdf::World &_world) +{ + this->sdfWorld = _world; + + // Instantiate the SDF creator + auto creator = std::make_unique(this->entityCompMgr, + this->eventMgr); + + // We create the world entity so that the simulation runner can inject + // some components + Entity worldEntity = this->entityCompMgr.CreateEntity(); + this->entityCompMgr.CreateComponent(worldEntity, components::World()); + + // 1. Level manager read performers and levels. Add components to the + // performers and levels so that the SdfEntityCreator knows whether to + // create them or not. Make sure to set parents properly + // 2. Create entities. + + // Read the level information. This will create components containing + // information about which entities should be created for the current + // level. + this->levelMgr->ReadLevelPerformerInfo(this->sdfWorld); + + // Configure the default level + this->levelMgr->ConfigureDefaultLevel(); + + // Create components based on the contents of the server configuration. + this->entityCompMgr.CreateComponent(worldEntity, + components::PhysicsEnginePlugin(this->serverConfig.PhysicsEngine())); + + this->entityCompMgr.CreateComponent(worldEntity, + components::RenderEngineServerPlugin( + this->serverConfig.RenderEngineServer())); + + this->entityCompMgr.CreateComponent(worldEntity, + components::RenderEngineServerHeadless( + this->serverConfig.HeadlessRendering())); + + this->entityCompMgr.CreateComponent(worldEntity, + components::RenderEngineGuiPlugin( + this->serverConfig.RenderEngineGui())); + + // Load the world entities from SDF + creator->CreateEntities(&this->sdfWorld, worldEntity); + + // Load the active levels + this->levelMgr->UpdateLevelsState(); + + // Some entities and component should be removed based on the levels. + this->entityCompMgr.ProcessRemoveEntityRequests(); + this->entityCompMgr.ClearRemovedComponents(); + + // Load any additional plugins from the Server Configuration + this->LoadServerPlugins(this->serverConfig.Plugins()); + + auto loadedWorldPlugins = this->systemMgr->TotalByEntity(worldEntity); + // If we have reached this point and no world systems have been loaded, then + // load a default set of systems. + + auto worldElem = this->sdfWorld.Element(); + bool includeServerConfigPlugins = true; + if (worldElem) + { + auto policies = worldElem->FindElement(std::string(kPoliciesTag)); + if (policies) + { + includeServerConfigPlugins = + policies + ->Get("include_server_config_plugins", includeServerConfigPlugins) + .first; + } + } + + if (includeServerConfigPlugins || loadedWorldPlugins.empty()) + { + bool isPlayback = !this->serverConfig.LogPlaybackPath().empty(); + auto defaultPlugins = gz::sim::loadPluginInfo(isPlayback); + if (loadedWorldPlugins.empty()) + { + gzmsg << "No systems loaded from SDF, loading defaults" << std::endl; + } + else + { + std::unordered_set loadedWorldPluginFileNames; + for (const auto &pl : loadedWorldPlugins) + { + loadedWorldPluginFileNames.insert(pl.fname); + } + auto isPluginLoaded = + [&loadedWorldPluginFileNames](const ServerConfig::PluginInfo &_pl) + { + return loadedWorldPluginFileNames.count(_pl.Plugin().Filename()) != 0; + }; + + // Remove plugin if it's already loaded so as to not duplicate world + // plugins. + defaultPlugins.remove_if(isPluginLoaded); + + gzdbg << "Additional plugins to load:\n"; + for (const auto &plugin : defaultPlugins) + { + gzdbg << plugin.Plugin().Name() << " " << plugin.Plugin().Filename() + << "\n"; + } + } + + this->LoadServerPlugins(defaultPlugins); + // Load logging plugins after all server plugins so that necessary + // plugins such as SceneBroadcaster are loaded first. This might be + // a bug or an assumption made in the logging plugins. + this->LoadLoggingPlugins(this->serverConfig); + + }; + + // Store the initial state of the ECM; + this->initialEntityCompMgr.CopyFrom(this->entityCompMgr); +} diff --git a/src/SimulationRunner.hh b/src/SimulationRunner.hh index 438fc329ba..8fe03511e7 100644 --- a/src/SimulationRunner.hh +++ b/src/SimulationRunner.hh @@ -77,7 +77,7 @@ namespace gz /// \param[in] _world Pointer to the SDF world. /// \param[in] _systemLoader Reference to system manager. /// \param[in] _useLevels Whether to use levles or not. False by default. - public: explicit SimulationRunner(const sdf::World *_world, + public: explicit SimulationRunner(const sdf::World &_world, const SystemLoaderPtr &_systemLoader, const ServerConfig &_config = ServerConfig()); @@ -151,8 +151,8 @@ namespace gz const sdf::Plugins &_plugins); /// \brief Load server plugins for a given entity. - /// \param[in] _config Configuration to load plugins from. - /// plugins based on the _config contents + /// \param[in] _plugins Load any additional plugins from the + /// Server Configuration public: void LoadServerPlugins( const std::list &_plugins); @@ -373,6 +373,11 @@ namespace gz /// Physics component of the world, if any. public: void UpdatePhysicsParams(); + /// \brief Create entities for the world simulated by this runner based + /// on the provided SDF Root object. + /// \param[in] _world SDF world created entities from. + public: void CreateEntities(const sdf::World &_world); + /// \brief Process entities with the components::Recreate component. /// Put in a request to make them as removed private: void ProcessRecreateEntitiesRemove(); @@ -477,8 +482,8 @@ namespace gz /// \brief Connection to the load plugins event. private: common::ConnectionPtr loadPluginsConn; - /// \brief Pointer to the sdf::World object of this runner - private: const sdf::World *sdfWorld; + /// \brief The sdf::World object of this runner + private: sdf::World sdfWorld; /// \brief The real time factor calculated based on sim and real time /// averages. @@ -543,6 +548,9 @@ namespace gz /// at the appropriate time. private: std::unique_ptr newWorldControlState; + /// \brief Set if we need to remove systems due to entity removal + private: bool threadsNeedCleanUp{false}; + private: bool resetInitiated{false}; friend class LevelManager; }; diff --git a/src/SimulationRunner_TEST.cc b/src/SimulationRunner_TEST.cc index 10ca3a9406..11bf658287 100644 --- a/src/SimulationRunner_TEST.cc +++ b/src/SimulationRunner_TEST.cc @@ -16,7 +16,6 @@ */ #include - #include #include @@ -111,7 +110,6 @@ void rootClockCb(const msgs::Clock &_msg) rootClockMsgs.push_back(_msg); } - ///////////////////////////////////////////////// TEST_P(SimulationRunnerTest, CreateEntities) { @@ -124,7 +122,7 @@ TEST_P(SimulationRunnerTest, CreateEntities) // Create simulation runner auto systemLoader = std::make_shared(); - SimulationRunner runner(root.WorldByIndex(0), systemLoader); + SimulationRunner runner(*root.WorldByIndex(0), systemLoader); // Check component types EXPECT_TRUE(runner.EntityCompMgr().HasComponentType( @@ -658,7 +656,7 @@ TEST_P(SimulationRunnerTest, CreateLights) // Create simulation runner auto systemLoader = std::make_shared(); - SimulationRunner runner(root.WorldByIndex(0), systemLoader); + SimulationRunner runner(*root.WorldByIndex(0), systemLoader); // Check entities // 1 x world + 1 x (default) level + 1 x wind + 1 x model + 1 x link + 1 x @@ -928,7 +926,7 @@ TEST_P(SimulationRunnerTest, CreateJointEntities) // Create simulation runner auto systemLoader = std::make_shared(); - SimulationRunner runner(root.WorldByIndex(0), systemLoader); + SimulationRunner runner(*root.WorldByIndex(0), systemLoader); // Check component types EXPECT_TRUE(runner.EntityCompMgr().HasComponentType( @@ -1073,7 +1071,7 @@ TEST_P(SimulationRunnerTest, Time) // Create simulation runner auto systemLoader = std::make_shared(); - SimulationRunner runner(root.WorldByIndex(0), systemLoader); + SimulationRunner runner(*root.WorldByIndex(0), systemLoader); // Check state EXPECT_TRUE(runner.Paused()); @@ -1202,7 +1200,7 @@ TEST_P(SimulationRunnerTest, GZ_UTILS_TEST_DISABLED_ON_WIN32(LoadPlugins) ) // Create simulation runner auto systemLoader = std::make_shared(); - SimulationRunner runner(root.WorldByIndex(0), systemLoader); + SimulationRunner runner(*root.WorldByIndex(0), systemLoader); // Get world entity Entity worldId{kNullEntity}; @@ -1302,7 +1300,7 @@ TEST_P(SimulationRunnerTest, // Create simulation runner auto systemLoader = std::make_shared(); - SimulationRunner runner(rootWithout.WorldByIndex(0), systemLoader, + SimulationRunner runner(*rootWithout.WorldByIndex(0), systemLoader, serverConfig); ASSERT_EQ(2u, runner.SystemCount()); @@ -1333,7 +1331,7 @@ TEST_P(SimulationRunnerTest, // Create simulation runner auto systemLoader = std::make_shared(); - SimulationRunner runner(rootWithout.WorldByIndex(0), systemLoader, + SimulationRunner runner(*rootWithout.WorldByIndex(0), systemLoader, serverConfig); // Get world entity @@ -1414,7 +1412,7 @@ TEST_P(SimulationRunnerTest, // Create simulation runner auto systemLoader = std::make_shared(); - SimulationRunner runner(rootWithout.WorldByIndex(0), systemLoader); + SimulationRunner runner(*rootWithout.WorldByIndex(0), systemLoader); ASSERT_EQ(3u, runner.SystemCount()); common::unsetenv(kServerConfigPathEnv); } @@ -1431,7 +1429,7 @@ TEST_P(SimulationRunnerTest, // Create simulation runner auto systemLoader = std::make_shared(); - SimulationRunner runner(rootWithout.WorldByIndex(0), systemLoader); + SimulationRunner runner(*rootWithout.WorldByIndex(0), systemLoader); runner.SetPaused(false); // Get model entities @@ -1484,8 +1482,7 @@ TEST_P(SimulationRunnerTest, EXPECT_TRUE(runner.EntityCompMgr().EntityHasComponentType(sphereEntity, componentId)) << componentId; - // Remove entities that have plugin - this is not unloading or destroying - // the plugin though! + // Remove entities that have plugin auto entityCount = runner.EntityCompMgr().EntityCount(); const_cast( runner.EntityCompMgr()).RequestRemoveEntity(boxEntity); @@ -1530,11 +1527,19 @@ TEST_P(SimulationRunnerTest, // Create simulation runner auto systemLoader = std::make_shared(); - SimulationRunner runner(rootWithout.WorldByIndex(0), systemLoader, + SimulationRunner runner(*rootWithout.WorldByIndex(0), systemLoader, serverConfig); - // 1 model plugin from SDF and 2 world plugins from config - ASSERT_EQ(3u, runner.SystemCount()); + // 1 model plugin from SDF and 1 world plugin from config + // and 1 model plugin from theconfig + EXPECT_EQ(3u, runner.SystemCount()); + runner.SetPaused(false); + runner.Run(1); + + // Remove the model. Only 1 world plugin should remain. + EXPECT_TRUE(runner.RequestRemoveEntity("box")); + runner.Run(2); + EXPECT_EQ(1u, runner.SystemCount()); } ///////////////////////////////////////////////// @@ -1549,7 +1554,7 @@ TEST_P(SimulationRunnerTest, GuiInfo) // Create simulation runner auto systemLoader = std::make_shared(); - SimulationRunner runner(root.WorldByIndex(0), systemLoader); + SimulationRunner runner(*root.WorldByIndex(0), systemLoader); // Create requester transport::Node node; @@ -1586,7 +1591,7 @@ TEST_P(SimulationRunnerTest, GenerateWorldSdf) // Create simulation runner auto systemLoader = std::make_shared(); - SimulationRunner runner(root.WorldByIndex(0), systemLoader); + SimulationRunner runner(*root.WorldByIndex(0), systemLoader); msgs::SdfGeneratorConfig req; msgs::StringMsg genWorldSdf; @@ -1635,7 +1640,7 @@ TEST_P(SimulationRunnerTest, GeneratedSdfHasNoSpuriousPlugins) // Create simulation runner auto systemLoader = std::make_shared(); - SimulationRunner runner(root.WorldByIndex(0), systemLoader); + SimulationRunner runner(*root.WorldByIndex(0), systemLoader); msgs::SdfGeneratorConfig req; msgs::StringMsg genWorldSdf; diff --git a/src/SystemInternal.hh b/src/SystemInternal.hh index 0f4d442e4d..17b32b6758 100644 --- a/src/SystemInternal.hh +++ b/src/SystemInternal.hh @@ -48,6 +48,8 @@ namespace gz configure(systemPlugin->QueryInterface()), configureParameters( systemPlugin->QueryInterface()), + configurePriority( + systemPlugin->QueryInterface()), reset(systemPlugin->QueryInterface()), preupdate(systemPlugin->QueryInterface()), update(systemPlugin->QueryInterface()), @@ -66,6 +68,8 @@ namespace gz configure(dynamic_cast(_system.get())), configureParameters( dynamic_cast(_system.get())), + configurePriority( + dynamic_cast(_system.get())), reset(dynamic_cast(_system.get())), preupdate(dynamic_cast(_system.get())), update(dynamic_cast(_system.get())), @@ -95,6 +99,11 @@ namespace gz /// Will be nullptr if the System doesn't implement this interface. public: ISystemConfigureParameters *configureParameters = nullptr; + /// \brief Access this system via the ISystemConfigurePriority + /// interface. + /// Will be nullptr if the System doesn't implement this interface. + public: ISystemConfigurePriority *configurePriority = nullptr; + /// \brief Access this system via the ISystemReset interface /// Will be nullptr if the System doesn't implement this interface. public: ISystemReset *reset = nullptr; diff --git a/src/SystemLoader.cc b/src/SystemLoader.cc index 582ebbbabf..23409c2188 100644 --- a/src/SystemLoader.cc +++ b/src/SystemLoader.cc @@ -64,8 +64,7 @@ class gz::sim::SystemLoaderPrivate public: bool InstantiateSystemPlugin(const sdf::Plugin &_sdfPlugin, gz::plugin::PluginPtr &_gzPlugin) { - // Deprecated: accept ignition-gazebo-prefixed systems. Remove this on - // gz-sim9 + // Deprecated: accept ignition-gazebo-prefixed systems. std::string deprecatedPrefix{"ignition-gazebo"}; auto filename = _sdfPlugin.Filename(); auto pos = filename.find(deprecatedPrefix); @@ -125,7 +124,7 @@ class gz::sim::SystemLoaderPrivate std::string pluginToInstantiate = _sdfPlugin.Name().empty() ? pluginName : _sdfPlugin.Name(); - // Deprecated: accept ignition plugins. Remove this on gz-sim9 + // Deprecated: accept ignition plugins. std::string deprecatedPluginNamePrefix{"ignition::gazebo"}; pos = pluginToInstantiate.find(deprecatedPluginNamePrefix); if (pos != std::string::npos) @@ -200,7 +199,6 @@ class gz::sim::SystemLoaderPrivate // Default plugin search path environment variable public: std::string pluginPathEnv{"GZ_SIM_SYSTEM_PLUGIN_PATH"}; - public: std::string pluginPathEnvDeprecated{"IGN_GAZEBO_SYSTEM_PLUGIN_PATH"}; /// \brief Plugin loader instace public: gz::plugin::Loader loader; diff --git a/src/SystemManager.cc b/src/SystemManager.cc index fd43f5330d..dcb24902c1 100644 --- a/src/SystemManager.cc +++ b/src/SystemManager.cc @@ -16,12 +16,18 @@ */ #include +#include #include +#include +#include #include +#include "SystemInternal.hh" #include "gz/sim/components/SystemPluginInfo.hh" #include "gz/sim/Conversions.hh" +#include "gz/sim/System.hh" +#include "gz/sim/Util.hh" #include "SystemManager.hh" using namespace gz; @@ -106,6 +112,24 @@ size_t SystemManager::ActivatePendingSystems() { this->systems.push_back(system); + PriorityType p {System::kDefaultPriority}; + if (system.configurePriority) + { + p = system.configurePriority->ConfigurePriority(); + } + const std::string kPriorityElementName + {gz::sim::System::kPriorityElementName}; + if (system.configureSdf && + system.configureSdf->HasElement(kPriorityElementName)) + { + PriorityType newPriority = + system.configureSdf->Get(kPriorityElementName); + gzdbg << "Changing priority for system [" << system.name + << "] from {" << p + << "} to {" << newPriority << "}\n"; + p = newPriority; + } + if (system.configure) this->systemsConfigure.push_back(system.configure); @@ -116,13 +140,21 @@ size_t SystemManager::ActivatePendingSystems() this->systemsReset.push_back(system.reset); if (system.preupdate) - this->systemsPreupdate.push_back(system.preupdate); + { + this->systemsPreupdate.try_emplace(p); + this->systemsPreupdate[p].push_back(system.preupdate); + } if (system.update) - this->systemsUpdate.push_back(system.update); + { + this->systemsUpdate.try_emplace(p); + this->systemsUpdate[p].push_back(system.update); + } if (system.postupdate) + { this->systemsPostupdate.push_back(system.postupdate); + } } this->pendingSystems.clear(); @@ -282,6 +314,7 @@ const std::vector& SystemManager::SystemsConfigure() return this->systemsConfigure; } +////////////////////////////////////////////////// const std::vector& SystemManager::SystemsConfigureParameters() { @@ -295,13 +328,15 @@ const std::vector &SystemManager::SystemsReset() } ////////////////////////////////////////////////// -const std::vector& SystemManager::SystemsPreUpdate() +const SystemManager::PrioritizedSystems& +SystemManager::SystemsPreUpdate() { return this->systemsPreupdate; } ////////////////////////////////////////////////// -const std::vector& SystemManager::SystemsUpdate() +const SystemManager::PrioritizedSystems& +SystemManager::SystemsUpdate() { return this->systemsUpdate; } @@ -334,6 +369,11 @@ bool SystemManager::EntitySystemAddService(const msgs::EntityPlugin_V &_req, { std::lock_guard lock(this->systemsMsgMutex); this->systemsToAdd.push_back(_req); + + // The response is set to true to indicate that the service request is + // handled but it does not necessarily mean the system is added + // successfully + // \todo(iche033) Return false if system is not added successfully? _res.set_data(true); return true; } @@ -393,8 +433,21 @@ void SystemManager::ProcessPendingEntitySystems() if (req.plugins().empty()) { - gzwarn << "Unable to add plugins to Entity: '" << entity - << "'. No plugins specified." << std::endl; + gzerr << "Unable to add plugins to Entity: '" << entity + << "'. No plugins specified." << std::endl; + continue; + } + + // set to world entity if entity id is not specified in the request. + if (entity == kNullEntity || entity == 0u) + { + entity = worldEntity(*this->entityCompMgr); + } + // otherwise check if entity exists before attempting to load the plugin. + else if (!this->entityCompMgr->HasEntity(entity)) + { + gzerr << "Unable to add plugins to Entity: '" << entity + << "'. Entity does not exist." << std::endl; continue; } @@ -409,3 +462,147 @@ void SystemManager::ProcessPendingEntitySystems() } this->systemsToAdd.clear(); } + +template +struct identity +{ + using type = T; +}; + +////////////////////////////////////////////////// +/// TODO(arjo) - When we move to C++20 we can just use erase_if +/// Removes all items that match the given predicate. +/// This function runs in O(n) time and uses memory in-place +template +void RemoveFromVectorIf(std::vector& vec, + typename identity>::type pred) +{ + vec.erase(std::remove_if(vec.begin(), vec.end(), pred), vec.end()); +} + +////////////////////////////////////////////////// +void SystemManager::ProcessRemovedEntities( + const EntityComponentManager &_ecm, + bool &_needsCleanUp) +{ + // Note: This function has O(n) time when an entity is removed + // where n is number of systems. Ideally we would only iterate + // over entities marked for removal but that would involve having + // a key value map. This can be marked as a future improvement. + if (!_ecm.HasEntitiesMarkedForRemoval()) + { + return; + } + + std::unordered_set resetSystemsToBeRemoved; + std::unordered_set preupdateSystemsToBeRemoved; + std::unordered_set updateSystemsToBeRemoved; + std::unordered_set postupdateSystemsToBeRemoved; + std::unordered_set configureSystemsToBeRemoved; + std::unordered_set + configureParametersSystemsToBeRemoved; + for (const auto &system : this->systems) + { + if (_ecm.IsMarkedForRemoval(system.parentEntity)) + { + if (system.reset) + { + resetSystemsToBeRemoved.insert(system.reset); + } + if (system.preupdate) + { + preupdateSystemsToBeRemoved.insert(system.preupdate); + } + if (system.update) + { + updateSystemsToBeRemoved.insert(system.update); + } + if (system.postupdate) + { + postupdateSystemsToBeRemoved.insert(system.postupdate); + // If system with a PostUpdate is marked for removal + // mark all worker threads for removal. + _needsCleanUp = true; + } + if (system.configure) + { + configureSystemsToBeRemoved.insert(system.configure); + } + if (system.configureParameters) + { + configureParametersSystemsToBeRemoved.insert( + system.configureParameters); + } + } + } + + RemoveFromVectorIf(this->systemsReset, + [&](const auto system) { + if (resetSystemsToBeRemoved.count(system)) { + return true; + } + return false; + }); + for (auto it = this->systemsPreupdate.begin(); + it != this->systemsPreupdate.end();) + { + RemoveFromVectorIf(it->second, + [&](const auto& system) { + if (preupdateSystemsToBeRemoved.count(system)) { + return true; + } + return false; + }); + if (it->second.empty()) + it = this->systemsPreupdate.erase(it); + else + ++it; + } + for (auto it = this->systemsUpdate.begin(); + it != this->systemsUpdate.end();) + { + RemoveFromVectorIf(it->second, + [&](const auto& system) { + if (updateSystemsToBeRemoved.count(system)) { + return true; + } + return false; + }); + if (it->second.empty()) + it = this->systemsUpdate.erase(it); + else + ++it; + } + + RemoveFromVectorIf(this->systemsPostupdate, + [&](const auto& system) { + if (postupdateSystemsToBeRemoved.count(system)) { + return true; + } + return false; + }); + RemoveFromVectorIf(this->systemsConfigure, + [&](const auto& system) { + if (configureSystemsToBeRemoved.count(system)) { + return true; + } + return false; + }); + RemoveFromVectorIf(this->systemsConfigureParameters, + [&](const auto& system) { + if (configureParametersSystemsToBeRemoved.count(system)) { + return true; + } + return false; + }); + RemoveFromVectorIf(this->systems, + [&](const SystemInternal& _arg) { + return _ecm.IsMarkedForRemoval(_arg.parentEntity); + }); + + std::lock_guard lock(this->pendingSystemsMutex); + RemoveFromVectorIf(this->pendingSystems, + [&](const SystemInternal& _system) { + return _ecm.IsMarkedForRemoval(_system.parentEntity); + }); +} diff --git a/src/SystemManager.hh b/src/SystemManager.hh index acd82c09dc..c9c61c7944 100644 --- a/src/SystemManager.hh +++ b/src/SystemManager.hh @@ -19,6 +19,8 @@ #include +#include +#include #include #include #include @@ -29,6 +31,7 @@ #include "gz/sim/config.hh" #include "gz/sim/EntityComponentManager.hh" #include "gz/sim/Export.hh" +#include "gz/sim/System.hh" #include "gz/sim/SystemLoader.hh" #include "gz/sim/Types.hh" @@ -44,6 +47,13 @@ namespace gz /// \brief Used to load / unload sysetms as well as iterate over them. class GZ_SIM_VISIBLE SystemManager { + /// \brief Ordered map of priority values to a vector of System + /// interfaces. + using PriorityType = System::PriorityType; + template + class PrioritizedSystems : public std::map> + {}; + /// \brief Constructor /// \param[in] _systemLoader A pointer to a SystemLoader to load plugins /// from files @@ -116,35 +126,45 @@ namespace gz /// \return Vector of systems' configure interfaces. public: const std::vector& SystemsConfigure(); - /// \brief Get an vector of all active systems implementing + /// \brief Get a vector of all active systems implementing /// "ConfigureParameters" /// \return Vector of systems's configure interfaces. public: const std::vector& SystemsConfigureParameters(); - /// \brief Get an vector of all active systems implementing "Reset" + /// \brief Get a vector of all active systems implementing "Reset" /// \return Vector of systems' reset interfaces. public: const std::vector& SystemsReset(); - /// \brief Get an vector of all active systems implementing "PreUpdate" - /// \return Vector of systems's pre-update interfaces. - public: const std::vector& SystemsPreUpdate(); + /// \brief Get an ordered map of systems by priority that implement + /// "PreUpdate" + /// \return Priortized map of systems's pre-update interfaces. + public: const PrioritizedSystems& SystemsPreUpdate(); - /// \brief Get an vector of all active systems implementing "Update" - /// \return Vector of systems's update interfaces. - public: const std::vector& SystemsUpdate(); + /// \brief Get an ordered map of systems by priority that implement + /// "Update" + /// \return Priortized map of systems's update interfaces. + public: const PrioritizedSystems& SystemsUpdate(); - /// \brief Get an vector of all active systems implementing "PostUpdate" + /// \brief Get a vector of all active systems implementing "PostUpdate" /// \return Vector of systems's post-update interfaces. public: const std::vector& SystemsPostUpdate(); - /// \brief Get an vector of all systems attached to a given entity. + /// \brief Get a vector of all systems attached to a given entity. /// \return Vector of systems. public: std::vector TotalByEntity(Entity _entity); /// \brief Process system messages and add systems to entities public: void ProcessPendingEntitySystems(); + /// \brief Remove systems that are attached to removed entities + /// \param[in] _entityCompMgr - ECM with entities marked for removal + /// \param[out] _needsCleanUp - Set to true if a system with a + /// PostUpdate was removed, and its thread needs to be terminated + public: void ProcessRemovedEntities( + const EntityComponentManager &_entityCompMgr, + bool &_needsCleanUp); + /// \brief Implementation for AddSystem functions that takes an SDF /// element. This calls the AddSystemImpl that accepts an SDF Plugin. /// \param[in] _system Generic representation of a system. @@ -197,10 +217,10 @@ namespace gz private: std::vector systemsReset; /// \brief Systems implementing PreUpdate - private: std::vector systemsPreupdate; + private: PrioritizedSystems systemsPreupdate; /// \brief Systems implementing Update - private: std::vector systemsUpdate; + private: PrioritizedSystems systemsUpdate; /// \brief Systems implementing PostUpdate private: std::vector systemsPostupdate; diff --git a/src/SystemManager_TEST.cc b/src/SystemManager_TEST.cc index 5026842a9f..956cb06810 100644 --- a/src/SystemManager_TEST.cc +++ b/src/SystemManager_TEST.cc @@ -28,6 +28,8 @@ using namespace gz::sim; +constexpr System::PriorityType kPriority = 64; + ///////////////////////////////////////////////// class SystemWithConfigure: public System, @@ -69,6 +71,16 @@ class SystemWithUpdates: const EntityComponentManager &) override {}; }; +///////////////////////////////////////////////// +class SystemWithPrioritizedUpdates: + public SystemWithUpdates, + public ISystemConfigurePriority +{ + // Documentation inherited + public: System::PriorityType ConfigurePriority() override + { return kPriority; } +}; + ///////////////////////////////////////////////// TEST(SystemManager, Constructor) { @@ -127,26 +139,40 @@ TEST(SystemManager, AddSystemNoEcm) EXPECT_EQ(1u, systemMgr.TotalByEntity(configEntity).size()); auto updateSystem = std::make_shared(); + auto prioritizedSystem = + std::make_shared(); Entity updateEntity{456u}; systemMgr.AddSystem(updateSystem, updateEntity, nullptr); + systemMgr.AddSystem(prioritizedSystem, updateEntity, nullptr); EXPECT_EQ(1u, systemMgr.ActiveCount()); - EXPECT_EQ(1u, systemMgr.PendingCount()); - EXPECT_EQ(2u, systemMgr.TotalCount()); + EXPECT_EQ(2u, systemMgr.PendingCount()); + EXPECT_EQ(3u, systemMgr.TotalCount()); EXPECT_EQ(1u, systemMgr.SystemsConfigure().size()); EXPECT_EQ(0u, systemMgr.SystemsPreUpdate().size()); EXPECT_EQ(0u, systemMgr.SystemsUpdate().size()); EXPECT_EQ(0u, systemMgr.SystemsPostUpdate().size()); - EXPECT_EQ(1u, systemMgr.TotalByEntity(updateEntity).size()); + EXPECT_EQ(2u, systemMgr.TotalByEntity(updateEntity).size()); systemMgr.ActivatePendingSystems(); - EXPECT_EQ(2u, systemMgr.ActiveCount()); + EXPECT_EQ(3u, systemMgr.ActiveCount()); EXPECT_EQ(0u, systemMgr.PendingCount()); - EXPECT_EQ(2u, systemMgr.TotalCount()); + EXPECT_EQ(3u, systemMgr.TotalCount()); EXPECT_EQ(1u, systemMgr.SystemsConfigure().size()); - EXPECT_EQ(1u, systemMgr.SystemsPreUpdate().size()); - EXPECT_EQ(1u, systemMgr.SystemsUpdate().size()); - EXPECT_EQ(1u, systemMgr.SystemsPostUpdate().size()); - EXPECT_EQ(1u, systemMgr.TotalByEntity(updateEntity).size()); + // Expect PreUpdate and Update to contain two map entries: + // 1. Priority {0} and a vector of length 1. + // 2. Priority {kPriority} and a vector of length 1. + EXPECT_EQ(2u, systemMgr.SystemsPreUpdate().size()); + EXPECT_EQ(1u, systemMgr.SystemsPreUpdate().count(0)); + EXPECT_EQ(1u, systemMgr.SystemsPreUpdate().count(kPriority)); + EXPECT_EQ(1u, systemMgr.SystemsPreUpdate().at(0).size()); + EXPECT_EQ(1u, systemMgr.SystemsPreUpdate().at(kPriority).size()); + EXPECT_EQ(2u, systemMgr.SystemsUpdate().size()); + EXPECT_EQ(1u, systemMgr.SystemsUpdate().count(0)); + EXPECT_EQ(1u, systemMgr.SystemsUpdate().count(kPriority)); + EXPECT_EQ(1u, systemMgr.SystemsUpdate().at(0).size()); + EXPECT_EQ(1u, systemMgr.SystemsUpdate().at(kPriority).size()); + EXPECT_EQ(2u, systemMgr.SystemsPostUpdate().size()); + EXPECT_EQ(2u, systemMgr.TotalByEntity(updateEntity).size()); } ///////////////////////////////////////////////// @@ -195,23 +221,116 @@ TEST(SystemManager, AddSystemEcm) EXPECT_EQ(0u, systemMgr.SystemsPostUpdate().size()); auto updateSystem = std::make_shared(); + auto prioritizedSystem = + std::make_shared(); systemMgr.AddSystem(updateSystem, kNullEntity, nullptr); + systemMgr.AddSystem(prioritizedSystem, kNullEntity, nullptr); EXPECT_EQ(1u, systemMgr.ActiveCount()); - EXPECT_EQ(1u, systemMgr.PendingCount()); - EXPECT_EQ(2u, systemMgr.TotalCount()); + EXPECT_EQ(2u, systemMgr.PendingCount()); + EXPECT_EQ(3u, systemMgr.TotalCount()); + EXPECT_EQ(1u, systemMgr.SystemsConfigure().size()); + EXPECT_EQ(0u, systemMgr.SystemsPreUpdate().size()); + EXPECT_EQ(0u, systemMgr.SystemsUpdate().size()); + EXPECT_EQ(0u, systemMgr.SystemsPostUpdate().size()); + + systemMgr.ActivatePendingSystems(); + EXPECT_EQ(3u, systemMgr.ActiveCount()); + EXPECT_EQ(0u, systemMgr.PendingCount()); + EXPECT_EQ(3u, systemMgr.TotalCount()); EXPECT_EQ(1u, systemMgr.SystemsConfigure().size()); + // Expect PreUpdate and Update to contain two map entries: + // 1. Priority {0} and a vector of length 1. + // 2. Priority {kPriority} and a vector of length 1. + EXPECT_EQ(2u, systemMgr.SystemsPreUpdate().size()); + EXPECT_EQ(1u, systemMgr.SystemsPreUpdate().count(0)); + EXPECT_EQ(1u, systemMgr.SystemsPreUpdate().count(kPriority)); + EXPECT_EQ(1u, systemMgr.SystemsPreUpdate().at(0).size()); + EXPECT_EQ(1u, systemMgr.SystemsPreUpdate().at(kPriority).size()); + EXPECT_EQ(2u, systemMgr.SystemsUpdate().size()); + EXPECT_EQ(1u, systemMgr.SystemsUpdate().count(0)); + EXPECT_EQ(1u, systemMgr.SystemsUpdate().count(kPriority)); + EXPECT_EQ(1u, systemMgr.SystemsUpdate().at(0).size()); + EXPECT_EQ(1u, systemMgr.SystemsUpdate().at(kPriority).size()); + EXPECT_EQ(2u, systemMgr.SystemsPostUpdate().size()); +} + +///////////////////////////////////////////////// +TEST(SystemManager, AddAndRemoveSystemEcm) +{ + auto loader = std::make_shared(); + + auto ecm = EntityComponentManager(); + auto eventManager = EventManager(); + + auto paramRegistry = std::make_unique< + gz::transport::parameters::ParametersRegistry>("SystemManager_TEST"); + SystemManager systemMgr( + loader, &ecm, &eventManager, std::string(), paramRegistry.get()); + + EXPECT_EQ(0u, systemMgr.ActiveCount()); + EXPECT_EQ(0u, systemMgr.PendingCount()); + EXPECT_EQ(0u, systemMgr.TotalCount()); + EXPECT_EQ(0u, systemMgr.SystemsConfigure().size()); + EXPECT_EQ(0u, systemMgr.SystemsPreUpdate().size()); + EXPECT_EQ(0u, systemMgr.SystemsUpdate().size()); + EXPECT_EQ(0u, systemMgr.SystemsPostUpdate().size()); + + auto configSystem = std::make_shared(); + systemMgr.AddSystem(configSystem, kNullEntity, nullptr); + + auto entity = ecm.CreateEntity(); + + auto updateSystemWithChild = std::make_shared(); + auto prioritizedSystemWithChild = + std::make_shared(); + systemMgr.AddSystem(updateSystemWithChild, entity, nullptr); + systemMgr.AddSystem(prioritizedSystemWithChild, entity, nullptr); + + // Configure called during AddSystem + EXPECT_EQ(1, configSystem->configured); + EXPECT_EQ(1, configSystem->configuredParameters); + + EXPECT_EQ(0u, systemMgr.ActiveCount()); + EXPECT_EQ(3u, systemMgr.PendingCount()); + EXPECT_EQ(3u, systemMgr.TotalCount()); + EXPECT_EQ(0u, systemMgr.SystemsConfigure().size()); EXPECT_EQ(0u, systemMgr.SystemsPreUpdate().size()); EXPECT_EQ(0u, systemMgr.SystemsUpdate().size()); EXPECT_EQ(0u, systemMgr.SystemsPostUpdate().size()); systemMgr.ActivatePendingSystems(); - EXPECT_EQ(2u, systemMgr.ActiveCount()); + EXPECT_EQ(3u, systemMgr.ActiveCount()); EXPECT_EQ(0u, systemMgr.PendingCount()); - EXPECT_EQ(2u, systemMgr.TotalCount()); + EXPECT_EQ(3u, systemMgr.TotalCount()); EXPECT_EQ(1u, systemMgr.SystemsConfigure().size()); - EXPECT_EQ(1u, systemMgr.SystemsPreUpdate().size()); - EXPECT_EQ(1u, systemMgr.SystemsUpdate().size()); - EXPECT_EQ(1u, systemMgr.SystemsPostUpdate().size()); + // Expect PreUpdate and Update to contain two map entries: + // 1. Priority {0} and a vector of length 1. + // 2. Priority {kPriority} and a vector of length 1. + EXPECT_EQ(2u, systemMgr.SystemsPreUpdate().size()); + EXPECT_EQ(1u, systemMgr.SystemsPreUpdate().count(0)); + EXPECT_EQ(1u, systemMgr.SystemsPreUpdate().count(kPriority)); + EXPECT_EQ(1u, systemMgr.SystemsPreUpdate().at(0).size()); + EXPECT_EQ(1u, systemMgr.SystemsPreUpdate().at(kPriority).size()); + EXPECT_EQ(2u, systemMgr.SystemsUpdate().size()); + EXPECT_EQ(1u, systemMgr.SystemsUpdate().count(0)); + EXPECT_EQ(1u, systemMgr.SystemsUpdate().count(kPriority)); + EXPECT_EQ(1u, systemMgr.SystemsUpdate().at(0).size()); + EXPECT_EQ(1u, systemMgr.SystemsUpdate().at(kPriority).size()); + EXPECT_EQ(2u, systemMgr.SystemsPostUpdate().size()); + + // Remove the entity + ecm.RequestRemoveEntity(entity); + bool needsCleanUp; + systemMgr.ProcessRemovedEntities(ecm, needsCleanUp); + + EXPECT_TRUE(needsCleanUp); + EXPECT_EQ(1u, systemMgr.ActiveCount()); + EXPECT_EQ(0u, systemMgr.PendingCount()); + EXPECT_EQ(1u, systemMgr.TotalCount()); + EXPECT_EQ(1u, systemMgr.SystemsConfigure().size()); + EXPECT_EQ(0u, systemMgr.SystemsPreUpdate().size()); + EXPECT_EQ(0u, systemMgr.SystemsUpdate().size()); + EXPECT_EQ(0u, systemMgr.SystemsPostUpdate().size()); } ///////////////////////////////////////////////// diff --git a/src/Util.cc b/src/Util.cc index e7b444bfe7..4d9a459ed4 100644 --- a/src/Util.cc +++ b/src/Util.cc @@ -15,12 +15,17 @@ * */ +#include +#include +#include + #include #include #include #include #include +#include #include #include #include @@ -397,12 +402,19 @@ std::string asFullPath(const std::string &_uri, const std::string &_filePath) { return _uri; } +#elif defined(_WIN32) + if (_uri.find("://") != std::string::npos || + common::isFile(_uri)) + { + return _uri; + } #else if (_uri.find("://") != std::string::npos || !common::isRelativePath(_uri)) { return _uri; } + #endif // When SDF is loaded from a string instead of a file @@ -699,12 +711,15 @@ std::optional sphericalCoordinates(Entity _entity, // lat / lon / elevation in rad / rad / m auto rad = sphericalCoordinatesComp->Data().PositionTransform( - xyzPose.Pos(), - math::SphericalCoordinates::LOCAL2, + math::CoordinateVector3::Metric(xyzPose.Pos()), + math::SphericalCoordinates::LOCAL, math::SphericalCoordinates::SPHERICAL); + if (!rad.has_value() || !rad->IsSpherical()) + return std::nullopt; + // Return degrees - return math::Vector3d(GZ_RTOD(rad.X()), GZ_RTOD(rad.Y()), rad.Z()); + return math::Vector3d(rad->Lat()->Degree(), rad->Lon()->Degree(), *rad->Z()); } ////////////////////////////////////////////////// @@ -735,15 +750,31 @@ std::optional getGridFieldCoordinates( } } auto position = origin->Data().PositionTransform( - _worldPosition, math::SphericalCoordinates::LOCAL2, + math::CoordinateVector3::Metric(_worldPosition), + math::SphericalCoordinates::LOCAL, _gridField->reference); + if (!position.has_value()) + return std::nullopt; + + if (position->IsMetric()) + return position->AsMetricVector(); + + math::Vector3d out; if (_gridField->reference == math::SphericalCoordinates::SPHERICAL && _gridField->units == components::EnvironmentalData::ReferenceUnits::DEGREES) { - position.X(GZ_RTOD(position.X())); - position.Y(GZ_RTOD(position.Y())); + out.X(position->Lat()->Degree()); + out.Y(position->Lon()->Degree()); } - return position; + else + { + out.X(position->Lat()->Radian()); + out.Y(position->Lon()->Radian()); + } + out.Z(*position->Z()); + + // \todo(iche033) Change the return type to math::CoordinateVector3 + return out; } ////////////////////////////////////////////////// @@ -855,8 +886,109 @@ const common::Mesh *loadMesh(const sdf::Mesh &_meshSdf) << "]." << std::endl; return nullptr; } + + if (mesh && _meshSdf.Optimization() != sdf::MeshOptimization::NONE) + { + const common::Mesh *optimizedMesh = optimizeMesh(_meshSdf, *mesh); + if (optimizedMesh) + return optimizedMesh; + else + gzwarn << "Failed to optimize Mesh " << mesh->Name() << std::endl; + } + return mesh; } + +const common::Mesh *optimizeMesh(const sdf::Mesh &_meshSdf, + const common::Mesh &_mesh) +{ + if (_meshSdf.Optimization() != + sdf::MeshOptimization::CONVEX_DECOMPOSITION && + _meshSdf.Optimization() != + sdf::MeshOptimization::CONVEX_HULL) + return nullptr; + + auto &meshManager = *common::MeshManager::Instance(); + std::size_t maxConvexHulls = 16u; + std::size_t voxelResolution = 200000u; + if (_meshSdf.ConvexDecomposition()) + { + // limit max number of convex hulls to generate + maxConvexHulls = _meshSdf.ConvexDecomposition()->MaxConvexHulls(); + voxelResolution = _meshSdf.ConvexDecomposition()->VoxelResolution(); + } + if (_meshSdf.Optimization() == sdf::MeshOptimization::CONVEX_HULL) + { + /// create 1 convex hull for the whole submesh + maxConvexHulls = 1u; + } + + // Check if MeshManager contains the decomposed mesh already. If not + // add it to the MeshManager so we do not need to decompose it again. + const std::string convexMeshName = + _mesh.Name() + "_" + _meshSdf.Submesh() + "_CONVEX_" + + std::to_string(maxConvexHulls) + "_" + std::to_string(voxelResolution); + auto *optimizedMesh = meshManager.MeshByName(convexMeshName); + if (!optimizedMesh) + { + std::unique_ptr meshToDecompose = + std::make_unique(); + // check if a particular submesh is requested + if (!_meshSdf.Submesh().empty()) + { + for (unsigned int submeshIdx = 0; + submeshIdx < _mesh.SubMeshCount(); + ++submeshIdx) + { + auto submesh = _mesh.SubMeshByIndex(submeshIdx).lock(); + if (submesh->Name() == _meshSdf.Submesh()) + { + if (_meshSdf.CenterSubmesh()) + submesh->Center(math::Vector3d::Zero); + meshToDecompose->AddSubMesh(*submesh.get()); + break; + } + } + } + else + { + // Merge meshes before convex decomposition + meshToDecompose = + gz::common::MeshManager::MergeSubMeshes(_mesh); + } + + if (meshToDecompose && meshToDecompose->SubMeshCount() == 1u) + { + // Decompose and add mesh to MeshManager + auto mergedSubmesh = meshToDecompose->SubMeshByIndex(0u).lock(); + std::vector decomposed = + gz::common::MeshManager::ConvexDecomposition( + *mergedSubmesh.get(), maxConvexHulls, voxelResolution); + gzdbg << "Optimizing mesh (" << _meshSdf.OptimizationStr() << "): " + << _mesh.Name() << std::endl; + // Create decomposed mesh and add it to MeshManager + // Note: MeshManager will call delete on this mesh in its destructor + // \todo(iche033) Consider updating MeshManager to accept + // unique pointers instead + common::Mesh *convexMesh = new common::Mesh; + convexMesh->SetName(convexMeshName); + for (const auto & submesh : decomposed) + convexMesh->AddSubMesh(submesh); + meshManager.AddMesh(convexMesh); + if (decomposed.empty()) + { + // Print an error if convex decomposition returned empty submeshes + // but still add it to MeshManager to avoid going through the + // expensive convex decomposition process for the same mesh again + gzerr << "Convex decomposition generated zero meshes: " + << _mesh.Name() << std::endl; + } + optimizedMesh = meshManager.MeshByName(convexMeshName); + } + } + return optimizedMesh; +} + } } } diff --git a/src/Util_TEST.cc b/src/Util_TEST.cc index 6be8d27903..9ec0e9be1a 100644 --- a/src/Util_TEST.cc +++ b/src/Util_TEST.cc @@ -1023,4 +1023,12 @@ TEST_F(UtilTest, LoadMesh) "test", "media", "duck.dae"); meshSdf.SetFilePath(filePath); EXPECT_NE(nullptr, loadMesh(meshSdf)); + + EXPECT_TRUE(meshSdf.SetOptimization("convex_decomposition")); + sdf::ConvexDecomposition convexDecomp; + convexDecomp.SetMaxConvexHulls(16u); + meshSdf.SetConvexDecomposition(convexDecomp); + auto *optimizedMesh = loadMesh(meshSdf); + EXPECT_NE(nullptr, optimizedMesh); + EXPECT_EQ(16u, optimizedMesh->SubMeshCount()); } diff --git a/src/cmd/cmdsim.rb.in b/src/cmd/cmdsim.rb.in index 64ee638c7b..866dc83ca5 100755 --- a/src/cmd/cmdsim.rb.in +++ b/src/cmd/cmdsim.rb.in @@ -458,16 +458,6 @@ has properly set the DYLD_LIBRARY_PATH environment variables." else resourcePathEnv = ENV['GZ_SIM_RESOURCE_PATH'] - if resourcePathEnv.nil? - resourcePathEnv = ENV['IGN_GAZEBO_RESOURCE_PATH'] - - if !resourcePathEnv.nil? - puts " -Using deprecated environment variable [IGN_GAZEBO_RESOURCE_PATH]. -Please use [GZ_SIM_RESOURCE_PATH] instead." - end - end - if !resourcePathEnv.nil? resourcePaths = resourcePathEnv.split(':') for resourcePath in resourcePaths @@ -598,12 +588,6 @@ See https://github.com/gazebosim/gz-sim/issues/168 for more info." options['record-period'], options['seed']) # Otherwise run the gui else options['gui'] - if plugin.end_with? ".dll" - puts "`gz sim` currently only works with the -s argument on Windows. -See https://github.com/gazebosim/gz-sim/issues/168 for more info." - exit(-1) - end - ENV['RMT_PORT'] = '1501' Importer.runGui(options['gui_config'], options['file'], options['wait_gui'], options['render_engine_gui'], diff --git a/src/gui/Gui.cc b/src/gui/Gui.cc index 72c6335e4d..2d6f4549f1 100644 --- a/src/gui/Gui.cc +++ b/src/gui/Gui.cc @@ -18,6 +18,7 @@ #include #include #include +#include #include @@ -29,7 +30,10 @@ #include #include #include +#include +#include +#include "gz/sim/Constants.hh" #include "gz/sim/InstallationDirectories.hh" #include "gz/sim/Util.hh" #include "gz/sim/config.hh" @@ -47,6 +51,119 @@ namespace sim { // Inline bracket to help doxygen filtering. inline namespace GZ_SIM_VERSION_NAMESPACE { +namespace { + +std::unique_ptr parseDefaultPlugins( + const std::string &_defaultConfig) +{ + const auto resolvedDefaultConfigPath = + gz::gui::App()->ResolveConfigFile(_defaultConfig); + auto pluginsDoc = std::make_unique(); + if (pluginsDoc->LoadFile(resolvedDefaultConfigPath.c_str()) == + tinyxml2::XML_SUCCESS) + { + // Remove everything that's not a plugin + for (auto elem = pluginsDoc->FirstChildElement(); elem != nullptr;) + { + if (std::strcmp("plugin", elem->Value()) != 0) + { + auto tmp = elem; + elem = elem->NextSiblingElement(); + pluginsDoc->DeleteChild(tmp); + } + else + { + elem = elem->NextSiblingElement(); + } + } + } + + return pluginsDoc; +} + +auto combineUserAndDefaultPlugins( + std::unique_ptr _userPlugins, + const tinyxml2::XMLDocument &_defaultPlugins, bool _includeDefaultPlugins) +{ + if (_includeDefaultPlugins) + { + auto combinedPlugins = std::make_unique(); + _defaultPlugins.DeepCopy(combinedPlugins.get()); + + std::set processedUserPlugins; + for (auto pluginElem = _userPlugins->FirstChildElement("plugin"); + pluginElem != nullptr; + pluginElem = pluginElem->NextSiblingElement("plugin")) + { + const char *pluginFilename = pluginElem->Attribute("filename"); + + bool replacedPlugin{false}; + for (auto elem = combinedPlugins->FirstChildElement("plugin"); + elem != nullptr && processedUserPlugins.count(elem) == 0; + elem = elem->NextSiblingElement("plugin")) + { + if (elem->Attribute("filename", pluginFilename)) + { + auto tmp = elem; + // Insert the replacement + auto clonedPlugin = pluginElem->DeepClone(combinedPlugins.get()); + elem = combinedPlugins->InsertAfterChild(elem, clonedPlugin) + ->ToElement(); + // Remove the original + combinedPlugins->DeleteNode(tmp); + replacedPlugin = true; + } + } + if (!replacedPlugin) + { + auto clonedPlugin = pluginElem->DeepClone(combinedPlugins.get()); + auto insertedElem = combinedPlugins->InsertEndChild(clonedPlugin); + processedUserPlugins.insert(insertedElem ); + } + } + + return combinedPlugins; + } + return _userPlugins; +} + +/// \brief Various policies that affect the behavior of the GUI +struct GuiPolicies +{ + /// \brief Whether to include default plugins + bool includeGuiDefaultPlugins{true}; + + /// \brief Parse policies from a GUI message + /// \param[in] _msg Input message + /// \return A GuiPolicies object populated from parsing the message. + static GuiPolicies ParsePolicies(const msgs::GUI &_msg) + { + GuiPolicies policies; + for (const auto &data : _msg.header().data()) + { + if (data.key() == "gz:policies") + { + tinyxml2::XMLDocument doc; + if (data.value_size() > 0) + { + if (doc.Parse(data.value(0).c_str()) == tinyxml2::XML_SUCCESS) + { + tinyxml2::XMLHandle handle(doc); + auto elem = handle.FirstChildElement(kPoliciesTag.data()) + .FirstChildElement("include_gui_default_plugins") + .ToElement(); + if (elem) + { + elem->QueryBoolText(&policies.includeGuiDefaultPlugins); + } + } + } + } + } + return policies; + } +}; +} namespace gui { /// \brief Get the path to the default config file. If the file doesn't exist @@ -221,6 +338,18 @@ std::unique_ptr createGui( qputenv("QT_AUTO_SCREEN_SCALE_FACTOR", "1"); } + // check for wayland and force to use X for rendering + if (QString::fromLocal8Bit(qgetenv("XDG_SESSION_TYPE")) == "wayland") + { + if (QString::fromLocal8Bit(qgetenv("QT_QPA_PLATFORM")).isEmpty()) + { + gzmsg << "Detected Wayland. Setting Qt to use the xcb plugin: " + << "'QT_QPA_PLATFORM=xcb'." << std::endl; + qputenv("QT_QPA_PLATFORM", "xcb"); + } + } + + bool isPlayback = (nullptr != _guiConfig && std::string(_guiConfig) == "_playback_"); auto defaultConfig = defaultGuiConfigFile(isPlayback, _defaultGuiConfig); @@ -422,6 +551,9 @@ std::unique_ptr createGui( // Load plugins after creating GuiRunner, so they can access worldName if (_loadPluginsFromSdf) { + const auto guiPolicies = GuiPolicies::ParsePolicies(res); + auto userPlugins = std::make_unique(); + std::string pluginsXml = ""; for (int p = 0; p < res.plugin_size(); ++p) { const auto &plugin = res.plugin(p); @@ -432,13 +564,13 @@ std::unique_ptr createGui( if (fileName == "GzScene3D") { std::vector extras{"GzSceneManager", - "InteractiveViewControl", - "CameraTracking", - "MarkerManager", - "SelectEntities", - "EntityContextMenuPlugin", - "Spawn", - "VisualizationCapabilities"}; + "InteractiveViewControl", + "CameraTracking", + "MarkerManager", + "SelectEntities", + "EntityContextMenuPlugin", + "Spawn", + "VisualizationCapabilities"}; std::string msg{ "The [GzScene3D] GUI plugin has been removed since Garden.\n" @@ -448,7 +580,7 @@ std::unique_ptr createGui( for (auto extra : extras) { - msg += "* " + extra + "\n"; + msg += "* " + extra + "\n"; auto newPlugin = res.add_plugin(); newPlugin->set_filename(extra); @@ -466,15 +598,23 @@ std::unique_ptr createGui( fileName = "MinimalScene"; } + pluginsXml += "" + + plugin.innerxml() + "\n"; + } + userPlugins->Parse(pluginsXml.c_str()); - std::string pluginStr = "" + - plugin.innerxml() + ""; - - tinyxml2::XMLDocument pluginDoc; - pluginDoc.Parse(pluginStr.c_str()); + const auto defaultPlugins = parseDefaultPlugins(defaultConfig); + auto pluginsToLoad = combineUserAndDefaultPlugins( + std::move(userPlugins), *defaultPlugins, + guiPolicies.includeGuiDefaultPlugins); - app->LoadPlugin(fileName, - pluginDoc.FirstChildElement("plugin")); + gzdbg << "Loading plugins:\n"; + for (auto pluginElem = pluginsToLoad->FirstChildElement("plugin"); + pluginElem != nullptr; + pluginElem = pluginElem->NextSiblingElement("plugin")) + { + app->LoadPlugin(pluginElem->Attribute("filename"), pluginElem); + gzdbg << pluginElem->Attribute("filename") << "\n"; } } } diff --git a/src/gui/plugins/component_inspector_editor/ComponentInspectorEditor.qml b/src/gui/plugins/component_inspector_editor/ComponentInspectorEditor.qml index f1e47ccd81..c21f8787f1 100644 --- a/src/gui/plugins/component_inspector_editor/ComponentInspectorEditor.qml +++ b/src/gui/plugins/component_inspector_editor/ComponentInspectorEditor.qml @@ -373,6 +373,14 @@ Rectangle { } } + MenuItem { + id: coneLink + text: "Cone" + onClicked: { + ComponentInspectorEditor.OnAddEntity("cone", "link"); + } + } + MenuItem { id: cylinderLink text: "Cylinder" diff --git a/src/gui/plugins/component_inspector_editor/ModelEditor.cc b/src/gui/plugins/component_inspector_editor/ModelEditor.cc index a838054b80..2f32916f66 100644 --- a/src/gui/plugins/component_inspector_editor/ModelEditor.cc +++ b/src/gui/plugins/component_inspector_editor/ModelEditor.cc @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -330,6 +331,14 @@ std::optional ModelEditorPrivate::CreateGeom( geom.SetSphereShape(shape); geom.SetType(sdf::GeometryType::SPHERE); } + else if (_eta.geomOrLightType == "cone") + { + sdf::Cone shape; + shape.SetRadius(size.X() * 0.5); + shape.SetLength(size.Z()); + geom.SetConeShape(shape); + geom.SetType(sdf::GeometryType::CONE); + } else if (_eta.geomOrLightType == "cylinder") { sdf::Cylinder shape; diff --git a/src/gui/plugins/entity_tree/EntityTree.qml b/src/gui/plugins/entity_tree/EntityTree.qml index 8b6db13123..1d65b9e7be 100644 --- a/src/gui/plugins/entity_tree/EntityTree.qml +++ b/src/gui/plugins/entity_tree/EntityTree.qml @@ -202,6 +202,15 @@ Rectangle { } } + MenuItem + { + id: cone + text: "Cone" + onClicked: { + EntityTree.OnInsertEntity("cone") + } + } + MenuItem { id: cylinder diff --git a/src/gui/plugins/global_illumination_civct/CiVctCascadePrivate.cc b/src/gui/plugins/global_illumination_civct/CiVctCascadePrivate.cc index de9149b5b1..97a2912208 100644 --- a/src/gui/plugins/global_illumination_civct/CiVctCascadePrivate.cc +++ b/src/gui/plugins/global_illumination_civct/CiVctCascadePrivate.cc @@ -21,8 +21,6 @@ #include "GlobalIlluminationCiVct.hh" -#include "gz/rendering/GlobalIlluminationCiVct.hh" - using namespace gz; using namespace sim; diff --git a/src/gui/plugins/global_illumination_civct/CiVctCascadePrivate.hh b/src/gui/plugins/global_illumination_civct/CiVctCascadePrivate.hh index 13f53e45dc..eacde8b043 100644 --- a/src/gui/plugins/global_illumination_civct/CiVctCascadePrivate.hh +++ b/src/gui/plugins/global_illumination_civct/CiVctCascadePrivate.hh @@ -26,18 +26,8 @@ #include "Tsa.hh" -namespace gz -{ - namespace rendering - { - inline namespace GZ_SIM_VERSION_NAMESPACE - { - /// Forward declare the only ptr we need - class CiVctCascade; - typedef std::shared_ptr CiVctCascadePtr; - } // namespace GZ_SIM_GAZEBO_VERSION_NAMESPACE - } // namespace rendering -} // namespace gz + +#include "gz/rendering/GlobalIlluminationCiVct.hh" namespace gz { diff --git a/src/gui/plugins/global_illumination_civct/GlobalIlluminationCiVct.cc b/src/gui/plugins/global_illumination_civct/GlobalIlluminationCiVct.cc index d2b8f4c20c..cfab7f414f 100644 --- a/src/gui/plugins/global_illumination_civct/GlobalIlluminationCiVct.cc +++ b/src/gui/plugins/global_illumination_civct/GlobalIlluminationCiVct.cc @@ -172,13 +172,13 @@ GlobalIlluminationCiVct::~GlobalIlluminationCiVct() } ///////////////////////////////////////////////// -void GlobalIlluminationCiVct::LoadGlobalIlluminationCiVct() +bool GlobalIlluminationCiVct::LoadGlobalIlluminationCiVct() REQUIRES(this->dataPtr->serviceMutex) { auto loadedEngNames = rendering::loadedEngines(); if (loadedEngNames.empty()) { - return; + return false; } // assume there is only one engine loaded @@ -194,11 +194,11 @@ void GlobalIlluminationCiVct::LoadGlobalIlluminationCiVct() { gzerr << "Internal error: failed to load engine [" << engineName << "]. GlobalIlluminationCiVct plugin won't work." << std::endl; - return; + return false; } if (engine->SceneCount() == 0) - return; + return false; // assume there is only one scene // load scene @@ -206,12 +206,13 @@ void GlobalIlluminationCiVct::LoadGlobalIlluminationCiVct() if (!scene) { gzerr << "Internal error: scene is null." << std::endl; - return; + return false; } - if (!scene->IsInitialized() || scene->VisualCount() == 0) + if (!scene->IsInitialized() || scene->VisualCount() == 0 || + scene->LightCount() == 0) { - return; + return false; } // Create visual @@ -225,6 +226,7 @@ void GlobalIlluminationCiVct::LoadGlobalIlluminationCiVct() << std::endl; gz::gui::App()->findChild()->removeEventFilter(this); + return false; } else { @@ -254,6 +256,7 @@ void GlobalIlluminationCiVct::LoadGlobalIlluminationCiVct() this->OnRefreshCamerasImpl(); } + return true; } /// \brief XML helper to retrieve values and handle errors @@ -358,9 +361,7 @@ void GlobalIlluminationCiVct::LoadConfig( const tinyxml2::XMLElement *_pluginElem) { if (this->title.empty()) - this->title = "Global Illumination (VCT)"; - - std::lock_guard lock(this->dataPtr->serviceMutex); + this->title = "Global Illumination (CI VCT)"; if (auto elem = _pluginElem->FirstChildElement("enabled")) { @@ -447,15 +448,25 @@ bool GlobalIlluminationCiVct::eventFilter(QObject *_obj, QEvent *_event) { if (_event->type() == gz::gui::events::Render::kType) { - // This event is called in Scene3d's RenderThread, so it's safe to make + // This event is called in the render thread, so it's safe to make // rendering calls here - std::lock_guard lock(this->dataPtr->serviceMutex); if (!this->dataPtr->initialized) { - this->LoadGlobalIlluminationCiVct(); + if (this->LoadGlobalIlluminationCiVct()) + { + this->SetEnabled(this->dataPtr->enabled); + this->SetBounceCount(this->dataPtr->bounceCount); + this->SetHighQuality(this->dataPtr->highQuality); + this->SetAnisotropic(this->dataPtr->anisotropic); + this->SetDebugVisualizationMode(this->dataPtr->debugVisMode); + this->EnabledChanged(); + this->LightingChanged(); + this->DebugVisualizationModeChanged(); + } } + std::lock_guard lock(this->dataPtr->serviceMutex); if (this->dataPtr->gi) { if (!this->dataPtr->visualDirty && !this->dataPtr->gi->Enabled() && @@ -563,10 +574,6 @@ bool GlobalIlluminationCiVct::eventFilter(QObject *_obj, QEvent *_event) this->dataPtr->resetRequested = false; } } - else - { - gzerr << "GI pointer is not set" << std::endl; - } } // Standard event processing diff --git a/src/gui/plugins/global_illumination_civct/GlobalIlluminationCiVct.hh b/src/gui/plugins/global_illumination_civct/GlobalIlluminationCiVct.hh index 6ebaee5741..d5327ae32f 100644 --- a/src/gui/plugins/global_illumination_civct/GlobalIlluminationCiVct.hh +++ b/src/gui/plugins/global_illumination_civct/GlobalIlluminationCiVct.hh @@ -103,7 +103,8 @@ inline namespace GZ_SIM_VERSION_NAMESPACE public: bool eventFilter(QObject *_obj, QEvent *_event) override; /// \brief Load the scene and attach LidarVisual to the scene - public: void LoadGlobalIlluminationCiVct(); + /// \return True if GI CIVCT is loaded successfully, false otherwise. + public: bool LoadGlobalIlluminationCiVct(); /// \brief Set debug visualization mode GlogbalIllumination /// \param[in] _mode Index of selected debug visualization mode diff --git a/src/gui/plugins/global_illumination_vct/GlobalIlluminationVct.cc b/src/gui/plugins/global_illumination_vct/GlobalIlluminationVct.cc index 0d6400c1a5..3abbabaec4 100644 --- a/src/gui/plugins/global_illumination_vct/GlobalIlluminationVct.cc +++ b/src/gui/plugins/global_illumination_vct/GlobalIlluminationVct.cc @@ -166,13 +166,13 @@ GlobalIlluminationVct::~GlobalIlluminationVct() } ///////////////////////////////////////////////// -void GlobalIlluminationVct::LoadGlobalIlluminationVct() +bool GlobalIlluminationVct::LoadGlobalIlluminationVct() REQUIRES(this->dataPtr->serviceMutex) { auto loadedEngNames = rendering::loadedEngines(); if (loadedEngNames.empty()) { - return; + return false; } // assume there is only one engine loaded @@ -188,11 +188,11 @@ void GlobalIlluminationVct::LoadGlobalIlluminationVct() { gzerr << "Internal error: failed to load engine [" << engineName << "]. GlobalIlluminationVct plugin won't work." << std::endl; - return; + return false; } if (engine->SceneCount() == 0) - return; + return false; // assume there is only one scene // load scene @@ -200,15 +200,16 @@ void GlobalIlluminationVct::LoadGlobalIlluminationVct() if (!scene) { gzerr << "Internal error: scene is null." << std::endl; - return; + return false; } - if (!scene->IsInitialized() || scene->VisualCount() == 0) + if (!scene->IsInitialized() || scene->VisualCount() == 0 || + scene->LightCount() == 0) { - return; + return false; } - // Create lidar visual + // Create GI gzdbg << "Creating GlobalIlluminationVct" << std::endl; auto root = scene->RootVisual(); @@ -219,6 +220,7 @@ void GlobalIlluminationVct::LoadGlobalIlluminationVct() << std::endl; gz::gui::App()->findChild()->removeEventFilter(this); + return false; } else { @@ -228,6 +230,7 @@ void GlobalIlluminationVct::LoadGlobalIlluminationVct() this->dataPtr->scene = scene; this->dataPtr->initialized = true; } + return true; } /// \brief XML helper to retrieve values and handle errors @@ -319,8 +322,6 @@ void GlobalIlluminationVct::LoadConfig(const tinyxml2::XMLElement *_pluginElem) if (this->title.empty()) this->title = "Global Illumination (VCT)"; - std::lock_guard lock(this->dataPtr->serviceMutex); - if (auto elem = _pluginElem->FirstChildElement("enabled")) { GetXmlBool(elem, this->dataPtr->enabled); @@ -394,15 +395,35 @@ bool GlobalIlluminationVct::eventFilter(QObject *_obj, QEvent *_event) { if (_event->type() == gz::gui::events::Render::kType) { - // This event is called in Scene3d's RenderThread, so it's safe to make + // This event is called in render thread, so it's safe to make // rendering calls here - std::lock_guard lock(this->dataPtr->serviceMutex); if (!this->dataPtr->initialized) { - this->LoadGlobalIlluminationVct(); + if (this->LoadGlobalIlluminationVct()) + { + // update properties and notify QML + this->SetEnabled(this->dataPtr->enabled); + this->SetResolutionX(this->dataPtr->resolution[0]); + this->SetResolutionY(this->dataPtr->resolution[1]); + this->SetResolutionZ(this->dataPtr->resolution[2]); + this->SetOctantCountX(this->dataPtr->octantCount[0]); + this->SetOctantCountY(this->dataPtr->octantCount[1]); + this->SetOctantCountZ(this->dataPtr->octantCount[2]); + this->SetBounceCount(this->dataPtr->bounceCount); + this->SetHighQuality(this->dataPtr->highQuality); + this->SetAnisotropic(this->dataPtr->anisotropic); + this->SetConserveMemory(this->dataPtr->conserveMemory); + this->SetThinWallCounter(this->dataPtr->thinWallCounter); + this->SetDebugVisualizationMode(this->dataPtr->debugVisMode); + this->EnabledChanged(); + this->LightingChanged(); + this->SettingsChanged(); + this->DebugVisualizationModeChanged(); + } } + std::lock_guard lock(this->dataPtr->serviceMutex); if (this->dataPtr->gi) { if (this->dataPtr->resetVisual) @@ -485,10 +506,6 @@ bool GlobalIlluminationVct::eventFilter(QObject *_obj, QEvent *_event) this->dataPtr->debugVisualizationDirty = false; } } - else - { - gzerr << "GI pointer is not set" << std::endl; - } } // Standard event processing diff --git a/src/gui/plugins/global_illumination_vct/GlobalIlluminationVct.hh b/src/gui/plugins/global_illumination_vct/GlobalIlluminationVct.hh index fde6a6979b..19a6d3ec61 100644 --- a/src/gui/plugins/global_illumination_vct/GlobalIlluminationVct.hh +++ b/src/gui/plugins/global_illumination_vct/GlobalIlluminationVct.hh @@ -156,7 +156,8 @@ inline namespace GZ_SIM_VERSION_NAMESPACE public: bool eventFilter(QObject *_obj, QEvent *_event) override; /// \brief Load the scene and attach LidarVisual to the scene - public: void LoadGlobalIlluminationVct(); + /// \return True if GI VCT is loaded successfully, false otherwise. + public: bool LoadGlobalIlluminationVct(); /// \brief Set debug visualization mode GlogbalIllumination /// \param[in] _mode Index of selected debug visualization mode diff --git a/src/gui/plugins/joint_position_controller/JointPositionController.hh b/src/gui/plugins/joint_position_controller/JointPositionController.hh index 0e23ca4bc9..172f893f65 100644 --- a/src/gui/plugins/joint_position_controller/JointPositionController.hh +++ b/src/gui/plugins/joint_position_controller/JointPositionController.hh @@ -25,6 +25,17 @@ #include #include +#ifndef _WIN32 +# define JointPositionController_EXPORTS_API \ + __attribute__ ((visibility ("default"))) +#else +# if (defined(JointPositionController_EXPORTS)) +# define JointPositionController_EXPORTS_API __declspec(dllexport) +# else +# define JointPositionController_EXPORTS_API __declspec(dllimport) +# endif +#endif + Q_DECLARE_METATYPE(gz::sim::Entity) namespace gz @@ -36,7 +47,8 @@ namespace gui class JointPositionControllerPrivate; /// \brief Model holding information about joints - class JointsModel : public QStandardItemModel + class JointPositionController_EXPORTS_API JointsModel : + public QStandardItemModel { Q_OBJECT @@ -88,7 +100,8 @@ namespace gui /// ``: Load the widget pointed at the given model, so it's not /// necessary to select it. If a model is given at startup, the plugin starts /// in locked mode. - class JointPositionController : public sim::GuiSystem + class JointPositionController_EXPORTS_API JointPositionController : + public sim::GuiSystem { Q_OBJECT diff --git a/src/gui/plugins/modules/EntityContextMenu.cc b/src/gui/plugins/modules/EntityContextMenu.cc index 812a787540..6af4435972 100644 --- a/src/gui/plugins/modules/EntityContextMenu.cc +++ b/src/gui/plugins/modules/EntityContextMenu.cc @@ -19,10 +19,12 @@ #include "EntityContextMenu.hh" #include -#include +#include #include +#include #include +#include #include #include @@ -35,14 +37,21 @@ namespace gz::sim /// \brief Private data class for EntityContextMenu class EntityContextMenuPrivate { + + /// \brief Protects variable changed through services. + public: std::mutex mutex; + /// \brief Gazebo communication node. public: transport::Node node; /// \brief Move to service name public: std::string moveToService; - /// \brief Follow service name - public: std::string followService; + /// \brief Track topic name + public: std::string trackTopic; + + /// \brief Currently tracked topic name + public: std::string currentTrackTopic; /// \brief Remove service name public: std::string removeService; @@ -76,12 +85,37 @@ namespace gz::sim /// \brief Name of world. public: std::string worldName; + + /// \brief Storing last follow target for look at. + public: std::string followTargetLookAt; + + /// \brief Flag used to disable look at when not following target. + public: bool followingTarget{false}; + + /// \brief /gui/track publisher + public: transport::Node::Publisher trackPub; }; } using namespace gz; using namespace sim; +///////////////////////////////////////////////// +void EntityContextMenu::OnCurrentlyTrackedSub(const msgs::CameraTrack &_msg) +{ + std::lock_guard lock(this->dataPtr->mutex); + this->dataPtr->followingTarget = false; + if (_msg.track_mode() == gz::msgs::CameraTrack::FOLLOW || + _msg.track_mode() == gz::msgs::CameraTrack::FOLLOW_LOOK_AT || + _msg.track_mode() == gz::msgs::CameraTrack::FOLLOW_FREE_LOOK) + { + this->dataPtr->followingTarget = true; + } + this->FollowingTargetChanged(); + + return; +} + ///////////////////////////////////////////////// void GzSimPlugin::registerTypes(const char *_uri) { @@ -94,11 +128,17 @@ void GzSimPlugin::registerTypes(const char *_uri) EntityContextMenu::EntityContextMenu() : dataPtr(std::make_unique()) { + this->dataPtr->currentTrackTopic = "/gui/currently_tracked"; + this->dataPtr->node.Subscribe(this->dataPtr->currentTrackTopic, + &EntityContextMenu::OnCurrentlyTrackedSub, this); + gzmsg << "Currently tracking topic on [" + << this->dataPtr->currentTrackTopic << "]" << std::endl; + // For move to service requests this->dataPtr->moveToService = "/gui/move_to"; - // For follow service requests - this->dataPtr->followService = "/gui/follow"; + // For track topic message + this->dataPtr->trackTopic = "/gui/track"; // For remove service requests this->dataPtr->removeService = "/world/default/remove"; @@ -129,11 +169,27 @@ EntityContextMenu::EntityContextMenu() // For paste service requests this->dataPtr->pasteService = "/gui/paste"; + + this->dataPtr->trackPub = + this->dataPtr->node.Advertise(this->dataPtr->trackTopic); } ///////////////////////////////////////////////// EntityContextMenu::~EntityContextMenu() = default; +///////////////////////////////////////////////// +void EntityContextMenu::SetFollowingTarget(bool &_followingTarget) +{ + this->dataPtr->followingTarget = _followingTarget; + this->FollowingTargetChanged(); +} + +///////////////////////////////////////////////// +bool EntityContextMenu::FollowingTarget() const +{ + return this->dataPtr->followingTarget; +} + ///////////////////////////////////////////////// void EntityContextMenu::OnRemove( const QString &_data, const QString &_type) @@ -196,9 +252,40 @@ void EntityContextMenu::OnRequest(const QString &_request, const QString &_data) } else if (request == "follow") { - msgs::StringMsg req; - req.set_data(_data.toStdString()); - this->dataPtr->node.Request(this->dataPtr->followService, req, cb); + msgs::CameraTrack followMsg; + followMsg.mutable_follow_target()->set_name(_data.toStdString()); + followMsg.set_track_mode(msgs::CameraTrack::FOLLOW); + this->dataPtr->followTargetLookAt = followMsg.follow_target().name(); + gzmsg << "Follow target: " << followMsg.follow_target().name() << std::endl; + this->dataPtr->trackPub.Publish(followMsg); + } + else if (request == "free_look") + { + msgs::CameraTrack followMsg; + followMsg.mutable_follow_target()->set_name(_data.toStdString()); + followMsg.set_track_mode(msgs::CameraTrack::FOLLOW_FREE_LOOK); + this->dataPtr->followTargetLookAt = followMsg.follow_target().name(); + gzmsg << "Follow target: " << followMsg.follow_target().name() << std::endl; + this->dataPtr->trackPub.Publish(followMsg); + } + else if (request == "look_at") + { + msgs::CameraTrack followMsg; + followMsg.mutable_track_target()->set_name(_data.toStdString()); + followMsg.set_track_mode(msgs::CameraTrack::FOLLOW_LOOK_AT); + followMsg.mutable_follow_target()->set_name( + this->dataPtr->followTargetLookAt); + gzmsg << "Follow target: " << followMsg.follow_target().name() << std::endl; + gzmsg << "Look at target: " << followMsg.track_target().name() << std::endl; + this->dataPtr->trackPub.Publish(followMsg); + } + else if (request == "track") + { + msgs::CameraTrack trackMsg; + trackMsg.mutable_track_target()->set_name(_data.toStdString()); + trackMsg.set_track_mode(msgs::CameraTrack::TRACK); + gzmsg << "Track target: " << trackMsg.track_target().name() << std::endl; + this->dataPtr->trackPub.Publish(trackMsg); } else if (request == "view_transparent") { diff --git a/src/gui/plugins/modules/EntityContextMenu.hh b/src/gui/plugins/modules/EntityContextMenu.hh index 381417f61e..5f09419f32 100644 --- a/src/gui/plugins/modules/EntityContextMenu.hh +++ b/src/gui/plugins/modules/EntityContextMenu.hh @@ -19,6 +19,7 @@ #define GZ_SIM_GUI_ENTITYCONTEXTMENU_HH_ #include +#include #include #include @@ -46,6 +47,13 @@ namespace sim class EntityContextMenu : public QQuickItem { Q_OBJECT + /// \brief followingTarget + Q_PROPERTY( + bool followingTarget + READ FollowingTarget + WRITE SetFollowingTarget + NOTIFY FollowingTargetChanged + ) /// \brief Constructor public: EntityContextMenu(); @@ -53,6 +61,21 @@ namespace sim /// \brief Destructor public: ~EntityContextMenu() override; + /// \brief Get whether it is following target + /// \return True if followingTarget + public: Q_INVOKABLE bool FollowingTarget() const; + + /// \brief Set whether followingTarget + /// \param[in] _followingTarget True if followingTarget + public: Q_INVOKABLE void SetFollowingTarget(bool &_followingTarget); + + /// \brief Notify that followingTarget has changed + signals: void FollowingTargetChanged(); + + /// \brief Callback function to get data from the message + /// \param[in] _msg CameraTrack message + public: void OnCurrentlyTrackedSub(const msgs::CameraTrack &_msg); + /// \brief Callback when a context menu item is invoked /// \param[in] _data Request data /// \param[in] _type Entity type diff --git a/src/gui/plugins/modules/EntityContextMenu.qml b/src/gui/plugins/modules/EntityContextMenu.qml index 1d3cd66120..72626d3f95 100644 --- a/src/gui/plugins/modules/EntityContextMenu.qml +++ b/src/gui/plugins/modules/EntityContextMenu.qml @@ -28,9 +28,19 @@ Item { onTriggered: context.OnRequest("move_to", context.entity) } MenuItem { - id: followMenu - text: "Follow" - onTriggered: context.OnRequest("follow", context.entity) + id: followOptionsSubmenu + text: "Follow Options >" + MouseArea { + id: followOptionsSubMouseArea + anchors.fill: parent + hoverEnabled: true + onEntered: secondMenu.open() + } + } + MenuItem { + id: trackMenu + text: "Track" + onTriggered: context.OnRequest("track", context.entity) } MenuItem { id: removeMenu @@ -67,13 +77,42 @@ Item { id: viewSubMouseArea anchors.fill: parent hoverEnabled: true - onEntered: secondMenu.open() + onEntered: thirdMenu.open() } } } Menu { id: secondMenu x: menu.x + menu.width + y: menu.y + followOptionsSubmenu.y + MenuItem { + id: followMenu + text: "Follow" + onTriggered: { + menu.close() + context.OnRequest("follow", context.entity) + } + } + MenuItem { + id: followFreeLookMenu + text: "Free Look" + onTriggered: { + menu.close() + context.OnRequest("free_look", context.entity) + } + } + MenuItem { + id: followLookAtMenu + text: "Look At" + onTriggered: { + menu.close() + context.OnRequest("look_at", context.entity) + } + } + } + Menu { + id: thirdMenu + x: menu.x + menu.width y: menu.y + viewSubmenu.y MenuItem { id: viewCOMMenu @@ -140,6 +179,9 @@ Item { context.type = _type moveToMenu.enabled = false followMenu.enabled = false + followFreeLookMenu.enabled = false + followLookAtMenu.enabled = false + trackMenu.enabled = false removeMenu.enabled = false viewTransparentMenu.enabled = false; viewCOMMenu.enabled = false; @@ -156,6 +198,12 @@ Item { { moveToMenu.enabled = true followMenu.enabled = true + followFreeLookMenu.enabled = true + if (context.followingTarget) + { + followLookAtMenu.enabled = true + } + trackMenu.enabled = true } if (context.type == "model" || context.type == "light") diff --git a/src/gui/plugins/plot_3d/Plot3D.hh b/src/gui/plugins/plot_3d/Plot3D.hh index d3d7836e5a..180f68a785 100644 --- a/src/gui/plugins/plot_3d/Plot3D.hh +++ b/src/gui/plugins/plot_3d/Plot3D.hh @@ -24,6 +24,16 @@ #include "gz/gui/qt.h" +#ifndef _WIN32 +# define Plot3D_EXPORTS_API __attribute__ ((visibility ("default"))) +#else +# if (defined(Plot3D_EXPORTS)) +# define Plot3D_EXPORTS_API __declspec(dllexport) +# else +# define Plot3D_EXPORTS_API __declspec(dllimport) +# endif +#endif + namespace gz { namespace sim @@ -59,7 +69,7 @@ namespace gui /// After this number is reached, the older points start being deleted. /// Defaults to 1000. /// - class Plot3D : public gz::sim::GuiSystem + class Plot3D_EXPORTS_API Plot3D : public gz::sim::GuiSystem { Q_OBJECT diff --git a/src/gui/plugins/resource_spawner/ResourceSpawner.cc b/src/gui/plugins/resource_spawner/ResourceSpawner.cc index 7e61cc0c4e..e206415218 100644 --- a/src/gui/plugins/resource_spawner/ResourceSpawner.cc +++ b/src/gui/plugins/resource_spawner/ResourceSpawner.cc @@ -553,7 +553,7 @@ void ResourceSpawner::OnDownloadFuelResource(const QString &_path, // Set the waiting cursor while the resource downloads QGuiApplication::setOverrideCursor(Qt::WaitCursor); if (this->dataPtr->fuelClient->DownloadModel( - common::URI(_path.toStdString()), localPath)) + common::URI(_path.toStdString(), true), localPath)) { // Successful download, set thumbnail std::string thumbnailPath = common::joinPaths(localPath, "thumbnails"); @@ -739,7 +739,7 @@ void ResourceSpawner::RunFetchResourceListThread(const std::string &_owner) resource.isFuel = true; resource.isDownloaded = false; resource.owner = id.Owner(); - resource.sdfPath = id.UniqueName(); + resource.sdfPath = id.Url().Str(); QMetaObject::invokeMethod( this, "UpdateOwnerListModel", Qt::QueuedConnection, diff --git a/src/gui/plugins/shapes/Shapes.qml b/src/gui/plugins/shapes/Shapes.qml index 781eaca28f..fb80c1431f 100644 --- a/src/gui/plugins/shapes/Shapes.qml +++ b/src/gui/plugins/shapes/Shapes.qml @@ -67,6 +67,23 @@ ToolBar { Shapes.OnMode("sphere") } } + ToolButton { + id: cone + ToolTip.text: "Cone" + ToolTip.visible: hovered + ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval + contentItem: Image { + fillMode: Image.Pad + horizontalAlignment: Image.AlignHCenter + verticalAlignment: Image.AlignVCenter + source: "cone.png" + sourceSize.width: 24; + sourceSize.height: 24; + } + onClicked: { + Shapes.OnMode("cone") + } + } ToolButton { id: cylinder ToolTip.text: "Cylinder" diff --git a/src/gui/plugins/shapes/Shapes.qrc b/src/gui/plugins/shapes/Shapes.qrc index 754d997c5d..00fddab57c 100644 --- a/src/gui/plugins/shapes/Shapes.qrc +++ b/src/gui/plugins/shapes/Shapes.qrc @@ -3,6 +3,7 @@ Shapes.qml box.png sphere.png + cone.png cylinder.png capsule.png ellipsoid.png diff --git a/src/gui/plugins/shapes/cone.png b/src/gui/plugins/shapes/cone.png new file mode 100644 index 0000000000..53f26d831c Binary files /dev/null and b/src/gui/plugins/shapes/cone.png differ diff --git a/src/gui/plugins/video_recorder/VideoRecorder.cc b/src/gui/plugins/video_recorder/VideoRecorder.cc index 1425476071..f6e17abf34 100644 --- a/src/gui/plugins/video_recorder/VideoRecorder.cc +++ b/src/gui/plugins/video_recorder/VideoRecorder.cc @@ -341,7 +341,7 @@ void VideoRecorder::OnStart(const QString &_format) { std::unique_lock lock(this->dataPtr->recordMutex); this->dataPtr->format = _format.toStdString(); - this->dataPtr->filename = "ign_recording." + this->dataPtr->format; + this->dataPtr->filename = "gz_recording." + this->dataPtr->format; this->dataPtr->recordVideo = true; this->dataPtr->recording = true; } diff --git a/src/gui/plugins/visualization_capabilities/VisualizationCapabilities.cc b/src/gui/plugins/visualization_capabilities/VisualizationCapabilities.cc index c5d24bc7ab..92ceedbfbb 100644 --- a/src/gui/plugins/visualization_capabilities/VisualizationCapabilities.cc +++ b/src/gui/plugins/visualization_capabilities/VisualizationCapabilities.cc @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -39,6 +40,7 @@ #include #include #include +#include #include #include @@ -63,6 +65,7 @@ #include #include +#include #include #include #include @@ -1191,6 +1194,13 @@ rendering::GeometryPtr VisualizationCapabilitiesPrivate::CreateGeometry( capsule->SetLength(_geom.CapsuleShape()->Length()); geom = capsule; } + else if (_geom.Type() == sdf::GeometryType::CONE) + { + geom = this->scene->CreateCone(); + scale.X() = _geom.ConeShape()->Radius() * 2; + scale.Y() = scale.X(); + scale.Z() = _geom.ConeShape()->Length(); + } else if (_geom.Type() == sdf::GeometryType::CYLINDER) { geom = this->scene->CreateCylinder(); @@ -1239,14 +1249,25 @@ rendering::GeometryPtr VisualizationCapabilitiesPrivate::CreateGeometry( // Assume absolute path to mesh file descriptor.meshName = fullPath; - descriptor.subMeshName = _geom.MeshShape()->Submesh(); - descriptor.centerSubMesh = _geom.MeshShape()->CenterSubmesh(); - gz::common::MeshManager *meshManager = gz::common::MeshManager::Instance(); descriptor.mesh = meshManager->Load(descriptor.meshName); if (descriptor.mesh) { + if (_geom.MeshShape()->Optimization() != sdf::MeshOptimization::NONE) + { + const common::Mesh *optimizedMesh = + optimizeMesh(*_geom.MeshShape(), *descriptor.mesh); + if (optimizedMesh) + { + descriptor.mesh = optimizedMesh; + // if submesh is requested, it should be handled in the optimizeMesh + // function so we do not need need to pass these flags to + // gz-rendering + descriptor.subMeshName = ""; + descriptor.centerSubMesh = false; + } + } geom = this->scene->CreateMesh(descriptor); } else diff --git a/src/gui/plugins/visualize_lidar/VisualizeLidar.cc b/src/gui/plugins/visualize_lidar/VisualizeLidar.cc index 25dd945633..2a2bb721ca 100644 --- a/src/gui/plugins/visualize_lidar/VisualizeLidar.cc +++ b/src/gui/plugins/visualize_lidar/VisualizeLidar.cc @@ -82,9 +82,6 @@ inline namespace GZ_SIM_VERSION_NAMESPACE public: rendering::LidarVisualType visualType{ rendering::LidarVisualType::LVT_TRIANGLE_STRIPS}; - /// \brief URI sequence to the lidar link - public: std::string lidarString{""}; - /// \brief LaserScan message from sensor public: msgs::LaserScan msg; @@ -122,7 +119,7 @@ inline namespace GZ_SIM_VERSION_NAMESPACE public: bool visualDirty{false}; /// \brief lidar sensor entity dirty flag - public: bool lidarEntityDirty{true}; + public: bool lidarEntityDirty{false}; }; } } @@ -264,65 +261,44 @@ void VisualizeLidar::Update(const UpdateInfo &, std::lock_guard lock(this->dataPtr->serviceMutex); + if (this->dataPtr->topicName.empty()) + return; + if (this->dataPtr->lidarEntityDirty) { - auto lidarURIVec = common::split(common::trimmed( - this->dataPtr->lidarString), "::"); - if (lidarURIVec.size() > 0) + std::string topic = this->dataPtr->topicName; + auto lidarEnt = + _ecm.EntityByComponents(components::SensorTopic(topic)); + if (lidarEnt == kNullEntity) { - auto baseEntity = _ecm.EntityByComponents( - components::Name(lidarURIVec[0])); - if (!baseEntity) - { - gzerr << "Error entity " << lidarURIVec[0] - << " doesn't exist and cannot be used to set lidar visual pose" - << std::endl; - return; - } - else + if (topic[0] == '/') + topic = topic.substr(1); + lidarEnt = + _ecm.EntityByComponents(components::SensorTopic(topic)); + } + + static bool informed{false}; + if (lidarEnt == kNullEntity) + { + if (!informed) { - auto parent = baseEntity; - bool success = false; - for (size_t i = 0u; i < lidarURIVec.size()-1; i++) - { - auto children = _ecm.EntitiesByComponents( - components::ParentEntity(parent)); - bool foundChild = false; - for (auto child : children) - { - std::string nextstring = lidarURIVec[i+1]; - auto comp = _ecm.Component(child); - if (!comp) - { - continue; - } - std::string childname = std::string( - comp->Data()); - if (nextstring.compare(childname) == 0) - { - parent = child; - foundChild = true; - if (i+1 == lidarURIVec.size()-1) - { - success = true; - } - break; - } - } - if (!foundChild) - { - gzerr << "The entity could not be found." - << "Error displaying lidar visual" <dataPtr->lidarEntity = parent; - this->dataPtr->lidarEntityDirty = false; - } + gzerr << "The lidar entity with topic '['" << this->dataPtr->topicName + << "'] could not be found. " + << "Error displaying lidar visual. " << std::endl; + informed = true; } + return; } + informed = false; + this->dataPtr->lidarEntity = lidarEnt; + this->dataPtr->lidarEntityDirty = false; + } + + if (!_ecm.HasEntity(this->dataPtr->lidarEntity)) + { + this->dataPtr->resetVisual = true; + this->dataPtr->topicName = ""; + return; } // Only update lidarPose if the lidarEntity exists and the lidar is @@ -332,7 +308,7 @@ void VisualizeLidar::Update(const UpdateInfo &, // data arrives, the visual is offset from the obstacle if the sensor is // moving fast. if (!this->dataPtr->lidarEntityDirty && this->dataPtr->initialized && - !this->dataPtr->visualDirty) + !this->dataPtr->visualDirty) { this->dataPtr->lidarPose = worldPose(this->dataPtr->lidarEntity, _ecm); } @@ -379,13 +355,17 @@ void VisualizeLidar::UpdateSize(int _size) ////////////////////////////////////////////////// void VisualizeLidar::OnTopic(const QString &_topicName) { + std::string topic = _topicName.toStdString(); + if (this->dataPtr->topicName == topic) + return; + if (!this->dataPtr->topicName.empty() && !this->dataPtr->node.Unsubscribe(this->dataPtr->topicName)) { gzerr << "Unable to unsubscribe from topic [" << this->dataPtr->topicName <<"]" <dataPtr->topicName = _topicName.toStdString(); + this->dataPtr->topicName = topic; std::lock_guard lock(this->dataPtr->serviceMutex); // Reset visualization @@ -401,6 +381,8 @@ void VisualizeLidar::OnTopic(const QString &_topicName) } this->dataPtr->visualDirty = false; gzmsg << "Subscribed to " << this->dataPtr->topicName << std::endl; + + this->dataPtr->lidarEntityDirty = true; } ////////////////////////////////////////////////// @@ -489,27 +471,22 @@ void VisualizeLidar::OnScan(const msgs::LaserScan &_msg) this->dataPtr->msg.ranges().begin(), this->dataPtr->msg.ranges().end())); - this->dataPtr->visualDirty = true; - - for (auto data_values : this->dataPtr->msg.header().data()) + if (!math::equal(this->dataPtr->maxVisualRange, + this->dataPtr->msg.range_max())) { - if (data_values.key() == "frame_id") - { - if (this->dataPtr->lidarString.compare( - common::trimmed(data_values.value(0))) != 0) - { - this->dataPtr->lidarString = common::trimmed(data_values.value(0)); - this->dataPtr->lidarEntityDirty = true; - this->dataPtr->maxVisualRange = this->dataPtr->msg.range_max(); - this->dataPtr->minVisualRange = this->dataPtr->msg.range_min(); - this->dataPtr->lidar->SetMaxRange(this->dataPtr->maxVisualRange); - this->dataPtr->lidar->SetMinRange(this->dataPtr->minVisualRange); - this->MinRangeChanged(); - this->MaxRangeChanged(); - break; - } - } + this->dataPtr->maxVisualRange = this->dataPtr->msg.range_max(); + this->dataPtr->lidar->SetMaxRange(this->dataPtr->maxVisualRange); + this->MaxRangeChanged(); + } + if (!math::equal(this->dataPtr->minVisualRange, + this->dataPtr->msg.range_min())) + { + this->dataPtr->minVisualRange = this->dataPtr->msg.range_min(); + this->dataPtr->lidar->SetMinRange(this->dataPtr->minVisualRange); + this->MinRangeChanged(); } + + this->dataPtr->visualDirty = true; } } diff --git a/src/network/NetworkConfig_TEST.cc b/src/network/NetworkConfig_TEST.cc index d68be8ab33..4014518033 100644 --- a/src/network/NetworkConfig_TEST.cc +++ b/src/network/NetworkConfig_TEST.cc @@ -15,6 +15,7 @@ * */ + #include #include @@ -30,33 +31,33 @@ TEST(NetworkManager, ValueConstructor) { // Primary without number of secondaries is invalid auto config = NetworkConfig::FromValues("PRIMARY", 0); - assert(config.role == NetworkRole::None); - assert(config.numSecondariesExpected == 0); + ASSERT_EQ(config.role, NetworkRole::None); + ASSERT_EQ(config.numSecondariesExpected, 0); // Expect console warning as well } { // Primary with number of secondaries is valid auto config = NetworkConfig::FromValues("PRIMARY", 3); - assert(config.role == NetworkRole::SimulationPrimary); - assert(config.numSecondariesExpected == 3); + ASSERT_EQ(config.role, NetworkRole::SimulationPrimary); + ASSERT_EQ(config.numSecondariesExpected, 3); } { // Secondary is always valid auto config = NetworkConfig::FromValues("SECONDARY", 0); - assert(config.role == NetworkRole::SimulationSecondary); + ASSERT_EQ(config.role, NetworkRole::SimulationSecondary); } { // Readonly is always valid auto config = NetworkConfig::FromValues("READONLY"); - assert(config.role == NetworkRole::ReadOnly); + ASSERT_EQ(config.role, NetworkRole::ReadOnly); } { // Anything else is invalid auto config = NetworkConfig::FromValues("READ_WRITE"); - assert(config.role == NetworkRole::None); + ASSERT_EQ(config.role, NetworkRole::None); } } diff --git a/src/rendering/MarkerManager.cc b/src/rendering/MarkerManager.cc index 50b761cecb..04b1bef0e1 100644 --- a/src/rendering/MarkerManager.cc +++ b/src/rendering/MarkerManager.cc @@ -387,6 +387,8 @@ rendering::MarkerType MarkerManagerPrivate::MsgToType( return rendering::MarkerType::MT_BOX; case msgs::Marker::CAPSULE: return rendering::MarkerType::MT_CAPSULE; + case msgs::Marker::CONE: + return rendering::MarkerType::MT_CONE; case msgs::Marker::CYLINDER: return rendering::MarkerType::MT_CYLINDER; case msgs::Marker::LINE_STRIP: diff --git a/src/rendering/RenderUtil.cc b/src/rendering/RenderUtil.cc index 0c833a60ca..9b2cbf8acd 100644 --- a/src/rendering/RenderUtil.cc +++ b/src/rendering/RenderUtil.cc @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -1323,6 +1324,18 @@ void RenderUtil::Update() gzerr << "Failed to create sensor [" << sensorName << "]" << std::endl; } + else + { + auto sensorNode = this->dataPtr->sceneManager.NodeById(entity); + auto semPose = dataSdf.SemanticPose(); + math::Pose3d sensorPose; + sdf::Errors errors = semPose.Resolve(sensorPose); + if (!errors.empty()) + { + sensorPose = dataSdf.RawPose(); + } + sensorNode->SetLocalPose(sensorPose); + } } } } @@ -2388,86 +2401,6 @@ void RenderUtilPrivate::UpdateRenderingEntities( this->entityPoses[_entity] = _pose->Data(); return true; }); - - // Update cameras - _ecm.Each( - [&](const Entity &_entity, - const components::Camera *, - const components::Pose *_pose)->bool - { - this->entityPoses[_entity] = _pose->Data(); - return true; - }); - - // Update depth cameras - _ecm.Each( - [&](const Entity &_entity, - const components::DepthCamera *, - const components::Pose *_pose)->bool - { - this->entityPoses[_entity] = _pose->Data(); - return true; - }); - - // Update RGBD cameras - _ecm.Each( - [&](const Entity &_entity, - const components::RgbdCamera *, - const components::Pose *_pose)->bool - { - this->entityPoses[_entity] = _pose->Data(); - return true; - }); - - // Update gpu_lidar - _ecm.Each( - [&](const Entity &_entity, - const components::GpuLidar *, - const components::Pose *_pose)->bool - { - this->entityPoses[_entity] = _pose->Data(); - return true; - }); - - // Update thermal cameras - _ecm.Each( - [&](const Entity &_entity, - const components::ThermalCamera *, - const components::Pose *_pose)->bool - { - this->entityPoses[_entity] = _pose->Data(); - return true; - }); - - // Update segmentation cameras - _ecm.Each( - [&](const Entity &_entity, - const components::SegmentationCamera *, - const components::Pose *_pose)->bool - { - this->entityPoses[_entity] = _pose->Data(); - return true; - }); - - // Update bounding box cameras - _ecm.Each( - [&](const Entity &_entity, - const components::BoundingBoxCamera *, - const components::Pose *_pose)->bool - { - this->entityPoses[_entity] = _pose->Data(); - return true; - }); - - // Update wide angle cameras - _ecm.Each( - [&](const Entity &_entity, - const components::WideAngleCamera *, - const components::Pose *_pose)->bool - { - this->entityPoses[_entity] = _pose->Data(); - return true; - }); } ////////////////////////////////////////////////// diff --git a/src/rendering/SceneManager.cc b/src/rendering/SceneManager.cc index d94e6aa8b7..c1aefe8918 100644 --- a/src/rendering/SceneManager.cc +++ b/src/rendering/SceneManager.cc @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -667,6 +668,13 @@ rendering::GeometryPtr SceneManager::LoadGeometry(const sdf::Geometry &_geom, capsule->SetLength(_geom.CapsuleShape()->Length()); geom = capsule; } + else if (_geom.Type() == sdf::GeometryType::CONE) + { + geom = this->dataPtr->scene->CreateCone(); + scale.X() = _geom.ConeShape()->Radius() * 2; + scale.Y() = scale.X(); + scale.Z() = _geom.ConeShape()->Length(); + } else if (_geom.Type() == sdf::GeometryType::CYLINDER) { geom = this->dataPtr->scene->CreateCylinder(); diff --git a/src/systems/CMakeLists.txt b/src/systems/CMakeLists.txt index 1603a13930..1ec6aa8d7e 100644 --- a/src/systems/CMakeLists.txt +++ b/src/systems/CMakeLists.txt @@ -131,6 +131,7 @@ add_subdirectory(kinetic_energy_monitor) add_subdirectory(label) add_subdirectory(lens_flare) add_subdirectory(lift_drag) +add_subdirectory(lighter_than_air_dynamics) add_subdirectory(log) add_subdirectory(log_video_recorder) add_subdirectory(logical_audio_sensor_plugin) @@ -153,6 +154,7 @@ add_subdirectory(rf_comms) add_subdirectory(scene_broadcaster) add_subdirectory(sensors) add_subdirectory(shader_param) +add_subdirectory(spacecraft_thruster_model) add_subdirectory(thermal) add_subdirectory(thruster) add_subdirectory(touch_plugin) diff --git a/src/systems/ackermann_steering/AckermannSteering.cc b/src/systems/ackermann_steering/AckermannSteering.cc index f01c72dfb9..f1398c6c95 100644 --- a/src/systems/ackermann_steering/AckermannSteering.cc +++ b/src/systems/ackermann_steering/AckermannSteering.cc @@ -508,8 +508,8 @@ void AckermannSteering::PreUpdate(const UpdateInfo &_info, if (_info.dt < std::chrono::steady_clock::duration::zero()) { gzwarn << "Detected jump back in time [" - << std::chrono::duration_cast(_info.dt).count() - << "s]. System may not work properly." << std::endl; + << std::chrono::duration(_info.dt).count() + << "s]. System may not work properly." << std::endl; } // If the joints haven't been identified yet, look for them @@ -677,6 +677,11 @@ void AckermannSteering::PostUpdate(const UpdateInfo &_info, // Nothing left to do if paused. if (_info.paused) return; + + if (this->dataPtr->leftSteeringJoints.empty() || + this->dataPtr->rightSteeringJoints.empty()) + return; + if (this->dataPtr->steeringOnly) { this->dataPtr->UpdateAngle(_info, _ecm); diff --git a/src/systems/advanced_lift_drag/AdvancedLiftDrag.cc b/src/systems/advanced_lift_drag/AdvancedLiftDrag.cc index a7c60109fd..80065691d5 100644 --- a/src/systems/advanced_lift_drag/AdvancedLiftDrag.cc +++ b/src/systems/advanced_lift_drag/AdvancedLiftDrag.cc @@ -802,8 +802,8 @@ void AdvancedLiftDrag::PreUpdate(const UpdateInfo &_info, if (_info.dt < std::chrono::steady_clock::duration::zero()) { gzwarn << "Detected jump back in time [" - << std::chrono::duration_cast(_info.dt).count() - << "s]. System may not work properly." << std::endl; + << std::chrono::duration(_info.dt).count() + << "s]. System may not work properly." << std::endl; } if (!this->dataPtr->initialized) diff --git a/src/systems/air_pressure/AirPressure.cc b/src/systems/air_pressure/AirPressure.cc index 7dba18337f..3a99824e76 100644 --- a/src/systems/air_pressure/AirPressure.cc +++ b/src/systems/air_pressure/AirPressure.cc @@ -132,8 +132,8 @@ void AirPressure::PostUpdate(const UpdateInfo &_info, if (_info.dt < std::chrono::steady_clock::duration::zero()) { gzwarn << "Detected jump back in time [" - << std::chrono::duration_cast(_info.dt).count() - << "s]. System may not work properly." << std::endl; + << std::chrono::duration(_info.dt).count() + << "s]. System may not work properly." << std::endl; } this->dataPtr->CreateSensors(_ecm); diff --git a/src/systems/air_speed/AirSpeed.cc b/src/systems/air_speed/AirSpeed.cc index 73c9e92466..39904b9575 100644 --- a/src/systems/air_speed/AirSpeed.cc +++ b/src/systems/air_speed/AirSpeed.cc @@ -134,8 +134,8 @@ void AirSpeed::PostUpdate(const UpdateInfo &_info, if (_info.dt < std::chrono::steady_clock::duration::zero()) { gzwarn << "Detected jump back in time [" - << std::chrono::duration_cast(_info.dt).count() - << "s]. System may not work properly." << std::endl; + << std::chrono::duration(_info.dt).count() + << "s]. System may not work properly." << std::endl; } this->dataPtr->CreateSensors(_ecm); diff --git a/src/systems/altimeter/Altimeter.cc b/src/systems/altimeter/Altimeter.cc index ecd3ee1ea1..7f45d2899f 100644 --- a/src/systems/altimeter/Altimeter.cc +++ b/src/systems/altimeter/Altimeter.cc @@ -132,8 +132,8 @@ void Altimeter::PostUpdate(const UpdateInfo &_info, if (_info.dt < std::chrono::steady_clock::duration::zero()) { gzwarn << "Detected jump back in time [" - << std::chrono::duration_cast(_info.dt).count() - << "s]. System may not work properly." << std::endl; + << std::chrono::duration(_info.dt).count() + << "s]. System may not work properly." << std::endl; } this->dataPtr->CreateSensors(_ecm); diff --git a/src/systems/apply_joint_force/ApplyJointForce.cc b/src/systems/apply_joint_force/ApplyJointForce.cc index 19add069df..b7ea5eba18 100644 --- a/src/systems/apply_joint_force/ApplyJointForce.cc +++ b/src/systems/apply_joint_force/ApplyJointForce.cc @@ -134,8 +134,8 @@ void ApplyJointForce::PreUpdate(const UpdateInfo &_info, if (_info.dt < std::chrono::steady_clock::duration::zero()) { gzwarn << "Detected jump back in time [" - << std::chrono::duration_cast(_info.dt).count() - << "s]. System may not work properly." << std::endl; + << std::chrono::duration(_info.dt).count() + << "s]. System may not work properly." << std::endl; } // If the joint hasn't been identified yet, look for it diff --git a/src/systems/battery_plugin/LinearBatteryPlugin.cc b/src/systems/battery_plugin/LinearBatteryPlugin.cc index 357b01df97..0f84e1423a 100644 --- a/src/systems/battery_plugin/LinearBatteryPlugin.cc +++ b/src/systems/battery_plugin/LinearBatteryPlugin.cc @@ -480,6 +480,9 @@ void LinearBatteryPlugin::PreUpdate( { GZ_PROFILE("LinearBatteryPlugin::PreUpdate"); + if (!this->dataPtr->battery) + return; + // Recalculate the total power load among consumers double total_power_load = this->dataPtr->initialPowerLoad; _ecm.Each( @@ -546,12 +549,15 @@ void LinearBatteryPlugin::Update(const UpdateInfo &_info, { GZ_PROFILE("LinearBatteryPlugin::Update"); + if (!this->dataPtr->battery) + return; + // \TODO(anyone) Support rewind if (_info.dt < std::chrono::steady_clock::duration::zero()) { gzwarn << "Detected jump back in time [" - << std::chrono::duration_cast(_info.dt).count() - << "s]. System may not work properly." << std::endl; + << std::chrono::duration(_info.dt).count() + << "s]. System may not work properly." << std::endl; } if (_info.paused) @@ -610,6 +616,9 @@ void LinearBatteryPlugin::PostUpdate(const UpdateInfo &_info, if (_info.paused || !this->dataPtr->statePub) return; + if (!this->dataPtr->battery) + return; + // Publish battery state msgs::BatteryState msg; msg.mutable_header()->mutable_stamp()->CopyFrom( diff --git a/src/systems/breadcrumbs/Breadcrumbs.cc b/src/systems/breadcrumbs/Breadcrumbs.cc index 0f22687984..7db739da98 100644 --- a/src/systems/breadcrumbs/Breadcrumbs.cc +++ b/src/systems/breadcrumbs/Breadcrumbs.cc @@ -387,8 +387,33 @@ bool Breadcrumbs::MakeStatic(Entity _entity, EntityComponentManager &_ecm) Entity childLinkEntity = _ecm.EntityByComponents( components::CanonicalLink(), components::ParentEntity(_entity)); + // If the entity does not have a canonical link, it may be a nested model if (childLinkEntity == kNullEntity) - return false; + { + // Find canonical link within nested model + auto findCanonicalLink = [&_ecm](Entity _parent, auto &&_findCanonicalLink) + { + auto nestedEntities = _ecm.EntitiesByComponents( + components::Model(), components::ParentEntity(_parent)); + for (const auto ent : nestedEntities) + { + auto comp = _ecm.Component(ent); + if (comp) + { + return comp->Data(); + } + else + { + // recursively search for canonical link + return _findCanonicalLink(ent, _findCanonicalLink); + } + } + return kNullEntity; + }; + childLinkEntity = findCanonicalLink(_entity, findCanonicalLink); + if (childLinkEntity == kNullEntity) + return false; + } Entity detachableJointEntity = _ecm.CreateEntity(); _ecm.CreateComponent(detachableJointEntity, diff --git a/src/systems/contact/Contact.cc b/src/systems/contact/Contact.cc index f4b8019f0c..0dcea6a8b4 100644 --- a/src/systems/contact/Contact.cc +++ b/src/systems/contact/Contact.cc @@ -276,8 +276,8 @@ void Contact::PostUpdate(const UpdateInfo &_info, if (_info.dt < std::chrono::steady_clock::duration::zero()) { gzwarn << "Detected jump back in time [" - << std::chrono::duration_cast(_info.dt).count() - << "s]. System may not work properly." << std::endl; + << std::chrono::duration(_info.dt).count() + << "s]. System may not work properly." << std::endl; } if (!_info.paused) diff --git a/src/systems/diff_drive/DiffDrive.cc b/src/systems/diff_drive/DiffDrive.cc index 04fc968279..b65af3fce6 100644 --- a/src/systems/diff_drive/DiffDrive.cc +++ b/src/systems/diff_drive/DiffDrive.cc @@ -396,8 +396,8 @@ void DiffDrive::PreUpdate(const UpdateInfo &_info, if (_info.dt < std::chrono::steady_clock::duration::zero()) { gzwarn << "Detected jump back in time [" - << std::chrono::duration_cast(_info.dt).count() - << "s]. System may not work properly." << std::endl; + << std::chrono::duration(_info.dt).count() + << "s]. System may not work properly." << std::endl; } // If the joints haven't been identified yet, look for them diff --git a/src/systems/elevator/vender/afsm/include/afsm/detail/transitions.hpp b/src/systems/elevator/vender/afsm/include/afsm/detail/transitions.hpp index e4fe1fa821..7b5843c009 100644 --- a/src/systems/elevator/vender/afsm/include/afsm/detail/transitions.hpp +++ b/src/systems/elevator/vender/afsm/include/afsm/detail/transitions.hpp @@ -525,7 +525,8 @@ class state_transition_table { void check_default_transition() { - auto const& ttable = transition_table( state_indexes{} ); + auto st = state_indexes{}; + auto const& ttable = transition_table(st); ttable[current_state()](*this, none{}); } @@ -544,7 +545,8 @@ class state_transition_table { void exit(Event&& event) { - auto const& table = exit_table( state_indexes{} ); + auto st = state_indexes{}; + auto const& table = exit_table(st); table[current_state()](states_, ::std::forward(event), *fsm_); } @@ -552,7 +554,8 @@ class state_transition_table { actions::event_process_result process_transition_event(Event&& event) { - auto const& inv_table = transition_table( state_indexes{} ); + auto st = state_indexes{}; + auto const& inv_table = transition_table(st); return inv_table[current_state()](*this, ::std::forward(event)); } @@ -655,10 +658,11 @@ class state_transition_table { event_set current_handled_events() const { - auto const& table = get_current_events_table(state_indexes{}); + auto st = state_indexes{}; + auto const& table = get_current_events_table(st); auto res = table[current_state_](states_); auto const& available_transitions - = get_available_transitions_table(state_indexes{}); + = get_available_transitions_table(st); auto const& trans = available_transitions[current_state_]; res.insert( trans.begin(), trans.end()); return res; @@ -667,7 +671,8 @@ class state_transition_table { event_set current_deferrable_events() const { - auto const& table = get_current_deferred_events_table(state_indexes{}); + auto st = state_indexes{}; + auto const& table = get_current_deferred_events_table(st); return table[current_state_](states_); } diff --git a/src/systems/follow_actor/FollowActor.cc b/src/systems/follow_actor/FollowActor.cc index 99d8315882..88f72c3f84 100644 --- a/src/systems/follow_actor/FollowActor.cc +++ b/src/systems/follow_actor/FollowActor.cc @@ -209,7 +209,9 @@ void FollowActor::PreUpdate(const UpdateInfo &_info, this->dataPtr->lastUpdate = _info.simTime; // Is there a follow target? - if (this->dataPtr->targetEntity == kNullEntity) + if (this->dataPtr->targetEntity == kNullEntity || + !_ecm.HasEntity(this->dataPtr->targetEntity) || + !_ecm.HasEntity(this->dataPtr->actorEntity)) return; // Current world pose diff --git a/src/systems/force_torque/ForceTorque.cc b/src/systems/force_torque/ForceTorque.cc index 652c793265..42f8454c4a 100644 --- a/src/systems/force_torque/ForceTorque.cc +++ b/src/systems/force_torque/ForceTorque.cc @@ -44,7 +44,9 @@ #include "gz/sim/components/Pose.hh" #include "gz/sim/components/Sensor.hh" #include "gz/sim/components/World.hh" +#include "gz/sim/components/WrenchMeasured.hh" #include "gz/sim/EntityComponentManager.hh" +#include "gz/sim/System.hh" #include "gz/sim/Util.hh" using namespace gz; @@ -77,7 +79,7 @@ class gz::sim::systems::ForceTorquePrivate public: sensors::SensorFactory sensorFactory; /// \brief Keep list of sensors that were created during the previous - /// `PostUpdate`, so that components can be created during the next + /// `Update`, so that components can be created during the next /// `PreUpdate`. public: std::unordered_set newSensors; @@ -128,6 +130,13 @@ ForceTorque::ForceTorque() ////////////////////////////////////////////////// ForceTorque::~ForceTorque() = default; +////////////////////////////////////////////////// +System::PriorityType ForceTorque::ConfigurePriority() +{ + // Execute after Physics::Update but before systems with default priority. + return ::gz::sim::systems::kPostPhysicsSensorPriority; +} + ////////////////////////////////////////////////// void ForceTorque::PreUpdate(const UpdateInfo &/*_info*/, EntityComponentManager &_ecm) @@ -157,17 +166,17 @@ void ForceTorque::PreUpdate(const UpdateInfo &/*_info*/, } ////////////////////////////////////////////////// -void ForceTorque::PostUpdate(const UpdateInfo &_info, - const EntityComponentManager &_ecm) +void ForceTorque::Update(const UpdateInfo &_info, + EntityComponentManager &_ecm) { - GZ_PROFILE("ForceTorque::PostUpdate"); + GZ_PROFILE("ForceTorque::Update"); // \TODO(anyone) Support rewind if (_info.dt < std::chrono::steady_clock::duration::zero()) { gzwarn << "Detected jump back in time [" - << std::chrono::duration_cast(_info.dt).count() - << "s]. System may not work properly." << std::endl; + << std::chrono::duration(_info.dt).count() + << "s]. System may not work properly." << std::endl; } this->dataPtr->CreateSensors(_ecm); @@ -176,15 +185,15 @@ void ForceTorque::PostUpdate(const UpdateInfo &_info, if (!_info.paused) { // check to see if update is necessary - // we only update if there is at least one sensor that needs data - // and that sensor has subscribers. + // we only update if there is at least one sensor that needs data. // note: gz-sensors does its own throttling. Here the check is mainly // to avoid doing work in the ForceTorquePrivate::Update function bool needsUpdate = false; - for (auto &it : this->dataPtr->entitySensorMap) + for (const auto &[sensorEntity, sensor] : this->dataPtr->entitySensorMap) { - if (it.second->NextDataUpdateTime() <= _info.simTime && - it.second->HasConnections()) + if (sensor->NextDataUpdateTime() <= _info.simTime && + (sensor->HasConnections() || + _ecm.Component(sensorEntity) != nullptr)) { needsUpdate = true; break; @@ -193,11 +202,23 @@ void ForceTorque::PostUpdate(const UpdateInfo &_info, if (!needsUpdate) return; + // Transform joint wrench to sensor wrench and write to sensor this->dataPtr->Update(_ecm); - for (auto &it : this->dataPtr->entitySensorMap) + for (auto &[sensorEntity, sensor] : this->dataPtr->entitySensorMap) { - it.second->Update(_info.simTime, false); + // Call gz::sensors::ForceTorqueSensor::Update + // * Convert to user-specified frame + // * Apply noise + // * Publish to gz-transport topic + sensor->Update(_info.simTime, false); + auto wrenchComponent = + _ecm.Component(sensorEntity); + if (wrenchComponent) + { + const auto &measuredWrench = sensor->MeasuredWrench(); + *wrenchComponent = components::WrenchMeasured(measuredWrench); + } } } @@ -269,7 +290,8 @@ void ForceTorquePrivate::Update(const EntityComponentManager &_ecm) return true; } - // Appropriate components haven't been populated by physics yet + // Return early if JointTransmittedWrench component has not yet been + // populated by the Physics system auto jointWrench = _ecm.Component( jointLinkIt->second.joint); if (nullptr == jointWrench) @@ -438,8 +460,9 @@ void ForceTorquePrivate::RemoveForceTorqueEntities( } GZ_ADD_PLUGIN(ForceTorque, System, + ForceTorque::ISystemConfigurePriority, ForceTorque::ISystemPreUpdate, - ForceTorque::ISystemPostUpdate + ForceTorque::ISystemUpdate ) GZ_ADD_PLUGIN_ALIAS(ForceTorque, "gz::sim::systems::ForceTorque") diff --git a/src/systems/force_torque/ForceTorque.hh b/src/systems/force_torque/ForceTorque.hh index 09b5b7ed70..62e2231e80 100644 --- a/src/systems/force_torque/ForceTorque.hh +++ b/src/systems/force_torque/ForceTorque.hh @@ -41,8 +41,9 @@ namespace systems /// quantites are expressed, not the point of application. class ForceTorque: public System, + public ISystemConfigurePriority, public ISystemPreUpdate, - public ISystemPostUpdate + public ISystemUpdate { /// \brief Constructor public: ForceTorque(); @@ -50,13 +51,16 @@ namespace systems /// \brief Destructor public: ~ForceTorque() override; + /// Documentation inherited + public: System::PriorityType ConfigurePriority() final; + /// Documentation inherited public: void PreUpdate(const UpdateInfo &_info, EntityComponentManager &_ecm) final; /// Documentation inherited - public: void PostUpdate(const UpdateInfo &_info, - const EntityComponentManager &_ecm) final; + public: void Update(const UpdateInfo &_info, + EntityComponentManager &_ecm) final; /// \brief Private data pointer. private: std::unique_ptr dataPtr; diff --git a/src/systems/hydrodynamics/Hydrodynamics.cc b/src/systems/hydrodynamics/Hydrodynamics.cc index 171ca8f021..eed111deff 100644 --- a/src/systems/hydrodynamics/Hydrodynamics.cc +++ b/src/systems/hydrodynamics/Hydrodynamics.cc @@ -340,7 +340,11 @@ void Hydrodynamics::Configure( << "\thttps://github.com/gazebosim/gz-sim/pull/1888" << std::endl; } + // Added mass according to Fossen's equations (p 37) + // Note: Adding added mass here is deprecated and will be removed in + // Gazebo J as this formulation has instabilities. + bool addedMassSpecified = false; this->dataPtr->Ma = Eigen::MatrixXd::Zero(6, 6); for(auto i = 0; i < 6; i++) { @@ -350,12 +354,25 @@ void Hydrodynamics::Configure( prefix += "Dot"; prefix += snameConventionVel[j]; this->dataPtr->Ma(i, j) = SdfParamDouble(_sdf, prefix, 0); + addedMassSpecified = (std::abs(this->dataPtr->Ma(i, j)) > 1e-6) + || addedMassSpecified; } } _sdf->Get("disable_coriolis", this->dataPtr->disableCoriolis, false); - _sdf->Get("disable_added_mass", this->dataPtr->disableAddedMass, false); - + _sdf->Get("disable_added_mass", + this->dataPtr->disableAddedMass, false); + if (!this->dataPtr->disableAddedMass && addedMassSpecified) + { + gzwarn << "The use of added mass through this plugin is deprecated and " + << "will be removed in Gazebo J* as this formulation has instabilities. " + << "We recommend using the SDF `` tag based method " + << "[http://sdformat.org/spec?ver=1.11&elem=link" + << "#inertial_fluid_added_mass]" + << "To get rid of this warning we recommend setting " + << "`` to true and updating your model" + << std::endl; + } // Create model object, to access convenient functions auto model = gz::sim::Model(_entity); diff --git a/src/systems/hydrodynamics/Hydrodynamics.hh b/src/systems/hydrodynamics/Hydrodynamics.hh index 1ada82b467..42d960a0ce 100644 --- a/src/systems/hydrodynamics/Hydrodynamics.hh +++ b/src/systems/hydrodynamics/Hydrodynamics.hh @@ -46,12 +46,12 @@ namespace systems /// quadratic drag and coriolis force. /// /// ### Diagonal terms: - /// * - Added mass in x direction [kg] - /// * - Added mass in y direction [kg] - /// * - Added mass in z direction [kg] - /// * - Added mass in roll direction [kgm^2] - /// * - Added mass in pitch direction [kgm^2] - /// * - Added mass in yaw direction [kgm^2] + /// * - (Deprecated) Added mass in x direction [kg] + /// * - (Deprecated) Added mass in y direction [kg] + /// * - (Deprecated) Added mass in z direction [kg] + /// * - (Deprecated) Added mass in roll direction [kgm^2] + /// * - (Deprecated) Added mass in pitch direction [kgm^2] + /// * - (Deprecated) Added mass in yaw direction [kgm^2] /// * - Quadratic damping, 2nd order, x component [kg/m] /// * - Linear damping, 1st order, x component [kg] /// * - Quadratic damping, 2nd order, y component [kg/m] @@ -70,10 +70,13 @@ namespace systems /// non-diagonal sides. We use the SNAMe convention of naming search terms. /// (x, y, z) correspond to the respective axis. (k, m, n) correspond to /// roll, pitch and yaw. Similarly U, V, W represent velocity vectors in - /// X, Y and Z axis while P, Q, R representangular velocity in roll, pitch + /// X, Y and Z axis while P, Q, R represent angular velocity in roll, pitch /// and yaw axis respectively. /// * Added Mass: <{x|y|z|k|m|n}Dot{U|V|W|P|Q|R}> e.g. - /// Units are either kg or kgm^2 depending on the choice of terms. + /// (Deprecated) Units are either kg or kgm^2 depending on the + /// choice of terms. You should use the sdf method based spec + // for `fluid_added_mass`: + /// http://sdformat.org/spec?ver=1.11&elem=link#inertial_fluid_added_mass /// * Quadratic Damping With abs term (this is probably what you want): /// <{x|y|z|k|m|n}{U|V|W|P|Q|R}abs{U|V|W|P|Q|R}> /// e.g. diff --git a/src/systems/imu/Imu.cc b/src/systems/imu/Imu.cc index eebbe77b6c..6026cda124 100644 --- a/src/systems/imu/Imu.cc +++ b/src/systems/imu/Imu.cc @@ -136,8 +136,8 @@ void Imu::PostUpdate(const UpdateInfo &_info, if (_info.dt < std::chrono::steady_clock::duration::zero()) { gzwarn << "Detected jump back in time [" - << std::chrono::duration_cast(_info.dt).count() - << "s]. System may not work properly." << std::endl; + << std::chrono::duration(_info.dt).count() + << "s]. System may not work properly." << std::endl; } this->dataPtr->CreateSensors(_ecm); diff --git a/src/systems/joint_controller/JointController.cc b/src/systems/joint_controller/JointController.cc index 40f5caac33..cf8f54187c 100644 --- a/src/systems/joint_controller/JointController.cc +++ b/src/systems/joint_controller/JointController.cc @@ -265,8 +265,8 @@ void JointController::PreUpdate(const UpdateInfo &_info, if (_info.dt < std::chrono::steady_clock::duration::zero()) { gzwarn << "Detected jump back in time [" - << std::chrono::duration_cast(_info.dt).count() - << "s]. System may not work properly." << std::endl; + << std::chrono::duration(_info.dt).count() + << "s]. System may not work properly." << std::endl; } // If the joints haven't been identified yet, look for them diff --git a/src/systems/joint_position_controller/JointPositionController.cc b/src/systems/joint_position_controller/JointPositionController.cc index b2835160ff..4b36a5076c 100644 --- a/src/systems/joint_position_controller/JointPositionController.cc +++ b/src/systems/joint_position_controller/JointPositionController.cc @@ -339,8 +339,8 @@ void JointPositionController::PreUpdate( if (_info.dt < std::chrono::steady_clock::duration::zero()) { gzwarn << "Detected jump back in time [" - << std::chrono::duration_cast(_info.dt).count() - << "s]. System may not work properly." << std::endl; + << std::chrono::duration(_info.dt).count() + << "s]. System may not work properly." << std::endl; } // If the joints haven't been identified yet, look for them diff --git a/src/systems/lens_flare/LensFlare.cc b/src/systems/lens_flare/LensFlare.cc index 8f099d6653..6739bb4e71 100644 --- a/src/systems/lens_flare/LensFlare.cc +++ b/src/systems/lens_flare/LensFlare.cc @@ -128,8 +128,9 @@ void LensFlare::Configure( } // Get Camera Name - this->dataPtr->cameraName = scopedName(this->dataPtr->entity, - _ecm, "::", false); + this->dataPtr->cameraName = + removeParentScope(scopedName(this->dataPtr->entity, + _ecm, "::", false), "::"); // call function that connects to post render event this->dataPtr->postRenderConn = diff --git a/src/systems/lift_drag/LiftDrag.cc b/src/systems/lift_drag/LiftDrag.cc index c83837c5ff..902bfec942 100644 --- a/src/systems/lift_drag/LiftDrag.cc +++ b/src/systems/lift_drag/LiftDrag.cc @@ -550,8 +550,8 @@ void LiftDrag::PreUpdate(const UpdateInfo &_info, EntityComponentManager &_ecm) if (_info.dt < std::chrono::steady_clock::duration::zero()) { gzwarn << "Detected jump back in time [" - << std::chrono::duration_cast(_info.dt).count() - << "s]. System may not work properly." << std::endl; + << std::chrono::duration(_info.dt).count() + << "s]. System may not work properly." << std::endl; } if (!this->dataPtr->initialized) diff --git a/src/systems/lighter_than_air_dynamics/CMakeLists.txt b/src/systems/lighter_than_air_dynamics/CMakeLists.txt new file mode 100644 index 0000000000..71e42f7f36 --- /dev/null +++ b/src/systems/lighter_than_air_dynamics/CMakeLists.txt @@ -0,0 +1,7 @@ +gz_add_system(lighter_than_air_dynamics + SOURCES + LighterThanAirDynamics.cc + PUBLIC_LINK_LIBS + gz-common${GZ_COMMON_VER}::gz-common${GZ_COMMON_VER} + gz-math${GZ_MATH_VER}::eigen3 +) diff --git a/src/systems/lighter_than_air_dynamics/LighterThanAirDynamics.cc b/src/systems/lighter_than_air_dynamics/LighterThanAirDynamics.cc new file mode 100644 index 0000000000..050cc43674 --- /dev/null +++ b/src/systems/lighter_than_air_dynamics/LighterThanAirDynamics.cc @@ -0,0 +1,459 @@ +/* + * Copyright (C) 2023 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#include + +#include + +#include +#include + +#include + +#include "gz/sim/components/AngularVelocity.hh" +#include "gz/sim/components/Environment.hh" +#include "gz/sim/components/LinearVelocity.hh" +#include "gz/sim/components/Pose.hh" +#include "gz/sim/components/World.hh" +#include "gz/sim/components/Wind.hh" +#include "gz/sim/components/Inertial.hh" + +#include "gz/sim/Link.hh" +#include "gz/sim/Model.hh" +#include "gz/sim/System.hh" +#include "gz/sim/Util.hh" +#include "gz/sim/Entity.hh" +#include "gz/sim/EntityComponentManager.hh" + +#include + +#include "gz/math/Matrix3.hh" + +#include "gz/transport/Node.hh" + +#include +#include + +#include "LighterThanAirDynamics.hh" + +using namespace gz; +using namespace sim; +using namespace systems; + +/// \brief Private LighterThanAirDynamics data class. +class gz::sim::systems::LighterThanAirDynamicsPrivateData +{ + + /// \brief air density [kg/m^3]. + public: double airDensity; + + /// \brief force coefficient of the viscous flow contribution to the hull + public: double forceViscCoeff; + + /// \brief force coefficient of the inviscid flow contribution to the hull + public: double forceInviscCoeff; + + /// \brief moment coefficient of the viscous flow contribution to the hull + public: double momentViscCoeff; + + /// \brief moment coefficient of the inviscid flow contribution to the hull + public: double momentInviscCoeff; + + /// \brief Top left [3x3] matrix of the [6x6] added mass matrix + public: math::Matrix3d m11; + + /// \brief Top right [3x3] matrix of the [6x6] added mass matrix + public: math::Matrix3d m12; + + /// \brief Bottom left [3x3] matrix of the [6x6] added mass matrix + public: math::Matrix3d m21; + + /// \brief Bottom right [3x3] matrix of the [6x6] added mass matrix + public: math::Matrix3d m22; + + /// \brief distance measured from the nose, where the flow stopped + /// being potential + public: double epsV; + + /// \brief axial drag coefficient of the hull + public: double axialDragCoeff; + + /// \brief The Gazebo Transport node + public: transport::Node node; + + /// \brief Link entity + public: Entity linkEntity; + + /// \brief World frame wind + public: math::Vector3d windVector {0, 0, 0}; + + /// \brief wind current callback + public: void UpdateWind(const msgs::Vector3d &_msg); + + /// \brief Mutex + public: std::mutex mtx; +}; + +///////////////////////////////////////////////// +void LighterThanAirDynamicsPrivateData::UpdateWind(const msgs::Vector3d &_msg) +{ + std::lock_guard lock(this->mtx); + this->windVector = gz::msgs::Convert(_msg); +} + +///////////////////////////////////////////////// +void AddAngularVelocityComponent( + const gz::sim::Entity &_entity, + gz::sim::EntityComponentManager &_ecm) +{ + if (!_ecm.Component(_entity)) + { + _ecm.CreateComponent(_entity, + gz::sim::components::AngularVelocity()); + } + + // Create an angular velocity component if one is not present. + if (!_ecm.Component( + _entity)) + { + _ecm.CreateComponent(_entity, + gz::sim::components::WorldAngularVelocity()); + } +} + +///////////////////////////////////////////////// +void AddWorldPose( + const gz::sim::Entity &_entity, + gz::sim::EntityComponentManager &_ecm) +{ + if (!_ecm.Component(_entity)) + { + _ecm.CreateComponent(_entity, gz::sim::components::WorldPose()); + } +} + +///////////////////////////////////////////////// +void AddWorldInertial( + const gz::sim::Entity &_entity, + gz::sim::EntityComponentManager &_ecm) +{ + if (!_ecm.Component(_entity)) + { + _ecm.CreateComponent(_entity, gz::sim::components::Inertial()); + } +} + +///////////////////////////////////////////////// +void AddWorldLinearVelocity( + const gz::sim::Entity &_entity, + gz::sim::EntityComponentManager &_ecm) +{ + if (!_ecm.Component( + _entity)) + { + _ecm.CreateComponent(_entity, + gz::sim::components::WorldLinearVelocity()); + } +} + +///////////////////////////////////////////////// +double SdfParamDouble( + const std::shared_ptr &_sdf, + const std::string& _field, + double _default) +{ + return _sdf->Get(_field, _default).first; +} + +math::Matrix3d LighterThanAirDynamics::SkewSymmetricMatrix(math::Vector3d mat) +{ + math::Matrix3d skewSymmetric(0, -1.0*mat[2], mat[1], + mat[2], 0, -1.0*mat[0], + -1.0*mat[1], mat[0], 0); + + + return skewSymmetric; +} + + +math::Vector3d LighterThanAirDynamics::AddedMassForce( + math::Vector3d lin_vel, math::Vector3d ang_vel, + math::Matrix3d m11, math::Matrix3d m12) +{ + auto skewAngVel = this->SkewSymmetricMatrix(ang_vel); + math::Vector3d forces = skewAngVel * (m11 * lin_vel + m12 * ang_vel); + + return forces; +} + +math::Vector3d LighterThanAirDynamics::AddedMassTorque( + math::Vector3d lin_vel, math::Vector3d ang_vel, + math::Matrix3d m11, math::Matrix3d m12, math::Matrix3d m21, + math::Matrix3d m22) +{ + auto skewAngVel = this->SkewSymmetricMatrix(ang_vel); + auto skewLinVel = this->SkewSymmetricMatrix(lin_vel); + + // note: the m11*lin_vel term: it is already accounted in the + // inviscous term see [2], and thus removed by the zero multiplication, + // but is added here for visibility. + math::Vector3d torque = skewLinVel * (m11 * lin_vel*0 + m12 * ang_vel) + + skewAngVel * (m21 * lin_vel + m22 * ang_vel); + + return torque; +} + +math::Vector3d LighterThanAirDynamics::LocalVelocity(math::Vector3d lin_vel, + math::Vector3d ang_vel, math::Vector3d dist) +{ + return lin_vel + ang_vel.Cross(dist); +} + +double LighterThanAirDynamics::DynamicPressure( + math::Vector3d vec, double air_density) +{ + return 0.5 * air_density * vec.SquaredLength(); +} + +///////////////////////////////////////////////// +LighterThanAirDynamics::LighterThanAirDynamics() +{ + this->dataPtr = std::make_unique(); +} + +///////////////////////////////////////////////// +LighterThanAirDynamics::~LighterThanAirDynamics() +{ + // Do nothing +} + +///////////////////////////////////////////////// +void LighterThanAirDynamics::Configure( + const gz::sim::Entity &_entity, + const std::shared_ptr &_sdf, + gz::sim::EntityComponentManager &_ecm, + gz::sim::EventManager &/*_eventMgr*/ +) +{ + if (_sdf->HasElement("air_density")) + { + this->dataPtr->airDensity = SdfParamDouble(_sdf, "air_density", 1.225); + } + + // Create model object, to access convenient functions + auto model = gz::sim::Model(_entity); + + if (!_sdf->HasElement("link_name")) + { + gzerr << "You must specify a for the lighter than air" + << " plugin to act upon"; + return; + } + auto linkName = _sdf->Get("link_name"); + this->dataPtr->linkEntity = model.LinkByName(_ecm, linkName); + if (!_ecm.HasEntity(this->dataPtr->linkEntity)) + { + gzerr << "Link name" << linkName << "does not exist"; + return; + } + + if(_sdf->HasElement("moment_inviscid_coeff")) + { + this->dataPtr->momentInviscCoeff = + _sdf->Get("moment_inviscid_coeff"); + + gzdbg << "moment_inviscid_coeff: " + << this->dataPtr->momentInviscCoeff << "\n"; + }else{ + gzerr << "moment_inviscid_coeff not found \n"; + } + + if(_sdf->HasElement("moment_viscous_coeff")) + { + this->dataPtr->momentViscCoeff = _sdf->Get("moment_viscous_coeff"); + gzdbg << "moment_viscous_coeff: " + << this->dataPtr->momentViscCoeff << "\n"; + }else{ + gzerr << "moment_viscous_coeff not found \n"; + return; + } + + if(_sdf->HasElement("force_inviscid_coeff")) + { + this->dataPtr->forceInviscCoeff = + _sdf->Get("force_inviscid_coeff"); + + gzdbg << "force_inviscid_coeff: " + << this->dataPtr->forceInviscCoeff << "\n"; + }else{ + gzerr << "force_inviscid_coeff not found \n"; + return; + } + + if(_sdf->HasElement("force_viscous_coeff")) + { + this->dataPtr->forceViscCoeff = _sdf->Get("force_viscous_coeff"); + + gzdbg << "force_inviscous_coeff: " << this->dataPtr->forceViscCoeff << "\n"; + }else{ + gzerr << "force_inviscous_coeff not found \n"; + return; + } + + if(_sdf->HasElement("eps_v")) + { + this->dataPtr->epsV = _sdf->Get("eps_v"); + gzdbg << "eps_v: " << this->dataPtr->epsV << "\n"; + }else{ + gzerr << "eps_v not found \n"; + return; + } + + if(_sdf->HasElement("axial_drag_coeff")) + { + this->dataPtr->axialDragCoeff = _sdf->Get("axial_drag_coeff"); + gzdbg << "axial_drag_coeff: " << this->dataPtr->axialDragCoeff << "\n"; + }else{ + gzerr << "axial_drag_coeff not found \n"; + return; + } + + AddWorldPose(this->dataPtr->linkEntity, _ecm); + AddWorldInertial(this->dataPtr->linkEntity, _ecm); + AddAngularVelocityComponent(this->dataPtr->linkEntity, _ecm); + AddWorldLinearVelocity(this->dataPtr->linkEntity, _ecm); + + gz::sim::Link baseLink(this->dataPtr->linkEntity); + + math::Matrix6d added_mass_matrix = + (baseLink.WorldFluidAddedMassMatrix(_ecm)).value(); + + this->dataPtr->m11 = added_mass_matrix.Submatrix( + math::Matrix6d::Matrix6Corner::TOP_LEFT); + this->dataPtr->m12 = added_mass_matrix.Submatrix( + math::Matrix6d::Matrix6Corner::TOP_RIGHT); + this->dataPtr->m21 = added_mass_matrix.Submatrix( + math::Matrix6d::Matrix6Corner::BOTTOM_LEFT); + this->dataPtr->m22 = added_mass_matrix.Submatrix( + math::Matrix6d::Matrix6Corner::BOTTOM_RIGHT); +} + +///////////////////////////////////////////////// +void LighterThanAirDynamics::PreUpdate( + const gz::sim::UpdateInfo &_info, + gz::sim::EntityComponentManager &_ecm) +{ + if (_info.paused) + return; + + // Get vehicle state + gz::sim::Link baseLink(this->dataPtr->linkEntity); + auto linearVelocity = _ecm.Component( + this->dataPtr->linkEntity); + auto rotationalVelocity = baseLink.WorldAngularVelocity(_ecm); + + if (!linearVelocity) + { + gzerr << "no linear vel" <<"\n"; + return; + } + + // Transform state to local frame + auto pose = baseLink.WorldPose(_ecm); + // Since we are transforming angular and linear velocity we only care about + // rotation. Also this is where we apply the effects of current to the link + auto linearVel = pose->Rot().Inverse() * (linearVelocity->Data()); + auto angularVelocity = pose->Rot().Inverse() * *rotationalVelocity; + + // Calculate viscous forces + math::Vector3d velEpsV = LocalVelocity(linearVel, angularVelocity, + math::Vector3d(-this->dataPtr->epsV, 0, 0)); + + double q0EpsV = DynamicPressure(velEpsV, this->dataPtr->airDensity); + double gammaEpsV = 0.0f; + + gammaEpsV = std::atan2(std::sqrt(velEpsV[1]*velEpsV[1] + + velEpsV[2]*velEpsV[2]), velEpsV[0]); + + double forceInviscCoeff = this->dataPtr->forceInviscCoeff; + double forceViscCoeff = this->dataPtr->forceViscCoeff; + double momentInviscCoeff = this->dataPtr->momentInviscCoeff; + double momentViscCoeff = this->dataPtr->momentViscCoeff; + + double forceViscMag_ = q0EpsV*(-forceInviscCoeff*std::sin(2*gammaEpsV) + + forceViscCoeff*std::sin(gammaEpsV)*std::sin(gammaEpsV)); + + double momentViscMag_ = q0EpsV*(-momentInviscCoeff*std::sin(2*gammaEpsV) + + momentViscCoeff*std::sin(gammaEpsV)*std::sin(gammaEpsV)); + + double viscNormalMag_ = std::sqrt(velEpsV[1]*velEpsV[1] + + velEpsV[2]*velEpsV[2]); + + double viscNormalY_ = 0.0; + double viscNormalZ_ = 0.0; + + if(viscNormalMag_ > std::numeric_limits::epsilon()){ + + viscNormalY_ = velEpsV[1]/viscNormalMag_; + viscNormalZ_ = velEpsV[2]/viscNormalMag_; + } + + auto forceVisc = forceViscMag_ * math::Vector3d(0, + -viscNormalY_, + -viscNormalZ_); + + auto momentVisc = momentViscMag_ * math::Vector3d(0, + viscNormalZ_, + -viscNormalY_); + + // Added Mass forces & Torques + auto forceAddedMass = -1.0 * this->AddedMassForce(linearVel, + angularVelocity, + this->dataPtr->m11, + this->dataPtr->m12); + + auto momentAddedMass = -1.0 * this->AddedMassTorque(linearVel, + angularVelocity, + this->dataPtr->m11, + this->dataPtr->m12, + this->dataPtr->m21, + this->dataPtr->m22); + + // Axial drag + double q0 = DynamicPressure(linearVel, this->dataPtr->airDensity); + double angleOfAttack = std::atan2(linearVel[2], linearVel[0]); + double axialDragCoeff = this->dataPtr->axialDragCoeff; + + auto forceAxialDrag = math::Vector3d(-q0 * axialDragCoeff * + std::cos(angleOfAttack) * std::cos(angleOfAttack), 0, 0); + + math::Vector3d totalForce = forceAddedMass + forceVisc + forceAxialDrag; + math::Vector3d totalTorque = momentAddedMass + momentVisc; + + baseLink.AddWorldWrench(_ecm, + pose->Rot()*(totalForce), + pose->Rot()*totalTorque); +} + +GZ_ADD_PLUGIN( + LighterThanAirDynamics, System, + LighterThanAirDynamics::ISystemConfigure, + LighterThanAirDynamics::ISystemPreUpdate +) + +GZ_ADD_PLUGIN_ALIAS( + LighterThanAirDynamics, + "gz::sim::systems::LighterThanAirDynamics") diff --git a/src/systems/lighter_than_air_dynamics/LighterThanAirDynamics.hh b/src/systems/lighter_than_air_dynamics/LighterThanAirDynamics.hh new file mode 100644 index 0000000000..a7c0971d55 --- /dev/null +++ b/src/systems/lighter_than_air_dynamics/LighterThanAirDynamics.hh @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2023 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#ifndef GZ_SIM_SYSTEMS_LighterThanAirDynamics_HH_ +#define GZ_SIM_SYSTEMS_LighterThanAirDynamics_HH_ + +#include +#include + +namespace gz +{ +namespace sim +{ +// Inline bracket to help doxygen filtering. +inline namespace GZ_SIM_VERSION_NAMESPACE { +namespace systems +{ + class LighterThanAirDynamicsPrivateData; + + /// \brief This class provides the effect of viscousity on the hull of + /// lighter-than-air vehicles such as airships. The equations implemented + /// is based on the work published in [1], which describes a modeling + /// approach for the nonlinear dynamics simulation of airships and [2] + /// providing more insight of the modelling of an airship. + /// + /// ## System Parameters + /// * `` sets the density of air that surrounds + /// the buoyant object. [Units: kgm^-3] + + /// * `` is the second coefficient in Eq (11) in [1]: + /// \f$ \eta C_{DC} \cdot \int_{\varepsilon_{0}}^{L} 2R \,d\varepsilon \f$ + + /// * `` is the second coefficient in Eq (14) in [1]: + /// \f$ \eta C_{DC} \cdot \int_{\varepsilon_{0}}^{L} 2R (\varepsilon_{m} - + /// \varepsilon) \,d\varepsilon \f$ + + /// * `` is the point on the hull where the flow ceases being + /// potential + + /// * `` the actual drag coefficient of the hull + + /// ## Notes + /// + /// This class only implements the viscous effects on the hull of an + /// airship and currently does not take into the account wind. + /// This class should be used in combination with the boyuancy, added mass + /// and gravity plugins to simulate the behaviour of an airship. + /// Its important to provide a collision property to the hull, since this is + /// from which the buoyancy plugin determines the volume. + /// + /// # Citations + /// [1] Li, Y., & Nahon, M. (2007). Modeling and simulation of airship + /// dynamics. Journal of Guidance, Control, and Dynamics, 30(6), 1691–1700. + /// + /// [2] Li, Y., Nahon, M., & Sharf, I. (2011). Airship dynamics modeling: + /// A literature review. Progress in Aerospace Sciences, 47(3), 217–239. + + class LighterThanAirDynamics: + public gz::sim::System, + public gz::sim::ISystemConfigure, + public gz::sim::ISystemPreUpdate + { + /// \brief Constructor + public: LighterThanAirDynamics(); + + /// \brief Destructor + public: ~LighterThanAirDynamics() override; + + /// Documentation inherited + public: void Configure( + const gz::sim::Entity &_entity, + const std::shared_ptr &_sdf, + gz::sim::EntityComponentManager &_ecm, + gz::sim::EventManager &/*_eventMgr*/) override; + + /// Documentation inherited + public: void PreUpdate( + const gz::sim::UpdateInfo &_info, + gz::sim::EntityComponentManager &_ecm) override; + + ///////////////////////////////////////////////// + /// \brief Calculates the local velocity at an offset from a origin + /// \param[in] lin_vel - The linear body velocity + /// \param[in] ang_vel - The angular body velocity + /// \param[in] dist - The distance vector from the origin + /// \return The local velocity at the distance vector + public: math::Vector3d LocalVelocity(math::Vector3d lin_vel, + math::Vector3d ang_vel, math::Vector3d dist); + + ///////////////////////////////////////////////// + /// \brief Calculates dynamic pressure + /// \param[in] vec - The linear velocity + /// \param[in] air_density - The air density [kg/m^3] + /// \return The dynamic pressure, q + public: double DynamicPressure(math::Vector3d vec, double air_density); + + ///////////////////////////////////////////////// + /// \brief Calculates the potential flow aerodynamic forces that a LTA + /// vehicle experience when moving in a potential fluid. The aerodynamic + /// force is derived using Kirchoff's equation. + /// \param[in] lin_vel - The body linear velocity + /// \param[in] ang_vel - The body angular velocity + /// \param[in] m11 - The left upper [3x3] matrix of the added mass matrix + /// \param[in] m12 - The right upper [3x3] matrix of the added mass matrix + /// \param[in] m21 - The left lower [3x3] matrix of the added mass matrix + /// \param[in] m22 - The right lower [3x3] matrix of the added mass matrix + /// \return The aerodynamic force. + public: math::Vector3d AddedMassTorque(math::Vector3d lin_vel, + math::Vector3d ang_vel, + math::Matrix3d m11, math::Matrix3d m12, + math::Matrix3d m21, math::Matrix3d m22); + + ///////////////////////////////////////////////// + /// \brief Calculates the potential flow aerodynamic torques that a LTA + /// vehicle experience when moving in a potential fluid. The aerodynamic + /// torques is derived using Kirchoff's equation. + /// \param[in] lin_vel - The body linear velocity + /// \param[in] ang_vel - The body angular velocity + /// \param[in] m11 - The left upper [3x3] matrix of the added mass matrix + /// \param[in] m12 - The right upper [3x3] matrix of the added mass matrix + /// \return The aerodynamic torque + public: math::Vector3d AddedMassForce(math::Vector3d lin_vel, + math::Vector3d ang_vel, + math::Matrix3d m11, math::Matrix3d m12); + + ///////////////////////////////////////////////// + /// \brief Skew-symmetric matrices can be used to represent cross products + /// as matrix multiplications. + /// \param[in] mat - A [3x1] vector + /// \return The skew-symmetric matrix of mat + public: math::Matrix3d SkewSymmetricMatrix(math::Vector3d mat); + + /// \brief Private data pointer + private: std::unique_ptr dataPtr; + }; +} +} +} +} +#endif diff --git a/src/systems/log/LogPlayback.cc b/src/systems/log/LogPlayback.cc index df759ae79f..b217a35142 100644 --- a/src/systems/log/LogPlayback.cc +++ b/src/systems/log/LogPlayback.cc @@ -510,14 +510,6 @@ void LogPlayback::Update(const UpdateInfo &_info, EntityComponentManager &_ecm) { auto msgType = iter->Type(); - // Support ignition.msgs for backwards compatibility. Remove on gz-sim9 - std::string deprecatedPrefix{"ignition.msgs"}; - auto pos = msgType.find(deprecatedPrefix); - if (pos != std::string::npos) - { - msgType.replace(pos, deprecatedPrefix.size(), "gz.msgs"); - } - if (msgType == "gz.msgs.SerializedState") { msgs::SerializedState msg; diff --git a/src/systems/log/LogRecord.cc b/src/systems/log/LogRecord.cc index 8cbeadbfb1..3361c407f4 100644 --- a/src/systems/log/LogRecord.cc +++ b/src/systems/log/LogRecord.cc @@ -669,8 +669,8 @@ void LogRecord::PostUpdate(const UpdateInfo &_info, if (_info.dt < std::chrono::steady_clock::duration::zero()) { gzwarn << "Detected jump back in time [" - << std::chrono::duration_cast(_info.dt).count() - << "s]. System may not work properly." << std::endl; + << std::chrono::duration(_info.dt).count() + << "s]. System may not work properly." << std::endl; } // Publish only once diff --git a/src/systems/logical_audio_sensor_plugin/LogicalAudioSensorPlugin.cc b/src/systems/logical_audio_sensor_plugin/LogicalAudioSensorPlugin.cc index a4e5a7f195..e4d633cd59 100644 --- a/src/systems/logical_audio_sensor_plugin/LogicalAudioSensorPlugin.cc +++ b/src/systems/logical_audio_sensor_plugin/LogicalAudioSensorPlugin.cc @@ -405,12 +405,11 @@ void LogicalAudioSensorPluginPrivate::CreateAudioSource( }; // create services for this source - const auto fullName = scopedName(entity, _ecm); - auto validName = transport::TopicUtils::AsValidTopic(fullName); + const auto validName = topicFromScopedName(entity, _ecm, true); if (validName.empty()) { gzerr << "Failed to create valid topics with entity scoped name [" - << fullName << "]" << std::endl; + << scopedName(entity, _ecm) << "]" << std::endl; return; } if (!this->node.Advertise(validName + "/play", playSrvCb)) @@ -504,7 +503,7 @@ void LogicalAudioSensorPluginPrivate::CreateMicrophone( // create the detection publisher for this microphone auto pub = this->node.Advertise( - scopedName(entity, _ecm) + "/detection"); + topicFromScopedName(entity, _ecm, true) + "/detection"); if (!pub) { gzerr << "Error creating a detection publisher for microphone " diff --git a/src/systems/logical_camera/LogicalCamera.cc b/src/systems/logical_camera/LogicalCamera.cc index 1ddfe2146f..ded8c9c36f 100644 --- a/src/systems/logical_camera/LogicalCamera.cc +++ b/src/systems/logical_camera/LogicalCamera.cc @@ -134,8 +134,8 @@ void LogicalCamera::PostUpdate(const UpdateInfo &_info, if (_info.dt < std::chrono::steady_clock::duration::zero()) { gzwarn << "Detected jump back in time [" - << std::chrono::duration_cast(_info.dt).count() - << "s]. System may not work properly." << std::endl; + << std::chrono::duration(_info.dt).count() + << "s]. System may not work properly." << std::endl; } this->dataPtr->CreateSensors(_ecm); diff --git a/src/systems/magnetometer/Magnetometer.cc b/src/systems/magnetometer/Magnetometer.cc index 499664a046..72474d5cb9 100644 --- a/src/systems/magnetometer/Magnetometer.cc +++ b/src/systems/magnetometer/Magnetometer.cc @@ -98,7 +98,7 @@ static constexpr const int8_t inclination_table[13][37] = \ { 71, 71, 72, 73, 75, 77, 78, 80, 81, 81, 80, 79, 77, 76, 74, 73, 73, 73, 73, 73, 73, 74, 74, 75, 76, 77, 78, 78, 78, 78, 77, 75, 73, 72, 71, 71, 71 }, // NOLINT }; -// strength data in centi-Tesla +// strength data in centi gauss static constexpr const int8_t strength_table[13][37] = \ { { 62, 60, 58, 56, 54, 52, 49, 46, 43, 41, 38, 36, 34, 32, 31, 31, 30, 30, 30, 31, 33, 35, 38, 42, 46, 51, 55, 59, 62, 64, 66, 67, 67, 66, 65, 64, 62 }, // NOLINT @@ -134,6 +134,12 @@ class gz::sim::systems::MagnetometerPrivate /// True if the rendering component is initialized public: bool initialized = false; + /// \brief True if the magnetic field is reported in gauss rather than tesla. + public: bool useUnitsGauss = true; + + /// \brief True if the magnetic field earth frame is NED rather than ENU. + public: bool useEarthFrameNED = true; + /// \brief Create sensor /// \param[in] _ecm Immutable reference to ECM. /// \param[in] _entity Entity of the IMU @@ -226,7 +232,7 @@ class gz::sim::systems::MagnetometerPrivate return get_table_data(lat, lon, inclination_table); } - // return magnetic field strength in centi-Tesla + // return magnetic field strength in centi-Gauss float get_mag_strength(float lat, float lon) { return get_table_data(lat, lon, strength_table); @@ -242,6 +248,29 @@ Magnetometer::Magnetometer() : System(), dataPtr( ////////////////////////////////////////////////// Magnetometer::~Magnetometer() = default; +////////////////////////////////////////////////// +void Magnetometer::Configure(const Entity &/*_entity*/, + const std::shared_ptr &_sdf, + EntityComponentManager &/*_ecm*/, + EventManager &/*_eventMgr*/) +{ + if (_sdf->HasElement("use_units_gauss")) + { + this->dataPtr->useUnitsGauss = _sdf->Get("use_units_gauss"); + } + gzdbg << "Magnetometer: using param [use_units_gauss: " + << this->dataPtr->useUnitsGauss << "]." + << std::endl; + + if (_sdf->HasElement("use_earth_frame_ned")) + { + this->dataPtr->useEarthFrameNED = _sdf->Get("use_earth_frame_ned"); + } + gzdbg << "Magnetometer: using param [use_earth_frame_ned: " + << this->dataPtr->useEarthFrameNED << "]." + << std::endl; +} + ////////////////////////////////////////////////// void Magnetometer::PreUpdate(const UpdateInfo &/*_info*/, EntityComponentManager &_ecm) @@ -274,8 +303,8 @@ void Magnetometer::PostUpdate(const UpdateInfo &_info, if (_info.dt < std::chrono::steady_clock::duration::zero()) { gzwarn << "Detected jump back in time [" - << std::chrono::duration_cast(_info.dt).count() - << "s]. System may not work properly." << std::endl; + << std::chrono::duration(_info.dt).count() + << "s]. System may not work properly." << std::endl; } this->dataPtr->CreateSensors(_ecm); @@ -430,7 +459,7 @@ void MagnetometerPrivate::Update( auto latLonEle = sphericalCoordinates(_entity, _ecm); if (!latLonEle) { - gzwarn << "Failed to update NavSat sensor enity [" << _entity + gzwarn << "Failed to update Magnetometer sensor enity [" << _entity << "]. Spherical coordinates not set." << std::endl; return true; } @@ -446,16 +475,32 @@ void MagnetometerPrivate::Update( get_mag_inclination( lat_rad * 180 / GZ_PI, lon_rad * 180 / GZ_PI) * GZ_PI / 180; - // Magnetic strength (10^5xnanoTesla) + // Magnetic strength in gauss (10^5 nano tesla = 10^-2 centi gauss) float strength_ga = 0.01f * get_mag_strength(lat_rad * 180 / GZ_PI, lon_rad * 180 / GZ_PI); - // Magnetic filed components are calculated by http://geomag.nrcan.gc.ca/mag_fld/comp-en.php - float H = strength_ga * cosf(inclination_rad); - float Z = tanf(inclination_rad) * H; - float X = H * cosf(declination_rad); - float Y = H * sinf(declination_rad); + // Magnetic intensity measured in telsa + float strength_tesla = 1.0E-4 * strength_ga; + + // Magnetic field components are calculated in world NED frame using: + // http://geomag.nrcan.gc.ca/mag_fld/comp-en.php + float H = cosf(inclination_rad); + H *= this->useUnitsGauss ? strength_ga : strength_tesla; + float Z_ned = tanf(inclination_rad) * H; + float X_ned = H * cosf(declination_rad); + float Y_ned = H * sinf(declination_rad); + + float X = X_ned; + float Y = Y_ned; + float Z = Z_ned; + if (!this->useEarthFrameNED) + { + // Use ENU convention for earth frame. + X = Y_ned; + Y = X_ned; + Z = -1.0 * Z_ned; + } math::Vector3d magnetic_field_I(X, Y, Z); it->second->SetWorldMagneticField(magnetic_field_I); @@ -494,6 +539,7 @@ void MagnetometerPrivate::RemoveMagnetometerEntities( } GZ_ADD_PLUGIN(Magnetometer, System, + Magnetometer::ISystemConfigure, Magnetometer::ISystemPreUpdate, Magnetometer::ISystemPostUpdate ) diff --git a/src/systems/magnetometer/Magnetometer.hh b/src/systems/magnetometer/Magnetometer.hh index 854eb44a3b..e2e1c5d166 100644 --- a/src/systems/magnetometer/Magnetometer.hh +++ b/src/systems/magnetometer/Magnetometer.hh @@ -37,6 +37,7 @@ namespace systems /// current location. class Magnetometer: public System, + public ISystemConfigure, public ISystemPreUpdate, public ISystemPostUpdate { @@ -46,6 +47,12 @@ namespace systems /// \brief Destructor public: ~Magnetometer() override; + // Documentation inherited + public: void Configure(const Entity &_entity, + const std::shared_ptr &_sdf, + EntityComponentManager &_ecm, + EventManager &_eventMgr) override; + /// Documentation inherited public: void PreUpdate(const UpdateInfo &_info, EntityComponentManager &_ecm) final; diff --git a/src/systems/mecanum_drive/MecanumDrive.cc b/src/systems/mecanum_drive/MecanumDrive.cc index 2f585307c7..732bc07b1d 100644 --- a/src/systems/mecanum_drive/MecanumDrive.cc +++ b/src/systems/mecanum_drive/MecanumDrive.cc @@ -333,8 +333,8 @@ void MecanumDrive::PreUpdate(const gz::sim::UpdateInfo &_info, if (_info.dt < std::chrono::steady_clock::duration::zero()) { gzwarn << "Detected jump back in time [" - << std::chrono::duration_cast(_info.dt).count() - << "s]. System may not work properly." << std::endl; + << std::chrono::duration(_info.dt).count() + << "s]. System may not work properly." << std::endl; } // If the joints haven't been identified yet, look for them diff --git a/src/systems/multicopter_control/MulticopterVelocityControl.cc b/src/systems/multicopter_control/MulticopterVelocityControl.cc index 96f547fa4d..d352a68a7a 100644 --- a/src/systems/multicopter_control/MulticopterVelocityControl.cc +++ b/src/systems/multicopter_control/MulticopterVelocityControl.cc @@ -349,8 +349,8 @@ void MulticopterVelocityControl::PreUpdate( if (_info.dt < std::chrono::steady_clock::duration::zero()) { gzwarn << "Detected jump back in time [" - << std::chrono::duration_cast(_info.dt).count() - << "s]. System may not work properly." << std::endl; + << std::chrono::duration(_info.dt).count() + << "s]. System may not work properly." << std::endl; } // Nothing left to do if paused. diff --git a/src/systems/multicopter_motor_model/MulticopterMotorModel.cc b/src/systems/multicopter_motor_model/MulticopterMotorModel.cc index 8e6e421ce9..02fa87b774 100644 --- a/src/systems/multicopter_motor_model/MulticopterMotorModel.cc +++ b/src/systems/multicopter_motor_model/MulticopterMotorModel.cc @@ -399,8 +399,8 @@ void MulticopterMotorModel::PreUpdate(const UpdateInfo &_info, if (_info.dt < std::chrono::steady_clock::duration::zero()) { gzwarn << "Detected jump back in time [" - << std::chrono::duration_cast(_info.dt).count() - << "s]. System may not work properly." << std::endl; + << std::chrono::duration(_info.dt).count() + << "s]. System may not work properly." << std::endl; } // If the joint or links haven't been identified yet, look for them @@ -411,7 +411,8 @@ void MulticopterMotorModel::PreUpdate(const UpdateInfo &_info, const auto parentLinkName = _ecm.Component( this->dataPtr->jointEntity); - this->dataPtr->parentLinkName = parentLinkName->Data(); + if (parentLinkName) + this->dataPtr->parentLinkName = parentLinkName->Data(); } if (this->dataPtr->linkEntity == kNullEntity) @@ -447,14 +448,6 @@ void MulticopterMotorModel::PreUpdate(const UpdateInfo &_info, doUpdateForcesAndMoments = false; } - if (!_ecm.Component( - this->dataPtr->jointEntity)) - { - _ecm.CreateComponent(this->dataPtr->jointEntity, - components::JointVelocityCmd({0})); - doUpdateForcesAndMoments = false; - } - if (!_ecm.Component(this->dataPtr->linkEntity)) { _ecm.CreateComponent(this->dataPtr->linkEntity, components::WorldPose()); @@ -682,11 +675,10 @@ void MulticopterMotorModelPrivate::UpdateForcesAndMoments( refMotorRotVel = this->rotorVelocityFilter->UpdateFilter( this->refMotorInput, this->samplingTime); - const auto jointVelCmd = _ecm.Component( - this->jointEntity); - *jointVelCmd = components::JointVelocityCmd( - {this->turningDirection * refMotorRotVel - / this->rotorVelocitySlowdownSim}); + _ecm.SetComponentData( + this->jointEntity, + {this->turningDirection * refMotorRotVel + / this->rotorVelocitySlowdownSim}); } } } diff --git a/src/systems/navsat/NavSat.cc b/src/systems/navsat/NavSat.cc index f438a9d240..dd6ac144f7 100644 --- a/src/systems/navsat/NavSat.cc +++ b/src/systems/navsat/NavSat.cc @@ -128,8 +128,8 @@ void NavSat::PostUpdate(const UpdateInfo &_info, if (_info.dt < std::chrono::steady_clock::duration::zero()) { gzwarn << "Detected jump back in time [" - << std::chrono::duration_cast(_info.dt).count() - << "s]. System may not work properly." << std::endl; + << std::chrono::duration(_info.dt).count() + << "s]. System may not work properly." << std::endl; } this->dataPtr->CreateSensors(_ecm); diff --git a/src/systems/odometry_publisher/OdometryPublisher.cc b/src/systems/odometry_publisher/OdometryPublisher.cc index 8473328320..fe5270ebe2 100644 --- a/src/systems/odometry_publisher/OdometryPublisher.cc +++ b/src/systems/odometry_publisher/OdometryPublisher.cc @@ -59,7 +59,9 @@ class gz::sim::systems::OdometryPublisherPrivate public: transport::Node node; /// \brief Model interface + //! [modelDeclaration] public: Model model{kNullEntity}; + //! [modelDeclaration] /// \brief Name of the world-fixed coordinate frame for the odometry message. public: std::string odomFrame; @@ -133,12 +135,14 @@ OdometryPublisher::OdometryPublisher() } ////////////////////////////////////////////////// +//! [Configure] void OdometryPublisher::Configure(const Entity &_entity, const std::shared_ptr &_sdf, EntityComponentManager &_ecm, EventManager &/*_eventMgr*/) { this->dataPtr->model = Model(_entity); + //! [Configure] if (!this->dataPtr->model.Valid(_ecm)) { @@ -233,8 +237,10 @@ void OdometryPublisher::Configure(const Entity &_entity, } else { + //! [definePub] this->dataPtr->odomPub = this->dataPtr->node.Advertise( odomTopicValid); + //! [definePub] gzmsg << "OdometryPublisher publishing odometry on [" << odomTopicValid << "]" << std::endl; } @@ -297,17 +303,8 @@ void OdometryPublisher::PreUpdate(const gz::sim::UpdateInfo &_info, if (_info.dt < std::chrono::steady_clock::duration::zero()) { gzwarn << "Detected jump back in time [" - << std::chrono::duration_cast(_info.dt).count() - << "s]. System may not work properly." << std::endl; - } - - // Create the pose component if it does not exist. - auto pos = _ecm.Component( - this->dataPtr->model.Entity()); - if (!pos) - { - _ecm.CreateComponent(this->dataPtr->model.Entity(), - components::Pose()); + << std::chrono::duration(_info.dt).count() + << "s]. System may not work properly." << std::endl; } } @@ -347,7 +344,9 @@ void OdometryPublisherPrivate::UpdateOdometry( } // Construct the odometry message and publish it. + //! [declarePoseMsg] msgs::Odometry msg; + //! [declarePoseMsg] const std::chrono::duration dt = std::chrono::steady_clock::time_point(_info.simTime) - lastUpdateTime; @@ -357,7 +356,10 @@ void OdometryPublisherPrivate::UpdateOdometry( return; // Get and set robotBaseFrame to odom transformation. + //! [worldPose] const math::Pose3d rawPose = worldPose(this->model.Entity(), _ecm); + //! [worldPose] + //! [setPoseMsg] math::Pose3d pose = rawPose * this->offset; msg.mutable_pose()->mutable_position()->set_x(pose.Pos().X()); msg.mutable_pose()->mutable_position()->set_y(pose.Pos().Y()); @@ -366,6 +368,7 @@ void OdometryPublisherPrivate::UpdateOdometry( { msg.mutable_pose()->mutable_position()->set_z(pose.Pos().Z()); } + //! [setPoseMsg] // Get linear and angular displacements from last updated pose. double linearDisplacementX = pose.Pos().X() - this->lastUpdatePose.Pos().X(); @@ -476,7 +479,9 @@ void OdometryPublisherPrivate::UpdateOdometry( this->lastOdomPubTime = _info.simTime; if (this->odomPub.Valid()) { + //! [publishMsg] this->odomPub.Publish(msg); + //! [publishMsg] } // Generate odometry with covariance message and publish it. diff --git a/src/systems/odometry_publisher/OdometryPublisher.hh b/src/systems/odometry_publisher/OdometryPublisher.hh index cab9bd6d14..33c8fd23af 100644 --- a/src/systems/odometry_publisher/OdometryPublisher.hh +++ b/src/systems/odometry_publisher/OdometryPublisher.hh @@ -76,6 +76,13 @@ namespace systems /// - ``: Standard deviation of the Gaussian noise to be added /// to pose and twist messages. This element is optional, and the default /// value is 0. + /// + /// ## Components + /// + /// This system uses the following components: + /// + /// - gz::sim::components::Pose: Pose represented by gz::math::Pose3d. Used + /// to calculate the odometry to publish. class OdometryPublisher : public System, public ISystemConfigure, diff --git a/src/systems/optical_tactile_plugin/OpticalTactilePlugin.cc b/src/systems/optical_tactile_plugin/OpticalTactilePlugin.cc index f1d6d618fa..e2bf376e1e 100644 --- a/src/systems/optical_tactile_plugin/OpticalTactilePlugin.cc +++ b/src/systems/optical_tactile_plugin/OpticalTactilePlugin.cc @@ -530,6 +530,7 @@ void OpticalTactilePluginPrivate::Load(const EntityComponentManager &_ecm) int contactSensorCounter = 0; sdf::Sensor depthCameraSdf; components::Pose depthCameraPose = components::Pose(); + std::string depthCameraTopic; for (const Entity &sensor : sensorsInsideLink) { if (_ecm.EntityHasComponentType(sensor, components::DepthCamera::typeId)) @@ -538,6 +539,10 @@ void OpticalTactilePluginPrivate::Load(const EntityComponentManager &_ecm) depthCameraSdf = _ecm.Component(sensor)->Data(); depthCameraPose = *(_ecm.Component(sensor)); + auto depthCameraTopicComp = + _ecm.Component(sensor); + if (depthCameraTopicComp) + depthCameraTopic = depthCameraTopicComp->Data(); } if (_ecm.EntityHasComponentType(sensor, components::ContactSensor::typeId)) @@ -558,16 +563,6 @@ void OpticalTactilePluginPrivate::Load(const EntityComponentManager &_ecm) } // Store depth camera update rate - if (!depthCameraSdf.Element()->HasElement("update_rate")) - { - if (!this->initErrorPrinted) - { - gzerr << "Depth camera should have an value " - << "(only printed once)" << std::endl; - this->initErrorPrinted = true; - } - return; - } this->cameraUpdateRate = depthCameraSdf.UpdateRate(); // Depth camera data is float, so convert Pose3d to Pose3f @@ -581,20 +576,27 @@ void OpticalTactilePluginPrivate::Load(const EntityComponentManager &_ecm) depthCameraPose.Data().Rot().Z()); // Configure subscriber for depth camera images - if (!depthCameraSdf.Element()->HasElement("topic")) + if (depthCameraTopic.empty()) { - gzwarn << "Depth camera publishing to __default__ topic. " - << "It's possible that two depth cameras are publishing into the same " - << "topic" << std::endl; + // get the topic from sdf if the one in sensor topic component is empty + depthCameraTopic = depthCameraSdf.Topic(); } - else + + if (depthCameraTopic.empty()) { - gzdbg << "Depth camera publishing to " - << depthCameraSdf.Topic() << " topic" << std::endl; + if (!this->initErrorPrinted) + { + gzerr << "Depth camera topic is empty. " << std::endl; + this->initErrorPrinted = true; + } + return; } + gzdbg << "Depth camera publishing to " + << depthCameraTopic << " topic" << std::endl; + std::string topic = - "/" + depthCameraSdf.Topic() + "/points"; + "/" + depthCameraTopic + "/points"; if (!this->node.Subscribe(topic, &OpticalTactilePluginPrivate::DepthCameraCallback, this)) { diff --git a/src/systems/physics/Physics.cc b/src/systems/physics/Physics.cc index abf986f96e..5d12e422b5 100644 --- a/src/systems/physics/Physics.cc +++ b/src/systems/physics/Physics.cc @@ -53,6 +53,7 @@ #include #include +#include #include #include #include @@ -92,6 +93,7 @@ #include "gz/sim/EntityComponentManager.hh" #include "gz/sim/Model.hh" +#include "gz/sim/System.hh" #include "gz/sim/Util.hh" // Components @@ -99,6 +101,7 @@ #include "gz/sim/components/AngularAcceleration.hh" #include "gz/sim/components/AngularVelocity.hh" #include "gz/sim/components/AngularVelocityCmd.hh" +#include "gz/sim/components/AngularVelocityReset.hh" #include "gz/sim/components/AxisAlignedBox.hh" #include "gz/sim/components/BatterySoC.hh" #include "gz/sim/components/CanonicalLink.hh" @@ -126,6 +129,7 @@ #include "gz/sim/components/LinearAcceleration.hh" #include "gz/sim/components/LinearVelocity.hh" #include "gz/sim/components/LinearVelocityCmd.hh" +#include "gz/sim/components/LinearVelocityReset.hh" #include "gz/sim/components/Link.hh" #include "gz/sim/components/Model.hh" #include "gz/sim/components/Name.hh" @@ -433,7 +437,7 @@ class gz::sim::systems::PhysicsPrivate } return true; }}; - /// \brief msgs::Contacts equality comparison function. + /// \brief msgs::Wrench equality comparison function. public: std::function wrenchEql{ [](const msgs::Wrench &_a, const msgs::Wrench &_b) @@ -449,8 +453,6 @@ class gz::sim::systems::PhysicsPrivate /// \brief Environment variable which holds paths to look for engine plugins public: std::string pluginPathEnv = "GZ_SIM_PHYSICS_ENGINE_PATH"; - public: std::string pluginPathEnvDeprecated = \ - "IGN_GAZEBO_PHYSICS_ENGINE_PATH"; ////////////////////////////////////////////////// ////////////// Optional Features ///////////////// @@ -624,8 +626,15 @@ class gz::sim::systems::PhysicsPrivate gz::physics::Solver>{}; ////////////////////////////////////////////////// - // Nested Models + // CollisionPairMaxContacts + /// \brief Feature list for setting and getting the max total contacts for + /// collision pairs + public: struct CollisionPairMaxContactsFeatureList : + gz::physics::FeatureList< + gz::physics::CollisionPairMaxContacts>{}; + ////////////////////////////////////////////////// + // Nested Models /// \brief Feature list to construct nested models public: struct NestedModelFeatureList : physics::FeatureList< MinimumFeatureList, @@ -648,7 +657,8 @@ class gz::sim::systems::PhysicsPrivate NestedModelFeatureList, CollisionDetectorFeatureList, SolverFeatureList, - WorldModelFeatureList + WorldModelFeatureList, + CollisionPairMaxContactsFeatureList >; /// \brief A map between world entity ids in the ECM to World Entities in @@ -756,6 +766,13 @@ Physics::Physics() : System(), dataPtr(std::make_unique()) { } +////////////////////////////////////////////////// +System::PriorityType Physics::ConfigurePriority() +{ + // Use constant from System.hh + return ::gz::sim::systems::kPhysicsPriority; +} + ////////////////////////////////////////////////// void Physics::Configure(const Entity &_entity, const std::shared_ptr &_sdf, @@ -836,8 +853,8 @@ void Physics::Configure(const Entity &_entity, physics::FeaturePolicy3d>>(); if (classNames.empty()) { - gzerr << "No physics plugins found in library [" << pathToLib << "]." - << std::endl; + gzerr << "No physics plugins implementing required interface found in " + << "library [" << pathToLib << "]." << std::endl; return; } @@ -1026,6 +1043,57 @@ void PhysicsPrivate::CreateWorldEntities(const EntityComponentManager &_ecm, solverFeature->SetSolver(solverComp->Data()); } } + auto solverItersComp = + _ecm.Component(_entity); + if (solverItersComp) + { + auto solverFeature = + this->entityWorldMap.EntityCast( + _entity); + if (!solverFeature) + { + static bool informed{false}; + if (!informed) + { + gzdbg << "Attempting to set physics options, but the " + << "phyiscs engine doesn't support feature " + << "[SolverFeature]. Options will be ignored." + << std::endl; + informed = true; + } + } + else + { + solverFeature->SetSolverIterations(solverItersComp->Data()); + } + } + + auto physicsComp = + _ecm.Component(_entity); + if (physicsComp) + { + auto maxContactsFeature = + this->entityWorldMap.EntityCast< + CollisionPairMaxContactsFeatureList>(_entity); + if (!maxContactsFeature) + { + static bool informed{false}; + if (!informed) + { + gzdbg << "Attempting to set physics options, but the " + << "phyiscs engine doesn't support feature " + << "[CollisionPairMaxContacts]. " + << "Options will be ignored." + << std::endl; + informed = true; + } + } + else + { + maxContactsFeature->SetCollisionPairMaxContacts( + physicsComp->Data().MaxContacts()); + } + } // World Model proxy (used for joints directly under in SDF) auto worldModelFeature = @@ -1288,6 +1356,15 @@ void PhysicsPrivate::CreateLinkEntities(const EntityComponentManager &_ecm, link.SetInertial(inertial->Data()); } + // get link gravity + const components::GravityEnabled *gravityEnabled = + _ecm.Component(_entity); + if (nullptr != gravityEnabled) + { + // gravityEnabled set in SdfEntityCreator::CreateEntities() + link.SetEnableGravity(gravityEnabled->Data()); + } + auto constructLinkFeature = this->entityModelMap.EntityCast( _parent->Data()); @@ -2645,6 +2722,122 @@ void PhysicsPrivate::UpdatePhysics(EntityComponentManager &_ecm) return true; }); + // Reset link linear velocity in world frame + _ecm.Each( + [&](const Entity &_entity, const components::Link *, + const components::WorldLinearVelocityReset *_worldlinearvelocityreset) + { + if (!this->entityLinkMap.HasEntity(_entity)) + { + gzwarn << "Failed to find link [" << _entity + << "]." << std::endl; + return true; + } + + auto linkPtrPhys = this->entityLinkMap.Get(_entity); + if (nullptr == linkPtrPhys) + return true; + + auto freeGroup = linkPtrPhys->FindFreeGroup(); + if (!freeGroup) + return true; + + auto rootLinkPtr = freeGroup->RootLink(); + if (rootLinkPtr != linkPtrPhys) + { + gzdbg << "Attempting to set linear velocity for link [ " << _entity + << " ] which is not root link of the FreeGroup." + << "Velocity won't be set." + << std::endl; + + return true; + } + + this->entityFreeGroupMap.AddEntity(_entity, freeGroup); + + auto worldLinearVelFeature = this->entityFreeGroupMap + .EntityCast(_entity); + if (!worldLinearVelFeature) + { + static bool informed{false}; + if (!informed) + { + gzdbg << "Attempting to set link linear velocity, but the " + << "physics engine doesn't support velocity commands. " + << "Velocity won't be set." + << std::endl; + informed = true; + } + return true; + } + + // Linear velocity in world frame + math::Vector3d worldLinearVel = _worldlinearvelocityreset->Data(); + + worldLinearVelFeature->SetWorldLinearVelocity( + math::eigen3::convert(worldLinearVel)); + + return true; + }); + + // Reset link angular velocity in world frame + _ecm.Each( + [&](const Entity &_entity, const components::Link *, + const components::WorldAngularVelocityReset + *_worldangularvelocityreset) + { + if (!this->entityLinkMap.HasEntity(_entity)) + { + gzwarn << "Failed to find link [" << _entity + << "]." << std::endl; + return true; + } + + auto linkPtrPhys = this->entityLinkMap.Get(_entity); + if (nullptr == linkPtrPhys) + return true; + + auto freeGroup = linkPtrPhys->FindFreeGroup(); + if (!freeGroup) + return true; + + auto rootLinkPtr = freeGroup->RootLink(); + if(rootLinkPtr != linkPtrPhys) + { + gzdbg << "Attempting to set angular velocity for link [ " << _entity + << " ] which is not root link of the FreeGroup." + << "Velocity won't be set." + << std::endl; + + return true; + } + + this->entityFreeGroupMap.AddEntity(_entity, freeGroup); + + auto worldAngularVelFeature = this->entityFreeGroupMap + .EntityCast(_entity); + + if (!worldAngularVelFeature) + { + static bool informed{false}; + if (!informed) + { + gzdbg << "Attempting to set link angular velocity, but the " + << "physics engine doesn't support velocity commands. " + << "Velocity won't be set." + << std::endl; + informed = true; + } + return true; + } + // Angular velocity in world frame + math::Vector3d worldAngularVel = _worldangularvelocityreset->Data(); + + worldAngularVelFeature->SetWorldAngularVelocity( + math::eigen3::convert(worldAngularVel)); + + return true; + }); // Populate bounding box info // Only compute bounding box if component exists to avoid unnecessary @@ -3581,6 +3774,34 @@ void PhysicsPrivate::UpdateSim(EntityComponentManager &_ecm, _ecm.RemoveComponent(entity); } + std::vector entitiesLinearVelocityReset; + _ecm.Each( + [&](const Entity &_entity, + components::WorldLinearVelocityReset *) -> bool + { + entitiesLinearVelocityReset.push_back(_entity); + return true; + }); + + for (const auto entity : entitiesLinearVelocityReset) + { + _ecm.RemoveComponent(entity); + } + + std::vector entitiesAngularVelocityReset; + _ecm.Each( + [&](const Entity &_entity, + components::WorldAngularVelocityReset *) -> bool + { + entitiesAngularVelocityReset.push_back(_entity); + return true; + }); + + for (const auto entity : entitiesAngularVelocityReset) + { + _ecm.RemoveComponent(entity); + } + std::vector entitiesCustomContactSurface; _ecm.Each( [&](const Entity &_entity, @@ -3631,12 +3852,20 @@ void PhysicsPrivate::UpdateSim(EntityComponentManager &_ecm, return true; }); - _ecm.Each( - [&](const Entity &, components::JointVelocityCmd *_vel) -> bool - { - std::fill(_vel->Data().begin(), _vel->Data().end(), 0.0); - return true; - }); + { + std::vector entitiesJointVelocityCmd; + _ecm.Each( + [&](const Entity &_entity, components::JointVelocityCmd *) -> bool + { + entitiesJointVelocityCmd.push_back(_entity); + return true; + }); + + for (const auto entity : entitiesJointVelocityCmd) + { + _ecm.RemoveComponent(entity); + } + } _ecm.Each( [&](const Entity &, components::SlipComplianceCmd *_slip) -> bool @@ -3644,21 +3873,37 @@ void PhysicsPrivate::UpdateSim(EntityComponentManager &_ecm, std::fill(_slip->Data().begin(), _slip->Data().end(), 0.0); return true; }); - GZ_PROFILE_END(); - _ecm.Each( - [&](const Entity &, components::AngularVelocityCmd *_vel) -> bool - { - _vel->Data() = math::Vector3d::Zero; - return true; - }); + { + std::vector entitiesAngularVelocityCmd; + _ecm.Each( + [&](const Entity &_entity, components::AngularVelocityCmd *) -> bool + { + entitiesAngularVelocityCmd.push_back(_entity); + return true; + }); - _ecm.Each( - [&](const Entity &, components::LinearVelocityCmd *_vel) -> bool - { - _vel->Data() = math::Vector3d::Zero; - return true; - }); + for (const auto entity : entitiesAngularVelocityCmd) + { + _ecm.RemoveComponent(entity); + } + } + + { + std::vector entitiesLinearVelocityCmd; + _ecm.Each( + [&](const Entity &_entity, components::LinearVelocityCmd *) -> bool + { + entitiesLinearVelocityCmd.push_back(_entity); + return true; + }); + + for (const auto entity : entitiesLinearVelocityCmd) + { + _ecm.RemoveComponent(entity); + } + } + GZ_PROFILE_END(); // Update joint positions GZ_PROFILE_BEGIN("Joints"); @@ -3767,6 +4012,18 @@ void PhysicsPrivate::UpdateCollisions(EntityComponentManager &_ecm) if (!_ecm.HasComponentType(components::ContactSensorData::typeId)) return; + // Also check if any entity currently has a ContactSensorData component. + bool needContactSensorData = false; + _ecm.Each( + [&](const Entity &/*unused*/, components::Collision *, + components::ContactSensorData */*unused*/) -> bool + { + needContactSensorData = true; + return false; + }); + if (!needContactSensorData) + return; + // TODO(addisu) If systems are assumed to only have one world, we should // capture the world Entity in a Configure call Entity worldEntity = _ecm.EntityByComponents(components::World()); diff --git a/src/systems/physics/Physics.hh b/src/systems/physics/Physics.hh index cec62d341c..698050a9dc 100644 --- a/src/systems/physics/Physics.hh +++ b/src/systems/physics/Physics.hh @@ -26,6 +26,7 @@ // Features need to be defined ahead of entityCast #include #include +#include #include #include #include @@ -86,6 +87,7 @@ namespace systems class Physics: public System, public ISystemConfigure, + public ISystemConfigurePriority, public ISystemReset, public ISystemUpdate { @@ -101,6 +103,9 @@ namespace systems EntityComponentManager &_ecm, EventManager &_eventMgr) final; + /// Documentation inherited + public: System::PriorityType ConfigurePriority() final; + // Documentation inherited public: void Reset(const UpdateInfo &_info, EntityComponentManager &_ecm) final; diff --git a/src/systems/pose_publisher/PosePublisher.cc b/src/systems/pose_publisher/PosePublisher.cc index 58661d4816..395f3dc9bf 100644 --- a/src/systems/pose_publisher/PosePublisher.cc +++ b/src/systems/pose_publisher/PosePublisher.cc @@ -252,8 +252,7 @@ void PosePublisher::Configure(const Entity &_entity, this->dataPtr->usePoseV = _sdf->Get("use_pose_vector_msg", this->dataPtr->usePoseV).first; - std::string poseTopic = scopedName(_entity, _ecm) + "/pose"; - poseTopic = transport::TopicUtils::AsValidTopic(poseTopic); + std::string poseTopic = topicFromScopedName(_entity, _ecm, true) + "/pose"; if (poseTopic.empty()) { poseTopic = "/pose"; @@ -297,8 +296,8 @@ void PosePublisher::PostUpdate(const UpdateInfo &_info, if (_info.dt < std::chrono::steady_clock::duration::zero()) { gzwarn << "Detected jump back in time [" - << std::chrono::duration_cast(_info.dt).count() - << "s]. System may not work properly." << std::endl; + << std::chrono::duration(_info.dt).count() + << "s]. System may not work properly." << std::endl; } // Nothing left to do if paused. diff --git a/src/systems/sensors/Sensors.cc b/src/systems/sensors/Sensors.cc index 3ce3f4b619..601a4b3c9d 100644 --- a/src/systems/sensors/Sensors.cc +++ b/src/systems/sensors/Sensors.cc @@ -23,7 +23,6 @@ #include #include #include -#include #include #include @@ -41,6 +40,7 @@ #include #include #include +#include #include #include @@ -228,6 +228,9 @@ class gz::sim::systems::SensorsPrivate /// \brief Check if any of the sensors have connections public: bool SensorsHaveConnections(); + /// \brief Returns all sensors that have a pending trigger + public: std::unordered_set SensorsWithPendingTrigger(); + /// \brief Use to optionally set the background color. public: std::optional backgroundColor; @@ -745,11 +748,15 @@ void Sensors::PostUpdate(const UpdateInfo &_info, this->dataPtr->sensorsToUpdate, _info.simTime); } + std::unordered_set sensorsWithPendingTriggers = + this->dataPtr->SensorsWithPendingTrigger(); + // notify the render thread if updates are available if (hasRenderConnections || this->dataPtr->nextUpdateTime <= _info.simTime || this->dataPtr->renderUtil.PendingSensors() > 0 || - this->dataPtr->forceUpdate) + this->dataPtr->forceUpdate || + !sensorsWithPendingTriggers.empty()) { if (this->dataPtr->disableOnDrainedBattery) this->dataPtr->UpdateBatteryState(_ecm); @@ -769,6 +776,9 @@ void Sensors::PostUpdate(const UpdateInfo &_info, std::unique_lock lockSensors(this->dataPtr->sensorsMutex); this->dataPtr->activeSensors = std::move(this->dataPtr->sensorsToUpdate); + // Add all sensors that have pending triggers. + this->dataPtr->activeSensors.insert(sensorsWithPendingTriggers.begin(), + sensorsWithPendingTriggers.end()); } this->dataPtr->nextUpdateTime = this->dataPtr->NextUpdateTime( @@ -1024,6 +1034,11 @@ std::chrono::steady_clock::duration SensorsPrivate::NextUpdateTime( continue; } + if (rs->IsTriggered()) + { + continue; + } + std::chrono::steady_clock::duration time; // if sensor's next update tims is less or equal to current sim time then // it's in the process of being updated by the render loop @@ -1075,6 +1090,27 @@ bool SensorsPrivate::SensorsHaveConnections() return false; } +////////////////////////////////////////////////// +std::unordered_set +SensorsPrivate::SensorsWithPendingTrigger() +{ + std::unordered_set sensorsWithPendingTrigger; + for (auto id : this->sensorIds) + { + sensors::Sensor *s = this->sensorManager.Sensor(id); + if (nullptr == s) + { + continue; + } + + if (s->HasPendingTrigger()) + { + sensorsWithPendingTrigger.insert(id); + } + } + return sensorsWithPendingTrigger; +} + GZ_ADD_PLUGIN(Sensors, System, Sensors::ISystemConfigure, Sensors::ISystemReset, diff --git a/src/systems/spacecraft_thruster_model/CMakeLists.txt b/src/systems/spacecraft_thruster_model/CMakeLists.txt new file mode 100644 index 0000000000..9554f52dd1 --- /dev/null +++ b/src/systems/spacecraft_thruster_model/CMakeLists.txt @@ -0,0 +1,6 @@ +gz_add_system(spacecraft-thruster-model + SOURCES + SpacecraftThrusterModel.cc + PUBLIC_LINK_LIBS + gz-common${GZ_COMMON_VER}::gz-common${GZ_COMMON_VER} +) diff --git a/src/systems/spacecraft_thruster_model/SpacecraftThrusterModel.cc b/src/systems/spacecraft_thruster_model/SpacecraftThrusterModel.cc new file mode 100644 index 0000000000..7f65212ae1 --- /dev/null +++ b/src/systems/spacecraft_thruster_model/SpacecraftThrusterModel.cc @@ -0,0 +1,383 @@ +/* + * Copyright 2015 Fadri Furrer, ASL, ETH Zurich, Switzerland + * Copyright 2015 Michael Burri, ASL, ETH Zurich, Switzerland + * Copyright 2015 Mina Kamel, ASL, ETH Zurich, Switzerland + * Copyright 2015 Janosch Nikolic, ASL, ETH Zurich, Switzerland + * Copyright 2015 Markus Achtelik, ASL, ETH Zurich, Switzerland + * Copyright 2016 Geoffrey Hunter + * Copyright (C) 2024 Open Source Robotics Foundation + * Copyright (C) 2024 Benjamin Perseghetti, Rudis Laboratories + * Copyright (C) 2024 Pedro Roque, DCS, KTH, Sweden + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "SpacecraftThrusterModel.hh" + +#include +#include +#include +#include + +#include + +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#include "gz/sim/components/Actuators.hh" +#include "gz/sim/components/ExternalWorldWrenchCmd.hh" +#include "gz/sim/components/Pose.hh" +#include "gz/sim/Link.hh" +#include "gz/sim/Model.hh" +#include "gz/sim/Util.hh" + +using namespace gz; +using namespace sim; +using namespace systems; + +class gz::sim::systems::SpacecraftThrusterModelPrivate +{ + /// \brief Callback for actuator commands. + public: void OnActuatorMsg(const msgs::Actuators &_msg); + + /// \brief Apply link forces and moments based on propeller state. + public: void UpdateForcesAndMoments(EntityComponentManager &_ecm); + + /// \brief Link Entity + public: Entity linkEntity; + + /// \brief Link name + public: std::string linkName; + + /// \brief Model interface + public: Model model{kNullEntity}; + + /// \brief sub topic for actuator commands. + public: std::string subTopic; + + /// \brief Topic namespace. + public: std::string topic; + + /// \brief Simulation time tracker + public: double simTime = 0.01; + + /// \brief Index of motor in Actuators msg on multirotor_base. + public: int actuatorNumber = 0; + + /// \brief Duty cycle frequency + public: double dutyCycleFrequency = 10.0; + + /// \brief Cycle start time + public: double cycleStartTime = 0.0; + + /// \brief Sampling time with the cycle period. + public: double samplingTime = 0.01; + + /// \brief Actuator maximum thrust + public: double maxThrust = 0.0; + + /// \brief Received Actuators message. This is nullopt if no message has been + /// received. + public: std::optional recvdActuatorsMsg; + + /// \brief Mutex to protect recvdActuatorsMsg. + public: std::mutex recvdActuatorsMsgMutex; + + /// \brief Gazebo communication node. + public: transport::Node node; +}; + +////////////////////////////////////////////////// +SpacecraftThrusterModel::SpacecraftThrusterModel() + : dataPtr(std::make_unique()) +{ +} + +////////////////////////////////////////////////// +void SpacecraftThrusterModel::Configure(const Entity &_entity, + const std::shared_ptr &_sdf, + EntityComponentManager &_ecm, + EventManager &/*_eventMgr*/) +{ + this->dataPtr->model = Model(_entity); + + if (!this->dataPtr->model.Valid(_ecm)) + { + gzerr << "SpacecraftThrusterModel plugin should be attached to a model " + << "entity. Failed to initialize." << std::endl; + return; + } + + auto sdfClone = _sdf->Clone(); + + this->dataPtr->topic.clear(); + + if (sdfClone->HasElement("topic")) + { + this->dataPtr->topic = + sdfClone->Get("topic"); + } + else + { + gzwarn << "No topic set using entity name.\n"; + this->dataPtr->topic = this->dataPtr->model.Name(_ecm); + } + + if (sdfClone->HasElement("link_name")) + { + this->dataPtr->linkName = sdfClone->Get("link_name"); + } + + if (this->dataPtr->linkName.empty()) + { + gzerr << "SpacecraftThrusterModel found an empty link_name parameter. " + << "Failed to initialize."; + return; + } + + if (sdfClone->HasElement("actuator_number")) + { + this->dataPtr->actuatorNumber = + sdfClone->GetElement("actuator_number")->Get(); + } + else + { + gzerr << "Please specify a actuator_number.\n"; + } + + if (sdfClone->HasElement("max_thrust")) + { + this->dataPtr->maxThrust = + sdfClone->GetElement("max_thrust")->Get(); + } + else + { + gzerr << "Please specify actuator " + << this->dataPtr->actuatorNumber <<" max_thrust.\n"; + } + + if (sdfClone->HasElement("duty_cycle_frequency")) + { + this->dataPtr->dutyCycleFrequency = + sdfClone->GetElement("duty_cycle_frequency")->Get(); + } + else + { + gzerr << "Please specify actuator " + << this->dataPtr->actuatorNumber <<" duty_cycle_frequency.\n"; + } + + std::string topic; + if (sdfClone->HasElement("sub_topic")) + { + this->dataPtr->subTopic = + sdfClone->Get("sub_topic"); + topic = transport::TopicUtils::AsValidTopic( + this->dataPtr->topic + "/" + this->dataPtr->subTopic); + } + else + { + topic = transport::TopicUtils::AsValidTopic( + this->dataPtr->topic); + } + + // Subscribe to actuator command message + if (topic.empty()) + { + gzerr << "Failed to create topic for [" << this->dataPtr->topic + << "]" << std::endl; + return; + } + else + { + gzdbg << "Listening to topic: " << topic << std::endl; + } + this->dataPtr->node.Subscribe(topic, + &SpacecraftThrusterModelPrivate::OnActuatorMsg, this->dataPtr.get()); + + // Look for components + // If the link hasn't been identified yet, look for it + if (this->dataPtr->linkEntity == kNullEntity) + { + this->dataPtr->linkEntity = + this->dataPtr->model.LinkByName(_ecm, this->dataPtr->linkName); + } + + if ( this->dataPtr->linkEntity == kNullEntity) + { + gzerr << "Failed to find link entity. " + << "Failed to initialize." << std::endl; + return; + } + + // skip UpdateForcesAndMoments if needed components are missing + bool providedAllComponents = true; + if (!_ecm.Component(this->dataPtr->linkEntity)) + { + _ecm.CreateComponent(this->dataPtr->linkEntity, components::WorldPose()); + } + + if (!providedAllComponents) { + gzdbg << "Created necessary components." << std::endl; + } + +} + +////////////////////////////////////////////////// +void SpacecraftThrusterModelPrivate::OnActuatorMsg( + const msgs::Actuators &_msg) +{ + std::lock_guard lock(this->recvdActuatorsMsgMutex); + this->recvdActuatorsMsg = _msg; + if (this->actuatorNumber == 0) + gzdbg << "Received actuator message!" << std::endl; +} + +////////////////////////////////////////////////// +void SpacecraftThrusterModel::PreUpdate(const UpdateInfo &_info, + EntityComponentManager &_ecm) +{ + GZ_PROFILE("SpacecraftThrusterModel::PreUpdate"); + // \TODO(anyone) Support rewind + if (_info.dt < std::chrono::steady_clock::duration::zero()) + { + gzwarn << "Detected jump back in time [" + << std::chrono::duration_cast(_info.dt).count() + << "s]. System may not work properly." << std::endl; + } + + // Nothing left to do if paused. + if (_info.paused) + return; + + this->dataPtr->simTime = std::chrono::duration(_info.simTime).count(); + this->dataPtr->UpdateForcesAndMoments(_ecm); +} + +////////////////////////////////////////////////// +void SpacecraftThrusterModelPrivate::UpdateForcesAndMoments( + EntityComponentManager &_ecm) +{ + GZ_PROFILE("SpacecraftThrusterModelPrivate::UpdateForcesAndMoments"); + std::optional msg; + auto actuatorMsgComp = + _ecm.Component(this->model.Entity()); + + // Actuators messages can come in from transport or via a component. If a + // component is available, it takes precedence. + if (actuatorMsgComp) + { + msg = actuatorMsgComp->Data(); + } + else + { + std::lock_guard lock(this->recvdActuatorsMsgMutex); + if (this->recvdActuatorsMsg.has_value()) + { + msg = *this->recvdActuatorsMsg; + } + } + + if (msg.has_value()) + { + if (this->actuatorNumber > msg->normalized_size() - 1) + { + gzerr << "You tried to access index " << this->actuatorNumber + << " of the Actuator array which is of size " + << msg->normalized_size() << std::endl; + return; + } + } + else + { + return; + } + + // METHOD: + // + // targetDutyCycle starts as a normalized value between 0 and 1, so we + // need to convert it to the corresponding time in the duty cycle + // period + // |________| |________ + // | ^ | | | + // __| | |____| |__ + // a b c d + // a: cycle start time + // b: sampling time + // c: target duty cycle + // d: cycle period + double targetDutyCycle = + msg->normalized(this->actuatorNumber) * (1.0 / this->dutyCycleFrequency); + if (this->actuatorNumber == 0) + gzdbg << this->actuatorNumber + << ": target duty cycle: " << targetDutyCycle << std::endl; + + // Calculate cycle start time + if (this->samplingTime >= 1.0/this->dutyCycleFrequency) { + if (this->actuatorNumber == 0) + gzdbg << this->actuatorNumber + << ": Cycle completed. Resetting cycle start time." + << std::endl; + this->cycleStartTime = this->simTime; + } + + // Calculate sampling time instant within the cycle + this->samplingTime = this->simTime - this->cycleStartTime; + if (this->actuatorNumber == 0) + gzdbg << this->actuatorNumber + << ": PWM Period: " << 1.0/this->dutyCycleFrequency + << " Cycle Start time: " << this->cycleStartTime + << " Sampling time: " << this->samplingTime << std::endl; + + // Apply force if the sampling time is less than the target ON duty cycle + double force = this->samplingTime <= targetDutyCycle ? this->maxThrust : 0.0; + if(targetDutyCycle < 1e-9) force = 0.0; + + if (this->actuatorNumber == 0) + gzdbg << this->actuatorNumber + << ": Force: " << force + << " Sampling time: " << this->samplingTime + << " Tgt duty cycle: " << targetDutyCycle << std::endl; + + // Apply force to the link + Link link(this->linkEntity); + const auto worldPose = link.WorldPose(_ecm); + link.AddWorldForce(_ecm, + worldPose->Rot().RotateVector(math::Vector3d(0, 0, force))); + if (this->actuatorNumber == 0) + gzdbg << this->actuatorNumber + << ": Input Value: " << msg->normalized(this->actuatorNumber) + << " Calc. Force: " << force << std::endl; +} + +GZ_ADD_PLUGIN(SpacecraftThrusterModel, + System, + SpacecraftThrusterModel::ISystemConfigure, + SpacecraftThrusterModel::ISystemPreUpdate) + +GZ_ADD_PLUGIN_ALIAS(SpacecraftThrusterModel, + "gz::sim::systems::SpacecraftThrusterModel") + +// TODO(CH3): Deprecated, remove on version 8 +GZ_ADD_PLUGIN_ALIAS(SpacecraftThrusterModel, + "ignition::gazebo::systems::SpacecraftThrusterModel") diff --git a/src/systems/spacecraft_thruster_model/SpacecraftThrusterModel.hh b/src/systems/spacecraft_thruster_model/SpacecraftThrusterModel.hh new file mode 100644 index 0000000000..77b0dbc715 --- /dev/null +++ b/src/systems/spacecraft_thruster_model/SpacecraftThrusterModel.hh @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2019 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#ifndef GZ_SIM_SYSTEMS_SPACECRAFTTHRUSTERMODEL_HH_ +#define GZ_SIM_SYSTEMS_SPACECRAFTTHRUSTERMODEL_HH_ + +#include +#include + +namespace gz +{ +namespace sim +{ +// Inline bracket to help doxygen filtering. +inline namespace GZ_SIM_VERSION_NAMESPACE { +namespace systems +{ + // Forward declaration + class SpacecraftThrusterModelPrivate; + + /// \brief This system applies a thrust force to models with RCS-like + /// thrusters. See tutorials/spacecraft_thrusters.md for a tutorial usage. + /// Below follow the minimum necessary parameters needed by the plugin: + /// \param link_name Name of the link that the thruster is attached to. + /// \param actuator_number Index of the element to be used from the + /// actuators message for a joint. + /// \param duty_cycle_frequency Frequency of the duty cycle signal in Hz. + /// \param max_thrust Maximum thrust force in Newtons, applied on the + /// «on» phase of the duty cycle. + /// \param topic Name of the topic where the commanded normalized + /// thrust is published. Unit is <0, 1>, corresponding to the + /// percentage of the duty cycle that the thruster is on. + /// Default uses the models name. + /// \param sub_topic [optional] Name of the sub_topic to listen to actuator + /// message on. + /// + /// This plugin replicates the PWM thruster behavior in: + /// Nakka, Yashwanth Kumar, et al. "A six degree-of-freedom spacecraft + /// dynamics simulator for formation control research." 2018 AAS/AIAA + /// Astrodynamics Specialist Conference. 2018. -> 'Thruster Firing Time' + /// Phodapol, S. (2023). Predictive Controllers for Load Transportation in + /// Microgravity Environments (Dissertation). -> '5.3.4 PWM Controller Node' + /// Retrieved from https://urn.kb.se/resolve?urn=urn:nbn:se:kth:diva-344440 + + class SpacecraftThrusterModel + : public System, + public ISystemConfigure, + public ISystemPreUpdate + { + /// \brief Constructor + public: SpacecraftThrusterModel(); + + /// \brief Destructor + public: ~SpacecraftThrusterModel() override = default; + + // Documentation inherited + public: void Configure(const Entity &_entity, + const std::shared_ptr &_sdf, + EntityComponentManager &_ecm, + EventManager &_eventMgr) override; + + // Documentation inherited + public: void PreUpdate( + const gz::sim::UpdateInfo &_info, + gz::sim::EntityComponentManager &_ecm) override; + + /// \brief Private data pointer + private: std::unique_ptr dataPtr; + }; + } +} +} +} + +#endif diff --git a/src/systems/thruster/Thruster.cc b/src/systems/thruster/Thruster.cc index 87aa50dcb4..62e2e0cf11 100644 --- a/src/systems/thruster/Thruster.cc +++ b/src/systems/thruster/Thruster.cc @@ -694,9 +694,6 @@ void Thruster::PreUpdate( gz::sim::Link link(this->dataPtr->linkEntity); - - auto pose = worldPose(this->dataPtr->linkEntity, _ecm); - // TODO(arjo129): add logic for custom coordinate frame // Convert joint axis to the world frame const auto linkWorldPose = worldPose(this->dataPtr->linkEntity, _ecm); diff --git a/src/systems/touch_plugin/TouchPlugin.cc b/src/systems/touch_plugin/TouchPlugin.cc index 91007cfbcc..d9cab9fd1d 100644 --- a/src/systems/touch_plugin/TouchPlugin.cc +++ b/src/systems/touch_plugin/TouchPlugin.cc @@ -251,8 +251,8 @@ void TouchPluginPrivate::Update(const UpdateInfo &_info, if (_info.dt < std::chrono::steady_clock::duration::zero()) { gzwarn << "Detected jump back in time [" - << std::chrono::duration_cast(_info.dt).count() - << "s]. System may not work properly." << std::endl; + << std::chrono::duration(_info.dt).count() + << "s]. System may not work properly." << std::endl; } { diff --git a/src/systems/trajectory_follower/TrajectoryFollower.cc b/src/systems/trajectory_follower/TrajectoryFollower.cc index 1db5e1c2d0..e295686946 100644 --- a/src/systems/trajectory_follower/TrajectoryFollower.cc +++ b/src/systems/trajectory_follower/TrajectoryFollower.cc @@ -111,9 +111,6 @@ class gz::sim::systems::TrajectoryFollowerPrivate /// \brief Whether the trajectory follower behavior should be paused or not. public: bool paused = false; - /// \brief Angular velocity set to zero - public: bool zeroAngVelSet = false; - /// \brief Force angular velocity to be zero when bearing is reached public: bool forceZeroAngVel = false; }; @@ -390,37 +387,22 @@ void TrajectoryFollower::PreUpdate( // Transform the force and torque to the world frame. // Move commands. The vehicle always move forward (X direction). gz::math::Vector3d forceWorld; + gz::math::Vector3d torqueWorld; if (std::abs(bearing.Degree()) <= this->dataPtr->bearingTolerance) { forceWorld = (*comPose).Rot().RotateVector( gz::math::Vector3d(this->dataPtr->forceToApply, 0, 0)); // force angular velocity to be zero when bearing is reached - if (this->dataPtr->forceZeroAngVel && !this->dataPtr->zeroAngVelSet && + if (this->dataPtr->forceZeroAngVel && math::equal (std::abs(bearing.Degree()), 0.0, this->dataPtr->bearingTolerance * 0.5)) { this->dataPtr->link.SetAngularVelocity(_ecm, math::Vector3d::Zero); - this->dataPtr->zeroAngVelSet = true; } } - gz::math::Vector3d torqueWorld; - if (std::abs(bearing.Degree()) > this->dataPtr->bearingTolerance) + else { - // remove angular velocity component otherwise the physics system will set - // the zero ang vel command every iteration - if (this->dataPtr->forceZeroAngVel && this->dataPtr->zeroAngVelSet) - { - auto angVelCmdComp = _ecm.Component( - this->dataPtr->link.Entity()); - if (angVelCmdComp) - { - _ecm.RemoveComponent( - this->dataPtr->link.Entity()); - this->dataPtr->zeroAngVelSet = false; - } - } - int sign = static_cast(std::abs(bearing.Degree()) / bearing.Degree()); torqueWorld = (*comPose).Rot().RotateVector( gz::math::Vector3d(0, 0, sign * this->dataPtr->torqueToApply)); diff --git a/src/systems/user_commands/UserCommands.cc b/src/systems/user_commands/UserCommands.cc index c79e392551..0eedaa27e5 100644 --- a/src/systems/user_commands/UserCommands.cc +++ b/src/systems/user_commands/UserCommands.cc @@ -80,6 +80,7 @@ #include "gz/sim/EntityComponentManager.hh" #include "gz/sim/Model.hh" #include "gz/sim/SdfEntityCreator.hh" +#include "gz/sim/System.hh" #include "gz/sim/Util.hh" #include "gz/sim/World.hh" #include "gz/sim/components/ContactSensorData.hh" @@ -583,6 +584,12 @@ UserCommands::~UserCommands() = default; bool UserCommandsInterface::HasContactSensor(const Entity _collision) { auto *linkEntity = ecm->Component(_collision); + + if (linkEntity == nullptr) + { + return false; + } + auto allLinkSensors = ecm->EntitiesByComponents(components::Sensor(), components::ParentEntity(*linkEntity)); @@ -611,6 +618,13 @@ bool UserCommandsInterface::HasContactSensor(const Entity _collision) return false; } +////////////////////////////////////////////////// +System::PriorityType UserCommands::ConfigurePriority() +{ + // Use constant from System.hh + return ::gz::sim::systems::kUserCommandsPriority; +} + ////////////////////////////////////////////////// void UserCommands::Configure(const Entity &_entity, const std::shared_ptr &_sdf, @@ -1268,7 +1282,6 @@ bool CreateCommand::Execute() // Spherical coordinates if (createMsg->has_spherical_coordinates()) { - gzerr << "HasSphericalCoordinates" << std::endl; auto scComp = this->iface->ecm->Component( this->iface->worldEntity); if (nullptr == scComp) @@ -1280,25 +1293,33 @@ bool CreateCommand::Execute() } else { - // deg to rad - math::Vector3d latLonEle{ + auto vec = math::CoordinateVector3::Spherical( GZ_DTOR(createMsg->spherical_coordinates().latitude_deg()), GZ_DTOR(createMsg->spherical_coordinates().longitude_deg()), - createMsg->spherical_coordinates().elevation()}; - - auto pos = scComp->Data().PositionTransform(latLonEle, + createMsg->spherical_coordinates().elevation()); + auto pos = scComp->Data().PositionTransform(vec, math::SphericalCoordinates::SPHERICAL, - math::SphericalCoordinates::LOCAL2); - - // Override pos and add to yaw - if (!createPose.has_value()) - createPose = math::Pose3d::Zero; - createPose.value().SetX(pos.X()); - createPose.value().SetY(pos.Y()); - createPose.value().SetZ(pos.Z()); - createPose.value().Rot() = math::Quaterniond(0, 0, - GZ_DTOR(createMsg->spherical_coordinates().heading_deg())) * - createPose.value().Rot(); + math::SphericalCoordinates::LOCAL); + + if (!pos.has_value() || !pos->IsMetric()) + { + gzerr << "Trying to create entity [" << desiredName + << "] using spherical coordinates, but spherical to local " + << "position conversion failed. Entity will be created at the " + << "world origin." << std::endl; + } + else + { + // Override pos and add to yaw + if (!createPose.has_value()) + createPose = math::Pose3d::Zero; + createPose.value().SetX(*pos->X()); + createPose.value().SetY(*pos->Y()); + createPose.value().SetZ(*pos->Z()); + createPose.value().Rot() = math::Quaterniond(0, 0, + GZ_DTOR(createMsg->spherical_coordinates().heading_deg())) * + createPose.value().Rot(); + } } } @@ -1688,17 +1709,22 @@ bool SphericalCoordinatesCommand::Execute() return false; } - // deg to rad - math::Vector3d latLonEle{ + auto vec = math::CoordinateVector3::Spherical( GZ_DTOR(sphericalCoordinatesMsg->latitude_deg()), GZ_DTOR(sphericalCoordinatesMsg->longitude_deg()), - sphericalCoordinatesMsg->elevation()}; - - auto pos = scComp->Data().PositionTransform(latLonEle, + sphericalCoordinatesMsg->elevation()); + auto pos = scComp->Data().PositionTransform(vec, math::SphericalCoordinates::SPHERICAL, - math::SphericalCoordinates::LOCAL2); + math::SphericalCoordinates::LOCAL); + if (!pos.has_value() || !pos->IsMetric()) + { + gzerr << "Trying to move entity [" << entity + << "] using spherical coordinates, but spherical to local " + << "position conversion failed." << std::endl; + return false; + } - math::Pose3d pose{pos.X(), pos.Y(), pos.Z(), 0, 0, + math::Pose3d pose{*pos->X(), *pos->Y(), *pos->Z(), 0, 0, GZ_DTOR(sphericalCoordinatesMsg->heading_deg())}; auto poseCmdComp = diff --git a/src/systems/user_commands/UserCommands.hh b/src/systems/user_commands/UserCommands.hh index 0aa396bd00..791fc8548d 100644 --- a/src/systems/user_commands/UserCommands.hh +++ b/src/systems/user_commands/UserCommands.hh @@ -73,6 +73,7 @@ namespace systems class UserCommands final: public System, public ISystemConfigure, + public ISystemConfigurePriority, public ISystemPreUpdate { /// \brief Constructor @@ -87,6 +88,9 @@ namespace systems EntityComponentManager &_ecm, EventManager &_eventMgr) override; + /// Documentation inherited + public: System::PriorityType ConfigurePriority() final; + /// \brief All received commands are queued in order of reception and /// executed in order during PreUpdate. /// \param[in] _info Contains information about the current simulation diff --git a/src/systems/velocity_control/VelocityControl.cc b/src/systems/velocity_control/VelocityControl.cc index b5a1ccbde1..7af66e743b 100644 --- a/src/systems/velocity_control/VelocityControl.cc +++ b/src/systems/velocity_control/VelocityControl.cc @@ -188,8 +188,8 @@ void VelocityControl::PreUpdate(const UpdateInfo &_info, if (_info.dt < std::chrono::steady_clock::duration::zero()) { gzwarn << "Detected jump back in time [" - << std::chrono::duration_cast(_info.dt).count() - << "s]. System may not work properly." << std::endl; + << std::chrono::duration(_info.dt).count() + << "s]. System may not work properly." << std::endl; } // Nothing left to do if paused. diff --git a/src/systems/wind_effects/WindEffects.cc b/src/systems/wind_effects/WindEffects.cc index 5ef1e51aa4..e237d0b1bc 100644 --- a/src/systems/wind_effects/WindEffects.cc +++ b/src/systems/wind_effects/WindEffects.cc @@ -716,8 +716,8 @@ void WindEffects::PreUpdate(const UpdateInfo &_info, if (_info.dt < std::chrono::steady_clock::duration::zero()) { gzwarn << "Detected jump back in time [" - << std::chrono::duration_cast(_info.dt).count() - << "s]. System may not work properly." << std::endl; + << std::chrono::duration(_info.dt).count() + << "s]. System may not work properly." << std::endl; } // Process commands diff --git a/test/benchmark/ecm_serialize.cc b/test/benchmark/ecm_serialize.cc index 2d976df4db..ad314e9693 100644 --- a/test/benchmark/ecm_serialize.cc +++ b/test/benchmark/ecm_serialize.cc @@ -41,24 +41,24 @@ inline namespace GZ_SIM_VERSION_NAMESPACE { namespace components { using IntComponent = components::Component; -GZ_SIM_REGISTER_COMPONENT("ign_gazebo_components.IntComponent", +GZ_SIM_REGISTER_COMPONENT("gz_sim_components.IntComponent", IntComponent) using UIntComponent = components::Component; -GZ_SIM_REGISTER_COMPONENT("ign_gazebo_components.UIntComponent", +GZ_SIM_REGISTER_COMPONENT("gz_sim_components.UIntComponent", UIntComponent) using DoubleComponent = components::Component; -GZ_SIM_REGISTER_COMPONENT("ign_gazebo_components.DoubleComponent", +GZ_SIM_REGISTER_COMPONENT("gz_sim_components.DoubleComponent", DoubleComponent) using StringComponent = components::Component; -GZ_SIM_REGISTER_COMPONENT("ign_gazebo_components.StringComponent", +GZ_SIM_REGISTER_COMPONENT("gz_sim_components.StringComponent", StringComponent) using BoolComponent = components::Component; -GZ_SIM_REGISTER_COMPONENT("ign_gazebo_components.BoolComponent", +GZ_SIM_REGISTER_COMPONENT("gz_sim_components.BoolComponent", BoolComponent) } } diff --git a/test/integration/CMakeLists.txt b/test/integration/CMakeLists.txt index 61f350bd73..da422d2a89 100644 --- a/test/integration/CMakeLists.txt +++ b/test/integration/CMakeLists.txt @@ -44,6 +44,7 @@ set(tests level_manager.cc level_manager_runtime_performers.cc light.cc + lighter_than_air_dynamics.cc link.cc logical_camera_system.cc logical_audio_sensor_plugin.cc @@ -71,6 +72,7 @@ set(tests reset.cc reset_detachable_joint.cc save_world.cc + spacecraft.cc scene_broadcaster_system.cc sdf_frame_semantics.cc sdf_include.cc diff --git a/test/integration/actor_trajectory.cc b/test/integration/actor_trajectory.cc index 7a6819ba28..901789ce2e 100644 --- a/test/integration/actor_trajectory.cc +++ b/test/integration/actor_trajectory.cc @@ -99,7 +99,7 @@ class ActorFixture : public InternalFixture> // Load the actor_trajectory.sdf world that animates a box (actor) to follow // a trajectory. Verify that the box pose changes over time on the rendering // side. -TEST_F(ActorFixture, GZ_UTILS_TEST_DISABLED_ON_MAC(ActorTrajectoryNoMesh)) +TEST_F(ActorFixture, ActorTrajectoryNoMesh) { sim::ServerConfig serverConfig; diff --git a/test/integration/camera_sensor_background_from_scene.cc b/test/integration/camera_sensor_background_from_scene.cc index 88d55cfc67..d53234258f 100644 --- a/test/integration/camera_sensor_background_from_scene.cc +++ b/test/integration/camera_sensor_background_from_scene.cc @@ -77,7 +77,7 @@ void cameraCb(const msgs::Image & _msg) ///////////////////////////////////////////////// // Test sensors use the background color of by default TEST_F(CameraSensorBackgroundFixture, - GZ_UTILS_TEST_DISABLED_ON_MAC(RedBackgroundFromScene)) + RedBackgroundFromScene) { const auto sdfFile = common::joinPaths(std::string(PROJECT_SOURCE_PATH), "test", "worlds", "camera_sensor_scene_background.sdf"); diff --git a/test/integration/camera_video_record_system.cc b/test/integration/camera_video_record_system.cc index 97380db6d7..a9f2dc5a60 100644 --- a/test/integration/camera_video_record_system.cc +++ b/test/integration/camera_video_record_system.cc @@ -43,6 +43,15 @@ class CameraVideoRecorderTest : public InternalFixture<::testing::Test> ///////////////////////////////////////////////// TEST_F(CameraVideoRecorderTest, GZ_UTILS_TEST_DISABLED_ON_MAC(RecordVideo)) { + // This test fails on Github Actions. Skip it for now. + // Note: The GITHUB_ACTIONS environment variable is automatically set when + // running on Github Actions. + std::string githubAction; + if (common::env("GITHUB_ACTIONS", githubAction)) + { + GTEST_SKIP(); + } + // Start server ServerConfig serverConfig; serverConfig.SetSdfFile(std::string(PROJECT_SOURCE_PATH) + diff --git a/test/integration/distortion_camera.cc b/test/integration/distortion_camera.cc index 5550f71a65..0e6613a3de 100644 --- a/test/integration/distortion_camera.cc +++ b/test/integration/distortion_camera.cc @@ -69,6 +69,15 @@ void imageCb(const msgs::Image &_msg) TEST_F(DistortionCameraTest, DISABLED_DistortionCameraBox) { + // This test fails on Github Actions. Skip it for now. + // Note: The GITHUB_ACTIONS environment variable is automatically set when + // running on Github Actions. + std::string githubAction; + if (common::env("GITHUB_ACTIONS", githubAction)) + { + GTEST_SKIP(); + } + // Start server ServerConfig serverConfig; const auto sdfFile = common::joinPaths(std::string(PROJECT_SOURCE_PATH), diff --git a/test/integration/each_new_removed.cc b/test/integration/each_new_removed.cc index 144381aa43..cbd367b333 100644 --- a/test/integration/each_new_removed.cc +++ b/test/integration/each_new_removed.cc @@ -37,7 +37,7 @@ using namespace gz; using namespace std::chrono_literals; using IntComponent = sim::components::Component; -GZ_SIM_REGISTER_COMPONENT("ign_gazebo_components.IntComponent", +GZ_SIM_REGISTER_COMPONENT("gz_sim_components.IntComponent", IntComponent) class EachNewRemovedFixture : public InternalFixture<::testing::Test> diff --git a/test/integration/examples_build.cc b/test/integration/examples_build.cc index 51cb4ec84a..4b20a80f84 100644 --- a/test/integration/examples_build.cc +++ b/test/integration/examples_build.cc @@ -57,28 +57,6 @@ struct ExampleEntry } }; -////////////////////////////////////////////////// -/// Filter examples that are known to not build or require -/// specific configurations -/// \param[in] _entry Example entry to check -/// \return true if example entry should be built, false otherwise -bool FilterEntry(const ExampleEntry &_entry) -{ - math::SemanticVersion cmakeVersion{std::string(CMAKE_VERSION)}; - if (cmakeVersion < math::SemanticVersion(3, 11, 0) && - (_entry.base == "custom_sensor_system" || - _entry.base == "gtest_setup")) - { - gzdbg << "Skipping [" << _entry.base - << "] test, which requires CMake version " - << ">= 3.11.0. Currently using CMake " - << cmakeVersion - << std::endl; - return false; - } - return true; -} - ////////////////////////////////////////////////// /// Generate a list of examples to be built. std::vector GetExamples() @@ -116,11 +94,6 @@ void ExamplesBuild::Build(const ExampleEntry &_entry) { common::Console::SetVerbosity(4); - if (!FilterEntry(_entry)) - { - GTEST_SKIP(); - } - // Path to examples of the given type ASSERT_TRUE(gz::common::exists(_entry.sourceDir)); diff --git a/test/integration/force_torque_system.cc b/test/integration/force_torque_system.cc index dd4684e627..1841ef1728 100644 --- a/test/integration/force_torque_system.cc +++ b/test/integration/force_torque_system.cc @@ -24,10 +24,15 @@ #include #include +#include "gz/sim/components/ForceTorque.hh" +#include "gz/sim/components/Model.hh" #include "gz/sim/components/Name.hh" #include "gz/sim/components/Sensor.hh" -#include "gz/sim/components/ForceTorque.hh" +#include "gz/sim/components/WrenchMeasured.hh" +#include "gz/sim/Joint.hh" +#include "gz/sim/Link.hh" +#include "gz/sim/Model.hh" #include "gz/sim/Server.hh" #include "gz/sim/SystemLoader.hh" #include "test_config.hh" @@ -43,7 +48,7 @@ class ForceTorqueTest : public InternalFixture<::testing::Test> }; ///////////////////////////////////////////////// -TEST_F(ForceTorqueTest, GZ_UTILS_TEST_DISABLED_ON_WIN32(MeasureWeight)) +TEST_F(ForceTorqueTest, GZ_UTILS_TEST_DISABLED_ON_WIN32(MeasureWeightTopic)) { using namespace std::chrono_literals; // Start server @@ -59,8 +64,8 @@ TEST_F(ForceTorqueTest, GZ_UTILS_TEST_DISABLED_ON_WIN32(MeasureWeight)) // Having iters exactly in sync with update rate can lead to a race condition // in the test between simulation and transport - size_t iters = 999u; - size_t updates = 100u; + const size_t iters = 999u; + const size_t updates = 100u; std::vector wrenches; wrenches.reserve(updates); @@ -100,6 +105,95 @@ TEST_F(ForceTorqueTest, GZ_UTILS_TEST_DISABLED_ON_WIN32(MeasureWeight)) } } +///////////////////////////////////////////////// +TEST_F(ForceTorqueTest, GZ_UTILS_TEST_DISABLED_ON_WIN32(MeasureWeightECM)) +{ + using namespace std::chrono_literals; + // Start server + ServerConfig serverConfig; + const auto sdfFile = common::joinPaths( + std::string(PROJECT_SOURCE_PATH), "test", "worlds", "force_torque.sdf"); + serverConfig.SetSdfFile(sdfFile); + + Server server(serverConfig); + EXPECT_FALSE(server.Running()); + EXPECT_FALSE(*server.Running(0)); + server.SetUpdatePeriod(1ns); + + // Get entity for //test1/scale/sensor_joint/force_torque_sensor and add + // WrenchMeasured component on first PreUpdate + Entity sensorEntity; + bool firstRun = true; + auto addWrenchComponent = + [&firstRun, &sensorEntity]( + const UpdateInfo &, EntityComponentManager &_ecm) + { + if (firstRun) + { + firstRun = false; + + // Get sensorEntity + auto test1ModelEntity = _ecm.EntityByComponents( + components::Model(), components::Name("test1")); + Model test1Model(test1ModelEntity); + + Model scaleModel(test1Model.ModelByName(_ecm, "scale")); + ASSERT_TRUE(scaleModel.Valid(_ecm)); + + Joint sensorJoint(scaleModel.JointByName(_ecm, "sensor_joint")); + ASSERT_TRUE(sensorJoint.Valid(_ecm)); + + sensorEntity = sensorJoint.SensorByName(_ecm, "force_torque_sensor"); + + // Expect it doesn't yet have WrenchMeasured + EXPECT_EQ(nullptr, + _ecm.Component(sensorEntity)); + + // Add WrenchMeasured + _ecm.CreateComponent(sensorEntity, components::WrenchMeasured()); + + EXPECT_NE(nullptr, + _ecm.Component(sensorEntity)); + } + }; + + // Get the MeasuredWrench for //test1/scale/sensor_joint/force_torque_sensor + msgs::Wrench wrench; + auto getMeasuredWrench = + [&wrench, &sensorEntity](const UpdateInfo &, + const EntityComponentManager &_ecm) + { + auto measuredWrench = + _ecm.Component(sensorEntity); + ASSERT_NE(nullptr, measuredWrench); + if (measuredWrench) + { + wrench = measuredWrench->Data(); + } + }; + + // Add the system + test::Relay testSystem; + testSystem.OnPreUpdate(addWrenchComponent); + testSystem.OnPostUpdate(getMeasuredWrench); + server.AddSystem(testSystem.systemPtr); + server.Run(true, 1, false); + + const size_t iters = 999u; + + // Run server (iters-1) steps, since we already took 1 step above + server.Run(true, iters - 1, false); + ASSERT_EQ(iters, *server.IterationCount()); + + const double kSensorMass = 0.2; + const double kWeightMass = 10; + const double kGravity = 9.8; + const math::Vector3 expectedForce = + math::Vector3d{0, 0, kGravity * (kSensorMass + kWeightMass)}; + EXPECT_EQ(expectedForce, msgs::Convert(wrench.force())); + EXPECT_EQ(math::Vector3d::Zero, msgs::Convert(wrench.torque())); +} + ///////////////////////////////////////////////// TEST_F(ForceTorqueTest, GZ_UTILS_TEST_DISABLED_ON_WIN32(SensorPoseOffset)) { diff --git a/test/integration/lighter_than_air_dynamics.cc b/test/integration/lighter_than_air_dynamics.cc new file mode 100644 index 0000000000..b2edf7cf0c --- /dev/null +++ b/test/integration/lighter_than_air_dynamics.cc @@ -0,0 +1,365 @@ +/* + * Copyright (C) 2023 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "gz/sim/Link.hh" +#include "gz/sim/Model.hh" +#include "gz/sim/Server.hh" +#include "gz/sim/SystemLoader.hh" +#include "gz/sim/TestFixture.hh" +#include "gz/sim/Util.hh" +#include "gz/sim/World.hh" + +#include "test_config.hh" +#include "../helpers/EnvTestFixture.hh" + +using namespace gz; +using namespace sim; + +class LighterThanAirDynamicsTest : public InternalFixture<::testing::Test> +{ + /// \brief Test a world file + /// \param[in] _world Path to world file + /// \param[in] _namespace Namespace for topic + public: std::vector TestWorld(const std::string &_world, + const std::string &_namespace); + + public: std::vector> + TestUnstableYaw( + const std::string &_world, const std::string &_namespace); + + public: std::vector> + TestUnstablePitch( + const std::string &_world, const std::string &_namespace); + + private: std::vector worldPoses; + private: std::vector bodyAngularVels; + private: std::vector> state; +}; + + +////////////////////////////////////////////////// +std::vector> +LighterThanAirDynamicsTest::TestUnstablePitch( +const std::string &_world, const std::string &_namespace) +{ + // Maximum verbosity for debugging + common::Console::SetVerbosity(4); + + // Start server + ServerConfig serverConfig; + serverConfig.SetSdfFile(_world); + + TestFixture fixture(serverConfig); + + Model model; + Link body; + + worldPoses.clear(); + bodyAngularVels.clear(); + state.clear(); + + fixture. + OnConfigure( + [&](const Entity &_worldEntity, + const std::shared_ptr &/*_sdf*/, + EntityComponentManager &_ecm, + EventManager &/*eventMgr*/) + { + World world(_worldEntity); + + auto modelEntity = world.ModelByName(_ecm, _namespace); + EXPECT_NE(modelEntity, kNullEntity); + model = Model(modelEntity); + + auto bodyEntity = model.LinkByName(_ecm, _namespace + "_link"); + EXPECT_NE(bodyEntity, kNullEntity); + + body = Link(bodyEntity); + body.EnableVelocityChecks(_ecm); + + auto WorldPose = body.WorldPose(_ecm); + + math::Matrix3d DCM(WorldPose.value().Rot()); + // Small disturbance to enduce munk moment + math::Vector3d body_z_force(0, 0, 100); + math::Vector3d world_frame_force = DCM * body_z_force; + + body.AddWorldForce(_ecm, world_frame_force); + + }). + OnPostUpdate([&](const UpdateInfo &/*_info*/, + const EntityComponentManager &_ecm) + { + auto worldPose = body.WorldPose(_ecm); + math::Vector3d bodyAngularVel = \ + (body.WorldPose(_ecm)).value().Rot().Inverse() * \ + body.WorldAngularVelocity(_ecm).value(); + ASSERT_TRUE(worldPose); + worldPoses.push_back(worldPose.value()); + bodyAngularVels.push_back(bodyAngularVel); + state.push_back(std::make_pair(worldPose.value(), bodyAngularVel)); + }). + OnPreUpdate([&](const UpdateInfo &/*_info*/, + EntityComponentManager &_ecm) + { + auto WorldPose = body.WorldPose(_ecm); + + math::Matrix3d DCM(WorldPose.value().Rot()); + math::Vector3d body_x_force(100, 0, 0); + + math::Vector3d world_frame_force = DCM * body_x_force; + + body.AddWorldForce(_ecm, world_frame_force); + }). + Finalize(); + + fixture.Server()->Run(true, 2000, false); + EXPECT_EQ(2000u, worldPoses.size()); + + EXPECT_NE(model.Entity(), kNullEntity); + EXPECT_NE(body.Entity(), kNullEntity); + + return state; +} + +////////////////////////////////////////////////// +std::vector> + LighterThanAirDynamicsTest::TestUnstableYaw( + const std::string &_world, const std::string &_namespace) +{ + // Maximum verbosity for debugging + common::Console::SetVerbosity(4); + + // Start server + ServerConfig serverConfig; + serverConfig.SetSdfFile(_world); + + TestFixture fixture(serverConfig); + + Model model; + Link body; + worldPoses.clear(); + bodyAngularVels.clear(); + state.clear(); + fixture. + OnConfigure( + [&](const Entity &_worldEntity, + const std::shared_ptr &/*_sdf*/, + EntityComponentManager &_ecm, + EventManager &/*eventMgr*/) + { + World world(_worldEntity); + + auto modelEntity = world.ModelByName(_ecm, _namespace); + EXPECT_NE(modelEntity, kNullEntity); + model = Model(modelEntity); + + auto bodyEntity = model.LinkByName(_ecm, _namespace + "_link"); + EXPECT_NE(bodyEntity, kNullEntity); + + body = Link(bodyEntity); + body.EnableVelocityChecks(_ecm); + + auto WorldPose = body.WorldPose(_ecm); + + math::Matrix3d DCM(WorldPose.value().Rot()); + // Small disturbance to enduce some lateral movement + // to allow munk moment to grow + math::Vector3d body_y_force(0, 10, 0); + math::Vector3d world_frame_force = DCM * body_y_force; + + body.AddWorldForce(_ecm, world_frame_force); + + }). + OnPostUpdate([&](const UpdateInfo &/*_info*/, + const EntityComponentManager &_ecm) + { + auto worldPose = body.WorldPose(_ecm); + math::Vector3d bodyAngularVel = + (body.WorldPose(_ecm)).value().Rot().Inverse() * \ + body.WorldAngularVelocity(_ecm).value(); + ASSERT_TRUE(worldPose); + worldPoses.push_back(worldPose.value()); + bodyAngularVels.push_back(bodyAngularVel); + state.push_back(std::make_pair(worldPose.value(), bodyAngularVel)); + + }). + OnPreUpdate([&](const UpdateInfo &/*_info*/, + EntityComponentManager &_ecm) + { + auto WorldPose = body.WorldPose(_ecm); + + math::Matrix3d DCM(WorldPose.value().Rot()); + math::Vector3d body_x_force(100, 0, 0); + + math::Vector3d world_frame_force = DCM * body_x_force; + + body.AddWorldForce(_ecm, world_frame_force); + }). + Finalize(); + + fixture.Server()->Run(true, 2000, false); + EXPECT_EQ(2000u, worldPoses.size()); + + EXPECT_NE(model.Entity(), kNullEntity); + EXPECT_NE(body.Entity(), kNullEntity); + + return state; +} + +////////////////////////////////////////////////// +// Test if the plugin can be loaded succesfully and +// runs +std::vector LighterThanAirDynamicsTest::TestWorld( + const std::string &_world, const std::string &_namespace) +{ + // Maximum verbosity for debugging + common::Console::SetVerbosity(4); + + // Start server + ServerConfig serverConfig; + serverConfig.SetSdfFile(_world); + + TestFixture fixture(serverConfig); + + Model model; + Link body; + std::vector bodyVels; + fixture. + OnConfigure( + [&](const Entity &_worldEntity, + const std::shared_ptr &/*_sdf*/, + EntityComponentManager &_ecm, + EventManager &/*eventMgr*/) + { + World world(_worldEntity); + + auto modelEntity = world.ModelByName(_ecm, _namespace); + EXPECT_NE(modelEntity, kNullEntity); + model = Model(modelEntity); + + auto bodyEntity = model.LinkByName(_ecm, _namespace + "_link"); + EXPECT_NE(bodyEntity, kNullEntity); + + body = Link(bodyEntity); + body.EnableVelocityChecks(_ecm); + + }). + OnPostUpdate([&](const UpdateInfo &/*_info*/, + const EntityComponentManager &_ecm) + { + auto bodyVel = body.WorldLinearVelocity(_ecm); + ASSERT_TRUE(bodyVel); + bodyVels.push_back(bodyVel.value()); + }). + Finalize(); + + fixture.Server()->Run(true, 2000, false); + EXPECT_EQ(2000u, bodyVels.size()); + + EXPECT_NE(model.Entity(), kNullEntity); + EXPECT_NE(body.Entity(), kNullEntity); + + return bodyVels; +} + +///////////////////////////////////////////////// +/// This test evaluates whether the lighter-than-air-dynamics plugin +/// is loaded successfully and is stable +TEST_F(LighterThanAirDynamicsTest, GZ_UTILS_TEST_DISABLED_ON_WIN32(SpawnModel)) +{ + auto world = common::joinPaths(std::string(PROJECT_SOURCE_PATH), + "test", "worlds", "lighter_than_air_dynamics.sdf"); + + this->TestWorld(world, "hull"); + +} + +///////////////////////////////////////////////// +// Test whether the yaw of the hull will grow +// due to a a small disturbance in its lateral system +TEST_F(LighterThanAirDynamicsTest, GZ_UTILS_TEST_DISABLED_ON_WIN32( + UnstableMunkMomentInYaw)) +{ + auto world = common::joinPaths(std::string(PROJECT_SOURCE_PATH), + "test", "worlds", "lighter_than_air_dynamics.sdf"); + + auto states = this->TestUnstableYaw(world, "hull"); + auto states2 = this->TestUnstableYaw(world, "hull2"); + + math::Vector3d ang_vel = states.back().second; + math::Vector3d end_pos = states.back().first.Pos(); + math::Quaterniond end_rot = states.back().first.Rot(); + + math::Vector3d ang_vel2 = states2.back().second; + math::Vector3d end_pos2 = states2.back().first.Pos(); + math::Quaterniond end_rot2 = states2.back().first.Rot(); + + // Drag from hull should result in translating less than an object with no + // aerodynamic drag + EXPECT_LE(end_pos.X(), end_pos2.X()); + + // Due to the munk moment, the yaw of the aircraft should grow + EXPECT_GT(abs(end_rot.Euler().Z()), abs(end_rot2.Euler().Z())); + + // Due to the munk moment, the yawrate needs to increase + EXPECT_GT(abs(ang_vel.Z()), abs(ang_vel2.Z())); + +} + +///////////////////////////////////////////////// +// Test whether the pitch of the hull will grow +// due to a a small disturbance in its longitudinal system +TEST_F(LighterThanAirDynamicsTest, GZ_UTILS_TEST_DISABLED_ON_WIN32( + UnstableMunkMomentInPitch)) +{ + auto world = common::joinPaths(std::string(PROJECT_SOURCE_PATH), + "test", "worlds", "lighter_than_air_dynamics.sdf"); + + auto states = this->TestUnstablePitch(world, "hull"); + auto states2 = this->TestUnstablePitch(world, "hull2"); + + math::Vector3d ang_vel = states.back().second; + math::Vector3d end_pos = states.back().first.Pos(); + math::Quaterniond end_rot = states.back().first.Rot(); + + math::Vector3d ang_vel2 = states2.back().second; + math::Vector3d end_pos2 = states2.back().first.Pos(); + math::Quaterniond end_rot2 = states2.back().first.Rot(); + + // Drag from hull should result in translating less than an object with no + // aerodynamic drag + EXPECT_LE(end_pos.X(), end_pos2.X()); + + // Due to the munk moment, the yaw of the aircraft should grow + EXPECT_GT(abs(end_rot.Euler().Y()), abs(end_rot2.Euler().Y())); + + // Due to the munk moment, the yawrate needs to increase + EXPECT_GT(abs(ang_vel.Y()), abs(ang_vel2.Y())); + +} diff --git a/test/integration/log_system.cc b/test/integration/log_system.cc index f867ca731a..a2335cae56 100644 --- a/test/integration/log_system.cc +++ b/test/integration/log_system.cc @@ -774,6 +774,18 @@ TEST_F(LogSystemTest, GZ_UTILS_TEST_DISABLED_ON_WIN32(RecordAndPlayback)) // Start server Server playServer(playServerConfig); + // Simulate a client + gz::transport::Node node; + std::atomic numMsgs = 0; + std::function mockClient = + [&](const msgs::SerializedStepMap &/*_res*/) + { + numMsgs++; + }; + using namespace std::placeholders; + EXPECT_TRUE(node.Subscribe( + "/world/default/state", mockClient)); + // Callback function for entities played back // Compare current pose being played back with the pose from the stateMsg test::Relay playbackPoseTester; @@ -837,6 +849,9 @@ TEST_F(LogSystemTest, GZ_UTILS_TEST_DISABLED_ON_WIN32(RecordAndPlayback)) // checked in the playbackPoseTester playServer.Run(true, 500, false); + // The client should have received some messages. + EXPECT_NE(numMsgs, 0); + // Count the total number of state messages in the log file int nTotal{0}; for (auto it = batch.begin(); it != batch.end(); ++it, ++nTotal) { } diff --git a/test/integration/material.cc b/test/integration/material.cc index 6d8ca1599f..22567a8a87 100644 --- a/test/integration/material.cc +++ b/test/integration/material.cc @@ -296,3 +296,30 @@ TEST_F(MaterialTest, InvalidColor) EXPECT_EQ(math::Color(0.0f, 0.0f, 0.0f, 1.0f), boxVisualComp->Data().Specular()); } + +TEST_F(MaterialTest, WorldWithClassicMaterial) +{ + ServerConfig serverConfig; + serverConfig.SetSdfFile(common::joinPaths(PROJECT_SOURCE_PATH, + "test", "worlds", "classic_material.sdf")); + + std::cout << "Loading: " << serverConfig.SdfFile() << std::endl; + this->StartServer(serverConfig); + + auto model = this->GetModel("box"); + ASSERT_TRUE(model.Valid(*this->ecm)); + + auto boxVisualEntity = + this->ecm->EntityByComponents(components::Name("box_visual")); + ASSERT_NE(kNullEntity, boxVisualEntity); + + // Blue color + auto boxVisualComp = + this->ecm->Component(boxVisualEntity); + EXPECT_EQ(math::Color(0.0f, 0.0f, 1.0f, 1.0f), + boxVisualComp->Data().Ambient()); + EXPECT_EQ(math::Color(0.0f, 0.0f, 1.0f, 1.0f), + boxVisualComp->Data().Diffuse()); + EXPECT_EQ(math::Color(0.1f, 0.1f, 0.1f, 1.0f), + boxVisualComp->Data().Specular()); +} diff --git a/test/integration/model.cc b/test/integration/model.cc index f878088631..f4a0f00b2c 100644 --- a/test/integration/model.cc +++ b/test/integration/model.cc @@ -164,6 +164,30 @@ TEST_F(ModelIntegrationTest, SourceFilePath) EXPECT_EQ("/tmp/path", model.SourceFilePath(ecm)); } +////////////////////////////////////////////////// +TEST_F(ModelIntegrationTest, ModelByName) +{ + EntityComponentManager ecm; + + // Model + auto eModel = ecm.CreateEntity(); + Model model(eModel); + EXPECT_EQ(eModel, model.Entity()); + EXPECT_EQ(0u, model.ModelCount(ecm)); + + // Nested Model + auto eNestedModel = ecm.CreateEntity(); + ecm.CreateComponent(eNestedModel, components::Model()); + ecm.CreateComponent(eNestedModel, + components::ParentEntity(eModel)); + ecm.CreateComponent(eNestedModel, + components::Name("nested_model_name")); + + // Check model + EXPECT_EQ(eNestedModel, model.ModelByName(ecm, "nested_model_name")); + EXPECT_EQ(1u, model.ModelCount(ecm)); +} + ////////////////////////////////////////////////// TEST_F(ModelIntegrationTest, LinkByName) { diff --git a/test/integration/physics_system.cc b/test/integration/physics_system.cc index 027005a406..7a50c8c7db 100644 --- a/test/integration/physics_system.cc +++ b/test/integration/physics_system.cc @@ -48,6 +48,7 @@ #include "gz/sim/components/AngularAcceleration.hh" #include "gz/sim/components/AngularVelocity.hh" +#include "gz/sim/components/AngularVelocityReset.hh" #include "gz/sim/components/AxisAlignedBox.hh" #include "gz/sim/components/CanonicalLink.hh" #include "gz/sim/components/Collision.hh" @@ -67,6 +68,7 @@ #include "gz/sim/components/Link.hh" #include "gz/sim/components/LinearAcceleration.hh" #include "gz/sim/components/LinearVelocity.hh" +#include "gz/sim/components/LinearVelocityReset.hh" #include "gz/sim/components/Material.hh" #include "gz/sim/components/Model.hh" #include "gz/sim/components/Name.hh" @@ -747,6 +749,272 @@ TEST_F(PhysicsSystemFixture, EXPECT_NEAR(pos0, positions[1], 0.01); } +///////////////////////////////////////////////// +/// Test linear veocity reset component +TEST_F(PhysicsSystemFixture, + GZ_UTILS_TEST_DISABLED_ON_WIN32(LinearVelocityResetComponent)) +{ + ServerConfig serverConfig; + + const auto sdfFile = std::string(PROJECT_SOURCE_PATH) + + "/test/worlds/diff_drive.sdf"; + + sdf::Root root; + root.Load(sdfFile); + sdf::World *world = root.WorldByIndex(0); + ASSERT_TRUE(nullptr != world); + + // Disable gravity + world->SetGravity(math::Vector3d::Zero); + + serverConfig.SetSdfRoot(root); + + Server server(serverConfig); + + server.SetUpdatePeriod(1ms); + + // Create a system just to get the ECM + EntityComponentManager *ecm{nullptr}; + test::Relay testSystem; + testSystem.OnPreUpdate([&](const UpdateInfo &, + EntityComponentManager &_ecm) + { + ecm = &_ecm; + }); + server.AddSystem(testSystem.systemPtr); + + // Run server and check we have the ECM + EXPECT_EQ(nullptr, ecm); + server.Run(true, 1, false); + EXPECT_NE(nullptr, ecm); + + // Get Link Entity's and objects + const std::string rootLinkName{"chassis"}; + auto rootLinkEntity = ecm->EntityByComponents( + components::Link(), components::Name(rootLinkName)); + Link rootLink(rootLinkEntity); + ASSERT_TRUE(rootLink.Valid(*ecm)); + + const std::string childLinkName{"caster"}; + auto childLinkEntity = ecm->EntityByComponents( + components::Link(), components::Name(childLinkName)); + Link childLink(childLinkEntity); + ASSERT_TRUE(childLink.Valid(*ecm)); + + // Enable velocity checks for each link + rootLink.EnableVelocityChecks(*ecm, true); + childLink.EnableVelocityChecks(*ecm, true); + EXPECT_NE(nullptr, + ecm->Component(rootLinkEntity)); + EXPECT_NE(nullptr, + ecm->Component(childLinkEntity)); + + // Step and expect velocities near zero + server.Run(true, 1, false); + { + auto rootLinkLinearVel = rootLink.WorldLinearVelocity(*ecm); + auto childLinkLinearVel = childLink.WorldLinearVelocity(*ecm); + ASSERT_NE(std::nullopt, rootLinkLinearVel); + ASSERT_NE(std::nullopt, childLinkLinearVel); + EXPECT_EQ(math::Vector3d::Zero, rootLinkLinearVel); + EXPECT_EQ(math::Vector3d::Zero, childLinkLinearVel); + } + + const math::Vector3d vel0(0.125, -0.5, 3.5); + + // Reset child link velocity to nonzero components (upward in Z) + // This shouldn't do anything, since it is not the root link of its FreeGroup + ecm->CreateComponent(childLinkEntity, + components::WorldLinearVelocityReset(vel0)); + + // Step and expect that all velocities are still zero + server.Run(true, 1, false); + { + auto rootLinkLinearVel = rootLink.WorldLinearVelocity(*ecm); + auto childLinkLinearVel = childLink.WorldLinearVelocity(*ecm); + ASSERT_NE(std::nullopt, rootLinkLinearVel); + ASSERT_NE(std::nullopt, childLinkLinearVel); + EXPECT_EQ(math::Vector3d::Zero, rootLinkLinearVel); + EXPECT_EQ(math::Vector3d::Zero, childLinkLinearVel); + } + + // Reset root link velocity to nonzero components (upward in Z) + ecm->CreateComponent(rootLinkEntity, + components::WorldLinearVelocityReset(vel0)); + + // Step and expect all links to have similar velocity + server.Run(true, 1, false); + { + auto rootLinkLinearVel = rootLink.WorldLinearVelocity(*ecm); + auto childLinkLinearVel = childLink.WorldLinearVelocity(*ecm); + ASSERT_NE(std::nullopt, rootLinkLinearVel); + ASSERT_NE(std::nullopt, childLinkLinearVel); + EXPECT_EQ(vel0, rootLinkLinearVel); + EXPECT_EQ(vel0, childLinkLinearVel); + } + // expect that reset component has been removed + EXPECT_EQ(nullptr, + ecm->Component(rootLinkEntity)); + + // Reset root link velocity to zero + ecm->CreateComponent(rootLinkEntity, + components::WorldLinearVelocityReset(math::Vector3d::Zero)); + + // Step and expect all links to have zero velocity + server.Run(true, 1, false); + { + auto rootLinkLinearVel = rootLink.WorldLinearVelocity(*ecm); + auto childLinkLinearVel = childLink.WorldLinearVelocity(*ecm); + ASSERT_NE(std::nullopt, rootLinkLinearVel); + ASSERT_NE(std::nullopt, childLinkLinearVel); + // TODO(scpeters) figure out why this isn't working + // EXPECT_EQ(math::Vector3d::Zero, rootLinkLinearVel); + // EXPECT_EQ(math::Vector3d::Zero, childLinkLinearVel); + } + // expect that reset component has been removed + EXPECT_EQ(nullptr, + ecm->Component(rootLinkEntity)); +} + +///////////////////////////////////////////////// +/// Test angular veocity reset component +TEST_F(PhysicsSystemFixture, + GZ_UTILS_TEST_DISABLED_ON_WIN32(AngularVelocityResetComponent)) +{ + ServerConfig serverConfig; + + const auto sdfFile = std::string(PROJECT_SOURCE_PATH) + + "/test/worlds/diff_drive.sdf"; + + sdf::Root root; + root.Load(sdfFile); + sdf::World *world = root.WorldByIndex(0); + ASSERT_TRUE(nullptr != world); + + // Disable gravity + world->SetGravity(math::Vector3d::Zero); + + serverConfig.SetSdfRoot(root); + + Server server(serverConfig); + + server.SetUpdatePeriod(1ms); + + // Create a system just to get the ECM + EntityComponentManager *ecm{nullptr}; + test::Relay testSystem; + testSystem.OnPreUpdate([&](const UpdateInfo &, + EntityComponentManager &_ecm) + { + ecm = &_ecm; + }); + server.AddSystem(testSystem.systemPtr); + + // Run server and check we have the ECM + EXPECT_EQ(nullptr, ecm); + server.Run(true, 1, false); + EXPECT_NE(nullptr, ecm); + + // Get model Entity's + const std::string modelName("vehicle"); + auto modelEntity = ecm->EntityByComponents( + components::Model(), components::Name(modelName)); + Model model(modelEntity); + ASSERT_TRUE(model.Valid(*ecm)); + + // Get Link Entity's and objects + const std::string rootLinkName{"chassis"}; + auto rootLinkEntity = ecm->EntityByComponents( + components::Link(), components::Name(rootLinkName)); + Link rootLink(rootLinkEntity); + ASSERT_TRUE(rootLink.Valid(*ecm)); + + const std::string childLinkName{"caster"}; + auto childLinkEntity = ecm->EntityByComponents( + components::Link(), components::Name(childLinkName)); + Link childLink(childLinkEntity); + ASSERT_TRUE(childLink.Valid(*ecm)); + + // Enable velocity checks for each link + rootLink.EnableVelocityChecks(*ecm, true); + childLink.EnableVelocityChecks(*ecm, true); + EXPECT_NE(nullptr, + ecm->Component(rootLinkEntity)); + EXPECT_NE(nullptr, + ecm->Component(childLinkEntity)); + + // Step and expect velocities near zero + server.Run(true, 1, false); + { + auto rootLinkAngularVel = rootLink.WorldAngularVelocity(*ecm); + auto childLinkAngularVel = childLink.WorldAngularVelocity(*ecm); + ASSERT_NE(std::nullopt, rootLinkAngularVel); + ASSERT_NE(std::nullopt, childLinkAngularVel); + EXPECT_EQ(math::Vector3d::Zero, rootLinkAngularVel); + EXPECT_EQ(math::Vector3d::Zero, childLinkAngularVel); + } + + const math::Pose3d pose0(0, 0, 1, 0, 0, 0); + // To avoid collision with ground plane while rotating + model.SetWorldPoseCmd(*ecm, pose0); + server.Run(true, 1, false); + + const math::Vector3d vel0(0.1, 5.0, 0.1); + + // Reset child link velocity to nonzero components + // This shouldn't do anything, since it is not the root link of its FreeGroup + ecm->CreateComponent(childLinkEntity, + components::WorldAngularVelocityReset(vel0)); + + // Step and expect that all velocities are still zero + server.Run(true, 1, false); + { + auto rootLinkAngularVel = rootLink.WorldAngularVelocity(*ecm); + auto childLinkAngularVel = childLink.WorldAngularVelocity(*ecm); + ASSERT_NE(std::nullopt, rootLinkAngularVel); + ASSERT_NE(std::nullopt, childLinkAngularVel); + EXPECT_EQ(math::Vector3d::Zero, rootLinkAngularVel); + EXPECT_EQ(math::Vector3d::Zero, childLinkAngularVel); + } + + // Reset root link velocity to nonzero components + ecm->CreateComponent(rootLinkEntity, + components::WorldAngularVelocityReset(vel0)); + + // Step and expect all links to have similar velocity + server.Run(true, 1, false); + { + auto rootLinkAngularVel = rootLink.WorldAngularVelocity(*ecm); + auto childLinkAngularVel = childLink.WorldAngularVelocity(*ecm); + ASSERT_NE(std::nullopt, rootLinkAngularVel); + ASSERT_NE(std::nullopt, childLinkAngularVel); + EXPECT_EQ(vel0, rootLinkAngularVel); + EXPECT_EQ(vel0, childLinkAngularVel); + } + // expect that reset component has been removed + EXPECT_EQ(nullptr, + ecm->Component(rootLinkEntity)); + + // Reset root link velocity to zero + ecm->CreateComponent(rootLinkEntity, + components::WorldAngularVelocityReset(math::Vector3d::Zero)); + + // Step and expect all links to have zero velocity + server.Run(true, 1, false); + { + auto rootLinkAngularVel = rootLink.WorldAngularVelocity(*ecm); + auto childLinkAngularVel = childLink.WorldAngularVelocity(*ecm); + ASSERT_NE(std::nullopt, rootLinkAngularVel); + ASSERT_NE(std::nullopt, childLinkAngularVel); + // TODO(scpeters) figure out why this isn't working + // EXPECT_EQ(math::Vector3d::Zero, rootLinkLinearVel); + // EXPECT_EQ(math::Vector3d::Zero, childLinkLinearVel); + } + // expect that reset component has been removed + EXPECT_EQ(nullptr, + ecm->Component(rootLinkEntity)); +} + ///////////////////////////////////////////////// /// Test joint veocity reset component TEST_F(PhysicsSystemFixture, @@ -1834,12 +2102,13 @@ TEST_F(PhysicsSystemFixture, PhysicsOptions) serverConfig.SetSdfFile(common::joinPaths(std::string(PROJECT_SOURCE_PATH), "test", "worlds", "physics_options.sdf")); - bool checked{false}; + bool checkedDart{false}; + bool checkedBullet{false}; // Create a system to check components test::Relay testSystem; testSystem.OnPostUpdate( - [&checked](const sim::UpdateInfo &, + [&checkedDart, &checkedBullet](const sim::UpdateInfo &, const sim::EntityComponentManager &_ecm) { _ecm.EachData()); } - checked = true; + checkedDart = true; + return true; + }); + _ecm.Each( + [&](const gz::sim::Entity &, const components::World *, + const components::PhysicsSolverIterations *_solverIters)->bool + { + EXPECT_NE(nullptr, _solverIters); + if (_solverIters) + { + EXPECT_EQ(100, _solverIters->Data()); + } + checkedBullet = true; return true; }); + }); sim::Server server(serverConfig); server.AddSystem(testSystem.systemPtr); server.Run(true, 1, false); - EXPECT_TRUE(checked); + EXPECT_TRUE(checkedDart); + EXPECT_TRUE(checkedBullet); } ///////////////////////////////////////////////// diff --git a/test/integration/reset_sensors.cc b/test/integration/reset_sensors.cc index 40544b92c4..be9c3b6a38 100644 --- a/test/integration/reset_sensors.cc +++ b/test/integration/reset_sensors.cc @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -140,10 +141,19 @@ common::Image toImage(const msgs::Image &_msg) } ///////////////////////////////////////////////// -/// This test checks that that the sensors system handles cases where entities -/// are removed and then added back +/// This test checks that that air-pressure and camera sensor systems +/// handle Reset events TEST_F(ResetFixture, GZ_UTILS_TEST_DISABLED_ON_MAC(HandleReset)) { + // This test fails on Github Actions. Skip it for now. + // Note: The GITHUB_ACTIONS environment variable is automatically set when + // running on Github Actions. + std::string githubAction; + if (common::env("GITHUB_ACTIONS", githubAction)) + { + GTEST_SKIP(); + } + gz::sim::ServerConfig serverConfig; const std::string sdfFile = common::joinPaths(PROJECT_SOURCE_PATH, @@ -327,6 +337,7 @@ TEST_F(ResetFixture, GZ_UTILS_TEST_DISABLED_ON_MAC(HandleReset)) imageReceiver.msgReceived = false; server.Run(true, 2000 - server.IterationCount().value(), false); + std::this_thread::sleep_for(20ms); // Check iterator state EXPECT_EQ(2000u, server.IterationCount().value()); diff --git a/test/integration/scene_broadcaster_system.cc b/test/integration/scene_broadcaster_system.cc index 5c0185d5c9..41508e9466 100644 --- a/test/integration/scene_broadcaster_system.cc +++ b/test/integration/scene_broadcaster_system.cc @@ -1091,9 +1091,7 @@ TEST_P(SceneBroadcasterTest, EXPECT_DOUBLE_EQ(0.5, projector.fov()); EXPECT_NE(std::string::npos, projector.texture().find("path/to/dummy_image.png")); - auto header = projector.header().data(0); - EXPECT_EQ("visibility_flags", header.key()); - EXPECT_EQ(0x01, std::stoul(header.value(0))); + EXPECT_EQ(0x01, projector.visibility_flags()); } } diff --git a/test/integration/sensors_system.cc b/test/integration/sensors_system.cc index 84e95e94f6..a116d54cf7 100644 --- a/test/integration/sensors_system.cc +++ b/test/integration/sensors_system.cc @@ -183,7 +183,7 @@ void testDefaultTopics(const std::vector &_topics) ///////////////////////////////////////////////// /// This test checks that that the sensors system handles cases where entities /// are removed and then added back -TEST_F(SensorsFixture, GZ_UTILS_TEST_DISABLED_ON_MAC(HandleRemovedEntities)) +TEST_F(SensorsFixture, HandleRemovedEntities) { gz::sim::ServerConfig serverConfig; diff --git a/test/integration/sensors_system_battery.cc b/test/integration/sensors_system_battery.cc index 4eb35ec92f..49bfd0cd0b 100644 --- a/test/integration/sensors_system_battery.cc +++ b/test/integration/sensors_system_battery.cc @@ -83,7 +83,7 @@ class SensorsFixture : public InternalFixture> ///////////////////////////////////////////////// // Battery -TEST_F(SensorsFixture, GZ_UTILS_TEST_DISABLED_ON_MAC(SensorsBatteryState)) +TEST_F(SensorsFixture, SensorsBatteryState) { const auto sdfPath = common::joinPaths(std::string(PROJECT_SOURCE_PATH), "test", "worlds", "sensors_system_battery.sdf"); diff --git a/test/integration/sensors_system_update_rate.cc b/test/integration/sensors_system_update_rate.cc index 9df4de1d86..55d9130b13 100644 --- a/test/integration/sensors_system_update_rate.cc +++ b/test/integration/sensors_system_update_rate.cc @@ -76,7 +76,7 @@ class SensorsFixture : public InternalFixture> }; ///////////////////////////////////////////////// -TEST_F(SensorsFixture, GZ_UTILS_TEST_DISABLED_ON_MAC(UpdateRate)) +TEST_F(SensorsFixture, UpdateRate) { gz::sim::ServerConfig serverConfig; diff --git a/test/integration/shader_param_system.cc b/test/integration/shader_param_system.cc index 7f5307a3b9..ccec7a3bd5 100644 --- a/test/integration/shader_param_system.cc +++ b/test/integration/shader_param_system.cc @@ -62,6 +62,15 @@ void imageCb(const msgs::Image &_msg) // custom material shaders TEST_F(ShaderParamTest, GZ_UTILS_TEST_DISABLED_ON_MAC(ShaderParam)) { + // This test fails on Github Actions. Skip it for now. + // Note: The GITHUB_ACTIONS environment variable is automatically set when + // running on Github Actions. + std::string githubAction; + if (common::env("GITHUB_ACTIONS", githubAction)) + { + GTEST_SKIP(); + } + // Start server ServerConfig serverConfig; const auto sdfFile = common::joinPaths(std::string(PROJECT_SOURCE_PATH), diff --git a/test/integration/spacecraft.cc b/test/integration/spacecraft.cc new file mode 100644 index 0000000000..1af76f87a0 --- /dev/null +++ b/test/integration/spacecraft.cc @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2024 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include "gz/sim/components/AngularVelocity.hh" +#include "gz/sim/components/Joint.hh" +#include "gz/sim/components/JointVelocity.hh" +#include "gz/sim/components/LinearVelocity.hh" +#include "gz/sim/components/Link.hh" +#include "gz/sim/components/Name.hh" +#include "gz/sim/components/Model.hh" +#include "gz/sim/components/Pose.hh" + +#include "gz/sim/Model.hh" +#include "gz/sim/Server.hh" +#include "gz/sim/SystemLoader.hh" +#include "test_config.hh" + +#include "../helpers/Relay.hh" +#include "../helpers/EnvTestFixture.hh" + +using namespace gz; +using namespace sim; +using namespace std::chrono_literals; + +class SpacecraftTest : public InternalFixture<::testing::Test> +{ + protected: std::unique_ptr StartServer(const std::string &_filePath) + { + ServerConfig serverConfig; + const auto sdfFile = std::string(PROJECT_SOURCE_PATH) + _filePath; + serverConfig.SetSdfFile(sdfFile); + + auto server = std::make_unique(serverConfig); + EXPECT_FALSE(server->Running()); + EXPECT_FALSE(*server->Running(0)); + + using namespace std::chrono_literals; + server->SetUpdatePeriod(1ns); + return server; + } +}; + +///////////////////////////////////////////////// +// Test that a thruster duty cycle command is applied +TEST_F(SpacecraftTest, GZ_UTILS_TEST_DISABLED_ON_WIN32(InputTest)) +{ + // Start server + auto server = this->StartServer("/examples/worlds/spacecraft.sdf"); + + test::Relay testSystem; + transport::Node node; + auto cmdDutyCyclePublisher = + node.Advertise("/dart/command/duty_cycle"); + + const std::size_t iterTestStart{100}; + const std::size_t nIters{500}; + std::vector poses; + + testSystem.OnPostUpdate( + [&](const UpdateInfo &_info, + const EntityComponentManager &_ecm) + { + // Command a thruster duty cycle + const double cmdDutyCycle{0}; + if (_info.iterations == iterTestStart) + { + msgs::Actuators msg; + msg.mutable_normalized()->Resize(12, cmdDutyCycle); + msg.mutable_normalized()->Set(0, 1.0); + cmdDutyCyclePublisher.Publish(msg); + } + else + { + auto id = _ecm.EntityByComponents( + components::Model(), + components::Name("dart")); + EXPECT_NE(kNullEntity, id); + + auto poseComp = _ecm.Component(id); + ASSERT_NE(nullptr, poseComp); + // Collect poses + poses.push_back(poseComp->Data()); + } + }); + + server->AddSystem(testSystem.systemPtr); + server->Run(true, iterTestStart + nIters, false); + + // Check for movement in the right direction + math::Pose3d lastPose = poses.back(); + EXPECT_GT(lastPose.Pos().Y(), 0.0); +} diff --git a/test/integration/thermal_sensor_system.cc b/test/integration/thermal_sensor_system.cc index 16dfde6f6e..9ad00dcdaf 100644 --- a/test/integration/thermal_sensor_system.cc +++ b/test/integration/thermal_sensor_system.cc @@ -74,7 +74,7 @@ void thermalCb(const msgs::Image &_msg) ///////////////////////////////////////////////// TEST_F(ThermalSensorTest, - GZ_UTILS_TEST_DISABLED_ON_MAC(ThermalSensorSystemInvalidConfig)) + ThermalSensorSystemInvalidConfig) { // Start server ServerConfig serverConfig; diff --git a/test/integration/thermal_system.cc b/test/integration/thermal_system.cc index 1e9155514c..bc5e81692f 100644 --- a/test/integration/thermal_system.cc +++ b/test/integration/thermal_system.cc @@ -49,7 +49,7 @@ class ThermalTest : public InternalFixture<::testing::Test> }; ///////////////////////////////////////////////// -TEST_F(ThermalTest, GZ_UTILS_TEST_DISABLED_ON_MAC(TemperatureComponent)) +TEST_F(ThermalTest, TemperatureComponent) { // Start server ServerConfig serverConfig; @@ -173,7 +173,7 @@ TEST_F(ThermalTest, GZ_UTILS_TEST_DISABLED_ON_MAC(TemperatureComponent)) } ///////////////////////////////////////////////// -TEST_F(ThermalTest, GZ_UTILS_TEST_DISABLED_ON_MAC(ThermalSensorSystem)) +TEST_F(ThermalTest, ThermalSensorSystem) { // Start server ServerConfig serverConfig; diff --git a/test/integration/wide_angle_camera.cc b/test/integration/wide_angle_camera.cc index d2857bdccd..930b065e70 100644 --- a/test/integration/wide_angle_camera.cc +++ b/test/integration/wide_angle_camera.cc @@ -61,8 +61,17 @@ void imageCb(const msgs::Image &_msg) ///////////////////////////////////////////////// // The test checks the Wide Angle Camera readings -TEST_F(WideAngleCameraTest, GZ_UTILS_TEST_DISABLED_ON_MAC(WideAngleCameraBox)) +TEST_F(WideAngleCameraTest, WideAngleCameraBox) { + // This test fails on Github Actions. Skip it for now. + // Note: The GITHUB_ACTIONS environment variable is automatically set when + // running on Github Actions. + std::string githubAction; + if (common::env("GITHUB_ACTIONS", githubAction)) + { + GTEST_SKIP(); + } + // Start server ServerConfig serverConfig; const auto sdfFile = common::joinPaths(std::string(PROJECT_SOURCE_PATH), diff --git a/test/media/levels_log/state.tlog b/test/media/levels_log/state.tlog index 9408ea34b8..b9d7e513df 100644 Binary files a/test/media/levels_log/state.tlog and b/test/media/levels_log/state.tlog differ diff --git a/test/test_config.hh.in b/test/test_config.hh.in index 4681208058..89118966c9 100644 --- a/test/test_config.hh.in +++ b/test/test_config.hh.in @@ -23,7 +23,6 @@ #define PROJECT_SOURCE_PATH "${PROJECT_SOURCE_DIR}" #define PROJECT_BINARY_PATH "${CMAKE_BINARY_DIR}" -#define CMAKE_VERSION "${CMAKE_VERSION}" #define GZ_CONFIG_PATH "@CMAKE_BINARY_DIR@/test/conf" @@ -49,6 +48,9 @@ struct TestWorldSansPhysics static std::string world = std::string("" "" "" + "" + " false" + "" "" diff --git a/test/worlds/README.md b/test/worlds/README.md new file mode 100644 index 0000000000..faaa619e1d --- /dev/null +++ b/test/worlds/README.md @@ -0,0 +1,9 @@ +# Integration Testing Files + +## Purpose + +This folder contains world files specifically designed for purpose of integration testing of system plugins. **They are strictly for testing and not meant to be run as examples.** + +## Running system plugin examples + +The world files are present in the `examples/worlds` directory of this repository. diff --git a/test/worlds/air_pressure.sdf b/test/worlds/air_pressure.sdf index 0679f9e280..d66cb0fccd 100644 --- a/test/worlds/air_pressure.sdf +++ b/test/worlds/air_pressure.sdf @@ -5,6 +5,9 @@ 0 + + false + diff --git a/test/worlds/air_speed.sdf b/test/worlds/air_speed.sdf index feb3347655..b23d7ee6d5 100644 --- a/test/worlds/air_speed.sdf +++ b/test/worlds/air_speed.sdf @@ -5,6 +5,9 @@ 0 + + false + diff --git a/test/worlds/classic_material.sdf b/test/worlds/classic_material.sdf new file mode 100644 index 0000000000..3cf61e6109 --- /dev/null +++ b/test/worlds/classic_material.sdf @@ -0,0 +1,32 @@ + + + + + + + + + + 1 1 1 + + + + + + + + 1 1 1 + + + + + + + + + + + diff --git a/test/worlds/force_torque.sdf b/test/worlds/force_torque.sdf index 66c7dd9e87..0c90ff63bb 100644 --- a/test/worlds/force_torque.sdf +++ b/test/worlds/force_torque.sdf @@ -5,7 +5,7 @@ 0 - + true diff --git a/test/worlds/joint_trajectory_controller.sdf b/test/worlds/joint_trajectory_controller.sdf index e761ab49b2..78ae047a68 100644 --- a/test/worlds/joint_trajectory_controller.sdf +++ b/test/worlds/joint_trajectory_controller.sdf @@ -324,10 +324,10 @@ RR_velocity_control_link1 1 0 0 + + 0.02 + - - 0.02 - 0 0 0.1 0 0 0 @@ -335,10 +335,10 @@ RR_velocity_control_link2 1 0 0 + + 0.01 + - - 0.01 - + + + + + 0.001 + + 0 + + + 0 0 0 + + + + + + 0 0 30 0 0 0 + + 0 0 0 0 0 0 + + 5.815677050417254 + + 0.0069861272 + 0 + 0 + 0.1292433532 + 0 + 0.1292433532 + + + + 0.0256621271421697 + 0.0 + 0.0 + 0.0 + 0.0 + 0.0 + + 0.538639694339695 + 0.0 + 0.0 + 0.0 + 0.189291512412098 + + 0.538639694339695 + 0.0 + -0.189291512412098 + 0.0 + + 0.000160094457776136 + 0.0 + 0.0 + + 2.02957381640185 + 0.0 + + 2.02957381640185 + + + + + + 0 0 0 0 1.5707963267948966 0 + + + 3 + 0.75 + + + + + 0 0 0 0 1.5707963267948966 0 + + + 3 + 0.75 + + + + + + + hull_link + 1.2754 + -0.06425096870274437 + 0.07237374316691345 + 0.08506449448628849 + -0.0941254292661463 + 0.1 + 2.4389047 + + + + + 0 5 30 0 0 0 + + 0 0 0 0 0 0 + + 5.815677050417254 + + 0.0069861272 + 0 + 0 + 0.1292433532 + 0 + 0.1292433532 + + + + + 0 0 0 0 1.5707963267948966 0 + + + 3 + 0.75 + + + + + 0 0 0 0 1.5707963267948966 0 + + + 3 + 0.75 + + + + + + + + diff --git a/test/worlds/physics_options.sdf b/test/worlds/physics_options.sdf index 3659636878..1d68175219 100644 --- a/test/worlds/physics_options.sdf +++ b/test/worlds/physics_options.sdf @@ -8,6 +8,11 @@ pgs + + + 100 + + 0 - 0 0 0 1.57 0 0 0 + 0 0 0 1.57 0 0 0.2 @@ -136,7 +136,7 @@ - 0 0 0 1.57 0 0 0 + 0 0 0 1.57 0 0 0.2 @@ -181,7 +181,7 @@ - 0 0 0 1.57 0 0 0 + 0 0 0 1.57 0 0 0.2 @@ -226,7 +226,7 @@ - 0 0 0 1.57 0 0 0 + 0 0 0 1.57 0 0 0.2 diff --git a/test/worlds/quadcopter_velocity_control.sdf b/test/worlds/quadcopter_velocity_control.sdf index 09390c0274..1650b5852c 100644 --- a/test/worlds/quadcopter_velocity_control.sdf +++ b/test/worlds/quadcopter_velocity_control.sdf @@ -91,7 +91,7 @@ - 0 0 0 1.57 0 0 0 + 0 0 0 1.57 0 0 0.2 @@ -136,7 +136,7 @@ - 0 0 0 1.57 0 0 0 + 0 0 0 1.57 0 0 0.2 @@ -181,7 +181,7 @@ - 0 0 0 1.57 0 0 0 + 0 0 0 1.57 0 0 0.2 @@ -226,7 +226,7 @@ - 0 0 0 1.57 0 0 0 + 0 0 0 1.57 0 0 0.2 diff --git a/test/worlds/reset_sensors.sdf b/test/worlds/reset_sensors.sdf index 733658fefc..7f8db14a7e 100644 --- a/test/worlds/reset_sensors.sdf +++ b/test/worlds/reset_sensors.sdf @@ -1,6 +1,9 @@ + + false + diff --git a/tutorials.md.in b/tutorials.md.in index 46c3f4be71..b6f895ec5d 100644 --- a/tutorials.md.in +++ b/tutorials.md.in @@ -4,72 +4,83 @@ Welcome to the Gazebo @GZ_DESIGNATION_CAP@ tutorials. These tutorials will guide you through the process of understanding the capabilities of the Gazebo @GZ_DESIGNATION_CAP@ library and how to use the library effectively. -## Getting Started +## Tutorials + +The Tutorials section contains step-by-step instructions with self-contained examples to help you get started using Gazebo. + +### Beginner * \subpage install "Installation": Install instructions. -* \subpage terminology "Terminology": List of terms used across the documentation. -* \subpage gui_config "GUI configuration": Customizing your layout. -* \subpage server_config "Server configuration": Customizing what system plugins are loaded. +* \subpage move_camera_to_model Move camera to model +* \subpage apply_force_torque "Apply Force and Torque": Apply forces and/or torques to models during simulation through the GUI. +* \subpage mouse_drag "Mouse Drag": Move models by dragging them in the scene using forces and torques. * \subpage model_command "Model Command": Use the CLI to get information about the models in a simulation. +* \subpage pause_run_simulation "Pause and Run simulation": Use Gazebo transport API to pause and run simulation. * \subpage reset_simulation Reset simulation -* \subpage resources "Finding resources": The different ways in which Gazebo looks for files. -* \subpage debugging "Debugging": Information about debugging Gazebo. +* \subpage headless_rendering "Headless rendering": Access the GPU on a remote machine to produce sensor data without an X server. -### GUI and rendering features +### Intermediate -* \subpage move_camera_to_model Move camera to model -* \subpage model_photo_shoot "Model Photo Shoot" Taking perspective, top, front, and side pictures of a model. -* \subpage videorecorder "Video Recorder": Record videos from the 3D render window. -* \subpage headless_rendering "Headless rendering": Access the GPU on a remote machine to produce sensor data without an X server. -* \subpage apply_force_torque "Apply Force and Torque": Applying forces and/or torques to models during simulation through the GUI. -* \subpage mouse_drag "Mouse Drag": Move models by dragging them in the scene using forces and torques. +* \subpage log "Logging": Record and play back time series of world state. +* \subpage levels "Levels": Load entities on demand in large environments. -### Migration from Gazebo classic +### Advanced -* \subpage migrationplugins "Plugins": Walk through the differences between writing plugins for Gazebo classic and Gazebo -* \subpage migrationsdf "SDF": Migrating SDF files from Gazebo classic to Gazebo -* \subpage migrationworldapi "World API": Guide on what World C++ functions to call in Gazebo when migrating from Gazebo classic -* \subpage migrationmodelapi "Model API": Guide on what Model C++ functions to call in Gazebo when migrating from Gazebo classic -* \subpage migrationlightapi "Light API": Guide on what Light C++ functions to call in Gazebo when migrating from Gazebo classic -* \subpage migrationjointapi "Joint API": Guide on what Joint C++ functions to call in Gazebo when migrating from Gazebo classic -* \subpage migrationactorapi "Actor API": Guide on what Actor C++ functions to call in Gazebo when migrating from Gazebo classic -* \subpage migrationlinkapi "Link API": Guide on what Link C++ functions to call in Gazebo when migrating from Gazebo classic -* \subpage migrationsensorapi "Sensor API": Guide on what Sensor C++ functions to call in Gazebo when migrating from Gazebo classic -* \subpage ardupilot "Case Study": Migrating the ArduPilot ModelPlugin from Gazebo classic to Gazebo. +* \subpage erbtemplate "ERB Template": Use ERB, a templating language, to generate SDF files for simulation worlds. -## Intermediate +## How-to Guides -* \subpage log "Logging": Record and play back time series of world state. -* \subpage light_config "Light config": Configure lights in the scene. -* \subpage levels "Levels": Load entities on demand in large environments. -* \subpage python_interfaces Python interfaces +The How-to Guides provide recipes for specific "How to..." questions or things you may want to do. +They assume some experience. +If you are an absolute beginner, start with the Tutorials section. -### Specific systems and features +### Beginner -* \subpage detachablejoints "Detachable Joints": Creating models that start off rigidly attached and then get detached during simulation. -* \subpage triggeredpublisher "Triggered Publisher": Using the TriggeredPublisher system to orchestrate actions in simulation. +* \subpage gui_config "GUI configuration": Customize your layout. +* \subpage server_config "Server configuration": Customize what system plugins are loaded. +* \subpage model_photo_shoot "Model Photo Shoot" Taking perspective, top, front, and side pictures of a model. +* \subpage videorecorder "Video Recorder": Record videos from the 3D render window. + +### Intermediate + +* \subpage light_config "Light config": Configure lights in the scene. +* \subpage entity_creation "Entity creation": Insert models or lights using services. * \subpage battery "Battery": Keep track of battery charge on robot models. -* \subpage particle_emitter "Particle emitter": Using particle emitters in simulation -* \subpage spherical_coordinates "Spherical coordinates": Working with latitude and longitude +* \subpage particle_emitter "Particle emitter": Use particle emitters in simulation +* \subpage detachablejoints "Detachable Joints": Create models that start off rigidly attached and then get detached during simulation. +* \subpage triggeredpublisher "Triggered Publisher": Use the TriggeredPublisher system to orchestrate actions in simulation. +* \subpage spherical_coordinates "Spherical coordinates": Work with latitude and longitude +* \subpage underwater_vehicles "Underwater Vehicles": Understand how to simulate underwater vehicles * \subpage logicalaudiosensor "Logical Audio Sensor": Using the LogicalAudioSensor system to mimic logical audio emission and detection in simulation. * \subpage auto_inertia_calculation "Automatic Inertia Calculation": Automatically compute inertia values(mass, mass matrix, center of mass) for SDFormat links. * \subpage jointcontrollers "Joint Controllers": Using available joint controller plugins to control joints. +* \subpage shadow_texture_size "Shadow texture size": Change the texture size of shadows casted by lights. -## Advanced users +### Advanced -* \subpage physics "Physics engines": Loading different physics engines. -* \subpage entity_creation "Entity creation": Insert models or lights using services. -* \subpage erbtemplate "ERB Template": Use ERB, a templating language, to generate SDF files for simulation worlds. -* \subpage distributedsimulation "Distributed Simulation": Spread simulation across several processes. +* \subpage physics "Physics engines": Load different physics engines. -## Developers +### Developers * \subpage createsystemplugins "Create System Plugins": Programmatically access simulation using C++ plugins. -* \subpage usingcomponents "Using components": Using components in a system plugin. +* \subpage usingcomponents "Using components": Using components in a system plugin.* * \subpage rendering_plugins "Rendering plugins": Write plugins that use Gazebo Rendering on the server and client. * \subpage test_fixture "Test Fixture": Writing automated CI tests -## 3D modeling help +### Migration from Gazebo classic + +* \subpage migrationplugins "Plugins": Walk through the differences between writing plugins for Gazebo classic and Gazebo +* \subpage migrationsdf "SDF": Migrating SDF files from Gazebo classic to Gazebo +* \subpage migrationworldapi "World API": Guide on what World C++ functions to call in Gazebo when migrating from Gazebo classic +* \subpage migrationmodelapi "Model API": Guide on what Model C++ functions to call in Gazebo when migrating from Gazebo classic +* \subpage migrationlightapi "Light API": Guide on what Light C++ functions to call in Gazebo when migrating from Gazebo classic +* \subpage migrationjointapi "Joint API": Guide on what Joint C++ functions to call in Gazebo when migrating from Gazebo classic +* \subpage migrationactorapi "Actor API": Guide on what Actor C++ functions to call in Gazebo when migrating from Gazebo classic +* \subpage migrationlinkapi "Link API": Guide on what Link C++ functions to call in Gazebo when migrating from Gazebo classic +* \subpage migrationsensorapi "Sensor API": Guide on what Sensor C++ functions to call in Gazebo when migrating from Gazebo classic +* \subpage ardupilot "Case Study": Migrating the ArduPilot ModelPlugin from Gazebo classic to Gazebo. + +### 3D modeling help * \subpage collada_world_exporter "Collada World Exporter": Export an entire world to a single Collada mesh. * \subpage meshtofuel "Importing a Mesh to Fuel": Build a model directory around a mesh so it can be added to the Gazebo Fuel app. @@ -82,7 +93,7 @@ Gazebo @GZ_DESIGNATION_CAP@ library and how to use the library effectively. * \subpage blender_distort_meshes "Blender mesh distortion": Use a Blender Python script to programmatically deform and distort meshes to customized extents. * \subpage blender_procedural_datasets "Generation of Procedural Datasets with Blender": Use Blender with a Python script to generate procedural datasets of SDF models. -## Maritime +### Maritime * \subpage theory_buoyancy "Buoyancy: " Describe the theory of operation of the buoyancy plugin. @@ -102,6 +113,22 @@ underwater robot. * \subpage surface_vehicles "Create a surface vehicle:" How to create a maritime surface robot. + +## Reference + +The reference section contains information that may come in handy for lookup. + +* \subpage resources "Finding resources": The different ways in which Gazebo looks for files. +* \subpage python_interfaces Python interfaces +* \subpage debugging "Debugging": Information about debugging Gazebo. + +## Concepts + +The concepts section explains things that require additional understanding than the scope of the Tutorials and How-to Guides. + +* \subpage terminology "Terminology": List of terms used across the documentation. +* \subpage distributedsimulation "Distributed Simulation": Spread simulation across several processes. + ## License The code associated with this documentation is licensed under an [Apache 2.0 License](https://www.apache.org/licenses/LICENSE-2.0). diff --git a/tutorials/adding_system_plugins.md b/tutorials/adding_system_plugins.md index 0df852a22d..8cfa596b63 100644 --- a/tutorials/adding_system_plugins.md +++ b/tutorials/adding_system_plugins.md @@ -1,4 +1,4 @@ -\page adding_system_plugins +\page adding_system_plugins Adding system plugins # Overview @@ -10,15 +10,15 @@ capabilities to it. Make sure to go through the following tutorial first, where you'll learn how to create the vehicle used in this tutorial. -https://gazebosim.org/api/sim/8/create_vehicle.html +\ref create_vehicle ## Related tutorials -https://gazebosim.org/api/sim/8/createsystemplugins.html +\ref createsystemplugins # Adding a system plugin -[This Gazebo tutorial](https://gazebosim.org/api/sim/8/createsystemplugins.html) +[This Gazebo tutorial](https://gazebosim.org/api/sim/9/createsystemplugins.html) describes what is a system plugin in depth. Intuitively, you can envision a system plugin as a piece of code that modifies the behavior of the simulation when the general physics engine does not exactly capture your needs. @@ -46,7 +46,7 @@ your turtle does not sink anymore. ```bash mkdir -p ~/gazebo_maritime/worlds -wget https://raw.githubusercontent.com/gazebosim/gz-sim/gz-sim8/tutorials/files/adding_system_plugins/buoyant_turtle.sdf -O ~/gazebo_maritime/worlds/buoyant_turtle.sdf +wget https://raw.githubusercontent.com/gazebosim/gz-sim/gz-sim9/tutorials/files/adding_system_plugins/buoyant_turtle.sdf -O ~/gazebo_maritime/worlds/buoyant_turtle.sdf export GZ_SIM_RESOURCE_PATH=:$HOME/gazebo_maritime/models gz sim -r ~/gazebo_maritime/worlds/buoyant_turtle.sdf ``` @@ -130,7 +130,7 @@ And run Gazebo: gz sim -r ~/gazebo_maritime/worlds/buoyant_turtle.sdf ``` -Now, when our simple trajectory controller reaches its target and stops appling +Now, when our simple trajectory controller reaches its target and stops applying force, the turtle stops moving acting like the fluid decelerates its motion. Additionally you can notice how the up and down oscillations are also damped by the effect of the hydrodynamics. diff --git a/tutorials/adding_visuals.md b/tutorials/adding_visuals.md index 7df40c70b2..d3f6f7a8a9 100644 --- a/tutorials/adding_visuals.md +++ b/tutorials/adding_visuals.md @@ -1,4 +1,4 @@ -\page adding_visuals +\page adding_visuals Adding visuals # Overview @@ -8,7 +8,7 @@ mesh to our turtle, making it look much better. ## Related tutorials -https://gazebosim.org/api/sim/8/meshtofuel.html +\ref meshtofuel The next tutorials, although still relevant, are from an older version of Gazebo and some details might be different than the current versions: diff --git a/tutorials/blender_distort_meshes.md b/tutorials/blender_distort_meshes.md index 5f619be97f..3db2f88722 100644 --- a/tutorials/blender_distort_meshes.md +++ b/tutorials/blender_distort_meshes.md @@ -25,7 +25,7 @@ If newer versions do not work for you, Blender 2.92 can be found ## Usage Locate or download the Blender Python -[script](https://github.com/gazebosim/gz-sim/blob/gz-sim8/examples/scripts/blender/distort_mesh.py). +[script](https://github.com/gazebosim/gz-sim/blob/main/examples/scripts/blender/distort_mesh.py). Launch the Blender GUI. diff --git a/tutorials/blender_sdf_exporter.md b/tutorials/blender_sdf_exporter.md index 604ea41d0c..e1b193c7cf 100644 --- a/tutorials/blender_sdf_exporter.md +++ b/tutorials/blender_sdf_exporter.md @@ -12,7 +12,7 @@ mesh/materials/lights feature set. As such feel free to customize the script as ## Using the Blender SDF Exporter -1. Download the blender script in [sdf_exporter.py](https://github.com/gazebosim/gz-sim/tree/gz-sim8/examples/scripts/blender/sdf_exporter.py). +1. Download the blender script in [sdf_exporter.py](https://github.com/gazebosim/gz-sim/tree/main/examples/scripts/blender/sdf_exporter.py). 2. Open the script under Blender's Scripting tab and run it. diff --git a/tutorials/component_pose.md b/tutorials/component_pose.md new file mode 100644 index 0000000000..34d440e12e --- /dev/null +++ b/tutorials/component_pose.md @@ -0,0 +1,80 @@ +\page posecomponent Case Study: Using the Pose Component + +We will show how to use the gz::sim::components::Pose component in a system. + +An example usage of the component can be found in the +gz::sim::systems::OdometryPublisher system +([source code](https://github.com/gazebosim/gz-sim/tree/gz-sim8/src/systems/odometry_publisher)), +which reads the pose component of a model through the Model entity, uses the +pose for some calculations, and then publishes the result as a message. + +More usage can be found in the +[integration test](https://github.com/gazebosim/gz-sim/blob/gz-sim8/test/integration/odometry_publisher.cc) +for the system, with test worlds `odometry*.sdf` +[here](https://github.com/gazebosim/gz-sim/tree/main/test/worlds). + +### Objects of interest + +- gz::sim::components::Pose: A component containing pose information +- gz::math::Pose3d: The actual data underlying a pose component +- gz::sim::systems::OdometryPublisher: A system that reads the pose component + of a model +- gz::sim::Model: The type underlying a model entity (gz::sim::Entity) + +### Find the model entity + +First, we will need access to an entity, the \ref gz::sim::Model entity in this +case. +`OdometryPublisher` happens to be a system meant to be specified under `` +in the SDF, so at the time `Configure()` is called, it has access to a model +entity from which we can extract a \ref gz::sim::Model: + +\snippet src/systems/odometry_publisher/OdometryPublisher.cc modelDeclaration +\snippet src/systems/odometry_publisher/OdometryPublisher.cc Configure + +### Read the pose component + +Once we have the handle to an entity, we can access components associated with +it. +A component may have been created at the time the world is loaded, or you may +create a component at runtime if it does not exist yet. + +In this case, we use the model entity found above to access its pose component, +which is created by default on every model entity. + +In `PostUpdate()`, which happens after physics has updated, we can get the +world pose of a model through gz::sim::worldPose, by passing in the model +entity and the entity component manager. + +\snippet src/systems/odometry_publisher/OdometryPublisher.cc worldPose + +It returns the raw data to us in the gz::math::Pose3d type, which is also the +data type underlying a pose component. +We can perform calculations on the gz::math::Pose3d type, not the +gz::sim::components::Pose type, which is just a wrapper. + +### Use the pose component + +Now we can use the pose data as we like. + +Here, we manipulate the pose and package the result into a gz::msgs::Odometry +message to be published: + +\snippet src/systems/odometry_publisher/OdometryPublisher.cc declarePoseMsg + +\snippet src/systems/odometry_publisher/OdometryPublisher.cc setPoseMsg + +See the source code for setting other fields of the message, such as twist and +the header. + +The message is then published: + +\snippet src/systems/odometry_publisher/OdometryPublisher.cc publishMsg + +where `odomPub` is defined in `Configure()`: + +\snippet src/systems/odometry_publisher/OdometryPublisher.cc definePub + +Outside the scope of this tutorial, the odometry publisher system also +calculates the covariance and publishes a pose vector on a TF topic. +See the source code to learn more. diff --git a/tutorials/create_system_plugins.md b/tutorials/create_system_plugins.md index dec24591d0..7cc3ac0964 100644 --- a/tutorials/create_system_plugins.md +++ b/tutorials/create_system_plugins.md @@ -10,7 +10,7 @@ following entity types: * Actor To create a system plugin for use in the simulation environment, follow the -steps below. +steps below. The fully working example is available [here](https://github.com/gazebosim/gz-sim/tree/gz-sim9/examples/plugin/system_plugin). ## Decide on interfaces to implement @@ -85,16 +85,15 @@ Implement the system class as usual, for example: In your `CMakeLists.txt` add the following ``` -gz_find_package(gz-plugin2 REQUIRED COMPONENTS register) -set(GZ_PLUGIN_VER ${gz-plugin2_VERSION_MAJOR}) +find_package(gz-plugin3 REQUIRED COMPONENTS register) +set(GZ_PLUGIN_VER ${gz-plugin3_VERSION_MAJOR}) # Add sources for each plugin to be registered. -add_library(SampleSystem SampleSystem.cc SampleSystem2.cc) +add_library(SampleSystem SHARED SampleSystem.cc SampleSystem2.cc) set_property(TARGET SampleSystem PROPERTY CXX_STANDARD 17) target_link_libraries(SampleSystem - gz-common${GZ_COMMON_VER}::gz-common${GZ_COMMON_VER} - gz-plugin${GZ_PLUGIN_VER}::gz-plugin${GZ_PLUGIN_VER} -) + PRIVATE gz-plugin${GZ_PLUGIN_VER}::gz-plugin${GZ_PLUGIN_VER} + PRIVATE gz-sim9::gz-sim9) ``` ## Loading your plugin diff --git a/tutorials/create_vehicle.md b/tutorials/create_vehicle.md index 988fd32ba4..e66002b42e 100644 --- a/tutorials/create_vehicle.md +++ b/tutorials/create_vehicle.md @@ -1,4 +1,4 @@ -\page create_vehicle +\page create_vehicle Create a maritime vehicle # Overview @@ -46,8 +46,8 @@ Create a `model.sdf` file that contains the Simulator Description Format of the model. You can find more information on the [SDF website](http://sdformat.org/). ```xml - - + + true @@ -65,7 +65,7 @@ model. You can find more information on the [SDF website](http://sdformat.org/). - + 1 1 0.009948450858321252 @@ -73,7 +73,7 @@ model. You can find more information on the [SDF website](http://sdformat.org/). - + 0.08 0 0.05 0 0 0 diff --git a/tutorials/entity_creation.md b/tutorials/entity_creation.md index 6772fb1538..8f82e4774a 100644 --- a/tutorials/entity_creation.md +++ b/tutorials/entity_creation.md @@ -55,7 +55,7 @@ gz service --list /world/world_name/state_async ``` -# Factory message +## Factory message To create new entities in the world we need to use the [gz::msgs::EntityFactory](https://gazebosim.org/api/msgs/9/classgz_1_1msgs_1_1EntityFactory.html) @@ -65,7 +65,7 @@ This message allows us to create entities from strings, files, [Lights](https://gazebosim.org/api/msgs/9/classgz_1_1msgs_1_1Light.html) or even clone models. This tutorial introduces how to create entities from SDF strings and light messages. -## Insert an entity based on a string +### Insert an entity based on a string We will open an empty Gazebo world, let's start creating a sphere in the world. In the next snippet you can see how to create models based on strings. @@ -87,7 +87,7 @@ happen. You may see some traces in the console showing this information. There is an option to create a new entity every time that the message is sent by setting `allow_renaming` to true (you can use the method `set_allow_renaming()`). -## Insert a light +### Insert a light To insert a light in the world we have two options: diff --git a/tutorials/files/lander/lander_visuals.png b/tutorials/files/lander/lander_visuals.png index af12dd03a4..51b08228f2 100644 Binary files a/tutorials/files/lander/lander_visuals.png and b/tutorials/files/lander/lander_visuals.png differ diff --git a/tutorials/files/particle_emitter/particle_emitter_scatter_effects.sdf b/tutorials/files/particle_emitter/particle_emitter_scatter_effects.sdf new file mode 100644 index 0000000000..4a1c8e666b --- /dev/null +++ b/tutorials/files/particle_emitter/particle_emitter_scatter_effects.sdf @@ -0,0 +1,309 @@ + + + + + + + 0.001 + 1.0 + + + + ogre2 + + + + + + + + + RGB camera + floating + 350 + 315 + + camera + false + + + + Depth camera + floating + 350 + 315 + 500 + + depth_camera + false + + + + RGBD: image + floating + 350 + 315 + 320 + + rgbd_camera/image + false + + + + RGBD: depth + floating + 350 + 315 + 500 + 320 + + rgbd_camera/depth_image + false + + + + Thermal camera + floating + 350 + 315 + 500 + 640 + + thermal_camera + false + + + + + + + + + true + 0 0 10 0 0 0 + 1 1 1 1 + 0.5 0.5 0.5 1 + + 1000 + 0.9 + 0.01 + 0.001 + + -0.5 0.1 -0.9 + + + + true + + + + + 0 0 1 + + + + + + + 0 0 1 + 100 100 + + + + 0.8 0.8 0.8 1 + 0.8 0.8 0.8 1 + 0.8 0.8 0.8 1 + + + + + + + https://fuel.gazebosim.org/1.0/openrobotics/models/fog generator + + + + 8 0 0.5 0 0.0 3.14 + + 0.05 0.05 0.05 0 0 0 + + 0.1 + + 0.000166667 + 0.000166667 + 0.000166667 + + + + + + 0.1 0.1 0.1 + + + + + + + 0.1 0.1 0.1 + + + + + + + 1.047 + + 320 + 240 + + + 0.1 + 100 + + + 1 + 30 + true + camera + + + + 10 + depth_camera + + 1.05 + + 320 + 240 + R_FLOAT32 + + + 0.1 + 100.0 + + + + + " + lidar + 10 + + + + 640 + 1 + -1.396263 + 1.396263 + + + 1 + 0.01 + 0 + 0 + + + + 0.08 + 10.0 + 0.01 + + + 1 + true + + + + true + + + + 8 0 0.5 0 0.0 3.14 + true + + 0.05 0.05 0.05 0 0 0 + + + + 0.1 0.1 0.1 + + + + + + + 0.1 0.1 0.1 + + + + + + 1.047 + + 320 + 240 + + + 0.1 + 100 + + + 1 + 30 + true + rgbd_camera + + + true + + + + 8 0 0.5 0 0.0 3.14 + true + + 0.05 0.05 0.05 0 0 0 + + + + 0.1 0.1 0.1 + + + + + + + 0.1 0.1 0.1 + + + + + + 1.047 + + 320 + 240 + + + 0.1 + 100 + + + 1 + 30 + true + thermal_camera + + + + + + 0 0 0 0 0 1.570796 + rescue_randy + https://fuel.gazebosim.org/1.0/OpenRobotics/models/Rescue Randy + + + + diff --git a/tutorials/files/pause_run_simlation/gui_pause_run.png b/tutorials/files/pause_run_simlation/gui_pause_run.png new file mode 100644 index 0000000000..49dd539653 Binary files /dev/null and b/tutorials/files/pause_run_simlation/gui_pause_run.png differ diff --git a/tutorials/files/point_cloud_to_mesh/blob2.jpg b/tutorials/files/point_cloud_to_mesh/blob2.jpg new file mode 100644 index 0000000000..b78a854313 Binary files /dev/null and b/tutorials/files/point_cloud_to_mesh/blob2.jpg differ diff --git a/tutorials/files/point_cloud_to_mesh/blob2.png b/tutorials/files/point_cloud_to_mesh/blob2.png deleted file mode 100644 index 47c23cbf9c..0000000000 Binary files a/tutorials/files/point_cloud_to_mesh/blob2.png and /dev/null differ diff --git a/tutorials/files/point_cloud_to_mesh/error.jpg b/tutorials/files/point_cloud_to_mesh/error.jpg new file mode 100644 index 0000000000..f1d57861cd Binary files /dev/null and b/tutorials/files/point_cloud_to_mesh/error.jpg differ diff --git a/tutorials/files/point_cloud_to_mesh/hidden_polygons2.jpg b/tutorials/files/point_cloud_to_mesh/hidden_polygons2.jpg new file mode 100644 index 0000000000..584a98976b Binary files /dev/null and b/tutorials/files/point_cloud_to_mesh/hidden_polygons2.jpg differ diff --git a/tutorials/files/point_cloud_to_mesh/hidden_polygons2.png b/tutorials/files/point_cloud_to_mesh/hidden_polygons2.png deleted file mode 100644 index 6dfa068610..0000000000 Binary files a/tutorials/files/point_cloud_to_mesh/hidden_polygons2.png and /dev/null differ diff --git a/tutorials/files/point_cloud_to_mesh/min_space.jpg b/tutorials/files/point_cloud_to_mesh/min_space.jpg new file mode 100644 index 0000000000..e19615659b Binary files /dev/null and b/tutorials/files/point_cloud_to_mesh/min_space.jpg differ diff --git a/tutorials/files/point_cloud_to_mesh/min_space.png b/tutorials/files/point_cloud_to_mesh/min_space.png deleted file mode 100644 index 183a67b119..0000000000 Binary files a/tutorials/files/point_cloud_to_mesh/min_space.png and /dev/null differ diff --git a/tutorials/files/shadow_texture_size/shadow_texsize_2k_to_16k.gif b/tutorials/files/shadow_texture_size/shadow_texsize_2k_to_16k.gif new file mode 100644 index 0000000000..09cfabbea8 Binary files /dev/null and b/tutorials/files/shadow_texture_size/shadow_texsize_2k_to_16k.gif differ diff --git a/tutorials/files/spacecraft/dart.png b/tutorials/files/spacecraft/dart.png new file mode 100644 index 0000000000..2f109cd5c0 Binary files /dev/null and b/tutorials/files/spacecraft/dart.png differ diff --git a/tutorials/files/spacecraft/kth_spacecraft_simulator.png b/tutorials/files/spacecraft/kth_spacecraft_simulator.png new file mode 100644 index 0000000000..11fa8807d1 Binary files /dev/null and b/tutorials/files/spacecraft/kth_spacecraft_simulator.png differ diff --git a/tutorials/frame_reference.md b/tutorials/frame_reference.md index 3d904fa19e..e720f1242d 100644 --- a/tutorials/frame_reference.md +++ b/tutorials/frame_reference.md @@ -1,4 +1,4 @@ -\page frame_reference +\page frame_reference Frame of reference # Overview @@ -7,7 +7,7 @@ the conventions used in Gazebo. ## Related tutorials -https://gazebosim.org/api/sim/8/spherical_coordinates.html +\ref spherical_coordinates # Gazebo world frame diff --git a/tutorials/gui_config.md b/tutorials/gui_config.md index 9cc42fa1e0..0304f1131a 100644 --- a/tutorials/gui_config.md +++ b/tutorials/gui_config.md @@ -16,10 +16,33 @@ There are a few places where the GUI configuration can come from: 3. The default configuration file at `$HOME/.gz/sim/<#>/gui.config` \*, where `<#>` is Gazebo Sim's major version. -Each of the items above takes precedence over the ones below it. For example, -if a user chooses a `--gui-config`, the SDF's `` element is ignored. And -the default configuration file is only loaded if no configuration is passed -through the command line or the SDF file. +If a configuration file is specified using `--gui-config`, Gazebo will +ignore both the `` element inside the SDF file and the default +configuration file. Otherwise, Gazebo will load plugins by combining +plugins in the `` element and the default configuration file. +How Gazebo combines these plugins is determined by the +`` policy set in ``: + +- `true`: Plugins + from the default configuration file merged with plugins from the SDF file. + Plugins from SDF files take precedence over plugins from + the default configuration file. This means, if a plugin is specified in + both places, by default, only the one specified in the SDF file will be + loaded. If replacement occurs, the replacement + plugin will take the position of the replaced plugin in the order of plugins. + If replacement does not occur, the plugin is appended to the end of the list. + + The main use case for this policy is for users to rely on + the default list of plugins and only add extra plugins they need for the + application. This policy is also useful for overriding the parameters of a small + subset of the default plugins. This is the default setting in + Gazebo Ionic and later. + +- `false`: If + there are any plugins specified in the SDF file, plugins from the default + configuration file are ignored. This allows the user to have complete + control over which plugins are loaded. This is the default setting in + Gazebo Harmonic and earlier. > \* For log-playback, the default file is > `$HOME/.gz/sim/<#>/playback_gui.config` @@ -28,10 +51,9 @@ through the command line or the SDF file. ### Default configuration -Let's try this in practice. First, let's open Gazebo without passing -any arguments: +Let's try this in practice. First, let's open the default Gazebo world: -`gz sim` +`gz sim default.sdf` You should see an empty world with several plugins loaded by default, such as the 3D Scene, the play/pause button, etc. @@ -57,7 +79,7 @@ Let's try customizing it: 3. Reload Gazebo: - `gz sim` + `gz sim default.sdf` Note how the UI is now in dark mode! @@ -75,7 +97,7 @@ will be created with default values: Let's try overriding the default configuration from an SDF file. Open your favorite editor and save this file as `fuel_preview.sdf`: -``` +```xml @@ -84,6 +106,10 @@ favorite editor and save this file as `fuel_preview.sdf`: name="gz::sim::systems::SceneBroadcaster"> + + false + + @@ -137,7 +163,7 @@ Now let's load this world: `gz sim /fuel_preview.sdf` -Notice how the application has only one GUI plugin loaded, the 3D scene, as defined +Notice how the application has only 3 GUI plugins loaded, as defined on the SDF file above. @image html files/gui_config/fuel_preview.png @@ -147,6 +173,75 @@ the same model loaded into the default GUI layout. @image html files/gui_config/fuel_preview_no_gui.png +Now, let's change the policy so that default plugins are included + +```xml + + + + + + + + true + + + + + + + 3D View + false + docked + + + ogre2 + scene + 1.0 1.0 1.0 + 0.4 0.6 1.0 + 8.3 7 7.8 0 0.5 -2.4 + + + + false + 5 + 5 + floating + false + + + + + false + 5 + 5 + floating + false + + + + + + + https://fuel.gazebosim.org/1.0/OpenRobotics/models/Sun + + + + https://fuel.gazebosim.org/1.0/OpenRobotics/models/Gazebo + + + + +``` + +You will now see the same model loaded in the default GUI layout +similar to when you deleted the `` element altogether. Note that this will also +be the behavior if we removed `` tag. + +@image html files/gui_config/fuel_preview_no_gui.png + ### Command line It's often inconvenient to embed your GUI layout directly into every SDF file. diff --git a/tutorials/headless_rendering.md b/tutorials/headless_rendering.md index 6c01ef756d..787755da47 100644 --- a/tutorials/headless_rendering.md +++ b/tutorials/headless_rendering.md @@ -13,7 +13,7 @@ An easier solution is through the use of [EGL](https://www.khronos.org/egl), whi Example usage: ``` -gz sim -v 4 -s --headless-rendering sensors_demo.sdf +DISPLAY= gz sim -v 4 -s -r --headless-rendering sensors_demo.sdf ``` If you are using Gazebo as a library, then you can configure the @@ -63,14 +63,23 @@ here](https://www.ogre3d.org/2021/02/06/ogre-2-2-5-cerberus-released-and-egl-hea ``` sudo reboot ``` -11. [Install Gazebo](https://gazebosim.org/docs/latest/install). -12. Run a Gazebo world that uses OGRE2 with camera sensors using headless rendering. This will enable EGL. +15. [Install Gazebo](https://gazebosim.org/docs/latest/install). +16. Run a Gazebo world that uses OGRE2 with camera sensors using headless rendering. This will enable EGL. ``` - gz sim -v 4 -s -r --headless-rendering sensors_demo.sdf + DISPLAY= gz sim -v 4 -s -r --headless-rendering sensors_demo.sdf ``` -13. Check that simulation is producing sensor data by ssh'ing into the EC2 - instance from a new terminal and echoing a sensor topic. +17. Check that simulation is producing sensor data by ssh'ing into the EC2 + instance from a new terminal and testing the frequency of a sensor topic. ``` ssh -i SSH_PEM_FILE_USED_DURING_LAUNCH ubuntu@EC_INSTANCE_PUBLIC_IP - gz topic -et /thermal_camera + gz topic -ft /thermal_camera + ``` +18. To verify that the EGL backend was used, try the following command: + ``` + grep -R "EGL Subsystem" ~/.gz/rendering/ogre2.log + ``` + Its output should not be empty, e.g.: + ``` + *** Starting EGL Subsystem *** + *** Stopping EGL Subsystem *** ``` diff --git a/tutorials/install.md b/tutorials/install.md index 2e5bbecea7..7ecadbfd3c 100644 --- a/tutorials/install.md +++ b/tutorials/install.md @@ -1,7 +1,8 @@ \page install Installation -These instructions are for installing only Gazebo. If you're interested -in using all the Gazebo libraries, not only Igniton Gazebo, check out this +These instructions are for installing only Gazebo Sim. If you're interested +in using all the Gazebo libraries including gz-launch, or to install all +libraries from source, check out [Gazebo installation](https://gazebosim.org/docs/latest/install). We recommend following the binary install instructions to get up and running as @@ -50,6 +51,27 @@ installing Gazebo from source. brew install gz-sim<#> ``` +### Windows + +1. First, follow the steps 1-4 of [Gazebo source install](https://gazebosim.org/docs/ionic/install_windows_src/#install-dependencies) + tutorial to install Conda, Visual Studio and set up a Conda environment (we'll call it `gz-ws`). + + Do not forget to activate the Conda env (if not already activated) + inside the Visual Studio Command Prompt: + + ``` + conda activate gz-ws + ``` + +2. Now install Gazebo. + + ``` + conda install gz-sim<#> --channel conda-forge + ``` + + Be sure to replace `<#>` with a number value, such as 8 or 9, depending on + which version you need. + ## Source Install Install from source if you're interested in changing the source code or need a @@ -98,7 +120,7 @@ feature which hasn't been released yet. 2. Install dependencies ``` - brew install --only-dependencies ignition-gazebo<#> + brew install --only-dependencies gz-sim<#> ``` 3. Configure and build @@ -115,6 +137,50 @@ feature which hasn't been released yet. sudo make install ``` +### Windows + +1. First, follow the steps 1-4 of [Gazebo source install](https://gazebosim.org/docs/ionic/install_windows_src/#install-dependencies) + tutorial to install Conda, Visual Studio and set up a Conda environment (we'll call it `gz-ws`). + + Do not forget to activate the Conda env (if not already activated) + inside the Visual Studio Command Prompt: + + ``` + conda activate gz-ws + ``` + +2. Install Gazebo dependencies: + + You can view available versions and their dependencies: + ``` + conda search libgz-sim* --channel conda-forge --info + ``` + + Install the dependencies, replacing `<#>` with the required versions: + ``` + conda install gz-cmake<#> gz-common<#> gz-fuel-tools<#> gz-gui<#> gz-math<#> gz-msgs<#> gz-physics<#> gz-plugin<#> gz-rendering<#> gz-sensors<#> gz-tools<#> gz-transport<#> gz-utils<#> sdformat<#> --channel conda-forge + ``` + +3. Navigate to where you would like to build the library, and clone the repository. + ``` + # Optionally, append `-b gz-sim#` (replace # with a number) to check out a specific version + git clone https://github.com/gazebosim/gz-sim.git + ``` + +4. Configure and build + ``` + cd gz-sim + mkdir build + cd build + cmake .. -DBUILD_TESTING=OFF # Optionally, -DCMAKE_INSTALL_PREFIX=path\to\install + cmake --build . --config Release + ``` + +5. Optionally, install + ``` + cmake --install . --config Release + ``` + ## Documentation API documentation and tutorials can be accessed at [https://gazebosim.org/libs/gazebo](https://gazebosim.org/libs/gazebo) @@ -123,7 +189,10 @@ You can also generate the documentation from a clone of this repository by follo 1. You will need [Doxygen](http://www.doxygen.org/). On Ubuntu Doxygen can be installed using ``` + # Linux sudo apt-get install doxygen + # Windows + conda install doxygen --channel conda-forge ``` 2. Clone the repository @@ -137,7 +206,7 @@ You can also generate the documentation from a clone of this repository by follo mkdir build cd build cmake ../ - make doc + cmake --build . --target doc ``` 4. View the documentation by running the following command from the `build` directory. diff --git a/tutorials/joint_controller.md b/tutorials/joint_controller.md index 12b77666e2..ad01d53925 100644 --- a/tutorials/joint_controller.md +++ b/tutorials/joint_controller.md @@ -376,7 +376,7 @@ A detailed description and related parameter of JointTrajectoryController can be The trajectory message can be published or subscribed at `/model/${MODEL_NAME}/joint_trajectory` by default. -Message type: [`JointTrajectory`](https://gazebosim.org/api/msgs/7.2/classignition_1_1msgs_1_1JointTrajectory.html) +Message type: [`JointTrajectory`](https://github.com/gazebosim/gz-msgs/blob/gz-msgs10/proto/gz/msgs/joint_trajectory.proto) ### Example usage: @@ -385,7 +385,7 @@ Let’s set up a new model for this example. A two-linked manipulator arm which - SDF file: ```xml - @@ -653,7 +653,7 @@ gz topic -t "topic_name" -m gz.msgs.JointTrajectory -p ' sec: 1 nsec: 0 } - } + }' ```
diff --git a/tutorials/lander.md b/tutorials/lander.md index ecff012177..3c1552ebfe 100644 --- a/tutorials/lander.md +++ b/tutorials/lander.md @@ -1,4 +1,4 @@ -\page lander +\page lander Create a lander vehicle # Overview @@ -9,17 +9,17 @@ to the surface while they collect sensor measurements. ## Related tutorials -https://gazebosim.org/api/sim/8/create_vehicle.html +\ref create_vehicle -https://gazebosim.org/api/sim/8/adding_visuals.html +\ref adding_visuals -https://gazebosim.org/api/sim/8/frame_reference.html +\ref frame_reference -https://gazebosim.org/api/sim/8/adding_system_plugins.html +\ref adding_system_plugins -https://gazebosim.org/api/sim/8/theory_buoyancy.html +\ref theory_buoyancy -https://gazebosim.org/api/sim/8/theory_hydrodynamics.html +\ref theory_hydrodynamics # Create your vehicle @@ -37,7 +37,7 @@ mkdir -p ~/gazebo_maritime/models/my_lander && cd ~/gazebo_maritime/models/my_la Download the `model.config` file and copy it within that directory: ```bash -wget https://raw.githubusercontent.com/gazebosim/gz-sim/gz-sim8/tutorials/files/lander/model.config -O ~/gazebo_maritime/models/my_lander/model.config +wget https://raw.githubusercontent.com/gazebosim/gz-sim/gz-sim9/tutorials/files/lander/model.config -O ~/gazebo_maritime/models/my_lander/model.config ``` In its simple version, the lander does not have any moving pieces, so the SDF @@ -95,7 +95,7 @@ Let's now add nicer-looking visuals to our lander. Download the following ```bash mkdir ~/gazebo_maritime/models/my_lander/meshes -wget https://raw.githubusercontent.com/gazebosim/gz-sim/gz-sim8/tutorials/files/lander/inkfish-lander.dae -O ~/gazebo_maritime/models/my_lander/meshes/inkfish-lander.dae +wget https://raw.githubusercontent.com/gazebosim/gz-sim/gz-sim9/tutorials/files/lander/inkfish-lander.dae -O ~/gazebo_maritime/models/my_lander/meshes/inkfish-lander.dae ``` Replace the `chassis_visual` element in your `model.sdf` with the following @@ -114,7 +114,7 @@ block: And launch Gazebo to see the results: ```bash -gz sim ~/my_models/my_lander/model.sdf +gz sim ~/gazebo_maritime/models/my_lander/model.sdf ``` Your box should now be replaced with a better looking mesh. @@ -153,7 +153,7 @@ Let's start with the world. Download the following world: ```bash mkdir -p ~/gazebo_maritime/worlds -wget https://raw.githubusercontent.com/gazebosim/gz-sim/gz-sim8/tutorials/files/lander/buoyant_lander.sdf -O ~/gazebo_maritime/worlds/buoyant_lander.sdf +wget https://raw.githubusercontent.com/gazebosim/gz-sim/gz-sim9/tutorials/files/lander/buoyant_lander.sdf -O ~/gazebo_maritime/worlds/buoyant_lander.sdf export GZ_SIM_RESOURCE_PATH=:$HOME/gazebo_maritime/models ``` diff --git a/tutorials/migrating_ardupilot_plugin.md b/tutorials/migrating_ardupilot_plugin.md index e58dd764b2..69bebd3245 100644 --- a/tutorials/migrating_ardupilot_plugin.md +++ b/tutorials/migrating_ardupilot_plugin.md @@ -727,14 +727,14 @@ In the new code we explicitly reference each Gazebo package that we use: ``` # NEW -find_package(sdformat14 REQUIRED) -find_package(gz-common5-all REQUIRED) -find_package(gz-sim8-all REQUIRED) -find_package(gz-math7-all REQUIRED) -find_package(gz-msgs10-all REQUIRED) -find_package(gz-physics7-all REQUIRED) -find_package(gz-sensors8-all REQUIRED) -find_package(gz-transport13-all REQUIRED) +find_package(sdformat15 REQUIRED) +find_package(gz-common6-all REQUIRED) +find_package(gz-sim9-all REQUIRED) +find_package(gz-math8-all REQUIRED) +find_package(gz-msgs11-all REQUIRED) +find_package(gz-physics8-all REQUIRED) +find_package(gz-sensors9-all REQUIRED) +find_package(gz-transport14-all REQUIRED) ``` In the old code we need only refer to the build configuration retrieved from the Gazebo package: diff --git a/tutorials/migration_plugins.md b/tutorials/migration_plugins.md index bad1919e6f..a134f796e8 100644 --- a/tutorials/migration_plugins.md +++ b/tutorials/migration_plugins.md @@ -1,7 +1,7 @@ \page migrationplugins Migration from Gazebo Classic: Plugins Gazebo Classic supports -[6 different C++ plugin types](http://gazebosim.org/tutorials?tut=plugins_hello_world&cat=write_plugin), +[6 different C++ plugin types](https://classic.gazebosim.org/tutorials?tut=plugins_hello_world#PluginTypes), each providing access to different parts of the API, like physics, rendering, sensors, GUI, etc. Due to Gazebo Sim's architecture based on an [ECS](https://en.wikipedia.org/wiki/Entity_component_system) diff --git a/tutorials/migration_sdf.md b/tutorials/migration_sdf.md index 37a6a444c0..a9ae0008db 100644 --- a/tutorials/migration_sdf.md +++ b/tutorials/migration_sdf.md @@ -250,7 +250,7 @@ side-by-side on the same file. Instead, keep separate files and inject the plugi needed. There isn't a built-in mechanism on SDFormat to inject plugins into files yet, -but users can make use of templating tools like [ERB](erb_template.html) +but users can make use of templating tools like [ERB](https://github.com/gazebosim/gz-sim/blob/gz-sim9/tutorials/erb_template.md) and [xacro](http://wiki.ros.org/xacro) to generate SDF files with the correct plugins. ### Default plugins @@ -260,8 +260,8 @@ For example, by default, Gazebo will load all the system plugins defined on the `~/.gz/sim/<#>/server.config` file and all GUI plugins defined on the `~/.gz/sim/<#>/gui.config` file. But the user can always remove plugins from those files, or choose different ones by adding `` tags to the SDF file. -(For more details, see the [Server configuration tutorial](server_config.html) -and the [GUI configuration tutorial](gui_config.html)). +(For more details, see the [Server configuration tutorial](https://github.com/gazebosim/gz-sim/blob/gz-sim9/tutorials/server_config.md) +and the [GUI configuration tutorial](https://github.com/gazebosim/gz-sim/blob/gz-sim9/tutorials/gui_config.md)). This is important to keep in mind when migrating your SDF files, because files that worked on Gazebo classic may need more plugins on Gazebo. diff --git a/tutorials/migration_sensor_api.md b/tutorials/migration_sensor_api.md index de2c996104..b9e3504d23 100644 --- a/tutorials/migration_sensor_api.md +++ b/tutorials/migration_sensor_api.md @@ -61,24 +61,24 @@ Classic | Gazebo -- | -- Category | TODO FillMsg | TODO -Id | `ignition::gazebo::Sensor::Entity` +Id | `gz::sim::Sensor::Entity` IsActive | TODO LastMeasurementTime | TODO LastUpdateTime | TODO -Name | `ignition::gazebo::Sensor::Name` +Name | `gz::sim::Sensor::Name` NextRequiredTimestamp | TODO Noise | TODO -Pose | `ignition::gazebo::Sensor::Pose` +Pose | `gz::sim::Sensor::Pose` ResetLastUpdateTime | TODO -ScopedName | `ignition::gazebo::scopedName` +ScopedName | `gz::sim::scopedName` SetActive | TODO SetPose | TODO SetUpdateRate | TODO -Topic | `ignition::gazebo::Sensor::Topic` -Type | `ignition::gazebo::entityType` +Topic | `gz::sim::Sensor::Topic` +Type | `gz::sim::entityType` UpdateRate | TODO Visualize | TODO -WorldName | `ignition::gazebo::worldEntity` +WorldName | `gz::sim::worldEntity` --- @@ -94,8 +94,8 @@ they deal with entity IDs. Classic | Gazebo -- | -- -ParentId | `ignition::gazebo::Sensor::Parent` -ParentName | `ignition::gazebo::Sensor::Parent` +ParentId | `gz::sim::Sensor::Parent` +ParentName | `gz::sim::Sensor::Parent` --- @@ -124,6 +124,6 @@ Classic | Gazebo ConnectUpdated | TODO Fini | N/A Init | N/A -Load | `ignition::gazebo::SdfEntityCreator::CreateEntities` +Load | `gz::sim::SdfEntityCreator::CreateEntities` Update | Entities are updated by systems --- diff --git a/tutorials/model_photo_shoot.md b/tutorials/model_photo_shoot.md index 7d7209d0f8..0730e337a1 100644 --- a/tutorials/model_photo_shoot.md +++ b/tutorials/model_photo_shoot.md @@ -21,61 +21,53 @@ SDF is used to load and configure the `Model Photo Shoot` plugin. The demo SDF contains a good example of the different options and other related plugins: 1. The physics plugin: - -``` + ``` -``` - -A physics plugin is needed only if the `` option is to -be used. This will allow the `Model Photo Shoot` plugin to set the joints -to random positions. + ``` + A physics plugin is needed only if the `` option is to + be used. This will allow the `Model Photo Shoot` plugin to set the joints + to random positions. 2. The render engine plugin: - -``` + ``` ogre2 - 1, 1, 1 + 1.0, 1.0, 1.0 -``` - -A render plugin is needed to render the image. If `ogre2` is used, as shown in -the example, the `` tag can be used to set the background -of the pictures taken by the plugin. Please note that lights added by the -plugin will also affect the final resulting background color on the images. + ``` + A render plugin is needed to render the image. If `ogre2` is used, as shown in + the example, the `` tag can be used to set the background + of the pictures taken by the plugin. Please note that lights added by the + plugin will also affect the final resulting background color on the images. 3. The model and the photo shoot plugin: - -``` + ``` https://fuel.gazebosim.org/1.0/OpenRobotics/models/Robonaut poses.txt - true + false -``` - -The model is loaded through the `` tag. Then the `model photo shoot` -plugin and its options are specified: - -* ``: (optional) Location to store the camera -translation, scaling data and joints position (if using the -`` option) that can be used to replicate the -pictures using other systems. -* ``: (optional) When set to `true` the joints in the model -will be set to random positions prior to taking the pictures. + ``` + The model is loaded through the `` tag. Then the `model photo shoot` + plugin and its options are specified: + * ``: (optional) Location to store the camera + translation, scaling data and joints position (if using the + `` option) that can be used to replicate the + pictures using other systems. + * ``: (optional) When set to `true` the joints in the model + will be set to random positions prior to taking the pictures. 4. Camera sensor: - -``` + ``` 0 0 0 0 0 0 @@ -99,8 +91,7 @@ will be set to random positions prior to taking the pictures. true -``` - -A `camera sensor` must be added as it will be used by the plugin to take the -pictures. This allows plugin users to set the different parameters of the -camera to their desired values. + ``` + A `camera sensor` must be added as it will be used by the plugin to take the + pictures. This allows plugin users to set the different parameters of the + camera to their desired values. diff --git a/tutorials/move_camera_to_model.md b/tutorials/move_camera_to_model.md index 0ba5287eeb..8b6eac8172 100644 --- a/tutorials/move_camera_to_model.md +++ b/tutorials/move_camera_to_model.md @@ -7,7 +7,7 @@ This tutorial gives an introduction to Gazebo's service `/gui/move_to/model`. Th 1. Load the **View Angle** plugin. This service is only available when the **View Angle** plugin is loaded. 2. Call the service using the request message type `gz.msgs.GUICamera` and the response message type `gz.msgs.Boolean`. The distance to the object is defined as the z coordinate, and the direction of the camera with a quaternion. It's possible to select the projection type. -For example, Let's move the camera to the `box` model looking down from 5 meters away. +For example, Let's move the camera to the `box` model looking down from 5 meters away. This example uses the `shapes.sdf` world. ```bash gz service -s /gui/move_to/model --reqtype gz.msgs.GUICamera --reptype gz.msgs.Boolean -r 'name: "box", pose: {position: {z:5}, orientation: {x:0, y:0, z: -1, w:0}}, projection_type: "orbit"' --timeout 5000 diff --git a/tutorials/particle_tutorial.md b/tutorials/particle_tutorial.md index c0c37e73b5..7150027684 100644 --- a/tutorials/particle_tutorial.md +++ b/tutorials/particle_tutorial.md @@ -97,7 +97,18 @@ The particles are not only a visual effect in simulation, they also have an effe * `gpu_lidar`: The particles have a scattering effect on the lidar range readings. * `thermal_camera`: The particles are not visible in the thermal camera image. -The gif below shows an [example world](https://gist.github.com/iche033/bcd3b7d3f4874e1e707e392d6dbb0aa0) with six different sensors looking at the fog generator with a rescue randy model inside the fog. + +The [particle_emitter_scatter_effects.sdf](https://github.com/gazebosim/gz-sim/blob/gz-sim9/tutorials/files/particle_emitter/particle_emitter_scatter_effects.sdf) +demo world shows six different sensors looking at the fog generator with a rescue randy model inside the fog. + +Download the example world file and launch it to see what it looks like. + +```bash +gz sim -v 4 -r particle_emitter_scatter_effects.sdf +``` + +Navigate to the Visualize Lidar plugin on the right and click on the refresh button to set the lidar topic. You should see the sensor images and lidar visualization like below: + @image html files/particle_emitter/sensor_scatter_tutorial.gif diff --git a/tutorials/pause_run_simulation.md b/tutorials/pause_run_simulation.md new file mode 100644 index 0000000000..ef757b75a0 --- /dev/null +++ b/tutorials/pause_run_simulation.md @@ -0,0 +1,57 @@ +\page pause_run_simulation Pause and Run simulation + +A Gazebo transport API is exposed to allow starting and stopping the simulation. +It's possible to call this API using the command line or through the GUI. + +To repeat this demo, run the `default` world: +```bash +gz sim default.sdf +``` + +## Transport API + +When Gazebo is run headless, this is an easy way to start the simulation. + +To pause and play over the transport API, we should call the service `/world//control` and fill the request message type +`gz.msgs.WorldControl`. This service returns a `gz.msgs.Boolean` with the status of the request (true means everything was fine, false otherwise). + +The `WorldControl` message contains a `pause` field for starting and stopping the simulation. + +To start the simulation: + +```bash +gz service -s /world/default/control --reqtype gz.msgs.WorldControl --reptype gz.msgs.Boolean --timeout 3000 --req 'pause: false' +``` + +To pause the simulation: + +```bash +gz service -s /world/default/control --reqtype gz.msgs.WorldControl --reptype gz.msgs.Boolean --timeout 3000 --req 'pause: true' +``` + +When paused, time will stop in Gazebo, and the physics will not be running. + +To check the current state of the simulator, check the `paused` field in `/stats` like so: +```bash +gz topic --echo --topic /stats -n 1 +``` +If the simulator is currently paused but was running before, we would see something similar to this: +```text +sim_time { + sec: 8 + nsec: 707000000 +} +real_time { + sec: 8 + nsec: 824323281 +} +iterations: 8707 +real_time_factor: 0.998022916602211 +``` + + +## GUI + +We included a button in the `World Control` plugin allowing to start and stop the simulation from the GUI. + +@image html files/pause_run_simulation/gui_pause_run.png diff --git a/tutorials/physics.md b/tutorials/physics.md index 98a08b8a78..1099560f5a 100644 --- a/tutorials/physics.md +++ b/tutorials/physics.md @@ -10,7 +10,7 @@ by default. Downstream developers may also integrate other physics engines by creating new Gazebo Physics engine plugins. See -[Gazebo Physics](https://gazebosim.org/api/physics/7/tutorials.html)'s +[Gazebo Physics](https://gazebosim.org/api/physics/8/tutorials.html)'s tutorials to learn how to integrate a new engine. ## How Gazebo finds engines @@ -25,20 +25,33 @@ If you've created a custom engine plugin, you can tell Gazebo where to find it by setting the `GZ_SIM_PHYSICS_ENGINE_PATH` environment variable to the directory where the plugin's shared library can be found. -For example, if you've created the following physics engine shared library on -Linux: +For example, if you've created the following physics engine shared library: -`/home/physics_engines/libCustomEngine.so` +``` +# Linux +/home/physics_engines/libCustomEngine.so +# Windows +C:\Users\Robot\physics_engines\CustomEngine.dll +``` You should set the variable as follows: -`export GZ_SIM_PHYSICS_ENGINE_PATH=/home/physics_engines` - +``` +# Linux +export GZ_SIM_PHYSICS_ENGINE_PATH=/home/physics_engines +# Windows +set GZ_SIM_PHYSICS_ENGINE_PATH=C:\Users\Robot\physics_engines +``` If you have several libraries installed in different paths, you can add more paths, for example: -`export GZ_SIM_PHYSICS_ENGINE_PATH=/home/physics_engines:/home/more_engines` +``` +# Linux, separate with : +export GZ_SIM_PHYSICS_ENGINE_PATH=/home/physics_engines:/home/more_engines +# Windows, separate with ; +set GZ_SIM_PHYSICS_ENGINE_PATH=C:\Users\Robot\physics_engines;C:\MoreEngines +``` ## Tell Gazebo what engine to load @@ -47,6 +60,7 @@ There are a few different ways of telling Gazebo which engine to load. For any method, you should provide the name of your plugin's shared library, but the `lib` prefix and the file extension are optional. So in this example, the file is `libCustomEngine.so` but it's enough to set `CustomEngine`. +That is also prefered to support multiple operating systems. ### From SDF @@ -70,7 +84,9 @@ For the example above, you can load it like this: Alternatively, you can choose a plugin from the command line using the `--physics-engine` option, for example: -`gz sim --physics-engine CustomEngine` +``` +gz sim --physics-engine CustomEngine +``` To use an existing alternative engine (e.g. Bullet Featherstone) @@ -96,10 +112,10 @@ Gazebo supports the following physics engine configurations through SDF. These options are available to all physics engines, but not all engines may support them. The default physics engine, DART, supports all these options. -* [//physics/dart/collision_detector](http://sdformat.org/spec?ver=1.8&elem=physics#dart_collision_detector) +* [//physics/dart/collision_detector](http://sdformat.org/spec?ver=1.11&elem=physics#dart_collision_detector) * Options supported by DART: `ode` (default), `bullet`, `fcl`, `dart`. -* [//physics/dart/solver/solver_type](http://sdformat.org/spec?ver=1.8&elem=physics#solver_solver_type) +* [//physics/dart/solver/solver_type](http://sdformat.org/spec?ver=1.11&elem=physics#solver_solver_type) * Options supported by DART: `dantzig` (default), `pgs` ## Troubleshooting @@ -107,12 +123,12 @@ may support them. The default physics engine, DART, supports all these options. > Failed to find plugin [libCustomEngine.so]. Have you checked the > GZ_SIM_PHYSICS_ENGINE_PATH environment variable? -Gazebo can't find out where `libCustomEngine.so` is located. +Gazebo can't find out where `libCustomEngine.so` or `CustomEngine.dll` is located. If that's an engine you believe should be installed with Gazebo Physics, check if the relevant plugin is installed. -If that's a 3rd party engine, find where the `.so` file is installed and add +If that's a 3rd party engine, find where the `.so` or `.dll` file is installed and add that path to the environment variable as described above. > Unable to load the [/home/physics_engines/libCustomEngine.so] library. @@ -120,6 +136,12 @@ that path to the environment variable as described above. There was some problem loading that file. Check that it exists, that you have permissions to access it, and that it's acually a physics engine plugin. +> No physics plugins implementing required interface found in library +> [/home/physics_engines/libCustomEngine.so] + +The library was found but none of the plugins in the library implement the +required interface to be considered a physics plugin. + > No plugins with all required features found in library > [/home/physics_engines/libCustomEngine.so] diff --git a/tutorials/point_cloud_to_mesh.md b/tutorials/point_cloud_to_mesh.md index d0628d8f66..04d710013c 100644 --- a/tutorials/point_cloud_to_mesh.md +++ b/tutorials/point_cloud_to_mesh.md @@ -13,6 +13,7 @@ Next, you’ll need to download CloudCompare. This is the software we will be using to convert our point cloud to a mesh. CloudCompare is free and open source. You can download it [here](http://www.danielgm.net/cc/release/). +This tutorial was created using CloudCompare 1.13.2. ## Converting with CloudCompare @@ -20,7 +21,7 @@ After installing, open CloudCompare and import your point cloud file by going to Depending on the number of points in your point cloud, this could take several minutes. Once loaded, you should see the following tunnel section: -![Opening the point cloud](https://raw.githubusercontent.com/gazebosim/gz-sim/main/tutorials/files/point_cloud_to_mesh/cloudcompare2.png) +\image html files/point_cloud_to_mesh/cloudcompare2.png Many 3D scans will be composed of millions, sometimes hundreds of millions of points. Converting a scan to a 3D model with that many points would be very difficult due to the number of polygons that would be created and the long processing time necessary to compute the normals. @@ -34,13 +35,13 @@ We'll still walk through reducing points, however, to make the process quicker. To reduce the number of points in your cloud, click on the tunnel so a yellow cube outline appears around it, then go to `Edit` > `Subsample`. -![Min. space between points](https://raw.githubusercontent.com/gazebosim/gz-sim/main/tutorials/files/point_cloud_to_mesh/min_space.png) +\image html files/point_cloud_to_mesh/min_space.jpg The number you will need to enter in the `min. space between points` field will vary depending on your point cloud. A value of .01 was sufficient to bring our point cloud to an easy-to-manage 1 million points. Point count is visible in the `Properties` window on the lower left side of the screen. -![Point count of subsample](https://raw.githubusercontent.com/gazebosim/gz-sim/main/tutorials/files/point_cloud_to_mesh/properties.png) +\image html files/point_cloud_to_mesh/properties.png How many points you reduce down to will largely depend on how long you are willing to wait for the point cloud to be converted into a mesh. The more points you start with, the longer it will take to compute the normals and create the mesh. @@ -48,7 +49,7 @@ The more points you start with, the longer it will take to compute the normals a After the operation is complete you’ll have two clouds in your scene: the original point cloud and your subsampled point cloud. Most operations in CloudCompare will create new point clouds and keep the original, so make sure that you have the new point cloud selected before running an operation. -![Selecting the new point cloud](https://raw.githubusercontent.com/gazebosim/gz-sim/main/tutorials/files/point_cloud_to_mesh/secondcloud.png) +\image html files/point_cloud_to_mesh/secondcloud.png ### Create a Polygonal Mesh @@ -59,7 +60,7 @@ A normal is essentially the direction a polygon is facing. To do this, go to `Edit` > `Normals` > `Compute`. You'll see this dialog box: -![Choose Triangulation surface model](https://raw.githubusercontent.com/gazebosim/gz-sim/main/tutorials/files/point_cloud_to_mesh/compute_normals.png) +\image html files/point_cloud_to_mesh/compute_normals.png You’ll see various options in the dialog box that appears. The main thing you’ll want to consider is what `Local surface model` to use. @@ -72,7 +73,7 @@ Now we get to actually convert our point cloud to a mesh. To do this go to `Plugins` > `PoissonRecon`. You'll see this dialog box: -![Octree depth and output density selection](https://raw.githubusercontent.com/gazebosim/gz-sim/main/tutorials/files/point_cloud_to_mesh/outputdensity.png) +\image html files/point_cloud_to_mesh/outputdensity.png The value you enter in the `Octree depth` field will determine the polygon count of the created model. You may have to run the surface reconstruction a couple times with varying values before you land on a polygon count that is suitable for your needs. @@ -93,18 +94,18 @@ Our tunnel has turned into a blob shape. This is because the mesh that CloudCompare creates will always be water tight even if it has to add polygons where there are no points. We just want our tunnels, though, so we need to remove those unnecessary polygons. -![The blob shape](https://raw.githubusercontent.com/gazebosim/gz-sim/main/tutorials/files/point_cloud_to_mesh/blob2.png) +\image html files/point_cloud_to_mesh/blob2.jpg This is where our scalar field comes in. In the mesh's `Properties` window go to `SF display params` and take the left handle in the graph and drag it to the right until it hits the area where the bulk of the scalar field starts. -![Adjusting scalar filed params](https://raw.githubusercontent.com/gazebosim/gz-sim/main/tutorials/files/point_cloud_to_mesh/sf_display.png) +\image html files/point_cloud_to_mesh/sf_display.png This will display only the polygons that were created from the point cloud and hide the polygons used to make the model watertight. The polygons are only hidden however. We still need to actually remove them. -![Display original polygons](https://raw.githubusercontent.com/gazebosim/gz-sim/main/tutorials/files/point_cloud_to_mesh/hidden_polygons2.png) +\image html files/point_cloud_to_mesh/hidden_polygons2.jpg To remove the hidden polygons go to `Edit` > `Scalar fields` > `Filter By Value`. @@ -113,7 +114,11 @@ Hitting export will simply export the mesh within that range. Instead, we'll hit `Split` to create two meshes. One with the polygons inside our specified range and one containing polygons outside that range. -![Splitting the mesh](https://raw.githubusercontent.com/gazebosim/gz-sim/main/tutorials/files/point_cloud_to_mesh/split.png) +\image html files/point_cloud_to_mesh/split.png + +It is possible that you will get error "Visibility table not instantiated". This error causes only the "wanted" part of the mesh to be created when splitting, while the other ("unwanted") part will not be generated. But that is not a problem for our use-case. + +\image html files/point_cloud_to_mesh/error.jpg ### The Completed Model @@ -121,6 +126,8 @@ By hitting `Split` we can view the model before exporting by simply going to `Fi Remember to have the correct mesh selected (`.part`) since choosing `Split` will give you two new meshes, plus you will still have your original, complete mesh. Your file format will depend on the software you want to use but `.obj` is a widely supported format that should work in most 3D applications. -![The completed mesh](https://raw.githubusercontent.com/gazebosim/gz-sim/main/tutorials/files/point_cloud_to_mesh/complete2.png) +Before exporting, check your mesh for holes. If still checked, disable display of the pointcloud, so that only your output mesh is shown. Inspect ground parts of the mesh (or any other parts you are most interested in) and check if there aren't holes in it. If there are some, you can try to repeat the trimming step with more relaxed threshold. + +\image html files/point_cloud_to_mesh/complete2.png You can find more information on CloudCompare and a more in depth look at the tools we used in this tutorial on [the CloudCompare website](https://www.cloudcompare.org/) and the [CloudCompare wiki](https://www.cloudcompare.org/doc/wiki/index.php?title=Main_Page). diff --git a/tutorials/python_interfaces.md b/tutorials/python_interfaces.md index a696512dc0..108080d121 100644 --- a/tutorials/python_interfaces.md +++ b/tutorials/python_interfaces.md @@ -43,7 +43,7 @@ server.run(True, 1000, False) ## Run the example In the -[examples/scripts/python_api](https://github.com/gazebosim/gz-sim/tree/gz-sim8/examples/scripts/python_api) +[examples/scripts/python_api](https://github.com/gazebosim/gz-sim/tree/main/examples/scripts/python_api) folder there is a Python script that shows how to make use of this API. If you compiled Gazebo from source you should modify your `PYTHONPATH`: @@ -82,7 +82,7 @@ post_iterations 1000 pre_iterations 1000 ``` -# Gazebo Systems written in Python +## Gazebo Systems written in Python Gazebo also provides a way to write systems in Python. This is done using the `gz::sim::systems::PythonSystemLoader` system which loads a given python module @@ -96,8 +96,8 @@ Example python system: ```python -from gz.math7 import Vector3d -from gz.sim8 import Model, Link +from gz.math8 import Vector3d +from gz.sim9 import Model, Link import random diff --git a/tutorials/reset_simulation.md b/tutorials/reset_simulation.md index 7a62a3fa3d..bf8ae121ab 100644 --- a/tutorials/reset_simulation.md +++ b/tutorials/reset_simulation.md @@ -4,6 +4,11 @@ The Reset Gazebo transport API is exposed to allow resetting simulation to time It's possible to call this API using the command line or through the GUI. In addition to the API, we have also expanded the simulation system API with a Reset interface. +To repeat this demo, run the `rolling_shapes.sdf` file: +```bash +gz sim rolling_shapes.sdf +``` + ## Reset interface System authors may now choose to implement the Reset interface to have a more intelligent @@ -16,7 +21,7 @@ Follow the tutorial \subpage createsystemplugins to see how to support Reset by ## Transport API To invoke reset over transport API, we should call the service `/world//control` and fill the request message type -`gz.msgs.WorldControl`. This service returns a `gz.msgs.Boolean` with the status of the reset (true means everything was fine, false otherwise) +`gz.msgs.WorldControl`. This service returns a `gz.msgs.Boolean` with the status of the reset (true means everything was fine, false otherwise). The `WorldControl` message now contains a `reset` field for resetting the world: diff --git a/tutorials/server_config.md b/tutorials/server_config.md index bbab5f5110..650eafe0c4 100644 --- a/tutorials/server_config.md +++ b/tutorials/server_config.md @@ -17,11 +17,26 @@ There are a few places where the plugins can be defined: 3. The default configuration file at `$HOME/.gz/sim/<#>/server.config` \*, where `<#>` is Gazebo Sim's major version. -Each of the items above takes precedence over the ones below it. For example, -if a the SDF file has any `` elements, then the -`GZ_SIM_SERVER_CONFIG_PATH` variable is ignored. And the default configuration -file is only loaded if no plugins are passed through the SDF file or the -environment variable. +The behavior of Gazebo when loading these plugins depends on the +`` policy set in ``: + +- `true`: Plugins + in the SDF file are first loaded, followed by plugins from config files + (either `GZ_SIM_SERVER_CONFIG_PATH` or the default configuration file). + Plugins from SDF files take precedence over plugins from config files, this + means, if a plugin is specified in both places, only the one specified in the + SDF file will be loaded. The main use case for this is for users to rely on + the default list of plugins and only add extra plugins they need for the + application. This is the default setting in Gazebo Ionic and later. + +- `false`: If + there are any plugins specified in the SDF file, plugins from the config files + (either `GZ_SIM_SERVER_CONFIG_PATH` or the default configuration file) are + ignored. This allows the user to have complete control over which plugins are + loaded. This is the default setting in Gazebo Harmonic and earlier. + +In both policy settings, the default configuration file is only loaded if no +plugins are passed through the `GZ_SIM_SERVER_CONFIG_PATH` environment variable. > \* For log-playback, the default file is > `$HOME/.gz/sim/<#>/playback_server.config` @@ -85,7 +100,7 @@ will be created with default values: Let's try overriding the default configuration from an SDF file. Open your favorite editor and save this file as `fuel_preview.sdf`: -``` +```xml @@ -147,8 +162,84 @@ Now let's load this world: `gz sim -r /fuel_preview.sdf` -Notice how the application has only one system plugin loaded, the scene -broadcaster, as defined on the SDF file above. Physics is not loaded, so even +Notice how the application has loaded the scene +broadcaster, as defined on the SDF file above as well as the default plugins +`Physics` and `UserCommands`. Since `SceneBroadcaster` is loaded from the SDF file, +it's not loaded again. We see that the cone falls due to gravity since all the +necessary plugins are loaded. + +@image html files/server_config/from_sdf_no_plugins.gif + +Now, let's modify the SDF file to change the policy `false` + +```xml + + + + + + false + + + + + + + + + + + 3D View + false + docked + + + ogre2 + scene + 1.0 1.0 1.0 + 0.4 0.6 1.0 + 8.3 7 7.8 0 0.5 -2.4 + + + + false + 5 + 5 + floating + false + + + + + false + 5 + 5 + floating + false + + + + + + + https://fuel.gazebosim.org/1.0/OpenRobotics/models/Sun + + + + https://fuel.gazebosim.org/1.0/OpenRobotics/models/Construction Cone + + + + +``` +Let's load this world again: + +`gz sim -r /fuel_preview.sdf` + +Notice how the application has only one system plugin loaded, the `SceneBroadcaster`, +as defined on the SDF file above. `Physics` is not loaded, so even though the simulation is running (started with `-r`), the cone doesn't fall with gravity. @@ -299,3 +390,8 @@ the background color is the default grey, instead of the blue color set on the GUI `GzScene` plugin. @image html files/server_config/camera_env.gif + + +### Order of Execution of Plugins +The order of execution of plugins can be controlled by setting +the `` tag inside ``. See example in examples/plugin/priority_printer_plugin and the associated README.md file to learn more. diff --git a/tutorials/shadow_texture_size.md b/tutorials/shadow_texture_size.md new file mode 100644 index 0000000000..b117370deb --- /dev/null +++ b/tutorials/shadow_texture_size.md @@ -0,0 +1,118 @@ +\page shadow_texture_size Shadow Texture Size + +This tutorial showcases how to change the texture size of shadows casted by lights. + +
+ \image html files/shadow_texture_size/shadow_texsize_2k_to_16k.gif width=60% +
+ +## Texture size options + +Supported texture sizes include: 512px, 1024px, 2048px, 4096px, 8192px, 16384px. + +The default shadow texture size for all lights is 2K. The default max texture size is 16K, but for less powerful computers, it may be a lower value, like 8K. + +## Changing shadow texture size + +The shadow texture size can be changed for the GUI with the usage of the Minimal Scene GUI plugin in the SDF. + +Currently, light type is restricted to only directional light. As a scene should typically have at most one directional light (representing the sun), this will ensure that an increase in shadow texture size does not increase VRAM too much. + +### Example usage for the GUI + +Let's change the shadow texture size for directional light with the SDF file below. (The finished SDF file can be viewed [here](https://github.com/gazebosim/gz-sim/tree/gz-sim9/examples/worlds/shadow_texture_size.sdf).) + +1) Save the below in an SDF file named `shadow_texture_size.sdf`: + +```xml + + + + + 0.1 0.1 0.1 + 0.0 0.0 0.0 + + + + + + ogre2 + -10 0 7 0 0.5 0 + + + + + + + + + 0 0 8 0 0 0 + 1 1 1 1 + 0 0 0 0 + + 50 + 0 + 0 + 0 + + true + -2 2 -1.5 + 1.0 + + + + + 0 0 0 0 0 -1.57 + + https://fuel.gazebosim.org/1.0/OpenRobotics/models/Garden Mascot + + + + + -5 0 -0.5 0 0 0 + true + + + + + 15 15 1 + + + + + + + 15 15 1 + + + + 0.8 0.8 0.8 1 + 0.8 0.8 0.8 1 + 0.8 0.8 0.8 1 + + + + + + +``` + +2) Add the following lines to the Minimal Scene GUI plugin. + +```xml + + 8192 + +``` + +The `` value can be changed as per the Texture size options section. + +3) Open the `shadow_texsize_demo.sdf` world with + +```bash +gz sim shadow_texture_size.sdf +``` + +## Impact on VRAM usage + +Keep in mind that the larger the shadow texture size, the more VRAM is used. Thus, in a scene populated with many lights, changing the directional light's shadow texture size should be fine because there is typically only one directional light in a scene. But if the scene has many point lights and the point light's shadow texture size is increased, the VRAM consumption goes up significantly. diff --git a/tutorials/spacecraft_thrusters.md b/tutorials/spacecraft_thrusters.md new file mode 100644 index 0000000000..5cbf8f6bd4 --- /dev/null +++ b/tutorials/spacecraft_thrusters.md @@ -0,0 +1,105 @@ +\page spacecraft_thrusters Spacecraft thrusters + +## Spacecraft Thrusters Model + +To enable a seamless transition of space robotics control and planning schemes from simulation to real, we introduce +a spacecraft thrusters model in Gazebo. The model provides a very simple interface to a solenoid valve that controls +the flow of gas to the thruster. The thruster model is as follows: + +- force output equals max_thrust when the command is 1 +- force output equals 0 when the command is 0 +- force output is modeled according to a duty cycle with a given frequency, and thrust output is maximum at the ON state of the duty cycle + +In short, if the duty cycle signal is high, the solenoid valve behaves as a fully-opened thruster, providing maximum thrust. +If the duty cycle signal is low, the solenoid valve behaves as a fully-closed thruster, providing no thrust. + +## Setting up the SpacecraftThrusterModel plugin + +Here follows an example instance of the `SpacecraftThrusterModel` plugin in an SDF file: +```xml + + thruster_0 + 0 + 10 + 1.4 + command/duty_cycle + +``` + +In this case, each thruster link should be placed in the proper location in the spacecraft model. +An example of this goes below: +```xml + + -0.12 0.12 0.2 3.14159 1.57079 3.14159 + base_link + thruster_0 + + + 0 + 0 + + 0 0 1 + + -1e+16 + 1e+16 + + + + + + 0 + 0.2 + + + + + + true + 0 0 0 0 -0 0 + + 0 0 0 0 0 0 + 0.001 + + 1e-05 + 0 + 0 + 1e-05 + 0 + 1e-05 + + + +``` + +## Testing an implementation of a Spacecraft model +An example of a spacecraft with thrusters is implemented in the [DART spacecraft model](https://app.gazebosim.org/proque/fuel/models/dart). To run the example, run the following command: +```bash +gz sim spacecraft.sdf +``` + +This spacecraft has 12 thrusters. To send inputs to `thruster_0`, run the following command: +```bash +gz topic -p 'normalized:[1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]' -t /dart/command/duty_cycle --msgtype gz.msgs.Actuators +``` + +This command will send the maximum force of a thruster over one sampling time. + +Below, an image of the spacecraft: +![Spacecraft](./files/spacecraft/dart.png) + +## 2D Spacecraft Simulator - Ground Space Robotics testbed + +An example of a ground testbed for spacecrafts is also available, where the spacecraft moves a 2D plane using thrusters. The testbed spacecraft model has 8 thrusters, and the thrusters are controlled by the `SpacecraftThrusterModel` plugin. This replicates the real [DISCOWER](https://www.discower.io/) testbed at KTH Space Robotics Laboratory in Stockholm Sweden. + +To run this example, run the following command: +```bash +gz sim ground_spacecraft_testbed.sdf +``` + +This spacecraft has 8 thrusters. To send inputs to `thruster_0`, run the following command: +```bash +gz topic -p 'normalized:[1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]' -t /kth_freeflyer/command/duty_cycle --msgtype gz.msgs.Actuators +``` + +Below is a picture of the simulator: +![Spacecraft simulator](./files/spacecraft/kth_spacecraft_simulator.png) diff --git a/tutorials/surface_vehicles.md b/tutorials/surface_vehicles.md index 812edf98ba..a89186a502 100644 --- a/tutorials/surface_vehicles.md +++ b/tutorials/surface_vehicles.md @@ -1,4 +1,4 @@ -\page surface_vehicles +\page surface_vehicles Create a surface vehicle # Overview @@ -8,15 +8,15 @@ with the presence of waves and wind. ## Related tutorials -https://gazebosim.org/api/sim/8/create_vehicle.html +\ref create_vehicle -https://gazebosim.org/api/sim/8/adding_visuals.html +\ref adding_visuals -https://gazebosim.org/api/sim/8/frame_reference.html +\ref frame_reference -https://gazebosim.org/api/sim/8/adding_system_plugins.html +\ref adding_system_plugins -https://gazebosim.org/api/sim/8/theory_hydrodynamics.html +\ref theory_hydrodynamics # Adding an environment diff --git a/tutorials/terminology.md b/tutorials/terminology.md index 7103fee7ec..ec0391475b 100644 --- a/tutorials/terminology.md +++ b/tutorials/terminology.md @@ -26,7 +26,7 @@ to developers touching the source code. * **System**: Logic that operates on all entities that have a given set of components. Systems are plugins that can be loaded at runtime. - Gazebo ships with various systems, and downstream develpers can + Gazebo ships with various systems, and downstream developers can [create their own systems](createsystemplugins.html). * **Entity-component manager** (**ECM**): Provides functions for diff --git a/tutorials/theory_buoyancy.md b/tutorials/theory_buoyancy.md index f464ad8fc1..19bb2dad8d 100644 --- a/tutorials/theory_buoyancy.md +++ b/tutorials/theory_buoyancy.md @@ -1,4 +1,4 @@ -\page theory_buoyancy +\page theory_buoyancy Buoyancy # Overview diff --git a/tutorials/theory_hydrodynamics.md b/tutorials/theory_hydrodynamics.md index 7f2c7cd5a5..d2e3deb47d 100644 --- a/tutorials/theory_hydrodynamics.md +++ b/tutorials/theory_hydrodynamics.md @@ -1,4 +1,4 @@ -\page theory_hydrodynamics +\page theory_hydrodynamics Hydrodynamics # Overview diff --git a/tutorials/triggered_publisher.md b/tutorials/triggered_publisher.md index 5d497dda86..ffad764809 100644 --- a/tutorials/triggered_publisher.md +++ b/tutorials/triggered_publisher.md @@ -16,10 +16,10 @@ Publisher systems can be chained together by showing how the falling of the box can trigger another box to fall. Last, it covers how a service call can be triggered to reset the robot pose. The finished world SDFormat file for this tutorial can be found in -[examples/worlds/triggered_publisher.sdf](https://github.com/gazebosim/gz-sim/blob/gz-sim8/examples/worlds/triggered_publisher.sdf) +[examples/worlds/triggered_publisher.sdf](https://github.com/gazebosim/gz-sim/blob/main/examples/worlds/triggered_publisher.sdf) We will use the differential drive vehicle from -[examples/worlds/diff_drive.sdf](https://github.com/gazebosim/gz-sim/blob/gz-sim8/examples/worlds/diff_drive.sdf), +[examples/worlds/diff_drive.sdf](https://github.com/gazebosim/gz-sim/blob/main/examples/worlds/diff_drive.sdf), but modify the input topic of the `DiffDrive` system to `cmd_vel`. A snippet of the change to the `DiffDrive` system is shown below: diff --git a/tutorials/underwater_vehicles.md b/tutorials/underwater_vehicles.md index 86d76ba995..1efcd90aef 100644 --- a/tutorials/underwater_vehicles.md +++ b/tutorials/underwater_vehicles.md @@ -1,4 +1,4 @@ -\page underwater_vehicles +\page underwater_vehicles Create an underwater vehicle # Overview @@ -11,17 +11,17 @@ guide you through the setup of the [MBARI LRAUV](https://app.gazebosim.org/accur ## Related tutorials -https://gazebosim.org/api/sim/8/create_vehicle.html +\ref create_vehicle -https://gazebosim.org/api/sim/8/adding_visuals.html +\ref adding_visuals -https://gazebosim.org/api/sim/8/frame_reference.html +\ref frame_reference -https://gazebosim.org/api/sim/8/adding_system_plugins.html +\ref adding_system_plugins -https://gazebosim.org/api/sim/8/theory_buoyancy.html +\ref theory_buoyancy -https://gazebosim.org/api/sim/8/theory_hydrodynamics.html +\ref theory_hydrodynamics # Create your vehicle diff --git a/tutorials/using_components.md b/tutorials/using_components.md index 717beea566..1c91d47e46 100644 --- a/tutorials/using_components.md +++ b/tutorials/using_components.md @@ -72,3 +72,4 @@ The rest of the tutorial is case studies that walk through the usage of specific components. - \subpage jointforcecmdcomponent "JointForceCmd" +- \subpage posecomponent "Pose" diff --git a/tutorials/video_recorder.md b/tutorials/video_recorder.md index efea6422c2..432bd94f21 100644 --- a/tutorials/video_recorder.md +++ b/tutorials/video_recorder.md @@ -13,12 +13,18 @@ Gazebo that already has this plugin included in the GUI. gz sim -v 4 video_record_dbl_pendulum.sdf ``` -In this demo world, you should see a video recorder icon positioned on the top. +In this demo world, you should see a video recorder icon positioned on the top left area of the window along with other buttons. Clicking on the video recorder button gives you the video format options that are available. @image html files/video_recorder/video_recorder.png +> **NOTE** +> The OGV format might not be supported by the underlying libavcodec implementation. +> This is especially true on Windows with the conda-installed dependencies. +> There is no easy way to add the VP8 codec support except for building +> libavcodec manually and enabling the support. + Once an option is selected, recording starts immediately as indicated by a flashing video recorder icon. At anytime that you wish to stop recording, click on the flashing icon and select `Stop`. A file dialog window should pop up @@ -107,5 +113,5 @@ generated video. The default bitrate is 2Mbps. Since Gazebo Common 3.10.2, there is support for utilizing the power of GPUs to speed up the video encoding process. See the -[Hardware-accelerated Video Encoding tutorial](https://gazebosim.org/api/common/5/hw-encoding.html) +[Hardware-accelerated Video Encoding tutorial](https://gazebosim.org/api/common/6/hw-encoding.html) for more details.