Пример #1
0
def _log(segment_id, file_path, frequency, logger_id):
    # creating an o80 frontend
    try:
        frontend = o80_pam.FrontEnd(segment_id)
    except:
        print(("\nfailed to start an o80 frontend on segment_id: {}. "
               "Is a corresponding robot running ?\n").format(segment_id))
        return
    # serializer will compress observation instances into string
    serializer = o80_pam.Serializer()
    # setting the collecting loop frequency
    frequency_manager = o80.FrequencyManager(frequency)
    latest = None
    # running the loop
    with open(file_path, "wb+") as f:
        # any other process may stop this loop by calling
        # _set_stop
        while not _should_stop(segment_id, logger_id):
            if latest is None:
                # first call
                observations = [frontend.latest()]
            else:
                # reading all new observations written by the backend
                # since the last pass of this loop
                observations = frontend.get_observations_since(latest + 1)
            for observation in observations:
                # serializing and writting all observations
                f.write(serializer.serialize(observation))
                f.flush()
            if observations:
                # keeping track of the latest observation written
                latest = observations[-1].get_iteration()
            # running at desired frequency
            frequency_manager.wait()
Пример #2
0
def _set_min_pressures(config, pam_config):

    frontend = o80_pam.FrontEnd(config.segment_id)
    for dof in range(4):
        min_ago = pam_config.min_pressure(dof, pam_interface.sign.agonist)
        min_antago = pam_config.min_pressure(dof, pam_interface.sign.antagonist)
        frontend.add_command(dof, min_ago, min_antago, o80.Duration_us.seconds(3), o80.Mode.OVERWRITE)
    frontend.pulse()
    del frontend
    def __init__(self, segment_id, frontend=None, burster=None):
        if frontend is None:
            self._frontend = o80_pam.FrontEnd(segment_id)
        else:
            self._frontend = frontend

        if burster is None:
            self._burster = self._frontend
        else:
            self._burster = burster
    def __init__(self, segment_id, config, frequency, window=WINDOW):

        # o80 frontend
        self._frontend = o80_pam.FrontEnd(segment_id)

        # data holder. "False" means the value has not been plotted
        # yet
        self._desired = [[ReadOnce(), ReadOnce()] for dof in range(4)]
        self._observed = [[ReadOnce(), ReadOnce()] for dof in range(4)]
        self._positions = [ReadOnce() for dof in range(4)]
        self._frequency = ReadOnce()

        # only data corresponding to a new iteration will be plotted
        self._iteration = None

        # avoiding concurrent read and write of data holder
        self._lock = threading.Lock()

        # a thread will get the data using the frontend
        # (and the fyplot instance will read the data)
        self._running = False
        self._thread = None

        # dynamic plot
        self._plot = fyplot.Plot(segment_id, 50, window)

        # min and max pressure according to the configuration
        self._min_pressure = min(config.min_pressures_ago +
                                 config.min_pressures_antago)
        self._max_pressure = max(config.max_pressures_ago +
                                 config.max_pressures_antago)

        # 1 plot per dof (desired and observed pressure, position/angle)
        for dof in range(4):
            dof_plot = (
                (partial(self.get_desired, dof, 0), (150, 0, 0)),
                (partial(self.get_desired, dof, 1), (0, 150, 0)),
                (partial(self.get_observed, dof, 0), (0, 255, 0)),
                (partial(self.get_observed, dof, 1), (255, 0, 0)),
            )

            self._plot.add_subplot((self._min_pressure, self._max_pressure),
                                   500, dof_plot)

        # 1 plot for the frequency
        freq_plot = ((self.get_frequency, (255, 255, 0)), )
        max_frequency = frequency + frequency / 10.0
        self._plot.add_subplot((0, max_frequency), 500, freq_plot)
Пример #5
0
    def start(self):
        """
        starts the observations collecting process
        """
        try:
            frontend = o80_pam.FrontEnd(self._segment_id)
            del frontend
        except Exception as e:
            raise Exception("Failed to create an o80 frontend" +
                            "on segment_id {}: {}".format(self._segment_id, e))

        self._process = Process(
            target=_log,
            args=(self._segment_id, self._file_path, self._frequency,
                  self._id),
        )
        _set_start(self._segment_id, self._id)
        self._process.start()
Пример #6
0
    def init(cls, segment_id, width=100):

        cls._frontend = o80_pam.FrontEnd(segment_id)

        # display config

        cls._width = width

        # init curses

        cls._screen = curses.initscr()
        curses.noecho()
        curses.curs_set(0)
        curses.start_color()
        curses.use_default_colors()
        curses.init_pair(1, curses.COLOR_GREEN, -1)
        curses.init_pair(2, curses.COLOR_BLUE, -1)
        cls._screen.keypad(1)
        cls._monitor_exit_thread = threading.Thread(target=cls._monitor_exit)
        cls._monitor_exit_thread.setDaemon(True)
        cls._monitor_exit_thread.start()
Пример #7
0
def _run(
    tennicam_segment_id: str,
    o80_pam_segment_id: str,
    frequency: float,
    filepath: pathlib.Path,
):
    """
    Creates an tennicam_client frontend and a o80_pam frontend and
    uses them to get all ball observations and pam robot observations;
    and dumping them in a corresponding string in filepath.
    """

    ball_frontend = tennicam_client.FrontEnd(tennicam_segment_id)
    robot_frontend = o80_pam.FrontEnd(o80_pam_segment_id)
    iteration = ball_frontend.latest().get_iteration()

    ball_getters = ("get_ball_id", "get_time_stamp", "get_position",
                    "get_velocity")
    robot_getters = ("get_time_stamp", "get_positions", "get_velocities")

    frequency_manager = o80.FrequencyManager(frequency)

    with filepath.open(mode="w") as f:
        try:
            while not signal_handler.has_received_sigint():
                iteration += 1
                obs_ball = ball_frontend.latest()
                obs_robot = robot_frontend.latest()
                ball_values = tuple(
                    (getattr(obs_ball, getter)() for getter in ball_getters))
                robot_values = tuple(
                    (getattr(obs_robot, getter)() for getter in robot_getters))
                str_ = repr((ball_values, robot_values))
                f.write(str_)
                f.write("\n")
                frequency_manager.wait()
        except (KeyboardInterrupt, SystemExit):
            pass
        except Exception as e:
            print("Error:", e)
Пример #8
0
    def __init__(self, hysr_config, reward_function):

        self._hysr_config = hysr_config

        # we will track the episode number
        self._episode_number = -1

        # we will track the step number (reset at the start
        # of each episode)
        self._step_number = -1

        # we end an episode after a fixed number of steps
        self._nb_steps_per_episode = hysr_config.nb_steps_per_episode
        # note: if self._nb_steps_per_episode is 0 or less,
        #       an episode will end based on a threshold
        #       in the z component of the ball position
        #       (see method _episode_over)

        # this instance of HysrOneBall interacts with several
        # instances of mujoco (pseudo real robot, simulated robot,
        # possibly instances of mujoco for extra balls).
        # Listing all the corresponding mujoco_ids
        self._mujoco_ids = []

        # pam muscles configuration
        self._pam_config = pam_interface.JsonConfiguration(
            hysr_config.pam_config_file)

        # to control pseudo-real robot (pressure control)
        if not hysr_config.real_robot:
            self._real_robot_handle, self._real_robot_frontend = configure_mujoco.configure_pseudo_real(
                hysr_config.pam_config_file,
                graphics=hysr_config.graphics_pseudo_real,
                accelerated_time=hysr_config.accelerated_time,
            )
            self._mujoco_ids.append(self._real_robot_handle.get_mujoco_id())
        else:
            # real robot: making some sanity check that the
            # rest of the configuration is ok
            if hysr_config.instant_reset:
                raise ValueError(
                    str("HysrOneBall configured for "
                        "real robot and instant reset."
                        "Real robot does not support "
                        "instant reset."))
            if hysr_config.accelerated_time:
                raise ValueError(
                    str("HysrOneBall configured for "
                        "real robot and accelerated time."
                        "Real robot does not support "
                        "accelerated time."))

        # to control the simulated robot (joint control)
        self._simulated_robot_handle = configure_mujoco.configure_simulation(
            hysr_config)
        self._mujoco_ids.append(self._simulated_robot_handle.get_mujoco_id())

        # where we want to shoot the ball
        self._target_position = hysr_config.target_position
        self._goal = self._simulated_robot_handle.interfaces[SEGMENT_ID_GOAL]

        # to read all recorded trajectory files
        self._trajectory_reader = context.BallTrajectories()

        # if requested, logging info about the frequencies of the steps and/or the
        # episodes
        if hysr_config.frequency_monitoring_step:
            size = 1000
            self._frequency_monitoring_step = frequency_monitoring.FrequencyMonitoring(
                SEGMENT_ID_STEP_FREQUENCY, size)
        else:
            self._frequency_monitoring_step = None
        if hysr_config.frequency_monitoring_episode:
            size = 1000
            self._frequency_monitoring_episode = (
                frequency_monitoring.FrequencyMonitoring(
                    SEGMENT_ID_EPISODE_FREQUENCY, size))
        else:
            self._frequency_monitoring_episode = None

        # if o80_pam (i.e. the pseudo real robot)
        # has been started in accelerated time,
        # the corresponding o80 backend will burst through
        # an algorithm time step
        self._accelerated_time = hysr_config.accelerated_time
        if self._accelerated_time:
            self._o80_time_step = hysr_config.o80_pam_time_step
            self._nb_robot_bursts = int(hysr_config.algo_time_step /
                                        hysr_config.o80_pam_time_step)

        # pam_mujoco (i.e. simulated ball and robot) should have been
        # started in accelerated time. It burst through algorithm
        # time steps
        self._mujoco_time_step = hysr_config.mujoco_time_step
        self._nb_sim_bursts = int(hysr_config.algo_time_step /
                                  hysr_config.mujoco_time_step)

        # the config sets either a zero or positive int (playing the
        # corresponding indexed pre-recorded trajectory) or a negative int
        # (playing randomly selected indexed trajectories)
        if hysr_config.trajectory >= 0:
            self._ball_behavior = _BallBehavior(index=hysr_config.trajectory)
        else:
            self._ball_behavior = _BallBehavior(random=True)

        # the robot will interpolate between current and
        # target posture over this duration
        self._period_ms = hysr_config.algo_time_step

        # reward configuration
        self._reward_function = reward_function

        # to get information regarding the ball
        # (instance of o80_pam.o80_ball.o80Ball)
        self._ball_communication = self._simulated_robot_handle.interfaces[
            SEGMENT_ID_BALL]

        # to send pressure commands to the real or pseudo-real robot
        # (instance of o80_pam.o80_pressures.o80Pressures)
        # hysr_config.real robot is either false (i.e. pseudo real
        # mujoco robot) or the segment_id of the real robot backend
        if not hysr_config.real_robot:
            self._pressure_commands = self._real_robot_handle.interfaces[
                SEGMENT_ID_PSEUDO_REAL_ROBOT]
        else:
            self._real_robot_frontend = o80_pam.FrontEnd(
                hysr_config.real_robot)
            self._pressure_commands = o80_pam.o80Pressures(
                hysr_config.real_robot, frontend=self._real_robot_frontend)

        # will encapsulate all information
        # about the ball (e.g. min distance with racket, etc)
        self._ball_status = context.BallStatus(hysr_config.target_position)

        # to send mirroring commands to simulated robots
        self._mirrorings = [
            self._simulated_robot_handle.interfaces[SEGMENT_ID_ROBOT_MIRROR]
        ]

        # to move the hit point marker
        # (instance of o80_pam.o80_hit_point.o80HitPoint)
        self._hit_point = self._simulated_robot_handle.interfaces[
            SEGMENT_ID_HIT_POINT]

        # tracking if this is the first step of the episode
        # (a call to the step function sets it to false, call to reset function sets it
        # back to true)
        self._first_episode_step = True

        # normally an episode ends when the ball z position goes
        # below a certain threshold (see method _episode_over)
        # this is to allow user to force ending an episode
        # (see force_episode_over method)
        self._force_episode_over = False

        # if false, the system will reset via execution of commands
        # if true, the system will reset by resetting the simulations
        # Only "false" is supported by the real robot
        self._instant_reset = hysr_config.instant_reset

        # adding extra balls (if any)
        if (hysr_config.extra_balls_sets is not None
                and hysr_config.extra_balls_sets > 0):

            self._extra_balls = []

            for setid in range(hysr_config.extra_balls_sets):

                # balls: list of instances of _ExtraBalls (defined in this file)
                # mirroring : for sending mirroring command to the robot
                #             of the set (joint controlled)
                #             (instance of
                #             o80_pam.o80_robot_mirroring.o80RobotMirroring)
                balls, mirroring, mujoco_id, frontend = _get_extra_balls(
                    setid, hysr_config)

                self._extra_balls.extend(balls)
                self._mirrorings.append(mirroring)
                self._mujoco_ids.append(mujoco_id)
                self._extra_balls_frontend = frontend
        else:
            self._extra_balls = []
            self._extra_balls_frontend = None

        # for running all simulations (main + for extra balls)
        # in parallel (i.e. when bursting is called, all mujoco
        # instance perform step(s) in parallel)
        self._parallel_burst = pam_mujoco.mirroring.ParallelBurst(
            self._mirrorings)

        # if set, logging the position of the robot at the end of reset, and possibly
        # get a warning when this position drifts as the number of episodes increase
        if hysr_config.robot_integrity_check is not None:
            self._robot_integrity = robot_integrity.RobotIntegrity(
                hysr_config.robot_integrity_threshold,
                file_path=hysr_config.robot_integrity_check,
            )
        else:
            self._robot_integrity = None

        # when starting, the real robot and the virtual robot(s)
        # may not be aligned, which may result in graphical issues,
        # so aligning them
        # (get values of real robot via self._pressure_commands,
        # and set values to all simulated robot via self._mirrorings)
        # source of mirroring in pam_mujoco.mirroring.py
        pam_mujoco.mirroring.align_robots(self._pressure_commands,
                                          self._mirrorings)
Пример #9
0
import time
import o80_pam

segment_id = "o80_pam_robot"

frontend = o80_pam.FrontEnd(segment_id)


def _print_title(title):
    print("\n")
    print("\n".join(["-" * len(title), title, "-" * len(title)]), "\n")


_print_title("latest observation")

# newest observation generated by the backend
observation = frontend.latest()

# here all the attributes encapsulated by an observation

# at each backend iteration, an observation is generated.
print("iteration", observation.get_iteration())

# the time stamp at which the observation was generated.
# it is an integer representing time in nano seconds
print("timestamp", observation.get_time_stamp())

# the pressure of the muscles as measured by sensors
print("observed pressures", observation.get_observed_pressures())

# the pressure of the muscles as the commands requested them to be.
Пример #10
0
def _get_frontend(config):
    try :
        frontend = o80_pam.FrontEnd(config.segment_id)
        return frontend
    except Exception as e :
        return None
Пример #11
0
    def __init__(
        self,
        mujoco_id: str,
        burst_mode: bool = False,
        accelerated_time: bool = False,
        graphics: bool = True,
        time_step: float = 0.002,
        table: MujocoTable = None,
        robot1: MujocoRobot = None,
        robot2: MujocoRobot = None,
        balls: list = [],  # list of mujoco_item.MujocoItem
        goals: list = [],  # list of mujoco_item.MujocoItem
        hit_points: list = [],
        # list of mujoco_item.MujocoItems (with 's' at the end)
        combined: MujocoItems = None,
        read_only: bool = False,
    ):

        self._mujoco_id = mujoco_id

        if not read_only:

            # combined (instance of mujoco_item.MujocoItems)
            # supports only a limited set of size (see source of MujocoItems)
            self.combined = combined
            if combined and (combined.size not in combined.accepted_sizes):
                raise ValueError(
                    "pam_mujoco.mujoco_item.MujocoItems supports "
                    "a limited set of size ({}). "
                    "{} provided".format(
                        ", ".join([str(a) for a in combined.accepted_sizes]),
                        combined.size,
                    ))

            # creating the mujoco xml model file

            logging.info(
                "creating the xml model file for {}".format(mujoco_id))

            if combined:
                all_balls = list(balls) + combined.items["balls"]
                all_goals = list(goals) + combined.items["goals"]
                all_hit_points = list(
                    hit_points) + combined.items["hit_points"]
            else:
                all_balls = balls
                all_goals = goals
                all_hit_points = hit_points

            items = models.model_factory(
                mujoco_id,
                time_step=time_step,
                table=table,
                balls=all_balls,
                goals=all_goals,
                hit_points=all_hit_points,
                robot1=robot1,
                robot2=robot2,
            )

            # creating the mujoco config

            logging.info(
                "creating mujoco configuration for {}".format(mujoco_id))

            config = pam_mujoco_wrp.MujocoConfig(mujoco_id)
            config.set_burst_mode(burst_mode)
            config.set_accelerated_time(accelerated_time)
            config.set_model_path(items["path"])
            config.set_graphics(graphics)

            if items["robot1"]:
                config.set_racket_robot1(items["robot1"].geom_racket)

            if items["robot2"]:
                config.set_racket_robot2(items["robot2"].geom_racket)

            if items["table"]:
                config.set_table(items["table"].geom_plate)

            _get_ball = partial(_get_mujoco_item_control,
                                pam_mujoco_wrp.MujocoItemTypes.ball)
            _get_hit_point = partial(_get_mujoco_item_control,
                                     pam_mujoco_wrp.MujocoItemTypes.hit_point)
            _get_goal = partial(_get_mujoco_item_control,
                                pam_mujoco_wrp.MujocoItemTypes.goal)

            mujoco_item_controls = []
            if balls:
                logging.info("creating item controls for {} balls".format(
                    len(balls)))
                mujoco_item_controls.extend([
                    _get_ball(mujoco_item, model_item)
                    for mujoco_item, model_item in zip(
                        balls, items["balls"][:len(balls)])
                ])

            if hit_points:
                logging.info("creating item controls for {} hit points".format(
                    len(hit_points)))
                mujoco_item_controls.extend([
                    _get_hit_point(mujoco_item, model_item)
                    for mujoco_item, model_item in zip(
                        hit_points, items["hit_points"][:len(hit_points)])
                ])

            if goals:
                logging.info("creating item controls for {} goals".format(
                    len(goals)))
                mujoco_item_controls.extend([
                    _get_goal(mujoco_item, model_item)
                    for mujoco_item, model_item in zip(
                        goals, items["goals"][:len(goals)])
                ])

            for mujoco_item_control in mujoco_item_controls:
                config.add_control(mujoco_item_control)

            if combined:

                logging.info("creating item controls for combined items")

                mujoco_combined_items_control = _get_mujoco_items_control(
                    combined,
                    items["balls"][len(balls):],
                    items["goals"][len(goals):],
                    items["hit_points"][len(hit_points):],
                    items["robot1"].geom_racket,
                )

                # function name, depending on the number of combined mujoco items.
                # e.g. add_3_control or add_10_control, see
                # include/mujoco_config.hpp and/or srcpy/wrappers.cpp
                add_function_name = "_".join(
                    ["add", str(combined.size), "control"])

                # pointer to the function
                add_function = getattr(config, add_function_name)
                # calling the function
                add_function(mujoco_combined_items_control)

            for key, robot in zip(("robot1", "robot2"), (robot1, robot2)):
                if robot:
                    logging.info("creating item controls for {}".format(key))
                    r = _get_mujoco_robot_control(robot, items[key])
                    if r:
                        config.add_control(r)

            # waiting for pam_mujoco to have created the shared memory segment_id

            logging.info("waiting for pam_mujoco {}".format(mujoco_id))
            created = False
            while not created:
                created = shared_memory.wait_for_segment(mujoco_id, 100)

            # writing the mujoco config in the shared memory.
            # the mujoco executable is expected to read it and start

            logging.info(
                "sharing mujoco configuration for {}".format(mujoco_id))

            pam_mujoco_wrp.set_mujoco_config(config)

        # waiting for pam_mujoco to have created the shared memory segment_id

        logging.info("waiting for pam_mujoco {}".format(mujoco_id))
        created = False
        while not created:
            created = shared_memory.wait_for_segment(mujoco_id, 100)

        # waiting for mujoco to report it is ready

        logging.info("waiting for mujoco executable {}".format(mujoco_id))

        pam_mujoco_wrp.wait_for_mujoco(mujoco_id)

        # if read only, we did not create the mujoco configuration,
        # (which has been written by another process)
        # so we read it from the shared memory

        if read_only:

            config = pam_mujoco_wrp.get_mujoco_config(mujoco_id)
            balls, goals, hit_points = [], [], []
            for item in config.item_controls:
                if item.active_only:
                    control = MujocoItem.COMMAND_ACTIVE_CONTROL
                else:
                    control = MujocoItem.CONSTANT_CONTROL
                instance = MujocoItem(item.segment_id, control=control)
                if item.type == pam_mujoco_wrp.MujocoItemTypes.ball:
                    balls.append(instance)
                elif item.type == pam_mujoco_wrp.MujocoItemTypes.goal:
                    goals.append(instance)
                else:
                    hit_points.append(instance)
            robots = []
            for joint_robot in config.joint_controls:
                r = MujocoRobot(
                    True,  # has no effect, because no xml config file is written
                    joint_robot.segment_id,
                    control=MujocoRobot.JOINT_CONTROL,
                )
                robots.append(r)
            for pressure_robot in config.pressure_controls:
                r = MujocoRobot(
                    True,  # has no effect, because no xml config file is written
                    joint_robot.segment_id,
                    control=MujocoRobot.PRESSURE_CONTROL,
                )
                robots.append(r)
            try:
                robot1 = robots[0]
            except Exception:
                robot1 = None
            try:
                robot2 = robots[1]
            except Exception:
                robot2 = None

            class _Combined:
                size = 0
                segment_id = None

            combined = None
            item_controls_attrs = [(nb_balls,
                                    "item_{}_controls".format(nb_balls))
                                   for nb_balls in (3, 10, 20, 50, 100)]
            for (nb_balls, attr) in item_controls_attrs:
                mujoco_items_control_instance = getattr(config, attr)
                if mujoco_items_control_instance:
                    combined = _Combined
                    combined.size = nb_balls
                    combined.segment_id = mujoco_items_control_instance[
                        0].segment_id
                    break

        # if bursting mode, creating a burster client
        if burst_mode:
            self._burster_client = o80.BursterClient(mujoco_id)
        else:
            self._burster_client = None

        # creating o80 frontends

        self.frontends = {}
        self.interfaces = {}

        for item in balls:
            if item.control != MujocoItem.NO_CONTROL:
                logging.info("creating o80 frontend for ball {} /  {}".format(
                    mujoco_id, item.segment_id))
                frontend = o80_pam.BallFrontEnd(item.segment_id)
                interface = o80_pam.o80Ball(item.segment_id, frontend)
                self.frontends[item.segment_id] = frontend
                self.interfaces[item.segment_id] = interface

        for item in goals:
            if item.control != MujocoItem.NO_CONTROL:
                logging.info("creating o80 frontend for goal {} /  {}".format(
                    mujoco_id, item.segment_id))
                frontend = o80_pam.BallFrontEnd(item.segment_id)
                interface = o80_pam.o80Goal(item.segment_id, frontend)
                self.frontends[item.segment_id] = frontend
                self.interfaces[item.segment_id] = interface

        for item in hit_points:
            if item.control != MujocoItem.NO_CONTROL:
                logging.info(
                    "creating o80 frontend for hit point {} /  {}".format(
                        mujoco_id, item.segment_id))
                frontend = o80_pam.BallFrontEnd(item.segment_id)
                interface = o80_pam.o80HitPoint(item.segment_id, frontend)
                self.frontends[item.segment_id] = frontend
                self.interfaces[item.segment_id] = interface

        for robot in robot1, robot2:
            if robot:
                if robot.control == MujocoRobot.JOINT_CONTROL:
                    logging.info(
                        "creating o80 frontend for joint control of {} /  {}".
                        format(mujoco_id, robot.segment_id))
                    frontend = o80_pam.JointFrontEnd(robot.segment_id)
                    interface = o80_pam.o80RobotMirroring(
                        robot.segment_id,
                        frontend=frontend,
                        burster=self._burster_client,
                    )
                    self.frontends[robot.segment_id] = frontend
                    self.interfaces[robot.segment_id] = interface
                if robot.control == MujocoRobot.PRESSURE_CONTROL:
                    logging.info(
                        "creating o80 frontend for pressure control of {} /  {}"
                        .format(mujoco_id, robot.segment_id))
                    frontend = o80_pam.FrontEnd(robot.segment_id)
                    interface = o80_pam.o80Pressures(
                        robot.segment_id,
                        frontend=frontend,
                        burster=self._burster_client,
                    )
                    self.frontends[robot.segment_id] = frontend
                    self.interfaces[robot.segment_id] = interface

        if combined:
            self.frontends[
                combined.segment_id] = self.get_extra_balls_frontend(
                    combined.segment_id, combined.size)

        # for tracking contact
        self.contacts = {}

        for item in list(balls) + list(goals) + list(hit_points):
            if item.contact_type == pam_mujoco_wrp.ContactTypes.table:
                self.contacts[item.segment_id] = item.segment_id + "_table"
            if item.contact_type == pam_mujoco_wrp.ContactTypes.racket1:
                self.contacts[item.segment_id] = item.segment_id + "_racket1"
            if item.contact_type == pam_mujoco_wrp.ContactTypes.racket2:
                self.contacts[item.segment_id] = item.segment_id + "_racket2"

        if combined and not read_only:
            # see src/add_controllers.cpp, function add_items_control
            for index, item in enumerate(list(combined.iterate())):
                if item.contact_type == pam_mujoco_wrp.ContactTypes.table:
                    self.contacts[item.segment_id] = (combined.segment_id +
                                                      "_table_" + str(index))
                if item.contact_type == pam_mujoco_wrp.ContactTypes.racket1:
                    self.contacts[item.segment_id] = (combined.segment_id +
                                                      "_racket1_" + str(index))
                if item.contact_type == pam_mujoco_wrp.ContactTypes.racket2:
                    self.contacts[item.segment_id] = (combined.segment_id +
                                                      "_racket2_" + str(index))

        for sid in self.contacts.keys():
            self.reset_contact(sid)

        logging.info("handle for mujoco {} created".format(mujoco_id))