From 8115f07589aa097fe1c6e346f8b0330ddec3e1c2 Mon Sep 17 00:00:00 2001 From: Rainer Kartmann <rainer.kartmann@student.kit.edu> Date: Fri, 15 Feb 2019 14:10:07 +0100 Subject: [PATCH] Added class ClampedNormalDistribution --- VirtualRobot/CMakeLists.txt | 1 + .../math/ClampedNormalDistribution.hpp | 146 ++++++++++++++++++ 2 files changed, 147 insertions(+) create mode 100644 VirtualRobot/math/ClampedNormalDistribution.hpp diff --git a/VirtualRobot/CMakeLists.txt b/VirtualRobot/CMakeLists.txt index 1c3c69dda..c5cb216df 100644 --- a/VirtualRobot/CMakeLists.txt +++ b/VirtualRobot/CMakeLists.txt @@ -311,6 +311,7 @@ Import/RobotImporterFactory.h Import/MeshImport/STLReader.h Tools/Gravity.h math/MathForwardDefinitions.h +math/ClampedNormalDistribution.hpp math/Contact.h math/ContactList.h math/Line.h diff --git a/VirtualRobot/math/ClampedNormalDistribution.hpp b/VirtualRobot/math/ClampedNormalDistribution.hpp new file mode 100644 index 000000000..b5c4403a5 --- /dev/null +++ b/VirtualRobot/math/ClampedNormalDistribution.hpp @@ -0,0 +1,146 @@ +#pragma once + +#include <random> + + +namespace VirtualRobot +{ + + /** + * @class A normal distribution with lower and upper bounds (min and max). + * + * The normal distribution's mean is not limited to the clamping bounds. + * + * When a new number is generated, random numbers are generated according + * to the normal distribution until a number between min and max is + * generated. If the number of attempts exceeds a threshold, the last + * generated number drawn uniformly between min and max. + */ + template <typename RealType> + class ClampedNormalDistribution + { + public: + + /// Construct an unclamped standard normal distribution. + ClampedNormalDistribution() + {} + + /// Construct a clamped normal distribution centered between min and max + /// with t * stddev being equal to the distance from center to bound. + ClampedNormalDistribution(RealType min, RealType max, RealType t = 3) : + _distrib(.5 * (min + max), .5 / t * (max - min)), _min(min), _max(max) + {} + + /// Construct a clamped normal distribution with given parameters. + ClampedNormalDistribution(RealType mean, RealType stddev, RealType min, RealType max) : + _distrib(mean, stddev), _min(min), _max(max) + {} + + /// Construct an unclamped normal distribution with given mean and standard deviation. + static ClampedNormalDistribution Unclamped(RealType mean = 0, RealType stddev = 1) + { + return ClampedNormalDistribution(mean, stddev, std::numeric_limits<RealType>::min(), + std::numeric_limits<RealType>::max()); + } + + /// Get the mean of the normal distribution. + RealType mean() const { return _distrib.mean(); } + /// Set the mean of the normal distribution. + void setMean(RealType value) { this->_distrib = { value, _distrib.stddev() }; } + + /// Get the standard deviation of the normal distribution. + RealType stddev() const { return _distrib.stddev(); } + /// Set the standard deviation of the normal distribution. + void setStddev(RealType value) { this->_distrib = { _distrib.mean(), value }; } + + /// Get the lower clipping bound. + RealType min() const { return _min; } + /// Set the lower clipping bound. + void setMin(RealType value) { this->_min = value; } + + /// Get the upper clipping bound. + RealType max() const { return _max; } + /// Set the lower clipping bound. + void setMax(RealType value) { this->_max = value; } + + + /// Get the maximal number of attempts. + int maxAttempts() const { return _maxAttempts; } + /// Set the maximal number of attempts. + void setMaxAttempts(int value) { this->_maxAttempts = value; } + + + /// Generate a new random number between min and max. + template <class Generator> + RealType operator()(Generator& gen) + { + assert(_min < _max); + int attempts = 0; + RealType result; + do + { + result = _distrib(gen); + } + while ((result < _min || _max < result) && attempts++ < _maxAttempts); + + if (attempts >= _maxAttempts) + { + result = uniform(gen); + } + return result; + } + + /// Generate a new random number drawn uniformly between min and max. + template <class Generator> + RealType uniform(Generator& gen) const + { + std::uniform_real_distribution<RealType> uniformDistrib(_min, _max); + return uniformDistrib(gen); + } + + + /// Equality operator (member-wise equality). + bool operator==(const ClampedNormalDistribution& rhs) const + { + return this == &rhs || + (this->mean() == rhs.mean() + && this->stddev() == rhs.stddev() + && this->_min == rhs._min + && this->_max == rhs._max + && this->_maxAttempts == rhs._maxAttempts); + } + + + private: + + /// The normal distribution. + std::normal_distribution<RealType> _distrib {0, 1}; + + /// The lower bound. + RealType _min = std::numeric_limits<RealType>::min(); + /// The upper bound. + RealType _max = std::numeric_limits<RealType>::max(); + + /// The maximal number of times it is attempted to generate a value + /// inside the bounds before uniformly drawing the result. + int _maxAttempts = 100; + + }; + + /// Stream a human-readable description of rhs to os. + template <typename RealT> + std::ostream& operator<< (std::ostream& os, const ClampedNormalDistribution<RealT>& rhs) + { + // for some reason, the first string must be wrapped in string constructor + os << std::string("<ClampedNormalDistribution mean=") << rhs.mean() << " stddev=" + << rhs.stddev() << " [min, max]=[" << rhs.min() << ", " << rhs.max() << "] >"; + return os; + } + + + /// A clamped normal distribution of floats. + using ClampedNormalDistributionf = ClampedNormalDistribution<float>; + /// A clamped normal distribution of doubles. + using ClampedNormalDistributiond = ClampedNormalDistribution<double>; + +} -- GitLab