def main(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("finger_type") args = parser.parse_args(sys.argv[1:2]) finger_type = finger_types_data.check_finger_type(args.finger_type) num_fingers = finger_types_data.get_number_of_fingers(finger_type) parser = argparse.ArgumentParser(description=__doc__) if num_fingers == 1: parser.add_argument("data_file") elif num_fingers == 3: parser.add_argument("data_file_0") parser.add_argument("data_file_120") parser.add_argument("data_file_240") args = parser.parse_args(sys.argv[2:]) if num_fingers == 3: main_trifinger( finger_type, args.data_file_0, args.data_file_120, args.data_file_240, ) else: main_finger(finger_type, args.data_file)
def test_get_number_of_fingers(): for name in ftd.finger_types_data.keys(): assert (ftd.get_number_of_fingers(name) == ftd.finger_types_data[name].number_of_fingers) with pytest.raises(ValueError): ftd.check_finger_type("invalid")
def __init__( self, finger_type, time_step=0.004, enable_visualization=False, sim_joint_friction=0.0, ): """ Constructor, initializes the physical world we will work in. Args: finger_type (string): Name of the finger type. Use :meth:`get_valid_finger_types` to get a list of all supported types. time_step (float): Time (in seconds) between two simulation steps. Don't set this to be larger than 1/60. The gains etc. are set according to a time_step of 0.004 s. enable_visualization (bool): Set this to 'True' for a GUI interface to the simulation. sim_joint_friction (float or float array): Set this to non-zero to apply negative forces on applied torques to simulate joint friction """ self.finger_type = finger_types_data.check_finger_type(finger_type) self.number_of_fingers = finger_types_data.get_number_of_fingers( self.finger_type) self.time_step_s = time_step #: The kp gains for the pd control of the finger(s). Note, this depends #: on the simulation step size and has been set for a simulation rate #: of 250 Hz. self.position_gains = np.array([10.0, 10.0, 10.0] * self.number_of_fingers) #: The kd gains for the pd control of the finger(s). Note, this depends #: on the simulation step size and has been set for a simulation rate #: of 250 Hz. self.velocity_gains = np.array([0.1, 0.3, 0.001] * self.number_of_fingers) #: The kd gains used for damping the joint motor velocities during the #: safety torque check on the joint motors. self.safety_kd = np.array([0.08, 0.08, 0.04] * self.number_of_fingers) #: The maximum allowable torque that can be applied to each motor. self.max_motor_torque = 0.396 self._t = -1 self.__create_link_lists() self.__set_urdf_path() self._pybullet_client_id = self.__connect_to_pybullet( enable_visualization) self.__setup_pybullet_simulation() self.kinematics = pinocchio_utils.Kinematics(self.finger_urdf_path, self.tip_link_names) self.sim_joint_friction = sim_joint_friction
def main(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument( "--finger-type", required=True, choices=finger_types_data.get_valid_finger_types(), help="Specify valid finger type.", ) parser.add_argument( "--real-time-mode", "-r", action="store_true", help="""Run simulation in real time. If not set, the simulation runs as fast as possible. """, ) args = parser.parse_args() # select the correct types/functions based on which robot is used num_fingers = finger_types_data.get_number_of_fingers(args.finger_type) if num_fingers == 1: finger_types = robot_interfaces.finger create_backend = ( robot_fingers.pybullet_drivers.create_single_finger_backend) elif num_fingers == 3: finger_types = robot_interfaces.trifinger create_backend = ( robot_fingers.pybullet_drivers.create_trifinger_backend) robot_data = finger_types.SingleProcessData() # Create backend with the simulation as driver. # Simply replace this line by creating a backend for the real robot to run # the same code on the real robot. backend = create_backend(robot_data, real_time_mode=args.real_time_mode, visualize=True) frontend = finger_types.Frontend(robot_data) backend.initialize() # Simple example application that moves the finger to random positions. while True: action = finger_types.Action(position=get_random_position(num_fingers)) for _ in range(300): t = frontend.append_desired_action(action) frontend.wait_until_timeindex(t) # print current position from time to time current_position = frontend.get_observation(t).position print("Position: %s" % current_position)
def __init__( self, finger_type: str, time_step: float = 0.001, enable_visualization: bool = False, robot_position_offset: typing.Sequence[float] = (0, 0, 0), ): """ Args: finger_type: Name of the finger type. Use :meth:`~finger_types_data.get_valid_finger_types` to get a list of all supported types. time_step: Time (in seconds) between two simulation steps. Don't set this to be larger than 1/60. The gains etc. are set according to a time_step of 0.001 s. enable_visualization: Set this to 'True' for a GUI interface to the simulation. robot_position_offset: (x, y, z)-Position offset with which the robot is placed in the world. Use this, for example, to change the height of the fingers above the table. """ self.finger_type = finger_types_data.check_finger_type(finger_type) self.number_of_fingers = finger_types_data.get_number_of_fingers( self.finger_type ) self.time_step_s = time_step self.__set_default_pd_gains() #: The kd gains used for damping the joint motor velocities during the #: safety torque check on the joint motors. self.safety_kd = np.array([0.08, 0.08, 0.04] * self.number_of_fingers) #: The maximum allowable torque that can be applied to each motor. self.max_motor_torque = 0.396 self._t = -1 self.__create_link_lists() self.__set_urdf_path() self._pybullet_client_id = self.__connect_to_pybullet( enable_visualization ) self.__setup_pybullet_simulation(robot_position_offset) self.kinematics = pinocchio_utils.Kinematics( self.finger_urdf_path, self.tip_link_names )
def __init__( self, finger_type, time_step=0.004, enable_visualization=False, ): """Constructor, initializes the physical world we will work in. Args: finger_type : See :attr:`finger_type` time_step : See :attr:`time_step` enable_visualization : See :attr:`enable_visualization` """ self.finger_type = finger_types_data.check_finger_type(finger_type) self.number_of_fingers = finger_types_data.get_number_of_fingers( self.finger_type) self.time_step_s = time_step #: The kp gains for the pd control of the finger(s). Note, this depends #: on the simulation step size and has been set for a simulation rate #: of 250 Hz. self.position_gains = np.array([10.0, 10.0, 10.0] * self.number_of_fingers) #: The kd gains for the pd control of the finger(s). Note, this depends #: on the simulation step size and has been set for a simulation rate #: of 250 Hz. self.velocity_gains = np.array([0.1, 0.3, 0.001] * self.number_of_fingers) #: The kd gains used for damping the joint motor velocities during the #: safety torque check on the joint motors. self.safety_kd = np.array([0.08, 0.08, 0.04] * self.number_of_fingers) #: The maximum allowable torque that can be applied to each motor. self.max_motor_torque = 0.36 self._t = -1 self.__create_link_lists() self.__set_urdf_path() self.__connect_to_pybullet(enable_visualization) self.__setup_pybullet_simulation() self.pinocchio_utils = pinocchio_utils.PinocchioUtils( self.finger_urdf_path, self.tip_link_names)
def _run_position_test(self, finger_type, goal_positions): """Run position test for single or tri-finger. Moves the robot in a sequence of goal positions in joint-space. After each step, it is checked if the robot reached the goal with some tolerance. Args: finger_type: Which robot to use. goal_positions: A list of joint goal positions. """ # select the correct types/functions based on which robot is used num_fingers = finger_types_data.get_number_of_fingers(finger_type) if num_fingers == 1: finger_types = robot_interfaces.finger create_backend = ( robot_fingers.pybullet_drivers.create_single_finger_backend ) elif num_fingers == 3: finger_types = robot_interfaces.trifinger create_backend = ( robot_fingers.pybullet_drivers.create_trifinger_backend ) robot_data = finger_types.SingleProcessData() backend = create_backend( robot_data, real_time_mode=False, visualize=False ) frontend = finger_types.Frontend(robot_data) backend.initialize() # Simple example application that moves the finger to random positions. for goal in goal_positions: action = finger_types.Action(position=goal) for _ in range(300): t = frontend.append_desired_action(action) frontend.wait_until_timeindex(t) # check if desired position is reached current_position = frontend.get_observation(t).position np.testing.assert_array_almost_equal( goal, current_position, decimal=1 )
def main(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument( "--finger-type", choices=finger_types_data.get_valid_finger_types(), required=True, help="""Pass a valid finger type.""", ) parser.add_argument( "--real-time-mode", "-r", action="store_true", help="""Run simulation in real time. If not set, the simulation runs as fast as possible. """, ) parser.add_argument( "--first-action-timeout", "-t", type=float, default=math.inf, help="""Timeout (in seconds) for reception of first action after starting the backend. If not set, the timeout is disabled. """, ) parser.add_argument( "--max-number-of-actions", "-a", type=int, default=0, help="""Maximum numbers of actions that are processed. After this the backend shuts down automatically. """, ) parser.add_argument( "--robot-logfile", "-l", type=str, help="""Path to a file to which the robot data log is written. If not specified, no log is generated. """, ) parser.add_argument( "--camera-logfile", type=str, help="""Path to a file to which the camera data log is written. If not specified, no log is generated. """, ) parser.add_argument( "--ready-indicator", type=str, metavar="READY_INDICATOR_FILE", help="""Path to a file that will be created once the backend is ready and will be deleted again when it stops (before storing the logs). """, ) parser.add_argument( "--add-cube", action="store_true", help="""Spawn a cube and run the object tracker backend.""", ) parser.add_argument( "--cameras", action="store_true", help="""Run camera backend using rendered images.""", ) parser.add_argument( "--visualize", "-v", action="store_true", help="Run pyBullet's GUI for visualization.", ) args = parser.parse_args() # configure the logging module log_handler = logging.StreamHandler(sys.stdout) logging.basicConfig( format="[SIM_TRIFINGER_BACKEND %(levelname)s %(asctime)s] %(message)s", level=logging.DEBUG, handlers=[log_handler], ) # select the correct types/functions based on which robot is used num_fingers = finger_types_data.get_number_of_fingers(args.finger_type) if num_fingers == 1: shared_memory_id = "finger" finger_types = robot_interfaces.finger create_backend = drivers.create_single_finger_backend elif num_fingers == 3: shared_memory_id = "trifinger" finger_types = robot_interfaces.trifinger create_backend = drivers.create_trifinger_backend # If max_number_of_actions is set, choose the history size of the time # series such that the whole episode fits in (+1 for the status message # containing the "limit exceeded" error). if args.max_number_of_actions: history_size = args.max_number_of_actions + 1 else: history_size = 1000 robot_data = finger_types.MultiProcessData( shared_memory_id, True, history_size=history_size ) robot_logger = finger_types.Logger(robot_data) backend = create_backend( robot_data, args.real_time_mode, args.visualize, args.first_action_timeout, args.max_number_of_actions, ) backend.initialize() # # Camera and Object Tracker Interface # Important: These objects need to be created _after_ the simulation is # initialized (i.e. after the SimFinger instance is created). # if args.cameras and not args.add_cube: # If cameras are enabled but not the object, use the normal # PyBulletTriCameraDriver. from trifinger_cameras import tricamera camera_data = tricamera.MultiProcessData("tricamera", True, 10) camera_driver = tricamera.PyBulletTriCameraDriver() camera_backend = tricamera.Backend(camera_driver, camera_data) # noqa elif args.add_cube: # If the cube is enabled, use the PyBulletTriCameraObjectTrackerDriver. # In case the cameras are not requested, disable rendering of the # images to save time. import trifinger_object_tracking.py_tricamera_types as tricamera # spawn a cube in the centre of the arena cube = collision_objects.Block(position=[0.0, 0.0, 0.035]) render_images = args.cameras camera_data = tricamera.MultiProcessData("tricamera", True, 10) camera_driver = tricamera.PyBulletTriCameraObjectTrackerDriver( cube, robot_data, render_images ) camera_backend = tricamera.Backend(camera_driver, camera_data) # noqa # # Camera/Object Logger # camera_logger = None if args.camera_logfile: try: camera_data except NameError: logging.critical("Cannot create camera log camera is not running.") return # TODO camera_fps = 10 robot_rate_hz = 1000 buffer_length_factor = 1.5 episode_length_s = args.max_number_of_actions / robot_rate_hz # Compute camera log size based on number of robot actions plus some # safety buffer log_size = int(camera_fps * episode_length_s * buffer_length_factor) logging.info("Initialize camera logger with buffer size %d", log_size) camera_logger = tricamera.Logger(camera_data, log_size) # if specified, create the "ready indicator" file to indicate that the # backend is ready if args.ready_indicator: pathlib.Path(args.ready_indicator).touch() backend.wait_until_first_action() if camera_logger: camera_logger.start() logging.info("Start camera logging") backend.wait_until_terminated() # delete the ready indicator file to indicate that the backend has shut # down if args.ready_indicator: pathlib.Path(args.ready_indicator).unlink() if camera_logger: logging.info( "Save recorded camera data to file %s", args.camera_logfile ) camera_logger.stop_and_save(args.camera_logfile) if camera_data: # stop the camera backend logging.info("Stop camera backend") camera_backend.shutdown() if args.robot_logfile: logging.info("Save robot data to file %s", args.robot_logfile) if args.max_number_of_actions: end_index = args.max_number_of_actions else: end_index = -1 robot_logger.write_current_buffer_binary( args.robot_logfile, start_index=0, end_index=end_index ) # cleanup stuff before the simulation (backend) is terminated if args.add_cube: del cube logging.info("Done.")
def __init__( self, finger_type, finger_config_suffix, enable_visualization=False, ): """ Constructor, initializes the physical world we will work in. Args: finger_type (string): Name of the finger type. In order to get a list of the valid finger types, call :meth:`.finger_types_data.get_valid_finger_types`. finger_config_suffix (int): ID of the finger that is used. Has to be one of [0, 120, 240]. This is only if a single finger is to be used on any of the robots, and is ignored otherwise. enable_visualization (bool, optional): Set to 'True' to run a GUI for visualization. This uses pyBullet but only for visualization, i.e. the state of the simulation is constantly set to match the one of the real robot. """ # Simulation is only used for visualization, so only run it when needed self.simulator = None if enable_visualization: self.simulator = SimFinger( finger_type=finger_type, time_step=0.001, # todo: not sure if this is correct enable_visualization=True, ) number_of_fingers = finger_types_data.get_number_of_fingers( finger_type) if number_of_fingers == 1: if finger_type == "fingerone": config_file_path = os.path.join( rospkg.RosPack().get_path("robot_fingers"), "config", "finger_%s.yml" % finger_config_suffix, ) elif finger_type == "fingeredu": config_file_path = os.path.join( rospkg.RosPack().get_path("robot_fingers"), "config", "fingeredu_%s.yml" % finger_config_suffix, ) finger_data = robot_interfaces.finger.SingleProcessData() self.real_finger_backend = ( robot_fingers.create_real_finger_backend( finger_data, config_file_path)) self.robot = robot_interfaces.finger.Frontend(finger_data) self.Action = robot_interfaces.finger.Action elif number_of_fingers == 3: if finger_type == "trifingerone": config_file_path = os.path.join( rospkg.RosPack().get_path("robot_fingers"), "config", "trifinger.yml", ) elif finger_type == "trifingeredu": config_file_path = os.path.join( rospkg.RosPack().get_path("robot_fingers"), "config", "trifingeredu.yml", ) finger_data = robot_interfaces.trifinger.SingleProcessData() self.real_finger_backend = robot_fingers.create_trifinger_backend( finger_data, config_file_path) self.robot = robot_interfaces.trifinger.Frontend(finger_data) self.Action = robot_interfaces.trifinger.Action self.real_finger_backend.initialize()
def main(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument( "--finger-type", choices=finger_types_data.get_valid_finger_types(), required=True, help="""Pass a valid finger type.""", ) parser.add_argument( "--real-time-mode", "-r", action="store_true", help="""Run simulation in real time. If not set, the simulation runs as fast as possible. """, ) parser.add_argument( "--first-action-timeout", "-t", type=float, default=math.inf, help="""Timeout (in seconds) for reception of first action after starting the backend. If not set, the timeout is disabled. """, ) parser.add_argument( "--max-number-of-actions", "-a", type=int, default=0, help="""Maximum numbers of actions that are processed. After this the backend shuts down automatically. """, ) parser.add_argument( "--logfile", "-l", type=str, help="""Path to a file to which the data log is written. If not specified, no log is generated. """, ) parser.add_argument( "--add-cube", action="store_true", help="""Spawn a cube and run the object tracker backend.""", ) parser.add_argument( "--cameras", action="store_true", help="""Run camera backend using rendered images.""", ) parser.add_argument( "--visualize", "-v", action="store_true", help="Run pyBullet's GUI for visualization.", ) args = parser.parse_args() # select the correct types/functions based on which robot is used num_fingers = finger_types_data.get_number_of_fingers(args.finger_type) if num_fingers == 1: shared_memory_id = "finger" finger_types = robot_interfaces.finger create_backend = drivers.create_single_finger_backend elif num_fingers == 3: shared_memory_id = "trifinger" finger_types = robot_interfaces.trifinger create_backend = drivers.create_trifinger_backend # If max_number_of_actions is set, choose the history size of the time # series such that the whole episode fits in (+1 for the status message # containing the "limit exceeded" error). if args.max_number_of_actions: history_size = args.max_number_of_actions + 1 else: history_size = 1000 robot_data = finger_types.MultiProcessData(shared_memory_id, True, history_size=history_size) logger = finger_types.Logger(robot_data) backend = create_backend( robot_data, args.real_time_mode, args.visualize, args.first_action_timeout, args.max_number_of_actions, ) backend.initialize() # Object and Object Tracker Interface # Important: These objects need to be created _after_ the simulation is # initialized (i.e. after the SimFinger instance is created). if args.add_cube: # only import when really needed import trifinger_object_tracking.py_object_tracker as object_tracker # spawn a cube in the arena cube = collision_objects.Block() # initialize the object tracker interface object_tracker_data = object_tracker.Data("object_tracker", True) object_tracker_backend = object_tracker.SimulationBackend( # noqa object_tracker_data, cube, args.real_time_mode) if args.cameras: from trifinger_cameras import tricamera camera_data = tricamera.MultiProcessData("tricamera", True, 10) camera_driver = tricamera.PyBulletTriCameraDriver() camera_backend = tricamera.Backend(camera_driver, camera_data) # noqa backend.wait_until_terminated() if args.logfile: if args.max_number_of_actions: end_index = args.max_number_of_actions else: end_index = -1 logger.write_current_buffer(args.logfile, start_index=0, end_index=end_index)
def __init__( self, control_rate_s, finger_type, enable_visualization, ): """Intializes the constituents of the pushing environment. Constructor sets up the finger robot depending on the finger type, sets up the observation and action spaces, smoothing for reducing jitter on the robot, and provides for a way to synchronize robots being trained independently. Args: control_rate_s (float): the rate (in seconds) at which step method of the env will run. The actual robot controller may run at a higher rate, so this is used to compute the number of robot control updates per environment step. finger_type (string): Name of the finger type. In order to get a list of the valid finger types, call :meth:`.finger_types_data.get_valid_finger_types` enable_visualization (bool): if the simulation env is to be visualized """ #: an instance of the simulated robot depending on the desired #: robot type self.finger = SimFinger( finger_type=finger_type, enable_visualization=enable_visualization, ) self.num_fingers = finger_types_data.get_number_of_fingers(finger_type) #: the number of times the same action is to be applied to #: the robot in one step. self.steps_per_control = int( round(control_rate_s / self.finger.time_step_s)) assert (abs(control_rate_s - self.steps_per_control * self.finger.time_step_s) <= 0.000001) #: the types of observations that should be a part of the environment's #: observed state self.observations_keys = [ "joint_positions", "joint_velocities", "action_joint_positions", "goal_position", "object_position", ] #: the size of each of the observation type that is part of the #: observation keys (in the same order) self.observations_sizes = [ 3 * self.num_fingers, 3 * self.num_fingers, 3 * self.num_fingers, 3, 3, ] # sets up the observation and action spaces for the environment, # unscaled spaces have the custom bounds set up for each observation # or action type, whereas all the values in the observation and action # spaces lie between 1 and -1 self.spaces = FingerSpaces( num_fingers=self.num_fingers, observations_keys=self.observations_keys, observations_sizes=self.observations_sizes, separate_goals=False, ) self.unscaled_observation_space = ( self.spaces.get_unscaled_observation_space()) self.unscaled_action_space = self.spaces.get_unscaled_action_space() self.observation_space = self.spaces.get_scaled_observation_space() self.action_space = self.spaces.get_scaled_action_space() #: a logger to enable logging of observations if desired self.logger = DataLogger() #: the object that has to be pushed self.block = collision_objects.Block() #: a marker to visualize where the target goal position for the episode #: is self.goal_marker = visual_objects.Marker( number_of_goals=1, goal_size=0.0325, initial_position=[0.19, 0.08, 0.0425], ) self.reset()
def __init__( self, control_rate_s, finger_type, enable_visualization, smoothing_params, use_real_robot=False, finger_config_suffix="0", synchronize=False, ): """Intializes the constituents of the reaching environment. Constructor sets up the finger robot depending on the finger type, and also whether an instance of the simulated or the real robot is to be created. Also sets up the observation and action spaces, smoothing for reducing jitter on the robot, and provides for a way to synchronize robots being trained independently. Args: control_rate_s (float): the rate (in seconds) at which step method of the env will run. The actual robot controller may run at a higher rate, so this is used to compute the number of robot control updates per environment step. finger_type (string): Name of the finger type. In order to get a dictionary of the valid finger types, call :meth:`.finger_types_data.get_valid_finger_types` enable_visualization (bool): if the simulation env is to be visualized smoothing_params (dict): num_episodes (int): the total number of episodes for which the training is performed start_after (float): the fraction of episodes after which the smoothing of applied actions to the motors should start final_alpha (float): smoothing coeff that will be reached at the end of the smoothing stop_after (float): the fraction of total episodes by which final alpha is to be reached, after which the same final alpha will be used for smoothing in the remainder of the episodes is_test (bool, optional): Include this for testing use_real_robot (bool): if training is to be performed on the real robot ([default] False) finger_config_suffix: pass this if only one of the three fingers is to be trained. Valid choices include [0, 120, 240] ([default] 0) synchronize (bool): Set this to True if you want to train independently on three fingers in separate processes, but have them synchronized. ([default] False) """ #: an instance of a simulated, or a real robot depending on #: what is desired. if use_real_robot: from pybullet_fingers.real_finger import RealFinger self.finger = RealFinger( finger_type=finger_type, finger_config_suffix=finger_config_suffix, enable_visualization=enable_visualization, ) else: self.finger = SimFinger( finger_type=finger_type, enable_visualization=enable_visualization, ) self.num_fingers = finger_types_data.get_number_of_fingers(finger_type) #: the number of times the same action is to be applied to #: the robot. self.steps_per_control = int( round(control_rate_s / self.finger.time_step_s)) assert (abs(control_rate_s - self.steps_per_control * self.finger.time_step_s) <= 0.000001) #: the types of observations that should be a part of the environment's #: observed state self.observations_keys = [ "joint_positions", "joint_velocities", "goal_position", "action_joint_positions", ] self.observations_sizes = [ 3 * self.num_fingers, 3 * self.num_fingers, 3 * self.num_fingers, 3 * self.num_fingers, ] # sets up the observation and action spaces for the environment, # unscaled spaces have the custom bounds set up for each observation # or action type, whereas all the values in the observation and action # spaces lie between 1 and -1 self.spaces = FingerSpaces( num_fingers=self.num_fingers, observations_keys=self.observations_keys, observations_sizes=self.observations_sizes, separate_goals=True, ) self.unscaled_observation_space = ( self.spaces.get_unscaled_observation_space()) self.unscaled_action_space = self.spaces.get_unscaled_action_space() self.observation_space = self.spaces.get_scaled_observation_space() self.action_space = self.spaces.get_scaled_action_space() #: a logger to enable logging of observations if desired self.logger = DataLogger() # sets up smooothing if "is_test" in smoothing_params: self.smoothing_start_episode = 0 self.smoothing_alpha = smoothing_params["final_alpha"] self.smoothing_increase_step = 0 self.smoothing_stop_episode = math.inf else: self.smoothing_stop_episode = int( smoothing_params["num_episodes"] * smoothing_params["stop_after"]) self.smoothing_start_episode = int( smoothing_params["num_episodes"] * smoothing_params["start_after"]) num_smoothing_increase_steps = (self.smoothing_stop_episode - self.smoothing_start_episode) self.smoothing_alpha = 0 self.smoothing_increase_step = (smoothing_params["final_alpha"] / num_smoothing_increase_steps) self.smoothed_action = None self.episode_count = 0 #: a marker to visualize where the target goal position for the episode #: is to which the tip link(s) of the robot should reach self.enable_visualization = enable_visualization if self.enable_visualization: self.goal_marker = visual_objects.Marker( number_of_goals=self.num_fingers) # set up synchronization if it's set to true self.synchronize = synchronize if synchronize: now = datetime.datetime.now() self.next_start_time = datetime.datetime(now.year, now.month, now.day, now.hour, now.minute + 1) else: self.next_start_time = None self.seed() self.reset()