Git Product home page Git Product logo

data_tamer's Introduction

Data Tamer

cmake Ubuntu ros2 codecov

DataTamer is a library to log/trace numerical variables over time and takes periodic "snapshots" of their values, to later visualize them as timeseries.

It works great with PlotJuggler, the timeseries visualization tool (note: you will need PlotJuggler 3.8.2 or later).

DataTamer is "fearless data logger" because you can record hundreds or thousands of variables: even 1 million points per second should have a fairly small CPU overhead.

Since all the values are aggregated in a single "snapshot", it is usually meant to record data in a periodic loop (a very frequent use case, in robotics applications).

Kudos to pal_statistics, for inspiring this project.

How it works

architecture

DataTamer can be used to monitor multiple variables in your applications.

Channels are used to take "snapshots" of a subset of variables at a given time. If you want to record at different frequencies, you can use different channels.

DataTamer will forward the collected data to 1 or multiple sinks; a sink may save the information immediately in a file (currently, we support MCAP) or publish it using an inter-process communication, for instance, a ROS2 publisher.

You can easily create your own, specialized sinks.

Use PlotJuggler to visualize your logs offline or in real-time.

Features

  • Serialization schema is created at run-time: no need to do code generation.
  • Suitable for real-time applications: very low latency (on the side of the callee).
  • Multi-sink architecture: recorded data can be forwarded to multiple "backends".
  • Very low serialization overhead, in the order of 1 bit per traced value.
  • The user can enable/disable traced variables at run-time.

Limitations

  • Traced variables can not be added (registered) once the recording starts (first takeSnapshot).
  • Focused on periodic recording. Not the best option for sporadic, asynchronous events.
  • If you use DataTamer::registerValue you must be careful about the lifetime of the object. If you prefer a safer RAII interface, use DataTamer::createLoggedValue instead.

Examples

Basic example

#include "data_tamer/data_tamer.hpp"
#include "data_tamer/sinks/mcap_sink.hpp"

int main()
{
  // Multiple channels can use this sink. Data will be saved in mylog.mcap
  auto mcap_sink = std::make_shared<DataTamer::MCAPSink>("mylog.mcap");

  // Create a channel and attach a sink. A channel can have multiple sinks
  auto channel = DataTamer::LogChannel::create("my_channel");
  channel->addDataSink(mcap_sink);

  // You can register any arithmetic value. You are responsible for their lifetime!
  double value_real = 3.14;
  int value_int = 42;
  auto id1 = channel->registerValue("value_real", &value_real);
  auto id2 = channel->registerValue("value_int", &value_int);

  // If you prefer to use RAII, use this method instead
  // logged_real will unregister itself when it goes out of scope.
  auto logged_real = channel->createLoggedValue<float>("my_real");

  // Store the current value of all the registered values
  channel->takeSnapshot();

  // You can disable (i.e., stop recording) a value like this
  channel->setEnabled(id1, false);
  // or, in the case of a LoggedValue
  logged_real->setEnabled(false);

  // The next snapshot will contain only [value_int], i.e. [id2],
  // since the other two were disabled
  channel->takeSnapshot();
}

How to register custom types

Containers such as std::vector and std::array are supported out of the box. You can also register a custom type, as shown in the example below.

#include "data_tamer/data_tamer.hpp"
#include "data_tamer/sinks/mcap_sink.hpp"
#include "data_tamer/custom_types.hpp"

// a custom type
struct Point3D
{
  double x;
  double y;
  double z;
};

namespace DataTamer
{
template <> struct TypeDefinition<Point3D>
{
  // Provide the name of the type
  std::string typeName() const { return "Point3D"; }
  // List all the member variables that you want to be saved (including their name)
  template <class Function> void typeDef(Function& addField)
  {
    addField("x", &Point3D::x);
    addField("y", &Point3D::y);
    addField("z", &Point3D::z);
  }
}
} // end namespace DataTamer

int main()
{
  auto channel = DataTamer::LogChannel::create("my_channel");
  channel->addDataSink(std::make_shared<DataTamer::MCAPSink>("mylog.mcap"));

  // Array/vectors are supported natively
  std::vector<double> values = {1, 2, 3, 4};
  channel->registerValue("values", &values);

  // Requires the implementation of DataTamer::TypeDefinition<Point3D>
  Point3D position = {0.1, -0.2, 0.3};
  channel->registerValue("position", &position);

  // save the data as usual ...
  channel->takeSnapshot();
}

Compilation

Compiling with ROS2

Just use colcon :)

Compiling with Conan (not ROS2 support)

Note that the ROS2 publisher will NOT be built when using this method.

Assuming conan 2.x installed. From the source directory.

Release:

conan install . -s compiler.cppstd=gnu17 --build=missing -s build_type=Release
cmake -S . -B build/Release -DCMAKE_BUILD_TYPE=Release \
      -DCMAKE_TOOLCHAIN_FILE="build/Release/generators/conan_toolchain.cmake"
cmake --build build/Release --parallel

Debug:

conan install . -s compiler.cppstd=gnu17 --build=missing -s build_type=Debug
cmake -S . -B build/Debug -DCMAKE_BUILD_TYPE=Debug \
      -DCMAKE_TOOLCHAIN_FILE="build/Debug/generators/conan_toolchain.cmake"
cmake --build build/Debug --parallel

How to deserialize data recorded with DataTamer

I will write more extensively about the serialization format used by DataTamer, but for the time being I created a single header file without external dependencies that you can just copy into your project: data_tamer_parser.hpp

You can see how it is used in this example: mcap_reader

data_tamer's People

Contributors

awesomebytes avatar facontidavide avatar henrygerardmoore avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

data_tamer's Issues

Colcon build error

The project name is data_tamer_cpp or data_tamer?

project(data_tamer VERSION 0.9.1)

<name>data_tamer_cpp</name>

--- stderr: data_tamer_cpp                                                                                                                                                           
CMake Error at /opt/ros/humble/share/ament_cmake_core/cmake/core/ament_package_xml.cmake:53 (message):
  ament_package_xml() package name 'data_tamer_cpp' in '/package.xml' does
  not match current PROJECT_NAME 'data_tamer'.  You must call project() with
  the same package name before.
Call Stack (most recent call first):
  /opt/ros/humble/share/ament_cmake_core/cmake/core/ament_package.cmake:63 (ament_package_xml)
  CMakeLists.txt:92 (ament_package)

Another error is

/src/data_tamer/data_tamer_cpp/include/data_tamer_parser/data_tamer_parser.hpp:45:19: note: ‘std::variant’ is only available from C++17 onwards
   45 | using VarNumber = std::variant<bool, char, int8_t, uint8_t, int16_t, uint16_t, int32_t,

Should we add the following line to cmakelist?

set(CMAKE_CXX_STANDARD 17)

Plotjuggler support

I'm not sure if the written MCAP is already readable with plotjuggler or I need a plugin?

MCAP sinks sometimes fail

I modified example.cpp, with the only change being that I used an MCAP sink instead of the dummy sink. Sometimes with this change (not always), the program will fail with the following error.

[henry-picknik-laptop][add_example] data_tamer$ ./build/Debug/examples/example 
terminate called after throwing an instance of 'std::out_of_range'
  what():  _Map_base::at
Aborted (core dumped)

This also happens sometimes (but seems to be more rare) in Release mode:

[henry-picknik-laptop][add_example] data_tamer$ ./build/Release/examples/example 
terminate called after throwing an instance of 'std::out_of_range'
  what():  _Map_base::at
Aborted (core dumped)

Here's a screenshot of my code modifications
image

Question about real time safety of snapshot call

This isn't an issue more of just a question about the snapshot call. I see a lock there so i'm assuming it's not considered a real time safe call? If i'm mistaken could you maybe comment further on it or if you have any future plans to address it? Thanks!

Serialization format for data_tamer

Hi, I would like to understand more about the serialization format of data_tamer. I saw the header file that you provided, however deserialize it in python because I need to use another module which is written in Python.

By going through the code, I understand the msg_buffer consist of

<mask_size> <active_mask> <data_size> <serialized payload one after the other....>

Am I correct?

so with the following example,

  int32_t v3 = 129;
  uint16_t v4 = 129;
  
  Pose pose;
  pose.pos = {4, 4, 4};
  pose.rot = {4,4,4,4};

  channelA->registerValue("val_int32", &v3);
  channelA->registerValue("val_int16", &v4);
  channelA->registerValue("pose", &pose);

The message_buffer in bytestring is:

01 00 00 00 <--- mask_size
ff <--- active_mask
3e 00 00 00 <-- data_size
81 00 00 00 <-- v3
81 00 <-- v4
00 00 00 00 00 00 10 40 <-- should be pose.pose.x, but the encoding is weird.... am I wrong?
00 00 00 00 00 00 10 40 
00 00 00 00 00 00 10 40 
00 00 00 00 00 00 10 40 
00 00 00 00 00 00 10 40 
00 00 00 00 00 00 10 40 
00 00 00 00 00 00 10 40 

Decode mcap files with Python

Hello

great tool! It is really easy to integrate this in C++ code to log data for debugging controller code.
I am able to visualize the mcap files in plotjuggler but I have not managed to open them in python yet.

Do I need to re-implement the DataTamerParser as a DecoderFactory in python?
If yes, do you plan to plan to do this and add a python package to the project?
I thought that one of the advantages of mcap was that the file format is self contained but if it requires the users to write custom parsers it makes things a bit complicated.

Using as static library in ROS1 cause linking error with LZ4

Hello,

I built the library following Compiling with Conan instruction.
Then I want to install so I did sudo cmake --install build/Release (Is this the correct way to install?)

Then in the CMakeList of the ros1 package I did find_package(data_tamer REQUIRED) and
target_link_libraries(${TARGET} data_tamer ${catkin_LIBRARIES} )
and added the example code.

Then after calling catkin_make, I get the following error, would you please give some instruction on using this with ROS1?
Should this be built as shared library?

/usr/bin/ld: /usr/local/lib/libdata_tamer.a(mcap_sink.cpp.o): in function `mcap::ZStdWriter::~ZStdWriter()':
mcap_sink.cpp:(.text+0x9eb): undefined reference to `ZSTD_freeCCtx'
/usr/bin/ld: /usr/local/lib/libdata_tamer.a(mcap_sink.cpp.o): in function `mcap::LZ4Reader::~LZ4Reader()':
mcap_sink.cpp:(.text+0xa60): undefined reference to `LZ4F_freeDecompressionContext'
/usr/bin/ld: /usr/local/lib/libdata_tamer.a(mcap_sink.cpp.o): in function `mcap::ZStdWriter::ZStdWriter(mcap::CompressionLevel, unsigned long)':
mcap_sink.cpp:(.text+0x1a51): undefined reference to `ZSTD_createCCtx'
/usr/bin/ld: mcap_sink.cpp:(.text+0x1a6d): undefined reference to `ZSTD_CCtx_setParameter'
/usr/bin/ld: /usr/local/lib/libdata_tamer.a(mcap_sink.cpp.o): in function `mcap::LZ4Reader::LZ4Reader()':
mcap_sink.cpp:(.text+0x3c84): undefined reference to `LZ4F_createDecompressionContext'
/usr/bin/ld: mcap_sink.cpp:(.text+0x3c8f): undefined reference to `LZ4F_isError'
/usr/bin/ld: mcap_sink.cpp:(.text+0x3cc4): undefined reference to `LZ4F_getErrorName'
/usr/bin/ld: /usr/local/lib/libdata_tamer.a(mcap_sink.cpp.o): in function `mcap::LZ4Writer::end()':
mcap_sink.cpp:(.text+0x71fb): undefined reference to `LZ4F_compressFrameBound'
/usr/bin/ld: mcap_sink.cpp:(.text+0x7229): undefined reference to `LZ4F_compressFrame'
/usr/bin/ld: mcap_sink.cpp:(.text+0x7234): undefined reference to `LZ4F_isError'
/usr/bin/ld: mcap_sink.cpp:(.text+0x72e4): undefined reference to `LZ4F_getErrorName'
/usr/bin/ld: /usr/local/lib/libdata_tamer.a(mcap_sink.cpp.o): in function `mcap::ZStdWriter::end()':
mcap_sink.cpp:(.text+0x732e): undefined reference to `ZSTD_compressBound'
/usr/bin/ld: mcap_sink.cpp:(.text+0x735d): undefined reference to `ZSTD_compress2'
/usr/bin/ld: mcap_sink.cpp:(.text+0x7368): undefined reference to `ZSTD_isError'
/usr/bin/ld: mcap_sink.cpp:(.text+0x737e): undefined reference to `ZSTD_CCtx_reset'
/usr/bin/ld: mcap_sink.cpp:(.text+0x73fc): undefined reference to `ZSTD_getErrorCode'
/usr/bin/ld: mcap_sink.cpp:(.text+0x7422): undefined reference to `ZSTD_getErrorName'
/usr/bin/ld: mcap_sink.cpp:(.text+0x744c): undefined reference to `ZSTD_getErrorString'
/usr/bin/ld: /usr/local/lib/libdata_tamer.a(mcap_sink.cpp.o): in function `mcap::ZStdReader::DecompressAll(std::byte const*, unsigned long, unsigned long, std::vector<std::byte, std::allocator<std::byte> >*)':
mcap_sink.cpp:(.text+0x7681): undefined reference to `ZSTD_decompress'
/usr/bin/ld: mcap_sink.cpp:(.text+0x76d1): undefined reference to `ZSTD_isError'
/usr/bin/ld: mcap_sink.cpp:(.text+0x76e5): undefined reference to `ZSTD_getErrorName'
/usr/bin/ld: /usr/local/lib/libdata_tamer.a(mcap_sink.cpp.o): in function `mcap::LZ4Reader::decompressAll(std::byte const*, unsigned long, unsigned long, std::vector<std::byte, std::allocator<std::byte> >*)':
mcap_sink.cpp:(.text+0x7acb): undefined reference to `LZ4F_resetDecompressionContext'
/usr/bin/ld: mcap_sink.cpp:(.text+0x7af8): undefined reference to `LZ4F_decompress'
/usr/bin/ld: mcap_sink.cpp:(.text+0x7d44): undefined reference to `LZ4F_isError'
/usr/bin/ld: mcap_sink.cpp:(.text+0x7d58): undefined reference to `LZ4F_getErrorName'
collect2: error: ld returned 1 exit status
make[2]: *** [iiwa_interactive_controller/CMakeFiles/iiwa_interactive_controller_cartesian_bringup.dir/build.make:145: /home/yifei/kuka_ws/devel/lib/iiwa_interactive_controller/iiwa_interactive_controller_cartesian_bringup] Error 1
make[1]: *** [CMakeFiles/Makefile2:6997: iiwa_interactive_controller/CMakeFiles/iiwa_interactive_controller_cartesian_bringup.dir/all] Error 2
make: *** [Makefile:146: all] Error 2
Invoking "make -j16 -l16" failed

clang build error

Hi there,

I got the build error data_tamer_cpp/include/data_tamer/channel.hpp:309:22: error: lambda capture 'this' is not used [-Werror,-Wunused-lambda-capture] followed by multiple successive errors in the same vain with using clang17. The self-reference could either be removed or the error suppressed.

Nice work by the way 😀

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.