Skip to content

Commit 6e55cc0

Browse files
authored
Merge pull request #147 from Pitt-RAS/obst
Obst
2 parents 635b856 + a9c396f commit 6e55cc0

10 files changed

Lines changed: 339 additions & 141 deletions

action/QuadMove.action

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ float64 z_position
2222
# velocity task
2323
float64 x_velocity
2424
float64 y_velocity
25+
float64 velocity_duration
2526
---
2627
# result definition
2728
bool success

param/motion_command_coordinator.yaml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
transform_timeout : 0.2
55
# Minimum height for any XY translationanl maneuvers
66
# It used by tasks as well as the state monitor
7-
min_maneuver_height : 0.3
7+
min_maneuver_height : 0.1
88
# Recovery height for when landing is cancelled
99
recovery_height : 0.65
1010
# Recovery velocity for when landing is cancelled
@@ -27,3 +27,7 @@ linear_motion_profile_acceleration: 1.0
2727
linear_motion_profile_max_acceleration: 3.0
2828
# Length of time to generate a linear motion profile for
2929
linear_motion_profile_duration: 0.2
30+
31+
kickout_distance: 1.5
32+
new_task_distance: 1.8
33+
safe_distance: 2.1

param/obstacle_avoider.yaml

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
# The maximum perpendicular distance between a velocity vector and an obstacle before the obstacle is ignored
2-
obst_avoid_norm_limit: 1
2+
obst_avoid_norm_limit: 2.1
33
# How many steps to take when searching for a safe flight vector (effectivly determining the resolution)
44
# Steps are sampled over a range of [0, 2*pi]
55
obst_avoid_step_size: 90
66
# Obstacles outside this radius are ignored
7-
obst_avoid_radius: 1
7+
obst_avoid_radius: 4
8+
obst_idle_avoid_radius: 2.5
89
# Flight vector magnitude - only used by the idle avoider
9-
obst_avoid_magnitude: 0.2
10+
obst_avoid_magnitude: 0.5
1011
# Minimum avoid flight vector magnitude - only used by the task helper
11-
obst_avoid_min_magnitude: 0.1
12+
obst_avoid_min_magnitude: 0.4
1213

14+
obst_avoid_blending_speed: 0.7
15+
16+
obst_avoid_avoid_distance: 3.0
17+
obst_avoid_response_strength: 1.0
18+
obst_avoid_predict_time: 2.0

param/tasks.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ max_translation_speed: 1.0
66
# maximum z velocity
77
max_z_velocity: 2.0
88

9+
max_velocity_time_duration: 30.0
910

1011
# TAKEOFF TASK
1112
# Delay between arming and taking off
@@ -70,7 +71,7 @@ track_roomba_pid_settings:
7071
# HIT ROOMBA
7172
# max distance to roomba allowed for
7273
# descent to occur
73-
max_roomba_descent_dist: 0.2
74+
max_roomba_descent_dist: 3.0
7475
# descent velocity when hitting/blocking a roomba
7576
hit_descent_velocity: -1.5
7677
# ascent velocity after hitting a roomba

src/iarc7_motion/iarc_tasks/task_utilities/obstacle_avoid_helper.py

Lines changed: 83 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,18 @@
77

88
from math import sin, cos, atan2, pi
99

10-
from iarc7_safety.iarc_safety_exception import IARCSafetyException
10+
from iarc7_safety.iarc_safety_exception import IARCSafetyException, IARCFatalSafetyException
1111
from iarc7_msgs.msg import ObstacleArray
1212
from geometry_msgs.msg import PointStamped
1313

1414
class ObstacleAvoider(object):
1515
def __init__(self, tf_buffer):
1616
self._lock = threading.RLock()
1717

18+
self._avoid_distance = rospy.get_param('~obst_avoid_avoid_distance')
19+
self._predict_time = rospy.get_param('~obst_avoid_predict_time')
20+
self._response_strength = rospy.get_param('~obst_avoid_response_strength')
21+
1822
# Maximum allowed distance from an obstacle to its projection on the nearest flight vector
1923
self._unsafe_obstacle_threshold = rospy.get_param("~obst_avoid_norm_limit")
2024
# Obstacles outside this radius are ignored
@@ -23,6 +27,8 @@ def __init__(self, tf_buffer):
2327
self._vector_step_size = rospy.get_param("~obst_avoid_step_size") # Divide by 180 to get degrees/step
2428
# Minimum avoid vector
2529
self._minimum_magnitude = rospy.get_param("~obst_avoid_min_magnitude")
30+
# Blending speed
31+
self._blending_speed = rospy.get_param('~obst_avoid_blending_speed')
2632
# Transform timeout
2733
self._timeout = rospy.Duration(rospy.get_param("~transform_timeout"))
2834
self._obstacle_points = None
@@ -68,40 +74,82 @@ def _update_obstacles(self, obstacles):
6874
tf2_ros.ExtrapolationException) as ex:
6975
rospy.logwarn("ObstacleAvoider: Couldn't lookup transform from {} to level_quad".format(obstacles.header.frame_id))
7076

71-
def get_safe_vector(self, desired_vector):
77+
def get_safe_vector(self, desired_vector, curr_vel):
7278
# Find the norm and direction of the velocity in the horizontal plane
73-
original_vector_magnitude = np.linalg.norm(desired_vector[:2])
74-
original_vector_direction = atan2(desired_vector[1], desired_vector[0])
79+
#original_vector_magnitude = np.linalg.norm(desired_vector[:2])
80+
#original_vector_direction = atan2(desired_vector[1], desired_vector[0])
81+
curr_vel = np.array(curr_vel)
82+
desired_vector = np.array(desired_vector)
7583
with self._lock:
76-
for angle in np.linspace(0, pi, num=self._vector_step_size):
77-
for sign in [-1, 1]:
78-
vector_is_safe = True
79-
new_vector = np.array([cos(original_vector_direction+angle*sign), sin(original_vector_direction+angle*sign)])
80-
for obstacle in self._obstacle_points:
81-
# Project the obstacle vector onto the flight vector
82-
obstacle_proj_new_vector = (np.dot(new_vector, obstacle)/np.dot(new_vector, new_vector))*new_vector
83-
84-
# Don't look at obstacles projected onto the reflection of this vector
85-
if np.sign(obstacle_proj_new_vector[0]) == np.sign(new_vector[0]) and np.sign(obstacle_proj_new_vector[1]) == np.sign(new_vector[1]):
86-
# find distance of the obstacle to its projection on the flight vector
87-
obstacle_to_vector_distance = np.linalg.norm(obstacle - obstacle_proj_new_vector)
88-
# throw out vectors with obstacles that are too close
89-
if obstacle_to_vector_distance <= self._unsafe_obstacle_threshold:
90-
vector_is_safe = False
91-
break
92-
93-
# If this flight vector is safe, go for it
94-
if vector_is_safe:
95-
if angle != 0:
96-
rospy.logwarn("ObstacleAvoider: modified requested velocity by {} radians".format(angle))
97-
# We need to avoid, so make sure the vector magnitude is non-zero
98-
original_vector_magnitude = max(original_vector_magnitude, self._minimum_magnitude)
99-
100-
# Copy Z velocity from commanded vector
101-
# New flight vector has same magnitude as the commanded
102-
return np.array([original_vector_magnitude*cos(original_vector_direction+angle), original_vector_magnitude*sin(original_vector_direction+angle), desired_vector[2]])
103-
104-
rospy.logwarn("ObstacleAvoider: couldn't find safe velocity!")
105-
# Couldn't find a safe vector, just stop
106-
return np.array([0, 0, desired_vector[2]])
84+
for obstacle in self._obstacle_points:
85+
if np.linalg.norm(obstacle) == 0:
86+
unit_vector = np.array([1, 0, 0])
87+
else:
88+
unit_vector = obstacle / np.linalg.norm(obstacle)
89+
away_unit_vector = -unit_vector
90+
91+
turn_dir = np.array([0, 0, np.cross(obstacle, curr_vel)])
92+
if np.linalg.norm(turn_dir) < 1e-6:
93+
turn_dir = np.array([0, 0, 1])
94+
95+
side_vector = np.cross(turn_dir, obstacle)
96+
unit_side = side_vector / np.linalg.norm(side_vector)
97+
98+
rel_vel = np.dot(unit_vector, curr_vel)
99+
violation = max(0,
100+
self._avoid_distance
101+
- np.linalg.norm(obstacle)
102+
+ rel_vel * self._predict_time)
103+
104+
# Push away
105+
desired_vector[:2] += away_unit_vector \
106+
* self._response_strength * violation**2
107+
108+
# Push around
109+
desired_vector += unit_side * violation * 0.5
110+
return desired_vector
111+
#for angle in np.linspace(0, pi, num=self._vector_step_size):
112+
# for sign in [-1, 1]:
113+
# vector_is_safe = True
114+
# new_vector = np.array([cos(original_vector_direction+angle*sign), sin(original_vector_direction+angle*sign)])
115+
# for obstacle in self._obstacle_points:
116+
# # Project the obstacle vector onto the flight vector
117+
# obstacle_proj_new_vector = (np.dot(new_vector, obstacle)/np.dot(new_vector, new_vector))*new_vector
118+
119+
# # Don't look at obstacles projected onto the reflection of this vector
120+
# if np.sign(obstacle_proj_new_vector[0]) == np.sign(new_vector[0]) and np.sign(obstacle_proj_new_vector[1]) == np.sign(new_vector[1]):
121+
# # find distance of the obstacle to its projection on the flight vector
122+
# obstacle_to_vector_distance = np.linalg.norm(obstacle - obstacle_proj_new_vector)
123+
# # throw out vectors with obstacles that are too close
124+
# if obstacle_to_vector_distance <= self._unsafe_obstacle_threshold:
125+
# vector_is_safe = False
126+
# break
127+
128+
# # If this flight vector is safe, go for it
129+
# if vector_is_safe:
130+
# if angle != 0:
131+
# rospy.logwarn("ObstacleAvoider: modified requested velocity by {} radians".format(angle))
132+
# # We need to avoid, so make sure the vector magnitude is non-zero
133+
# response_magnitude = max(original_vector_magnitude, self._minimum_magnitude)
134+
135+
# # Copy Z velocity from commanded vector
136+
# # New flight vector has same magnitude as the commanded
137+
# barely_safe_vel = np.array([
138+
# response_magnitude
139+
# * cos(original_vector_direction+angle*sign),
140+
# response_magnitude
141+
# * sin(original_vector_direction+angle*sign),
142+
# desired_vector[2]])
143+
# reflected_safe_vel = np.array([
144+
# response_magnitude
145+
# * cos(original_vector_direction+2*angle*sign),
146+
# response_magnitude
147+
# * sin(original_vector_direction+2*angle*sign),
148+
# desired_vector[2]])
149+
# blend_constant = np.exp(-original_vector_magnitude / self._blending_speed)
150+
# return barely_safe_vel * blend_constant + reflected_safe_vel * (1-blend_constant)
151+
152+
#rospy.logwarn("ObstacleAvoider: couldn't find safe velocity!")
153+
## Couldn't find a safe vector, just stop
154+
#return np.array([0, 0, desired_vector[2]])
107155

src/iarc7_motion/iarc_tasks/task_utilities/task_topic_buffer.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,20 @@ def __init__(self):
4848
'roomba_tracking_status', Odometry,
4949
queue_size=10)
5050

51+
def wait_until_ready(self, timeout):
52+
start_time = rospy.Time.now()
53+
rate = rospy.Rate(30)
54+
while not rospy.is_shutdown() and rospy.Time.now() - start_time < timeout:
55+
if (self.has_landing_message()
56+
and self.has_odometry_message()
57+
and self.has_roomba_message()):
58+
return
59+
rate.sleep()
60+
rospy.logerr('Have landing: %s', self.has_landing_message())
61+
rospy.logerr('Have odom: %s', self.has_odometry_message())
62+
rospy.logerr('Have roomba: %s', self.has_roomba_message())
63+
raise IARCFatalSafetyException('TaskTopicBuffer not ready')
64+
5165
def _receive_roomba_status(self, data):
5266
self._roomba_array = data
5367

src/iarc7_motion/iarc_tasks/velocity_task.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ def __init__(self, task_request):
4444
self._TRANSFORM_TIMEOUT = rospy.get_param('~transform_timeout')
4545
self._MAX_TRANSLATION_SPEED = rospy.get_param('~max_translation_speed')
4646
self._MAX_Z_VELOCITY = rospy.get_param('~max_z_velocity')
47+
self._MAX_TIME_DURATION = rospy.Duration(rospy.get_param('~max_velocity_time_duration'))
4748

4849
except KeyError as e:
4950
rospy.logerr('Could not lookup a parameter for velocity task')
@@ -58,6 +59,12 @@ def __init__(self, task_request):
5859
else:
5960
self._z_holder = HeightHolder()
6061

62+
if task_request.velocity_duration <= 0.0:
63+
self._time_duration = self._MAX_TIME_DURATION
64+
else:
65+
self._time_duration = rospy.Duration(task_request.velocity_duration)
66+
67+
6168
self._height_checker = HeightSettingsChecker()
6269
self._limiter = AccelerationLimiter()
6370

@@ -76,15 +83,20 @@ def get_desired_command(self):
7683
self._state = VelocityTaskState.waiting
7784
else:
7885
self._state = VelocityTaskState.moving
86+
self._start_time = rospy.Time.now()
7987

8088
if (self._state == VelocityTaskState.waiting):
8189
if not self.topic_buffer.has_odometry_message():
8290
return (TaskRunning(), NopCommand())
8391
else:
8492
self._state = VelocityTaskState.moving
93+
self._start_time = rospy.Time.now()
8594

8695
if self._state == VelocityTaskState.moving:
8796

97+
if rospy.Time.now() - self._start_time > self._time_duration:
98+
return (TaskDone(), )
99+
88100
predicted_motion_point = self._linear_motion_profile_generator.expected_point_at_time(
89101
rospy.Time.now())
90102

@@ -117,8 +129,11 @@ def get_desired_command(self):
117129
if self._current_velocity is None:
118130
self._current_velocity = [drone_vel_x, drone_vel_y, drone_vel_z]
119131

120-
desired_vel = self.topic_buffer.get_obstacle_avoider().get_safe_vector(desired_vel)
121-
desired_vel = self._limiter.limit_acceleration(self._current_velocity, desired_vel)
132+
obst_avoider = self.topic_buffer.get_obstacle_avoider()
133+
desired_vel = obst_avoider.get_safe_vector(
134+
desired_vel, self._current_velocity[:2])
135+
desired_vel = self._limiter.limit_acceleration(
136+
self._current_velocity, desired_vel)
122137

123138
velocity = TwistStamped()
124139
velocity.header.frame_id = 'level_quad'

0 commit comments

Comments
 (0)