32 m_last_selected_sector(
std::numeric_limits<
unsigned int>::max())
34 if (INI_FILE !=
nullptr)
initialize(*INI_FILE);
39 options.loadFromConfigFile(INI_FILE, getConfigFileSectionName());
43 options.saveToConfigFile(c, getConfigFileSectionName());
51 const auto ptg = getAssociatedPTG();
52 const double ptg_ref_dist = ptg ? ptg->getRefDistance() : 1.0;
56 unsigned int selectedSector;
57 double riskEvaluation;
67 const auto trg = *ni.
targets.rbegin();
73 ni.
obstacles, 1.0 , gaps, trg, selectedSector,
74 evaluation, situation, riskEvaluation, *log);
76 if (situation == SITUATION_NO_WAY_FOUND)
90 const double targetNearnessFactor =
91 m_enableApproachTargetSlowDown
94 trg.norm() / (options.TARGET_SLOW_APPROACHING_DISTANCE /
98 const double riskFactor =
99 std::min(1.0, riskEvaluation / options.RISK_EVALUATION_DISTANCE);
101 ni.
maxRobotSpeed * std::min(riskFactor, targetNearnessFactor);
104 m_last_selected_sector = selectedSector;
111 int i, n = gaps.size();
112 log->gaps_ini.resize(n);
113 log->gaps_end.resize(n);
114 for (i = 0; i < n; i++)
116 log->gaps_ini[i] = gaps[i].ini;
117 log->gaps_end[i] = gaps[i].end;
121 log->selectedSector = selectedSector;
122 log->evaluation = evaluation;
123 log->situation = situation;
124 log->riskEvaluation = riskEvaluation;
131 void CHolonomicND::gapsEstimator(
135 const size_t n = obstacles.size();
139 const int GAPS_MIN_WIDTH = ceil(n * 0.01);
140 const double GAPS_MIN_DEPTH_CONSIDERED = 0.6;
141 const double GAPS_MAX_RELATIVE_DEPTH = 0.5;
146 double overall_max_dist = std::numeric_limits<float>::min(),
147 overall_min_dist = std::numeric_limits<float>::max();
148 for (
size_t i = 1; i < (n - 1); i++)
153 double max_depth = overall_max_dist - overall_min_dist;
158 gaps_temp.reserve(150);
160 for (
double threshold_ratio = 0.95; threshold_ratio >= 0.05;
161 threshold_ratio -= 0.05)
163 const double dist_threshold =
164 threshold_ratio * overall_max_dist +
165 (1.0f - threshold_ratio) *
166 min(target.
norm(), GAPS_MIN_DEPTH_CONSIDERED);
168 bool is_inside =
false;
169 size_t sec_ini = 0, sec_end = 0;
172 for (
size_t i = 0; i < n; i++)
174 if (!is_inside && (obstacles[i] >= dist_threshold))
177 maxDist = obstacles[i];
182 (i == (n - 1) || obstacles[i] < dist_threshold))
184 if (obstacles[i] < dist_threshold)
191 if ((sec_end - sec_ini) >= (size_t)GAPS_MIN_WIDTH)
194 gaps_temp.resize(gaps_temp.size() + 1);
195 TGap& newGap = *gaps_temp.rbegin();
197 newGap.
ini = sec_ini;
198 newGap.
end = sec_end;
200 min(obstacles[sec_ini], obstacles[sec_end]);
205 if (is_inside) maxDist = std::max(maxDist, obstacles[i]);
212 const size_t nTempGaps = gaps_temp.size();
214 std::vector<bool> delete_gaps;
215 delete_gaps.assign(nTempGaps,
false);
218 for (
size_t i = 0; i < nTempGaps; i++)
220 if (delete_gaps[i] == 1)
continue;
222 for (
size_t j = i + 1; j < nTempGaps; j++)
224 if (gaps_temp[i].ini == gaps_temp[j].ini ||
225 gaps_temp[i].
end == gaps_temp[j].
end)
226 delete_gaps[j] =
true;
231 for (
size_t i = 0; i < nTempGaps; i++)
233 if (delete_gaps[i] == 1)
continue;
235 if ((gaps_temp[i].maxDistance - gaps_temp[i].minDistance) >
236 max_depth * GAPS_MAX_RELATIVE_DEPTH)
237 delete_gaps[i] =
true;
241 for (
size_t i = 0; i < nTempGaps; i++)
243 if (delete_gaps[i])
continue;
245 unsigned int inner_gap_count = 0;
247 for (
unsigned int j = 0; j < nTempGaps; j++)
249 if (i == j || delete_gaps[j])
continue;
252 if (gaps_temp[j].ini >= gaps_temp[i].ini &&
253 gaps_temp[j].
end <= gaps_temp[i].
end)
254 if (++inner_gap_count > 1)
256 delete_gaps[i] =
true;
263 for (
size_t i = 0; i < nTempGaps; i++)
265 if (delete_gaps[i])
continue;
267 for (
unsigned int j = 0; j < nTempGaps; j++)
269 if (i == j || delete_gaps[j])
continue;
270 if (gaps_temp[i].ini <= gaps_temp[j].ini &&
271 gaps_temp[i].
end >= gaps_temp[j].
end)
272 delete_gaps[j] =
true;
279 gaps_out.reserve(nTempGaps / 2);
280 for (
size_t i = 0; i < nTempGaps; i++)
282 if (delete_gaps[i])
continue;
285 calcRepresentativeSectorForGap(gaps_temp[i], target, obstacles);
287 gaps_out.push_back(gaps_temp[i]);
294 void CHolonomicND::searchBestGap(
295 const std::vector<double>& obstacles,
const double maxObsRange,
297 unsigned int& out_selDirection,
double& out_selEvaluation,
298 TSituations& out_situation,
double& out_riskEvaluation,
302 unsigned int min_risk_eval_sector = 0;
303 unsigned int max_risk_eval_sector = obstacles.size() - 1;
304 const unsigned int target_sector =
305 direction2sector(atan2(target.
y, target.
x), obstacles.size());
306 const double target_dist = std::max(0.01, target.
norm());
311 const int freeSectorsNearTarget = ceil(0.02 * obstacles.size());
312 bool theyAreFree =
true, caseD1 =
false;
313 if (target_sector > static_cast<unsigned int>(freeSectorsNearTarget) &&
315 static_cast<unsigned int>(obstacles.size() - freeSectorsNearTarget))
317 const double min_free_dist =
318 std::min(1.05 * target_dist, 0.95 * maxObsRange);
319 for (
int j = -freeSectorsNearTarget;
320 theyAreFree && j <= freeSectorsNearTarget; j++)
321 if (obstacles[(
int(target_sector) + j) % obstacles.size()] <
324 caseD1 = theyAreFree;
330 out_selDirection = target_sector;
334 1.0 + std::max(0.0, (maxObsRange - target_dist) / maxObsRange);
335 out_situation = SITUATION_TARGET_DIRECTLY;
340 std::vector<double> gaps_evaluation;
341 int selected_gap = -1;
342 double selected_gap_eval = -100;
345 obstacles, maxObsRange, in_gaps, target_sector, target_dist,
374 if (selected_gap == -1)
375 for (
unsigned int i = 0; i < in_gaps.size(); i++)
376 if (gaps_evaluation[i] > selected_gap_eval)
378 selected_gap_eval = gaps_evaluation[i];
384 if (selected_gap_eval <= 0)
388 out_selDirection = 0;
389 out_selEvaluation = 0.0;
390 out_situation = SITUATION_NO_WAY_FOUND;
395 const TGap& gap = in_gaps[selected_gap];
397 const unsigned int sectors_to_be_wide =
398 round(options.WIDE_GAP_SIZE_PERCENT * obstacles.size());
400 out_selDirection = in_gaps[selected_gap].representative_sector;
401 out_selEvaluation = selected_gap_eval;
405 if ((gap.
end - gap.
ini) < sectors_to_be_wide)
409 out_situation = SITUATION_SMALL_GAP;
415 out_situation = SITUATION_WIDE_GAP;
419 min_risk_eval_sector = gap.
ini;
420 max_risk_eval_sector = gap.
end;
426 const unsigned int risk_eval_nsectors =
427 round(options.RISK_EVALUATION_SECTORS_PERCENT * obstacles.size());
428 const unsigned int sec_ini = std::max(
429 min_risk_eval_sector, risk_eval_nsectors < out_selDirection
430 ? out_selDirection - risk_eval_nsectors
432 const unsigned int sec_fin =
433 std::min(max_risk_eval_sector, out_selDirection + risk_eval_nsectors);
435 out_riskEvaluation = 0.0;
436 for (
unsigned int i = sec_ini; i <= sec_fin; i++)
437 out_riskEvaluation += obstacles[i];
438 out_riskEvaluation /= (sec_fin - sec_ini + 1);
445 void CHolonomicND::calcRepresentativeSectorForGap(
447 const std::vector<double>& obstacles)
450 const unsigned int sectors_to_be_wide =
451 round(options.WIDE_GAP_SIZE_PERCENT * obstacles.size());
452 const unsigned int target_sector =
453 direction2sector(atan2(target.
y, target.
x), obstacles.size());
461 float min_dist_obs_near_ini = 1, min_dist_obs_near_end = 1;
463 for (i = gap.
ini; i >= max(0, gap.
ini - 2); i--)
464 min_dist_obs_near_ini = min(min_dist_obs_near_ini, obstacles[i]);
465 for (i = gap.
end; i <= min((
int)obstacles.size() - 1, gap.
end + 2); i++)
466 min_dist_obs_near_end = min(min_dist_obs_near_end, obstacles[i]);
468 (min_dist_obs_near_ini * gap.
ini +
469 min_dist_obs_near_end * gap.
end) /
470 (min_dist_obs_near_ini + min_dist_obs_near_end));
479 if (dist_ini > 0.5 * obstacles.size())
480 dist_ini = obstacles.size() - dist_ini;
481 if (dist_end > 0.5 * obstacles.size())
482 dist_end = obstacles.size() - dist_end;
485 if (dist_ini < dist_end)
496 sector = sector +
dir *
static_cast<int>(sectors_to_be_wide) / 2;
500 keep_min(sector, static_cast<int>(obstacles.size()) - 1);
508 void CHolonomicND::evaluateGaps(
509 const std::vector<double>& obstacles,
const double maxObsRange,
510 const TGapArray& gaps,
const unsigned int target_sector,
511 const float target_dist, std::vector<double>& out_gaps_evaluation)
513 out_gaps_evaluation.resize(gaps.size());
515 const double targetAng = CParameterizedTrajectoryGenerator::index2alpha(
516 target_sector, obstacles.size());
517 const double target_x = target_dist * cos(targetAng);
518 const double target_y = target_dist * sin(targetAng);
520 for (
unsigned int i = 0; i < gaps.size(); i++)
523 const TGap* gap = &gaps[i];
525 const float d =
min3(
530 const double phi = CParameterizedTrajectoryGenerator::index2alpha(
532 const double x = d * cos(phi);
533 const double y = d * sin(phi);
538 float meanDist = 0.f;
539 for (
unsigned int j = gap->
ini; j <= gap->
end; j++)
540 meanDist += obstacles[j];
541 meanDist /= (gap->
end - gap->
ini + 1);
546 factor1 = std::min(target_dist, meanDist) / target_dist;
556 if (dif > 0.5 * obstacles.size()) dif = obstacles.size() - dif;
558 const double factor2 = exp(-
square(dif / (obstacles.size() * 0.25)));
563 double closestX, closestY;
570 const float factor3 =
571 (maxObsRange - std::min(maxObsRange, dist_eucl)) / maxObsRange;
576 double factor_AntiCab;
578 if (m_last_selected_sector != std::numeric_limits<unsigned int>::max())
583 if (dist >
unsigned(0.1 * obstacles.size()))
584 factor_AntiCab = 0.0;
586 factor_AntiCab = 1.0;
593 ASSERT_(options.factorWeights.size() == 4);
596 options.TOO_CLOSE_OBSTACLE)
597 out_gaps_evaluation[i] = 0;
599 out_gaps_evaluation[i] =
600 (options.factorWeights[0] * factor1 +
601 options.factorWeights[1] * factor2 +
602 options.factorWeights[2] * factor3 +
603 options.factorWeights[3] * factor_AntiCab) /
608 unsigned int CHolonomicND::direction2sector(
609 const double a,
const unsigned int N)
615 return static_cast<unsigned int>(idx);
618 uint8_t CLogFileRecord_ND::serializeGetVersion()
const {
return 1; }
621 out << gaps_ini << gaps_end << gaps_eval;
622 out << selectedSector << evaluation << riskEvaluation
623 << (uint32_t)situation;
626 void CLogFileRecord_ND::serializeFrom(
638 in.
ReadBuffer(&(*gaps_ini.begin()),
sizeof(gaps_ini[0]) * n);
639 in.
ReadBuffer(&(*gaps_end.begin()),
sizeof(gaps_end[0]) * n);
643 in.
ReadBuffer(&(*gaps_eval.begin()),
sizeof(gaps_eval[0]) * n);
645 in >> selectedSector >> evaluation >> riskEvaluation >> n;
653 in >> gaps_ini >> gaps_end >> gaps_eval;
654 in >> selectedSector >> evaluation >> riskEvaluation >> n;
666 CHolonomicND::TOptions::TOptions() : factorWeights{1.0, 0.5, 2.0, 0.4} {}
675 MAX_SECTOR_DIST_FOR_D2_PERCENT,
double, source, section);
677 RISK_EVALUATION_SECTORS_PERCENT,
double, source, section);
681 TARGET_SLOW_APPROACHING_DISTANCE,
double, source, section);
684 section,
"factorWeights", std::vector<double>(), factorWeights,
true);
685 ASSERT_(factorWeights.size() == 4);
701 RISK_EVALUATION_DISTANCE,
"In normalized ps-meters [0,1]");
704 TARGET_SLOW_APPROACHING_DISTANCE,
"In normalized ps-meters");
710 "%.2f %.2f %.2f %.2f", factorWeights[0], factorWeights[1],
711 factorWeights[2], factorWeights[3]),
713 "[0]=Free space, [1]=Dist. in sectors, [2]=Closer to target " 714 "(Euclidean), [3]=Hysteresis");
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...
app initialize(argc, argv)
std::string std::string format(std::string_view fmt, ARGS &&... args)
An implementation of the holonomic reactive navigation method "Nearness-Diagram". ...
unsigned int m_last_selected_sector
int MRPT_SAVE_NAME_PADDING()
Default padding sizes for macros MRPT_SAVE_CONFIG_VAR_COMMENT(), etc.
double RISK_EVALUATION_DISTANCE
This file implements several operations that operate element-wise on individual or pairs of container...
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. ...
A base class for holonomic reactive navigation methods.
double WIDE_GAP_SIZE_PERCENT
const T min3(const T &A, const T &B, const T &C)
std::vector< TGap > TGapArray
TOptions options
Parameters of the algorithm (can be set manually or loaded from CHolonomicND::initialize or options...
#define MRPT_THROW_UNKNOWN_SERIALIZATION_VERSION(__V)
For use in CSerializable implementations.
unsigned int representative_sector
#define ASSERT_(f)
Defines an assertion mechanism.
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.
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.
#define ASSERT_EQUAL_(__A, __B)
Assert comparing two values, reporting their actual values upon failure.
A base class for log records for different holonomic navigation methods.
double desiredDirection
The desired motion direction, in the range [-PI, PI].
CONTAINER::Scalar sum(const CONTAINER &v)
Computes the sum of all the elements.
mrpt::config::CConfigFileBase CConfigFileBase
std::vector< double > factorWeights
Vector of 4 weights: [0]=Free space, [1]=Dist.
int MRPT_SAVE_VALUE_PADDING()
CHolonomicLogFileRecord::Ptr logRecord
The navigation method will create a log record and store it here via a smart pointer.
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.
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())
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...
double desiredSpeed
The desired motion speed in that direction, from 0 up to NavInput::maxRobotSpeed. ...
double RISK_EVALUATION_SECTORS_PERCENT
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...
double MAX_SECTOR_DIST_FOR_D2_PERCENT
const_iterator end() const
This is the global namespace for all Mobile Robot Programming Toolkit (MRPT) libraries.
Virtual base class for "archives": classes abstracting I/O streams.
The structure used to store a detected gap in obstacles.
mrpt::vision::TStereoCalibResults out
double TARGET_SLOW_APPROACHING_DISTANCE
A class for storing extra information about the execution of CHolonomicND navigation.
size_t ReadBuffer(void *Buffer, size_t Count)
Reads a block of bytes from the stream into Buffer.
T norm() const
Point norm: |v| = sqrt(x^2+y^2)
#define MRPT_SAVE_CONFIG_VAR_COMMENT(variableName, __comment)
uint8_t serializeGetVersion() const override
Must return the current versioning number of the object.
IMPLEMENTS_SERIALIZABLE(CHolonomicND, CAbstractHolonomicReactiveMethod, mrpt::nav) CHolonomicND
Initialize the parameters of the navigator, from some configuration file, or default values if filena...
void serializeTo(mrpt::serialization::CArchive &out) const override
Pure virtual method for writing (serializing) to an abstract archive.
std::vector< double > gaps_eval
double minimumDistanceFromPointToSegment(const double Px, const double Py, const double x1, const double y1, const double x2, const double y2, T &out_x, T &out_y)
Computes the closest point from a given point to a segment, and returns that minimum distance...
Output for CAbstractHolonomicReactiveMethod::navigate()
double TOO_CLOSE_OBSTACLE
TSituations
The set of posible situations for each trajectory.
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 ...
int round(const T value)
Returns the closer integer (int) to x.