예제 #1
0
    def compute_neighbors(self):
        """
        Computes the neighbors of this agent.
        """
        rangeSq = rvo_math.square(self.time_horizon_obst_ * self.max_speed_ +
                                  self.radius_)
        self.obstacle_neighbors_ = []
        self.simulator_.kd_tree_.compute_obstacle_neighbors(self, rangeSq)

        self.agent_neighbors_ = []
        if self.max_neighbors_ > 0:
            rangeSq = rvo_math.square(self.neighbor_dist_)
            self.simulator_.kd_tree_.compute_agent_neighbors(self, rangeSq)
예제 #2
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_)
예제 #3
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
예제 #4
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_)
예제 #5
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
예제 #6
0
    def query_agent_tree_recursive(self, agent, rangeSq, node):
        """
        Recursive method for computing the agent neighbors of the specified agent.

        Args:
            agent (Agent): The agent for which agent neighbors are to be computed.
            rangeSq (float): The squared range around the agent.
            node (int): The current agent k-D tree node index.
        """
        if self.agentTree_[node].end_ - self.agentTree_[
                node].begin_ <= MAX_LEAF_SIZE:
            for i in range(self.agentTree_[node].begin_,
                           self.agentTree_[node].end_):
                rangeSq = agent.insert_agent_neighbor(self.agents_[i], rangeSq)
        else:
            distSqLeft = rvo_math.square(
                max(
                    0.0, self.agentTree_[self.agentTree_[node].left_].minX_ -
                    agent.position_.x_)) + rvo_math.square(
                        max(
                            0.0, agent.position_.x_ -
                            self.agentTree_[self.agentTree_[node].left_].maxX_)
                    ) + rvo_math.square(
                        max(
                            0.0,
                            self.agentTree_[self.agentTree_[node].left_].minY_
                            - agent.position_.y_)) + rvo_math.square(
                                max(
                                    0.0, agent.position_.y_ - self.agentTree_[
                                        self.agentTree_[node].left_].maxY_))
            distSqRight = rvo_math.square(
                max(
                    0.0, self.agentTree_[self.agentTree_[node].right_].minX_ -
                    agent.position_.x_)
            ) + rvo_math.square(
                max(
                    0.0, agent.position_.x_ -
                    self.agentTree_[self.agentTree_[node].right_].maxX_)
            ) + rvo_math.square(
                max(
                    0.0, self.agentTree_[self.agentTree_[node].right_].minY_ -
                    agent.position_.y_)) + rvo_math.square(
                        max(
                            0.0, agent.position_.y_ - self.agentTree_[
                                self.agentTree_[node].right_].maxY_))

            if distSqLeft < distSqRight:
                if distSqLeft < rangeSq:
                    rangeSq = self.query_agent_tree_recursive(
                        agent, rangeSq, self.agentTree_[node].left_)

                    if distSqRight < rangeSq:
                        rangeSq = self.query_agent_tree_recursive(
                            agent, rangeSq, self.agentTree_[node].right_)
            else:
                if distSqRight < rangeSq:
                    rangeSq = self.query_agent_tree_recursive(
                        agent, rangeSq, self.agentTree_[node].right_)

                    if distSqLeft < rangeSq:
                        rangeSq = self.query_agent_tree_recursive(
                            agent, rangeSq, self.agentTree_[node].left_)
        return rangeSq