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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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