Skip to content

webots: use real inertial unit for camera imu#1

Merged
Jiu-xiao merged 12 commits intomasterfrom
feat/webotscamera-lifetime-cleanup-20260427
Apr 28, 2026
Merged

webots: use real inertial unit for camera imu#1
Jiu-xiao merged 12 commits intomasterfrom
feat/webotscamera-lifetime-cleanup-20260427

Conversation

@Jiu-xiao
Copy link
Copy Markdown
Contributor

@Jiu-xiao Jiu-xiao commented Apr 28, 2026

摘要

  • WebotsCamera 改为使用 Webots 真实 GyroAccelerometerInertialUnit 传感器。
  • InertialUnit::getQuaternion()xyzw 输出转换为 libxr 使用的 wxyz
  • 图像采样周期按目标相机帧率设置,不再让相机设备以 basicTimeStep 误跑到 1kHz。
  • 传感器侧时间戳随原始帧/IMU 发布,供 sync 统一处理。

验证

  • Ubuntu24 Webots camera/sync 矩阵 flow=0.1 / 1.0latest_imu / raw_probe:PASS。
  • IMU 仿真域 1000Hz,图像/同步帧约 100Hz,无图像 drop,无 sync pair mismatch。

边界

  • BSP/world 接线在独立 PR。
  • detector/tracker 不在本轮范围内。

Summary by Sourcery

Switch Webots camera IMU handling to use the simulator’s real inertial unit sensors and align camera sampling with the configured frame rate.

New Features:

  • Support reading camera orientation from Webots InertialUnit and publishing quaternion data consistent with existing IMU topics.

Enhancements:

  • Validate and store Webots device and topic names as owned std::string values instead of raw C strings, centralizing name handling in a dedicated helper class.
  • Align Webots camera sampling period with the target image frame rate to reduce unnecessary rendering load while keeping IMU at simulation timestep resolution.
  • Unify image publication through a helper that emits both lightweight sync events and full image payloads with consistent timestamps.

Documentation:

  • Document the requirement for matching Webots IMU devices (gyro, accelerometer, inertial unit) in the camera frame and clarify that quaternion outputs now come from the inertial unit rather than the supervisor node.

@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented Apr 28, 2026

Reviewer's Guide

Refactors WebotsCamera to use real Webots IMU devices (Gyro, Accelerometer, InertialUnit) and align their sampling/timestamping and quaternion conventions with libxr, while adjusting camera sampling to target FPS and updating the README for the new IMU wiring and semantics.

Sequence diagram for WebotsCamera capture loop with real IMU and image scheduling

sequenceDiagram
  participant CaptureThread
  participant WebotsCamera
  participant Webots_Camera_device as Webots_Camera
  participant Webots_Gyro as Webots_Gyro
  participant Webots_Accelerometer as Webots_Accelerometer
  participant Webots_InertialUnit as Webots_InertialUnit
  participant LibXR_Topics as LibXR_Topics

  CaptureThread->>WebotsCamera: StartCaptureThread()
  loop while running
    CaptureThread->>WebotsCamera: ThreadLoopTick()
    WebotsCamera->>WebotsCamera: LibXR_Thread_Sleep(time_step_ms_)
    WebotsCamera->>WebotsCamera: clock = ReadSimClockSample()
    WebotsCamera->>WebotsCamera: InitializeImageScheduleIfNeeded(clock.step)
    WebotsCamera->>WebotsCamera: PollSensorSyncProbeCommand()

    WebotsCamera->>Webots_InertialUnit: getQuaternion()
    Webots_InertialUnit-->>WebotsCamera: raw_xyzw
    WebotsCamera->>WebotsCamera: ReadCameraPoseSample(clock.timestamp, pose) bool
    alt inertial_unit_available
      WebotsCamera->>LibXR_Topics: PublishGimbalRotation(pose)

      WebotsCamera->>Webots_Gyro: getValues()
      Webots_Gyro-->>WebotsCamera: angular_velocity_xyz
      WebotsCamera->>Webots_Accelerometer: getValues()
      Webots_Accelerometer-->>WebotsCamera: linear_acceleration_xyz
      WebotsCamera->>LibXR_Topics: Publish raw_gyro_topic_
      WebotsCamera->>LibXR_Topics: Publish raw_accl_topic_
      WebotsCamera->>LibXR_Topics: Publish raw_quat_topic_

      WebotsCamera->>WebotsCamera: ShouldPublishImageAtStep(clock.step)?
      alt should_publish_image
        WebotsCamera->>Webots_Camera_device: getImage()
        Webots_Camera_device-->>WebotsCamera: BGRA_image
        WebotsCamera->>WebotsCamera: PublishImageFrame(clock)
        WebotsCamera->>LibXR_Topics: PublishImageEvent(timestamp, step)
        WebotsCamera->>LibXR_Topics: PublishImagePayload(timestamp)
        WebotsCamera->>WebotsCamera: AdvanceImageScheduleAfterPublish()
      else skip_image
        WebotsCamera->>WebotsCamera: AdvanceImageScheduleWithoutPublish()
      end

      WebotsCamera->>WebotsCamera: ReportRepeatedFailureIfNeeded()
    else inertial_unit_missing_or_invalid
      WebotsCamera-->>CaptureThread: skip_this_step
    end
  end
Loading

Updated class diagram for WebotsCamera and WebotsCameraNames

classDiagram

class WebotsCameraNames {
  -string device_name_
  -string pose_def_name_
  -string image_topic_name_
  -string imu_topic_name_
  +WebotsCameraNames(device_name, pose_def_name, image_topic_name, imu_topic_name)
  +DeviceNameOwned() string
  +PoseDefNameOwned() string
  +ImageTopicNameOwned() string
  +ImuTopicNameOwned() string
}

class LibXR_Application {
  <<interface>>
}

class CameraBase {
  <<template CameraInfoV>>
}

class WebotsCamera {
  <<template CameraInfoV>>
  -RuntimeParam runtime_
  -string gyro_topic_name_
  -string accl_topic_name_
  -string quat_topic_name_
  -string image_event_topic_name_
  -LibXR_Topic raw_gyro_topic_
  -LibXR_Topic raw_accl_topic_
  -LibXR_Topic raw_quat_topic_
  -LibXR_Topic image_event_topic_
  -LibXR_Topic sensor_sync_cmd_topic_
  -webots_Robot* robot_
  -webots_Camera* cam_
  -webots_Gyro* gyro_
  -webots_Accelerometer* accelerometer_
  -webots_InertialUnit* inertial_unit_
  -ImuSensorNames imu_sensor_names_
  -int time_step_ms_
  -int base_image_interval_steps_
  +WebotsCamera(hw, app, runtime)
  +OnStart() void
  +OnStop() void
  +OnMonitor() void
  -InitRobot() void
  -InitCamera() void
  -InitImuSensors() void
  -ConfigureSamplingOnStartup() void
  -ReadSimClockSample() SimClockSample
  -CaptureOnce(clock) void
  -ReadCameraPoseSample(timestamp, pose) bool
  -PublishGimbalRotation(pose) void
  -PublishImageEvent(timestamp, step) void
  -PublishImagePayload(timestamp) void
  -PublishImageFrame(clock) void
}

class RuntimeParam {
  +string device_name
  +int fps
  +double exposure
  +double gain
  +string pose_def_name
  +string image_topic_name
  +string imu_topic_name
}

class ImuSensorNames {
  +string gyro
  +string accelerometer
  +string inertial_unit
}

class SimClockSample {
  +uint64_t step
  +LibXR_MicrosecondTimestamp timestamp
}

class LibXR_Topic {
}

class webots_Robot {
}
class webots_Camera {
}
class webots_Gyro {
}
class webots_Accelerometer {
}
class webots_InertialUnit {
}

WebotsCameraNames <|-- WebotsCamera
LibXR_Application <|.. WebotsCamera
CameraBase <|.. WebotsCamera

WebotsCamera "1" o-- "1" RuntimeParam
WebotsCamera "1" o-- "1" ImuSensorNames
WebotsCamera "1" o-- "1" SimClockSample
WebotsCamera "1" --> "1" webots_Robot
WebotsCamera "1" --> "1" webots_Camera
WebotsCamera "1" --> "1" webots_Gyro
WebotsCamera "1" --> "1" webots_Accelerometer
WebotsCamera "1" --> "1" webots_InertialUnit
WebotsCamera "1" --> "*" LibXR_Topic
RuntimeParam "1" --> "1" WebotsCameraNames : values_init
Loading

File-Level Changes

Change Details Files
Introduce WebotsCameraNames helper to own and validate runtime string parameters and remove legacy C-string helpers.
  • Add WebotsCameraNames class to store device/pose/topic names with runtime non-empty validation and accessors
  • Change WebotsCamera to privately inherit WebotsCameraNames and pass owned strings into CameraBase
  • Replace const char* runtime fields with std::string in RuntimeParam and remove RequireNonEmptyCStr/RequireNonEmptyString utilities
WebotsCamera.hpp
Switch pose and motion sourcing from Supervisor node orientation to real InertialUnit and convert quaternion convention from xyzw to wxyz.
  • Bind a new InertialUnit device derived from pose_def_name and require it at startup
  • Enable InertialUnit with the basic time step and store it as inertial_unit_
  • Replace Supervisor/cam_node orientation-based pose sampling with ReadCameraPoseSample that reads InertialUnit::getQuaternion(), converts xyzw to libxr wxyz, and feeds the existing zero-calibration pipeline
  • Update motion and gimbal rotation publishing to use the new pose-sampling path and short-circuit OnSimulationStep if pose cannot be read
WebotsCamera.hpp
README.md
Unify IMU device management and logging, and remove Supervisor dependency entirely.
  • Extend ImuSensorNames with inertial_unit and log all three IMU devices on init
  • Fold IMU enabling into InitImuSensors, removing separate EnableImuSensors/DisableImuSensors and associated destructor logic
  • Delete supervisor_ and cam_node_ members and InitSupervisor/RefreshCameraNode code paths so WebotsCamera no longer depends on a Supervisor
WebotsCamera.hpp
Align camera sampling period with target FPS instead of basicTimeStep and adjust first-frame scheduling and image publication helper.
  • Compute base_image_interval_steps_ from FPS, derive a camera_sampling_period_ms, and enable the Camera with that period to reduce unnecessary 1kHz rendering load
  • Change schedule initialization so the first image is published after a full camera period instead of immediately at reset
  • Factor out PublishImageFrame to emit both the ImageEvent and ImagePayload coherently and use it from OnSimulationStep
WebotsCamera.hpp
Tighten timestamping and sync semantics, and clarify IMU wiring and coordinate expectations in documentation.
  • Ensure SimClockSample timestamp/step are propagated consistently through pose, motion, and image publication paths
  • Simplify sensor_sync_cmd topic creation and adjust control-thread sleep comment to describe the REALTIME-driven step behavior
  • Update README to require gyro/accelerometer/inertial_unit devices in the camera frame and to state that camera_quat/gimbal/rotation now come from the InertialUnit instead of Supervisor node orientation
WebotsCamera.hpp
README.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've left some high level feedback:

  • In ReadCameraPoseSample, returning false when inertial_unit_ is null or getQuaternion() returns null will silently drop pose/IMU publishing for that step; consider emitting a throttled log or error path so that misconfigured or failing inertial units are easier to diagnose at runtime.
  • The destructor no longer disables the camera or IMU devices, whereas ConfigureSamplingOnStartup and InitImuSensors call enable on them; if these controllers are reloaded or reused in the same Webots process, consider reintroducing symmetric disable() calls or clarifying that lifetime is single-shot to avoid subtle resource or timing issues.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `ReadCameraPoseSample`, returning `false` when `inertial_unit_` is null or `getQuaternion()` returns null will silently drop pose/IMU publishing for that step; consider emitting a throttled log or error path so that misconfigured or failing inertial units are easier to diagnose at runtime.
- The destructor no longer disables the camera or IMU devices, whereas `ConfigureSamplingOnStartup` and `InitImuSensors` call `enable` on them; if these controllers are reloaded or reused in the same Webots process, consider reintroducing symmetric `disable()` calls or clarifying that lifetime is single-shot to avoid subtle resource or timing issues.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@Jiu-xiao Jiu-xiao merged commit 8d2f8df into master Apr 28, 2026
2 checks passed
@Jiu-xiao Jiu-xiao deleted the feat/webotscamera-lifetime-cleanup-20260427 branch April 28, 2026 21:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant