diff --git a/src/g2_2025_odometry_pkg/CMakeLists.txt b/src/g2_2025_odometry_pkg/CMakeLists.txt index e98170a..5c5fdb9 100644 --- a/src/g2_2025_odometry_pkg/CMakeLists.txt +++ b/src/g2_2025_odometry_pkg/CMakeLists.txt @@ -7,9 +7,32 @@ endif() # find dependencies find_package(ament_cmake REQUIRED) -# uncomment the following section in order to fill in -# further dependencies manually. -# find_package( REQUIRED) +find_package(rclcpp REQUIRED) +find_package(sensor_msgs REQUIRED) +find_package(geometry_msgs REQUIRED) + +# Add executable for position_speed_approximator_node +add_executable(position_speed_approximator_node + src/g2_2025_imu_position_approximator_node/nodes/IMUPositionApproximator.cpp + src/g2_2025_imu_position_approximator_node/Main.cpp +) + +target_include_directories(position_speed_approximator_node PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/src + ${CMAKE_CURRENT_SOURCE_DIR}/src/g2_2025_imu_position_approximator_node +) + +ament_target_dependencies(position_speed_approximator_node + rclcpp + sensor_msgs + geometry_msgs +) + +# Install the executable +install(TARGETS + position_speed_approximator_node + DESTINATION lib/${PROJECT_NAME} +) find_package(rclcpp REQUIRED) find_package(sensor_msgs REQUIRED) find_package(geometry_msgs REQUIRED) diff --git a/src/g2_2025_odometry_pkg/package.xml b/src/g2_2025_odometry_pkg/package.xml index de60dca..afe8611 100644 --- a/src/g2_2025_odometry_pkg/package.xml +++ b/src/g2_2025_odometry_pkg/package.xml @@ -9,6 +9,10 @@ ament_cmake + rclcpp + sensor_msgs + geometry_msgs + ament_lint_auto ament_lint_common ament_cmake_gtest diff --git a/src/g2_2025_odometry_pkg/src/g2_2025_imu_position_approximator_node/Main.cpp b/src/g2_2025_odometry_pkg/src/g2_2025_imu_position_approximator_node/Main.cpp new file mode 100644 index 0000000..0609745 --- /dev/null +++ b/src/g2_2025_odometry_pkg/src/g2_2025_imu_position_approximator_node/Main.cpp @@ -0,0 +1,14 @@ +#include "rclcpp/rclcpp.hpp" + +#include "nodes/IMUPositionApproximator.hpp" + +int main(int argc, char *argv[]) { + rclcpp::init(argc, argv); + + auto node = std::make_shared(); + + rclcpp::spin(node->get_node_base_interface()); + rclcpp::shutdown(); + + return 0; +} diff --git a/src/g2_2025_odometry_pkg/src/g2_2025_imu_position_approximator_node/nodes/IMUPositionApproximator.cpp b/src/g2_2025_odometry_pkg/src/g2_2025_imu_position_approximator_node/nodes/IMUPositionApproximator.cpp new file mode 100644 index 0000000..a16304c --- /dev/null +++ b/src/g2_2025_odometry_pkg/src/g2_2025_imu_position_approximator_node/nodes/IMUPositionApproximator.cpp @@ -0,0 +1,126 @@ +#include "IMUPositionApproximator.hpp" + +namespace assignments::three::g2_2025_imu_position_approximator_node { + +IMUPositionApproximator::IMUPositionApproximator() +: Node("imu_position_approximator") +, x_(0.0), y_(0.0), z_(0.0) +, theta_(0.0) +, vx_(0.0), vy_(0.0), vz_(0.0) +, last_time_(this->now()) +{ + RCLCPP_INFO(this->get_logger(), "IMU Position Approximator node started"); + + // Get initial position from parameters + x_ = this->declare_parameter("initial_x", 0.0); + y_ = this->declare_parameter("initial_y", 0.0); + z_ = this->declare_parameter("initial_z", 0.0); + theta_ = this->declare_parameter("initial_theta", 0.0); + imu_topic_ = this->declare_parameter("imu_topic", "imu_data"); + + RCLCPP_INFO(this->get_logger(), + "Initial position set to: x=%.3f, y=%.3f, z=%.3f, theta=%.3f rad", + x_, y_, z_, theta_); + + + imu_subscription_ = this->create_subscription( + imu_topic_, 10, + std::bind(&IMUPositionApproximator::imu_callback, this, std::placeholders::_1) + ); + + position_reset_subscription_ = this->create_subscription( + "position_reset", 10, + std::bind(&IMUPositionApproximator::position_reset_callback, this, std::placeholders::_1) + ); + + RCLCPP_INFO(this->get_logger(), "Subscribed to '%s' and 'position_reset'", imu_topic_.c_str()); + + position_publisher_ = this->create_publisher("estimated_position", 10); + velocity_publisher_ = this->create_publisher("estimated_velocity", 10); + + RCLCPP_INFO(this->get_logger(), "Publishing to 'estimated_position' and 'estimated_velocity'"); +} + +void IMUPositionApproximator::imu_callback(const sensor_msgs::msg::Imu::SharedPtr msg) { + auto current_time = this->now(); + double dt = (current_time - last_time_).seconds(); + last_time_ = current_time; + + if (dt <= 0.0 || dt > 1.0) { + RCLCPP_WARN(this->get_logger(), "skipping iteration due to invalid delta time (dt=%.3f)", dt); + return; + } + + Position pos_robot; + pos_robot.x = msg->linear_acceleration.x; + pos_robot.y = msg->linear_acceleration.y; + // remove gravity, standard gravity is ~9.81 m/s^2 + pos_robot.z = msg->linear_acceleration.z - 9.81; + + // calculate and normalize theta_ to be within [-pi, pi] + double omega_z = msg->angular_velocity.z; + theta_ += omega_z * dt; + + while (theta_ > M_PI) theta_ -= 2.0 * M_PI; + while (theta_ < -M_PI) theta_ += 2.0 * M_PI; + + // transform from robot frame to map frame + Position pos_map; + double cos_theta = std::cos(theta_); + double sin_theta = std::sin(theta_); + pos_map.x = pos_robot.x * cos_theta - pos_robot.y * sin_theta; + pos_map.y = pos_robot.x * sin_theta + pos_robot.y * cos_theta; + pos_map.z = pos_robot.z; + + // account for centripetal acceleration due to rotation when rotating, + // linear velocity creates centripetal acceleration + pos_map.x -= -omega_z * vy_; + pos_map.y -= omega_z * vx_; + + // integrate acceleration to get velocity + vx_ += pos_map.x * dt; + vy_ += pos_map.y * dt; + vz_ += pos_map.z * dt; + + // integrate velocity to get position + x_ += vx_ * dt; + y_ += vy_ * dt; + z_ += vz_ * dt; + + auto position_msg = geometry_msgs::msg::Pose2D(); + position_msg.x = x_; + position_msg.y = y_; + position_msg.theta = theta_; + position_publisher_->publish(position_msg); + + auto velocity_msg = geometry_msgs::msg::Twist(); + velocity_msg.linear.x = vx_; + velocity_msg.linear.y = vy_; + velocity_msg.linear.z = vz_; + velocity_msg.angular.z = omega_z; + velocity_publisher_->publish(velocity_msg); + + RCLCPP_INFO(this->get_logger(), + "Position: (%.3f, %.3f, %.3f) m, θ=%.3f rad | Velocity: (%.3f, %.3f, %.3f) m/s", + x_, y_, z_, theta_, vx_, vy_, vz_ + ); +} + +void IMUPositionApproximator::position_reset_callback(const geometry_msgs::msg::Pose2D::SharedPtr msg) { + RCLCPP_INFO(this->get_logger(), + "resetting position, was: (%.3f, %.3f, %.3f), now: (%.3f, %.3f, %.3f)", + x_, y_, theta_, msg->x, msg->y, msg->theta + ); + + x_ = msg->x; + y_ = msg->y; + theta_ = msg->theta; + + vx_ = 0.0; + vy_ = 0.0; + vz_ = 0.0; + + last_time_ = this->now(); +} + +} // namespace assignments::three::g2_2025_imu_position_approximator_node diff --git a/src/g2_2025_odometry_pkg/src/g2_2025_imu_position_approximator_node/nodes/IMUPositionApproximator.hpp b/src/g2_2025_odometry_pkg/src/g2_2025_imu_position_approximator_node/nodes/IMUPositionApproximator.hpp new file mode 100644 index 0000000..d4f207d --- /dev/null +++ b/src/g2_2025_odometry_pkg/src/g2_2025_imu_position_approximator_node/nodes/IMUPositionApproximator.hpp @@ -0,0 +1,36 @@ +#include + +#include "rclcpp/rclcpp.hpp" +#include "sensor_msgs/msg/imu.hpp" +#include "geometry_msgs/msg/pose2_d.hpp" +#include "geometry_msgs/msg/twist.hpp" + +namespace assignments::three::g2_2025_imu_position_approximator_node { + +struct Position { + double x; + double y; + double z; +}; + +class IMUPositionApproximator : public rclcpp::Node { +public: + IMUPositionApproximator(); + +private: + rclcpp::Subscription::SharedPtr imu_subscription_; + rclcpp::Subscription::SharedPtr position_reset_subscription_; + rclcpp::Publisher::SharedPtr position_publisher_; + rclcpp::Publisher::SharedPtr velocity_publisher_; + + std::string imu_topic_ { "imu_data" }; + double x_, y_, z_; // Position + double theta_; // Orientation (yaw angle) + double vx_, vy_, vz_; // Velocity + rclcpp::Time last_time_; + + void imu_callback(const sensor_msgs::msg::Imu::SharedPtr msg); + void position_reset_callback(const geometry_msgs::msg::Pose2D::SharedPtr msg); +}; + +} // namespace assignments::three::g2_2025_imu_position_approximator_node