This section assumes previous knowledge of the concepts of nodes, topics and parameters. It's also recommended to read through the HRIM component model section before moving forward.
We use XML to define our models in a machine-readable language.
Our module models will always contain a minimum of 9 mandatory topics (7 common ones, 1 defining it's specs and at least 1 purpose-specific one) and can contain mandatory parameters and optional topics and parameters. This model defines anything said module type could need to work communication wise, both as input or output.
Not all components will make use of every option defined inside a model, for example a motor would not be able to broadcast it's temperature if it doesn't have that capability, i.e. doesn't have an integrated thermometer. That's where the mandatory and optional topics and parameters come in: every communication/parameter deemed necessary for the module's integration in the system is mandatory and every module of that type will always make use of them, extra capabilities that not every module of that type would have are therefore optional.
As a (maybe not so) quick explanation:
A module is represented by a module tag (descriptive, right?) and would correlate to a node. It must indicate the represented component's type name (i.e. rangefinder), it's module category as per our categorization, and a short description.
A module contains the mandatory and optional (which is, funnily enough, optional) tags.
Each of those tags can contain topic, which would correlate to a topic or service, and/or param tags, which represent the node's parameters (so far so good).
Each topic tag must declare the represented topic's name, it's communication pattern type (publish if the module publishes that information, subscribe if it waits for it, service or action), a description of what that topic is for and a fileName*. It contains the definition of each of the values the topic's message would hold in the form of properties.
Each property tag must declare it's name and type (data type), and can optionally declare a unit and a description to provide meta-data on that information piece. The property's type attribute can also be used to define it being a list using common notation (i.e. string for an unbounded list or string[n] for a bounded one). A property tag also either contains a value tag to declare a value for that field or other subproperties, indicating a message inside a message. In this case, said property containing the subproperties would have to define a fileName* as a topic would, indicating the file to be generated. Also, the type attribute of that topic would have no value other than indicating if it'd be a list, following the same notation as usual. A property correlates to a single value inside a message.
*fileName indicates the name of the message file to be generated for the implementation. It also acts as a way for multiple topics to use the same message definition without each having it's own file. For an example on this, our camera module would use the same message file, with the same definition, for it's mandatory image_raw and optional image_mono, image_color, image_rect, and image_rect_color topics.
Phew! Finally got through that one! Don't worry, it all makes more sense when looking at a complete model. You can take a look at all the models we've developed so far inside the Models section.
You can also take a look at said models at our github repository inside the actuator, composite, power and sensor folders. If you do, you'll see how each module has it's own folder (i.e. sensor/rangefinder) and each of them has it's xml model (rangefinder.xml), a development version of this model (rangefinder_clean.xml) which will disappear in time, and a topics folder that contains multiple xml files. Each of these files are related to the module's own topics, which are then referenced from the main model file (rangefinder.xml).
Taking a look at said file you'll see the main structure defined above, some parameters, and some xi:include tags pointing to a generic/base.xml, topics/specs.xml, topics/distance.xml and topics/reconfiguration.xml, those having other references of their own. The first one references the 7 common topics mentioned initially and each of the others references a topic of the rangefinder, you can probably see some similarities between this and it's model page. You can also take a look at the development rangefinder_clean.xml file to see how the complete file would look like after the references are processed.
The functions mentioned in this subsection are not implemented yet. Still, they are part of the core functions of HRIM's tooling and are therefore explained here. Do take into account that this process is subject to changes as it's still in development.
In the previous subsection we defined how a module's model is structured. However, they describe all the capabilities a module of that type could have, but not what a specific one might necessarily have. Not only that, but a module's capabilities might be composed of multiple models, e.g. a robotic arm with an integrated camera at the end. These wouldn't be two separated modules, but a single one with multiple capabilities, as the camera would be integrated. For this, you'd use the model composition:
First, you'd indicate to the tool that you want to generate a composite model and it's parts. Following the example above of an arm with an integrated camera:
python hrim.py compose composite/arm sensor/camera
This will generate an xml file (model.xml by default) in the repository's root with a structure like the following, where each of those component's shows all it's optional capabilities (the mandatory ones are, well, mandatory, so the tool won't let you choose between those):
<composition name="defaultName"> <model type="composite" subtype="arm" path="models/composite/arm/arm.xml"> <topic name="rc"/> <topic name="reconfiguration"/> <param name="rc_max_velocity"/> <param name="rc_max_yaw"/> </model> <model type="sensor" subtype="camera" path="models/sensor/camera/camera.xml"> <topic name="audio_raw"/> <topic name="compressed"/> <topic name="image_camera_info"/> <topic name="image_color"/> <topic name="image_mono"/> <topic name="image_rect"/> <topic name="image_rect_color"/> <topic name="ptz"/> <topic name="reconfiguration"/> <topic name="set_camera_info"/> <param name="auto_binning"/> <param name="auto_brightness"/> <param name="auto_contrast"/> <param name="auto_exposure"/> <param name="auto_focus"/> <param name="auto_gain"/> <param name="auto_gamma"/> <param name="auto_hue"/> <param name="auto_iris"/> <param name="auto_iso"/> <param name="auto_saturation"/> <param name="auto_sharpness"/> <param name="auto_shutter"/> <param name="auto_white_balance"/> <param name="auto_zoom"/> <param name="binning_x"/> <param name="binning_y"/> <param name="brightness"/> <param name="contrast"/> <param name="exposure"/> <param name="focus"/> <param name="frecuency_rate"/> <param name="gain"/> <param name="gain_blue"/> <param name="gain_green"/> <param name="gain_red"/> <param name="gamma"/> <param name="hue"/> <param name="iris"/> <param name="iso"/> <param name="pan_angle_end"/> <param name="pan_angle_start"/> <param name="pan_speed"/> <param name="password"/> <param name="saturation"/> <param name="sharpness "/> <param name="shutter"/> <param name="tild_angle_end"/> <param name="tild_angle_start"/> <param name="tild_speed"/> <param name="username"/> <param name="white_balance_blue"/> <param name="white_balance_red"/> </model> </composition>
Moving forward, now is when you remove the optional capabilities your component doesn't support. You might be left with something like:
<composition name="myOwnModule"> <module type="composite" subType="arm" path="models/composite/arm/arm.xml"> <topic name="rc"/> </module> <module type="sensor" subType="camera" path="models/sensor/camera/camera.xml"> <topic name="image_color"/> <topic name="image_rect_color"/> <param name="auto_binning"> <param name="binning_x"> <param name="binning_y"> </module> </composition>
After saving the changes you'd now "compile" the model of your composed component. That is to say, the tool will parse the composition xml and each of the module models (indicated by the
path attribute), and generate the necessary packages containing the files needed to communicate with your component (now module!). These packages and files will come from the components' mandatory topics and parameters and the indicated optional ones.
python hrim.py compile model.xml
You can find the generated files under the
composition folder, in a directory with the composition name you indicated on
myOwnModule in the example).
Note: currently only supports ROS 2 implementations