Skip to content

Add RosPTOModel: ROS 2 HIL bridge implementing IPTOModel #9

Description

@salhus

Summary

Implement a RosPTOModel class that implements the existing seastack::pto::IPTOModel interface and bridges the Chrono/SEA-Stack PTO control loop to ROS 2, enabling hardware-in-the-loop (HIL) operation of the VGOSWEC-45 flap. This is the drop-in path already sketched in docs/HIL_MIGRATION.md.

Background

As of PR #3, all four PTO controllers (PassiveDamper, OptimalPassive, ComplexConjugateControl, ExcitationFeedforwardPID) share the single IPTOModel::ComputeForce(displacement, velocity, time) interface, and torque is applied to the hinge via ChLinkRSDA + vgoswec::RsdaPtoFunctor (called every Chrono sub-step). This means a ROS 2 HIL controller is a pure drop-in: a new IPTOModel implementation, with zero changes to the physics/wiring.

The full validated physics stack (active PTO, excitation feedforward, RSDA actuation) landed today — see the end-of-day summary and PRs #1#8.

Proposed implementation

Create src/ros_pto_model.{h,cpp} implementing IPTOModel:

  • Subscribe to /vgoswec/pto_torque_cmd (std_msgs/msg/Float64) → cache latest commanded torque (mutex-guarded).
  • Publish flap state each ComputeForce call: /vgoswec/flap_angle and /vgoswec/flap_velocity (std_msgs/msg/Float64).
  • Publish feedforward via ExcitationForceProvider::GetLatestExcitationTorque() on /vgoswec/exc_torque so hardware controllers can anticipate.
  • ComputeForce(disp, vel, t) returns the latest cached torque command.
  • Add ros as a new --controller type in demo_vgoswec.cpp (or a dedicated ROS 2 node target), gated so the non-ROS build is unaffected.

Reference skeleton is in docs/HIL_MIGRATION.md (§"Creating a ROS 2 HIL controller" and §"Dropping it in").

Wiring (unchanged from sim)

auto ros_controller = std::make_shared<RosPTOModel>(node);
auto rsda = chrono_types::make_shared<ChLinkRSDA>();
rsda->Initialize(base_body, flap_body, false, hinge_frame, hinge_frame);
rsda->RegisterTorqueFunctor(std::make_shared<vgoswec::RsdaPtoFunctor>(ros_controller));
system.AddLink(rsda);

Latency considerations (from HIL_MIGRATION.md)

  • ComputeForce runs at each Chrono sub-step (dt ≈ 0.005 s). For real-time HIL the ROS callback must deliver torque within one timestep.
  • Network latency > dt → stale torque (benign for slow systems, but can destabilize resonant WECs near ω₀ — the VGOSWEC operates near resonance by design).
  • Recommend intra-process comms / dedicated real-time executor; run the ROS 2 node on the same machine as the sim.

Acceptance criteria

  • RosPTOModel implements IPTOModel and compiles behind a ROS 2 build guard (no impact on the standalone non-ROS build).
  • Subscribes to /vgoswec/pto_torque_cmd; publishes /vgoswec/flap_angle, /vgoswec/flap_velocity, /vgoswec/exc_torque.
  • Thread-safe torque handoff (mutex or lock-free) between the ROS executor and the Chrono sub-step callback.
  • Selectable via --controller ros (or an equivalent ROS 2 node entry point).
  • A loopback smoke test: an echo node returning a fixed torque produces the expected steady flap response, matching an equivalent in-process controller within tolerance.
  • Documented latency/real-time guidance and a note on resonance sensitivity.

Related

  • Companion issue in salhus/Marine_Robotics_HIL_SEA-Stack: integrate this cpp-vgoswec PTO stack (port the HIL node off the legacy ChLinkMotorRotationTorque motor onto ChLinkRSDA + RsdaPtoFunctor).
  • Docs: docs/HIL_MIGRATION.md, docs/CONTROLLERS.md.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions