{"id":28851819,"url":"https://github.com/robotology/robometry","last_synced_at":"2025-10-28T17:04:17.805Z","repository":{"id":38236207,"uuid":"312017420","full_name":"robotology/robometry","owner":"robotology","description":"Telemetry suite for logging data from your robot 🤖","archived":false,"fork":false,"pushed_at":"2025-06-18T12:14:07.000Z","size":5317,"stargazers_count":17,"open_issues_count":26,"forks_count":9,"subscribers_count":10,"default_branch":"master","last_synced_at":"2025-06-18T13:24:40.751Z","etag":null,"topics":["cpp","robotics","telemetry"],"latest_commit_sha":null,"homepage":"https://robotology.github.io/robometry","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/robotology.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2020-11-11T15:52:32.000Z","updated_at":"2025-06-18T12:09:31.000Z","dependencies_parsed_at":"2023-12-05T15:55:42.256Z","dependency_job_id":"3c5b8b36-6716-45bd-81d6-05aff8d75d65","html_url":"https://github.com/robotology/robometry","commit_stats":{"total_commits":314,"total_committers":11,"mean_commits":"28.545454545454547","dds":0.2898089171974523,"last_synced_commit":"3b5493f2e6e1b98e2204b23d89e7d630c4755f73"},"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"purl":"pkg:github/robotology/robometry","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robotology%2Frobometry","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robotology%2Frobometry/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robotology%2Frobometry/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robotology%2Frobometry/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/robotology","download_url":"https://codeload.github.com/robotology/robometry/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robotology%2Frobometry/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":260837665,"owners_count":23070558,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cpp","robotics","telemetry"],"created_at":"2025-06-19T21:38:38.430Z","updated_at":"2025-10-28T17:04:17.799Z","avatar_url":"https://github.com/robotology.png","language":"C++","readme":"\nRobometry\n=========\n\n![Continuous Integration](https://github.com/robotology/robometry/workflows/CI%20Workflow/badge.svg)\n\n[![Anaconda-Server Badge](https://anaconda.org/robotology/robometry/badges/downloads.svg)](https://anaconda.org/conda-forge/librobometry)\n[![Anaconda-Server Badge](https://anaconda.org/robotology/robometry/badges/installer/conda.svg)](https://anaconda.org/conda-forge/librobometry)\n[![Anaconda-Server Badge](https://anaconda.org/robotology/robometry/badges/platforms.svg)](https://anaconda.org/conda-forge/librobometry)\n\nTelemetry suite for logging data from your robot 🤖.\n\n## Tested OSes\n- Windows 10\n- Ubuntu 20.04, 22.04\n- macOS \u003e= 10.15\n\n## Installation from binaries\n\n### Conda packages\n\nIt is possible to install on `linux`, `macOS` and `Windows` via [conda](https://anaconda.org/conda-forge/librobometry), just running:\n```bash\nconda install -c conda-forge librobometry\n```\n\n## Installation from sources\n\n### Dependencies\n\nThe dependencies are:\n- [CMake](https://cmake.org/install/) (minimum version 3.12)\n- [Boost](https://www.boost.org/)\n- [matio-cpp](https://github.com/ami-iit/matio-cpp#installation) (minimum version 0.1.1)\n- [nlohmann_json](https://github.com/nlohmann/json#integration) (minimum version 3.10.0)\n- [Catch2](https://github.com/catchorg/Catch2.git) (v3.7.1, for the unit tests)\n\nThe optional dependencies are:\n- [YARP](https://www.yarp.it/git-master/install.html) (minimum version 3.5.0)\n- [icub-main](https://robotology.github.io/robotology-documentation/doc/html/index.html) (minimum version 2.7.0)\n\n### Linux/macOS\n```\ngit clone https://github.com/robotology/robometry\ncd robometry\nmkdir build \u0026\u0026 cd build\ncmake ../\nmake\n[sudo] make install\n```\nNotice: sudo is not necessary if you specify the CMAKE_INSTALL_PREFIX. In this case it is necessary to add in the .bashrc or .bash_profile the following lines:\n\n`export robometry_DIR=/path/where/you/installed/`\n\n### Windows\n\nWith IDE build tool facilities, such as Visual Studio:\n```\ngit clone https://github.com/robotology/robometry\ncd robometry\nmkdir build \u0026\u0026 cd build\ncmake ..\ncmake --build . --target ALL_BUILD --config Release\ncmake --build . --target INSTALL --config Release\n```\nIn order to allow CMake finding robometry, you have to specify the path where you installed in the `CMAKE_PREFIX_PATH` or exporting the `robometry_DIR` env variable pointing to the same path.\n\n## librobometry\nIn order to use this library in your own application, add this lines in your `CMakeLists.txt`\n```cmake\nfind_package(robometry)\n\nadd_executable(myApp)\ntarget_link_libraries(myApp robometry::robometry)\n```\n\n### Example scalar variable\n\nHere is the code snippet for dumping in a `.mat` file 3 samples of the scalar variables `\"one\"` and `\"two\"`. The type of the channel is inferred when pushing the first time\n\n```c++\n    robometry::BufferConfig bufferConfig;\n\n    // We use the default config, setting only the number of samples (no auto/periodic saving)\n    bufferConfig.n_samples = n_samples;\n\n    robometry::BufferManager bm(bufferConfig);\n    bm.setFileName(\"buffer_manager_test\");\n    robometry::ChannelInfo var_one{ \"one\", {1} };\n    robometry::ChannelInfo var_two{ \"two\", {1} };\n\n    bool ok = bm.addChannel(var_one);\n    ok = ok \u0026\u0026 bm.addChannel(var_two);\n    if (!ok) {\n        std::cout \u003c\u003c \"Problem adding variables....\"\u003c\u003cstd::endl;\n        return 1;\n    }\n\n    for (int i = 0; i \u003c 3; i++) {\n        bm.push_back(i , \"one\");\n        std::this_thread::sleep_for(std::chrono::milliseconds(200));\n        bm.push_back(i + 1.0, \"two\");\n    }\n\n    if (bm.saveToFile())\n        std::cout \u003c\u003c \"File saved correctly!\" \u003c\u003c std::endl;\n    else\n        std::cout \u003c\u003c \"Something went wrong...\" \u003c\u003c std::endl;\n\n```\nAnd here is the resulting .mat file:\n\n```\nbuffer_manager_test =\n\n  struct with fields:\n\n    description_list: {[1×0 char]}\n                 two: [1×1 struct]\n                 one: [1×1 struct]\n\n\nbuffer_manager_test.one =\n\n  struct with fields:\n\n              data: [1×3 int32]\n        dimensions: [1 3]\n    elements_names: {'element_0'}\n  units_of_measure: {'n.d.'}\n              name: 'one'\n        timestamps: [1.6481e+09 1.6481e+09 1.6481e+09]\n```\n\n### Example vector variable\n\nIt is possible to save and dump also vector variables.\nHere is the code snippet for dumping in a `.mat` file 3 samples of the 4x1 vector variables `\"one\"` and `\"two\"`.\n\n```c++\n    robometry::BufferConfig bufferConfig;\n    bufferConfig.auto_save = true; // It will save when invoking the destructor\n    bufferConfig.channels = { {\"one\", {4,1}, {}, {\"meters\"}}, {\"two\", {4,1}, {}, {\"degrees\"}} };\n    bufferConfig.filename = \"buffer_manager_test_vector\";\n    bufferConfig.n_samples = 3;\n\n    robometry::BufferManager bm_v(bufferConfig); //Only vectors of doubles are accepted\n    for (int i = 0; i \u003c 3; i++) {\n        bm_v.push_back({ i+1.0, i+2.0, i+3.0, i+4.0  }, \"one\");\n        std::this_thread::sleep_for(std::chrono::milliseconds(200));\n        bm_v.push_back({ (double)i, i*2.0, i*3.0, i*4.0 }, \"two\");\n    }\n\n```\n\n```\nbuffer_manager_test_vector =\n\n  struct with fields:\n\n    description_list: {[1×0 char]}\n                 two: [1×1 struct]\n                 one: [1×1 struct]\n\n\n\u003e\u003e buffer_manager_test_vector.one\n\nans =\n\n  struct with fields:\n\n              data: [4×1×3 double]\n        dimensions: [4 1 3]\n    elements_names: {4×1 cell}\n  units_of_measure: {4×1 cell}\n              name: 'one'\n        timestamps: [1.6481e+09 1.6481e+09 1.6481e+09]\n\n\n\u003e\u003e buffer_manager_test_vector.one.elements_names\n\nans =\n\n  4×1 cell array\n\n    {'element_0'}\n    {'element_1'}\n    {'element_2'}\n    {'element_3'}\n\n\u003e\u003e buffer_manager_test_vector.one.units_of_measure\n\nans =\n\n  4×1 cell array\n\n    {'m'}\n    {'m'}\n    {'m'}\n    {'m'}\n```\n\nIt is also possible to specify the name of the elements of each variable with\n```c++\nrobometry::ChannelInfo var_one{ \"one\", {4,1}, {\"A\", \"B\", \"C\", \"D\"}, {\"m\", \"cm\", \"mm\", \"nm\"}};\n```\n\n\n### Example matrix variable\n\nHere is the code snippet for dumping in a `.mat` file 3 samples of the 2x3 matrix variable`\"one\"` and of the 3x2 matrix variable `\"two\"`.\n``BufferManager``  expects all the inputs to be of vector types, but then input is remapped into a matrix of the specified type.\n\n```c++\n    robometry::BufferManager bm_m;\n    bm_m.resize(3);\n    bm_m.setFileName(\"buffer_manager_test_matrix\");\n    bm_m.enablePeriodicSave(0.1); // This will try to save a file each 0.1 sec\n    bm_m.setDefaultPath(\"/my/preferred/path\");\n    bm_m.setDescriptionList({\"head\", \"left_arm\"});\n    std::vector\u003crobometry::ChannelInfo\u003e vars{ { \"one\",{2,3} },\n                                                    { \"two\",{3,2} } };\n\n    bool ok = bm_m.addChannels(vars);\n    if (!ok) {\n        std::cout \u003c\u003c \"Problem adding variables....\"\u003c\u003cstd::endl;\n        return 1;\n    }\n\n    for (int i = 0; i \u003c 3; i++) {\n        bm_m.push_back({ i + 1, i + 2, i + 3, i + 4, i + 5, i + 6 }, \"one\");\n        std::this_thread::sleep_for(std::chrono::milliseconds(200));\n        bm_m.push_back({ i * 1, i * 2, i * 3, i * 4, i * 5, i * 6 }, \"two\");\n    }\n\n```\n\n```\n\u003e\u003e buffer_manager_test_matrix.one\n\nans =\n\n  struct with fields:\n\n          data: [2×3×3 int32]\n    dimensions: [2 3 3]\n          name: 'one'\n    timestamps: [112104.7605783 112104.9608881 112105.1611651]\n\n```\n### Example nested struct\n\nIt is possible to save and dump vectors and matrices into nested `mat` structures. To add an element into the matlab struct the you should use the separator `::`. For instance the to store a vector in `A.B.C.my_vector` you should define the channel name as `A::B::C::my_vector`\nHere is the code snippet for dumping in a `.mat` file 3 samples of the 4x1 vector variables `\"one\"` and `\"two\"` into `struct1` and `struct2`.\n\n```c++\n    robometry::BufferConfig bufferConfig;\n    bufferConfig.auto_save = true; // It will save when invoking the destructor\n    bufferConfig.channels = { {\"struct1::one\",{4,1}}, {\"struct1::two\",{4,1}}, {\"struct2::one\",{4,1}} }; // Definition of the elements into substruct\n    bufferConfig.filename = \"buffer_manager_test_nested_vector\";\n    bufferConfig.n_samples = 3;\n\n    robometry::BufferManager bm_v(bufferConfig);\n    for (int i = 0; i \u003c 3; i++) {\n        bm_v.push_back({ i+1.0, i+2.0, i+3.0, i+4.0  }, \"struct1::one\");\n        std::this_thread::sleep_for(std::chrono::milliseconds(200));\n        bm_v.push_back({ (double)i, i*2.0, i*3.0, i*4.0 }, \"struct1::two\");\n        std::this_thread::sleep_for(std::chrono::milliseconds(200));\n        bm_v.push_back({ (double)i, i/2.0, i/3.0, i/4.0 }, \"struct2::one\");\n    }\n\n```\n\n```\nbuffer_manager_test_nested_vector =\n\n  struct with fields:\n\n    description_list: {[1×0 char]}\n             struct2: [1×1 struct]\n             struct1: [1×1 struct]\n\n\u003e\u003e buffer_manager_test_nested_vector.struct1\n\nans =\n\n  struct with fields:\n\n    two: [1×1 struct]\n    one: [1×1 struct]\n\n\u003e\u003e buffer_manager_test_nested_vector.struct1.one\n\nans =\n\n  struct with fields:\n\n          data: [4×1×3 double]\n    dimensions: [4 1 3]\n          name: 'one'\n    timestamps: [1.6415e+09 1.6415e+09 1.6415e+09]\n```\n\n### Example multiple types\n\n``BufferManager`` can be used to store channels of different types, including ``struct``s. In order to store a ``struct``, it is necessary to use the ``VISITABLE_STRUCT`` macro (see https://github.com/garbageslam/visit_struct). The available conversions depend on [``matio-cpp``](https://github.com/ami-iit/matio-cpp).\n```c++\nstruct testStruct\n{\n    int a;\n    double b;\n};\nVISITABLE_STRUCT(testStruct, a, b);\n\n...\n\n    robometry::BufferManager bm;\n    robometry::BufferConfig bufferConfig;\n\n    robometry::ChannelInfo var_int{ \"int_channel\", {1}};\n    robometry::ChannelInfo var_double{ \"double_channel\", {1}};\n    robometry::ChannelInfo var_string{ \"string_channel\", {1}};\n    robometry::ChannelInfo var_vector{ \"vector_channel\", {4, 1}};\n    robometry::ChannelInfo var_struct{ \"struct_channel\", {1}};\n\n    bm.addChannel(var_int);\n    bm.addChannel(var_double);\n    bm.addChannel(var_string);\n    bm.addChannel(var_vector);\n    bm.addChannel(var_struct);\n\n    bufferConfig.n_samples = 3;\n    bufferConfig.filename = \"buffer_manager_test_multiple_types\";\n    bufferConfig.auto_save = true;\n\n    bm.configure(bufferConfig);\n\n    testStruct item;\n\n    for (int i = 0; i \u003c 3; i++) {\n        bm.push_back(i, \"int_channel\");\n        bm.push_back(i * 1.0, \"double_channel\");\n        bm.push_back(\"iter\" + std::to_string(i), \"string_channel\");\n        bm.push_back({i + 0.0, i + 1.0, i + 2.0, i + 3.0}, \"vector_channel\");\n        item.a = i;\n        item.b = i;\n        bm.push_back(item, \"struct_channel\");\n\n        std::this_thread::sleep_for(std::chrono::milliseconds(10));\n    }\n}\n\n```\nThe above snippet of code generates channels of different types. It produces the following output.\n```\n\u003e\u003e buffer_manager_test_multiple_types\n\nbuffer_manager_test_multiple_types =\n\n  struct with fields:\n\n    description_list: {[1×0 char]}\n     yarp_robot_name: [1×0 char]\n      struct_channel: [1×1 struct]\n      vector_channel: [1×1 struct]\n      string_channel: [1×1 struct]\n      double_channel: [1×1 struct]\n         int_channel: [1×1 struct]\n\n\u003e\u003e buffer_manager_test_multiple_types.string_channel\n\nans =\n\n  struct with fields:\n\n              data: {1×3 cell}\n        dimensions: [1 3]\n    elements_names: {'element_0'}\n  units_of_measure: {'n.d.'}\n              name: 'string_channel'\n        timestamps: [1.6512e+09 1.6512e+09 1.6512e+09]\n\n\u003e\u003e buffer_manager_test_multiple_types.vector_channel\n\nans =\n\n  struct with fields:\n\n              data: [4×1×3 double]\n        dimensions: [4 1 3]\n    elements_names: {4×1 cell}\n  units_of_measure: {'n.d.'}\n              name: 'vector_channel'\n        timestamps: [1.6512e+09 1.6512e+09 1.6512e+09]\n\n```\n\n### Example additional callback\n\n``BufferManager`` can call an additional callback every time the save function is called. The\nfollowing example define a custom callback that saves a dummy `txt` file along with the `mat` saved\nby the telemetry\n```c++\nbool myCallback(const std::string\u0026 file_name, const SaveCallbackSaveMethod\u0026 method) {\n  std::string file_name_with_extension = file_name + \".txt\";\n  std::ofstream my_file(file_name_with_extension.c_str());\n\n  // Write to the file\n  my_file \u003c\u003c \"Dummy file!\";\n\n  // Close the file\n  my_file.close();\n\n  return true;\n};\n\n\nrobometry::BufferManager bm;\nbm.setSaveCallback(myCallback);\n```\n\n### Example configuration file\n\nIt is possible to load the configuration of a BufferManager **from a json file**\n```c++\n   robometry::BufferManager bm;\n   robometry::BufferConfig bufferConfig;\n   bool ok = bufferConfigFromJson(bufferConfig,\"test_json.json\");\n   ok = ok \u0026\u0026 bm.configure(bufferConfig);\n```\n\nWhere the file has to have this format:\n```json\n{\n  \"yarp_robot_name\": \"robot\",\n  \"description_list\": [\n    \"This is a test\",\n    \"Or it isn't?\"\n  ],\n  \"path\":\"/my/preferred/path\",\n  \"filename\": \"buffer_manager_test_conf_file\",\n  \"n_samples\": 20,\n  \"save_period\": 1.0,\n  \"data_threshold\": 10,\n  \"auto_save\": true,\n  \"save_periodically\": true,\n  \"channels\": [\n    {\n      \"dimensions\": [1,1],\n      \"elements_names\": [\"element_0\"],\n      \"name\": \"one\",\n      \"units_of_measure\": [\"meters\"]\n    },\n    {\n      \"dimensions\": [1,1],\n      \"elements_names\": [\"element_0\"],\n      \"name\": \"two\",\n      \"units_of_measure\": [\"degrees\"]\n    }\n  ],\n  \"enable_compression\": true,\n  \"file_indexing\": \"%Y_%m_%d_%H_%M_%S\",\n  \"mat_file_version\": \"v7_3\"\n}\n```\nThe configuration can be saved **to a json file**\n```c++\n    robometry::BufferConfig bufferConfig;\n    bufferConfig.n_samples = 10;\n    bufferConfig.save_period = 0.1; //seconds\n    bufferConfig.data_threshold = 5;\n    bufferConfig.save_periodically = true;\n    std::vector\u003crobometry::ChannelInfo\u003e vars{ { \"one\",{2,3} },\n                                                    { \"two\",{3,2} } };\n    bufferConfig.channels = vars;\n\n    auto ok = bufferConfigToJson(bufferConfig, \"test_json_write.json\");\n```\n\n\n## TelemetryDeviceDumper\n\nThe `telemetryDeviceDumper` is a [yarp device](http://yarp.it/git-master/note_devices.html) that has to be launched through the [`yarprobotinterface`](http://yarp.it/git-master/yarprobotinterface.html) for dumping quantities from your robot(e.g. encoders, velocities etc.) in base of what specified in the configuration. It currently needs icub-main version equal or higher than `2.7.0`. Specificially this is needed when enabling the parameter `logIRawValuesPublisher`, which is used for dumping any type of raw data values coming from the low level, e.g. raw encoder data. Moreover, if you would like to enable the flag `logIMotorTemperatures`, which will allow to collect motor temperatures in `motors_state::temperatures`, you need to use the yarp framework on [this branch](https://github.com/ami-iit/yarp/tree/yarp-3.10.1-motor-temperature), as explained in [this PR](https://github.com/robotology/yarp/pull/3188).\n\n\u003e[!NOTE]\n    Note that since robometry depends on `icub-main`, if you would like to enable the streaming of the motor temperatures, you need to also modify manually [these lines](https://github.com/robotology/icub-main/blob/master/CMakeLists.txt#L21-L22) in the `CMakeLists.txt` file in icub-main otherwise you will get a complain about the yarp minimum version used. Moreover, consider that, since this is still not merged on a stable branch, you might have compilation issues at the moment.\n\n### Export the env variables\n* Add `${CMAKE_INSTALL_PREFIX}/share/yarp` (where `${CMAKE_INSTALL_PREFIX}` needs to be substituted to the directory that you choose as the `CMAKE_INSTALL_PREFIX`) to your `YARP_DATA_DIRS` environment variable (for more on the `YARP_DATA_DIRS` env variable, see [YARP documentation on data directories](http://www.yarp.it/yarp_data_dirs.html) ).\n* Once you do that, you should be able to find the `telemetryDeviceDumper` device compiled by this repo using the command `yarp plugin telemetryDeviceDumper`, which should have an output similar to:\n~~~\nYes, this is a YARP plugin\n  * library:        CMAKE_INSTALL_PREFIX/lib/yarp/yarp_telemetryDeviceDumper.dll\n  * system version: 5\n  * class name:     robometry::TelemetryDeviceDumper\n  * base class:     yarp::dev::DeviceDriver\n~~~\nIf this is not the case, there could be some problems in finding the plugin. In that case, just move yourself to the `${CMAKE_INSTALL_PREFIX}/share/yarp` directory and launch the device from there.\n\nFurther documentation about the configuration parameters and the mapping of the variables inside the .mat file can be browsed [here](https://robotology.github.io/robometry/classrobometry_1_1TelemetryDeviceDumper.html)\n\n## Contributing\n\nPull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.\n\n\n## License\n[See License](https://github.com/robotology/robometry/blob/master/LICENSE)\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frobotology%2Frobometry","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frobotology%2Frobometry","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frobotology%2Frobometry/lists"}