/* UMANS: Unified Microscopic Agent Navigation Simulator ** Copyright (C) 2018-2020 Inria Rennes Bretagne Atlantique - Rainbow - Julien Pettré ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . ** ** Contact: crowd_group@inria.fr ** Website: https://project.inria.fr/crowdscience/ ** See the file AUTHORS.md for a list of all contributors. */ #ifndef LIB_COST_FUNCTION_H #define LIB_COST_FUNCTION_H #include #include #include #include #include #include #include // std::reference_wrapper class WorldBase; class Agent; class CostFunction; struct SamplingParameters; struct PhantomAgent; typedef std::vector AgentNeighborList; typedef std::vector ObstacleNeighborList; typedef std::pair NeighborList; typedef std::vector> CostFunctionList; const float MaxFloat = std::numeric_limits::max(); /// @defgroup costfunctions Cost functions /// Implementations of specific cost functions for local navigation. /// An abstract class for a cost function that agents use for their (local) navigation. /// To create a new cost function, you need to create a new class that inherits from CostFunction. /// Your class should implement the following: /// /// const static std::string GetName(): This method should return the name that identifies the function in configuration files. /// GetCost(): This method should compute and return the cost of a velocity according to your cost function. /// (optionally) GetGradient(): This method should compute and return the gradient of your cost function at the given velocity. /// If you do not implement it, the program will automatically approximate the gradient via sampling. /// (optionally) parseParameters(): This method should parse any additional parameters that are specific to your cost function. /// Make sure to call the parent version of parseParameters() here, to parse any parameters that were already defined. /// /// Finally, to be able to use your cost function, add the following line to the file core/costFunctionFactory.cpp: /// registerCostFunction(); /// /// This will make sure that the program can dynamically create instances of your cost function when it is used in an XML file. /// /// The GenericCost class is a template file that you can use for your own classes, if you wish. /** ### Example of implementing a subclass Add a file CostFunctions/MyNewFunction.h: #ifndef MY_NEW_FUNCTION_H #define MY_NEW_FUNCTION_H class MyNewFunction : public CostFunction { public: MyNewFunction() : CostFunction() {} virtual ~MyNewFunction() {} const static std::string GetName() { return "MyNewFunctionName"; } float GetCost(const Vector2D& velocity, const Agent* agent, const WorldBase * world) const override; //// optional Vector2D GetGradient(const Vector2D& velocity, const Agent* agent, const WorldBase * world) const override; //// optional void parseParameters(const CostFunctionParameters & params) override; }; #endif //MY_NEW_FUNCTION_H Add a file CostFunctions/MyNewFunction.cpp: #include float MyNewFunction::GetCost(const Vector2D& velocity, const Agent* agent, const WorldBase * world) const { //// Implementation } //// optional Vector2D MyNewFunction::GetGradient(const Vector2D& velocity, const Agent* agent, const WorldBase * world) const { //// Implementation } //// optional void MyNewFunction::parseParameters(const CostFunctionParameters & params) { CostFunction::parseParameters(params); //// Implementation } Add the following lines to core/costFunctionFactory.cpp: #include //// ... registerCostFunction(); */ class CostFunction { protected: /// The interaction range (in meters) for this cost function. /// This is used as the search radius for nearest-neighbor queries. /// The value corresponds to the XML parameter "range". float range_ = 5.0f; public: CostFunction(); virtual ~CostFunction(); /// Returns the interaction range of this cost function. /// This is the radius of the circle (around the active agent) in which the cost function operates. inline float GetRange() const { return range_; } #pragma region [Main operations] /// @name Main operations /// The core operations that each CostFunction should support. /// @{ /// Computes the cost of a given velocity. /// The cost can be thought of as the (un)attractiveness of the given velocity for the given agent. /// A lower cost implies that the velocity is better for the agent. /// Note: Every (non-abstract) child class of CostFunction must implement this method. /// The velocity for which the cost is requested. /// The agent that would use the requested velocity. /// The world in which the simulation takes place. /// A floating-point cost indicating the (un)attractiveness of the given velocity for the given agent. virtual float GetCost(const Vector2D& velocity, Agent* agent, const WorldBase* world) const = 0; /// Computes the gradient of the cost function at a given velocity. /// The gradient is a 2D vector that points in the direction of steepest ascent, i.e. the direction in which the cost increases the most. /// By default, this method uses sampling to approximate the gradient. /// Subclasses of CostFunction may choose to implement something more specific (e.g. a closed-form gradient). /// The velocity for which the gradient is requested. /// The agent that would use the requested velocity. /// The world in which the simulation takes place. /// A 2D vector denoting the gradient of the cost function at the given velocity. virtual Vector2D GetGradient(const Vector2D& velocity, Agent* agent, const WorldBase* world) const; /// Computes the global minimum of the cost function, i.e. the velocity with minimum cost. /// By default, this method uses sampling to approximate the global minimum. /// Subclasses of CostFunction may choose to implement a better (e.g. closed-form) solution. /// The agent for which the optimal velocity is requested. /// The world in which the simulation takes place. /// The velocity with minimum cost (or an approximation thereof) for the given agent. virtual Vector2D GetGlobalMinimum(Agent* agent, const WorldBase* world) const; /// @} #pragma endregion /// Uses sampling to approximate the global minimum of a list of cost functions. /// This method tries out several candidate velocities (sampled according to 'params'), /// computes the total cost for each candidate (combining all functions in 'costFunctions'), /// and returns the velocity with the lowest cost. /// The agent for which the optimal velocity is requested. /// The world in which the simulation takes place. /// Parameters for sampling the velocity space. /// A list of cost functions to evaluate. /// The sample velocity for which the sum of all cost-function values is lowest. static Vector2D ApproximateGlobalMinimumBySampling(Agent* agent, const WorldBase* world, const SamplingParameters& params, const CostFunctionList& costFunctions); /// Parses the parameters of the cost function. /// By default, this method already loads the "range" parameter. /// Override this method to let it load any additional parameters of your interest. /// An XML block that contains all parameters of this cost function. virtual void parseParameters(const CostFunctionParameters & params); protected: #pragma region [Helper methods for common cost-function tasks] /// @name Helper methods for common cost-function tasks /// Useful methods that can come in handy for many CostFunction implementations. /// @{ /// Computes the expected time to collision of two disk-shaped objects with two hypothetical velocities. /// The current position of object 1. /// The hypothetical velocity of object 1. /// The radius (in meters) of object 1. /// The current position of object 2. /// The hypothetical velocity of object 2. /// The radius (in meters) of object 2. /// The time (in seconds) after which the two objects will collide if they move at the given velocities. /// This value is 0 if the objects are already colliding, and it is MaxFloat if the objects will never collide. float ComputeTimeToCollision( const Vector2D& position1, const Vector2D& velocity1, const float radius1, const Vector2D& position2, const Vector2D& velocity2, const float radius2) const; /// Computes the expected time to the first collision with a set of neighboring agents and obstacles. /// The current position of the querying agent. /// The hypothetical velocity of the querying agent. /// The radius (in meters) of the querying agent. /// A list of neighboring agents and obstacles. /// The maximum distance between the query position and a neighboring object. /// Any objects farther away will be ignored. /// Whether or not to ignore any collisions that are already happening now. /// The smallest time to collision (in seconds) of the querying agent with all neighbors in the list. /// This value may be 0 if there is already a collision with any of the neighboring objects. /// However, if ignoreCurrentCollisions is set to true, any neighbors that are already colliding will be ignored, /// and the result will be larger than zero. /// float ComputeTimeToFirstCollision( const Vector2D& position, const Vector2D& velocity, const float radius, const NeighborList& neighbors, float maximumDistance, bool ignoreCurrentCollisions) const; /// Computes the expected time at which the distance between two disk-shaped objects is minimal, and the value of this distance. /// The current position of object 1. /// The hypothetical velocity of object 1. /// The radius (in meters) of object 1. /// The current position of object 2. /// The hypothetical velocity of object 2. /// The radius (in meters) of object 2. /// A pair of two floating-point numbers, where the first is the time to closest approach (ttca) /// and the second is the distance of closest approach (dca). std::pair ComputeTimeAndDistanceToClosestApproach( const Vector2D& position1, const Vector2D& velocity1, const float radius1, const Vector2D& position2, const Vector2D& velocity2, const float radius2) const; /// Converts a gradient in the "(angle,speed) domain" to a gradient in the Euclidean (vx,vy) domain. /// The angular component of the gradient to convert. /// The speed component of the gradient to convert. /// The directional vector that defines the basis of the angle-speed coordinate system. /// The speed that defines the scale of the angle-speed coordinate system. /// A Vector2D that represents the same gradient in the (vx,vy) domain. Vector2D RotateGradientToEuclideanCoordinates( const float GradTh, const float GradS, const Vector2D& direction, const float speed) const; /// @} #pragma endregion private: /// Solves (for x) the quadratic equation ax^2 + bx + c = 0. /// The parameter a of the quadratic equation. /// The parameter b of the quadratic equation. /// The parameter c of the quadratic equation. /// [out] Will store the first solution of the equation, if it exists. /// This will only receive a value if there are 1 or 2 solutions. /// [out] Will store the second solution of the equation, if it exists. /// This will only receive a value if there are 2 solutions. /// The number of solutions to the equation, i.e. 0, 1, or 2. int SolveQuadraticEquation(float a, float b, float c, float& answer1, float& answer2) const; /// Computes the expected time to collision of a moving disk-shaped object and a static line segment. /// The current position of the moving object. /// The hypothetical velocity of the moving object. /// The radius (in meters) of the moving object. /// The static line-segment object. /// The time (in seconds) after which the two objects will collide if the disk moves at the given velocity. /// This value is 0 if the objects are already colliding, and it is MaxFloat if the objects will never collide. float ComputeTimeToCollision_LineSegment(const Vector2D& position, const Vector2D& velocity, const float radius, const LineSegment2D& lineSegment) const; float ComputeTimeToCollision_LineSegmentInterior(const Vector2D& position, const Vector2D& velocity, const float radius, const LineSegment2D& lineSegment) const; }; #endif //LIB_COST_FUNCTION_H