API Reference / Adding New Sensors

![]() |
![]() |
Introduction
The sensors are responsible for capturing the data needed to adjust the videos. Depending on the application, different types of IMU sensors may be required to measure the angular rate and orientation of objects. To add a new sensor, follow these steps:
- Define the New Sensor class, inheriting from the ISensor class.
- Register the new sensor as either of the following options:
- Inside the ISensor Interface (enum and factory) as a built-in sensor. See Add the New Sensor to ISensor
- Compile it as an external sensor and generate a shared object (.so). See Add the New Sensor as an External_Sensor (new unstable feature).
Define the New Sensor
The RidgeRun Sensor Interface, known as ISensor, is an extensible class designed to support various types of IMU sensors. To add a new sensor, the user must implement a new sensor class that derives from the ISensor class. This class must implement the following methods:
- Start: Initializes and enables the sensor to begin data collection.
- Stop: Stops the sensor.
- Get: Retrieves data from the IMU sensor.
Defining the Start method
This method receives a constant Sensor Parameter configuration. This configuration includes the sensor's operating frequency (in Hz), the sample rate of the measurements (in Hz), and the sensor ID, which helps distinguish the connected sensors.
RuntimeError NewSensorExample::Start( const std::shared_ptr<SensorParams> config) { RuntimeError ret{}; /* Sensor initialization logic */ return ret; }
Defining the Stop method
This method disables data reception from the sensor when called. It does not receive any parameters, so the logic to stop the sensor is managed internally.
RuntimeError NewSensorExample::Stop() { RuntimeError ret{}; /* Sensor stop logic */ return ret; }
Defining the Get method
This method gets data from the IMU sensor. It receives a sensor payload parameter that consists of data from the accelerometer (m/s²), gyroscope (rad/s), and magnetometer (if needed). The data for each axis (x, y, z) includes the corresponding timestamp (in microseconds). The measured values must be saved in the payload to be accessible to the user-space.
RuntimeError NewSensorExample::Get(std::shared_ptr<SensorPayload> payload) { RuntimeError ret{}; /* Get data logic */ /* Assign the accelerator data obtained */ payload->accel.x = accel_data_obtained.x; payload->accel.y = accel_data_obtained.y; payload->accel.z = accel_data_obtained.z; payload->accel.timestamp = accel_data_obtained.sensortime; /* Assign the gyroscope data obtained */ payload->gyro.x = gyro_data_obtained.x; payload->gyro.y = gyro_data_obtained.y; payload->gyro.z = gyro_data_obtained.z; payload->gyro.timestamp = gyro_data_obtained.sensortime; /* Assign the gyroscope data obtained (if measured) */ payload->mag.x = mag_data_obtained.x; payload->mag.y = mag_data_obtained.y; payload->mag.z = mag_data_obtained.z; payload->mag.timestamp = mag_data_obtained.sensortime; return ret; }
It is crucial to take into account that the orientations may change depending on the sensor. For that, please refer to SensorParams::orientation.
Add the New Sensor to ISensor
In order to add the new sensor class to the sensor interface follow these steps:
- Add the constructor method to the new sensor header file.
- Extend the Sensors enumeration with the new IMU sensor.
- Add the new sensor to the ISensor Build method.
Note: follow this steps if you have the source code. Otherwise, follow the Add the New Sensor as an External Sensor section.
Add the constructor method
The constructor method receives the sensor settings parameter. These settings include the device_filename, which refers to the bus where the sensor is mounted.
class NewSensorExample : public ISensor { public: explicit NewSensorExample(std::shared_ptr<SensorSettings> settings); /* Rest of class */ }
Extend the Sensors enumeration
To enable the ISensor interface to create an instance of the new sensor, it must be added to the Sensors enumeration.
enum class Sensors { kRb5Imu = 0, kBmi160 = 0, /* Add new sensor */ kNewSensorExample = 0 };
Add New Sensor to ISensor Build method
Finally, the new sensor case must be added to the ISensor build method to allow the interface to instantiate the sensor.
std::shared_ptr<ISensor> ISensor::Build( const Sensors sensor, const std::shared_ptr<SensorSettings> settings) { switch (sensor) { case Sensors::kRb5Imu: return std::make_shared<Rb5Imu>(settings); case Sensors::kBmi160: return std::make_shared<Bmi160>(settings); /* Add new sensor */ case Sensors::kNewSensorExample: return std::make_shared<NewSensorExample>(settings); default: return nullptr; } }
Testing the Sensor
To test the implementation, please, refer to the BMI160 example to create a test for your sensor.
Add the New Sensor as an External Sensor
Differently from adding a sensor to ISensor (built-in), the external sensor support compiles a shared object that can be used as a floating sensor (without having access to the source code). It is practical to perform development tests before going to built-in and when only the binaries are available (as in an evaluation case).
To add a sensor as an external sensor, please check you have met the following requirements:
- The headers: isensor.hpp and runtimeerror.hpp.
- The evaluation library binary: librvs.so (please, check the Evaluating the Library section).
- The GStreamer-related binaries: libgstcameradrivermeta.so, libgstnvarguscamerasrc.so, libgstrvstabilize.so, libgstrrvideo4linux2.so (please, check the Evaluating the Library section).
- A C++-17 compatible compiler (GNU Compiler is recommended).
- The Makefile build system.
Usually, all the headers and files are given with the evaluation binary pack delivered by RidgeRun. Follow the readme inside of it.
Add the constructor method
The constructor method receives the sensor settings parameter. As an external sensor, the settings parameters cannot be customised, and the device_filename
contains the path to the .so file that is added as an external module. So, setting the device location and other properties flashed into the constructor is highly recommended.
Still, the constructor is necessary and has to have the following signature:
class NewSensorExample : public ISensor { public: explicit NewSensorExample(std::shared_ptr<SensorSettings> settings); /* Rest of class */ }
Compile the new sensor
Assuming the new sensor has been implemented in a file called newsensor.cpp
, it is necessary to prepare a Makefile accordingly. For reference, you can use the following:
# Copyright (C) 2024 RidgeRun, LLC (http://www.ridgerun.com) .phony: all clean SRCS := newsensor.cpp OBJS := $(SRCS:.cpp=.o) TARGET := libnewsensor.so CFLAGS := -g -O0 --std=c++17 -fPIC $(shell pkg-config rvs --cflags) LIBS := $(shell pkg-config rvs --libs) # Define your additional CFLAGS and LIBS below EXT_CFLAGS := EXT_CLIBS := all: $(TARGET) $(TARGET): $(OBJS) $(CXX) $^ -shared -o $@ $(LIBS) $(EXT_CLIBS) %.o: %.cpp $(CXX) -c $< $(CFLAGS) $(EXT_CFLAGS) clean: $(RM) *.o *.so
The Makefile from above assumes that the source code of the external sensor is newsensor.cpp
and the final library name is libnewsensor.so
. Both can be modified in the variables SRCS
and TARGET
, respectively. It is also possible to inject your own dependencies in the EXT_CFLAGS
and in the EXT_CLIBS
libraries.
Afterwards, only run:
make
and the .so file is generated if everything is compiled properly. Within the evaluation binary, you will find a directory called external-imu-example
for your reference.
Test the new sensor
You can use external-imu-example to check if the implementation is able to capture as expected. Please use a command similar to the following:
external-imu-example -d external-imu-example/libnewsensor.so -s 100
Notice that the -d
option indicates the path to the .so file.
If possible, you can also test the GStreamer pipeline:
gst-launch-1.0 ... <Put the capture Here> rvstabilize undistort=cuda imu-sensor=external \ imu-sensor-device="external-imu-example/libnewsensor.so" \ imu-data-order="zyx" stabilizer-smooth-constant=0.2 \ undistort-fov-scale=0.7 \ undistort-intrinsic-matrix="<845.63f, 0.f, 348.764f, 0.f, 849.12470406f, 273.80647627f, 0.f, 0.f, 1.f>" \ ! queue ! ... <Put the sink Here>