diff --git a/rae_description/urdf/rae_ros2_control.urdf.xacro b/rae_description/urdf/rae_ros2_control.urdf.xacro
index da0d399..266fc6c 100644
--- a/rae_description/urdf/rae_ros2_control.urdf.xacro
+++ b/rae_description/urdf/rae_ros2_control.urdf.xacro
@@ -30,6 +30,11 @@
0.1
0.0005
+ 5.3
+ 4.7
+ 0.3
+ 0
+
30
gpiochip0
diff --git a/rae_hw/CMakeLists.txt b/rae_hw/CMakeLists.txt
index 9f34ae0..5a0fc3d 100644
--- a/rae_hw/CMakeLists.txt
+++ b/rae_hw/CMakeLists.txt
@@ -12,7 +12,7 @@ set(CMAKE_POSITION_INDEPENDENT_CODE ON)
find_package(ament_cmake_auto REQUIRED)
ament_auto_find_build_dependencies()
find_package(ALSA REQUIRED)
-
+find_package(sensor_msgs REQUIRED)
find_library(GPIOD_LIBRARY NAMES libgpiodcxx.so)
if(NOT GPIOD_LIBRARY)
message(FATAL_ERROR "gpiod library not found. Install apt install libgpiod-dev")
@@ -26,7 +26,7 @@ pkg_check_modules(MPG123 REQUIRED libmpg123)
find_path(SNDFILE_INCLUDE_DIR sndfile.h)
find_library(SNDFILE_LIBRARY NAMES sndfile)
-include_directories(include ${GST_INCLUDE_DIRS} ${MPG123_INCLUDE_DIRS} ${SNDFILE_INCLUDE_DIR})
+include_directories(include ${GST_INCLUDE_DIRS} ${MPG123_INCLUDE_DIRS} ${SNDFILE_INCLUDE_DIR} ${sensor_msgs_INCLUDE_DIRS})
ament_auto_add_library(
${PROJECT_NAME}
@@ -35,7 +35,9 @@ ament_auto_add_library(
src/rae_motors.cpp
)
-ament_target_dependencies(${PROJECT_NAME} ${DEPENDENCIES} ALSA std_srvs)
+ament_target_dependencies(${PROJECT_NAME} ${DEPENDENCIES} nav_msgs ALSA std_srvs)
+set(dependencies
+ sensor_msgs)
target_link_libraries(
${PROJECT_NAME}
diff --git a/rae_hw/config/controller.yaml b/rae_hw/config/controller.yaml
index fbd1daa..860c427 100644
--- a/rae_hw/config/controller.yaml
+++ b/rae_hw/config/controller.yaml
@@ -29,7 +29,7 @@ diff_controller:
# velocity computation filtering
velocity_rolling_window_size: 1
- open_loop: false
+ open_loop: true
enable_odom_tf: false
cmd_vel_timeout: 0.5
diff --git a/rae_hw/include/rae_hw/rae_hw.hpp b/rae_hw/include/rae_hw/rae_hw.hpp
index f615765..eba0889 100644
--- a/rae_hw/include/rae_hw/rae_hw.hpp
+++ b/rae_hw/include/rae_hw/rae_hw.hpp
@@ -2,7 +2,9 @@
#define RAE_HW__RAE_HW_HPP_
#include
+#include
#include
+#include
#include
#include "gpiod.hpp"
@@ -10,10 +12,12 @@
#include "hardware_interface/hardware_info.hpp"
#include "hardware_interface/system_interface.hpp"
#include "hardware_interface/types/hardware_interface_return_values.hpp"
+#include "nav_msgs/msg/odometry.hpp"
#include "rae_hw/rae_motors.hpp"
#include "rae_hw/visibility_control.h"
#include "rclcpp/macros.hpp"
#include "rclcpp/rclcpp.hpp"
+#include "rclcpp_lifecycle/node_interfaces/lifecycle_node_interface.hpp"
#include "rclcpp_lifecycle/state.hpp"
namespace rae_hw {
@@ -48,11 +52,18 @@ class RaeHW : public hardware_interface::SystemInterface {
hardware_interface::return_type write(const rclcpp::Time& time, const rclcpp::Duration& period) override;
private:
+ void imu_callback(const sensor_msgs::msg::Imu::SharedPtr msg);
+ void odom_callback(const nav_msgs::msg::Odometry::SharedPtr msg);
std::unique_ptr motorL, motorR;
double leftMotorCMD, rightMotorCMD;
int pwmA, pwmB, phA, phB;
+ double yaw_imu, yaw_odom, kp, ki, kd, sumerr, prev_yaw_imu, prev_yaw_odom, prev_err, static_err;
+ bool static_correction;
double leftPos, rightPos, leftVel, rightVel;
std::string leftWheelName, rightWheelName;
+ rclcpp::Subscription::SharedPtr imu_subscriber_;
+ rclcpp::Subscription::SharedPtr odom_subscriber_;
+ rclcpp::Node::SharedPtr node_;
};
} // namespace rae_hw
diff --git a/rae_hw/include/rae_hw/rae_motors.hpp b/rae_hw/include/rae_hw/rae_motors.hpp
index e80c6e9..f5e90f0 100644
--- a/rae_hw/include/rae_hw/rae_motors.hpp
+++ b/rae_hw/include/rae_hw/rae_motors.hpp
@@ -15,6 +15,8 @@
#include
#include
+#include "sensor_msgs/msg/imu.hpp"
+
namespace rae_hw {
/// @brief Struct representing encoder GPIO states. Following states are used to calculate rotation:
///
diff --git a/rae_hw/package.xml b/rae_hw/package.xml
index 36762b1..de009a2 100644
--- a/rae_hw/package.xml
+++ b/rae_hw/package.xml
@@ -23,6 +23,7 @@
sensor_msgs
geometry_msgs
+ nav_msgs
rae_msgs
std_srvs
cv_bridge
diff --git a/rae_hw/src/rae_hw.cpp b/rae_hw/src/rae_hw.cpp
index cd1742b..c66b772 100644
--- a/rae_hw/src/rae_hw.cpp
+++ b/rae_hw/src/rae_hw.cpp
@@ -1,5 +1,6 @@
#include "rae_hw/rae_hw.hpp"
+#include
#include
#include
#include
@@ -37,6 +38,25 @@ hardware_interface::CallbackReturn RaeHW::on_init(const hardware_interface::Hard
PID pidR{std::stof(info_.hardware_parameters["PID_P_R"]), std::stof(info_.hardware_parameters["PID_I_R"]), std::stof(info_.hardware_parameters["PID_D_R"])};
motorR = std::make_unique(rightWheelName, chipName, pwmName, pwmR, phR, enRA, enRB, encTicsPerRevR, maxVelR, false, closedLoopR, pidR);
+ yaw_imu = 0.0;
+ yaw_odom = 0.0;
+ prev_yaw_imu = 0.0;
+ prev_yaw_odom = 0.0;
+ sumerr = 0.0;
+ prev_err = 0.0;
+ static_err = 0.0;
+ static_correction = static_cast(std::stoi(info_.hardware_parameters["static_correction"]));
+ ;
+ PID pidIMU{std::stof(info_.hardware_parameters["PID_P_IMU"]),
+ std::stof(info_.hardware_parameters["PID_I_IMU"]),
+ std::stof(info_.hardware_parameters["PID_D_IMU"])};
+ kp = pidIMU.P;
+ ki = pidIMU.I;
+ kd = pidIMU.D;
+ rclcpp::NodeOptions options;
+ options.arguments({"--ros-args", "-r", "__node:=topic_based_ros2_control_"});
+ node_ = rclcpp::Node::make_shared("_", options);
+
return CallbackReturn::SUCCESS;
}
@@ -44,6 +64,10 @@ hardware_interface::CallbackReturn RaeHW::on_configure(const rclcpp_lifecycle::S
motorL->run();
motorR->run();
+ imu_subscriber_ = node_->create_subscription("/rae/imu/data", 10, std::bind(&RaeHW::imu_callback, this, std::placeholders::_1));
+ odom_subscriber_ =
+ node_->create_subscription("/diff_controller/odom", 10, std::bind(&RaeHW::odom_callback, this, std::placeholders::_1));
+
return CallbackReturn::SUCCESS;
}
@@ -84,8 +108,30 @@ hardware_interface::CallbackReturn RaeHW::on_shutdown(const rclcpp_lifecycle::St
return CallbackReturn::SUCCESS;
}
+void RaeHW::imu_callback(const sensor_msgs::msg::Imu::SharedPtr msg) {
+ float q_x = msg->orientation.x;
+ float q_y = msg->orientation.y;
+ float q_z = msg->orientation.z;
+ float q_w = msg->orientation.w;
+
+ // Calculate yaw from the quaternion
+ yaw_imu = std::atan2(2.0 * (q_w * q_z + q_x * q_y), 1.0 - 2.0 * (std::pow(q_y, 2) + std::pow(q_z, 2)));
+}
+
+void RaeHW::odom_callback(const nav_msgs::msg::Odometry::SharedPtr msg) {
+ double q_x = msg->pose.pose.orientation.x;
+ double q_y = msg->pose.pose.orientation.y;
+ double q_z = msg->pose.pose.orientation.z;
+ double q_w = msg->pose.pose.orientation.w;
+
+ yaw_odom = std::atan2(2.0 * (q_w * q_z + q_x * q_y), 1.0 - 2.0 * (std::pow(q_y, 2) + std::pow(q_z, 2)));
+}
hardware_interface::return_type RaeHW::read(const rclcpp::Time& /*time*/, const rclcpp::Duration& /*period*/) {
+ if(rclcpp::ok()) // spin node
+ {
+ rclcpp::spin_some(node_);
+ }
float currLeftPos = motorL->getPos();
float currRightPos = motorR->getPos();
leftVel = motorL->getSpeed();
@@ -96,10 +142,52 @@ hardware_interface::return_type RaeHW::read(const rclcpp::Time& /*time*/, const
return hardware_interface::return_type::OK;
}
-hardware_interface::return_type RaeHW::write(const rclcpp::Time& /*time*/, const rclcpp::Duration& /*period*/) {
+hardware_interface::return_type RaeHW::write(const rclcpp::Time& /*time*/, const rclcpp::Duration& period) {
+ double dt = period.seconds();
+ auto adjustForWrap = [](double previousYaw, double currentYaw) { // lambda for wraping, see nunmpy unwrap
+ double delta = currentYaw - previousYaw;
+ if(delta > M_PI) {
+ currentYaw -= 2 * M_PI;
+ } else if(delta < -M_PI) {
+ currentYaw += 2 * M_PI;
+ }
+ return currentYaw;
+ };
+
+ double yaw_imu_wraped = adjustForWrap(prev_yaw_imu, yaw_imu); // to handle -pi pi discontinuity
+ double yaw_odom_wraped = adjustForWrap(prev_yaw_odom, yaw_odom - static_err);
+ double err = yaw_odom_wraped - yaw_imu_wraped;
+ double out = 0;
+
+ if(static_correction) {
+ sumerr += err * dt;
+ if(sumerr > 10) { // anti windup
+ sumerr = 10;
+ }
+ if(sumerr < -10) {
+ sumerr = -10;
+ }
+ float delta_err = (err - prev_err) / dt;
+ if(yaw_imu) { // prevent unwanted moves before initialization
+ if(leftMotorCMD || rightMotorCMD) {
+ out = err * kp + sumerr * ki + delta_err * kd;
+ } else {
+ out = (std::signbit(err) ? -3.95 : 3.95) + err * kp + sumerr * ki + delta_err * kd;
+ } // feedforward controller to bypass deadzone
+ }
+ } else {
+ out = 0.0;
+ if(abs(err) > 0.01) static_err += err;
+ }
+ leftMotorCMD -= out;
+ rightMotorCMD += out;
+
+ prev_yaw_imu = yaw_imu_wraped;
+ prev_yaw_odom = yaw_odom_wraped;
+ prev_err = err;
+
motorL->motorSet(leftMotorCMD);
motorR->motorSet(rightMotorCMD);
-
return hardware_interface::return_type::OK;
}