arm/User/component/arm_kinematics/arm6dof.cpp

251 lines
7.9 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;
/**
* dh_params = [
{"a": 0, "alpha": pi / 2, "d": 0, "theta_offset": 0}, # Joint 1
{"a": 0.404, "alpha": 0.0, "d": 0, "theta_offset": 0}, # Joint 2
{"a": 0.091579, "alpha": -pi / 2, "d": 0, "theta_offset": pi / 2}, # Joint 3
{"a": 0, "alpha": pi / 2, "d": 0.2411, "theta_offset": 0},
{"a": 0, "alpha": -pi / 2, "d": 0, "theta_offset": 0},
{"a": 0, "alpha": 0, "d": 0.1094, "theta_offset": 0},
{"a": 0.0577, "alpha": pi / 2, "d": 0.033001, "theta_offset": 0},
{"a": 0.05, "alpha": 0, "d": 0, "theta_offset": 0},
]
* @brief 根据参数创建DH参数链表
* @note 标准DH参数参考 DH_PARAMETERS.md 文档
*
* DH参数表标准DH
* 关节 | θᵢ(变量) | dᵢ | aᵢ | αᵢ | 说明
* -----|----------|-------|------|--------|------------------
* 1 | q1 | h0 | 0 | π/2 | 底座旋转Yaw
* 2 | q2 | 0 | L1 | 0 | 肩部俯仰Pitch
* 3 | q3 | 0 | L2 | 0 | 肘部俯仰Pitch
* 4 | q4 | d4 | 0 | π/2 | 腕部滚转Roll
* 5 | q5 | 0 | 0 | -π/2 | 腕部俯仰Pitch
* 6 | q6 | d6 | 0 | 0 | 末端旋转Roll
/**
* @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;
}
// 将关节角度转换为矩阵格式
Matrixf<6, 1> q_mat;
for (int i = 0; i < 6; i++) {
q_mat[i][0] = q->q[i];
}
// 计算正运动学
Matrixf<4, 4> T = s_robot->fkine(q_mat);
// 提取位置 (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;
}
// 将关节角度转换为矩阵格式
Matrixf<6, 1> q_mat;
for (int i = 0; i < 6; i++) {
q_mat[i][0] = q->q[i];
}
// 计算到第k个关节的变换矩阵
Matrixf<4, 4> T = s_robot->fkine(q_mat, 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];
}
}
// 调用toolbox的逆运动学求解器牛顿法
Matrixf<6, 1> q_solved = s_robot->ikine(T_target, q_guess, tol, max_iter);
// 将结果复制到输出
for (int i = 0; i < 6; i++) {
q_result->q[i] = q_solved[i][0];
}
// 验证解的精度:位置误差 + 姿态误差双y重校验
Matrixf<4, 4> T_verify = s_robot->fkine(q_solved);
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 利用牛顿-欧拉逆动力学(rne)设速度qv=0、加速度qa=0
* 计算的关节力矩即为平衡重力所需的力矩。
*/
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];
}
return 0;
}