API Reference ============= Lookup ------ .. autoclass:: hebi.Lookup() :members: The following classes are used to retrieve information about modules discovered on the network. Viewing Discovered Modules ++++++++++++++++++++++++++ .. autoclass:: hebi._internal.lookup.EntryList() :members: This class acts as an iterator for modules discovered on the network. The elements returned from the iterator are of type :class:`.Entry`. To use, iterate in a loop like:: for entry in lookup.entrylist: # print data, or gather information .. autoclass:: hebi._internal.lookup.Entry() :members: Group ----- A group is the interface through which you can communicate with the modules by sending commands and receiving data. Group commands are not created directly - they are returned from methods in the :class:`.Lookup` class, as well as from the :meth:`~hebi.util.create_imitation_group` function. An imitation group can be created without any modules, mainly for testing and prototyping purposes. See :ref:`imitation-group`. .. autoclass:: hebi._internal.group.Group() :members: IPython Issues ++++++++++++++ Some IDE and development tools such as Jupyter Notebook and Spyder use a Python command shell called `IPython `_. IPython is a very useful tool which unfortunately creates some subtle issues if used with this API. Currently, the only known issue is when a script is run multiple times **while** the user assigns a :class:`.Group` object from the script into the running environment repetitively. For example, assume the following commands are input within a running IPython instance: :: In [1]: run ./your_script.py In [2]: group1 # This is a group instance from your python script run above Out[2]: In [3]: run ./your_script.py ``group1`` is saved to a hidden variable in IPython named ``Out``. If the command at ``In [2]`` was not executed, there would be no leaking of the group instance referenced by ``group1`` - rerunning the script would reference a different group and allow the original one to be disposed. *However*, because ``group1`` is saved to the dict named ``Out``, it will stay alive until it is explicitly deleted. This can become problematic if done repetitively, especially if the groups have a high feedback frequency set. One way to resolve this issue is to use a function called :meth:`.util.clear_all_groups`. This function explicitly disposes of all current group instances created by the API. However, it is generally recommended that you ensure to not allow multiple group instances, which refer to the same modules, to be run simultaneously. Kinematics ---------- Robot Model +++++++++++ The Robot Model interface is used to create a kinematic body on which one can perform subsequent forward and inverse kinematics computations. .. autoclass:: hebi.robot_model.RobotModel() :members: HRDF Importing ++++++++++++++ .. automethod:: hebi.robot_model.import_from_hrdf .. automethod:: hebi.robot_model.import_from_hrdf_string Metadata ++++++++ A :class:`~hebi.robot_model.RobotModel` contains metadata for each component of the kinematic body. Using the `metadata` property on an instance, one can retrieve a list of all metadata comprising the body. All metadata elements are guaranteed to have the following properties: * ``type``, which returns an enum value * ``is_dof``, which returns a boolean The `type` property returns an enum value corresponding to the type of element it represents. All possible values are: * ``hebi.robot_model.enums.RobotModelElementTypeActuator`` * ``hebi.robot_model.enums.RobotModelElementTypeBracket`` * ``hebi.robot_model.enums.RobotModelElementTypeJoint`` * ``hebi.robot_model.enums.RobotModelElementTypeLink`` * ``hebi.robot_model.enums.RobotModelElementTypeRigidBody`` * ``hebi.robot_model.enums.RobotModelElementTypeEndEffector`` Using this property, you can determine what fields are present on a metadata object. .. autoclass:: hebi.robot_model.OtherMetaData :members: .. autoclass:: hebi.robot_model.ActuatorMetaData :members: .. autoclass:: hebi.robot_model.BracketMetaData :members: .. autoclass:: hebi.robot_model.JointMetaData :members: .. autoclass:: hebi.robot_model.LinkMetaData :members: .. autoclass:: hebi.robot_model.RigidBodyMetaData :members: .. autoclass:: hebi.robot_model.EndEffectorMetaData :members: Objective Functions +++++++++++++++++++ These functions are meant to be used with the :class:`~hebi.robot_model.RobotModel` IK solver. .. automethod:: hebi.robot_model.endeffector_position_objective .. automethod:: hebi.robot_model.endeffector_so3_objective .. automethod:: hebi.robot_model.endeffector_tipaxis_objective .. automethod:: hebi.robot_model.joint_limit_constraint .. automethod:: hebi.robot_model.custom_objective Trajectory Planning ------------------- Creating Trajectories +++++++++++++++++++++ To create a trajectory, a simple interface is provided. .. automodule:: hebi.trajectory :members: Trajectory Objects +++++++++++++++++++ .. autoclass:: hebi._internal.trajectory.Trajectory() :members: Arm --- The arm API is a high level interface intended to be used with robotic chains which have a *simple* (*i.e.*, non-tree) topology. As the name suggests, this interface is best suited for robotic arms. This API encapsulates the module communication and kinematic description, while also exposing a few tunables and state associated with common kit use cases. Components ++++++++++ Arm ~~~ .. autoclass:: hebi.arm.Arm :members: End Effector ~~~~~~~~~~~~ An arm can optionally have an end effector associated with it. .. autoclass:: hebi.arm.EndEffector :members: .. autoclass:: hebi.arm.Gripper :members: Plugins ~~~~~~~ Plugins provide extra functionality for the arm, through an interface which is essentially a collection of callbacks invoked by the arm on certain methods. To add a plugin to an existing arm, one can simply add it via :meth:`.arm.Arm.add_plugin`. .. autoclass:: hebi.arm.ArmPlugin :members: Some plugin implementations are provided: .. autoclass:: hebi.arm.EffortOffset :members: .. autoclass:: hebi.arm.ImpedanceController :members: .. autoclass:: hebi.arm.DoubledJointMirror :members: Creating an Arm Object ++++++++++++++++++++++ .. automethod:: hebi.arm.create Utility Functions and Objects ----------------------------- Miscellaneous functions which don't fall under any other category appear here. Logging +++++++ Opening a Log File ~~~~~~~~~~~~~~~~~~ .. automethod:: hebi.util.load_log Log File Objects ~~~~~~~~~~~~~~~~ .. autoclass:: hebi._internal.log_file.LogFile() :members: .. autoclass:: hebi._internal.log_file.TimedGroupFeedback() :members: .. _mobile-io: Mobile IO +++++++++ **Notice:** This API is considered **experimental** and may change in ways which may break backwards compatibility in future versions. Creating a MobileIO Instance ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. automethod:: hebi.util.create_mobile_io Mobile IO Classes ~~~~~~~~~~~~~~~~~~ .. autoclass:: hebi._internal.mobile_io.MobileIO :members: .. autoclass:: hebi._internal.mobile_io.ButtonState .. _imitation-group: Imitation Group +++++++++++++++ An imitation group is an instance of a :class:`.Group` which provides all of the same methods and attributes of a group representing a physically connected collection of HEBI modules on a local network. However, the imitation group differs in implementation due to the fact that it represents a virtual collection of modules. .. automethod:: hebi.util.create_imitation_group .. _imitation-group-contrast: Contrasted to Physical Group ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ For simplicity, think of the "physical" group returned from a lookup operation as a *PhysicalGroup* and the imitation group returned from this function as an *ImitationGroup*. Commands ^^^^^^^^ While both interfaces allow for commands to be sent, there is no network communication involved in the *ImitationGroup* implementation. Consequently, any commands sent to an imitation group will be visible immediately. For example:: group = create_imitation_group(3) print('Position:') # Prints [0.0, 0.0, 0.0] print(group.get_next_feedback().position) # Move modules in group cmd = GroupCommand(group.size) cmd.position = [4.0, 2.0, -2.0] # Send command, which will be visible immediately group.send_command(cmd) # Prints [4.0, 2.0, -2.0] print(group.get_next_feedback().position) Info ^^^^ An *ImitationGroup* will never return info. In fact, any calls to :meth:`.Group.request_info` will return ``None``. A *PhysicalGroup* should always return valid info. Feedback ^^^^^^^^ An *ImitationGroup* will always have feedback available - *i.e.*, calling :meth:`.Group.get_next_feedback` on an *ImitationGroup* will *always* return feedback immediately. In contrast, a *PhysicalGroup* requires a non-zero feedback frequency, or the :meth:`.Group.send_feedback_request` function to be called. To achieve some level of deterministic behavior, the feedback in an *ImitationGroup* is always set to an *initial state*, which is basically a set of sane values for each field in a :class:`.GroupFeedback` object. Therefore, the following code is valid with the imitation group:: group = create_imitation_group(3) print('3 Positions:') # Prints the same position 3 times ([0.0, 0.0, 0.0]) print(group.get_next_feedback().position) print(group.get_next_feedback().position) print(group.get_next_feedback().position) The default feedback frequency for an imitation group is 0. This means that any attached feedback handlers will *not* be invoked when the object is in its *initial state*. You must explicitly set the feedback frequency to a positive number if feedback handlers are desired. Remember that unless commands are actively being set, the feedback data will be the same exact value on every feedback handler invocation. Misc Group Utilities ++++++++++++++++++++ .. automethod:: hebi.util.clear_all_groups Message Types ------------- These classes are used to communicate with HEBI modules. With the exception of :class:`~hebi.GroupCommand`, they are read-only. Commanding ++++++++++ .. autoclass:: hebi.GroupCommand :members: :inherited-members: Feedback ++++++++ .. autoclass:: hebi.GroupFeedback :members: :inherited-members: Info ++++ .. autoclass:: hebi.GroupInfo :members: :inherited-members: