diff --git a/python/example_robot_data/robots_loader.py b/python/example_robot_data/robots_loader.py
index 61b537be9eebe911d26eec9bcbc6ded834ecfafa..60752adb87d07f8953c2d9027259b3f30cc624ab 100644
--- a/python/example_robot_data/robots_loader.py
+++ b/python/example_robot_data/robots_loader.py
@@ -9,6 +9,10 @@ from pinocchio.robot_wrapper import RobotWrapper
 pin.switchToNumpyArray()
 
 
+def _depr_msg(deprecated, key):
+    return "`%s` is deprecated. Please use `load('%s')`" % (deprecated, key)
+
+
 def getModelPath(subpath, printmsg=False):
     source = dirname(dirname(dirname(__file__)))  # top level source directory
     paths = [
@@ -42,75 +46,97 @@ def readParamsFromSrdf(model, SRDF_PATH, verbose=False, has_rotor_parameters=Tru
     return q0
 
 
-def addFreeFlyerJointLimits(model):
-    ub = model.upperPositionLimit
-    ub[:7] = 1
-    model.upperPositionLimit = ub
-    lb = model.lowerPositionLimit
-    lb[:7] = -1
-    model.lowerPositionLimit = lb
+class RobotLoader(object):
+    path = ''
+    urdf_filename = ''
+    srdf_filename = ''
+    urdf_subpath = 'robots'
+    srdf_subpath = 'srdf'
+    ref_posture = 'half_sitting'
+    has_rotor_parameters = False
+    free_flyer = False
+    verbose = False
+
+    def __init__(self):
+        urdf_path = join(self.path, self.urdf_subpath, self.urdf_filename)
+        self.model_path = getModelPath(urdf_path, self.verbose)
+        self.urdf_path = join(self.model_path, urdf_path)
+        self.robot = RobotWrapper.BuildFromURDF(self.urdf_path, [join(self.model_path, '../..')],
+                                                pin.JointModelFreeFlyer() if self.free_flyer else None)
+
+        if self.srdf_filename:
+            self.srdf_path = join(self.model_path, self.path, self.srdf_subpath, self.srdf_filename)
+            self.q0 = readParamsFromSrdf(self.robot.model, self.srdf_path, self.verbose, self.has_rotor_parameters,
+                                         self.ref_posture)
+        else:
+            self.srdf_path = None
+            self.q0 = None
+
+        if self.free_flyer:
+            self.addFreeFlyerJointLimits()
+
+    def addFreeFlyerJointLimits(self):
+        ub = self.robot.model.upperPositionLimit
+        ub[:7] = 1
+        self.robot.model.upperPositionLimit = ub
+        lb = self.robot.model.lowerPositionLimit
+        lb[:7] = -1
+        self.robot.model.lowerPositionLimit = lb
+
+
+class ANYmalLoader(RobotLoader):
+    path = 'anymal_b_simple_description'
+    urdf_filename = "anymal.urdf"
+    srdf_filename = "anymal.srdf"
+    ref_posture = "standing"
+    free_flyer = True
+
+
+class ANYmalKinovaLoader(ANYmalLoader):
+    urdf_filename = "anymal-kinova.urdf"
+    srdf_filename = "anymal-kinova.srdf"
+    ref_posture = "standing_with_arm_up"
 
 
-def robot_loader(path,
-                 urdf_filename,
-                 srdf_filename=None,
-                 urdf_subpath='robots',
-                 verbose=False,
-                 has_rotor_parameters=False,
-                 ref_posture='half_sitting',
-                 free_flyer=False):
-    """Helper function to load a robot."""
-    urdf_path = join(path, urdf_subpath, urdf_filename)
-    model_path = getModelPath(urdf_path)
-    robot = RobotWrapper.BuildFromURDF(join(model_path, urdf_path), [join(model_path, '../..')],
-                                       pin.JointModelFreeFlyer() if free_flyer else None)
+def loadANYmal(withArm=None):
+    if withArm:
+        warnings.warn(_depr_msg('loadANYmal()', 'anymal'), DeprecationWarning, 2)
+        loader = ANYmalKinovaLoader
+    else:
+        warnings.warn(_depr_msg('loadANYmal(kinova)', 'anymal_kinova'), DeprecationWarning, 2)
+        loader = ANYmalLoader
+    return loader().robot
 
-    if srdf_filename is not None:
-        robot.q0 = readParamsFromSrdf(robot.model, join(model_path, path, 'srdf', srdf_filename), verbose,
-                                      has_rotor_parameters, ref_posture)
-    if free_flyer:
-        addFreeFlyerJointLimits(robot.model)
 
-    return robot
+class TalosLoader(RobotLoader):
+    path = 'talos_data'
+    urdf_filename = "talos_reduced.urdf"
+    srdf_filename = "talos.srdf"
+    free_flyer = True
 
 
-def loadANYmal(withArm=None):
-    if withArm is None:
-        URDF_FILENAME = "anymal.urdf"
-        SRDF_FILENAME = "anymal.srdf"
-        REF_POSTURE = "standing"
-    elif withArm == "kinova":
-        URDF_FILENAME = "anymal-kinova.urdf"
-        SRDF_FILENAME = "anymal-kinova.srdf"
-        REF_POSTURE = "standing_with_arm_up"
-
-    return robot_loader('anymal_b_simple_description',
-                        URDF_FILENAME,
-                        SRDF_FILENAME,
-                        ref_posture=REF_POSTURE,
-                        free_flyer=True)
+class TalosBoxLoader(TalosLoader):
+    urdf_filename = "talos_reduced_box.urdf"
 
 
-def loadTalos(legs=False, arm=False, full=False, box=False):
-    if arm:
-        URDF_FILENAME = "talos_left_arm.urdf"
-    elif full:
-        URDF_FILENAME = "talos_full_v2.urdf"
-        if box:
-            URDF_FILENAME = "talos_full_v2_box.urdf"
-    else:
-        URDF_FILENAME = "talos_reduced.urdf"
-        if box:
-            URDF_FILENAME = "talos_reduced_box.urdf"
-    SRDF_FILENAME = "talos.srdf"
+class TalosFullLoader(TalosLoader):
+    urdf_filename = "talos_full_v2.urdf"
 
-    robot = robot_loader('talos_data', URDF_FILENAME, SRDF_FILENAME, free_flyer=not arm)
 
-    assert (robot.model.armature[:6] == 0.).all()
+class TalosFullBoxLoader(TalosLoader):
+    urdf_filename = "talos_full_v2_box.urdf"
 
-    if legs:
+
+class TalosArmLoader(TalosLoader):
+    urdf_filename = "talos_left_arm.urdf"
+    free_flyer = False
+
+
+class TalosLegsLoader(TalosLoader):
+    def __init__(self):
+        super(TalosLegsLoader, self).__init__()
         legMaxId = 14
-        m1 = robot.model
+        m1 = self.robot.model
         m2 = pin.Model()
         for j, M, name, parent, Y in zip(m1.joints, m1.jointPlacements, m1.names, m1.parents, m1.inertias):
             if j.id < legMaxId:
@@ -139,165 +165,329 @@ def loadTalos(legs=False, arm=False, full=False, box=False):
         effort[:6] = np.inf
         m2.effortLimit = effort
 
-        # q2 = robot.q0[:19]
+        # q2 = self.robot.q0[:19]
         for f in m1.frames:
             if f.parent < legMaxId:
                 m2.addFrame(f)
 
         g2 = pin.GeometryModel()
-        for g in robot.visual_model.geometryObjects:
+        for g in self.robot.visual_model.geometryObjects:
             if g.parentJoint < 14:
                 g2.addGeometryObject(g)
 
-        robot.model = m2
-        robot.data = m2.createData()
-        robot.visual_model = g2
-        # robot.q0=q2
-        robot.visual_data = pin.GeometryData(g2)
+        self.robot.model = m2
+        self.robot.data = m2.createData()
+        self.robot.visual_model = g2
+        # self.robot.q0=q2
+        self.robot.visual_data = pin.GeometryData(g2)
 
         # Load SRDF file
-        robot.q0 = robot.q0[:robot.model.nq]
-        model_path = getModelPath(join('talos_data/robots', URDF_FILENAME))
-        robot.q0 = readParamsFromSrdf(robot.model, join(model_path, 'talos_data/srdf', SRDF_FILENAME), False)
+        self.q0 = readParamsFromSrdf(self.robot.model, self.srdf_path, self.verbose, self.has_rotor_parameters,
+                                     self.ref_posture)
 
         assert (m2.armature[:6] == 0.).all()
         # Add the free-flyer joint limits to the new model
-        addFreeFlyerJointLimits(robot.model)
+        self.addFreeFlyerJointLimits()
 
-    return robot
+
+def loadTalos(legs=False, arm=False, full=False, box=False):
+    if legs:
+        warnings.warn(_depr_msg('loadTalos(legs)', 'talos_legs'), DeprecationWarning, 2)
+        loader = TalosLegsLoader
+    elif arm:
+        warnings.warn(_depr_msg('loadTalos(arm)', 'talos_arm'), DeprecationWarning, 2)
+        loader = TalosArmLoader
+    elif full:
+        if box:
+            warnings.warn(_depr_msg('loadTalos(full, box)', 'talos_full_box'), DeprecationWarning, 2)
+            loader = TalosFullBoxLoader
+        else:
+            warnings.warn(_depr_msg('loadTalos(full)', 'talos_full'), DeprecationWarning, 2)
+            loader = TalosFullLoader
+    else:
+        if box:
+            warnings.warn(_depr_msg('loadTalos(box)', 'talos_box'), DeprecationWarning, 2)
+            loader = TalosBoxLoader
+        else:
+            warnings.warn(_depr_msg('loadTalos()', 'talos'), DeprecationWarning, 2)
+            loader = TalosLoader
+    return loader().robot
 
 
 def loadTalosLegs():
-    warnings.warn("`loadTalosLegs()` is deprecated. Please use `loadTalos(legs=True)`", DeprecationWarning, 2)
+    warnings.warn(_depr_msg('loadTalosLegs()', 'talos_legs'), DeprecationWarning, 2)
     return loadTalos(legs=True)
 
 
 def loadTalosArm():
-    warnings.warn("`loadTalosArm()` is deprecated. Please use `loadTalos(arm=True)`", DeprecationWarning, 2)
+    warnings.warn(_depr_msg('loadTalosArm()', 'talos_arm'), DeprecationWarning, 2)
     return loadTalos(arm=True)
 
 
+class HyQLoader(RobotLoader):
+    path = "hyq_description"
+    urdf_filename = "hyq_no_sensors.urdf"
+    srdf_filename = "hyq.srdf"
+    ref_posture = "standing"
+    free_flyer = True
+
+
 def loadHyQ():
-    return robot_loader('hyq_description', "hyq_no_sensors.urdf", "hyq.srdf", ref_posture="standing", free_flyer=True)
+    warnings.warn(_depr_msg('loadHyQ()', 'hyq'), DeprecationWarning, 2)
+    return HyQLoader().robot
+
+
+class SoloLoader(RobotLoader):
+    path = 'solo_description'
+    urdf_filename = "solo.urdf"
+    srdf_filename = "solo.srdf"
+    ref_posture = "standing"
+    free_flyer = True
+
+
+class Solo12Loader(SoloLoader):
+    urdf_filename = "solo12.urdf"
 
 
 def loadSolo(solo=True):
-    return robot_loader('solo_description',
-                        "solo.urdf" if solo else "solo12.urdf",
-                        "solo.srdf",
-                        ref_posture="standing",
-                        free_flyer=True)
+    warnings.warn(_depr_msg('loadSolo()', 'solo'), DeprecationWarning, 2)
+    loader = SoloLoader if solo else Solo12Loader
+    return loader().robot
+
+
+class KinovaLoader(RobotLoader):
+    path = "kinova_description"
+    urdf_filename = "kinova.urdf"
+    srdf_filename = "kinova.srdf"
+    ref_posture = "arm_up"
 
 
 def loadKinova():
-    return robot_loader('kinova_description', "kinova.urdf", "kinova.srdf", ref_posture="arm_up")
+    warnings.warn(_depr_msg('loadKinova()', 'kinova'), DeprecationWarning, 2)
+    return KinovaLoader().robot
+
+
+class TiagoLoader(RobotLoader):
+    path = "tiago_description"
+    urdf_filename = "tiago.urdf"
+
+
+class TiagoNoHandLoader(TiagoLoader):
+    urdf_filename = "tiago_no_hand.urdf"
 
 
 def loadTiago(hand=True):
-    return robot_loader('tiago_description', "tiago.urdf" if hand else "tiago_no_hand.urdf")
+    if hand:
+        warnings.warn(_depr_msg('loadTiago()', 'tiago'), DeprecationWarning, 2)
+        loader = TiagoLoader
+    else:
+        warnings.warn(_depr_msg('loadTiago(hand=False)', 'tiago_no_hand'), DeprecationWarning, 2)
+        loader = TiagoNoHandLoader
+    return loader().robot
 
 
 def loadTiagoNoHand():
-    warnings.warn("`loadTiagoNoHand()` is deprecated. Please use `loadTiago(hand=False)`", DeprecationWarning, 2)
+    warnings.warn(_depr_msg('loadTiagoNoHand()', 'tiago_no_hand'), DeprecationWarning, 2)
     return loadTiago(hand=False)
 
 
+class ICubLoader(RobotLoader):
+    path = "icub_description"
+    urdf_filename = "icub.urdf"
+    srdf_filename = "icub.srdf"
+    free_flyer = True
+
+
+class ICubReducedLoader(ICubLoader):
+    urdf_filename = "icub_reduced.urdf"
+
+
 def loadICub(reduced=True):
-    return robot_loader('icub_description',
-                        "icub_reduced.urdf" if reduced else "icub.urdf",
-                        "icub.srdf",
-                        free_flyer=True)
+    if reduced:
+        warnings.warn(_depr_msg('loadICub()', 'icub_reduced'), DeprecationWarning, 2)
+        loader = ICubReducedLoader
+    else:
+        warnings.warn(_depr_msg('loadICub(reduced=False)', 'icub'), DeprecationWarning, 2)
+        loader = ICubLoader
+    return loader().robot
+
+
+class PandaLoader(RobotLoader):
+    path = "panda_description"
+    urdf_filename = "panda.urdf"
+    urdf_subpath = "urdf"
 
 
 def loadPanda():
-    return robot_loader('panda_description', "panda.urdf", urdf_subpath='urdf')
+    warnings.warn(_depr_msg('loadPanda()', 'panda'), DeprecationWarning, 2)
+    return PandaLoader().robot
 
 
-def loadUR(robot=5, limited=False, gripper=False):
-    assert not (gripper and (robot == 10 or limited))
-    URDF_FILENAME = "ur%i%s_%s.urdf" % (robot, "_joint_limited" if limited else '', 'gripper' if gripper else 'robot')
-    if robot == 5 or robot == 3 and gripper:
-        SRDF_FILENAME = "ur%i%s.srdf" % (robot, '_gripper' if gripper else '')
-    else:
-        SRDF_FILENAME = None
+class UR3Loader(RobotLoader):
+    path = "ur_description"
+    urdf_filename = "ur3_robot.urdf"
+    urdf_subpath = "urdf"
+    ref_posture = None
+
+
+class UR3GripperLoader(UR3Loader):
+    urdf_filename = "ur3_gripper.urdf"
+    srdf_filename = "ur3_gripper.srdf"
+
+
+class UR3LimitedLoader(UR3Loader):
+    urdf_filename = "ur3_joint_limited_robot.urdf"
+
+
+class UR5Loader(UR3Loader):
+    urdf_filename = "ur5_robot.urdf"
+    srdf_filename = "ur5.srdf"
+
+
+class UR5GripperLoader(UR5Loader):
+    urdf_filename = "ur5_gripper.urdf"
+    srdf_filename = "ur5_gripper.srdf"
+
+
+class UR5LimitedLoader(UR5Loader):
+    urdf_filename = "ur5_joint_limited_robot.urdf"
+
 
-    return robot_loader('ur_description', URDF_FILENAME, SRDF_FILENAME, urdf_subpath='urdf', ref_posture=None)
+class UR10Loader(UR3Loader):
+    urdf_filename = "ur10_robot.urdf"
+
+
+class UR10LimitedLoader(UR10Loader):
+    urdf_filename = "ur10_joint_limited_robot.urdf"
+
+
+def loadUR(robot=5, limited=False, gripper=False):
+    if robot == 3:
+        if limited:
+            warnings.warn(_depr_msg('loadUr(3, limited)', 'ur3_limited'), DeprecationWarning, 2)
+            loader = UR3LimitedLoader
+        elif gripper:
+            warnings.warn(_depr_msg('loadUr(3, gripper)', 'ur3_gripper'), DeprecationWarning, 2)
+            loader = UR3GripperLoader
+        else:
+            warnings.warn(_depr_msg('loadUr(3)', 'ur3'), DeprecationWarning, 2)
+            loader = UR3Loader
+    elif robot == 5:
+        if limited:
+            warnings.warn(_depr_msg('loadUr(limited)', 'ur5_limited'), DeprecationWarning, 2)
+            loader = UR5LimitedLoader
+        elif gripper:
+            warnings.warn(_depr_msg('loadUr(gripper)', 'ur5_gripper'), DeprecationWarning, 2)
+            loader = UR5GripperLoader
+        else:
+            warnings.warn(_depr_msg('loadUr()', 'ur5'), DeprecationWarning, 2)
+            loader = UR5Loader
+    elif robot == 10:
+        if limited:
+            warnings.warn(_depr_msg('loadUr(10, limited)', 'ur10_limited'), DeprecationWarning, 2)
+            loader = UR10LimitedLoader
+        else:
+            warnings.warn(_depr_msg('loadUr(10)', 'ur10'), DeprecationWarning, 2)
+            loader = UR10Loader
+    return loader().robot
+
+
+class HectorLoader(RobotLoader):
+    path = "hector_description"
+    urdf_filename = "quadrotor_base.urdf"
+    free_flyer = True
 
 
 def loadHector():
-    return robot_loader('hector_description', "quadrotor_base.urdf", free_flyer=True)
+    warnings.warn(_depr_msg('loadHector()', 'hector'), DeprecationWarning, 2)
+    return HectorLoader().robot
+
+
+class DoublePendulumLoader(RobotLoader):
+    path = "double_pendulum_description"
+    urdf_filename = "double_pendulum.urdf"
+    urdf_subpath = "urdf"
 
 
 def loadDoublePendulum():
-    return robot_loader('double_pendulum_description', "double_pendulum.urdf", urdf_subpath='urdf')
+    warnings.warn(_depr_msg('loadDoublePendulum()', 'double_pendulum'), DeprecationWarning, 2)
+    return DoublePendulumLoader().robot
+
+
+class RomeoLoader(RobotLoader):
+    path = "romeo_description"
+    urdf_filename = "romeo.urdf"
+    urdf_subpath = "urdf"
+    free_flyer = True
 
 
 def loadRomeo():
-    return robot_loader('romeo_description', "romeo.urdf", urdf_subpath='urdf', free_flyer=True)
+    warnings.warn(_depr_msg('loadRomeo()', 'romeo'), DeprecationWarning, 2)
+    return RomeoLoader().robot
+
+
+class IrisLoader(RobotLoader):
+    path = "iris_description"
+    urdf_filename = "iris_simple.urdf"
+    free_flyer = True
 
 
 def loadIris():
-    return robot_loader('iris_description', "iris_simple.urdf", free_flyer=True)
+    warnings.warn(_depr_msg('loadIris()', 'iris'), DeprecationWarning, 2)
+    return IrisLoader().robot
 
 
 ROBOTS = {
-    'anymal': (loadANYmal, {}),
-    'anymal_kinova': (loadANYmal, {
-        'withArm': 'kinova'
-    }),
-    'double_pendulum': (loadDoublePendulum, {}),
-    'hector': (loadHector, {}),
-    'hyq': (loadHyQ, {}),
-    'icub': (loadICub, {
-        'reduced': False
-    }),
-    'icub_reduced': (loadICub, {
-        'reduced': True
-    }),
-    'iris': (loadIris, {}),
-    'kinova': (loadKinova, {}),
-    'panda': (loadPanda, {}),
-    'romeo': (loadRomeo, {}),
-    'solo': (loadSolo, {}),
-    'solo12': (loadSolo, {
-        'solo': False
-    }),
-    'talos': (loadTalos, {}),
-    'talos_box': (loadTalos, {
-        'box': True
-    }),
-    'talos_arm': (loadTalos, {
-        'arm': True
-    }),
-    'talos_legs': (loadTalos, {
-        'legs': True
-    }),
-    'talos_full': (loadTalos, {
-        'full': True
-    }),
-    'talos_full_box': (loadTalos, {
-        'full': True,
-        'box': True
-    }),
-    'tiago': (loadTiago, {}),
-    'tiago_no_hand': (loadTiago, {
-        'hand': False
-    }),
-    'ur5': (loadUR, {}),
-    'ur5_gripper': (loadUR, {
-        'gripper': True
-    }),
-    'ur5_limited': (loadUR, {
-        'limited': True
-    }),
+    'anymal': ANYmalLoader,
+    'anymal_kinova': ANYmalKinovaLoader,
+    'double_pendulum': DoublePendulumLoader,
+    'hector': HectorLoader,
+    'hyq': HyQLoader,
+    'icub': ICubLoader,
+    'icub_reduced': ICubReducedLoader,
+    'iris': IrisLoader,
+    'kinova': KinovaLoader,
+    'panda': PandaLoader,
+    'romeo': RomeoLoader,
+    'solo': SoloLoader,
+    'solo12': Solo12Loader,
+    'talos': TalosLoader,
+    'talos_box': TalosBoxLoader,
+    'talos_arm': TalosArmLoader,
+    'talos_legs': TalosLegsLoader,
+    'talos_full': TalosFullLoader,
+    'talos_full_box': TalosFullBoxLoader,
+    'tiago': TiagoLoader,
+    'tiago_no_hand': TiagoNoHandLoader,
+    'ur3': UR5Loader,
+    'ur3_gripper': UR3GripperLoader,
+    'ur3_limited': UR3LimitedLoader,
+    'ur5': UR5Loader,
+    'ur5_gripper': UR5GripperLoader,
+    'ur5_limited': UR5LimitedLoader,
+    'ur10': UR10Loader,
+    'ur10_limited': UR10LimitedLoader,
 }
 
 
-def load(name, display=False):
+def loader(name, display=False):
     """Load a robot by its name, and optionnaly display it in a viewer."""
-    loader, kwargs = ROBOTS[name]
-    robot = loader(**kwargs)
+    if name not in ROBOTS:
+        robots = ", ".join(sorted(ROBOTS.keys()))
+        raise ValueError("Robot '%s' not found. Possible values are %s" % (name, robots))
+    inst = ROBOTS[name]()
     if display:
-        robot.initViewer(loadModel=True)
-        robot.display(robot.q0)
-    return robot
+        inst.robot.initViewer(loadModel=True)
+        inst.robot.display(inst.q0)
+    return inst
+
+
+def load(name, display=False):
+    """Load a robot by its name, and optionnaly display it in a viewer."""
+    return loader(name, display).robot
+
+
+def load_full(name, display=False):
+    """Load a robot by its name, optionnaly display it in a viewer, and provide its q0 and paths."""
+    inst = loader(name, display)
+    return inst.robot, inst.q0, inst.urdf_path, inst.srdf_path