项目介绍
本项目是一款开源的 6 自由度桌面机械臂,有效负载 500g,重复定位精度±0.5mm,支持正逆运动学解算和笛卡尔空间轨迹规划。
技术参数
| 参数 | 规格 |
|---|---|
| 自由度 | 6 (J1-J6) |
| 工作半径 | 400mm |
| 有效负载 | 500g |
| 重复精度 | ±0.5mm |
| 最大速度 | 200mm/s |
| 控制周期 | 2ms |
机械设计
结构方案
采用串联式 6 轴结构:
- J1: 基座旋转 (±180°)
- J2: 大臂俯仰 (-90°~+90°)
- J3: 小臂俯仰 (-90°~+120°)
- J4: 手腕旋转 (±180°)
- J5: 手腕俯仰 (-90°~+90°)
- J6: 末端旋转 (±180°)
3D 打印件
材料:PLA+ / PETG
- 壁厚:3mm (关键部位 4mm)
- 填充率:40%
- 层厚:0.2mm
传动设计
| 关节 | 传动方式 | 减速比 |
|---|---|---|
| J1 | 同步带 | 3:1 |
| J2 | 同步带 | 4:1 |
| J3 | 同步带 | 4:1 |
| J4 | 齿轮 | 5:1 |
| J5 | 齿轮 | 5:1 |
| J6 | 直连 | 1:1 |
硬件系统
控制板
主控: STM32F429
- 主频:180MHz
- FPU: 硬件单双精度
- 定时器:17 个
驱动: TMC2209
- 静音步进驱动
- 电流:0.2-1.5A
- 微步:256 细分
编码器: AS5047P
- 磁编码器
- 分辨率:14 位
- SPI 接口
电机配置
| 关节 | 电机型号 | 保持扭矩 |
|---|---|---|
| J1 | NEMA17 | 0.5Nm |
| J2 | NEMA17 | 0.5Nm |
| J3 | NEMA17 | 0.5Nm |
| J4 | NEMA14 | 0.2Nm |
| J5 | NEMA14 | 0.2Nm |
| J6 | NEMA14 | 0.2Nm |
运动学建模
D-H 参数表
| 关节 | θ | d(mm) | a(mm) | α(°) |
|---|---|---|---|---|
| J1 | θ1 | 150 | 0 | 90 |
| J2 | θ2 | 0 | 200 | 0 |
| J3 | θ3 | 0 | 180 | 90 |
| J4 | θ4 | 220 | 0 | -90 |
| J5 | θ5 | 0 | 0 | 90 |
| J6 | θ6 | 80 | 0 | 0 |
正运动学
typedef struct {
float x, y, z;
float rx, ry, rz;
} Pose_t;
Pose_t forward_kinematics(float joints[6]) {
Pose_t pose;
// D-H 变换矩阵连乘
// T = A1 * A2 * A3 * A4 * A5 * A6
// 提取位置
pose.x = T[0][3];
pose.y = T[1][3];
pose.z = T[2][3];
// 提取姿态 (欧拉角)
pose.rx = atan2(T[2][1], T[2][2]);
pose.ry = atan2(-T[2][0], sqrt(T[2][1]*T[2][1] + T[2][2]*T[2][2]));
pose.rz = atan2(T[1][0], T[0][0]);
return pose;
}
逆运动学 (数值解法)
#define IK_MAX_ITERATIONS 100
#define IK_CONVERGENCE_TH 0.001
int inverse_kinematics(Pose_t target, float *joints) {
float current_joints[6] = {0};
float error;
int iter = 0;
while (iter < IK_MAX_ITERATIONS) {
// 1. 计算当前位姿
Pose_t current = forward_kinematics(current_joints);
// 2. 计算误差
error = sqrt(pow(target.x - current.x, 2) +
pow(target.y - current.y, 2) +
pow(target.z - current.z, 2));
// 3. 收敛判断
if (error < IK_CONVERGENCE_TH) {
memcpy(joints, current_joints, sizeof(float)*6);
return 0; // 成功
}
// 4. 雅可比矩阵计算
float J[6][6];
compute_jacobian(current_joints, J);
// 5. 计算关节增量 (伪逆)
float dJ[6];
float error_vec[6] = {
target.x - current.x,
target.y - current.y,
target.z - current.z,
target.rx - current.rx,
target.ry - current.ry,
target.rz - current.rz
};
// dJ = J^+ * error (伪逆)
pseudo_inverse_multiply(J, error_vec, dJ);
// 6. 更新关节角
for (int i = 0; i < 6; i++) {
current_joints[i] += 0.5 * dJ[i]; // 阻尼因子
}
iter++;
}
return -1; // 失败
}
轨迹规划
关节空间规划
void joint_space_trajectory(float start[6], float end[6],
float duration, float dt) {
int steps = duration / dt;
for (int i = 0; i <= steps; i++) {
float t = (float)i / steps;
// 五次多项式插值
float s = 10*t*t*t - 15*t*t*t*t + 6*t*t*t*t*t;
for (int j = 0; j < 6; j++) {
float target = start[j] + (end[j] - start[j]) * s;
motor_move_to(j, target);
}
delay_ms(dt * 1000);
}
}
笛卡尔空间规划
void cartesian_trajectory(Pose_t start, Pose_t end,
float duration, float dt) {
int steps = duration / dt;
for (int i = 0; i <= steps; i++) {
float t = (float)i / steps;
// 直线插值
Pose_t current;
current.x = start.x + (end.x - start.x) * t;
current.y = start.y + (end.y - start.y) * t;
current.z = start.z + (end.z - start.z) * t;
// 姿态插值 (SLERP)
slerp(&start.rx, &end.rx, t, ¤t.rx);
slerp(&start.ry, &end.ry, t, ¤t.ry);
slerp(&start.rz, &end.rz, t, ¤t.rz);
// 逆运动学解算
float joints[6];
if (inverse_kinematics(current, joints) == 0) {
for (int j = 0; j < 6; j++) {
motor_move_to(j, joints[j]);
}
}
delay_ms(dt * 1000);
}
}
控制软件
实时控制循环
void control_loop_2ms(void) {
static uint32_t cycle_count = 0;
// 1. 读取编码器
for (int i = 0; i < 6; i++) {
joint_angle[i] = encoder_read(i);
}
// 2. 运动学解算
if (trajectory_active) {
trajectory_update(joint_angle);
}
// 3. PID 控制
for (int i = 0; i < 6; i++) {
float error = target_angle[i] - joint_angle[i];
float pwm = pid_calc(&pid[i], error);
stepper_set_pwm(i, pwm);
}
// 4. 限位检测
check_limit_switches();
// 5. 状态上报
if (cycle_count % 10 == 0) {
send_status_report();
}
cycle_count++;
}
G 代码解析器
void parse_gcode(char *line) {
char cmd[10];
float params[26]; // A-Z
sscanf(line, "%s", cmd);
if (strcmp(cmd, "G0") == 0 || strcmp(cmd, "G1") == 0) {
// 直线运动
parse_params(line, params);
Pose_t target;
target.x = params['X'-'A'];
target.y = params['Y'-'A'];
target.z = params['Z'-'A'];
float feedrate = params['F'-'A'];
cartesian_move(&target, feedrate);
}
else if (strcmp(cmd, "G2") == 0 || strcmp(cmd, "G3") == 0) {
// 圆弧运动
// ...
}
}
调试与优化
问题 1:逆解不收敛
现象:某些位姿无法求解
原因:奇异点附近雅可比矩阵病态
解决:
- 加入阻尼最小二乘法 (DLS)
- 奇异点检测与规避
- 多初始值尝试
问题 2:末端抖动
现象:定位后持续微抖
原因:PID 参数过强 + 机械间隙
解决:
- 降低微分项
- 增加死区补偿
- 机械预紧消除间隙
问题 3:轨迹跟踪误差
现象:高速运动时轨迹偏差大
原因:前馈不足 + 控制带宽低
解决:
- 加入速度/加速度前馈
- 提高控制频率 (1ms → 0.5ms)
- 优化轨迹平滑度
性能测试
定位精度测试
| 测试点 | 理论值 (mm) | 实测值 (mm) | 误差 (mm) |
|---|---|---|---|
| P1 | (200, 0, 100) | (199.6, 0.2, 99.8) | 0.45 |
| P2 | (150, 100, 150) | (149.7, 99.8, 150.3) | 0.42 |
| P3 | (100, -150, 200) | (99.5, -149.7, 199.6) | 0.58 |
重复定位精度
连续 100 次定位同一位置:
- X 轴标准差:0.12mm
- Y 轴标准差:0.15mm
- Z 轴标准差:0.18mm
- 重复精度: ±0.45mm (3σ)
应用案例
- 3D 打印: 加装挤出机实现 3D 打印
- 激光雕刻: 加装激光头进行雕刻
- 物料搬运: 加装夹爪进行分拣
- 视觉检测: 加装相机进行定位检测
开源资料
参考资源
| 资源类型 | 链接 | 说明 |
|---|---|---|
| 机械臂开源项目 | https://github.com/topics/robot-arm | GitHub 机械臂项目 |
| 机械图纸 | https://grabcad.com/library | 3D 模型下载平台 |
| 电路参考 | https://oshwhub.com/search?q=机械臂 | 立创 EDA 开源 |
| 固件源码 | https://github.com/hexepods/Hexepod | 开源机器人固件 |
| 上位机软件 | https://github.com/RoboticsBlog/robotic-arm-control | Python 控制软件 |
推荐学习
总结
本项目完整实现了 6 自由度机械臂的机械设计、运动学建模、轨迹规划和控制软件。通过开源方式分享,为机器人学习提供实践平台。
项目周期:2 个月 | 代码量:8000 行 | 版本:v2.0