202 lines
5.5 KiB
C++
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
|