class MoveDorna:
    def __init__(self):
        path = os.path.dirname(os.path.abspath(sys.argv[0]))
        path = path + "/config/dorna.yaml"
        # Objekt der Dorna-Klasse instanziieren
        self.robot = Dorna(path)
        # Verbindung mit dem Roboter herstellen
        self.robot.connect()

    # Homing-Prozess
    def home(self):
        proceed = False

        while not proceed:
            _input = input("Home Dorna? (y/n)")

            if _input == "y":
                proceed = True

            elif _input == "n":
                self.terminate()
                break

        if proceed:
            # Dorna in die Home-Position fahren
            self.robot.home(["j0", "j1", "j2", "j3"])
            print(self.robot.homed())
            print(self.robot.position())
            proceed = False

            while not proceed:
                _input = input("j3 und j4 auf 0 setzen? (y/n)")

                if _input == "y":
                    proceed = True

                elif _input == "n":
                    break

            if proceed:
                # Gelenke des Endeffektors nullen
                self.set_joint_j3_0()

    def set_joint_j3_0(self):
        self.robot.set_joint({"j3": 0})
        self.robot.set_joint({"j4": 0})

    # Endeffektor zu einer bestimmten Koordinate fahren
    def move_to_position(self):
        # Koordinate eingeben
        i = Input()
        output = i.input_values()
        x = output.get("x")
        y = output.get("y")
        z = output.get("z")
        # Koordinate berechnen
        position = JointValue(x, y, z)
        position_result = position.calc_joint_values()
        position_array = self.get_joints(position_result)

        print(position_array)
        proceed = True

        while proceed:
            _input = input("Bewegung ausführen? (y/n)")

            if _input == "n":
                proceed = False

            elif _input == "y":
                break

        if proceed:
            # Bewegung ausführen
            self.move_dorna(position_array)

    # Ablaufsteuerung zum Bälle einsammeln ausführen
    def collect_balls(self):
        n = eval(input("Anzahl der Bälle: "))
        balls_array = []
        out_of_range = False
        # Koordinaten der Bälle eingeben
        for j in range(n):
            print("Position des", j + 1, ". Balls:")
            i = Input()
            output = i.input_values()
            x = output.get("x")
            y = output.get("y")
            z = output.get("z")
            # Werte der Gelenke berechnen
            seven_above_ball = JointValue(x, y, z + 7)  # 7cm über dem Ball
            five_above_ball = JointValue(x, y, z + 5)   # 5cm über dem Ball
            one_above_ball = JointValue(x, y, z + 1)    # 1cm über dem Ball

            seven_above_ball_result = seven_above_ball.calc_joint_values()
            five_above_ball_result = five_above_ball.calc_joint_values()
            one_above_ball_result = one_above_ball.calc_joint_values()

            if five_above_ball == 1 or one_above_ball == 1:
                out_of_range = True
                break

            seven_above_ball_array = self.get_joints(seven_above_ball_result)
            five_above_ball_array = self.get_joints(five_above_ball_result)
            one_above_ball_array = self.get_joints(one_above_ball_result)

            balls_dict = {"1.": five_above_ball_array, "2.": one_above_ball_array, "3.": seven_above_ball_array}

            balls_array.append(balls_dict)

        print("Position des Behälters: ")
        # Koordinate des Behälters angeben
        i = Input()
        output = i.input_values()
        x = output.get("x")
        y = output.get("y")
        z = output.get("z")
        # Werte der Gelenke berechnen
        fifteen_above_container = JointValue(x, y, z + 15)  # 15cm über dem Behälter
        fifteen_above_container_result = fifteen_above_container.calc_joint_values()

        if five_above_ball == 1 or one_above_ball == 1:
            out_of_range = True

        fifteen_above_container_array = self.get_joints(fifteen_above_container_result)

        print(balls_array)
        balls_array_len = len(balls_array)

        if not out_of_range:
            for x in range(balls_array_len):
                print(balls_array[x])
            print(fifteen_above_container_array)
            proceed = True

            while proceed:
                _input = input("Bewegung ausführen? (y/n)")

                if _input == "n":
                    proceed = False

                elif _input == "y":
                    break

            if proceed:
                for i in range(n):
                    first = balls_array[i].get("1.")
                    second = balls_array[i].get("2.")
                    third = balls_array[i].get("3.")
                    # Ablaufsteuerung
                    self.open_gripper()
                    self.move_dorna(first)
                    self.move_dorna(second)
                    self.close_gripper()
                    self.move_dorna(third)
                    self.move_dorna(fifteen_above_container_array)
                    self.open_gripper()

                print("Bewegung ausgeführt.")

        else:
            print("Bewegung konnte nicht ausgeführt werden")

    def terminate(self):
        self.robot.terminate()

    def close_gripper(self):
        self.robot.set_io({"servo": 700})

    def open_gripper(self):
        self.robot.set_io({"servo": 0})

    def get_joints(self, height):
        j0 = height.get("j0")
        j1 = height.get("j1")
        j2 = height.get("j2")
        j3 = height.get("j3")

        return [j0, j1, j2, j3, 0]

    def move_dorna(self, position):
        self.robot.move({"movement": 0, "path": "joint", "joint": position})

    # Nullposition
    def zero(self):
        self.robot.move({"movement": 0, "path": "joint", "joint": [0, 0, 0, 0, 0]})
Esempio n. 2
0
class DornaRobot:
    def __init__(self):
        self._config_path = os.path.dirname(
            os.path.realpath(__file__)) + str("/config.yaml")
        self.params = list(yaml.safe_load(open(self._config_path, 'r')).keys())
        self.joint_names = ['j0', 'j1', 'j2', 'j3', 'j4']
        self.axis_names = ['x', 'y', 'z', 'a', 'b']

        self._robot = Dorna(self._config_path)

        self._default_home = dict(zip(self.joint_names, [0, 145, -90, 0, 0]))
        self.connection_status = None
        self._elevator_demo = False
        self._hand = True
        self.triggered = False

    def init(self, home=True):
        '''
        Connect to the robot and then home or set the stepper motors
        '''
        try:
            self.connect()
        except ConnectionException:
            logger.error(
                "ConnectionError [dorna_robot]: There was a connection issue. Check Robot and try again"
            )
            raise
        else:
            if home:
                self.home()
            else:
                self.set_joint()
            logger.info("Robot startup success")

################################
#### Command calls to robot ####
################################

    def calibrate(self):
        '''
        Set angle offsets for homeing 
        '''
        joints = self.get_joint_angles()
        self._robot.calibrate(joints)
        self._robot.save_config(save_path=self._config_path)
        self.set_joint()

    def connect(self, usb=None):
        '''
        Connects to Dorna\n
        Note: Make sure to setup dorna udev rule
        '''
        logger.info('Connecting to Dorna...')

        if not usb:
            usb = "/dev/actuators/dorna"

        response = json.loads(self._robot.connect(port_name=usb))
        self.connection_status = self.get_connection_status()

        while response["connection"] == DornaConnection.CONNECTING:
            time.sleep(0.5)
            response = json.loads(self._robot.device())

        # if response["connection"] == DornaConnection.CONNECTED:

        if response["connection"] == DornaConnection.DISCONNECTED:
            logger.error("ConnectionError")
            logger.info(
                "There was a connection issue. Check Robot and try again")
            raise ConnectionException()

    def disconnect(self):
        '''
        Disconects from Arduino
        '''
        self._robot.disconnect()

    def halt(self):
        '''
        Stops Dorna and delets queue
        '''
        self._robot.halt()

    def force_fulfill(self, joint, desired_pose):
        '''
        Waits until joint angle equal to desired pose\n
        Note: only works for single joint and angle
        '''
        # TODO: Make this better
        not_in_position = True
        while not_in_position:
            joint_angles = self.get_joint_angles()
            if int(joint_angles[joint]) == desired_pose:
                not_in_position = False

    def force_fulfill_joints(self, desired):
        not_in_position = True
        while not_in_position and not self.triggered:
            current = self.get_joint_angles()
            check = np.linalg.norm(
                np.diff(np.row_stack((current, desired)), axis=0))
            not_in_position = check >= 0.1

    def force_fulfill_xyzab(self, desired):
        not_in_position = True
        while not_in_position and not self.triggered:
            current = self.get_cartesian_position()
            check = np.linalg.norm(
                np.diff(np.row_stack((current, desired)), axis=0))
            not_in_position = check >= 0.1

    def elevator_demo_safe_home(self, i):
        '''
        Turn the base 90 degrees before calibrating next motor\n 
        Then move base back to origin
        '''
        if i == 1:
            self.move_joints(path="joint",
                             movement=DornaMovement.GLOBAL.value,
                             speed=DornaMovement.DEFAULT_JOINT_SPEED.value,
                             joint_angles={'j0': -90})

            self.force_fulfill(0, -90)
        elif i == 2:
            self.move_joints(path="joint",
                             movement=DornaMovement.GLOBAL.value,
                             speed=DornaMovement.DEFAULT_JOINT_SPEED.value,
                             joint_angles={'j0': 0})
            self.force_fulfill(0, 0)

    def home(self):
        '''
        Calibrate stepper motors
        '''
        logger.info('Homing Robot...')
        try:
            ## Only homing joints 0-2 and setting joints 3-4 so that tool head does not colide with robot
            if self._hand:
                n = 3
                self.set_joint({"j3": 0, "j4": 0})
            else:
                n = len(self.joint_names)

            for i in range(n):
                if self._elevator_demo:
                    self.elevator_demo_safe_home(i)

                motor_name = "j" + str(i)
                response = json.loads(self._robot.home(motor_name))
                if response[motor_name] != 1:
                    raise HomingException()

            logger.info('Finished Homing Robot...')
        except HomingException:
            logger.error(
                'HomingError [dorna_robot]: Motor {} was unable to home'.
                format(i))

    def jog(self, path, cmd_dict):
        '''
        Jogs single motor or robot in single direction\n
        path = 'joint' or 'line'\n
        cmd_dict = dictionary of joint_name or axis_name as keys and angle or position as values
        '''
        logger.info('Jogging...')
        if path == "joint":
            speed = DornaMovement.DEFAULT_JOINT_SPEED
        elif path == "line":
            speed = DornaMovement.DEFAULT_XYZ_SPEED

        if list(cmd_dict.keys())[0] in self.joint_names:
            self.move_joints(path=path,
                             movement=DornaMovement.RELATIVE,
                             speed=speed,
                             joint_angles=cmd_dict)

        elif list(cmd_dict.keys())[0] in self.axis_names:
            self.move_xyzab(path=path,
                            movement=DornaMovement.RELATIVE,
                            speed=speed,
                            xyzab=cmd_dict)

    def move_xyzab(self, path, movement, speed, xyzab, fulfill=True):
        '''
        path = 'joint' or 'line'\n
        movement = relative or global\n
        speed = units/second\n
        xyzab = can be a list of all positions or a dict of single direction
        '''
        try:
            if type(xyzab) == list and len(xyzab) == 5:
                cmd_dict = dict(zip(self.axis_names, xyzab))
            elif type(xyzab) == dict:
                cmd_dict = xyzab
                pass
            else:
                raise TypeError
        except:
            raise

        parameters = dorna_cmd_utils.get_move_msg(path=path,
                                                  movement=movement,
                                                  speed=speed,
                                                  cmd_dict=cmd_dict)

        cmd = dorna_cmd_utils.get_play_msg(command="move",
                                           parameters=parameters,
                                           fulfill=fulfill)
        logger.info(cmd)

        self.play(cmd)

    def move_joints(self, path, movement, speed, joint_angles, fulfill=True):
        '''
        path = 'joint' or 'line'\n
        movement = relative or global\n
        speed = units/second\n
        joint_angles = can be a list of all angles or a dict of some angles
        '''
        try:
            if type(joint_angles) == list and len(joint_angles) == 5:
                cmd_dict = dict(zip(self.joint_names, joint_angles))
                # print(cmd_dict)
            elif type(joint_angles) == dict:
                cmd_dict = joint_angles
            else:
                raise TypeError
        except:
            raise

        parameters = dorna_cmd_utils.get_move_msg(path=path,
                                                  movement=movement,
                                                  speed=speed,
                                                  cmd_dict=cmd_dict)

        cmd = dorna_cmd_utils.get_play_msg(command="move",
                                           parameters=parameters,
                                           fulfill=fulfill)
        logger.info(cmd)

        self.play(cmd)

    def move_to_home(self):
        '''
        Moves Dorna to default home position at default speed
        '''
        self.move_joints(path="joint",
                         movement=DornaMovement.GLOBAL,
                         speed=DornaMovement.DEFAULT_JOINT_SPEED,
                         joint_angles=self._default_home)

    def move_to_zeropose(self):
        '''
        Moves Dorna to joint angles all 0
        '''
        cmd_dict = dict.fromkeys(self.joint_names, 0)
        self.move_joints(path="joint",
                         movement=DornaMovement.GLOBAL,
                         speed=DornaMovement.DEFAULT_JOINT_SPEED,
                         joint_angles=cmd_dict)

    def pause(self):
        '''
        Pauses Dorna command queue
        '''
        self._robot.pause()

    def play(self, path):
        '''
        Adds path to command queue
        '''
        logger.info('Playing Path...')

        try:
            self._robot.play(path, append=True)
        except KeyboardInterrupt:
            self.halt()
            pass

    def set_joint(self, joint_dict=None):
        '''
        Sets values for motor angle
        '''
        if joint_dict:
            self._robot.set_joint(joint_dict)
        else:
            logger.debug(self._default_home)
            self._robot.set_joint(self._default_home)

    def terminate(self):
        '''
        Kills threads in Dorna 
        '''
        self._robot.terminate()

    def xyz_to_joint(self, xyzab):
        '''
        Checks if xyz position is in workspace\n
        If true then returns inverse kinematics
        '''
        if self._robot._config["unit"]["length"] == "mm":
            xyzab[0:3] = [self._robot._mm_to_inch(xyz) for xyz in xyzab[0:3]]

        joint_response_dict = self._robot._xyz_to_joint(np.asarray(xyzab))

        if joint_response_dict['status'] == 0:
            logger.info("Can achieve xyz position and safe")
            return joint_response_dict['joint']
        elif joint_response_dict['status'] == 1:
            logger.info(
                "Can achieve xyz position but NOT safe, USE WITH CAUTION")
            return joint_response_dict['joint']
        elif joint_response_dict['status'] == 2:
            logger.error("Cannot achieve xyz position")
            return None


####################################
#### Get information from robot ####
####################################

    def get_cartesian_position(self):
        '''
        Get cartesian postion of end effector\n
        Return in units[mm, inch] set by params
        '''
        pos = json.loads(self._robot.position("xyz"))

        return pos

    def get_connection_status(self):
        '''
        Get connection statues
        '''
        status = json.loads(self._robot.device())
        return status["connection"] == DornaConnection.CONNECTED

    def get_homed_status(self):
        '''
        Returns if stepper motors calibrated
        '''
        homed = json.loads(self._robot.homed())

        return homed

    def get_joint_angles(self, units='deg'):
        '''
        Returns motor joint angles
        '''
        joints = json.loads(self._robot.position("joint"))
        if units == 'rad':
            joints = [joint * np.pi / 180. for joint in joints]

        return joints

    def get_state(self):
        '''
        Returns if robot is moving
        '''
        state = json.loads(self._robot.device())['state']

        return state

    def get_param(self, param_name):
        '''
        Gets value of param from param_list
        '''
        if param_name in self.params:
            if param_name == 'calibrate':
                logger.warn('Dorna param calibrate is only a setting function')
                return
            if hasattr(self._robot, param_name):
                call = getattr(self._robot, param_name, None)
                response = json.loads(call())
                # logger.info("Get robot param: "+str(response))
                return response
            else:
                logger.warn(
                    'Dorna does not have method: {}'.format(param_name))
        else:
            logger.warn('[{}] not in param list'.format(param_name))

    def set_param(self, param_name, value):
        '''
        Sets value of param from param_list
        '''
        if param_name in self.params:
            attr = 'set_' + param_name
            if hasattr(self._robot, attr):
                call = getattr(self._robot, attr, None)
                response = call(value)
                logger.info("Set robot param: " + str(response))
            else:
                logger.warn('Dorna does not have method: {}'.format(attr))
        else:
            logger.warn("[{}] not in param list".format(param_name))
Esempio n. 3
0
class Arm(object):
    def __init__(self):
        self.robot = Dorna("./config.yaml")

    def go(self, position=HOME):
        print("Go to ", position)
        tmpcmd = {"command": "move", "prm": position}
        self.robot.play(tmpcmd)

    """============================================================
        CONNECTION AND HOMING THE ROBOT
        ===========================================================
    """

    def connect(self):
        return self.robot.connect()

    def dis(self):
        return self.robot.disconnect()

    def connectAndHomeIfneed(self):

        self.robot.connect()
        self.homeIfneed()

    def connectAndHomes(self):

        self.robot.connect()
        self.robot.home("j0")
        self.robot.home("j1")
        self.robot.home("j2")
        self.robot.home("j3")

    def isConnected(self):
        device = json.loads(r.robot.device())
        if device['connection'] == 2:
            return True
        else:
            return False

    def isHomed(self):
        if not self.isConnected():
            print("Robot is not connected")
            return False

        homed = c
        for key in homed:
            if homed[key] == 1:
                print(key, " is homed")
            else:
                print(key, " is not homed yet")

    def homes(self):
        self.robot.home("j0")
        self.robot.home("j1")
        self.robot.home("j2")
        self.robot.home("j3")

    def homeIfneed(self):
        if not self.isConnected():
            print("Robot is not connected")
            return False
        homed = json.loads(self.robot.homed())
        for key in homed:
            if homed[key] == 1:
                print(key, " is homed")
            else:
                print("Homing ", key)
                self.robot.home(key)

    """===================================================================
    GET CURRENT POSITION AS XYZ SYSTEM
    ==================================================================
    """

    def posXYZ(self):
        return json.loads(self.robot.position('xyz'))

    """===================================================================
        GET CURRENT POSITION AS JOINTS SYSTEM
        ==================================================================
    """

    def pos(self):
        return json.loads(self.robot.position())

    """==============================================
    ADJUST the joints
    =================================================
    """

    #pass in joint and value as parameter ex:adjust("j0",10)
    def adjust(self, joint, degrees: float, relatively=True):
        # creates a position-movement description that can be fed to go().
        tempPath = deepcopy(PRM_TEMPLATE)
        for j in JOINTS:
            if j == joint:

                tempPath[j] = degrees
                break
        tempPath["movement"] = 1 if relatively else 0
        self.go(tempPath)
        return tempPath

    #pass in joints object as parameter ex:adjust({"j1":0,"j2":0})
    def adjustJoints(self, joints, relatively=True):
        # creates a position-movement description that can be fed to go().
        tempPath = deepcopy(PRM_TEMPLATE)
        print(joints)
        for key in joints:
            for validJoint in JOINTS:
                if key == validJoint:
                    tempPath[validJoint] = joints[key]

        tempPath["movement"] = 1 if relatively else 0
        self.go(tempPath)
        return tempPath

    """==================================================
        MOVE with x,y,z Coordinate System
        =================================================
    """

    #pass in axis and value as parameter
    #ex:moveToPosition('z',10)
    def adjustAxis(self, axis, degrees, relatively=True):
        tempPath = deepcopy(PRM_TEMPLATE)
        tempPath['path'] = 'line'

        for c in COORDINATE:
            if c == axis:
                tempPath[c] = degrees
                break
        tempPath["movement"] = 1 if relatively else 0
        self.go(tempPath)
        return tempPath

    """==================================================
        MOVE with xyz Coordinate System
        =================================================
    """

    #pass in coordinate object as parameter
    #ex:moveToCoordinate({'x':10,'y':20,'z':10})
    def adjustCoordinate(self, coor, relatively=True):
        tempPath = deepcopy(PRM_TEMPLATE)
        tempPath['path'] = 'line'

        for key in coor:
            for validJoint in COORDINATE:
                if key == validJoint:
                    tempPath[validJoint] = coor[key]
        tempPath["movement"] = 1 if relatively else 0
        self.go(tempPath)
        return tempPath

    """==================================================
        wait 
        =================================================
    """

    def wait(self, value):
        time.sleep(value)

    def goHome(self):
        self.go(HOME)

    def goFlat(self):
        self.go(FLAT)

    """==================================================
        MOVE tools head relatively to current angle
        =================================================
    """

    def adjustHead(self, angle):
        self.adjust('j3', angle)
        return self.pos()[3]

    """==================================================
        MOVE tools head to specific Angle
        =================================================
    """

    def setHeadAngle(self, angle):
        self.adjust('j3', angle, False)
        return self.pos()[3]

    """==================================================
        MOVE with specific directin
        =================================================
    """

    def move(self, direct: Direction.FORWARD, value: float):
        if direct == Direction.FORWARD:
            self.adjustAxis('x', value)
        if direct == Direction.BACKWARD:
            moveAxis('x', value * -1)
        if direct == Direction.UP:
            self.adjustAxis('z', value)
        if direct == Direction.DOWN:
            self.adjustAxis('z', value * -1)
        if direct == Direction.LEFT:
            self.adjustAxis('y', value)
        if direct == Direction.RIGHT:
            self.adjustAxis('y', value * -1)

    """==================================================
        MOVE move to a coordiation
        ex:moveTo((1,2,6)) x=1,y=2,z6
        =================================================
    """

    def moveTo(self, xyz):
        self.adjustCoordinate({'x': xyz[0], 'y': xyz[1], 'z': xyz[2]}, False)

    """==================================================
        Collection of Motion
    """

    def pickIt(self):
        self.adjustJoints({'j1': 45, 'j2': -90, 'j3': 45, 'j4': 0}, False)
        self.adjustCoordinate({'x': 15, 'y': 0, 'z': 3.5}, False)
        self.wait(10)
        self.adjustCoordinate({'x': 16, 'y': 0, 'z': 10}, False)
        self.adjust('j0', 180)
        self.adjustCoordinate({'x': 4, 'y': 0, 'z': -5})
        self.adjustCoordinate({'x': 0, 'y': 0, 'z': -3})
        self.wait(10)
        self.adjustCoordinate({'x': -5, 'y': 0, 'z': 8})
        self.adjust('j0', -180)
        self.adjustCoordinate({'x': 15, 'y': 0, 'z': 3.5}, False)
        self.wait(10)
        self.go()

    def preHit(self):
        self.adjustJoints({
            'j0': 0,
            'j1': 45,
            'j2': -123,
            'j3': 78,
            'j4': 0
        }, False)
        self.adjustCoordinate({'x': -10, 'y': 0, 'z': 0})
        self.adjustCoordinate({'x': 60, 'y': 0, 'z': 0})

    def hit(self):
        self.adjustCoordinate({'x': 1, 'y': 0, 'z': 0})
        for i in [0, 1, 2, 3, 4]:
            self.adjustCoordinate({'x': -60, 'y': 0, 'z': 0})
            self.adjustCoordinate({'x': 60, 'y': 0, 'z': 0})
            self.wait(10)
            self.adjustCoordinate({'x': -10, 'y': 0, 'z': 0})

        self.adjustCoordinate({'x': -10, 'y': 0, 'z': 0})

    def preHitTop(self):
        self.adjustJoints({
            'j0': 0,
            'j1': 45,
            'j2': -123,
            'j3': 78,
            'j4': 0
        }, False)
        self.adjustCoordinate({'x': 0, 'y': 0, 'z': 80})
        self.adjustCoordinate({'x': 80, 'y': 0, 'z': 0, 'a': -90})
        self.adjustCoordinate({'x': 0, 'y': 0, 'z': 14})

    def hitTop(self):
        self.adjustCoordinate({'x': 0, 'y': 0, 'z': -1})
        for i in [0, 1, 2, 3, 4]:
            self.adjustCoordinate({'x': 0, 'y': 0, 'z': 20})
            self.adjustCoordinate({'x': 0, 'y': 0, 'z': -20})
            self.wait(6)

    def prRight(self):
        self.adjustJoints({
            'j0': 0,
            'j1': 45,
            'j2': -123,
            'j3': 78,
            'j4': 0
        }, False)

    def hitRight(self):
        self.adjustCoordinate({'x': 0, 'y': 1, 'z': 0})
        for i in [0, 1, 2, 3, 4]:
            self.adjustCoordinate({'x': 0, 'y': -20, 'z': 0})
            self.adjustCoordinate({'x': 0, 'y': 20, 'z': 0})
            self.wait(6)
Esempio n. 4
0
from dorna import Dorna
import json
import time

# creat Dorna object and connect to the robot
robot = Dorna('./my_config_new.yaml')
robot.connect()

print(robot.homed())
print(robot.position())

print(robot.home("j0"))
print(robot.homed())
print(robot.position())
print(robot.home("j1"))
print(robot.homed())
print(robot.position())
print(robot.home("j2"))
print(robot.homed())
print(robot.position())
print(robot.home("j3"))
print(robot.homed())
print(robot.position())

# pose: pre shelf [-60, 120, -120, -10, 0]

# open servo robot.play({"command": "set_io", "prm": { "servo": 500 }})
# servo 0 fully opened

# pose: at shelf [-63, 85, -95, -10, 0]