コード例 #1
0
    def load_planner(self, base_planner=None):
        """
        Method to load a particular planner for Locobot base

        :param base_planner: Name of the planner to be loaded

        :type base_planner: string
        """

        if not self._as.is_available():
            rospy.logwarn("Current planner is use by action server.\
                Please consider using cancel_goal method before calling this method."
                          )
            return False

        if base_planner is None:
            base_planner = self.configs.BASE.BASE_PLANNER
        assert base_planner in [
            "movebase",
            "none",
        ], "BASE.[BASE_PLANNER] should be movebase or none."
        if base_planner == "movebase":
            self.planner = MoveBasePlanner(self.configs, self._as)
        elif base_planner == "none":
            # No path planning is done here.
            self.planner = None
        self.base_planner = base_planner

        rospy.sleep(2)
コード例 #2
0
    def __init__(
        self,
        configs,
        map_img_dir=None,
        base_controller="ilqr",
        base_planner=None,
        base=None,
    ):
        """
        The constructor for LoCoBotBase class.

        :param configs: configurations read from config file
        :param map_img_dir: parent directory of the saved
               RGB images and depth images

        :type configs: YACS CfgNode
        :type map_img_dir: string
        """
        super(LoCoBotBase, self).__init__(configs=configs)
        use_base = rospy.get_param("use_base", False) or rospy.get_param(
            "use_sim", False)
        if not use_base:
            rospy.logwarn("Neither use_base, nor use_sim, is not set to True "
                          "when the LoCoBot driver is launched. "
                          "You may not be able to command the base "
                          "correctly using PyRobot!")
            return
        if base is None:
            base = configs.BASE.BASE_TYPE
        assert base in [
            "kobuki",
            "create",
        ], "BASE should be one of kobuki, create but is {:s}".format(base)

        if map_img_dir is None:
            map_img_dir = os.path.join(expanduser("~"), ".ros/Imgs")

        self.build_map = rospy.get_param("use_vslam", False)
        self.base_state = BaseState(base, self.build_map, map_img_dir, configs)

        # Path planner
        if base_planner is None:
            base_planner = configs.BASE.BASE_PLANNER
        assert base_planner in [
            "movebase",
            "none",
        ], "BASE.[BASE_PLANNER] should be movebase or none."
        if base_planner == "movebase":
            self.planner = MoveBasePlanner(self.configs)
        elif base_planner == "none":
            # No path planning is done here.
            self.planner = None
        self.base_planner = base_planner

        # Set up low-level controllers.
        if base_controller is None:
            base_controller = configs.BASE.BASE_CONTROLLER
        assert base_controller in [
            "proportional", "ilqr", "movebase"
        ], ("BASE.BASE_CONTROLLER should be one of proportional, ilqr, "
            "movebase but is {:s}".format(base_controller))

        self.base_controller = base_controller
        if base_controller == "ilqr":
            self.controller = ILQRControl(self.base_state, self.ctrl_pub,
                                          self.configs)
        elif base_controller == "proportional":
            self.controller = ProportionalControl(self.base_state,
                                                  self.ctrl_pub, self.configs)
        elif base_controller == "movebase":
            self.controller = MoveBaseControl(self.base_state, self.configs)

        rospy.on_shutdown(self.clean_shutdown)
コード例 #3
0
class LoCoBotBase(Base):
    """
    This is a common base class for the locobot and locobot-lite base.
    """
    def __init__(
        self,
        configs,
        map_img_dir=None,
        base_controller="ilqr",
        base_planner=None,
        base=None,
    ):
        """
        The constructor for LoCoBotBase class.

        :param configs: configurations read from config file
        :param map_img_dir: parent directory of the saved
               RGB images and depth images

        :type configs: YACS CfgNode
        :type map_img_dir: string
        """
        super(LoCoBotBase, self).__init__(configs=configs)
        use_base = rospy.get_param("use_base", False) or rospy.get_param(
            "use_sim", False)
        if not use_base:
            rospy.logwarn("Neither use_base, nor use_sim, is not set to True "
                          "when the LoCoBot driver is launched. "
                          "You may not be able to command the base "
                          "correctly using PyRobot!")
            return
        if base is None:
            base = configs.BASE.BASE_TYPE
        assert base in [
            "kobuki",
            "create",
        ], "BASE should be one of kobuki, create but is {:s}".format(base)

        if map_img_dir is None:
            map_img_dir = os.path.join(expanduser("~"), ".ros/Imgs")

        self.build_map = rospy.get_param("use_vslam", False)
        self.base_state = BaseState(base, self.build_map, map_img_dir, configs)

        # Path planner
        if base_planner is None:
            base_planner = configs.BASE.BASE_PLANNER
        assert base_planner in [
            "movebase",
            "none",
        ], "BASE.[BASE_PLANNER] should be movebase or none."
        if base_planner == "movebase":
            self.planner = MoveBasePlanner(self.configs)
        elif base_planner == "none":
            # No path planning is done here.
            self.planner = None
        self.base_planner = base_planner

        # Set up low-level controllers.
        if base_controller is None:
            base_controller = configs.BASE.BASE_CONTROLLER
        assert base_controller in [
            "proportional", "ilqr", "movebase"
        ], ("BASE.BASE_CONTROLLER should be one of proportional, ilqr, "
            "movebase but is {:s}".format(base_controller))

        self.base_controller = base_controller
        if base_controller == "ilqr":
            self.controller = ILQRControl(self.base_state, self.ctrl_pub,
                                          self.configs)
        elif base_controller == "proportional":
            self.controller = ProportionalControl(self.base_state,
                                                  self.ctrl_pub, self.configs)
        elif base_controller == "movebase":
            self.controller = MoveBaseControl(self.base_state, self.configs)

        rospy.on_shutdown(self.clean_shutdown)

    def clean_shutdown(self):
        rospy.loginfo("Stop LoCoBot Base")
        if self.base_controller == "movebase":
            self.controller.cancel_goal()
        self.stop()

    def get_state(self, state_type):
        """
        Returns the requested base pose in the (x,y, yaw)
        format as computed either from
        Wheel encoder readings or Visual-SLAM

        :param state_type: Requested state type. Ex: Odom, SLAM, etc
        :type state_type: string
        :return: pose of the form [x, y, yaw]
        :rtype: list
        """
        if state_type == "odom":
            return self.base_state._get_odom_state()
        elif state_type == "vslam":
            assert self.build_map, ("Error: Cannot vslam state "
                                    "without enabling build map feature")
            assert self.build_map, "build_map was set to False"
            return self.base_state.vslam.base_pose

    def _get_plan(self, xyt_position):
        """
        Generates a plan that can take take the robot to given goal state.

        :param xyt_position: The goal state of the form (x,y,t)

        :type xyt_position: list
        """
        plan, status = self.planner.get_plan_absolute(xyt_position[0],
                                                      xyt_position[1],
                                                      xyt_position[2])
        if not status:
            raise ValueError("Failed to find a valid plan!")
        return self.planner.parse_plan(plan)

    def go_to_relative(self,
                       xyt_position,
                       use_map=False,
                       close_loop=True,
                       smooth=False):
        """
        Moves the robot to the robot to given goal state
        relative to its initial pose.

        :param xyt_position: The  relative goal state of the form (x,y,t)
        :param use_map: When set to "True", ensures that controler is
                        using only free space on the map to move the robot.
        :param close_loop: When set to "True", ensures that controler is
                           operating in open loop by taking
                           account of odometry.
        :param smooth: When set to "True", ensures that the
                       motion leading to the goal is a smooth one.

        :type xyt_position: list or np.ndarray
        :type use_map: bool
        :type close_loop: bool
        :type smooth: bool

        :return: True if successful; False otherwise (timeout, etc.)
        :rtype: bool
        """
        start_pos = self.base_state.state.state_f.copy()
        goal_pos = _get_absolute_pose(xyt_position, start_pos.ravel())
        return self.go_to_absolute(goal_pos, use_map, close_loop, smooth)

    def go_to_absolute(self,
                       xyt_position,
                       use_map=False,
                       close_loop=True,
                       smooth=False):
        """
        Moves the robot to the robot to given goal state in the world frame.

        :param xyt_position: The goal state of the form (x,y,t)
                             in the world (map) frame.
        :param use_map: When set to "True", ensures that controler is
                        using only free space on the map to move the robot.
        :param close_loop: When set to "True", ensures that controler
                           is operating in open loop by taking
                           account of odometry.
        :param smooth: When set to "True", ensures that the motion
                       leading to the goal is a smooth one.

        :type xyt_position: list or np.ndarray
        :type use_map: bool
        :type close_loop: bool
        :type smooth: bool

        :return: True if successful; False otherwise (timeout, etc.)
        :rtype: bool
        """

        xyt_position = np.asarray(xyt_position)

        try:
            if use_map:
                assert self.build_map, ("Error: Cannot use map without "
                                        "enabling build map feature")
                if self.base_controller == "ilqr":
                    goto = partial(self.go_to_relative,
                                   close_loop=close_loop,
                                   smooth=smooth)
                    self.planner.move_to_goal(xyt_position, goto)
                    return
                elif self.base_controller == "proportional":
                    self.planner.move_to_goal(xyt_position,
                                              self.controller.goto)
                    return

            self.controller.go_to_absolute(xyt_position, close_loop, smooth)
        except AssertionError as error:
            print(error)
            return False
        except:
            print("Unexpected error encountered during positon control!")
            return False
        return True

    def track_trajectory(self, states, controls=None, close_loop=True):
        """
        State trajectory that the robot should track.

        :param states: sequence of (x,y,t) states that the robot should track.
        :param controls: optionally specify control sequence as well.
        :param close_loop: whether to close loop on the
                           computed control sequence or not.

        :type states: list
        :type controls: list
        :type close_loop: bool

        :return: True if successful; False otherwise (timeout, etc.)
        :rtype: bool
        """

        if len(states) == 0:
            rospy.loginfo("The given trajectory is empty")
            return

        try:
            if self.base_controller == "ilqr":
                self.controller.track_trajectory(states, controls, close_loop)
            else:
                plan_idx = 0

                while True:
                    plan_idx = min(plan_idx, len(states) - 1)
                    point = states[plan_idx]
                    self.controller.go_to_absolute(point,
                                                   close_loop=close_loop)

                    if plan_idx == len(states) - 1:
                        break
                    plan_idx += self.configs.BASE.TRACKED_POINT
        except AssertionError as error:
            print(error)
            return False
        except:
            print("Unexpected error encountered during trajectory tracking!")
            return False
        return True
コード例 #4
0
class LoCoBotBase(Base):
    """
    This is a common base class for the locobot and locobot-lite base.
    """
    def __init__(
        self,
        configs,
        map_img_dir=None,
        base_controller="ilqr",
        base_planner=None,
        base=None,
    ):
        """
        The constructor for LoCoBotBase class.

        :param configs: configurations read from config file
        :param map_img_dir: parent directory of the saved
               RGB images and depth images

        :type configs: YACS CfgNode
        :type map_img_dir: string
        """
        super(LoCoBotBase, self).__init__(configs=configs)
        use_base = rospy.get_param("use_base", False) or rospy.get_param(
            "use_sim", False)
        if not use_base:
            rospy.logwarn("Neither use_base, nor use_sim, is not set to True "
                          "when the LoCoBot driver is launched. "
                          "You may not be able to command the base "
                          "correctly using PyRobot!")
            return
        if base is None:
            base = configs.BASE.BASE_TYPE
        assert base in [
            "kobuki",
            "create",
        ], "BASE should be one of kobuki, create but is {:s}".format(base)

        if map_img_dir is None:
            map_img_dir = os.path.join(expanduser("~"), ".ros/Imgs")

        self.build_map = rospy.get_param("use_vslam", False)
        self.base_state = BaseState(base, self.build_map, map_img_dir, configs)

        ###################### Action server specific things######################

        self.action_name = "pyrobot/locobot/base/controller_server"
        self.controller_sub = rospy.Subscriber(
            self.action_name,
            Empty,
            self._execute_controller,
        )
        self._as = LocalActionServer()
        self.controller_pub = rospy.Publisher(self.action_name,
                                              Empty,
                                              queue_size=1)
        # class variables shared when communicating between client and server
        self.smooth, self.use_map, self.close_loop = None, None, None
        self.xyt_position = None
        self.xyt_positions = None
        self.controls = None

        self.load_planner(base_planner)
        self.load_controller(base_controller)
        rospy.on_shutdown(self.clean_shutdown)

    # TODO: change this to a planner object.
    # This requires uniformity across all controllers
    def load_planner(self, base_planner=None):
        """
        Method to load a particular planner for Locobot base

        :param base_planner: Name of the planner to be loaded

        :type base_planner: string
        """

        if not self._as.is_available():
            rospy.logwarn("Current planner is use by action server.\
                Please consider using cancel_goal method before calling this method."
                          )
            return False

        if base_planner is None:
            base_planner = self.configs.BASE.BASE_PLANNER
        assert base_planner in [
            "movebase",
            "none",
        ], "BASE.[BASE_PLANNER] should be movebase or none."
        if base_planner == "movebase":
            self.planner = MoveBasePlanner(self.configs, self._as)
        elif base_planner == "none":
            # No path planning is done here.
            self.planner = None
        self.base_planner = base_planner

        rospy.sleep(2)

    # TODO: change this to a controller object.
    # This requires uniformity across all controllers
    def load_controller(self, base_controller=None):
        """
        Method to load a particular controller for Locobot base

        :param base_controller: Name of the controller to be loaded

        :type base_controller: string
        """
        if not self._as.is_available():
            rospy.logwarn("Current controller is use by action server.\
                Please consider using cancel_goal method before calling this method."
                          )
            return False

        # Set up low-level controllers.
        if base_controller is None:
            base_controller = self.configs.BASE.BASE_CONTROLLER
        assert base_controller in [
            "proportional", "ilqr", "movebase"
        ], ("BASE.BASE_CONTROLLER should be one of proportional, ilqr, "
            "movebase but is {:s}".format(base_controller))
        self.base_controller = base_controller
        if base_controller == "ilqr":
            self.controller = ILQRControl(self.base_state, self.ctrl_pub,
                                          self.configs, self._as)
        elif base_controller == "proportional":
            self.controller = ProportionalControl(self.base_state,
                                                  self.ctrl_pub, self.configs,
                                                  self._as)
        elif base_controller == "movebase":
            self.controller = MoveBaseControl(self.base_state, self.configs,
                                              self._as)

        rospy.sleep(2)

    def _execute_controller(self, goal):

        assert self._called_method in [
            "go_to_absolute",
            "track_trajectory",
        ], "Invalid action server method called"
        if self._called_method == "go_to_absolute":
            result = self._go_to_absolute()
        elif self._called_method == "track_trajectory":
            result = self._track_trajectory()

        if self._as.is_disabled():
            print("Locobot base server is disabled")
        elif self._as.is_preempt_requested():
            rospy.logwarn("Current action has been Preempted")
            self._as.set_preempted()
        elif result:
            self._as.set_succeeded()
        else:
            self._as.set_aborted()

    def _track_trajectory(self):

        try:
            if self.base_controller == "ilqr":
                result = self.controller.track_trajectory(
                    self.xyt_states, self.controls, self.close_loop)
            else:
                plan_idx = 0
                result = True
                while True:

                    if self._as.is_preempt_requested():
                        rospy.loginfo("Preempted the tracjectory execution")
                        result = False
                        break

                    plan_idx = min(plan_idx, len(self.xyt_states) - 1)
                    point = self.xyt_states[plan_idx]
                    if not self.controller.go_to_absolute(
                            point, close_loop=self.close_loop):
                        print("hhlhlhlhlhlh")
                        result = False
                        break
                    if plan_idx == len(self.xyt_states) - 1:
                        break
                    plan_idx += self.configs.BASE.TRACKED_POINT
        except AssertionError as error:
            print(error)
            result = False
        except:
            print("Unexpected error encountered during trajectory tracking!")
            result = False

        return result

    def _go_to_absolute(self):

        # try:
        if self.use_map:
            assert self.build_map, ("Error: Cannot use map without "
                                    "enabling build map feature")
            if self.base_controller == "ilqr":
                goto = partial(
                    self.go_to_relative,
                    close_loop=self.close_loop,
                    smooth=self.smooth,
                )
                result = self.planner.move_to_goal(self.xyt_position, goto)
            elif self.base_controller == "proportional":
                result = self.planner.move_to_goal(self.xyt_position,
                                                   self.controller.goto)
        else:
            result = self.controller.go_to_absolute(self.xyt_position,
                                                    self.close_loop,
                                                    self.smooth)

        return result

    def clean_shutdown(self):
        rospy.loginfo(
            "Stopping LoCoBot Base. Waiting for base thread to finish")
        self._as.disable()
        self.stop()
        self.controller_sub.unregister()

    def get_state(self, state_type):
        """
        Returns the requested base pose in the (x,y, yaw)
        format as computed either from
        Wheel encoder readings or Visual-SLAM

        :param state_type: Requested state type. Ex: Odom, SLAM, etc
        :type state_type: string
        :return: pose of the form [x, y, yaw]
        :rtype: list
        """
        if state_type == "odom":
            return self.base_state._get_odom_state()
        elif state_type == "vslam":
            assert self.build_map, ("Error: Cannot vslam state "
                                    "without enabling build map feature")
            assert self.build_map, "build_map was set to False"
            return self.base_state.vslam.base_pose

    def get_plan(self, xyt_position):
        """
        Generates a plan that can take take the robot to given goal state.

        :param xyt_position: The goal state of the form (x,y,t)

        :type xyt_position: list
        """
        plan, status = self.planner.get_plan_absolute(xyt_position[0],
                                                      xyt_position[1],
                                                      xyt_position[2])
        if not status:
            raise ValueError("Failed to find a valid plan!")
        return self.planner.parse_plan(plan)

    def _wait(self, wait):

        status = self._as.get_state()
        if wait:
            while status != LocalActionStatus.SUCCEEDED:
                if (status == LocalActionStatus.ABORTED
                        or status == LocalActionStatus.PREEMPTED
                        or status == LocalActionStatus.DISABLED):
                    return False
                status = self._as.get_state()
            return True
        else:
            return None

    def go_to_relative(self,
                       xyt_position,
                       use_map=False,
                       close_loop=True,
                       smooth=False,
                       wait=True):
        """
        Moves the robot to the robot to given goal state
        relative to its initial pose.

        :param xyt_position: The  relative goal state of the form (x,y,t)
        :param use_map: When set to "True", ensures that controler is
                        using only free space on the map to move the robot.
        :param close_loop: When set to "True", ensures that controler is
                           operating in open loop by taking
                           account of odometry.
        :param smooth: When set to "True", ensures that the
                       motion leading to the goal is a smooth one.
        :param wait: Makes the process wait at this funciton until the execution is 
                       complete

        :type xyt_position: list or np.ndarray
        :type use_map: bool
        :type close_loop: bool
        :type smooth: bool
        :type wait: bool

        :return: True if successful; False otherwise (timeout, etc.)
        :rtype: bool or None
        """
        start_pos = self.base_state.state.state_f.copy()
        goal_pos = _get_absolute_pose(xyt_position, start_pos.ravel())
        return self.go_to_absolute(goal_pos, use_map, close_loop, smooth, wait)

    def go_to_absolute(self,
                       xyt_position,
                       use_map=False,
                       close_loop=True,
                       smooth=False,
                       wait=True):
        """
        Moves the robot to the robot to given goal state in the world frame.

        :param xyt_position: The goal state of the form (x,y,t)
                             in the world (map) frame.
        :param use_map: When set to "True", ensures that controler is
                        using only free space on the map to move the robot.
        :param close_loop: When set to "True", ensures that controler
                           is operating in open loop by taking
                           account of odometry.
        :param smooth: When set to "True", ensures that the motion
                       leading to the goal is a smooth one.
        :param wait: Makes the process wait at this funciton until the execution is 
                       complete

        :type xyt_position: list or np.ndarray
        :type use_map: bool
        :type close_loop: bool
        :type smooth: bool
        :type wait: bool

        :return: True if successful; False otherwise (timeout, etc.)
        :rtype: bool
        """

        if not self._as.is_available():
            rospy.logwarn(
                "Base action server already in use by a different goal.\
                           Please consider using cancel_goal method before calling this method."
            )
            return False
        self._as.set_active()  # block action server and set active
        self.xyt_position = np.asarray(xyt_position)
        self.use_map = use_map
        self.smooth = smooth
        self.close_loop = close_loop
        self._called_method = "go_to_absolute"
        self.controller_pub.publish(Empty())

        self._wait(wait)

    def get_last_goal_result(self):
        """
        Returns the status of the commanded goal action.
        None - No last action to report
        ACTIVE = 1
        PREEMPTED  = 2
        SUCCEEDED = 3
        ABORTED = 4
        FREE = 5
        UNKOWN = 6
        PREEMPTING = 7     
        :rtype: LocalActionStatus

        """

        return self._as.get_state()

    def cancel_last_goal(self, stop_robot=True):
        """
        Cancels the last action server command executions.

        :param stop_robot: stops the robot abruptly if enabled.

        :type stop_robot: bool
        """

        self._as.cancel_goal()

        if stop_robot:
            self.stop()

    def track_trajectory(self,
                         states,
                         controls=None,
                         close_loop=True,
                         wait=True):
        """
        State trajectory that the robot should track.

        :param states: sequence of (x,y,t) states that the robot should track.
        :param controls: optionally specify control sequence as well.
        :param close_loop: whether to close loop on the
                           computed control sequence or not.

        :type states: list
        :type controls: list
        :type close_loop: bool

        :return: True if successful; False otherwise (timeout, etc.)
        :rtype: bool
        """

        if len(states) == 0:
            rospy.loginfo("The given trajectory is empty")
            return

        if not self._as.is_available():
            rospy.logwarn(
                "Base action server already in use by a different goal.\
                           Please consider using cancel_goal method before calling this method."
            )
            return False
        self._as.set_active()  # block action server and set active

        self.xyt_states = states
        self.controls = controls
        self.close_loop = close_loop
        self._called_method = "track_trajectory"
        self.controller_pub.publish(Empty())

        self._wait(wait)
コード例 #5
0
ファイル: base.py プロジェクト: Improbable-AI/pyrobot
    def __init__(self,
                 configs,
                 map_img_dir=None,
                 base_controller='ilqr',
                 base_planner=None,
                 base=None,
                 ):
        """
        The constructor for LoCoBotBase class.

        :param configs: configurations read from config file
        :param map_img_dir: parent directory of the saved
               RGB images and depth images

        :type configs: YACS CfgNode
        :type map_img_dir: string
        """
        super(LoCoBotBase, self).__init__(configs=configs)
        use_base = rospy.get_param(
            'use_base', False) or rospy.get_param(
            'use_sim', False)
        if not use_base:
            rospy.logwarn(
                'Neither use_base, nor use_sim, is not set to True '
                'when the LoCoBot driver is launched. '
                'You may not be able to command the base '
                'correctly using PyRobot!')
            return
        if base is None:
            base = configs.BASE.BASE_TYPE
        assert (base in ['kobuki', 'create', ]), \
            'BASE should be one of kobuki, create but is {:s}'.format(base)

        if map_img_dir is None:
            map_img_dir = os.path.join(expanduser("~"), '.ros/Imgs')

        self.build_map = rospy.get_param('use_vslam', False)
        self.base_state = BaseState(base, self.build_map, map_img_dir, configs)

        # Path planner
        if base_planner is None:
            base_planner = configs.BASE.BASE_PLANNER
        assert (base_planner in ['movebase', 'none']), \
            'BASE.[BASE_PLANNER] should be movebase or none.'
        if base_planner == 'movebase':
            self.planner = MoveBasePlanner(self.configs)
        elif base_planner == 'none':
            # No path planning is done here.
            self.planner = None
        self.base_planner = base_planner

        # Set up low-level controllers.
        if base_controller is None:
            base_controller = configs.BASE.BASE_CONTROLLER
        assert (base_controller in ['proportional', 'ilqr', 'movebase']), \
            'BASE.BASE_CONTROLLER should be one of proportional, ilqr, ' \
            'movebase but is {:s}'.format(base_controller)

        self.base_controller = base_controller
        if base_controller == 'ilqr':
            self.controller = ILQRControl(
                self.base_state, self.ctrl_pub, self.configs)
        elif base_controller == 'proportional':
            self.controller = ProportionalControl(
                self.base_state, self.ctrl_pub, self.configs)
        elif base_controller == 'movebase':
            self.controller = MoveBaseControl(self.base_state, self.configs)

        rospy.on_shutdown(self.clean_shutdown)