Package: yarok
YAROK - Yet another robot frameworkIs aPythonlibrary for interfacing with sensors and actuators, and composing robots and robotic environments.
Aimed for quick prototyping, learning, researching, etc. Simulation first (MuJoCo).It can be seen as a minimalistic alternative to ROS: Python-centric, OS independent and single process.It is centered around modularity for reusability:
each component is self-contained with a template, an environment-independent
controller Python class and interfaces for handling each environmnent (MuJoCo or real).
It supports nesting an existing component onto another e.g. a gripper onto a robot arm.Checkout theDemorepository, and start coding your next project.Installpip3 install yarokrun an example world and behaviour,# test.py
    
    from yarok import Platform
    from yarok.examples.grasp_rope_world import GraspRopeWorld, GraspRopeWorld
    
    Platform.create({
        'world': GraspRopeWorld,
        'behaviour': GraspRoleBehaviour
    }).run()python -m testTry other worldsNameDescriptionEmptyWorldJust an empty world. Can be used as the base for other worlds.DigitWorldA Pick and Place with a UR5 arm, Robotiq gripper, and the DIGIT tactile sensorGelTipWorldA UR5 arm and Robotiq 2 finger gripper, equipped with GelTip sensors, pulling a ropeOther projects using YarokIf you are using yarok, let us know.NameDescriptionGelTipSimGelTip simulation model projectModoVT(WIP) Manipulation of Deformable Objects using vision and TouchAtWork(WIP) Playground for experimenting with manipulation and mobile robots, featRoboCup@WorkExample componentsExample components are included in this package to reduce any friction with getting started with the library, if
you would like us to include/reference yours let us know.You can import these components into your world/component as follows. (see the example worlds)from yarok.comm.components.abc_xyz.abc_xyz import AbcXyz
    
    @component(
        ...
        components=[
            AbcXyz
        ],
        template="""
            <mujoco>
                <worldbody>
                    <abc-xyz />            
        """SensorsNameDescriptionCamWraps the py_mujoco and OpenCV APIs for handling generic cameras.DigitDIGIT optical tactile sensor,Lambeta et al.. Simulation model and assets ported fromZhao et. al.GelSight2014(WIP) tactile sensor as proposed inRui Li et al.and the simulation model proposed inGomes et al.GelTip(WIP) Finger-shaped tactile sensor,Gomes et al., and simulation modelGelTip SimulationActuatorsNameDescriptionAnetA30FFF 3D printer modeled after the Geeetech ANET A30 (or Creality CR-10) to be used as 3 DoF a cartesian actuator. Real hardware viaserial(USB).UR5The 6 DoFUniversal Robots UR5. Real hardware viapython-urxlibrary.Robotiq2f85TheRobotiq 2F-85 gripper. Real hardware viapython-urxand the UR5 computer.Write your first ComponentComponentscan be used to represent robot parts (sensors or actuators), robots, or an entire world.from yarok import component, Injector
from yarok.comm.worlds.empty_world import EmptyWorld

@component(
    tag='some-component',
    extends=EmptyWorld,
    components=[
        OneSubcomponent,
        AnotherSubcomponent
    ],
    defaults={
      'interface_mjc': SomeComponentInterfaceMJC,
      'interface_hw': SomeComponentInterfaceHW,
      'a_config: 'a_value'
    },
    # language=xml
    template="""
    <mujoco>
       <worldbody>
          <body> .... </body>
          <one-subcomponent name='sub'>
             <another-subcomponent name='subsub' parent='body-in-parent'/>
             ...
          </one-subcomponent>
       </worldbody>
       <actuator> ... </actuator>
       <sensor> ..  <sensor>
   </mujoco>
    """
)
class SomeComponent

    def __init__(sub: OneSubcomponent, injector: Injector):
        self.sub = sub
        self.subsub = injector.get('subsub')
    
    def do_something_else(args):
        this.sub.do_it()
        this.subsub.do_it_also()
    
    def do_something_else(pos):
        # implemented by the interface
        passWhat to know when coding aComponent decoratorandclassNameDescriptiontagTo be used as the tag in the .xml templates.extendsReferences the parent template. This component body is appended to the "parent" body.ComponentsReferences to the classes/components to be used as subcomponents in this component.DefaultsConfigs that can be referenced in the template using{conf}, and can be (re)configured when launching the platform.InterfacesMuJoCointerface_mjcor Real Hardwareinterface_hwinterfaces, used to interface with the real/sim platforms. Before instantiating the component, Yarok merges the appropriate environment interface with the component class, effectively becoming one. Thus, the component class should declare the interface(s) common public methods (api) as empty/pass for static typing.TemplateThe component is described in .xml, using the MJCF format (from MuJoCo), with aditional options for component composition and passing variables. At runtime, Yarok handles the nesting of components, renaming / namespacing to avoid name collisions. (# language=xml can be prepended for syntax highlight in PyCharm)ClassThe Python class used to interface with the component programmatically.ConstructorYou can "request" the reference of the instantiated (sub)components in the template, by addingname: ComponentClassto the constructor arguments. You can also request the injectorinjector: Injectorand then callinjector.get(name).Using a component within another component<another-subcomponent name='subsub' parent='body-in-parent'/>NameDescriptiontagnameThe name of the component, declared in the component decoratortag@nameThe name/id of the component instance.@parent(optional) When sub-nesting (check example) you can use the @parent attribute to reference the parent body in the parent component, wherein the subcomponent will be nested. Make sure that a body with such name existis in the parent component.Eval<geom pos="
            {0.5 + 0.082*x if z % 2 == 0 else 0.58} 
            {0.48 if z % 2 == 0 else 0.4 + 0.082*x}
            {0.061*z}" />You can use{}(${}is deprecated) to have python expressions evaluated within the template.
You can use variables here, from the config/defaults or the tree context.If block and attribute<if the='x == 4'>
  ...
</for>

<geom if="y == 2" .../>The if block / attribute. You can use variables here.For loop<for each='range(n_elements)' as='i'>
  ...
</for>eacha python expression that returns an iterable, you can use variables here.asis the name of the iterable. You'll be able to use it in the evals/ifs/fors down the chain.usage example⚠ All paths for meshes are relative to the component directory.Here we described the extensions on top of the MJCF language, for the full reference check
theMuJoCo XML.Interface with Sim or Real sensors or actuatorsTo directly interact with sensors or actuators, you should write an Interface.The MuJoCo Interfacefrom yarok.mjc.interface import InterfaceMJC

@interface(
  defaults={
    'some_config': 'some_value'
  }
)
class SomeComponentInterfaceMJC:
    
    def __init__(self, interface: InterfaceMJC):
        self.interface = inteface
        self.pos = 0

    def step():
        self.interface.set_ctrl(0, self.pos)
        
    def do_something_else(pos):
        self.pos = posNameDescriptionconstructorReceives as argument an instance ofInterfaceMJCto be used to interface with MuJoCo.step()(optional) This method is called every update step of the simulation, and can be used to read from or update the simulation state.InterfaceMJC useful methods:NameDescriptionset_ctrl(k,v)Sets the control of an actuator.kis the index or name of the actuator.vis the control value.sensordata()Returns the data from the sensors in the component.read_camera(...)Reads a raw MuJoCo camera specified in the component template.camera_nameis the name of the camera,shape=(480,640) is the size of the returned image (numpy style),depth=Falseandrgb=True` indicate whether you want the rgb and/or depth to be returned. The depth is in meters (and not in z-buffer format, as in the raw mujoco api)Check the InterfaceMJC implementation/APIhere.The Real Hardware Interface(check out existing interfaces for reference.)class SomeComponentInterfaceHW:
    
    def step():
       # call some serial api etc.
       
   def do_something_else(pos):
        self.pos = posRun your world componentIn a "write once run everywhere" fashion, Yarok allows you to setup platform specific or platform shared configurations.Config example / schema.conf = {
    'world': SomeWorld,    # class ref of the world/root component
    'behaviour': GraspRoleBehaviour,
    'environment': 'sim',
    'defaults': {
        'behaviour': {
            'conf1': 'value'
            ...
        },
        # configs to be passed to the component constructor,
        # and/or template
        'components': {
            # path to the component, in the tree of components. 
            # '/' would be the root component / world
            '/': { 
                'confx': 'valuex'
            },
            # sub component named 'sub' in the root component.
            '/sub': {
                'confy': 'valuey'
            },
            # and so forth.. (subsub inside sub)
            '/sub/subsub': {
                'confz': 'valuez'
            }
        },
        # pluggins that can be called at every step of the control loop
        'plugins': [
            Cv2Inspector(),
        ]
    },
    # environment specific configurations.
    # These are choosen depending on the 'environment' setting above and override the defaults.
    'environments': {
        'sim': {
            'platform': {
                'class': PlatformMJC
            },
            'behaviour': {
                'where_iam': 'In Simulation !'
            },
            'interfaces': {
                 '/sub/subsub': {
                     'serial_path': '/dev/ttyUSB0'
                 },
            }
        },
        'real': {
            'platform': PlatformHW,
            'behaviour': {
                'where_iam': 'In The Real World !'
            },
            'interfaces': {
                 '/sub/subsub': {
                     'serial_path': '/dev/ttyUSB0'
                 },
            }
        }
    },
}Platform.create(conf).run()Let's make your robot moveTo make your robot do something useful, and keep it separate from world code, you should write a
behaviour class. In the constructor you can request the same (sub)components as in the World component.from yarok import Platform

class SomeBehaviour:
    
    def __init__(self, robot: SomeRobot, pl: Platform):
        self.robot = robot
        self.pl = pl
        
    def on_start():
        pass
                
    def on_update():
        self.robot.go(100)
        self.pl.wait(lambda: self.robot.at(100))
        
        self.robot.go(10)
        self.pl.wait(lambda: self.robot.at(10))Yarok is single threaded, therefore if you write a loop to wait for anything/seconds, you'll lock the running loop,
stopping the simulation and thestep()calls.
Therefore, when your Behaviour needs to wait for something you should useNameDescriptionwait(fn, cb)waits untilfnreturnsTrue.cbcan be used to do stuff every step, such as logging/plotting imageswait_seconds(s, cb)waitsssecondswait_forever(cb)wait_once()PluginsPlugins can be used to extend yarok further with everything "meta".
Current pluginsNameDescriptionCV2WaitKeyCallscv2.waitKey(1)every step of the control loop, so that you can usecv2.imshowin your components/behaviours.CV2InspectorIterates through all components and callsprobe(component)method declared in the component defaults/config. The returned data is displayed as OpenCV frames (when using CV2Inspector, you don't need to use CV2WaitKey)A Plugin, can be any Python class that implements astep()method.Todo listHandle/rename MuJoCo defaults / classesBehaviour to each component vs single behaviour. (Problems with platform.wait?)Add probe to interface decoratorHandle compiler optionsAdd support for mujoco's native includeQuick bugfixesFor:libGL error: MESA-LOADER: failed to open swrast: /usr/lib/dri/swrast_dri.so: cannot open shared object fileCheck where's your libstdc++.so.6:find / -name libstdc++.so.6 2>/dev/nullThen:echo 'export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libstdc++.so.6' > ~/.bashrcstackoverflowIf having conflicts after installingcv2, comment import imageioOn MuJoCo 1.0 (pre deepmind aquisition), the following
exportexport LD_PRELOAD='/usr/lib/x86_64-linux-gnu/libGLEW.so'was used to successfully have the simulation
running with the GPU.