Download
latest Releases : 2.0.0-RELESE
2.0.0-RELESE | Download page |
Number of Projects
RT-Component | 153.5 |
RT-Middleware | 34 |
Tools | 22 |
Documentation | 2 |
Choreonoid
Motion editor/Dynamics simulator
OpenHRP3
Dynamics simulator
OpenRTP
Integrated Development Platform
AIST RTC collection
RT-Components collection by AIST
TORK
Tokyo Opensource Robotics Association
DAQ-Middleware
Middleware for DAQ (Data Aquisition) by KEK
What is a data port?
A data port is a port for continuous data exchange between RTCs. A data port that sends data to other RTCs is called an OutPort, and a data port which receives from other RTCs is called an InPort. OutPort and InPort together are termed data ports.
RTCs can be written in a various progamming languages and distributed across a network or all run locally, or even within the same process. Communication between data ports can be carried out transparently no matter where or how the other end of the connection is implemented.
An RTC can have any number of data ports. For example, making an RTC that obtains output from a sensor requires at least one OutPort to provide the data.
On the other hand, if you make a motor component that drives a motor according to the given reference torque, this component must have at least one InPort to receive reference torque data. If you make a controller component by using these components, it must have an InPort to receive sensor data, and an InPort to receive reference data and an OutPort to output torque data respectively.
Let's look at a simple example of InPort and OutPort ports in actual code. Each object functions as follows.
On the first line, the current value of the encoder is read using the read() function of the device object. The received value is written into encoderData.data. This object acts as a reference to the port's current value. On the second line, the write() function of the OutPort object is called to write the current value (i.e. the value of encoderData) to the port, sending it to all connected InPorts.
On the other hand, a motor component with an InPort can be written as follows:
On the first line, the port is checked for any new data to read. When there is, calling read() on the port retrieves the first value in the input buffer (the length of this buffer can be changed to suit your needs). The value is copied into the the motorData object. It is then used to drive the motor by calling the output() method of the motor device object.
Finally, a controller component with two InPorts and an OutPort can be written as follows:
This example is a combination of the two previous examples, so we will skip the detailed explanation. The OpenRTM-aist framework for RT-Components hides differences such as how other components are implemented and network transports, making it easy to exchange data in this way.
Variable types
None of the examples given so far have shown the variable types involved in communications. This section describes them.
Basic Types
The above example assumes that the data is stored in the TimedDouble data type. It is very similar to the following C-style structure.
The following rules are defined for the types used with data ports.
This means that, in all the examples above, all ports should use the TimedDouble data type.
OpenRTM-aist provides the following basic types for use with data ports. These basic data types all have a "tm" field to store a time stamp.
The conversion rules from the IDL to a language is called a mapping. Please see the CORBA language mapping specifications or your ORB documentation for more details.
More complex data types
The sequence types, suffixed with "seq," correspond to the basic types. There is one sequence for each basic type, allowing arrays of basic types to be used.
As shown in above, you can use the sequence types in C++. They are more convenient than raw arrays, similar to the STL vector class, but functionality is weaker. In Java, a holder class for the array type is automatically generated and used. In Python, arrays are directly mapped to Python's normal array.
In the above example, only a single encoder and a single motor are used. However, in real robot systems, which have many degrees of freedom, many encoders and motors may be handled by a component. In this case, it is not good idea to make a port for each degree of freedom, because it may degrade the performance of communication and synchronization. In this case, sequence data type can be utilized to handle several sets of data effectively.
Custom data types
You might want to use more complex data structures not provided by OpenRTM-aist. In this case, you can define a new data type and use it as a data port's data type. See the "Data Port (Advanced)" section for details.
Data port connection
Connector
Connections between InPorts and OutPorts are made using tools such as RTSystemEditor and rtshell.
In order to connect between InPort and OutPort on RTCs tools such as RTSystemEditor and/or rtcshell are used. After connecting ports, data sent from OutPorts goes through the network and is received by InPorts. The connection can select types from several options based on system structure and characteristics of components.
Interface type
The interface type allows you to select kind of interface that is used for data transportation. By default, only the corba_cdr type is available, and this is recommended for general usage. However, you can extend OpenRTM-aist to use other interface types.
Data flow type
There are two types of data exchange. One is the "push" type, in which the OutPort sends data to InPort, and other is the "pull" type, in which the InPort requests data from OutPort.
In the push type, the entity to send data to the InPort is the OutPort side's activity, which is usually the on_execute() callback function. The data sending timing is controlled with subscription types, described below.
On the other hand, in the pull type, the entity that fetches data from OutPort is the InPort side's activity, which is usually the on_execute() callback function. In this case, data reception is performed when the read() function is called at the InPort side.
Subscription type
The subscription type is a property that is valid only when the data flow type is push. The default setting is "flush," for synchronous communication. The asynchronous subscription types "new" and "periodic" are also available.
When flush is used, the actual sending of data from the OutPort to the InPort is performed in the call to the write() function. This means that, once write() returns, delivery of data is guaranteed (except for errors). However, it can add significant delays to the component execution for a number of reasons, such as network lag. This is likely to break real-time capability, so flush should be avoided in time-critical systems.
The new and periodic types use a separate publisher thread to perform the communications. The write() funciton only pushes data into a buffer, and returns immediately. The data is written at a later point in time as determined by the publisher.
When new is used, a signal is broadcast to publisher threads waiting for data to become available for transmission. This causes the publishers to wake up and send their data. If the data transmission time is small enough, there is little waiting time before transmission, and so new functions much like flush, but with less effect on the RTC. On the other hand, if the connection to the InPort is slow or unreliable, there is no guarantee that all data will be delivered to the receiver. This is a best-effort data transmission type.
In the "periodic" subscription type, the publisher fetches data from a the buffer and sends it to receiver. It performs this at a specified constant period. This period is set externally when the connection is established. If data transmission takes longer than the period time, the publisher will start failing to meet its periodic goals. If the user is careless in managing the ratio between writing data into the buffer (an activity producing data too fast), reading data from the buffer to write to the port and a correct data transmission policy, the buffer may enter a buffer-full or buffer-empty status. This is known as the producer-consumer problem, or the bounded-buffer problem.
Data transmission policy
If the subscription type is "new" or "periodic", the OutPort has a buffer. The way in which the data from the buffer is transmitted is known as the transmission policy.
There are four types of data transmission policy: "all," for sending all data in a buffer in one time, "fifo," for sending in a first-in, first-out style, "skip," for sending with skipping in a specified pattern, and "new" for sending only the newest data and discarding other old data.
If you chose asynchronous subscriptions such as "new" and "periodic", the transmission policy should be properly chosen based on the data production ratio, the data consuming ratio and the bandwidth of the communication path.
InPort programming
From now, let's take a look at how data ports are used in actual program.
When using InPort, writing programs according to the following rules in mind is recommended.
An OutPort which is connected an InPort might be owned by RTC running on other node. A port might be connected to any other ports, and data might not be sent from anywhere. In case of array data, the length of array might change in the next data. Connections might be destroyed, or other RTC might stop and data stops suddenly.
When modularizing, it is very important making it with less assumptions and prerequisites, and independency from other elements, and reusability of them and usability depends on these design rules.
Now, the structure of InPort is explained before seeing the actual usage of InPort.
The substance of InPort is an object. In C++ language, it is defined as a class template InPort<T> type. The data type which a data port uses is substituted for T. The lower example shows InPort declaration of the ConsoleOut component which is attached as a sample. InPort is declared with the TimedLong type.
If you are using RTCBuilder and rtc-template, these declarations and initialization will be generated automatically. When using InPort, one T type variable connected to the InPort object is defined. TimedLong type variable m_in shown in the above-mentioned example is the variable. This is called an InPort variable.
InPort and an InPort variable are associated at the time of initialization, and if data read function read() of InPort is called, one data will be read from the buffer which InPort has, and they will be copied to an InPort variable. Thus, the data sent to InPort are used through an InPort variable.
InPort objects
The functions defined by the InPort class template are shown in the tables below.
Although these are functions of the InPort class in C++, the API is virtually identical in other languages. The reference manual of these functions can be seen from "Start" -> "OpenRTM-aist" -> "C++" -> "Documents" -> "Class reference" in Windows. In Linux, if the documentation package is installed, you can find them under ${prefix}/share/OpenRTM-aist/docs/ClassReference. The manual is described in the doxygen format, please display a class list from the "name space" of a top menu, and refer to the InPort class.
The most-commonly used functions are isNew() and read(). See the following example for use:
This checks if data has arrived using m_inIn.isNew(), and reads the new data into the InPort variable using m_inIn.read(). The value of m_inIn is displayed over std::cout.
As in this example, data handling for InPorts is typically performed by the onExecute() function, which is implemented such that it regularly processes the ports.
Most functions are simple. However, the setOnRead() and setOnReadConvert() functions are more complex. They are described in the advanced section.
OutPort
The OutPort is easier to manage than the InPort, because it does not have to acquire data.
The structure of the OutPort is almost the same as the InPort. It is defined in C++ as a class template which takes a type parameter, T. T is the data type for the OutPort and it can send data only to InPorts with the same type T. The OutPort, like the InPort, is used with an OutPort variable. If the write() function of the OutPort is called after setting the value of the OutPort data variable, that data will be sent to all InPorts connected to the OutPort.
OutPort object
The functions defined by the OutPort class template are shown in the tables below, in C++. The API is virtually identical in other languages. Please also see the OutPort documentation in the generated API documentation.
The most-commonly used functions in an OutPort are write() and getStatusList().
The example above begins by receiving values from standard input and placing them into the OutPort data variable. The data is then sent over the OutPort using m_outOut.write(). When the return value is false, the status of a port is investigated and which connection the error occurred in is printed.
Summary
This chapter described the fundamental concepts of the InPort and OutPort data port objects. Although the declaration of a DataPort can be made using RTCBuilder or rtc-template, the component developer must implement the code for actually sending or receiving data. Only a small subset of the API (such as the isNew() and read() functions of InPort) is needed for effective communication between ports.