next commit

This commit is contained in:
2017-04-05 18:45:00 +02:00
parent 6171582b40
commit b2adb16b49
18 changed files with 1602 additions and 360 deletions

View File

@@ -58,7 +58,7 @@ ADD_DEFINITIONS(
-fstack-protector-all -fstack-protector-all
-g3 -g3
-O0 -O2
-march=native -march=native
-DWITH_TESTS -DWITH_TESTS

View File

@@ -25,7 +25,7 @@
#include <KLib/math/statistics/Statistics.h> #include <KLib/math/statistics/Statistics.h>
#include "Structs.h" #include "Structs.h"
#include "Plotty.h" #include "plots/Plotty.h"
#include "CSV.h" #include "CSV.h"
#include "Helper.h" #include "Helper.h"

View File

@@ -16,6 +16,8 @@
#include "Indoor/floorplan/v2/FloorplanCeilings.h" #include "Indoor/floorplan/v2/FloorplanCeilings.h"
#include "Indoor/sensors/radio/model/WiFiModelLogDistCeiling.h" #include "Indoor/sensors/radio/model/WiFiModelLogDistCeiling.h"
#include "Indoor/sensors/offline/FileReader.h"
#include "Helper.h" #include "Helper.h"
using APAtFloor = std::pair<Floorplan::AccessPoint*, Floorplan::Floor*>; using APAtFloor = std::pair<Floorplan::AccessPoint*, Floorplan::Floor*>;
@@ -39,28 +41,71 @@ protected:
std::vector<APAtFloor> mapAPs; std::vector<APAtFloor> mapAPs;
WiFiOptimizer::Base* base; WiFiOptimizer::Base* base;
public:
/** ctor with map and fingerprints */ /** ctor with map and fingerprints */
EvalCompareOpt(const std::string& mapFile, const std::string& fpFile, const bool ignoreStaircases, const bool ignoreOutdoor) { EvalCompareOpt(const std::string& mapFile, const std::string& fpFile, const bool ignoreStaircases, const bool ignoreOutdoor, const bool ignoreIndoor) {
setup(mapFile);
// load fingerprints
calib = WiFiFingerprints(fpFile);
if (ignoreOutdoor) {calib = LeHelper::removeOutdoor(calib);}
if (ignoreStaircases) {calib = LeHelper::removeStaircases(calib);}
if (ignoreIndoor) {calib = LeHelper::removeIndoor(calib);}
cleanup();
}
/** ctor with map and live-walk [ground-truth] */
EvalCompareOpt(const std::string& mapFile, std::vector<std::pair<std::string, std::vector<int>>> walks) {
setup(mapFile);
// ensure each AP is present at least once
WiFiMeasurements mes;
for (const APAtFloor& apf : mapAPs) {
const Floorplan::AccessPoint* ap = apf.first;
WiFiMeasurement m(AccessPoint(MACAddress(ap->mac)), -120);
mes.entries.push_back(m);
}
WiFiFingerprint fpDummy(Point3(-99, -99, -99), mes);
calib.add(fpDummy);
for (const auto& it : walks) {
const std::string walkFile = it.first;
const std::vector<int> gtPathIndices = it.second;
// load "fingerprints" by matching ground-truth against live walk
Offline::FileReader reader(walkFile);
Offline::FileReader::GroundTruth gt = reader.getGroundTruth(map, gtPathIndices);
for (const auto& it : reader.getWiFiGroupedByTime()) {
const Timestamp ts = Timestamp::fromMS(it.ts); // time of the measurement
const WiFiMeasurements& mes = it.data; // wifi measurement
const Point3 gtPos_m = gt.get(ts); // location on the ground-truth during time of measuring
const WiFiFingerprint fp(gtPos_m, mes); // location + measurement = fingerprint
calib.add(fp);
}
}
cleanup();
}
private:
void setup(const std::string& mapFile) {
// load floorplan // load floorplan
map = Floorplan::Reader::readFromFile(mapFile); map = Floorplan::Reader::readFromFile(mapFile);
// how to group VAPs // how to group VAPs
vap = new VAPGrouper(VAPGrouper::Mode::LAST_MAC_DIGIT_TO_ZERO, VAPGrouper::Aggregation::AVERAGE); vap = new VAPGrouper(VAPGrouper::Mode::LAST_MAC_DIGIT_TO_ZERO, VAPGrouper::Aggregation::MEDIAN);
// load fingerprints
calib = WiFiFingerprints(fpFile);
LeHelper::removeNonFHWS(calib);
if (ignoreOutdoor) {calib = LeHelper::removeOutdoor(calib);}
if (ignoreStaircases) {calib = LeHelper::removeStaircases(calib);}
//LeHelper::plot(map, calib);
WiFiFingerprints calib2 = calib;
for (WiFiFingerprint& fp : calib2.getFingerprints()) {
fp.measurements = vap->group(fp.measurements);
}
LeHelper::plot(map, calib2);
// some ceiling calculations // some ceiling calculations
ceilings = Floorplan::Ceilings(map); ceilings = Floorplan::Ceilings(map);
@@ -68,15 +113,34 @@ protected:
// all APs within the map // all APs within the map
mapAPs = FloorplanHelper::getAPs(map); mapAPs = FloorplanHelper::getAPs(map);
}
void cleanup() {
LeHelper::removeNonFHWS(calib);
//LeHelper::plot(map, calib);
// used to aggreagate fingerprints // used to aggreagate fingerprints
base = new WiFiOptimizer::Base(*vap); base = new WiFiOptimizer::Base(*vap);
base->addFingerprints(calib); base->addFingerprints(calib);
// VAP-group fingerprints
for (WiFiFingerprint& fp : calib.getFingerprints()) {
fp.measurements = vap->group(fp.measurements);
}
// plot
LeHelper::plot(map, calib);
} }
/** get the error for the given AP at the provided location */ protected:
/** get the error for the given APs */
K::Statistics<float> analyzeErrorForAPs(const std::vector<WiFiOptimizer::LogDistCeiling::APParamsMAC>& aps) { K::Statistics<float> analyzeErrorForAPs(const std::vector<WiFiOptimizer::LogDistCeiling::APParamsMAC>& aps) {
// overall error
K::Statistics<float> statsAbs; K::Statistics<float> statsAbs;
// process each AP // process each AP
@@ -85,7 +149,6 @@ protected:
} }
// done // done
//std::cout << "overall error: " << std::endl << statsAbs.asString() << std::endl;
return statsAbs; return statsAbs;
} }
@@ -114,17 +177,25 @@ protected:
const WiFiOptimizer::LogDistCeiling::APParams& params = ap.params; const WiFiOptimizer::LogDistCeiling::APParams& params = ap.params;
const MACAddress& mac = ap.mac; const MACAddress& mac = ap.mac;
// always using the same model // empty model
WiFiModelLogDistCeiling model(map); WiFiModelLogDistCeiling model(map);
// add configured AP
model.addAP(mac, WiFiModelLogDistCeiling::APEntry(params.getPos(), params.txp, params.exp, params.waf), false); model.addAP(mac, WiFiModelLogDistCeiling::APEntry(params.getPos(), params.txp, params.exp, params.waf), false);
// get all fingerprints for the given AP // get all fingerprints for the given AP
const std::vector<WiFiOptimizer::RSSIatPosition> entries = base->getFingerprintsFor(mac); const std::vector<WiFiOptimizer::RSSIatPosition> entries = base->getFingerprintsFor(mac);
// sanity check. seems ok
// const std::vector<WiFiFingerprint> entries2 = calib.getFingerprintsFor(mac);
// if (entries.size() != entries2.size()) {
// throw Exception("detected size mismatch");
// }
// stats // stats
K::Statistics<float> stats; K::Statistics<float> stats;
// process each fingerprint for this ap // process each fingerprint for this ap to estimate the error
for (const WiFiOptimizer::RSSIatPosition& reading : entries) { for (const WiFiOptimizer::RSSIatPosition& reading : entries) {
// get the model-estimation for the fingerprint's position // get the model-estimation for the fingerprint's position
@@ -144,15 +215,6 @@ protected:
} }
};
/** fixed ap pos, fixed ap params */
class EvalCompareOptAllFixed : public EvalCompareOpt {
private: private:
// looks good // looks good
@@ -162,15 +224,37 @@ private:
public: public:
EvalCompareOptAllFixed(const std::string& mapFile, const std::string& fpFile, const bool ignoreStaircases, const bool ignoreOutdoor) : // EvalCompareOptAllFixed(const std::string& mapFile, const std::string& fpFile, const bool ignoreStaircases, const bool ignoreOutdoor) :
EvalCompareOpt(mapFile, fpFile, ignoreStaircases, ignoreOutdoor) { // EvalCompareOpt(mapFile, fpFile, ignoreStaircases, ignoreOutdoor) {
; // ;
// }
struct Result {
// the configured model
WiFiModelLogDistCeiling model;
// the resulting error
K::Statistics<float> errAbs;
Result(const WiFiModelLogDistCeiling& model, const K::Statistics<float>& errAbs) : model(model), errAbs(errAbs) {;}
};
WiFiModelLogDistCeiling::APEntry toModel(const WiFiOptimizer::LogDistCeiling::APParams& p) {
return WiFiModelLogDistCeiling::APEntry(
Point3(p.x, p.y, p.z),
p.txp,
p.exp,
p.waf
);
} }
/** get the error when using the given 3 params for ALL aps */ /** get the error when using the given 3 params for ALL aps */
K::Statistics<float> getStatsAll(const float txp, const float exp, const float waf) { Result getStatsAll(const float txp, const float exp, const float waf) {
WiFiModelLogDistCeiling model(map);
// all access points with params // all access points with params
std::vector<WiFiOptimizer::LogDistCeiling::APParamsMAC> aps; std::vector<WiFiOptimizer::LogDistCeiling::APParamsMAC> aps;
@@ -190,31 +274,33 @@ public:
WiFiOptimizer::LogDistCeiling::APParamsMAC ap(mac, params); WiFiOptimizer::LogDistCeiling::APParamsMAC ap(mac, params);
aps.push_back(ap); aps.push_back(ap);
model.addAP(mac, toModel(params), false);
} }
return analyzeErrorForAPs(aps); K::Statistics<float> errAbs = analyzeErrorForAPs(aps);
return Result(model, errAbs);
} }
/** calculate error for fixed positions and fixed constants */ /** calculate error for fixed positions and fixed constants */
K::Statistics<float> fixedPosFixedParamsForAll() { Result fixedPosFixedParamsForAll() {
// fire // fire
K::Statistics<float> stats = getStatsAll(txp, exp, waf); Result res = getStatsAll(txp, exp, waf);
std::cout << "----------------------------------------------------" << std::endl; std::cout << "----------------------------------------------------" << std::endl;
std::cout << "AP POS FROM MAP, FIXED TXP/EXP/WAF FOR ALL APS" << std::endl; std::cout << "AP POS FROM MAP, FIXED TXP/EXP/WAF FOR ALL APS" << std::endl;
std::cout << stats.asString() << std::endl; std::cout << res.errAbs.asString() << std::endl;
std::cout << std::endl; std::cout << std::endl;
return stats; return res;
} }
/** calculate error for fixed positions and optimized constants, but the same 3 for all APs */ /** calculate error for fixed positions and optimized constants, but the same 3 for all APs */
K::Statistics<float> fixedPosOptParamsForAll() { Result fixedPosOptParamsForAll() {
auto func = [&] (const float* params) { auto func = [&] (const float* params) {
return getStatsAll(params[0], params[1], params[2]).getAvg(); return getStatsAll(params[0], params[1], params[2]).errAbs.getAvg();
}; };
auto callback = [&] (const int run, const int iteration, const float , const float* ) { auto callback = [&] (const int run, const int iteration, const float , const float* ) {
@@ -241,18 +327,20 @@ public:
// opt.setMutation(0.25); // opt.setMutation(0.25);
// opt.calculateOptimum(func, params); // opt.calculateOptimum(func, params);
K::Statistics<float> stats = getStatsAll(params[0], params[1], params[2]); Result res = getStatsAll(params[0], params[1], params[2]);
std::cout << "----------------------------------------------------" << std::endl; std::cout << "----------------------------------------------------" << std::endl;
std::cout << "AP POS FROM MAP, OPTIMIZING TXP/EXP/WAF: THE SAME FOR ALL APS" << std::endl; std::cout << "AP POS FROM MAP, OPTIMIZING TXP/EXP/WAF: THE SAME FOR ALL APS" << std::endl;
std::cout << "params: " << params[0] << "," << params[1] << "," << params[2] << std::endl; std::cout << "params: " << params[0] << "," << params[1] << "," << params[2] << std::endl;
std::cout << stats.asString() << std::endl; std::cout << res.errAbs.asString() << std::endl;
std::cout << std::endl; std::cout << std::endl;
return stats; return res;
} }
/** calculate error for fixed positions and optimized constants, each AP on its own */ /** calculate error for fixed positions and optimized constants, each AP on its own */
K::Statistics<float> fixedPosOptParamsForEach() { Result fixedPosOptParamsForEach() {
WiFiModelLogDistCeiling model(map);
K::Statistics<float> _dstAbs; K::Statistics<float> _dstAbs;
@@ -294,18 +382,25 @@ public:
std::cout << "--" << mac.asString() << " params: " << params[0] << ",\t" << params[1] << ",\t" << params[2] << "\terr: " << tmp.getAvg() << std::endl; std::cout << "--" << mac.asString() << " params: " << params[0] << ",\t" << params[1] << ",\t" << params[2] << "\terr: " << tmp.getAvg() << std::endl;
analyzeErrorForAP(mac, pos, params[0], params[1], params[2], _dstAbs); analyzeErrorForAP(mac, pos, params[0], params[1], params[2], _dstAbs);
// add the optimized AP to the final model
model.addAP(mac, pos, params[0], params[1], params[2], false);
} }
std::cout << "----------------------------------------------------" << std::endl; std::cout << "----------------------------------------------------" << std::endl;
std::cout << "AP POS FROM MAP, OPTIMIZING TXP/EXP/WAF INDIVIDUALLY FOR EACH AP" << std::endl; std::cout << "AP POS FROM MAP, OPTIMIZING TXP/EXP/WAF INDIVIDUALLY FOR EACH AP" << std::endl;
std::cout << _dstAbs.asString() << std::endl; std::cout << _dstAbs.asString() << std::endl;
std::cout << std::endl; std::cout << std::endl;
return _dstAbs; return Result(model, _dstAbs);
} }
/** calculate error for fixed positions and optimized constants, each AP on its own */ /** calculate error for fixed positions and optimized constants, each AP on its own */
K::Statistics<float> optPosOptParamsForEach() { Result optPosOptParamsForEach() {
// output only
WiFiModelLogDistCeiling model(map);
K::Statistics<float> _dstAbs; K::Statistics<float> _dstAbs;
@@ -318,7 +413,12 @@ public:
// fixed // fixed
const MACAddress mac(mapAP.first->mac); const MACAddress mac(mapAP.first->mac);
const Point3 pos = mapAP.first->getPos(mapAP.second); //const Point3 pos = mapAP.first->getPos(mapAP.second);
if (calib.getFingerprintsFor(mac).empty()) {
std::cout << "skipping AP " << mac.asString() << "!!! not seen during calibration" << std::endl;
continue;
}
// opt-func for one AP // opt-func for one AP
auto func = [&] (const float* params) { auto func = [&] (const float* params) {
@@ -359,19 +459,33 @@ public:
std::cout << "--" << mac.asString() << " params: " << params[0] << ",\t" << params[1] << ",\t" << params[2] << ",\t" << params[3] << ",\t" << params[4] << ",\t" << params[5] << "\terr: " << tmp.getAvg() << std::endl; std::cout << "--" << mac.asString() << " params: " << params[0] << ",\t" << params[1] << ",\t" << params[2] << ",\t" << params[3] << ",\t" << params[4] << ",\t" << params[5] << "\terr: " << tmp.getAvg() << std::endl;
analyzeErrorForAP(mac, Point3(params[0], params[1], params[2]), params[3], params[4], params[5], _dstAbs); analyzeErrorForAP(mac, Point3(params[0], params[1], params[2]), params[3], params[4], params[5], _dstAbs);
// add to model for later reuse
model.addAP(mac, Point3(params[0], params[1], params[2]), params[3], params[4], params[5], false);
} }
std::cout << "----------------------------------------------------" << std::endl; std::cout << "----------------------------------------------------" << std::endl;
std::cout << "OPTIMIZING POS/TXP/EXP/WAF INDIVIDUALLY FOR EACH AP" << std::endl; std::cout << "OPTIMIZING POS/TXP/EXP/WAF INDIVIDUALLY FOR EACH AP" << std::endl;
std::cout << _dstAbs.asString() << std::endl; std::cout << _dstAbs.asString() << std::endl;
std::cout << std::endl; std::cout << std::endl;
return _dstAbs; return Result(model, _dstAbs);
} }
}; };
///** fixed ap pos, fixed ap params */
//class EvalCompareOptAllFixed : public EvalCompareOpt {
//};
#endif // EVALCOMPAREOPT_H #endif // EVALCOMPAREOPT_H

458
EvalCompareOpt2.h Normal file
View File

@@ -0,0 +1,458 @@
#ifndef EVALCOMPAREOPT2_H
#define EVALCOMPAREOPT2_H
#include <KLib/math/statistics/Statistics.h>
#include "Indoor/sensors/radio/setup/WiFiOptimizer.h"
#include "Indoor/sensors/radio/setup/WiFiFingerprint.h"
#include "Indoor/sensors/radio/setup/WiFiFingerprints.h"
#include "Indoor/sensors/radio/setup/WiFiOptimizer.h"
#include "Indoor/sensors/radio/setup/WiFiOptimizerLogDistCeiling.h"
#include "Indoor/sensors/radio/VAPGrouper.h"
#include "Indoor/floorplan/v2/Floorplan.h"
#include "Indoor/floorplan/v2/FloorplanReader.h"
#include "Indoor/floorplan/v2/FloorplanHelper.h"
#include "Indoor/floorplan/v2/FloorplanCeilings.h"
#include "Indoor/sensors/radio/model/WiFiModelLogDistCeiling.h"
#include "Indoor/sensors/offline/FileReader.h"
#include "Helper.h"
#include <vector>
class EvalCompareOpt2 {
int power = 1;
Floorplan::IndoorMap* map;
WiFiFingerprints calib;
VAPGrouper* vap;
Floorplan::Ceilings ceilings;
std::vector<APAtFloor> mapAPs;
public:
/** ctor with map and fingerprints */
EvalCompareOpt2(const std::string& mapFile, const std::string& fpFile, std::function<bool(const WiFiFingerprint& fp)> remove) {
setup(mapFile);
// load fingerprints
calib = WiFiFingerprints(fpFile);
// if (ignoreOutdoor) {calib = LeHelper::removeOutdoor(calib);}
// if (ignoreStaircases) {calib = LeHelper::removeStaircases(calib);}
// if (ignoreIndoor) {calib = LeHelper::removeIndoor(calib);}
calib = LeHelper::removeIf(calib, remove);
cleanup();
}
/** ctor with map and live-walk [ground-truth] */
EvalCompareOpt2(const std::string& mapFile, std::vector<std::pair<std::string, std::vector<int>>> walks) {
setup(mapFile);
// ensure each AP is present at least once
WiFiMeasurements mes;
for (const APAtFloor& apf : mapAPs) {
const Floorplan::AccessPoint* ap = apf.first;
WiFiMeasurement m(AccessPoint(MACAddress(ap->mac)), -100);
mes.entries.push_back(m);
}
WiFiFingerprint fpDummy(Point3(-50, -50, -50), mes);
calib.add(fpDummy);
for (const auto& it : walks) {
const std::string walkFile = it.first;
const std::vector<int> gtPathIndices = it.second;
// load "fingerprints" by matching ground-truth against live walk
Offline::FileReader reader(walkFile);
Offline::FileReader::GroundTruth gt = reader.getGroundTruth(map, gtPathIndices);
for (const auto& it : reader.getWiFiGroupedByTime()) {
const Timestamp ts = Timestamp::fromMS(it.ts); // time of the measurement
const WiFiMeasurements& mes = it.data; // wifi measurement
const Point3 gtPos_m = gt.get(ts); // location on the ground-truth during time of measuring
const WiFiFingerprint fp(gtPos_m, mes); // location + measurement = fingerprint
calib.add(fp);
}
}
cleanup();
}
private:
void setup(const std::string& mapFile) {
// load floorplan
map = Floorplan::Reader::readFromFile(mapFile);
// how to group VAPs
vap = new VAPGrouper(VAPGrouper::Mode::LAST_MAC_DIGIT_TO_ZERO, VAPGrouper::Aggregation::MEDIAN);
// some ceiling calculations
ceilings = Floorplan::Ceilings(map);
// all APs within the map
mapAPs = FloorplanHelper::getAPs(map);
}
void cleanup() {
LeHelper::removeNonFHWS(calib);
// VAP-group fingerprints
for (WiFiFingerprint& fp : calib.getFingerprints()) {
fp.measurements = vap->group(fp.measurements);
}
// plot
LeHelper::plot(map, calib);
}
public:
struct Result {
// the configured model
WiFiModelLogDistCeiling model;
// the resulting error
K::Statistics<float> errAvg;
K::Statistics<float> errSingle;
Result(Floorplan::IndoorMap* map) : model(map) {;}
};
Result fixedPosFixedParamsForAll() {
const float txp = -40;
const float exp = 2.65;
const float waf = -6.5;
Result res(map);
// process each AP
for (const auto _ap : mapAPs) {
const Floorplan::AccessPoint* ap = _ap.first;
const Floorplan::Floor* floor = _ap.second;
const MACAddress mac(ap->mac);
const Point3 pos_m = ap->getPos(floor);
res.model.addAP(mac, pos_m, txp, exp, waf, true);
K::Statistics<float> err = getError(mac, res.model);
res.errAvg.add(err.getAvg());
res.errSingle.add(err);
}
dump(res);
return res;
}
Result fixedPosOptParamsForAll() {
// resulting error using the given txp,exp,waf for ALL APs
auto errFunc = [&] (const float txp, const float exp, const float waf) -> Result {
Result res(map);
// process each AP
for (const auto _ap : mapAPs) {
const Floorplan::AccessPoint* ap = _ap.first;
const Floorplan::Floor* floor = _ap.second;
const MACAddress mac(ap->mac);
const Point3 pos_m = ap->getPos(floor);
res.model.addAP(mac, pos_m, txp, exp, waf, false); // allow unsafe txp,exp,waf params
K::Statistics<float> err = getError(mac, res.model);
res.errAvg.add(err.getAvg());
res.errSingle.add(err);
}
return res;
};
// to-be-optimized function: reduce average error among all fingerprints/AP's
auto optFunc = [&] (const float* params) {
return errFunc(params[0], params[1], params[2]).errSingle.getAvg();
};
// use simplex
float params[3] = {-40, 2, -8};
K::NumOptAlgoDownhillSimplex<float> opt(3);
opt.setMaxIterations(40);
opt.setNumRestarts(20);
opt.calculateOptimum(optFunc, params);
// // use genetic
// K::NumOptAlgoGenetic<float> opt(3);
// opt.setPopulationSize(100);
// opt.setMaxIterations(100);
// opt.setValRange({1, 0.1, 0.2});
// opt.setElitism(0.05f);
// opt.setMutation(0.25);
// opt.calculateOptimum(optFunc, params);
// get the error result for all APs
Result res = errFunc(params[0], params[1], params[2]);
// done
dump(res);
return res;
}
Result fixedPosOptParamsForEach() {
Result res(map);
// process each AP
for (const auto _ap : mapAPs) {
// fixed AP params
const Floorplan::AccessPoint* ap = _ap.first;
const Floorplan::Floor* floor = _ap.second;
const MACAddress mac(ap->mac);
const Point3 pos_m = ap->getPos(floor);
const std::vector<WiFiFingerprint> fps = getFingerprints(mac);
// resulting error using the given txp,exp,waf for ALL APs
auto errFunc = [&] (const float txp, const float exp, const float waf) -> K::Statistics<float> {
WiFiModelLogDistCeiling model(map); // new, empty model for this AP
model.addAP(mac, pos_m, txp, exp, waf, false); // allow unsafe txp,exp,waf params
K::Statistics<float> err = getError(mac, model, fps); // calculate error among all fingerprints
return err;
};
// to-be-optimized function: reduce average error among all fingerprints/AP's
auto optFunc = [&] (const float* params) {
return errFunc(params[0], params[1], params[2]).getAvg();
};
// use simplex
float params[3] = {-40, 2, -8};
K::NumOptAlgoDownhillSimplex<float> opt(3);
opt.setMaxIterations(40);
opt.setNumRestarts(20);
opt.calculateOptimum(optFunc, params);
// // use genetic
// K::NumOptAlgoGenetic<float> opt(3);
// opt.setPopulationSize(100);
// opt.setMaxIterations(100);
// opt.setValRange({1, 0.1, 0.2});
// opt.setElitism(0.05f);
// opt.setMutation(0.25);
// opt.calculateOptimum(optFunc, params);
// get the error result for the current AP
K::Statistics<float> err = errFunc(params[0], params[1], params[2]);
res.errAvg.add(err.getAvg());
res.errSingle.add(err);
// add finalized params to the model
res.model.addAP(mac, pos_m, params[0], params[1], params[2], false);
}
// done
dump(res);
return res;
}
Result optPosOptParamsForEach() {
Result res(map);
// process each AP
for (const auto _ap : mapAPs) {
// fixed AP params
const Floorplan::AccessPoint* ap = _ap.first;
const MACAddress mac(ap->mac);
const std::vector<WiFiFingerprint> fps = getFingerprints(mac);
// resulting error using the given txp,exp,waf for ALL APs
auto errFunc = [&] (const Point3 pos_m, const float txp, const float exp, const float waf) -> K::Statistics<float> {
WiFiModelLogDistCeiling model(map); // new, empty model for this AP
model.addAP(mac, pos_m, txp, exp, waf, false); // allow unsafe txp,exp,waf params
K::Statistics<float> err = getError(mac, model, fps); // calculate error among all fingerprints
return err;
};
// to-be-optimized function: reduce average error among all fingerprints/AP's
auto optFunc = [&] (const float* params) {
const Point3 pos_m(params[0], params[1], params[2]);
return errFunc(pos_m, params[3], params[4], params[5]).getAvg(); // AVG
};
// params
float params[6] = {0};
std::minstd_rand gen;
BBox3 mapBBox = FloorplanHelper::getBBox(map);
std::uniform_real_distribution<float> distX(mapBBox.getMin().x, mapBBox.getMax().x);
std::uniform_real_distribution<float> distY(mapBBox.getMin().y, mapBBox.getMax().y);
std::uniform_real_distribution<float> distZ(mapBBox.getMin().z, mapBBox.getMax().z);
// initializer for the optimizer: random position within the map's bbox
auto init = [&] (const int childIdx, float* params) {
(void) childIdx;
params[0] = distX(gen);
params[1] = distY(gen);
params[2] = distZ(gen);
params[3] = -40;
params[4] = 2;
params[5] = -8;
};
// // use genetic
// K::NumOptAlgoGenetic<float> opt(6);
// opt.setPopulationSize(300);
// opt.setMaxIterations(50);
// opt.setValRange({1, 1, 1, 1, 0.1, 0.2});
// opt.setElitism(0.05f);
// opt.setMutation(0.25);
// opt.calculateOptimum(optFunc, params, init);
using LeOpt = K::NumOptAlgoRangeRandom<float>;
const std::vector<LeOpt::MinMax> valRegion = {
LeOpt::MinMax(mapBBox.getMin().x, mapBBox.getMax().x), // x
LeOpt::MinMax(mapBBox.getMin().y, mapBBox.getMax().y), // y
LeOpt::MinMax(mapBBox.getMin().z, mapBBox.getMax().z), // z
LeOpt::MinMax(-50, -30), // txp
LeOpt::MinMax( 1, 4), // exp
LeOpt::MinMax(-15, -0), // waf
};
K::NumOptAlgoRangeRandom<float> opt(valRegion);
opt.setPopulationSize(300);
opt.setNumIerations(75);
opt.calculateOptimum(optFunc, params);
// K::NumOptAlgoDownhillSimplex<float> opt(6);
// opt.setMaxIterations(40);
// opt.setNumRestarts(20);
// opt.calculateOptimum(optFunc, params);
// get the error result for the current AP
const Point3 pos_m(params[0], params[1], params[2]);
K::Statistics<float> err = errFunc(pos_m, params[3], params[4], params[5]);
res.errAvg.add(err.getAvg());
res.errSingle.add(err);
// add finalized params to the model
res.model.addAP(mac, pos_m, params[3], params[4], params[5], false);
}
// done
dump(res);
return res;
}
private:
std::vector<WiFiFingerprint> getFingerprints(const MACAddress& mac) {
// get all fingerprints where the given mac was seen
const std::vector<WiFiFingerprint> fps = calib.getFingerprintsFor(mac);
// sanity check
// if (fps.size() < 5) {throw Exception("not enought fingerprints for AP " + mac.asString());}
return fps;
}
void dump(const Result& res) {
std::cout << res.errSingle.asString() << std::endl;
std::cout << res.errAvg.asString() << std::endl;
std::cout << "------------------------------------------------------" << std::endl;
}
/** calculate the error for the given AP [mac + configured model] for all fingerprints for this mac */
K::Statistics<float> getError(const MACAddress& mac, const WiFiModelLogDistCeiling& model) {
// get all fingerprints where the given mac was seen
const std::vector<WiFiFingerprint> fps = getFingerprints(mac);
// fire
return getError(mac, model, fps);
}
/** calculate the error for the given AP [mac + configured model] for all of the given FPS */
K::Statistics<float> getError(const MACAddress& mac, const WiFiModelLogDistCeiling& model, const std::vector<WiFiFingerprint>& fps) {
K::Statistics<float> res;
// calculate the model error for each fingerprint
for (const WiFiFingerprint& fp : fps) {
// sanity checks
if (fp.measurements.entries.size() != 1) {throw Exception("invalid fingerprint");}
if (fp.measurements.entries.front().getAP().getMAC() != mac) {throw Exception("invalid fingerprint");}
// rssi prediction from the configured model
const float model_rssi = model.getRSSI(mac, fp.pos_m);
// sanity check
if (model_rssi != model_rssi) {throw Exception("model rssi not available for " + mac.asString());}
// rssi measured during scan at this location
const float scan_rssi = fp.measurements.entries.front().getRSSI();
// sanity check
if (scan_rssi < -100) {throw Exception("scan rssi out of range for " + mac.asString());}
if (scan_rssi > -35) {throw Exception("scan rssi out of range for " + mac.asString());}
// difference
//const float err = model_rssi - scan_rssi;
const float err = -std::log(Distribution::Normal<float>::getProbability(model_rssi, 7, scan_rssi));
float aErr = std::abs(err);
// penalty
// if (model.getAP(mac).waf > -2) {aErr += 9999;}
// if (model.getAP(mac).txp > -30) {aErr += 9999;}
// if (model.getAP(mac).txp < -50) {aErr += 9999;}
res.add(aErr);
}
// done
return res;
}
};
#endif // EVALCOMPAREOPT2_H

View File

@@ -25,7 +25,10 @@
#include <KLib/math/statistics/Statistics.h> #include <KLib/math/statistics/Statistics.h>
#include "Structs.h" #include "Structs.h"
#include "Plotty.h" #include "plots/Plotty.h"
#include "plots/PlotErrTime.h"
#include "plots/PlotErrFunc.h"
#include "CSV.h" #include "CSV.h"
#include <unordered_set> #include <unordered_set>
@@ -89,6 +92,16 @@ private:
WiFiModel* wiModel = nullptr; WiFiModel* wiModel = nullptr;
std::vector<int> gtIndices; std::vector<int> gtIndices;
// error in meter
PlotErrFunc* pef;
PlotErrTime* pet;
// error in probability
PlotErrFunc* pef2;
PlotErrTime* pet2;
Plotty* plot;
public: public:
/** ctor with map and fingerprints */ /** ctor with map and fingerprints */
@@ -108,30 +121,70 @@ public:
// the optimizer // the optimizer
// opt = new WiFiOptimizer::LogDistCeiling(map, *vap, *calib, WiFiOptimizer::LogDistCeiling::Mode::MEDIUM); // opt = new WiFiOptimizer::LogDistCeiling(map, *vap, *calib, WiFiOptimizer::LogDistCeiling::Mode::MEDIUM);
// how to handle VAPs
vap = new VAPGrouper(VAPGrouper::Mode::LAST_MAC_DIGIT_TO_ZERO, VAPGrouper::Aggregation::MEDIAN);
pef = new PlotErrFunc("\\small{error (m)}", "\\small{measurements (\\%)}");
pef->showMarkers(false);
pef2 = new PlotErrFunc("\\small{-log(p(..))}", "\\small{measurements (\\%)}");
pef2->showMarkers(false);
pet = new PlotErrTime("2", "3", "");
pet->getPlot().setRangeY(K::GnuplotAxisRange(0, 40));
pet2 = new PlotErrTime("2", "3", "");
plot = new Plotty(map);
plot->buildFloorplan();
plot->setGroundTruth(gtIndices);
} }
void fixedParams(const float txp, const float exp, const float waf) { void load(const std::string& xmlFile, const std::string& name) {
// how to handle VAPs
vap = new VAPGrouper(VAPGrouper::Mode::LAST_MAC_DIGIT_TO_ZERO, VAPGrouper::Aggregation::AVERAGE);
// setup the model // setup the model
WiFiModelLogDistCeiling* wiModel = new WiFiModelLogDistCeiling(map); WiFiModelLogDistCeiling* wiModel = new WiFiModelLogDistCeiling(map);
wiModel->loadAPs(map, *vap, txp, exp, waf, false); wiModel->loadXML(xmlFile);
this->wiModel = wiModel; this->wiModel = wiModel;
// fire // fire
run(); build(name);
} }
// void fixedParams(const float txp, const float exp, const float waf) {
// // how to handle VAPs
// vap = new VAPGrouper(VAPGrouper::Mode::LAST_MAC_DIGIT_TO_ZERO, VAPGrouper::Aggregation::AVERAGE);
// // setup the model
// WiFiModelLogDistCeiling* wiModel = new WiFiModelLogDistCeiling(map);
// wiModel->loadAPs(map, *vap, txp, exp, waf, false);
// this->wiModel = wiModel;
// // fire
// run();
// }
private: private:
void run() { void build(const std::string& name) {
static int idx = -1; ++idx;
const Offline::FileReader::GroundTruth gtp = reader.getGroundTruth(map, gtIndices);
Plotty* plot = new Plotty(map);
Line<Point3> path; Line<Point3> path;
plot->setGroundTruth(gtIndices); K::GnuplotSplotElementLines* gpPath = new K::GnuplotSplotElementLines();
plot->splot.add(gpPath);
K::Statistics<float>* stats = new K::Statistics<float>();
K::Statistics<float>* statsProbOnGT = new K::Statistics<float>();
pef->add(name, stats);
pef2->add(name, statsProbOnGT);
// process each wifi entry within the offline file // process each wifi entry within the offline file
for (const auto wifi : reader.wifi) { for (const auto wifi : reader.wifi) {
@@ -146,42 +199,71 @@ private:
const WiFiMeasurements mes = vap->group(_mes); const WiFiMeasurements mes = vap->group(_mes);
// error calculation // error calculation
auto func = [&] (float* params) -> double { auto func = [&] (const float* params) -> double {
// crop z to 1 meter // crop z to 1 meter
params[2] = std::round(params[2]); //params[2] = std::round(params[2]);
// suggested position // suggested position
const Point3 pos_m(params[0], params[1], params[2]); const Point3 pos_m(params[0], params[1], params[2]);
const float sigma = 6.0; const float sigma = 8.0;
double prob = 1.0; double prob = 1.0;
// calculate error for above position using the currently available measurements if (1 == 1) {
for (const WiFiMeasurement& m : mes.entries) {
// skip non-FHWS APs // calculate error for above position using the currently available measurements
if (!LeHelper::isFHWS_AP(m.getAP().getMAC())) {continue;} for (const WiFiMeasurement& m : mes.entries) {
// get model's rssi for the given location // skip non-FHWS APs
const float rssi_model = wiModel->getRSSI(m.getAP().getMAC(), pos_m); if (!LeHelper::isFHWS_AP(m.getAP().getMAC())) {continue;}
// get model's rssi for the given location
const float rssi_model = wiModel->getRSSI(m.getAP().getMAC(), pos_m);
// skip APs unknown to the model
if (rssi_model != rssi_model) {
std::cout << "unknown ap: " << m.getAP().getMAC().asString() << std::endl;
continue;
}
// get scan's rssi
const float rssi_scan = m.getRSSI();
// likelyhood
const double p = Distribution::Normal<double>::getProbability(rssi_model, sigma, rssi_scan);
//const double p = Distribution::Region<double>::getProbability(rssi_model, sigma, rssi_scan);
// adjust
prob *= p;
// skip APs unknown to the model
if (rssi_model != rssi_model) {
std::cout << "unknown ap: " << m.getAP().getMAC().asString() << std::endl;
continue;
} }
// get scan's rssi } else {
const float rssi_scan = m.getRSSI();
// likelyhood // //const float limit = -85;
const double p = Distribution::Normal<double>::getProbability(rssi_model, sigma, rssi_scan);
// adjust // for (const AccessPoint& ap : wiModel->getAllAPs()) {
prob *= p;
// // get model's rssi for the given location
// float rssi_model = wiModel->getRSSI(ap.getMAC(), pos_m);
// if (rssi_model < limit) {rssi_model = limit;}
// // get scan's rssi
// const WiFiMeasurement* mesModel = mes.getForMac(ap.getMAC());
// float rssi_scan = (mesModel) ? (mesModel->getRSSI()) : (limit);
// if (rssi_scan < limit) {rssi_scan = limit;}
// // likelyhood
// const double p = Distribution::Normal<double>::getProbability(rssi_model, sigma, rssi_scan);
// // adjust
// prob *= p;
// }
} }
@@ -190,36 +272,80 @@ private:
}; };
std::minstd_rand gen; // parameters
std::uniform_real_distribution<float> distX(mapBBox.getMin().x, mapBBox.getMax().x);
std::uniform_real_distribution<float> distY(mapBBox.getMin().y, mapBBox.getMax().y);
std::uniform_real_distribution<float> distZ(mapBBox.getMin().z, mapBBox.getMax().z);
// initializer for the optimizer: random position within the map's bbox
auto init = [&] (const int childIdx, float* params) {
params[0] = distX(gen);
params[1] = distY(gen);
params[2] = distZ(gen);
};
float params[3]; float params[3];
K::NumOptAlgoGenetic<float> opt(3);
// std::minstd_rand gen;
// std::uniform_real_distribution<float> distX(mapBBox.getMin().x, mapBBox.getMax().x);
// std::uniform_real_distribution<float> distY(mapBBox.getMin().y, mapBBox.getMax().y);
// std::uniform_real_distribution<float> distZ(mapBBox.getMin().z, mapBBox.getMax().z);
// // initializer for the optimizer: random position within the map's bbox
// auto init = [&] (const int childIdx, float* params) {
// (void) childIdx;
// params[0] = distX(gen);
// params[1] = distY(gen);
// params[2] = distZ(gen);
// };
// K::NumOptAlgoGenetic<float> opt(3);
// opt.setPopulationSize(400);
// opt.setMaxIterations(20);
// opt.calculateOptimum(func, params, init);
using LeOpt = K::NumOptAlgoRangeRandom<float>;
const std::vector<LeOpt::MinMax> valRegion = {
LeOpt::MinMax(mapBBox.getMin().x, mapBBox.getMax().x), // x
LeOpt::MinMax(mapBBox.getMin().y, mapBBox.getMax().y), // y
LeOpt::MinMax(mapBBox.getMin().z, mapBBox.getMax().z), // z
};
K::NumOptAlgoRangeRandom<float> opt(valRegion);
opt.setPopulationSize(200); opt.setPopulationSize(200);
opt.setMaxIterations(20); opt.setNumIerations(50);
opt.calculateOptimum(func, params, init); //opt.calculateOptimum(func, params);
std::cout << params[0] << "," << params[1] << "," << params[2] << std::endl; std::cout << params[0] << "," << params[1] << "," << params[2] << std::endl;
path.add(Point3(params[0], params[1], params[2])); const Point3 curEst(params[0], params[1], params[2]);
path.add(curEst);
// draw a smoothed version of th epath const Timestamp ts = mes.entries.front().getTimestamp();
plot->pathEst.clear();
// draw a smoothed version of the path
gpPath->clear();
for (const Point3 p : path.getAverage(2)) { for (const Point3 p : path.getAverage(2)) {
const K::GnuplotPoint3 gp3(p.x, p.y, p.z); const K::GnuplotPoint3 gp3(p.x, p.y, p.z);
plot->pathEst.add(gp3); gpPath->add(gp3);
} }
// groud-truth
const Point3 gt = gtp.get(ts);
plot->gp << "set arrow 1 at " << gt.x << "," << gt.y << "," << gt.z << " to " << gt.x << "," << gt.y << "," << (gt.z+1) << "\n";
// error
//const float err_m = gt.xy().getDistance(curEst.xy());
const float err_m = gt.getDistance(curEst);
stats->add(err_m);
float gtFloat[3] = {gt.x, gt.y, gt.z};
const double probOnGT = -func(gtFloat);
const double logProbOnGT = -log(probOnGT);
statsProbOnGT->add(logProbOnGT);
// plot err
pet->addErr(ts, err_m, idx);
pet2->addErr(ts, logProbOnGT/mes.entries.size(), idx);
// fire
plot->plot(); plot->plot();
// pet->plot();
// pef->plot();
pet2->plot();
pef2->plot();
} }

View File

@@ -26,7 +26,7 @@
#include <KLib/math/statistics/Statistics.h> #include <KLib/math/statistics/Statistics.h>
#include "Structs.h" #include "Structs.h"
#include "Plotty.h" #include "plots/Plotty.h"
#include "CSV.h" #include "CSV.h"
#include "Helper.h" #include "Helper.h"

144
Helper.h
View File

@@ -6,7 +6,7 @@
#include <Indoor/sensors/radio/setup/WiFiFingerprints.h> #include <Indoor/sensors/radio/setup/WiFiFingerprints.h>
#include <Indoor/floorplan/v2/Floorplan.h> #include <Indoor/floorplan/v2/Floorplan.h>
#include "Plotty.h" #include "plots/Plotty.h"
class LeHelper { class LeHelper {
@@ -28,31 +28,69 @@ public:
} }
static inline bool isOutdoor(const WiFiFingerprint& fp) {
const Point3 pos = fp.pos_m;
// garden
if (pos == Point3(21, 12, 1.3)) {return true;}
if (pos == Point3(21, 27, 1.3)) {return true;}
if (pos == Point3(39, 12, 1.3)) {return true;}
if (pos == Point3(39, 27, 1.3)) {return true;}
if (pos == Point3(67, 12, 1.3)) {return true;}
if (pos == Point3(67, 27, 1.3)) {return true;}
if (pos == Point3(88, 12, 1.3)) {return true;}
if (pos == Point3(88, 27, 1.3)) {return true;}
// front door
if (pos == Point3(94, 43, 4+1.3)) {return true;}
if (pos == Point3(110, 43, 4+1.3)) {return true;}
if (pos == Point3(94, 55, 4+1.3)) {return true;}
if (pos == Point3(110, 55, 4+1.3)) {return true;}
if (pos == Point3(67.2, 55, 4+1.3)) {return true;}
return false;
}
static inline bool isStaircase(const WiFiFingerprint& fp) {
const Point3 pos = fp.pos_m;
// floor 0
if (pos == Point3(67.2, 48.5, 1.3)) {return true;}
if (pos == Point3(11.2, 48.3, 1.3)) {return true;}
if (pos == Point3(16.5, 11.9, 1.3)) {return true;}
if (pos == Point3(57.4, 2.6, 1.3)) {return true;}
if (pos == Point3(57.4, -3.2, 3.0)) {return true;}
// floor 1
if (pos == Point3(67.2, 48.3, 4+1.3)) {return true;}
if (pos == Point3(11.4, 48.3, 4+1.3)) {return true;}
if (pos == Point3(16.1, 11.9, 4+1.3)) {return true;}
if (pos == Point3(57.4, 2.4, 4+1.3)) {return true;}
// floor 2
if (pos == Point3(67.0, 48.5, 3.4+4+1.3)) {return true;}
if (pos == Point3(11.6, 48.5, 3.4+4+1.3)) {return true;}
if (pos == Point3(16.3, 11.9, 3.4+4+1.3)) {return true;}
// floor 3
if (pos == Point3(67.0, 48.3, 3.4+3.4+4+1.3)) {return true;}
if (pos == Point3(11.4, 48.5, 3.4+3.4+4+1.3)) {return true;}
if (pos == Point3(16.3, 11.9, 3.4+3.4+4+1.3)) {return true;}
return false;
}
/** remove all outdoor fingerprints */ /** remove all outdoor fingerprints */
static WiFiFingerprints removeOutdoor(const WiFiFingerprints& inp) { static WiFiFingerprints removeOutdoor(const WiFiFingerprints& inp) {
WiFiFingerprints res; WiFiFingerprints res;
for (const WiFiFingerprint& fp : inp.getFingerprints()) { for (const WiFiFingerprint& fp : inp.getFingerprints()) {
const Point3 pos = fp.pos_m; if (isOutdoor(fp)) {continue;}
// garden
if (pos == Point3(21, 12, 1.3)) {continue;}
if (pos == Point3(21, 27, 1.3)) {continue;}
if (pos == Point3(39, 12, 1.3)) {continue;}
if (pos == Point3(39, 27, 1.3)) {continue;}
if (pos == Point3(67, 12, 1.3)) {continue;}
if (pos == Point3(67, 27, 1.3)) {continue;}
if (pos == Point3(88, 12, 1.3)) {continue;}
if (pos == Point3(88, 27, 1.3)) {continue;}
// front door
if (pos == Point3(94, 43, 4+1.3)) {continue;}
if (pos == Point3(110, 43, 4+1.3)) {continue;}
if (pos == Point3(94, 55, 4+1.3)) {continue;}
if (pos == Point3(110, 55, 4+1.3)) {continue;}
if (pos == Point3(67.2, 55, 4+1.3)) {continue;}
res.add(fp); res.add(fp);
} }
@@ -65,38 +103,52 @@ public:
WiFiFingerprints res; WiFiFingerprints res;
for (const WiFiFingerprint& fp : inp.getFingerprints()) { for (const WiFiFingerprint& fp : inp.getFingerprints()) {
const Point3 pos = fp.pos_m; if (isStaircase(fp)) {continue;}
// floor 0
if (pos == Point3(67.2, 48.5, 1.3)) {continue;}
if (pos == Point3(11.2, 48.3, 1.3)) {continue;}
if (pos == Point3(16.5, 11.9, 1.3)) {continue;}
if (pos == Point3(57.4, 2.6, 1.3)) {continue;}
if (pos == Point3(57.4, -3.2, 3.0)) {continue;}
// floor 1
if (pos == Point3(67.2, 48.3, 4+1.3)) {continue;}
if (pos == Point3(11.4, 48.3, 4+1.3)) {continue;}
if (pos == Point3(16.1, 11.9, 4+1.3)) {continue;}
if (pos == Point3(57.4, 2.4, 4+1.3)) {continue;}
// floor 2
if (pos == Point3(67.0, 48.5, 3.4+4+1.3)) {continue;}
if (pos == Point3(11.6, 48.5, 3.4+4+1.3)) {continue;}
if (pos == Point3(16.3, 11.9, 3.4+4+1.3)) {continue;}
// floor 3
if (pos == Point3(67.0, 48.3, 3.4+3.4+4+1.3)) {continue;}
if (pos == Point3(11.4, 48.5, 3.4+3.4+4+1.3)) {continue;}
if (pos == Point3(16.3, 11.9, 3.4+3.4+4+1.3)) {continue;}
res.add(fp); res.add(fp);
} }
return res; return res;
} }
static WiFiFingerprints removeIndoor(const WiFiFingerprints& inp) {
WiFiFingerprints res;
for (const WiFiFingerprint& fp : inp.getFingerprints()) {
if (!isStaircase(fp) && !isOutdoor(fp)) {continue;}
res.add(fp);
}
return res;
}
static WiFiFingerprints removeIf(const WiFiFingerprints& inp, std::function<bool(const WiFiFingerprint&)> remove) {
WiFiFingerprints res;
for (const WiFiFingerprint& fp : inp.getFingerprints()) {
if (remove(fp)) {continue;}
res.add(fp);
}
return res;
}
static WiFiFingerprints onlyOutdoor(const WiFiFingerprints& inp) {
WiFiFingerprints res;
for (const WiFiFingerprint& fp : inp.getFingerprints()) {
if (!isOutdoor(fp)) {continue;}
res.add(fp);
}
return res;
}
static void plot(const Floorplan::IndoorMap* map, const WiFiFingerprints& inp, const std::string& title = "Fingerprints") { static void plot(const Floorplan::IndoorMap* map, const WiFiFingerprints& inp, const std::string& title = "Fingerprints") {
Plotty* p = new Plotty(map); Plotty* p = new Plotty(map);

View File

@@ -1,100 +0,0 @@
#ifndef PLOTERRFUNC_H
#define PLOTERRFUNC_H
#include <KLib/math/statistics/Statistics.h>
#include <KLib/misc/gnuplot/Gnuplot.h>
#include <KLib/misc/gnuplot/GnuplotPlot.h>
#include <KLib/misc/gnuplot/GnuplotPlotElementLines.h>
class PlotErrFunc {
struct Entry {
std::string name;
K::Statistics<float> stats;
Entry(const std::string& name, const K::Statistics<float>& stats) : name(name), stats(stats) {;}
};
std::vector<Entry> entries;
std::vector<K::GnuplotPlotElementLines*> lines;
K::Gnuplot gp;
K::GnuplotPlot gplot;
//std::vector<std::string> colors = {"#000000", "#ff0000", "#00bb00", "#0000ff"};
std::vector<std::string> colors = {"#000000", "#999999", "#0000ff", "#9999ff"};
std::string codeFile;
public:
/** ctor with x-axis label */
PlotErrFunc(const std::string& xLabel, const std::string& yLabel) {
gplot.setLabelX(xLabel);
gplot.setLabelY(yLabel);
}
/** add one curve */
void add(const std::string name, const K::Statistics<float> stats) {
entries.push_back(Entry(name, stats));
K::GnuplotPlotElementLines* gpel = new K::GnuplotPlotElementLines();
gpel->setTitle(name);
gpel->setLineWidth(2);
lines.push_back(gpel);
gplot.add(gpel);
}
void clear() {
entries.clear();
}
K::Gnuplot& getGP() {
return gp;
}
void writeCodeTo(const std::string& file) {
this->codeFile = file;
}
/** plot all curves */
void plot() {
gp << "set grid\n";
for (size_t i = 0; i < entries.size(); ++i) {
const Entry e = entries[i];
K::GnuplotPlotElementLines* line = lines[i];
line->clear();
//line.setTitle(e.name);
line->setColorHex(colors[i]);
// 0 - 80%
for (int i = 0; i <= 85; i+= 5) {
const float q = i / 100.0f;
const float y = e.stats.getQuantile(q);
K::GnuplotPoint2 gp(y, q*100);
line->add(gp);
}
}
gp.draw(gplot);
if (codeFile != "") {
std::ofstream out(codeFile);
out << gp.getBuffer();
out.close();
}
gp.flush();
}
};
#endif // PLOTERRFUNC_H

View File

@@ -20,6 +20,7 @@ namespace Settings {
const std::string wifiAllOptPar = pathWiFi + "allOptPar.xml"; const std::string wifiAllOptPar = pathWiFi + "allOptPar.xml";
const std::string wifiEachOptPar = pathWiFi + "eachOptPar.xml"; const std::string wifiEachOptPar = pathWiFi + "eachOptPar.xml";
const std::string wifiEachOptParPos = pathWiFi + "eachOptParPos.xml"; const std::string wifiEachOptParPos = pathWiFi + "eachOptParPos.xml";
const std::string wifiEachOptParPosNoStairNoOut = pathWiFi + "eachOptParPosNoStairNoOut.xml";
@@ -33,10 +34,10 @@ namespace Settings {
float smartphoneAboveGround = 1.3; float smartphoneAboveGround = 1.3;
namespace IMU { namespace IMU {
const float turnSigma = 1.5 + 0.5;//1.5; const float turnSigma = 1.25;//1.5;
const float stepLength = 0.85; const float stepLength = 0.70;
const float stepSigma = 0.40; const float stepSigma = 0.45;
const float absHeadSigma = 90; const float absHeadSigma = 80;
} }
namespace Grid { namespace Grid {
@@ -57,19 +58,19 @@ namespace Settings {
namespace WiFiModel { namespace WiFiModel {
constexpr float sigma = 8.0; constexpr float sigma = 11.0;
/** if the wifi-signal-strengths are stored on the grid-nodes, this needs a grid rebuild! */ /** if the wifi-signal-strengths are stored on the grid-nodes, this needs a grid rebuild! */
constexpr float TXP = -44; // constexpr float TXP = -44;
constexpr float EXP = 2.5; // constexpr float EXP = 2.5;
constexpr float WAF = -8.0; // constexpr float WAF = -8.0;
// constexpr float TXP = -64.5896; // constexpr float TXP = -64.5896;
// constexpr float EXP = 1.25986; // constexpr float EXP = 1.25986;
// constexpr float WAF = -2.47467; // constexpr float WAF = -2.47467;
// how to perform VAP grouping // how to perform VAP grouping
const VAPGrouper vg_calib = VAPGrouper(VAPGrouper::Mode::LAST_MAC_DIGIT_TO_ZERO, VAPGrouper::Aggregation::AVERAGE); const VAPGrouper vg_calib = VAPGrouper(VAPGrouper::Mode::LAST_MAC_DIGIT_TO_ZERO, VAPGrouper::Aggregation::MEDIAN);
const VAPGrouper vg_eval = VAPGrouper(VAPGrouper::Mode::LAST_MAC_DIGIT_TO_ZERO, VAPGrouper::Aggregation::AVERAGE); const VAPGrouper vg_eval = VAPGrouper(VAPGrouper::Mode::LAST_MAC_DIGIT_TO_ZERO, VAPGrouper::Aggregation::MEDIAN);
} }

301
main.cpp
View File

@@ -21,20 +21,23 @@
#include "Settings.h" #include "Settings.h"
#include "EvalCompareOpt.h" #include "EvalCompareOpt.h"
#include "EvalCompareOpt2.h"
#include "EvalApOpt.h" #include "EvalApOpt.h"
#include "EvalData.h" #include "EvalData.h"
#include "EvalWiFi.h" #include "EvalWiFi.h"
#include "EvalWiFiSigStrength.h" #include "EvalWiFiSigStrength.h"
#include "pf/EvalWalk.h" #include "pf/EvalWalk.h"
#include "PlotErrFunc.h" #include "plots/PlotErrFunc.h"
// build plots for the paper // build plots for the paper
void paperOutputs() { void paperOutputs() {
Floorplan::IndoorMap* map = Floorplan::Reader::readFromFile(Settings::fMap); Floorplan::IndoorMap* map = Floorplan::Reader::readFromFile(Settings::fMap);
if (1==1){ // show fingerprints as plot
if (1==0){
EvalWiFiSigStrength sig(Settings::fMap, Settings::fCalib); EvalWiFiSigStrength sig(Settings::fMap, Settings::fCalib);
Plotty* p = new Plotty(map); Plotty* p = new Plotty(map);
p->writeCodeTo(Settings::fPathGFX + "compare-wifi-in-out.gp"); p->writeCodeTo(Settings::fPathGFX + "compare-wifi-in-out.gp");
@@ -52,22 +55,48 @@ void paperOutputs() {
delete p; delete p;
} }
if (1==1) { // perform varios AP-param optimizations
// generate error plot showing the performance of each
// save the resulting wifi-models to XML for later re-use during the walk-eval <<<<<< !!!!
if (1 == 1) {
const bool ignoreStaircases = false; // // use walks?
const bool ignoreOutdoor = false; // std::vector<std::pair<std::string, std::vector<int>>> calibWalks = {
EvalCompareOptAllFixed allFixed(Settings::fMap, Settings::fCalib, ignoreStaircases, ignoreOutdoor); // std::make_pair(Settings::path1a, Settings::GroundTruth::path1),
// std::make_pair(Settings::path1b, Settings::GroundTruth::path1),
// std::make_pair(Settings::path2a, Settings::GroundTruth::path2),
// std::make_pair(Settings::path2b, Settings::GroundTruth::path2),
// };
// EvalCompareOpt2 opt1(Settings::fMap, calibWalks);
K::Statistics<float> s1 = allFixed.fixedPosFixedParamsForAll(); //BREAK; auto remove = [] (const WiFiFingerprint& fp) -> bool {
K::Statistics<float> s2 = allFixed.fixedPosOptParamsForAll(); //BREAK; return fp.pos_m.z != 4;
K::Statistics<float> s3 = allFixed.fixedPosOptParamsForEach(); //BREAK; };
K::Statistics<float> s4 = allFixed.optPosOptParamsForEach(); //BREAK;
// use fingerprints?
bool ignoreStaircases = false;
bool ignoreOutdoor = false;
bool ignoreIndoor = false;
EvalCompareOpt2 opt1(Settings::fMap, Settings::fCalib, remove);
EvalCompareOpt2::Result s1 = opt1.fixedPosFixedParamsForAll(); //BREAK;
EvalCompareOpt2::Result s2 = opt1.fixedPosOptParamsForAll(); //BREAK;
EvalCompareOpt2::Result s3 = opt1.fixedPosOptParamsForEach(); //BREAK;
EvalCompareOpt2::Result s4 = opt1.optPosOptParamsForEach(); //BREAK;
// save models to file
s1.model.saveXML(Settings::wifiAllFixed);
s2.model.saveXML(Settings::wifiAllOptPar);
s3.model.saveXML(Settings::wifiEachOptPar);
s4.model.saveXML(Settings::wifiEachOptParPos);
PlotErrFunc pef("\\small{error (dB)}", "\\small{fingerprints (\\%)}"); PlotErrFunc pef("\\small{error (dB)}", "\\small{fingerprints (\\%)}");
pef.add("\\small{empiric}", s1); pef.add("\\small{empiric}", &s1.errSingle);
pef.add("\\small{real pos, opt params}", s2); pef.add("\\small{real pos, opt params}", &s2.errSingle);
pef.add("\\small{real pos, opt params [per AP]}", s3); pef.add("\\small{real pos, opt params [per AP]}", &s3.errSingle);
pef.add("\\small{opt pos, opt params [per AP]}", s4); pef.add("\\small{opt pos, opt params [per AP]}", &s4.errSingle);
pef.getGP().setTerminal("epslatex", K::GnuplotSize(8.5, 5)); pef.getGP().setTerminal("epslatex", K::GnuplotSize(8.5, 5));
pef.getGP().setOutput(Settings::fPathGFX + "wifi-opt-error-hist-methods.tex"); pef.getGP().setOutput(Settings::fPathGFX + "wifi-opt-error-hist-methods.tex");
@@ -77,25 +106,87 @@ void paperOutputs() {
pef.getGP() << "set tmargin 0.4\n"; pef.getGP() << "set tmargin 0.4\n";
pef.plot(); pef.plot();
return;
//sleep(1000);
} }
// error histogram all pos, all params, between in/out/stair, in/out, in/stair, in // REPLACED BY EVAL OPT 2
if(1==1){ // // perform varios AP-param optimizations
EvalCompareOptAllFixed e1(Settings::fMap, Settings::fCalib, false, false); // // generate error plot showing the performance of each
EvalCompareOptAllFixed e2(Settings::fMap, Settings::fCalib, true, false); // // save the resulting wifi-models to XML for later re-use during the walk-eval <<<<<< !!!!
EvalCompareOptAllFixed e3(Settings::fMap, Settings::fCalib, false, true); // if (1 == 0) {
EvalCompareOptAllFixed e4(Settings::fMap, Settings::fCalib, true, true);
K::Statistics<float> s1 = e1.optPosOptParamsForEach(); // bool ignoreStaircases = false;
K::Statistics<float> s2 = e2.optPosOptParamsForEach(); // bool ignoreOutdoor = false;
K::Statistics<float> s3 = e3.optPosOptParamsForEach(); // bool ignoreIndoor = false;
K::Statistics<float> s4 = e4.optPosOptParamsForEach();
// // use walks?
//// std::vector<std::pair<std::string, std::vector<int>>> calibWalks = {
//// std::make_pair(Settings::path1a, Settings::GroundTruth::path1),
//// std::make_pair(Settings::path1b, Settings::GroundTruth::path1),
//// std::make_pair(Settings::path2a, Settings::GroundTruth::path2),
//// std::make_pair(Settings::path2b, Settings::GroundTruth::path2),
//// };
//// EvalCompareOpt opt1(Settings::fMap, calibWalks);
// // use fingerprints?
// EvalCompareOpt opt1(Settings::fMap, Settings::fCalib, ignoreStaircases, ignoreOutdoor, ignoreIndoor);
// EvalCompareOpt::Result s1 = opt1.fixedPosFixedParamsForAll(); //BREAK;
// EvalCompareOpt::Result s2 = opt1.fixedPosOptParamsForAll(); //BREAK;
// EvalCompareOpt::Result s3 = opt1.fixedPosOptParamsForEach(); //BREAK;
// EvalCompareOpt::Result s4 = opt1.optPosOptParamsForEach(); //BREAK;
// ignoreStaircases = true;
// ignoreOutdoor = true;
// EvalCompareOpt opt2(Settings::fMap, Settings::fCalib, ignoreStaircases, ignoreOutdoor, ignoreIndoor);
// EvalCompareOpt::Result s4b = opt2.optPosOptParamsForEach(); //BREAK;
// // save models to file
// s1.model.saveXML(Settings::wifiAllFixed);
// s2.model.saveXML(Settings::wifiAllOptPar);
// s3.model.saveXML(Settings::wifiEachOptPar);
// s4.model.saveXML(Settings::wifiEachOptParPos);
// s4b.model.saveXML(Settings::wifiEachOptParPosNoStairNoOut);
// PlotErrFunc pef("\\small{error (dB)}", "\\small{fingerprints (\\%)}");
// pef.add("\\small{empiric}", s1.errAbs);
// pef.add("\\small{real pos, opt params}", s2.errAbs);
// pef.add("\\small{real pos, opt params [per AP]}", s3.errAbs);
// pef.add("\\small{opt pos, opt params [per AP]}", s4.errAbs);
// pef.add("\\small{opt pos, opt params [per AP, no stair, no out]}", s4b.errAbs);
// pef.getGP().setTerminal("epslatex", K::GnuplotSize(8.5, 5));
// pef.getGP().setOutput(Settings::fPathGFX + "wifi-opt-error-hist-methods.tex");
// pef.writeCodeTo(Settings::fPathGFX + "wifi-opt-error-hist-methods.gp");
// pef.getGP() << "set key right bottom width -4 samplen 0.5\n";
// pef.getGP() << "set rmargin 0.4\n";
// pef.getGP() << "set tmargin 0.4\n";
// pef.plot();
// }
// error histogram all pos, all params, between in/out/stair, in/out, in/stair, in
if(1==0){
EvalCompareOpt e1(Settings::fMap, Settings::fCalib, false, false, false);
EvalCompareOpt e2(Settings::fMap, Settings::fCalib, true, false, false);
EvalCompareOpt e3(Settings::fMap, Settings::fCalib, false, true, false);
EvalCompareOpt e4(Settings::fMap, Settings::fCalib, true, true, false);
K::Statistics<float> s1 = e1.optPosOptParamsForEach().errAbs;
K::Statistics<float> s2 = e2.optPosOptParamsForEach().errAbs;
K::Statistics<float> s3 = e3.optPosOptParamsForEach().errAbs;
K::Statistics<float> s4 = e4.optPosOptParamsForEach().errAbs;
PlotErrFunc pef("\\small{error (dB)}", "\\small{fingerprints (\\%)}"); PlotErrFunc pef("\\small{error (dB)}", "\\small{fingerprints (\\%)}");
pef.add("\\small{floor + stairs + out}", s1); pef.add("\\small{floor + stairs + out}", &s1);
pef.add("\\small{floor + out}", s2); pef.add("\\small{floor + out}", &s2);
pef.add("\\small{floor + stairs}", s3); pef.add("\\small{floor + stairs}", &s3);
pef.add("\\small{floor}", s4); pef.add("\\small{floor}", &s4);
pef.getGP().setTerminal("epslatex", K::GnuplotSize(8.5, 5)); pef.getGP().setTerminal("epslatex", K::GnuplotSize(8.5, 5));
pef.getGP().setOutput(Settings::fPathGFX + "wifi-opt-error-hist-stair-outdoor.tex"); pef.getGP().setOutput(Settings::fPathGFX + "wifi-opt-error-hist-stair-outdoor.tex");
@@ -107,14 +198,106 @@ void paperOutputs() {
} }
// wifi issue for path1
if (1 == 0) {
Offline::FileReader reader(Settings::path1a);
PlotWifiMeasurements plot;
for (int i = 0; i < 60; ++i) {
const WiFiMeasurements mes = reader.getWiFiGroupedByTime().at(i).data;
const WiFiMeasurements mes2 = Settings::WiFiModel::vg_eval.group(mes);
plot.add(mes2);
}
K::GnuplotObjectRectangle rect(
K::GnuplotCoordinate2(0, K::GnuplotCoordinateSystem::FIRST, 0, K::GnuplotCoordinateSystem::GRAPH),
K::GnuplotCoordinate2(10, K::GnuplotCoordinateSystem::FIRST, 1, K::GnuplotCoordinateSystem::GRAPH),
K::GnuplotFill(K::GnuplotFillStyle::SOLID, K::GnuplotColor::fromRGB(128,128,128), 0.5),
K::GnuplotStroke()
);
plot.getPlot().setGrid(true);
plot.getPlot().setLegend(false);
plot.getPlot().getObjects().add(&rect);
plot.plot();
sleep(100);
}
}
void testWAF() {
Floorplan::Ceilings ceilings;
ceilings.addCeiling(3);
ceilings.addCeiling(6);
ceilings.addCeiling(9);
ceilings.addCeiling(12);
K::Gnuplot gp;
K::GnuplotPlot gplot;
K::GnuplotPlotElementLines lines; gplot.add(&lines);
const Point3 posAP(0, 0, 8);
for (float z = 0; z < 15; z += 0.1) {
const Point3 posMe(0, 0, z);
float factor = ceilings.numCeilingsBetweenFloat(posAP, posMe);
lines.add({z, factor});
}
gp.draw(gplot);
gp.flush();
sleep(1000);
} }
int main(void) { int main(void) {
// paperOutputs(); return 0; //testWAF();
if (1 == 1) { //paperOutputs(); // return 0;
// calib error in/out
if (1 == 0) {
// outdoor only
bool ignoreStaircases = true;
bool ignoreOutdoor = false;
bool ignoreIndoor = true;
EvalCompareOpt opt1(Settings::fMap, Settings::fCalib, ignoreStaircases, ignoreOutdoor, ignoreIndoor);
EvalCompareOpt::Result s1 = opt1.optPosOptParamsForEach();
std::cout << s1.errAbs.asString() << std::endl;
// stairs only
ignoreStaircases = false;
ignoreOutdoor = true;
ignoreIndoor = true;
EvalCompareOpt opt3(Settings::fMap, Settings::fCalib, ignoreStaircases, ignoreOutdoor, ignoreIndoor);
EvalCompareOpt::Result s3 = opt3.optPosOptParamsForEach();
std::cout << s3.errAbs.asString() << std::endl;
// indoor only
ignoreStaircases = true;
ignoreOutdoor = true;
ignoreIndoor = false;
EvalCompareOpt opt2(Settings::fMap, Settings::fCalib, ignoreStaircases, ignoreOutdoor, ignoreIndoor);
EvalCompareOpt::Result s2 = opt2.optPosOptParamsForEach();
std::cout << s2.errAbs.asString() << std::endl;
}
// walks
if (1 == 0) {
Floorplan::IndoorMap* map = Floorplan::Reader::readFromFile(Settings::fMap);; Floorplan::IndoorMap* map = Floorplan::Reader::readFromFile(Settings::fMap);;
EvalWalk walk(map); EvalWalk walk(map);
walk.walk1(); walk.walk1();
@@ -139,12 +322,19 @@ int main(void) {
// test wifi within data files // test wifi within data files
if (1 == 0) { if (1 == 1) {
EvalWiFi ew1(Settings::fMap, Settings::path1a, Settings::GroundTruth::path1);
//EvalWiFi ew1(Settings::fMap, Settings::path2a, Settings::GroundTruth::path2); //EvalWiFi ew1(Settings::fMap, Settings::path2a, Settings::GroundTruth::path2);
EvalWiFi ew1(Settings::fMap, Settings::path2a, Settings::GroundTruth::path2);
//ew1.fixedParams(-40, 2.5, -8); BREAK; //ew1.fixedParams(-40, 2.5, -8); BREAK;
//ew1.fixedParams(-64.5905, 1.25988, -2.47863); BREAK; //ew1.fixedParams(-64.5905, 1.25988, -2.47863); BREAK;
ew1.fixedParams(-59.4903,1.52411,-3.25077); BREAK; //ew1.fixedParams(-59.4903,1.52411,-3.25077); BREAK;
//ew1.load(Settings::wifiEachOptParPos);
ew1.load(Settings::wifiAllFixed, "empirc");
ew1.load(Settings::wifiAllOptPar, "opt params all APs");
ew1.load(Settings::wifiEachOptPar, "opt params each AP");
ew1.load(Settings::wifiEachOptParPos, "everything opt");
sleep(1000);
} }
@@ -161,22 +351,23 @@ int main(void) {
// compare wifi opt methods // compare wifi opt methods
if (1 == 1) { if (1 == 0) {
const bool ignoreStaircases = false; const bool ignoreStaircases = false;
const bool ignoreOutdoor = false; const bool ignoreOutdoor = false;
EvalCompareOptAllFixed allFixed(Settings::fMap, Settings::fCalib, ignoreStaircases, ignoreOutdoor); const bool ignoreIndoor = false;
EvalCompareOpt opt(Settings::fMap, Settings::fCalib, ignoreStaircases, ignoreOutdoor, ignoreIndoor);
K::Statistics<float> s1 = allFixed.fixedPosFixedParamsForAll(); //BREAK; EvalCompareOpt::Result s1 = opt.fixedPosFixedParamsForAll(); //BREAK;
K::Statistics<float> s2 = allFixed.fixedPosOptParamsForAll(); //BREAK; EvalCompareOpt::Result s2 = opt.fixedPosOptParamsForAll(); //BREAK;
K::Statistics<float> s3 = allFixed.fixedPosOptParamsForEach(); //BREAK; EvalCompareOpt::Result s3 = opt.fixedPosOptParamsForEach(); //BREAK;
K::Statistics<float> s4 = allFixed.optPosOptParamsForEach(); //BREAK; EvalCompareOpt::Result s4 = opt.optPosOptParamsForEach(); //BREAK;
PlotErrFunc pef("error (dB)", "fingerprints (%)"); PlotErrFunc pef("error (dB)", "fingerprints (%)");
pef.add("empiric", s1); pef.add("empiric", &s1.errAbs);
pef.add("real pos, opt params [same for all]", s2); pef.add("real pos, opt params [same for all]", &s2.errAbs);
pef.add("real pos, opt params [for each]", s3); pef.add("real pos, opt params [for each]", &s3.errAbs);
pef.add("opt pos, opt params [for each]", s4); pef.add("opt pos, opt params [for each]", &s4.errAbs);
pef.plot(); pef.plot();
} }
@@ -184,21 +375,21 @@ int main(void) {
// compare leaving out fingerprints // compare leaving out fingerprints
if (1 == 0) { if (1 == 0) {
EvalCompareOptAllFixed e1(Settings::fMap, Settings::fCalib, false, false); EvalCompareOpt e1(Settings::fMap, Settings::fCalib, false, false, false);
EvalCompareOptAllFixed e2(Settings::fMap, Settings::fCalib, true, false); EvalCompareOpt e2(Settings::fMap, Settings::fCalib, true, false, false);
EvalCompareOptAllFixed e3(Settings::fMap, Settings::fCalib, false, true); EvalCompareOpt e3(Settings::fMap, Settings::fCalib, false, true, false);
EvalCompareOptAllFixed e4(Settings::fMap, Settings::fCalib, true, true); EvalCompareOpt e4(Settings::fMap, Settings::fCalib, true, true, false);
K::Statistics<float> s1 = e1.optPosOptParamsForEach(); K::Statistics<float> s1 = e1.optPosOptParamsForEach().errAbs;
K::Statistics<float> s2 = e2.optPosOptParamsForEach(); K::Statistics<float> s2 = e2.optPosOptParamsForEach().errAbs;
K::Statistics<float> s3 = e3.optPosOptParamsForEach(); K::Statistics<float> s3 = e3.optPosOptParamsForEach().errAbs;
K::Statistics<float> s4 = e4.optPosOptParamsForEach(); K::Statistics<float> s4 = e4.optPosOptParamsForEach().errAbs;
PlotErrFunc pef("error (dB)", "fingerprints (%)"); PlotErrFunc pef("error (dB)", "fingerprints (%)");
pef.add("floor + stairs + out", s1); pef.add("floor + stairs + out", &s1);
pef.add("floor + out", s2); pef.add("floor + out", &s2);
pef.add("floor + stairs", s3); pef.add("floor + stairs", &s3);
pef.add("floor", s4); pef.add("floor", &s4);
pef.plot(); pef.plot();
} }

View File

@@ -8,8 +8,9 @@
#include <KLib/math/filter/particles/estimation/ParticleFilterEstimationWeightedAverage.h> #include <KLib/math/filter/particles/estimation/ParticleFilterEstimationWeightedAverage.h>
#include <KLib/math/filter/particles/resampling/ParticleFilterResamplingSimple.h> #include <KLib/math/filter/particles/resampling/ParticleFilterResamplingSimple.h>
#include <KLib/math/filter/particles/resampling/ParticleFilterResamplingPercent.h> #include <KLib/math/filter/particles/resampling/ParticleFilterResamplingPercent.h>
#include <KLib/math/filter/particles/resampling/ParticleFilterResamplingNEff.h>
#include "../PlotErrFunc.h" #include "../plots/PlotErrFunc.h"
#include <thread> #include <thread>
@@ -23,6 +24,7 @@
#include "Indoor/sensors/radio/VAPGrouper.h" #include "Indoor/sensors/radio/VAPGrouper.h"
#include "Indoor/sensors/imu/StepDetection.h" #include "Indoor/sensors/imu/StepDetection.h"
#include "Indoor/sensors/imu/TurnDetection.h" #include "Indoor/sensors/imu/TurnDetection.h"
#include "Indoor/sensors/activity/ActivityDetector.h"
#include "Indoor/floorplan/v2/Floorplan.h" #include "Indoor/floorplan/v2/Floorplan.h"
#include "Indoor/floorplan/v2/FloorplanReader.h" #include "Indoor/floorplan/v2/FloorplanReader.h"
@@ -34,6 +36,9 @@
#include "Indoor/sensors/offline/FileReader.h" #include "Indoor/sensors/offline/FileReader.h"
#include "../Helper.h" #include "../Helper.h"
#include "PF.h" #include "PF.h"
#include "../plots/PlotErrTime.h"
#include <Indoor/debug/PlotWifiMeasurements.h>
#include <Indoor/sensors/offline/FilePlayer.h> #include <Indoor/sensors/offline/FilePlayer.h>
#include <Indoor/sensors/offline/FileReader.h> #include <Indoor/sensors/offline/FileReader.h>
@@ -48,6 +53,7 @@ class EvalWalk : public Offline::Listener {
WiFiModelLogDistCeiling wifiModel; WiFiModelLogDistCeiling wifiModel;
Plotty plotty; Plotty plotty;
PlotWifiMeasurements plotWifi;
Offline::FileReader reader; Offline::FileReader reader;
Offline::FilePlayer player; Offline::FilePlayer player;
@@ -61,18 +67,24 @@ class EvalWalk : public Offline::Listener {
StepDetection stepDetect; StepDetection stepDetect;
TurnDetection turnDetect; TurnDetection turnDetect;
ActivityDetector actDetect;
std::vector<Point3> groundTruth; std::vector<Point3> groundTruth;
Floorplan::IndoorMap* map; Floorplan::IndoorMap* map;
EarthMapping em;
float absHead = 0;
public: public:
EvalWalk(Floorplan::IndoorMap* map) : wifiModel(map), plotty(map), map(map) { EvalWalk(Floorplan::IndoorMap* map) : wifiModel(map), plotty(map), map(map), em(map) {
const std::string saveFile = Settings::pathData + "/grid.dat"; const std::string saveFile = Settings::pathData + "/grid.dat";
grid = new Grid<MyGridNode>(Settings::Grid::gridSize_cm); grid = new Grid<MyGridNode>(Settings::Grid::gridSize_cm);
// once
plotty.buildFloorplan(); plotty.buildFloorplan();
// deserialize grid // deserialize grid
@@ -93,17 +105,24 @@ public:
pf = new K::ParticleFilter<MyState, MyControl, MyObservation>( Settings::numParticles, std::unique_ptr<PFInit>(new PFInit(grid)) ); pf = new K::ParticleFilter<MyState, MyControl, MyObservation>( Settings::numParticles, std::unique_ptr<PFInit>(new PFInit(grid)) );
// TODO: flexible model // TODO: flexible model
wifiModel.loadAPs(map, Settings::WiFiModel::TXP, Settings::WiFiModel::EXP, Settings::WiFiModel::WAF, false); //wifiModel.loadAPs(map, Settings::WiFiModel::TXP, Settings::WiFiModel::EXP, Settings::WiFiModel::WAF, false);
std::unique_ptr<PFEval> eval = std::unique_ptr<PFEval>( new PFEval(grid, wifiModel) );
pf->setEvaluation( std::move(eval) );
wifiModel.saveXML("/tmp/test.xml"); // transition
wifiModel.loadXML("/tmp/test.xml"); pf->setTransition( std::unique_ptr<PFTrans>( new PFTrans(grid)) );
// resampling step? // resampling step?
pf->setNEffThreshold(0.5); //pf->setNEffThreshold(0.5);
//pf->setResampling( std::unique_ptr<K::ParticleFilterResamplingSimple<MyState>>(new K::ParticleFilterResamplingSimple<MyState>()) ); //pf->setResampling( std::unique_ptr<K::ParticleFilterResamplingSimple<MyState>>(new K::ParticleFilterResamplingSimple<MyState>()) );
pf->setResampling( std::unique_ptr<K::ParticleFilterResamplingPercent<MyState>>(new K::ParticleFilterResamplingPercent<MyState>(0.10)) );
//pf->setNEffThreshold(0.75);
//pf->setResampling( std::unique_ptr<K::ParticleFilterResamplingPercent<MyState>>(new K::ParticleFilterResamplingPercent<MyState>(0.10)) );
//pf->setNEffThreshold(0.75);
//pf->setResampling( std::unique_ptr<K::ParticleFilterResamplingPercent<MyState>>(new K::ParticleFilterResamplingPercent<MyState>(0.05)) );
pf->setNEffThreshold(1.0);
pf->setResampling( std::unique_ptr<K::ParticleFilterResamplingNEff<MyState>>(new K::ParticleFilterResamplingNEff<MyState>(0.50, 0.05)) );
// state estimation step // state estimation step
pf->setEstimation( std::unique_ptr<K::ParticleFilterEstimationWeightedAverage<MyState>>(new K::ParticleFilterEstimationWeightedAverage<MyState>())); pf->setEstimation( std::unique_ptr<K::ParticleFilterEstimationWeightedAverage<MyState>>(new K::ParticleFilterEstimationWeightedAverage<MyState>()));
@@ -117,15 +136,32 @@ public:
void walk1() { void walk1() {
runName = "path2_forward_simple"; // path1
std::string path = Settings::path1a; absHead = M_PI/2;
groundTruth = FloorplanHelper::getGroundTruth(map, Settings::GroundTruth::path1); const std::string path = Settings::path1b;
const std::vector<int> pathPoints = Settings::GroundTruth::path1;
//GridWalkSimpleControl<MyGridNode>* walk = new GridWalkSimpleControl<MyGridNode>(); // path2
pf->setTransition( std::unique_ptr<PFTrans>( new PFTrans(grid)) ); // absHead = 0;
// const std::string path = Settings::path2a;
// const std::vector<int> pathPoints = Settings::GroundTruth::path2;
runName = "";
// get ground-truth
groundTruth = FloorplanHelper::getGroundTruth(map, pathPoints);
// wifi model
WiFiModelLogDistCeiling wifiModel(map);
wifiModel.loadXML(Settings::wifiEachOptParPos);
// eval
std::unique_ptr<PFEval> eval = std::unique_ptr<PFEval>( new PFEval(grid, wifiModel, em) );
pf->setEvaluation( std::move(eval) );
// data-file
reader.open(path); reader.open(path);
groundTruthLive = reader.getGroundTruth(map, Settings::GroundTruth::path1); groundTruthLive = reader.getGroundTruth(map, pathPoints);
player.setReader(&reader); player.setReader(&reader);
player.setListener(this); player.setListener(this);
player.start(); player.start();
@@ -152,6 +188,7 @@ public:
++curCtrl.numStepsSinceLastTransition; ++curCtrl.numStepsSinceLastTransition;
} }
gotSensorData(ts); gotSensorData(ts);
curCtrl.activityNew = actDetect.add(ts, data);
} }
virtual void onGravity(const Timestamp ts, const GravityData data) override { virtual void onGravity(const Timestamp ts, const GravityData data) override {
@@ -161,6 +198,8 @@ public:
virtual void onWiFi(const Timestamp ts, const WiFiMeasurements data) override { virtual void onWiFi(const Timestamp ts, const WiFiMeasurements data) override {
std::cout << "WIFI" << std::endl; std::cout << "WIFI" << std::endl;
curObs.wifi = data; curObs.wifi = data;
plotWifi.add(Settings::WiFiModel::vg_eval.group(data));
plotWifi.plot();
} }
virtual void onBarometer(const Timestamp ts, const BarometerData data) override { virtual void onBarometer(const Timestamp ts, const BarometerData data) override {
@@ -225,17 +264,31 @@ private:
K::Statistics<float> statsErr; K::Statistics<float> statsErr;
int updateCount = 0;
int getNumFHWSAPs(const WiFiMeasurements& mes) {
std::unordered_set<std::string> set;
for (const WiFiMeasurement& m : mes.entries) {
std::string mac = m.getAP().getMAC().asString();
mac.back() = '0';
set.insert(mac);
}
return set.size();
}
/** perform a filter-update (called from a background-loop) */ /** perform a filter-update (called from a background-loop) */
void filterUpdate() { void filterUpdate() {
++updateCount;
static PlotErrTime pet("\\small{time (sec)}", "\\small{error (m)}", "\\small{APs visible}");
static PlotErrFunc pef("\\small{error (m)}", "\\small{updates (\\%)}"); static PlotErrFunc pef("\\small{error (m)}", "\\small{updates (\\%)}");
pef.showMarkers(true);
std::cout << "update" << std::endl; std::cout << "update" << std::endl;
MyControl ctrlCopy = curCtrl; MyControl ctrlCopy = curCtrl;
static float absHead = M_PI/2; absHead += ctrlCopy.turnSinceLastTransition_rad; //static float absHead = relHeadingOffset;
absHead += ctrlCopy.turnSinceLastTransition_rad;
//lastEst = curEst; //lastEst = curEst;
curEst = pf->update(&curCtrl, curObs); curEst = pf->update(&curCtrl, curObs);
@@ -245,25 +298,41 @@ private:
plotty.setCurEst(curEst.position.inMeter()); plotty.setCurEst(curEst.position.inMeter());
plotty.setGroundTruth(curGT); plotty.setGroundTruth(curGT);
// error between ground-truth and estimation if (updateCount > 4) {
const float estRealErr = curEst.position.inMeter().getDistance(curGT);
statsErr.add(estRealErr); // error between ground-truth and estimation
pef.clear(); const float estRealErr = curEst.position.inMeter().getDistance(curGT);
pef.add("", statsErr); statsErr.add(estRealErr);
pef.plot(); pef.clear();
pef.add("", &statsErr);
pef.plot();
// timed error
pet.addErr(lastTransition, estRealErr);
pet.addB(lastTransition, getNumFHWSAPs(curObs.wifi));
pet.plot();
// update estimated path
const K::GnuplotPoint3 p3(curEst.position.x_cm, curEst.position.y_cm, curEst.position.z_cm);
plotty.pathEst.add(p3/100);
}
std::cout << statsErr.asString() << std::endl; std::cout << statsErr.asString() << std::endl;
const K::GnuplotPoint3 p3(curEst.position.x_cm, curEst.position.y_cm, curEst.position.z_cm); // show particles
plotty.pathEst.add(p3/100); float maxWeight = 0;
float minWeight = 99;
plotty.particles.clear(); plotty.particles.clear();
for (const auto p : pf->getParticles()) { for (const auto p : pf->getParticles()) {
const K::GnuplotPoint3 p3(p.state.position.x_cm, p.state.position.y_cm, p.state.position.z_cm); const K::GnuplotPoint3 p3(p.state.position.x_cm, p.state.position.y_cm, p.state.position.z_cm);
plotty.particles.add(p3/100); plotty.particles.add(p3/100, p.weight);
if (p.weight > maxWeight) {maxWeight = p.weight;}
if (p.weight < minWeight) {minWeight = p.weight;}
} }
plotty.gp << "set cbrange [" << minWeight << ":" << maxWeight << "] \n";
// GT // show ground-truth
plotty.pathReal.clear(); plotty.pathReal.clear();
for (const Point3 pt : groundTruth) { for (const Point3 pt : groundTruth) {
plotty.pathReal.add(K::GnuplotPoint3(pt.x, pt.y, pt.z)); plotty.pathReal.add(K::GnuplotPoint3(pt.x, pt.y, pt.z));
@@ -272,9 +341,12 @@ private:
std::string title = std::string title =
" time " + std::to_string(curObs.currentTime.sec()) + " time " + std::to_string(curObs.currentTime.sec()) +
" steps: " + std::to_string(ctrlCopy.numStepsSinceLastTransition) + " steps: " + std::to_string(ctrlCopy.numStepsSinceLastTransition) +
" turn: " + std::to_string(ctrlCopy.turnSinceLastTransition_rad); " turn: " + std::to_string(ctrlCopy.turnSinceLastTransition_rad) +
" APs: " + std::to_string(curObs.wifi.entries.size()) +
" Act: " + std::to_string((int)curCtrl.activityNew);
plotty.setTitle(title); plotty.setTitle(title);
// relative heading and compass
{ {
Point2 cen(0.1, 0.9); Point2 cen(0.1, 0.9);
Point2 dir(std::cos(absHead), std::sin(absHead)); Point2 dir(std::cos(absHead), std::sin(absHead));
@@ -289,7 +361,7 @@ private:
// plot // plot
plotty.plot(); plotty.plot();
std::this_thread::sleep_for(std::chrono::milliseconds(30)); std::this_thread::sleep_for(std::chrono::milliseconds(5));
curCtrl.resetAfterTransition(); curCtrl.resetAfterTransition();

40
pf/PF.h
View File

@@ -14,6 +14,8 @@
#include <Indoor/sensors/radio/WiFiProbabilityFree.h> #include <Indoor/sensors/radio/WiFiProbabilityFree.h>
#include <Indoor/sensors/radio/WiFiProbabilityGrid.h> #include <Indoor/sensors/radio/WiFiProbabilityGrid.h>
#include <Indoor/sensors/gps/GPSData.h> #include <Indoor/sensors/gps/GPSData.h>
#include <Indoor/sensors/gps/GPSProbability.h>
#include <Indoor/sensors/activity/Activity.h>
#include <Indoor/grid/walk/v2/GridWalker.h> #include <Indoor/grid/walk/v2/GridWalker.h>
#include <Indoor/grid/walk/v2/modules/WalkModuleHeadingControl.h> #include <Indoor/grid/walk/v2/modules/WalkModuleHeadingControl.h>
@@ -113,7 +115,9 @@ struct MyControl {
// TODO: switch to a general activity enum/detector using barometer + accelerometer? // TODO: switch to a general activity enum/detector using barometer + accelerometer?
/** currently detected activity */ /** currently detected activity */
ActivityButterPressure::Activity activity; ActivityButterPressure::Activity activity = ActivityButterPressure::Activity::STAY;
Activity activityNew;
/** reset the control-data after each transition */ /** reset the control-data after each transition */
void resetAfterTransition() { void resetAfterTransition() {
@@ -180,10 +184,10 @@ public:
walker.addModule(&modRelHead); walker.addModule(&modRelHead);
walker.addModule(&modAbsHead); walker.addModule(&modAbsHead);
walker.addModule(&modActivity);
//walker.addModule(&modFavorZ); //walker.addModule(&modFavorZ);
//walker.addModule(&modImportance); //walker.addModule(&modImportance);
//walker.addModule(&modActivity);
// if (Settings::destination != GridPoint(0,0,0)) { // if (Settings::destination != GridPoint(0,0,0)) {
// //walker.addModule(&modDestination); // //walker.addModule(&modDestination);
@@ -215,6 +219,8 @@ public:
K::Particle<MyState>& p = particles[i]; K::Particle<MyState>& p = particles[i];
p.weight = std::pow(p.weight, 0.8);
double prob; double prob;
p.state = walker.getDestination(*grid, p.state, dist_m, prob); p.state = walker.getDestination(*grid, p.state, dist_m, prob);
//p.weight *= prob;//(prob > 0.01) ? (1.0) : (0.15); //p.weight *= prob;//(prob > 0.01) ? (1.0) : (0.15);
@@ -223,11 +229,16 @@ public:
//p.weight = 1.0; // reset //p.weight = 1.0; // reset
//p.weight = std::pow(p.weight, 0.1); // make all particles a little more equal [less strict] //p.weight = std::pow(p.weight, 0.1); // make all particles a little more equal [less strict]
//p.weight *= std::pow(prob, 0.1); // add grid-walk-probability //p.weight *= std::pow(prob, 0.1); // add grid-walk-probability
p.weight = prob; // grid-walk-probability p.weight *= prob; // grid-walk-probability
if (p.weight != p.weight) {throw Exception("nan");} if (p.weight != p.weight) {throw Exception("nan");}
} }
// normalize
double weightSum = 0;
for (const auto& p : particles) {weightSum += p.weight;}
for (auto& p : particles) {p.weight /= weightSum;}
} }
}; };
@@ -238,6 +249,8 @@ class PFEval : public K::ParticleFilterEvaluation<MyState, MyObservation> {
WiFiModelLogDistCeiling& wifiModel; WiFiModelLogDistCeiling& wifiModel;
EarthMapping& em;
WiFiObserverFree wiFiProbability; // free-calculation WiFiObserverFree wiFiProbability; // free-calculation
//WiFiObserverGrid<MyGridNode> wiFiProbability; // grid-calculation //WiFiObserverGrid<MyGridNode> wiFiProbability; // grid-calculation
@@ -247,8 +260,8 @@ class PFEval : public K::ParticleFilterEvaluation<MyState, MyObservation> {
public: public:
PFEval(Grid<MyGridNode>* grid, WiFiModelLogDistCeiling& wifiModel) : PFEval(Grid<MyGridNode>* grid, WiFiModelLogDistCeiling& wifiModel, EarthMapping& em) :
grid(grid), wifiModel(wifiModel), grid(grid), wifiModel(wifiModel), em(em),
wiFiProbability(Settings::WiFiModel::sigma, wifiModel) { // WiFi free wiFiProbability(Settings::WiFiModel::sigma, wifiModel) { // WiFi free
//wiFiProbability(Settings::WiFiModel::sigma) { // WiFi grid //wiFiProbability(Settings::WiFiModel::sigma) { // WiFi grid
@@ -280,6 +293,7 @@ public:
} }
double evaluation(std::vector<K::Particle<MyState>>& particles, const MyObservation& _observation) override { double evaluation(std::vector<K::Particle<MyState>>& particles, const MyObservation& _observation) override {
double sum = 0; double sum = 0;
@@ -292,6 +306,9 @@ public:
const WiFiMeasurements wifiObs = Settings::WiFiModel::vg_eval.group(_observation.wifi); const WiFiMeasurements wifiObs = Settings::WiFiModel::vg_eval.group(_observation.wifi);
const int numAP2 = wifiObs.entries.size(); const int numAP2 = wifiObs.entries.size();
// GPS
const GPSProbability gpsProb(em);
Log::add("Filter", "VAP: " + std::to_string(numAP1) + " -> " + std::to_string(numAP2)); Log::add("Filter", "VAP: " + std::to_string(numAP1) + " -> " + std::to_string(numAP2));
// sanity check // sanity check
@@ -301,6 +318,8 @@ public:
for (int i = 0; i < Settings::numParticles; ++i) { for (int i = 0; i < Settings::numParticles; ++i) {
K::Particle<MyState>& p = particles[i]; K::Particle<MyState>& p = particles[i];
const MyGridNode& node = grid->getNodeFor(p.state.position);
// WiFi free // WiFi free
const double pWiFi = wiFiProbability.getProbability(p.state.position.inMeter()+person, observation.currentTime, wifiObs); const double pWiFi = wiFiProbability.getProbability(p.state.position.inMeter()+person, observation.currentTime, wifiObs);
@@ -309,12 +328,13 @@ public:
//const MyGridNode& node = grid->getNodeFor(p.state.position); //const MyGridNode& node = grid->getNodeFor(p.state.position);
//const double pWiFi = wiFiProbability.getProbability(node, observation.currentTime, wifiObs); //const double pWiFi = wiFiProbability.getProbability(node, observation.currentTime, wifiObs);
//const double pStair = 1;//getStairProb(p, observation.activity);
const double pGPS = gpsProb.getProbability(p.state.position.inMeter(), observation.gps);
const double prob = pWiFi * pGPS;
//Log::add("xxx", std::to_string(observation.currentTime.ms()) + "_" + std::to_string(wifiObs.entries[0].ts.ms())); if (pGPS != 1) {
int i = 0; (void) i;
const double pStair = 1;//getStairProb(p, observation.activity); }
const double pGPS = 1;
const double prob = pWiFi * pGPS * pStair;
p.weight *= prob; // NOTE: keeps the weight returned by the transition step! p.weight *= prob; // NOTE: keeps the weight returned by the transition step!
//p.weight = prob; // does NOT keep the weights returned by the transition step //p.weight = prob; // does NOT keep the weights returned by the transition step

141
plots/PlotErrFunc.h Normal file
View File

@@ -0,0 +1,141 @@
#ifndef PLOTERRFUNC_H
#define PLOTERRFUNC_H
#include <KLib/math/statistics/Statistics.h>
#include <KLib/misc/gnuplot/Gnuplot.h>
#include <KLib/misc/gnuplot/GnuplotPlot.h>
#include <KLib/misc/gnuplot/GnuplotPlotElementLines.h>
/**
* helper class to plot error development stored within Statistics<T>.
* statistics are given as pointers and may be altered outside.
* when plot() is called, everything is rebuild using the current contents
* of each Statistics<T> object
*/
class PlotErrFunc {
/** group name and statistics together */
struct Entry {
std::string name;
const K::Statistics<float>* stats;
K::GnuplotPlotElementLines* line = nullptr;
Entry(const std::string& name, const K::Statistics<float>* stats) : name(name), stats(stats) {;}
};
std::vector<Entry> entries;
K::Gnuplot gp;
K::GnuplotPlot gplot;
//std::vector<std::string> colors = {"#000000", "#ff0000", "#00bb00", "#0000ff"};
std::vector<std::string> colors = {"#000000", "#999999", "#0000ff", "#9999ff", "#ff0000"};
bool markers = false;
std::string codeFile;
public:
/** ctor with x-axis label */
PlotErrFunc(const std::string& xLabel, const std::string& yLabel) {
gplot.setLabelX(xLabel);
gplot.setLabelY(yLabel);
gplot.setRangeX(K::GnuplotAxisRange(0, K::GnuplotAxisRange::AUTO));
gplot.setRangeY(K::GnuplotAxisRange(0, K::GnuplotAxisRange::AUTO));
}
/** add one curve. Statistics<T> are allowed to be altered outside afterwards! */
void add(const std::string name, const K::Statistics<float>* stats) {
Entry entry(name, stats);
entry.line = new K::GnuplotPlotElementLines();
entry.line->setTitle(name);
entry.line->setLineWidth(2);
gplot.add(entry.line);
entries.push_back(entry);
}
/** remove all previously added entries */
void clear() {
for (Entry& e : entries) {
gplot.remove(e.line);
delete e.line;
}
entries.clear();
}
K::Gnuplot& getGP() {
return gp;
}
void writeCodeTo(const std::string& file) {
this->codeFile = file;
}
void showMarkers(const bool show) {
this->markers = show;
}
/** plot all curves */
void plot() {
gp << "set grid\n";
for (size_t i = 0; i < entries.size(); ++i) {
const Entry& e = entries[i];
e.line->clear();
e.line->setColorHex(colors[i]);
// distancen between min and max
const float minX = e.stats->getQuantile(0);
const float range = e.stats->getQuantile(0.85) - minX;
const float space = range * 0.07;
// 0 - 80%
for (int j = 0; j <= 85; j+= 5) {
const float y = j / 100.0f;
const float x = e.stats->getQuantile(y);
K::GnuplotPoint2 gp2(x, y*100);
e.line->add(gp2);
if (markers) {
int id = (i+1) * 100;
if (j == 50) {
gp << "set object " << id+1 << " circle at " << x << "," << j << " size screen 0.02,0.02\n";
gp << "set label " << id+2 << " at " << x+space << "," << j << " '" << x << "'\n";
gp << "set arrow " << id+3 << " from " << x << "," << 0 << " to " << x << "," << j << " nohead\n";
gp << "set arrow " << id+4 << " from " << 0 << "," << j << " to " << x << "," << j << " nohead\n";
} else if (j == 75) {
gp << "set object " << id+5 << " circle at " << x << "," << j << " size screen 0.02,0.02\n";
gp << "set label " << id+6 << " at " << x+space << "," << j << " '" << x << "'\n";
gp << "set arrow " << id+7 << " from " << x << "," << 0 << " to " << x << "," << j << " nohead\n";
gp << "set arrow " << id+8 << " from " << 0 << "," << j << " to " << x << "," << j << " nohead\n";
}
}
}
}
gp.draw(gplot);
if (codeFile != "") {
std::ofstream out(codeFile);
out << gp.getBuffer();
out.close();
}
gp.flush();
}
};
#endif // PLOTERRFUNC_H

71
plots/PlotErrTime.h Normal file
View File

@@ -0,0 +1,71 @@
#ifndef PLOTERRTIME_H
#define PLOTERRTIME_H
#include <KLib/misc/gnuplot/Gnuplot.h>
#include <KLib/misc/gnuplot/GnuplotPlot.h>
#include <KLib/misc/gnuplot/GnuplotPlotElementLines.h>
#include <Indoor/data/Timestamp.h>
/**
* plot error behaviour over time
*/
class PlotErrTime {
private:
K::Gnuplot gp;
K::GnuplotPlot gpplot;
K::GnuplotPlotElementLines lineErr[8];
K::GnuplotPlotElementLines lineB;
K::GnuplotPlotElementLines lineC;
public:
/** ctor */
PlotErrTime(const std::string& xLabel, const std::string& yLabel, const std::string& y2Label) {
gpplot.setLabelX(xLabel);
gpplot.setLabelY(yLabel);
gpplot.setLabelY2(y2Label);
gpplot.add(&lineErr[0]); lineErr[0].setLineWidth(1.5); lineErr[0].setColorHex("#000000");
gpplot.add(&lineErr[1]); lineErr[1].setLineWidth(1.5); lineErr[1].setColorHex("#FF0000");
gpplot.add(&lineErr[2]); lineErr[2].setLineWidth(1.5); lineErr[2].setColorHex("#00FF00");
gpplot.add(&lineErr[3]); lineErr[3].setLineWidth(1.5); lineErr[3].setColorHex("#0000FF");
gpplot.add(&lineB); lineB.setLineWidth(1); lineB.setColorHex("#0000ff");
gpplot.add(&lineC);
gpplot.setGrid(true);
}
K::GnuplotPlot& getPlot() {
return gpplot;
}
/** add the give error value to the idx-th error line */
void addErr(const Timestamp t, const float err, const int idx = 0) {
K::GnuplotPoint2 pt(t.sec(), err);
lineErr[idx].add(pt);
}
void addB(const Timestamp t, const float something) {
K::GnuplotPoint2 pt(t.sec(), something);
lineB.add(pt);
}
void addC(const Timestamp t, const float something) {
K::GnuplotPoint2 pt(t.sec(), something);
lineC.add(pt);
}
void plot() {
gp.draw(gpplot);
gp.flush();
}
};
#endif // PLOTERRTIME_H

View File

@@ -94,7 +94,7 @@ public:
K::GnuplotSplotElementLines pathReal; K::GnuplotSplotElementLines pathReal;
K::GnuplotSplotElementLines pathEst; K::GnuplotSplotElementLines pathEst;
K::GnuplotSplotElementPoints particles; K::GnuplotSplotElementColorPoints particles;
K::GnuplotSplotElementLines mapOutlineGlass; K::GnuplotSplotElementLines mapOutlineGlass;
K::GnuplotSplotElementLines mapOutlineDrywall; K::GnuplotSplotElementLines mapOutlineDrywall;
@@ -117,14 +117,14 @@ public:
//gp << "set view equal xy\n"; //gp << "set view equal xy\n";
gp << "set palette model RGB\n"; //gp << "set palette model RGB\n";
//gp << "set palette defined (0 '#0000ff', 1 '#ff0000')\n"; //gp << "r(x) = (x < 0) ? 0 : (x/2)\n";
gp << "r(x) = (x < 0) ? 0 : (x/2)\n"; //gp << "g(x) = 0\n";
gp << "g(x) = 0\n"; //gp << "b(x) = (x > 0) ? 0 : (-x/2)\n";
gp << "b(x) = (x > 0) ? 0 : (-x/2)\n"; //gp << "set palette model RGB functions r(gray),g(gray),b(gray)\n";
gp << "set palette model RGB functions r(gray),g(gray),b(gray)\n";
gp << "set ticslevel 0\n"; gp << "set ticslevel 0\n";
// how to draw the floorplan // how to draw the floorplan
mapOutlineConcrete.setColorHex("#888888"); mapOutlineConcrete.setLineWidth(2); mapOutlineConcrete.setColorHex("#888888"); mapOutlineConcrete.setLineWidth(2);
mapOutlineDrywall.setColorHex("#888888"); mapOutlineDrywall.setColorHex("#888888");
@@ -133,7 +133,7 @@ public:
splot.add(&mapOutlineDrywall); splot.add(&mapOutlineDrywall);
splot.add(&mapOutlineGlass); splot.add(&mapOutlineGlass);
splot.add(&particles); particles.setPointSize(0.20); particles.setColorHex("#777777"); splot.add(&particles); particles.setPointSize(0.20); //particles.setColorHex("#777777");
splot.add(&pathReal); pathReal.setLineWidth(2); pathReal.setColorHex("#000000"); splot.add(&pathReal); pathReal.setLineWidth(2); pathReal.setColorHex("#000000");
splot.add(&pathEst); pathEst.setLineWidth(2); pathEst.setColorHex("#0000ff"); splot.add(&pathEst); pathEst.setLineWidth(2); pathEst.setColorHex("#0000ff");

View File

@@ -195,6 +195,8 @@
\input{chapters/relatedwork} \input{chapters/relatedwork}
\input{chapters/work}
\input{chapters/experiments} \input{chapters/experiments}
\input{chapters/conclusion} \input{chapters/conclusion}

View File

@@ -1,10 +1,72 @@
experiments experiments
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
wir betrachten nur die fest-installierten APs die man meist anhand einer bestimmten mac-range ausmachen kann
portable geraete von studenten, beamer, aehnliches werden ignoriert
modell direkt fuer den gelaufenen pfad optimiert (also wirklich jede wifi messung direkt auf den ground-truth)
der fehler wird zwar kleiner, ist aber immernoch deutlich spürbar. das spricht dafür, dass das modell einfach nicht
gut geeignet ist.
optimierungs input: alle 4 walks samt ground-truth
dann kommt fuer die 4 typen [fixed, all same par, each par, each par pos]
log probability 50 75, meter 50, 75
path1
31.8|38.9 7.8|11.6
27.3|36.8 7.2|9.8
24.0|30.3 5.8|10.24
22.9|29.9 5.0|7.6
hoherer fehler weil mehr outdoor anteil
path2
32.0|42.4 12.6|20.9
28.4|35.2 10.1|16.1
27.0|34.0 7.0|10.1
25.4|33.3 8.0|17.2
je mehr outdoor, desto schlechter wird es.
outdoor schadet auch der optimierung
outdoor schadet mehr als indoor, weil das wifi modell fuer indoor noch halbwegs passt
aber fuer outdoor so garned
fenster sind metallbedampft und schirmen stark ab
siehe beispielgrafik
gps wird so schnell nicht warm, versagt denn auf dem hof als hilfestellung
reines wifi eval mittels num-opt springt stark durch die gegend
d.h. das bewegungsmodell rettet uns
kann man auch testen wenn man beim particle-filter das resampling ganz aus macht
\input{gfx/compare-wifi-in-out.tex} \input{gfx/compare-wifi-in-out.tex}
starker einfluss der glasscheiben.. 3 meter nach dem AP ist nur noch sehr wenig uebrig starker einfluss der glasscheiben.. 3 meter nach dem AP ist nur noch sehr wenig uebrig
ware das grid-model nicht da, wuerde der outdoor teil richtig schlecht laufen,
weil das wlan hier absolut ungenau ist.. da die partikel aber aufgrund des vorherigen
walks schon recht dicht beisamen sind, kittet das das ganze sehr gut.
kann man testen, indem man z.B. weniger resampling macht und mehr alte partikel aufhebt.
geht sofort kaputt sobald man aus dem gebäude raus kommt
signalstaerke limitieren, wie : alles was im model oder scan < -90 ist, wird auf -90 abgeschnitten hilft
zwar an manchen stellen, im groben und ganzen führt es aber eher zu fehlern als zu verbesserungen.
zudem ist zu erwarten, dass diese zahl stark vom geraet/hardware abhaengt
jeweils beim weighting die niedrigste wifi probability weglassen [je nach particle also ein anderer AP]
bringt auch nicht immer was.. killt gelegentlich floor-changes. zudem stehen am ende nur sehr wenige
APs zur verfügung. da einen zu ignorieren, macht noch mehr kaputt
auch ein versuch wie werfe alle APs aus dem handy-scan weg, die kleiner -90 sind, birgt die selben risiken
es scheint wirklich am sinnvollsten, die scan-daten einfach 1:1 zu nehmen wie sie sind
kurz vor ende von path 2 will die estimation nicht in die cafeteria, weil ein paar particle
die treppe richtung h.1.5 hochgehen und durch das wlan sehr sehr hoch gewichtet werden.
die mittelwert-estimation versagt hier
\input{gfx/wifi-opt-error-hist-methods.tex} \input{gfx/wifi-opt-error-hist-methods.tex}

32
tex/chapters/work.tex Normal file
View File

@@ -0,0 +1,32 @@
log normal shadowing model
so wie log-dist modell nur mit waf. hier: fuer decken. waende werden aktuell nicht betrachtet
wie wird optimiert
a) bekannte pos + empirische params
b) bekannte pos + opt params (fur alle APs gleich) [simplex]
c) bekannte pos + opt params (eigene je AP) [simplex]
d) alles opt: pos und params (je ap) [range-random]
probleme bei der optimierung beschreiben. convex usw..
wo geht simplex gut, wo eher nicht
harte WAF übergänge scheinen beim optimieren als auch beim matchen nicht so gut
gleitende übergänge mittels sigmoid wirken besser
war eine wichtige erkenntnis
die vom AP bekannte position wird NICHT als input fuer die alles-OPT funktion benutzt
die ist wirklich 'irgendwo'
range-random algo
domain bekannt [map groesse, txp/exp/waf in etwa]
genetic refinement mit cooling [= erst grob, dann fein]
optimierung ist tricky. auch wegen dem WAF der ja sprunghaft dazu kommt, sobald messung und AP in zwei unterschiedlichen
stockwerken liegen.. und das selbst wenn hier vlt sichtkontakt möglich wäre, da der test 2D ist und nicht 3D
aps sind (statistisch) unaebhaengig. d.h., jeder AP kann fuer sich optimiert werden.
optimierung des gesamtsystems ist nicht notwendig.
pro AP also 6 params. pos x/y/z, txp, exp, waf