Skip to content

Commit

Permalink
Added behavior for marking enemy robots (#2176)
Browse files Browse the repository at this point in the history

Co-authored-by: Sid Parikh <[email protected]>
  • Loading branch information
jvogt23 and sid-parikh authored Mar 6, 2024
1 parent 66dd969 commit e52b026
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 9 deletions.
1 change: 1 addition & 0 deletions soccer/src/soccer/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ set(ROBOCUP_LIB_SRC
strategy/agent/position/defense.cpp
strategy/agent/position/waller.cpp
strategy/agent/position/seeker.cpp
strategy/agent/position/marker.cpp
strategy/agent/position/goal_kicker.cpp
strategy/agent/position/penalty_player.cpp
)
Expand Down
36 changes: 34 additions & 2 deletions soccer/src/soccer/strategy/agent/position/defense.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace strategy {

Defense::Defense(int r_id) : Position(r_id, "Defense") {}
Defense::Defense(int r_id) : Position(r_id, "Defense"), marker_{field_dimensions_} {}

std::optional<RobotIntent> Defense::derived_get_task(RobotIntent intent) {
current_state_ = update_state();
Expand Down Expand Up @@ -37,6 +37,14 @@ Defense::State Defense::update_state() {
walling_robots_ = {(u_int8_t)robot_id_};
break;
case WALLING:
// If a wall is already full,
// Remove the robot with the highest ID from a wall
// and make them a marker instead.
if (walling_robots_.size() > kMaxWallers &&
this->robot_id_ == *max_element(walling_robots_.begin(), walling_robots_.end())) {
send_leave_wall_request();
next_state = ENTERING_MARKING;
}
break;
case SEARCHING:
break;
Expand All @@ -61,12 +69,28 @@ Defense::State Defense::update_state() {
if (check_is_done()) {
next_state = IDLING;
}
case MARKING:
if (marker_.get_target() == -1 || marker_.target_out_of_bounds(world_state)) {
next_state = ENTERING_MARKING;
}
break;
case ENTERING_MARKING:
marker_.choose_target(world_state);
int target_id = marker_.get_target();
if (target_id == -1) {
next_state = ENTERING_MARKING;
} else {
next_state = MARKING;
}
}

return next_state;
}

std::optional<RobotIntent> Defense::state_to_task(RobotIntent intent) {
// if (robot_id_ == 2) {
// SPDLOG_INFO("{} current state of 2", current_state_);
// }
if (current_state_ == IDLING) {
auto empty_motion_cmd = planning::MotionCommand{};
intent.motion_command = empty_motion_cmd;
Expand Down Expand Up @@ -113,7 +137,7 @@ std::optional<RobotIntent> Defense::state_to_task(RobotIntent intent) {
Waller waller{waller_id_, (int)walling_robots_.size()};
return waller.get_task(intent, last_world_state_, this->field_dimensions_);
}
} else if (current_state_ = FACING) {
} else if (current_state_ == FACING) {
rj_geometry::Point robot_position =
last_world_state_->get_robot(true, robot_id_).pose.position();
auto current_location_instant =
Expand All @@ -123,6 +147,14 @@ std::optional<RobotIntent> Defense::state_to_task(RobotIntent intent) {
planning::MotionCommand{"path_target", current_location_instant, face_ball};
intent.motion_command = face_ball_cmd;
return intent;
} else if (current_state_ == ENTERING_MARKING) {
// Prepares a robot for marking. NOTE: May update to add move to center of field
auto empty_motion_cmd = planning::MotionCommand{};
intent.motion_command = empty_motion_cmd;
return intent;
} else if (current_state_ == MARKING) {
// Marker marker = Marker((u_int8_t) robot_id_);
return marker_.get_task(intent, last_world_state_, this->field_dimensions_);
}

return std::nullopt;
Expand Down
22 changes: 15 additions & 7 deletions soccer/src/soccer/strategy/agent/position/defense.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#include <rj_msgs/action/robot_move.hpp>

#include "marker.hpp"
#include "planning/instant.hpp"
#include "position.hpp"
#include "rj_common/field_dimensions.hpp"
Expand Down Expand Up @@ -44,6 +45,8 @@ class Defense : public Position {
private:
int move_ct_ = 0;

static constexpr int kMaxWallers{3};

/**
* @brief The derived_get_task method returns the task for the defensive robot
* to do based on the game situation. The method will continuously look to assign
Expand All @@ -56,13 +59,15 @@ class Defense : public Position {
std::optional<RobotIntent> derived_get_task(RobotIntent intent) override;

enum State {
IDLING, // simply staying in place
JOINING_WALL, // send message to find its place in the wall
WALLING, // participating in the wall
SEARCHING, // moving around on the field to do something
RECEIVING, // physically intercepting the ball from a pass
PASSING, // physically kicking the ball towards another robot
FACING, // turning to face the passing robot
IDLING, // simply staying in place
JOINING_WALL, // send message to find its place in the wall
WALLING, // participating in the wall
SEARCHING, // moving around on the field to do something
RECEIVING, // physically intercepting the ball from a pass
PASSING, // physically kicking the ball towards another robot
FACING, // turning to face the passing robot
MARKING, // Following closely to an offense robot
ENTERING_MARKING, // Choosing/waiting for a robot to mark
};

State update_state();
Expand Down Expand Up @@ -113,6 +118,9 @@ class Defense : public Position {
// current state of the defense agent (state machine)
int get_waller_id();
State current_state_ = JOINING_WALL;

int get_marker_target_id();
Marker marker_;
};

} // namespace strategy
49 changes: 49 additions & 0 deletions soccer/src/soccer/strategy/agent/position/marker.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#include "marker.hpp"

namespace strategy {
Marker::Marker(FieldDimensions field_dimensions) {
this->y_bound = field_dimensions.length() / 2;
this->marker_follow_cutoff = field_dimensions.width() / 2;
}

std::optional<RobotIntent> Marker::get_task(RobotIntent intent, const WorldState* world_state,
[[maybe_unused]] FieldDimensions field_dimensions) {
this->y_bound = field_dimensions.length() / 2;
this->marker_follow_cutoff = field_dimensions.width() / 2;

rj_geometry::Point targetPoint = world_state->get_robot(false, target_).pose.position();
rj_geometry::Point ballPoint = world_state->ball.position;
rj_geometry::Point targetToBall = (ballPoint - targetPoint).normalized(0.55f);
planning::LinearMotionInstant goal{targetPoint + targetToBall, rj_geometry::Point{0.0, 0.0}};
intent.motion_command =
planning::MotionCommand{"path_target", goal, planning::FaceBall{}, true};
return intent;
}

void Marker::choose_target(const WorldState* ws) {
// TODO: (James Vogt, github: jvogt23)
// If we ever use multiple Markers, they should choose different
// robots to track from each other. Logic for this operation must be
// added because multiple markers currently mark the same robot.
for (int i = 0; i < kNumShells; i++) {
if (std::fabs(ws->get_robot(false, i).pose.position().x()) < marker_follow_cutoff &&
ws->get_robot(false, i).pose.position().y() < y_bound &&
(ws->ball.position - ws->get_robot(false, i).pose.position()).mag() > .25) {
target_ = i;
return;
}
}
target_ = -1;
}

bool Marker::target_out_of_bounds(const WorldState* ws) {
if (target_ == -1) return true;
if (ws->get_robot(false, target_).pose.position().y() > y_bound) {
target_ = -1;
return true;
}
return false;
}

int Marker::get_target() { return target_; }
} // namespace strategy
43 changes: 43 additions & 0 deletions soccer/src/soccer/strategy/agent/position/marker.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#pragma once
#include <rclcpp/rclcpp.hpp>

#include <rj_geometry/segment.hpp>
#include <rj_msgs/msg/robot_state.hpp>
#include <rj_msgs/msg/world_state.hpp>

#include "position.hpp"
#include "rj_geometry/point.hpp"
#include "role_interface.hpp"

namespace strategy {

/**
* Represents a Marker, a defensive role that targets an enemy robot
* and follows it around the field while it is on our side, blocking passes.
*/
class Marker : public RoleInterface {
private:
int target_{-1};

// Calculated from field dimensions - Prevent the marker from
// marking any enemies that are out of range; e. g. on the other
// side of the field or the sidelines.
float y_bound{FieldDimensions::kDefaultDimensions.length() / 2};
float marker_follow_cutoff{FieldDimensions::kDefaultDimensions.width() / 2};

public:
Marker(FieldDimensions field_dimensions);
~Marker() = default;
Marker(const Marker& other) = default;
Marker(Marker&& other) = default;
Marker& operator=(const Marker& other) = default;
Marker& operator=(Marker&& other) = default;

std::optional<RobotIntent> get_task(RobotIntent intent, const WorldState* world_state,
FieldDimensions field_dimensions) override;

void choose_target(const WorldState* ws);
int get_target();
bool target_out_of_bounds(const WorldState* ws);
};
} // namespace strategy

0 comments on commit e52b026

Please sign in to comment.