Skip to content

Latest commit

 

History

119 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Engineer-Components

适用于RoboMaster工程机器人机械臂控制组件库。

本仓库提供一套面向嵌入式(STM32/RM 工程机器人)的机械臂控制框架,包含:

  • tools/:纯数学工具层(矩阵、向量、四元数、坐标变换、解析逆运动学)
  • module/:机械臂模块层(连杆 DH 模型、串联机械臂、轨迹规划器、通用机械臂 FSM 基类)
  • demo/:实车上稳定运行的使用示例

目录结构

Engineer-Components/
├── tools/
│   ├── Inc/
│   │   ├── matrix.hpp          # 矩阵模板类(纯数组实现)
│   │   ├── vector3d.hpp        # 三维向量类
│   │   ├── quaternion.hpp      # 四元数类
│   │   ├── transformation.hpp  # 姿态/齐次变换互转函数
│   │   ├── operation.hpp       # 叉积/点积/四元数乘法/HTM 乘法
│   │   ├── utils.hpp           # 常用数学工具函数
│   │   └── ikine_func.hpp      # ikine1~5 解析逆解声明
│   └── Src/
│       ├── transformation.cpp
│       └── ikine1.cpp ~ ikine5.cpp
├── module/
│   ├── Inc/
│   │   ├── arm_cmd.hpp               # 笛卡尔指令结构体
│   │   ├── link.hpp                 # DH 参数 / 连杆类
│   │   ├── serial_link.hpp          # 串联连杆 / 雅可比 / 牛顿欧拉
│   │   ├── joint_planner.hpp        # 单段五次多项式关节规划器
│   │   ├── multinode_joint_planner.hpp   # 多节点关节规划器
│   │   ├── linear_planner.hpp       # 标量线性插值规划器
│   │   ├── cartesian_planner.hpp    # 笛卡尔直线/圆弧规划器
│   │   ├── multinode_cartesian_planner.hpp # 多节点笛卡尔规划器
│   │   ├── planner.hpp              # 规划器统一入口(include 合集)
│   │   └── module_arm.hpp           # 通用机械臂 FSM 模板基类
│   └── Src/
│       ├── link.cpp
│       ├── serial_link.cpp
│       ├── linear_planner.cpp
│       └── cartesian_planner.cpp
└── demo/
    ├── ins_arm.hpp / ins_arm.cpp   # 6 轴机械臂连杆(DH)定义
    ├── arm_fsm.hpp / arm_fsm.cpp   # 6 轴机械臂 FSM(继承 ModuleArm<6>)
    ├── mode_config.hpp             # 各工作模式的关节/笛卡尔指令常量
    └── main_fsm.hpp / main_fsm.cpp # 上层整机 FSM(遥控/裁判系统调度)

一、tools —— 数学工具层

所有类位于 namespace engineerutils.hpp 中另有 engineer::IsFloatEqual 等)。

1. Matrix<rows_, cols_> —— 矩阵类(matrix.hpp)

纯内存数组实现的定长矩阵模板,行优先存储 float data_[rows_*cols_],无堆分配,适合嵌入式实时环境。

#include "matrix.hpp"
using namespace engineer;

Matrix<3, 1> v({1.0f, 2.0f, 3.0f});   // 数组构造
Matrix<3, 3> A = eye<3, 3>();         // 单位阵
Matrix<3, 3> Z = zeros<3, 3>();       // 零阵
Matrix<2, 2> O = ones<2, 2>();        // 全一阵
Matrix<4, 4> D = diag(Matrix<4, 1>({1,2,3,4})); // 对角阵

float x = m(0, 0);                    // 元素读写
A += B; A -= B; A *= 2.0f; A /= 3.0f; // 就地运算
Matrix<3, 3> C = A + B;               // 加/减/数乘/数除
Matrix<3, 2> P = A * M32;             // 矩阵乘法(M32 为 Matrix<3,2>)
Matrix<2, 3> T = A.trans();           // 转置
Matrix<1, 3> r = A.row(0);            // 取行/列
Matrix<2, 2> b = A.block<2, 2>(0, 0); // 取子块
A.replace<2, 2>(1, 1, b);             // 写子块
float tr = A.trace();                 //
float n  = A.norm();                  // Frobenius 范数
bool eq  = (A == B);                  // 相等比较(容差 1e-4)

2. Vector3 —— 三维向量(vector3d.hpp)

Vector3 v(1.0f, 2.0f, 3.0f);
Vector3 w = Vector3(mat3x1);          // 由 Matrix<3,1> 构造
v.x(); v.y(); v.z();                  // 分量访问
Vector3 a = v + w, b = v - w, c = v * 2.0f, d = v / 2.0f;
v += w; v -= w; v *= 2.0f; v /= 2.0f;
float n = v.norm();                  // 模长
Vector3 u = v.normalize();           // 归一化
float d = v.dot(w);                  // 点积
Vector3 c = v.cross(w);              // 叉积
bool eq = (v == w);                  // 容差比较
Matrix<3,1> m = v.matrix();          // 转矩阵

3. Quaternion —— 四元数(quaternion.hpp)

约定 [w,x,y,z],即 w=cos(θ/2)xyz = r·sin(θ/2)

Quaternion q1(1.0f, 0, 0, 0);
Quaternion q2(vec3, angle);          // 轴角构造(vec 自动归一化)
Quaternion q3(mat4x1);                // 由 Matrix<4,1> 构造
Quaternion q4(rot3x3);                // 由旋转矩阵构造(Shepperd 法,数值稳定)

q.w(); q.x(); q.y(); q.z();
Quaternion p = q1 * q2;               // 四元数乘法
Quaternion s = q1 * 0.5f;             // 数乘/加减/数除
float n = q.norm();
Quaternion nq = q.normalize();
Quaternion cq = q.conjugate();        // 共轭
Quaternion iq = q.inv();              //
Matrix<3,3> R = q.r();                // 转旋转矩阵
float ang = q.angvec(axis);           // 转轴角(输出单位轴+角度 rad)
Matrix<4,1> m = q.matrix();           // 转矩阵

4. 变换函数(transformation.hpp / .cpp)

以下函数全部以 Matrix 为数据结构,按"姿态表示"分组互转:

函数 说明
r2rpy / rpy2r 旋转矩阵 ↔ RPY 欧拉角 [yaw;pitch;roll]
r2angvec / angvec2r 旋转矩阵 ↔ 轴角 [rx;ry;rz;θ]
r2quat / quat2r 旋转矩阵 ↔ 四元数 [w;x;y;z]
quat2rpy / rpy2quat 四元数 ↔ RPY 欧拉角
quat2angvec / angvec2quat 四元数 ↔ 轴角
t2r / r2t 齐次变换矩阵 ↔ 旋转矩阵
t2p / p2t 齐次变换矩阵 ↔ 平移向量
rp2t 旋转矩阵 + 平移 → 齐次变换矩阵
invT 齐次变换矩阵求逆 T⁻¹=[Rᵀ,-Rᵀp;0,1]
t2rpy / rpy2t HTM ↔ RPY 欧拉角
t2angvec / angvec2t HTM ↔ 轴角
t2quat / quat2t HTM ↔ 四元数
t2twist / twist2t HTM ↔ 扭转坐标 [p;φ](φ=rθ)
dh2trans(theta, d, a, alpha) 标准 DH 参数 → 齐次变换矩阵
Matrix<3,1> rpy = r2rpy(R);              // yaw/pitch/roll
Matrix<4,1> q = r2quat(R);               // 注意是 [w;x;y;z]
Matrix<3,3> R2 = quat2r(q);
Matrix<4,4> T = rp2t(R, p);
Matrix<4,4> Tinv = invT(T);
Matrix<3,1> px = t2p(Tinv * T);          // 应为 0

3.3 运算函数(operation.hpp

Matrix<3,1> cv = cross(a, b);       // 3 维向量叉积(Matrix / Vector3 重载)
float d = dot(a, b);                // 点积
float qd = QuatDot(qa, qb);         // 四元数点积(Matrix4x1 / Quaternion 重载)
Matrix<4,1> qm = QuatCross(qa, qb); // 四元数乘法(Hamilton)
Matrix<4,4> T = HTMMultiply(A, B);  // 齐次变换矩阵乘法(R1R2 + p 组合,避免直接 4x4 乘)

4. utils.hpp —— 数学工具

IsFloatEqual(a, b);                 // 浮点近似相等
IsFloatArrayEqual(arrA, arrB, len); // 数组近似相等
float r = limit(v, min, max);       // 限幅
float n = NormalizePeriodData(lb, ub, data); // 周期数据归一化到 [lb, ub)
float a = NormAngle(angle);         // 归一化到 [-PI, PI)
Matrix<3,3> S = hat(v);             // 反对称矩阵
EngineerAtan2(y, x);                //
EngineerAcos(x); EngineerAsin(x);
EngineerCos(x);  EngineerSin(x);    // 均封装 arm_math 库
bool ok = SinCosBoundProcess(v);    // sin/cos 值越界处理(夹到 ±1)

5. 逆运动学 ikine1 ~ ikine5(ikine_func.hpp + Src)

内置 5 种构型的解析逆解,全部以 ModuleCartesianCmd(mode 定义于 arm_cmd.hpp)为输入:

// ikine1 —— 6 轴,DH (modified):均为 0,pi/2,pi/2,a=337,269.74,200… / d=339.01
uint8_t ikine1(ModuleCartesianCmd cmd, float l[4], Matrix<8,6> &sol);

// ikine2 —— 7 根连杆 6 自由度实际 6 轴,l[4]
uint8_t ikine2(ModuleCartesianCmd cmd, float l[4], Matrix<8,6> &sol);

// ikine3 —— 7 轴 L(3) 冗余,多余关节 theta6、theta7 作为输入参数
uint8_t ikine3(ModuleCartesianCmd cmd, float l[4], float theta6, float theta7, Matrix<4,7> &sol);

// ikine4 —— 7 轴(1 个冗余:theta7),l[6]
uint8_t ikine4(ModuleCartesianCmd cmd, float l[6], float theta7, Matrix<8,7> &sol);

// ikine5 —— 6 轴,l[3] = {L3.a, L4.d, L6.d}(demo 使用),l[3] = {0.335, 0.280, 0.074}
uint8_t ikine5(ModuleCartesianCmd cmd, float l[3], Matrix<8,6> &sol);

返回值为有效解组数(每行为一组关节角,弧度);无效行对应元素置 0。调用后需自行对解做角度归一化(NormPeriodData(-PI,PI))并选解(相对当前关节最小 L2 距离且满足限位),module_arm.hppModuleArm::selectSolution() 已内置此逻辑,可直接复用。

各构型具体 DH 参数见 ikine_func.hpp 顶部注释(单位:mm),使用前换算为 m。


二、module —— 机械臂控制层

6. 指令结构体(arm_cmd.hpp)

struct ModuleCartesianCmd { Matrix<3,1> pos; Matrix<4,1> quat; };  // 矩阵表达(规划器/逆解用)
struct CartesianCmd      { Vector3 pos; Quaternion quat; };        // 类表达(上层 FSM 用)
// CartesianCmd::module() 可转为 ModuleCartesianCmd

7. DH / LinkParam / Link —— 连杆(link.hpp + link.cpp)

struct LinkParam {  // 连杆描述参数
  float offset;     // 关节零点偏置(rad)
  float d;          // DH 参数 d(m)
  float a;          // DH 参数 a(m)
  float alpha;      // DH 参数 alpha(rad)
  float qmin_, qmax_;   // 关节限位(rad),默认 ±PI
  float m;              // 质量(kg)
  Matrix<3,1> centroid; // 质心(本体系,m)
  Matrix<3,3> inertia;  // 惯量(kg·m²)
};

Link link(link_param);          // 构造
Matrix<4,4> T = link.forward(q);    // 正解(DH 阵 → 基坐标系下的变换)
Matrix<4,4> Tb = link.backward(q);  // 逆(转置)
link.setJointLimit(qmin, qmax);     // 修改限位
link.setDHParams(offset,d,a,alpha); // 修改 DH 参数
link.setDynamicParam(m, centroid, inertia);
Link::getDH() / getDynamicParam() 可读回参数。

8. SerialLink —— 串联连杆模型(serial_link.hpp + serial_link.cpp)

SerialLink sl;
sl.append(Link(link_param1));   // 按基座→末端依次添加连杆
sl.append(Link(link_param2));

float theta[6] = {...};
sl.fkine(theta);                        // 正解(内部连乘各 link 的 forward)
sl.getT();                              // 末端齐次变换
sl.getR();                              // 旋转矩阵
sl.getP();                              // 平移向量
sl.getQuat();                           // 末端姿态(四元数)
sl.getPos();                            // 末端位置(Vector3)

// 按索引设置/读取各连杆
sl.setLinkDHParams(idx, offset, d, a, alpha);
sl.setLinkJointLimit(idx, qmin, qmax);
sl.getLinkJointLimit(idx, qmin, qmax);  // ModuleArm 用它读限位
sl.setLinkDynamicParam(idx, m, centroid, inertia);
sl.getLinkDynamicParam(idx, m, centroid, inertia);

// 雅可比(模板参数为关节数)
Matrix<6, N> J = sl.Jocobian<N>(theta);

// 动力学(重力补偿用,内部自动计算)
sl.gravityCompensation(theta, torq_out);   // 输出:各关节重力补偿力矩

全局函数:

  • GetJocobian<N>(T):由各连杆 Matrix<4,4> 数组计算 6×N 雅可比
  • NewtonEuler(num, R, P, param, dtheta, ddtheta, torq, end_force, end_torque):完整牛顿-欧拉迭代(含末端外负载),输出各关节力矩
  • StaticNewtonEuler(num, R, P, param, torq):静态(重力)迭代,SerialLink::gravityCompensation 内部调用

9. JointPlanner 与其它规划器 —— 路径规划核心

JointPlanner<N>(joint_planner.hpp)—— 单段五次多项式关节规划

// ctrl_period:控制周期 (s);initial_vel_opt:是否带初始速度;periodsub:逐关节的周期性边界标志(1 表示该关节按周期角处理跨 ±π 差值)
JointPlanner<6> jp(0.001f, false, zeros<6,1>());
Matrix<6,1> start = ..., end = ...;
jp.setTraj(start, end, 2.0f);       // 规划起点→终点,用时 2s
Matrix<6,1> pos;
jp.plan(pos);                        // 每个控制周期调用,输出当前位置(rad)
bool done = jp.isPlanFinished();

MultinodeJointPlanner<N>(multinode_joint_planner.hpp)—— 多节点串行关节规划

JointPlanner 之上增加节点队列,逐段执行。module_arm 默认使用此类

MultinodeJointPlanner<6> mjp(0.001f);
mjp.setStartPos(now);                 // 必须先设置起点
mjp.appendNode(node1, t1);            // 依次添加节点与对应规划时间
mjp.appendNode(node2, t2);
mjp.plan(pos);                        // 每周期调用;完成后 pos=终点
mjp.isPlanFinished();

LinearPlanner(linear_planner.hpp)—— 标量线性插值

对单个 float 值做匀速插值(速度 = 位移/时间):

LinearPlanner lp(0.001f);
lp.setLinearTraj(0.0f, 1.0f, 2.0f);
float cmd; lp.plan(cmd);              // cmd 从 0 → 1,历时 2s

CartesianPlanner(cartesian_planner.hpp + .cpp)—— 笛卡尔空间规划

支持直线(位置 5 次多项式 + 姿态球面线性插值 slerp)与圆弧(三点定圆)。

CartesianPlanner cp(0.001f);
ModuleCartesianCmd p1, p2;

cp.setLinearTraj(p1, p2, 2.0f);       // 直线
//
cp.setCircleTraj(p1, pmid, p3, 2.0f); // 圆弧(起点/中点/终点)
ModuleCartesianCmd out;
cp.plan(out);                         // 每周期调用,输出位置(pos)+姿态(quat)

MultinodeCartesianPlanner(multinode_cartesian_planner.hpp)

CartesianPlanner 之上串行执行多段直线节点(setStartPos + 依次 appendNode)。


10. ModuleArm<NJ> —— 通用机械臂 FSM 基类(module_arm.hpp)

核心类。模板参数 NJ = 关节数(编译期常量),封装了机械臂控制的完整 FSM 周期:

update() → genCmd(cmd) → run()          (每控制周期调用)
  • update():读取电机角度/速度(TD 微分可选)→ 正解 → 重力前馈
  • genCmd():根据规划类型(Joint/Linear/Circular/None)生成运动指令 → 逆解 → 关节角度参考 → PID 计算输出
  • run():工作状态 → 驱动电机(普通 / MIT 模式达妙电机);失能 → 力矩清零 + 锁位

使用方法(通常通过派生类):

#define NJ 6
class MyArm : public engineer::ModuleArm<NJ> {
  // 1. 纯虚函数:必须实现解析逆解
  uint8_t solveIkine(ModuleCartesianCmd cmd, const float *free_var,
                     Matrix<kMaxIkineSol, NJ> &sol) override {
    float l[3] = {0.335f, 0.28f, 0.074f};
    return ikine5(cmd, l, sol);          // 6 轴用 ikine5
  }
  // 2. (可选)MIT 模式(达妙电机):哪些关节用 MIT
  bool isMitModeMotor(uint8_t idx) override { return idx >= 3; }
  float mitKd(uint8_t idx) override { return 1.5f; }
  // 3. (可选)末端执行器偏移(fk 时叠加在末端坐标 x 上)
  Matrix<3,1> getEndEffectorOffset() override { ... }
  // 4. (可选)自定义 PID/限位/电机输出
  MultiNodesPid *getActivePid(uint8_t idx) override { ... }
  float getJointLimitMax/Min(idx) override {...}   // 默认自 serial_link 读
  // 5. (可选)重写 calcMotorInputRef/setMotorInput/runOnWorking 完全自定义
};

MyArm arm;
arm.registerMotor(0, &motor0);              // 普通电机
arm.registerDaMiao(4, &dm_motor4);          // 达妙电机(同时注册为 Motor)
arm.registerMotorPid(i, &pid_i);            // 各关节 PID
arm.registerMotorTd(i, &td_i);              // (可选)速度滤波 TD
arm.registerJointPlanner(&joint_planner);   // MultinodeJointPlanner<NJ>
arm.registerCartesianPlanner(&cartesian_planner); // CartesianPlanner
arm.registerSerialLink(&serial_link);       // SerialLink(连杆 + 限位来源)

// 预逆解:把将来要用到的笛卡尔指令提前解算(id 可不连续)
ArmCmd pre_cmd;
pre_cmd.id = 5;
pre_cmd.work_space = kModuleArmWorkSpaceCartesian;
pre_cmd.cartesian_cmd = ...;
pre_cmd.joint(0,0) = 1.57f;                 // NJ>6 时冗余关节固定值存再这里
arm.setNeedToPreIkine(pre_cmd);

arm.init();   // 缓存关节限位到 joint_max/min[],并执行预逆解队列

主循环使用:

Cmd cmd;  // ModuleArmFsmCmd<NJ>
cmd.work_state = kModuleArmWorkStateWorking;
cmd.plan_type = kArmPlanTypeJoint;          // 类型:
//   kModuleArmPlanTypeJoint   关节空间(多节点,节点可混合 join 指令或笛卡尔指令【预逆解】)
//   kModuleArmPlanTypeLinear  笛卡尔空间直线(2 节点:起点、终点)
//   kModuleArmPlanTypeCircular 笛卡尔空间圆弧(3 节点:起点、中点、终点)
//   kModuleArmPlanTypeNone    瞬间速度(把指令直接作为目标)
cmd.node_num = 1;
cmd.cmd[0].work_space = kModuleArmWorkSpaceJoint;
cmd.cmd[0].joint = ...;
cmd.traj_time[0] = 2.0f;                    // 各节点规划时间(s)
arm.update();                                // 1. 先更新传感数据/正解
arm.genCmd(cmd);                            // 2. 规划/逆解/PID计算
arm.run();                                  // 3. 驱动电机

查询接口:arm.pose() 返回 ModuleArmPose<NJ>(含 joint/cartesian_pose/matrix);arm.isAtPos(cmd) 判断是否到位(关节 <0.1 rad,笛卡尔 <30mm)。


分支与说明

master分支与cpp-dev分支的主要区别

  • master分支使用arm_math库进行加速,其他无明显区别
  • cpp-dev分支部分功能还未在实车上验证,比如通用轨迹规划器和数值逆解算

经测量,demo每周期运行时间最高2ms(计算重力补偿+逆解算),实际控制周期建议设置为3ms

About

No description, website, or topics provided.

Resources

Stars

8 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages