/*--------------------------------------------------------------- addMapFrame ---------------------------------------------------------------*/ unsigned int CIncrementalMapPartitioner::addMapFrame( const CSensoryFramePtr &frame, const CPose3DPDFPtr &robotPose ) { size_t i=0,j=0,n=0; CPose3D pose_i, pose_j, relPose; CPose3DPDFPtr posePDF_i, posePDF_j; CSensoryFramePtr sf_i, sf_j; CMultiMetricMap *map_i=NULL,*map_j=NULL; int debug_CheckPoint = 0; mrpt::utils::TMatchingPairList corrs; static CPose3D nullPose(0,0,0); // Set options in graph partition: CGraphPartitioner::DEBUG_SAVE_EIGENVECTOR_FILES = options.debugSaveAllEigenvectors; // Create the maps: TSetOfMetricMapInitializers mapInitializer; TMetricMapInitializer mapElement; mapElement.metricMapClassType = CLASS_ID( CSimplePointsMap ); mapInitializer.push_back( mapElement ); // mapElement.metricMapClassType = CLASS_ID( COccupancyGridMap2D ); // mapElement.occupancyGridMap2D_options.resolution = options.gridResolution; // mapInitializer.push_back( mapElement ); mapElement.metricMapClassType = CLASS_ID( CLandmarksMap ); // mapElement.landmarksMap_options.insertionOpts. mapInitializer.push_back( mapElement ); // Add new metric map to "m_individualMaps" // -------------------------------------------- //CMultiMetricMap *newMetricMap = new CMultiMetricMap( &mapInitializer ); m_individualMaps.push_back( CMultiMetricMap() ); CMultiMetricMap &newMetricMap = m_individualMaps.back(); newMetricMap.setListOfMaps( &mapInitializer ); MRPT_START // Create the metric map: // ----------------------------------------------------------------- ASSERT_(newMetricMap.m_pointsMaps.size()>0); newMetricMap.m_pointsMaps[0]->insertionOptions.isPlanarMap = false; // true newMetricMap.m_pointsMaps[0]->insertionOptions.minDistBetweenLaserPoints = 0.20f; options.minDistForCorrespondence = max(options.minDistForCorrespondence,1.3f*newMetricMap.m_pointsMaps[0]->insertionOptions.minDistBetweenLaserPoints); // JLBC,17/AGO/2006: "m_individualMaps" were created from the robot pose, but it is // more convenient now to save them as the robot being at (0,0,0). //frame->insertObservationsInto( newMetricMap.m_pointsMaps[0] ); newMetricMap.m_pointsMaps[0]->copyFrom( * frame->buildAuxPointsMap<CPointsMap>(&newMetricMap.m_pointsMaps[0]->insertionOptions)); // Faster :-) // Insert just the VisualLandmarkObservations: newMetricMap.m_landmarksMap->insertionOptions.insert_SIFTs_from_monocular_images = false; newMetricMap.m_landmarksMap->insertionOptions.insert_SIFTs_from_stereo_images = false; newMetricMap.m_landmarksMap->insertionOptions.insert_Landmarks_from_range_scans = false; frame->insertObservationsInto( newMetricMap.m_landmarksMap ); debug_CheckPoint=1; // Add to corresponding vectors: m_individualFrames.insert(robotPose, frame); // Already added to "m_individualMaps" above debug_CheckPoint=2; // Ampliar la matriz de adyacencias: // ----------------------------------------------------------------- n = m_A.getColCount(); n++; m_A.setSize(n,n); debug_CheckPoint=3; ASSERT_(m_individualMaps.size() == n); ASSERT_(m_individualFrames.size() == n); // Ajustar tamaño del vector de nodos modificados: // --------------------------------------------------- // El nuevo evidentemente debe ser tenido en cuenta: m_modified_nodes.push_back(true); // Methods to compute adjacency matrix: // true: matching between maps // false: matching between observations through "CObservation::likelihoodWith" // ------------------------------------------------------------------------------ bool useMapOrSF = options.useMapMatching; debug_CheckPoint=4; // Calcular los nuevos matchings y meterlos en la matriz: // ---------------------------------------------------------------- //for (i=n-1;i<n;i++) i=n-1; // Solo ejecutar para "i=n-1" la ultima fila/columna que esta vacia { // Get node "i": m_individualFrames.get(i, posePDF_i, sf_i); posePDF_i->getMean(pose_i); // And its points map: map_i = &m_individualMaps[i]; debug_CheckPoint=5; // for (j=0;j<n;j++) for (j=0;j<n-1;j++) { // Get node "j": m_individualFrames.get(j, posePDF_j, sf_j); posePDF_j->getMean( pose_j ); debug_CheckPoint=6; relPose = pose_j - pose_i; // And its points map: map_j = &m_individualMaps[j]; debug_CheckPoint=66; // Compute matching ratio: if (useMapOrSF) { m_A(i,j) = map_i->compute3DMatchingRatio( map_j, relPose, options.minDistForCorrespondence, options.minMahaDistForCorrespondence ); } else { //m_A(i,j) = sf_i->likelihoodWith(sf_j.pointer()); m_A(i,j) = observationsOverlap(sf_i, sf_j, &relPose ); } } // for j } // for i for (i=0;i<n-1;i++) // Solo ejecutar para "i=n-1" la ultima fila/columna que esta vacia { debug_CheckPoint=8; // Get node "i": m_individualFrames.get(i, posePDF_i, sf_i); posePDF_i->getMean(pose_i); // And its points map: map_i = &m_individualMaps[i]; debug_CheckPoint=9; j=n-1; //for (j=n-1;j<n;j++) { // Get node "j": m_individualFrames.get(j, posePDF_j, sf_j); posePDF_j->getMean(pose_j); debug_CheckPoint=10; relPose = pose_j - pose_i; // And its points map: map_j = &m_individualMaps[j]; // Compute matching ratio: if (useMapOrSF) { m_A(i,j) = map_i->compute3DMatchingRatio( map_j, CPose3D(relPose), options.minDistForCorrespondence, options.minMahaDistForCorrespondence ); } else { //m_A(i,j) = sf_i->likelihoodWith(sf_j.pointer()); m_A(i,j) = observationsOverlap(sf_i, sf_j, &relPose ); } debug_CheckPoint=12; } // for j } // for i debug_CheckPoint=13; // Self-similatity: Not used m_A(n-1,n-1) = 0; // Hacer que la matriz sea simetrica: // ----------------------------------------------------------------- for (i=0;i<n;i++) for (j=i+1;j<n;j++) m_A(i,j) = m_A(j,i) = 0.5f * (m_A(i,j) + m_A(j,i) ); debug_CheckPoint=14; /* DEBUG: Guardar la matriz: * / A.saveToTextFile("debug_matriz.txt",1); / **/ // Añadir a la lista de nodos modificados los nodos afectados: // ----------------------------------------------------------------- for (i=0;i<n;i++) m_modified_nodes[i] = m_A(i,n-1) > 0; debug_CheckPoint=15; if (m_last_last_partition_are_new_ones) { // Insert into the "new_ones" partition: m_last_partition[m_last_partition.size()-1].push_back( n-1 ); } else { // Add a new partition: vector_uint dummyPart; dummyPart.push_back(n-1); m_last_partition.push_back( dummyPart ); // The last one is the new_ones partition: m_last_last_partition_are_new_ones = true; } return n-1; // Index of the new node MRPT_END_WITH_CLEAN_UP( \ cout << "Unexpected runtime error at checkPoint="<< debug_CheckPoint << "\n"; \ cout << "\tn=" << n << "\n"; \ cout << "\ti=" << i << "\n"; \ cout << "\tj=" << j << "\n"; \ cout << "\tmap_i=" << map_i << "\n"; \ cout << "\tmap_j=" << map_j << "\n"; \ cout << "relPose: "<< relPose << endl; \ cout << "map_i.size()=" << map_i->m_pointsMaps[0]->size() << "\n"; \ cout << "map_j.size()=" << map_j->m_pointsMaps[0]->size() << "\n"; \ map_i->m_pointsMaps[0]->save2D_to_text_file(string("debug_DUMP_map_i.txt")); \ map_j->m_pointsMaps[0]->save2D_to_text_file(string("debug_DUMP_map_j.txt")); \ m_A.saveToTextFile("debug_DUMP_exception_A.txt"); \ );
// ------------------------------------------------------ // TestGeometry3D // ------------------------------------------------------ void TestLaser2Imgs() { // Set your rawlog file name if (!mrpt::system::fileExists(RAWLOG_FILE)) THROW_EXCEPTION_CUSTOM_MSG1("Rawlog file does not exist: %s",RAWLOG_FILE.c_str()) CActionCollectionPtr action; CSensoryFramePtr observations; size_t rawlogEntry = 0; //bool end = false; CDisplayWindow wind; // Set relative path for externally-stored images in rawlogs: string rawlog_images_path = extractFileDirectory( RAWLOG_FILE ); rawlog_images_path+="/Images"; CImage::IMAGES_PATH_BASE = rawlog_images_path; // Set it. CFileGZInputStream rawlogFile( RAWLOG_FILE ); for (;;) { if (os::kbhit()) { char c = os::getch(); if (c==27) break; } // Load action/observation pair from the rawlog: // -------------------------------------------------- cout << "Reading act/oct pair from rawlog..." << endl; if (! CRawlog::readActionObservationPair( rawlogFile, action, observations, rawlogEntry) ) break; // file EOF // CAMERA............ // Get CObservationStereoImages CObservationStereoImagesPtr sImgs; CObservationImagePtr Img; sImgs = observations->getObservationByClass<CObservationStereoImages>(); if (!sImgs) { Img = observations->getObservationByClass<CObservationImage>(); if(!Img) continue; } CPose3D cameraPose; // Get Camera Pose (B) (CPose3D) CMatrixDouble33 K; // Get Calibration matrix (K) sImgs ? sImgs->getSensorPose( cameraPose ) : Img->getSensorPose( cameraPose ); K = sImgs ? sImgs->leftCamera.intrinsicParams : Img->cameraParams.intrinsicParams; // LASER............. // Get CObservationRange2D CObservation2DRangeScanPtr laserScan = observations->getObservationByClass<CObservation2DRangeScan>(); if (!laserScan) continue; // Get Laser Pose (A) (CPose3D) CPose3D laserPose; laserScan->getSensorPose( laserPose ); if (abs(laserPose.yaw())>DEG2RAD(90)) continue; // Only front lasers // Get 3D Point relative to the Laser coordinate Frame (P1) (CPoint3D) CPoint3D point; CSimplePointsMap mapa; mapa.insertionOptions.minDistBetweenLaserPoints = 0; observations->insertObservationsInto( &mapa ); // <- The map contains the pose of the points (P1) // Get the points into the map vector<float> X, Y, Z; vector<float>::iterator itX, itY, itZ; mapa.getAllPoints(X,Y,Z); unsigned int imgW = sImgs? sImgs->imageLeft.getWidth() : Img->image.getWidth(); unsigned int imgH = sImgs? sImgs->imageLeft.getHeight() : Img->image.getHeight(); //unsigned int idx = 0; vector<float> imgX,imgY; vector<float>::iterator itImgX,itImgY; imgX.resize( X.size() ); imgY.resize( Y.size() ); CImage image; image = sImgs ? sImgs->imageLeft : Img->image; // Get pixels in the image: // Pimg = (kx,ky,k)^T = K(I|0)*P2 // Main loop for( itX = X.begin(), itY = Y.begin(), itZ = Z.begin(), itImgX = imgX.begin(), itImgY = imgY.begin(); itX != X.end(); itX++, itY++, itZ++, itImgX++, itImgY++) { // Coordinates Transformation CPoint3D pLaser(*itX,*itY,*itZ); CPoint3D pCamera(pLaser-cameraPose); if( pCamera.z() > 0 ) { *itImgX = - K(0,0)*((pCamera.x())/(pCamera.z())) + K(0,2); *itImgY = - K(1,1)*((pCamera.y())/(pCamera.z())) + K(1,2); if( *itImgX > 0 && *itImgX < imgW && *itImgY > 0 && *itImgY < imgH ) image.filledRectangle(*itImgX-1,*itImgY-1,*itImgX+1,*itImgY+1,TColor(255,0,0)); } // end if } // end for action.clear_unique(); observations.clear_unique(); wind.showImage(image); mrpt::system::sleep(50); }; // end for mrpt::system::pause(); }
/*--------------------------------------------------------------- CLSLAM_RBPF_2DLASER Implements a 2D local SLAM method based on a RBPF over an occupancy grid map. A part of HMT-SLAM. \param LMH The local metric hypothesis which must be updated by this SLAM algorithm. \param act The action to process (or NULL). \param sf The observations to process (or NULL). WE ALREADY HAVE CONTROL OVER THE CRITICAL SECTION OF THE LMHs! --------------------------------------------------------------- */ void CLSLAM_RBPF_2DLASER::processOneLMH( CLocalMetricHypothesis *LMH, const CActionCollectionPtr &actions, const CSensoryFramePtr &sf ) { MRPT_START // Get the current robot pose estimation: TPoseID currentPoseID = LMH->m_currentRobotPose; // If this is the first iteration, just create a new robot pose at the origin: if (currentPoseID == POSEID_INVALID ) { currentPoseID = CHMTSLAM::generatePoseID(); LMH->m_currentRobotPose = currentPoseID; // Change it in the LMH // Create a new robot pose: CPose3D initPose(0,0,0); ASSERT_( LMH->m_particles.size()>0 ); for (CLocalMetricHypothesis::CParticleList::iterator it=LMH->m_particles.begin();it!=LMH->m_particles.end();++it) it->d->robotPoses[ currentPoseID ] = initPose; ASSERT_( m_parent->m_map.nodeCount()==1 ); m_parent->m_map_cs.enter(); CHMHMapNodePtr firstArea = m_parent->m_map.getFirstNode(); ASSERT_(firstArea); LMH->m_nodeIDmemberships[currentPoseID] = firstArea->getID(); // Set annotation for the reference pose: firstArea->m_annotations.setElemental( NODE_ANNOTATION_REF_POSEID, currentPoseID , LMH->m_ID); m_parent->m_map_cs.leave(); } bool insertNewRobotPose = false; if (sf) { if ( LMH->m_nodeIDmemberships.size()<2 ) // If there is just 1 node (the current robot pose), then there is no observation in the map yet! { // Update map if this is the first observation! insertNewRobotPose = true; } else { // Check minimum distance from current robot pose to those in the neighborhood: map< TPoseID, CPose3D > lstRobotPoses; LMH->getMeans( lstRobotPoses ); CPose3D *currentRobotPose = & lstRobotPoses[currentPoseID]; float minDistLin = 1e6f; float minDistAng = 1e6f; //printf("Poses in graph:\n"); for (map< TPoseID, CPose3D >::iterator it = lstRobotPoses.begin();it!=lstRobotPoses.end();++it) { if (it->first != currentPoseID ) { float linDist = it->second.distanceTo( *currentRobotPose ); float angDist = fabs(math::wrapToPi( it->second.yaw() - currentRobotPose->yaw() )); minDistLin = min( minDistLin, linDist ); if ( linDist < m_parent->m_options.SLAM_MIN_DIST_BETWEEN_OBS ) minDistAng = min(minDistAng, angDist); } } // time to insert a new node?? insertNewRobotPose = (minDistLin>m_parent->m_options.SLAM_MIN_DIST_BETWEEN_OBS) || ( minDistAng > m_parent->m_options.SLAM_MIN_HEADING_BETWEEN_OBS ); } } // end if there are SF // Save data in members so PF callback "prediction_and_update_pfXXXX" have access to them: m_insertNewRobotPose = insertNewRobotPose; // ------------------------------------------------ // Execute RBPF method: // 1) PROCESS ACTION // 2) PROCESS OBSERVATIONS // ------------------------------------------------ CParticleFilter pf; pf.m_options = m_parent->m_options.pf_options; pf.executeOn( *LMH, actions.pointer(), sf.pointer() ); // 3) The appearance observation: update the log likelihood // ... // ----------------------------------------------------------- // 4) UPDATE THE MAP // ----------------------------------------------------------- if (insertNewRobotPose) { m_parent->printf_debug("[CLSLAM_RBPF_2DLASER] Adding new pose...\n"); // Leave the up-to-now "current pose" in the map, insert the SF in it, and... // ---------------------------------------------------------------------------- TPoseID newCurrentPoseID = CHMTSLAM::generatePoseID(); // ...and create a new "current pose" making a copy of the previous one: // and insert the observations into the metric maps: // ---------------------------------------------------------------------------- for (CLocalMetricHypothesis::CParticleList::iterator partIt = LMH->m_particles.begin(); partIt!=LMH->m_particles.end(); partIt++) { const CPose3D *curRobotPose = &partIt->d->robotPoses[currentPoseID]; partIt->d->robotPoses[newCurrentPoseID]= *curRobotPose; sf->insertObservationsInto( &partIt->d->metricMaps, curRobotPose ); } // Add node membership: for now, a copy of the current pose: LMH->m_nodeIDmemberships[newCurrentPoseID] = LMH->m_nodeIDmemberships[currentPoseID]; // Now, insert the SF in the just added robot pose: // ----------------------------------------------------- LMH->m_SFs[ currentPoseID ] = *sf; // Sets the new poseID as current robot pose: // ---------------------------------------------------- TPoseID newlyAddedPose = currentPoseID; currentPoseID = LMH->m_currentRobotPose = newCurrentPoseID; // Mark the "newlyAddedPose" as pending to reconsider in the graph-cut method // (Done in the main loop @ LSLAM thread) // -------------------------------------------------------------------------------- LMH->m_posesPendingAddPartitioner.push_back( newlyAddedPose ); m_parent->printf_debug("[CLSLAM_RBPF_2DLASER] Added pose %i.\n", (int)newlyAddedPose); // Notice LC detectors: // ------------------------------ { synch::CCriticalSectionLocker lock( &m_parent->m_topLCdets_cs ); for (std::deque<CTopLCDetectorBase*>::iterator it=m_parent->m_topLCdets.begin();it!=m_parent->m_topLCdets.end();++it) (*it)->OnNewPose( newlyAddedPose, sf.pointer() ); } } // end of insertNewRobotPose MRPT_END }
/*--------------------------------------------------------------- process ---------------------------------------------------------------*/ void CDetectorDoorCrossing::process( CActionRobotMovement2D &in_poseChange, CSensoryFrame &in_sf, TDoorCrossingOutParams &out_estimation ) { // Variables for generic use: size_t i; out_estimation.cumulativeTurning = 0; MRPT_START // 1) Add new pair to the list: // ----------------------------------------- lastObs.addAction( in_poseChange ); lastObs.addObservations( in_sf ); // 2) Remove oldest pair: // ----------------------------------------- ASSERT_( options.windowSize > 1 ); ASSERT_( (lastObs.size() % 2) == 0 ); // Assure even size while (lastObs.size()>options.windowSize*2) { lastObs.remove(0); lastObs.remove(0); } if ( lastObs.size() < options.windowSize * 2 ) { // Not enought old data yet: out_estimation.enoughtInformation = false; return; } // 3) Build an occupancy grid map with observations // ------------------------------------------------- CPose2D p, pos; TSetOfMetricMapInitializers mapInitializer; { CSimplePointsMap::TMapDefinition def; mapInitializer.push_back( def ); } { COccupancyGridMap2D::TMapDefinition def; def.resolution = options.gridResolution; mapInitializer.push_back( def ); } CMultiMetricMap auxMap( &mapInitializer ); for (i=0;i<options.windowSize;i++) { CActionCollectionPtr acts = lastObs.getAsAction( i*2+0 ); CActionPtr act = acts->get(0); ASSERT_( act->GetRuntimeClass()->derivedFrom( CLASS_ID( CActionRobotMovement2D ) ) ) CActionRobotMovement2DPtr action = CActionRobotMovement2DPtr( act ); action->poseChange->getMean(pos); out_estimation.cumulativeTurning+= fabs(pos.phi()); // Get the cumulative pose for the current observation: p = p + pos; // Add SF to the grid map: CSensoryFramePtr sf = lastObs.getAsObservations( i*2+1 ); CPose3D pose3D(p); sf->insertObservationsInto( &auxMap, &pose3D ); } // 4) Compute the information differece between this // "map patch" and the previous one: // ------------------------------------------------------- auxMap.m_gridMaps[0]->computeEntropy( entropy ); if (!lastEntropyValid) { out_estimation.enoughtInformation = false; } else { // 5) Fill output data // --------------------------------- out_estimation.enoughtInformation = true; out_estimation.informationGain = entropy.I - lastEntropy.I; out_estimation.pointsMap.copyFrom( *auxMap.m_pointsMaps[0] ); } // For next iterations: lastEntropy = entropy; lastEntropyValid = true; MRPT_END }