arm/User/component/arm_kinematics/arm6dof.cpp
2026-03-20 00:20:40 +08:00

431 lines
14 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
******************************************************************************
* @file arm.cpp
* @brief 6-DOF robotic arm kinematics control implementation
******************************************************************************
*/
#include "arm6dof.h"
#include "toolbox/robotics.h"
#include "toolbox/matrix.h"
#include "user_math.h"
#include <string.h>
/* 静态变量 */
static robotics::Link* s_links = nullptr;
static robotics::Serial_Link<6>* s_robot = nullptr;
static bool s_initialized = false;
// 当前机械臂URDF(j1~j6)关节原始定义parent->child 先 origin再绕 joint axis 旋转。
// 这里显式按URDF链式连乘实现FK避免将URDF joint origin强行映射到标准DH后产生几何失真。
static const float s_urdf_joint_origin_xyz[6][3] = {
{0.0f, 0.0f, 0.024f},
{-0.001395f,0.0f, 0.1015f},
{0.3265f, 0.0f, -0.0071975f},
{0.0905f, 0.052775f, 0.0058025f},
{0.001627f, 0.0f, 0.18467f},
{0.10487f, 0.0013347f,0.0f},
};
// URDF顺序为(roll, pitch, yaw)。robotics::rpy2r输入顺序为(yaw, pitch, roll)。
static const float s_urdf_joint_origin_rpy[6][3] = {
{0.0f, 0.0f, 0.0f},
{1.5708f, 0.0f, -1.5708f},
{0.0f, 0.0f, 0.0f},
{1.5708f, 0.0f, 3.1416f},
{1.5708f, 0.0f, 0.0f},
{1.5708f, 0.0f, 1.5708f},
};
// Link1~Link6 质心在各自link坐标系下的位置直接取自 xin.urdf 的 inertial/origin
static const float s_urdf_link_com_xyz[6][3] = {
{-0.0071819f, 0.00031034f, 0.061598f},
{ 0.22947f, -0.0031632f, -0.0022707f},
{ 0.086963f, 0.0016734f, -0.017806f},
{ 0.000055295f,-0.002302704f,0.10972717f},
{ 0.056542f, 0.0013106f, 0.0010268f},
{-0.0000077997f,0.000012767f,0.10159f},
};
static Matrixf<4, 4> Arm6dof_UrdfJointTransform(uint8_t index, float q) {
const float roll = s_urdf_joint_origin_rpy[index][0];
const float pitch = s_urdf_joint_origin_rpy[index][1];
const float yaw = s_urdf_joint_origin_rpy[index][2];
float ypr_origin[3] = {yaw, pitch, roll};
Matrixf<3, 3> R_origin = robotics::rpy2r(Matrixf<3, 1>(ypr_origin));
float p_raw[3] = {
s_urdf_joint_origin_xyz[index][0],
s_urdf_joint_origin_xyz[index][1],
s_urdf_joint_origin_xyz[index][2],
};
Matrixf<3, 1> p_origin(p_raw);
// 当前URDF关节轴均为 local-zR_joint = RotZ(q)
float ypr_joint[3] = {q, 0.0f, 0.0f};
Matrixf<3, 3> R_joint = robotics::rpy2r(Matrixf<3, 1>(ypr_joint));
return robotics::rp2t(R_origin, p_origin)
* robotics::rp2t(R_joint, matrixf::zeros<3, 1>());
}
static Matrixf<4, 4> Arm6dof_UrdfForwardTransform(const Arm6dof_JointAngles_t* q,
uint8_t joint_num) {
Matrixf<4, 4> T = matrixf::eye<4, 4>();
for (uint8_t i = 0; i < joint_num; ++i) {
T = T * Arm6dof_UrdfJointTransform(i, q->q[i]);
}
return T;
}
static float Arm6dof_UrdfPotentialEnergy(const Arm6dof_JointAngles_t* q) {
constexpr float g = 9.81f;
float U = 0.0f;
Matrixf<4, 4> T_link = matrixf::eye<4, 4>();
for (uint8_t i = 0; i < 6; ++i) {
T_link = T_link * Arm6dof_UrdfJointTransform(i, q->q[i]);
float p_com_raw[3] = {
s_urdf_link_com_xyz[i][0],
s_urdf_link_com_xyz[i][1],
s_urdf_link_com_xyz[i][2],
};
Matrixf<3, 1> p_com_local(p_com_raw);
Matrixf<4, 4> T_com = T_link * robotics::p2t(p_com_local);
Matrixf<3, 1> p_com_world = robotics::t2p(T_com);
U += s_links[i].m() * g * p_com_world[2][0];
}
return U;
}
static Matrixf<6, 1> Arm6dof_UrdfPoseError(const Matrixf<4, 4>& T_target,
const Matrixf<4, 4>& T_cur) {
Matrixf<6, 1> err;
Matrixf<3, 1> pe = robotics::t2p(T_target) - robotics::t2p(T_cur);
Matrixf<3, 1> we = robotics::t2twist(T_target * robotics::invT(T_cur)).block<3, 1>(3, 0);
for (int k = 0; k < 3; ++k) {
err[k][0] = pe[k][0];
err[k + 3][0] = we[k][0];
}
return err;
}
static Matrixf<6, 6> Arm6dof_UrdfNumericJacobian(const Arm6dof_JointAngles_t* q) {
Matrixf<6, 6> J = matrixf::zeros<6, 6>();
const float eps = 1e-4f;
for (int i = 0; i < 6; ++i) {
Arm6dof_JointAngles_t q_plus = *q;
Arm6dof_JointAngles_t q_minus = *q;
q_plus.q[i] += eps;
q_minus.q[i] -= eps;
Matrixf<4, 4> T_plus = Arm6dof_UrdfForwardTransform(&q_plus, 6);
Matrixf<4, 4> T_minus = Arm6dof_UrdfForwardTransform(&q_minus, 6);
Matrixf<3, 1> p_plus = robotics::t2p(T_plus);
Matrixf<3, 1> p_minus = robotics::t2p(T_minus);
Matrixf<3, 1> dp = (p_plus - p_minus) / (2.0f * eps);
Matrixf<3, 3> R_plus = robotics::t2r(T_plus);
Matrixf<3, 3> R_minus = robotics::t2r(T_minus);
Matrixf<3, 3> R_rel = R_plus * R_minus.trans();
Matrixf<4, 1> angvec = robotics::r2angvec(R_rel);
Matrixf<3, 1> w;
w[0][0] = angvec[0][0] * angvec[3][0] / (2.0f * eps);
w[1][0] = angvec[1][0] * angvec[3][0] / (2.0f * eps);
w[2][0] = angvec[2][0] * angvec[3][0] / (2.0f * eps);
J[0][i] = dp[0][0];
J[1][i] = dp[1][0];
J[2][i] = dp[2][0];
J[3][i] = w[0][0];
J[4][i] = w[1][0];
J[5][i] = w[2][0];
}
return J;
}
/**
* @brief 初始化机械臂运动学模型
*/
void Arm6dof_Init(const Arm6dof_DHParams_t dh_params[6]) {
if (dh_params == nullptr) {
return;
}
// 创建DH链表
if (s_links != nullptr) {
delete[] s_links;
}
s_links = new robotics::Link[6];
for (int i = 0; i < 6; i++) {
// 构建质心向量DH连杆坐标系下由URDF经Rz(-theta_offset)旋转得到)
Matrixf<3, 1> rc_vec;
rc_vec[0][0] = dh_params[i].rc[0];
rc_vec[1][0] = dh_params[i].rc[1];
rc_vec[2][0] = dh_params[i].rc[2];
s_links[i] = robotics::Link(
dh_params[i].theta, // 初始theta值通常为0
dh_params[i].d, // 连杆偏移 (m)
dh_params[i].a, // 连杆长度 (m)
dh_params[i].alpha, // 扭转角 (rad)
robotics::R, // 旋转关节
dh_params[i].theta_offset, // theta偏移 (rad)
dh_params[i].qmin, // 最小角度 (rad)
dh_params[i].qmax, // 最大角度 (rad)
dh_params[i].m, // 质量 (kg)
rc_vec // 质心坐标DH连杆坐标系m
);
}
// 创建机器人模型
if (s_robot != nullptr) {
delete s_robot;
}
s_robot = new robotics::Serial_Link<6>(s_links);
s_initialized = true;
}
/**
* @brief 正运动学
*/
int Arm6dof_ForwardKinematics(const Arm6dof_JointAngles_t* q, Arm6dof_Pose_t* pose) {
if (!s_initialized || q == nullptr || pose == nullptr) {
return -1;
}
// 使用URDF原生链式FKT = Π( T_origin_i * RotZ(q_i) )
Matrixf<4, 4> T = Arm6dof_UrdfForwardTransform(q, 6);
// 提取位置 (x, y, z)
Matrixf<3, 1> pos = robotics::t2p(T);
pose->x = pos[0][0];
pose->y = pos[1][0];
pose->z = pos[2][0];
// 提取姿态 (RPY欧拉角)
Matrixf<3, 1> rpy = robotics::t2rpy(T);
pose->yaw = rpy[0][0];
pose->pitch = rpy[1][0];
pose->roll = rpy[2][0];
return 0;
}
/**
* @brief 获取指定关节的位姿
*/
int Arm6dof_GetJointPose(const Arm6dof_JointAngles_t* q, uint8_t joint_num, Arm6dof_Pose_t* pose) {
if (!s_initialized || q == nullptr || pose == nullptr) {
return -1;
}
if (joint_num < 1 || joint_num > 6) {
return -1;
}
// 计算到第k个关节的URDF链式变换矩阵
Matrixf<4, 4> T = Arm6dof_UrdfForwardTransform(q, joint_num);
// 提取位置和姿态
Matrixf<3, 1> pos = robotics::t2p(T);
pose->x = pos[0][0];
pose->y = pos[1][0];
pose->z = pos[2][0];
Matrixf<3, 1> rpy = robotics::t2rpy(T);
pose->yaw = rpy[0][0];
pose->pitch = rpy[1][0];
pose->roll = rpy[2][0];
return 0;
}
/**
* @brief 逆运动学
*/
int Arm6dof_InverseKinematics(const Arm6dof_Pose_t* pose, const Arm6dof_JointAngles_t* q_init,
Arm6dof_JointAngles_t* q_result, float tol, uint16_t max_iter) {
if (!s_initialized || pose == nullptr || q_result == nullptr) {
return -1;
}
// 构造目标变换矩阵
Matrixf<3, 1> rpy_vec;
rpy_vec[0][0] = pose->yaw;
rpy_vec[1][0] = pose->pitch;
rpy_vec[2][0] = pose->roll;
Matrixf<3, 3> R_target = robotics::rpy2r(rpy_vec);
Matrixf<3, 1> p_target;
p_target[0][0] = pose->x;
p_target[1][0] = pose->y;
p_target[2][0] = pose->z;
Matrixf<4, 4> T_target = robotics::rp2t(R_target, p_target);
// 初始猜测值
Matrixf<6, 1> q_guess = matrixf::zeros<6, 1>();
if (q_init != nullptr) {
for (int i = 0; i < 6; i++) {
q_guess[i][0] = q_init->q[i];
}
}
// 基于URDF链式FK的LM数值迭代避免FK/IK模型不一致。
Matrixf<6, 1> q_cur = q_guess;
float lambda = 1.0f;
constexpr float lambda_up = 10.0f;
constexpr float lambda_down = 0.1f;
constexpr float lambda_max = 1e6f;
constexpr float lambda_min = 1e-6f;
for (uint16_t iter = 0; iter < max_iter; ++iter) {
Arm6dof_JointAngles_t q_cur_struct;
for (int i = 0; i < 6; ++i) {
q_cur_struct.q[i] = q_cur[i][0];
}
Matrixf<4, 4> T_cur = Arm6dof_UrdfForwardTransform(&q_cur_struct, 6);
Matrixf<6, 1> err = Arm6dof_UrdfPoseError(T_target, T_cur);
float err_norm = err.norm();
if (err_norm < tol) {
break;
}
Matrixf<6, 6> J = Arm6dof_UrdfNumericJacobian(&q_cur_struct);
Matrixf<6, 6> H = J.trans() * J + lambda * matrixf::eye<6, 6>();
Matrixf<6, 1> g = J.trans() * err;
Matrixf<6, 1> dq = matrixf::inv(H) * g;
if (dq[0][0] == INFINITY || dq[0][0] != dq[0][0]) {
lambda *= lambda_up;
if (lambda > lambda_max) {
return -1;
}
continue;
}
Matrixf<6, 1> q_new = q_cur + dq;
// 对循环关节折叠,避免等效角导致数值发散。
for (int i = 0; i < 6; ++i) {
constexpr float TWO_PI = 2.0f * (float)M_PI;
bool is_cyclic = (s_links[i].qmin_ < 0.1f) && (s_links[i].qmax_ > TWO_PI - 0.1f);
if (is_cyclic) {
q_new[i][0] = math::loopLimit(q_new[i][0], s_links[i].qmin_, s_links[i].qmax_);
}
}
Arm6dof_JointAngles_t q_new_struct;
for (int i = 0; i < 6; ++i) {
q_new_struct.q[i] = q_new[i][0];
}
Matrixf<4, 4> T_new = Arm6dof_UrdfForwardTransform(&q_new_struct, 6);
Matrixf<6, 1> new_err = Arm6dof_UrdfPoseError(T_target, T_new);
float new_err_norm = new_err.norm();
if (new_err_norm < err_norm) {
q_cur = q_new;
lambda *= lambda_down;
if (lambda < lambda_min) {
lambda = lambda_min;
}
} else {
lambda *= lambda_up;
if (lambda > lambda_max) {
return -1;
}
}
}
for (int i = 0; i < 6; ++i) {
q_result->q[i] = q_cur[i][0];
}
// 验证解的精度:位置误差 + 姿态误差双重校验
Arm6dof_JointAngles_t q_verify_struct;
for (int i = 0; i < 6; ++i) {
q_verify_struct.q[i] = q_cur[i][0];
}
Matrixf<4, 4> T_verify = Arm6dof_UrdfForwardTransform(&q_verify_struct, 6);
Matrixf<3, 1> p_verify = robotics::t2p(T_verify);
float pos_error = (p_verify - p_target).norm();
if (pos_error > tol * 10.0f) { // 位置误差过大,未收敛
return -1;
}
// 姿态误差:计算两旋转矩阵的测地距离 theta = arccos((trace(R_v * R_t^T) - 1) / 2)
// 使用旋转矩阵方法避免 RPY 奇异点和角度折叠问题
Matrixf<3, 3> R_verify = robotics::t2r(T_verify);
Matrixf<3, 3> R_diff = R_verify * R_target.trans();
float trace_val = R_diff[0][0] + R_diff[1][1] + R_diff[2][2];
float cos_theta = (trace_val - 1.0f) * 0.5f;
// 数值钳位防止 acosf 域溢出
if (cos_theta > 1.0f) cos_theta = 1.0f;
if (cos_theta < -1.0f) cos_theta = -1.0f;
float ang_error = acosf(cos_theta); // [0, π]
const float ang_tol = 0.1f; // 姿态收敛阈值 (rad ≈ 5.7°)
if (ang_error > ang_tol) { // 姿态未收敛(收敛到错误分支或近奇异点)
return -1;
}
return 0;
}
/**
* @brief 计算重力补偿力矩
* @details 与当前URDF链式运动学保持一致通过势能 U(q)=Σ(mgz) 的数值梯度
* τg = ∂U/∂q 计算重力补偿力矩。
*/
int Arm6dof_GravityCompensation(const Arm6dof_JointAngles_t* q, float gravity_torques[6]) {
if (!s_initialized || q == nullptr || gravity_torques == nullptr) {
return -1;
}
// // 关节角度
// Matrixf<6, 1> q_mat;
// for (int i = 0; i < 6; i++) {
// q_mat[i][0] = q->q[i];
// }
// // 速度和加速度均为零 —— 只计算重力项
// Matrixf<6, 1> qv = matrixf::zeros<6, 1>();
// Matrixf<6, 1> qa = matrixf::zeros<6, 1>();
// // 调用牛顿-欧拉逆动力学
// Matrixf<6, 1> torques = s_robot->rne(q_mat, qv, qa);
// for (int i = 0; i < 6; i++) {
// gravity_torques[i] = torques[i][0];
// }
constexpr float eps = 1e-4f;
for (int i = 0; i < 6; ++i) {
Arm6dof_JointAngles_t q_plus = *q;
Arm6dof_JointAngles_t q_minus = *q;
q_plus.q[i] += eps;
q_minus.q[i] -= eps;
float U_plus = Arm6dof_UrdfPotentialEnergy(&q_plus);
float U_minus = Arm6dof_UrdfPotentialEnergy(&q_minus);
gravity_torques[i] = (U_plus - U_minus) / (2.0f * eps);
}
return 0;
}