DataLogger

Raisin provides a buffered data logger with CSV export support.

The global logger is accessed via raisin::DataLogger::getInstance(). Dynamically loaded controllers and plugins should use their injected dataLogger_ instead of the global singleton.

Creating a log directory

Initialize the logger once with createALogFile(deviceType, directory):

raisin::DataLogger::getInstance().createALogFile(
  "raibo2", raisin::getLogDirectory("raisin_data_logger"));

This creates a directory named <deviceType>_YYYY-mm-dd-hh-mm-ss under the provided base directory and writes log_0.raisin_data (rotating across up to 4 files as size grows). In raisin_raibo2_node this call is already handled at startup, so you should not call it again from plugins or controllers.

Logging data groups

Logging starts by declaring a data group with initializeAnotherDataGroup. The ownersName becomes the CSV file prefix, and the args are pairs of std::string label and data values. Supported data types include:

  • Eigen::Matrix<T, S1, S2>: A matrix type from the Eigen library, useful for handling mathematical matrices with specific dimensions.

  • std::vector<T>: A standard vector, which can contain elements of any type T.

  • raisim::VecDyn: A dynamic vector type specific to the Raisim library, typically used in robotics and simulation.

  • raisim::Mat<n, m>: A matrix type from the Raisim library, defined by its dimensions n and m.

  • T: A generic placeholder representing a variable of any basic data type (e.g., int, float, double).

The method returns a size_t logIdx that identifies the group. Use the same order and shapes when calling append; otherwise the reader will warn about mismatched lengths.

For vectors and matrices, the values must be initialized before calling initializeAnotherDataGroup because the logger captures the shape in the label row.

To complete the logging process and export the data to a CSV file, the following steps should be taken:

raisin::DataLogger::getInstance().flush();
raisin::DataReader file(raisin::DataLogger::getInstance().getDataDirectory());
file.exportToCsv();

These steps are also managed within raisin_raibo2_node, so there is no need to repeat them in other parts of the application. exportToCsv writes <ownersName>_<index>.csv files into the same log directory.

To control rotation size, call setAllowedDataSize (bytes). raisin_raibo2_node sets this from max_logging_volume (MB).

For data visualization, you can use tools like PlotJuggler, as detailed in the operation manual.

For practical implementation examples, refer to the test_raisin_data_logger.cpp.

// Copyright (c) 2020 Robotics and Artificial Intelligence Lab, KAIST
//
// Any unauthorized copying, alteration, distribution, transmission,
// performance, display or use of this material is prohibited.
//
// All rights reserved.

// gtest
#include <gtest/gtest.h>

// std
#include <string>
#include <vector>

// ros

// raisin
#include "../include/raisin_data_logger/raisin_data_logger.hpp"

//
// Tests
//

TEST(ParameterTest, ParameterTest1) {
  /// initialize logging variables
  double dDouble1 = 1.5, dDouble2 = 2.5;
  float dFloat1 = 2.3f, dFloat2 = 4.2f;
  bool dBool1 = true, dBool2 = false;
  std::string dString1 = "hello", dString2 = "hi";
  Eigen::MatrixXd dEigen1(20, 10);
  Eigen::MatrixXf dEigen2(3, 2);
  Eigen::Matrix3f dEigen3;
  dEigen1.setConstant(2);
  dEigen2.setConstant(2);
  dEigen3.setConstant(3);
  std::vector<double> dSV = {1, 1, 1};
  std::vector<float> fFV = {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2};
  raisim::Mat<3, 3> rotMat;
  raisim::VecDyn gc(6);
  gc.e() << 0, 1, 2, 3, 4, 5;
  rotMat.setIdentity();

  /// create this log file one in the main function
  raisin::DataLogger::getInstance().createALogFile("test", "/tmp");

  /// initialize a set of
  size_t logIdx1 = raisin::DataLogger::getInstance().initializeAnotherDataGroup(
    "raisin_datalogger_test", "life", dDouble1, "dead", dBool1, "killed", dBool2, "name", dString1,
    "name2", dString2, "mana", dFloat2, "massMat", dEigen1, "bias", dEigen3, "coolTime", dFloat1,
    "dSV", dSV, "fFV", fFV, "gc", gc, "rotMat", rotMat);

  /// initialize a set of
  size_t logIdx2 = raisin::DataLogger::getInstance().initializeAnotherDataGroup(
    "raisin_datalogger_test2", "life", dDouble1, "dead", dBool1, "killed", dBool2);

  raisin::DataLogger::getInstance().setAllowedDataSize(20000000);

  for (int i = 0; i < 71300; i++) {
    auto start = std::chrono::system_clock::now();
    raisin::DataLogger::getInstance().append(logIdx1, dDouble1, dBool1, dBool2, dString1, dString2,
      dFloat2, dEigen1, dEigen3, dFloat1, dSV, fFV, gc, rotMat);

    if (i % 2 == 0) {
      raisin::DataLogger::getInstance().append(logIdx2, dDouble1, dBool1, dBool2);
    }

    auto end = std::chrono::system_clock::now();
    auto elapsedTime = (end - start).count();
    usleep(200);
    // std::cout<<"elapsed time is " << elapsedTime<<std::endl;
    RSFATAL_IF(
      elapsedTime > 1000000, "took too long to save. ellapsed time (us) is " << elapsedTime)
  }

  raisin::DataLogger::getInstance().flush();
  RSINFO("reading " << raisin::DataLogger::getInstance().getDataDirectory())
  raisin::DataReader file(raisin::DataLogger::getInstance().getDataDirectory());

  auto& dataSet = file.getDataSet("raisin_datalogger_test");
  auto data = dataSet.data;

  auto& dataSet2 = file.getDataSet("raisin_datalogger_test2");
  auto data2 = dataSet2.data;

  EXPECT_EQ(1.5, std::stod(data["life"][0]));
  EXPECT_EQ(true, bool(std::stod(data["dead"][0])));
  EXPECT_EQ(false, bool(std::stod(data["killed"][2])));
  EXPECT_EQ(4.2, std::stod(data["mana"][0]));
  EXPECT_EQ(2.0, std::stod(data["massMat_2_3"][6]));
  EXPECT_EQ(3., std::stod(data["bias_2_1"][4]));
  EXPECT_EQ(2.3, std::stod(data["coolTime"][3]));
  EXPECT_EQ("hello", data["name"][5]);
  EXPECT_EQ(1., std::stod(data["dSV_2"][3]));
  EXPECT_EQ(2., std::stod(data["fFV_4"][1]));
  EXPECT_EQ(1., std::stod(data["rotMat_2_2"][1]));
  EXPECT_EQ(3., std::stod(data["gc_3"][1]));

  EXPECT_EQ(1.5, std::stod(data2["life"][0]));

  file.exportToCsv();
}

API

class DataLogger

Public Functions

void createALogFile(const std::string &deviceType, const std::string &directory)

create a log file in a given directory

Parameters:

directory – to create a log file

inline const std::string &getDataDirectory()
Returns:

directory where log file is saved

template<typename ...Args>
inline size_t initializeAnotherDataGroup(const std::string &ownersName, Args... args)
Parameters:
  • ownersName – owner of the log

  • args – pairs of (data name, data variable)

Returns:

index of log counter. this should be used in append

template<typename ...Args>
inline void append(size_t logIdx, Args... args)
Parameters:
  • logIdx – index of log counter.

  • args – data variables

void flush()

flush data

inline void setAllowedDataSize(size_t allowedDataSize)

set the allowed data size

Parameters:

allowedDataSize – (in byte)

class DataReader
struct LoggingSet