完整教程:三轴机械臂的逆解全流程

介绍:

本文关于三轴机械臂的逆解计算只用到了高中的三角函数,所以对于还没学线代或者刚入门机械臂的小白来说十分友好,可为以后六轴机械臂的逆解计算打基础,文中还用到了很多插图来帮助读者理解,并且提供了相关代码帮助读者将知识加以应用

机械臂结构定义:

  • L1:大臂长度(J1J2的距离)

  • L2:小臂长度(J2J3的距离)

  • 目标坐标(X,Y,Z):机械臂末端(J3)要到达的位置


    建立坐标系


    简图

逆解算公式推导(高中数学可懂)

逆解算就是“已知目标坐标,求J1,J2,J3的角度”

关节角度基准(所有关节角度为 0°)

在这里插入图片描述

  • J1(底盘旋转关节):0° 时,机械臂的 “大臂 - 小臂” 整体沿 基座坐标系 X 轴正方向(即水平向前),无左右旋转。
  • J2(大臂摆动关节):0° 时,大臂(J1-J2 段)与 基座坐标系 X 轴完全重合,无上下摆动。
  • J3(小臂摆动关节):0° 时,小臂(J2-J3 段)与 大臂(J1-J2 段)完全共线,且沿 X 轴正方向延伸。

J1(底盘旋转角)

立体图
在这里插入图片描述
俯视图
在这里插入图片描述

J1控制末端在XY平面的旋转,用反正切函数即可求解:
J1 =arctan2(Y/X)
计算出J1 ∈ (-π, π](arctan2 ()默认结果),先转换为0°~360°范围(负角加 360°)

【arctan2()和arctan()的区别:】
arctan():可计算Y/X的反正切值,但无法分辨(1,1)和(-1,-1)这类符号相同但象限相反的点(结果范围:(-π/2, π/2))
arctan2():可判断坐标所在象限,如:X=-1,Y=-1,先明确该点在坐标系的第四象限,再结合Y/X的绝对值计算角度,并且可以解决X=0的情况(结果范围:(-π, π])

J2(大臂摆动角)

在这里插入图片描述
分类讨论:(计算过程类似,这里只详解一种)

H>Z

在这里插入图片描述
参数定义

  • l1J2轴 的 Z 坐标与目标点 Z 坐标的 差值l1 = Z - H(若为负数,则目标点在J2的下方)

  • l2:目标点在 XY 平面的投影到原点的距离(即水平距离),l2 = √(X² + Y²)

  • l3J2轴 到目标点的 直线距离(即三角形的斜边),l3 = √(l2² + l1²)

  • θ1:大臂L1J2轴 到末端的连线l3的夹角

  • θ2l₃与垂直方向(Z 轴)的夹角

    求解过程

  • 利用余弦定理求出cosθ1大小,再利用反正切和反余弦求出θ1θ2的大小
    θ1 = arccos( (L1²+l3²-L2²) / (2·L1·l3) )
    θ2 = arctan2(l2,|l1|)

  • 最后θ1+θ2相加得到J2+90°
    J2=θ1+θ2-90°

H<Z

计算逻辑与H>Z相同,J2=θ1+θ2-90°
在这里插入图片描述

H=Z

l1=0l3=l2
θ2=arctan2(l2, 0)=90°(因|l1|=0),θ1是大臂L1J2轴到末端的连线l3 的夹角(因 l3=l2,故等价于大臂与水平方向的夹角)
J2=θ1+θ2-90°=θ1

在这里插入图片描述

J3(小臂摆动角)

在这里插入图片描述
J3 = 180° - arccos( (L1² + L2² - l3²)/(2·L1·L2) )(余弦定理求的是J3的补角)

细节处理:

1. 判断解的存在性
  • 根据L1L2的长度计算目标点XY坐标范围
    l3 > L1+L2l3 < |L1-L2|(XY 平面投影超出连杆可达范围),无逆解。
  • 根据J2,J3的限位计算目标点Z坐标范围
    若 J2 有向下限位(如 J2≥-θ,θ 为向下最大角度):
    Z_max = H + L1 + L2(大臂向上、小臂向上共线)
    Z_min = H - L1・sinθ - L2(大臂向下到限位、小臂向下伸直)
    若 J2 无向下限位(J2≥-90°):
    Z_max = H + L1 + L2
    Z_min = H - (L1 + L2)(与原公式一致)
    Z < Z_minZ > Z_max(Z 轴高度超出可达范围),无逆解
2.处理限位范围问题
关节常见限位范围限位原因处理规则
J1±170°(或 0°~340°)防止底盘线缆缠绕(如动力线、信号线)特殊场景:若需连续旋转,可通过 “多圈计数” 优化
J2-30°~90°(或 0°~90°)1. 向下摆动超-30°(低于水平),大臂可能与底盘底座干涉;
2. 向上摆动超90°(垂直向上),大臂与小臂易发生连杆碰撞
若你的机械臂J2无向下摆动需求,可直接限定J2 ∈ [0°, 90°]
J30°~180°1. 小臂向后翻折(J3 < 0°)会与大臂干涉;
2. 小臂顺时针旋转超180°,小臂会折叠到自身极限
J3的限位是 “避免小臂与大臂干涉”,需与J2角度联动检查

逆解计算流程总结

  1. 输入参数L1(大臂长)、L2(小臂长)、H(J2 的 Z 高度)、目标坐标 (X,Y,Z)
  2. 计算 J1J1 = arctan2(Y, X)(转换为角度,范围 [-180°, 180°]);
  3. 计算辅助参数l2=√(X²+Y²)l1=Z-Hl3=√(l2²+l1²)
  4. 判断解的存在性:若目标点坐标不在机械臂可到达范围输出 “无逆解”,结束;
  5. 计算 J2
    • 计算θ1=arccos( (L1²+l3²-L2²)/(2·L1·l3) )(范围[0°, 180°]
    • 计算θ2=arctan2(l2, |l1|)(范围[0°, 90°]
    • 利用通式J2=θ1+θ2-90°,计算J2
  6. 计算 J3J3 = 180° - arccos( (L1² + L2² - l3²)/(2·L1·L2) )
  7. 限位检查:若 J2J3 超出机械臂物理限位,输出 “角度超限位”,否则输出 (J1, J2, J3)

完整的逆解计算示例

1.输入已知参数

参数类型具体数值备注
机械臂结构参数L1=500mm(大臂长度)J1 到 J2 的距离
L2=300mm(小臂长度)J2 到 J3(末端)的距离
H=100mm(J2 关节的 Z 高度)J2 固定在基座上方 100mm 处,Z 坐标恒为 100mm
目标坐标(X=400mm, Y=0mm, Z=400mm)末端需到达的位置(Y=0 表示在 X 轴正方向)
关节限位J1∈[0°, 340°],J2∈[-30°, 90°],J3∈[0°, 180°]常见桌面级机械臂限位

2.计算 J1(底盘旋转角)

  1. 公式:J1 = arctan2(Y, X)
  2. 代入数值:J1 = arctan2(0, 400) = 0°(Y=0,X>0,对应 X 轴正方向)
  3. 范围转换:因结果 0°∈[0°, 360°],无需调整,最终J1=0°

3.计算辅助参数(l1l2l3

  • l1 = Z - H
    代入数值:l1 = 400 - 100 = 300mm(l1>0,说明末端在 J2 上方,对应 H<Z 场景)
  • l2 = √(X² + Y²)
    代入数值:l2 = √(400² + 0²) = 400mm
  • l3 = √(l2² + l1²)
    代入数值:l3 = √(400² + 300²) = √(160000 + 90000) = √250000 = 500mm
  • 汇总:l2=400mm,l1=300mm,l3=500mm

4.判断解的存在性(避免无效计算)

需同时满足 “XY 平面可达” 和 “Z 轴可达” 两个条件,否则无逆解
  • XY 平面可达性(三角形边长关系)
      条件:`|L1 - L2| ≤ l3 ≤ L1 + L2`
      最小值:`|500 - 300| = 200mm`
      最大值:`500 + 300 = 800mm`
      当前 l3=500mm,满足 200mm≤500mm≤800mm,**XY 平面可达**
  • Z 轴可达性(结合 J2 限位)
      已知 J2 有向下限位 θ=30°(J2≥-30°),需计算 Z_min 和 Z_max:
      最大高度 Z_max:`H + L1 + L2`(大臂 + 小臂共线向上)
      代入:`Z_max = 100 + 500 + 300 = 900mm`
      最小高度 Z_min:`H - L1·sinθ - L2`(大臂向下到限位,小臂向下伸直)
      代入:`Z_min = 100 - 500×sin30° - 300 = 100 - 500×0.5 - 300 = 100 - 250 - 300 = -450mm`
      当前 Z=400mm,满足 - 450mm≤400mm≤900mm,**Z 轴可达**
  • 结论:目标点在机械臂工作空间内,有逆解,可继续计算

5.计算 J2(大臂摆动角)

  • 求 θ1(范围 [0°, 180°])
    公式:θ1 = arccos( (L1² + l3² - L2²) / (2·L1·l3) )
    代入数值:
    分子:500² + 500² - 300² = 250000 + 250000 - 90000 = 410000
    分母:2×500×500 = 500000
    比值:410000 / 500000 = 0.82
    查反余弦表 / 计算器:θ1 = arccos(0.82) ≈ 34.9°

  • 求 θ2(范围 [0°, 90°])
    公式:θ2 = arctan2(l2, |l1|)(|l1|=300mm,因 l1>0)
    代入数值:θ2 = arctan2(400, 300) ≈ 53.13°(直角三角形对边 400、邻边 300,tanθ=4/3,对应角度≈53.13°)

  • 求 J2(通式)
    公式:J2 = θ1 + θ2 - 90°
    代入数值:J2 = 34.9° + 53.13° - 90° ≈ -1.97°

6.计算 J3(小臂摆动角)

  • 公式:J3 = 180° - arccos( (L1² + L2² - l3²) / (2·L1·L2) )
  • 代入数值:
    分子:500² + 300² - 500² = 90000(L1² 与 - l3² 抵消)
    分母:2×500×300 = 300000
    比值:90000 / 300000 = 0.3
    查反余弦表 / 计算器:arccos(0.3) ≈ 72.54°(此为 J3 的补角)
  • 求 J3:J3 = 180° - 72.54° ≈ 107.46°

7.最终逆解结果与限位检查

关节计算结果是否在限位内物理运动描述
J1是(0°∈[0°,340°])底盘不旋转,末端朝 X 轴正方向
J2-1.97°是(-1.97°∈[-30°,90°])大臂轻微向下摆动,接近水平
J3107.46°是(107.46°∈[0°,180°])小臂向上折叠约 107°,到达目标点

8.正解验证

基于逆解结果(J1=0°、J2=-1.97°、J3=107.46°),分步反推末端坐标,验证与目标(400,0,400)的一致性:

  1. 算J2(大臂末端)坐标

    • X 方向:J2_X = L1×cos(-1.97°)≈500×0.9994≈499.7mm;
    • Y 方向:J1=0°,J2的Y=0(与目标Y一致);
    • Z 方向:J2_Z = H + L1×sin(-1.97°)≈100 - 17.2≈82.8mm;
    • J2 坐标:(499.7, 0, 82.8)
  2. 算J3(小臂末端)坐标

    • 小臂绝对角度(相对于X轴)= J2 + J3 = -1.97°+107.46°=105.49°;
    • 小臂X增量:ΔX = L2×cos(105.49°)≈300×(-0.267)≈-80.1mm;
    • 小臂Z增量:ΔZ = L2×sin(105.49°)≈300×0.964≈289.2mm;
    • J3坐标:(499.7-80.1, 0, 82.8+289.2)≈(419.6, 0, 372)
      3- 误差说明
    • 结果(419.6,0,372)与目标(400,0,400)的误差,源于手动近似角度(如 J2、J3 取 2 位小数)和三角函数值
    • 用计算器精确计算(保留 6 位小数),误差可缩小至 5mm 内,符合机械臂控制要求

基于STM32的代码实现

一.模块拆分说明

模块文件核心职责依赖关系
arm_kinematics.h数据结构、函数声明(逆解 / 正解 / 工具)无(仅依赖标准库)
arm_kinematics.c逆解核心算法、正解验证、解的存在性判断依赖 arm_kinematics.hmath.h
arm_servo.h舵机驱动函数声明依赖 STM32 HAL 库
arm_servo.c舵机 PWM 控制实现、角度校准依赖 arm_servo.h、STM32 HAL 库
arm_config.h机械臂参数配置(尺寸、限位、硬件引脚)
main.c初始化、调用流程、测试用例依赖所有头文件

二、各模块完整代码

1. 配置文件:arm_config.h(集中管理参数,无需修改其他文件)
#ifndef ARM_CONFIG_H
#define ARM_CONFIG_H
#include "stm32f4xx_hal.h"  // 根据实际STM32系列修改(如stm32f1xx_hal.h)
// -------------------------- 机械臂结构参数(必须按硬件修改)--------------------------
#define L1          500.0f    // 大臂长度(mm):J1→J2
#define L2          300.0f    // 小臂长度(mm):J2→J3
#define H           100.0f    // J2关节Z高度(mm):固定Z坐标
// -------------------------- 关节限位参数(按机械臂物理限位修改)--------------------------
#define J1_MIN      0.0f      // J1最小角度(°)
#define J1_MAX      340.0f    // J1最大角度(°)
#define J2_MIN      -30.0f    // J2最小角度(°)(向下)
#define J2_MAX      90.0f     // J2最大角度(°)(向上)
#define J3_MIN      0.0f      // J3最小角度(°)
#define J3_MAX      180.0f    // J3最大角度(°)
// -------------------------- 舵机硬件配置(按实际引脚/TIM通道修改)--------------------------
#define SERVO_TIM               &htim1          // 舵机控制TIM外设
#define SERVO_J1_CHANNEL        TIM_CHANNEL_1   // J1舵机TIM通道
#define SERVO_J2_CHANNEL        TIM_CHANNEL_2   // J2舵机TIM通道
#define SERVO_J3_CHANNEL        TIM_CHANNEL_3   // J3舵机TIM通道
// -------------------------- 舵机参数校准(按舵机 datasheet 修改)--------------------------
#define SERVO_MIN_PULSE         5000    // 0°对应脉冲宽度(0.1us单位):0.5ms=5000×0.1us
#define SERVO_MAX_PULSE         25000   // 180°对应脉冲宽度:2.5ms=25000×0.1us
#define SERVO_J2_OFFSET         90.0f   // J2舵机角度偏移(机械基准与舵机0°不一致时调整)
// -------------------------- 通用常量 --------------------------
#define PI          3.1415926535f  // 圆周率
#define RAD_TO_DEG  (180.0f / PI)  // 弧度→角度
#define DEG_TO_RAD  (PI / 180.0f)  // 角度→弧度
#endif // ARM_CONFIG_H
2. 运动学头文件:arm_kinematics.h(函数声明与数据结构)
#ifndef ARM_KINEMATICS_H
#define ARM_KINEMATICS_H
#include "arm_config.h"
#include <math.h>
  // -------------------------- 数据结构定义 --------------------------
  // 末端坐标结构体(单位:mm)
  typedef struct {
  float X;
  float Y;
  float Z;
  } Coordinate_t;
  // 关节角度结构体(单位:°)
  typedef struct {
  float J1;
  float J2;
  float J3;
  } JointAngle_t;
  // 逆解状态枚举(返回结果)
  typedef enum {
  INVERSE_SUCCESS = 0,    // 逆解成功
  INVERSE_NO_SOLUTION,    // 无逆解(超出工作空间)
  INVERSE_ANGLE_LIMIT     // 角度超限位
  } InverseStatus_t;
  // -------------------------- 函数声明 --------------------------
  /** * @brief  三轴机械臂逆解计算(核心函数) * @param  target: 目标末端坐标 * @param  angle: 输出关节角度 * @return 逆解状态 */
  InverseStatus_t InverseKinematics(Coordinate_t target, JointAngle_t *angle);
  /** * @brief  正解验证(反推末端坐标,校验逆解正确性) * @param  angle: 关节角度 * @param  coord: 输出末端坐标 */
  void ForwardKinematics(JointAngle_t angle, Coordinate_t *coord);
  /** * @brief  计算两点间3D距离(辅助函数) * @param  p1: 点1坐标 * @param  p2: 点2坐标 * @return 距离(mm) */
  float Calc3DDistance(Coordinate_t p1, Coordinate_t p2);
  #endif // ARM_KINEMATICS_H
3. 运动学核心实现:arm_kinematics.c(算法逻辑)
#include "arm_kinematics.h"
// -------------------------- 辅助函数:计算3D距离 --------------------------
float Calc3DDistance(Coordinate_t p1, Coordinate_t p2) {
return sqrtf( powf(p1.X-p2.X,2) + powf(p1.Y-p2.Y,2) + powf(p1.Z-p2.Z,2) );
}
// -------------------------- 正解验证实现 --------------------------
void ForwardKinematics(JointAngle_t angle, Coordinate_t *coord) {
float J1_rad = angle.J1 * DEG_TO_RAD;
float J2_rad = angle.J2 * DEG_TO_RAD;
float J3_rad = angle.J3 * DEG_TO_RAD;
// 1. 计算J2坐标(大臂末端)
float J2_X = L1 * cosf(J2_rad);
float J2_Z = H + L1 * sinf(J2_rad);
// 2. 小臂绝对角度(相对于基座X轴)
float arm2_angle = J2_rad + J3_rad;
// 3. 小臂位移增量
float delta_X = L2 * cosf(arm2_angle);
float delta_Z = L2 * sinf(arm2_angle);
// 4. 末端坐标(J1旋转影响XY平面)
float end_X = J2_X + delta_X;
float end_Z = J2_Z + delta_Z;
coord->X = end_X * cosf(J1_rad) - 0 * sinf(J1_rad);  // Y方向无偏移
coord->Y = end_X * sinf(J1_rad) + 0 * cosf(J1_rad);
coord->Z = end_Z;
}
// -------------------------- 逆解核心算法实现 --------------------------
InverseStatus_t InverseKinematics(Coordinate_t target, JointAngle_t *angle) {
float l2, l1, l3;
float theta1, theta2;
float J1, J2, J3;
// Step 1: 计算J1(底盘旋转角)
J1 = atan2f(target.Y, target.X) * RAD_TO_DEG;  // 范围(-180°,180°)
if (J1 < 0) J1 += 360.0f;  // 转换为0°~360°
// Step 2: 计算辅助参数(l1/l2/l3)
l2 = sqrtf( powf(target.X,2) + powf(target.Y,2) );  // XY平面水平距离
l1 = target.Z - H;                                  // Z轴差值
l3 = sqrtf( powf(l2,2) + powf(l1,2) );              // J2→目标点直线距离
// Step 3: 判断解的存在性(XY平面:三角形边长关系)
if (l3 > (L1 + L2) || l3 < fabsf(L1 - L2)) {
return INVERSE_NO_SOLUTION;
}
// Step 4: 判断Z轴可达性(结合J2限位)
float Z_max = H + L1 + L2;                          // 最大高度
float Z_min = H - L1*sinf(fabsf(J2_MIN)*DEG_TO_RAD) - L2;  // 最小高度
if (target.Z < Z_min || target.Z > Z_max) {
  return INVERSE_NO_SOLUTION;
  }
  // Step 5: 计算J2(大臂摆动角)
  float cos_theta1 = (powf(L1,2) + powf(l3,2) - powf(L2,2)) / (2 * L1 * l3);
  // 限制cos值范围(避免浮点误差)
  cos_theta1 = (cos_theta1 > 1.0f) ? 1.0f : (cos_theta1 < -1.0f ? -1.0f : cos_theta1);
  theta1 = acosf(cos_theta1) * RAD_TO_DEG;  // 0°~180°
  theta2 = atan2f(l2, fabsf(l1)) * RAD_TO_DEG;  // l3与垂直方向夹角(0°~90°)
  J2 = theta1 + theta2 - 90.0f;                // 通式计算J2
  // Step 6: 计算J3(小臂摆动角)
  float cos_J3_supplement = (powf(L1,2) + powf(L2,2) - powf(l3,2)) / (2 * L1 * L2);
  cos_J3_supplement = (cos_J3_supplement > 1.0f) ? 1.0f : (cos_J3_supplement < -1.0f ? -1.0f : cos_J3_supplement);
  float J3_supplement = acosf(cos_J3_supplement) * RAD_TO_DEG;  // J3补角
  J3 = 180.0f - J3_supplement;                                  // J3实际角度
  // Step 7: 限位检查
  if (J1 < J1_MIN || J1 > J1_MAX ||
    J2 < J2_MIN || J2 > J2_MAX ||
      J3 < J3_MIN || J3 > J3_MAX) {
        return INVERSE_ANGLE_LIMIT;
        }
        // Step 8: 输出结果
        angle->J1 = J1;
        angle->J2 = J2;
        angle->J3 = J3;
        return INVERSE_SUCCESS;
        }
4. 舵机驱动头文件:arm_servo.h(硬件驱动声明)
#ifndef ARM_SERVO_H
#define ARM_SERVO_H
#include "arm_config.h"
// -------------------------- 函数声明 --------------------------
/** * @brief  初始化舵机(启动TIM PWM输出) * @return 无 */
void Servo_Init(void);
/** * @brief  单个舵机角度控制 * @param  channel: TIM通道(SERVO_J1_CHANNEL等) * @param  angle: 目标角度(0°~180°) * @return 无 */
void Servo_SetSingleAngle(uint32_t channel, float angle);
/** * @brief  三舵机同步控制(按关节角度驱动) * @param  angle: 关节角度结构体 * @return 无 */
void Servo_SetJointAngles(JointAngle_t angle);
#endif // ARM_SERVO_H
5. 舵机驱动实现:arm_servo.c(硬件控制逻辑)
#include "arm_servo.h"
//声明外部TIM句柄(与arm_config.h中SERVO_TIM对应)
extern TIM_HandleTypeDef htim1;
// -------------------------- 舵机初始化 --------------------------
void Servo_Init(void) {
// 启动TIM PWM通道(需提前在STM32CubeMX中配置TIM为PWM模式,周期20ms)
HAL_TIM_PWM_Start(SERVO_TIM, SERVO_J1_CHANNEL);
HAL_TIM_PWM_Start(SERVO_TIM, SERVO_J2_CHANNEL);
HAL_TIM_PWM_Start(SERVO_TIM, SERVO_J3_CHANNEL);
}
// -------------------------- 单个舵机角度控制 --------------------------
void Servo_SetSingleAngle(uint32_t channel, float angle) {
// 限制角度范围(避免舵机损坏)
angle = (angle < 0.0f) ? 0.0f : (angle > 180.0f ? 180.0f : angle);
  // 计算脉冲宽度:线性映射(0°→SERVO_MIN_PULSE,180°→SERVO_MAX_PULSE)
  uint32_t pulse = SERVO_MIN_PULSE + (angle / 180.0f) * (SERVO_MAX_PULSE - SERVO_MIN_PULSE);
  // 设置PWM比较值
  __HAL_TIM_SET_COMPARE(SERVO_TIM, channel, pulse);
  }
  // -------------------------- 三舵机同步控制 --------------------------
  void Servo_SetJointAngles(JointAngle_t angle) {
  // J1舵机:直接映射(0°~340°→0°~180°,需根据舵机行程调整)
  float j1_servo = (angle.J1 / (J1_MAX - J1_MIN)) * 180.0f;
  // 新增:限制舵机角度在0°~180°
  j1_servo = (j1_servo < 0.0f) ? 0.0f : (j1_servo > 180.0f ? 180.0f : j1_servo);
    // J2舵机:添加偏移量(机械基准校准)
    float j2_servo = angle.J2 + SERVO_J2_OFFSET;
    j2_servo = (j2_servo < 0.0f) ? 0.0f : (j2_servo > 180.0f ? 180.0f : j2_servo);
      // J3舵机:直接映射(0°~180°)
      float j3_servo = angle.J3;
      // 驱动三个舵机
      Servo_SetSingleAngle(SERVO_J1_CHANNEL, j1_servo);
      Servo_SetSingleAngle(SERVO_J2_CHANNEL, j2_servo);
      Servo_SetSingleAngle(SERVO_J3_CHANNEL, j3_servo);
      }
6. 主函数:main.c(初始化与调用流程)
#include "main.h"
#include "arm_kinematics.h"
#include "arm_servo.h"
#include <stdio.h>
  // 外部声明(STM32CubeMX生成的外设句柄)
  extern TIM_HandleTypeDef htim1;
  extern UART_HandleTypeDef huart1;
  // 测试用目标坐标(与前文示例一致)
  #define TARGET_X 400.0f
  #define TARGET_Y 0.0f
  #define TARGET_Z 400.0f
  // 串口打印函数(格式化输出)
  int fputc(int ch, FILE *f) {
  HAL_UART_Transmit(&huart1, (uint8_t*)&ch, 1, HAL_MAX_DELAY);
  return ch;
  }
  int main(void) {
  // 1. 系统初始化(HAL库、时钟、GPIO、USART、TIM)
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_USART1_UART_Init();  // 串口初始化(用于打印日志)
  MX_TIM1_Init();         // TIM初始化(舵机PWM)
  // 2. 机械臂初始化
  Servo_Init();           // 舵机启动
  // 3. 逆解计算与舵机控制
  Coordinate_t target = {TARGET_X, TARGET_Y, TARGET_Z};
  JointAngle_t angle;
  InverseStatus_t status;
  while (1) {
  // 执行逆解
  status = InverseKinematics(target, &angle);
  // 根据逆解状态处理
  switch(status) {
  case INVERSE_SUCCESS:
  // 打印逆解结果
  printf("逆解成功!\r\n");
  printf("J1: %.2f°, J2: %.2f°, J3: %.2f°\r\n", angle.J1, angle.J2, angle.J3);
  // 正解验证
  Coordinate_t verify;
  ForwardKinematics(angle, &verify);
  printf("验证坐标:X=%.2f, Y=%.2f, Z=%.2f\r\n", verify.X, verify.Y, verify.Z);
  printf("目标坐标:X=%.2f, Y=%.2f, Z=%.2f\r\n", target.X, target.Y, target.Z);
  // 驱动舵机
  Servo_SetJointAngles(angle);
  break;
  case INVERSE_NO_SOLUTION:
  printf("逆解失败:目标点超出工作空间!\r\n");
  break;
  case INVERSE_ANGLE_LIMIT:
  printf("逆解失败:关节角度超限位!\r\n");
  break;
  }
  HAL_Delay(5000);  // 5秒循环一次(可根据需求修改)
  }
  }

三、模块使用说明

1. 自定义修改
  • 参数修改:仅需修改 arm_config.h(机械臂尺寸、限位、舵机引脚),无需改动核心算法;
  • 硬件适配:若更换 STM32 系列,仅需修改 arm_config.h 中的 #include "stm32xxxx_hal.h" 和外设句柄(如 &htim2);
  • 舵机校准:若舵机角度不准,调整 arm_config.h 中的 SERVO_MIN_PULSESERVO_MAX_PULSESERVO_J2_OFFSET
2. 调试建议
  • 先通过串口打印 InverseKinematics 的返回状态和关节角度,确认逆解是否成功;
  • 单独测试舵机驱动(调用 Servo_SetSingleAngle 输出固定角度),确保舵机正常响应;
  • 正解验证结果与目标坐标的误差在 5mm 内为正常(机械误差 + 浮点误差)。

四、关键注意事项

  1. TIM 配置:舵机需 TIM 输出 20ms 周期(50Hz)的 PWM 波,STM32CubeMX 配置示例:
    • 预分频器:71(假设系统时钟 72MHz,72MHz/(71+1)=1MHz);
    • 自动重载值:19999(1MHz×20ms=20000,故 ARR=19999);
    • PWM 模式:PWM Generation CH1~CH3(输出比较模式)。
  2. 浮点运算:STM32F1 需开启软件浮点(Options for TargetC/C++Define 添加 __FPU_PRESENT=1,__VFP_FP__),F4/F7 默认支持硬件浮点。
  3. 串口调试fputc 函数已重定向到 USART1,可通过串口助手查看日志(波特率需与 MX_USART1_UART_Init 配置一致,如 115200)。
posted @ 2025-12-02 14:46  clnchanpin  阅读(99)  评论(0)    收藏  举报