diff --git a/CMakeLists.txt b/CMakeLists.txt index 96c7ea6e7d66e74275329f4e8df41d2eabaf0f36..cb2d0eee3382b2b9882548df18b5caf5f47edd7b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -60,6 +60,29 @@ else() message(STATUS "FCL does not use Octomap") endif() +# Find FLANN (optional) +find_package(flann QUIET) +if (FLANN_FOUND) + set(FCL_HAVE_FLANN 1) + include_directories(${FLANN_INCLUDE_DIRS}) + link_directories(${FLANN_LIBRARY_DIRS}) + message(STATUS "FCL uses Flann") +else() + message(STATUS "FCL does not use Flann") +endif() + + +find_package(tinyxml QUIET) +if (TINYXML_FOUND) + set(FCL_HAVE_TINYXML 1) + include_directories(${TINYXML_INCLUDE_DIRS}) + link_directories(${TINYXML_LIBRARY_DIRS}) + message(STATUS "FCL uses tinyxml") +else() + message(STATUS "FCL does not use tinyxml") +endif() + + find_package(Boost COMPONENTS thread date_time filesystem system unit_test_framework REQUIRED) include_directories(${Boost_INCLUDE_DIR}) diff --git a/CMakeModules/Findflann.cmake b/CMakeModules/Findflann.cmake new file mode 100644 index 0000000000000000000000000000000000000000..3b3f6dc09474812f27f97268c9cd7e09c6fb20f6 --- /dev/null +++ b/CMakeModules/Findflann.cmake @@ -0,0 +1,14 @@ +# Find FLANN, a Fast Library for Approximate Nearest Neighbors + +include(FindPackageHandleStandardArgs) + +find_path(FLANN_INCLUDE_DIR flann.hpp PATH_SUFFIXES flann) +if (FLANN_INCLUDE_DIR) + file(READ "${FLANN_INCLUDE_DIR}/config.h" FLANN_CONFIG) + string(REGEX REPLACE ".*FLANN_VERSION_ \"([0-9.]+)\".*" "\\1" FLANN_VERSION ${FLANN_CONFIG}) + if(NOT FLANN_VERSION VERSION_LESS flann_FIND_VERSION) + string(REGEX REPLACE "/flann$" "" FLANN_INCLUDE_DIRS ${FLANN_INCLUDE_DIR}) + endif() +endif() + +find_package_handle_standard_args(flann DEFAULT_MSG FLANN_INCLUDE_DIRS) diff --git a/CMakeModules/Findtinyxml.cmake b/CMakeModules/Findtinyxml.cmake new file mode 100644 index 0000000000000000000000000000000000000000..4c7cbde895eaaeb3e324bad5bd073de695bb87c1 --- /dev/null +++ b/CMakeModules/Findtinyxml.cmake @@ -0,0 +1,27 @@ +# this module was taken from http://trac.evemu.org/browser/trunk/cmake/FindTinyXML.cmake +# - Find TinyXML +# Find the native TinyXML includes and library +# +# TINYXML_FOUND - True if TinyXML found. +# TINYXML_INCLUDE_DIR - where to find tinyxml.h, etc. +# TINYXML_LIBRARIES - List of libraries when using TinyXML. +# + +IF( TINYXML_INCLUDE_DIRS) +# Already in cache, be silent +SET( TinyXML_FIND_QUIETLY TRUE ) +ENDIF( TINYXML_INCLUDE_DIRS ) + +FIND_PATH( TINYXML_INCLUDE_DIRS "tinyxml.h" +PATH_SUFFIXES "tinyxml" ) + +FIND_LIBRARY( TINYXML_LIBRARY_DIRS +NAMES "tinyxml" +PATH_SUFFIXES "tinyxml" ) + +# handle the QUIETLY and REQUIRED arguments and set TINYXML_FOUND to TRUE if +# all listed variables are TRUE +INCLUDE( "FindPackageHandleStandardArgs" ) +FIND_PACKAGE_HANDLE_STANDARD_ARGS( "TinyXML" DEFAULT_MSG TINYXML_INCLUDE_DIRS TINYXML_LIBRARY_DIRS ) + +MARK_AS_ADVANCED( TINYXML_INCLUDE_DIRS TINYXML_LIBRARY_DIRS ) \ No newline at end of file diff --git a/include/fcl/BV/AABB.h b/include/fcl/BV/AABB.h index ec04609b043b141f2aea2043d692d88cc31d500b..3a4800fdb065674e2cae90418a5184cbbeaa7263 100644 --- a/include/fcl/BV/AABB.h +++ b/include/fcl/BV/AABB.h @@ -186,6 +186,12 @@ public: return (max_ - min_).sqrLength(); } + /// @brief Radius of the AABB + inline FCL_REAL radius() const + { + return (max_ - min_).length() / 2; + } + /// @brief Center of the AABB inline Vec3f center() const { diff --git a/include/fcl/BVH/BVH_model.h b/include/fcl/BVH/BVH_model.h index 4c6a0699d8c093618fb2d2ab5f69b1f3d2391f94..847db5bee0c6f9a94febf6e96dd98403e2d9f269 100644 --- a/include/fcl/BVH/BVH_model.h +++ b/include/fcl/BVH/BVH_model.h @@ -191,6 +191,62 @@ public: makeParentRelativeRecurse(0, I, Vec3f()); } + Vec3f computeCOM() const + { + FCL_REAL vol = 0; + Vec3f com; + for(int i = 0; i < num_tris; ++i) + { + const Triangle& tri = tri_indices[i]; + FCL_REAL d_six_vol = (vertices[tri[0]].cross(vertices[tri[1]])).dot(vertices[tri[2]]); + vol += d_six_vol; + com += (vertices[tri[0]] + vertices[tri[1]] + vertices[tri[2]]) * d_six_vol; + } + + return com / (vol * 4); + } + + FCL_REAL computeVolume() const + { + FCL_REAL vol = 0; + for(int i = 0; i < num_tris; ++i) + { + const Triangle& tri = tri_indices[i]; + FCL_REAL d_six_vol = (vertices[tri[0]].cross(vertices[tri[1]])).dot(vertices[tri[2]]); + vol += d_six_vol; + } + + return vol / 6; + } + + Matrix3f computeMomentofInertia() const + { + Matrix3f C(0, 0, 0, + 0, 0, 0, + 0, 0, 0); + + Matrix3f C_canonical(1/60.0, 1/120.0, 1/120.0, + 1/120.0, 1/60.0, 1/120.0, + 1/120.0, 1/120.0, 1/60.0); + + for(int i = 0; i < num_tris; ++i) + { + const Triangle& tri = tri_indices[i]; + const Vec3f& v1 = vertices[tri[0]]; + const Vec3f& v2 = vertices[tri[1]]; + const Vec3f& v3 = vertices[tri[2]]; + FCL_REAL d_six_vol = (v1.cross(v2)).dot(v3); + Matrix3f A(v1, v2, v3); + C += transpose(A) * C_canonical * A * d_six_vol; + } + + FCL_REAL trace_C = C(0, 0) + C(1, 1) + C(2, 2); + + return Matrix3f(trace_C - C(0, 0), -C(0, 1), -C(0, 2), + -C(1, 0), trace_C - C(1, 1), -C(1, 2), + -C(2, 0), -C(2, 1), trace_C - C(2, 2)); + } + public: /// @brief Geometry point data Vec3f* vertices; diff --git a/include/fcl/collision_data.h b/include/fcl/collision_data.h index 9621d33d6daa97e74cd142a9e1fe83bb50bae3dc..5ea511434682cea1bb059a635af8a0350bce0387 100644 --- a/include/fcl/collision_data.h +++ b/include/fcl/collision_data.h @@ -39,6 +39,10 @@ #define FCL_COLLISION_DATA_H #include "fcl/collision_object.h" +#include "fcl/learning/classifier.h" +#include "fcl/knn/nearest_neighbors.h" + + #include "fcl/math/vec_3f.h" #include <vector> #include <set> @@ -447,7 +451,7 @@ struct ContinuousCollisionRequest CCDSolverType ccd_solver_type; ContinuousCollisionRequest(std::size_t num_max_iterations_ = 10, - FCL_REAL toc_err_ = 0, + FCL_REAL toc_err_ = 0.0001, CCDMotionType ccd_motion_type_ = CCDM_TRANS, GJKSolverType gjk_solver_type_ = GST_LIBCCD, CCDSolverType ccd_solver_type_ = CCDC_NAIVE) : num_max_iterations(num_max_iterations_), @@ -467,6 +471,8 @@ struct ContinuousCollisionResult /// @brief time of contact in [0, 1] FCL_REAL time_of_contact; + + Transform3f contact_tf1, contact_tf2; ContinuousCollisionResult() : is_collide(false), time_of_contact(1.0) { @@ -474,28 +480,36 @@ struct ContinuousCollisionResult }; -enum PenetrationDepthType {PDT_LOCAL, PDT_AL}; +enum PenetrationDepthType {PDT_TRANSLATIONAL, PDT_GENERAL_EULER, PDT_GENERAL_QUAT, PDT_GENERAL_EULER_BALL, PDT_GENERAL_QUAT_BALL}; -struct PenetrationDepthMetricBase -{ - virtual FCL_REAL operator() (const Transform3f& tf1, const Transform3f& tf2) const = 0; -}; +enum KNNSolverType {KNN_LINEAR, KNN_GNAT, KNN_SQRTAPPROX}; -struct WeightEuclideanPDMetric : public PenetrationDepthMetricBase -{ - -}; struct PenetrationDepthRequest { + void* classifier; + + NearestNeighbors<Transform3f>::DistanceFunction distance_func; + + /// @brief KNN solver type + KNNSolverType knn_solver_type; + /// @brief PD algorithm type PenetrationDepthType pd_type; /// @brief gjk solver type GJKSolverType gjk_solver_type; - PenetrationDepthRequest(PenetrationDepthType pd_type_ = PDT_LOCAL, - GJKSolverType gjk_solver_type_ = GST_LIBCCD) : pd_type(pd_type_), + std::vector<Transform3f> contact_vectors; + + PenetrationDepthRequest(void* classifier_, + NearestNeighbors<Transform3f>::DistanceFunction distance_func_, + KNNSolverType knn_solver_type_ = KNN_LINEAR, + PenetrationDepthType pd_type_ = PDT_TRANSLATIONAL, + GJKSolverType gjk_solver_type_ = GST_LIBCCD) : classifier(classifier_), + distance_func(distance_func_), + knn_solver_type(knn_solver_type_), + pd_type(pd_type_), gjk_solver_type(gjk_solver_type_) { } @@ -507,9 +521,7 @@ struct PenetrationDepthResult FCL_REAL pd_value; /// @brief the transform where the collision is resolved - Transform3f resolve_trans; - - + Transform3f resolved_tf; }; diff --git a/include/fcl/collision_object.h b/include/fcl/collision_object.h index 00716051a33e711a41cc3baf6d7b04e1b5adb0d5..7d54ffd65fafd5059ffa8f405710ccdb53702582 100644 --- a/include/fcl/collision_object.h +++ b/include/fcl/collision_object.h @@ -116,6 +116,33 @@ public: /// @brief threshold for free (<= is free) FCL_REAL threshold_free; + /// @brief compute center of mass + virtual Vec3f computeCOM() const { return Vec3f(); } + + /// @brief compute the inertia matrix, related to the origin + virtual Matrix3f computeMomentofInertia() const { return Matrix3f(); } + + /// @brief compute the volume + virtual FCL_REAL computeVolume() const { return 0; } + + /// @brief compute the inertia matrix, related to the com + virtual Matrix3f computeMomentofInertiaRelatedToCOM() const + { + Matrix3f C = computeMomentofInertia(); + Vec3f com = computeCOM(); + FCL_REAL V = computeVolume(); + + return Matrix3f(C(0, 0) - V * (com[1] * com[1] + com[2] * com[2]), + C(0, 1) + V * com[0] * com[1], + C(0, 2) + V * com[0] * com[2], + C(1, 0) + V * com[1] * com[0], + C(1, 1) - V * (com[0] * com[0] + com[2] * com[2]), + C(1, 2) + V * com[1] * com[2], + C(2, 0) + V * com[2] * com[0], + C(2, 1) + V * com[2] * com[1], + C(2, 2) - V * (com[0] * com[0] + com[1] * com[1])); + } + }; /// @brief the object for collision or distance computation, contains the geometry and the transform information diff --git a/include/fcl/config.h.in b/include/fcl/config.h.in index ba1aed54e5c4e66a43e304ab1e80481b856d890f..e178b6b58f413a8dd3841d84d53b6cb0a672e3f2 100644 --- a/include/fcl/config.h.in +++ b/include/fcl/config.h.in @@ -42,5 +42,7 @@ #cmakedefine01 FCL_HAVE_SSE #cmakedefine01 FCL_HAVE_OCTOMAP +#cmakedefine01 FCL_HAVE_FLANN +#cmakedefine01 FCL_HAVE_TINYXML #endif diff --git a/include/fcl/continuous_collision.h b/include/fcl/continuous_collision.h index 0a6ab1ab19fa06b9940be25422bc9d7b644d90a1..ef6812d0ac9a037eda0a974267902048580c9729 100644 --- a/include/fcl/continuous_collision.h +++ b/include/fcl/continuous_collision.h @@ -9,15 +9,15 @@ namespace fcl { /// @brief continous collision checking between two objects -FCL_REAL continuous_collide(const CollisionGeometry* o1, const Transform3f& tf1_beg, const Transform3f& tf1_end, - const CollisionGeometry* o2, const Transform3f& tf2_beg, const Transform3f& tf2_end, - const ContinuousCollisionRequest& request, - ContinuousCollisionResult& result); +FCL_REAL continuousCollide(const CollisionGeometry* o1, const Transform3f& tf1_beg, const Transform3f& tf1_end, + const CollisionGeometry* o2, const Transform3f& tf2_beg, const Transform3f& tf2_end, + const ContinuousCollisionRequest& request, + ContinuousCollisionResult& result); -FCL_REAL continuous_collide(const CollisionObject* o1, const Transform3f& tf1_end, - const CollisionObject* o2, const Transform3f& tf2_end, - const ContinuousCollisionRequest& request, - ContinuousCollisionResult& result); +FCL_REAL continuousCollide(const CollisionObject* o1, const Transform3f& tf1_end, + const CollisionObject* o2, const Transform3f& tf2_end, + const ContinuousCollisionRequest& request, + ContinuousCollisionResult& result); FCL_REAL collide(const ContinuousCollisionObject* o1, const ContinuousCollisionObject* o2, const ContinuousCollisionRequest& request, diff --git a/include/fcl/exception.h b/include/fcl/exception.h new file mode 100644 index 0000000000000000000000000000000000000000..8f1b11beb353ae9061c1c9d949de2a5457563ff8 --- /dev/null +++ b/include/fcl/exception.h @@ -0,0 +1,35 @@ +#ifndef FCL_EXCEPTION_H +#define FCL_EXCEPTION_H + +#include <stdexcept> +#include <string> + +namespace fcl +{ + +class Exception : public std::runtime_error +{ +public: + + /** \brief This is just a wrapper on std::runtime_error */ + explicit + Exception(const std::string& what) : std::runtime_error(what) + { + } + + /** \brief This is just a wrapper on std::runtime_error with a + prefix added */ + Exception(const std::string &prefix, const std::string& what) : std::runtime_error(prefix + ": " + what) + { + } + + virtual ~Exception(void) throw() + { + } + +}; + +} + + +#endif diff --git a/include/fcl/knn/greedy_kcenters.h b/include/fcl/knn/greedy_kcenters.h new file mode 100644 index 0000000000000000000000000000000000000000..f2f64848e63d1564224e66dbfcdef2066677a8aa --- /dev/null +++ b/include/fcl/knn/greedy_kcenters.h @@ -0,0 +1,128 @@ +/********************************************************************* + * Software License Agreement (BSD License) + * + * Copyright (c) 2011, Rice University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of the Rice University nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *********************************************************************/ + +/* Author: Mark Moll */ + +#ifndef FCL_KNN_GREEDY_KCENTERS_H +#define FCL_KNN_GREEDY_KCENTERS_H + + +#include "fcl/math/sampling.h" + +namespace fcl +{ +/// @brief An instance of this class can be used to greedily select a given +/// number of representatives from a set of data points that are all far +/// apart from each other. +template<typename _T> +class GreedyKCenters +{ +public: + /// @brief The definition of a distance function + typedef boost::function<double(const _T&, const _T&)> DistanceFunction; + + GreedyKCenters(void) + { + } + + virtual ~GreedyKCenters(void) + { + } + + /// @brief Set the distance function to use + void setDistanceFunction(const DistanceFunction &distFun) + { + distFun_ = distFun; + } + + /// @brief Get the distance function used + const DistanceFunction& getDistanceFunction(void) const + { + return distFun_; + } + + /// @brief Greedy algorithm for selecting k centers + /// @param data a vector of data points + /// @param k the desired number of centers + /// @param centers a vector of length k containing the indices into data of the k centers + /// @param dists a 2-dimensional array such that dists[i][j] is the distance between data[i] and data[center[j]] + void kcenters(const std::vector<_T>& data, unsigned int k, + std::vector<unsigned int>& centers, std::vector<std::vector<double> >& dists) + { + // array containing the minimum distance between each data point + // and the centers computed so far + std::vector<double> minDist(data.size(), std::numeric_limits<double>::infinity()); + + centers.clear(); + centers.reserve(k); + dists.resize(data.size(), std::vector<double>(k)); + // first center is picked randomly + centers.push_back(rng_.uniformInt(0, data.size() - 1)); + for (unsigned i=1; i<k; ++i) + { + unsigned ind; + const _T& center = data[centers[i - 1]]; + double maxDist = -std::numeric_limits<double>::infinity(); + for (unsigned j=0; j<data.size(); ++j) + { + if ((dists[j][i-1] = distFun_(data[j], center)) < minDist[j]) + minDist[j] = dists[j][i - 1]; + // the j-th center is the one furthest away from center 0,..,j-1 + if (minDist[j] > maxDist) + { + ind = j; + maxDist = minDist[j]; + } + } + // no more centers available + if (maxDist < std::numeric_limits<double>::epsilon()) break; + centers.push_back(ind); + } + + const _T& center = data[centers.back()]; + unsigned i = centers.size() - 1; + for (unsigned j = 0; j < data.size(); ++j) + dists[j][i] = distFun_(data[j], center); + } + +protected: + /// @brief The used distance function + DistanceFunction distFun_; + + /// Random number generator used to select first center + RNG rng_; +}; +} + +#endif diff --git a/include/fcl/knn/nearest_neighbors.h b/include/fcl/knn/nearest_neighbors.h new file mode 100644 index 0000000000000000000000000000000000000000..100d8f878b4d66b7cd0806656c2ab3b351d5c7a9 --- /dev/null +++ b/include/fcl/knn/nearest_neighbors.h @@ -0,0 +1,115 @@ +/********************************************************************* + * Software License Agreement (BSD License) + * + * Copyright (c) 2008, Willow Garage, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of the Willow Garage nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *********************************************************************/ + +/* Author: Ioan Sucan */ + +#ifndef FCL_KNN_NEAREST_NEIGHBORS_H +#define FCL_KNN_NEAREST_NEIGHBORS_H + +#include <vector> +#include <boost/bind.hpp> +#include <boost/function.hpp> + +namespace fcl +{ +/// @brief Abstract representation of a container that can perform nearest neighbors queries +template<typename _T> +class NearestNeighbors +{ +public: + + /// @brief The definition of a distance function + typedef boost::function<double(const _T&, const _T&)> DistanceFunction; + + NearestNeighbors(void) + { + } + + virtual ~NearestNeighbors(void) + { + } + + /// @brief Set the distance function to use + virtual void setDistanceFunction(const DistanceFunction &distFun) + { + distFun_ = distFun; + } + + /// @brief Get the distance function used + const DistanceFunction& getDistanceFunction(void) const + { + return distFun_; + } + + /// @brief Clear the datastructure + virtual void clear(void) = 0; + + /// @brief Add an element to the datastructure + virtual void add(const _T &data) = 0; + + /// @brief Add a vector of points + virtual void add(const std::vector<_T> &data) + { + for (typename std::vector<_T>::const_iterator elt = data.begin() ; elt != data.end() ; ++elt) + add(*elt); + } + + /// @brief Remove an element from the datastructure + virtual bool remove(const _T &data) = 0; + + /// @brief Get the nearest neighbor of a point + virtual _T nearest(const _T &data) const = 0; + + /// @brief Get the k-nearest neighbors of a point + virtual void nearestK(const _T &data, std::size_t k, std::vector<_T> &nbh) const = 0; + + /// @brief Get the nearest neighbors of a point, within a specified radius + virtual void nearestR(const _T &data, double radius, std::vector<_T> &nbh) const = 0; + + /// @brief Get the number of elements in the datastructure + virtual std::size_t size(void) const = 0; + + /// @brief Get all the elements in the datastructure + virtual void list(std::vector<_T> &data) const = 0; + +protected: + + /// @brief The used distance function + DistanceFunction distFun_; + +}; +} + + +#endif diff --git a/include/fcl/knn/nearest_neighbors_GNAT.h b/include/fcl/knn/nearest_neighbors_GNAT.h new file mode 100644 index 0000000000000000000000000000000000000000..f7cd0d091a955bd1fcfc179ef182c0c123e82291 --- /dev/null +++ b/include/fcl/knn/nearest_neighbors_GNAT.h @@ -0,0 +1,696 @@ +/********************************************************************* + * Software License Agreement (BSD License) + * + * Copyright (c) 2011, Rice University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of the Rice University nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *********************************************************************/ + +/* Author: Mark Moll */ + +#ifndef FCL_KNN_NEAREST_NEIGHBORS_GNAT_H +#define FCL_KNN_NEAREST_NEIGHBORS_GNAT_H + +#include "fcl/knn/nearest_neighbors.h" +#include "fcl/knn/greedy_kcenters.h" +#include "fcl/exception.h" +#include <boost/unordered_set.hpp> +#include <queue> +#include <algorithm> + + +namespace fcl +{ + +/** \brief Geometric Near-neighbor Access Tree (GNAT), a data + structure for nearest neighbor search. + + See: + S. Brin, "Near neighbor search in large metric spaces," in Proc. 21st + Conf. on Very Large Databases (VLDB), pp. 574¡V584, 1995. + +*/ +template<typename _T> +class NearestNeighborsGNAT : public NearestNeighbors<_T> +{ +protected: + /// \cond IGNORE + // internally, we use a priority queue for nearest neighbors, paired + // with their distance to the query point + typedef std::pair<const _T*,double> DataDist; + struct DataDistCompare + { + bool operator()(const DataDist& d0, const DataDist& d1) + { + return d0.second < d1.second; + } + }; + typedef std::priority_queue<DataDist, std::vector<DataDist>, DataDistCompare> NearQueue; + + // another internal data structure is a priority queue of nodes to + // check next for possible nearest neighbors + class Node; + typedef std::pair<Node*,double> NodeDist; + struct NodeDistCompare + { + bool operator()(const NodeDist& n0, const NodeDist& n1) const + { + return (n0.second - n0.first->maxRadius_) > (n1.second - n1.first->maxRadius_); + } + }; + typedef std::priority_queue<NodeDist, std::vector<NodeDist>, NodeDistCompare> NodeQueue; + /// \endcond + +public: + NearestNeighborsGNAT(unsigned int degree = 4, unsigned int minDegree = 2, + unsigned int maxDegree = 6, unsigned int maxNumPtsPerLeaf = 50, + unsigned int removedCacheSize = 50, bool rebalancing = false) + : NearestNeighbors<_T>(), tree_(NULL), degree_(degree), + minDegree_(std::min(degree,minDegree)), maxDegree_(std::max(maxDegree,degree)), + maxNumPtsPerLeaf_(maxNumPtsPerLeaf), size_(0), + rebuildSize_(rebalancing ? maxNumPtsPerLeaf*degree : std::numeric_limits<std::size_t>::max()), + removedCacheSize_(removedCacheSize) + { + } + + virtual ~NearestNeighborsGNAT(void) + { + if (tree_) + delete tree_; + } + /// \brief Set the distance function to use + virtual void setDistanceFunction(const typename NearestNeighbors<_T>::DistanceFunction &distFun) + { + NearestNeighbors<_T>::setDistanceFunction(distFun); + pivotSelector_.setDistanceFunction(distFun); + if (tree_) + rebuildDataStructure(); + } + virtual void clear(void) + { + if (tree_) + { + delete tree_; + tree_ = NULL; + } + size_ = 0; + removed_.clear(); + if (rebuildSize_ != std::numeric_limits<std::size_t>::max()) + rebuildSize_ = maxNumPtsPerLeaf_ * degree_; + } + + virtual void add(const _T &data) + { + if (tree_) + { + if (isRemoved(data)) + rebuildDataStructure(); + tree_->add(*this, data); + } + else + { + tree_ = new Node(degree_, maxNumPtsPerLeaf_, data); + size_ = 1; + } + } + virtual void add(const std::vector<_T> &data) + { + if (tree_) + NearestNeighbors<_T>::add(data); + else if (data.size()>0) + { + tree_ = new Node(degree_, maxNumPtsPerLeaf_, data[0]); + for (unsigned int i=1; i<data.size(); ++i) + tree_->data_.push_back(data[i]); + if (tree_->needToSplit(*this)) + tree_->split(*this); + } + size_ += data.size(); + } + /// \brief Rebuild the internal data structure. + void rebuildDataStructure() + { + std::vector<_T> lst; + list(lst); + clear(); + add(lst); + } + /// \brief Remove data from the tree. + /// The element won't actually be removed immediately, but just marked + /// for removal in the removed_ cache. When the cache is full, the tree + /// will be rebuilt and the elements marked for removal will actually + /// be removed. + virtual bool remove(const _T &data) + { + if (!size_) return false; + NearQueue nbhQueue; + // find data in tree + bool isPivot = nearestKInternal(data, 1, nbhQueue); + if (*nbhQueue.top().first != data) + return false; + removed_.insert(nbhQueue.top().first); + size_--; + // if we removed a pivot or if the capacity of removed elements + // has been reached, we rebuild the entire GNAT + if (isPivot || removed_.size()>=removedCacheSize_) + rebuildDataStructure(); + return true; + } + + virtual _T nearest(const _T &data) const + { + if (size_) + { + std::vector<_T> nbh; + nearestK(data, 1, nbh); + if (!nbh.empty()) return nbh[0]; + } + throw Exception("No elements found in nearest neighbors data structure"); + } + + virtual void nearestK(const _T &data, std::size_t k, std::vector<_T> &nbh) const + { + nbh.clear(); + if (k == 0) return; + if (size_) + { + NearQueue nbhQueue; + nearestKInternal(data, k, nbhQueue); + postprocessNearest(nbhQueue, nbh); + } + } + + virtual void nearestR(const _T &data, double radius, std::vector<_T> &nbh) const + { + nbh.clear(); + if (size_) + { + NearQueue nbhQueue; + nearestRInternal(data, radius, nbhQueue); + postprocessNearest(nbhQueue, nbh); + } + } + + virtual std::size_t size(void) const + { + return size_; + } + + virtual void list(std::vector<_T> &data) const + { + data.clear(); + data.reserve(size()); + if (tree_) + tree_->list(*this, data); + } + + /// \brief Print a GNAT structure (mostly useful for debugging purposes). + friend std::ostream& operator<<(std::ostream& out, const NearestNeighborsGNAT<_T>& gnat) + { + if (gnat.tree_) + { + out << *gnat.tree_; + if (!gnat.removed_.empty()) + { + out << "Elements marked for removal:\n"; + for (typename boost::unordered_set<const _T*>::const_iterator it = gnat.removed_.begin(); + it != gnat.removed_.end(); it++) + out << **it << '\t'; + out << std::endl; + } + } + return out; + } + + // for debugging purposes + void integrityCheck() + { + std::vector<_T> lst; + boost::unordered_set<const _T*> tmp; + // get all elements, including those marked for removal + removed_.swap(tmp); + list(lst); + // check if every element marked for removal is also in the tree + for (typename boost::unordered_set<const _T*>::iterator it=tmp.begin(); it!=tmp.end(); it++) + { + unsigned int i; + for (i=0; i<lst.size(); ++i) + if (lst[i]==**it) + break; + if (i == lst.size()) + { + // an element marked for removal is not actually in the tree + std::cout << "***** FAIL!! ******\n" << *this << '\n'; + for (unsigned int j=0; j<lst.size(); ++j) std::cout<<lst[j]<<'\t'; + std::cout<<std::endl; + } + assert(i != lst.size()); + } + // restore + removed_.swap(tmp); + // get elements in the tree with elements marked for removal purged from the list + list(lst); + if (lst.size() != size_) + std::cout << "#########################################\n" << *this << std::endl; + assert(lst.size() == size_); + } +protected: + typedef NearestNeighborsGNAT<_T> GNAT; + + /// Return true iff data has been marked for removal. + bool isRemoved(const _T& data) const + { + return !removed_.empty() && removed_.find(&data) != removed_.end(); + } + + /// \brief Return in nbhQueue the k nearest neighbors of data. + /// For k=1, return true if the nearest neighbor is a pivot. + /// (which is important during removal; removing pivots is a + /// special case). + bool nearestKInternal(const _T &data, std::size_t k, NearQueue& nbhQueue) const + { + bool isPivot; + double dist; + NodeDist nodeDist; + NodeQueue nodeQueue; + + isPivot = tree_->insertNeighborK(nbhQueue, k, tree_->pivot_, data, + NearestNeighbors<_T>::distFun_(data, tree_->pivot_)); + tree_->nearestK(*this, data, k, nbhQueue, nodeQueue, isPivot); + while (nodeQueue.size() > 0) + { + dist = nbhQueue.top().second; // note the difference with nearestRInternal + nodeDist = nodeQueue.top(); + nodeQueue.pop(); + if (nbhQueue.size() == k && + (nodeDist.second > nodeDist.first->maxRadius_ + dist || + nodeDist.second < nodeDist.first->minRadius_ - dist)) + break; + nodeDist.first->nearestK(*this, data, k, nbhQueue, nodeQueue, isPivot); + } + return isPivot; + } + /// \brief Return in nbhQueue the elements that are within distance radius of data. + void nearestRInternal(const _T &data, double radius, NearQueue& nbhQueue) const + { + double dist = radius; // note the difference with nearestKInternal + NodeQueue nodeQueue; + NodeDist nodeDist; + + tree_->insertNeighborR(nbhQueue, radius, tree_->pivot_, + NearestNeighbors<_T>::distFun_(data, tree_->pivot_)); + tree_->nearestR(*this, data, radius, nbhQueue, nodeQueue); + while (nodeQueue.size() > 0) + { + nodeDist = nodeQueue.top(); + nodeQueue.pop(); + if (nodeDist.second > nodeDist.first->maxRadius_ + dist || + nodeDist.second < nodeDist.first->minRadius_ - dist) + break; + nodeDist.first->nearestR(*this, data, radius, nbhQueue, nodeQueue); + } + } + /// \brief Convert the internal data structure used for storing neighbors + /// to the vector that NearestNeighbor API requires. + void postprocessNearest(NearQueue& nbhQueue, std::vector<_T> &nbh) const + { + typename std::vector<_T>::reverse_iterator it; + nbh.resize(nbhQueue.size()); + for (it=nbh.rbegin(); it!=nbh.rend(); it++, nbhQueue.pop()) + *it = *nbhQueue.top().first; + } + + /// The class used internally to define the GNAT. + class Node + { + public: + /// \brief Construct a node of given degree with at most + /// \e capacity data elements and wit given pivot. + Node(int degree, int capacity, const _T& pivot) + : degree_(degree), pivot_(pivot), + minRadius_(std::numeric_limits<double>::infinity()), + maxRadius_(-minRadius_), minRange_(degree, minRadius_), + maxRange_(degree, maxRadius_) + { + // The "+1" is needed because we add an element before we check whether to split + data_.reserve(capacity+1); + } + + ~Node() + { + for (unsigned int i=0; i<children_.size(); ++i) + delete children_[i]; + } + + /// \brief Update minRadius_ and maxRadius_, given that an element + /// was added with distance dist to the pivot. + void updateRadius(double dist) + { + if (minRadius_ > dist) + minRadius_ = dist; + if (maxRadius_ < dist) + maxRadius_ = dist; + } + /// \brief Update minRange_[i] and maxRange_[i], given that an + /// element was added to the i-th child of the parent that has + /// distance dist to this Node's pivot. + void updateRange(unsigned int i, double dist) + { + if (minRange_[i] > dist) + minRange_[i] = dist; + if (maxRange_[i] < dist) + maxRange_[i] = dist; + } + /// Add an element to the tree rooted at this node. + void add(GNAT& gnat, const _T& data) + { + if (children_.size()==0) + { + data_.push_back(data); + gnat.size_++; + if (needToSplit(gnat)) + { + if (gnat.removed_.size() > 0) + gnat.rebuildDataStructure(); + else if (gnat.size_ >= gnat.rebuildSize_) + { + gnat.rebuildSize_ <<= 1; + gnat.rebuildDataStructure(); + } + else + split(gnat); + } + } + else + { + std::vector<double> dist(children_.size()); + double minDist = dist[0] = gnat.distFun_(data, children_[0]->pivot_); + int minInd = 0; + + for (unsigned int i=1; i<children_.size(); ++i) + if ((dist[i] = gnat.distFun_(data, children_[i]->pivot_)) < minDist) + { + minDist = dist[i]; + minInd = i; + } + for (unsigned int i=0; i<children_.size(); ++i) + children_[i]->updateRange(minInd, dist[i]); + children_[minInd]->updateRadius(minDist); + children_[minInd]->add(gnat, data); + } + } + /// Return true iff the node needs to be split into child nodes. + bool needToSplit(const GNAT& gnat) const + { + unsigned int sz = data_.size(); + return sz > gnat.maxNumPtsPerLeaf_ && sz > degree_; + } + /// \brief The split operation finds pivot elements for the child + /// nodes and moves each data element of this node to the appropriate + /// child node. + void split(GNAT& gnat) + { + std::vector<std::vector<double> > dists; + std::vector<unsigned int> pivots; + + children_.reserve(degree_); + gnat.pivotSelector_.kcenters(data_, degree_, pivots, dists); + for(unsigned int i=0; i<pivots.size(); i++) + children_.push_back(new Node(degree_, gnat.maxNumPtsPerLeaf_, data_[pivots[i]])); + degree_ = pivots.size(); // in case fewer than degree_ pivots were found + for (unsigned int j=0; j<data_.size(); ++j) + { + unsigned int k = 0; + for (unsigned int i=1; i<degree_; ++i) + if (dists[j][i] < dists[j][k]) + k = i; + Node* child = children_[k]; + if (j != pivots[k]) + { + child->data_.push_back(data_[j]); + child->updateRadius(dists[j][k]); + } + for (unsigned int i=0; i<degree_; ++i) + children_[i]->updateRange(k, dists[j][i]); + } + + for (unsigned int i=0; i<degree_; ++i) + { + // make sure degree lies between minDegree_ and maxDegree_ + children_[i]->degree_ = std::min(std::max( + degree_ * (unsigned int)(children_[i]->data_.size() / data_.size()), + gnat.minDegree_), gnat.maxDegree_); + // singleton + if (children_[i]->minRadius_ >= std::numeric_limits<double>::infinity()) + children_[i]->minRadius_ = children_[i]->maxRadius_ = 0.; + } + // this does more than clear(); it also sets capacity to 0 and frees the memory + std::vector<_T> tmp; + data_.swap(tmp); + // check if new leaves need to be split + for (unsigned int i=0; i<degree_; ++i) + if (children_[i]->needToSplit(gnat)) + children_[i]->split(gnat); + } + + /// Insert data in nbh if it is a near neighbor. Return true iff data was added to nbh. + bool insertNeighborK(NearQueue& nbh, std::size_t k, const _T& data, const _T& key, double dist) const + { + if (nbh.size() < k) + { + nbh.push(std::make_pair(&data, dist)); + return true; + } + else if (dist < nbh.top().second || + (dist < std::numeric_limits<double>::epsilon() && data==key)) + { + nbh.pop(); + nbh.push(std::make_pair(&data, dist)); + return true; + } + return false; + } + + /// \brief Compute the k nearest neighbors of data in the tree. + /// For k=1, isPivot is true if the nearest neighbor is a pivot + /// (which is important during removal; removing pivots is a + /// special case). The nodeQueue, which contains other Nodes + /// that need to be checked for nearest neighbors, is updated. + void nearestK(const GNAT& gnat, const _T &data, std::size_t k, + NearQueue& nbh, NodeQueue& nodeQueue, bool& isPivot) const + { + for (unsigned int i=0; i<data_.size(); ++i) + if (!gnat.isRemoved(data_[i])) + { + if (insertNeighborK(nbh, k, data_[i], data, gnat.distFun_(data, data_[i]))) + isPivot = false; + } + if (children_.size() > 0) + { + double dist; + Node* child; + std::vector<double> distToPivot(children_.size()); + std::vector<int> permutation(children_.size()); + + for (unsigned int i=0; i<permutation.size(); ++i) + permutation[i] = i; + std::random_shuffle(permutation.begin(), permutation.end()); + + for (unsigned int i=0; i<children_.size(); ++i) + if (permutation[i] >= 0) + { + child = children_[permutation[i]]; + distToPivot[permutation[i]] = gnat.distFun_(data, child->pivot_); + if (insertNeighborK(nbh, k, child->pivot_, data, distToPivot[permutation[i]])) + isPivot = true; + if (nbh.size()==k) + { + dist = nbh.top().second; // note difference with nearestR + for (unsigned int j=0; j<children_.size(); ++j) + if (permutation[j] >=0 && i != j && + (distToPivot[permutation[i]] - dist > child->maxRange_[permutation[j]] || + distToPivot[permutation[i]] + dist < child->minRange_[permutation[j]])) + permutation[j] = -1; + } + } + + dist = nbh.top().second; + for (unsigned int i=0; i<children_.size(); ++i) + if (permutation[i] >= 0) + { + child = children_[permutation[i]]; + if (nbh.size()<k || + (distToPivot[permutation[i]] - dist <= child->maxRadius_ && + distToPivot[permutation[i]] + dist >= child->minRadius_)) + nodeQueue.push(std::make_pair(child, distToPivot[permutation[i]])); + } + } + } + /// Insert data in nbh if it is a near neighbor. + void insertNeighborR(NearQueue& nbh, double r, const _T& data, double dist) const + { + if (dist <= r) + nbh.push(std::make_pair(&data, dist)); + } + /// \brief Return all elements that are within distance r in nbh. + /// The nodeQueue, which contains other Nodes that need to + /// be checked for nearest neighbors, is updated. + void nearestR(const GNAT& gnat, const _T &data, double r, NearQueue& nbh, NodeQueue& nodeQueue) const + { + double dist = r; //note difference with nearestK + + for (unsigned int i=0; i<data_.size(); ++i) + if (!gnat.isRemoved(data_[i])) + insertNeighborR(nbh, r, data_[i], gnat.distFun_(data, data_[i])); + if (children_.size() > 0) + { + Node* child; + std::vector<double> distToPivot(children_.size()); + std::vector<int> permutation(children_.size()); + + for (unsigned int i=0; i<permutation.size(); ++i) + permutation[i] = i; + std::random_shuffle(permutation.begin(), permutation.end()); + + for (unsigned int i=0; i<children_.size(); ++i) + if (permutation[i] >= 0) + { + child = children_[permutation[i]]; + distToPivot[i] = gnat.distFun_(data, child->pivot_); + insertNeighborR(nbh, r, child->pivot_, distToPivot[i]); + for (unsigned int j=0; j<children_.size(); ++j) + if (permutation[j] >=0 && i != j && + (distToPivot[i] - dist > child->maxRange_[permutation[j]] || + distToPivot[i] + dist < child->minRange_[permutation[j]])) + permutation[j] = -1; + } + + for (unsigned int i=0; i<children_.size(); ++i) + if (permutation[i] >= 0) + { + child = children_[permutation[i]]; + if (distToPivot[i] - dist <= child->maxRadius_ && + distToPivot[i] + dist >= child->minRadius_) + nodeQueue.push(std::make_pair(child, distToPivot[i])); + } + } + } + + void list(const GNAT& gnat, std::vector<_T> &data) const + { + if (!gnat.isRemoved(pivot_)) + data.push_back(pivot_); + for (unsigned int i=0; i<data_.size(); ++i) + if(!gnat.isRemoved(data_[i])) + data.push_back(data_[i]); + for (unsigned int i=0; i<children_.size(); ++i) + children_[i]->list(gnat, data); + } + + friend std::ostream& operator<<(std::ostream& out, const Node& node) + { + out << "\ndegree:\t" << node.degree_; + out << "\nminRadius:\t" << node.minRadius_; + out << "\nmaxRadius:\t" << node.maxRadius_; + out << "\nminRange:\t"; + for (unsigned int i=0; i<node.minRange_.size(); ++i) + out << node.minRange_[i] << '\t'; + out << "\nmaxRange: "; + for (unsigned int i=0; i<node.maxRange_.size(); ++i) + out << node.maxRange_[i] << '\t'; + out << "\npivot:\t" << node.pivot_; + out << "\ndata: "; + for (unsigned int i=0; i<node.data_.size(); ++i) + out << node.data_[i] << '\t'; + out << "\nthis:\t" << &node; + out << "\nchildren:\n"; + for (unsigned int i=0; i<node.children_.size(); ++i) + out << node.children_[i] << '\t'; + out << '\n'; + for (unsigned int i=0; i<node.children_.size(); ++i) + out << *node.children_[i] << '\n'; + return out; + } + + /// Number of child nodes + unsigned int degree_; + /// Data element stored in this Node + const _T pivot_; + /// Minimum distance between the pivot element and the elements stored in data_ + double minRadius_; + /// Maximum distance between the pivot element and the elements stored in data_ + double maxRadius_; + /// \brief The i-th element in minRange_ is the minimum distance between the + /// pivot and any data_ element in the i-th child node of this node's parent. + std::vector<double> minRange_; + /// \brief The i-th element in maxRange_ is the maximum distance between the + /// pivot and any data_ element in the i-th child node of this node's parent. + std::vector<double> maxRange_; + /// \brief The data elements stored in this node (in addition to the pivot + /// element). An internal node has no elements stored in data_. + std::vector<_T> data_; + /// \brief The child nodes of this node. By definition, only internal nodes + /// have child nodes. + std::vector<Node*> children_; + }; + + /// \brief The data structure containing the elements stored in this structure. + Node* tree_; + /// The desired degree of each node. + unsigned int degree_; + /// \brief After splitting a Node, each child Node has degree equal to + /// the default degree times the fraction of data elements from the + /// original node that got assigned to that child Node. However, its + /// degree can be no less than minDegree_. + unsigned int minDegree_; + /// \brief After splitting a Node, each child Node has degree equal to + /// the default degree times the fraction of data elements from the + /// original node that got assigned to that child Node. However, its + /// degree can be no larger than maxDegree_. + unsigned int maxDegree_; + /// \brief Maximum number of elements allowed to be stored in a Node before + /// it needs to be split into several nodes. + unsigned int maxNumPtsPerLeaf_; + /// \brief Number of elements stored in the tree. + std::size_t size_; + /// \brief If size_ exceeds rebuildSize_, the tree will be rebuilt (and + /// automatically rebalanced), and rebuildSize_ will be doubled. + std::size_t rebuildSize_; + /// \brief Maximum number of removed elements that can be stored in the + /// removed_ cache. If the cache is full, the tree will be rebuilt with + /// the elements in removed_ actually removed from the tree. + std::size_t removedCacheSize_; + /// \brief The data structure used to split data into subtrees. + GreedyKCenters<_T> pivotSelector_; + /// \brief Cache of removed elements. + boost::unordered_set<const _T*> removed_; +}; +} + +#endif diff --git a/include/fcl/knn/nearest_neighbors_flann.h b/include/fcl/knn/nearest_neighbors_flann.h new file mode 100644 index 0000000000000000000000000000000000000000..76380f3c39f4dae4277c29bf9d0d86b1d2186d01 --- /dev/null +++ b/include/fcl/knn/nearest_neighbors_flann.h @@ -0,0 +1,350 @@ +/********************************************************************* + * Software License Agreement (BSD License) + * + * Copyright (c) 2011, Rice University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of the Rice University nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *********************************************************************/ + +/* Author: Mark Moll */ + +#ifndef FCL_KNN_NEAREST_NEIGHBORS_FLANN_H +#define FCL_KNN_NEAREST_NEIGHBORS_FLANN_H + +#include "fcl/config.h" +#if FCL_HAVE_FLANN == 0 +# error FLANN is not available. Please use a different NearestNeighbors data structure +#else + +#include "fcl/knn/nearest_neighbors.h" +#include <flann/flann.hpp> +#include "fcl/exception.h" + +namespace fcl +{ +/** \brief Wrapper class to allow FLANN access to the + NearestNeighbors::distFun_ callback function +*/ +template<typename _T> +class FLANNDistance +{ +public: + typedef _T ElementType; + typedef double ResultType; + + FLANNDistance(const typename NearestNeighbors<_T>::DistanceFunction& distFun) + : distFun_(distFun) + { + } + + template <typename Iterator1, typename Iterator2> + ResultType operator()(Iterator1 a, Iterator2 b, + size_t /*size*/, ResultType /*worst_dist*/ = -1) const + { + return distFun_(*a, *b); + } +protected: + const typename NearestNeighbors<_T>::DistanceFunction& distFun_; +}; + +/** \brief Wrapper class for nearest neighbor data structures in the + FLANN library. + + See: + M. Muja and D.G. Lowe, "Fast Approximate Nearest Neighbors with + Automatic Algorithm Configuration", in International Conference + on Computer Vision Theory and Applications (VISAPP'09), 2009. + http://people.cs.ubc.ca/~mariusm/index.php/FLANN/FLANN +*/ +template<typename _T, typename _Dist = FLANNDistance<_T> > +class NearestNeighborsFLANN : public NearestNeighbors<_T> +{ +public: + + NearestNeighborsFLANN(const boost::shared_ptr<flann::IndexParams> ¶ms) + : index_(0), params_(params), searchParams_(32, 0., true), dimension_(1) + { + } + + virtual ~NearestNeighborsFLANN(void) + { + if (index_) + delete index_; + } + + virtual void clear(void) + { + if (index_) + { + delete index_; + index_ = NULL; + } + data_.clear(); + } + + virtual void setDistanceFunction(const typename NearestNeighbors<_T>::DistanceFunction &distFun) + { + NearestNeighbors<_T>::setDistanceFunction(distFun); + rebuildIndex(); + } + + virtual void add(const _T &data) + { + bool rebuild = index_ && (data_.size() + 1 > data_.capacity()); + + if (rebuild) + rebuildIndex(2 * data_.capacity()); + + data_.push_back(data); + const flann::Matrix<_T> mat(&data_.back(), 1, dimension_); + + if (index_) + index_->addPoints(mat, std::numeric_limits<float>::max()/size()); + else + createIndex(mat); + } + virtual void add(const std::vector<_T> &data) + { + unsigned int oldSize = data_.size(); + unsigned int newSize = oldSize + data.size(); + bool rebuild = index_ && (newSize > data_.capacity()); + + if (rebuild) + rebuildIndex(std::max(2 * oldSize, newSize)); + + if (index_) + { + std::copy(data.begin(), data.end(), data_.begin() + oldSize); + const flann::Matrix<_T> mat(&data_[oldSize], data.size(), dimension_); + index_->addPoints(mat, std::numeric_limits<float>::max()/size()); + } + else + { + data_ = data; + const flann::Matrix<_T> mat(&data_[0], data_.size(), dimension_); + createIndex(mat); + } + } + virtual bool remove(const _T& data) + { + if (!index_) return false; + _T& elt = const_cast<_T&>(data); + const flann::Matrix<_T> query(&elt, 1, dimension_); + std::vector<std::vector<size_t> > indices(1); + std::vector<std::vector<double> > dists(1); + index_->knnSearch(query, indices, dists, 1, searchParams_); + if (*index_->getPoint(indices[0][0]) == data) + { + index_->removePoint(indices[0][0]); + rebuildIndex(); + return true; + } + return false; + } + virtual _T nearest(const _T &data) const + { + if (size()) + { + _T& elt = const_cast<_T&>(data); + const flann::Matrix<_T> query(&elt, 1, dimension_); + std::vector<std::vector<size_t> > indices(1); + std::vector<std::vector<double> > dists(1); + index_->knnSearch(query, indices, dists, 1, searchParams_); + return *index_->getPoint(indices[0][0]); + } + throw Exception("No elements found in nearest neighbors data structure"); + } + virtual void nearestK(const _T &data, std::size_t k, std::vector<_T> &nbh) const + { + _T& elt = const_cast<_T&>(data); + const flann::Matrix<_T> query(&elt, 1, dimension_); + std::vector<std::vector<size_t> > indices; + std::vector<std::vector<double> > dists; + k = index_ ? index_->knnSearch(query, indices, dists, k, searchParams_) : 0; + nbh.resize(k); + for (std::size_t i = 0 ; i < k ; ++i) + nbh[i] = *index_->getPoint(indices[0][i]); + } + + virtual void nearestR(const _T &data, double radius, std::vector<_T> &nbh) const + { + _T& elt = const_cast<_T&>(data); + flann::Matrix<_T> query(&elt, 1, dimension_); + std::vector<std::vector<size_t> > indices; + std::vector<std::vector<double> > dists; + int k = index_ ? index_->radiusSearch(query, indices, dists, radius, searchParams_) : 0; + nbh.resize(k); + for (int i = 0 ; i < k ; ++i) + nbh[i] = *index_->getPoint(indices[0][i]); + } + + virtual std::size_t size(void) const + { + return index_ ? index_->size() : 0; + } + + virtual void list(std::vector<_T> &data) const + { + std::size_t sz = size(); + if (sz == 0) + { + data.resize(0); + return; + } + const _T& dummy = *index_->getPoint(0); + int checks = searchParams_.checks; + searchParams_.checks = size(); + nearestK(dummy, sz, data); + searchParams_.checks = checks; + } + + /// \brief Set the FLANN index parameters. + /// + /// The parameters determine the type of nearest neighbor + /// data structure to be constructed. + virtual void setIndexParams(const boost::shared_ptr<flann::IndexParams> ¶ms) + { + params_ = params; + rebuildIndex(); + } + + /// \brief Get the FLANN parameters used to build the current index. + virtual const boost::shared_ptr<flann::IndexParams>& getIndexParams(void) const + { + return params_; + } + + /// \brief Set the FLANN parameters to be used during nearest neighbor + /// searches + virtual void setSearchParams(const flann::SearchParams& searchParams) + { + searchParams_ = searchParams; + } + + /// \brief Get the FLANN parameters used during nearest neighbor + /// searches + flann::SearchParams& getSearchParams(void) + { + return searchParams_; + } + + /// \brief Get the FLANN parameters used during nearest neighbor + /// searches + const flann::SearchParams& getSearchParams(void) const + { + return searchParams_; + } + + unsigned int getContainerSize(void) const + { + return dimension_; + } + +protected: + + /// \brief Internal function to construct nearest neighbor + /// data structure with initial elements stored in mat. + void createIndex(const flann::Matrix<_T>& mat) + { + index_ = new flann::Index<_Dist>(mat, *params_, _Dist(NearestNeighbors<_T>::distFun_)); + index_->buildIndex(); + } + + /// \brief Rebuild the nearest neighbor data structure (necessary when + /// changing the distance function or index parameters). + void rebuildIndex(unsigned int capacity = 0) + { + if (index_) + { + std::vector<_T> data; + list(data); + clear(); + if (capacity) + data_.reserve(capacity); + add(data); + } + } + + /// \brief vector of data stored in FLANN's index. FLANN only indexes + /// references, so we need store the original data. + std::vector<_T> data_; + + /// \brief The FLANN index (the actual index type depends on params_). + flann::Index<_Dist>* index_; + + /// \brief The FLANN index parameters. This contains both the type of + /// index and the parameters for that type. + boost::shared_ptr<flann::IndexParams> params_; + + /// \brief The parameters used to seach for nearest neighbors. + mutable flann::SearchParams searchParams_; + + /// \brief If each element has an array-like structure that is exposed + /// to FLANN, then the dimension_ needs to be set to the length of + /// this array. + unsigned int dimension_; +}; + +template<> +void NearestNeighborsFLANN<double, flann::L2<double> >::createIndex( + const flann::Matrix<double>& mat) +{ + index_ = new flann::Index<flann::L2<double> >(mat, *params_); + index_->buildIndex(); +} + +template<typename _T, typename _Dist = FLANNDistance<_T> > +class NearestNeighborsFLANNLinear : public NearestNeighborsFLANN<_T, _Dist> +{ +public: + NearestNeighborsFLANNLinear() + : NearestNeighborsFLANN<_T, _Dist>( + boost::shared_ptr<flann::LinearIndexParams>( + new flann::LinearIndexParams())) + { + } +}; + +template<typename _T, typename _Dist = FLANNDistance<_T> > +class NearestNeighborsFLANNHierarchicalClustering : public NearestNeighborsFLANN<_T, _Dist> +{ +public: + NearestNeighborsFLANNHierarchicalClustering() + : NearestNeighborsFLANN<_T, _Dist>( + boost::shared_ptr<flann::HierarchicalClusteringIndexParams>( + new flann::HierarchicalClusteringIndexParams())) + { + } +}; + +} + +#endif + +#endif diff --git a/include/fcl/knn/nearest_neighbors_linear.h b/include/fcl/knn/nearest_neighbors_linear.h new file mode 100644 index 0000000000000000000000000000000000000000..3275fa7f795cf964dcd48876a4542fd2fa778afa --- /dev/null +++ b/include/fcl/knn/nearest_neighbors_linear.h @@ -0,0 +1,175 @@ +/********************************************************************* + * Software License Agreement (BSD License) + * + * Copyright (c) 2008, Willow Garage, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of the Willow Garage nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *********************************************************************/ + +/* Author: Ioan Sucan */ + +#ifndef FCL_KNN_NEAREST_NEIGHBORS_LINEAR_H +#define FCL_KNN_NEAREST_NEIGHBORS_LINEAR_H + +#include "fcl/knn/nearest_neighbors.h" +#include "fcl/exception.h" +#include <algorithm> + +namespace fcl +{ +/** \brief A nearest neighbors datastructure that uses linear + search. + + \li Search for nearest neighbor is O(n). + \li Search for k-nearest neighbors is O(n log(k)). + \li Search for neighbors within a range is O(n log(n)). + \li Adding an element to the datastructure is O(1). + \li Removing an element from the datastructure O(n). +*/ +template<typename _T> +class NearestNeighborsLinear : public NearestNeighbors<_T> +{ +public: + NearestNeighborsLinear(void) : NearestNeighbors<_T>() + { + } + + virtual ~NearestNeighborsLinear(void) + { + } + + virtual void clear(void) + { + data_.clear(); + } + + virtual void add(const _T &data) + { + data_.push_back(data); + } + + virtual void add(const std::vector<_T> &data) + { + data_.reserve(data_.size() + data.size()); + data_.insert(data_.end(), data.begin(), data.end()); + } + + virtual bool remove(const _T &data) + { + if (!data_.empty()) + for (int i = data_.size() - 1 ; i >= 0 ; --i) + if (data_[i] == data) + { + data_.erase(data_.begin() + i); + return true; + } + return false; + } + + virtual _T nearest(const _T &data) const + { + const std::size_t sz = data_.size(); + std::size_t pos = sz; + double dmin = 0.0; + for (std::size_t i = 0 ; i < sz ; ++i) + { + double distance = NearestNeighbors<_T>::distFun_(data_[i], data); + if (pos == sz || dmin > distance) + { + pos = i; + dmin = distance; + } + } + if (pos != sz) + return data_[pos]; + + throw Exception("No elements found in nearest neighbors data structure"); + } + + virtual void nearestK(const _T &data, std::size_t k, std::vector<_T> &nbh) const + { + nbh = data_; + if (nbh.size() > k) + { + std::partial_sort(nbh.begin(), nbh.begin() + k, nbh.end(), + ElemSort(data, NearestNeighbors<_T>::distFun_)); + nbh.resize(k); + } + else + { + std::sort(nbh.begin(), nbh.end(), ElemSort(data, NearestNeighbors<_T>::distFun_)); + } + } + + virtual void nearestR(const _T &data, double radius, std::vector<_T> &nbh) const + { + nbh.clear(); + for (std::size_t i = 0 ; i < data_.size() ; ++i) + if (NearestNeighbors<_T>::distFun_(data_[i], data) <= radius) + nbh.push_back(data_[i]); + std::sort(nbh.begin(), nbh.end(), ElemSort(data, NearestNeighbors<_T>::distFun_)); + } + + virtual std::size_t size(void) const + { + return data_.size(); + } + + virtual void list(std::vector<_T> &data) const + { + data = data_; + } + +protected: + + /** \brief The data elements stored in this structure */ + std::vector<_T> data_; + +private: + + struct ElemSort + { + ElemSort(const _T &e, const typename NearestNeighbors<_T>::DistanceFunction &df) : e_(e), df_(df) + { + } + + bool operator()(const _T &a, const _T &b) const + { + return df_(a, e_) < df_(b, e_); + } + + const _T &e_; + const typename NearestNeighbors<_T>::DistanceFunction &df_; + }; + +}; + +} + +#endif diff --git a/include/fcl/knn/nearest_neighbors_sqrtapprox.h b/include/fcl/knn/nearest_neighbors_sqrtapprox.h new file mode 100644 index 0000000000000000000000000000000000000000..5b6c46c119631fe045caf1d1c9a0feb3dc3eeee6 --- /dev/null +++ b/include/fcl/knn/nearest_neighbors_sqrtapprox.h @@ -0,0 +1,141 @@ +/********************************************************************* + * Software License Agreement (BSD License) + * + * Copyright (c) 2008, Willow Garage, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of the Willow Garage nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *********************************************************************/ + +/* Author: Ioan Sucan */ + +#ifndef FCL_KNN_NEAREST_NEIGHBORS_SQRT_APPROX_H +#define FCL_KNN_NEAREST_NEIGHBORS_SQRT_APPROX_H + +#include "fcl/knn/nearest_neighbors_linear.h" +#include "fcl/exception.h" +#include <algorithm> +#include <cmath> + +namespace fcl +{ +/** \brief A nearest neighbors datastructure that uses linear + search. The linear search is done over sqrt(n) elements + only. (Every sqrt(n) elements are skipped). + + \li Search for nearest neighbor is O(sqrt(n)). + \li Search for k-nearest neighbors is O(n log(k)). + \li Search for neighbors within a range is O(n log(n)). + \li Adding an element to the datastructure is O(1). + \li Removing an element from the datastructure O(n). +*/ +template<typename _T> +class NearestNeighborsSqrtApprox : public NearestNeighborsLinear<_T> +{ +public: + NearestNeighborsSqrtApprox(void) : NearestNeighborsLinear<_T>(), checks_(0), offset_(0) + { + } + + virtual ~NearestNeighborsSqrtApprox(void) + { + } + + virtual void clear(void) + { + NearestNeighborsLinear<_T>::clear(); + checks_ = 0; + offset_ = 0; + } + + virtual void add(const _T &data) + { + NearestNeighborsLinear<_T>::add(data); + updateCheckCount(); + } + + virtual void add(const std::vector<_T> &data) + { + NearestNeighborsLinear<_T>::add(data); + updateCheckCount(); + } + + virtual bool remove(const _T &data) + { + bool result = NearestNeighborsLinear<_T>::remove(data); + if (result) + updateCheckCount(); + return result; + } + + virtual _T nearest(const _T &data) const + { + const std::size_t n = NearestNeighborsLinear<_T>::data_.size(); + std::size_t pos = n; + + if (checks_ > 0 && n > 0) + { + double dmin = 0.0; + for (std::size_t j = 0 ; j < checks_ ; ++j) + { + std::size_t i = (j * checks_ + offset_) % n; + + double distance = NearestNeighbors<_T>::distFun_(NearestNeighborsLinear<_T>::data_[i], data); + if (pos == n || dmin > distance) + { + pos = i; + dmin = distance; + } + } + offset_ = (offset_ + 1) % checks_; + } + if (pos != n) + return NearestNeighborsLinear<_T>::data_[pos]; + + throw Exception("No elements found in nearest neighbors data structure"); + } + +protected: + + /** \brief The maximum number of checks to perform when searching for a nearest neighbor */ + inline void updateCheckCount(void) + { + checks_ = 1 + (std::size_t)floor(sqrt((double)NearestNeighborsLinear<_T>::data_.size())); + } + + /** \brief The number of checks to be performed when looking for a nearest neighbor */ + std::size_t checks_; + + /** \brief The offset to start checking at (between 0 and \e checks_) */ + mutable std::size_t offset_; + +}; + +} + +#endif diff --git a/include/fcl/learning/classifier.h b/include/fcl/learning/classifier.h new file mode 100644 index 0000000000000000000000000000000000000000..3c88ffa7298b3d05fa5eab6c36f780308745a3b7 --- /dev/null +++ b/include/fcl/learning/classifier.h @@ -0,0 +1,146 @@ +#ifndef FCL_LEARNING_CLASSIFIER_H +#define FCL_LEARNING_CLASSIFIER_H + +#include "fcl/math/vec_nf.h" + +namespace fcl +{ +template<std::size_t N> +struct Item +{ + Vecnf<N> q; + bool label; + FCL_REAL w; + + Item(const Vecnf<N>& q_, bool label_, FCL_REAL w_ = 1) : q(q_), + label(label_), + w(w_) + {} + + Item() {} +}; + +template<std::size_t N> +struct Scaler +{ + Vecnf<N> v_min, v_max; + Scaler() + { + // default no scale + for(std::size_t i = 0; i < N; ++i) + { + v_min[i] = 0; + v_max[i] = 1; + } + } + + Scaler(const Vecnf<N>& v_min_, const Vecnf<N>& v_max_) : v_min(v_min_), + v_max(v_max_) + {} + + Vecnf<N> scale(const Vecnf<N>& v) const + { + Vecnf<N> res; + for(std::size_t i = 0; i < N; ++i) + res[i] = (v[i] - v_min[i]) / (v_max[i] - v_min[i]); + return res; + } + + Vecnf<N> unscale(const Vecnf<N>& v) const + { + Vecnf<N> res; + for(std::size_t i = 0; i < N; ++i) + res[i] = v[i] * (v_max[i] - v_min[i]) + v_min[i]; + return res; + } +}; + + +struct PredictResult +{ + bool label; + FCL_REAL prob; + + PredictResult() {} + PredictResult(bool label_, FCL_REAL prob_ = 1) : label(label_), + prob(prob_) + {} +}; + +template<std::size_t N> +class SVMClassifier +{ +public: + + virtual PredictResult predict(const Vecnf<N>& q) const = 0; + virtual std::vector<PredictResult> predict(const std::vector<Vecnf<N> >& qs) const = 0; + + virtual std::vector<Item<N> > getSupportVectors() const = 0; + virtual void setScaler(const Scaler<N>& scaler) = 0; + + virtual void learn(const std::vector<Item<N> >& data) = 0; + + FCL_REAL error_rate(const std::vector<Item<N> >& data) const + { + std::size_t num = data.size(); + + std::size_t error_num = 0; + for(std::size_t i = 0; i < data.size(); ++i) + { + PredictResult res = predict(data[i].q); + if(res.label != data[i].label) + error_num++; + } + + return error_num / (FCL_REAL)num; + } +}; + +template<std::size_t N> +Scaler<N> computeScaler(const std::vector<Item<N> >& data) +{ + Vecnf<N> lower_bound, upper_bound; + for(std::size_t j = 0; j < N; ++j) + { + lower_bound[j] = std::numeric_limits<FCL_REAL>::max(); + upper_bound[j] = -std::numeric_limits<FCL_REAL>::max(); + } + + for(std::size_t i = 0; i < data.size(); ++i) + { + for(std::size_t j = 0; j < N; ++j) + { + if(data[i].q[j] < lower_bound[j]) lower_bound[j] = data[i].q[j]; + if(data[i].q[j] > upper_bound[j]) upper_bound[j] = data[i].q[j]; + } + } + + return Scaler<N>(lower_bound, upper_bound); +} + +template<std::size_t N> +Scaler<N> computeScaler(const std::vector<Vecnf<N> >& data) +{ + Vecnf<N> lower_bound, upper_bound; + for(std::size_t j = 0; j < N; ++j) + { + lower_bound[j] = std::numeric_limits<FCL_REAL>::max(); + upper_bound[j] = -std::numeric_limits<FCL_REAL>::max(); + } + + for(std::size_t i = 0; i < data.size(); ++i) + { + for(std::size_t j = 0; j < N; ++j) + { + if(data[i][j] < lower_bound[j]) lower_bound[j] = data[i][j]; + if(data[i][j] > upper_bound[j]) upper_bound[j] = data[i][j]; + } + } + + return Scaler<N>(lower_bound, upper_bound); +} + +} + +#endif + diff --git a/include/fcl/math/transform.h b/include/fcl/math/transform.h index e82c1f69d07c3199635be04c51e87a7a1d3e3e33..d66ea8310d918b990cb12b8ad09413c53187c012 100644 --- a/include/fcl/math/transform.h +++ b/include/fcl/math/transform.h @@ -77,6 +77,12 @@ public: /// @brief Quaternion to matrix void toRotation(Matrix3f& R) const; + /// @brief Euler to quaternion + void fromEuler(FCL_REAL a, FCL_REAL b, FCL_REAL c); + + /// @brief Quaternion to Euler + void toEuler(FCL_REAL& a, FCL_REAL& b, FCL_REAL& c) const; + /// @brief Axes to quaternion void fromAxes(const Vec3f axis[3]); @@ -134,6 +140,26 @@ public: Vec3f getRow(std::size_t i) const; + bool operator == (const Quaternion3f& other) const + { + for(std::size_t i = 0; i < 4; ++i) + { + if(data[i] != other[i]) + return false; + } + return true; + } + + bool operator != (const Quaternion3f& other) const + { + return !(*this == other); + } + + FCL_REAL operator [] (std::size_t i) const + { + return data[i]; + } + private: FCL_REAL data[4]; @@ -145,6 +171,7 @@ Quaternion3f conj(const Quaternion3f& q); /// @brief inverse of quaternion Quaternion3f inverse(const Quaternion3f& q); + /// @brief Simple transform class used locally by InterpMotion class Transform3f { @@ -333,6 +360,16 @@ public: matrix_set = true; } + bool operator == (const Transform3f& other) const + { + return (q == other.getQuatRotation()) && (T == other.getTranslation()); + } + + bool operator != (const Transform3f& other) const + { + return !(*this == other); + } + }; /// @brief inverse the transform diff --git a/include/fcl/math/vec_3f.h b/include/fcl/math/vec_3f.h index ba60e0e9b410a908b12c8f01887bda8c09f47529..840ba8de3a915781b78df126104c81ca486e7eb3 100644 --- a/include/fcl/math/vec_3f.h +++ b/include/fcl/math/vec_3f.h @@ -131,6 +131,17 @@ public: inline bool equal(const Vec3fX& other, U epsilon = std::numeric_limits<U>::epsilon() * 100) const { return details::equal(data, other.data, epsilon); } inline Vec3fX<T>& negate() { data.negate(); return *this; } + bool operator == (const Vec3fX& other) const + { + return equal(other, 0); + } + + bool operator != (const Vec3fX& other) const + { + return !(*this == other); + } + + inline Vec3fX<T>& ubound(const Vec3fX<T>& u) { data.ubound(u.data); diff --git a/include/fcl/penetration_depth.h b/include/fcl/penetration_depth.h index 97ccff5cab5042e06978a75f5f6ea0e863cdee6f..d61ed4f472fe6f482807a1fbc75843494e52fcbc 100644 --- a/include/fcl/penetration_depth.h +++ b/include/fcl/penetration_depth.h @@ -4,20 +4,109 @@ #include "fcl/math/vec_3f.h" #include "fcl/collision_object.h" #include "fcl/collision_data.h" +#include "fcl/learning/classifier.h" +#include "fcl/knn/nearest_neighbors.h" +#include <boost/mem_fn.hpp> +#include <iostream> namespace fcl { +typedef double (*TransformDistance)(const Transform3f&, const Transform3f&); + +class DefaultTransformDistancer +{ +public: + const CollisionGeometry* o1; + const CollisionGeometry* o2; + + FCL_REAL rot_x_weight, rot_y_weight, rot_z_weight; + + DefaultTransformDistancer() : o1(NULL), o2(NULL) + { + rot_x_weight = rot_y_weight = rot_z_weight = 1; + } + + DefaultTransformDistancer(const CollisionGeometry* o2_) + { + o2 = o2_; + init(); + } + + DefaultTransformDistancer(const CollisionGeometry* o1_, const CollisionGeometry* o2_) + { + o1 = o1_; + o2 = o2_; + init(); + } + + void init() + { + rot_x_weight = rot_y_weight = rot_z_weight = 1; + + if(o2) + { + FCL_REAL V = o2->computeVolume(); + Matrix3f C = o2->computeMomentofInertiaRelatedToCOM(); + FCL_REAL eigen_values[3]; + Vec3f eigen_vectors[3]; + eigen(C, eigen_values, eigen_vectors); + rot_x_weight = eigen_values[0] * 4 / V; + rot_y_weight = eigen_values[1] * 4 / V; + rot_z_weight = eigen_values[2] * 4 / V; + + // std::cout << rot_x_weight << " " << rot_y_weight << " " << rot_z_weight << std::endl; + } + } + + void printRotWeight() const + { + std::cout << rot_x_weight << " " << rot_y_weight << " " << rot_z_weight << std::endl; + } + + double distance(const Transform3f& tf1, const Transform3f& tf2) + { + Transform3f tf; + relativeTransform(tf1, tf2, tf); + + const Quaternion3f& quat = tf.getQuatRotation(); + const Vec3f& trans = tf.getTranslation(); + + FCL_REAL d = rot_x_weight * quat[1] * quat[1] + rot_y_weight * quat[2] * quat[2] + rot_z_weight * quat[3] * quat[3] + trans[0] * trans[0] + trans[1] * trans[1] + trans[2] * trans[2]; + return d; + } +}; + +static DefaultTransformDistancer default_transform_distancer; + +static NearestNeighbors<Transform3f>::DistanceFunction default_transform_distance_func = boost::bind(&DefaultTransformDistancer::distance, &default_transform_distancer, _1, _2); + + + +/// @brief precompute a learning model for penetration computation +std::vector<Transform3f> penetrationDepthModelLearning(const CollisionObject* o1, + const CollisionObject* o2, + PenetrationDepthType pd_type, + void* classifier, + std::size_t n_samples, + FCL_REAL margin, + KNNSolverType knn_solver_type, + std::size_t knn_k, + NearestNeighbors<Transform3f>::DistanceFunction distance_func = default_transform_distance_func); + + + + /// @brief penetration depth computation between two in-collision objects -FCL_REAL penetration_depth(const CollisionGeometry* o1, const Transform3f& tf1, - const CollisionGeometry* o2, const Transform3f& tf2, - const PenetrationDepthRequest& request, - PenetrationDepthResult& result); - -FCL_REAL penetration_depth(const CollisionObject* o1, - const CollisionObject* o2, - const PenetrationDepthRequest& request, - PenetrationDepthResult& result); +FCL_REAL penetrationDepth(const CollisionGeometry* o1, const Transform3f& tf1, + const CollisionGeometry* o2, const Transform3f& tf2, + const PenetrationDepthRequest& request, + PenetrationDepthResult& result); + +FCL_REAL penetrationDepth(const CollisionObject* o1, + const CollisionObject* o2, + const PenetrationDepthRequest& request, + PenetrationDepthResult& result); } diff --git a/include/fcl/shape/geometric_shapes.h b/include/fcl/shape/geometric_shapes.h index 818833ffdc8fb5843e9967203411e62134b7b653..f714129c1f1072003a6c10cdb8066ced80658ca8 100644 --- a/include/fcl/shape/geometric_shapes.h +++ b/include/fcl/shape/geometric_shapes.h @@ -93,6 +93,22 @@ public: /// @brief Get node type: a box NODE_TYPE getNodeType() const { return GEOM_BOX; } + + FCL_REAL computeVolume() const + { + return side[0] * side[1] * side[2]; + } + + Matrix3f computeMomentofInertia() const + { + FCL_REAL V = computeVolume(); + FCL_REAL a2 = side[0] * side[0] * V; + FCL_REAL b2 = side[1] * side[1] * V; + FCL_REAL c2 = side[2] * side[2] * V; + return Matrix3f((b2 + c2) / 12, 0, 0, + 0, (a2 + c2) / 12, 0, + 0, 0, (a2 + b2) / 12); + } }; /// @brief Center at zero point sphere @@ -111,6 +127,19 @@ public: /// @brief Get node type: a sphere NODE_TYPE getNodeType() const { return GEOM_SPHERE; } + + Matrix3f computeMomentofInertia() const + { + FCL_REAL I = 0.4 * radius * radius * computeVolume(); + return Matrix3f(I, 0, 0, + 0, I, 0, + 0, 0, I); + } + + FCL_REAL computeVolume() const + { + return 4 * boost::math::constants::pi<FCL_REAL>() * radius * radius / 3; + } }; /// @brief Center at zero point capsule @@ -132,6 +161,25 @@ public: /// @brief Get node type: a capsule NODE_TYPE getNodeType() const { return GEOM_CAPSULE; } + + FCL_REAL computeVolume() const + { + return boost::math::constants::pi<FCL_REAL>() * radius * radius *(lz + radius * 4/3.0); + } + + Matrix3f computeMomentofInertia() const + { + FCL_REAL v_cyl = radius * radius * lz * boost::math::constants::pi<FCL_REAL>(); + FCL_REAL v_sph = radius * radius * radius * boost::math::constants::pi<FCL_REAL>() * 4 / 3.0; + + FCL_REAL ix = v_cyl * lz * lz / 12.0 + 0.25 * v_cyl * radius + 0.4 * v_sph * radius * radius + 0.25 * v_sph * lz * lz; + FCL_REAL iz = (0.5 * v_cyl + 0.4 * v_sph) * radius * radius; + + return Matrix3f(ix, 0, 0, + 0, ix, 0, + 0, 0, iz); + } + }; /// @brief Center at zero cone @@ -153,6 +201,27 @@ public: /// @brief Get node type: a cone NODE_TYPE getNodeType() const { return GEOM_CONE; } + + FCL_REAL computeVolume() const + { + return boost::math::constants::pi<FCL_REAL>() * radius * radius * lz / 3; + } + + Matrix3f computeMomentofInertia() const + { + FCL_REAL V = computeVolume(); + FCL_REAL ix = V * (0.1 * lz * lz + 3 * radius * radius / 20); + FCL_REAL iz = 0.3 * V * radius * radius; + + return Matrix3f(ix, 0, 0, + 0, ix, 0, + 0, 0, iz); + } + + Vec3f computeCOM() const + { + return Vec3f(0, 0, -0.25 * lz); + } }; /// @brief Center at zero cylinder @@ -175,6 +244,21 @@ public: /// @brief Get node type: a cylinder NODE_TYPE getNodeType() const { return GEOM_CYLINDER; } + + FCL_REAL computeVolume() const + { + return boost::math::constants::pi<FCL_REAL>() * radius * radius * lz; + } + + Matrix3f computeMomentofInertia() const + { + FCL_REAL V = computeVolume(); + FCL_REAL ix = V * (3 * radius * radius + lz * lz) / 12; + FCL_REAL iz = V * radius * radius / 2; + return Matrix3f(ix, 0, 0, + 0, ix, 0, + 0, 0, iz); + } }; /// @brief Convex polytope @@ -253,6 +337,124 @@ public: /// @brief center of the convex polytope, this is used for collision: center is guaranteed in the internal of the polytope (as it is convex) Vec3f center; + /// based on http://number-none.com/blow/inertia/bb_inertia.doc + Matrix3f computeMomentofInertia() const + { + + Matrix3f C(0, 0, 0, + 0, 0, 0, + 0, 0, 0); + + Matrix3f C_canonical(1/60.0, 1/120.0, 1/120.0, + 1/120.0, 1/60.0, 1/120.0, + 1/120.0, 1/120.0, 1/60.0); + + int* points_in_poly = polygons; + int* index = polygons + 1; + for(int i = 0; i < num_planes; ++i) + { + Vec3f plane_center; + + // compute the center of the polygon + for(int j = 0; j < *points_in_poly; ++j) + plane_center += points[index[j]]; + plane_center = plane_center * (1.0 / *points_in_poly); + + // compute the volume of tetrahedron making by neighboring two points, the plane center and the reference point (zero) of the convex shape + const Vec3f& v3 = plane_center; + for(int j = 0; j < *points_in_poly; ++j) + { + int e_first = index[j]; + int e_second = index[(j+1)%*points_in_poly]; + const Vec3f& v1 = points[e_first]; + const Vec3f& v2 = points[e_second]; + FCL_REAL d_six_vol = (v1.cross(v2)).dot(v3); + Matrix3f A(v1, v2, v3); // this is A' in the original document + C += transpose(A) * C_canonical * A * d_six_vol; // change accordingly + } + + points_in_poly += (*points_in_poly + 1); + index = points_in_poly + 1; + } + + FCL_REAL trace_C = C(0, 0) + C(1, 1) + C(2, 2); + + return Matrix3f(trace_C - C(0, 0), -C(0, 1), -C(0, 2), + -C(1, 0), trace_C - C(1, 1), -C(1, 2), + -C(2, 0), -C(2, 1), trace_C - C(2, 2)); + + } + + Vec3f computeCOM() const + { + Vec3f com; + FCL_REAL vol = 0; + int* points_in_poly = polygons; + int* index = polygons + 1; + for(int i = 0; i < num_planes; ++i) + { + Vec3f plane_center; + + // compute the center of the polygon + for(int j = 0; j < *points_in_poly; ++j) + plane_center += points[index[j]]; + plane_center = plane_center * (1.0 / *points_in_poly); + + // compute the volume of tetrahedron making by neighboring two points, the plane center and the reference point (zero) of the convex shape + const Vec3f& v3 = plane_center; + for(int j = 0; j < *points_in_poly; ++j) + { + int e_first = index[j]; + int e_second = index[(j+1)%*points_in_poly]; + const Vec3f& v1 = points[e_first]; + const Vec3f& v2 = points[e_second]; + FCL_REAL d_six_vol = (v1.cross(v2)).dot(v3); + vol += d_six_vol; + com += (points[e_first] + points[e_second] + plane_center) * d_six_vol; + } + + points_in_poly += (*points_in_poly + 1); + index = points_in_poly + 1; + } + + return com / (vol * 4); // here we choose zero as the reference + } + + FCL_REAL computeVolume() const + { + FCL_REAL vol = 0; + int* points_in_poly = polygons; + int* index = polygons + 1; + for(int i = 0; i < num_planes; ++i) + { + Vec3f plane_center; + + // compute the center of the polygon + for(int j = 0; j < *points_in_poly; ++j) + plane_center += points[index[j]]; + plane_center = plane_center * (1.0 / *points_in_poly); + + // compute the volume of tetrahedron making by neighboring two points, the plane center and the reference point (zero point) of the convex shape + const Vec3f& v3 = plane_center; + for(int j = 0; j < *points_in_poly; ++j) + { + int e_first = index[j]; + int e_second = index[(j+1)%*points_in_poly]; + const Vec3f& v1 = points[e_first]; + const Vec3f& v2 = points[e_second]; + FCL_REAL d_six_vol = (v1.cross(v2)).dot(v3); + vol += d_six_vol; + } + + points_in_poly += (*points_in_poly + 1); + index = points_in_poly + 1; + } + + return vol / 6; + } + + + protected: /// @brief Get edge information void fillEdges(); diff --git a/src/continuous_collision.cpp b/src/continuous_collision.cpp index 260145fa55b98bee2332b75d2bc56a8f0790e360..fef5635806f88e329a3d87d72d194e223bb53875 100644 --- a/src/continuous_collision.cpp +++ b/src/continuous_collision.cpp @@ -50,6 +50,9 @@ FCL_REAL continuousCollideNaive(const CollisionGeometry* o1, const MotionBase* m for(std::size_t i = 0; i < n_iter; ++i) { FCL_REAL t = i / (FCL_REAL) (n_iter - 1); + motion1->integrate(t); + motion2->integrate(t); + motion1->getCurrentTransform(cur_tf1); motion2->getCurrentTransform(cur_tf2); @@ -60,6 +63,8 @@ FCL_REAL continuousCollideNaive(const CollisionGeometry* o1, const MotionBase* m { result.is_collide = true; result.time_of_contact = t; + result.contact_tf1 = cur_tf1; + result.contact_tf2 = cur_tf2; return t; } } @@ -113,6 +118,16 @@ FCL_REAL continuousCollideBVHPolynomial(const CollisionGeometry* o1_, const Tran result.is_collide = (node.pairs.size() > 0); result.time_of_contact = node.time_of_contact; + if(result.is_collide) + { + motion1->integrate(node.time_of_contact); + motion2->integrate(node.time_of_contact); + motion1->getCurrentTransform(tf1); + motion2->getCurrentTransform(tf2); + result.contact_tf1 = tf1; + result.contact_tf2 = tf2; + } + return result.time_of_contact; } @@ -199,6 +214,18 @@ FCL_REAL continuousCollideConservativeAdvancement(const CollisionGeometry* o1, c if(!nsolver_) delete nsolver; + if(result.is_collide) + { + motion1->integrate(result.time_of_contact); + motion2->integrate(result.time_of_contact); + + Transform3f tf1, tf2; + motion1->getCurrentTransform(tf1); + motion2->getCurrentTransform(tf2); + result.contact_tf1 = tf1; + result.contact_tf2 = tf2; + } + return res; } diff --git a/src/math/sampling.cpp b/src/math/sampling.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4838eda44474c783022a0d0f4979f3b9106a66f5 --- /dev/null +++ b/src/math/sampling.cpp @@ -0,0 +1,147 @@ +#include "fcl/math/sampling.h" +#include <boost/random/lagged_fibonacci.hpp> +#include <boost/random/uniform_int.hpp> +#include <boost/thread/mutex.hpp> +#include <boost/date_time/posix_time/posix_time.hpp> +#include <boost/math/constants/constants.hpp> + +namespace fcl +{ + +/// The seed the user asked for (cannot be 0) +static boost::uint32_t userSetSeed = 0; + +/// Flag indicating whether the first seed has already been generated or not +static bool firstSeedGenerated = false; + +/// The value of the first seed +static boost::uint32_t firstSeedValue = 0; + +/// Compute the first seed to be used; this function should be called only once +static boost::uint32_t firstSeed() +{ + static boost::mutex fsLock; + boost::mutex::scoped_lock slock(fsLock); + + if(firstSeedGenerated) + return firstSeedValue; + + if(userSetSeed != 0) + firstSeedValue = userSetSeed; + else firstSeedValue = (boost::uint32_t)(boost::posix_time::microsec_clock::universal_time() - boost::posix_time::ptime(boost::date_time::min_date_time)).total_microseconds(); + firstSeedGenerated = true; + + return firstSeedValue; +} + +/// We use a different random number generator for the seeds of the +/// Other random generators. The root seed is from the number of +/// nano-seconds in the current time. +static boost::uint32_t nextSeed() +{ + static boost::mutex rngMutex; + boost::mutex::scoped_lock slock(rngMutex); + static boost::lagged_fibonacci607 sGen(firstSeed()); + static boost::uniform_int<> sDist(1, 1000000000); + static boost::variate_generator<boost::lagged_fibonacci607&, boost::uniform_int<> > s(sGen, sDist); + return s(); +} + +boost::uint32_t RNG::getSeed() +{ + return firstSeed(); +} + +void RNG::setSeed(boost::uint32_t seed) +{ + if(firstSeedGenerated) + { + std::cerr << "Random number generation already started. Changing seed now will not lead to deterministic sampling." << std::endl; + } + if(seed == 0) + { + std::cerr << "Random generator seed cannot be 0. Using 1 instead." << std::endl; + userSetSeed = 1; + } + else + userSetSeed = seed; +} + +RNG::RNG() : generator_(nextSeed()), + uniDist_(0, 1), + normalDist_(0, 1), + uni_(generator_, uniDist_), + normal_(generator_, normalDist_) +{ +} + +double RNG::halfNormalReal(double r_min, double r_max, double focus) +{ + assert(r_min <= r_max); + + const double mean = r_max - r_min; + double v = gaussian(mean, mean / focus); + + if(v > mean) v = 2.0 * mean - v; + double r = v >= 0.0 ? v + r_min : r_min; + return r > r_max ? r_max : r; +} + +int RNG::halfNormalInt(int r_min, int r_max, double focus) +{ + int r = (int)floor(halfNormalReal((double)r_min, (double)(r_max) + 1.0, focus)); + return (r > r_max) ? r_max : r; +} + +// From: "Uniform Random Rotations", Ken Shoemake, Graphics Gems III, +// pg. 124-132 +void RNG::quaternion(double value[4]) +{ + double x0 = uni_(); + double r1 = sqrt(1.0 - x0), r2 = sqrt(x0); + double t1 = 2.0 * boost::math::constants::pi<double>() * uni_(), t2 = 2.0 * boost::math::constants::pi<double>() * uni_(); + double c1 = cos(t1), s1 = sin(t1); + double c2 = cos(t2), s2 = sin(t2); + value[0] = s1 * r1; + value[1] = c1 * r1; + value[2] = s2 * r2; + value[3] = c2 * r2; +} + +// From Effective Sampling and Distance Metrics for 3D Rigid Body Path Planning, by James Kuffner, ICRA 2004 +void RNG::eulerRPY(double value[3]) +{ + value[0] = boost::math::constants::pi<double>() * (2.0 * uni_() - 1.0); + value[1] = acos(1.0 - 2.0 * uni_()) - boost::math::constants::pi<double>() / 2.0; + value[2] = boost::math::constants::pi<double>() * (2.0 * uni_() - 1.0); +} + +void RNG::disk(double r_min, double r_max, double& x, double& y) +{ + double a = uniform01(); + double b = uniform01(); + double r = std::sqrt(a * r_max * r_max + (1 - a) * r_min * r_min); + double theta = 2 * boost::math::constants::pi<double>() * b; + x = r * std::cos(theta); + y = r * std::sin(theta); +} + +void RNG::ball(double r_min, double r_max, double& x, double& y, double& z) +{ + double a = uniform01(); + double b = uniform01(); + double c = uniform01(); + double r = std::pow(a * r_max * r_max * r_max + (1 - a) * r_min * r_min * r_min, 1 / 3.0); + double theta = std::acos(1 - 2 * b); + double phi = 2 * boost::math::constants::pi<double>() * c; + + double costheta = std::cos(theta); + double sintheta = std::sin(theta); + double cosphi = std::cos(phi); + double sinphi = std::sin(phi); + x = r * costheta; + y = r * sintheta * cosphi; + z = r * sintheta * sinphi; +} + +} diff --git a/src/math/transform.cpp b/src/math/transform.cpp index 665fe7ccca1c94e400eebb3605cb41a92e28a777..7670db042cbf8697e3efdc529bcb4eda9bab6169 100644 --- a/src/math/transform.cpp +++ b/src/math/transform.cpp @@ -36,6 +36,7 @@ #include "fcl/math/transform.h" +#include <boost/math/constants/constants.hpp> namespace fcl { @@ -327,6 +328,37 @@ Quaternion3f inverse(const Quaternion3f& q) return res.inverse(); } +void Quaternion3f::fromEuler(FCL_REAL a, FCL_REAL b, FCL_REAL c) +{ + Matrix3f R; + R.setEulerYPR(a, b, c); + + fromRotation(R); +} + +void Quaternion3f::toEuler(FCL_REAL& a, FCL_REAL& b, FCL_REAL& c) const +{ + Matrix3f R; + toRotation(R); + a = atan2(R(1, 0), R(0, 0)); + b = asin(-R(2, 0)); + c = atan2(R(2, 1), R(2, 2)); + + if(b == boost::math::constants::pi<double>() * 0.5) + { + if(a > 0) + a -= boost::math::constants::pi<double>(); + else + a += boost::math::constants::pi<double>(); + + if(c > 0) + c -= boost::math::constants::pi<double>(); + else + c += boost::math::constants::pi<double>(); + } +} + + Vec3f Quaternion3f::getColumn(std::size_t i) const { switch(i) diff --git a/src/penetration_depth.cpp b/src/penetration_depth.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6859e3d33d340fca6795113decfd7b69d9510ae8 --- /dev/null +++ b/src/penetration_depth.cpp @@ -0,0 +1,419 @@ +#include "fcl/penetration_depth.h" +#include "fcl/learning/classifier.h" +#include "fcl/math/sampling.h" +#include "fcl/collision.h" +#include "fcl/exception.h" + +#include "fcl/knn/nearest_neighbors_linear.h" +#include "fcl/knn/nearest_neighbors_GNAT.h" +#include "fcl/knn/nearest_neighbors_sqrtapprox.h" +#if FCL_HAVE_FLANN +#include "fcl/knn/nearest_neighbors_flann.h" +#endif + +#include "fcl/ccd/motion.h" +#include "fcl/continuous_collision.h" + +namespace fcl +{ + +std::vector<Transform3f> penetrationDepthModelLearning(const CollisionObject* o1, + const CollisionObject* o2, + PenetrationDepthType pd_type, + void* classifier_, + std::size_t n_samples, + FCL_REAL margin, + KNNSolverType knn_solver_type, + std::size_t knn_k, + NearestNeighbors<Transform3f>::DistanceFunction distance_func) +{ + std::vector<Transform3f> support_transforms_positive; + std::vector<Transform3f> support_transforms_negative; + + switch(pd_type) + { + case PDT_TRANSLATIONAL: + { + SVMClassifier<3>* classifier = static_cast<SVMClassifier<3>*>(classifier_); + SamplerR<3> sampler; + Vecnf<3> lower_bound, upper_bound; + AABB aabb1 = o1->getAABB(); + AABB aabb2 = o2->getAABB(); + lower_bound[0] = aabb1.min_[0] - aabb2.max_[0] - margin; + lower_bound[1] = aabb1.min_[1] - aabb2.max_[1] - margin; + lower_bound[2] = aabb1.min_[2] - aabb2.max_[2] - margin; + upper_bound[0] = aabb1.max_[0] - aabb2.min_[0] + margin; + upper_bound[1] = aabb1.max_[1] - aabb2.min_[1] + margin; + upper_bound[2] = aabb1.max_[2] - aabb2.min_[2] + margin; + + sampler.setBound(lower_bound, upper_bound); + + std::vector<Item<3> > data(n_samples); + for(std::size_t i = 0; i < n_samples; ++i) + { + Vecnf<3> q = sampler.sample(); + Transform3f tf(o2->getQuatRotation(), Vec3f(q[0], q[1], q[2])); + + CollisionRequest request; + CollisionResult result; + + int n_collide = collide(o1->getCollisionGeometry(), o1->getTransform(), + o2->getCollisionGeometry(), tf, request, result); + + data[i] = Item<3>(q, (n_collide > 0)); + } + + // classifier.setScaler(Scaler<3>(lower_bound, upper_bound)); + classifier->setScaler(computeScaler(data)); + + classifier->learn(data); + + std::vector<Item<3> > svs = classifier->getSupportVectors(); + for(std::size_t i = 0; i < svs.size(); ++i) + { + const Vecnf<3>& q = svs[i].q; + if(svs[i].label) + support_transforms_positive.push_back(Transform3f(o2->getQuatRotation(), Vec3f(q[0], q[1], q[2]))); + else + support_transforms_negative.push_back(Transform3f(o2->getQuatRotation(), Vec3f(q[0], q[1], q[2]))); + } + + break; + } + case PDT_GENERAL_EULER: + { + SVMClassifier<6>* classifier = static_cast<SVMClassifier<6>*>(classifier_); + SamplerSE3Euler sampler; + + FCL_REAL r1 = o1->getCollisionGeometry()->aabb_radius; + FCL_REAL r2 = o2->getCollisionGeometry()->aabb_radius; + Vec3f c1 = o1->getCollisionGeometry()->aabb_center; + Vec3f c2 = o2->getCollisionGeometry()->aabb_center; + + FCL_REAL r = r1 + r2 + c1.length() + c2.length() + margin; + sampler.setBound(Vec3f(-r, -r, -r), Vec3f(r, r, r)); + + std::vector<Item<6> > data(n_samples); + for(std::size_t i = 0; i < n_samples; ++i) + { + Vecnf<6> q = sampler.sample(); + + Quaternion3f quat; + quat.fromEuler(q[3], q[4], q[5]); + Transform3f tf(quat, Vec3f(q[0], q[1], q[2])); + + CollisionRequest request; + CollisionResult result; + + int n_collide = collide(o1->getCollisionGeometry(), Transform3f(), + o2->getCollisionGeometry(), tf, request, result); + + data[i] = Item<6>(q, (n_collide > 0)); + } + + /* + Vecnf<6> scaler_lower_bound, scaler_upper_bound; + for(std::size_t i = 0; i < 3; ++i) + { + scaler_lower_bound[i] = -r; + scaler_upper_bound[i] = r; + } + + for(std::size_t i = 3; i < 6; ++i) + { + scaler_lower_bound[i] = -boost::math::constants::pi<FCL_REAL>(); + scaler_upper_bound[i] = boost::math::constants::pi<FCL_REAL>(); + } + + classifier.setScaler(Scaler<6>(scaler_lower_bound, scaler_upper_bound)); + */ + + classifier->setScaler(computeScaler(data)); + + classifier->learn(data); + + std::vector<Item <6> > svs = classifier->getSupportVectors(); + for(std::size_t i = 0; i < svs.size(); ++i) + { + const Vecnf<6>& q = svs[i].q; + Quaternion3f quat; + quat.fromEuler(q[3], q[4], q[5]); + if(svs[i].label) + support_transforms_positive.push_back(Transform3f(quat, Vec3f(q[0], q[1], q[2]))); + else + support_transforms_negative.push_back(Transform3f(quat, Vec3f(q[0], q[1], q[2]))); + } + + break; + } + case PDT_GENERAL_QUAT: + { + SVMClassifier<7>* classifier = static_cast<SVMClassifier<7>*>(classifier_); + SamplerSE3Quat sampler; + + FCL_REAL r1 = o1->getCollisionGeometry()->aabb_radius; + FCL_REAL r2 = o2->getCollisionGeometry()->aabb_radius; + Vec3f c1 = o1->getCollisionGeometry()->aabb_center; + Vec3f c2 = o2->getCollisionGeometry()->aabb_center; + + FCL_REAL r = r1 + r2 + c1.length() + c2.length() + margin; + + sampler.setBound(Vec3f(-r, -r, -r), Vec3f(r, r, r)); + + std::vector<Item<7> > data(n_samples); + for(std::size_t i = 0; i < n_samples; ++i) + { + Vecnf<7> q = sampler.sample(); + + Quaternion3f quat(q[3], q[4], q[5], q[6]); + Transform3f tf(quat, Vec3f(q[0], q[1], q[2])); + + CollisionRequest request; + CollisionResult result; + + int n_collide = collide(o1->getCollisionGeometry(), Transform3f(), + o2->getCollisionGeometry(), tf, request, result); + + data[i] = Item<7>(q, (n_collide > 0)); + } + + classifier->setScaler(computeScaler(data)); + + classifier->learn(data); + + std::vector<Item<7> > svs = classifier->getSupportVectors(); + for(std::size_t i = 0; i < svs.size(); ++i) + { + const Vecnf<7>& q = svs[i].q; + if(svs[i].label) + support_transforms_positive.push_back(Transform3f(Quaternion3f(q[3], q[4], q[5], q[6]), Vec3f(q[0], q[1], q[2]))); + else + support_transforms_negative.push_back(Transform3f(Quaternion3f(q[3], q[4], q[5], q[6]), Vec3f(q[0], q[1], q[2]))); + } + + break; + } + case PDT_GENERAL_EULER_BALL: + { + SVMClassifier<6>* classifier = static_cast<SVMClassifier<6>*>(classifier_); + SamplerSE3Euler_ball sampler; + + FCL_REAL r1 = o1->getCollisionGeometry()->aabb_radius; + FCL_REAL r2 = o2->getCollisionGeometry()->aabb_radius; + Vec3f c1 = o1->getCollisionGeometry()->aabb_center; + Vec3f c2 = o2->getCollisionGeometry()->aabb_center; + + sampler.setBound(c1, r1 + margin, r2 + margin, c2); + + std::vector<Item<6> > data(n_samples); + for(std::size_t i = 0; i < n_samples; ++i) + { + Vecnf<6> q = sampler.sample(); + + Quaternion3f quat; + quat.fromEuler(q[3], q[4], q[5]); + Transform3f tf(quat, Vec3f(q[0], q[1], q[2])); + + CollisionRequest request; + CollisionResult result; + + int n_collide = collide(o1->getCollisionGeometry(), Transform3f(), + o2->getCollisionGeometry(), tf, request, result); + + data[i] = Item<6>(q, (n_collide > 0)); + } + + classifier->setScaler(computeScaler(data)); + + classifier->learn(data); + + std::vector<Item<6> > svs = classifier->getSupportVectors(); + for(std::size_t i = 0; i < svs.size(); ++i) + { + const Vecnf<6>& q = svs[i].q; + Quaternion3f quat; + quat.fromEuler(q[3], q[4], q[5]); + if(svs[i].label) + support_transforms_positive.push_back(Transform3f(quat, Vec3f(q[0], q[1], q[2]))); + else + support_transforms_negative.push_back(Transform3f(quat, Vec3f(q[0], q[1], q[2]))); + } + + break; + } + case PDT_GENERAL_QUAT_BALL: + { + SVMClassifier<7>* classifier = static_cast<SVMClassifier<7>*>(classifier_); + SamplerSE3Quat_ball sampler; + + FCL_REAL r1 = o1->getCollisionGeometry()->aabb_radius; + FCL_REAL r2 = o2->getCollisionGeometry()->aabb_radius; + Vec3f c1 = o1->getCollisionGeometry()->aabb_center; + Vec3f c2 = o2->getCollisionGeometry()->aabb_center; + + sampler.setBound(c1, r1 + margin, r2 + margin, c2); + + std::vector<Item<7> > data(n_samples); + for(std::size_t i = 0; i < n_samples; ++i) + { + Vecnf<7> q = sampler.sample(); + + Quaternion3f quat(q[3], q[4], q[5], q[6]); + + Transform3f tf(quat, Vec3f(q[0], q[1], q[2])); + + CollisionRequest request; + CollisionResult result; + + int n_collide = collide(o1->getCollisionGeometry(), Transform3f(), + o2->getCollisionGeometry(), tf, request, result); + + data[i] = Item<7>(q, (n_collide > 0)); + } + + classifier->setScaler(computeScaler(data)); + + classifier->learn(data); + + std::vector<Item<7> > svs = classifier->getSupportVectors(); + for(std::size_t i = 0; i < svs.size(); ++i) + { + const Vecnf<7>& q = svs[i].q; + if(svs[i].label) + support_transforms_positive.push_back(Transform3f(Quaternion3f(q[3], q[4], q[5], q[6]), Vec3f(q[0], q[1], q[2]))); + else + support_transforms_negative.push_back(Transform3f(Quaternion3f(q[3], q[4], q[5], q[6]), Vec3f(q[0], q[1], q[2]))); + } + + break; + } + default: + ; + } + + // from support transforms compute contact transforms + + NearestNeighbors<Transform3f>* knn_solver_positive = NULL; + NearestNeighbors<Transform3f>* knn_solver_negative = NULL; + + switch(knn_solver_type) + { + case KNN_LINEAR: + knn_solver_positive = new NearestNeighborsLinear<Transform3f>(); + knn_solver_negative = new NearestNeighborsLinear<Transform3f>(); + break; + case KNN_GNAT: + knn_solver_positive = new NearestNeighborsGNAT<Transform3f>(); + knn_solver_negative = new NearestNeighborsGNAT<Transform3f>(); + break; + case KNN_SQRTAPPROX: + knn_solver_positive = new NearestNeighborsSqrtApprox<Transform3f>(); + knn_solver_negative = new NearestNeighborsSqrtApprox<Transform3f>(); + break; + } + + + knn_solver_positive->setDistanceFunction(distance_func); + knn_solver_negative->setDistanceFunction(distance_func); + + knn_solver_positive->add(support_transforms_positive); + knn_solver_negative->add(support_transforms_negative); + + std::vector<Transform3f> contact_vectors; + + for(std::size_t i = 0; i < support_transforms_positive.size(); ++i) + { + std::vector<Transform3f> nbh; + knn_solver_negative->nearestK(support_transforms_positive[i], knn_k, nbh); + + for(std::size_t j = 0; j < nbh.size(); ++j) + { + ContinuousCollisionRequest request; + request.ccd_motion_type = CCDM_LINEAR; + request.num_max_iterations = 100; + ContinuousCollisionResult result; + continuousCollide(o1->getCollisionGeometry(), Transform3f(), Transform3f(), + o2->getCollisionGeometry(), nbh[j], support_transforms_positive[i], + request, result); + + //std::cout << result.time_of_contact << std::endl; + + contact_vectors.push_back(result.contact_tf2); + } + } + + for(std::size_t i = 0; i < support_transforms_negative.size(); ++i) + { + std::vector<Transform3f> nbh; + knn_solver_positive->nearestK(support_transforms_negative[i], knn_k, nbh); + + for(std::size_t j = 0; j < nbh.size(); ++j) + { + ContinuousCollisionRequest request; + request.ccd_motion_type = CCDM_LINEAR; + request.num_max_iterations = 100; + ContinuousCollisionResult result; + continuousCollide(o1->getCollisionGeometry(), Transform3f(), Transform3f(), + o2->getCollisionGeometry(), support_transforms_negative[i], nbh[j], + request, result); + + //std::cout << result.time_of_contact << std::endl; + + contact_vectors.push_back(result.contact_tf2); + } + } + + delete knn_solver_positive; + delete knn_solver_negative; + + return contact_vectors; + +} + + + +FCL_REAL penetrationDepth(const CollisionGeometry* o1, const Transform3f& tf1, + const CollisionGeometry* o2, const Transform3f& tf2, + const PenetrationDepthRequest& request, + PenetrationDepthResult& result) +{ + NearestNeighbors<Transform3f>* knn_solver = NULL; + switch(request.knn_solver_type) + { + case KNN_LINEAR: + knn_solver = new NearestNeighborsLinear<Transform3f>(); + break; + case KNN_GNAT: + knn_solver = new NearestNeighborsGNAT<Transform3f>(); + break; + case KNN_SQRTAPPROX: + knn_solver = new NearestNeighborsSqrtApprox<Transform3f>(); + break; + } + + knn_solver->setDistanceFunction(request.distance_func); + + knn_solver->add(request.contact_vectors); + + Transform3f tf; + relativeTransform(tf1, tf2, tf); + + result.resolved_tf = knn_solver->nearest(tf) * tf1; + result.pd_value = request.distance_func(result.resolved_tf, tf2); + result.pd_value = std::sqrt(result.pd_value); + + return result.pd_value; +} + +FCL_REAL penetrationDepth(const CollisionObject* o1, + const CollisionObject* o2, + const PenetrationDepthRequest& request, + PenetrationDepthResult& result) +{ + return penetrationDepth(o1->getCollisionGeometry(), o1->getTransform(), + o2->getCollisionGeometry(), o2->getTransform(), + request, + result); +} + +}