ros2_control - iron
Loading...
Searching...
No Matches
mujoco_simulation.hpp
1
20#pragma once
21
22#include <atomic>
23#include <condition_variable>
24#include <functional>
25#include <memory>
26#include <mutex>
27#include <string>
28#include <thread>
29#include <vector>
30
31#include <mujoco/mujoco.h>
32
33#include <mujoco_ros2_control_msgs/srv/reset_world.hpp>
34#include <mujoco_ros2_control_msgs/srv/set_pause.hpp>
35#include <mujoco_ros2_control_msgs/srv/step_simulation.hpp>
36#include <rclcpp/rclcpp.hpp>
37#include <realtime_tools/realtime_publisher.hpp>
38#include <rosgraph_msgs/msg/clock.hpp>
39
40#include "glfw_adapter.h" // for mj::GlfwAdapter
41#include "simulate.h" // must be on your include path, handled by CMake
42
43namespace mujoco_ros2_control
44{
45
65{
66public:
79 using ResetCallback = std::function<void(bool fill_initial_state)>;
80
84 MujocoSimulation() = default;
85
87
94 bool initialize(rclcpp::Node::SharedPtr node, const std::string& model_path, const std::string& mujoco_model_topic,
95 double sim_speed_factor, bool headless);
96
103 bool apply_keyframe(const std::string& keyframe_name);
104
112
116 void set_reset_callback(ResetCallback callback);
117
122
126 void shutdown();
127
131 mjModel* model()
132 {
133 return mj_model_;
134 }
135
139 mjData* data()
140 {
141 return mj_data_;
142 }
143
147 mjData* control_data()
148 {
149 return mj_data_control_;
150 }
151
155 std::recursive_mutex& mutex() const
156 {
157 RCLCPP_WARN_EXPRESSION(logger_, sim_mutex_ == nullptr, "Sim recursive mutex is still nullptr");
158 return *sim_mutex_;
159 }
160
166 std::vector<mjtNum>& xfrc_plugin_desired()
167 {
168 return xfrc_plugin_desired_;
169 }
170
175 void reset_world_state(bool fill_initial_state);
176
177private:
181 void physics_loop();
182
186 void publish_clock();
187
191 void update_sim_display();
192
193 // Service callbacks
194 void reset_world_callback(const std::shared_ptr<mujoco_ros2_control_msgs::srv::ResetWorld::Request> request,
195 std::shared_ptr<mujoco_ros2_control_msgs::srv::ResetWorld::Response> response);
196 void set_pause_callback(const std::shared_ptr<mujoco_ros2_control_msgs::srv::SetPause::Request> request,
197 std::shared_ptr<mujoco_ros2_control_msgs::srv::SetPause::Response> response);
198 void step_simulation_callback(const std::shared_ptr<mujoco_ros2_control_msgs::srv::StepSimulation::Request> request,
199 std::shared_ptr<mujoco_ros2_control_msgs::srv::StepSimulation::Response> response);
200
201 rclcpp::Logger get_logger() const
202 {
203 return logger_;
204 }
205
206 // Logger
207 rclcpp::Logger logger_ = rclcpp::get_logger("MujocoSimulation");
208
209 // ROS node (owned by the HW interface, used here for services and clock publisher).
210 rclcpp::Node::SharedPtr node_;
211
212 // System information
213 std::string model_path_;
214 std::string mujoco_model_topic_;
215
216 // MuJoCo data pointers
217 mjModel* mj_model_{ nullptr };
218 mjData* mj_data_{ nullptr };
219
220 // Data container for control data
221 mjData* mj_data_control_{ nullptr };
222
223 // TODO: Conslidate the control and these buffer to provide consistent, clear access for
224 // for mujoco data.
225 //
226 // Dedicated buffer for plugin xfrc contributions.
227 //
228 // mj_copyData contaminates mj_data_control_->xfrc_applied with viewer forces, so we cannot
229 // rely on mj_data_control_->xfrc_applied to hold plugin forces across physics iterations.
230 // Instead, the control thread (read()) zeroes mj_data_control_->xfrc_applied before every
231 // plugin update, lets plugins write fresh forces, then copies the result here. The physics
232 // thread uses this buffer (not mj_data_control_->xfrc_applied) when composing each mj_step.
233 // Because this buffer is never touched by mj_copyData, it holds the last plugin contribution
234 // cleanly until the next control cycle — no restore and no undo mechanism required.
235 std::vector<mjtNum> xfrc_viewer_capture_;
236 std::vector<mjtNum> xfrc_plugin_desired_;
241 std::vector<mjtNum> xfrc_last_restore_;
242
243 // For rendering
244 mjvCamera cam_;
245 mjvOption opt_;
246 mjvPerturb pert_;
247
248 // Speed scaling parameter. if set to >0 then we ignore the value set in the simulate app and instead
249 // attempt to loop at whatever this is set to. If this is <0, then we use the value from the app.
250 double sim_speed_factor_{ -1.0 };
251
252 // True when running without a display (no GLFW window)
253 bool headless_{ false };
254
255 // Primary simulate object
256 std::unique_ptr<mujoco::Simulate> sim_;
257
258 // Threads for rendering physics and the UI simulation
259 std::thread physics_thread_;
260 std::thread ui_thread_;
261
262 // Primary clock publisher for the world
263 std::shared_ptr<rclcpp::Publisher<rosgraph_msgs::msg::Clock>> clock_publisher_;
265
266 // Mutex used inside simulate.h for protecting model/data, we keep a reference
267 // here to protect access to shared data.
268 // TODO: It would be far better to put all relevant data into a single container with accessors
269 // in a common location rather than passing around the raw pointer to the mutex, but it would
270 // require more work to pull it out of simulate.h.
271 std::recursive_mutex* sim_mutex_{ nullptr };
272
273 // Reset world service
274 rclcpp::CallbackGroup::SharedPtr reset_world_cb_group_;
275 rclcpp::Service<mujoco_ros2_control_msgs::srv::ResetWorld>::SharedPtr reset_world_service_;
276
277 // Set pause service
278 rclcpp::CallbackGroup::SharedPtr set_pause_cb_group_;
279 rclcpp::Service<mujoco_ros2_control_msgs::srv::SetPause>::SharedPtr set_pause_service_;
280
281 // Step simulation service
282 rclcpp::CallbackGroup::SharedPtr step_simulation_cb_group_;
283 rclcpp::Service<mujoco_ros2_control_msgs::srv::StepSimulation>::SharedPtr step_simulation_service_;
284
285 // Pending steps to execute while paused, and synchronization for blocking callers
286 std::atomic<uint32_t> pending_steps_{ 0 };
287 std::atomic<bool> step_diverged_{ false };
288 std::atomic<bool> steps_interrupted_{ false };
289 std::atomic<bool> keyboard_step_requested_{ false };
290 std::atomic<uint64_t> step_count_{ 0 };
291 std::mutex steps_cv_mutex_;
292 std::condition_variable steps_cv_;
293
294 // Storage for initial state (used for reset_world)
295 std::vector<mjtNum> initial_qpos_;
296 std::vector<mjtNum> initial_qvel_;
297 std::vector<mjtNum> initial_ctrl_;
298
299 // Callback into the HW interface to perform component-side reset bookkeeping.
300 ResetCallback reset_callback_;
301};
302
303} // namespace mujoco_ros2_control
ROS 2-based container for the mujoco Simulate application.
Definition mujoco_simulation.hpp:65
void capture_initial_state()
Can be called by consumers of this class to store the current state as the "initial" state.
Definition mujoco_simulation.cpp:681
void reset_world_state(bool fill_initial_state)
Reset simulation state (qpos/qvel/ctrl/sensors/forces) to the captured initial state.
Definition mujoco_simulation.cpp:750
std::recursive_mutex & mutex() const
Accessor for the mutex which locks access to the data and model.
Definition mujoco_simulation.hpp:155
void start_physics_thread()
Start the physics thread. Must be called after load_model().
Definition mujoco_simulation.cpp:694
std::function< void(bool fill_initial_state)> ResetCallback
Callback invoked when the simulation's state must be reset.
Definition mujoco_simulation.hpp:79
mjData * control_data()
Accessor for the mujoco control data.
Definition mujoco_simulation.hpp:147
std::vector< mjtNum > & xfrc_plugin_desired()
Accessor for the stacking applied forces, these values will be added to xfrc_applied.
Definition mujoco_simulation.hpp:166
void shutdown()
Stop the physics and UI threads if they are running.
Definition mujoco_simulation.cpp:731
bool apply_keyframe(const std::string &keyframe_name)
Apply a keyframe to the simulation by name.
Definition mujoco_simulation.cpp:663
MujocoSimulation()=default
Construct a new Mujoco Simulation object. This is a no-op until initialization.
mjModel * model()
Accessor for the mujoco model.
Definition mujoco_simulation.hpp:131
mjData * data()
Accessor for the mujoco data.
Definition mujoco_simulation.hpp:139
void set_reset_callback(ResetCallback callback)
Register a callback function to be called on reset_world_state.
Definition mujoco_simulation.cpp:689
bool initialize(rclcpp::Node::SharedPtr node, const std::string &model_path, const std::string &mujoco_model_topic, double sim_speed_factor, bool headless)
Construct the Simulate application and start the UI thread (if not headless).
Definition mujoco_simulation.cpp:486
Definition realtime_publisher.hpp:55
Definition data.hpp:31