Welcome to roboglia’s documentation!

Installation

Requirements

roboglia requires Python 3. The CI builds test the package with:

  • Python 3.6 and 3.7

  • OS: Linux; distributions Xenial (16.04) and Bionic (18.04)

  • Architecture: AMD64 and ARM64

This doesn’t mean the package might not work on other OS / Architecture / Python version combinations, but they are not officially supported.

Due to the heavily hardware dependent nature of roboglia some of the functionality requires lower level modules to communicate with the physical devices. For example to use Dynamixel devices you need dynamixel_sdk module, for I2C devices smbus2, for SPI devices spidev, etc. These packages are not available for all platforms and Python version, so care must be taken when deciding what platform to use for the robot.

While the package includes these functionalities, we are aware that not all robots will need to use all these types of devices. For instance, a robot might use only PWM controlled devices accessed through an I2C multiplexer like this 16 Channel PWM Bonnet 1 from Adafruit. There is therefore no need to install dynamixel_sdk or spidev.

With this observation in mind we have decided not to explicitly include hard dependencies on these low level packages. This means that when you install roboglia it will not automatically install them for you. It will also not check if they are available, instead it will be your responsibility to install the dependencies as you need them, as explained in the next paragraphs. This is an important point to remember, so here it is emphasized in a warning:

Warning

roboglia does not automatically install dependent packages for hardware access. You will have to install them manually as your robot requires.

Installation procedure

You can install roboglia without installing the hardware dependencies, but when you will use it you must have those dependencies available otherwise Python will raise an No module exception.

You can install roboglia using pip:

pip install roboglia

This will work well, and is especially recommended, for conda 2 environments. This will install only the main package without hardware package dependencies, but with other dependencies (like PyYAML).

If you want to install a particular version of the package you can specify:

pip install roboglia==X.X.X

If you want to install the latest code from Github, you can clone it and install it from there:

cd /tmp
git clone https://github.com/sonelu/roboglia.git
cd roboglia
[sudo] python setup.py install

The last command might require you to enter the password to allow sudo elevation.

Installing hardware dependencies

The installer comes with a number of configurations for extra packages that can be installed as needed.

dynamixel_sdk 3 is released and maintained by ROBOTIS, the maker of the Dynamixel ecosystem. For more details about the package and up to date information and installation instructions visit the DynamixelSDK Manual 4 on ROBOTIS website.

To install dynamixel_sdk when you install roboglia you specify:

pip install roboglia[dynamixel]

Warning

dynamixel_sdk is itself dependent on pyserial and will attempt to install it. Not all platforms have support for pyserial.

If you plan to use I2C devices in your robot, then you need to install smbus2:

pip install roboglia[i2c]

Warning

Not all platforms have support for smbus2.

For more details about the package and up to date information and installation instructions visit the smbus2 Github 5 page.

If you plan to use SPI devices in your robot, then you need to install spidev:

pip install roboglia[spi]

For more details about the package and up to date information and installation instructions visit the spidev Github 6 page.

Warning

Not all platforms have support for spidev.

If you intend to use a combination of hardware you can install them by entering the codes above separated by comas, for instance if you need Dynamixel and I2C you would use:

pip install roboglia[dynamixel,i2c]

Warning

The pip syntax requires there are no blanks between the elements in the square brackets above.

To simplify things, if you need all communication packages, there is an option all that will install all the extra dependencies:

pip install roboglia[all]

Note

This option will be kept in line with future developments and, if new hardware dependencies will be added, will be updated to include them. So you can be assured that this installation option will install all extra dependencies in addition to the core dependencies.

roboglia Quick Start

The main idea behind the roboglia package is to provide developers with reusable components that would require as little coding as possible to put together the base of a robot.

Let’s suppose we just finished building a robot that we we would like to use with roboglia. Let’s say that the robot is just a pan-tilt with an IMU (inertial measurement unit) on top.

Within our code we could create all the instances of the robot components by calling the class constructors with the specifics of that component. But there is a more convenient way: use a robot definition file, a YAML document that describes the structure and the components of the robot. With such a definition file available (and we will discuss it’s content later) our code will simply call the from_yaml() class method of roboglia.base.BaseRobot:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
from roboglia.base import BaseRobot
import roboglia.dynamixel
import roboglia.i2c

robot = BaseRobot.from_yaml('path/to/my/robot.yml')
robot.start()

...
# use our robot
...

robot.stop()

Robot Definition File

So, what is in the robot definition file? Let’s see how such a file would look like for our example robot:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
 my_awesome_robot:

   buses:
     dyn_bus:
       class: SharedDynamixelBus
       port: '/dev/ttyUSB0'
       baudrate: 1000000
       protocol: 2.0

     i2c0:
       class: I2CBus
       port: 0

   devices:

     d01:
       class: DynamixelDevice
       bus: dyn_bus
       dev_id: 1
       model: XL-320

     d02:
       class: DynamixelDevice
       bus: dyn_bus
       dev_id: 2
       model: XL-320

     imu_g:
       class: I2CDevice
       bus: i2c0
       dev_id: 0x6a
       model: LSM330G

     imu_a:
       class: I2CDevice
       bus: i2c0
       dev_id: 0x1e
       model: LSM330A

   joints:
     pan:
       class: JointPVL
       device: d01
       pos_read: present_position_deg
       pos_write: goal_position_deg
       vel_read: present_speed_dps
       vel_write: moving_speed_dps
       load_read: present_load_perc
       load_write: torque_limit_perc
       activate: torque_enable
       minim: -90.0
       maxim: 90.0

     tilt:
       class: JointPVL
       device: d02
       inverse: True
       pos_read: present_position_deg
       pos_write: goal_position_deg
       vel_read: present_speed_dps
       vel_write: moving_speed_dps
       load_read: present_load_perc
       load_write: torque_limit_perc
       activate: torque_enable
       minim: -45.0
       maxim: 90.0

   sensors:
     accelerometer:
       class: SensorXYZ
       device: imu_a
       x_read: out_y_deg
       x_inverse: True
       y_read: out_z_deg
       z_read: out_x_deg
       z_offset: 45.0

     gyro:
       class: SensorXYZ
       device: imu_g
       x_read: out_y_deg
       x_inverse: True
       y_read: out_z_deg
       z_read: out_x_deg
       z_offset: 45.0

   groups:
     dev_servos:
       devices: [d01, d02]

     dev_imu:
       devices: [imu_g, imu_a]

     all_joints:
       joints: [pan, tilt]

   syncs:
     read_pslvt:
       # read position, speed, load, voltage, temperature
       class: DynamixelSyncReadLoop
       group: dev_servos
       registers: [present_position, present_speed, present_load,
                   present_voltage, present_temperature]
       frequency: 50.0
       throttle: 0.25

     write_psl:
       # write position, speed, load
       class: DynamixelSyncWriteLoop
       group: dev_servos
       registers: [goal_position, moving_speed, torque_limit]
       frequency: 50.0
       throttle: 0.25

     read_imu:
       class: I2CReadLoop
       group: dev_imu
       registers: [out_x, out_y, out_z]
       frequency: 25.0

   manager:
     frequency: 50.0
     throttle: 0.25
     group: all_joints
     p_function: mean
     v_function: max
     ld_function: max

I know, it’s a pretty long listing, but it’s not that hard to understand it. We will now go component by component and explain it’s content.

As you can see the YAML file is a large dictionary that includes one key-value pair: the name of the robot “my_awesome_robot” and the components of this robot.

Note

At this moment roboglia only supports one robot definition from the YAML file and will only look at the information for the first key-value pair. If multiple values are defined roboglia will issue a warning.

The values part of that dictionary is in itself a dictionary of robot components identified by a number of keywords that reflect the parameters of the robot class constructor (we’ll come to this in a second). We will look at them in the next sections.

Buses

The first is the busses section. This describes the communication channels that the robot uses to interact with the devices. In our framework buses deal not only with the access to the physical medium (opening, closing, reading, writing) but also deals with the particular communication protocol used by the device. For instance the packets used by Dynamixel devices have a certain structure and follow a number of conventions (ex. command codes, checksums, etc.).

At this moment there are several communication buses supported by roboglia, the important ones for our robot are: Dynamixel and I2C. The first one is used to communicate with the servos while the last one will be used for the communication with the IMU.

If you look in the listing above you see that the buses are described in a dictionary, with each bus identified by a name and a series of attributes. All these attributes reflect the constructor parameters for the class that implements that particular bus. For instance the class I2CBus inherits the parameters from BaseBus (name, robot, port and auto) while adding a couple of it’s own (mock and err). The name of the bus will be retrieved from the key of the dictionary, in our case they will be “dyn_upper”, “dyn_lower” and “i2c0”.

Warning

When naming the objects in the YAML file make sure that you use the same rules that you use for naming variables in Python: use only alphanumeric characters and “_” and make sure they do not start with a digit. In all cases the names have to be hashable and Python must be able to use them as dictionary keys. In some cases they even end up as instance attributes (ex. the registers of a device), in which case they should be defined with the the same care as when naming class attributes.

For details of attributes for each type of bus please see the robot YAML specification documentation.

Devices

The second important elements are the physical actuators and sensors that the robot employs. In roboglia they are represented by devices, the class of objects that act as a surrogate of the real device and with which the rest of the framework interacts. Traditionally these surrogate objects were created by writing classes that implemented the specific behavior of that device, sometimes taking advantage of inheritance to efficiently implement common functionality across a range of devices. While this is still the case in roboglia (on a significantly larger scale) the very big difference is that we use device definition files (as YAML files) to describe the type of a device. A more generic class in the framework will be responsible for creating an instance from the information provided in these definition files without having to write additional code or to subclass any “device” class.

For our robot roboglia already has support for XL-320 devices and we plan to leverage this. The IMU inside the robot is an LSM330 accelerometer / gyroscope that is also included in the framework. In general all devices have a name (the key in the dictionary), a class identifier, the bus they are attached to, a device id (dev_id is used in the YAML as id is a reserved word in Python and we should avoid it as an attribute name) and a model that indicates the type of device from that class. Depending on the device there might be additional mandatory or optional attributes that you can identify from the robot YAML specification documentation and the specific class constructor.

The device model is in itself implemented through a YAML file (a device definition) that describes the registers contained in the device and adds a series of useful value handling routines allowing for a more natural representation of the register’s information. For more details look at the devices defined in the devices/ directory in each of the class of objects (dynamixel, i2c, etc.) or look at the YAML device specification documentation. You can find out more about techniques like clone registers (that access the same physical device register, but provide a different representation of the content, like in the case of a positional register in an actuator that could have clones for the position in degrees or in radians, or the case of a bitwise status register that can have several clones with masked results representing the specific bit).

Joints

The actuator devices present in a robot can be of various types and with various capabilities. Joints aim to produce an uniform view of them so that higher level operations (like move controllers and scripts) can be run without having to keep in track of all devices’ technicalities.

There are 3 types of joints defined in roboglia: the simply named Joint only deals with the positional information. For this it uses two attributes that identify the device’s registries responsible for reading and writing its position. Please note that the units of measurement that are used by that register are automatically inherited, so if the register represents the position in degrees then the joint will also have the same unit of measurement. There are not unit conversions for joints, specifically because those can and should be incorporated at the register level and to avoid multiple layers of conversions. Optionally a Joint can have a specification for an activation register that controls the torque on the device, if omitted the joint is assumed to be active at all times. Also, optional, a joint can have an inverse parameter that indicates the coordinate system of the joint is inverse to the one of of the device, an offset that allows you to indicate that the 0 position of the joint is different from the one of the device as well as a minimum and a maximum range defined in the joints coordinate system (before applying inverse and offset) to limit the commands that can be provided to the joint.

JointPV includes velocity control on top of the positional control by including the reference to the device’s registries that read, respectively write the values for the joint velocity. JointPVL adds load control (or torque control if you want) to the joint, creating a complete managed joint.

The advantage of using joints in your design is that later you can use higher level constructs (like Script and Move to drive the devices and produce complex patterns.

Sensors

Sensors are similar to Joints in the sense that they abstract the information stored in the device;s registers and provide a uniform interface for accessing this data.

At the moment there are 2 flavours of Sensors, the simply called Sensor that allows the presentation of a single value from a device and a SensorXYZ that presents a triplet of data as X, Y, Z, suitable for instance for our accelerometer / gyroscope devices.

Like Joints, the Sensors can provide specifications for an activate register and can indicate an inverse and offset parameters (for SensorXYZ there is one of those for each axis). Interestingly, you can can assign the device’s registers in a different order than the one they are represented internally in order to compensate for the position of the device in the robot. In our example you can see that the sensor’s X axis is provided by the device’s Y axis and that the representation is inverse, reflecting the actual position of the sensor on the board in the robot.

Groups

Groups are ways of putting together several devices, or joints with the purpose of having a simpler qualifier for other objects that interact with them, like Syncs and Joint Manager.

The components of the groups can be a list of devices, joints or other groups, which is very convenient when constructing a hierarchical structure of devices, for instance for a humanoid robot where you can define a “left_arm” group and a “right_arm” and then group them together under an “arms” group that in turn can be combined with a “legs” groups, etc. This allows for a very flexible structuring of the components so that the access to them can be split according to need, while still retaining the overall grouping of all devices if necessary.

Syncs

The device classes that are instantiated by the BaseRobot according to the specifications in the robot definition file are only surrogate representations of the actual devices. Each register defined in the device instance has an int_value that reflects the internal representation of the register’s value. Typically any access to the value property of that register will trigger a read (if the accessor is a get) of the register value form the device through the communication bus, or a write if the (accessor is a set). This works fine for occasional access to registers (ex. the activation of a joint because we normally do that very rarely) but is not suitable for information that needs to be exchanged often. In those cases the buses provide (usually) more efficient communication methods that bundle multiple registers or even multiple devices into one request.

This facility is encapsulated in the concept of a Sync. The Sync is a process that runs in it’s own Thread and performs a bus bulk operation (either read or write) with a given frequency. The sync needs the group of devices and the list of registers that needs to synchronize. A sync is quite complex and include self monitoring and adjustment of the processing frequency so that the target requested is kept (due to the fact that we run Unix kernel there is no real-time guarantee for the thread execution and actual processing frequencies can vary wildly depending on the system performance) and support start, stop, pause and resume operations.

When syncs start they place a flag sync on the registers that are subject to sync replication and value properties no longer perform read or write operations, instead simply relying on the data already available in the register’s int_value member.

Joint Manager

While having the level of abstraction that is provided by Joint and it’s subclasses is nice, there is another problem that usually robots have to deal with: several streams of commands for the joints. It is common, for complex robot behavior, to have streams that might provide different instructions to the joints, according to their purpose. If they are not mitigated the robot can get in an oscillatory state and can be destabilized. Sometimes, one of the streams provides a “correction” message to the joints like in the case of a posture control loop that adjusts the joints to balance the robot while still allowing the main script or move to run their course.

For this a robot has one, and only one, Joint Manager object a construct that is responsible for mitigating the commands and transmitting an aggregated signal to the joints.

The Joint Manager is instantiated when the robot starts and runs (like the Syncs above) in a Python thread for which you have the possibility to specify a frequency as well as all the other monitoring parameters. When moves or scripts need to provide their requests, they do not interact directly with the joints, but submit these requests to the Joint Manager. Periodically the Joint Manager processes these requests and compounds a unique request that is passed to the joints under it’s control.

The Joint Manager allows you to specify the way the requests are aggregated for each of the joints’ parameters: position, velocity, load. By default all use mean over the request values (for that joint and particular parameter) but you can use other aggregation functions, like we used max in our example for velocity and load, meaning that if multiple orders for the same joint are received the position is averaged, but velocity and load attributes are determined by using the maximum between the request.

Moving the Robot

Now that the robot is loaded and ready for action how do you control it? roboglia offers two low level interaction methods that can be exploited into more complex behavior:

  • scripted behavior: this is represented by predefined actions that are described in a “Script” and can be executed on command

  • programmatic behavior: this is more complex interaction that is determined programmatically, for instance as a result of running a ML algorithm that dynamically produce the joint commands

Scripts

Scripts are sequences of joint commands that can be described in an YAML file. roboglia offers the support for loading of a script from a file, preparing all the necessary constructs and executing it on command. The actual execution of the script is performed in a dedicated thread and therefore inherits the other facilities provided by the Thread like early stopping, pause and resume.

Here is an example of a script:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
script_1:

  joints: [j01, j02, j03]
  defaults:
    duration: 0.2

  frames:

    start:
      positions: [0, 0, 0]
      velocities: [10, 10, 10]
      loads: [100, 100, 100]

    frame_01: [100, 100, 100]
    frame_02: [200, 200, 200]
    frame_03: [400, 400, 400]
    frame_04: [nan, nan, 300]
    frame_05: [nan, nan, 100]

  sequences:

    move_1:
      frames: [start, frame_01, frame_02, frame_03]
      durations: [0.2, 0.1, 0.2, 0.1]
      times: 1

    move_2:
      frames: [frame_04, frame_05]
      durations: [0.2, 0.15]
      times: 3

    empty:
      times: 1

    unequal:
      frames: [frame_01, frame_02]
      durations: [0.1, 0.2, 0.3]
      times: 1

  scenes:

    greet:
      sequences: [move_1, move_2, move_1.reverse]
      times: 2

  script: [greet]

A script is produced by layering a number of elements, pretty much like a film script. To start with, the Script defines a number of contextual elements that simplify the writing of the subsequent components:

  • joints: here the joints that the script plans to use a listed in order. The names of the joints have to respect those defined in the robot definition file and you must ensure that the joints have been advertised by the Joint Manager. Only joints defined in the Joint Manager can be controlled through a script. Defining the joints here in an ordered list simplifies later the writing of the Frames.

  • defaults: helps with defining values that will automatically be used in case no more specific values are provided later in the other components. This helps with eliminating the need to write repetitive information in the script.

The most basic structure is the Frame: this represents a particular instruction for the joints. A frame has a name (ex. “start” in the code above) and a dictionary of positions, velocities and load commands all provided as lists representing the joints in the exact order defined at the beginning of the file. You can use nan (not a number) to indicate that for a particular joint that value is not provided and should remain the one the joint already has. You can also provide the lists shorter than the number of joints and the processing will assume all the missing one are nan and pad the list accordingly to the right. Providing any of the control elements (position, velocity, load) is optional, so you can skip any of them if you don’t need to control that item. To make things even simpler, as most of the times you only want to provide positional instructions, you can do that by just supplying a list of positions instead of the dictionary and the code will assume those are “position” instructions. You can see that used for “frame_01”, “frame_02”, etc.

You can group the frames in a Sequence. This is an ordered list of Frames that have associated transition durations and additionally can be repeated a number of times to produce the desired effect. If durations are not provided for a sequence, the ones defined in the default section are used.

Sequences are grouped in Scenes were you can specify an order for the execution Sequences and, additionally, you can use the qualifier reverse to indicate that a particular Sequence should be executed in the reverse order of definition. Like Sequences, Scenes can be executed a number of times by using the qualifier with the same name.

Finally a list of Scenes are combined in a Script that also can specify a repetition parameters times like the previous components.

Once a Script is prepared in a YAML file, working with it is very simple. You load the definition with from_yaml() and then simply call the start() method to initiate the moves. The Script will run through all the Frames as and will gracefully complete when the sequence of instructions is completed. During this time you can pause the Script and resume it or you can prematurely stop it if needed. Please be aware that the Script sends all the commands to the Joint Manager and as a result you can combine multiple Script executions in the same time, even if they may have overlapping joints.

Here is an example of running the Script defined above under a curses loop:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import curses
from roboglia.move import Script

def main(win, robot):
  win.nodelay(True)
  key = ""
  win.clear()
  script = Script.from_yaml(robot=robot, file_name='my_script.yml'
  while(True):
    try:
      key = win.get_key()
      if str(key) == 's':
        # start the Script; if already running it will restart!
        script.start()
      elif str(key) == 'x':
        # stop the script
        script.stop()
      elif str(key) == 'p':
        script.pause()
      elif str(key) == 'r':
        script.resume()
      elif str(key) == 'q':
        # stops the main loop
        script.stop()
        break
    except Exception as e:
      # no input
      pass

# initialize robot
...

curses.wrapper(main)

Of course this is just a quick example, you are free to incorporate the functionality as needed in you main processing logic of your robot, but keep in mind how easy it is to control the execution of a script with these 4 methods.

Moves

Moves allows you to control the robot joints using arbitrary commands that are produced programmatically. You will normally subclass the Motion class and implement the methods that you need in order to perform the actions.

For instance the following code would move the head of a robot using a sinusoid trajectory:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from roboglia.move import Motion
from math import sin, cos

class HeadMove(Motion):

    def __init__(manager,       # robot manager object needed for super()
                head_yaw,       # head yaw joint
                head_pitch,     # head pitch joint
                yaw_ampli= 60,  # yaw move amplitude (degrees)
                pitch_ampli=30, # pitch move amplitude (degrees)
                cycle = 5):     # duration of a cycle
        super().__init__(name='HeadSinus', frequency=25.0,
                        manager=manager, joints=[head_yaw, head_pitch])
        self.head_yaw = head_yaw
        self.head_pitch = head_pitch
        self.yaw_ampli = yaw_ampli
        self.pitch_ampli = pitch_ampli
        self.cycle = cycle

    def atomic(self):
        # calculates the sin and cos for the yaw and pitch
        sin_pos = sin(self.ticks / self.cycle) * self.yaw_ampli
        cos_pos = cos(self.ticks / self.cycle) * self.pitch_ampli
        commands = {}
        commands[self.head_yaw.name] = PVL(sin_pos)
        commands[self.head_pitch.name] = PVL(cos_pos)
        self.manager.submit(self, commands)

And in the main code of your robot you can use it as follows:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
from roboglia.base import BaseRobot

robot = BaseRobot.from_yaml('/path/to/robot.yml')
robot.start()

...

head_motion = HeadMotion(robot.manager,
                         robot.joints['head_y'], robot.joints['head_p'])
head_motion.start()

...

robot.stop()

API Reference

base Module

Classes in roboglia can be categorized in two groups in relation to their position to the main robot class:

  • Downstream classes: are classes that are located between the robot class and the physical devices.

  • Upstream classes are classes that expose the robot capabilities in a uniform way like ‘joints’, ‘sensors’, ‘moves’, etc.

Downstream

The following classes from base module are provided for representing various structural elements of a robot.

Buses

BaseBus

A base abstract class for handling an arbitrary bus.

FileBus

A bus that writes to a file with cache provided for testing purposes.

SharedBus

Implements a bus that provides a locking mechanism for the access to the underlying hardware, aimed specifically for use in multi-threaded environments where multiple jobs could compete for access to one single bus.

SharedFileBus

This is a FileBus class that was wrapped for access to a shared resource.

Registers

BaseRegister

A minimal representation of a device register.

BoolRegister

A register with BOOL representation (true/false).

RegisterWithConversion

A register with an external representation that is produced by using a linear transformation.

RegisterWithDynamicConversion

A register that, in addition to the conversions provided by RegisterWithConversion can use the value provided by another register in the device as a factor adjustment.

RegisterWithThreshold

A register with an external representation that is represented by a threshold between negative and positive values.

RegisterWithMapping

A register that can specify a 1:1 mapping of internal values to external values.

Devices

BaseDevice

A base virtual class for all devices.

Threads and Loops

BaseThread

Implements a class that wraps a processing logic that is executed in a separate thread with the ability to pause / resume or fully stop the task.

BaseLoop

This is a thread that executes in a separate thread, scheduling a certain atomic work (encapsulated in the atomic method) periodically as prescribed by the frequency parameter.

BaseSync

Base processing for a sync loop.

BaseReadSync

A SyncLoop that performs a naive read of the registers by sequentially calling the read on each of them.

BaseWriteSync

A SyncLoop that performs a naive write of the registers by sequentially calling the read on each of them.

Middle

BaseRobot

A complete representation of a robot.

JointManager

Implements the management of the joints by alowing multiple movement streams to submit position commands to the robot.

Upstream

The following classes from base module are provided for helping with the synchronization of devices’ values task.

Joints

PVL

A representation of a (position, value, load) command that supports nan value components and implements a number of help functions like addition, substraction, negation, equality (with error margin) and representation.

PVLList

A class that holds a list of PVL commands and provides a number of extra manipulation functions.

Joint

A Joint is a convenient class to represent a positional device.

JointPV

A Joint with position and velocity control.

JointPVL

A Joint with position, velocity and load control.

Sensors

Sensor

A one-value sensor.

SensorXYZ

An XYZ sensor.

dynamixel Module

This module contains classes that are specific for interaction with dynamixel devices.

Buses

DynamixelBus

A communication bus that supports Dynamixel protocol.

SharedDynamixelBus

A DynamixelBus that can be used in multithreaded environment.

MockPacketHandler

A class used to simulate the Dynamixel communication without actually using a real bus or devices.

Devices

DynamixelDevice

Implements specific functionality for Dynamixel devices.

Syncs

DynamixelSyncReadLoop

Implements SyncRead as specified in the frequency parameter.

DynamixelSyncWriteLoop

Implements SyncWrite as specified in the frequency parameter.

DynamixelBulkReadLoop

Implements BulkRead as specified in the frequency parameter.

DynamixelBulkWriteLoop

Implements BulkWrite as specified in the frequency parameter.

i2c Module

This module contains classes that are specific for interaction with I2C devices.

Buses

I2CBus

Implements a communication bus for I2C devices.

SharedI2CBus

An I2C bus that can be shared between threads in a multi-threaded environment.

MockSMBus

Class for testing.

Devices

I2CDevice

Implements a representation of an I2C device.

Syncs

I2CReadLoop

Implements a read loop that is leveraging the ability to read a range of registers in one go.

I2CWriteLoop

Implements a write loop that is leveraging the ability to write a range of registers in one go.

move Module

This module contains classes that are concerned with higher level movements allowing to store and execute predetermined routine movements.

Loops

StepLoop

A thread that runs in the background and runs a sequence of steps.

Scrips

Script

A Script is the top level structure used for defining prescribed motion for a robot.

Scene

A Scene is a collection of Sequence presented in an ordered list.

Sequence

A Sequence is an ordered list of of frames that have associated durations in seconds and can be played in a loop a number of times.

Frame

A Frame is a single representation of the robots’ joints at one point in time.

Motion

Motion

Class that helps with the implementation of code-driven joint control.

utils Module

Factory

register_class(class_obj)

Registers a class with the class factory dictionary.

unregister_class(class_name)

Removes a class from the class factory dictionary thus making it unavaialble for dynamic instantiation.

get_registered_class(class_name)

Retrieves a class object from the class factory by name.

registered_classes()

Convenience function to inspect the dictionary of registered classes.

Check Utilities

check_key(key, dict_info, context, …[, …])

Checks if a key is in a dictionary dict_info and raises a customized exception message with better context.

check_type(value, to_type, context, …[, …])

Checks if a value is of a certain type and raises a customized exception message with better context.

check_options(value, options, context, …)

Checks if a value is in a list of allowed options.

Indices and tables