-
Notifications
You must be signed in to change notification settings - Fork 186
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: sanatd33 <[email protected]> Co-authored-by: jvogt23 <[email protected]> Co-authored-by: Peter Garud <[email protected]> Co-authored-by: Sid Parikh <[email protected]> Co-authored-by: sid-parikh <[email protected]>
- Loading branch information
1 parent
cd689b8
commit 0324315
Showing
5 changed files
with
317 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,191 @@ | ||
#include "seeker.hpp" | ||
|
||
namespace strategy { | ||
|
||
Seeker::Seeker(int robot_id) { robot_id_ = robot_id; } | ||
|
||
std::optional<RobotIntent> Seeker::get_task(RobotIntent intent, const WorldState* last_world_state, | ||
FieldDimensions field_dimensions) { | ||
// Determine target position for seeking | ||
rj_geometry::Point current_loc = last_world_state->get_robot(true, robot_id_).pose.position(); | ||
|
||
target_pt_ = get_open_point(last_world_state, current_loc, field_dimensions); | ||
|
||
planning::PathTargetFaceOption face_option = planning::FaceBall{}; | ||
bool ignore_ball = false; | ||
planning::LinearMotionInstant goal{target_pt_, rj_geometry::Point{0.0, 0.0}}; | ||
intent.motion_command = planning::MotionCommand{"path_target", goal, face_option, ignore_ball}; | ||
|
||
return intent; | ||
} | ||
|
||
rj_geometry::Point Seeker::get_open_point(const WorldState* world_state, | ||
rj_geometry::Point current_position, | ||
const FieldDimensions& field_dimensions) const { | ||
return Seeker::calculate_open_point(3.0, .2, current_position, world_state, field_dimensions); | ||
} | ||
|
||
rj_geometry::Point Seeker::calculate_open_point(double current_prec, double min_prec, | ||
rj_geometry::Point current_point, | ||
const WorldState* world_state, | ||
const FieldDimensions& field_dimensions) const { | ||
while (current_prec > min_prec) { | ||
rj_geometry::Point ball_pos = world_state->ball.position; | ||
rj_geometry::Point min = current_point; | ||
double min_val = Seeker::eval_point(ball_pos, current_point, world_state); | ||
double curr_val{}; | ||
// Points in a current_prec radius of the current point, at 45 degree intervals | ||
std::vector<rj_geometry::Point> check_points{ | ||
correct_point(current_point + rj_geometry::Point{current_prec, 0}, field_dimensions), | ||
correct_point(current_point + rj_geometry::Point{-current_prec, 0}, field_dimensions), | ||
correct_point(current_point + rj_geometry::Point{0, current_prec}, field_dimensions), | ||
correct_point(current_point + rj_geometry::Point{0, -current_prec}, field_dimensions), | ||
correct_point( | ||
current_point + rj_geometry::Point{current_prec * 0.707, current_prec * 0.707}, | ||
field_dimensions), | ||
correct_point( | ||
current_point + rj_geometry::Point{current_prec * 0.707, -current_prec * 0.707}, | ||
field_dimensions), | ||
correct_point( | ||
current_point + rj_geometry::Point{-current_prec * 0.707, current_prec * 0.707}, | ||
field_dimensions), | ||
correct_point( | ||
current_point + rj_geometry::Point{-current_prec * 0.707, -current_prec * 0.707}, | ||
field_dimensions)}; | ||
|
||
// Finds the best point out of the ones checked | ||
for (auto point : check_points) { | ||
curr_val = Seeker::eval_point(ball_pos, point, world_state); | ||
if (curr_val < min_val) { | ||
min_val = curr_val; | ||
min = point; | ||
} | ||
} | ||
current_prec *= 0.5; | ||
current_point = min; | ||
} | ||
return current_point; | ||
} | ||
|
||
rj_geometry::Point Seeker::correct_point(rj_geometry::Point p, | ||
const FieldDimensions& field_dimensions) const { | ||
double border_buffer = .2; | ||
double x = p.x(); | ||
double y = p.y(); | ||
|
||
// X Border | ||
if (p.x() > field_dimensions.field_x_right_coord() - border_buffer) { | ||
x = field_dimensions.field_x_right_coord() - border_buffer; | ||
} else if (p.x() < field_dimensions.field_x_left_coord() + border_buffer) { | ||
x = field_dimensions.field_x_left_coord() + border_buffer; | ||
} | ||
|
||
// Y Border | ||
if (p.y() > field_dimensions.their_goal_loc().y() - border_buffer) { | ||
y = field_dimensions.their_goal_loc().y() - border_buffer; | ||
} else if (p.y() < field_dimensions.our_goal_loc().y() + border_buffer) { | ||
y = field_dimensions.our_goal_loc().y() + border_buffer; | ||
} | ||
|
||
// Goalie Boxes | ||
if ((y < 1.2 || y > 7.8) && fabs(x) < 1.2) { | ||
if (y > 4.5) { | ||
y = 8.0 - border_buffer; | ||
} else { | ||
y = 1.0 + border_buffer; | ||
} | ||
|
||
if (x > .5) { | ||
x = 1.0 + border_buffer; | ||
} else { | ||
x = -1.0 - border_buffer; | ||
} | ||
} | ||
|
||
// Assigns robots to horizontal thirds | ||
if (robot_id_ == 1) { | ||
// Assign left | ||
if (x > field_dimensions.field_x_left_coord() + field_dimensions.width() / 2) { | ||
x = field_dimensions.field_x_left_coord() + field_dimensions.width() / 2 - | ||
border_buffer; | ||
} | ||
} else if (robot_id_ == 2) { | ||
// Assign right | ||
if (x < field_dimensions.field_x_right_coord() - field_dimensions.width() / 2) { | ||
x = field_dimensions.field_x_right_coord() - field_dimensions.width() / 2 + | ||
border_buffer; | ||
} | ||
} else { | ||
// Assign middle | ||
if (x < field_dimensions.field_x_left_coord() + field_dimensions.width() / 3) { | ||
x = field_dimensions.field_x_left_coord() + field_dimensions.width() / 3 + | ||
border_buffer; | ||
} else if (x > field_dimensions.field_x_right_coord() - field_dimensions.width() / 3) { | ||
x = field_dimensions.field_x_right_coord() - field_dimensions.width() / 3 - | ||
border_buffer; | ||
} | ||
} | ||
|
||
return rj_geometry::Point(x, y); | ||
} | ||
|
||
double Seeker::eval_point(rj_geometry::Point ball_pos, rj_geometry::Point current_point, | ||
const WorldState* world_state) { | ||
// Determines 'how good' a point is | ||
// A higher value is a worse point | ||
|
||
// Does not go into the goalie boxes | ||
rj_geometry::Rect goal_box{rj_geometry::Point{1, 8}, rj_geometry::Point{-1, 9}}; | ||
if (goal_box.contains_point(current_point)) { | ||
return 10000000; | ||
} | ||
|
||
// Line of Sight Heuristic | ||
double max = 0; | ||
double curr_dp = 0; | ||
for (auto robot : world_state->their_robots) { | ||
curr_dp = (current_point).norm().dot((robot.pose.position() - ball_pos).norm()); | ||
curr_dp *= curr_dp; | ||
if (curr_dp > max) { | ||
max = curr_dp; | ||
} | ||
} | ||
|
||
// Whether the path from ball to the point is blocked | ||
// Same logic in passing to check if target is open | ||
rj_geometry::Segment pass_path{ball_pos, current_point}; | ||
double min_robot_dist = 10000; | ||
float min_path_dist = 10000; | ||
for (auto bot : world_state->their_robots) { | ||
rj_geometry::Point opp_pos = bot.pose.position(); | ||
min_robot_dist = std::min(min_robot_dist, current_point.dist_to(opp_pos)); | ||
min_path_dist = std::min(min_path_dist, pass_path.dist_to(opp_pos)); | ||
} | ||
|
||
for (auto bot : world_state->our_robots) { | ||
rj_geometry::Point ally_pos = bot.pose.position(); | ||
min_robot_dist = std::min(min_robot_dist, current_point.dist_to(ally_pos)); | ||
min_path_dist = std::min(min_path_dist, pass_path.dist_to(ally_pos)); | ||
} | ||
|
||
min_path_dist = 0.1f / min_path_dist; | ||
min_robot_dist = 0.1 / min_robot_dist; | ||
|
||
// More Line of Sight Heuristics | ||
for (auto robot : world_state->our_robots) { | ||
curr_dp = (current_point - ball_pos).norm().dot((robot.pose.position() - ball_pos).norm()); | ||
curr_dp *= curr_dp; | ||
if (curr_dp > max) { | ||
max = curr_dp; | ||
} | ||
} | ||
|
||
// Additional heuristics for calculating optimal point | ||
double ball_proximity_loss = (current_point - ball_pos).mag() * .002; | ||
double goal_distance_loss = (9.0 - current_point.y()) * .008; | ||
|
||
// Final evaluation | ||
return max + ball_proximity_loss + goal_distance_loss + min_path_dist + min_robot_dist; | ||
} | ||
|
||
} // namespace strategy |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
#pragma once | ||
|
||
#include <cmath> | ||
|
||
#include <rclcpp/rclcpp.hpp> | ||
#include <rclcpp_action/rclcpp_action.hpp> | ||
#include <spdlog/spdlog.h> | ||
|
||
#include <rj_msgs/action/robot_move.hpp> | ||
|
||
#include "planning/instant.hpp" | ||
#include "position.hpp" | ||
#include "rj_common/field_dimensions.hpp" | ||
#include "rj_common/time.hpp" | ||
#include "rj_constants/constants.hpp" | ||
#include "rj_geometry/geometry_conversions.hpp" | ||
#include "rj_geometry/point.hpp" | ||
#include "role_interface.hpp" | ||
|
||
namespace strategy { | ||
|
||
/* | ||
* The Seeker role provides the implementation for a offensive robot that | ||
* is trying to get open, so that they can receive a pass | ||
*/ | ||
class Seeker : public RoleInterface { | ||
public: | ||
Seeker(int robot_id); | ||
~Seeker() = default; | ||
Seeker(const Seeker& other) = default; | ||
Seeker(Seeker&& other) = default; | ||
Seeker& operator=(const Seeker& other) = default; | ||
Seeker& operator=(Seeker&& other) = default; | ||
|
||
/** | ||
* @brief Returns a seeker behavior which aims to get open | ||
* | ||
* @param intent The RobotIntent to add the movement to | ||
* @param world_state The current WorldState | ||
* @param field_dimensions The dimensions of the field | ||
* | ||
* @return [RobotIntent with next target point for the robot] | ||
*/ | ||
std::optional<RobotIntent> get_task(RobotIntent intent, const WorldState* world_state, | ||
FieldDimensions field_dimensions) override; | ||
|
||
private: | ||
// The seeker's id | ||
int robot_id_; | ||
// The taret point to move to | ||
rj_geometry::Point target_pt_{0.0, 0.0}; | ||
|
||
/** | ||
* @brief Returns the point which is most 'open' | ||
* | ||
* @param world_state The current WorldState | ||
* @param current_position The current position of the seeker | ||
* @param field_dimensions The dimensions of the field | ||
* | ||
* @return rj_geometry::Point The target point | ||
*/ | ||
rj_geometry::Point get_open_point(const WorldState* world_state, | ||
rj_geometry::Point current_position, | ||
const FieldDimensions& field_dimensions) const; | ||
|
||
/** | ||
* @brief Calculates which point is the best by iteratively searching around the robot | ||
* | ||
* @param current_prec A double that represents how far away to look from the robot | ||
* @param min_prec A double that represents the minimum distance to look from the robot | ||
* @param current_point The robot's current position | ||
* @param world_state The current WorldState | ||
* @param field_dimensions The dimensions of the field | ||
* | ||
* @return rj_geometry::Point The best point found | ||
*/ | ||
rj_geometry::Point calculate_open_point(double current_prec, double min_prec, | ||
rj_geometry::Point current_point, | ||
const WorldState* world_state, | ||
const FieldDimensions& field_dimensions) const; | ||
|
||
/** | ||
* @brief Corrects the point to be within the field | ||
* | ||
* @param point The point to correct | ||
* @param field_dimensions The dimensions of the field | ||
* | ||
* @return rj_geometry::Point The corrected point | ||
*/ | ||
[[nodiscard]] rj_geometry::Point correct_point(rj_geometry::Point point, | ||
const FieldDimensions& field_dimensions) const; | ||
|
||
/** | ||
* @brief Calculates how 'good' a target point is | ||
* | ||
* @param ball_pos The current position of the ball | ||
* @param current_point The point that is being evaluated | ||
* @param world_state The current world state | ||
* | ||
* @return double The evaluation of that target point | ||
*/ | ||
static double eval_point(rj_geometry::Point ball_pos, rj_geometry::Point current_point, | ||
const WorldState* world_state); | ||
}; | ||
|
||
} // namespace strategy |