Often it is necessary to change the parameters in the program to be created according to the external environment of the system, the usage situation, the characteristics of individual devices, and the robot in constructing the robot system. In a simple program for simple experiments, you may be able to deal with it by hard-coding (padding) the parameters and directly rewriting and compiling each time you change them. By going a little further, reusability will be much higher by reading the parameters from file etc, passing in with the command line argument etc etc. In order to reuse one program depending on the application, externalization without embedding such parameters becomes very important.
In the RT system constructed by the RT component, various components made by various people cooperate, so the user freely defines the parameters used inside the core logic and changes it from the outside at the time of execution A function is provided for. This is called configuration (function). The configuration can have multiple parameter sets and parameter sets can be swapped all at once. By making the parameters changeable in advance, RTC can be easily reused on various systems.
In this section, we will explain the structure and actual usage of the configuration which is one of the important functions of the RT component.
The figure below shows a rough structure of configuration.
We call the name and value pair of parameters configuration parameter. A component can define more than one configuration parameter, which is called the configuration set.
Furthermore, one component can have more than one configuration set, only one of which will be the value of the actual parameter. This configuration set is called active configuration . A configuration set can be named and distinguished by its name.
You can use external tools (such as RTSystemEditor or rtshell) to change individual parameters or active configuration sets. The contents of the configuration are reflected in the variables tied to the configuration (parameter variable) and can be used with the logic in the RT component. In this way, it is possible to increase the reusability of the component by making it easy to change the parameters used inside the logic from the outside.
In a typed language, the configuration parameters can be used as parameters for any type that is available in that language. Of course, the same is true for languages without type, but the important point is that when setting these parameters externally, that value is given by a string.
The configuration converts the string to each parameter type and sets it to the actual variable. Even data types that can not be easily converted from character strings to data, such as structures and arrays, can be handled in the same way for any type of data by defining the conversion function. This is a big difference from data ports and service ports that require IDL definition in advance.
There are several ways to define the parameters used within the RT component.
Each method will be explained below.
The easiest way to define the configuration parameters is to use the RTC design tool RTCBuilder to define the configuration parameters during RTC design.
The figure below shows the definition screen of RTC Builder's configuration. By defining the necessary parameters on this screen, the code necessary for using the configuration parameters is automatically generated regardless of the language.
To use the configuration parameters, press the RTC Builder's Configuration tab and click the Add button next to the parameter list. Then, since one configuration parameter is added, the appropriate
As the name (conf_name 0 etc is default by default), please give a descriptive name briefly describing the nature of the parameter. The type names that can be selected from the drop-down list are appropriately converted and defined in each language. In languages that need not explicitly type declarations such as Python, the type name set here may not appear on the code.
As mentioned above, the configuration parameter can correspond to various types of parameters by giving the value as a string and converting the string to a specific type. However, since values are input from the outside as a character string, if there is an incorrect parameter input such as a nontranslatable character string, conversion may be an error. The default value set here is the value to be used instead when the conversion of the set value is illegal.
In addition, the following items are not required. Please enter it as necessary.
For details, refer to the hint on the right side of the screen and the RTCBuilder manual.
rtc-template is a component template generator to use from the command line. To use the configuration with rtc-template, specify as follows.
/usr/local/bin/rtc-template -bcxx --module-name=ConfigSample --module-type=DataFlowComponent --module-desc=Configuration example component --module-version=1.0 --module-vendor=Noriaki Ando, AIST --module-category=example --module-comp-type=DataFlowComponent --module-act-type=SPORADIC --module-max-inst=10 --config=int_param0:int:0 --config=int_param1:int:1 --config=double_param0:double:0.11 --config=double_param1:double:9.9 --config=str_param0:std::string:hoge --config=std_param:std::string:dara --config=vector_param0:std::vector<double>:0.0,1.0,2.0,3.0,4.0 # In fact, please input on one line, or supplement the continuation character at the end of the line (\, on UNIX ^ on Windows)
This is an example of specification with ConfigSample attached to the sample.
--config = <name>:<data type>:<default value>
It is specified as follows. For the data type, you specify the data type to be used in that language, but it may not work well with primitive types, or you may need to manually modify it.
Although less recommended, you can manually define the configuration parameters. It is effective when you want to add new parameters etc, but if you do not update the document or RTC.xml file etc., third parties do not have consistency of specification and implementation when using this RTC Please be aware that there is a possibility of confusion.
However, it is meaningful to know how the configuration is declared and used, so I will explain it here.
The following procedure is necessary to use the configuration.
As mentioned above, decide which part of the component you want to use the parameter, the name that characterizes the parameter, and the type name (for languages with type) at the time of implementation.
If it is a file generated by RTC Builder or rtc-template, there are parts surrounded by the following tags, so declare variables for the configuration parameters here.
// Configuration variable declaration // <rtc-template block="config_declare"> // </rtc-template>
In the above example of ConfigSample, it becomes as follows.
// Configuration variable declaration // <rtc-template block="config_declare"> int m_int_param0; int m_int_param1; double m_double_param0; double m_double_param1; std::string m_str_param0; std::string m_str_param1; std::vector<double> m_vector_param0; // </rtc-template>
Configuration parameters are managed within the component in a data store named Properties. Within this Properties,
conf.<configuration set name>.<parameter name>
Keeping the configuration parameters with the key. The default value default configuration set name is reserved and all default values are defined as this default configuration set.
For the above ConfigSample, add as follows.
// Module specification // <rtc-template block="module_spec"> static const char* configsample_spec[] = { "implementation_id", "ConfigSample", "type_name", "ConfigSample", "description", "Configuration example component", "version", "1.0", "vendor", "Noriaki Ando, AIST", "category", "example", "activity_type", "DataFlowComponent", "max_instance", "10", "language", "C++", "lang_type", "compile", // Configuration variables "conf.default.int_param0", "0", "conf.default.int_param1", "1", "conf.default.double_param0", "0.11", "conf.default.double_param1", "9.9", "conf.default.str_param0", "hoge", "conf.default.str_param1", "dara", "conf.default.vector_param0", "0.0,1.0,2.0,3.0,4.0", "" }; // </rtc-template>
Configuration variables The following part is the definition of the default configuration set.
Variables generated by RTCBuilder and rtc-template are not initialized by the initializer of the constructor, but it is better to initialize all variables with initializers of the constructor, if possible. In addition, since default values are set for each variable after the bindParameter () function in the onInitialize () function is called, in principle it should not be used before that.
Finally, by binding the names of variables and parameters, default values, and further conversion functions, we simply make the variables configuration parameters. We use bindParameter () which is a member function (method) of the RTObject class.
bindParameter (<parameter name (string)>, variable, <default value (character string)>, <conversion function>)
In the above ConfigSample (C ++ example) it looks like the following.
// <rtc-template block="bind_config"> // Bind variables and configuration variable bindParameter("int_param0", m_int_param0, "0"); bindParameter("int_param1", m_int_param1, "1"); bindParameter("double_param0", m_double_param0, "0.11"); bindParameter("double_param1", m_double_param1, "9.9"); bindParameter("str_param0", m_str_param0, "hoge"); bindParameter("str_param1", m_str_param1, "dara"); bindParameter("vector_param0", m_vector_param0, "0.0,1.0,2.0,3.0,4.0"); // </rtc-template>
In this way, each variable and configuration parameter are bound, and configuration parameters are made available that can manipulate these variables from RTSystemEditor etc.
Note that the conversion function given to bindParameter () is unnecessary for built-in type as in the above example, and it is not necessary to explicitly give it. However, if you want to use your own structure or complex type etc as configuration parameters, you need to define and give the conversion from these strings to those types. Details of the conversion function will be described later.
Using parameters is very easy. As we have just mentioned, we simply use variables declared as configuration parameters. However, there are some conditions to use, and it is necessary to use it by observing this condition.
The configuration variable can only be used within a specific callback function (onXXX ()). Changing the configuration variables from outside is done asynchronously. In such cases, it is necessary to control exclusive access to variables with mutex etc. In order to realize this, it is necessary for component developers to mutex protect themselves when accessing each variable. In order to avoid this, OpenRTM-aist makes external configuration changes be done outside the callback function.
The available callback functions are as follows.
Configuration parameters can be used within almost all callback functions. However, in onInitialize (), of course you can not use the configuration parameter before doing bindParameter (). Also, within onFinalize (), changes made to the configuration parameters may not be reflected immediately before the call.
The configuration parameter variable is changed from outside the component and its value is assigned to the parameter variable. However, for parameter variables Even if you write it inside a function such as onExecute (), it will not be reflected in the value of the parameter seen from the outside.
In this way, since changing the value of a variable is one way, writing to a variable from inside the component is meaningless. Let's use the configuration variable as read only.
The value of the configuration parameter is converted to the variable actually used by converting what is given as a character string from the outside by the conversion function as described above. Since it is a character string, it is possible that a character string is assigned to where the value should be assigned originally, or a variable of size larger than the upper limit may be assigned to the variable declared with short int. Therefore, on the receiving side, it is recommended that you always check on the program before use whether it is within the range of the expected value or not assigned an unlikely value.
We mentioned above that the configuration parameters have several sets and can change them at the same time at run time. On the other hand, when designing the component with RTC Builder or rtc-template, only the default configuration set could be defined. This section explains how to use the configuration set.
The default configuration set is embedded in the source code. In the same way, other configuration sets can also be increased in principle by embedding them in the source code. However, since the purpose of the RTC configuration function was to use one component for various purposes by changing the parameters according to the application without changing the source code, the other configuration set It is totally overwhelmed to embed.
The configuration set can be given in the component's configuration file. There is rtc.conf in the file that configures the component, which is mainly a configuration file for middleware that manages components, and the configuration file for the component is specified in rtc.conf as follows can.
corba.nameservers: localhost naming.formats: %h.host_cxt/%n.rtc example.ConfigSample.config_file: configsample.conf
The part of example.ConfigSample.config_file is the specified part of the component's configuration file. The part that specifies the configuration file is as follows.
<Category name>.<Module name>.config_file: <file name>
You can also give an instance name instead of the module name of the component.
<Category name>.<Instance name>.config_file: <file name>
Therefore, different configuration files can be given for each instance.
example.ConfigSample0.config_file: consout0.conf example.ConfigSample1.config_file: consout1.conf example.ConfigSample2.config_file: consout2.conf
In the configuration file, describe the configuration set you want to use.
configuration.active_config: mode1 conf.mode0.int_param0: 12345 conf.mode0.int_param1: 98765 conf.mode0.double_param0: 3.141592653589793238462643383279 conf.mode0.double_param1: 2.718281828459045235360287471352 conf.mode0.str_param0: mode0 conf.mode0.str_param1: foo conf.mode0.vector_param0: 0.0,0.1,0.2,0.3,0.4 conf.mode1.int_param0: -999 conf.mode1.int_param1: 999 conf.mode1.double_param0: 297992458 conf.mode1.double_param1: 2.97992458e+8 conf.mode1.str_param0: mode1 conf.mode1.str_param1: AIST conf.mode1.vector_param0: 1,2,3,4,5,6,7,8,9 conf.__widget__.int_param0: slider.1 conf.__widget__.int_param1: spin conf.__widget__.double_param0: slider.0.1 conf.__widget__.double_param1: text conf.__widget__.str_param0: radio conf.__widget__.str_param1: text conf.__widget__.vector_param0: text conf.__constraints__.int_param0: 0<=x<=150 conf.__constraints__.int_param1: 0<=x<=1000 conf.__constraints__.double_param0: 0<=x<=100 conf.__constraints__.double_param1: conf.__constraints__.str_param0: (default,mode0,mode1,foo,bar,radio) conf.__constraints__.str_param1: conf.__constraints__.vector_param0:
In the first line, configuration.active_config specifies the active configuration set name. In this case, it is necessary to specify the existing set name with the set name called mode 1.
configuration.active_config: mode1
Next is a list of parameters starting with conf.mode 0, which is a list of configuration parameters for set name mode 0 . How to specify is almost the same as source code
conf.<set name>.<parameter name>:<default value>
Has become. Be sure to specify all the configuration parameters that exist. If not specified, the default value is used. Next, there is a list of parameters starting with conf.mode 1, as well as mode 0, this is the setting of the parameter named mode 1.
Next, there is a setting list that starts with conf._ widget_. This is a special parameter used in RTSystemEditor. I mentioned above that widget can be specified when setting configuration parameters with RTCBuilder, but the contents set here will be set to conf._ widget_. You can set four kinds of slider, radio, spin, text, and when you open the configuration parameter setting dialog in RTSystemEditor, you can control parameters with slider, radio button, spin button, text box respectively .
conf.__widget __. <parameter name>: widget name
conf.__widget__.int_param0: slider.5
By setting as above, the step width of the slider can be set to 5. At this time, this step size can not be decimals. However, there is a possibility that it will be improved by future upgrading.
conf.__widget__.int_param1: spin
The step width of the spin button is always 1 increment. It is recommended that it be used only for integer value parameters such as int.
conf__widget__str_param0: radio
conf__widget__.str_param1: text
If these conf._widget_ parameters are set, the conf._ constraints_ parameter must also be set.
The conf._constraints_ parameter is a special parameter for setting a range of values. An example of setting is shown below. Be careful when setting invalid parameters because the widget will not be displayed properly.
conf.__constraints__.int_param0: 0<=x<=150
conf.__constraints__.int_param0: 0<=x<=1000
conf.__constraints__.str_param0: (default,mode0,mode1)
conf.__constraints__.str_param1: AIST
Below is an example of display in RTSystemEditor according to the above setting.
For C++ and others, you do not need to specify conversion functions, especially for built-in types such as int and double. Meanwhile, we used user-specific types such as structure and STL container There are also cases. In this case, it is necessary to give bindParameter () as a function how to convert from a character string to each type.
Regarding conversion functions, there are rules for each language as follows. The method for each language will be described below.
In C++, the prototype declaration of bindParameter is
template <typename VarType> bool bindParameter(const char* param_name, VarType& var, const char* def_val, bool (*trans)(VarType&, const char*) = coil::stringTo)
std::istream& operator>>(std::istream&, T)
If it is defined, it will automatically be used to convert from a string to a specific type.
That is, if std :: cin >> <variable of one type> can be written, operator >> () is defined for that type, and in particular as a configuration parameter without writing a conversion function You can use it.
If there is no conversion function, for example, a comma-separated numeric string
0.0,1.0,2.0,3.0,4.0
To convert to std :: vector <double>
#include <istream> #include <ostream> #include <vector> #include <string> #include <coil/stringutil.h> template<typename T> std::istream& operator>>(std::istream& is, std::vector<T>& v) { std::string s; std::vector<std::string> sv; is >> s; sv = coil::split(s ,","); v.resize(sv.size()); for (int i(0), len(sv.size()); i < len; ++i) { T tv; if (coil::stringTo(tv, sv[i].c_str())) { v[i] = tv; } } return is; }
It can be implemented in this way. This is VectorConvert.h included in the source of the OpenRTM-aist C++ version, ConfigSample component.
If we include this in the source called bindParameter () (eg ConfigSample.cpp if it is a ConfigSample component), usually in the implementation source of the component, the compiler will decide at compile time and the appropriate conversion function will be used .
In the case of Java, instead of giving a conversion function separately, describe the conversion from character string to actual type in the stringFrom () method defined in the holder class of the configuration variable.
Below is the conversion function for converting a comma-separated numeric string to Vector type defined in ConfigSampole of OpenRTM-aist Java version.
package RTMExamples.ConfigSample; import java.io.Serializable; import java.util.Vector; import jp.go.aist.rtm.RTC.util.ValueHolder; public class VectorHolder implements ValueHolder, Serializable { /** * Vector type data setting value */ public Vector value = null; /** * Default constructor * */ public VectorHolder() { } /** * constructor * * @param initialValue initial value * */ public VectorHolder(Vector initialValue) { value = new Vector(initialValue); } /** * Convert from character string to Vector type and set * * @param def_val Set value string representation * */ public void stringFrom(String def_val) throws Exception { value = new Vector(); String values[] = def_val.split(","); for( int intIdx=0;intIdx<values.length;intIdx++ ) { value.add(values[intIdx]); } } /** * Acquire setting value * * @return Setting value * */ public Vector getValue(){ return value; } /** * Convert setting value to character string * * @return Conversion string * */ public String toString(){ StringBuffer retVal = new StringBuffer(); while(value.iterator().hasNext()) { retVal.append(value.iterator().next()); if(value.iterator().hasNext()) retVal.append("'"); } return retVal.toString(); } }
In Python version OpenRTM - aist, by default, it corresponds to the basic type and its list, and if other conversion is necessary, define a function such as bool stringTo (type, string) and call the first of bindParameter () 4 Pass the function object as an argument.
Let's think about what you need to make as a configuration parameter to create an RT component.
There are certain parameters, and there are several ways to change this externally. How to change using the data port, change by using the service port, and change using the configuration.
The configuration function is used to change the parameters inside the component. Therefore, parameters in the logic should be externally changeable as configuration parameters. However, I think that there are cases where it is in doubt whether a certain variable should be used as a configuration parameter or not. Here I will think about a case like that.
Configuration parameters are usually used to give parameters externally only once the system is running or only when configuration changes are required. If the update frequency is about once or several times on the system life cycle, it is a good idea to use the configuration.
Also as mentioned above, the configuration is given as a string from the tool or application, and it is converted into each type in the component. It takes time to convert to some extent (although it is about several us to several hundreds of us in recent PCs), it is not suitable for use in sending data in 1 ms cycle, for example. How about changing the parameter at that frequency? When actually using it, although it depends on the number of parameters and the speed of the computer and the network, you can change the parameters with practically no problem at frequencies of hundreds of ms or more. However, if you need to change such values periodically, you should use the data port.
The configuration parameters can be updated at any time from tools such as RTSystemEditor and rtshell. However, actually changed parameters are reflected in actual variables at a certain timing before being referenced in functions used in functions such as onExecute and onActivated. The update timing is as follows.
At initialization | Immediately after onInitialize () |
When activated | Just before onActivated() is called |
On error | Immediately after onError() |
Active state | Immediately after onStateUpdate() ≒ After onExecute, immediately before the next onExecute() |
For example, consider a system that periodically sends data from a remote sensor to a central server. Data is sent only once per hour, and the server logs it. Should this data be sent using the data port? Or should I use the service port or should I use the configuration?
Since what is sent is data of the sensor, it can be said that it is best to send using the data port. Since the configuration is a mechanism to set the parameters from the outside, even if the update frequency is once per hour, it is inappropriate to communicate this data to the components in the configuration . However, if you want to realize complicated interactions (such as transactions) between the client and the server that can be realized with the data port, the service port may be used.
Whether it should be a data port or a configuration, in practice it will not get lost much. On the other hand, I think that there are many situations where you are at a loss as to whether you should change the parameters in the RTC logic from the service port or as configuration parameters.
If a component provides some sort of typical and somewhat comprehensive functionality, that functionality is provided externally through the interface of the service port. The service interface provides operations to acquire the target state and change the setting · mode · parameters. Apart from acquiring the state, the function of setting up and changing the mode parameter is very similar to the configuration.
As it turns out, it does not matter much which way you set it, but the function of the target RTC has already been defined as a service interface, and it is necessary to acquire and set the state, etc., to provide a somewhat complicated function In that case, it can be said that operation via the service interface is suitable. It is a good idea to use the configuration for setting other simple parameters and modes.
In this theory, I explained how to define and how to use the configuration function. Parameters in the logic should be externalized using this function as much as possible to improve the reusability of the component. You also need to pay attention to what you should or should not do for the configuration parameters. If you use the configuration function effectively, the components you create will also be highly reusable.