Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ if(BUILD_PLUGINS)

add_subdirectory(src/plugins/controller_synthetic_hands)
add_subdirectory(src/plugins/generic_3axis_pedal)
add_subdirectory(src/plugins/steering_wheel)
add_subdirectory(src/plugins/manus)
add_subdirectory(src/plugins/haptikos)
if(BUILD_PLUGIN_OAK_CAMERA)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

#pragma once

#include "tracker.hpp"

namespace core
{

struct SteeringWheelOutputTrackedT;

// Abstract base interface for SteeringWheelTracker implementations.
class ISteeringWheelTrackerImpl : public ITrackerImpl
{
public:
virtual const SteeringWheelOutputTrackedT& get_data() const = 0;
};

} // namespace core
2 changes: 2 additions & 0 deletions src/core/deviceio_trackers/cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ add_library(deviceio_trackers STATIC
controller_tracker.cpp
message_channel_tracker.cpp
generic_3axis_pedal_tracker.cpp
steering_wheel_tracker.cpp
frame_metadata_tracker_oak.cpp
full_body_tracker_pico.cpp
inc/deviceio_trackers/head_tracker.hpp
Expand All @@ -18,6 +19,7 @@ add_library(deviceio_trackers STATIC
inc/deviceio_trackers/message_channel_tracker.hpp
inc/deviceio_trackers/full_body_tracker_pico.hpp
inc/deviceio_trackers/generic_3axis_pedal_tracker.hpp
inc/deviceio_trackers/steering_wheel_tracker.hpp
inc/deviceio_trackers/frame_metadata_tracker_oak.hpp
)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

#pragma once

#include <deviceio_base/steering_wheel_tracker_base.hpp>
#include <schema/steering_wheel_generated.h>

#include <cstddef>
#include <string>

namespace core
{

/*!
* @brief Facade for steering wheel state exposed as ``SteeringWheelOutputTrackedT``.
*
* ``SteeringWheelOutput`` uses normalized joystick axis values independent of a specific wheel model.
* Producers should map raw device axes into [-1, 1] before publishing.
*/
class SteeringWheelTracker : public ITracker
{
public:
//! Default maximum FlatBuffer size for SteeringWheelOutput messages.
static constexpr size_t DEFAULT_MAX_FLATBUFFER_SIZE = 1024;

explicit SteeringWheelTracker(const std::string& collection_id,
size_t max_flatbuffer_size = DEFAULT_MAX_FLATBUFFER_SIZE);

std::string_view get_name() const override
{
return TRACKER_NAME;
}

const SteeringWheelOutputTrackedT& get_data(const ITrackerSession& session) const;

const std::string& collection_id() const
{
return collection_id_;
}

size_t max_flatbuffer_size() const
{
return max_flatbuffer_size_;
}

private:
static constexpr const char* TRACKER_NAME = "SteeringWheelTracker";

std::string collection_id_;
size_t max_flatbuffer_size_;
};

} // namespace core
19 changes: 19 additions & 0 deletions src/core/deviceio_trackers/cpp/steering_wheel_tracker.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

#include "inc/deviceio_trackers/steering_wheel_tracker.hpp"

namespace core
{

SteeringWheelTracker::SteeringWheelTracker(const std::string& collection_id, size_t max_flatbuffer_size)
: collection_id_(collection_id), max_flatbuffer_size_(max_flatbuffer_size)
{
}

const SteeringWheelOutputTrackedT& SteeringWheelTracker::get_data(const ITrackerSession& session) const
{
return static_cast<const ISteeringWheelTrackerImpl&>(session.get_tracker_impl(*this)).get_data();
}

} // namespace core
2 changes: 2 additions & 0 deletions src/core/deviceio_trackers/python/deviceio_trackers_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
MessageChannelTracker,
FrameMetadataTrackerOak,
Generic3AxisPedalTracker,
SteeringWheelTracker,
FullBodyTrackerPico,
ITrackerSession,
NUM_JOINTS,
Expand All @@ -28,6 +29,7 @@
"FrameMetadataTrackerOak",
"FullBodyTrackerPico",
"Generic3AxisPedalTracker",
"SteeringWheelTracker",
"HandTracker",
"HeadTracker",
"ITracker",
Expand Down
13 changes: 13 additions & 0 deletions src/core/deviceio_trackers/python/tracker_bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <deviceio_trackers/hand_tracker.hpp>
#include <deviceio_trackers/head_tracker.hpp>
#include <deviceio_trackers/message_channel_tracker.hpp>
#include <deviceio_trackers/steering_wheel_tracker.hpp>
#include <pybind11/numpy.h>
#include <pybind11/stl.h>
#include <schema/hand_generated.h>
Expand Down Expand Up @@ -150,6 +151,18 @@ PYBIND11_MODULE(_deviceio_trackers, m)
{ return self.get_data(session); },
py::arg("session"), "Get the current foot pedal tracked state (data is None when no data available)");

py::class_<core::SteeringWheelTracker, core::ITracker, std::shared_ptr<core::SteeringWheelTracker>>(
m, "SteeringWheelTracker")
.def(py::init<const std::string&, size_t>(), py::arg("collection_id"),
py::arg("max_flatbuffer_size") = core::SteeringWheelTracker::DEFAULT_MAX_FLATBUFFER_SIZE,
"Construct a SteeringWheelTracker for the given tensor collection ID")
.def(
"get_wheel_data",
[](const core::SteeringWheelTracker& self,
const core::ITrackerSession& session) -> core::SteeringWheelOutputTrackedT
{ return self.get_data(session); },
py::arg("session"), "Get the current steering wheel tracked state (data is None when no data available)");

py::class_<core::FullBodyTrackerPico, core::ITracker, std::shared_ptr<core::FullBodyTrackerPico>>(
m, "FullBodyTrackerPico")
.def(py::init<>())
Expand Down
2 changes: 2 additions & 0 deletions src/core/live_trackers/cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ add_library(live_trackers STATIC
live_message_channel_tracker_impl.cpp
live_full_body_tracker_pico_impl.cpp
live_generic_3axis_pedal_tracker_impl.cpp
live_steering_wheel_tracker_impl.cpp
live_frame_metadata_tracker_oak_impl.cpp
inc/live_trackers/schema_tracker_base.hpp
inc/live_trackers/schema_tracker.hpp
Expand All @@ -22,6 +23,7 @@ add_library(live_trackers STATIC
live_message_channel_tracker_impl.hpp
live_full_body_tracker_pico_impl.hpp
live_generic_3axis_pedal_tracker_impl.hpp
live_steering_wheel_tracker_impl.hpp
live_frame_metadata_tracker_oak_impl.hpp
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ class FullBodyTrackerPico;
class IFullBodyTrackerPicoImpl;
class Generic3AxisPedalTracker;
class IGeneric3AxisPedalTrackerImpl;
class SteeringWheelTracker;
class ISteeringWheelTrackerImpl;
class HandTracker;
class IHandTrackerImpl;
class HeadTracker;
Expand Down Expand Up @@ -62,6 +64,7 @@ class LiveDeviceIOFactory
std::unique_ptr<IFullBodyTrackerPicoImpl> create_full_body_tracker_pico_impl(const FullBodyTrackerPico* tracker);
std::unique_ptr<IGeneric3AxisPedalTrackerImpl> create_generic_3axis_pedal_tracker_impl(
const Generic3AxisPedalTracker* tracker);
std::unique_ptr<ISteeringWheelTrackerImpl> create_steering_wheel_tracker_impl(const SteeringWheelTracker* tracker);
std::unique_ptr<IFrameMetadataTrackerOakImpl> create_frame_metadata_tracker_oak_impl(
const FrameMetadataTrackerOak* tracker);

Expand Down
20 changes: 20 additions & 0 deletions src/core/live_trackers/cpp/live_deviceio_factory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "live_hand_tracker_impl.hpp"
#include "live_head_tracker_impl.hpp"
#include "live_message_channel_tracker_impl.hpp"
#include "live_steering_wheel_tracker_impl.hpp"

#include <deviceio_trackers/controller_tracker.hpp>
#include <deviceio_trackers/frame_metadata_tracker_oak.hpp>
Expand All @@ -18,6 +19,7 @@
#include <deviceio_trackers/hand_tracker.hpp>
#include <deviceio_trackers/head_tracker.hpp>
#include <deviceio_trackers/message_channel_tracker.hpp>
#include <deviceio_trackers/steering_wheel_tracker.hpp>
#include <oxr_utils/oxr_time.hpp>

#include <cassert>
Expand Down Expand Up @@ -79,6 +81,12 @@ std::unique_ptr<ITrackerImpl> try_create_generic_pedal_impl(LiveDeviceIOFactory&
return typed ? factory.create_generic_3axis_pedal_tracker_impl(typed) : nullptr;
}

std::unique_ptr<ITrackerImpl> try_create_steering_wheel_impl(LiveDeviceIOFactory& factory, const ITracker& tracker)
{
auto* typed = dynamic_cast<const SteeringWheelTracker*>(&tracker);
return typed ? factory.create_steering_wheel_tracker_impl(typed) : nullptr;
}

std::unique_ptr<ITrackerImpl> try_create_oak_impl(LiveDeviceIOFactory& factory, const ITracker& tracker)
{
auto* typed = dynamic_cast<const FrameMetadataTrackerOak*>(&tracker);
Expand All @@ -102,6 +110,7 @@ inline const TrackerDispatchEntry k_tracker_dispatch[] = {
{ &try_add_extensions<MessageChannelTracker, LiveMessageChannelTrackerImpl>, &try_create_message_channel_impl },
{ &try_add_extensions<FullBodyTrackerPico, LiveFullBodyTrackerPicoImpl>, &try_create_full_body_pico_impl },
{ &try_add_extensions<Generic3AxisPedalTracker, LiveGeneric3AxisPedalTrackerImpl>, &try_create_generic_pedal_impl },
{ &try_add_extensions<SteeringWheelTracker, LiveSteeringWheelTrackerImpl>, &try_create_steering_wheel_impl },
{ &try_add_extensions<FrameMetadataTrackerOak, LiveFrameMetadataTrackerOakImpl>, &try_create_oak_impl },
};

Expand Down Expand Up @@ -244,6 +253,17 @@ std::unique_ptr<IGeneric3AxisPedalTrackerImpl> LiveDeviceIOFactory::create_gener
return std::make_unique<LiveGeneric3AxisPedalTrackerImpl>(handles_, tracker, std::move(channels));
}

std::unique_ptr<ISteeringWheelTrackerImpl> LiveDeviceIOFactory::create_steering_wheel_tracker_impl(
const SteeringWheelTracker* tracker)
{
std::unique_ptr<SteeringWheelMcapChannels> channels;
if (should_record(tracker))
{
channels = LiveSteeringWheelTrackerImpl::create_mcap_channels(*writer_, get_name(tracker));
}
return std::make_unique<LiveSteeringWheelTrackerImpl>(handles_, tracker, std::move(channels));
}

std::unique_ptr<IFrameMetadataTrackerOakImpl> LiveDeviceIOFactory::create_frame_metadata_tracker_oak_impl(
const FrameMetadataTrackerOak* tracker)
{
Expand Down
58 changes: 58 additions & 0 deletions src/core/live_trackers/cpp/live_steering_wheel_tracker_impl.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

#include "live_steering_wheel_tracker_impl.hpp"

#include <mcap/recording_traits.hpp>
#include <schema/steering_wheel_bfbs_generated.h>

namespace core
{

namespace
{

SchemaTrackerConfig make_steering_wheel_tensor_config(const SteeringWheelTracker* tracker)
{
SchemaTrackerConfig cfg;
cfg.collection_id = tracker->collection_id();
cfg.max_flatbuffer_size = tracker->max_flatbuffer_size();
cfg.tensor_identifier = "steering_wheel";
cfg.localized_name = "SteeringWheelTracker";
return cfg;
}

} // namespace

std::unique_ptr<SteeringWheelMcapChannels> LiveSteeringWheelTrackerImpl::create_mcap_channels(mcap::McapWriter& writer,
std::string_view base_name)
{
return std::make_unique<SteeringWheelMcapChannels>(
writer, base_name, SteeringWheelRecordingTraits::schema_name,
std::vector<std::string>(SteeringWheelRecordingTraits::recording_channels.begin(),
SteeringWheelRecordingTraits::recording_channels.end()));
}

LiveSteeringWheelTrackerImpl::LiveSteeringWheelTrackerImpl(const OpenXRSessionHandles& handles,
const SteeringWheelTracker* tracker,
std::unique_ptr<SteeringWheelMcapChannels> mcap_channels)
: mcap_channels_(std::move(mcap_channels)),
m_schema_reader(handles,
make_steering_wheel_tensor_config(tracker),
mcap_channels_.get(),
/*mcap_channel_index=*/0,
/*mcap_channel_tracked_index=*/1)
{
}

void LiveSteeringWheelTrackerImpl::update(int64_t /*monotonic_time_ns*/)
{
m_schema_reader.update(m_tracked.data);
}

const SteeringWheelOutputTrackedT& LiveSteeringWheelTrackerImpl::get_data() const
{
return m_tracked;
}

} // namespace core
52 changes: 52 additions & 0 deletions src/core/live_trackers/cpp/live_steering_wheel_tracker_impl.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

#pragma once

#include "inc/live_trackers/schema_tracker.hpp"

#include <deviceio_trackers/steering_wheel_tracker.hpp>
#include <oxr_utils/oxr_session_handles.hpp>
#include <schema/steering_wheel_generated.h>

#include <cstdint>
#include <memory>
#include <string>
#include <string_view>
#include <vector>

namespace core
{

using SteeringWheelMcapChannels = McapTrackerChannels<SteeringWheelOutputRecord, SteeringWheelOutput>;
using SteeringWheelSchemaTracker = SchemaTracker<SteeringWheelOutputRecord, SteeringWheelOutput>;

class LiveSteeringWheelTrackerImpl : public ISteeringWheelTrackerImpl
{
public:
static std::vector<std::string> required_extensions()
{
return SchemaTrackerBase::get_required_extensions();
}
static std::unique_ptr<SteeringWheelMcapChannels> create_mcap_channels(mcap::McapWriter& writer,
std::string_view base_name);

LiveSteeringWheelTrackerImpl(const OpenXRSessionHandles& handles,
const SteeringWheelTracker* tracker,
std::unique_ptr<SteeringWheelMcapChannels> mcap_channels);

LiveSteeringWheelTrackerImpl(const LiveSteeringWheelTrackerImpl&) = delete;
LiveSteeringWheelTrackerImpl& operator=(const LiveSteeringWheelTrackerImpl&) = delete;
LiveSteeringWheelTrackerImpl(LiveSteeringWheelTrackerImpl&&) = delete;
LiveSteeringWheelTrackerImpl& operator=(LiveSteeringWheelTrackerImpl&&) = delete;

void update(int64_t monotonic_time_ns) override;
const SteeringWheelOutputTrackedT& get_data() const override;

private:
std::unique_ptr<SteeringWheelMcapChannels> mcap_channels_;
SteeringWheelSchemaTracker m_schema_reader;
SteeringWheelOutputTrackedT m_tracked;
};

} // namespace core
7 changes: 7 additions & 0 deletions src/core/mcap/cpp/inc/mcap/recording_traits.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@ struct PedalRecordingTraits
static constexpr std::array replay_channels = { "pedals_tracked" };
};

struct SteeringWheelRecordingTraits
{
static constexpr std::string_view schema_name = "core.SteeringWheelOutputRecord";
static constexpr std::array recording_channels = { "steering_wheel", "steering_wheel_tracked" };
static constexpr std::array replay_channels = { "steering_wheel_tracked" };
};

struct OakRecordingTraits
{
static constexpr std::string_view schema_name = "core.FrameMetadataOakRecord";
Expand Down
Loading