ros2_control - jazzy
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 <rclcpp/rclcpp.hpp>
32#include <realtime_tools/realtime_publisher.hpp>
33#include <rosgraph_msgs/msg/clock.hpp>
34
35#include "glfw_adapter.h" // for mj::GlfwAdapter
36#include "simulate.h" // must be on your include path, handled by CMake
37
38#include <mujoco/mujoco.h>
39
40#include <mujoco_ros2_control_msgs/srv/reset_world.hpp>
41#include <mujoco_ros2_control_msgs/srv/set_pause.hpp>
42#include <mujoco_ros2_control_msgs/srv/step_simulation.hpp>
43#include <mujoco_ros2_control_plugins/mujoco_ros2_control_plugins_base.hpp>
44
45namespace mujoco_ros2_control
46{
47
87{
88public:
99 using ResetCallback = std::function<void(bool fill_initial_state)>;
100
104 MujocoSimulation() = default;
105
107
114 bool initialize(rclcpp::Node::SharedPtr node, const std::string& model_path, const std::string& mujoco_model_topic,
115 double sim_speed_factor, bool headless);
116
123 bool apply_keyframe(const std::string& keyframe_name);
124
132
136 void set_reset_callback(ResetCallback callback);
137
142
146 void shutdown();
147
151 mjModel* model()
152 {
153 return mj_model_;
154 }
155
163 mjData* data()
164 {
165 return mj_data_;
166 }
167
172 void reset_world_state(bool fill_initial_state);
173
179 void copy_physics_model(mjModel*& destination);
180
186 void overwrite_physics_data(mjData* source);
187
194 void copy_physics_data(mjData*& destination);
195
204 void apply_control_data(mjData* control_data);
205
209 std::recursive_mutex& mutex() const
210 {
211 RCLCPP_WARN_EXPRESSION(logger_, sim_mutex_ == nullptr, "Sim recursive mutex is still nullptr");
212 return *sim_mutex_;
213 }
214
220 uint64_t step_count() const
221 {
222 return step_count_.load();
223 }
224
225private:
231 void handle_xfrc_applied();
232
236 void physics_loop();
237
241 void publish_clock();
242
246 void update_sim_display();
247
248 // Service callbacks
249 void reset_world_callback(const std::shared_ptr<mujoco_ros2_control_msgs::srv::ResetWorld::Request> request,
250 std::shared_ptr<mujoco_ros2_control_msgs::srv::ResetWorld::Response> response);
251 void set_pause_callback(const std::shared_ptr<mujoco_ros2_control_msgs::srv::SetPause::Request> request,
252 std::shared_ptr<mujoco_ros2_control_msgs::srv::SetPause::Response> response);
253 void step_simulation_callback(const std::shared_ptr<mujoco_ros2_control_msgs::srv::StepSimulation::Request> request,
254 std::shared_ptr<mujoco_ros2_control_msgs::srv::StepSimulation::Response> response);
255
256 rclcpp::Logger get_logger() const
257 {
258 return logger_;
259 }
260
261 // Logger
262 rclcpp::Logger logger_ = rclcpp::get_logger("MujocoSimulation");
263
264 // ROS node (owned by the HW interface, used here for services and clock publisher).
265 rclcpp::Node::SharedPtr node_;
266
267 // System information
268 std::string model_path_;
269 std::string mujoco_model_topic_;
270
271 // MuJoCo data pointers, these are the primary containers used by the physics simulation.
272 // It is generally not recommended to interact with them directly.
273 mjModel* mj_model_{ nullptr };
274
275 // Primary data container for the physics loop. We do not recommend interacting with this
276 // directly unless you are sure of what you are doing.
277 mjData* mj_data_{ nullptr };
278
279 // Buffers to track actively applied Cartesian forces from both the plugins and the Simulate /
280 // viewer-only drag forces.
281 std::vector<mjtNum> xfrc_plugin_desired_; // Tracks forces from plugins
282 std::vector<mjtNum> xfrc_viewer_capture_; // Tracks forces from the viewer
283 std::vector<mjtNum> xfrc_last_written_; // tracks the last value written to xfrc_applied
284
285 // For rendering
286 mjvCamera cam_;
287 mjvOption opt_;
288 mjvPerturb pert_;
289
290 // Speed scaling parameter. if set to >0 then we ignore the value set in the simulate app and instead
291 // attempt to loop at whatever this is set to. If this is <0, then we use the value from the app.
292 double sim_speed_factor_{ -1.0 };
293
294 // True when running without a display (no GLFW window)
295 bool headless_{ false };
296
297 // Primary simulate object
298 std::unique_ptr<mujoco::Simulate> sim_;
299
300 // Threads for rendering physics and the UI simulation
301 std::thread physics_thread_;
302 std::thread ui_thread_;
303
304 // Primary clock publisher for the world
305 std::shared_ptr<rclcpp::Publisher<rosgraph_msgs::msg::Clock>> clock_publisher_;
307
308 // Mutex used inside simulate.h for protecting model/data, we keep a reference
309 // here to protect access to shared data.
310 // TODO: It would be far better to put all relevant data into a single container with accessors
311 // in a common location rather than passing around the raw pointer to the mutex, but it would
312 // require more work to pull it out of simulate.h.
313 std::recursive_mutex* sim_mutex_{ nullptr };
314
315 // Reset world service
316 rclcpp::CallbackGroup::SharedPtr reset_world_cb_group_;
317 rclcpp::Service<mujoco_ros2_control_msgs::srv::ResetWorld>::SharedPtr reset_world_service_;
318
319 // Set pause service
320 rclcpp::CallbackGroup::SharedPtr set_pause_cb_group_;
321 rclcpp::Service<mujoco_ros2_control_msgs::srv::SetPause>::SharedPtr set_pause_service_;
322
323 // Step simulation service
324 rclcpp::CallbackGroup::SharedPtr step_simulation_cb_group_;
325 rclcpp::Service<mujoco_ros2_control_msgs::srv::StepSimulation>::SharedPtr step_simulation_service_;
326
327 // Pending steps to execute while paused, and synchronization for blocking callers
328 std::atomic<uint32_t> pending_steps_{ 0 };
329 std::atomic<bool> step_diverged_{ false };
330 std::atomic<bool> steps_interrupted_{ false };
331 std::atomic<bool> keyboard_step_requested_{ false };
332 std::atomic<uint64_t> step_count_{ 0 };
333 std::mutex steps_cv_mutex_;
334 std::condition_variable steps_cv_;
335
336 // Storage for initial state (used for reset_world)
337 std::vector<mjtNum> initial_qpos_;
338 std::vector<mjtNum> initial_qvel_;
339 std::vector<mjtNum> initial_ctrl_;
340
341 // Callback into the HW interface to perform component-side reset bookkeeping.
342 ResetCallback reset_callback_;
343};
344
345} // namespace mujoco_ros2_control
ROS 2-based container for the mujoco Simulate application.
Definition mujoco_simulation.hpp:87
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:209
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:99
void apply_control_data(mjData *control_data)
Copies control fields from control_data into the sim data in a thread safe way.
Definition mujoco_simulation.cpp:950
void copy_physics_data(mjData *&destination)
Copies mj_data_ into the provided container in a thread safe way.
Definition mujoco_simulation.cpp:939
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
void copy_physics_model(mjModel *&destination)
Copies mj_model_ into the provided container in a thread safe way.
Definition mujoco_simulation.cpp:927
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:151
mjData * data()
Accessor for the raw mujoco simulation data.
Definition mujoco_simulation.hpp:163
uint64_t step_count() const
Returns the number of steps takein by the physics simulation.
Definition mujoco_simulation.hpp:220
void overwrite_physics_data(mjData *source)
Copies the provided mjData into mj_data_ in a thread safe way.
Definition mujoco_simulation.cpp:933
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:482
Definition realtime_publisher.hpp:55
Definition data.hpp:31