int main(int argc, char* argv[]) { if (argc != 3) { puts("Usage: <ICP_SLAM init file> <port>"); return -1; } // connect to memdb db = db_connect(argv[2]); if (db == -1) { puts("control failed to connect to memdb."); return -1; } CConfigFile iniFile(argv[1]); // configurations file // Load configurations CMetricMapBuilderICP icp_slam; icp_slam.ICP_options.loadFromConfigFile(iniFile, "MappingApplication"); icp_slam.ICP_params.loadFromConfigFile(iniFile, "ICP"); icp_slam.initialize(); // pathfinding int resolution = 8; PathFinder pathFinder(resolution); deque<TPoint2D> path; deque<PathCommand> pathCommands; // timing sf::Clock globalClock; double transitionHoverStartTime; double startReadLidarTime; double prevTime = 0.0; double dt = 0.0; // THE state of the main control State state = INIT_HOVER; State nextState; double delta_distance = 0.0; double delta_phi = 0.0; sf::RenderWindow window(sf::VideoMode(800, 600), "2:00am"); window.setVerticalSyncEnabled(true); sf::Texture texture; texture.create(800, 600); Matrix<sf::Color> pixels(600, 800); sf::Sprite sprite(texture); sf::Clock fpsClock; int frameCount = 0; float prev_gyroz; deque<float> past_vx; deque<float> past_vy; globalClock.restart(); while (window.isOpen()) { // TODO: What will be our terminal case? target location reached? sf::Event event; while (window.pollEvent(event)) { switch (event.type) { case sf::Event::Closed: window.close(); break; } } double curTime = globalClock.getElapsedTime().asSeconds(); dt = curTime - prevTime; prevTime = curTime; // read the navadata from the ardrone char buffer[1024]; while (db_tryget(db, "navdata", buffer, sizeof(buffer)) != -1) { unsigned drone_dt_u; float cur_gyroz; sscanf(buffer, "%u,%f,%f,%f,%f,%f,%f", &drone_dt_u, &vx, &vy, &vz, &gyrox, &gyroy, &cur_gyroz); // compute the angle gyrox /= 1000; gyroy /= 1000; cur_gyroz /= 1000; cur_gyroz = fmod(cur_gyroz + 360, 360); gyroz += angle_diff(prev_gyroz, cur_gyroz); accumPhi = gyroz; prev_gyroz = cur_gyroz; // here goes the terrible part.... double drone_dt = (double)drone_dt_u / 1e6; past_vx.push_back(vx); past_vy.push_back(vy); if (past_vx.size() > 19) { past_vx.pop_front(); past_vy.pop_front(); } CObservationOdometryPtr obs = CObservationOdometry::Create(); obs->odometry = CPose2D(accumX/1000.0f, accumY/1000.0f, 0.0f); obs->hasEncodersInfo = false; obs->hasVelocities = false; // icp_slam.processObservation(obs); } accumX += median(past_vx) * dt; accumY += median(past_vy) * dt; accumDist = sqrt(pow(accumX, 2.0f) + pow(accumY, 2.0f)); if (fabs(accumX) > fabs(accumY) && accumX < 0) accumDist = -accumDist; if (fabs(accumY) > fabs(accumX) && accumY < 0) accumDist = -accumDist; if (state == INIT_HOVER) { if (globalClock.getElapsedTime().asSeconds() >= 10.0) { initialize_feedback(); state = READ_LIDAR; gyroz = accumPhi = 0.0f; startReadLidarTime = curTime; } else continue; } // get the map object and the grid representation of the map CMultiMetricMap* curMapEst = icp_slam.getCurrentlyBuiltMetricMap(); COccupancyGridMap2DPtr gridMap = curMapEst->m_gridMaps[0]; // Get the position estimates CPose3D curPosEst = icp_slam.getCurrentPoseEstimation()->getMeanVal(); // (estimated) X,Y coordinates of the robot, and robot yaw angle (direction) double robx = curPosEst.x(); double roby = curPosEst.y(); double robphi = curPosEst.yaw(); // Convert real coordinate to grid coordinate points int gridRobX = gridMap->x2idx(robx); int gridRobY = gridMap->y2idx(roby); // state machine actions if (state == READ_LIDAR) { // Extract the laser scan info and convert it into a range scan observation to feed into icp-slam // Need to define 2 values // 1.) scan: a vector of floats signalling the distances. Each element is a degree // 2.) validRange: a vector of ints where 1 signals the reading is good and 0 means its bad (and won't be used) while (db_tryget(db, "ultrasonic", buffer, sizeof(buffer)) != -1) { CObservation2DRangeScanPtr obs = CObservation2DRangeScan::Create(); obs->aperture = M_PI * 2; obs->scan.resize(360, 0); obs->validRange.resize(360, 0); float distances[4]; sscanf(buffer, "%f,%f,%f,%f", distances, distances+1, distances+2, distances+3); printf("Ultra: %f,%f,%f,%f\n", *distances, *(distances+1), *(distances+2), *(distances+3)); for (int k = 0; k < 4; ++k) { int sensorIndex = k * 90; int startIndex = sensorIndex - 20 + 180; int endIndex = sensorIndex + 20 + 180; for (int i = startIndex; i < endIndex; ++i) { int j = (i + 360) % 360; if (distances[k] >= 0) { obs->scan[j] = distances[k]; obs->validRange[j] = 1; } else { obs->scan[j] = distances[k]; obs->validRange[j] = 0; } } } icp_slam.processObservation(obs); } if (curTime - startReadLidarTime >= 5.0) state = PLAN; } else if (state == PLAN) { // Perform path finding pathFinder.update(*gridMap); bool pathFound = true; pathFound = pathFinder.findPath(TPoint2D(gridRobX, gridRobY), TPoint2D(gridRobX+40, gridRobY), path); printf("pathFound: %d\tpath length: %lu\n", pathFound, path.size()); /* path.push_back(TPoint2D(gridRobX, gridRobY)); path.push_back(TPoint2D(gridRobX+1000, gridRobY)); path.push_back(TPoint2D(gridRobX+1000, gridRobY+1000)); path.push_back(TPoint2D(gridRobX, gridRobY+1000)); path.push_back(TPoint2D(gridRobX, gridRobY)); */ float prevAngle = 0.0f; puts("PATH!!!!"); for (int i = 1; i < path.size(); i++) { float dx = path[i].x - path[i-1].x; float dy = path[i].y - path[i-1].y; printf("dx: %f, dy: %f\n", dx, dy); float angle = (float)fmod(atan2(dy, dx)*57.2957795 + 360, 360); PathCommand command; command.delta_phi = angle_diff(prevAngle, angle); command.delta_distance = sqrt(dx*dx + dy*dy) * 0.4 * 1000; pathCommands.push_back(command); prevAngle = angle; } for (int i = 0; i < path.size(); ++i) printf("%f, %f\n", pathCommands[i].delta_phi, pathCommands[i].delta_distance); delta_phi = pathCommands[0].delta_phi; delta_distance = pathCommands[0].delta_distance; pathCommands.pop_front(); initialize_feedback(); state = TURNING; } else if (state == TURNING) { float k = do_feedback_turn(delta_phi); printf("delta_phi: %f, turning k: %f\n", delta_phi, k); if (k != 0.0f) turn(k); else { nextState = MOVE_FORWARD; state = TRANSITION_HOVER; transitionHoverStartTime = curTime; state = MOVE_FORWARD; } } else if (state == MOVE_FORWARD) { float k = do_feedback_forward(delta_distance); printf("delta_distance: %f, forward k: %f\n", delta_distance, k); if (k != 0.0f) moveForward(k); else if (pathCommands.size() == 0) state = LAND; else { delta_phi = pathCommands[0].delta_phi; delta_distance = pathCommands[0].delta_distance; pathCommands.pop_front(); initialize_feedback(); nextState = TURNING; state = TRANSITION_HOVER; transitionHoverStartTime = curTime; } } else if (state == HOVER) { hover(); } else if (state == TRANSITION_HOVER) { hover(); if (curTime - transitionHoverStartTime > 2.0) { state = nextState; initialize_feedback(); } } else if (state == LAND) { land(); } // windows drawing window.clear(sf::Color::White); sf::View view; view.setSize(800, 600); view.setCenter(gridRobX, gridRobY); window.setView(view); // draw the grayscale probability map int yStart = max(0, gridRobY - 300); int yEnd = min((int)gridMap->getSizeY(), gridRobY + 300); int xStart = max(0, gridRobX - 400); int xEnd = min((int)gridMap->getSizeX(), gridRobX + 400); for (int y = yStart; y < yEnd; ++y) { for (int x = xStart; x < xEnd; ++x) { sf::Color &color = pixels(y-yStart, x-xStart); sf::Uint8 col = gridMap->getCell(x, y) * 255; color.r = col; color.g = col; color.b = col; } } texture.update((sf::Uint8*)pixels.getData()); sprite.setPosition(xStart, yStart); window.draw(sprite); // draw the robot's position sf::CircleShape circle(5); circle.setPosition(gridRobX-resolution/2, gridRobY-resolution/2); circle.setOutlineColor(sf::Color::Red); circle.setFillColor(sf::Color::Red); window.draw(circle); // draw the path std::vector<sf::Vertex> verticies; verticies.resize(path.size() + 1); verticies[0].position.x = (gridRobX / resolution) * resolution + resolution/2; verticies[0].position.y = (gridRobY / resolution) * resolution + resolution/2; verticies[0].color = sf::Color::Blue; for (unsigned i = 0; i < path.size(); ++i) { verticies[i+1].position.x = path[i].x * resolution + resolution / 2; verticies[i+1].position.y = path[i].y * resolution + resolution / 2; verticies[i+1].color = sf::Color::Blue; } window.draw(&verticies[0], verticies.size(), sf::LinesStrip); // draw the grid representation (only the occupied cells) sf::Color col = sf::Color::Red; col.a = 128; for (unsigned y = 0; y < pathFinder.occupancyGrid.height(); ++y) { for (unsigned x = 0; x < pathFinder.occupancyGrid.width(); ++x) { if (!pathFinder.occupancyGrid(y, x)) continue; sf::RectangleShape rect; rect.setPosition(x * resolution, y * resolution); rect.setSize(sf::Vector2f(resolution, resolution)); rect.setFillColor(col); window.draw(rect); } } window.display(); frameCount++; if (fpsClock.getElapsedTime().asSeconds() >= 1.0f) { char windowTitle[32]; sprintf(windowTitle, "%d fps", frameCount); window.setTitle(windowTitle); fpsClock.restart(); frameCount = 0; } } puts("exiting?!"); return 0; }
// ------------------------------------------------------ // BenchmarkGridmaps // ------------------------------------------------------ void BenchmarkGridmaps() { randomGenerator.randomize(333); CMultiMetricMap metricMap; TSetOfMetricMapInitializers mapInit; // Create gridmap: mapInit.loadFromConfigFile( CConfigFile(iniFile), "METRIC_MAPS" ); metricMap.setListOfMaps(&mapInit); // prepare the laser scan: CObservation2DRangeScan scan1; scan1.aperture = M_PIf; scan1.rightToLeft = true; scan1.validRange.resize( SCANS_SIZE ); scan1.scan.resize(SCANS_SIZE); ASSERT_( sizeof(SCAN_RANGES_1) == sizeof(float)*SCANS_SIZE ); memcpy( &scan1.scan[0], SCAN_RANGES_1, sizeof(SCAN_RANGES_1) ); memcpy( &scan1.validRange[0], SCAN_VALID_1, sizeof(SCAN_VALID_1) ); #if 1 CRawlog rawlog; rawlog.loadFromRawLogFile("/Trabajo/Code/MRPT/share/mrpt/datasets/2006-01ENE-21-SENA_Telecom Faculty_one_loop_only.rawlog"); scan1 = *rawlog.getAsObservations(400)->getObservationByClass<CObservation2DRangeScan>(); #endif ASSERT_( metricMap.m_gridMaps.size() ); COccupancyGridMap2DPtr gridMap = metricMap.m_gridMaps[0]; COccupancyGridMap2D gridMapCopy( *gridMap ); int i, N; CTicTac tictac; // test 1: getcell // ---------------------------------------- if (1) { N = 10000000; cout << "Running test #1: getCell... "; cout.flush(); //COccupancyGridMap2D::cellType cell; float p=0; tictac.Tic(); for (i=0;i<N;i++) { p += gridMap->getCell( 0, 0 ); } double T = tictac.Tac(); cout << "-> " << 1e9*T/N << " ns/iter. p=" << p << endl; // the "p" is to avoid optimizing out the entire loop! } // test 2: setcell // ---------------------------------------- if (1) { N = 10000000; cout << "Running test #2: setCell... "; cout.flush(); float p=0.8f; tictac.Tic(); for (i=0;i<N;i++) { gridMap->setCell( 0, 0, p ); } double T = tictac.Tac(); cout << "-> " << 1e9*T/N << " ns/iter." << endl; // the "p" is to avoid optimizing out the entire loop! } // test 3: updateCell // ---------------------------------------- if (1) { N = 1000000; cout << "Running test #3: updateCell... "; cout.flush(); float p=0.57f; tictac.Tic(); for (i=0;i<N;i++) { gridMap->updateCell( 0, 0, p ); } double T = tictac.Tac(); cout << "-> " << 1e9*T/N << " ns/iter." << endl; // the "p" is to avoid optimizing out the entire loop! } // test 4: updateCell_fast // ---------------------------------------- if (1) { N = 10000000; cout << "Running test #4: updateCell_fast... "; cout.flush(); float p=0.57f; COccupancyGridMap2D::cellType logodd_obs = COccupancyGridMap2D::p2l( p ); //float p_1 = 1-p; COccupancyGridMap2D::cellType *theMapArray = gridMap->getRow(0); unsigned theMapSize_x = gridMap->getSizeX(); COccupancyGridMap2D::cellType logodd_thres_occupied = COccupancyGridMap2D::OCCGRID_CELLTYPE_MIN+logodd_obs; tictac.Tic(); for (i=0;i<N;i++) { COccupancyGridMap2D::updateCell_fast_occupied( 2, 2, logodd_obs,logodd_thres_occupied, theMapArray, theMapSize_x); } double T = tictac.Tac(); cout << "-> " << 1e9*T/N << " ns/iter." << endl; // the "p" is to avoid optimizing out the entire loop! } #if 0 for (i=50;i<51;i++) { CPose3D pose3D(0.21,0.34,0,-2); //scan1.validRange.assign(scan1.validRange.size(), false); //scan1.validRange[i]=true; gridMap->clear(); gridMap->resizeGrid(-5,20,-15,15); gridMap->insertObservation( &scan1, &pose3D ); gridMap->saveAsBitmapFile(format("./gridmap_with_widening_%04i.png",i)); } #endif // test 5: Laser insertion // ---------------------------------------- if (1) { gridMap->insertionOptions.wideningBeamsWithDistance = false; N = 3000; cout << "Running test #5: Laser insert. w/o widen... "; cout.flush(); tictac.Tic(); for (i=0;i<N;i++) { #if 1 CPose2D pose( randomGenerator.drawUniform(-1.0,1.0), randomGenerator.drawUniform(-1.0,1.0), randomGenerator.drawUniform(-M_PI,M_PI) ); CPose3D pose3D(pose); #else CPose3D pose3D; #endif gridMap->insertObservation( &scan1, &pose3D ); } double T = tictac.Tac(); cout << "-> " << 1000*T/N << " ms/iter, scans/sec:" << N/T << endl; CPose3D pose3D; gridMap->clear(); gridMap->insertObservation( &scan1, &pose3D ); gridMap->saveAsBitmapFile("./gridmap_without_widening.png"); } // test 6: Laser insertion without widening // -------------------------------------------------- if (1) { gridMap->insertionOptions.wideningBeamsWithDistance = true; N = 3000; cout << "Running test #6: Laser insert. widen... "; cout.flush(); tictac.Tic(); for (i=0;i<N;i++) { #if 1 CPose2D pose( randomGenerator.drawUniform(-1.0,1.0), randomGenerator.drawUniform(-1.0,1.0), randomGenerator.drawUniform(-M_PI,M_PI) ); CPose3D pose3D(pose); #else CPose3D pose3D; #endif gridMap->insertObservation( &scan1, &pose3D ); } double T = tictac.Tac(); cout << "-> " << 1000*T/N << " ms/iter, scans/sec:" << N/T << endl; CPose3D pose3D; gridMap->clear(); gridMap->insertObservation( &scan1, &pose3D ); gridMap->saveAsBitmapFile("./gridmap_with_widening.png"); } // test 7: Grid resize // ---------------------------------------- if (1) { N = 400; cout << "Running test #7: Grid resize... "; cout.flush(); tictac.Tic(); for (i=0;i<N;i++) { *gridMap = gridMapCopy; gridMap->resizeGrid(-30,30,-40,40); } double T = tictac.Tac(); cout << "-> " << 1000*T/N << " ms/iter" << endl; } // test 8: Likelihood computation // ---------------------------------------- if (1) { N = 5000; *gridMap = gridMapCopy; CPose3D pose3D(0,0,0); gridMap->insertObservation( &scan1, &pose3D ); cout << "Running test #8: Likelihood... "; cout.flush(); double R = 0; tictac.Tic(); for (i=0;i<N;i++) { CPose2D pose( randomGenerator.drawUniform(-1.0,1.0), randomGenerator.drawUniform(-1.0,1.0), randomGenerator.drawUniform(-M_PI,M_PI) ); R+=gridMap->computeObservationLikelihood(&scan1,pose); } double T = tictac.Tac(); cout << "-> " << 1000*T/N << " ms/iter" << endl; } }
int main(int argc, char* argv[]) { if (argc < 4) { puts("Not enough arguments."); return -1; } ifstream laserLog, robotLog; string laserLine, robotLine; CConfigFile iniFile(argv[3]); // configurations file double accumX = 0.0, accumY = 0.0, accumPhi = 0.0; // pathfinding int resolution = 4; PathFinder pathFinder(resolution); deque<TPoint2D> path; // Load configurations CMetricMapBuilderICP icp_slam; icp_slam.ICP_options.loadFromConfigFile(iniFile, "MappingApplication"); icp_slam.ICP_params.loadFromConfigFile(iniFile, "ICP"); icp_slam.initialize(); laserLog.open(argv[1]); // log of laser scan robotLog.open(argv[2]); // log of robot odometer sf::RenderWindow window(sf::VideoMode(800, 600), "bam!"); sf::Texture texture; texture.create(800, 600); Matrix<sf::Color> pixels(600, 800); sf::Sprite sprite(texture); window.setVerticalSyncEnabled(true); bool paused = false; sf::Clock clock; unsigned frameCount = 0; while (laserLog.good() && window.isOpen()) { sf::Event event; while (window.pollEvent(event)) { switch (event.type) { case sf::Event::Closed: window.close(); break; } } double f; getline(laserLog, laserLine); getline(robotLog, robotLine); // Extract the laser scan info and convert it into a range scan observation to feed into icp-slam CObservation2DRangeScanPtr obs = CObservation2DRangeScan::Create(); stringstream ssLaser(laserLine); while (ssLaser >> f) { obs->scan.push_back(f); obs->validRange.push_back(1); } icp_slam.processObservation(obs); // Extract the odometer values and convert it into an observation to feed into icp-slam stringstream ssRobot(robotLine); ssRobot >> f; accumX += f; ssRobot >> f; accumY += f; ssRobot >> f; accumPhi += f; // Need the ABSOLUTE odometer readings, meaning the accumulated values CPose2DPtr rawOdo(new CPose2D(accumX, accumY, accumPhi)); CObservationOdometryPtr obs2 = CObservationOdometry::Create(); obs2->odometry = *rawOdo; obs2->hasEncodersInfo = false; obs2->hasVelocities = false; icp_slam.processObservation(obs2); // Extract current estimates // NOTE: coordinate points are in METERS // First get the grid map CMultiMetricMap* curMapEst = icp_slam.getCurrentlyBuiltMetricMap(); /* Grid representation of the current map. * Grid X Range: [0, getSizeX()] * Grid Y Range: [0, getSizeY()] * Convert from coordinate to grid loc: x2idx(float), y2idx(float) * Convert from grid loc to coordinate: idx2x(int), idx2y(float) * Use getCell(int x, int y) to tell if the cell is empty or not. A real value [0,1], which is the probablity that cell is empty */ COccupancyGridMap2DPtr gridMap = curMapEst->m_gridMaps[0]; // Get the position estimates CPose3D curPosEst = icp_slam.getCurrentPoseEstimation()->getMeanVal(); // (estimated) X,Y coordinates of the robot, and robot yaw angle (direction) double robx = curPosEst.x(); double roby = curPosEst.y(); double robphi = curPosEst.yaw(); // Convert real coordinate to grid coordinate points int gridRobX = gridMap->x2idx(robx); int gridRobY = gridMap->y2idx(roby); cout << "robot location: " << gridRobX << ' ' << gridRobY << '\n'; cout << "gridMap size: " << gridMap->getSizeX() << ' ' << gridMap->getSizeY() << '\n'; // Perform path finding bool pathFound = true; pathFinder.update(*gridMap); pathFound = pathFinder.findPath(TPoint2D(gridRobX, gridRobY), TPoint2D(890, 500), path); printf("pathFound: %d\tpath length: %d\n", pathFound, path.size()); // windows drawing window.clear(sf::Color::White); sf::View view; view.setSize(800, 600); view.setCenter(gridRobX, gridRobY); window.setView(view); // draw the grayscale probability map int yStart = max(0, gridRobY - 300); int yEnd = min((int)gridMap->getSizeY(), gridRobY + 300); int xStart = max(0, gridRobX - 400); int xEnd = min((int)gridMap->getSizeX(), gridRobX + 400); for (int y = yStart; y < yEnd; ++y) { for (int x = xStart; x < xEnd; ++x) { sf::Color &color = pixels(y-yStart, x-xStart); sf::Uint8 col = gridMap->getCell(x, y) * 255; color.r = col; color.g = col; color.b = col; } } texture.update((sf::Uint8*)pixels.getData()); sprite.setPosition(xStart, yStart); window.draw(sprite); // draw the robot's position sf::CircleShape circle(5); circle.setPosition(gridRobX-resolution/2, gridRobY-resolution/2); circle.setOutlineColor(sf::Color::Red); circle.setFillColor(sf::Color::Red); window.draw(circle); // draw the path std::vector<sf::Vertex> verticies; verticies.resize(path.size() + 1); verticies[0].position.x = (gridRobX / resolution) * resolution + resolution/2; verticies[0].position.y = (gridRobY / resolution) * resolution + resolution/2; verticies[0].color = sf::Color::Blue; for (unsigned i = 0; i < path.size(); ++i) { verticies[i+1].position.x = path[i].x * resolution + resolution / 2; verticies[i+1].position.y = path[i].y * resolution + resolution / 2; verticies[i+1].color = sf::Color::Blue; } window.draw(&verticies[0], verticies.size(), sf::LinesStrip); // draw the grid representation (only the occupied cells) /* sf::Color col = sf::Color::Yellow; col.a = 128; for (unsigned y = max(0, (gridRobY-400)/resolution); y < min<int>(pathFinder.occupancyGrid.height(), (gridRobY+400)/resolution); ++y) for (unsigned x = max(0, (gridRobX-400)/resolution); x < min<int>(pathFinder.occupancyGrid.width(), (gridRobX+400)/resolution); ++x) { if (!pathFinder.occupancyGrid(y, x)) continue; int xx = x * resolution; int yy = y * resolution; sf::RectangleShape rect; rect.setPosition(xx, yy); rect.setSize(sf::Vector2f(resolution, resolution)); rect.setFillColor(col); window.draw(rect); } */ window.display(); frameCount++; if (clock.getElapsedTime().asSeconds() >= 1.0) { char timestr[16]; sprintf(timestr, "%d fps", frameCount); window.setTitle(timestr); clock.restart(); frameCount = 0; } } return 0; }