示例#1
0
    def move(self, position_constraint_string, frame):
        p = PositionConstraint()
        p.constraint = position_constraint_string
        p.frame = frame
        plan = self.global_planner.getPlan(p)
        if plan:
            o = OrientationConstraint()
            o.frame = frame
            self.local_planner.setPlan(plan, p, o)

        return plan
示例#2
0
    def turn_towards(self, x, y, frame, offset=0.0):
        current_pose = self.get_location()
        p = PositionConstraint()
        p.constraint = "(x-%f)^2+(y-%f)^2 < %f^2" % (
            current_pose.frame.p.x(), current_pose.frame.p.y(), 0.1)
        p.frame = current_pose.frame_id
        plan = self.global_planner.getPlan(p)
        o = OrientationConstraint(look_at=geometry_msgs.msg.Point(x, y, 0.0),
                                  angle_offset=offset)
        o.frame = frame
        self.local_planner.setPlan(plan, p, o)

        return plan
def waypoint_constraint(waypoint_designator, radius, look=True):
    """
    Navigates to a radius from a waypoint

    :param waypoint_designator: designator resolving to the waypoint
    :param radius: allowed distance to the waypoint
    :param look: whether or not to take the orientation of the waypoint (default True)
    :return: navigation constraints, if the entity does not resolve, None is returned.
    :rtype: tuple(PositionConstraint, OrientationConstraint)
    """
    e = waypoint_designator.resolve()

    if not e:
        rospy.logerr(
            "waypoint_constraint function: No entity could be resolved from designator '%s'" % waypoint_designator)
        return None

    rospy.logdebug("Navigating to waypoint '{}'".format(e.id))

    try:
        x = e.pose.frame.p.x()
        y = e.pose.frame.p.y()
        rz, _, _ = e.pose.frame.M.GetEulerZYX()
    except Exception as e:
        rospy.logerr(e)
        return None

    pc = PositionConstraint(constraint="(x-%f)^2+(y-%f)^2 < %f^2" % (x, y, radius), frame="/map")
    oc = None
    if look:
        oc = OrientationConstraint(look_at=Point(x + 10, y, 0.0), angle_offset=rz, frame="/map")
    return pc, oc
示例#4
0
    def generateConstraint(self):
        arm = self.arm.resolve()

        if arm == self.robot.arms['left']:
            angle_offset = math.atan2(-self.robot.grasp_offset.y,
                                      self.robot.grasp_offset.x)
        elif arm == self.robot.arms['right']:
            angle_offset = math.atan2(self.robot.grasp_offset.y,
                                      self.robot.grasp_offset.x)
        else:
            raise IndexError('Arm not found')
        radius = math.hypot(self.robot.grasp_offset.x,
                            self.robot.grasp_offset.y)

        x = self.point.point.x
        y = self.point.point.y
        frame_id = self.point.header.frame_id

        # Outer radius
        radius -= 0.1
        ro = "(x-%f)^2+(y-%f)^2 < %f^2" % (x, y, radius + 0.075)
        ri = "(x-%f)^2+(y-%f)^2 > %f^2" % (x, y, radius - 0.075)
        pc = PositionConstraint(constraint=ri + " and " + ro, frame=frame_id)
        oc = OrientationConstraint(look_at=Point(x, y, 0.0),
                                   frame=frame_id,
                                   angle_offset=angle_offset)

        return pc, oc
    def execute(self, userdata=None):
        door_1_position = self._robot.ed.get_entity(
            id=challenge_knowledge.reentry_door_1).pose.position
        door_2_position = self._robot.ed.get_entity(
            id=challenge_knowledge.reentry_door_2).pose.position

        door_1_constraint = PositionConstraint()
        door_1_constraint.constraint = "(x-%f)^2 + (y-%f)^2 < %f^2" % (
            door_1_position.x, door_1_position.y, 0.7)

        door_2_constraint = PositionConstraint()
        door_2_constraint.constraint = "(x-%f)^2 + (y-%f)^2 < %f^2" % (
            door_2_position.x, door_2_position.y, 0.7)

        # TODO: make sure that waypoints exist!!!
        # TODO: make sure door id designator is writeable

        while True:
            plan1 = self._robot.base.global_planner.getPlan(door_1_constraint)
            plan2 = self._robot.base.global_planner.getPlan(door_2_constraint)
            print "Plan 1 length: %i" % len(plan1)
            print "Plan 2 length: %i" % len(plan2)
            if len(plan1) < 3:
                self._door_id_designator.writeable.write(
                    challenge_knowledge.target_door_1)
                return "door_found"
            elif len(plan2) < 3:
                self._door_id_designator.writeable.write(
                    challenge_knowledge.target_door_2)
                return "door_found"

            if self.preempt_requested():
                return 'preempted'

            time.sleep(1)
示例#6
0
    def get_position_constraint(self, entity_id_area_name_map):
        try:
            res = self._get_constraint_srv(entity_ids=[k for k in entity_id_area_name_map],
                                           area_names=[v for k, v in entity_id_area_name_map.iteritems()])
        except Exception as e:
            rospy.logerr("Can't get position constraint: {}".format(e))
            return None

        if res.error_msg != '':
            rospy.logerr(res.error_msg)
            return None

        return PositionConstraint(constraint=res.position_constraint_map_frame, frame="/map")
    def __init__(self, robot, entity_id, obstacle_radius):
        smach.State.__init__(self, outcomes=["done", "timeout"])
        self._robot = robot

        try:
            pose = robot.ed.get_entity(id=entity_id).pose
        except Exception as e:
            rospy.logerr(e)
            sys.exit(1)

        self.pc = PositionConstraint(frame="/map",
                                     constraint="(x-%f)^2+(y-%f)^2 < 0.05" %
                                     (pose.position.x, pose.position.y))
        self.obstacle_radius = obstacle_radius
示例#8
0
    def _turn_towards_operator(self):
        f = self._robot.base.get_location().frame
        robot_position = f.p
        operator_position = self._last_operator._pose.p

        p = PositionConstraint()
        p.constraint = "(x-%f)^2 + (y-%f)^2 < %f^2" % (operator_position.x(),
                                                       operator_position.y(),
                                                       self._operator_radius)

        o = OrientationConstraint()
        if self._operator_id:
            o.frame = self._operator_id
        else:
            o.frame = 'map'
            o.look_at = kdl_conversions.kdl_vector_to_point_msg(
                self._last_operator.pose.frame.p)

        dx = operator_position.x() - robot_position.x()
        dy = operator_position.y() - robot_position.y()

        yaw = math.atan2(dy, dx)
        # ToDo: make nice!
        pose = geometry_msgs.msg.PoseStamped()
        pose.header.frame_id = "/map"
        pose.header.stamp = rospy.Time.now()
        pose.pose.position.x = robot_position.x()
        pose.pose.position.y = robot_position.y()
        xx, yy, zz, ww = kdl.Rotation.RPY(0, 0, yaw).GetQuaternion()
        pose.pose.orientation.x = xx
        pose.pose.orientation.y = yy
        pose.pose.orientation.z = zz
        pose.pose.orientation.w = ww
        plan = [pose]
        rospy.loginfo("Operator within self._lookat_radius")

        self._robot.base.local_planner.setPlan(plan, p, o)
def pose_constraints(x, y, rz=None, radius=0.15, frame_id="/map"):
    """
    Generate navigation constraints for a radius from a pose

    :param x, y: x and y coordinates
    :param rz: orientation to assume. None=no orientation constraint (default None)
    :param radius: allowed distance to the pose
    :param frame_id: frame in which the pose is expressed
    :return: navigation constraints
    :rtype: tuple(PositionConstraint, OrientationConstraint)
    """
    pc = PositionConstraint(constraint="(x-%f)^2+(y-%f)^2 < %f^2" % (x, y, radius),
                            frame=frame_id)
    oc = None
    if rz:
        oc = OrientationConstraint(look_at=Point(x + 1, y, 0.0), angle_offset=rz, frame=frame_id)
    return pc, oc
def arms_reach_constraint(pose_designator, arm_designator, look=True):
    """
    Position so that the arm can reach the position/entity

    :param pose_designator: designator that resolves to a FrameStamped of the point to be reached
    :param arm_designator: PublicArmDesignator, arm to use for manipulation
    :param look: bool, whether or not the orientation must be constrained as well
    :return: navigation constraints, if a designator does not resolve None is returned
    :rtype: tuple(PositionConstraint, OrientationConstraint)
    """
    arm = arm_designator.resolve()
    if not arm:
        rospy.logerr("Could not resolve arm, Designator {} did not resolve".format(arm_designator))
        return None

    radius = math.hypot(arm.base_offset.x(), arm.base_offset.y())

    pose = pose_designator.resolve()
    if not pose:
        rospy.logerr("No such place_pose, Designator {} did not resolve".format(pose_designator))
        return None

    try:
        x = pose.frame.p.x()
        y = pose.frame.p.y()
    except KeyError as ke:
        rospy.logerr("Could not determine pose: ".format(ke))
        return None

    # Outer radius
    ro = "(x-%f)^2+(y-%f)^2 < %f^2" % (x, y, radius + 0.075)
    ri = "(x-%f)^2+(y-%f)^2 > %f^2" % (x, y, radius - 0.075)
    pc = PositionConstraint(constraint=ri + " and " + ro, frame="/map")

    oc = None
    if look:
        angle_offset = -math.atan2(arm.base_offset.y(), arm.base_offset.x())
        oc = OrientationConstraint(look_at=Point(x, y, 0.0), frame="/map", angle_offset=angle_offset)

    return pc, oc
示例#11
0
    def _distance_to_poi_area(self, frame_stamped, arm):
        """
        :return: length of the path the robot would need to drive to place at the given point
        :rtype: int [plan steps]
        """
        base_offset = arm.base_offset
        radius = math.hypot(base_offset.x(), base_offset.y())

        x = frame_stamped.frame.p.x()
        y = frame_stamped.frame.p.y()
        radius -= 0.1
        ro = "(x-%f)^2+(y-%f)^2 < %f^2" % (x, y, radius + 0.075)
        ri = "(x-%f)^2+(y-%f)^2 > %f^2" % (x, y, radius - 0.075)
        pos_constraint = PositionConstraint(constraint=ri + " and " + ro, frame=frame_stamped.frame_id)

        plan_to_poi = self.robot.base.global_planner.getPlan(pos_constraint)

        if plan_to_poi:
            distance = len(plan_to_poi)
            # print "Distance to {fs}: {dist}".format(dist=distance, fs=frame_stamped.frame.p)
        else:
            distance = None
        return distance
示例#12
0
    def execute(self, userdata=None):
        e = self.destination_designator.resolve()

        if not e:
            rospy.logerr(
                "CheckDoorPassable::execute: No entity could be resolved from designator '%s'"
                % self.destination_designator)
            return None

        try:
            x = e.pose.position.x
            y = e.pose.position.y
        except:
            return None

        pc = PositionConstraint(constraint="(x-%f)^2+(y-%f)^2 < %f^2" %
                                (x, y, 0.5),
                                frame="/map")
        plan = self.robot.base.global_planner.getPlan(pc)

        if plan and len(plan) < 3 and computePathLength(plan) < 3.0:
            return "passable"
        else:
            return "blocked"
示例#13
0
 def _replan(self):
     self._replan_attempts += 1
     rospy.loginfo("Trying to get a global plan")
     operator_position = self._last_operator._pose.p
     # Define end goal constraint, solely based on the (old) operator position
     self._replan_pc = PositionConstraint()
     self._replan_pc.constraint = "(x-%f)^2 + (y-%f)^2 < %f^2" % (
         operator_position.x(), operator_position.y(),
         self._operator_radius)
     ros_plan = self._robot.base.global_planner.getPlan(self._replan_pc)
     if not ros_plan or not self._robot.base.global_planner.checkPlan(
             ros_plan):
         rospy.loginfo("No global plan possible")
     else:
         self._robot.speech.speak("Just a sec, let me try this way.")
         rospy.loginfo("Found a global plan, sending it to local planner")
         self._replan_time = rospy.Time.now()
         self._replan_active = True
         oc = self._robot.base.local_planner.getCurrentOrientationConstraint(
         )
         self._visualize_plan(ros_plan)
         self._robot.base.local_planner.setPlan(ros_plan, self._replan_pc,
                                                oc)
         self._breadcrumbs = []
def dummy_constraint(name="test"):
    pc = PositionConstraint(constraint="constraint_"+name, frame="/map")
    oc = OrientationConstraint(look_at=Point(0, 0, 0), frame="/map")
    return pc, oc
def radius_constraint(entity, radius, margin):
    """
    Navigates to a radius from an ed entity. If a convex hull is present, the distance
    to the convex hull is used. Otherwise, the distance to the pose of the entity is used

    :param entity: EdEntityDesignator for the object to observe
    :param radius: (float) desired distance to the pose of the entity [m]
    :param margin: (float) allowed margin w.r.t. specified radius on both sides [m]
    :return: navigation constraints. If the entity does not resolve, None is returned.
    :rtype: tuple(PositionConstraint, OrientationConstraint)
    """
    e = entity.resolve()

    if not e:
        rospy.logerr("No entity from {}".format(entity))
        return None

    try:
        ch = e.shape.convex_hull
    except NotImplementedError:
        # In case of an entity without convex hull, we might not want to use this
        ch = []

    x = e.pose.frame.p.x()
    y = e.pose.frame.p.y()
    assert e.pose.frame_id.strip(
        "/"
    ) == "map", "radius constraint function assumes entities are defined w.r.t. map frame"

    if len(
            ch
    ) > 0:  # If a convex hull is present, use this to create the position constraint
        pci = ""  # Create an empty position constraint

        for i in xrange(len(ch)):  # Iterate over the convex hull
            j = (i + 1) % len(ch)
            dx = ch[j].x() - ch[i].x()
            dy = ch[j].y() - ch[i].y()

            length = math.hypot(dx, dy)

            xs = x + ch[i].x() + (dy / length) * radius
            ys = y + ch[i].y() - (dx / length) * radius

            if i != 0:
                pci = pci + ' and '

            pci = pci + "-(x-%f)*%f+(y-%f)*%f > 0.0 " % (xs, dy, ys, dx)

    else:  # If not, simply use the x and y position
        outer_radius = max(0., radius + margin)
        inner_radius = max(0., radius - margin)

        ro = "(x-%f)^2+(y-%f)^2 < %f^2" % (x, y, outer_radius)
        ri = "(x-%f)^2+(y-%f)^2 > %f^2" % (x, y, inner_radius)
        pci = ri + " and " + ro

    pc = PositionConstraint(
        constraint=pci,
        frame="/map")  # Create the position constraint from the string
    oc = None
    return pc, oc
    def execute(self, userdata=None):
        """ A concurrent state machine which follows and afterwards deletes breadcrumbs from the buffer variable

        :return: follow_bread if the list of breadcrumbs is not empty
                 no_follow_bread_ask_finalize if the single remaining breadcrumb is the operator
                 no_follow_bread_recovery if buffer is completely empty
        """
        while self._buffer:

            if self.preempt_requested():
                return 'aborted'
            crumb = self._buffer.popleft()
            if not self._breadcrumb or self._breadcrumb[-1].crumb.distance_to_2d(
                crumb._pose.p) > self._breadcrumb_distance:
                self._breadcrumb.append(CrumbWaypoint(crumb))
        rospy.loginfo("Self._breadcrumb length: {}".format(len(self._breadcrumb)))

        # We don't want to base this on distance since that relies on the operator behavior
        # if self._robot.base.local_planner.getDistanceToGoal() > 0.65:  # 2.0:  # len(buffer) > 5:
        #     self._have_followed = True

        robot_position = self._robot.base.get_location().frame

        if self._breadcrumb:
            self._operator = self._breadcrumb[-1].crumb

        else:
            return 'no_follow_bread_recovery'
        #
        # if self._operator:
        #     operator = self._robot.ed.get_entity(id=self._operator.id)

        # while True:  # Should be timer, I think
        try:
            _, semantics = self._robot.hmi.query(description="", grammar="T[true] -> %s stop" % self._robot.robot_name, target="T")
            if semantics:
                return 'no_follow_bread_ask_finalize'
        except hmi.TimeoutException:
            rospy.logwarn("[HMC] Listening timeout")

        # return 'no_follow_bread_ask_finalize'

        # if operator:
        #     rospy.loginfo("Distance to operator: {}".format(operator.distance_to_2d(robot_position.p)))
        #     if self._have_followed and operator.distance_to_2d(robot_position.p) < 1.0:
        #         self._have_followed = False
        #         return 'no_follow_bread_ask_finalize'

        # self._breadcrumb = [crwp for crwp in self._breadcrumb
        #                     if crwp.crumb.distance_to_2d(robot_position.p) > self._lookat_radius]

        current_index = -1
        for i, crumb in enumerate(self._breadcrumb):
            if crumb.crumb.distance_to_2d(robot_position.p) < self._lookat_radius:
                current_index = i

        # throw away all breadcrumbs before where we are
        self._breadcrumb = self._breadcrumb[current_index+1:]

        f = self._robot.base.get_location().frame
        previous_point = f.p
        operator_position = self._operator._pose.p

        ''' Define end goal constraint, solely based on the (old) operator position '''
        p = PositionConstraint()
        p.constraint = "(x-%f)^2 + (y-%f)^2 < %f^2" % (operator_position.x(), operator_position.y(),
                                                       self._operator_radius)
        o = OrientationConstraint()
        o.frame = self._operator.id
        ''' Calculate global plan from robot position, through breadcrumbs, to the operator '''

        for crumb_waypoint in self._breadcrumb:
            crumb = crumb_waypoint.crumb
            diff = crumb._pose.p - previous_point
            dx, dy = diff.x(), diff.y()
            length = crumb.distance_to_2d(previous_point)

            if length != 0:
                dx_norm = dx / length
                dy_norm = dy / length
                yaw = math.atan2(dy, dx)
                start = 0
                end = int(length / self._resolution)

                for i in range(start, end):
                    x = previous_point.x() + i * dx_norm * self._resolution
                    y = previous_point.y() + i * dy_norm * self._resolution
                    kdl_pose = kdl_conversions.kdl_frame_stamped_from_XYZRPY(x=x, y=y, z=0, yaw=yaw)
                    crumb_waypoint.waypoint = kdl_conversions.kdl_frame_stamped_to_pose_stamped_msg(kdl_pose)
            previous_point = copy.deepcopy(crumb._pose.p)

        if len(self._breadcrumb) > 0:
            ros_plan = [crwp.waypoint for crwp in self._breadcrumb]
            if not self._robot.base.global_planner.checkPlan(ros_plan):
                rospy.loginfo("Breadcrumb plan is blocked, removing blocked points")
                rospy.loginfo("Breadcrumbs before removing blocked points: {}".format(len(ros_plan)))
                ros_plan = [crwp.waypoint for crwp in self._breadcrumb
                            if self._robot.base.global_planner.checkPlan([crwp.waypoint])]
                rospy.loginfo("Breadcrumbs length after removing blocked points: {}".format(len(ros_plan)))

            # if the new ros plan is empty, we do have an operator but there are no breadcrumbs that we can reach
            # if not ros_plan:
            #     return 'no_follow_bread_recovery'
        else:
            return 'no_follow_bread_recovery'

        buffer_msg = Marker()
        buffer_msg.type = Marker.POINTS
        buffer_msg.header.stamp = rospy.get_rostime()
        buffer_msg.header.frame_id = "/map"
        buffer_msg.id = 0
        buffer_msg.action = Marker.DELETEALL
        self._breadcrumb_pub.publish(buffer_msg)

        buffer_msg = Marker()
        buffer_msg.type = Marker.POINTS
        buffer_msg.scale.x = 0.05
        buffer_msg.scale.y = 0.05
        buffer_msg.header.stamp = rospy.get_rostime()
        buffer_msg.header.frame_id = "/map"
        buffer_msg.color.a = 1
        buffer_msg.color.r = 1
        buffer_msg.color.g = 0
        buffer_msg.color.b = 0
        # buffer_msg.lifetime = rospy.Time(1)
        buffer_msg.id = 0
        buffer_msg.action = Marker.ADD

        for crumb in self._breadcrumb:
            buffer_msg.points.append(crumb.waypoint.pose.position)

        self._breadcrumb_pub.publish(buffer_msg)

        # line_strip = Marker()
        # line_strip.type = Marker.LINE_STRIP
        # line_strip.scale.x = 0.05
        # line_strip.header.frame_id = "/map"
        # line_strip.header.stamp = rospy.Time.now()
        # line_strip.color.a = 1
        # line_strip.color.r = 0
        # line_strip.color.g = 1
        # line_strip.color.b = 1
        # line_strip.id = 0
        # line_strip.action = Marker.ADD
        #
        # for crwp in ros_plan:
        #     line_strip.points.append(crwp)

        #self._plan_marker_pub.publish(line_strip)
        self._robot.base.local_planner.setPlan(ros_plan, p, o)
        rospy.sleep(rospy.Duration(0.5))
        return 'follow_bread'
示例#17
0
    def __init__(self, robot):
        smach.StateMachine.__init__(self, outcomes=['Done', 'Aborted'])

        self.robot = robot

        self.position_constraint = PositionConstraint()
        self.orientation_constraint = OrientationConstraint()
        self.position_constraint.constraint = "x^2+y^2 < 1"

        self.requested_location = None
        rospy.Subscriber("/location_request", std_msgs.msg.String,
                         self.requestedLocationcallback)

        self.random_nav_designator = RandomNavDesignator(self.robot)

        with self:

            smach.StateMachine.add("WAIT_A_SEC",
                                   states.WaitTime(robot, waittime=1.0),
                                   transitions={
                                       'waited': "SELECT_ACTION",
                                       'preempted': "Aborted"
                                   })

            smach.StateMachine.add("SELECT_ACTION",
                                   SelectAction(),
                                   transitions={
                                       'continue': "DETERMINE_TARGET",
                                       'pause': "SELECT_ACTION",
                                       'stop': "SAY_DONE"
                                   })

            @smach.cb_interface(
                outcomes=['target_determined', 'no_targets_available'],
                input_keys=[],
                output_keys=[])
            def determine_target(userdata, designator):

                entity_id = designator.getRandomGoal()

                sentences = [
                    "Lets go look at the %s", "Lets have a look at the %s",
                    "Lets go to the %s", "Lets move to the %s",
                    "I will go to the %s", "I will now move to the %s",
                    "I will now drive to the %s", "I will look the %s",
                    "The %s will be my next location", "The %s it is",
                    "New goal, the %s", "Going to look at the %s",
                    "Moving to the %s", "Driving to the %s", "On to the %s",
                    "On the move to the %s", "Going to the %s"
                ]
                robot.speech.speak(random.choice(sentences) % entity_id,
                                   block=False)

                return 'target_determined'

            smach.StateMachine.add('DETERMINE_TARGET',
                                   smach.CBState(determine_target,
                                                 cb_kwargs={
                                                     'designator':
                                                     self.random_nav_designator
                                                 }),
                                   transitions={
                                       'target_determined': 'DRIVE',
                                       'no_targets_available': 'SELECT_ACTION'
                                   })

            smach.StateMachine.add('DRIVE',
                                   states.NavigateToObserve(
                                       robot, self.random_nav_designator),
                                   transitions={
                                       "arrived": "SAY_SUCCEEDED",
                                       "unreachable": 'SAY_UNREACHABLE',
                                       "goal_not_defined": 'SELECT_ACTION'
                                   })

            smach.StateMachine.add("SAY_SUCCEEDED",
                                   states.Say(robot, [
                                       "I am here",
                                       "Goal succeeded",
                                       "Another goal succeeded",
                                       "Goal reached",
                                       "Another goal reached",
                                       "Target reached",
                                       "Another target reached",
                                       "Destination reached",
                                       "Another destination reached",
                                       "I have arrived",
                                       "I have arrived at my goal",
                                       "I have arrived at my target",
                                       "I have arrived at my destination",
                                       "I am at my goal",
                                       "I am at my target",
                                       "I am at my destination",
                                       "Here I am",
                                   ]),
                                   transitions={'spoken': 'SELECT_ACTION'})

            smach.StateMachine.add(
                "SAY_UNREACHABLE",
                states.Say(robot, [
                    "I can't find a way to my goal, better try something else",
                    "This goal is unreachable, I better find somewhere else to go",
                    "I am having a hard time getting there so I will look for a new target"
                ]),
                transitions={'spoken': 'SELECT_ACTION'})

            smach.StateMachine.add(
                "SAY_DONE",
                states.Say(robot, [
                    "That's all folks", "I'll stay here for a while", "Goodbye"
                ]),
                transitions={'spoken': 'Done'})
示例#18
0
    def _update_navigation(self):
        """Set the navigation plan to match the breadcrumbs collected into self._breadcrumbs.
        This list has all the Entity's of where the operator has been"""
        self._robot.head.cancel_goal()

        f = self._robot.base.get_location().frame
        robot_position = f.p
        operator_position = self._last_operator._pose.p
        ''' Define end goal constraint, solely based on the (old) operator position '''
        p = PositionConstraint()
        p.constraint = "(x-%f)^2 + (y-%f)^2 < %f^2" % (operator_position.x(),
                                                       operator_position.y(),
                                                       self._operator_radius)

        o = OrientationConstraint()
        if self._operator_id:
            o.frame = self._operator_id
        else:
            o.frame = 'map'
            o.look_at = kdl_conversions.kdl_vector_to_point_msg(
                self._last_operator.pose.frame.p)
        ''' Calculate global plan from robot position, through breadcrumbs, to the operator '''
        res = 0.05
        kdl_plan = []
        previous_point = robot_position

        if self._operator:
            breadcrumbs = self._breadcrumbs + [self._operator]
        else:
            breadcrumbs = self._breadcrumbs + [self._last_operator]
        for crumb in breadcrumbs:
            assert isinstance(crumb, Entity)
            diff = crumb._pose.p - previous_point
            dx, dy = diff.x(), diff.y()

            length = crumb.distance_to_2d(previous_point)

            if length != 0:
                dx_norm = dx / length
                dy_norm = dy / length
                yaw = math.atan2(dy, dx)

                start = 0
                end = int(length / res)

                for i in range(start, end):
                    x = previous_point.x() + i * dx_norm * res
                    y = previous_point.y() + i * dy_norm * res
                    kdl_plan.append(
                        kdl_conversions.kdl_frame_stamped_from_XYZRPY(x=x,
                                                                      y=y,
                                                                      z=0,
                                                                      yaw=yaw))

            previous_point = copy.deepcopy(crumb._pose.p)

        # Delete the elements from the plan within the operator radius from the robot
        cutoff = int(self._operator_radius / (2.0 * res))
        if len(kdl_plan) > cutoff:
            del kdl_plan[-cutoff:]

        ros_plan = frame_stampeds_to_pose_stampeds(kdl_plan)
        # Check if plan is valid. If not, remove invalid points from the path
        if not self._robot.base.global_planner.checkPlan(ros_plan):
            rospy.loginfo(
                "Breadcrumb plan is blocked, removing blocked points")
            # Go through plan from operator to robot and pick the first unoccupied point as goal point
            ros_plan = [
                point for point in ros_plan
                if self._robot.base.global_planner.checkPlan([point])
            ]

        self._visualize_plan(ros_plan)
        self._robot.base.local_planner.setPlan(ros_plan, p, o)