Skip to content
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
16f8668
feat: add debug parameters and logging for PID controller calculations
ppakr Oct 15, 2025
d333822
feat: add rcl_interfaces dependency and parameter callback for dynami…
ppakr Oct 22, 2025
a27998d
feat: update PID controller parameters
ppakr Oct 25, 2025
71df096
feat: add parameter handling with individual gains for x, y, z, roll,…
ppakr Nov 2, 2025
edcd63b
feat: update CMake configuration and add logging for PID controller w…
ppakr Nov 12, 2025
1ed1d42
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 12, 2025
98c5d57
WIP: before types migration
ppakr Jan 5, 2026
85f9b1a
refactor: update typedefs and conversions for Eta and Nu structures
ppakr Jan 5, 2026
457c2af
fix: correct rqt gain update
ppakr Jan 5, 2026
471e32d
Merge branch 'main' into dp-quaternion-control
ppakr Jan 9, 2026
b4aefb9
fix: update type aliases for Eta and Nu to use Pose and Twist
ppakr Feb 5, 2026
e7cdcf9
Merge remote-tracking branch 'origin/main' into dp-quaternion-control
ppakr Feb 21, 2026
2d71411
fix: add vortex_utils_ros to target dependencies
ppakr Feb 21, 2026
afab550
refactor: remove debug parameters and logging from PID controller imp…
ppakr Feb 21, 2026
f1df4f7
feat: transform joystick inputs from body frame to world frame in mov…
ppakr Feb 21, 2026
386fd99
fix: update PID parameters and joystick interface
ppakr Feb 21, 2026
7c72929
docs: update docs
ppakr Mar 6, 2026
9fe9af2
Merge remote-tracking branch 'origin/main' into dp-quaternion-control
ppakr Mar 6, 2026
bf97597
Merge remote-tracking branch 'origin/main' into dp-quaternion-control
ppakr Mar 11, 2026
d97c0f2
refactor: remove commented-out PID parameter arrays from pid_params.yaml
ppakr Mar 11, 2026
d69fed6
docs(controller): update readme
ppakr Mar 11, 2026
9a560f7
refactor: update message types in PID controller to use Odom as input…
ppakr Mar 21, 2026
ae83763
Merge remote-tracking branch 'origin/main' into dp-quaternion-control
ppakr Mar 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 56 additions & 4 deletions control/pid_controller_dp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.8)
project(pid_controller_dp)

if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD 20)
endif()

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
Expand All @@ -16,24 +16,71 @@ find_package(geometry_msgs REQUIRED)
find_package(Eigen3 REQUIRED)
find_package(tf2 REQUIRED)
find_package(vortex_msgs REQUIRED)
find_package(rcl_interfaces REQUIRED)
find_package(vortex_utils REQUIRED)
find_package(spdlog REQUIRED)
find_package(fmt REQUIRED)

include_directories(include)
set(LIB_NAME ${PROJECT_NAME}_lib)

add_executable(pid_controller_node
src/pid_controller_node.cpp
src/pid_controller_ros.cpp
add_library(${LIB_NAME} SHARED
src/pid_controller.cpp
src/pid_controller_utils.cpp
src/pid_controller_conversions.cpp
)

ament_target_dependencies(${LIB_NAME} PUBLIC

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sticking to the "keep non-ROS code separate from everything else", you wouldn't want to pull in anything to your lib using ament_target_dependencies. Instead, use pure cmake for the library, and link in ros-deps + the lib with ament for the final executable

rclcpp
geometry_msgs
nav_msgs
Eigen3
tf2
vortex_msgs
rcl_interfaces
vortex_utils
spdlog
fmt
)


install(TARGETS
${LIB_NAME}
DESTINATION lib/${PROJECT_NAME}
)

add_executable(pid_controller_node
src/pid_controller_node.cpp
src/pid_controller_ros.cpp
)

ament_target_dependencies(pid_controller_node
rclcpp
geometry_msgs
nav_msgs
Eigen3
tf2
vortex_msgs
rcl_interfaces
vortex_utils
spdlog
fmt
)

target_link_libraries(
pid_controller_node
${LIB_NAME}
spdlog::spdlog
# vortex_utilis::vortex_utils
)

ament_export_targets(export_${LIB_NAME})

install(TARGETS ${LIB_NAME}
EXPORT export_${LIB_NAME}
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin
)

install(TARGETS
Expand All @@ -46,4 +93,9 @@ install(DIRECTORY
DESTINATION share/${PROJECT_NAME}/
)


if(BUILD_TESTING)
add_subdirectory(test)
endif()

ament_package()
24 changes: 21 additions & 3 deletions control/pid_controller_dp/config/pid_params.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
/**:
ros__parameters:
Kp: [70.0, 70.0, 70.0, 12.0, 12.0, 12.0]
Ki: [2.0, 2.0, 2.0, 0.12, 0.12, 0.12]
Kd: [10.0, 10.0, 10.0, 4.0, 5.0, 4.0]
# Kp: [70.0, 70.0, 70.0, 12.0, 12.0, 12.0]
# Ki: [2.0, 2.0, 2.0, 0.12, 0.12, 0.12]
# Kd: [10.0, 10.0, 10.0, 4.0, 5.0, 4.0]
Kp_x: 10.0
Kp_y: 0.0
Kp_z: 0.0
Kp_roll: 0.0
Kp_pitch: 0.0
Kp_yaw: 0.0
Ki_x: 0.0
Ki_y: 0.0
Ki_z: 0.0
Ki_roll: 0.0
Ki_pitch: 0.0
Ki_yaw: 0.0
Kd_x: 0.0
Kd_y: 0.0
Kd_z: 0.0
Kd_roll: 0.0
Kd_pitch: 0.0
Kd_yaw: 0.0

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it not more tidy to have the parameters in vectors here?

Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#ifndef PID_CONTROLLER_HPP
#define PID_CONTROLLER_HPP

#include <spdlog/spdlog.h>
#include "pid_controller_dp/typedefs.hpp"

class PIDController {
Expand Down Expand Up @@ -36,6 +37,21 @@ class PIDController {
// @param dt: Time step
void set_time_step(double dt);

// parameters for debug
types::Eta eta_error_debug;
types::Vector6d nu_d_debug;
types::Vector6d error_nu_debug;
types::Vector6d P_debug;
types::Vector6d I_debug;
types::Vector6d D_debug;
types::Vector6d tau_debug;
types::Matrix6x7d J_inv_debug;

// debug gain
types::Matrix6d Kp_debug;
types::Matrix6d Ki_debug;
types::Matrix6d Kd_debug;

private:
types::Matrix6d Kp_;
types::Matrix6d Ki_;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <geometry_msgs/msg/twist_with_covariance_stamped.hpp>
#include <geometry_msgs/msg/wrench_stamped.hpp>
#include <nav_msgs/msg/odometry.hpp>
#include <rcl_interfaces/msg/set_parameters_result.hpp>
#include <rclcpp/rclcpp.hpp>
#include <std_msgs/msg/bool.hpp>
#include <std_msgs/msg/float64_multi_array.hpp>
Expand Down Expand Up @@ -55,6 +56,12 @@ class PIDControllerNode : public rclcpp::Node {
void guidance_callback(
const vortex_msgs::msg::ReferenceFilter::SharedPtr msg);

// TODO: parameter callback for dynamic reconfigure of PID gains
//@brief Callback function for parameter updates
// @param parameters: vector of parameters to be set
rcl_interfaces::msg::SetParametersResult parametersCallback(
const std::vector<rclcpp::Parameter>& parameters);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using a yaml-based approach like Anbit did. Mixing this approach with a service call (Trigger) allows for the same dynamic reconfiguration, but without relying so much on ROS.


PIDController pid_controller_;

rclcpp::Subscription<std_msgs::msg::Bool>::SharedPtr killswitch_sub_;
Expand Down Expand Up @@ -93,6 +100,8 @@ class PIDControllerNode : public rclcpp::Node {
bool killswitch_on_;

std::string software_mode_;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably applies to more than just this pr / package, but dealing with string-representation for stuff like this can get really annoying. if you need a string-rep at any point, i'd suggest converting to an enum (magic-enum f.ex) as early as possible in the chain

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nvm seems to have been changed in #656

OnSetParametersCallbackHandle::SharedPtr callback_handle_;
};

#endif
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
#ifndef PID_UTILS_HPP
#define PID_UTILS_HPP

#include <spdlog/spdlog.h>
#include <tf2/LinearMath/Matrix3x3.h>
#include <tf2/LinearMath/Quaternion.h>
#include <cmath>
#include <eigen3/Eigen/Geometry>
#include <std_msgs/msg/float64_multi_array.hpp>
#include <vortex/utils/types.hpp>
#include "pid_controller_dp/typedefs.hpp"

// @brief Calculate the sine of an angle in degrees
Expand Down Expand Up @@ -71,4 +73,6 @@ types::Vector7d anti_windup(const double dt,
const types::Eta& error,
const types::Vector7d& integral);

// void print_J_transformation(const types::J_transformation& J);
// void print_Jinv_transformation(const types::Matrix6x7d& J_inv);
#endif
2 changes: 2 additions & 0 deletions control/pid_controller_dp/package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
<depend>eigen</depend>
<depend>tf2</depend>
<depend>vortex_msgs</depend>
<depend>rcl_interfaces</depend>
<depend>vortex_utils</depend>

<export>
<build_type>ament_cmake</build_type>
Expand Down
110 changes: 103 additions & 7 deletions control/pid_controller_dp/src/pid_controller.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,71 @@
#include "pid_controller_dp/pid_controller.hpp"
#include "pid_controller_dp/pid_controller_utils.hpp"

void print_eta(const types::Eta& eta) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For all these printing utils, consider checking out #include <spdlog/fmt/ostr.h> and/or overloading << and/or adding custom fmt:: formatters to the vortex-utils lib, would make things much neater

// spdlog::info("Eta values:");
spdlog::info("Position - North: {}, East: {}, Down: {}", eta.pos[0],
eta.pos[1], eta.pos[2]);
spdlog::info("Orientation - w: {}, x: {}, y: {}, z: {}", eta.ori.w(),
eta.ori.x(), eta.ori.y(), eta.ori.z());
}

void print_nu(const types::Nu& nu) {
spdlog::info("Nu values:");
spdlog::info("Linear Speed - u: {}, v: {}, w: {}", nu.linear_speed[0],
nu.linear_speed[1], nu.linear_speed[2]);
spdlog::info("Angular Speed - p: {}, q: {}, r: {}", nu.angular_speed[0],
nu.angular_speed[1], nu.angular_speed[2]);
}

void print_vect_6d(const types::Vector6d& vec) {
spdlog::info("Vector6d values:");
for (int i = 0; i < 6; ++i) {
spdlog::info("Element[{}]: {}", i, vec[i]);
}
}

void print_J_transformation(const types::J_transformation& J) {
spdlog::info("J_transformation:");

spdlog::info("R (3x3) elements:");
for (int i = 0; i < J.R.rows(); ++i) {
for (int j = 0; j < J.R.cols(); ++j) {
spdlog::info("R[{},{}] = {}", i, j, J.R(i, j));
}
}

spdlog::info("T (4x3) elements:");
for (int i = 0; i < J.T.rows(); ++i) {
for (int j = 0; j < J.T.cols(); ++j) {
spdlog::info("T[{},{}] = {}", i, j, J.T(i, j));
}
}

spdlog::info("Combined Matrix (7x6) elements:");
auto M = J.as_matrix();
for (int i = 0; i < M.rows(); ++i) {
for (int j = 0; j < M.cols(); ++j) {
spdlog::info("M[{},{}] = {}", i, j, M(i, j));
}
}
}

void print_Jinv_transformation(const types::Matrix6x7d& J_inv) {
spdlog::info("J_pseudo_inverse (6x7):");
for (int i = 0; i < J_inv.rows(); ++i) {
std::string row;
row.reserve(128);
row += "[";
for (int j = 0; j < J_inv.cols(); ++j) {
row += std::to_string(J_inv(i, j));
if (j < J_inv.cols() - 1)
row += ", ";
}
row += "]";
spdlog::info("{}", row);
}
}

PIDController::PIDController()
: Kp_(types::Matrix6d::Identity()),
Ki_(types::Matrix6d::Zero()),
Expand All @@ -12,21 +77,52 @@ types::Vector6d PIDController::calculate_tau(const types::Eta& eta,
const types::Eta& eta_d,
const types::Nu& nu,
const types::Eta& eta_dot_d) {
types::Eta error = error_eta(eta, eta_d);
types::Eta error = error_eta(eta, eta_d); // calculate eta error

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it better to make the error 6d and change the corresponding matrices?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With 6d error you could also replace pseudo_inv with standard inverse


// set w = 0
error.ori.w() = 0.0; // only use vector part of quaternion for error

types::Matrix6x7d J_inv = calculate_J_sudo_inv(error);
auto eta_dot_d_copy = eta_dot_d;
eta_dot_d_copy.ori.w() = 0.0; // set w = 0 for desired eta_dot
// debug
// eta_error_debug = error;
spdlog::info("Eta: ");
print_eta(eta);
spdlog::info("Eta desired: ");
print_eta(eta_d);
spdlog::info("Eta error:");
print_eta(error);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would suggest making this spdlog::trace (you could pass in log level to the print methods). Lets you f.ex compile debug with trace log level and release with info/warn, and set different log levels for file/terminal (io is expensive)


types::Vector6d nu_d = J_inv * eta_dot_d.as_vector();
types::Matrix6x7d J_inv =
calculate_J_sudo_inv(eta); // calculate J pseudo inverse
J_inv_debug = J_inv;
print_Jinv_transformation(J_inv);

types::Vector6d error_nu = nu.as_vector() - nu_d;
types::Vector6d nu_d =
J_inv * eta_dot_d_copy.as_vector(); // calculate velocity
// nu_d_debug = nu_d;
// print_nu(nu_d);

types::Vector6d P = Kp_ * J_inv * error.as_vector();
types::Vector6d error_nu = nu.as_vector() - nu_d; // calculate vel error
// error_nu_debug = error_nu;
// print_vect_6d(error_nu);

types::Vector6d I = Ki_ * J_inv * integral_;
types::Vector6d P = Kp_ * J_inv * error.as_vector(); // P term
// P_debug = P;
Kp_debug = Kp_;

types::Vector6d D = Kd_ * error_nu;
types::Vector6d I = Ki_ * J_inv * integral_; // I term
// I_debug = I;
// Ki_debug = Ki_;

types::Vector6d D = Kd_ * error_nu; // D term
// D_debug = D;
// Kd_debug = Kd_;
types::Vector6d tau = -clamp_values((P + I + D), -80.0, 80.0);
// types::Vector6d tau = -clamp_values((P), -80.0, 80.0);

// debug: tau = 0
// types::Vector6d tau = types::Vector6d::Zero();

integral_ = anti_windup(dt_, error, integral_);

Expand Down
12 changes: 11 additions & 1 deletion control/pid_controller_dp/src/pid_controller_node.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
#include <spdlog/spdlog.h>
#include "pid_controller_dp/pid_controller_ros.hpp"

auto start_msg = R"(
____ ___ ____ ____ _ _ _
| _ \_ _| _ \ / ___|___ _ __ | |_ _ __ ___ | | | ___ _ __
| |_) | || | | | | | / _ \| '_ \| __| '__/ _ \| | |/ _ \ '__|
| __/| || |_| | | |__| (_) | | | | |_| | | (_) | | | __/ |
|_| |___|____/ \____\___/|_| |_|\__|_| \___/|_|_|\___|_|
)";

int main(int argc, char** argv) {
rclcpp::init(argc, argv);
RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Started PID Controller Node");
// RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Started PID Controller Node");
spdlog::info(start_msg);
rclcpp::spin(std::make_shared<PIDControllerNode>());
rclcpp::shutdown();
return 0;
Expand Down
Loading