18 #include <Eigen/Dense>      31 const 
unsigned int INVALID_K = 
std::numeric_limits<
unsigned int>::max();
    38       m_last_selected_sector(
std::numeric_limits<
unsigned int>::max())
    40     if (INI_FILE != 
nullptr) 
initialize(*INI_FILE);
    45     options.saveToConfigFile(c, getConfigFileSectionName());
    50     options.loadFromConfigFile(c, getConfigFileSectionName());
    55     int k_from{-1}, k_to{-1};
    57     bool contains_target_k{
false};
    62         : min_eval(
std::numeric_limits<double>::max()),
    63           max_eval(-
std::numeric_limits<double>::max())
    69 void CHolonomicFullEval::evalSingleTarget(
    73     const auto target = ni.
targets[target_idx];
    79     const auto ptg = getAssociatedPTG();
    82     const double target_dir = ::atan2(target.y, target.x);
    83     const unsigned int target_k =
    84         CParameterizedTrajectoryGenerator::alpha2index(target_dir, nDirs);
    85     const double target_dist = target.norm();
    87     m_dirs_scores.resize(nDirs, options.factorWeights.size() + 2);
    90     std::vector<mrpt::math::TPoint2D> obstacles_2d(nDirs);
    96     const auto& sc_lut = m_sincos_lut.getSinCosForScan(sp);
    98     for (
unsigned int i = 0; i < nDirs; i++)
   100         obstacles_2d[i].x = ni.
obstacles[i] * sc_lut.ccos[i];
   101         obstacles_2d[i].y = ni.
obstacles[i] * sc_lut.csin[i];
   108     for (
unsigned int i = 0; i < nDirs; i++)
   114         if (ni.
obstacles[i] < options.TOO_CLOSE_OBSTACLE &&
   115             !(i == target_k && ni.
obstacles[i] > 1.02 * target_dist))
   117             for (
size_t l = 0; l < 
NUM_FACTORS; l++) m_dirs_scores(i, l) = .0;
   121         const double d = std::min(ni.
obstacles[i], 0.95 * target_dist);
   124         const double x = d * sc_lut.ccos[i];
   125         const double y = d * sc_lut.csin[i];
   130             target_dist < 1.0 - options.TOO_CLOSE_OBSTACLE &&
   135                 std::max(target_dist, ni.
obstacles[i]) / (target_dist * 1.05);
   140                 std::max(0.0, ni.
obstacles[i] - options.TOO_CLOSE_OBSTACLE);
   147             const double max_real_freespace =
   148                 ptg->getActualUnloopedPathLength(i);
   149             const double max_real_freespace_norm =
   150                 max_real_freespace / ptg->getRefDistance();
   166         double min_dist_target_along_path = sg.
distance(target);
   172         const double endpt_dist_to_target = (target - 
TPoint2D(x, y)).
norm();
   173         const double endpt_dist_to_target_norm =
   174             std::min(1.0, endpt_dist_to_target);
   176         if ((endpt_dist_to_target_norm > target_dist &&
   177              endpt_dist_to_target_norm >= 0.95 * target_dist) &&
   179             min_dist_target_along_path >
   180                 1.05 * std::min(target_dist, endpt_dist_to_target_norm))
   185             min_dist_target_along_path = sg.
distance(target);
   188         scores[1] = 1.0 / (1.0 + 
square(min_dist_target_along_path));
   194         scores[2] = std::sqrt(1.01 - endpt_dist_to_target_norm);
   195         scores[5] = scores[2];
   202         if (m_last_selected_sector != std::numeric_limits<unsigned int>::max())
   205             const unsigned int hist_dist =
   208             if (hist_dist >= options.HYSTERESIS_SECTOR_COUNT)
   210                     1.0 - (hist_dist - options.HYSTERESIS_SECTOR_COUNT) /
   230             double& closest_obs = scores[4];
   234             const int W = std::max(1, 
round(nDirs * 0.1));
   235             const int i_min = std::max(0, static_cast<int>(i) - W);
   237                 std::min(static_cast<int>(nDirs) - 1, static_cast<int>(i) + W);
   238             for (
int oi = i_min; oi <= i_max; oi++)
   254         if (target_dist < 1.0 - options.TOO_CLOSE_OBSTACLE &&
   265             m_dirs_scores(i, l) = scores[l];
   273         if (!options.factorNormalizeOrNot[l]) 
continue;
   275         const double mmax = m_dirs_scores.col(l).maxCoeff();
   276         const double mmin = m_dirs_scores.col(l).minCoeff();
   277         const double span = mmax - mmin;
   278         if (span <= .0) 
continue;
   280         m_dirs_scores.col(l).array() -= mmin;
   281         m_dirs_scores.col(l).array() /= span;
   286     const unsigned int NUM_PHASES = options.PHASE_FACTORS.size();
   289     std::vector<double> weights_sum_phase(NUM_PHASES, .0),
   290         weights_sum_phase_inv(NUM_PHASES);
   291     for (
unsigned int i = 0; i < NUM_PHASES; i++)
   293         for (
unsigned int l : options.PHASE_FACTORS[i])
   294             weights_sum_phase[i] += options.factorWeights.at(l);
   295         ASSERT_(weights_sum_phase[i] > .0);
   296         weights_sum_phase_inv[i] = 1.0 / weights_sum_phase[i];
   300         NUM_PHASES, std::vector<double>(nDirs, .0));
   302     double last_phase_threshold = -1.0;  
   304     for (
unsigned int phase_idx = 0; phase_idx < NUM_PHASES; phase_idx++)
   306         double phase_min = std::numeric_limits<double>::max(), phase_max = .0;
   308         for (
unsigned int i = 0; i < nDirs; i++)
   310             double this_dir_eval = 0;
   313                     options.TOO_CLOSE_OBSTACLE ||  
   315                  phase_scores[phase_idx - 1][i] <
   316                      last_phase_threshold)  
   325                 for (
unsigned int l : options.PHASE_FACTORS[phase_idx])
   327                         options.factorWeights.at(l) *
   328                         std::log(std::max(1e-6, m_dirs_scores(i, l)));
   330                 this_dir_eval *= weights_sum_phase_inv[phase_idx];
   331                 this_dir_eval = std::exp(this_dir_eval);
   333             phase_scores[phase_idx][i] = this_dir_eval;
   340         ASSERT_(options.PHASE_THRESHOLDS.size() == NUM_PHASES);
   342             options.PHASE_THRESHOLDS[phase_idx] > .0 &&
   343             options.PHASE_THRESHOLDS[phase_idx] < 1.0);
   345         last_phase_threshold =
   346             options.PHASE_THRESHOLDS[phase_idx] * phase_max +
   347             (1.0 - options.PHASE_THRESHOLDS[phase_idx]) * phase_min;
   351     auto& dirs_eval = phase_scores.back();
   353     postProcessDirectionEvaluations(dirs_eval, ni, target_idx);
   358         double phase_min = std::numeric_limits<double>::max(), phase_max = .0;
   359         for (
unsigned int i = 0; i < nDirs; i++)
   364         last_phase_threshold =
   365             options.PHASE_THRESHOLDS.back() * phase_max +
   366             (1.0 - options.PHASE_THRESHOLDS.back()) * phase_min;
   370     for (
unsigned int i = 0; i < nDirs; i++)
   372         double& 
val = dirs_eval[i];
   373         if (
val < last_phase_threshold) 
val = .0;
   385     auto log = std::make_shared<CLogFileRecord_FullEval>();
   389     const size_t numTrgs = ni.
targets.size();
   390     std::vector<EvalOutput> evals(numTrgs);
   391     for (
unsigned int trg_idx = 0; trg_idx < numTrgs; trg_idx++)
   393         evalSingleTarget(trg_idx, ni, evals[trg_idx]);
   397     const auto nDirs = evals.front().phase_scores.back().size();
   402     std::vector<double> overall_scores;
   403     overall_scores.assign(nDirs, .0);
   404     for (
const auto& e : evals)
   406         for (
unsigned int i = 0; i < nDirs; i++)
   407             overall_scores[i] += e.phase_scores.back()[i];
   410     for (
unsigned int i = 0; i < nDirs; i++)
   411         overall_scores[i] *= (1.0 / numTrgs);
   417     std::vector<TGap> gaps;
   418     std::size_t best_gap_idx = std::string::npos;
   420         bool inside_gap = 
false;
   421         for (
unsigned int i = 0; i < nDirs; i++)
   423             const double val = overall_scores[i];
   431                     auto& active_gap = *gaps.rbegin();
   432                     active_gap.k_to = i - 1;
   445                     gaps.emplace_back(new_gap);
   452                 auto& active_gap = *gaps.rbegin();
   453                 if (
val >= active_gap.max_eval)
   455                     active_gap.k_best_eval = i;
   460                 if (best_gap_idx == std::string::npos ||
   461                     val > gaps[best_gap_idx].max_eval)
   463                     best_gap_idx = gaps.size() - 1;
   471             auto& active_gap = *gaps.rbegin();
   472             active_gap.k_to = nDirs - 1;
   479     double best_dir_eval = 0;
   484         ASSERT_(best_gap_idx < gaps.size());
   485         const TGap& best_gap = gaps[best_gap_idx];
   486         best_dir_k = best_gap.k_best_eval;
   487         best_dir_eval = overall_scores.at(best_dir_k);
   491     if (best_dir_eval == .0)
   500         const auto ptg = getAssociatedPTG();
   501         const double ptg_ref_dist = ptg ? ptg->getRefDistance() : 1.0;
   504             CParameterizedTrajectoryGenerator::index2alpha(best_dir_k, nDirs);
   508         const double targetNearnessFactor =
   509             m_enableApproachTargetSlowDown
   511                       1.0, ni.
targets.front().norm() /
   512                                (options.TARGET_SLOW_APPROACHING_DISTANCE /
   516         const double obs_dist = ni.
obstacles[best_dir_k];
   518         const double obs_dist_th = std::max(
   519             options.TOO_CLOSE_OBSTACLE,
   521         double riskFactor = 1.0;
   522         if (obs_dist <= options.TOO_CLOSE_OBSTACLE)
   527             obs_dist < obs_dist_th && obs_dist_th > options.TOO_CLOSE_OBSTACLE)
   529             riskFactor = (obs_dist - options.TOO_CLOSE_OBSTACLE) /
   530                          (obs_dist_th - options.TOO_CLOSE_OBSTACLE);
   533             ni.
maxRobotSpeed * std::min(riskFactor, targetNearnessFactor);
   536     m_last_selected_sector = best_dir_k;
   541         log->selectedTarget = 0;  
   542         log->selectedSector = best_dir_k;
   543         log->evaluation = best_dir_eval;
   547         log->dirs_eval = evals.front().phase_scores;
   548         log->dirs_eval.back() = overall_scores;
   550         if (options.LOG_SCORE_MATRIX)
   552             log->dirs_scores = m_dirs_scores;
   557 unsigned int CHolonomicFullEval::direction2sector(
   558     const double a, 
const unsigned int N)
   564         return static_cast<unsigned int>(idx);
   567 CLogFileRecord_FullEval::CLogFileRecord_FullEval() : dirs_scores() {}
   622     : factorWeights{0.1, 0.5, 0.5, 0.01, 1, 1, 1},
   623       factorNormalizeOrNot{0, 0, 0, 0, 1, 0, 0},
   624       PHASE_FACTORS{{1, 2}, {4}, {0, 2}},
   625       PHASE_THRESHOLDS{0.5, 0.6, 0.7}
   645         s, 
"factorWeights", std::vector<double>(), factorWeights, 
true);
   649         s, 
"factorNormalizeOrNot", factorNormalizeOrNot, factorNormalizeOrNot);
   650     ASSERT_EQUAL_(factorNormalizeOrNot.size(), factorWeights.size());
   656     PHASE_FACTORS.resize(PHASE_COUNT);
   657     PHASE_THRESHOLDS.resize(PHASE_COUNT);
   658     for (
int i = 0; i < PHASE_COUNT; i++)
   661             s, 
mrpt::format(
"PHASE%i_FACTORS", i + 1), PHASE_FACTORS[i],
   662             PHASE_FACTORS[i], 
true);
   667         ASSERT_(PHASE_THRESHOLDS[i] >= .0 && PHASE_THRESHOLDS[i] <= 1.0);
   683         "Directions with collision-free distances below this threshold are not "   686         TARGET_SLOW_APPROACHING_DISTANCE,
   687         "Start to reduce speed when closer than this to target.");
   689         OBSTACLE_SLOW_DOWN_DISTANCE,
   690         "Start to reduce speed when clearance is below this value ([0,1] ratio "   691         "wrt obstacle reference/max distance)");
   693         HYSTERESIS_SECTOR_COUNT,
   694         "Range of `sectors` (directions) for hysteresis over successive "   697         LOG_SCORE_MATRIX, 
"Save the entire score matrix in log files");
   699         clearance_threshold_ratio,
   700         "Ratio [0,1], times path_count, gives the minimum number of paths at "   701         "each side of a target direction to be accepted as desired direction");
   703         gap_width_ratio_threshold,
   704         "Ratio [0,1], times path_count, gives the minimum gap width to accept "   705         "a direct motion towards target.");
   711         "[0]=Free space, [1]=Dist. in sectors, [2]=Closer to target "   712         "(Euclidean), [3]=Hysteresis, [4]=clearance along path, [5]=Like [2] "   713         "without decimation if path obstructed");
   715         s, 
"factorNormalizeOrNot",
   717         "Normalize factors or not (1/0)");
   720         s, 
"PHASE_COUNT", PHASE_FACTORS.size(), WN, WV,
   721         "Number of evaluation phases to run (params for each phase below)");
   723     for (
unsigned int i = 0; i < PHASE_FACTORS.size(); i++)
   726             s, 
mrpt::format(
"PHASE%u_THRESHOLD", i + 1), PHASE_THRESHOLDS[i],
   728             "Phase scores must be above this relative range threshold [0,1] to "   729             "be considered in next phase (Default:`0.75`)");
   733             "Indices of the factors above to be considered in this phase");
   810     std::vector<double>& dir_evals, 
const NavInput& ni, 
unsigned int trg_idx)
 double gap_width_ratio_threshold
Ratio [0,1], times path_count, gives the minimum gap width to accept a direct motion towards target...
 
virtual void postProcessDirectionEvaluations(std::vector< double > &dir_evals, const NavInput &ni, unsigned int trg_idx)
If desired, override in a derived class to manipulate the final evaluations of each directions...
 
void keep_min(T &var, const K test_val)
If the second argument is below the first one, set the first argument to this lower value...
 
A class for storing extra information about the execution of CHolonomicFullEval navigation. 
 
double distance(const TPoint2D &point) const
Distance to point. 
 
void serializeFrom(mrpt::serialization::CArchive &in, uint8_t serial_version) override
Pure virtual method for reading (deserializing) from an abstract archive. 
 
TPoint2D_< double > TPoint2D
Lightweight 2D point. 
 
app initialize(argc, argv)
 
double TOO_CLOSE_OBSTACLE
Directions with collision-free distances below this threshold are not elegible. 
 
void saveToConfigFile(mrpt::config::CConfigFileBase &cfg, const std::string §ion) const override
This method saves the options to a ".ini"-like file or memory-stored string list. ...
 
std::string std::string format(std::string_view fmt, ARGS &&... args)
 
int MRPT_SAVE_NAME_PADDING()
Default padding sizes for macros MRPT_SAVE_CONFIG_VAR_COMMENT(), etc. 
 
void serializeTo(mrpt::serialization::CArchive &out) const override
Pure virtual method for writing (serializing) to an abstract archive. 
 
This file implements several operations that operate element-wise on individual or pairs of container...
 
A base class for holonomic reactive navigation methods. 
 
double TARGET_SLOW_APPROACHING_DISTANCE
Start to reduce speed when closer than this to target [m]. 
 
mrpt::nav const unsigned int INVALID_K
 
int32_t selectedTarget
Normally = 0. 
 
std::vector< int32_t > factorNormalizeOrNot
0/1 to normalize factors. 
 
IMPLEMENTS_SERIALIZABLE(CLogFileRecord_FullEval, CHolonomicLogFileRecord, mrpt::nav) IMPLEMENTS_SERIALIZABLE(CHolonomicFullEval
 
uint8_t serializeGetVersion() const override
Must return the current versioning number of the object. 
 
double clearance_threshold_ratio
Ratio [0,1], times path_count, gives the minimum number of paths at each side of a target direction t...
 
std::vector< double > PHASE_THRESHOLDS
Phase 1,2,N-1... 
 
TOptions options
Parameters of the algorithm (can be set manually or loaded from CHolonomicFullEval::initialize or opt...
 
#define MRPT_THROW_UNKNOWN_SERIALIZATION_VERSION(__V)
For use in CSerializable implementations. 
 
double OBSTACLE_SLOW_DOWN_DISTANCE
Start to reduce speed when clearance is below this value ([0,1] ratio wrt obstacle reference/max dist...
 
const unsigned NUM_FACTORS
 
#define ASSERT_(f)
Defines an assertion mechanism. 
 
int32_t selectedSector
Member data. 
 
This class allows loading and storing values and vectors of different types from a configuration text...
 
This base provides a set of functions for maths stuff. 
 
2D segment, consisting of two points. 
 
#define ASSERT_EQUAL_(__A, __B)
Assert comparing two values, reporting their actual values upon failure. 
 
Auxiliary struct that holds all the relevant geometry information about a 2D scan. 
 
A base class for log records for different holonomic navigation methods. 
 
double desiredDirection
The desired motion direction, in the range [-PI, PI]. 
 
mrpt::config::CConfigFileBase CConfigFileBase
 
std::vector< double > factorWeights
See docs above. 
 
int MRPT_SAVE_VALUE_PADDING()
 
#define MRPT_LOAD_CONFIG_VAR_NO_DEFAULT( variableName, variableType, configFileObject, sectionNameStr)
 
CHolonomicLogFileRecord::Ptr logRecord
The navigation method will create a log record and store it here via a smart pointer. 
 
double read_double(const std::string §ion, const std::string &name, double defaultValue, bool failIfNotFound=false) const
 
TPoint2D point2
Destiny point. 
 
void serializeFrom(mrpt::serialization::CArchive &in, uint8_t serial_version) override
Pure virtual method for reading (deserializing) from an abstract archive. 
 
T wrapToPi(T a)
Modifies the given angle to translate it into the ]-pi,pi] range. 
 
Full evaluation of all possible directions within the discrete set of input directions. 
 
void write(const std::string §ion, const std::string &name, enum_t value, const int name_padding_width=-1, const int value_padding_width=-1, const std::string &comment=std::string())
 
std::vector< std::vector< double > > dirs_eval
Final [N-1] and earlier stages [0...N-1] evaluation scores for each direction, in the same order of T...
 
void keep_max(T &var, const K test_val)
If the second argument is above the first one, set the first argument to this higher value...
 
TPoint2D point1
Origin point. 
 
double desiredSpeed
The desired motion speed in that direction, from 0 up to NavInput::maxRobotSpeed. ...
 
return_t square(const num_t x)
Inline function for the square of a number. 
 
#define MRPT_LOAD_CONFIG_VAR( variableName, variableType, configFileObject, sectionNameStr)
An useful macro for loading variables stored in a INI-like file under a key with the same name that t...
 
This is the global namespace for all Mobile Robot Programming Toolkit (MRPT) libraries. 
 
Virtual base class for "archives": classes abstracting I/O streams. 
 
uint8_t serializeGetVersion() const override
Must return the current versioning number of the object. 
 
mrpt::vision::TStereoCalibResults out
 
#define ASSERT_ABOVE_(__A, __B)
 
double HYSTERESIS_SECTOR_COUNT
Range of "sectors" (directions) for hysteresis over successive timesteps. 
 
#define MRPT_SAVE_CONFIG_VAR_COMMENT(variableName, __comment)
 
unsigned int m_last_selected_sector
 
void serializeTo(mrpt::serialization::CArchive &out) const override
Pure virtual method for writing (serializing) to an abstract archive. 
 
bool rightToLeft
Angles storage order: true=counterclockwise; false=clockwise. 
 
Output for CAbstractHolonomicReactiveMethod::navigate() 
 
mrpt::math::CMatrixD dirs_scores
Individual scores for each direction: (i,j), i (row) are directions, j (cols) are scores...
 
std::string sprintf_container(const char *fmt, const T &V)
Generates a string for a container in the format [A,B,C,...], and the fmt string for each vector elem...
 
std::vector< std::vector< double > > phase_scores
 
std::vector< std::vector< int32_t > > PHASE_FACTORS
Factor indices [0,4] for the factors to consider in each phase 1,2,...N of the movement decision (Def...
 
T abs_diff(const T a, const T b)
Efficient and portable evaluation of the absolute difference of two unsigned integer values (but will...
 
void read_vector(const std::string §ion, const std::string &name, const VECTOR_TYPE &defaultValue, VECTOR_TYPE &outValues, bool failIfNotFound=false) const
Reads a configuration parameter of type vector, stored in the file as a string: "[v1 v2 v3 ...
 
CONTAINER::Scalar norm(const CONTAINER &v)
 
void loadFromConfigFile(const mrpt::config::CConfigFileBase &source, const std::string §ion) override
This method load the options from a ".ini"-like file or memory-stored string list. 
 
int round(const T value)
Returns the closer integer (int) to x.