diff --git a/.clang-tidy b/.clang-tidy index 6e80b9026b3eb65a5417ca390eac4ccc1903e85e..2a651563d9077f067b1c59b59e2f46bc8e882ead 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -26,7 +26,8 @@ Checks: ' -cppcoreguidelines-pro-bounds-array-to-pointer-decay, -cppcoreguidelines-pro-type-member-init, -cppcoreguidelines-special-member-functions, - -cppcoreguidelines-owning-memory + -cppcoreguidelines-owning-memory, + -cppcoreguidelines-narrowing-conversions ' HeaderFilterRegex: '^.*(source|include).*$' CheckOptions: diff --git a/CMakeLists.txt b/CMakeLists.txt index 1fdc9b992adab59ca3b1ddd7d8a4e3d9d2194381..25e2b850ce1593711b470bcb2e9262e97ac2e155 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.10) +cmake_minimum_required(VERSION 3.18) # default settings set(ARMARX_ENABLE_DEPENDENCY_VERSION_CHECK_DEFAULT FALSE) @@ -11,17 +11,19 @@ armarx_enable_modern_cmake_project() armarx_project(navigation NAMESPACE armarx) # Specify each ArmarX Package dependency with the following macro -armarx_find_package(PUBLIC ArmarXGui) + +# - required armarx_find_package(PUBLIC RobotAPI REQUIRED) + +# - optional +armarx_find_package(PUBLIC ArmarXGui) armarx_find_package(PUBLIC MemoryX QUIET) armarx_find_package(PUBLIC VisionX QUIET) -add_subdirectory(etc) -add_subdirectory(external) - -# Required dependencies +# System dependencies +# - required -# Optional dependencies +# - optional armarx_find_package(PUBLIC OpenMP QUIET) armarx_find_package(PUBLIC Ceres QUIET) armarx_find_package(PUBLIC VTK QUIET) @@ -29,6 +31,14 @@ armarx_find_package(PUBLIC SemanticObjectRelations QUIET) armarx_find_package(PUBLIC OpenCV QUIET) # Required as RobotAPI is a legacy project. +add_subdirectory(etc) + +set(RANGES_VERBOSE_BUILD OFF) +set(RANGES_RELEASE_BUILD ON) +set(RANGES_CXX_STD 17) +add_subdirectory(external) + + #include(FetchContent) #FetchContent_Declare( @@ -46,6 +56,11 @@ armarx_find_package(PUBLIC OpenCV QUIET) # Required as RobotAPI is a legacy pro # ) # FetchContent_MakeAvailable(inotify_cpp) +add_definitions(-Werror=init-self) +add_definitions(-Werror=uninitialized) +add_definitions(-Werror=missing-field-initializers) +add_definitions(-Werror=reorder) +add_definitions(-Werror=narrowing) add_subdirectory(source) diff --git a/scenarios/NavigationSimulation/config/HandUnitDynamicSimulationApp.LeftHand.cfg b/scenarios/NavigationSimulation/config/HandUnitDynamicSimulationApp.LeftHand.cfg index 18e2d2dbc38674afde8395d91f1a6582b0a9116f..ddfb9cce396b5caf6cdd1b97581da4ea1d659130 100644 --- a/scenarios/NavigationSimulation/config/HandUnitDynamicSimulationApp.LeftHand.cfg +++ b/scenarios/NavigationSimulation/config/HandUnitDynamicSimulationApp.LeftHand.cfg @@ -76,6 +76,14 @@ ArmarX.ApplicationName = LeftHandUnitApp # ArmarX.EnableProfiling = false +# ArmarX.HandUnitDynamicSimulation.ArVizTopicName: Name of the ArViz topic +# Attributes: +# - Default: ArVizTopic +# - Case sensitivity: yes +# - Required: no +# ArmarX.HandUnitDynamicSimulation.ArVizTopicName = ArVizTopic + + # ArmarX.HandUnitDynamicSimulation.EnableProfiling: enable profiler which is used for logging performance events # Attributes: # - Default: false @@ -131,6 +139,13 @@ ArmarX.HandUnitDynamicSimulation.ObjectName = LeftHandUnit # ArmarX.HandUnitDynamicSimulation.RobotStateComponentName = RobotStateComponent +# ArmarX.HandUnitDynamicSimulation.Side: Name of the hand (left, right) +# Attributes: +# - Case sensitivity: yes +# - Required: yes +# ArmarX.HandUnitDynamicSimulation.Side = ::_NOT_SET_:: + + # ArmarX.HandUnitDynamicSimulation.SimulatorProxyName: Name of the simulator proxy to use. # Attributes: # - Default: Simulator @@ -139,6 +154,14 @@ ArmarX.HandUnitDynamicSimulation.ObjectName = LeftHandUnit # ArmarX.HandUnitDynamicSimulation.SimulatorProxyName = Simulator +# ArmarX.HandUnitDynamicSimulation.WorkingMemoryName: Name of the working memory that should be used +# Attributes: +# - Default: WorkingMemory +# - Case sensitivity: yes +# - Required: no +# ArmarX.HandUnitDynamicSimulation.WorkingMemoryName = WorkingMemory + + # ArmarX.HandUnitDynamicSimulation.inheritFrom: No Description # Attributes: # - Default: RobotConfig diff --git a/scenarios/NavigationSimulation/config/HandUnitDynamicSimulationApp.RightHand.cfg b/scenarios/NavigationSimulation/config/HandUnitDynamicSimulationApp.RightHand.cfg index 85cc434517acbe8d8f2fe87dcf357b4d4f86823d..cf952e6fb103f0ce16f4a4fdcf2f86c0b5208e74 100644 --- a/scenarios/NavigationSimulation/config/HandUnitDynamicSimulationApp.RightHand.cfg +++ b/scenarios/NavigationSimulation/config/HandUnitDynamicSimulationApp.RightHand.cfg @@ -76,6 +76,14 @@ ArmarX.ApplicationName = RightHandUnitApp # ArmarX.EnableProfiling = false +# ArmarX.HandUnitDynamicSimulation.ArVizTopicName: Name of the ArViz topic +# Attributes: +# - Default: ArVizTopic +# - Case sensitivity: yes +# - Required: no +# ArmarX.HandUnitDynamicSimulation.ArVizTopicName = ArVizTopic + + # ArmarX.HandUnitDynamicSimulation.EnableProfiling: enable profiler which is used for logging performance events # Attributes: # - Default: false @@ -131,6 +139,13 @@ ArmarX.HandUnitDynamicSimulation.ObjectName = RightHandUnit # ArmarX.HandUnitDynamicSimulation.RobotStateComponentName = RobotStateComponent +# ArmarX.HandUnitDynamicSimulation.Side: Name of the hand (left, right) +# Attributes: +# - Case sensitivity: yes +# - Required: yes +# ArmarX.HandUnitDynamicSimulation.Side = ::_NOT_SET_:: + + # ArmarX.HandUnitDynamicSimulation.SimulatorProxyName: Name of the simulator proxy to use. # Attributes: # - Default: Simulator @@ -139,6 +154,14 @@ ArmarX.HandUnitDynamicSimulation.ObjectName = RightHandUnit # ArmarX.HandUnitDynamicSimulation.SimulatorProxyName = Simulator +# ArmarX.HandUnitDynamicSimulation.WorkingMemoryName: Name of the working memory that should be used +# Attributes: +# - Default: WorkingMemory +# - Case sensitivity: yes +# - Required: no +# ArmarX.HandUnitDynamicSimulation.WorkingMemoryName = WorkingMemory + + # ArmarX.HandUnitDynamicSimulation.inheritFrom: No Description # Attributes: # - Default: RobotConfig diff --git a/scenarios/NavigationSimulation/config/LongtermMemory.cfg b/scenarios/NavigationSimulation/config/LongtermMemory.cfg index 0eae3f1ab86c16681c70d88228002d456dcffa33..e323230d5da82a370611eec3fad6358f913ce064 100644 --- a/scenarios/NavigationSimulation/config/LongtermMemory.cfg +++ b/scenarios/NavigationSimulation/config/LongtermMemory.cfg @@ -100,14 +100,6 @@ MemoryX.LongtermMemory.ClassCollections = memdb.Longterm_Objects -# MemoryX.LongtermMemory.CommonStorageName: Name of common storage. -# Attributes: -# - Default: CommonStorage -# - Case sensitivity: yes -# - Required: no -# MemoryX.LongtermMemory.CommonStorageName = CommonStorage - - # MemoryX.LongtermMemory.DatabaseName: Mongo database to store LTM data # Attributes: # - Case sensitivity: yes @@ -173,14 +165,6 @@ MemoryX.LongtermMemory.DatabaseName = CeBITdb # MemoryX.LongtermMemory.PredictionDataCollection = ltm_predictiondata -# MemoryX.LongtermMemory.PriorKnowledgeName: Name of prior knowledge. -# Attributes: -# - Default: PriorKnowledge -# - Case sensitivity: yes -# - Required: no -# MemoryX.LongtermMemory.PriorKnowledgeName = PriorKnowledge - - # MemoryX.LongtermMemory.ProfilerDataCollection: Mongo collection for storing Profiler data # Attributes: # - Default: ltm_profilerdata diff --git a/scenarios/NavigationSimulation/config/PriorKnowledge.cfg b/scenarios/NavigationSimulation/config/PriorKnowledge.cfg index d2a8fe85f077987cf9406710c2f49917fac04917..c44a2b83dc96d79b137906e8dd978541b343c8c2 100644 --- a/scenarios/NavigationSimulation/config/PriorKnowledge.cfg +++ b/scenarios/NavigationSimulation/config/PriorKnowledge.cfg @@ -99,14 +99,6 @@ MemoryX.PriorKnowledge.ClassCollections = CeBITdb.Prior_CeBIT -# MemoryX.PriorKnowledge.CommonStorageName: Name of common storage. -# Attributes: -# - Default: CommonStorage -# - Case sensitivity: yes -# - Required: no -# MemoryX.PriorKnowledge.CommonStorageName = CommonStorage - - # MemoryX.PriorKnowledge.EnableProfiling: enable profiler which is used for logging performance events # Attributes: # - Default: false diff --git a/scenarios/NavigationSimulation/config/WorkingMemory.cfg b/scenarios/NavigationSimulation/config/WorkingMemory.cfg index a6115c9f6b1e95a8d4a73aae1097813fdc128e58..f33e4b8bc9dee1a831a65ab858d3c47cb4d09019 100644 --- a/scenarios/NavigationSimulation/config/WorkingMemory.cfg +++ b/scenarios/NavigationSimulation/config/WorkingMemory.cfg @@ -272,14 +272,6 @@ MemoryX.ObjectLocalizationMemoryUpdater.WorkingMemoryName = "WorkingMemory" # MemoryX.Verbosity = Info -# MemoryX.WorkingMemory.CommonStorageName: Name of common storage. -# Attributes: -# - Default: CommonStorage -# - Case sensitivity: yes -# - Required: no -# MemoryX.WorkingMemory.CommonStorageName = CommonStorage - - # MemoryX.WorkingMemory.EnableProfiling: enable profiler which is used for logging performance events # Attributes: # - Default: false diff --git a/source/armarx/navigation/algorithms/CMakeLists.txt b/source/armarx/navigation/algorithms/CMakeLists.txt index d9ff35889b35886ee08ce6211cb04eb3f2404829..4b408d87b1b9381e23da99ec7846eeaa66ce41b3 100644 --- a/source/armarx/navigation/algorithms/CMakeLists.txt +++ b/source/armarx/navigation/algorithms/CMakeLists.txt @@ -9,7 +9,9 @@ armarx_add_library(algorithms OpenCV SOURCES ./algorithms.cpp - ./NavigationCostMap.cpp + ./Costmap.cpp + ./CostmapBuilder.cpp + ./util.cpp # A* ./astar/AStarPlanner.cpp ./astar/Node.cpp @@ -22,7 +24,9 @@ armarx_add_library(algorithms ./smoothing/CircularPathSmoothing.cpp HEADERS ./algorithms.h - ./NavigationCostMap.h + ./Costmap.h + ./CostmapBuilder.h + ./util.h # A* ./astar/AStarPlanner.h ./astar/Node.h diff --git a/source/armarx/navigation/algorithms/Costmap.cpp b/source/armarx/navigation/algorithms/Costmap.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bf9cad9738bc917d868012fa2ad561ccf6dd8d7c --- /dev/null +++ b/source/armarx/navigation/algorithms/Costmap.cpp @@ -0,0 +1,175 @@ +#include "Costmap.h" + +#include <algorithm> +#include <cmath> +#include <limits> + +#include <boost/geometry.hpp> +#include <boost/geometry/algorithms/detail/intersects/interface.hpp> + +#include <Eigen/Core> +#include <Eigen/Geometry> + +#include <IceUtil/Time.h> + +#include <VirtualRobot/BoundingBox.h> +#include <VirtualRobot/CollisionDetection/CollisionChecker.h> +#include <VirtualRobot/CollisionDetection/CollisionModel.h> +#include <VirtualRobot/Visualization/CoinVisualization/CoinVisualizationFactory.h> +#include <VirtualRobot/XML/ObjectIO.h> + +#include <ArmarXCore/core/exceptions/local/ExpressionException.h> +#include <ArmarXCore/core/logging/Logging.h> + +#include "armarx/navigation/algorithms/types.h" +#include "armarx/navigation/algorithms/util.h" +#include <armarx/navigation/conversions/eigen.h> + + +namespace armarx::navigation::algorithms +{ + + Costmap::Costmap(const Grid& grid, + const Parameters& parameters, + const SceneBounds& sceneBounds, + const std::optional<Mask>& mask) : + grid(grid), mask(mask), sceneBounds(sceneBounds), parameters(parameters) + { + if (mask.has_value()) + { + ARMARX_CHECK_EQUAL(grid.rows(), mask->rows()); + ARMARX_CHECK_EQUAL(grid.cols(), mask->cols()); + } + } + + Costmap::Position + Costmap::toPosition(const Costmap::Index& index) const + { + Eigen::Vector2f pos; + pos.x() = sceneBounds.min.x() + index.x() * parameters.cellSize + parameters.cellSize / 2; + pos.y() = sceneBounds.min.y() + index.y() * parameters.cellSize + parameters.cellSize / 2; + + return pos; + } + + Costmap::Vertex + Costmap::toVertex(const Eigen::Vector2f& v) const + { + const float vX = + (v.x() - parameters.cellSize / 2 - sceneBounds.min.x()) / parameters.cellSize; + const float vY = + (v.y() - parameters.cellSize / 2 - sceneBounds.min.y()) / parameters.cellSize; + + const int iX = std::round(vX); + const int iY = std::round(vY); + + const int iXSan = std::clamp<int>(iX, 0, grid.rows() - 1); + const int iYSan = std::clamp<int>(iY, 0, grid.cols() - 1); + + // FIXME accept one cell off + + // ARMARX_CHECK_GREATER(iX, 0); + // ARMARX_CHECK_GREATER(iY, 0); + + // ARMARX_CHECK_LESS_EQUAL(iX, grid.rows() - 1); + // ARMARX_CHECK_LESS_EQUAL(iY, grid.cols() - 1); + + return Vertex{.index = Index{iXSan, iYSan}, .position = v}; + } + + const SceneBounds& + Costmap::getSceneBounds() const noexcept + { + return sceneBounds; + } + + Costmap::Index + Costmap::minIndex() const noexcept + { + // index of the min element + Grid::Index row = 0; + Grid::Index col = 0; + + // value of the min element + float minVal = std::numeric_limits<float>::max(); + + for (int r = 0; r < grid.rows(); r++) + { + for (int c = 0; c < grid.cols(); c++) + { + if (mask.has_value()) + { + if (not mask.value()(r, c)) // skip invalid cells + { + continue; + } + + const float currentVal = grid(r, c); + if (currentVal >= minVal) + { + minVal = currentVal; + row = r; + col = c; + } + } + } + } + + + return Index{row, col}; + } + + const Costmap::Parameters& + Costmap::params() const noexcept + { + return parameters; + } + + const Costmap::Grid& + Costmap::getGrid() const + { + return grid; + } + + bool + Costmap::isInCollision(const Position& p) const + { + const auto v = toVertex(p); + return grid(v.index.x(), v.index.y()) == 0.F; + } + + bool + Costmap::add(const Costmap& other) + { + const auto startIdx = toVertex(other.sceneBounds.min); + + // merge other grid into this one => use max costs + + // TODO if one of both matrices has mask, it has to be considered. + ARMARX_TRACE; + grid.block(startIdx.index.x(), startIdx.index.y(), other.grid.rows(), other.grid.cols()) = + grid.block(startIdx.index.x(), startIdx.index.y(), other.grid.rows(), other.grid.cols()) + .cwiseMax(other.grid); + + if (other.mask.has_value()) + { + if (not mask.has_value()) + { + mask = Mask(grid.rows(), grid.cols()); + mask->setOnes(); + } + + // union of masks + ARMARX_TRACE; + mask = mask->cwiseMin(other.mask->value()); + + // apply mask to grid => all invalid points will be 0 + ARMARX_TRACE; + grid = grid.cwiseProduct(mask->cast<float>()); + } + + return true; + } + + +} // namespace armarx::navigation::algorithms diff --git a/source/armarx/navigation/algorithms/Costmap.h b/source/armarx/navigation/algorithms/Costmap.h new file mode 100644 index 0000000000000000000000000000000000000000..d35e77810c95bf7fbb0853b1def4ee44a6a7017a --- /dev/null +++ b/source/armarx/navigation/algorithms/Costmap.h @@ -0,0 +1,70 @@ +#pragma once + +#include <Eigen/Core> + +#include <armarx/navigation/algorithms/types.h> + +namespace armarx::navigation::algorithms +{ + + class Costmap + { + public: + EIGEN_MAKE_ALIGNED_OPERATOR_NEW + + friend class CostmapBuilder; + + struct Parameters + { + // if set to false, distance to obstacles will be computed and not only a binary collision check + bool binaryGrid{false}; + + /// How big each cell is in the uniform grid. + float cellSize = 100.f; + }; + + + using Index = Eigen::Array2i; + using Position = Eigen::Vector2f; + using Grid = Eigen::MatrixXf; + + // if 0, the cell is invalid + using Mask = Eigen::Matrix<bool, Eigen::Dynamic, Eigen::Dynamic>; + + Costmap(const Grid& grid, + const Parameters& parameters, + const SceneBounds& sceneBounds, + const std::optional<Mask>& mask = std::nullopt); + + struct Vertex + { + Index index; // row corresponds to y; column corresponds to x + Position position; + }; + + Position toPosition(const Index& index) const; + Vertex toVertex(const Position& v) const; + + const Grid& getGrid() const; + + bool isInCollision(const Position& p) const; + + const Parameters& params() const noexcept; + + const SceneBounds& getSceneBounds() const noexcept; + + Index minIndex() const noexcept; + + bool add(const Costmap& other); + + private: + Grid grid; + std::optional<Mask> mask; + + const SceneBounds sceneBounds; + + const Parameters parameters; + }; + + +} // namespace armarx::navigation::algorithms diff --git a/source/armarx/navigation/algorithms/CostmapBuilder.cpp b/source/armarx/navigation/algorithms/CostmapBuilder.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4fe02e875e5690dc4db9df66709f9e3dde3e3871 --- /dev/null +++ b/source/armarx/navigation/algorithms/CostmapBuilder.cpp @@ -0,0 +1,219 @@ +/** + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @author Fabian Reister ( fabian dot reister at kit dot edu ) + * @date 2022 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#include "CostmapBuilder.h" + +#include <cstddef> + +#include <VirtualRobot/CollisionDetection/CollisionChecker.h> +#include <VirtualRobot/Robot.h> + +#include "ArmarXCore/core/exceptions/local/ExpressionException.h" +#include "ArmarXCore/core/logging/Logging.h" +#include "ArmarXCore/util/CPPUtility/trace.h" + +#include "armarx/navigation/algorithms/types.h" +#include "armarx/navigation/algorithms/util.h" +#include "armarx/navigation/conversions/eigen.h" +#include "armarx/navigation/core/basic_types.h" + +namespace armarx::navigation::algorithms +{ + + + CostmapBuilder::CostmapBuilder(const VirtualRobot::RobotPtr& robot, + const VirtualRobot::SceneObjectSetPtr& obstacles, + const Costmap::Parameters& parameters, + const std::string& robotCollisonModelName) : + robot(robot), + obstacles(obstacles), + parameters(parameters), + robotCollisionModelName(robotCollisonModelName) + { + ARMARX_CHECK_NOT_NULL(robot) << "Robot must be set"; + ARMARX_CHECK_NOT_NULL(obstacles); + ARMARX_CHECK(robot->hasRobotNode(robotCollisionModelName)); + } + + Costmap + CostmapBuilder::create() + { + + const auto sceneBounds = computeSceneBounds(obstacles); + const auto grid = createUniformGrid(sceneBounds, parameters); + + ARMARX_INFO << "Creating grid"; + Costmap costmap(grid, parameters, sceneBounds); + + ARMARX_INFO << "Filling grid with size (" << costmap.getGrid().rows() << "/" + << costmap.getGrid().cols() << ")"; + fillGridCosts(costmap); + ARMARX_INFO << "Filled grid"; + + return costmap; + } + + + Eigen::MatrixXf + CostmapBuilder::createUniformGrid(const SceneBounds& sceneBounds, + const Costmap::Parameters& parameters) + { + ARMARX_TRACE; + + ARMARX_INFO << "Scene bounds are " << sceneBounds.min << " and " << sceneBounds.max; + + //+1 for explicit rounding up + size_t c_x = (sceneBounds.max.x() - sceneBounds.min.x()) / parameters.cellSize + 1; + size_t c_y = (sceneBounds.max.y() - sceneBounds.min.y()) / parameters.cellSize + 1; + + ARMARX_INFO << "Grid size: " << c_x << ", " << c_y; + + ARMARX_INFO << "Resetting grid"; + Eigen::MatrixXf grid(c_x, c_y); + grid.setZero(); + + return grid; + } + + + float + CostmapBuilder::computeCost(const Costmap::Position& position, + VirtualRobot::RobotPtr& collisionRobot, + const VirtualRobot::CollisionModelPtr& robotCollisionModel) + { + ARMARX_TRACE; + ARMARX_CHECK_NOT_NULL(collisionRobot); + ARMARX_CHECK_NOT_NULL(robotCollisionModel); + ARMARX_CHECK_NOT_NULL(VirtualRobot::CollisionChecker::getGlobalCollisionChecker()); + ARMARX_CHECK_NOT_NULL(obstacles); + + const core::Pose globalPose(Eigen::Translation3f(conv::to3D(position))); + collisionRobot->setGlobalPose(globalPose.matrix()); + + const float distance = + VirtualRobot::CollisionChecker::getGlobalCollisionChecker()->calculateDistance( + robotCollisionModel, obstacles); + + return distance; + + + // Eigen::Vector3f P1; + // Eigen::Vector3f P2; + // int id1, id2; + + // float minDistance = std::numeric_limits<float>::max(); + + // // TODO omp... + // for (size_t i = 0; i < obstacles->getSize(); i++) + // { + // // cheap collision check + // // VirtualRobot::BoundingBox obstacleBbox = + // // obstacles->getSceneObject(i)->getCollisionModel()->getBoundingBox(true); + // // if (not intersects(robotBbox, obstacleBbox)) + // // { + // // continue; + // // } + + // // precise collision check + // const float dist = + // VirtualRobot::CollisionChecker::getGlobalCollisionChecker()->calculateDistance( + // robotCollisionModel, + // obstacles->getSceneObject(i)->getCollisionModel(), + // P1, + // P2, + // &id1, + // &id2); + + // // check if objects collide + // if ((dist <= parameters.cellSize / 2) or + // VirtualRobot::CollisionChecker::getGlobalCollisionChecker()->checkCollision( + // robotCollisionModel, obstacles->getSceneObject(i)->getCollisionModel())) + // { + // minDistance = 0; + // break; + // } + + // minDistance = std::min(minDistance, dist); + // } + // // return n->position.x() >= sceneBounds.min.x() && n->position.x() <= sceneBounds.max.x() && + // // n->position.y() >= sceneBounds.min.y() && n->position.y() <= sceneBounds.max.y(); + + // return minDistance; + } + + + void + CostmapBuilder::fillGridCosts(Costmap& costmap) + { + + VirtualRobot::RobotPtr collisionRobot; + VirtualRobot::CollisionModelPtr robotCollisionModel; + + const std::size_t c_x = costmap.grid.rows(); + const std::size_t c_y = costmap.grid.cols(); + + robot->setUpdateVisualization(false); + +#pragma omp parallel for schedule(static) private(collisionRobot, \ + robotCollisionModel) default(shared) + for (unsigned int x = 0; x < c_x; x++) + { + // initialize if needed + if (collisionRobot == nullptr) + { +#pragma omp critical + { + ARMARX_DEBUG << "Copying robot"; + ARMARX_CHECK_NOT_NULL(robot); + collisionRobot = + robot->clone("collision_robot_" + std::to_string(omp_get_thread_num())); + collisionRobot->setUpdateVisualization(false); + ARMARX_DEBUG << "Copying done"; + } + + ARMARX_CHECK_NOT_NULL(collisionRobot); + ARMARX_CHECK(collisionRobot->hasRobotNode(robotCollisionModelName)); + + ARMARX_CHECK(collisionRobot); + const auto collisionRobotNode = + collisionRobot->getRobotNode(robotCollisionModelName); + ARMARX_CHECK_NOT_NULL(collisionRobotNode); + + robotCollisionModel = collisionRobotNode->getCollisionModel(); + ARMARX_CHECK(robotCollisionModel) << "Collision model not available. " + "Make sure that you load the robot correctly!"; + } + + ARMARX_CHECK_NOT_NULL(collisionRobot); + ARMARX_CHECK_NOT_NULL(robotCollisionModel); + + for (unsigned int y = 0; y < c_y; y++) + { + const Costmap::Index index{x, y}; + const Costmap::Position position = costmap.toPosition(index); + + costmap.grid(x, y) = computeCost(position, collisionRobot, robotCollisionModel); + } + } + } + + +} // namespace armarx::navigation::algorithms diff --git a/source/armarx/navigation/algorithms/CostmapBuilder.h b/source/armarx/navigation/algorithms/CostmapBuilder.h new file mode 100644 index 0000000000000000000000000000000000000000..3e1e4237cd1cf7bf92e99f08577ff3cfaeb75d17 --- /dev/null +++ b/source/armarx/navigation/algorithms/CostmapBuilder.h @@ -0,0 +1,57 @@ +/** + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @author Fabian Reister ( fabian dot reister at kit dot edu ) + * @date 2022 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#pragma once + +#include <VirtualRobot/VirtualRobot.h> + +#include <armarx/navigation/algorithms/Costmap.h> + +namespace armarx::navigation::algorithms +{ + + class CostmapBuilder + { + public: + CostmapBuilder(const VirtualRobot::RobotPtr& robot, + const VirtualRobot::SceneObjectSetPtr& obstacles, + const Costmap::Parameters& parameters, + const std::string& robotCollisonModelName); + + [[nodiscard]] Costmap create(); + + static Eigen::MatrixXf createUniformGrid(const SceneBounds& sceneBounds, + const Costmap::Parameters& parameters); + + private: + float computeCost(const Costmap::Position& position, + VirtualRobot::RobotPtr& collisionRobot, + const VirtualRobot::CollisionModelPtr& robotCollisionModel); + + void fillGridCosts(Costmap& costmap); + + const VirtualRobot::RobotPtr robot; + const VirtualRobot::SceneObjectSetPtr obstacles; + const Costmap::Parameters parameters; + const std::string robotCollisionModelName; + }; + +} // namespace armarx::navigation::algorithms diff --git a/source/armarx/navigation/algorithms/NavigationCostMap.cpp b/source/armarx/navigation/algorithms/NavigationCostMap.cpp deleted file mode 100644 index 9d994ecbb31171fe73130611ea04cd934f9b1003..0000000000000000000000000000000000000000 --- a/source/armarx/navigation/algorithms/NavigationCostMap.cpp +++ /dev/null @@ -1,243 +0,0 @@ -#include "NavigationCostMap.h" - -#include <algorithm> -#include <cmath> -#include <limits> - -#include <boost/geometry.hpp> -#include <boost/geometry/algorithms/detail/intersects/interface.hpp> - -#include <Eigen/Geometry> - -#include <IceUtil/Time.h> - -#include <VirtualRobot/BoundingBox.h> -#include <VirtualRobot/CollisionDetection/CollisionChecker.h> -#include <VirtualRobot/CollisionDetection/CollisionModel.h> -#include <VirtualRobot/Visualization/CoinVisualization/CoinVisualizationFactory.h> -#include <VirtualRobot/XML/ObjectIO.h> - -#include <ArmarXCore/core/exceptions/local/ExpressionException.h> -#include <ArmarXCore/core/logging/Logging.h> - -#include <armarx/navigation/conversions/eigen.h> - -namespace bg = boost::geometry; - -namespace armarx::navigation::algorithm -{ - - NavigationCostMap::NavigationCostMap(VirtualRobot::RobotPtr robot, - VirtualRobot::SceneObjectSetPtr obstacles, - const Parameters& parameters) : - robot(robot), obstacles(obstacles), parameters(parameters) - { - } - - NavigationCostMap::Position - NavigationCostMap::toPosition(const NavigationCostMap::Index& index) const - { - Eigen::Vector2f pos; - // TODO check if x and y must be switched - pos.x() = sceneBoundsMin.x() + index.x() * parameters.cellSize + parameters.cellSize / 2; - pos.y() = sceneBoundsMin.y() + index.y() * parameters.cellSize + parameters.cellSize / 2; - - return pos; - } - - void - NavigationCostMap::createUniformGrid() - { - ARMARX_TRACE; - - if (obstacles) - { - for (size_t i = 0; i < obstacles->getCollisionModels().size(); i++) - { - VirtualRobot::BoundingBox bb = obstacles->getCollisionModels()[i]->getBoundingBox(); - sceneBoundsMin.x() = std::min(bb.getMin().x(), sceneBoundsMin.x()); - sceneBoundsMin.y() = std::min(bb.getMin().y(), sceneBoundsMin.y()); - sceneBoundsMax.x() = std::max(bb.getMax().x(), sceneBoundsMax.x()); - sceneBoundsMax.y() = std::max(bb.getMax().y(), sceneBoundsMax.y()); - } - } - - ARMARX_INFO << "Scene bounds are " << sceneBoundsMin << " and " << sceneBoundsMax; - - //+1 for explicit rounding up - c_x = (sceneBoundsMax.x() - sceneBoundsMin.x()) / parameters.cellSize + 1; - c_y = (sceneBoundsMax.y() - sceneBoundsMin.y()) / parameters.cellSize + 1; - - ARMARX_INFO << "Grid size: " << c_y << ", " << c_x; - - ARMARX_INFO << "Resetting grid"; - grid = Eigen::MatrixXf(c_x, c_y); //.array(); - // grid = Eigen::ArrayXf(); //(c_x, c_y); - // grid->resize(c_x, c_y); - - ARMARX_INFO << "done."; - } - - - float - NavigationCostMap::computeCost(const NavigationCostMap::Position& position) - { - ARMARX_TRACE; - ARMARX_CHECK_NOT_NULL(robotCollisionModel); - ARMARX_CHECK_NOT_NULL(VirtualRobot::CollisionChecker::getGlobalCollisionChecker()); - ARMARX_CHECK_NOT_NULL(obstacles); - - const core::Pose globalPose = Eigen::Translation3f(conv::to3D(position)) * - Eigen::AngleAxisf(M_PI_2f32, Eigen::Vector3f::UnitX()); - - // auto colModel = robotCollisionModel->clone(); - // colModel->setGlobalPose(globalPose.matrix()); - - robotCollisionModel->setGlobalPose(globalPose.matrix()); - - // Unused. - if (false) - { - VirtualRobot::BoundingBox robotBbox = robotCollisionModel->getBoundingBox(true); - (void) robotBbox; - } - - Eigen::Vector3f P1; - Eigen::Vector3f P2; - int id1, id2; - - float minDistance = std::numeric_limits<float>::max(); - - // TODO omp... - for (size_t i = 0; i < obstacles->getSize(); i++) - { - // cheap collision check - // VirtualRobot::BoundingBox obstacleBbox = - // obstacles->getSceneObject(i)->getCollisionModel()->getBoundingBox(true); - // if (not intersects(robotBbox, obstacleBbox)) - // { - // continue; - // } - - // precise collision check - const float dist = - VirtualRobot::CollisionChecker::getGlobalCollisionChecker()->calculateDistance( - robotCollisionModel, - obstacles->getSceneObject(i)->getCollisionModel(), - P1, - P2, - &id1, - &id2); - - // check if objects collide - if ((dist <= parameters.cellSize / 2) or - VirtualRobot::CollisionChecker::getGlobalCollisionChecker()->checkCollision( - robotCollisionModel, obstacles->getSceneObject(i)->getCollisionModel())) - { - minDistance = 0; - break; - } - - minDistance = std::min(minDistance, dist); - } - // return n->position.x() >= sceneBoundsMin.x() && n->position.x() <= sceneBoundsMax.x() && - // n->position.y() >= sceneBoundsMin.y() && n->position.y() <= sceneBoundsMax.y(); - - return minDistance; - } - - NavigationCostMap::Vertex - NavigationCostMap::toVertex(const Eigen::Vector2f& v) const - { - const float v_x = (v.x() - parameters.cellSize / 2 - sceneBoundsMin.x()) / parameters.cellSize; - const float v_y = (v.y() - parameters.cellSize / 2 - sceneBoundsMin.y()) / parameters.cellSize; - - ARMARX_CHECK(v_x >= 0.F); - ARMARX_CHECK(v_y >= 0.F); - - ARMARX_CHECK(v_x <= (c_x - 1)); - ARMARX_CHECK(v_y <= (c_y - 1)); - - return Vertex{.index = Index{static_cast<int>(v_x), static_cast<int>(v_y)}, .position = v}; - } - - bool NavigationCostMap::isInCollision(const Position& p) const - { - const auto v = toVertex(p); - - return grid.value()(v.index.x(), v.index.y()) == 0.F; - } - - - void - NavigationCostMap::createCostmap() - { - ARMARX_TRACE; - std::vector<Eigen::Vector2f> result; - - ARMARX_CHECK_NOT_NULL(robot) << "Robot must be set"; - - - VirtualRobot::CoinVisualizationFactory factory; - const auto cylinder = factory.createCylinder(1500.F / 2, 2000.F); - - VirtualRobot::CollisionModelPtr collisionModel(new VirtualRobot::CollisionModel(cylinder)); - - - robotCollisionModel = collisionModel; - - - ARMARX_CHECK_NOT_NULL(robotCollisionModel) << "Collision model must be set!"; - - // ARMARX_CHECK(robot->hasRobotNode(robotColModelName)) << "No robot node " << robotColModelName; - // ARMARX_CHECK(robot->getRobotNode(robotColModelName)->getCollisionModel()) - // << "No collision model for robot node " << robotColModelName; - - // robotCollisionModel = robot->getRobotNode(robotColModelName)->getCollisionModel()->clone(); - robotCollisionModel->setGlobalPose(robot->getGlobalPose()); - - // static auto cylinder = VirtualRobot::ObjectIO::loadObstacle("/home/fabi/cylinder.xml"); - // ARMARX_CHECK_NOT_NULL(cylinder); - // cylinder->setGlobalPose(robot->getGlobalPose()); - - // robotCollisionModel = cylinder->getCollisionModel(); - - ARMARX_CHECK_NOT_NULL(robotCollisionModel); - - ARMARX_INFO << "Creating grid"; - createUniformGrid(); - - ARMARX_INFO << "Filling grid"; - fillGridCosts(); - - ARMARX_INFO << "Filled grid"; - } - - void - NavigationCostMap::setRobotCollisionModel(const VirtualRobot::CollisionModelPtr& collisionModel) - { - robotCollisionModel = collisionModel; - } - - void - NavigationCostMap::fillGridCosts() - { - int i = 0; - - for (unsigned int x = 0; x < c_x; x++) - { - for (unsigned int y = 0; y < c_y; y++) - { - const Index index{x, y}; - const Position position = toPosition(index); - - grid.value()(x, y) = computeCost(position); - - i++; - - ARMARX_INFO << i << "/" << c_x * c_y; - } - } - } - -} // namespace armarx::navigation::algorithm::astar diff --git a/source/armarx/navigation/algorithms/NavigationCostMap.h b/source/armarx/navigation/algorithms/NavigationCostMap.h deleted file mode 100644 index 75328ef436d6593ae05dbdef533ea34d03b682f7..0000000000000000000000000000000000000000 --- a/source/armarx/navigation/algorithms/NavigationCostMap.h +++ /dev/null @@ -1,92 +0,0 @@ -#pragma once - -#include <cstddef> - -#include <Eigen/Core> - -#include <VirtualRobot/CollisionDetection/CollisionModel.h> - - -namespace armarx::navigation::algorithm -{ - - /** - * The A* planner - */ - class NavigationCostMap - { - public: - EIGEN_MAKE_ALIGNED_OPERATOR_NEW - - struct Parameters - { - // if set to false, distance to obstacles will be computed and not only a binary collision check - bool binaryGrid{false}; - - /// How big each cell is in the uniform grid. - float cellSize = 100.f; - }; - - - using Index = Eigen::Array2i; - using Position = Eigen::Vector2f; - using Grid = Eigen::MatrixXf; - - NavigationCostMap(VirtualRobot::RobotPtr robot, - VirtualRobot::SceneObjectSetPtr obstacles, - const Parameters& parameters); - - NavigationCostMap(const Grid& grid, - const Parameters& parameters, - const Eigen::Vector2f& sceneBoundsMin) : - grid(grid), sceneBoundsMin(sceneBoundsMin), parameters(parameters) - { - } - - void createCostmap(); - - void setRobotCollisionModel(const VirtualRobot::CollisionModelPtr& collisionModel); - - - struct Vertex - { - Index index; // row corresponds to y; column corresponds to x - Position position; - }; - - Position toPosition(const Index& index) const; - - const Grid& - getGrid() const - { - return grid.value(); - } - - Vertex toVertex(const Position& v) const; - - bool isInCollision(const Position& p) const; - - - private: - void createUniformGrid(); - float computeCost(const NavigationCostMap::Position& position); - - void fillGridCosts(); - - private: - std::optional<Grid> grid; - - size_t c_x = 0; - size_t c_y = 0; - - VirtualRobot::RobotPtr robot; - VirtualRobot::SceneObjectSetPtr obstacles; - - VirtualRobot::CollisionModelPtr robotCollisionModel; - - Eigen::Vector2f sceneBoundsMin; - Eigen::Vector2f sceneBoundsMax; - - const Parameters parameters; - }; -} // namespace armarx::navigation::algorithm diff --git a/source/armarx/navigation/algorithms/astar/AStarPlanner.cpp b/source/armarx/navigation/algorithms/astar/AStarPlanner.cpp index 3bb7aad49855ded307d14a346d1d714dc275f62b..10d338ec0945a10986903c2f4e265458ee33a13a 100644 --- a/source/armarx/navigation/algorithms/astar/AStarPlanner.cpp +++ b/source/armarx/navigation/algorithms/astar/AStarPlanner.cpp @@ -43,12 +43,8 @@ namespace armarx::navigation::algorithm::astar return bg::intersects(box1, box2); } - AStarPlanner::AStarPlanner(VirtualRobot::RobotPtr robot, - VirtualRobot::SceneObjectSetPtr obstacles, - float cellSize) : - Planner2D(robot, obstacles), cellSize(cellSize) + AStarPlanner::AStarPlanner(const algorithms::Costmap& costmap) : costmap(costmap) { - ARMARX_CHECK_GREATER_EQUAL(static_cast<int>(obstacles->getSize()), 0); } float @@ -57,72 +53,34 @@ namespace armarx::navigation::algorithm::astar return (1.F - obstacleDistanceWeightFactor) * (n1->position - n2->position).norm(); } - float - AStarPlanner::computeObstacleDistance(const Eigen::Vector2f& pos, - VirtualRobot::RobotPtr& robot) const - { - - core::Pose global_T_robot = core::Pose::Identity(); - global_T_robot.translation().head<2>() = pos; - robot->setGlobalPose(global_T_robot.matrix()); - - const auto robotCollisionModel = - robot->getRobotNode(robotColModelName)->getCollisionModel(); - - const float distance = - VirtualRobot::CollisionChecker::getGlobalCollisionChecker()->calculateDistance( - robotCollisionModel, obstacles); - - return distance; - } - void AStarPlanner::createUniformGrid() { ARMARX_TRACE; - ARMARX_VERBOSE << "Scene bounds are " << sceneBoundsMin << " and " << sceneBoundsMax; - - //+1 for explicit rounding up - cols = (sceneBoundsMax.x() - sceneBoundsMin.x()) / cellSize + 1; - rows = (sceneBoundsMax.y() - sceneBoundsMin.y()) / cellSize + 1; - - ARMARX_VERBOSE << "Grid size: " << rows << ", " << cols; + const size_t rows = costmap.getGrid().rows(); + const size_t cols = costmap.getGrid().cols(); // create the grid with the correct size + ARMARX_INFO << "Creating grid"; for (size_t r = 0; r < rows; r++) { grid.push_back(std::vector<NodePtr>(cols)); } - ARMARX_INFO << "Using " << omp_get_num_threads() << " threads to build grid"; - - VirtualRobot::RobotPtr collisionRobot; - -#pragma omp parallel for schedule(static) private(collisionRobot) default(shared) + // intitializing grid for (size_t r = 0; r < rows; r++) { - // initialize if needed - if (collisionRobot == nullptr) - { - collisionRobot = - robot->clone("collision_robot_" + std::to_string(omp_get_thread_num())); - collisionRobot->setUpdateVisualization(false); - } - for (size_t c = 0; c < cols; c++) { - Eigen::Vector2f pos; - pos.x() = sceneBoundsMin.x() + c * cellSize + cellSize / 2; - pos.y() = sceneBoundsMin.y() + r * cellSize + cellSize / 2; - - const float obstacleDistance = computeObstacleDistance(pos, robot); + const auto pos = costmap.toPosition({r, c}); + const float obstacleDistance = costmap.getGrid()(r, c); grid[r][c] = std::make_shared<Node>(pos, obstacleDistance); } } - ARMARX_INFO << "Finished initialization of grid"; + ARMARX_INFO << "Creating graph"; // Init successors for (size_t r = 0; r < rows; r++) @@ -140,24 +98,26 @@ namespace armarx::navigation::algorithm::astar { for (int nC = -1; nC <= 1; nC++) { - const int neighborIndexX = c + nC; - const int neighborIndexY = r + nR; - if (neighborIndexX < 0 || neighborIndexY < 0 || (nR == 0 && nC == 0)) + const int neighborIndexC = c + nC; + const int neighborIndexR = r + nR; + if (neighborIndexC < 0 || neighborIndexR < 0 || (nR == 0 && nC == 0)) { continue; } - if (neighborIndexY >= static_cast<int>(rows) || - neighborIndexX >= static_cast<int>(cols)) + if (neighborIndexR >= static_cast<int>(rows) || + neighborIndexC >= static_cast<int>(cols)) { continue; } - grid[neighborIndexY][neighborIndexX]->successors.push_back(candidate); + grid[neighborIndexR][neighborIndexC]->successors.push_back(candidate); } } } } + + ARMARX_INFO << "Done."; } bool @@ -165,51 +125,24 @@ namespace armarx::navigation::algorithm::astar { ARMARX_TRACE; ARMARX_CHECK_NOT_NULL(n); - ARMARX_CHECK_NOT_NULL(robotCollisionModel); - ARMARX_CHECK_NOT_NULL(VirtualRobot::CollisionChecker::getGlobalCollisionChecker()); - ARMARX_CHECK_NOT_NULL(obstacles); - - const core::Pose globalPose(Eigen::Translation3f(conv::to3D(n->position))); //* - //Eigen::AngleAxisf(M_PI_2f32, Eigen::Vector3f::UnitX()); - - robotCollisionModel->setGlobalPose(globalPose.matrix()); - - VirtualRobot::BoundingBox robotBbox = robotCollisionModel->getBoundingBox(true); - Eigen::Vector3f P1, P2; - for (size_t i = 0; i < obstacles->getSize(); i++) - { - // cheap collision check - VirtualRobot::BoundingBox obstacleBbox = - obstacles->getSceneObject(i)->getCollisionModel()->getBoundingBox(true); - if (not intersects(robotBbox, obstacleBbox)) - { - continue; - } - - // precise collision check - bool collision = - VirtualRobot::CollisionChecker::getGlobalCollisionChecker()->checkCollision( - robotCollisionModel, obstacles->getSceneObject(i)->getCollisionModel()); - - if (collision) - { - return false; - } - } - return n->position.x() >= sceneBoundsMin.x() && n->position.x() <= sceneBoundsMax.x() && - n->position.y() >= sceneBoundsMin.y() && n->position.y() <= sceneBoundsMax.y(); + return not costmap.isInCollision(n->position); } NodePtr AStarPlanner::closestNode(const Eigen::Vector2f& v) { - float r = (v.y() - cellSize / 2 - sceneBoundsMin.y()) / cellSize; - float c = (v.x() - cellSize / 2 - sceneBoundsMin.x()) / cellSize; + const auto vertex = costmap.toVertex(v); + + const int r = vertex.index.x(); + const int c = vertex.index.y(); ARMARX_CHECK_GREATER_EQUAL(r, 0.F); ARMARX_CHECK_GREATER_EQUAL(c, 0.F); + const int rows = static_cast<int>(costmap.getGrid().rows()); + const int cols = static_cast<int>(costmap.getGrid().cols()); + ARMARX_CHECK_LESS_EQUAL(r, rows - 1); ARMARX_CHECK_LESS_EQUAL(c, cols - 1); @@ -223,23 +156,6 @@ namespace armarx::navigation::algorithm::astar grid.clear(); std::vector<Eigen::Vector2f> result; - ARMARX_CHECK_NOT_NULL(robot); - - // ARMARX_CHECK(robot->hasRobotNode(robotColModelName)) << "No robot node " << robotColModelName; - // ARMARX_CHECK(robot->getRobotNode(robotColModelName)->getCollisionModel()) - // << "No collision model for robot node " << robotColModelName; - - // robotCollisionModel = robot->getRobotNode(robotColModelName)->getCollisionModel()->clone(); - ARMARX_CHECK_NOT_NULL(robotCollisionModel); - robotCollisionModel->setGlobalPose(robot->getGlobalPose()); - - // static auto cylinder = VirtualRobot::ObjectIO::loadObstacle("/home/fabi/cylinder.xml"); - // ARMARX_CHECK_NOT_NULL(cylinder); - // cylinder->setGlobalPose(robot->getGlobalPose()); - - // robotCollisionModel = cylinder->getCollisionModel(); - - createUniformGrid(); ARMARX_DEBUG << "Setting start node for position " << start; NodePtr nodeStart = closestNode(start); @@ -315,13 +231,6 @@ namespace armarx::navigation::algorithm::astar return result; } - void - AStarPlanner::setRobotCollisionModel(const std::string& collisionModelNodeName) - { - robotColModelName = collisionModelNodeName; - robotCollisionModel = robot->getRobotNode(collisionModelNodeName)->getCollisionModel(); - } - float AStarPlanner::costs(const NodePtr& n1, const NodePtr& n2) const { diff --git a/source/armarx/navigation/algorithms/astar/AStarPlanner.h b/source/armarx/navigation/algorithms/astar/AStarPlanner.h index 5737f62860491d37021af9b463f053eaa127aa96..872560850b4393e0275d6b86cf7c1b3c7688745a 100644 --- a/source/armarx/navigation/algorithms/astar/AStarPlanner.h +++ b/source/armarx/navigation/algorithms/astar/AStarPlanner.h @@ -6,6 +6,7 @@ #include "Node.h" #include "Planner2D.h" +#include "armarx/navigation/algorithms/Costmap.h" namespace armarx::navigation::algorithm::astar { @@ -13,18 +14,15 @@ namespace armarx::navigation::algorithm::astar /** * The A* planner */ - class AStarPlanner : public Planner2D + class AStarPlanner //: public Planner2D { public: EIGEN_MAKE_ALIGNED_OPERATOR_NEW - AStarPlanner(VirtualRobot::RobotPtr robot, - VirtualRobot::SceneObjectSetPtr obstacles = {}, - float cellSize = 100.f); + AStarPlanner(const algorithms::Costmap& costmap); - std::vector<Eigen::Vector2f> plan(const Eigen::Vector2f& start, const Eigen::Vector2f& goal) override; - - void setRobotCollisionModel(const std::string& collisionModelNodeName); + std::vector<Eigen::Vector2f> plan(const Eigen::Vector2f& start, + const Eigen::Vector2f& goal) /* override */; private: float heuristic(const NodePtr& n1, const NodePtr& n2); @@ -34,16 +32,14 @@ namespace armarx::navigation::algorithm::astar float costs(const NodePtr& a, const NodePtr& b) const; - float computeObstacleDistance(const Eigen::Vector2f& pos, VirtualRobot::RobotPtr& robot) const; + float computeObstacleDistance(const Eigen::Vector2f& pos, + VirtualRobot::RobotPtr& robot) const; + private: - /// How big each cell is in the uniform grid. - float cellSize; + const algorithms::Costmap costmap; std::vector<std::vector<NodePtr>> grid; - size_t cols = 0; - size_t rows = 0; - const float obstacleDistanceWeightFactor = 0.5; const float maxObstacleDistance = 1500; // [mm] diff --git a/source/armarx/navigation/algorithms/astar/Planner2D.h b/source/armarx/navigation/algorithms/astar/Planner2D.h index 5fb3b7e6c9e67ed38c8434f0414eec34b03f28ea..cd7b0540989ea9dd91c4aa9475d77000028c344e 100644 --- a/source/armarx/navigation/algorithms/astar/Planner2D.h +++ b/source/armarx/navigation/algorithms/astar/Planner2D.h @@ -26,7 +26,8 @@ namespace armarx::navigation::algorithm::astar virtual ~Planner2D() = default; // planners implement this method - virtual std::vector<Eigen::Vector2f> plan(const Eigen::Vector2f& start, const Eigen::Vector2f& goal) = 0; + virtual std::vector<Eigen::Vector2f> plan(const Eigen::Vector2f& start, + const Eigen::Vector2f& goal) = 0; /// Update obstacles void setObstacles(VirtualRobot::SceneObjectSetPtr obstacles); diff --git a/source/armarx/navigation/algorithms/spfa/ShortestPathFasterAlgorithm.cpp b/source/armarx/navigation/algorithms/spfa/ShortestPathFasterAlgorithm.cpp index 8fd472833abe06e4adbfa992c69ab12d01aee2f7..248db17da23a3eee36ec94e9bc03a52f73d6672e 100644 --- a/source/armarx/navigation/algorithms/spfa/ShortestPathFasterAlgorithm.cpp +++ b/source/armarx/navigation/algorithms/spfa/ShortestPathFasterAlgorithm.cpp @@ -22,12 +22,15 @@ #include "ShortestPathFasterAlgorithm.h" #include <algorithm> +#include <numeric> #include <Eigen/Core> #include "ArmarXCore/core/exceptions/local/ExpressionException.h" #include "ArmarXCore/core/logging/Logging.h" +#include "armarx/navigation/algorithms/Costmap.h" + namespace armarx::navigation::algorithms::spfa { @@ -50,26 +53,26 @@ namespace armarx::navigation::algorithms::spfa return p; } - ShortestPathFasterAlgorithm::ShortestPathFasterAlgorithm( - const algorithm::NavigationCostMap& grid, - const Parameters& params) : - grid(grid), params(params) + ShortestPathFasterAlgorithm::ShortestPathFasterAlgorithm(const Costmap& grid, + const Parameters& params) : + costmap(grid), params(params) { - ARMARX_INFO << "Grid with size (" << grid.getGrid().rows() << ", " << grid.getGrid().cols() << ")."; + ARMARX_INFO << "Grid with size (" << grid.getGrid().rows() << ", " << grid.getGrid().cols() + << ")."; } std::vector<Eigen::Vector2f> ShortestPathFasterAlgorithm::plan(const Eigen::Vector2f& start, const Eigen::Vector2f& goal) { - ARMARX_CHECK(not grid.isInCollision(start)) << "Start is in collision"; - ARMARX_CHECK(not grid.isInCollision(goal)) << "Goal is in collision"; + ARMARX_CHECK(not costmap.isInCollision(start)) << "Start is in collision"; + ARMARX_CHECK(not costmap.isInCollision(goal)) << "Goal is in collision"; - const algorithm::NavigationCostMap::Vertex startVertex = grid.toVertex(start); - const auto goalVertex = Eigen::Vector2i{grid.toVertex(goal).index}; + const Costmap::Vertex startVertex = costmap.toVertex(start); + const auto goalVertex = Eigen::Vector2i{costmap.toVertex(goal).index}; ARMARX_INFO << "Planning from " << startVertex.index << " to " << goalVertex; - const Result result = spfa(grid.getGrid(), Eigen::Vector2i{startVertex.index}); + const Result result = spfa(costmap.getGrid(), Eigen::Vector2i{startVertex.index}); const auto isStart = [&startVertex](const Eigen::Vector2i& v) { return (v.array() == startVertex.index.array()).all(); }; @@ -101,7 +104,7 @@ namespace armarx::navigation::algorithms::spfa break; } - path.push_back(grid.toPosition(parent.array())); + path.push_back(costmap.toPosition(parent.array())); pt = &parent; } @@ -115,11 +118,19 @@ namespace armarx::navigation::algorithms::spfa return path; } + ShortestPathFasterAlgorithm::Result + ShortestPathFasterAlgorithm::spfa(const Eigen::Vector2f& start) + { + return spfa(costmap.getGrid(), costmap.toVertex(start).index); + } + ShortestPathFasterAlgorithm::Result ShortestPathFasterAlgorithm::spfa(const Eigen::MatrixXf& inputMap, const Eigen::Vector2i& source) const { + ARMARX_CHECK_GREATER(inputMap(source.x(), source.y()), 0.F) << "Start must not be in collision"; + const float eps = 1e-6; const int num_dirs = 8; @@ -152,9 +163,8 @@ namespace armarx::navigation::algorithms::spfa float* dists = new float[max_num_verts]; for (int i = 0; i < max_num_verts; ++i) dists[i] = inf; - int* parents = new int[max_num_verts](); - for (int i = 0; i < max_num_verts; ++i) - parents[i] = -1; + + std::vector<int> parents(max_num_verts, -1); // Build graph ARMARX_INFO << "Build graph"; @@ -177,15 +187,15 @@ namespace armarx::navigation::algorithms::spfa continue; - const float obstacleDistance1 = inputMap(row, col); - const float obstacleDistance2 = inputMap(ip, jp); + // const float obstacleDistance1 = std::min(inputMap(row, col), 1000.F); + const float obstacleDistance2 = std::min(inputMap(ip, jp), params.obstacleMaxDistance); const float travelCost = dir_lengths[k]; - const float obstacleDistanceCost = obstacleDistance1 - obstacleDistance2; + // const float obstacleDistanceCost = std::max(obstacleDistance1 - obstacleDistance2, 0.F) / costmap.params().cellSize; + const float targetDistanceCost = params.obstacleDistanceWeight * (params.obstacleMaxDistance - obstacleDistance2) / params.obstacleMaxDistance; - const float edgeCost = params.obstacleDistanceCosts - ? travelCost + obstacleDistanceCost - : travelCost; + const float edgeCost = + params.obstacleDistanceCosts ? travelCost + targetDistanceCost : travelCost; int e = ravel(v, edge_counts[v], max_edges_per_vert); edges[e] = vp; @@ -241,6 +251,8 @@ namespace armarx::navigation::algorithms::spfa Eigen::Vector2i{-1, -1})); } + int invalids = 0; + for (int row = 0; row < num_rows; ++row) { for (int col = 0; col < num_cols; ++col) @@ -248,15 +260,20 @@ namespace armarx::navigation::algorithms::spfa int u = ravel(row, col, num_cols); output_dists(row, col) = (dists[u] < inf - eps) * dists[u]; - if(parents[u] == -1) // no parent + if (parents[u] == -1) // no parent { + invalids++; continue; } - + output_parents.at(row).at(col) = unravel(parents[u], num_cols); } } + + ARMARX_INFO << "Fraction of invalid cells: (" << invalids << "/" << parents.size() << ")"; + + // Free memory delete[] edges; delete[] edge_counts; @@ -264,11 +281,9 @@ namespace armarx::navigation::algorithms::spfa delete[] in_queue; delete[] weights; delete[] dists; - delete[] parents; ARMARX_INFO << "Done."; return Result{.distances = output_dists, .parents = output_parents}; - // return py::make_tuple(output_dists, output_parents); } diff --git a/source/armarx/navigation/algorithms/spfa/ShortestPathFasterAlgorithm.h b/source/armarx/navigation/algorithms/spfa/ShortestPathFasterAlgorithm.h index d0c22c09d238372e9425d7c2ddde378d3e674693..810783e458d35afaebea915e615377b98fc795d5 100644 --- a/source/armarx/navigation/algorithms/spfa/ShortestPathFasterAlgorithm.h +++ b/source/armarx/navigation/algorithms/spfa/ShortestPathFasterAlgorithm.h @@ -25,7 +25,7 @@ #include <Eigen/Core> -#include <armarx/navigation/algorithms/NavigationCostMap.h> +#include <armarx/navigation/algorithms/Costmap.h> namespace armarx::navigation::algorithms::spfa @@ -36,11 +36,13 @@ namespace armarx::navigation::algorithms::spfa public: struct Parameters { - bool obstacleDistanceCosts{true}; + bool obstacleDistanceCosts = true; + float obstacleMaxDistance = 1000.F; + + float obstacleDistanceWeight = 3.F; }; - ShortestPathFasterAlgorithm(const algorithm::NavigationCostMap& grid, - const Parameters& params); + ShortestPathFasterAlgorithm(const Costmap& costmap, const Parameters& params); struct Result { @@ -53,7 +55,9 @@ namespace armarx::navigation::algorithms::spfa const Eigen::Vector2f& goal); - // protected: + Result spfa(const Eigen::Vector2f& start); + + // protected: /** * @brief Implementation highly inspired by github.com:jimmyyhwu/spatial-action-maps * @@ -65,7 +69,7 @@ namespace armarx::navigation::algorithms::spfa private: - const algorithm::NavigationCostMap grid; + const Costmap costmap; const Parameters params; }; diff --git a/source/armarx/navigation/algorithms/test/algorithms_spfa_test.cpp b/source/armarx/navigation/algorithms/test/algorithms_spfa_test.cpp index 97c57f6b120d55e2303b590cb23122aa6610db01..bea64e74c6330d292a870874308769ecefb62b61 100644 --- a/source/armarx/navigation/algorithms/test/algorithms_spfa_test.cpp +++ b/source/armarx/navigation/algorithms/test/algorithms_spfa_test.cpp @@ -42,7 +42,8 @@ #include "ArmarXCore/core/system/ArmarXDataPath.h" #include <ArmarXCore/core/logging/Logging.h> -#include "armarx/navigation/algorithms/NavigationCostMap.h" +#include "armarx/navigation/algorithms/Costmap.h" +#include "armarx/navigation/algorithms/types.h" #include <armarx/navigation/Test.h> #include <armarx/navigation/algorithms/spfa/ShortestPathFasterAlgorithm.h> @@ -51,15 +52,19 @@ BOOST_AUTO_TEST_CASE(testSPFA) const int rows = 100; const int cols = 200; + const int cellSize = 100; + Eigen::Vector2i start{50, 130}; Eigen::Vector2i goal{50, 100}; Eigen::MatrixXf obstacleDistances(rows, cols); obstacleDistances.setOnes(); - const armarx::navigation::algorithm::NavigationCostMap::Parameters params; - const armarx::navigation::algorithm::NavigationCostMap costmap( - obstacleDistances, params, Eigen::Vector2f::Zero()); + const armarx::navigation::algorithms::SceneBounds sceneBounds{ + .min = Eigen::Vector2f::Zero(), .max = Eigen::Vector2f{rows * cellSize, cols * cellSize}}; + + const armarx::navigation::algorithms::Costmap::Parameters params; + const armarx::navigation::algorithms::Costmap costmap(obstacleDistances, params, sceneBounds); ARMARX_INFO << "computing ..."; @@ -110,9 +115,11 @@ BOOST_AUTO_TEST_CASE(testSPFAPlan) Eigen::MatrixXf obstacleDistances(rows, cols); obstacleDistances.setOnes(); - const armarx::navigation::algorithm::NavigationCostMap::Parameters params{.cellSize = cellSize}; - const armarx::navigation::algorithm::NavigationCostMap costmap( - obstacleDistances, params, Eigen::Vector2f::Zero()); + const armarx::navigation::algorithms::SceneBounds sceneBounds{ + .min = Eigen::Vector2f::Zero(), .max = Eigen::Vector2f{rows * cellSize, cols * cellSize}}; + + const armarx::navigation::algorithms::Costmap::Parameters params{.cellSize = cellSize}; + const armarx::navigation::algorithms::Costmap costmap(obstacleDistances, params, sceneBounds); ARMARX_INFO << "computing ..."; armarx::navigation::algorithms::spfa::ShortestPathFasterAlgorithm::Parameters spfaParams{ @@ -152,9 +159,15 @@ BOOST_AUTO_TEST_CASE(testGrid) BOOST_REQUIRE_GT(cellSize, 0); std::vector<float> sceneBoundsMinV(j["scene_bounds"]["min"]); + std::vector<float> sceneBoundsMaxV(j["scene_bounds"]["max"]); BOOST_REQUIRE_EQUAL(sceneBoundsMinV.size(), 2); + BOOST_REQUIRE_EQUAL(sceneBoundsMaxV.size(), 2); const Eigen::Vector2f sceneBoundsMin{sceneBoundsMinV.at(0), sceneBoundsMinV.at(1)}; + const Eigen::Vector2f sceneBoundsMax{sceneBoundsMaxV.at(0), sceneBoundsMaxV.at(1)}; + + const armarx::navigation::algorithms::SceneBounds sceneBounds{.min = sceneBoundsMin, + .max = sceneBoundsMax}; BOOST_REQUIRE(std::filesystem::exists(testExrFilename)); @@ -163,9 +176,9 @@ BOOST_AUTO_TEST_CASE(testGrid) Eigen::MatrixXf obstacleDistances; cv::cv2eigen(mat, obstacleDistances); - const armarx::navigation::algorithm::NavigationCostMap::Parameters params{.cellSize = cellSize}; - const armarx::navigation::algorithm::NavigationCostMap costmap( - obstacleDistances, params, sceneBoundsMin); + + const armarx::navigation::algorithms::Costmap::Parameters params{.cellSize = cellSize}; + const armarx::navigation::algorithms::Costmap costmap(obstacleDistances, params, sceneBounds); Eigen::Vector2f start{1500, 0}; @@ -200,9 +213,15 @@ BOOST_AUTO_TEST_CASE(testSPFAPlanWObstacleDistance) BOOST_REQUIRE_GT(cellSize, 0); std::vector<float> sceneBoundsMinV(j["scene_bounds"]["min"]); + std::vector<float> sceneBoundsMaxV(j["scene_bounds"]["max"]); BOOST_REQUIRE_EQUAL(sceneBoundsMinV.size(), 2); + BOOST_REQUIRE_EQUAL(sceneBoundsMaxV.size(), 2); const Eigen::Vector2f sceneBoundsMin{sceneBoundsMinV.at(0), sceneBoundsMinV.at(1)}; + const Eigen::Vector2f sceneBoundsMax{sceneBoundsMaxV.at(0), sceneBoundsMaxV.at(1)}; + + const armarx::navigation::algorithms::SceneBounds sceneBounds{.min = sceneBoundsMin, + .max = sceneBoundsMax}; BOOST_REQUIRE(std::filesystem::exists(testExrFilename)); @@ -212,9 +231,8 @@ BOOST_AUTO_TEST_CASE(testSPFAPlanWObstacleDistance) cv::cv2eigen(mat, obstacleDistances); obstacleDistances = obstacleDistances.transpose(); - const armarx::navigation::algorithm::NavigationCostMap::Parameters params{.cellSize = cellSize}; - const armarx::navigation::algorithm::NavigationCostMap costmap( - obstacleDistances, params, sceneBoundsMin); + const armarx::navigation::algorithms::Costmap::Parameters params{.cellSize = cellSize}; + const armarx::navigation::algorithms::Costmap costmap(obstacleDistances, params, sceneBounds); ARMARX_INFO << "computing ..."; armarx::navigation::algorithms::spfa::ShortestPathFasterAlgorithm::Parameters spfaParams{ @@ -228,5 +246,4 @@ BOOST_AUTO_TEST_CASE(testSPFAPlanWObstacleDistance) ARMARX_INFO << "done."; BOOST_CHECK(not path.empty()); - } diff --git a/source/armarx/navigation/algorithms/types.h b/source/armarx/navigation/algorithms/types.h new file mode 100644 index 0000000000000000000000000000000000000000..4e685859661210610a8c20060b44c9bf62b1b2dc --- /dev/null +++ b/source/armarx/navigation/algorithms/types.h @@ -0,0 +1,35 @@ +/** + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @author Fabian Reister ( fabian dot reister at kit dot edu ) + * @date 2022 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#pragma once + +#include <Eigen/Core> + +namespace armarx::navigation::algorithms +{ + + struct SceneBounds + { + Eigen::Vector2f min = Eigen::Vector2f::Zero(); + Eigen::Vector2f max = Eigen::Vector2f::Zero(); + }; + +} // namespace armarx::navigation::algorithms diff --git a/source/armarx/navigation/algorithms/util.cpp b/source/armarx/navigation/algorithms/util.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a991a3d3019a504cbbf315b2edeb3bb3b96fe432 --- /dev/null +++ b/source/armarx/navigation/algorithms/util.cpp @@ -0,0 +1,175 @@ +/** + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @author Fabian Reister ( fabian dot reister at kit dot edu ) + * @date 2022 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#include "util.h" + +#include <algorithm> +#include <iterator> + +#include <opencv2/core/eigen.hpp> +#include <opencv2/imgproc.hpp> + +#include <SimoxUtility/algorithm/apply.hpp> +#include <VirtualRobot/BoundingBox.h> +#include <VirtualRobot/CollisionDetection/CollisionModel.h> +#include <VirtualRobot/SceneObjectSet.h> +#include <VirtualRobot/Workspace/WorkspaceGrid.h> + +#include "ArmarXCore/core/exceptions/local/ExpressionException.h" + +#include "armarx/navigation/algorithms/Costmap.h" +#include "armarx/navigation/algorithms/CostmapBuilder.h" +#include "armarx/navigation/algorithms/types.h" + +namespace armarx::navigation::algorithms +{ + + + SceneBounds + computeSceneBounds(const VirtualRobot::SceneObjectSetPtr& obstacles) + { + SceneBounds bounds; + + ARMARX_CHECK_NOT_NULL(obstacles); + ARMARX_CHECK(not obstacles->getCollisionModels().empty()); + + for (size_t i = 0; i < obstacles->getCollisionModels().size(); i++) + { + VirtualRobot::BoundingBox bb = obstacles->getCollisionModels()[i]->getBoundingBox(); + bounds.min.x() = std::min(bb.getMin().x(), bounds.min.x()); + bounds.min.y() = std::min(bb.getMin().y(), bounds.min.y()); + bounds.max.x() = std::max(bb.getMax().x(), bounds.max.x()); + bounds.max.y() = std::max(bb.getMax().y(), bounds.max.y()); + } + + return bounds; + } + + + SceneBounds + toSceneBounds(const VirtualRobot::WorkspaceGrid::Extends& extends) + { + return {.min = Eigen::Vector2f{extends.minX, extends.minY}, + .max = Eigen::Vector2f{extends.maxX, extends.maxY}}; + } + + SceneBounds + merge(const std::vector<SceneBounds>& sceneBounds) + { + SceneBounds bounds; + + const auto expandBounds = [&bounds](const SceneBounds& sb) + { + bounds.min.x() = std::min(bounds.min.x(), sb.min.x()); + bounds.min.y() = std::min(bounds.min.y(), sb.min.y()); + + bounds.max.x() = std::max(bounds.max.x(), sb.max.x()); + bounds.max.y() = std::max(bounds.max.y(), sb.max.y()); + + return bounds; + }; + + std::for_each(sceneBounds.begin(), sceneBounds.end(), expandBounds); + + return bounds; + } + + + Costmap + toCostmap(const VirtualRobot::WorkspaceGrid& workspaceGrid) + { + const Costmap::Grid grid(workspaceGrid.getCells().x, workspaceGrid.getCells().y); + const SceneBounds sceneBounds = toSceneBounds(workspaceGrid.getExtends()); + + const Costmap::Parameters parameters{.binaryGrid = false, + .cellSize = workspaceGrid.getDiscretizeSize()}; + + return {grid, parameters, sceneBounds}; + } + + + Costmap + mergeUnaligned(const std::vector<Costmap>& costmaps, + const std::vector<float>& weights, + float cellSize) + { + ARMARX_CHECK_EQUAL(costmaps.size(), weights.size()); + + // if unset, use the smallest cell size + if (cellSize < 0) + { + const auto compCellSize = [](const Costmap& a, const Costmap& b) -> bool + { return a.params().cellSize < b.params().cellSize; }; + + cellSize = + std::min_element(costmaps.begin(), costmaps.end(), compCellSize)->params().cellSize; + } + + // scale each costmap + std::vector<Costmap> scaledCostmaps = simox::alg::apply( + costmaps, + [&cellSize](const Costmap& costmap) -> Costmap { return scaleCostmap(costmap, cellSize); }); + + // merge costmaps into one + + // - combine scene bounds + std::vector<SceneBounds> sceneBoundsAll = simox::alg::apply( + scaledCostmaps, [](const Costmap& costmap) { return costmap.getSceneBounds(); }); + + const SceneBounds sceneBounds = merge(sceneBoundsAll); + + // - create grid + const auto largeGrid = CostmapBuilder::createUniformGrid( + sceneBounds, Costmap::Parameters{.binaryGrid = false, .cellSize = cellSize}); + + Costmap largeCostmap( + largeGrid, Costmap::Parameters{.binaryGrid = false, .cellSize = cellSize}, sceneBounds); + + // - add all costmaps to this one + std::for_each(scaledCostmaps.begin(), + scaledCostmaps.end(), + [&largeCostmap](const Costmap& costmap) { largeCostmap.add(costmap); }); + + // return merged grid + return largeCostmap; + } + + Costmap + scaleCostmap(const Costmap& costmap, float cellSize) + { + const float scale = cellSize / costmap.params().cellSize; + + cv::Mat src; + cv::eigen2cv(costmap.getGrid(), src); + + cv::Mat dst; + cv::resize(src, dst, cv::Size{0, 0}, scale, scale); + + Eigen::MatrixXf scaledGrid; + cv::cv2eigen(dst, scaledGrid); + + return {scaledGrid, + Costmap::Parameters{.binaryGrid = false, .cellSize = cellSize}, + costmap.getSceneBounds()}; + } + + +} // namespace armarx::navigation::algorithms diff --git a/source/armarx/navigation/algorithms/util.h b/source/armarx/navigation/algorithms/util.h new file mode 100644 index 0000000000000000000000000000000000000000..1a4d1d0e780e26c53adedf562d3d4123825492a7 --- /dev/null +++ b/source/armarx/navigation/algorithms/util.h @@ -0,0 +1,49 @@ + +/** + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @author Fabian Reister ( fabian dot reister at kit dot edu ) + * @date 2022 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#pragma once + +#include <VirtualRobot/VirtualRobot.h> +#include <VirtualRobot/Workspace/WorkspaceGrid.h> + +#include <armarx/navigation/algorithms/Costmap.h> +#include <armarx/navigation/algorithms/types.h> + + +namespace armarx::navigation::algorithms +{ + SceneBounds computeSceneBounds(const VirtualRobot::SceneObjectSetPtr& obstacles); + + SceneBounds toSceneBounds(const VirtualRobot::WorkspaceGrid::Extends& extends); + + SceneBounds merge(const std::vector<SceneBounds>& sceneBounds); + + + Costmap toCostmap(const VirtualRobot::WorkspaceGrid& workspaceGrid); + + Costmap mergeUnaligned(const std::vector<Costmap>& costmaps, + const std::vector<float>& weights, + float cellSize = -1); + + Costmap scaleCostmap(const Costmap& costmap, float cellSize); + +} // namespace armarx::navigation::algorithms diff --git a/source/armarx/navigation/client/NavigationStackConfig.cpp b/source/armarx/navigation/client/NavigationStackConfig.cpp index 6acdc7f0e8aaa925ab770f14cbe421fc1bc10358..bfaaf93b57a0a72b803a366fd5940a1d391989d5 100644 --- a/source/armarx/navigation/client/NavigationStackConfig.cpp +++ b/source/armarx/navigation/client/NavigationStackConfig.cpp @@ -10,7 +10,6 @@ #include <RobotAPI/libraries/aron/core/data/variant/container/Dict.h> #include <RobotAPI/libraries/aron/core/data/variant/primitive/String.h> - #include <armarx/navigation/core/constants.h> #include <armarx/navigation/core/types.h> @@ -24,7 +23,8 @@ namespace armarx::navigation::client return element; } - aron::data::dto::DictPtr NavigationStackConfig::toAron() const + aron::data::dto::DictPtr + NavigationStackConfig::toAron() const { ARMARX_CHECK(isValid()) << "The NavigationStackConfig is not valid as some elements are not set!"; diff --git a/source/armarx/navigation/client/services/MemorySubscriber.h b/source/armarx/navigation/client/services/MemorySubscriber.h index 059305ddf91b90020a878e85dd1de24088c99d99..c7d79e1471beb197c15084ed7385d7c6092d0b40 100644 --- a/source/armarx/navigation/client/services/MemorySubscriber.h +++ b/source/armarx/navigation/client/services/MemorySubscriber.h @@ -41,7 +41,7 @@ namespace armarx::navigation::client void onEntityUpdate(const armem::MemoryID& subscriptionID, const std::vector<armem::MemoryID>& snapshotIDs); - + private: const std::string callerId; armem::client::MemoryNameSystem& memoryNameSystem; diff --git a/source/armarx/navigation/components/GraphImportExport/CMakeLists.txt b/source/armarx/navigation/components/GraphImportExport/CMakeLists.txt index ec6c529d6c3b934de744e2834c3a58f06426f446..123769e24867dff0c0a6026262560197e8e514ae 100644 --- a/source/armarx/navigation/components/GraphImportExport/CMakeLists.txt +++ b/source/armarx/navigation/components/GraphImportExport/CMakeLists.txt @@ -1,10 +1,3 @@ -#find_package(MemoryX QUIET) -#armarx_build_if(MemoryX_FOUND "MemoryX not available") -#if(MemoryX_FOUND) -# target_include_directories(${LIB_NAME} PUBLIC ${MemoryX_INCLUDE_DIRS}) -#endif() - - armarx_add_component(GraphImportExport DEPENDENCIES # ArmarXCore diff --git a/source/armarx/navigation/components/GraphImportExport/GraphImportExport.cpp b/source/armarx/navigation/components/GraphImportExport/GraphImportExport.cpp index e10c6e3ff2c1bab4565e3db237d49d598ccaadbe..bf2bb976f01a4ae725f4b515d3693c923b7b2ef9 100644 --- a/source/armarx/navigation/components/GraphImportExport/GraphImportExport.cpp +++ b/source/armarx/navigation/components/GraphImportExport/GraphImportExport.cpp @@ -37,9 +37,9 @@ #include <MemoryX/libraries/memorytypes/MemoryXTypesObjectFactories.h> #include <armarx/navigation/core/Graph.h> #include <armarx/navigation/core/aron/Graph.aron.generated.h> +#include <armarx/navigation/core/aron/Location.aron.generated.h> #include <armarx/navigation/graph/Visu.h> #include <armarx/navigation/graph/constants.h> -#include <armarx/navigation/core/aron/Location.aron.generated.h> #include <armarx/navigation/location/constants.h> diff --git a/source/armarx/navigation/components/NavigationMemory/NavigationMemory.cpp b/source/armarx/navigation/components/NavigationMemory/NavigationMemory.cpp index 292b497c9cfcea15a21c6cc0b8ce158d3849cb04..799c560411805e9c29a642d92dc581013727c52c 100644 --- a/source/armarx/navigation/components/NavigationMemory/NavigationMemory.cpp +++ b/source/armarx/navigation/components/NavigationMemory/NavigationMemory.cpp @@ -36,10 +36,10 @@ #include "Visu.h" #include <armarx/navigation/core/Graph.h> #include <armarx/navigation/core/aron/Graph.aron.generated.h> +#include <armarx/navigation/core/aron/Location.aron.generated.h> #include <armarx/navigation/core/aron/Trajectory.aron.generated.h> #include <armarx/navigation/core/aron/Twist.aron.generated.h> #include <armarx/navigation/graph/constants.h> -#include <armarx/navigation/core/aron/Location.aron.generated.h> #include <armarx/navigation/location/constants.h> diff --git a/source/armarx/navigation/components/NavigationMemory/Visu.cpp b/source/armarx/navigation/components/NavigationMemory/Visu.cpp index 328e21cee7ad6e63ce00ee998ccaaadf2b67f42b..07f364bc81a7a9aa862ffe3a0f0f253321468c16 100644 --- a/source/armarx/navigation/components/NavigationMemory/Visu.cpp +++ b/source/armarx/navigation/components/NavigationMemory/Visu.cpp @@ -26,8 +26,8 @@ #include <armarx/navigation/core/Graph.h> #include <armarx/navigation/core/aron/Graph.aron.generated.h> -#include <armarx/navigation/graph/Visu.h> #include <armarx/navigation/core/aron/Location.aron.generated.h> +#include <armarx/navigation/graph/Visu.h> namespace armarx::navigation::memory diff --git a/source/armarx/navigation/components/Navigator/Navigator.cpp b/source/armarx/navigation/components/Navigator/Navigator.cpp index fb665e71ed80222cb4480fd83eb2b19505377813..2ec54dc208f4322b01f63053362c9c6471805e47 100644 --- a/source/armarx/navigation/components/Navigator/Navigator.cpp +++ b/source/armarx/navigation/components/Navigator/Navigator.cpp @@ -26,18 +26,25 @@ #include <algorithm> #include <cmath> #include <cstddef> +#include <cstdint> #include <iterator> #include <memory> #include <utility> #include <Eigen/Geometry> +#include <opencv2/core/eigen.hpp> +#include <opencv2/imgcodecs.hpp> + #include <Ice/Current.h> +#include <SimoxUtility/color/ColorMap.h> +#include <SimoxUtility/color/cmaps/colormaps.h> #include <SimoxUtility/math/convert/rpy_to_mat3f.h> #include <VirtualRobot/MathTools.h> #include <VirtualRobot/Robot.h> +#include "ArmarXCore/core/Component.h" #include <ArmarXCore/core/exceptions/LocalException.h> #include <ArmarXCore/core/logging/LogSender.h> #include <ArmarXCore/core/logging/Logging.h> @@ -48,15 +55,21 @@ #include "ArmarXGui/libraries/RemoteGui/Client/Widgets.h" +#include "RobotAPI/components/ArViz/Client/Client.h" #include "RobotAPI/components/ArViz/Client/Elements.h" #include "RobotAPI/components/ArViz/Client/elements/Color.h" +#include "RobotAPI/interface/ArViz/Elements.h" #include "RobotAPI/libraries/armem/core/MemoryID.h" #include "RobotAPI/libraries/armem/core/Time.h" #include "RobotAPI/libraries/armem_vision/OccupancyGridHelper.h" #include "RobotAPI/libraries/armem_vision/client/occupancy_grid/Reader.h" #include <RobotAPI/libraries/core/remoterobot/RemoteRobot.h> -#include <armarx/navigation/algorithms/NavigationCostMap.h> +#include "armarx/navigation/algorithms/CostmapBuilder.h" +#include "armarx/navigation/algorithms/spfa/ShortestPathFasterAlgorithm.h" +#include "armarx/navigation/algorithms/util.h" +#include "armarx/navigation/conversions/eigen.h" +#include <armarx/navigation/algorithms/Costmap.h> #include <armarx/navigation/algorithms/astar/util.h> #include <armarx/navigation/client/NavigationStackConfig.h> #include <armarx/navigation/client/PathBuilder.h> @@ -215,7 +228,15 @@ namespace armarx::navigation::components { std::lock_guard g{scene.staticSceneMtx}; - scene.staticScene = staticScene(); + + if (not scene.staticScene.has_value()) + { + scene.staticScene = staticScene(); + + cv::Mat1f mat; + cv::eigen2cv(scene.staticScene->costmap->getGrid(), mat); + cv::imwrite("/tmp/grid.exr", mat); + } } // TODO dynamic scene @@ -312,6 +333,7 @@ namespace armarx::navigation::components << "Navigator config for caller `" << callerId << "` not registered!"; updateContext(); + visualizeSPFA(); navigators.at(callerId).moveTo(wps, core::NavigationFrameNames.from_name(navigationMode)); // navigators.at(callerId).moveTo() @@ -438,16 +460,79 @@ namespace armarx::navigation::components return def; } + void + visualize(const algorithms::Costmap& costmap, viz::Client& arviz, const std::string& name) + { + const auto cmap = simox::color::cmaps::viridis(); + const float vmax = costmap.getGrid().array().maxCoeff(); + + const auto asColor = [&cmap, &vmax](const float distance) -> viz::data::Color + { + const auto color = cmap.at(distance, 0.F, vmax); + return {color.a, color.r, color.g, color.b}; + }; + + auto layer = arviz.layer("costmap_" + name); + + const std::int64_t cols = costmap.getGrid().cols(); + const std::int64_t rows = costmap.getGrid().rows(); + + auto mesh = viz::Mesh(""); + + std::vector<std::vector<Eigen::Vector3f>> vertices; + std::vector<std::vector<viz::data::Color>> colors; + + for (int r = 0; r < rows; r++) + { + auto& verticesRow = vertices.emplace_back(cols); + auto& colorsRow = colors.emplace_back(cols); + + for (int c = 0; c < cols; c++) + { + verticesRow.at(c) = conv::to3D(costmap.toPosition({r, c})); + colorsRow.at(c) = asColor(costmap.getGrid()(r, c)); + } + } + + mesh.grid2D(vertices, colors); + + layer.add(mesh); + + arviz.commit(layer); + } + core::StaticScene components::Navigator::staticScene() { ARMARX_TRACE; const objpose::ObjectPoseSeq objectPoses = ObjectPoseClientPluginUser::getObjectPoses(); - core::StaticScene scene{.objects = util::asSceneObjects(objectPoses)}; + const auto objects = util::asSceneObjects(objectPoses); + + ARMARX_INFO << "Creating costmap"; + + algorithms::CostmapBuilder costmapBuilder( + getRobot(), + objects, + algorithms::Costmap::Parameters{.binaryGrid = false, .cellSize = 100}, + "Platform-navigation-colmodel"); + + const auto costmap = costmapBuilder.create(); + + ARMARX_INFO << "Done"; + + ARMARX_TRACE; + const auto grid = costmap.getGrid(); + + core::StaticScene scene{.objects = objects, + .costmap = std::make_unique<algorithms::Costmap>(costmap)}; ARMARX_INFO << "The object scene consists of " << scene.objects->getSize() << " objects"; + + visualize(costmap, arviz, "distance"); + + const armem::vision::occupancy_grid::client::Reader::Result result = occupancyGridReader.query(armem::vision::occupancy_grid::client::Reader::Query{ .providerName = "CartographerMappingAndLocalization", @@ -482,13 +567,13 @@ namespace armarx::navigation::components ARMARX_INFO << "Creating costmap"; - algorithm::NavigationCostMap costmap( + algorithms::CostmapBuilder costmapBuilder( getRobot(), scene.objects, - algorithm::NavigationCostMap::Parameters{.binaryGrid = false, - .cellSize = 100}); + algorithms::Costmap::Parameters{.binaryGrid = false, .cellSize = 100}, + "Platform-navigation-colmodel"); - costmap.createCostmap(); + const auto costmap = costmapBuilder.create(); ARMARX_INFO << "Done"; @@ -509,13 +594,32 @@ namespace armarx::navigation::components return scene; } + void + components::Navigator::visualizeSPFA() + { + algorithms::spfa::ShortestPathFasterAlgorithm::Parameters spfaParams{ + .obstacleDistanceCosts = true}; + + ARMARX_INFO << "spfa..."; + algorithms::spfa::ShortestPathFasterAlgorithm spfa(*scene.staticScene->costmap, spfaParams); + const auto result123 = spfa.spfa(getRobot()->getGlobalPosition().head<2>()); + ARMARX_INFO << "spfa done..."; + + const auto sceneBounds = algorithms::computeSceneBounds(scene.staticScene->objects); + + ARMARX_INFO << "Costmap SPFA"; + algorithms::Costmap cm(result123.distances, algorithms::Costmap::Parameters{}, sceneBounds); + visualize(cm, arviz, "spfa"); + ARMARX_INFO << "Done."; + } + VirtualRobot::RobotPtr components::Navigator::getRobot() { ARMARX_TRACE; auto robot = RemoteRobot::createLocalCloneFromFile( - getRobotStateComponent(), VirtualRobot::RobotIO::RobotDescription::eFull); + getRobotStateComponent(), VirtualRobot::RobotIO::RobotDescription::eCollisionModel); // auto robot = RemoteRobot::createLocalClone(getRobotStateComponent()); ARMARX_CHECK_NOT_NULL(robot); diff --git a/source/armarx/navigation/components/Navigator/Navigator.h b/source/armarx/navigation/components/Navigator/Navigator.h index 2d7411d8dee32e46afc81ae6221ee4203a70c001..45785e2e0cce310e5a4cead02494d1a0189675ff 100644 --- a/source/armarx/navigation/components/Navigator/Navigator.h +++ b/source/armarx/navigation/components/Navigator/Navigator.h @@ -161,6 +161,7 @@ namespace armarx::navigation::components server::Navigator* activeNavigator(); private: + void visualizeSPFA(); // TODO update context periodically // TODO: Remove dependency to PlatformUnit. This component is generally diff --git a/source/armarx/navigation/core/CMakeLists.txt b/source/armarx/navigation/core/CMakeLists.txt index ec1598c68ac4a430c0663a76421f6e9a9e00a855..ca477220b3479fc2e8f21bb3200c87eb89e5a215 100644 --- a/source/armarx/navigation/core/CMakeLists.txt +++ b/source/armarx/navigation/core/CMakeLists.txt @@ -45,7 +45,7 @@ armarx_add_library(core Simox::VirtualRobot armarx_navigation::core_aron PRIVATE - range-v3 + range-v3::range-v3 ) @@ -57,5 +57,5 @@ armarx_add_test(core_test ArmarXCore armarx_navigation::core PRIVATE - range-v3 + range-v3::range-v3 ) diff --git a/source/armarx/navigation/core/StaticScene.h b/source/armarx/navigation/core/StaticScene.h index 4d05a2febffa36dfacbf1443ace58c1c68915542..1f4ad33aae03bc2da57da3cec8cb45d003a205b9 100644 --- a/source/armarx/navigation/core/StaticScene.h +++ b/source/armarx/navigation/core/StaticScene.h @@ -26,12 +26,15 @@ #include <VirtualRobot/VirtualRobot.h> +#include <armarx/navigation/algorithms/Costmap.h> + namespace armarx::navigation::core { struct StaticScene { VirtualRobot::SceneObjectSetPtr objects; + std::unique_ptr<algorithms::Costmap> costmap; }; using StaticScenePtr = std::shared_ptr<StaticScene>; diff --git a/source/armarx/navigation/core/Trajectory.cpp b/source/armarx/navigation/core/Trajectory.cpp index 9f5679a0e46cff2f6655ad0b4b4952b25ea4f027..e7bf3ea1d608a7805be467e1b147f2d423074878 100644 --- a/source/armarx/navigation/core/Trajectory.cpp +++ b/source/armarx/navigation/core/Trajectory.cpp @@ -589,11 +589,11 @@ namespace armarx::navigation::core return 0; // FIXME } - bool Trajectory::isValid() const noexcept + bool + Trajectory::isValid() const noexcept { - const auto isValid = [](const TrajectoryPoint& pt) -> bool { - return std::isfinite(pt.velocity) and pt.velocity < 3000; - }; + const auto isValid = [](const TrajectoryPoint& pt) -> bool + { return std::isfinite(pt.velocity) and pt.velocity < 3000; }; return std::all_of(pts.begin(), pts.end(), isValid); } diff --git a/source/armarx/navigation/core/json_conversions.cpp b/source/armarx/navigation/core/json_conversions.cpp index 30b8a70f492e70c2730d43fab23342709d84a06a..661331fb0e427048b9001d9c1da9c9bc2f9c00a0 100644 --- a/source/armarx/navigation/core/json_conversions.cpp +++ b/source/armarx/navigation/core/json_conversions.cpp @@ -21,21 +21,23 @@ #include "json_conversions.h" -#include <RobotAPI/libraries/aron/core/data/rw/writer/nlohmannJSON/NlohmannJSONWriter.h> #include <RobotAPI/libraries/aron/core/data/rw/reader/nlohmannJSON/NlohmannJSONReader.h> +#include <RobotAPI/libraries/aron/core/data/rw/writer/nlohmannJSON/NlohmannJSONWriter.h> namespace armarx::navigation::core { - void arondto::to_json(nlohmann::json& j, const Edge& bo) + void + arondto::to_json(nlohmann::json& j, const Edge& bo) { armarx::aron::data::writer::NlohmannJSONWriter writer; j = bo.write(writer); } - void arondto::from_json(const nlohmann::json& j, Edge& bo) + void + arondto::from_json(const nlohmann::json& j, Edge& bo) { armarx::aron::data::reader::NlohmannJSONReader reader; bo.read<armarx::aron::data::reader::NlohmannJSONReader::InputType>(reader, j); diff --git a/source/armarx/navigation/core/json_conversions.h b/source/armarx/navigation/core/json_conversions.h index 57cee75dfdffb6640c15b49053e0f35e20f7a306..a8fd2caac4355d8eea6898050bb4cccea04626de 100644 --- a/source/armarx/navigation/core/json_conversions.h +++ b/source/armarx/navigation/core/json_conversions.h @@ -33,4 +33,4 @@ namespace armarx::navigation::core::arondto void from_json(const nlohmann::json& j, arondto::Edge& bo); -} // namespace armarx::navigation::graph +} // namespace armarx::navigation::core::arondto diff --git a/source/armarx/navigation/core/test/coreTest.cpp b/source/armarx/navigation/core/test/coreTest.cpp index 8e323a973e14203da03c8e168f4253b1801f8e02..c62597ce82a30d3e460fbdbdc72b30ee263f0709 100644 --- a/source/armarx/navigation/core/test/coreTest.cpp +++ b/source/armarx/navigation/core/test/coreTest.cpp @@ -24,11 +24,11 @@ #include <algorithm> #include <vector> +#include <range/v3/view/zip.hpp> + #include <ArmarXCore/core/exceptions/local/ExpressionException.h> #include <ArmarXCore/core/logging/Logging.h> -#include <range/v3/view/zip.hpp> - #include <SemanticObjectRelations/Shapes/Shape.h> #include <armarx/navigation/core/Graph.h> #include <armarx/navigation/core/Trajectory.h> diff --git a/source/armarx/navigation/core/types.h b/source/armarx/navigation/core/types.h index 464966dfa62ff83484c00c7f3b88288633f6501e..0e5b96facb6dd53297fab1935525624a86b61ac3 100644 --- a/source/armarx/navigation/core/types.h +++ b/source/armarx/navigation/core/types.h @@ -80,7 +80,7 @@ namespace armarx::navigation::core { mutable std::mutex staticSceneMtx; std::optional<core::StaticScene> staticScene = std::nullopt; - + mutable std::mutex dynamicSceneMtx; std::optional<core::DynamicScene> dynamicScene = std::nullopt; // TopologicMapPtr topologicMap; diff --git a/source/armarx/navigation/factories/GlobalPlannerFactory.cpp b/source/armarx/navigation/factories/GlobalPlannerFactory.cpp index 757b56e84dc861a907d8b27fd1baf38e1dbc81ed..f6e20580bb27557400ebd2bcb28d3177b59d0d55 100644 --- a/source/armarx/navigation/factories/GlobalPlannerFactory.cpp +++ b/source/armarx/navigation/factories/GlobalPlannerFactory.cpp @@ -5,6 +5,7 @@ #include <RobotAPI/libraries/aron/core/data/variant/container/Dict.h> #include <RobotAPI/libraries/aron/core/data/variant/primitive/String.h> +#include "armarx/navigation/global_planning/SPFA.h" #include <armarx/navigation/core/constants.h> #include <armarx/navigation/global_planning/AStar.h> #include <armarx/navigation/global_planning/Point2Point.h> @@ -13,8 +14,7 @@ namespace armarx::navigation::fac { global_planning::GlobalPlannerPtr - GlobalPlannerFactory::create(const aron::data::DictPtr& params, - const core::Scene& ctx) + GlobalPlannerFactory::create(const aron::data::DictPtr& params, const core::Scene& ctx) { namespace layer = global_planning; @@ -24,22 +24,24 @@ namespace armarx::navigation::fac } // algo name - const auto algoName = - aron::data::String::DynamicCast(params->getElement(core::NAME_KEY)); + const auto algoName = aron::data::String::DynamicCast(params->getElement(core::NAME_KEY)); ARMARX_CHECK_NOT_NULL(algoName); const layer::Algorithms algo = layer::AlgorithmNames.from_name(algoName->getValue()); // algo params - const auto algoParams = - aron::data::Dict::DynamicCast(params->getElement(core::PARAMS_KEY)); + const auto algoParams = aron::data::Dict::DynamicCast(params->getElement(core::PARAMS_KEY)); ARMARX_CHECK_NOT_NULL(algoParams); global_planning::GlobalPlannerPtr globalPlanner; switch (algo) { case global_planning::Algorithms::AStar: - globalPlanner = std::make_shared<global_planning::AStar>( - global_planning::AStarParams::FromAron(algoParams), ctx); + // globalPlanner = std::make_shared<global_planning::AStar>( + // global_planning::AStarParams::FromAron(algoParams), ctx); + // break; + case global_planning::Algorithms::SPFA: + globalPlanner = std::make_shared<global_planning::SPFA>( + global_planning::SPFAParams::FromAron(algoParams), ctx); break; case global_planning::Algorithms::Point2Point: globalPlanner = std::make_shared<global_planning::Point2Point>( diff --git a/source/armarx/navigation/factories/GlobalPlannerFactory.h b/source/armarx/navigation/factories/GlobalPlannerFactory.h index 4ef9a82393c6966cad3c4029bebcaf9f1c97092a..da60c83946c15573daa6b6a3d96f716e2a4704ff 100644 --- a/source/armarx/navigation/factories/GlobalPlannerFactory.h +++ b/source/armarx/navigation/factories/GlobalPlannerFactory.h @@ -36,8 +36,8 @@ namespace armarx::navigation::fac class GlobalPlannerFactory { public: - static global_planning::GlobalPlannerPtr - create(const aron::data::DictPtr& params, const core::Scene& ctx); + static global_planning::GlobalPlannerPtr create(const aron::data::DictPtr& params, + const core::Scene& ctx); }; } // namespace armarx::navigation::fac diff --git a/source/armarx/navigation/factories/LocalPlannerFactory.cpp b/source/armarx/navigation/factories/LocalPlannerFactory.cpp index e386f55d05ef1cb027667a5e63aeab33d5f87f0b..aed7f192efabc4a752ae77d2a054e1737839688e 100644 --- a/source/armarx/navigation/factories/LocalPlannerFactory.cpp +++ b/source/armarx/navigation/factories/LocalPlannerFactory.cpp @@ -11,8 +11,7 @@ namespace armarx::navigation::fac { loc_plan::LocalPlannerPtr - LocalPlannerFactory::create(const aron::data::DictPtr& params, - const core::Scene& ctx) + LocalPlannerFactory::create(const aron::data::DictPtr& params, const core::Scene& ctx) { namespace layer = loc_plan; @@ -22,14 +21,12 @@ namespace armarx::navigation::fac } // algo name - const auto algoName = - aron::data::String::DynamicCast(params->getElement(core::NAME_KEY)); + const auto algoName = aron::data::String::DynamicCast(params->getElement(core::NAME_KEY)); ARMARX_CHECK_NOT_NULL(algoName); const layer::Algorithms algo = layer::AlgorithmNames.from_name(algoName->getValue()); // algo params - const auto algoParams = - aron::data::Dict::DynamicCast(params->getElement(core::PARAMS_KEY)); + const auto algoParams = aron::data::Dict::DynamicCast(params->getElement(core::PARAMS_KEY)); ARMARX_CHECK_NOT_NULL(algoParams); loc_plan::LocalPlannerPtr localPlanner; diff --git a/source/armarx/navigation/factories/NavigationStackFactory.cpp b/source/armarx/navigation/factories/NavigationStackFactory.cpp index e6c90a5cf1373dad0b87c13f7e161b4974e5ba70..665bfecbc2e10763d1c296624051981de18216f7 100644 --- a/source/armarx/navigation/factories/NavigationStackFactory.cpp +++ b/source/armarx/navigation/factories/NavigationStackFactory.cpp @@ -14,13 +14,11 @@ namespace armarx::navigation::fac { server::NavigationStack - NavigationStackFactory::create(const aron::data::DictPtr& params, - const core::Scene& ctx) + NavigationStackFactory::create(const aron::data::DictPtr& params, const core::Scene& ctx) { using aron::data::Dict; - const auto getElementOrNull = - [¶ms](const core::StackLayer& layer) -> Dict::PointerType + const auto getElementOrNull = [¶ms](const core::StackLayer& layer) -> Dict::PointerType { const std::string key = core::StackLayerNames.to_name(layer); diff --git a/source/armarx/navigation/factories/NavigationStackFactory.h b/source/armarx/navigation/factories/NavigationStackFactory.h index f0ee4e1906391595e4fbdb22442e3ad6cdbb1a1d..abd6be4143968e667ce9c9dbd1637f0f6064ee52 100644 --- a/source/armarx/navigation/factories/NavigationStackFactory.h +++ b/source/armarx/navigation/factories/NavigationStackFactory.h @@ -22,11 +22,10 @@ #pragma once -#include <armarx/navigation/server/NavigationStack.h> - #include <RobotAPI/libraries/aron/core/data/variant/forward_declarations.h> #include <armarx/navigation/core/fwd.h> +#include <armarx/navigation/server/NavigationStack.h> namespace armarx::navigation::fac { diff --git a/source/armarx/navigation/factories/SafetyControllerFactory.cpp b/source/armarx/navigation/factories/SafetyControllerFactory.cpp index 8dec9fb3dc7eb6cbb2c80a8dc9f725c817e4c804..a4173c4294c15813ceeda73e7857b05bfd502ae4 100644 --- a/source/armarx/navigation/factories/SafetyControllerFactory.cpp +++ b/source/armarx/navigation/factories/SafetyControllerFactory.cpp @@ -13,8 +13,7 @@ namespace armarx::navigation::fac { safe_ctrl::SafetyControllerPtr - SafetyControllerFactory::create(const aron::data::DictPtr& params, - const core::Scene& ctx) + SafetyControllerFactory::create(const aron::data::DictPtr& params, const core::Scene& ctx) { namespace layer = safe_ctrl; @@ -24,14 +23,12 @@ namespace armarx::navigation::fac } // algo name - const auto algoName = - aron::data::String::DynamicCast(params->getElement(core::NAME_KEY)); + const auto algoName = aron::data::String::DynamicCast(params->getElement(core::NAME_KEY)); ARMARX_CHECK_NOT_NULL(algoName); const layer::Algorithms algo = layer::AlgorithmNames.from_name(algoName->getValue()); // algo params - const auto algoParams = - aron::data::Dict::DynamicCast(params->getElement(core::PARAMS_KEY)); + const auto algoParams = aron::data::Dict::DynamicCast(params->getElement(core::PARAMS_KEY)); ARMARX_CHECK_NOT_NULL(algoParams); safe_ctrl::SafetyControllerPtr controller; diff --git a/source/armarx/navigation/factories/SafetyControllerFactory.h b/source/armarx/navigation/factories/SafetyControllerFactory.h index 023a762416f59f31f7cd4b15f6a9ccc84856c295..02c427c990f3cd1b1b78693e0a90f0f3f7a8225e 100644 --- a/source/armarx/navigation/factories/SafetyControllerFactory.h +++ b/source/armarx/navigation/factories/SafetyControllerFactory.h @@ -37,8 +37,8 @@ namespace armarx::navigation::fac class SafetyControllerFactory { public: - static safe_ctrl::SafetyControllerPtr - create(const aron::data::DictPtr& params, const core::Scene& ctx); + static safe_ctrl::SafetyControllerPtr create(const aron::data::DictPtr& params, + const core::Scene& ctx); protected: private: diff --git a/source/armarx/navigation/factories/TrajectoryControllerFactory.cpp b/source/armarx/navigation/factories/TrajectoryControllerFactory.cpp index c30fe546a39f7e4cbab26c25fb098dc65161c336..277d277deaffc3d019d93ebeb027c3451573ca0c 100644 --- a/source/armarx/navigation/factories/TrajectoryControllerFactory.cpp +++ b/source/armarx/navigation/factories/TrajectoryControllerFactory.cpp @@ -12,8 +12,7 @@ namespace armarx::navigation::fac { traj_ctrl::TrajectoryControllerPtr - TrajectoryControllerFactory::create(const aron::data::DictPtr& params, - const core::Scene& ctx) + TrajectoryControllerFactory::create(const aron::data::DictPtr& params, const core::Scene& ctx) { namespace layer = traj_ctrl; @@ -23,14 +22,12 @@ namespace armarx::navigation::fac } // algo name - const auto algoName = - aron::data::String::DynamicCast(params->getElement(core::NAME_KEY)); + const auto algoName = aron::data::String::DynamicCast(params->getElement(core::NAME_KEY)); ARMARX_CHECK_NOT_NULL(algoName); const layer::Algorithms algo = layer::AlgorithmNames.from_name(algoName->getValue()); // algo params - const auto algoParams = - aron::data::Dict::DynamicCast(params->getElement(core::PARAMS_KEY)); + const auto algoParams = aron::data::Dict::DynamicCast(params->getElement(core::PARAMS_KEY)); ARMARX_CHECK_NOT_NULL(algoParams); traj_ctrl::TrajectoryControllerPtr controller; diff --git a/source/armarx/navigation/factories/TrajectoryControllerFactory.h b/source/armarx/navigation/factories/TrajectoryControllerFactory.h index eb603d5f44483d0ff9af2b820726eab3caa7f391..16328e1c76889c6fbdefbab975baf9b4a40bb5a4 100644 --- a/source/armarx/navigation/factories/TrajectoryControllerFactory.h +++ b/source/armarx/navigation/factories/TrajectoryControllerFactory.h @@ -38,8 +38,8 @@ namespace armarx::navigation::fac class TrajectoryControllerFactory { public: - static traj_ctrl::TrajectoryControllerPtr - create(const aron::data::DictPtr& params, const core::Scene& ctx); + static traj_ctrl::TrajectoryControllerPtr create(const aron::data::DictPtr& params, + const core::Scene& ctx); protected: private: diff --git a/source/armarx/navigation/factories/test/factoriesTest.cpp b/source/armarx/navigation/factories/test/factoriesTest.cpp index adb15278328d732d3b2bccda4c35885db313f027..2e7d7fdbefdf5ebc2048dc96704d14885f6ca8f8 100644 --- a/source/armarx/navigation/factories/test/factoriesTest.cpp +++ b/source/armarx/navigation/factories/test/factoriesTest.cpp @@ -66,8 +66,8 @@ BOOST_AUTO_TEST_CASE(testStackCreation) BOOST_CHECK(navStack.globalPlanner.get() != nullptr); BOOST_CHECK(navStack.trajectoryControl.get() != nullptr); - BOOST_CHECK(dynamic_cast<armarx::navigation::global_planning::AStar*>(navStack.globalPlanner.get()) != - nullptr); + BOOST_CHECK(dynamic_cast<armarx::navigation::global_planning::AStar*>( + navStack.globalPlanner.get()) != nullptr); BOOST_CHECK(dynamic_cast<armarx::navigation::traj_ctrl::TrajectoryFollowingController*>( navStack.trajectoryControl.get()) != nullptr); } diff --git a/source/armarx/navigation/global_planning/AStar.cpp b/source/armarx/navigation/global_planning/AStar.cpp index 641492e1027ffea1cc59757872c890c4890f09bf..dde26d2fbe5c5a2c6179570b7873ddf4d89c8a01 100644 --- a/source/armarx/navigation/global_planning/AStar.cpp +++ b/source/armarx/navigation/global_planning/AStar.cpp @@ -159,12 +159,8 @@ namespace armarx::navigation::global_planning ARMARX_TRACE; std::lock_guard g{scene.staticSceneMtx}; - algorithm::astar::AStarPlanner planner(scene.robot, scene.staticScene->objects, 100.F); - // planner.setRobotColModel("Platform-colmodel"); - - // planner.setRobotColModel("Platform-colmodel"); - //planner.setRobotCollisionModel(robotCollisionModel()); - planner.setRobotCollisionModel("Platform-navigation-colmodel"); + // FIXME check if costmap is available + algorithm::astar::AStarPlanner planner(*scene.staticScene->costmap); const Eigen::Vector2f goalPos = conv::to2D(goal.translation()); diff --git a/source/armarx/navigation/global_planning/CMakeLists.txt b/source/armarx/navigation/global_planning/CMakeLists.txt index 7100ea9068fc250c98076cceb441e9d2ff259a57..ca9fcf745026385ef960cdcce81abfb866741985 100644 --- a/source/armarx/navigation/global_planning/CMakeLists.txt +++ b/source/armarx/navigation/global_planning/CMakeLists.txt @@ -3,6 +3,7 @@ armarx_add_aron_library(global_planning_aron aron/GlobalPlannerParams.xml aron/Point2PointParams.xml aron/AStarParams.xml + aron/SPFAParams.xml ) armarx_add_library(global_planning @@ -16,16 +17,18 @@ armarx_add_library(global_planning armarx_navigation::algorithms armarx_navigation::global_planning_aron PRIVATE - range-v3 + range-v3::range-v3 SOURCES ./GlobalPlanner.cpp ./AStar.cpp + ./SPFA.cpp ./Point2Point.cpp ./aron_conversions.cpp ./optimization/OrientationOptimizer.cpp HEADERS ./GlobalPlanner.h ./AStar.h + ./SPFA.h ./Point2Point.h ./aron_conversions.h ./optimization/OrientationOptimizer.h diff --git a/source/armarx/navigation/global_planning/SPFA.cpp b/source/armarx/navigation/global_planning/SPFA.cpp new file mode 100644 index 0000000000000000000000000000000000000000..26363016e3727f6dda658faa773300b01e0e6539 --- /dev/null +++ b/source/armarx/navigation/global_planning/SPFA.cpp @@ -0,0 +1,231 @@ +#include "SPFA.h" + +#include <algorithm> +#include <mutex> +#include <optional> + +#include <Eigen/Geometry> + +#include <VirtualRobot/Robot.h> +#include <VirtualRobot/RobotNodeSet.h> +#include <VirtualRobot/Visualization/CoinVisualization/CoinVisualizationFactory.h> + +#include <ArmarXCore/core/exceptions/LocalException.h> +#include <ArmarXCore/core/exceptions/local/ExpressionException.h> +#include <ArmarXCore/core/logging/Logging.h> + +#include <RobotAPI/libraries/aron/core/data/variant/container/Dict.h> +#include <RobotAPI/libraries/aron/core/data/variant/primitive/String.h> + +#include "armarx/navigation/algorithms/spfa/ShortestPathFasterAlgorithm.h" +#include <armarx/navigation/algorithms/smoothing/ChainApproximation.h> +#include <armarx/navigation/algorithms/smoothing/CircularPathSmoothing.h> +#include <armarx/navigation/conversions/eigen.h> +#include <armarx/navigation/core/Trajectory.h> +#include <armarx/navigation/core/constants.h> +#include <armarx/navigation/global_planning/GlobalPlanner.h> +#include <armarx/navigation/global_planning/aron/SPFAParams.aron.generated.h> +#include <armarx/navigation/global_planning/aron_conversions.h> +#include <armarx/navigation/global_planning/core.h> +#include <armarx/navigation/global_planning/optimization/OrientationOptimizer.h> + + +namespace armarx::navigation::global_planning +{ + + // SPFAParams + + Algorithms + SPFAParams::algorithm() const + { + return Algorithms::SPFA; + } + + aron::data::DictPtr + SPFAParams::toAron() const + { + arondto::SPFAParams dto; + + SPFAParams bo; + aron_conv::toAron(dto, bo); + + return dto.toAron(); + } + + SPFAParams + SPFAParams::FromAron(const aron::data::DictPtr& dict) + { + ARMARX_CHECK_NOT_NULL(dict); + + // ARMARX_DEBUG << dict->getAllKeysAsString(); + + arondto::SPFAParams dto; + dto.fromAron(dict); + + SPFAParams bo; + aron_conv::fromAron(dto, bo); + + return bo; + } + + // SPFA + + SPFA::SPFA(const SPFAParams& params, const core::Scene& ctx) : + GlobalPlanner(ctx), params(params) + { + } + + + std::optional<GlobalPlannerResult> + SPFA::plan(const core::Pose& goal) + { + const core::Pose start(scene.robot->getGlobalPose()); + return plan(start, goal); + } + + std::optional<GlobalPlannerResult> + SPFA::plan(const core::Pose& start, const core::Pose& goal) + { + ARMARX_TRACE; + + std::lock_guard g{scene.staticSceneMtx}; + // FIXME check if costmap is available + + algorithms::spfa::ShortestPathFasterAlgorithm::Parameters spfaParams; + algorithms::spfa::ShortestPathFasterAlgorithm planner(*scene.staticScene->costmap, + spfaParams); + + const Eigen::Vector2f goalPos = conv::to2D(goal.translation()); + + const auto timeStart = IceUtil::Time::now(); + + std::vector<Eigen::Vector2f> plan; + try + { + plan = planner.plan(conv::to2D(start.translation()), goalPos); + } + catch (...) + { + ARMARX_INFO << "Could not plan collision-free path from" + << "(" << start.translation().x() << "," << start.translation().y() << ")" + << " to " + << "(" << goal.translation().x() << "," << goal.translation().y() << ")" + << " due to exception " << GetHandledExceptionString(); + + return std::nullopt; + } + const auto timeEnd = IceUtil::Time::now(); + + ARMARX_IMPORTANT << "A* planning took " << (timeEnd - timeStart).toMilliSeconds() << " ms"; + ARMARX_IMPORTANT << "Path contains " << plan.size() << " points"; + + if (plan.size() < 2) // failure + { + ARMARX_INFO << "Could not plan collision-free path from" + << "(" << start.translation().x() << "," << start.translation().y() << ")" + << " to " + << "(" << goal.translation().x() << "," << goal.translation().y() << ")"; + return std::nullopt; + } + + ARMARX_DEBUG << "The plan consists of the following positions:"; + for (const auto& position : plan) + { + ARMARX_DEBUG << position; + } + + const core::Pose startPose(Eigen::Translation3f(conv::to3D(plan.front()))); + const core::Pose goalPose(Eigen::Translation3f(conv::to3D(plan.back()))); + + const auto plan3d = conv::to3D(plan); + + std::vector<core::Position> wpts; + for (size_t i = 1; i < (plan3d.size() - 1); i++) + { + wpts.push_back(plan3d.at(i)); + } + + ARMARX_TRACE; + auto smoothPlan = postProcessPath(plan); + ARMARX_IMPORTANT << "Smooth path contains " << smoothPlan.size() << " points"; + + ARMARX_TRACE; + // we need to strip the first and the last points from the plan as they encode the start and goal position + smoothPlan.erase(smoothPlan.begin()); + smoothPlan.pop_back(); + + ARMARX_TRACE; + auto trajectory = + core::Trajectory::FromPath(start, conv::to3D(smoothPlan), goal, params.linearVelocity); + + // TODO(fabian.reister): resampling of trajectory + + // FIXME param + std::optional<core::Trajectory> resampledTrajectory; + + try + { + resampledTrajectory = trajectory.resample(params.resampleDistance); + } + catch (...) + { + ARMARX_INFO << "Caught exception during resampling: " << GetHandledExceptionString(); + // return std::nullopt; + resampledTrajectory = trajectory; + } + + ARMARX_IMPORTANT << "Resampled trajectory contains " << resampledTrajectory->points().size() + << " points"; + + resampledTrajectory->setMaxVelocity(params.linearVelocity); + + + // ARMARX_CHECK(resampledTrajectory.hasMaxDistanceBetweenWaypoints(params.resampleDistance)); + + if (resampledTrajectory->points().size() == 2) + { + ARMARX_INFO << "Only start and goal provided. Not optimizing orientation"; + ARMARX_TRACE; + return GlobalPlannerResult{.trajectory = resampledTrajectory.value()}; + } + + ARMARX_TRACE; + OrientationOptimizer optimizer(resampledTrajectory.value(), OrientationOptimizer::Params{}); + const auto result = optimizer.optimize(); + + if (not result) + { + ARMARX_ERROR << "Optimizer failure"; + return std::nullopt; + } + + // TODO circular path smoothing should be done now + + algorithm::CircularPathSmoothing smoothing; + auto smoothTrajectory = smoothing.smooth(result.trajectory.value()); + smoothTrajectory.setMaxVelocity(params.linearVelocity); + + ARMARX_TRACE; + return GlobalPlannerResult{.trajectory = result.trajectory.value()}; + } + + + std::vector<Eigen::Vector2f> + SPFA::postProcessPath(const std::vector<Eigen::Vector2f>& path) + { + /// chain approximation + algorithm::ChainApproximation approx( + path, algorithm::ChainApproximation::Params{.distanceTh = 200.F}); + approx.approximate(); + const auto p = approx.approximatedChain(); + + // visualizePath(p, "approximated", simox::Color::green()); + + // algo::CircularPathSmoothing smoothing; + // const auto points = smoothing.smooth(p); + + return p; + } + + +} // namespace armarx::navigation::global_planning diff --git a/source/armarx/navigation/global_planning/SPFA.h b/source/armarx/navigation/global_planning/SPFA.h new file mode 100644 index 0000000000000000000000000000000000000000..b3af4fc107b6c2760a0e8d9e017a0bfcf631bef4 --- /dev/null +++ b/source/armarx/navigation/global_planning/SPFA.h @@ -0,0 +1,71 @@ +/** + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @author Fabian Reister ( fabian dot reister at kit dot edu ) + * @author Christian R. G. Dreher ( c dot dreher at kit dot edu ) + * @date 2021 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#pragma once + +#include "GlobalPlanner.h" +#include <armarx/navigation/core/types.h> + +namespace armarx::navigation::global_planning +{ + + /** + * @brief Parameters for AStar + * + */ + struct SPFAParams : public GlobalPlannerParams + { + float linearVelocity{500}; + float resampleDistance{500}; + + Algorithms algorithm() const override; + aron::data::DictPtr toAron() const override; + + static SPFAParams FromAron(const aron::data::DictPtr& dict); + }; + + /** + * @class AStar + * @ingroup Library-GlobalPlanner + * + * Implements the A* algorithm + */ + class SPFA : public GlobalPlanner + { + public: + using Params = SPFAParams; + + SPFA(const Params& params, const core::Scene& ctx); + ~SPFA() override = default; + + std::optional<GlobalPlannerResult> plan(const core::Pose& goal) override; + std::optional<GlobalPlannerResult> plan(const core::Pose& start, + const core::Pose& goal) override; + + protected: + std::vector<Eigen::Vector2f> postProcessPath(const std::vector<Eigen::Vector2f>& path); + + private: + Params params; + }; + +} // namespace armarx::navigation::global_planning diff --git a/source/armarx/navigation/global_planning/aron/SPFAParams.xml b/source/armarx/navigation/global_planning/aron/SPFAParams.xml new file mode 100644 index 0000000000000000000000000000000000000000..b1a34a4d350513b1e1189a4a10a4c2c5da671ab8 --- /dev/null +++ b/source/armarx/navigation/global_planning/aron/SPFAParams.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<AronTypeDefinition> + <CodeIncludes> + <Include include="<armarx/navigation/global_planning/aron/GlobalPlannerParams.aron.generated.h>" /> + </CodeIncludes> + <AronIncludes> + <Include include="GlobalPlannerParams.xml" /> + </AronIncludes> + + <GenerateTypes> + + <Object name='armarx::navigation::global_planning::arondto::SPFAParams'> + <!-- <Object name='armarx::navigation::global_planning::arondto::SPFAParams' extends="armarx::navigation::global_planning::arondto::GlobalPlannerParams"> --> + <ObjectChild key='linearVelocity'> + <float /> + </ObjectChild> + <ObjectChild key='resampleDistance'> + <float /> + </ObjectChild> + + </Object> + + + </GenerateTypes> +</AronTypeDefinition> diff --git a/source/armarx/navigation/global_planning/aron_conversions.cpp b/source/armarx/navigation/global_planning/aron_conversions.cpp index 84ec219a337197fdfc1c656975ffcfe14da14047..358efefcb01c7c722d9f278bbea662e01dd7d99e 100644 --- a/source/armarx/navigation/global_planning/aron_conversions.cpp +++ b/source/armarx/navigation/global_planning/aron_conversions.cpp @@ -7,9 +7,11 @@ #include <armarx/navigation/global_planning/AStar.h> #include <armarx/navigation/global_planning/GlobalPlanner.h> #include <armarx/navigation/global_planning/Point2Point.h> +#include <armarx/navigation/global_planning/SPFA.h> #include <armarx/navigation/global_planning/aron/AStarParams.aron.generated.h> #include <armarx/navigation/global_planning/aron/GlobalPlannerParams.aron.generated.h> #include <armarx/navigation/global_planning/aron/Point2PointParams.aron.generated.h> +#include <armarx/navigation/global_planning/aron/SPFAParams.aron.generated.h> namespace armarx::navigation::global_planning::aron_conv { @@ -58,4 +60,25 @@ namespace armarx::navigation::global_planning::aron_conv aron::fromAron(dto.linearVelocity, bo.linearVelocity); aron::fromAron(dto.resampleDistance, bo.resampleDistance); } + + void + toAron(arondto::SPFAParams& dto, const SPFAParams& bo) + { + // toAron(static_cast<arondto::GlobalPlannerParams&>(dto), + // static_cast<const GlobalPlannerParams&>(bo)); + + aron::toAron(dto.linearVelocity, bo.linearVelocity); + aron::toAron(dto.resampleDistance, bo.resampleDistance); + } + + void + fromAron(const arondto::SPFAParams& dto, SPFAParams& bo) + { + // fromAron(static_cast<const arondto::GlobalPlannerParams&>(dto), + // static_cast<GlobalPlannerParams&>(bo)); + + aron::fromAron(dto.linearVelocity, bo.linearVelocity); + aron::fromAron(dto.resampleDistance, bo.resampleDistance); + } + } // namespace armarx::navigation::global_planning::aron_conv diff --git a/source/armarx/navigation/global_planning/aron_conversions.h b/source/armarx/navigation/global_planning/aron_conversions.h index a27a6b0246452e9725187587a9bc7c7c82a14975..d5b14e9dbfe9070056f25468a2465e7e72491291 100644 --- a/source/armarx/navigation/global_planning/aron_conversions.h +++ b/source/armarx/navigation/global_planning/aron_conversions.h @@ -5,12 +5,14 @@ namespace armarx::navigation::global_planning struct GlobalPlannerParams; struct Point2PointParams; struct AStarParams; + struct SPFAParams; namespace arondto { struct GlobalPlannerParams; struct Point2PointParams; struct AStarParams; + struct SPFAParams; } // namespace arondto @@ -27,4 +29,7 @@ namespace armarx::navigation::global_planning::aron_conv void toAron(arondto::AStarParams& dto, const AStarParams& bo); void fromAron(const arondto::AStarParams& dto, AStarParams& bo); + void toAron(arondto::SPFAParams& dto, const SPFAParams& bo); + void fromAron(const arondto::SPFAParams& dto, SPFAParams& bo); + } // namespace armarx::navigation::global_planning::aron_conv diff --git a/source/armarx/navigation/global_planning/core.h b/source/armarx/navigation/global_planning/core.h index 792e7b6f9f36afdc5a438e59375fd33bdca9c877..f8b6eaf7011841c296685500e27216449dda466b 100644 --- a/source/armarx/navigation/global_planning/core.h +++ b/source/armarx/navigation/global_planning/core.h @@ -37,7 +37,8 @@ namespace armarx::navigation::global_planning */ enum class Algorithms { - AStar, ///< see AStar + AStar, ///< see AStar + SPFA, Point2Point ///< see Point2Point }; diff --git a/source/armarx/navigation/global_planning/optimization/math.h b/source/armarx/navigation/global_planning/optimization/math.h index 2007edb3906c9ab58c03c8caaa8cf0bc4fc5b2e5..c8854e1fc6b1213c60040380a34a5362a4a7c561 100644 --- a/source/armarx/navigation/global_planning/optimization/math.h +++ b/source/armarx/navigation/global_planning/optimization/math.h @@ -61,7 +61,7 @@ namespace armarx::navigation::global_planning::optimization 1 - ceres::pow(ceres::sin(a) * ceres::sin(b) + ceres::cos(a) * ceres::cos(b), 2); // prevent division by 0 - if(denomSquared < 0.001) + if (denomSquared < 0.001) { return 0.; } diff --git a/source/armarx/navigation/gui-plugins/LocationGraphEditor/WidgetController.cpp b/source/armarx/navigation/gui-plugins/LocationGraphEditor/WidgetController.cpp index b4487009018c118f5d3ed49ba73d65c05aeb7cb7..894b527dcac09505145bfda3e270002a6e3325d9 100644 --- a/source/armarx/navigation/gui-plugins/LocationGraphEditor/WidgetController.cpp +++ b/source/armarx/navigation/gui-plugins/LocationGraphEditor/WidgetController.cpp @@ -41,9 +41,9 @@ #include "widgets/graph_scene/Widget.h" #include "widgets/utils.h" #include <armarx/navigation/core/aron/Graph.aron.generated.h> +#include <armarx/navigation/core/aron/Location.aron.generated.h> #include <armarx/navigation/graph/constants.h> #include <armarx/navigation/gui-plugins/LocationGraphEditor/ui_LocationGraphEditorWidget.h> -#include <armarx/navigation/core/aron/Location.aron.generated.h> #include <armarx/navigation/location/constants.h> // Qt headers diff --git a/source/armarx/navigation/gui-plugins/LocationGraphEditor/WidgetController.h b/source/armarx/navigation/gui-plugins/LocationGraphEditor/WidgetController.h index f06f0bde91993d7b98847138de4eea9797127c9b..080b961704568478c2aa18763ab524cc89e679d3 100644 --- a/source/armarx/navigation/gui-plugins/LocationGraphEditor/WidgetController.h +++ b/source/armarx/navigation/gui-plugins/LocationGraphEditor/WidgetController.h @@ -40,8 +40,8 @@ #include "Visu.h" #include "widgets/graph_scene.h" #include <armarx/navigation/core/Graph.h> -#include <armarx/navigation/gui-plugins/LocationGraphEditor/ui_LocationGraphEditorWidget.h> #include <armarx/navigation/core/aron/Location.aron.generated.h> +#include <armarx/navigation/gui-plugins/LocationGraphEditor/ui_LocationGraphEditorWidget.h> class QDialog; diff --git a/source/armarx/navigation/local_planning/CMakeLists.txt b/source/armarx/navigation/local_planning/CMakeLists.txt index 5894b6afaee40284153c101b3a99dc0a6bf71a57..78a91fc0ad08db6ef22a3d443781d824d7aaad44 100644 --- a/source/armarx/navigation/local_planning/CMakeLists.txt +++ b/source/armarx/navigation/local_planning/CMakeLists.txt @@ -9,12 +9,3 @@ armarx_add_library(local_planning HEADERS ./LocalPlanner.h ./TimedElasticBands.h ) - - -# find_package(MyLib QUIET) armarx_build_if(MyLib_FOUND "MyLib not available") -# all target_include_directories must be guarded by if(Xyz_FOUND) for multiple -# libraries write: if(X_FOUND AND Y_FOUND).... if(MyLib_FOUND) -# target_include_directories(local_planning PUBLIC ${MyLib_INCLUDE_DIRS}) -# endif() - -# add unit tests add_subdirectory(test) diff --git a/source/armarx/navigation/memory/client/graph/Reader.cpp b/source/armarx/navigation/memory/client/graph/Reader.cpp index 7d3513a1ce38a4d579fcc32753d598fcb8c03bf6..9804927250f74b59c7f160cc65cf7f753de9431a 100644 --- a/source/armarx/navigation/memory/client/graph/Reader.cpp +++ b/source/armarx/navigation/memory/client/graph/Reader.cpp @@ -14,8 +14,8 @@ #include <armarx/navigation/core/Graph.h> #include <armarx/navigation/core/aron/Graph.aron.generated.h> -#include <armarx/navigation/graph/constants.h> #include <armarx/navigation/core/aron/Location.aron.generated.h> +#include <armarx/navigation/graph/constants.h> #include <armarx/navigation/location/constants.h> namespace armarx::navigation::mem::client::graph diff --git a/source/armarx/navigation/memory/client/graph/Reader.h b/source/armarx/navigation/memory/client/graph/Reader.h index 9a65ed9f03657248c62af5b81577adfd3328e564..a61bc7c6dbafcbf24e5e36afa82e3704566b988c 100644 --- a/source/armarx/navigation/memory/client/graph/Reader.h +++ b/source/armarx/navigation/memory/client/graph/Reader.h @@ -24,8 +24,8 @@ #include <RobotAPI/libraries/armem/client/util/SimpleReaderBase.h> #include <armarx/navigation/core/Graph.h> -#include <armarx/navigation/core/types.h> #include <armarx/navigation/core/aron/Location.aron.generated.h> +#include <armarx/navigation/core/types.h> namespace armarx::navigation::mem::client::graph { diff --git a/source/armarx/navigation/memory/client/parameterization/Writer.cpp b/source/armarx/navigation/memory/client/parameterization/Writer.cpp index d40f300f28d8cc787cc487d736e2f3049e11a8c0..b1b8610d6b680047078e44283b50700e068d17fd 100644 --- a/source/armarx/navigation/memory/client/parameterization/Writer.cpp +++ b/source/armarx/navigation/memory/client/parameterization/Writer.cpp @@ -8,10 +8,9 @@ namespace armarx::navigation::mem::client::param { bool - Writer::store( - const std::unordered_map<core::StackLayer, aron::data::DictPtr>& stack, - const std::string& clientID, - const core::TimestampUs& timestamp) + Writer::store(const std::unordered_map<core::StackLayer, aron::data::DictPtr>& stack, + const std::string& clientID, + const core::TimestampUs& timestamp) { ARMARX_CHECK(not stack.empty()); diff --git a/source/armarx/navigation/memory/client/parameterization/Writer.h b/source/armarx/navigation/memory/client/parameterization/Writer.h index d48c3e807aa8b1502e3a5669f548982b6c1cdd67..eda0e83ea2e8fd32b2e28952916f53ab858e108f 100644 --- a/source/armarx/navigation/memory/client/parameterization/Writer.h +++ b/source/armarx/navigation/memory/client/parameterization/Writer.h @@ -36,8 +36,7 @@ namespace armarx::navigation::mem::client::param using armem::client::util::SimpleWriterBase::SimpleWriterBase; - bool store(const std::unordered_map<core::StackLayer, - aron::data::DictPtr>& stack, + bool store(const std::unordered_map<core::StackLayer, aron::data::DictPtr>& stack, const std::string& clientID, const core::TimestampUs& timestamp); diff --git a/source/armarx/navigation/safety_control/LaserBasedProximity.h b/source/armarx/navigation/safety_control/LaserBasedProximity.h index 94108ade932d7be0c1e5eeea79b2a9ec1eb9b3d9..1f1cf51989a0d90585bf8ecc61c71df07bbef0ce 100644 --- a/source/armarx/navigation/safety_control/LaserBasedProximity.h +++ b/source/armarx/navigation/safety_control/LaserBasedProximity.h @@ -33,8 +33,7 @@ namespace armarx::navigation::safe_ctrl Algorithms algorithm() const override; aron::data::DictPtr toAron() const override; - static LaserBasedProximityParams - FromAron(const aron::data::DictPtr& dict); + static LaserBasedProximityParams FromAron(const aron::data::DictPtr& dict); }; class LaserBasedProximity : virtual public SafetyController diff --git a/source/armarx/navigation/server/CMakeLists.txt b/source/armarx/navigation/server/CMakeLists.txt index 51cd9baeb7234d53b2b7da46a3bbb4bbcd9d96e4..b9f7a261ce1b87d80a340ede8bb1c34c3c1d3ef2 100644 --- a/source/armarx/navigation/server/CMakeLists.txt +++ b/source/armarx/navigation/server/CMakeLists.txt @@ -47,7 +47,7 @@ armarx_add_library(server armarx_navigation::safety_control armarx_navigation::memory PRIVATE - range-v3 + range-v3::range-v3 ) armarx_add_test(server_test diff --git a/source/armarx/navigation/server/introspection/ArvizIntrospector.h b/source/armarx/navigation/server/introspection/ArvizIntrospector.h index 1cd9cf1265893097e3c5ba97b33c6ec0dcd74288..39d45649941b1ee1552ba9163338fbc9c7287cd2 100644 --- a/source/armarx/navigation/server/introspection/ArvizIntrospector.h +++ b/source/armarx/navigation/server/introspection/ArvizIntrospector.h @@ -59,7 +59,7 @@ namespace armarx::navigation::server void onSafetyControllerResult(const safe_ctrl::SafetyControllerResult& result) override; void onGoal(const core::Pose& goal) override; - + void success(); void failure(); @@ -68,7 +68,6 @@ namespace armarx::navigation::server void onGlobalGraph(const core::Graph& graph) override; - // // Non-API ArvizIntrospector(ArvizIntrospector&& other) noexcept; ArvizIntrospector& operator=(ArvizIntrospector&&) noexcept; diff --git a/source/armarx/navigation/server/introspection/MemoryIntrospector.h b/source/armarx/navigation/server/introspection/MemoryIntrospector.h index c153b5218a529f8db719ad001a638e7c45f8cb0a..18ecf76906c6e5310bee7765b17a72347ee66c7a 100644 --- a/source/armarx/navigation/server/introspection/MemoryIntrospector.h +++ b/source/armarx/navigation/server/introspection/MemoryIntrospector.h @@ -46,8 +46,8 @@ namespace armarx::navigation::server void onGoal(const core::Pose& goal) override; - void success() override {}; - void failure() override {}; + void success() override{}; + void failure() override{}; void onGlobalShortestPath(const std::vector<core::Pose>& path) override diff --git a/source/armarx/navigation/server/monitoring/GoalReachedMonitor.h b/source/armarx/navigation/server/monitoring/GoalReachedMonitor.h index 9382cbae92a4108d9b453ea506ddc42e4cb76f35..34f9e49d72d2a69b3430a15397b5b09c02cb357b 100644 --- a/source/armarx/navigation/server/monitoring/GoalReachedMonitor.h +++ b/source/armarx/navigation/server/monitoring/GoalReachedMonitor.h @@ -33,7 +33,7 @@ namespace armarx::navigation::server struct GoalReachedMonitorConfig { - float posTh{70.F}; // [mm] + float posTh{70.F}; // [mm] float oriTh{VirtualRobot::MathTools::deg2rad(5.F)}; // [rad] float linearVelTh{100.F}; // [mm/s] diff --git a/source/armarx/navigation/server/parameterization/MemoryParameterizationService.cpp b/source/armarx/navigation/server/parameterization/MemoryParameterizationService.cpp index 94bbd51bbab1b4c2beb8d6f1234f45b9dab8e64d..68067ad9aed8358ec23e99ce28f3646ecb1994af 100644 --- a/source/armarx/navigation/server/parameterization/MemoryParameterizationService.cpp +++ b/source/armarx/navigation/server/parameterization/MemoryParameterizationService.cpp @@ -19,8 +19,7 @@ namespace armarx::navigation::server using aron::data::Dict; - const auto getElementOrNull = - [¶ms](const core::StackLayer& layer) -> Dict::PointerType + const auto getElementOrNull = [¶ms](const core::StackLayer& layer) -> Dict::PointerType { const std::string key = core::StackLayerNames.to_name(layer); diff --git a/source/armarx/navigation/server/test/serverTest.cpp b/source/armarx/navigation/server/test/serverTest.cpp index 7bcfcc6db4599200ef5501eede53208890aa0924..5fb6ce9232b61ea5dcbfb3d0709a0c87958809dd 100644 --- a/source/armarx/navigation/server/test/serverTest.cpp +++ b/source/armarx/navigation/server/test/serverTest.cpp @@ -55,11 +55,11 @@ BOOST_AUTO_TEST_CASE(testNavigator) scene.robot->setGlobalPose(Eigen::Matrix4f::Identity(), false); // TODO create static shared ctor - server::NavigationStack stack{ - .globalPlanner = - std::make_shared<global_planning::Point2Point>(global_planning::Point2PointParams(), scene), - .trajectoryControl = std::make_shared<traj_ctrl::TrajectoryFollowingController>( - traj_ctrl::TrajectoryFollowingControllerParams(), scene)}; + server::NavigationStack stack{.globalPlanner = std::make_shared<global_planning::Point2Point>( + global_planning::Point2PointParams(), scene), + .trajectoryControl = + std::make_shared<traj_ctrl::TrajectoryFollowingController>( + traj_ctrl::TrajectoryFollowingControllerParams(), scene)}; // Executor. server::DummyExecutor executor{scene.robot, server::DummyExecutor::Params()}; diff --git a/source/armarx/navigation/trajectory_control/TrajectoryFollowingController.h b/source/armarx/navigation/trajectory_control/TrajectoryFollowingController.h index 064de2fc8b7b043bea6658e6ee4f6d3f96beedc8..a9a8a94c46ed3b149d7a48f65e04660a6b5758c6 100644 --- a/source/armarx/navigation/trajectory_control/TrajectoryFollowingController.h +++ b/source/armarx/navigation/trajectory_control/TrajectoryFollowingController.h @@ -40,8 +40,7 @@ namespace armarx::navigation::traj_ctrl Algorithms algorithm() const override; aron::data::DictPtr toAron() const override; - static TrajectoryFollowingControllerParams - FromAron(const aron::data::DictPtr& dict); + static TrajectoryFollowingControllerParams FromAron(const aron::data::DictPtr& dict); }; class TrajectoryFollowingController : virtual public TrajectoryController diff --git a/source/armarx/navigation/util/util.cpp b/source/armarx/navigation/util/util.cpp index ba409ab3b12782d07b3bc5113b026a681c69b204..3438ce6719616a684adb532fc0a96ad58fbcc725 100644 --- a/source/armarx/navigation/util/util.cpp +++ b/source/armarx/navigation/util/util.cpp @@ -37,9 +37,9 @@ #include <ArmarXCore/core/exceptions/local/ExpressionException.h> -#include <RobotAPI/libraries/core/FramedPose.h> #include <RobotAPI/libraries/ArmarXObjects/ObjectFinder.h> #include <RobotAPI/libraries/armem_vision/OccupancyGridHelper.h> +#include <RobotAPI/libraries/core/FramedPose.h> namespace armarx::navigation::util { @@ -63,7 +63,8 @@ namespace armarx::navigation::util } VirtualRobot::SceneObjectSetPtr - asSceneObjects(const armem::OccupancyGrid& occupancyGrid, const OccupancyGridHelper::Params& params) + asSceneObjects(const armem::OccupancyGrid& occupancyGrid, + const OccupancyGridHelper::Params& params) { const OccupancyGridHelper ocHelper(occupancyGrid, params); const auto obstacles = ocHelper.obstacles(); diff --git a/source/armarx/navigation/util/util.h b/source/armarx/navigation/util/util.h index c15487f1ae194847c8a53e74fa52f389ba786dcd..805b7f7c6a1dafeac257ab432084bbe9c2d168af 100644 --- a/source/armarx/navigation/util/util.h +++ b/source/armarx/navigation/util/util.h @@ -25,13 +25,14 @@ #include <VirtualRobot/VirtualRobot.h> #include <RobotAPI/libraries/ArmarXObjects/ObjectPose.h> -#include <RobotAPI/libraries/armem_vision/types.h> #include <RobotAPI/libraries/armem_vision/OccupancyGridHelper.h> +#include <RobotAPI/libraries/armem_vision/types.h> namespace armarx::navigation::util { VirtualRobot::SceneObjectSetPtr asSceneObjects(const objpose::ObjectPoseSeq& objectPoses); - VirtualRobot::SceneObjectSetPtr asSceneObjects(const armem::OccupancyGrid& occupancyGrid, const OccupancyGridHelper::Params& params); + VirtualRobot::SceneObjectSetPtr asSceneObjects(const armem::OccupancyGrid& occupancyGrid, + const OccupancyGridHelper::Params& params); } // namespace armarx::navigation::util