1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
/**
 * @file TeamBallLocatorMedian.h
 *
 * Implementation of class TeamBallLocatorMedian
 */

#include "TeamBallLocatorMedian.h"

TeamBallLocatorMedian::TeamBallLocatorMedian()
{
  DEBUG_REQUEST_REGISTER("TeamBallLocatorMedian:draw_ball_on_field", "draw the team ball model on the field", false);
  DEBUG_REQUEST_REGISTER("TeamBallLocatorMedian:draw_teamball_input", "draw all the balls uses for teamball", false);
  DEBUG_REQUEST_REGISTER("TeamBallLocatorMedian:draw_ownball", "draw all the balls uses for teamball", false);
  
  getDebugParameterList().add(&params);
}

TeamBallLocatorMedian::~TeamBallLocatorMedian()
{
    getDebugParameterList().remove(&params);
}

void TeamBallLocatorMedian::execute()
{
  // collect all balls seen by teammates and myself
  for (auto const& it: getTeamMessage().data) {
    if(it.first != getPlayerInfo().playerNumber)
    {
      const unsigned int& playerNumber = it.first;
      const TeamMessageData& msg = it.second;

      // check if the robot is able to play (inactive robots)
      if(!getTeamMessagePlayersState().isPlaying(playerNumber)) { continue; }
      
      // -1 means "ball never seen" and only "new" messages
      if(msg.ballAge >= 0 && lastMessages[playerNumber] < msg.frameInfo.getTime())
      {
        lastMessages[playerNumber] = msg.frameInfo.getTime();
        // global position of the ball and time last seen
        Vector2dTS ballPosTS;
        ballPosTS.vec = msg.pose * msg.ballPosition;
        ballPosTS.t = msg.frameInfo.getTime() - static_cast<int>(msg.ballAge);
        // collect balls
        ballPosHist.push_back(ballPosTS);

        // set time to the latest received message
        if (msg.frameInfo.getTime() > getTeamBallModel().time )
        {
          getTeamBallModel().time = msg.frameInfo.getTime();
        }
      }
    } else {
      // me myself and only "new" messages
      const unsigned int& playerNumber = it.first;
      const TeamMessageData& msg = it.second;
      
      // -1 means "ball never seen"
      if(msg.ballAge >= 0 && lastMessages[playerNumber] < msg.frameInfo.getTime())
      {
        lastMessages[playerNumber] = msg.frameInfo.getTime();
        // global position of the ball and time last seen
        Vector2dTS ballPosTS;
        ballPosTS.vec = msg.pose * msg.ballPosition;
        ballPosTS.t = msg.frameInfo.getTime() - static_cast<int>(msg.ballAge);
        // collect balls
        ownballPosHist.push_back(ballPosTS);
      }
    }
  }

  // find oldest balls and erase the ones, which ar older than 'maxTimeOffset'
  sort(ballPosHist.begin(), ballPosHist.end());
  std::vector<Vector2dTS>::iterator cutOff;
  // we are iterating through the sorted array from small (old) to high (new) times
  for(cutOff = ballPosHist.begin(); cutOff != ballPosHist.end(); cutOff++)
  {
    if (cutOff->t >= getTeamBallModel().time - params.maxTimeOffset)
    {
      break;
    }
  }

  ballPosHist.erase(ballPosHist.begin(), cutOff);
  
  // find oldest balls and erase the ones, which ar older than 'maxTimeOffset'
  sort(ownballPosHist.begin(), ownballPosHist.end());
  // we are iterating through the sorted array from small (old) to high (new) times
  for(cutOff = ownballPosHist.begin(); cutOff != ownballPosHist.end(); cutOff++)
  {
    if (cutOff->t >= getTeamBallModel().time - params.maxTimeOffset)
    {
      break;
    }
  }
  ownballPosHist.erase(ownballPosHist.begin(), cutOff);
  
  DEBUG_REQUEST("TeamBallLocatorMedian:draw_teamball_input",
    FIELD_DRAWING_CONTEXT;
    PEN("0000FF", 20);
    for(size_t i = 0; i < ballPosHist.size(); i++)
    {
      CIRCLE(ballPosHist[i].vec.x, ballPosHist[i].vec.y, 50);
    }
  );
  
  DEBUG_REQUEST("TeamBallLocatorMedian:draw_ownball",
    FIELD_DRAWING_CONTEXT;
    PEN("66FFFF", 20);
    for(size_t i = 0; i < ownballPosHist.size(); i++)
    {
      CIRCLE(ownballPosHist[i].vec.x, ownballPosHist[i].vec.y, 50);
    }
  );
  
  if(ballPosHist.size() > 0)
  {
    // median in x and y
    std::vector<double> xHist(ballPosHist.size());
    std::vector<double> yHist(ballPosHist.size());
    for(size_t i = 0; i < ballPosHist.size(); i++)
    {
      xHist[i] = ballPosHist[i].vec.x;
      yHist[i] = ballPosHist[i].vec.y;
    }
    sort(xHist.begin(), xHist.end());
    sort(yHist.begin(), yHist.end());
    Vector2d teamball;
    teamball.x = xHist[xHist.size()/2];
    teamball.y = yHist[yHist.size()/2];
    
    // root mean squared error
    getTeamBallModel().rmse = 0.0;
    for(size_t i = 0; i < ballPosHist.size(); i++)
    {
      getTeamBallModel().rmse += (teamball.x - xHist[i])*(teamball.x - xHist[i]) + (teamball.y - yHist[i])*(teamball.y - yHist[i]);
    }
    getTeamBallModel().rmse = getTeamBallModel().rmse / static_cast<double>(ballPosHist.size());
    getTeamBallModel().rmse = sqrt(getTeamBallModel().rmse);

    // write result and transform
    getTeamBallModel().positionOnField = teamball;
    getTeamBallModel().position = getRobotPose() / getTeamBallModel().positionOnField;
  }

  // set validity of the teamball -> invalidate after a certain time
  getTeamBallModel().valid = (getTeamBallModel().time + params.maxTimeValid >= getFrameInfo().getTime());

  DEBUG_REQUEST("TeamBallLocatorMedian:draw_ball_on_field",
    FIELD_DRAWING_CONTEXT;
    PEN("FF0000", 20);
    FILLOVAL(getTeamBallModel().positionOnField.x, getTeamBallModel().positionOnField.y, 50, 50);
    TEXT_DRAWING(getTeamBallModel().positionOnField.x+100, getTeamBallModel().positionOnField.y+100, ballPosHist.size());
    TEXT_DRAWING(getTeamBallModel().positionOnField.x+100, getTeamBallModel().positionOnField.y-100, getTeamBallModel().rmse);
  );
}