/*--------------------------------------------------------------- Navigate ---------------------------------------------------------------*/ void CHolonomicND::navigate( const mrpt::math::TPoint2D &target, const std::vector<double> &obstacles, double maxRobotSpeed, double &desiredDirection, double &desiredSpeed, CHolonomicLogFileRecordPtr &logRecord, const double max_obstacle_dist) { TGapArray gaps; TSituations situation; unsigned int selectedSector; double riskEvaluation; CLogFileRecord_NDPtr log; double evaluation; // Create a log record for returning data. if (!logRecord.present()) { log = CLogFileRecord_ND::Create(); logRecord = log; } // Search gaps: gaps.clear(); gapsEstimator( obstacles, target, gaps); // Select best gap: searchBestGap( obstacles, 1.0, gaps, target, selectedSector, evaluation, situation, riskEvaluation, log); if (situation == SITUATION_NO_WAY_FOUND) { // No way found! desiredDirection = 0; desiredSpeed = 0; } else { // A valid movement: desiredDirection = (double)(M_PI*(-1 + 2*(0.5f+selectedSector)/((double)obstacles.size()))); // Speed control: Reduction factors // --------------------------------------------- const double targetNearnessFactor = std::min( 1.0, target.norm()/(options.TARGET_SLOW_APPROACHING_DISTANCE)); const double riskFactor = std::min(1.0, riskEvaluation / options.RISK_EVALUATION_DISTANCE ); desiredSpeed = maxRobotSpeed * std::min(riskFactor,targetNearnessFactor); } m_last_selected_sector = selectedSector; // LOG -------------------------- if (log) { // gaps: { int i,n = gaps.size(); log->gaps_ini.resize(n); log->gaps_end.resize(n); for (i=0;i<n;i++) { log->gaps_ini[i] = gaps[i].ini; log->gaps_end[i] = gaps[i].end; } } // Selection: log->selectedSector = selectedSector; log->evaluation = evaluation; log->situation = situation; log->riskEvaluation = riskEvaluation; } }
/*--------------------------------------------------------------- Evaluate each gap ---------------------------------------------------------------*/ void CHolonomicND::evaluateGaps( const std::vector<double> &obstacles, const double maxObsRange, const TGapArray &gaps, const unsigned int target_sector, const float target_dist, std::vector<double> &out_gaps_evaluation ) { out_gaps_evaluation.resize( gaps.size()); double targetAng = M_PI*(-1 + 2*(0.5+target_sector)/double(obstacles.size())); double target_x = target_dist*cos(targetAng); double target_y = target_dist*sin(targetAng); for (unsigned int i=0;i<gaps.size();i++) { // Short cut: const TGap *gap = &gaps[i]; const float d = min3( obstacles[ gap->representative_sector ], maxObsRange, 0.95*target_dist ); // The TP-Space representative coordinates for this gap: const double phi = M_PI*(-1 + 2*(0.5+gap->representative_sector)/double(obstacles.size())); const double x = d*cos(phi); const double y = d*sin(phi); // Factor #1: Maximum reachable distance with this PTG: // ----------------------------------------------------- // It computes the average free distance of the gap: float meanDist = 0.f; for (unsigned int j=gap->ini;j<=gap->end;j++) meanDist+= obstacles[j]; meanDist/= ( gap->end - gap->ini + 1); double factor1; if (mrpt::utils::abs_diff(gap->representative_sector,target_sector)<=1 && target_dist<1) factor1 = std::min(target_dist,meanDist) / target_dist; else factor1 = meanDist; // Factor #2: Distance to target in "sectors" // ------------------------------------------- unsigned int dif = mrpt::utils::abs_diff(target_sector, gap->representative_sector ); // Handle the -PI,PI circular topology: if (dif> 0.5*obstacles.size()) dif = obstacles.size() - dif; const double factor2= exp(-square( dif / (obstacles.size()*0.25))) ; // Factor #3: Punish paths that take us far away wrt the target: **** I don't understand it ********* // ----------------------------------------------------- double closestX,closestY; double dist_eucl = math::minimumDistanceFromPointToSegment( target_x, target_y, // Point 0,0, x,y, // Segment closestX,closestY // Out ); const float factor3 = ( maxObsRange - std::min(maxObsRange ,dist_eucl) ) / maxObsRange; // Factor #4: Stabilizing factor (hysteresis) to avoid quick switch among very similar paths: // ------------------------------------------------------------------------------------------ double factor_AntiCab; if (m_last_selected_sector != std::numeric_limits<unsigned int>::max() ) { unsigned int dist = mrpt::utils::abs_diff(m_last_selected_sector, gap->representative_sector); if (dist > unsigned(0.1*obstacles.size())) factor_AntiCab = 0.0; else factor_AntiCab = 1.0; } else { factor_AntiCab = 0; } ASSERT_(options.factorWeights.size()==4); if ( obstacles[gap->representative_sector] < options.TOO_CLOSE_OBSTACLE ) // Too close to obstacles out_gaps_evaluation[i] = 0; else out_gaps_evaluation[i] = ( options.factorWeights[0] * factor1 + options.factorWeights[1] * factor2 + options.factorWeights[2] * factor3 + options.factorWeights[3] * factor_AntiCab ) / (math::sum(options.factorWeights)) ; } // for each gap }
/*--------------------------------------------------------------- Find gaps in the obtacles (Beta version) ---------------------------------------------------------------*/ void CHolonomicND::gapsEstimator( const std::vector<double> & obstacles, const mrpt::math::TPoint2D & target, TGapArray & gaps_out) { const size_t n = obstacles.size(); ASSERT_(n>2); // ================ Parameters ================ const int GAPS_MIN_WIDTH = ceil(n*0.01); // was: 3 const double GAPS_MIN_DEPTH_CONSIDERED = 0.6; const double GAPS_MAX_RELATIVE_DEPTH = 0.5; // ============================================ // Find the maximum distances to obstacles: // ---------------------------------------------------------- float overall_max_dist = std::numeric_limits<float>::min(), overall_min_dist = std::numeric_limits<float>::max(); for (size_t i=1;i<(n-1);i++) { mrpt::utils::keep_max(overall_max_dist, obstacles[i]); mrpt::utils::keep_min(overall_min_dist, obstacles[i]); } double max_depth = overall_max_dist - overall_min_dist; // Build list of "GAPS": // -------------------------------------------------------- TGapArray gaps_temp; gaps_temp.reserve( 150 ); for (double threshold_ratio = 0.95;threshold_ratio>=0.05;threshold_ratio-=0.05) { const double dist_threshold = threshold_ratio* overall_max_dist + (1.0f-threshold_ratio)*min(target.norm(), GAPS_MIN_DEPTH_CONSIDERED); bool is_inside = false; size_t sec_ini=0, sec_end=0; double maxDist=0.; for (size_t i=0;i<n;i++) { if ( !is_inside && ( obstacles[i]>=dist_threshold) ) //A gap begins { sec_ini = i; maxDist = obstacles[i]; is_inside = true; } else if (is_inside && (i==(n-1) || obstacles[i]<dist_threshold )) //A gap ends { if (obstacles[i]<dist_threshold) sec_end = i-1; else sec_end = i; is_inside = false; if ( (sec_end-sec_ini) >= (size_t)GAPS_MIN_WIDTH ) { // Add new gap: gaps_temp.resize( gaps_temp.size() + 1 ); TGap & newGap = *gaps_temp.rbegin(); newGap.ini = sec_ini; newGap.end = sec_end; newGap.minDistance = min( obstacles[sec_ini], obstacles[sec_end] ); newGap.maxDistance = maxDist; } } if (is_inside) maxDist = std::max( maxDist, obstacles[i] ); } } //Start to filter the gap list //-------------------------------------------------------------- const size_t nTempGaps = gaps_temp.size(); std::vector<bool> delete_gaps; delete_gaps.assign( nTempGaps, false); // First, remove redundant gaps for (size_t i=0;i<nTempGaps;i++) { if (delete_gaps[i] == 1) continue; for (size_t j=i+1;j<nTempGaps;j++) { if (gaps_temp[i].ini == gaps_temp[j].ini || gaps_temp[i].end == gaps_temp[j].end) delete_gaps[j] = 1; } } // Remove gaps with a big depth for (size_t i=0;i<nTempGaps;i++) { if (delete_gaps[i] == 1) continue; if ((gaps_temp[i].maxDistance - gaps_temp[i].minDistance) > max_depth*GAPS_MAX_RELATIVE_DEPTH) delete_gaps[i] = 1; } //Delete gaps which contain more than one other gaps for (size_t i=0;i<nTempGaps;i++) { if (delete_gaps[i]) continue; unsigned int inner_gap_count = 0; for (unsigned int j=0;j<nTempGaps;j++) { if (i==j || delete_gaps[j]) continue; // j is inside of i? if (gaps_temp[j].ini >= gaps_temp[i].ini && gaps_temp[j].end <= gaps_temp[i].end ) if (++inner_gap_count>1) { delete_gaps[i] = 1; break; } } } //Delete gaps included in other gaps for (size_t i=0;i<nTempGaps;i++) { if (delete_gaps[i]) continue; for (unsigned int j=0;j<nTempGaps;j++) { if (i==j || delete_gaps[j]) continue; if (gaps_temp[i].ini <= gaps_temp[j].ini && gaps_temp[i].end >= gaps_temp[j].end) delete_gaps[j] = 1; } } // Copy as result only those gaps not marked for deletion: // -------------------------------------------------------- gaps_out.clear(); gaps_out.reserve( nTempGaps/2 ); for (size_t i=0;i<nTempGaps;i++) { if (delete_gaps[i]) continue; // Compute the representative direction ("sector") for this gap: calcRepresentativeSectorForGap( gaps_temp[i], target, obstacles); gaps_out.push_back( gaps_temp[i] ); } }
/*--------------------------------------------------------------- Search the best gap. ---------------------------------------------------------------*/ void CHolonomicND::searchBestGap( const std::vector<double> & obstacles, const double maxObsRange, const TGapArray & in_gaps, const mrpt::math::TPoint2D & target, unsigned int & out_selDirection, double & out_selEvaluation, TSituations & out_situation, double & out_riskEvaluation, CLogFileRecord_NDPtr log) { // For evaluating the "risk": unsigned int min_risk_eval_sector = 0; unsigned int max_risk_eval_sector = obstacles.size()-1; const unsigned int target_sector = direction2sector(atan2(target.y,target.x),obstacles.size()); const double target_dist = std::max(0.01,target.norm()); // (Risk is evaluated at the end, for all the situations) // D1 : Straight path? // -------------------------------------------------------- const int freeSectorsNearTarget = ceil(0.02*obstacles.size()); bool theyAreFree = true, caseD1 = false; if (target_sector>static_cast<unsigned int>(freeSectorsNearTarget) && target_sector<static_cast<unsigned int>(obstacles.size()-freeSectorsNearTarget) ) { const double min_free_dist = std::min(1.05*target_dist, 0.95*maxObsRange); for (int j=-freeSectorsNearTarget;theyAreFree && j<=freeSectorsNearTarget;j++) if (obstacles[ (int(target_sector) + j) % obstacles.size()]<min_free_dist) theyAreFree = false; caseD1 = theyAreFree; } if (caseD1) { // S1: Move straight towards target: out_selDirection = target_sector; // In case of several paths, the shortest: out_selEvaluation = 1.0 + std::max( 0.0, (maxObsRange - target_dist) / maxObsRange ); out_situation = SITUATION_TARGET_DIRECTLY; } else { // Evaluate all gaps (if any): std::vector<double> gaps_evaluation; int selected_gap =-1; double selected_gap_eval = -100; evaluateGaps( obstacles, maxObsRange, in_gaps, target_sector, target_dist, gaps_evaluation ); if (log) log->gaps_eval = gaps_evaluation; // D2: is there any gap "beyond" the target (and not too far away)? (Not used) // ---------------------------------------------------------------- //unsigned int dist; //for ( unsigned int i=0;i<in_gaps.size();i++ ) //{ // dist = mrpt::utils::abs_diff(target_sector, in_gaps[i].representative_sector ); // if (dist > 0.5*obstacles.size()) // dist = obstacles.size() - dist; // // if ( in_gaps[i].maxDistance >= target_dist && dist <= (int)floor(options.MAX_SECTOR_DIST_FOR_D2_PERCENT * obstacles.size()) ) // // if ( gaps_evaluation[i]>selected_gap_eval ) // { // selected_gap_eval = gaps_evaluation[i]; // selected_gap = i; // } //} // Keep the best gaps (if none was picked up to this point) if ( selected_gap==-1 ) for ( unsigned int i=0;i<in_gaps.size();i++ ) if ( gaps_evaluation[i]>selected_gap_eval ) { selected_gap_eval = gaps_evaluation[i]; selected_gap = i; } // D3: Wasn't a good enough gap (or there were none)? // ---------------------------------------------------------- if ( selected_gap_eval <= 0 ) { // S2: No way found // ------------------------------------------------------ out_selDirection = 0; out_selEvaluation = 0.0; // Worst case out_situation = SITUATION_NO_WAY_FOUND; } else { // The selected gap: const TGap & gap = in_gaps[selected_gap]; const unsigned int sectors_to_be_wide = round( options.WIDE_GAP_SIZE_PERCENT * obstacles.size() ); out_selDirection = in_gaps[selected_gap].representative_sector; out_selEvaluation = selected_gap_eval; // D4: Is it a WIDE gap? // ----------------------------------------------------- if ( (gap.end-gap.ini) < sectors_to_be_wide ) { // S3: Narrow gap // ------------------------------------------- out_situation = SITUATION_SMALL_GAP; } else { // S4: Wide gap // ------------------------------------------- out_situation = SITUATION_WIDE_GAP; } // Evaluate the risk only within the gap: min_risk_eval_sector = gap.ini; max_risk_eval_sector = gap.end; } } // Evaluate short-term minimum distance to obstacles, in a small interval around the selected direction: const unsigned int risk_eval_nsectors = round( options.RISK_EVALUATION_SECTORS_PERCENT * obstacles.size() ); const unsigned int sec_ini = std::max(min_risk_eval_sector, risk_eval_nsectors<out_selDirection ? out_selDirection-risk_eval_nsectors : 0 ); const unsigned int sec_fin = std::min(max_risk_eval_sector, out_selDirection + risk_eval_nsectors ); out_riskEvaluation = 0.0; for (unsigned int i=sec_ini;i<=sec_fin;i++) out_riskEvaluation+= obstacles[ i ]; out_riskEvaluation /= (sec_fin - sec_ini + 1 ); }
/*--------------------------------------------------------------- Navigate ---------------------------------------------------------------*/ void CHolonomicND::navigate( poses::CPoint2D &target, vector_double &obstacles, double maxRobotSpeed, double &desiredDirection, double &desiredSpeed, CHolonomicLogFileRecordPtr &logRecord) { TGapArray gaps; TSituations situation; int selectedSector; double riskEvaluation; CLogFileRecord_NDPtr log; double evaluation; // Create a log record for returning data. if (!logRecord.present()) { log = CLogFileRecord_ND::Create(); logRecord = log; } // Search gaps: gaps.clear(); gapsEstimator( obstacles, target, gaps ); // Select best gap: searchBestGap( obstacles, 1.0f, gaps, target, selectedSector, evaluation, situation, riskEvaluation, log); if (situation == SITUATION_NO_WAY_FOUND) { // No way found! desiredDirection = 0; desiredSpeed = 0; } else { // A valid movement: desiredDirection = (double)(M_PI*(-1 + 2*(0.5f+selectedSector)/((double)obstacles.size()))); // Speed control: Reduction factors // --------------------------------------------- double targetNearnessFactor = max(0.20, min(1.0, 1.0-exp(-(target.norm()+0.01)/TARGET_SLOW_APPROACHING_DISTANCE))); //printf(" TARGET NEARNESS = %f\n",targetNearnessFactor); double riskFactor = min(1.0, riskEvaluation / RISK_EVALUATION_DISTANCE ); desiredSpeed = maxRobotSpeed * min(riskFactor,targetNearnessFactor); } last_selected_sector = selectedSector; // LOG -------------------------- if (log) { // gaps: if (situation != SITUATION_TARGET_DIRECTLY ) { int i,n = gaps.size(); log->gaps_ini.resize(n); log->gaps_end.resize(n); for (i=0;i<n;i++) { log->gaps_ini[i] = gaps[i].ini; log->gaps_end[i] = gaps[i].end; } } // Selection: log->selectedSector = selectedSector; log->evaluation = evaluation; log->situation = situation; log->riskEvaluation = riskEvaluation; } }
/*--------------------------------------------------------------- Evaluate each gap ---------------------------------------------------------------*/ void CHolonomicND::evaluateGaps( const vector_double &obstacles, const double maxObsRange, const TGapArray &gaps, const int TargetSector, const double TargetDist, vector_double &out_gaps_evaluation ) { out_gaps_evaluation.resize( gaps.size()); double targetAng = M_PI*(-1 + 2*(0.5+TargetSector)/double(obstacles.size())); double target_x = TargetDist*cos(targetAng); double target_y = TargetDist*sin(targetAng); for (unsigned int i=0;i<gaps.size();i++) { // Para referenciarlo mas facilmente: const TGap *gap = &gaps[i]; double d; d = min( obstacles[ gap->representative_sector ], min( maxObsRange, 0.95f*TargetDist) ); // Las coordenadas (en el TP-Space) representativas del gap: double phi = M_PI*(-1 + 2*(0.5+gap->representative_sector)/double(obstacles.size())); double x = d*cos(phi); double y = d*sin(phi); // Factor 1: Distancia hasta donde llego por esta GPT: // ----------------------------------------------------- double factor1; /* if (gap->representative_sector == TargetSector ) factor1 = min(TargetDist,obstacles[gap->representative_sector]) / TargetDist; else { if (TargetDist>1) factor1 = obstacles[gap->representative_sector] / TargetDist; else factor1 = obstacles[gap->representative_sector]; } */ // Calcular la distancia media a donde llego por este gap: double meanDist = 0; for (int j=gap->ini;j<=gap->end;j++) meanDist+= obstacles[j]; meanDist/= ( gap->end - gap->ini + 1); if (abs(gap->representative_sector-TargetSector)<=1 && TargetDist<1) factor1 = min(TargetDist,meanDist) / TargetDist; else factor1 = meanDist; // Factor 2: Distancia en sectores: // ------------------------------------------- double dif = fabs(((double)( TargetSector - gap->representative_sector ))); // if (dif> (0.5f*obstacles.size()) ) dif = obstacles.size() - dif; // Solo si NO estan el target y el gap atravesando el alfa = "-pi" o "pi" if (dif> (0.5f*obstacles.size()) && (TargetSector-0.5f*obstacles.size())*(gap->representative_sector-0.5f*obstacles.size())<0 ) dif = obstacles.size() - dif; double factor2= exp(-square( dif / (obstacles.size()/4))) ; // Factor3: Para evitar cabeceos entre 2 o mas caminos que sean casi iguales: // ------------------------------------------- double dist = (double)(abs(last_selected_sector - gap->representative_sector)); // if (dist> (0.5f*obstacles.size()) ) dist = obstacles.size() - dist; double factor_AntiCab; if (last_selected_sector==-1) factor_AntiCab = 0; else factor_AntiCab = (dist > 0.10f*obstacles.size()) ? 0.0f:1.0f; // Factor3: Minima distancia entre el segmento y el target: // Se valora negativamente el alejarse del target // ----------------------------------------------------- double closestX,closestY; double dist_eucl = math::minimumDistanceFromPointToSegment( target_x, target_y, 0,0, x,y, closestX,closestY); double factor3= ( maxObsRange - min(maxObsRange ,dist_eucl) ) / maxObsRange; ASSERT_(factorWeights.size()==4); if ( obstacles[gap->representative_sector] < TOO_CLOSE_OBSTACLE ) // Too close to obstacles out_gaps_evaluation[i] = 0; else out_gaps_evaluation[i] = ( factorWeights[0] * factor1 + factorWeights[1] * factor2 + factorWeights[2] * factor3 + factorWeights[3] * factor_AntiCab ) / (math::sum(factorWeights)) ; } // for each gap }
/*--------------------------------------------------------------- Search the best gap. ---------------------------------------------------------------*/ void CHolonomicND::searchBestGap( vector_double &obstacles, double maxObsRange, TGapArray &in_gaps, poses::CPoint2D &target, int &out_selDirection, double &out_selEvaluation, TSituations &out_situation, double &out_riskEvaluation, CLogFileRecord_NDPtr log) { // Para evaluar el risk: unsigned int min_risk_eval_sector = 0; unsigned int max_risk_eval_sector = obstacles.size()-1; unsigned int TargetSector = direction2sector(atan2(target.y(),target.x()),obstacles.size()); const double TargetDist = std::max(0.01,target.norm()); // El "risk" se calcula al final para todos los casos. // D1 : Camino directo? // -------------------------------------------------------- const int freeSectorsNearTarget = 10; // 3 bool theyAreFree = true, caseD1 = false; if (TargetSector>(unsigned int)freeSectorsNearTarget && TargetSector<(unsigned int)(obstacles.size()-freeSectorsNearTarget) ) { for (int j=-freeSectorsNearTarget;j<=freeSectorsNearTarget;j++) if (obstacles[ TargetSector + j ]<0.95*TargetDist) theyAreFree = false; caseD1 = theyAreFree; } if (caseD1) { // S1: Camino libre hacia target: out_selDirection = TargetSector; // Si hay mas de una, la que llegue antes out_selEvaluation = 1.0 + std::max( 0.0, (maxObsRange - TargetDist) / maxObsRange ); out_situation = SITUATION_TARGET_DIRECTLY; } else { // Evaluar los GAPs (Si no hay ninguno, nada, claro): vector_double gaps_evaluation; int selected_gap =-1; double selected_gap_eval = -100; evaluateGaps( obstacles, maxObsRange, in_gaps, TargetSector, TargetDist, gaps_evaluation ); if (log) log->gaps_eval = gaps_evaluation; // D2: Hay algun gap que pase por detras del target? // ( y no este demasiado lejos): // ------------------------------------------------- for ( unsigned int i=0;i<in_gaps.size();i++ ) if ( in_gaps[i].maxDistance >= TargetDist && abs((int)(in_gaps[i].representative_sector-(int)TargetSector)) <= (int)floor(MAX_SECTOR_DIST_FOR_D2_PERCENT * obstacles.size()) ) if ( gaps_evaluation[i]>selected_gap_eval ) { selected_gap_eval = gaps_evaluation[i]; selected_gap = i; } // Coger el mejor GAP: // (Esto ya solo si no se ha cogido antes) if ( selected_gap==-1 ) for ( unsigned int i=0;i<in_gaps.size();i++ ) if ( gaps_evaluation[i]>selected_gap_eval ) { selected_gap_eval = gaps_evaluation[i]; selected_gap = i; } // D3: No es suficientemente bueno? ( o no habia ninguno?) // ------------------------------------------------------------ if ( selected_gap_eval <= 0 ) { // S2: No way found // ------------------------------------------------------ out_selDirection = 0; out_selEvaluation = 0.0f; // La peor out_situation = SITUATION_NO_WAY_FOUND; } else { // El seleccionado: TGap gap = in_gaps[selected_gap]; int sectors_to_be_wide = round( WIDE_GAP_SIZE_PERCENT * obstacles.size() ); out_selDirection = in_gaps[selected_gap].representative_sector; out_selEvaluation = selected_gap_eval; // D4: Es un gap ancho? // ----------------------------------------------------- if ( (gap.end-gap.ini) < sectors_to_be_wide ) { // S3: Small gap // ------------------------------------------- out_situation = SITUATION_SMALL_GAP; } else { // S4: Wide gap // ------------------------------------------- out_situation = SITUATION_WIDE_GAP; } // Que el risk no salga del gap: min_risk_eval_sector = gap.ini; max_risk_eval_sector = gap.end; } } // Calcular minima distancia a obstaculos a corto plazo, en // un intervalo de sectores en torno al sector elegido: int ancho_sectores = round( RISK_EVALUATION_SECTORS_PERCENT * obstacles.size() ); int sec_ini = max((int)min_risk_eval_sector, out_selDirection - ancho_sectores ); int sec_fin = min((int)max_risk_eval_sector, out_selDirection + ancho_sectores ); out_riskEvaluation = 0.0; for (int i=sec_ini;i<=sec_fin;i++) out_riskEvaluation+= obstacles[ i ]; out_riskEvaluation /= (sec_fin - sec_ini + 1 ); }
/*--------------------------------------------------------------- Find gaps in the obtacles. ---------------------------------------------------------------*/ void CHolonomicND::gapsEstimator( vector_double &obstacles, poses::CPoint2D &target, TGapArray &gaps_out ) { unsigned int i,n; int nMaximos=0; double MaximoAbsoluto = -100; double MinimoAbsoluto = 100; vector_int MaximoIdx; vector_double MaximoValor; // Hacer una lista con los maximos de las distancias a obs: // ---------------------------------------------------------- MaximoIdx.resize(obstacles.size()); MaximoValor.resize(obstacles.size()); n = obstacles.size(); for (i=1;i<(n-1);i++) { // Actualizar max. y min. absolutos: MaximoAbsoluto= max( MaximoAbsoluto, obstacles[i] ); MinimoAbsoluto= min( MinimoAbsoluto, obstacles[i] ); // Buscar maximos locales: if ( ( obstacles[i] >= obstacles[i+1] && obstacles[i] > obstacles[i-1] ) || ( obstacles[i] > obstacles[i+1] && obstacles[i] >= obstacles[i-1] ) ) { MaximoIdx[nMaximos] = i; MaximoValor[nMaximos++] = obstacles[i]; } } // Crear GAPS: // -------------------------------------------------------- TGapArray gaps_temp; gaps_temp.reserve( 150 ); for (double factorUmbral = 0.975f;factorUmbral>=0.04f;factorUmbral-=0.05f) { double umbral = factorUmbral* MaximoAbsoluto + (1.0f-factorUmbral)*MinimoAbsoluto; bool dentro = false; int sec_ini=0, sec_end; double maxDist=0; for (i=0;i<n;i++) { if ( !dentro && (!i || obstacles[i]>=umbral) ) { sec_ini = i; maxDist = obstacles[i]; dentro = true; } else if (dentro && (i==(n-1) || obstacles[i]<umbral )) { sec_end = i; dentro = false; if ( (sec_end-sec_ini) > 2 ) { // Add new gap: TGap newGap; newGap.ini = sec_ini; newGap.end = sec_end; newGap.entranceDistance = min( obstacles[sec_ini], obstacles[sec_end] ); newGap.maxDistance = maxDist; gaps_temp.push_back(newGap); } } if (dentro) maxDist = max( maxDist, obstacles[i] ); } } // Proceso de eliminacion de huecos redundantes: // ------------------------------------------------------------- std::vector<bool> borrar_gap; borrar_gap.resize( gaps_temp.size() ); for (i=0;i<gaps_temp.size();i++) borrar_gap[i] = false; // Eliminar huecos con muy poca profundidad si estan dentro de otros: double maxProfundidad = 0; for (i=0;i<gaps_temp.size();i++) { double profundidad = gaps_temp[i].maxDistance - gaps_temp[i].entranceDistance; maxProfundidad = max(maxProfundidad, profundidad); } for (i=0;i<gaps_temp.size();i++) { double profundidad = gaps_temp[i].maxDistance - gaps_temp[i].entranceDistance; if ( profundidad< maxProfundidad / 10.0f ) borrar_gap[i]=true; } // Si es muy estrecho, pero hay uno casi igual pero UN POCO mas grande, // borrar el estrecho: for (i=0;i<gaps_temp.size();i++) { int ini_i = gaps_temp[i].ini; int fin_i = gaps_temp[i].end; int ancho_i = fin_i - ini_i; if ( !borrar_gap[i] ) { for (unsigned int j=0;j<gaps_temp.size() && !borrar_gap[i];j++) { if (i!=j) { int ini_j = gaps_temp[j].ini; int fin_j = gaps_temp[j].end; int ancho_j = fin_j - ini_j; // j dentro de i y UN POCO mas grande nada mas: if ( !borrar_gap[j] && ini_j>=ini_i && fin_j<=fin_i && ancho_i < (0.05f*n) && ancho_j < (0.25f*n) ) borrar_gap[i] = true; } } } } // Si dentro tiene mas de 1, borrarlo: for (i=0;i<gaps_temp.size();i++) { int ini_i = gaps_temp[i].ini; int fin_i = gaps_temp[i].end; int nDentro = 0; if ( !borrar_gap[i] ) { for (unsigned int j=0;j<gaps_temp.size();j++) { if (i!=j) { int ini_j = gaps_temp[j].ini; int fin_j = gaps_temp[j].end; // j dentro de i: if ( !borrar_gap[j] && ini_j>=ini_i && fin_j<=fin_i ) nDentro++; } } if (nDentro>1) borrar_gap[i] = true; } } // Uno dentro de otro y practicamente a la misma altura: Eliminarlo tambien: for (i=0;i<gaps_temp.size();i++) { if (!borrar_gap[i]) { double ent_i = gaps_temp[i].entranceDistance; int ini_i = gaps_temp[i].ini; int fin_i = gaps_temp[i].end; double MIN_GAPS_ENTR_DIST = (MaximoAbsoluto-MinimoAbsoluto)/10.0f; for (unsigned int j=0;j<gaps_temp.size() && !borrar_gap[i];j++) if (i!=j) { double ent_j = gaps_temp[j].entranceDistance; int ini_j = gaps_temp[j].ini; int fin_j = gaps_temp[j].end; // j dentro de i y casi misma "altura": if ( !borrar_gap[j] && !borrar_gap[i] && ini_j>=ini_i && fin_j<=fin_i && fabs(ent_i-ent_j)< MIN_GAPS_ENTR_DIST ) borrar_gap[i]=true; } } } // Copiar solo huecos no marcados para borrar: // --------------------------------------------------- gaps_out.clear(); gaps_out.reserve(15); for (i=0;i<gaps_temp.size();i++) if ( !borrar_gap[i] ) { // Calcular direccion representativa: calcRepresentativeSectorForGap( gaps_temp[i], target, obstacles); gaps_out.push_back( gaps_temp[i] ); } }
/*--------------------------------------------------------------- Navigate ---------------------------------------------------------------*/ void CHolonomicND::navigate(const NavInput& ni, NavOutput& no) { const auto ptg = getAssociatedPTG(); const double ptg_ref_dist = ptg ? ptg->getRefDistance() : 1.0; TGapArray gaps; TSituations situation; unsigned int selectedSector; double riskEvaluation; double evaluation; // Create a log record for returning data. CLogFileRecord_ND::Ptr log = mrpt::make_aligned_shared<CLogFileRecord_ND>(); no.logRecord = log; // Search gaps: gaps.clear(); ASSERT_(!ni.targets.empty()); const auto trg = *ni.targets.rbegin(); gapsEstimator(ni.obstacles, trg, gaps); // Select best gap: searchBestGap( ni.obstacles, 1.0 /* max obs range*/, gaps, trg, selectedSector, evaluation, situation, riskEvaluation, *log); if (situation == SITUATION_NO_WAY_FOUND) { // No way found! no.desiredDirection = 0; no.desiredSpeed = 0; } else { // A valid movement: no.desiredDirection = CParameterizedTrajectoryGenerator::index2alpha( selectedSector, ni.obstacles.size()); // Speed control: Reduction factors // --------------------------------------------- const double targetNearnessFactor = m_enableApproachTargetSlowDown ? std::min( 1.0, trg.norm() / (options.TARGET_SLOW_APPROACHING_DISTANCE / ptg_ref_dist)) : 1.0; const double riskFactor = std::min(1.0, riskEvaluation / options.RISK_EVALUATION_DISTANCE); no.desiredSpeed = ni.maxRobotSpeed * std::min(riskFactor, targetNearnessFactor); } m_last_selected_sector = selectedSector; // LOG -------------------------- if (log) { // gaps: { int i, n = gaps.size(); log->gaps_ini.resize(n); log->gaps_end.resize(n); for (i = 0; i < n; i++) { log->gaps_ini[i] = gaps[i].ini; log->gaps_end[i] = gaps[i].end; } } // Selection: log->selectedSector = selectedSector; log->evaluation = evaluation; log->situation = situation; log->riskEvaluation = riskEvaluation; } }