Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Is done planners #1911

Merged
merged 29 commits into from
Jul 8, 2022
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
60c6921
add some comments
kfu02 Jun 20, 2022
91a1361
stub out isdone
kfu02 Jun 20, 2022
038188f
Merge branch 'ros2' into is-done-planners
kfu02 Jun 20, 2022
19e3064
make is_done return false for every planner
kfu02 Jun 20, 2022
261904b
add pathtarget isdone
kfu02 Jun 21, 2022
c4c6399
add nearly_equals to linear motion instant
kfu02 Jun 26, 2022
e32a2a3
change pathtarget and python is_done to check desired vel
kfu02 Jun 26, 2022
7e92cb8
cleanup pass
kfu02 Jun 27, 2022
582d150
fix clang warnings
kfu02 Jun 29, 2022
3e5ae27
add new capture only play
kfu02 Jun 29, 2022
fe5e463
tweak capture is_done python, fix bugs
kfu02 Jun 29, 2022
d6741cb
tune capture params
kfu02 Jun 29, 2022
4fa0488
add collect is_done
kfu02 Jun 29, 2022
30633fe
apply_low_pass_filter commented and used
kfu02 Jul 5, 2022
60cc38a
add settle is done
kfu02 Jul 5, 2022
e33dd11
"add" escape obstacles is_done
kfu02 Jul 6, 2022
427d91a
add kick test play, reenable capture is done
kfu02 Jul 6, 2022
69b4f41
add line kick is done
kfu02 Jul 6, 2022
677c1d5
pivot is done
kfu02 Jul 6, 2022
96c4e5a
fix style
kfu02 Jul 6, 2022
3ff66a6
bugfix
kfu02 Jul 6, 2022
80f952e
more style fixes
kfu02 Jul 6, 2022
b319af4
add docstrings for new nearly_equals
kfu02 Jul 6, 2022
5ae4ae6
sad admission
kfu02 Jul 6, 2022
9427f4b
add docstring
kfu02 Jul 6, 2022
174e131
add nodiscard flags
kfu02 Jul 7, 2022
28a13a8
add another nodiscard
kfu02 Jul 7, 2022
4d5f97e
make cached vars private
kfu02 Jul 7, 2022
dc31aa1
Merge branch 'ros2' into is-done-planners
kfu02 Jul 8, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions config/plays.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ penalty_defense.PenaltyDefense()
penalty_offense.PenaltyOffense()
test_motion_planning.OneRobot()
test_motion_planning.AllBots()
test_motion_planning.KickBall()
None
15 changes: 15 additions & 0 deletions rj_common/include/rj_common/utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,21 @@ static inline T fix_angle_radians(T a) {
return remainder(a, 2 * M_PI);
}

/*
* @brief Approximate moving average with first-order low pass filter.
*
* @details For those without a digital signal processing background, think of
* it as giving small weights to new estimates compared to the existing belief.
* For example, to approximate the avg velocity, with a gain of 0.8, we can do:
* new_avg_vel = (0.8 * old_avg_vel) + (0.2 * most_recent_vel)
* every time a new velocity comes in. This protects us from noisy velocity
* readings while also updating the average as it changes.
*
* @param old_value current value of moving average
* @param new_value new value to add to moving average
* @param gain resistance to noise (higher = more weight to old_value)
* @return new moving average estimate
*/
template <typename T>
inline T apply_low_pass_filter(const T& old_value, const T& new_value, double gain) {
return gain * new_value + (1 - gain) * old_value;
Expand Down
43 changes: 42 additions & 1 deletion rj_gameplay/rj_gameplay/play/test_motion_planning.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import stp.role.cost
from rj_msgs.msg import RobotIntent

from rj_gameplay.skill import move
from rj_gameplay.skill import move, pivot_kick


class State(Enum):
Expand Down Expand Up @@ -159,3 +159,44 @@ def tick(
intents[i] = self._move_skills[i].tick(world_state)

return intents


class KickBall(stp.play.Play):
"""
Make robot 0 capture, then kick the ball.

Directly overrides the STP architecture to send RI to gameplay.
"""

def __init__(self):
super().__init__()
self._state = "capturing"
self.capture_skill = None
self.pivot_kick_skill = None

self.robot_id = 0
self.robot = None

def tick(
self,
world_state: stp.rc.WorldState,
) -> List[RobotIntent]:

self.robot = world_state.our_robots[self.robot_id]
intents = [None for _ in range(16)]
my_robot_intent = None

# None on INIT and state changes
if self.pivot_kick_skill is None:
self.pivot_kick_skill = pivot_kick.PivotKick(
robot=self.robot,
target_point=np.array([0.0, 6.0]),
chip=True,
kick_speed=5.0,
)
else:
my_robot_intent = self.pivot_kick_skill.tick(world_state)

intents[self.robot_id] = my_robot_intent

return intents
4 changes: 3 additions & 1 deletion rj_gameplay/rj_gameplay/role/seeker.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ def get_open_point(self, world_state, region: List, centroid) -> np.ndarray:
# line-of-sight calculations
# (LOS is maximized by making dot product of angle between unit vectors as close to 0 as possible)
ball_dir = lambda pos: pos - world_state.ball.pos
ball_dir_norm = lambda pos: ball_dir(pos) / np.linalg.norm(ball_dir(pos))
ball_dir_norm = lambda pos: ball_dir(pos) / (
1e-9 + np.linalg.norm(ball_dir(pos))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What difference did this addition make?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if the norm of the ball dir vector is exactly 0, this causes a div by 0 error without the + tiny value

)
opps_to_ball = [
ball_dir_norm(opp_robot.pose[0:2]) for opp_robot in world_state.their_robots
]
Expand Down
22 changes: 9 additions & 13 deletions rj_gameplay/rj_gameplay/skill/capture.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,14 @@
from abc import ABC, abstractmethod

import rj_gameplay.eval as eval
import argparse
import py_trees
import sys
import time
from rj_msgs.msg import RobotIntent, CollectMotionCommand
from typing import Optional

import stp.skill as skill
import stp.role as role
import numpy as np
import stp.action as action
import stp.rc as rc
from typing import Optional
import numpy as np

from stp.utils.constants import RobotConstants
import stp.skill as skill
from rj_msgs.msg import CollectMotionCommand, RobotIntent
from stp.utils.constants import BallConstants, RobotConstants


class Capture(skill.Skill):
Expand Down Expand Up @@ -46,8 +40,10 @@ def is_done(self, world_state) -> bool:

dist_to_ball = np.linalg.norm(robot_pos - ball_pos)

ball_slow = ball_speed < 1.0
ball_close = dist_to_ball < RobotConstants.RADIUS * 1.3
ball_slow = ball_speed < 0.05
ball_close = (
dist_to_ball - (RobotConstants.RADIUS + BallConstants.RADIUS) < 0.03
)

return ball_slow and ball_close

Expand Down
2 changes: 1 addition & 1 deletion rj_gameplay/rj_gameplay/skill/line_kick.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,9 @@ def is_done(self, world_state: rc.WorldState):
KICK_DOT_THRESHOLD = 0.4
KICK_BALL_SPEED_THRESHOLD = 0.9

# TODO: make pivot kick and line kick inherit from some common kick superclass to make this cleaner
if self.robot is None:
return False

ball_vel_unit = world_state.ball.vel / np.linalg.norm(world_state.ball.vel)
heading_angle = world_state.our_robots[self.robot.id].pose[2]
heading_vect = np.array([np.cos(heading_angle), np.sin(heading_angle)])
Expand Down
27 changes: 14 additions & 13 deletions rj_gameplay/rj_gameplay/skill/move.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,21 +61,22 @@ def tick(self, world_state: rc.WorldState) -> RobotIntent:
return intent

def is_done(self, world_state: rc.WorldState) -> bool:
threshold = 0.3
position_tolerance = 1e-2
velocity_tolerance = 1e-1
if self.robot.id is None or world_state is None:
return False
elif (
math.sqrt(
(world_state.our_robots[self.robot.id].pose[0] - self.target_point[0])
** 2
+ (world_state.our_robots[self.robot.id].pose[1] - self.target_point[1])
** 2
)
< threshold
):
return True
else:
return False

# only linear for now (so this is technically not pose, which
# includes angular position)
robot_pt = world_state.our_robots[self.robot.id].pose[:2]
goal_pt = self.target_point
robot_vel = world_state.our_robots[self.robot.id].twist[:2]
goal_vel = self.target_vel

return (
np.linalg.norm(robot_pt - goal_pt) < position_tolerance
and np.linalg.norm(robot_vel - goal_vel) < velocity_tolerance
)

def __str__(self):
ignore_ball_str = ", ignoring ball" if self.ignore_ball else ""
Expand Down
3 changes: 1 addition & 2 deletions rj_gameplay/rj_gameplay/skill/pivot.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def __init__(
pivot_point: np.ndarray = None,
target_point: np.ndarray = None,
dribble_speed: float = 1.0,
threshold: float = 0.2,
threshold: float = 0.05,
priority: int = 1,
):
self.robot = robot
Expand Down Expand Up @@ -48,7 +48,6 @@ def is_done(self, world_state: rc.WorldState) -> bool:
if self.robot is None:
return False
angle_threshold = self.threshold
angle_threshold = 0.05
stopped_threshold = (
5 * self.threshold
) # We don't _really_ care about this when we're kicking, if not for latency
Expand Down
14 changes: 4 additions & 10 deletions rj_gameplay/rj_gameplay/skill/receive.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,14 @@
from abc import ABC, abstractmethod

import rj_gameplay.eval as eval
import argparse
import py_trees
import sys
import time
import numpy as np
from typing import Optional

import stp.skill as skill
import stp.role as role
from rj_gameplay.skill import settle, capture
from rj_msgs.msg import RobotIntent, SettleMotionCommand
import stp.rc as rc
import stp.skill as skill
from rj_msgs import msg
from rj_msgs.msg import RobotIntent

from rj_gameplay.skill import capture, settle


class Receive(skill.Skill):
Expand All @@ -38,7 +33,6 @@ def tick(self, world_state: rc.WorldState) -> RobotIntent:
return self.settle.tick(world_state)

def is_done(self, world_state: rc.WorldState) -> bool:

return self.capture.is_done(world_state)

def __str__(self):
Expand Down
34 changes: 29 additions & 5 deletions rj_geometry/include/rj_geometry/point.hpp
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
#pragma once

#include <rj_protos/Point.pb.h>
#include <cmath>
#include <sstream>
#include <string>

#include <Eigen/Dense>
#include <QtCore/QPointF>
#include <boost/functional/hash.hpp>
#include <cmath>

#include <rj_geometry/util.hpp>
#include <rj_geometry_msgs/msg/point.hpp>
#include <sstream>
#include <string>
#include <rj_protos/Point.pb.h>

namespace rj_geometry {

Expand Down Expand Up @@ -375,7 +377,29 @@ class Point {
return acos(std::max(std::min(angle, 1.0), -1.0));
}

[[nodiscard]] bool nearly_equals(Point other) const;
/*
* @brief Return true if this point is close enough to another Point.
*
* @param other Point to compare with this point
* @tolerance maximum allowed difference in x/y coord to be considered equal
* @return true if x/y of this Point are both less than tolerance away from other
*/
[[nodiscard]] bool nearly_equals(Point other, double tolerance = 1e-4) const {
return nearly_equal(static_cast<float>(x()), static_cast<float>(other.x()), tolerance) &&
nearly_equal(static_cast<float>(y()), static_cast<float>(other.y()), tolerance);
}

/*
* @brief Return true if points a and b are close enough to each other.
*
* @param a Point a
* @param b Point b
* @tolerance maximum allowed difference in x/y coord to be considered equal
* @return true if x/y of Point a are both less than tolerance away from Point b
*/
static bool nearly_equals(const Point& a, const Point& b, double tolerance = 1e-4) {
return a.nearly_equals(b, tolerance);
}

[[nodiscard]] std::string to_string() const {
std::stringstream str;
Expand Down
14 changes: 10 additions & 4 deletions rj_geometry/include/rj_geometry/util.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,14 @@ static inline T sign(T f) {
}

// TODO(1485): Make this smaller once we figure out why test are failing in O3.
// static const float FLOAT_EPSILON = 0.00001;
static const float kFloatEpsilon = 1e-4;
static bool nearly_equal(float a, float b) {
return std::fabs(a - b) < kFloatEpsilon;
/*
* @brief Return true if doubles a and b are close enough to each other.
*
* @param a double a
* @param b double b
* @tolerance maximum allowed difference in x/y coord to be considered equal
* @return true if x/y of double a are both less than tolerance away from double b
*/
static bool nearly_equal(double a, double b, double tolerance = 1e-4) {
return std::fabs(a - b) < tolerance;
}
6 changes: 2 additions & 4 deletions rj_geometry/src/point.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
#include <rj_geometry/util.hpp>

namespace rj_geometry {
bool Point::nearly_equals(Point other) const {
return nearly_equal(static_cast<float>(x()), static_cast<float>(other.x())) &&
nearly_equal(static_cast<float>(y()), static_cast<float>(other.y()));
}
// TODO(Kevin): delete this file
// did not do it yet because build system make me sad
} // namespace rj_geometry
19 changes: 11 additions & 8 deletions rj_geometry/testing/point_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -203,14 +203,17 @@ TEST(Point, normalized) {
}

TEST(Point, nearly_equals) {
EXPECT_TRUE(Point(0, 5).nearly_equals(Point(0, 5)));
EXPECT_TRUE(Point(0, 5).nearly_equals(Point(0 - kFloatEpsilon / 2, 5 + kFloatEpsilon / 2)));
EXPECT_TRUE(Point(0, 5).nearly_equals(Point(0 + kFloatEpsilon / 2, 5 - kFloatEpsilon / 2)));

EXPECT_FALSE(
Point(0, 5).nearly_equals(Point(0 - kFloatEpsilon * 1.1, 5 + kFloatEpsilon * 1.1)));
EXPECT_FALSE(
Point(0, 5).nearly_equals(Point(0 + kFloatEpsilon * 1.1, 5 - kFloatEpsilon * 1.1)));
double test_tolerance = 1e-4;
EXPECT_TRUE(Point(0, 5).nearly_equals(Point(0, 5), test_tolerance));
EXPECT_TRUE(Point(0, 5).nearly_equals(Point(0 - test_tolerance / 2, 5 + test_tolerance / 2),
test_tolerance));
EXPECT_TRUE(Point(0, 5).nearly_equals(Point(0 + test_tolerance / 2, 5 - test_tolerance / 2),
test_tolerance));

EXPECT_FALSE(Point(0, 5).nearly_equals(
Point(0 - test_tolerance * 1.1, 5 + test_tolerance * 1.1), test_tolerance));
EXPECT_FALSE(Point(0, 5).nearly_equals(
Point(0 + test_tolerance * 1.1, 5 - test_tolerance * 1.1), test_tolerance));
}
// TODO(ashaw596) Add tests for angle_to and angle_between once those changes are
// merged
16 changes: 16 additions & 0 deletions soccer/src/soccer/planning/instant.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,22 @@ struct LinearMotionInstant {
rj_geometry::Point position;
rj_geometry::Point velocity;

/**
* @brief Return true if LinearMotionInstant a and b are nearly equal.
*
* @param a LinearMotionInstant a
* @param b LinearMotionInstant b
* @param position_tolerance tolerance on position component of LinearMotionInstant
* @param velocity_tolerance tolerance on velocity component of LinearMotionInstant
* @return True if a and b are within position_tolerance of each other AND velocity_tolerance of
* each other
*/
static bool nearly_equals(const LinearMotionInstant& a, const LinearMotionInstant& b,
double position_tolerance = 1e-4, double velocity_tolerance = 1e-4) {
return rj_geometry::Point::nearly_equals(a.position, b.position, position_tolerance) &&
rj_geometry::Point::nearly_equals(a.velocity, b.velocity, velocity_tolerance);
}

friend std::ostream& operator<<(std::ostream& stream,
const LinearMotionInstant& instant) {
return stream << "LinearMotionInstant(position=" << instant.position
Expand Down
Loading