worked on grid walker
This commit is contained in:
@@ -84,43 +84,67 @@ namespace GW3 {
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* data-structure to track to-be-visited nodes
|
||||
* push_back, pop_front
|
||||
* as pop_front is costly, we omit the pop and use a head-index instead
|
||||
* memory-consumption vs speed
|
||||
*/
|
||||
struct ToVisit {
|
||||
size_t nextIdx = 0;
|
||||
std::vector<uint32_t> vec;
|
||||
ToVisit() {vec.reserve(256);}
|
||||
void add(const uint32_t nodeIdx) {vec.push_back(nodeIdx);}
|
||||
uint32_t next() {return vec[nextIdx++];}
|
||||
bool empty() const {return nextIdx >= vec.size();}
|
||||
};
|
||||
|
||||
/** get an iterator over all nodes reachable from the given start */
|
||||
template <typename Node> class ReachableIteratorUnsorted {
|
||||
template <typename Node, typename Conditions> class ReachableIteratorUnsorted {
|
||||
|
||||
const Grid<Node>& grid;
|
||||
const Node& start;
|
||||
|
||||
Node* curNode = nullptr;
|
||||
std::unordered_set<uint32_t> visited;
|
||||
std::vector<uint32_t> toVisit;
|
||||
ToVisit toVisit;
|
||||
|
||||
Conditions cond;
|
||||
|
||||
public:
|
||||
|
||||
ReachableIteratorUnsorted(const Grid<Node>& grid, const Node& start) : grid(grid), start(start) {
|
||||
toVisit.push_back(start.getIdx());
|
||||
ReachableIteratorUnsorted(const Grid<Node>& grid, const Node& start, const Conditions cond) : grid(grid), start(start), cond(cond) {
|
||||
toVisit.add(start.getIdx());
|
||||
}
|
||||
|
||||
bool hasNext() const {
|
||||
return !toVisit.empty();
|
||||
}
|
||||
|
||||
const Node& next(const std::function<bool(const Node&)>& skip) {
|
||||
|
||||
const uint32_t curIdx = toVisit.front(); //visit from inside out (needed for correct distance)
|
||||
toVisit.erase(toVisit.begin());
|
||||
visited.insert(curIdx);
|
||||
//const Node& next(const std::function<bool(const Node&)>& skip) {
|
||||
//template <typename Skip> const Node& next(const Skip skip) {
|
||||
const Node& next() {
|
||||
|
||||
// get the next to-be-visited node
|
||||
const uint32_t curIdx = toVisit.next(); //visit from inside out (needed for correct distance)
|
||||
const Node& curNode = grid[curIdx];
|
||||
|
||||
for (int i = 0; i < curNode.getNumNeighbors(); ++i) {
|
||||
// mark as "visited"
|
||||
visited.insert(curIdx);
|
||||
|
||||
const int neighborIdx = curNode.getNeighborIdx(i);
|
||||
// get all neighbors
|
||||
const int numNeighbors = curNode.getNumNeighbors();
|
||||
for (int i = 0; i < numNeighbors; ++i) {
|
||||
|
||||
const uint32_t neighborIdx = curNode.getNeighborIdx(i);
|
||||
const Node& neighbor = grid[neighborIdx];
|
||||
|
||||
const bool visit = cond.visit(neighbor) ;
|
||||
|
||||
// not yet reached -> store distance
|
||||
if (!skip(neighbor)) {
|
||||
if (visit) {
|
||||
if (visited.find(neighborIdx) == visited.end()) {
|
||||
toVisit.push_back(neighborIdx);
|
||||
toVisit.add(neighborIdx);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,8 @@ namespace GW3 {
|
||||
const GridPoint gp = Helper<Node>::p3ToGp(pEnd);
|
||||
const Node& node = grid->getNodeFor(gp);
|
||||
const double p = node.getWalkImportance();
|
||||
return std::pow(p,10);
|
||||
return p;
|
||||
//return std::pow(p,10);
|
||||
|
||||
}
|
||||
|
||||
@@ -50,11 +51,19 @@ namespace GW3 {
|
||||
/** evaluate the difference between head(start,end) and the requested heading */
|
||||
template <typename Node> class WalkEvalHeadingStartEnd : public WalkEvaluator<Node> {
|
||||
|
||||
const double sigma;
|
||||
const double sigma_rad;
|
||||
const double kappa;
|
||||
Distribution::VonMises<double> _dist;
|
||||
Distribution::LUT<double> dist;
|
||||
|
||||
public:
|
||||
|
||||
WalkEvalHeadingStartEnd(const double sigma = 0.04) : sigma(sigma) {;}
|
||||
// kappa = 1/var = 1/sigma^2
|
||||
// https://en.wikipedia.org/wiki/Von_Mises_distribution
|
||||
WalkEvalHeadingStartEnd(const double sigma_rad = 0.04) :
|
||||
sigma_rad(sigma_rad), kappa(1.0/(sigma_rad*sigma_rad)), _dist(0, kappa), dist(_dist.getLUT()) {
|
||||
;
|
||||
}
|
||||
|
||||
virtual double getProbability(const Point3 pStart, const Point3 pEnd, const WalkParams& params) const override {
|
||||
|
||||
@@ -68,7 +77,8 @@ namespace GW3 {
|
||||
const Heading head(pStart.xy(), pEnd.xy());
|
||||
const float diff = head.getDiffHalfRAD(params.heading);
|
||||
//const float diff = Heading::getSignedDiff(params.heading, head);
|
||||
return Distribution::Normal<double>::getProbability(0, sigma, diff);
|
||||
//return Distribution::Normal<double>::getProbability(0, sigma, diff);
|
||||
return dist.getProbability(diff);
|
||||
|
||||
}
|
||||
|
||||
@@ -79,14 +89,19 @@ namespace GW3 {
|
||||
|
||||
const double sigma;
|
||||
|
||||
const Distribution::Normal<double> dist;
|
||||
|
||||
public:
|
||||
|
||||
WalkEvalDistance(const double sigma = 0.1) : sigma(sigma) {;}
|
||||
WalkEvalDistance(const double sigma = 0.1) : sigma(sigma), dist(0, sigma) {;}
|
||||
|
||||
virtual double getProbability(const Point3 pStart, const Point3 pEnd, const WalkParams& params) const override {
|
||||
|
||||
const float requestedDistance_m = params.distance_m;
|
||||
const float walkedDistance_m = pStart.getDistance(pEnd);
|
||||
return Distribution::Normal<double>::getProbability(params.distance_m, sigma, walkedDistance_m);
|
||||
const float diff = walkedDistance_m - requestedDistance_m;
|
||||
return dist.getProbability(diff);
|
||||
//return Distribution::Normal<double>::getProbability(params.distance_m, sigma, walkedDistance_m);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -26,18 +26,22 @@ namespace GW3 {
|
||||
public:
|
||||
|
||||
/** get a new destination for the given start */
|
||||
virtual const WalkResult getDestination(Grid<Node>& grid, const WalkParams& params) const = 0;
|
||||
virtual const WalkResult getDestination(const WalkParams& params) const = 0;
|
||||
|
||||
};
|
||||
|
||||
template <typename Node> class WalkerDirectDestination : public WalkerBase<Node> {
|
||||
|
||||
//RandomGenerator rnd;
|
||||
|
||||
Grid<Node>& grid;
|
||||
std::vector<WalkEvaluator<Node>*> evals;
|
||||
|
||||
public:
|
||||
|
||||
/** ctor */
|
||||
WalkerDirectDestination(Grid<Node>& grid) : grid(grid) {
|
||||
;
|
||||
}
|
||||
|
||||
/** make the code a little more readable */
|
||||
using Helper = GW3::Helper<Node>;
|
||||
using Walk = typename GW3::Walk<Node>;
|
||||
@@ -50,7 +54,7 @@ namespace GW3 {
|
||||
}
|
||||
|
||||
/** perform the walk based on the configured setup */
|
||||
const WalkResult getDestination(Grid<Node>& grid, const WalkParams& params) const override {
|
||||
const WalkResult getDestination(const WalkParams& params) const override {
|
||||
|
||||
Assert::isNot0(params.distance_m, "walking distance must be > 0");
|
||||
|
||||
@@ -142,8 +146,20 @@ namespace GW3 {
|
||||
|
||||
std::vector<WalkEvaluator<Node>*> evals;
|
||||
|
||||
Grid<Node>& grid;
|
||||
const float gridSize_m;
|
||||
|
||||
mutable std::minstd_rand rndGen;
|
||||
mutable std::uniform_real_distribution<float> dFinal;
|
||||
|
||||
public:
|
||||
|
||||
/** ctor */
|
||||
WalkerWeightedRandom(Grid<Node>& grid) :
|
||||
grid(grid), gridSize_m(grid.getGridSize_cm() / 100.0f), dFinal(-gridSize_m*0.49f, +gridSize_m*0.49f) {
|
||||
;
|
||||
}
|
||||
|
||||
/** make the code a little more readable */
|
||||
using Helper = GW3::Helper<Node>;
|
||||
using Walk = typename GW3::Walk<Node>;
|
||||
@@ -156,79 +172,71 @@ namespace GW3 {
|
||||
}
|
||||
|
||||
/** perform the walk based on the configured setup */
|
||||
const WalkResult getDestination(Grid<Node>& grid, const WalkParams& params) const override {
|
||||
const WalkResult getDestination(const WalkParams& params) const override {
|
||||
|
||||
Assert::isNot0(params.distance_m, "walking distance must be > 0");
|
||||
|
||||
static std::minstd_rand rndGen;
|
||||
|
||||
const GridPoint gpStart = Helper::p3ToGp(params.start);
|
||||
const Node* startNode = grid.getNodePtrFor(gpStart);
|
||||
if (!startNode) {throw Exception("start node not found!");}
|
||||
|
||||
// // include one additional grid-cell (increased distance)
|
||||
// const float secBuffer_m = params.lookFurther_m + (grid.getGridSize_cm() / 100.0f) + (params.distance_m * 1.05);
|
||||
const float maxDist = params.distance_m + gridSize_m;
|
||||
|
||||
// ReachableSettings set;
|
||||
// set.limitDistance = true;
|
||||
// set.limitHeading = true;
|
||||
// set.dist_m = params.distance_m + secBuffer_m;
|
||||
// set.heading = params.heading;
|
||||
// set.maxHeadingDiff_rad = M_PI/2;
|
||||
// const Nodes reachableNodes = Helper::getAllReachableNodes(grid, startNode, set);
|
||||
|
||||
const float gridSize_m = grid.getGridSize_cm() / 100.0f;
|
||||
//std::uniform_int_distribution<int> dNode(0, (int)reachableNodes.size() - 1);
|
||||
|
||||
|
||||
Point3 best;
|
||||
double bestP = 0;
|
||||
// DrawList<Point3> drawer;
|
||||
Point3 best; double bestP = 0;
|
||||
//DrawList<Point3> drawer;
|
||||
|
||||
const Point3 start = params.start;
|
||||
|
||||
// try X random destinations, evaluate them, draw one of em according to probability (reduces the number of "stupid particles")
|
||||
//for (int i = 0; i < 500; ++i) {
|
||||
|
||||
// const Node* dstNode = reachableNodes[dNode(rndGen)];
|
||||
|
||||
std::uniform_real_distribution<float> dFinal(-gridSize_m*0.49f, +gridSize_m*0.49f);
|
||||
|
||||
|
||||
|
||||
ReachableIteratorUnsorted<Node> ri(grid, *startNode);
|
||||
const float maxDist = params.distance_m * 1.25 + gridSize_m;
|
||||
|
||||
auto skip = [&] (const Node& n) {
|
||||
const float dist_m = n.getDistanceInMeter(gpStart);
|
||||
return dist_m > maxDist;
|
||||
struct RICond {
|
||||
const GridPoint gpStart;
|
||||
const float maxDist;
|
||||
RICond(const GridPoint gpStart, const float maxDist) : gpStart(gpStart), maxDist(maxDist) {;}
|
||||
bool visit (const Node& n) const {
|
||||
const float dist_m = n.getDistanceInMeter(gpStart);
|
||||
return dist_m < maxDist;
|
||||
}
|
||||
};
|
||||
RICond riCond(gpStart, maxDist);
|
||||
|
||||
//for (const Node* dstNode : reachableNodes) {
|
||||
while(ri.hasNext()) {
|
||||
// iterate over all reachable nodes that satisfy a certain criteria (e.g. max distance)
|
||||
ReachableIteratorUnsorted<Node, RICond> ri(grid, *startNode, riCond);
|
||||
|
||||
const Node* dstNode = &ri.next(skip);
|
||||
// const float dist_m = dstNode->getDistanceInMeter(gpStart);
|
||||
int numVisitedNodes = 0;
|
||||
|
||||
// if (dist_m > maxDist) {
|
||||
// break;
|
||||
// }
|
||||
|
||||
for (int i = 0; i < 25; ++i) {
|
||||
#define MODE 1
|
||||
|
||||
#if (MODE == 1)
|
||||
|
||||
double bestNodeP = 0;
|
||||
const Node* bestNode = nullptr;
|
||||
|
||||
while(ri.hasNext()) {
|
||||
const Node* dstNode = &ri.next();
|
||||
const Point3 nodeCenter = Helper::gpToP3(*dstNode);
|
||||
double p = 1.0;
|
||||
for (const WalkEvaluator<Node>* eval : evals) {
|
||||
const double p1 = eval->getProbability(start, nodeCenter, params);
|
||||
p *= p1;
|
||||
}
|
||||
if (p > bestNodeP) {
|
||||
bestNodeP = p;
|
||||
bestNode = dstNode;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
|
||||
const Point3 nodeCenter = Helper::gpToP3(*bestNode);
|
||||
|
||||
// random position within destination-node
|
||||
const Point3 dstOffset(dFinal(rndGen), dFinal(rndGen), 0);
|
||||
const float ox = dFinal(rndGen);
|
||||
const float oy = dFinal(rndGen);
|
||||
|
||||
// destination = node-center + offset (within the node's bbox)
|
||||
const Point3 end = Helper::gpToP3(*dstNode) + dstOffset;
|
||||
|
||||
// sanity check
|
||||
if (start == end) {continue;}
|
||||
if (!grid.hasNodeFor(Helper::p3ToGp(end))) {
|
||||
std::cout << "random destination is not part of the grid" << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
//Assert::isTrue(grid.hasNodeFor(Helper::p3ToGp(end)), "random destination is not part of the grid");
|
||||
// destination = nodeCenter + offset (within the node's bbox, (x,y) only! keep z as-is)
|
||||
const Point3 end(nodeCenter.x + ox, nodeCenter.y + oy, nodeCenter.z);
|
||||
|
||||
double p = 1;
|
||||
for (const WalkEvaluator<Node>* eval : evals) {
|
||||
@@ -237,20 +245,63 @@ namespace GW3 {
|
||||
}
|
||||
|
||||
if (p > bestP) {bestP = p; best = end;}
|
||||
//drawer.add(end, p);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
#elif (MODE == 2)
|
||||
|
||||
//const Point3 end = drawer.get();
|
||||
// all reachable nodes
|
||||
while(ri.hasNext()) {
|
||||
|
||||
const Node* dstNode = &ri.next();
|
||||
++numVisitedNodes;
|
||||
|
||||
const Point3 nodeCenter = Helper::gpToP3(*dstNode);
|
||||
|
||||
// try multiple locations within each reachable node
|
||||
for (int i = 0; i < 1; ++i) {
|
||||
|
||||
// random position within destination-node
|
||||
const float ox = dFinal(rndGen);
|
||||
const float oy = dFinal(rndGen);
|
||||
|
||||
// destination = nodeCenter + offset (within the node's bbox, (x,y) only! keep z as-is)
|
||||
const Point3 end(nodeCenter.x + ox, nodeCenter.y + oy, nodeCenter.z);
|
||||
|
||||
// sanity check
|
||||
if (start == end) {continue;}
|
||||
// if (!grid.hasNodeFor(Helper::p3ToGp(end))) {
|
||||
// std::cout << "random destination is not part of the grid" << std::endl;
|
||||
// continue;
|
||||
// }
|
||||
//Assert::isTrue(grid.hasNodeFor(Helper::p3ToGp(end)), "random destination is not part of the grid");
|
||||
|
||||
double p = 1;
|
||||
for (const WalkEvaluator<Node>* eval : evals) {
|
||||
const double p1 = eval->getProbability(start, end, params);
|
||||
p *= p1;
|
||||
}
|
||||
|
||||
if (p > bestP) {bestP = p; best = end;}
|
||||
// drawer.add(end, p);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
//std::cout << numVisitedNodes << std::endl;
|
||||
|
||||
//double drawProb = 0; const Point3 end = drawer.get(drawProb);
|
||||
const Point3 end = best;
|
||||
WalkResult res;
|
||||
if (start == end) {
|
||||
res.probability = 0;
|
||||
} else {
|
||||
res.heading = Heading(start.xy(), end.xy());
|
||||
res.probability = bestP;
|
||||
//res.probability = drawProb; // when using DrawList
|
||||
res.probability = bestP; // when using bestP
|
||||
}
|
||||
res.position = end;
|
||||
return res;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include "../sensors/imu/GyroscopeData.h"
|
||||
#include "../geo/Heading.h"
|
||||
|
||||
#include "../math/distribution/Normal.h"
|
||||
#include "../math/Distributions.h"
|
||||
|
||||
/**
|
||||
* simulates acceleromter and gyroscope data
|
||||
@@ -41,6 +41,8 @@ private:
|
||||
Distribution::Normal<float> dChange = Distribution::Normal<float>(1.0, 0.25);
|
||||
Distribution::Normal<float> dHeadErr = Distribution::Normal<float>(0.15, 0.10); // heading error, slightly biased
|
||||
|
||||
Distribution::Uniform<float> dRadDiff = Distribution::Uniform<float>(40,100);
|
||||
|
||||
|
||||
std::vector<Listener*> listeners;
|
||||
|
||||
@@ -68,7 +70,7 @@ protected:
|
||||
Heading desiredHead = Heading(0);
|
||||
Heading curHead = Heading(0);
|
||||
Point3 lastPos = Point3(NAN, NAN, NAN);
|
||||
float change;
|
||||
double change = 0;
|
||||
|
||||
inline float clamp(const float val, const float min, const float max) {
|
||||
if (val < min) {return min;}
|
||||
@@ -86,27 +88,33 @@ protected:
|
||||
if (lastPos.x != lastPos.x) {
|
||||
lastPos = curPos;
|
||||
} else {
|
||||
desiredHead = Heading(lastPos.x, lastPos.y, curPos.x, curPos.y) + dHeadErr.draw();;
|
||||
desiredHead = Heading(lastPos.x, lastPos.y, curPos.x, curPos.y) + dHeadErr.draw();
|
||||
lastPos = curPos;
|
||||
}
|
||||
|
||||
// difference between current-heading and desired-heading
|
||||
const float diffRad = Heading::getSignedDiff(curHead, desiredHead);
|
||||
const double maxChange = dMaxChange.draw();
|
||||
const double diffRad = Heading::getSignedDiff(curHead, desiredHead);
|
||||
//change = clamp(diffRad / dRadDiff.draw(), -maxChange, +maxChange);
|
||||
change = clamp(diffRad / 25, -maxChange, +maxChange);
|
||||
|
||||
// slowly change the current heading to match the desired one
|
||||
const float maxChange = dMaxChange.draw();
|
||||
const float toChange = clamp(diffRad, -maxChange, +maxChange);
|
||||
//if (change < toChange) {change += toChange*0.01;}
|
||||
if (change > toChange) {change *= 0.93;}
|
||||
if (change < toChange) {change += dChange.draw()/10000;}
|
||||
//if (change > toChange) {change -= dChange.draw();}
|
||||
|
||||
// // slowly change the current heading to match the desired one
|
||||
// //const double maxChange = dMaxChange.draw();
|
||||
// //const double toChange = clamp(diffRad, -maxChange, +maxChange);
|
||||
// const double toChange = diffRad;
|
||||
// //if (change < toChange) {change += toChange*0.01;}
|
||||
// if (change > toChange) {change *= 0.93;}
|
||||
// //if (change < toChange) {change += dChange.draw()/10000;} // does not work for small changes?!
|
||||
// if (change < toChange) {change += (toChange-change) * 0.07;}
|
||||
// //if (change > toChange) {change -= dChange.draw();}
|
||||
|
||||
|
||||
|
||||
curHead += change;
|
||||
|
||||
// convert to gyro's radians-per-second
|
||||
const float radPerSec = change * 1000 / deltaTs.ms();;
|
||||
const double radPerSec = change * 1000 / deltaTs.ms();;
|
||||
|
||||
const float accX = 0.00 + dAccX.draw();
|
||||
const float accY = 0.00 + dAccY.draw();
|
||||
|
||||
Reference in New Issue
Block a user