Writing a driver for a new system#
A driver translates a mocap system into the MOCAP4ROS2 interfaces. Everything else —
lifecycle, start/stop, system announcement, diagnostics, stamps, frame numbers, TF —
comes from mocap4r2_control, so every tool works with the new system from the
first frame.
#include "mocap4r2_control/frame_publisher.hpp"
#include "mocap4r2_control/mocap_system_node.hpp"
#include "mocap4r2_control/system_main.hpp"
class MyDriver : public mocap4r2_control::MocapSystemNode
{
public:
explicit MyDriver(const rclcpp::NodeOptions & options = rclcpp::NodeOptions())
: MocapSystemNode("my_driver", "my_system", options)
{
declare_parameter<std::string>("server", "192.168.1.5");
mocap4r2_control::declare_frame_publisher_parameters(*this);
}
~MyDriver() override {stop();} // stop SDK threads before the node goes away
protected:
CallbackReturn on_system_configure() override
{
sdk_.connect(get_parameter("server").as_string()); // throw on failure
publisher_ = std::make_unique<mocap4r2_control::FramePublisher>(*this);
return CallbackReturn::SUCCESS;
}
CallbackReturn on_system_activate() override
{
sdk_.on_frame([this](const SdkFrame & f) {frame(f);}); // SDK thread
return CallbackReturn::SUCCESS;
}
CallbackReturn on_system_deactivate() override {stop(); return CallbackReturn::SUCCESS;}
CallbackReturn on_system_cleanup() override
{
sdk_.disconnect();
publisher_.reset();
return CallbackReturn::SUCCESS;
}
double nominal_frame_rate() const override {return sdk_.rate();}
private:
void frame(const SdkFrame & f)
{
mocap4r2_control::MocapFrame out;
out.frame_number = f.number; // the system's own number
out.capture_time = now() - rclcpp::Duration::from_seconds(f.latency);
// fill out.markers, out.rigid_bodies, out.skeletons: metres, Z-up,
// stable ids, tracking_valid = false for untracked bodies
publisher_->publish(std::move(out)); // thread safe, drops if inactive
}
void stop() {sdk_.on_frame(nullptr);}
MySdk sdk_;
std::unique_ptr<mocap4r2_control::FramePublisher> publisher_;
};
int main(int argc, char ** argv)
{
return mocap4r2_control::system_main<MyDriver>(argc, argv);
}
Rules#
Convert to metres and Z-up in the driver; never leave axis fixes to consumers.
Ids must be stable across frames (from the SDK or from names); never use the position in an array.
Never publish NaN or zero poses for untracked bodies: set
tracking_valid: false.Read the SDK in its own thread; the base class gives you
is_active()and theFramePublisheris safe to call from any thread.Stop every SDK thread in
on_system_deactivate/on_system_cleanupand in the destructor.Write the SDK-independent part (frame conversion) as pure functions and test them; the existing drivers test their integration against fake servers or fake sources.
The OptiTrack, Vicon and Qualisys drivers are complete examples of this structure.