Commit 8c1fef0d authored by Matthew Hausknecht's avatar Matthew Hausknecht

Updated high level feature set.

parent 372d87a9
...@@ -106,9 +106,138 @@ void FeatureExtractor::addNormFeature(float val, float min_val, float max_val) { ...@@ -106,9 +106,138 @@ void FeatureExtractor::addNormFeature(float val, float min_val, float max_val) {
void FeatureExtractor::checkFeatures() { void FeatureExtractor::checkFeatures() {
assert(feature_vec.size() == numFeatures); assert(feature_vec.size() == numFeatures);
for (int i=0; i<numFeatures; ++i) { for (int i=0; i<numFeatures; ++i) {
if (feature_vec[i] == FEAT_INVALID) {
continue;
}
if (feature_vec[i] < FEAT_MIN || feature_vec[i] > FEAT_MAX) { if (feature_vec[i] < FEAT_MIN || feature_vec[i] > FEAT_MAX) {
std::cout << "Invalid Feature! Indx:" << i << " Val:" << feature_vec[i] << std::endl; std::cout << "Invalid Feature! Indx:" << i << " Val:" << feature_vec[i] << std::endl;
exit(1); exit(1);
} }
} }
} }
bool FeatureExtractor::valid(const rcsc::PlayerObject& player) {
// Check if the player is too far left
const rcsc::Vector2D& pos = player.pos();
if (!player.posValid() ||
pos.x < -ALLOWED_PITCH_FRAC * rcsc::ServerParam::i().pitchHalfLength()) {
return false;
}
return player.unum() > 0 && pos.isValid();
}
float FeatureExtractor::angleToPoint(const rcsc::Vector2D &self,
const rcsc::Vector2D &point) {
return (point - self).th().radian();
}
void FeatureExtractor::angleDistToPoint(const rcsc::Vector2D &self,
const rcsc::Vector2D &point,
float &ang, float &dist) {
Vector2D d = point - self;
ang = d.th().radian();
dist = d.r();
}
float FeatureExtractor::angleBetween3Points(const rcsc::Vector2D &point1,
const rcsc::Vector2D &centerPoint,
const rcsc::Vector2D &point2) {
Vector2D diff1 = point1 - centerPoint;
Vector2D diff2 = point2 - centerPoint;
float angle1 = atan2(diff1.y,diff1.x);
float angle2 = atan2(diff2.y,diff2.x);
return fabs(angle1 - angle2);
}
void FeatureExtractor::calcClosestOpp(const rcsc::WorldModel &wm,
const rcsc::Vector2D &point,
float &ang, float &minDist) {
minDist = std::numeric_limits<float>::max();
const PlayerCont& opps = wm.opponents();
for (PlayerCont::const_iterator it=opps.begin(); it != opps.end(); ++it) {
const PlayerObject& opponent = *it;
if (valid(opponent)) {
float dist;
float th;
angleDistToPoint(point, opponent.pos(), th, dist);
if (dist < minDist) {
minDist = dist;
ang = th;
}
}
}
}
float FeatureExtractor::calcLargestTeammateAngle(const rcsc::WorldModel &wm,
const rcsc::Vector2D &self,
const Vector2D &teammate) {
float angTeammate = angleToPoint(self, teammate);
float angTop = angTeammate + M_PI / 4;
float angBot = angTeammate - M_PI / 4;
return calcLargestOpenAngle(wm, self, angTop, angBot, (self - teammate).r());
}
float FeatureExtractor::calcLargestGoalAngle(const rcsc::WorldModel &wm,
const rcsc::Vector2D &self) {
const rcsc::ServerParam & SP = rcsc::ServerParam::i();
Vector2D goalPostTop(SP.pitchHalfLength(), SP.goalHalfWidth());
Vector2D goalPostBot(SP.pitchHalfLength(), -SP.goalHalfWidth());
float angTop = angleToPoint(self, goalPostTop);
float angBot = angleToPoint(self, goalPostBot);
//std::cout << "starting: " << RAD_T_DEG * angTop << " " << RAD_T_DEG * angBot << std::endl;
float res = calcLargestOpenAngle(wm, self, angTop, angBot, 99999);
//std::cout << angTop << " " << angBot << " | " << res << std::endl;
return res;
}
float FeatureExtractor::calcLargestOpenAngle(const rcsc::WorldModel &wm,
const rcsc::Vector2D &self,
float angTop, float angBot,
float maxDist) {
const rcsc::ServerParam & SP = rcsc::ServerParam::i();
std::vector<OpenAngle> openAngles;
openAngles.push_back(OpenAngle(angBot,angTop));
const PlayerCont& opps = wm.opponents();
for (PlayerCont::const_iterator it=opps.begin(); it != opps.end(); ++it) {
const PlayerObject& opp = *it;
if (valid(opp)) {
float oppAngle, oppDist;
angleDistToPoint(self, opp.pos(), oppAngle, oppDist);
// theta = arctan (opponentWidth / opponentDist)
float halfWidthAngle = atan2(SP.defaultKickableArea() * 0.5, oppDist);
//float oppAngleBottom = oppAngle;
//float oppAngleTop = oppAngle;
float oppAngleBottom = oppAngle - halfWidthAngle;
float oppAngleTop = oppAngle + halfWidthAngle;
//std::cout << " to split? " << oppDist << " " << maxDist << std::endl;
if (oppDist < maxDist) {
splitAngles(openAngles,oppAngleBottom,oppAngleTop);
}
}
}
float largestOpening = 0;
for (uint i = 0; i < openAngles.size(); ++i) {
OpenAngle &open = openAngles[i];
//std::cout << " opening: " << RAD_T_DEG * open.first << " " << RAD_T_DEG * open.second << std::endl;
float opening = open.second - open.first;
if (opening > largestOpening) {
largestOpening = opening;
}
}
return largestOpening;
}
void FeatureExtractor::splitAngles(std::vector<OpenAngle> &openAngles,
float oppAngleBottom, float oppAngleTop) {
std::vector<OpenAngle> resAngles;
for (uint i = 0; i < resAngles.size(); ++i) {
OpenAngle& open = resAngles[i];
if ((oppAngleTop < open.first) || (oppAngleBottom > open.second)) {
resAngles.push_back(open);
} else {
resAngles.push_back(OpenAngle(open.first, oppAngleBottom));
resAngles.push_back(OpenAngle(oppAngleTop, open.second));
}
}
openAngles = resAngles;
}
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
#include <rcsc/player/player_agent.h> #include <rcsc/player/player_agent.h>
#include <vector> #include <vector>
typedef std::pair<float, float> OpenAngle;
class FeatureExtractor { class FeatureExtractor {
public: public:
FeatureExtractor(); FeatureExtractor();
...@@ -18,6 +20,54 @@ public: ...@@ -18,6 +20,54 @@ public:
// How many features are there? // How many features are there?
inline int getNumFeatures() { return numFeatures; } inline int getNumFeatures() { return numFeatures; }
public:
// Determines if a player is a part of the HFO game or is inactive.
static bool valid(const rcsc::PlayerObject& player);
// Returns the angle (in radians) from self to a given point
static float angleToPoint(const rcsc::Vector2D &self,
const rcsc::Vector2D &point);
// Returns the angle (in radians) and distance from self to a given point
static void angleDistToPoint(const rcsc::Vector2D &self,
const rcsc::Vector2D &point,
float &ang, float &dist);
// Returns the angle between three points: Example opponent, self,
// goal center
static float angleBetween3Points(const rcsc::Vector2D &point1,
const rcsc::Vector2D &centerPoint,
const rcsc::Vector2D &point2);
// Find the opponent closest to the given point. Returns the
// distance and angle (in radians) from point to the closest
// opponent.
static void calcClosestOpp(const rcsc::WorldModel &wm,
const rcsc::Vector2D &point,
float &ang, float &dist);
// Returns the largest open (in terms of opponents) angle (radians)
// from self to the slice defined by [angBot, angTop].
static float calcLargestOpenAngle(const rcsc::WorldModel &wm,
const rcsc::Vector2D &self,
float angTop, float angBot, float maxDist);
// Returns the angle (in radians) corresponding to largest open shot
// on the goal.
static float calcLargestGoalAngle(const rcsc::WorldModel &wm,
const rcsc::Vector2D &self);
// Returns the largest open angle from self to a given teammate's
// position.
static float calcLargestTeammateAngle(const rcsc::WorldModel &wm,
const rcsc::Vector2D &self,
const rcsc::Vector2D &teammate);
// Helper function to split the open angles by the opponent angles
static void splitAngles(std::vector<OpenAngle> &openAngles,
float oppAngleBottom,
float oppAngleTop);
protected: protected:
// Encodes an angle feature as the sin and cosine of that angle, // Encodes an angle feature as the sin and cosine of that angle,
// effectively transforming a single angle into two features. // effectively transforming a single angle into two features.
...@@ -49,10 +99,15 @@ protected: ...@@ -49,10 +99,15 @@ protected:
void checkFeatures(); void checkFeatures();
protected: protected:
const static float RAD_T_DEG = 180.0 / M_PI;
const static float ALLOWED_PITCH_FRAC = 0.33;
int featIndx; int featIndx;
std::vector<float> feature_vec; // Contains the current features std::vector<float> feature_vec; // Contains the current features
const static float FEAT_MIN = -1; const static float FEAT_MIN = -1;
const static float FEAT_MAX = 1; const static float FEAT_MAX = 1;
const static float FEAT_INVALID = -2;
int numFeatures; // Total number of features int numFeatures; // Total number of features
// Observed values of some parameters. // Observed values of some parameters.
const static float observedSelfSpeedMax = 0.46; const static float observedSelfSpeedMax = 0.46;
......
...@@ -17,8 +17,11 @@ HighLevelFeatureExtractor::HighLevelFeatureExtractor(int num_teammates, ...@@ -17,8 +17,11 @@ HighLevelFeatureExtractor::HighLevelFeatureExtractor(int num_teammates,
{ {
assert(numTeammates >= 0); assert(numTeammates >= 0);
assert(numOpponents >= 0); assert(numOpponents >= 0);
numFeatures = num_basic_features + numFeatures = num_basic_features + features_per_teammate * numTeammates;
features_per_player * (numTeammates + numOpponents); if (numOpponents > 0) {
// One extra basic feature and one feature per teammate
numFeatures += 1 + numTeammates;
}
feature_vec.resize(numFeatures); feature_vec.resize(numFeatures);
} }
...@@ -28,165 +31,94 @@ const std::vector<float>& HighLevelFeatureExtractor::ExtractFeatures( ...@@ -28,165 +31,94 @@ const std::vector<float>& HighLevelFeatureExtractor::ExtractFeatures(
const WorldModel& wm) { const WorldModel& wm) {
featIndx = 0; featIndx = 0;
const ServerParam& SP = ServerParam::i(); const ServerParam& SP = ServerParam::i();
// ======================== SELF FEATURES ======================== //
const SelfObject& self = wm.self(); const SelfObject& self = wm.self();
const Vector2D& self_pos = self.pos(); const Vector2D& self_pos = self.pos();
const AngleDeg& self_ang = self.body(); const float self_ang = self.body().radian();
addFeature(self.posValid() ? FEAT_MAX : FEAT_MIN); const PlayerCont& teammates = wm.teammates();
// ADD_FEATURE(self_pos.x); float maxR = sqrtf(SP.pitchHalfLength() * SP.pitchHalfLength()
// ADD_FEATURE(self_pos.y); + SP.pitchHalfWidth() * SP.pitchHalfWidth());
Vector2D goalCenter(SP.pitchHalfLength(), 0);
// Direction and speed of the agent. // features about self pos
addFeature(self.velValid() ? FEAT_MAX : FEAT_MIN); float r;
if (self.velValid()) { float th;
addAngFeature(self_ang - self.vel().th()); angleDistToPoint(self_pos, goalCenter, th, r);
addNormFeature(self.speed(), 0., observedSelfSpeedMax); addNormFeature(r, 0, maxR);
} else { addNormFeature(th, -2*M_PI, 2*M_PI);
addFeature(0); addNormFeature(self_ang, -2*M_PI, 2*M_PI);
addFeature(0); // features about our open angle to goal
addFeature(0); addNormFeature(calcLargestGoalAngle(wm, self_pos), 0, M_PI);
//std::cout << "goal angle: " << RAD_T_DEG * features[ind-1] << std::endl;
// teammate's open angle to goal
int detected_teammates = 0;
for (PlayerCont::const_iterator it=teammates.begin(); it != teammates.end(); ++it) {
const PlayerObject& teammate = *it;
if (valid(teammate) && detected_teammates < numTeammates) {
addNormFeature(calcLargestGoalAngle(wm, teammate.pos()), 0, M_PI);
detected_teammates++;
}
} }
// Add zero features for any missing teammates
// Global Body Angle -- 0:right -90:up 90:down 180/-180:left for (int i=detected_teammates; i<numTeammates; ++i) {
addAngFeature(self_ang); addFeature(FEAT_INVALID);
// Neck Angle -- We probably don't need this unless we are
// controlling the neck manually.
// std::cout << "Face Error: " << self.faceError() << std::endl;
// if (self.faceValid()) {
// std::cout << "FaceAngle: " << self.face() << std::endl;
// }
addNormFeature(self.stamina(), 0., observedStaminaMax);
addFeature(self.isFrozen() ? FEAT_MAX : FEAT_MIN);
// Probabilities - Do we want these???
// std::cout << "catchProb: " << self.catchProbability() << std::endl;
// std::cout << "tackleProb: " << self.tackleProbability() << std::endl;
// std::cout << "fouldProb: " << self.foulProbability() << std::endl;
// Features indicating if we are colliding with an object
addFeature(self.collidesWithBall() ? FEAT_MAX : FEAT_MIN);
addFeature(self.collidesWithPlayer() ? FEAT_MAX : FEAT_MIN);
addFeature(self.collidesWithPost() ? FEAT_MAX : FEAT_MIN);
addFeature(self.isKickable() ? FEAT_MAX : FEAT_MIN);
// inertiaPoint estimates the ball point after a number of steps
// self.inertiaPoint(n_steps);
// ======================== LANDMARK FEATURES ======================== //
// Top Bottom Center of Goal
rcsc::Vector2D goalCenter(pitchHalfLength, 0);
addLandmarkFeatures(goalCenter, self_pos, self_ang);
rcsc::Vector2D goalPostTop(pitchHalfLength, -goalHalfWidth);
addLandmarkFeatures(goalPostTop, self_pos, self_ang);
rcsc::Vector2D goalPostBot(pitchHalfLength, goalHalfWidth);
addLandmarkFeatures(goalPostBot, self_pos, self_ang);
// Top Bottom Center of Penalty Box
rcsc::Vector2D penaltyBoxCenter(pitchHalfLength - penaltyAreaLength, 0);
addLandmarkFeatures(penaltyBoxCenter, self_pos, self_ang);
rcsc::Vector2D penaltyBoxTop(pitchHalfLength - penaltyAreaLength,
-penaltyAreaWidth / 2.);
addLandmarkFeatures(penaltyBoxTop, self_pos, self_ang);
rcsc::Vector2D penaltyBoxBot(pitchHalfLength - penaltyAreaLength,
penaltyAreaWidth / 2.);
addLandmarkFeatures(penaltyBoxBot, self_pos, self_ang);
// Corners of the Playable Area
rcsc::Vector2D centerField(0, 0);
addLandmarkFeatures(centerField, self_pos, self_ang);
rcsc::Vector2D cornerTopLeft(0, -pitchHalfWidth);
addLandmarkFeatures(cornerTopLeft, self_pos, self_ang);
rcsc::Vector2D cornerTopRight(pitchHalfLength, -pitchHalfWidth);
addLandmarkFeatures(cornerTopRight, self_pos, self_ang);
rcsc::Vector2D cornerBotRight(pitchHalfLength, pitchHalfWidth);
addLandmarkFeatures(cornerBotRight, self_pos, self_ang);
rcsc::Vector2D cornerBotLeft(0, pitchHalfWidth);
addLandmarkFeatures(cornerBotLeft, self_pos, self_ang);
// Distances to the edges of the playable area
if (self.posValid()) {
// Distance to Left field line
addDistFeature(self_pos.x, pitchHalfLength);
// Distance to Right field line
addDistFeature(pitchHalfLength - self_pos.x, pitchHalfLength);
// Distance to top field line
addDistFeature(pitchHalfWidth + self_pos.y, pitchWidth);
// Distance to Bottom field line
addDistFeature(pitchHalfWidth - self_pos.y, pitchWidth);
} else {
addFeature(0);
addFeature(0);
addFeature(0);
addFeature(0);
} }
// ======================== BALL FEATURES ======================== // // dist to our closest opp
const BallObject& ball = wm.ball(); if (numOpponents > 0) {
// Angle and distance to the ball calcClosestOpp(wm, self_pos, th, r);
addFeature(ball.rposValid() ? FEAT_MAX : FEAT_MIN); addNormFeature(r, 0, maxR);
if (ball.rposValid()) { //addNormFeature(th,-M_PI,M_PI);
addAngFeature(ball.angleFromSelf());
addDistFeature(ball.distFromSelf(), maxHFORadius); // teammates dists to closest opps
} else { detected_teammates = 0;
addFeature(0); for (PlayerCont::const_iterator it=teammates.begin(); it != teammates.end(); ++it) {
addFeature(0); const PlayerObject& teammate = *it;
addFeature(0); if (valid(teammate) && detected_teammates < numTeammates) {
} //addNormFeature(calcClosestOpp(wm,teammate.pos),0,maxR);
// Velocity and direction of the ball calcClosestOpp(wm, teammate.pos(), th, r);
addFeature(ball.velValid() ? FEAT_MAX : FEAT_MIN); addNormFeature(r, 0, maxR);
if (ball.velValid()) { //addNormFeature(th,-M_PI,M_PI);
// SeverParam lists ballSpeedMax a 2.7 which is too low detected_teammates++;
addNormFeature(ball.vel().r(), 0., observedBallSpeedMax); }
addAngFeature(ball.vel().th()); }
} else { // Add zero features for any missing teammates
addFeature(0); for (int i=detected_teammates; i<numTeammates; ++i) {
addFeature(0); addFeature(FEAT_INVALID);
addFeature(0); }
} }
assert(featIndx == num_basic_features); // open angle to teammates
detected_teammates = 0;
// ======================== TEAMMATE FEATURES ======================== // for (PlayerCont::const_iterator it=teammates.begin(); it != teammates.end(); ++it) {
// Vector of PlayerObject pointers sorted by increasing distance from self const PlayerObject& teammate = *it;
int detected_teammates = 0; if (valid(teammate) && detected_teammates < numTeammates) {
const PlayerPtrCont& teammates = wm.teammatesFromSelf(); addNormFeature(calcLargestTeammateAngle(wm, self_pos, teammate.pos()),0,M_PI);
for (PlayerPtrCont::const_iterator it = teammates.begin();
it != teammates.end(); ++it) {
PlayerObject* teammate = *it;
if (teammate->pos().x > 0 && teammate->unum() > 0 &&
detected_teammates < numTeammates) {
addPlayerFeatures(*teammate, self_pos, self_ang);
detected_teammates++; detected_teammates++;
} }
} }
// Add zero features for any missing teammates // Add zero features for any missing teammates
for (int i=detected_teammates; i<numTeammates; ++i) { for (int i=detected_teammates; i<numTeammates; ++i) {
for (int j=0; j<features_per_player; ++j) { addFeature(FEAT_INVALID);
addFeature(0);
}
} }
// ======================== OPPONENT FEATURES ======================== // // dist and angle to teammates
int detected_opponents = 0; detected_teammates = 0;
const PlayerPtrCont& opponents = wm.opponentsFromSelf(); for (PlayerCont::const_iterator it=teammates.begin(); it != teammates.end(); ++it) {
for (PlayerPtrCont::const_iterator it = opponents.begin(); const PlayerObject& teammate = *it;
it != opponents.end(); ++it) { if (valid(teammate) && detected_teammates < numTeammates) {
PlayerObject* opponent = *it; angleDistToPoint(self_pos, teammate.pos(), th, r);
if (opponent->pos().x > 0 && opponent->unum() > 0 && addNormFeature(r,0,maxR);
detected_opponents < numOpponents) { addNormFeature(th,-M_PI,M_PI);
addPlayerFeatures(*opponent, self_pos, self_ang); detected_teammates++;
detected_opponents++;
} }
} }
// Add zero features for any missing opponents // Add zero features for any missing teammates
for (int i=detected_opponents; i<numOpponents; ++i) { for (int i=detected_teammates; i<numTeammates; ++i) {
for (int j=0; j<features_per_player; ++j) { addFeature(FEAT_INVALID);
addFeature(0); addFeature(FEAT_INVALID);
}
} }
assert(featIndx == numFeatures); assert(featIndx == numFeatures);
checkFeatures(); checkFeatures();
//std::cout << "features: " << features.rows(0,ind-1).t();
return feature_vec; return feature_vec;
} }
...@@ -16,9 +16,9 @@ public: ...@@ -16,9 +16,9 @@ public:
protected: protected:
// Number of features for non-player objects. // Number of features for non-player objects.
const static int num_basic_features = 58; const static int num_basic_features = 4;
// Number of features for each player or opponent in game. // Number of features for each player or opponent in game.
const static int features_per_player = 8; const static int features_per_teammate = 4;
int numTeammates; // Number of teammates in HFO int numTeammates; // Number of teammates in HFO
int numOpponents; // Number of opponents in HFO int numOpponents; // Number of opponents in HFO
bool playingOffense; // Are we playing offense or defense? bool playingOffense; // Are we playing offense or defense?
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment