armarx_control HowTos

HowTo Title

Description (the change_this part of the section identifier must be edited to be descriptive and unique)

Slave and Device for an EtherCAT slave

Slave

Every EtherCAT slave needs a corresponding Slave class. This class contains all code that has to be run for the slave in each step of the bus cycle. For example, most slaves allow for some configuration before the slave is used in normal operation mode (=OP). This configuration usually is done using SDO entries (=CoE objects) in the slave. The Slave class is responsible for caching this configuration until it is switched to Safe-OP.

Most data that the slave delivers is available as processdata. On the EtherCAT bus the processdata of a slave forms a frame that maps to a packed struct in C++. The SlaveIO.h file contains the structs for the processdata that is transferred from the slave to us (SlaveOut) and the processdata that is transferred from us to the slave (SlaveIn).

For every EtherCAT slave there also exists a Data class. It is responsible for decoding and converting the SlaveOut struct and setting the values in the SlaveIn struct. The Data class has access to the HardwareConfig.xml file that contains configuration for the slave. In the Data class only conversion parameters are important. Conversion Parameters parameterize the way the raw sensor value is converted into the value that is given to armarx.

Device

A Device bundles one or more slaves. For example there is an elmo slave that can control a motor and a sensorboard slave that has sensors to measure the torque, ... So these two slaves form a SensorActorUnit device.

For each such set of slaves a Device class is needed. This class is responsible for assigning all slaves, delegating read and write requests to the slaves, updating the struct that is given to armarx and supplying the joint controllers.

JointController

A JointController represents one "way of controlling" a device. A JointController uses the data that is set in a ControlTarget. A device can have one or more ControlTargets. Each ControlTarget corresponds to a ControlMode String. For example, the head board for armar7de has two control targets:

  • ControlMode_HeadPWM
  • ControlMode_HeadLEDColor

Each device can only have one JointController active at any time. In case of the above mentioned head board a trick is used to control the leds and the motor at the same time: The Led part of the device is put into a subdevice. This principle can also be used for example if one ethercat slave can control multiple actors. That is the case, for example, for the imagine_board.

Implementation

First implement the SlaveIO.h file. The input and output processdata are given. They can, for example, be read from the Excel table that is used to create C code. The order and paddings are important. Paddings can be inserted by adding a uint of correct length and naming it something like pad1. It is important that these structs are packed because otherwise the addresses of the fields inside the structs can be wrong.

Next the Data class can be created. The most important aspect here is that the raw values are converted into what is later used by other parts of the software. In the constructor it takes two RapidXmlReaderNode. These can be used to get the conversion parameters from the HardwareConfig.xml file. In the constructor it is useful to initialize all helper classes for parameterized conversions. In Data::rtReadSensorValues the current contents of the SlaveOut struct are converted and cached. In Data::rtWriteTargetValues anything that should be sent to the slave using its input pdo data is taken from the cached values and converted and then put into the SlaveIn struct. The cached values for slave inputs are changed by setters for each data. The cached values that were read and converted in Data::rtReadSensorValues should be accessible by getters. Often a linear conversion is the conversion you want - use the LinearConvertedValue utility class in this case. If other parameterized conversions are needed it is useful to create a class similar to LinearConvertedValue. If a conversion is not parameterized it is not needed to create a helper object but instead only have a function that does the reading and conversion.

Now the Slave class can be implemented. First implement Slave::isSlaveIdentifierAccepted. Here the vendor id, product code and serial number are compared with the ones given in the argument.

Most of the functions that have to be overridden can stay empty but some are important: Slave::hasError checks if an error occured. Slave::getOutputsPtr can be used to obtain the processdata. It is helpful to also print errors in the log. Use the ARMARX_RT_LOGF macros for error logging in the rt-thread. Slave::prepareForSafeOp is the place to write the SDO data that has been cached to the slave. Slave::isEmergencyStopActive should return true iff the slave detected an emergency stop.

Then there are all the setters for the SDO values and the variables to cache those values.

After implementing the slaves a Device is needed. Create the Device class: In Device::tryAssign the slaves are assigned. For most slaves this is done by calling armarx::control::ethercat::tryAssignUsingSerialNumer and checking the result. In Device::onAllAssigned the pointers to the slaves are tested to be not null. If all slaves are inizialized the SDO values for the slave are obtained from the HardwareConfig.xml and passed to the corresponding setters in the Slave classes. If not all slaves are inizialized then Result::slavesMissing should be returned.

In Device::postSwitchToSafeOp the controllers are initialized. The Device::rtReadSensorValues method should delegate the call to the slaves' Data objects, can check for warnings, log them and update the struct given to armarx. Device::rtWriteTargetValues simply delegates the call to the Data objects. The Device class needs getters for the slave numbers and identifiers. Device::getElmoSlaveNumber needs to return a pointer to the struct with the sensor values that should be given to armarx.

Members of the Device class:

  • SharedPointers to all slaves' Data classes or a combined Data class that containes all members from all the slaves.
  • Slave Identifiers for all slaves
  • SensorValue object
  • pointers to the Slave classes
  • shared_ptr to the joint controllers
  • (shared) pointer to the Data object

File system overview: The following files exist per Device:

  • Device.cpp
  • Device.h
  • joint_controllers directory
  • Data.h (optional)
  • Data.cpp (optional)

The following files exist per Slave:

  • Slave.h
  • Slave.cpp
  • SlaveIO.h

Example device

The devices/ethercat/template_device is an example of a device that does not exist in reality, but contains what is most commonly needed when implementing a new device. The device is named TemplateDevice and consists of one slave named Slave1. The device has one control mode with which the RGB Led of Slave1 can be controlled. There are also some values that can be retrieved from the Slave, namely StatusBits, MainUpdateRate, temperature, anotherSensorValue and sensorValueWithNonLinearConversion. Slave1 also has one CoE object ledStripCount.

The code has a lot of comments, so feel free to read, how a small device could be implemented or use the device as a template.

Please note that the slaves can be defined in a different package than the device in case they are part of different slaves.

This is the corresponding HardwareConfig:

<DefaultConfiguration>
<TemplateDeviceDefaultConfig>
<Enabled>1</Enabled>
</TemplateDeviceDefaultConfig>
<TemplateSlave1DefaultConfig>
<LedCount>1</LedCount>
</TemplateSlave1DefaultConfig>
</DefaultConfiguration>
<TemplateDevice>
<Slave1>
<VendorID>0x7BC</VendorID>
<ProductID>0x403</ProductID>
<Serial>0</Serial>
<ConversionParameters>
<anotherSensor factor="1.0" offset="0.0"/>
</ConversionParameters>
<LedCount>2</LedCount>
</Slave1>
</TemplateDevice>
armarx::armem::human::Robot
@ Robot
Definition: util.h:14