rm_vision/tools/math_tools.cpp
2025-12-15 02:33:20 +08:00

202 lines
5.5 KiB
C++

#include "math_tools.hpp"
#include <cmath>
#include <opencv2/core.hpp> // CV_PI
namespace tools
{
double limit_rad(double angle)
{
while (angle > CV_PI) angle -= 2 * CV_PI;
while (angle <= -CV_PI) angle += 2 * CV_PI;
return angle;
}
Eigen::Vector3d eulers(Eigen::Quaterniond q, int axis0, int axis1, int axis2, bool extrinsic)
{
if (!extrinsic) std::swap(axis0, axis2);
auto i = axis0, j = axis1, k = axis2;
auto is_proper = (i == k);
if (is_proper) k = 3 - i - j;
auto sign = (i - j) * (j - k) * (k - i) / 2;
double a, b, c, d;
Eigen::Vector4d xyzw = q.coeffs();
if (is_proper) {
a = xyzw[3];
b = xyzw[i];
c = xyzw[j];
d = xyzw[k] * sign;
} else {
a = xyzw[3] - xyzw[j];
b = xyzw[i] + xyzw[k] * sign;
c = xyzw[j] + xyzw[3];
d = xyzw[k] * sign - xyzw[i];
}
Eigen::Vector3d eulers;
auto n2 = a * a + b * b + c * c + d * d;
eulers[1] = std::acos(2 * (a * a + b * b) / n2 - 1);
auto half_sum = std::atan2(b, a);
auto half_diff = std::atan2(-d, c);
auto eps = 1e-7;
auto safe1 = std::abs(eulers[1]) >= eps;
auto safe2 = std::abs(eulers[1] - CV_PI) >= eps;
auto safe = safe1 && safe2;
if (safe) {
eulers[0] = half_sum + half_diff;
eulers[2] = half_sum - half_diff;
} else {
if (!extrinsic) {
eulers[0] = 0;
if (!safe1) eulers[2] = 2 * half_sum;
if (!safe2) eulers[2] = -2 * half_diff;
} else {
eulers[2] = 0;
if (!safe1) eulers[0] = 2 * half_sum;
if (!safe2) eulers[0] = 2 * half_diff;
}
}
for (int i = 0; i < 3; i++) eulers[i] = limit_rad(eulers[i]);
if (!is_proper) {
eulers[2] *= sign;
eulers[1] -= CV_PI / 2;
}
if (!extrinsic) std::swap(eulers[0], eulers[2]);
return eulers;
}
Eigen::Vector3d eulers(Eigen::Matrix3d R, int axis0, int axis1, int axis2, bool extrinsic)
{
Eigen::Quaterniond q(R);
return eulers(q, axis0, axis1, axis2, extrinsic);
}
Eigen::Matrix3d rotation_matrix(const Eigen::Vector3d & ypr)
{
double roll = ypr[2];
double pitch = ypr[1];
double yaw = ypr[0];
double cos_yaw = cos(yaw);
double sin_yaw = sin(yaw);
double cos_pitch = cos(pitch);
double sin_pitch = sin(pitch);
double cos_roll = cos(roll);
double sin_roll = sin(roll);
// clang-format off
Eigen::Matrix3d R{
{cos_yaw * cos_pitch, cos_yaw * sin_pitch * sin_roll - sin_yaw * cos_roll, cos_yaw * sin_pitch * cos_roll + sin_yaw * sin_roll},
{sin_yaw * cos_pitch, sin_yaw * sin_pitch * sin_roll + cos_yaw * cos_roll, sin_yaw * sin_pitch * cos_roll - cos_yaw * sin_roll},
{ -sin_pitch, cos_pitch * sin_roll, cos_pitch * cos_roll}
};
// clang-format on
return R;
}
Eigen::Vector3d xyz2ypd(const Eigen::Vector3d & xyz)
{
auto x = xyz[0], y = xyz[1], z = xyz[2];
auto yaw = std::atan2(y, x);
auto pitch = std::atan2(z, std::sqrt(x * x + y * y));
auto distance = std::sqrt(x * x + y * y + z * z);
return {yaw, pitch, distance};
}
Eigen::MatrixXd xyz2ypd_jacobian(const Eigen::Vector3d & xyz)
{
auto x = xyz[0], y = xyz[1], z = xyz[2];
auto dyaw_dx = -y / (x * x + y * y);
auto dyaw_dy = x / (x * x + y * y);
auto dyaw_dz = 0.0;
auto dpitch_dx = -(x * z) / ((z * z / (x * x + y * y) + 1) * std::pow((x * x + y * y), 1.5));
auto dpitch_dy = -(y * z) / ((z * z / (x * x + y * y) + 1) * std::pow((x * x + y * y), 1.5));
auto dpitch_dz = 1 / ((z * z / (x * x + y * y) + 1) * std::pow((x * x + y * y), 0.5));
auto ddistance_dx = x / std::pow((x * x + y * y + z * z), 0.5);
auto ddistance_dy = y / std::pow((x * x + y * y + z * z), 0.5);
auto ddistance_dz = z / std::pow((x * x + y * y + z * z), 0.5);
// clang-format off
Eigen::MatrixXd J{
{dyaw_dx, dyaw_dy, dyaw_dz},
{dpitch_dx, dpitch_dy, dpitch_dz},
{ddistance_dx, ddistance_dy, ddistance_dz}
};
// clang-format on
return J;
}
Eigen::Vector3d ypd2xyz(const Eigen::Vector3d & ypd)
{
auto yaw = ypd[0], pitch = ypd[1], distance = ypd[2];
auto x = distance * std::cos(pitch) * std::cos(yaw);
auto y = distance * std::cos(pitch) * std::sin(yaw);
auto z = distance * std::sin(pitch);
return {x, y, z};
}
Eigen::MatrixXd ypd2xyz_jacobian(const Eigen::Vector3d & ypd)
{
auto yaw = ypd[0], pitch = ypd[1], distance = ypd[2];
double cos_yaw = std::cos(yaw);
double sin_yaw = std::sin(yaw);
double cos_pitch = std::cos(pitch);
double sin_pitch = std::sin(pitch);
auto dx_dyaw = distance * cos_pitch * -sin_yaw;
auto dy_dyaw = distance * cos_pitch * cos_yaw;
auto dz_dyaw = 0.0;
auto dx_dpitch = distance * -sin_pitch * cos_yaw;
auto dy_dpitch = distance * -sin_pitch * sin_yaw;
auto dz_dpitch = distance * cos_pitch;
auto dx_ddistance = cos_pitch * cos_yaw;
auto dy_ddistance = cos_pitch * sin_yaw;
auto dz_ddistance = sin_pitch;
// clang-format off
Eigen::MatrixXd J{
{dx_dyaw, dx_dpitch, dx_ddistance},
{dy_dyaw, dy_dpitch, dy_ddistance},
{dz_dyaw, dz_dpitch, dz_ddistance}
};
// clang-format on
return J;
}
double delta_time(
const std::chrono::steady_clock::time_point & a, const std::chrono::steady_clock::time_point & b)
{
std::chrono::duration<double> c = a - b;
return c.count();
}
double get_abs_angle(const Eigen::Vector2d & vec1, const Eigen::Vector2d & vec2)
{
if (vec1.norm() == 0. || vec2.norm() == 0.) {
return 0.;
}
return std::acos(vec1.dot(vec2) / (vec1.norm() * vec2.norm()));
}
double limit_min_max(double input, double min, double max)
{
if (input > max)
return max;
else if (input < min)
return min;
return input;
}
} // namespace tools