26#include <condition_variable>
34#include <rclcpp/rclcpp.hpp>
35#include <realtime_tools/realtime_publisher.hpp>
36#include <rosgraph_msgs/msg/clock.hpp>
38#include "glfw_adapter.h"
41#include <mujoco/mujoco.h>
43#include <geometry_msgs/msg/pose_stamped.hpp>
44#include <geometry_msgs/msg/twist_stamped.hpp>
45#include <mujoco_ros2_control_msgs/msg/free_joint_state.hpp>
46#include <mujoco_ros2_control_msgs/msg/simulation_state.hpp>
47#include <mujoco_ros2_control_msgs/srv/reset_world.hpp>
48#include <mujoco_ros2_control_msgs/srv/set_free_joint_state.hpp>
49#include <mujoco_ros2_control_msgs/srv/set_pause.hpp>
50#include <mujoco_ros2_control_msgs/srv/step_simulation.hpp>
51#include <mujoco_ros2_control_plugins/mujoco_ros2_control_plugins_base.hpp>
52#include <sensor_msgs/msg/joint_state.hpp>
60constexpr double kIdleSleepFraction = 0.2;
75 return std::min(std::chrono::microseconds(1000),
76 std::chrono::microseconds(
static_cast<int64_t
>(std::llround(timestep * kIdleSleepFraction * 1e6))));
170 bool initialize(rclcpp::Node::SharedPtr node,
const std::string& model_path,
const std::string& mujoco_model_topic,
171 double sim_speed_factor,
bool headless);
250 bool set_free_joint_states(
const std::vector<mujoco_ros2_control_msgs::msg::FreeJointState>& free_joints,
251 std::string& error_message);
306 std::vector<mjtNum> qpos;
307 std::vector<mjtNum> qvel;
308 std::vector<mjtNum> act;
309 std::vector<mjtNum> qfrc_actuator;
310 std::vector<mjtNum> sensordata;
311 std::vector<mjtNum> ctrl;
339 RCLCPP_WARN_EXPRESSION(logger_, sim_mutex_ ==
nullptr,
"Sim recursive mutex is still nullptr");
350 return step_count_.load();
359 void apply_staged_control_inputs();
367 void publish_control_state();
376 void refresh_data_snapshot();
386 void publish_clock();
391 void update_sim_display();
403 void reset_world_state(
bool fill_initial_state,
const mujoco_ros2_control_msgs::msg::SimulationState& state_overrides);
412 int frame_body_id(
const std::string& frame_id)
const;
420 bool validate_frame_id(
const std::string& frame_id,
const std::string& field_label, std::string& error_message)
const;
425 int find_free_joint_id(
int body_id)
const;
435 bool validate_free_joint_states(
const std::vector<mujoco_ros2_control_msgs::msg::FreeJointState>& free_joints,
436 std::string& error_message);
446 void apply_free_joint_states(
const std::vector<mujoco_ros2_control_msgs::msg::FreeJointState>& free_joints);
458 bool validate_joint_state_overrides(
const sensor_msgs::msg::JointState& joint_state, std::string& error_message);
466 void apply_joint_state_overrides(
const sensor_msgs::msg::JointState& joint_state);
469 void reset_world_callback(
const std::shared_ptr<mujoco_ros2_control_msgs::srv::ResetWorld::Request> request,
470 std::shared_ptr<mujoco_ros2_control_msgs::srv::ResetWorld::Response> response);
471 void set_pause_callback(
const std::shared_ptr<mujoco_ros2_control_msgs::srv::SetPause::Request> request,
472 std::shared_ptr<mujoco_ros2_control_msgs::srv::SetPause::Response> response);
473 void step_simulation_callback(
const std::shared_ptr<mujoco_ros2_control_msgs::srv::StepSimulation::Request> request,
474 std::shared_ptr<mujoco_ros2_control_msgs::srv::StepSimulation::Response> response);
476 set_free_joint_state_callback(
const std::shared_ptr<mujoco_ros2_control_msgs::srv::SetFreeJointState::Request> request,
477 std::shared_ptr<mujoco_ros2_control_msgs::srv::SetFreeJointState::Response> response);
479 rclcpp::Logger get_logger()
const
485 rclcpp::Logger logger_ = rclcpp::get_logger(
"MujocoSimulation");
488 rclcpp::Node::SharedPtr node_;
491 std::string model_path_;
492 std::string mujoco_model_topic_;
496 mjModel* mj_model_{
nullptr };
500 mjData* mj_data_{
nullptr };
507 mjData* snapshot_write_{
nullptr };
508 mjData* snapshot_read_{
nullptr };
509 bool snapshot_ready_{
false };
512 std::vector<mjtNum> ctrl_staged_;
513 std::vector<mjtNum> qfrc_applied_staged_;
517 bool control_inputs_staged_{
false };
521 std::mutex data_exchange_mutex_;
526 std::atomic<bool> snapshot_refresh_requested_{
true };
533 std::mutex control_staging_mutex_;
539 ControlState control_state_;
540 std::mutex control_state_mutex_;
549 double sim_speed_factor_{ -1.0 };
552 bool headless_{
false };
555 std::unique_ptr<mujoco::Simulate> sim_;
558 std::thread physics_thread_;
559 std::thread ui_thread_;
561 std::atomic<bool> explicit_shutdown_requested_{
false };
564 std::shared_ptr<rclcpp::Publisher<rosgraph_msgs::msg::Clock>> clock_publisher_;
572 std::recursive_mutex* sim_mutex_{
nullptr };
575 rclcpp::CallbackGroup::SharedPtr reset_world_cb_group_;
576 rclcpp::Service<mujoco_ros2_control_msgs::srv::ResetWorld>::SharedPtr reset_world_service_;
579 rclcpp::CallbackGroup::SharedPtr set_pause_cb_group_;
580 rclcpp::Service<mujoco_ros2_control_msgs::srv::SetPause>::SharedPtr set_pause_service_;
583 rclcpp::CallbackGroup::SharedPtr step_simulation_cb_group_;
584 rclcpp::Service<mujoco_ros2_control_msgs::srv::StepSimulation>::SharedPtr step_simulation_service_;
587 rclcpp::CallbackGroup::SharedPtr set_free_joint_state_cb_group_;
588 rclcpp::Service<mujoco_ros2_control_msgs::srv::SetFreeJointState>::SharedPtr set_free_joint_state_service_;
591 std::atomic<uint32_t> pending_steps_{ 0 };
592 std::atomic<bool> step_diverged_{
false };
593 std::atomic<bool> steps_interrupted_{
false };
594 std::atomic<bool> keyboard_step_requested_{
false };
595 std::atomic<uint64_t> step_count_{ 0 };
596 std::mutex steps_cv_mutex_;
597 std::condition_variable steps_cv_;
600 std::vector<mjtNum> initial_qpos_;
601 std::vector<mjtNum> initial_qvel_;
602 std::vector<mjtNum> initial_ctrl_;
ROS 2-based container for the mujoco Simulate application.
Definition mujoco_simulation.hpp:134
bool set_free_joint_states(const std::vector< mujoco_ros2_control_msgs::msg::FreeJointState > &free_joints, std::string &error_message)
Sets the pose and velocity of one or more free-joint objects, identified by body name.
Definition mujoco_simulation.cpp:1269
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:773
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:855
std::recursive_mutex & mutex() const
Accessor for the mutex which locks access to the data and model.
Definition mujoco_simulation.hpp:337
void start_physics_thread()
Start the physics thread. Must be called after load_model().
Definition mujoco_simulation.cpp:797
std::function< void(bool fill_initial_state)> ResetCallback
Callback invoked when the simulation's state must be reset.
Definition mujoco_simulation.hpp:146
void apply_control_data(mjData *control_data)
Stages control fields from control_data for the physics loop in a thread safe way.
Definition mujoco_simulation.cpp:1364
mjData * acquire_data_snapshot()
Borrows the latest completed post-step snapshot of mj_data_ (producer-pays copying).
Definition mujoco_simulation.cpp:1344
void set_pre_step_callback(PreStepCallback callback)
Register the callback run before every mj_step().
Definition mujoco_simulation.cpp:786
void copy_physics_data(mjData *&destination)
Copies mj_data_ into the provided container in a thread safe way.
Definition mujoco_simulation.cpp:1329
void shutdown()
Stop the physics and UI threads if they are running.
Definition mujoco_simulation.cpp:834
bool apply_keyframe(const std::string &keyframe_name)
Apply a keyframe to the simulation by name.
Definition mujoco_simulation.cpp:755
void copy_control_state(ControlState &destination)
Copies the latest per-step control state into the provided container.
Definition mujoco_simulation.cpp:1401
void copy_physics_model(mjModel *&destination)
Copies mj_model_ into the provided container in a thread safe way.
Definition mujoco_simulation.cpp:1315
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:215
mjData * data()
Accessor for the raw mujoco simulation data.
Definition mujoco_simulation.hpp:227
std::function< void(mjData *data)> PreStepCallback
Callback function type the set_pre_step_callback hook.
Definition mujoco_simulation.hpp:155
uint64_t step_count() const
Returns the number of steps takein by the physics simulation.
Definition mujoco_simulation.hpp:348
void overwrite_physics_data(mjData *source)
Copies the provided mjData into mj_data_ in a thread safe way.
Definition mujoco_simulation.cpp:1321
void set_reset_callback(ResetCallback callback)
Register a callback function to be called on reset_world_state.
Definition mujoco_simulation.cpp:781
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:529
std::chrono::microseconds physics_loop_sleep_duration(double timestep)
Idle sleep used by the physics loop between iterations when not busy-waiting.
Definition mujoco_simulation.hpp:73
Small snapshot of the state the hardware interface needs every control cycle.
Definition mujoco_simulation.hpp:304