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
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.
Summary
Implement a
RosPTOModelclass that implements the existingseastack::pto::IPTOModelinterface 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 indocs/HIL_MIGRATION.md.Background
As of PR #3, all four PTO controllers (
PassiveDamper,OptimalPassive,ComplexConjugateControl,ExcitationFeedforwardPID) share the singleIPTOModel::ComputeForce(displacement, velocity, time)interface, and torque is applied to the hinge viaChLinkRSDA+vgoswec::RsdaPtoFunctor(called every Chrono sub-step). This means a ROS 2 HIL controller is a pure drop-in: a newIPTOModelimplementation, 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}implementingIPTOModel:/vgoswec/pto_torque_cmd(std_msgs/msg/Float64) → cache latest commanded torque (mutex-guarded).ComputeForcecall:/vgoswec/flap_angleand/vgoswec/flap_velocity(std_msgs/msg/Float64).ExcitationForceProvider::GetLatestExcitationTorque()on/vgoswec/exc_torqueso hardware controllers can anticipate.ComputeForce(disp, vel, t)returns the latest cached torque command.rosas a new--controllertype indemo_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)
Latency considerations (from HIL_MIGRATION.md)
ComputeForceruns at each Chrono sub-step (dt ≈ 0.005 s). For real-time HIL the ROS callback must deliver torque within one timestep.Acceptance criteria
RosPTOModelimplementsIPTOModeland compiles behind a ROS 2 build guard (no impact on the standalone non-ROS build)./vgoswec/pto_torque_cmd; publishes/vgoswec/flap_angle,/vgoswec/flap_velocity,/vgoswec/exc_torque.--controller ros(or an equivalent ROS 2 node entry point).Related
salhus/Marine_Robotics_HIL_SEA-Stack: integrate this cpp-vgoswec PTO stack (port the HIL node off the legacyChLinkMotorRotationTorquemotor ontoChLinkRSDA+RsdaPtoFunctor).docs/HIL_MIGRATION.md,docs/CONTROLLERS.md.