#ifndef FIXEDLAGEVALBASE_H #define FIXEDLAGEVALBASE_H #include "../Settings.h" #include "../Helper.h" #include "../Vis.h" #include #include #include #include #include #include #include #include "GroundTruthWay.h" #include "../particles/MyState.h" #include "../particles/MyObservation.h" #include "../particles/MyEvaluation.h" #include "../particles/MyTransition.h" #include "../particles/MyInitializer.h" #include "../particles/smoothing/MySmoothingTransition.h" #include "../particles/smoothing/MySmoothingTransitionSimple.h" #include "../particles/smoothing/MySmoothingTransitionExperimental.h" #include "../reader/SensorReader.h" #include "../reader/SensorReaderStep.h" #include "../reader/SensorReaderTurn.h" #include "../reader/SensorReaderAccel.h" #include "../lukas/TurnObservation.h" #include "../lukas/StepObservation.h" #include "../lukas/ActivityDetection.h" #include "../toni/BarometerSensorReader.h" #include "../frank/WiFiSensorReader.h" #include "../frank/BeaconSensorReader.h" #include "../frank/OrientationSensorReader.h" class FixedLagEvalBase { protected: Grid grid; Helper::FHWSFloors floors; Vis vis; K::ParticleFilterHistory* pf; K::BackwardFilter* bf; SensorReader* sr; SensorReaderTurn* srt; SensorReaderStep* srs; std::string runName; GroundTruthWay gtw; // OLD //std::vector way0 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 2, 1, 0}; //std::vector way1 = {29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 13, 14, 15, 16, 17, 18, 19, 2, 1, 0}; //std::vector way2 = {29, 28, 27, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 1, 2, 19, 18, 17, 16, 15, 14, 13, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29}; // NEW std::vector path1 = {29, 28,27,26,255,25,24,23,22,21,20}; std::vector path1dbl = {29, 29, 28,27,26,255,25,24,23,22,21,20}; std::vector path2 = {19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 23, 7, 6}; std::vector path2dbl = {19, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 23, 7, 6}; std::vector path3 = {5, 27, 26, 255, 25, 4, 3, 2, 215, 1, 0, 30, 31}; std::vector path3dbl = {5, 5, 27, 26, 255, 25, 4, 3, 2, 215, 1, 0, 30, 31}; std::vector path4 = {29, 28, 27, 32, 33, 34, 35, 36, 10, 9, 8, 22, 37, 38, 39, 40, 41, 42, 43, 44}; std::vector path4dbl = {29, 29, 28, 27, 32, 33, 34, 35, 36, 10, 9, 8, 22, 37, 38, 39, 40, 41, 42, 43, 44}; // duplicate 1st waypoint! float stepSize = 0.71; public: FixedLagEvalBase() : grid(MiscSettings::gridSize_cm), floors(Helper::getFloors(grid)) { // build the grid Helper::buildTheGrid(grid, floors); // setup the visualisation vis.addFloor(floors.f0, floors.h0); vis.addFloor(floors.f1, floors.h1); vis.addFloor(floors.f2, floors.h2); vis.addFloor(floors.f3, floors.h3); vis.floors.setColorHex("#666666"); vis.groundTruth.setCustomAttr("dashtype 3"); vis.groundTruth.setColorHex("#009900"); vis.gp << "unset cbrange\n"; } static GridPoint conv(const Point3& p) { return GridPoint(p.x, p.y, p.z); } GroundTruthWay getGroundTruthWay(SensorReader& sr, const std::unordered_map& waypoints, std::vector ids) { // construct the ground-truth-path by using all contained waypoint ids std::vector path; for (int id : ids) { auto it = waypoints.find(id); if(it == waypoints.end()) {throw "not found";} path.push_back(it->second); } // new created the timed path GroundTruthWay gtw; int i = 0; while (sr.hasNext()) { const SensorEntry se = sr.getNext(); if (se.data.empty()) {continue;} // why necessary?? if (se.idx == 99) { gtw.add(se.ts, path[i]); ++i; } } // ensure the sensor-data contained usable timestamps for the ground-truth mapping assert(i>0); sr.rewind(); return gtw; } void run() { // sensor numbers const int s_wifi = 8; const int s_beacons = 9; const int s_barometer = 5; const int s_orientation = 6; const int s_accel = 0; //const int s_linearAcceleration = 2; std::list turn_observations; std::list step_observations; //Create an BarometerSensorReader BarometerSensorReader baroSensorReader; // activity detection ActivityDetection actDet; //Read all turn Observations while(srt->hasNext()) { SensorEntryTurn set = srt->getNext(); TurnObservation to; to.ts = set.ts; to.delta_heading = set.delta_heading; to.delta_motion = set.delta_motion; turn_observations.push_back(to); } //Step Observations while(srs->hasNext()) { SensorEntryStep ses = srs->getNext(); StepObservation so; so.ts = ses.ts; step_observations.push_back(so); } // the to-be-evaluated observation MyObservation obs; obs.step = new StepObservation(); obs.step->steps = 0; obs.turn = new TurnObservation(); obs.turn->delta_heading = 0; obs.turn->delta_motion = 0; // control data MyControl ctrl; //History of all estimated particles. Used for smoothing std::vector>> pfHistory; std::vector smoothedEst; std::vector tsHistory; std::vector pathEst; uint64_t lastTransitionTS = 0; int64_t start_time = -1; K::Statistics statsFiltering; K::Statistics statsSmoothing; K::Statistics statsDistFiltering; K::Statistics statsDistSmoothing; int cnt = 0; // error per update-step std::vector errorsNorm; std::vector errorsSmooth; std::vector errorsDistNorm; std::vector errorsDistSmooth; // number of updates to skip before starting the smoothing process const int skip = 35; //stats file std::ofstream statsout("/tmp/unsmoothed_" + runName + ".stats"); //heading double currentHeadingGivenByLukas = 0.0; // process each single sensor reading while(sr->hasNext()) { // get the next sensor reading from the CSV const SensorEntry se = sr->getNext(); //start_time needed for time calculation of steps and turns obs.latestSensorDataTS = se.ts; if (start_time == -1) {start_time = se.ts;} int64_t current_time = se.ts - start_time; switch(se.idx) { case s_wifi: { obs.wifi = WiFiSensorReader::readWifi(se); break; } case s_beacons: { BeaconObservationEntry boe = BeaconSensorReader::getBeacon(se); if (!boe.mac.empty()) { obs.beacons.entries.push_back(boe); } // add the observed beacon obs.beacons.removeOld(obs.latestSensorDataTS); break; } case s_barometer: { obs.barometer = baroSensorReader.readBarometer(se); actDet.addBaro(baroSensorReader.getHPA(se)); break; } case s_accel: { float acc[3]; SensorReaderAccel sre; sre.read(se, acc); actDet.addAccel(acc); break; } // case s_linearAcceleration:{ // baroSensorReader.readVerticalAcceleration(se); // break; // } case s_orientation: { obs.orientation = OrientationSensorReader::read(se); break; } } // process all occurred turns while (!step_observations.empty() && current_time > step_observations.front().ts) { const StepObservation _so = step_observations.front(); step_observations.pop_front(); (void) _so; obs.step->steps++; ctrl.walked_m = obs.step->steps * stepSize; } // process all occurred steps while (!turn_observations.empty() && current_time > turn_observations.front().ts) { const TurnObservation _to = turn_observations.front(); turn_observations.pop_front(); obs.turn->delta_heading += _to.delta_heading; obs.turn->delta_motion += _to.delta_motion; ctrl.headingChange_rad = Angle::degToRad(obs.turn->delta_heading); currentHeadingGivenByLukas = obs.turn->delta_heading; } // currently detected activity // TODO: feed sensor values! ctrl.currentActivitiy = actDet.getCurrentActivity(); // this is just for testing purposes obs.currentActivity = actDet.getCurrentActivity(); // time for a transition? if (se.ts - lastTransitionTS > MiscSettings::timeSteps) { lastTransitionTS = se.ts; // timed updates ((MyTransition*)pf->getTransition())->setCurrentTime(lastTransitionTS); // update the particle filter (transition + eval), estimate a new current position and add it to the estimated path const MyState est = pf->update(&ctrl, obs); const Point3 curEst = est.pCur; //EXPERIMENTAL: Set all Particle Angles to the estimated angle of the particle set // if(cnt % 30 == 0){ // pf->setAngle(est.avgAngle * 3.14159265359 / 180); // } // error calculation. compare ground-truth to estimation const int offset = 0; const Point3 curGT = gtw.getPosAtTime(se.ts - offset); const Point3 diff = curEst - curGT; pathEst.push_back(curEst); const float err = diff.length(); const float errDist = gtw.getMinDist(curEst); // minimum distance between estimation and ground-truth // count number of transitions ++cnt; if (cnt > 35) { statsFiltering.add(err); statsDistFiltering.add(errDist); errorsNorm.push_back(err); errorsDistNorm.push_back(errDist); //std::cout << "FilteringTime: " << se.ts << " " << statsFiltering.asString() << std::endl; //std::cout << "FilteringDist: " << se.ts << " " << statsDistFiltering.asString() << std::endl; } if(cnt > skip){ //save the current estimation for later smoothing. pfHistory.push_back(pf->getNonResamplingParticles()); tsHistory.push_back(se.ts); } //fixed-lag smoothing MyState estBF; if(pfHistory.size() > MiscSettings::lag){ bf->reset(); //use every n-th (std = 1) particle set of the history within a given lag (std = 5) for(int i = 0; i <= MiscSettings::lag; i += MiscSettings::fixedLagGap){ ((MySmoothingTransitionSimple*)bf->getTransition())->setCurrentTime(tsHistory[(tsHistory.size() - 1) - i]); estBF = bf->update(pfHistory[(pfHistory.size() - 1) - i]); } //use and visualize the smoothed particle set const Point3 curSmoothedEst = estBF.pCur; smoothedEst.push_back(curSmoothedEst); // error calculation. compare ground-truth to estimation //const Point3 curGTSmoothed = gtw.getPosAtTime(se.ts - (MiscSettings::lag * MiscSettings::timeSteps)); const Point3 curGTSmoothed = gtw.getPosAtTime(tsHistory[(tsHistory.size() - 1) - MiscSettings::lag]); const Point3 diffSmoothed = curSmoothedEst - curGTSmoothed; // skip the first 35 scans due to uniform distribution start const float errSmoothed = diffSmoothed.length(); const float errDistSmoothed = gtw.getMinDist(curSmoothedEst); // minimum distance between smoothed-estimation and ground-truth statsSmoothing.add(errSmoothed); statsDistSmoothing.add(errDistSmoothed); errorsSmooth.push_back(errSmoothed); errorsDistSmooth.push_back(errDistSmoothed); //std::cout << "SmoothingTime: " << tsHistory[(tsHistory.size() - 1) - MiscSettings::lag] << " " << statsSmoothing.asString() << std::endl; //std::cout << "SmoothingDist: " << tsHistory[(tsHistory.size() - 1) - MiscSettings::lag] << " " << statsDistSmoothing.asString() << std::endl; //plot vis.clearStates(); for (int j = 0; j < (int) bf->getbackwardParticles().back().size(); j+=15) { const K::Particle& p = bf->getbackwardParticles().back()[j]; vis.addState(p.state.walkState); } //vis.setTimestamp(se.ts); vis.setFilteringMedian(statsFiltering.getMedian()); vis.setSmoothingMedian(statsSmoothing.getMedian()); vis.addGroundTruth(gtw); vis.addEstPath(pathEst); vis.addSmoothPath(smoothedEst); vis.setEstAndShould(curEst, curGT); vis.setEstAndShould2(curSmoothedEst, curGTSmoothed); if (obs.barometer != nullptr) { vis.gp << "set label 112 'baro: " << obs.barometer->hpa << "' at screen 0.1,0.2\n"; } vis.gp << "set label 111 '" << ctrl.walked_m << ":" << ctrl.headingChange_rad << "' at screen 0.1,0.1\n"; vis.gp << "set label 112 '" << "Act: " << actDet.toString() << "' at screen 0.1,0.15\n"; //double avgAngleRad = estBF.avgAngle * 180/3.14159265359; //std::cout << "Measurement: "<< std::fmod((-(obs.orientation.values[0] * 180/3.14159265359) + 720 + 30), 360) << std::endl; //std::cout << "Measurement: "<< std::fmod(((obs.orientation.values[0] * 180/3.14159265359) + 720 + 60), 360) << std::endl; // std::cout << "MeasurementLukas: " << currentHeadingGivenByLukas << std::endl; // std::cout << "EstimationS: " << estBF.avgAngle << std::endl; // std::cout << "EstimationF: " << est.avgAngle << std::endl; //vis.gp << "set label 113 ' EstAngle:" << avgAngleRad << "' at screen 0.1,0.15\n"; //vis.gp << "set label 111 '" <getbackwardParticles().back().size(); j+=15) { const K::Particle& p = bf->getbackwardParticles().back()[j]; vis.addState(p.state.walkState); } //vis.setTimestamp(se.ts); vis.setFilteringMedian(statsFiltering.getMedian()); vis.setSmoothingMedian(statsSmoothing.getMedian()); vis.addGroundTruth(gtw); vis.addEstPath(pathEst); vis.addSmoothPath(smoothedEst); //vis.setEstAndShould(curEst, curGT); //vis.setEstAndShould2(curSmoothedEst, curGTSmoothed); if (obs.barometer != nullptr) { vis.gp << "set label 112 'baro: " << obs.barometer->hpa << "' at screen 0.1,0.2\n"; } vis.gp << "set label 111 '" <