Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implementation of a pid dp controller #499

Open
wants to merge 63 commits into
base: develop
Choose a base branch
from

Conversation

Talhanc
Copy link

@Talhanc Talhanc commented Nov 8, 2024

DP controller with quaternion representation used.
The controller is tuned and works in the simulator

Added in docstring for the functions in .hpp file
ROS2 functions are defined separate from the normal functions ( cough Christopher asked for this cough )

Source: Trust me bro

Talhanc and others added 30 commits September 27, 2024 13:38
* chore(pylint): add .pylintrc configuration file

* refactor: fix pylint warning W1510

* refactor: fix pylint warning C0116

* refactor: fix pylint warning C0411

* refactor: fix pylint warning R1731

* refactor: fix pylint warning R1705

* refactor: fix pylint warning W1514

* refactor: fix pylint warning C0200

* refactor: fix pylint warning W0611

* refactor: fix pylint warning W0702

* refactor: fix pylint warning C0121

* refactor: fix pylint warning W0107

* refactor: fix pylint warning R1714

* refactor: fix pylint warning C0201

* refactor: fix pylint warning C0303 and W0613

* refactor: fix pylint warning W0621

* refactor: fix pylint warning R0402

* refactor: fix pylint warning W0212

* chore(pylint): update rule adjustments and formatting

* style: format code with black

* style: format imports with isort

* ci: add isort and pylint to Python pipeline

* Committing black-format changes

* chore: add pyproject.toml for project configuration

* ci: update order of pipeline jobs

* ci: update pylint job and pylint rules

* ci: update which python versions pylint test

* ci: update python pipeline to only run on pull_request

* refactor: fix pylint warning C0103

* chore: update linting and project configuration rules in .pylintrc and pyproject.toml

* Rename files to snake_case

* ci: add pipeline for grammar in comments

* refactor: fix spelling mistakes in comments

* ci: update job codespell_fix to continue on error

* refactor: fix spelling mistakes in comments

* ci: update pipeline codespell to only have one job

* refactor: fix variable naming issues and adjust imports as per review

* Committing codespell fixes

* refactor: spelling correction

* ci: update CI file and job names for consistency

* ci: update CI file for more clarity

* chore: add codespell configuration and ignore list

* chore: add type hints and return types

* ci: add mypy.ini for type checking configuration

* ci: add mypy type checking workflow

* refactor: format code with black

* refactor: format all yaml files using prettier

* ci: add pipeline for yaml formatting using prettier

* ci: update yaml formatting pipeline to run on pull request

* refactor: remove deprecated typing

* refactor: fix import sorting

* ci: remove faulty ci pipelines and combine python pipelines into single file

* fix(security): replace subprocess call with safer alternative to remove shell=true

* feat: add pre-commit hooks for black, isort, and codespell

* ci: update python pipeline to use latest versions

* refactor: apply code formatting fixes via pre-commit hooks

* ci: split python ci/cd pipelines into multiple files

* ci: add ci pipeline for checking style clang-format

* refactor: remove unused config file for clang-format

* refactor: remove duplicate file

* ci: add ci pipeline that tests that codebase can build

* refactor: update ci-build pipeline to only run on pull request

* refactor: update clang-format config

* refactor: apply clang-format

* feat: update pipeline to build and run tests

* refactor: disable linting checks when running colcon test

* refactor: update .pre-commit-config file (#480)

---------

Co-authored-by: Black Robot <[email protected]>
Co-authored-by: Codespell Robot <[email protected]>
Co-authored-by: Sondre Haugen <[email protected]>
control/pid_controller_dp/package.xml Outdated Show resolved Hide resolved
control/pid_controller_dp/package.xml Outdated Show resolved Hide resolved
control/pid_controller_dp/src/pid_controller_utils.cpp Outdated Show resolved Hide resolved
control/pid_controller_dp/src/pid_controller_utils.cpp Outdated Show resolved Hide resolved
guidance/reference_filter_dp/package.xml Outdated Show resolved Hide resolved
guidance/reference_filter_dp/package.xml Outdated Show resolved Hide resolved
guidance/reference_filter_dp/src/reference_filter_ros.cpp Outdated Show resolved Hide resolved
guidance/reference_filter_dp/src/reference_filter_ros.cpp Outdated Show resolved Hide resolved
@vortexntnu vortexntnu deleted a comment from Andeshog Nov 8, 2024
@Talhanc Talhanc requested a review from Andeshog November 8, 2024 18:31
Copy link
Contributor

@Andeshog Andeshog left a comment

Choose a reason for hiding this comment

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

Also; write a README.md for both the pid package and ref. filter package

control/pid_controller_dp/CMakeLists.txt Outdated Show resolved Hide resolved
Comment on lines +11 to +13
// @param eta: 7D vector containing the vehicle pose [x, y, z, w, x, y, z]
// @param eta_d: 7D vector containing the desired vehicle pose [x, y, z, w,
// x, y, z]
Copy link
Contributor

Choose a reason for hiding this comment

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

consider using a different notation for quat, since having two sets of xyz in the same vector can be misleading/confusing.

Copy link
Member

Choose a reason for hiding this comment

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

Also see my comment relating to quaternion operations, Eigen::Quaterniond exists for a good reason 🔥

Copy link
Member

@chrstrom chrstrom left a comment

Choose a reason for hiding this comment

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

First review pass here, I think I'll stop by the office tomorrow if you have any other questions 🫡 (There are also a few choices here relating to data structures and how the code is laid out that I think it's easier to discuss in-person)

control/pid_controller_dp/CMakeLists.txt Outdated Show resolved Hide resolved
Comment on lines +6 to +26
odometry_sub_ = this->create_subscription<nav_msgs::msg::Odometry>(
"/nucleus/odom", 10,
std::bind(&PIDControllerNode::odometry_callback, this,
std::placeholders::_1));
guidance_sub_ =
this->create_subscription<vortex_msgs::msg::ReferenceFilter>(
"/dp/reference", 10,
std::bind(&PIDControllerNode::guidance_callback, this,
std::placeholders::_1));
kp_sub_ = this->create_subscription<std_msgs::msg::Float64MultiArray>(
"/pid/kp", 10,
std::bind(&PIDControllerNode::kp_callback, this,
std::placeholders::_1));
ki_sub_ = this->create_subscription<std_msgs::msg::Float64MultiArray>(
"/pid/ki", 10,
std::bind(&PIDControllerNode::ki_callback, this,
std::placeholders::_1));
kd_sub_ = this->create_subscription<std_msgs::msg::Float64MultiArray>(
"/pid/kd", 10,
std::bind(&PIDControllerNode::kd_callback, this,
std::placeholders::_1));
Copy link
Member

Choose a reason for hiding this comment

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

Note that this can get really annoying if you want to add more parameters. There is support for dynamic setting of rosparams that doesnt rely on topics, but i actually think subscribers are nicer in this specific case. IF (and only if) you need to add more subscribers here, this way of doing it requires some annoying boilerplate. In that case, you might want to make your life a bit easier by defining some helpers, like:

template <typename MessageType>
void add_subscriber(const std::string &topic, void (PIDControllerNode::*callback)(const std::shared_ptr<MessageType>)) {
    constexpr std::size_t queue_size = 10;
    subscriptions_.emplace_back(
        this->create_subscription<MessageType>(
            topic, queue_size , [this, callback](const std::shared_ptr<MessageType> msg) {
                (this->*callback)(msg);
            }));
}

that adds to the member variable

std::vector<rclcpp::SubscriptionBase::SharedPtr> subscriptions_

which lets you add your subscriptions like

subscriptions_ = {
    add_subscriber<nav_msgs::msg::Odometry>("/nucleus/odom", &PIDControllerNode::odometry_callback),
    add_subscriber<vortex_msgs::msg::ReferenceFilter>("/dp/reference", &PIDControllerNode::guidance_callback),
    add_subscriber<std_msgs::msg::Float64MultiArray>("/pid/kp", &PIDControllerNode::kp_callback),
    add_subscriber<std_msgs::msg::Float64MultiArray>("/pid/ki", &PIDControllerNode::ki_callback),
    add_subscriber<std_msgs::msg::Float64MultiArray>("/pid/kd", &PIDControllerNode::kd_callback)
};

control/pid_controller_dp/src/pid_controller_utils.cpp Outdated Show resolved Hide resolved
Comment on lines 115 to 124
Eigen::Vector4d q_error(
(q_eta_d(0) * q_eta(0)) -
(q_eta_d(1) * q_eta(1) + q_eta_d(2) * q_eta(2) +
q_eta_d(3) * q_eta(3)),
(q_eta_d(0) * q_eta(1)) + (q_eta(0) * q_eta_d(1)) +
((-q_eta_d(3) * q_eta(2)) + (q_eta_d(2) * q_eta(3))),
(q_eta_d(0) * q_eta(2)) + (q_eta(0) * q_eta_d(2)) +
((q_eta_d(3) * q_eta(1)) + (-q_eta_d(1) * q_eta(3))),
(q_eta_d(0) * q_eta(3)) + (q_eta(0) * q_eta_d(3)) +
((-q_eta_d(2) * q_eta(1)) + (q_eta_d(1) * q_eta(2))));
Copy link
Member

@chrstrom chrstrom Nov 8, 2024

Choose a reason for hiding this comment

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

This should raise a red flag, specifically the choice of maintaining quaternions as part of a larger Eigen:: vector, or as an Eigen::Vector4d. I suggest tracing back through the code and using Eigen::Quaterniond directly (for example, you can just use * between two Eigen quats and it does quaternion multiplication for you).

If you absolutely need the quaternion to be part of the Eigen::Vector7d type, do the conversion right before you need it.

Something like

struct Eta
{
    Eigen::Vector3d pos;
    Eigen::Quaterniond rot;
}

could maybe make sense?

Comment on lines 129 to 131

void ReferenceFilterNode::execute(
const std::shared_ptr<rclcpp_action::ServerGoalHandle<
Copy link
Member

Choose a reason for hiding this comment

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

This method is fairly unwieldy, you should consider adding a few free functions to make it more manageable. Also make sure to document how this call is blocking, and what conditions apply wrt that

Comment on lines +3 to +23
Matrix3d calculate_R(const Vector6d& eta) {
double phi = eta(3);
double theta = eta(4);
double psi = eta(5);

double cphi = cos(phi);
double sphi = sin(phi);
double ctheta = cos(theta);
double stheta = sin(theta);
double cpsi = cos(psi);
double spsi = sin(psi);

Matrix3d rotation_matrix;

rotation_matrix << cpsi * ctheta, cpsi * stheta * sphi - spsi * cphi,
cpsi * stheta * cphi + spsi * sphi, spsi * ctheta,
spsi * stheta * sphi + cpsi * cphi, spsi * stheta * cphi - cpsi * sphi,
-stheta, ctheta * sphi, ctheta * cphi;

return rotation_matrix;
}
Copy link
Member

Choose a reason for hiding this comment

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

Writing out the equations in full makes it really hard to see which convention is used, might want to just rely on Eigen:: builtins:

Eigen::Matrix3d calculate_r(const Eigen::Vector6d& eta) {
    const double roll = eta(3); 
    const double pitch = eta(4); 
    const double yaw = eta(5);

    const Eigen::AngleAxisd roll_angle(roll, Eigen::Vector3d::UnitX());
    const Eigen::AngleAxisd pitch_angle(pitch, Eigen::Vector3d::UnitY());
    const Eigen::AngleAxisd yaw_angle(yaw, Eigen::Vector3d::UnitZ());

    const Eigen::Quaterniond q = yaw_angle * pitch_angle * roll_angle;

    return q.toRotationMatrix();
}

Comment on lines +40 to +41
transformation_matrix << 1, sphi * stheta / ctheta, cphi * stheta / ctheta,
0, cphi, -sphi, 0, sphi / ctheta, cphi / ctheta;
Copy link
Member

Choose a reason for hiding this comment

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

Would it be possible to follow a similar pattern to my suggestion above?

Comment on lines 34 to 36
if (ctheta == 0) {
throw std::runtime_error("Division by zero in calculate_T.");
}
Copy link
Member

Choose a reason for hiding this comment

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

As far as I can see, this is never caught?

Either catch it and handle it appropriately, or crash and die

Copy link
Author

@Talhanc Talhanc Nov 10, 2024

Choose a reason for hiding this comment

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

I thought maybe this works better, what do you think? Maybe still change the transformation matrix?

try {
        if (ctheta == 0) {
            throw std::runtime_error("Division by zero in calculate_T.");
        } else {
            transformation_matrix << 1, sphi * stheta / ctheta, cphi * stheta / ctheta, 0, cphi, -sphi, 0, sphi / ctheta, cphi / ctheta;
        }
    } catch (std::runtime_error& e) {
        transformation_matrix = Eigen::Matrix3d::Identity();
    }

@Talhanc Talhanc assigned Andeshog and unassigned Andeshog Nov 9, 2024
@kluge7 kluge7 linked an issue Nov 10, 2024 that may be closed by this pull request
3 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[TASK] DP controller
6 participants