11#include " gz_noisy_odometry/NoisyOdometryPlugin.hpp"
2- #include < gz/sim/components/Pose.hh >
3- #include < gz/plugin/Register.hh >
2+ #include < gz/sim/components/Pose.hpp >
3+ #include < gz/plugin/Register.hpp >
44#include < tf2/LinearMath/Quaternion.h>
55
66GZ_ADD_PLUGIN (
@@ -13,45 +13,30 @@ GZ_ADD_PLUGIN_ALIAS(custom_plugins::NoisyOdometryPlugin, "custom_plugins::NoisyO
1313
1414namespace custom_plugins
1515{
16- NoisyOdometryPlugin::NoisyOdometryPlugin () : generator_ (std::random_device{}())
17- {
18- }
16+ NoisyOdometryPlugin::NoisyOdometryPlugin () {}
1917
2018 void NoisyOdometryPlugin::Configure (const gz::sim::Entity &_entity,
2119 const std::shared_ptr<const sdf::Element> &_sdf,
2220 gz::sim::EntityComponentManager &_ecm,
2321 gz::sim::EventManager &)
2422 {
2523 model_ = gz::sim::Model (_entity);
24+ if (!model_.Valid (_ecm)) return ;
2625
27- if (!model_.Valid (_ecm))
28- {
29- return ;
30- }
31-
32- double linear_std_dev = _sdf->Get <double >(" linear_noise_std_dev" , 0.05 ).first ;
33- double angular_std_dev = _sdf->Get <double >(" angular_noise_std_dev" , 0.05 ).first ;
34-
35- linear_noise_dist_ = std::normal_distribution<double >(0.0 , linear_std_dev);
36- angular_noise_dist_ = std::normal_distribution<double >(0.0 , angular_std_dev);
37-
38- ros_topic_ = _sdf->Get <std::string>(" ros_topic" , " /noisy_odom" ).first ;
39- gz_cmd_vel_topic_ = _sdf->Get <std::string>(" gz_cmd_vel_topic" , " /cmd_vel" ).first ;
26+ // Parámetros del SDF
27+ gaussian_noise_coeff_ = _sdf->Get <double >(" gaussian_noise" , 0.05 ).first ;
28+ ros_topic_ = _sdf->Get <std::string>(" ros_topic" , " /turtlebot3/odom_noisy" ).first ;
29+ gz_cmd_vel_topic_ = _sdf->Get <std::string>(" gz_cmd_vel_topic" , " /turtlebot3/cmd_vel" ).first ;
4030 frame_id_ = _sdf->Get <std::string>(" frame_id" , " odom" ).first ;
41- child_frame_id_ = _sdf->Get <std::string>(" child_frame_id" , " base_link " ).first ;
31+ child_frame_id_ = _sdf->Get <std::string>(" child_frame_id" , " base_footprint " ).first ;
4232
4333 gz_node_.Subscribe (gz_cmd_vel_topic_, &NoisyOdometryPlugin::OnCmdVel, this );
4434
45- if (!rclcpp::ok ())
46- {
47- rclcpp::init (0 , nullptr );
48- }
49-
50- ros_node_ = rclcpp::Node::make_shared (" noisy_odometry_node_" + model_.Name (_ecm));
35+ if (!rclcpp::ok ()) rclcpp::init (0 , nullptr );
36+ ros_node_ = rclcpp::Node::make_shared (" noisy_odom_node_" + model_.Name (_ecm));
5137
52- rclcpp::QoS qos (rclcpp::KeepLast ( 10 ) );
38+ rclcpp::QoS qos (10 );
5339 qos.transient_local ();
54-
5540 ros_pub_ = ros_node_->create_publisher <nav_msgs::msg::Odometry>(ros_topic_, qos);
5641 }
5742
@@ -65,28 +50,22 @@ namespace custom_plugins
6550 void NoisyOdometryPlugin::PostUpdate (const gz::sim::UpdateInfo &_info,
6651 const gz::sim::EntityComponentManager &_ecm)
6752 {
68- if (_info.paused )
69- {
70- return ;
71- }
53+ if (_info.paused ) return ;
7254
7355 if (!initialized_)
7456 {
7557 auto poseComp = _ecm.Component <gz::sim::components::Pose>(model_.Entity ());
76- if (poseComp)
77- {
78- current_pose_ = poseComp-> Data () ;
58+ if (poseComp) {
59+ noisy_pose_internal_ = poseComp-> Data ();
60+ last_update_time_ = _info. simTime ;
7961 initialized_ = true ;
8062 }
8163 return ;
8264 }
8365
84- double dt = std::chrono::duration<double >(_info.dt ).count ();
85-
86- if (dt <= 0.0 )
87- {
88- return ;
89- }
66+ double dt = std::chrono::duration<double >(_info.simTime - last_update_time_).count ();
67+ if (dt <= 0.0 ) return ;
68+ last_update_time_ = _info.simTime ;
9069
9170 double v_cmd, w_cmd;
9271 {
@@ -95,32 +74,38 @@ namespace custom_plugins
9574 w_cmd = current_w_;
9675 }
9776
98- double v_noisy = v_cmd + linear_noise_dist_ (generator_);
99- double w_noisy = w_cmd + angular_noise_dist_ (generator_);
77+ // MATEMÁTICA HÍBRIDA: Integración Estocástica Proporcional
78+ // El ruido es proporcional a la velocidad y escalado por sqrt(dt) para un Random Walk correcto
79+ double linear_noise = gaussian_noise_coeff_ * gz::math::Rand::DblNormal (0 , 1 ) * std::sqrt (dt) * std::abs (v_cmd);
80+ double angular_noise = gaussian_noise_coeff_ * gz::math::Rand::DblNormal (0 , 1 ) * std::sqrt (dt) * std::abs (w_cmd);
10081
101- double yaw = current_pose_.Yaw ();
102- double dx = v_noisy * cos (yaw + (w_noisy * dt / 2.0 )) * dt;
103- double dy = v_noisy * sin (yaw + (w_noisy * dt / 2.0 )) * dt;
104- double dyaw = w_noisy * dt;
82+ double v_noisy = v_cmd + (linear_noise / dt);
83+ double w_noisy = w_cmd + (angular_noise / dt);
10584
106- current_pose_.Pos ().X () += dx;
107- current_pose_.Pos ().Y () += dy;
85+ // Actualización de orientación (Yaw)
86+ double yaw_prev = noisy_pose_internal_.Rot ().Yaw ();
87+ double yaw_new = yaw_prev + (w_noisy * dt);
88+
89+ // Actualización de posición (Cinemática Diferencial)
90+ double distance = v_noisy * dt;
91+ noisy_pose_internal_.Pos ().X () += distance * std::cos (yaw_prev + (w_noisy * dt / 2.0 ));
92+ noisy_pose_internal_.Pos ().Y () += distance * std::sin (yaw_prev + (w_noisy * dt / 2.0 ));
10893
109- gz::math::Quaterniond new_rot ;
110- new_rot .SetFromEuler (0.0 , 0.0 , yaw + dyaw );
111- current_pose_ .Rot () = new_rot ;
94+ gz::math::Quaterniond q_new ;
95+ q_new .SetFromEuler (0 , 0 , yaw_new );
96+ noisy_pose_internal_ .Rot () = q_new ;
11297
98+ // Publicación en ROS 2
11399 nav_msgs::msg::Odometry odom_msg;
114100 odom_msg.header .stamp = ros_node_->now ();
115101 odom_msg.header .frame_id = frame_id_;
116102 odom_msg.child_frame_id = child_frame_id_;
117103
118- odom_msg.pose .pose .position .x = current_pose_.Pos ().X ();
119- odom_msg.pose .pose .position .y = current_pose_.Pos ().Y ();
120- odom_msg.pose .pose .position .z = current_pose_.Pos ().Z ();
121-
104+ odom_msg.pose .pose .position .x = noisy_pose_internal_.Pos ().X ();
105+ odom_msg.pose .pose .position .y = noisy_pose_internal_.Pos ().Y ();
106+
122107 tf2::Quaternion q;
123- q.setRPY (0.0 , 0.0 , current_pose_. Yaw () );
108+ q.setRPY (0 , 0 , yaw_new );
124109 odom_msg.pose .pose .orientation .x = q.x ();
125110 odom_msg.pose .pose .orientation .y = q.y ();
126111 odom_msg.pose .pose .orientation .z = q.z ();
0 commit comments