Example #1
0
 def reached_goal(self):
     # Check if all agents have reached their goals.
     for i in range(self.simulator_.num_agents):
         if rvo_math.abs_sq(self.simulator_.agents_[i].position_ -
                            self.goals_[i]) > 400.0:
             return False
     return True
Example #2
0
    def insert_agent_neighbor(self, agent, rangeSq):
        """
        Inserts an agent neighbor into the set of neighbors of this agent.
         
        Args:
            agent (Agent): A pointer to the agent to be inserted.
            rangeSq (float): The squared range around this agent.
        """
        if self != agent:
            distSq = rvo_math.abs_sq(self.position_ - agent.position_)

            if distSq < rangeSq:
                if len(self.agent_neighbors_) < self.max_neighbors_:
                    self.agent_neighbors_.append((distSq, agent))

                i = len(self.agent_neighbors_) - 1
                while i != 0 and distSq < self.agent_neighbors_[i - 1][0]:
                    self.agent_neighbors_[i] = self.agent_neighbors_[i - 1]
                    i -= 1

                self.agent_neighbors_[i] = (distSq, agent)

                if len(self.agent_neighbors_) == self.max_neighbors_:
                    rangeSq = self.agent_neighbors_[len(self.agent_neighbors_)
                                                    - 1][0]
        return rangeSq
Example #3
0
 def dist_to_goals(self, pos, goals):
     dists = []
     for i in range(len(goals)):
         goal = goals[i]
         dist = math.sqrt(rvo_math.abs_sq(goal-pos))
         dists.append(dist)
     return dists
Example #4
0
    def query_obstacle_tree_recursive(self, agent, rangeSq, node):
        """
        Recursive method for computing the obstacle neighbors of the specified agent.

        Args:
            agent (Agent): The agent for which obstacle neighbors are to be computed.
            rangeSq (float): The squared range around the agent.
            node (ObstacleTreeNode): The current obstacle k-D node.
        """
        if node is not None:
            obstacle1 = node.obstacle_
            obstacle2 = obstacle1.next_

            agentLeftOfLine = rvo_math.left_of(obstacle1.point_,
                                               obstacle2.point_,
                                               agent.position_)

            self.query_obstacle_tree_recursive(
                agent, rangeSq,
                node.left_ if agentLeftOfLine >= 0.0 else node.right_)

            distSqLine = rvo_math.square(agentLeftOfLine) / rvo_math.abs_sq(
                obstacle2.point_ - obstacle1.point_)

            if distSqLine < rangeSq:
                if agentLeftOfLine < 0.0:
                    # Try obstacle at this node only if agent is on right side of obstacle (and can see obstacle).
                    agent.insert_obstacle_neighbor(node.obstacle_, rangeSq)

                # Try other side of line.
                self.query_obstacle_tree_recursive(
                    agent, rangeSq,
                    node.right_ if agentLeftOfLine >= 0.0 else node.left_)
Example #5
0
    def reached_goal(self):
        """
        Check if all agents have reached their goals.
        """
        for i in range(self.simulator_.num_agents):
            if rvo_math.abs_sq(self.simulator_.agents_[i].position_ - self.goals_[i]) > self.simulator_.agents_[i].radius_ * self.simulator_.agents_[i].radius_:
                return False

        return True
Example #6
0
    def __abs__(self):
        """
        Computes the length of a specified two-dimensional vector.

        Args:
            vector (Vector2): The two-dimensional vector whose length is to be computed.

        Returns:
            float: The length of the two-dimensional vector.
        """
        return math.sqrt(rvo_math.abs_sq(self))
Example #7
0
    def set_preferred_velocities(self):
        """
        Set the preferred velocity to be a vector of unit magnitude (speed) in the direction of the goal.
        """
        for i in range(self.simulator_.num_agents):
            goal_vector = self.goals_[i] - self.simulator_.agents_[i].position_

            if rvo_math.abs_sq(goal_vector) > 1.0:
                goal_vector = rvo_math.normalize(goal_vector)

            self.simulator_.set_agent_pref_velocity(i, goal_vector)
Example #8
0
    def set_preferred_velocities(self):
        # Set the preferred velocity to be a vector of unit magnitude (speed) in the direction of the goal.
        for i in range(self.simulator_.num_agents):
            goal_vector = self.goals_[i] - self.simulator_.agents_[i].position_

            if rvo_math.abs_sq(goal_vector) > 1.0:
                goal_vector = rvo_math.normalize(goal_vector)

            self.simulator_.set_agent_pref_velocity(i, goal_vector)

            # Perturb a little to avoid deadlocks due to perfect symmetry.
            angle = random.random() * 2.0 * math.pi
            dist = random.random() * 0.0001

            self.simulator_.set_agent_pref_velocity(
                i, self.simulator_.agents_[i].pref_velocity_ +
                dist * Vector2(math.cos(angle), math.sin(angle)))
Example #9
0
    def linear_program2(self, lines, radius, optVelocity, directionOpt,
                        result):
        """
        Solves a two-dimensional linear program subject to linear constraints defined by lines and a circular constraint.

        Args:
            lines (list): Lines defining the linear constraints.
            radius (float): The radius of the circular constraint.
            optVelocity (Vector2): The optimization velocity.
            directionOpt (bool): True if the direction should be optimized.
            result (Vector2): A reference to the result of the linear program.

        Returns:
            int: The number of the line it fails on, and the number of lines if successful.
            Vector2: A reference to the result of the linear program.
        """
        if directionOpt:
            # Optimize direction. Note that the optimization velocity is of unit length in this case.
            result = optVelocity * radius
        elif rvo_math.abs_sq(optVelocity) > rvo_math.square(radius):
            # Optimize closest point and outside circle.
            result = rvo_math.normalize(optVelocity) * radius
        else:
            # Optimize closest point and inside circle.
            result = optVelocity

        for i in range(len(lines)):
            if rvo_math.det(lines[i].direction, lines[i].point - result) > 0.0:
                # Result does not satisfy constraint i. Compute new optimal result.
                tempResult = result
                success, result = self.linear_program1(lines, i, radius,
                                                       optVelocity,
                                                       directionOpt)
                if not success:
                    result = tempResult
                    return i, result

        return len(lines), result
Example #10
0
    def compute_new_velocity(self):
        """
        Computes the new velocity of this agent.
        """
        self.orca_lines_ = []

        invTimeHorizonObst = 1.0 / self.time_horizon_obst_

        # Create obstacle ORCA lines.
        for i in range(len(self.obstacle_neighbors_)):
            obstacle1 = self.obstacle_neighbors_[i][1]
            obstacle2 = obstacle1.next_

            relativePosition1 = obstacle1.point_ - self.position_
            relativePosition2 = obstacle2.point_ - self.position_

            # Check if velocity obstacle of obstacle is already taken care of by previously constructed obstacle ORCA lines.
            alreadyCovered = False

            for j in range(len(self.orca_lines_)):
                det1 = rvo_math.det(
                    invTimeHorizonObst * relativePosition1 -
                    self.orca_lines_[j].point, self.orca_lines_[j].direction)
                det2 = rvo_math.det(
                    invTimeHorizonObst * relativePosition2 -
                    self.orca_lines_[j].point, self.orca_lines_[j].direction)
                if (det1 - invTimeHorizonObst * self.radius_ >=
                        -rvo_math.EPSILON) and (
                            det2 - invTimeHorizonObst * self.radius_ >=
                            -rvo_math.EPSILON):
                    alreadyCovered = True
                    break

            if alreadyCovered:
                continue

            # Not yet covered. Check for collisions.
            distSq1 = rvo_math.abs_sq(relativePosition1)
            distSq2 = rvo_math.abs_sq(relativePosition2)

            radiusSq = rvo_math.square(self.radius_)

            obstacleVector = obstacle2.point_ - obstacle1.point_
            s = (-relativePosition1
                 @ obstacleVector) / rvo_math.abs_sq(obstacleVector)
            distSqLine = rvo_math.abs_sq(-relativePosition1 -
                                         s * obstacleVector)

            line = Line()

            if s < 0.0 and distSq1 <= radiusSq:
                # Collision with left vertex. Ignore if non-convex.
                if obstacle1.convex_:
                    line.point = Vector2(0.0, 0.0)
                    line.direction = rvo_math.normalize(
                        Vector2(-relativePosition1.y, relativePosition1.x))
                    self.orca_lines_.append(line)
                continue
            elif s > 1.0 and distSq2 <= radiusSq:
                # Collision with right vertex. Ignore if non-convex or if it will be taken care of by neighboring obstacle.
                if obstacle2.convex_ and rvo_math.det(
                        relativePosition2, obstacle2.direction_) >= 0.0:
                    line.point = Vector2(0.0, 0.0)
                    line.direction = rvo_math.normalize(
                        Vector2(-relativePosition2.y, relativePosition2.x))
                    self.orca_lines_.append(line)
                continue
            elif s >= 0.0 and s < 1.0 and distSqLine <= radiusSq:
                # Collision with obstacle segment.
                line.point = Vector2(0.0, 0.0)
                line.direction = -obstacle1.direction_
                self.orca_lines_.append(line)
                continue

            # No collision. Compute legs. When obliquely viewed, both legs can come from a single vertex. Legs extend cut-off line when non-convex vertex.
            leftLegDirection = None
            rightLegDirection = None

            if s < 0.0 and distSqLine <= radiusSq:
                # Obstacle viewed obliquely so that left vertex defines velocity obstacle.
                if not obstacle1.convex_:
                    # Ignore obstacle.
                    continue

                obstacle2 = obstacle1

                leg1 = math.sqrt(distSq1 - radiusSq)
                leftLegDirection = Vector2(
                    relativePosition1.x * leg1 - relativePosition1.y *
                    self.radius_, relativePosition1.x * self.radius_ +
                    relativePosition1.y * leg1) / distSq1
                rightLegDirection = Vector2(
                    relativePosition1.x * leg1 + relativePosition1.y *
                    self.radius_, -relativePosition1.x * self.radius_ +
                    relativePosition1.y * leg1) / distSq1
            elif s > 1.0 and distSqLine <= radiusSq:
                # Obstacle viewed obliquely so that right vertex defines velocity obstacle.
                if not obstacle2.convex_:
                    # Ignore obstacle.
                    continue

                obstacle1 = obstacle2

                leg2 = math.sqrt(distSq2 - radiusSq)
                leftLegDirection = Vector2(
                    relativePosition2.x * leg2 - relativePosition2.y *
                    self.radius_, relativePosition2.x * self.radius_ +
                    relativePosition2.y * leg2) / distSq2
                rightLegDirection = Vector2(
                    relativePosition2.x * leg2 + relativePosition2.y *
                    self.radius_, -relativePosition2.x * self.radius_ +
                    relativePosition2.y * leg2) / distSq2
            else:
                # Usual situation.
                if obstacle1.convex_:
                    leg1 = math.sqrt(distSq1 - radiusSq)
                    leftLegDirection = Vector2(
                        relativePosition1.x * leg1 - relativePosition1.y *
                        self.radius_, relativePosition1.x * self.radius_ +
                        relativePosition1.y * leg1) / distSq1
                else:
                    # Left vertex non-convex left leg extends cut-off line.
                    leftLegDirection = -obstacle1.direction_

                if obstacle2.convex_:
                    leg2 = math.sqrt(distSq2 - radiusSq)
                    rightLegDirection = Vector2(
                        relativePosition2.x * leg2 + relativePosition2.y *
                        self.radius_, -relativePosition2.x * self.radius_ +
                        relativePosition2.y * leg2) / distSq2
                else:
                    # Right vertex non-convex right leg extends cut-off line.
                    rightLegDirection = obstacle1.direction_

            # Legs can never point into neighboring edge when convex vertex, take cutoff-line of neighboring edge instead. If velocity projected on "foreign" leg, no constraint is added.

            leftNeighbor = obstacle1.previous_

            isLeftLegForeign = False
            isRightLegForeign = False

            if obstacle1.convex_ and rvo_math.det(
                    leftLegDirection, -leftNeighbor.direction_) >= 0.0:
                # Left leg points into obstacle.
                leftLegDirection = -leftNeighbor.direction_
                isLeftLegForeign = True

            if obstacle2.convex_ and rvo_math.det(rightLegDirection,
                                                  obstacle2.direction_) <= 0.0:
                # Right leg points into obstacle.
                rightLegDirection = obstacle2.direction_
                isRightLegForeign = True

            # Compute cut-off centers.
            leftCutOff = invTimeHorizonObst * (obstacle1.point_ -
                                               self.position_)
            rightCutOff = invTimeHorizonObst * (obstacle2.point_ -
                                                self.position_)
            cutOffVector = rightCutOff - leftCutOff

            # Project current velocity on velocity obstacle.

            # Check if current velocity is projected on cutoff circles.
            t = 0.5 if obstacle1 == obstacle2 else (
                (self.velocity_ -
                 leftCutOff) @ cutOffVector) / rvo_math.abs_sq(cutOffVector)
            tLeft = (self.velocity_ - leftCutOff) @ leftLegDirection
            tRight = (self.velocity_ - rightCutOff) @ rightLegDirection

            if (t < 0.0 and tLeft < 0.0) or (obstacle1 == obstacle2
                                             and tLeft < 0.0 and tRight < 0.0):
                # Project on left cut-off circle.
                unitW = rvo_math.normalize(self.velocity_ - leftCutOff)
                line.direction = Vector2(unitW.y, -unitW.x)
                line.point = leftCutOff + self.radius_ * invTimeHorizonObst * unitW
                self.orca_lines_.append(line)
                continue

            elif t > 1.0 and tRight < 0.0:
                # Project on right cut-off circle.
                unitW = rvo_math.normalize(self.velocity_ - rightCutOff)
                line.direction = Vector2(unitW.y, -unitW.x)
                line.point = rightCutOff + self.radius_ * invTimeHorizonObst * unitW
                self.orca_lines_.append(line)
                continue

            # Project on left leg, right leg, or cut-off line, whichever is closest to velocity.
            distSqCutoff = math.inf if t < 0.0 or t > 1.0 or obstacle1 == obstacle2 else rvo_math.abs_sq(
                self.velocity_ - (leftCutOff + t * cutOffVector))
            distSqLeft = math.inf if tLeft < 0.0 else rvo_math.abs_sq(
                self.velocity_ - (leftCutOff + tLeft * leftLegDirection))
            distSqRight = math.inf if tRight < 0.0 else rvo_math.abs_sq(
                self.velocity_ - (rightCutOff + tRight * rightLegDirection))

            if distSqCutoff <= distSqLeft and distSqCutoff <= distSqRight:
                # Project on cut-off line.
                line.direction = -obstacle1.direction_
                line.point = leftCutOff + self.radius_ * invTimeHorizonObst * Vector2(
                    -line.direction.y, line.direction.x)
                self.orca_lines_.append(line)
                continue

            if distSqLeft <= distSqRight:
                # Project on left leg.
                if isLeftLegForeign:
                    continue

                line.direction = leftLegDirection
                line.point = leftCutOff + self.radius_ * invTimeHorizonObst * Vector2(
                    -line.direction.y, line.direction.x)
                self.orca_lines_.append(line)

                continue

            # Project on right leg.
            if isRightLegForeign:
                continue

            line.direction = -rightLegDirection
            line.point = rightCutOff + self.radius_ * invTimeHorizonObst * Vector2(
                -line.direction.y, line.direction.x)
            self.orca_lines_.append(line)

        numObstLines = len(self.orca_lines_)

        invTimeHorizon = 1.0 / self.time_horizon_

        # Create agent ORCA lines.
        for i in range(len(self.agent_neighbors_)):
            other = self.agent_neighbors_[i][1]

            relativePosition = other.position_ - self.position_
            relativeVelocity = self.velocity_ - other.velocity_

            distSq = rvo_math.abs_sq(relativePosition)
            combinedRadius = self.radius_ + other.radius_
            combinedRadiusSq = rvo_math.square(combinedRadius)

            line = Line()
            u = Vector2()

            if distSq > combinedRadiusSq:
                # No collision.
                w = relativeVelocity - invTimeHorizon * relativePosition

                # Vector from cutoff center to relative velocity.
                wLengthSq = rvo_math.abs_sq(w)
                dotProduct1 = w @ relativePosition

                if dotProduct1 < 0.0 and rvo_math.square(
                        dotProduct1) > combinedRadiusSq * wLengthSq:
                    # Project on cut-off circle.
                    wLength = math.sqrt(wLengthSq)
                    unitW = w / wLength

                    line.direction = Vector2(unitW.y, -unitW.x)
                    u = (combinedRadius * invTimeHorizon - wLength) * unitW
                else:
                    # Project on legs.
                    leg = math.sqrt(distSq - combinedRadiusSq)

                    if rvo_math.det(relativePosition, w) > 0.0:
                        # Project on left leg.
                        line.direction = Vector2(
                            relativePosition.x * leg -
                            relativePosition.y * combinedRadius,
                            relativePosition.x * combinedRadius +
                            relativePosition.y * leg) / distSq
                    else:
                        # Project on right leg.
                        line.direction = -Vector2(
                            relativePosition.x * leg + relativePosition.y *
                            combinedRadius, -relativePosition.x *
                            combinedRadius + relativePosition.y * leg) / distSq

                    dotProduct2 = relativeVelocity @ line.direction
                    u = dotProduct2 * line.direction - relativeVelocity
            else:
                # Collision. Project on cut-off circle of time timeStep.
                invTimeStep = 1.0 / self.simulator_.time_step_

                # Vector from cutoff center to relative velocity.
                w = relativeVelocity - invTimeStep * relativePosition

                wLength = abs(w)
                unitW = w / wLength

                line.direction = Vector2(unitW.y, -unitW.x)
                u = (combinedRadius * invTimeStep - wLength) * unitW

            line.point = self.velocity_ + 0.5 * u
            self.orca_lines_.append(line)

        lineFail, self.new_velocity_ = self.linear_program2(
            self.orca_lines_, self.max_speed_, self.pref_velocity_, False,
            self.new_velocity_)

        if lineFail < len(self.orca_lines_):
            self.new_velocity_ = self.linear_program3(self.orca_lines_,
                                                      numObstLines, lineFail,
                                                      self.max_speed_,
                                                      self.new_velocity_)
Example #11
0
    def linear_program1(self, lines, lineNo, radius, optVelocity,
                        directionOpt):
        """
        Solves a one-dimensional linear program on a specified line subject to linear constraints defined by lines and a circular constraint.

        Args:
            lines (list): Lines defining the linear constraints.
            lineNo (int): The specified line constraint.
            radius (float): The radius of the circular constraint.
            optVelocity (Vector2): The optimization velocity.
            directionOpt (bool): True if the direction should be optimized.

        Returns:
            bool: True if successful.
            Vector2: A reference to the result of the linear program.
        """
        dotProduct = lines[lineNo].point @ lines[lineNo].direction
        discriminant = rvo_math.square(dotProduct) + rvo_math.square(
            radius) - rvo_math.abs_sq(lines[lineNo].point)

        if discriminant < 0.0:
            # Max speed circle fully invalidates line lineNo.
            return False, None

        sqrtDiscriminant = math.sqrt(discriminant)
        tLeft = -dotProduct - sqrtDiscriminant
        tRight = -dotProduct + sqrtDiscriminant

        for i in range(lineNo):
            denominator = rvo_math.det(lines[lineNo].direction,
                                       lines[i].direction)
            numerator = rvo_math.det(lines[i].direction,
                                     lines[lineNo].point - lines[i].point)

            if abs(denominator) <= rvo_math.EPSILON:
                # Lines lineNo and i are (almost) parallel.
                if numerator < 0.0:
                    return False, None
                continue

            t = numerator / denominator

            if denominator >= 0.0:
                # Line i bounds line lineNo on the right.
                tRight = min(tRight, t)
            else:
                # Line i bounds line lineNo on the left.
                tLeft = max(tLeft, t)

            if tLeft > tRight:
                return False, None

        if directionOpt:
            # Optimize direction.
            if optVelocity @ lines[lineNo].direction > 0.0:
                # Take right extreme.
                result = lines[lineNo].point + tRight * lines[lineNo].direction
            else:
                # Take left extreme.
                result = lines[lineNo].point + tLeft * lines[lineNo].direction
        else:
            # Optimize closest point.
            t = lines[lineNo].direction @ (optVelocity - lines[lineNo].point)

            if t < tLeft:
                result = lines[lineNo].point + tLeft * lines[lineNo].direction
            elif t > tRight:
                result = lines[lineNo].point + tRight * lines[lineNo].direction
            else:
                result = lines[lineNo].point + t * lines[lineNo].direction

        return True, result