《Python机器人基础原理》第七十三篇:工厂仿真——关节角步长驱动下的雅可比矩阵批量计算与奇异性分析

 

1. 实际工业应用场景

 

周三上午九点,某汽车零部件焊接工位的离线编程工作站。机械设计工程师小周刚完成了一条新产线的机器人可达性分析,现在正和仿真工程师小郑对着屏幕上的3D布局图。

 

"小郑,这条产线上有12个焊接点,分布在工件两侧。我需要确认机器人在每个焊接姿态下,末端执行器能不能沿焊缝方向稳定施焊。"小周指着屏幕,"我按5°步长扫了关节1和关节2的全范围——各69步,组成约4761个姿态组合。你能帮我批量算一下每一步的雅可比矩阵吗?我想看看哪些位置接近奇异构型,哪些方向的末端力传递效率会断崖式下降。"

 

小郑调出Python编辑器:"4761步,每步3×2的雅可比矩阵,手工算到明年。我写个批量计算工具——给定关节角变化步长,自动生成关节角网格序列,对每一步调用解析雅可比,把结果存成CSV。后面你还能拿去做可操作性椭球分析。"

 

"对,就用我们那台平面2R SCARA的参数——大臂0.45 m/2.8 kg,小臂0.35 m/1.5 kg。关节角范围±170°,步长5°(约0.087 rad)。你帮我跑一遍网格,把奇异点标出来。"

 

"行。不过说好了——这是离线仿真,只验证算法逻辑。真机上的雅可比得用实时关节反馈,还得考虑连杆柔性、齿轮间隙,那些我这里都不管。"

 

构型约定说明:本篇采用平面2R SCARA构型(大臂长0.45 m/质量2.8 kg,小臂长0.35 m/质量1.5 kg),给定关节角变化步长生成网格序列,对每一步解析计算3×2速度雅可比矩阵(前两行线速度映射,第3行角速度映射),批量保存结果。全部参数内置,自包含可复现。

⚠️ 声明:本篇全部代码为课程离线仿真,运行在普通PC上,用于雅可比矩阵批量计算与奇异性分析的算法演示。代码中不包含任何真实机器人控制器实时运算或力控制接口。不可直接用于产线设备。

 

2. 痛点分析

 

课程模块 本篇对应的工程痛点

第3章 连杆参数及运动学求解 → 3.4 速度雅可比 给定关节角序列,需逐点计算雅可比矩阵以评估速度映射关系和奇异性

第5章 机器人关节驱动与控制 → 5.3 关节空间与笛卡尔空间映射 关节速度→末端速度的线性映射矩阵是力控制、阻抗控制的前置条件

第9章 协作机器人 → 9.2 可操作度与奇异规避 雅可比行列式为零的构型导致末端失去某个方向的运动能力

 

本篇代码解决 L2 离线仿真校核层。输入:关节角变化步长 + 机器人连杆参数 + 关节范围。处理:①按步长生成关节角网格序列;②对每一步解析计算3×2速度雅可比矩阵;③计算行列式和可操作度指标;④批量保存矩阵结果。输出:4张分析图表 + CSV矩阵文件。

 

不覆盖:实时力控制、奇异规避轨迹重规划、连杆柔性变形、关节摩擦与背隙补偿、6自由度完整构型的动力学耦合。

 

3. 核心逻辑讲解

 

物理直觉

 

雅可比矩阵就是机器人的"变速器齿轮比表"。你拧关节电机,末端执行器朝哪个方向走、走多快,全靠这张表换算。不同关节角度下,这张表的数值完全不同——大臂水平伸直时和小臂蜷缩在基座旁时,同样的关节转速产生的末端线速度天差地别。

 

更关键的是:某些特殊姿态下,这张表会"失效"——数学上表现为行列式接近零。这时候机器人失去某个方向的运动能力,叫做奇异位形。比如平面2R机器人两个关节都转到0°(完全伸直指向正前方,即q2=0°),此时两个连杆共线,末端只能沿垂直于连杆的方向移动,沿连杆方向的速度完全无法由关节角速度产生。工厂里如果焊接路径恰好经过这种姿态,就会出现"机器人到了位置但焊枪推不动"的尴尬局面。

 

本篇做的事情:你告诉我每个关节每次走多大步(比如5°),我从起始角到终止角,一步一步走,每到一个新姿态就算一遍雅可比矩阵,存下来。最后你拿到一张表,上面标着哪些步接近奇异——这就是离线编程工作站最基础的功能之一。

 

业务逻辑 → 代码映射

 

输入:关节角范围[q_min, q_max] + 步长Δq + 连杆参数

  ↓

【步骤1:生成关节角网格序列】

  对2R的两个关节,各按步长生成69个值

  两两组合得到69×69=4761个(q1, q2)姿态

  ↓

【步骤2:解析计算速度雅可比矩阵】

  平面2R的雅可比(3×2):

    线速度行1:J11 = -l1·sin(q1) - l2·sin(q1+q2) | J12 = -l2·sin(q1+q2)

    线速度行2:J21 = l1·cos(q1) + l2·cos(q1+q2) | J22 = l2·cos(q1+q2)

    角速度行3:J31 = 1 | J32 = 1 (ωz = q̇1 + q̇2)

  ↓

【步骤3:奇异性指标计算】

  行列式 det(J_2×2) = l1·l2·sin(q2)

  可操作度 w = |det(J)| (度量整体运动能力大小,即椭球体积)

  条件数 κ = σ_max/σ_min(度量各方向均匀性,不在本篇主算但需区分概念)

  ↓

【步骤4:批量保存】

  每一步的雅可比矩阵写入CSV,含行列索引

  ↓

输出:CSV文件 + 4张分析图表

 

关键概念文字翻译

 

雅可比矩阵每一项的物理含义:J(i,j)表示第j个关节的单位角速度能贡献给第i个末端自由度的速度分量。平面2R有2个关节、末端有2个平动自由度+1个转动自由度,所以是3×2。J11是关节1角速度对末端X方向线速度的贡献——等于末端位置对q1的偏导。几何上看,就是大臂和小臂在X方向的"速度杠杆臂"长度之和。第3行角速度映射 [1, 1] 表示末端绕Z轴的角速度等于两个关节角速度之和——这是平面机构的基本运动学关系。

 

行列式为零 = 奇异:det(J_2×2) = l1·l2·sin(q2)。当q2=0°或180°时(两杆共线),行列式为零。物理意义:此时末端沿连杆方向的移动无法由任何关节角速度产生——因为关节转动只改变方向,不改变沿杆方向的位置。工厂里遇到这种情况,机器人会突然"卡死"在某个方向上。

 

可操作度 vs 条件数——不要混为一谈:可操作度 w = |det(J)| 度量的是机器人整体运动能力的大小(对应可操作性椭球的体积),数值大说明"能动的总量大"。但各方向运动是否均匀,要看条件数 κ = σ_max/σ_min(奇异值之比)——κ接近1说明各方向均匀,κ很大说明某些方向几乎动不了。大 w 不等于均匀,这是两个独立指标。

 

力映射用的是转置:速度关系 v = J·q̇,力关系 τ = Jᵀ·F。速度雅可比和力雅可比是转置关系,不是同一个矩阵的不同行。本篇只计算速度雅可比。

 

4. OOP代码实现

 

技术栈

 

严格限定:numpy, pandas, matplotlib, math(标准库)。不引入pybullet、roboticstoolbox等任何第三方重型库。雅可比矩阵使用解析偏导手写实现。

 

项目结构树

 

jacobian_batch_simulation/

├── run_jacobian_batch.py

├── requirements.txt

├── robot/

│ ├── __init__.py

│ ├── models.py

│ ├── jacobian_calculator.py

│ ├── sequence_generator.py

│ ├── singularity_analyzer.py

│ └── visualize_jacobian.py

├── tests/

│ ├── __init__.py

│ └── test_jacobian_batch.py

├── results/

└── output/

 

核心源码

 

"robot/models.py" — 数据模型

 

"""

数据模型:连杆参数、关节角序列配置、雅可比计算结果

全部参数内置,自包含可复现

"""

 

import numpy as np

from dataclasses import dataclass, field

from typing import List, Optional

 

 

@dataclass

class LinkParams:

    """单连杆参数"""

    name: str = ""

    a: float = 0.0 # 连杆长度 (m)

 

 

@dataclass

class RobotArmConfig:

    """机械臂构型配置"""

    links: List[LinkParams] = field(default_factory=list)

    q_min: float = -2.967 # 关节最小角度 (rad),约-170°

    q_max: float = 2.967 # 关节最大角度 (rad),约+170°

 

    def __post_init__(self):

        if not self.links:

            # SCARA平面2R:大臂0.45m/2.8kg,小臂0.35m/1.5kg

            self.links = [

                LinkParams(name="大臂", a=0.45),

                LinkParams(name="小臂", a=0.35),

            ]

 

 

@dataclass

class JointStepConfig:

    """关节角步长配置(网格扫描模式)"""

    q1_start: float = -2.967 # 关节1起始角 (rad)

    q1_end: float = 2.967 # 关节1终止角 (rad)

    q1_step: float = 0.087 # 关节1步长 (rad),约5°

    q2_start: float = -2.967 # 关节2起始角 (rad)

    q2_end: float = 2.967 # 关节2终止角 (rad)

    q2_step: float = 0.087 # 关节2步长 (rad)

 

 

@dataclass

class JacobianResult:

    """单步雅可比计算结果"""

    point_index: int = 0

    q1: float = 0.0 # 关节1角度 (rad)

    q2: float = 0.0 # 关节2角度 (rad)

    jacobian: Optional[np.ndarray] = None # 3×2 速度雅可比矩阵

    determinant: float = 0.0 # 2×2线速度部分行列式

    manipulability: float = 0.0 # 可操作度 |det(J)| (m²)

    is_singular: bool = False # 是否接近奇异(|det| < 阈值)

 

"robot/sequence_generator.py" — 关节角序列生成

 

"""

按给定步长生成关节角网格序列

双轴扫描:q1和q2都按步长从起始到终止

"""

 

 

import numpy as np

from robot.models import JointStepConfig, RobotArmConfig

 

 

class JointSequenceGenerator:

    """

    按步长生成关节角网格序列

    双轴扫描:q1和q2各按步长生成离散值,两两组合

    """

 

    def __init__(self, config: JointStepConfig, arm_config: RobotArmConfig):

        self.cfg = config

        self.arm_cfg = arm_config

 

    def generate_grid_scan(self):

        """

        双轴网格扫描:q1和q2都按步长扫描

        arange终点加半步长,避免浮点舍入丢最后一点

        返回:[(q1, q2), ...] 列表

        """

        q1_values = np.arange(

            self.cfg.q1_start,

            self.cfg.q1_end + self.cfg.q1_step * 0.5,

            self.cfg.q1_step

        )

        q2_values = np.arange(

            self.cfg.q2_start,

            self.cfg.q2_end + self.cfg.q2_step * 0.5,

            self.cfg.q2_step

        )

        sequence = []

        for q2 in q2_values:

            for q1 in q1_values:

                sequence.append((float(q1), float(q2)))

        return sequence

 

"robot/jacobian_calculator.py" — 雅可比矩阵解析计算

 

"""

平面2R机械臂速度雅可比矩阵解析计算

J是3×2:前两行线速度映射(Vx, Vy),第3行角速度映射(ωz)

速度关系:v = J·q̇,其中 v = [Vx, Vy, ωz]ᵀ,q̇ = [q̇1, q̇2]ᵀ

"""

 

 

import numpy as np

import math

from robot.models import RobotArmConfig

 

 

class Planar2RJacobian:

    """

    平面2R速度雅可比矩阵计算器

    解析偏导:J_ij = ∂(末端第i自由度)/∂(第j关节角)

    """

 

    def __init__(self, config: RobotArmConfig):

        self.cfg = config

        self.l1 = config.links[0].a # 大臂长度 (m)

        self.l2 = config.links[1].a # 小臂长度 (m)

 

    def compute(self, q1, q2):

        """

        计算给定关节角下的3×2速度雅可比矩阵

 

        末端位置:

          x = l1·cos(q1) + l2·cos(q1+q2)

          y = l1·sin(q1) + l2·sin(q1+q2)

        末端角速度:

          ωz = q̇1 + q̇2

 

        线速度雅可比(2×2):

          J11 = ∂x/∂q1 = -l1·sin(q1) - l2·sin(q1+q2)

          J12 = ∂x/∂q2 = -l2·sin(q1+q2)

          J21 = ∂y/∂q1 = l1·cos(q1) + l2·cos(q1+q2)

          J22 = ∂y/∂q2 = l2·cos(q1+q2)

 

        角速度雅可比(1×2):

          J31 = ∂ωz/∂q̇1 = 1

          J32 = ∂ωz/∂q̇2 = 1

 

        组装为3×2矩阵:

          [ J11 J12 ]

          [ J21 J22 ]

          [ 1 1 ]

        """

        s1 = math.sin(q1)

        c1 = math.cos(q1)

        s12 = math.sin(q1 + q2)

        c12 = math.cos(q1 + q2)

 

        J = np.array([

            [-self.l1 * s1 - self.l2 * s12, -self.l2 * s12], # Vx

            [ self.l1 * c1 + self.l2 * c12, self.l2 * c12], # Vy

            [ 1.0, 1.0] # ωz

        ], dtype=np.float64)

 

        return J

 

    def determinant(self, q1, q2):

        """

        计算线速度部分(2×2)的行列式

        det = J11·J22 - J12·J21 = l1·l2·sin(q2)

        当 q2=0 或 π 时行列式为0,对应奇异构型(两杆共线)

        """

        return self.l1 * self.l2 * math.sin(q2)

 

    def manipulability(self, q1, q2):

        """

        Yoshikawa 可操作度指标(度量整体运动能力大小)

        w = |det(J_linear)| = l1·l2·|sin(q2)|

        注意:可操作度大 ≠ 各方向均匀,均匀性由条件数 κ = σmax/σmin 度量

        """

        return abs(self.l1 * self.l2 * math.sin(q2))

 

"robot/singularity_analyzer.py" — 奇异性分析

 

"""

奇异性分析:批量计算雅可比行列式和可操作度

标记接近奇异的构型

阈值换算:|det| < 0.01 m² → |sin(q2)| < 0.01/(0.45×0.35) ≈ 0.0635

→ |q2| < 3.64°,即关节2接近0°时标记奇异

"""

 

 

import numpy as np

import csv

import os

from robot.models import RobotArmConfig, JointStepConfig, JacobianResult

from robot.jacobian_calculator import Planar2RJacobian

 

 

class SingularityAnalyzer:

    """

    对关节角网格序列批量计算雅可比矩阵

    评估奇异性指标,标记奇异点

    """

 

    def __init__(self, config: RobotArmConfig, singular_threshold=0.01):

        self.cfg = config

        self.jac_calc = Planar2RJacobian(config)

        self.threshold = singular_threshold

 

    def analyze_sequence(self, joint_sequence):

        """

        对关节角序列批量计算

        返回:List[JacobianResult]

        """

        results = []

        for idx, (q1, q2) in enumerate(joint_sequence):

            result = JacobianResult(point_index=idx, q1=q1, q2=q2)

 

            # 计算3×2速度雅可比矩阵

            J = self.jac_calc.compute(q1, q2)

            result.jacobian = J

 

            # 计算2×2线速度部分的行列式

            det = self.jac_calc.determinant(q1, q2)

            result.determinant = det

 

            # 可操作度(度量整体运动能力大小,非均匀性)

            result.manipulability = self.jac_calc.manipulability(q1, q2)

 

            # 奇异标记:行列式绝对值低于阈值

            if abs(det) < self.threshold:

                result.is_singular = True

 

            results.append(result)

 

        return results

 

    def save_matrices_to_csv(self, results, output_path):

        """

        将每一步的雅可比矩阵保存为CSV

        格式:每行 = 一步,列 = 元数据(5列) + J矩阵元素(6列)

        """

        os.makedirs(os.path.dirname(output_path), exist_ok=True)

 

        with open(output_path, 'w', newline='') as f:

            writer = csv.writer(f)

            # 表头:5个元数据 + 3×2=6个矩阵元素

            header = ['point_index', 'q1_rad', 'q2_rad',

                      'determinant_m2', 'manipulability_m2', 'is_singular']

            for i in range(3):

                for j in range(2):

                    header.append(f'J_{i}_{j}')

            writer.writerow(header)

 

            # 数据行

            for r in results:

                row = [r.point_index, r.q1, r.q2,

                       r.determinant, r.manipulability, r.is_singular]

                if r.jacobian is not None:

                    row.extend(r.jacobian.flatten().tolist())

                writer.writerow(row)

 

"robot/visualize_jacobian.py" — 可视化

 

"""

雅可比批量计算可视化

4张图表:行列式分布、可操作度等值线、奇异点空间分布、雅可比热力图

"""

 

 

import numpy as np

import matplotlib.pyplot as plt

 

plt.rcParams['font.sans-serif'] = ['SimHei', 'DejaVu Sans']

plt.rcParams['axes.unicode_minus'] = False

 

 

class JacobianVisualizer:

    def __init__(self, results_dir="results"):

        self.results_dir = results_dir

        import os

        os.makedirs(results_dir, exist_ok=True)

 

    def plot_determinant_vs_q1_at_q2_slices(self, results, q2_slices_deg=None):

        """① 不同q2切片下,雅可比行列式随q1的变化曲线"""

        fig, ax = plt.subplots(figsize=(10, 5))

 

        if q2_slices_deg is None:

            q2_slices_deg = [-90, -45, 0, 45, 90]

 

        q2_set = set(abs(r.q2) for r in results)

        # 找到最接近指定角度的q2值

        for q2_target_deg in q2_slices_deg:

            q2_target = np.deg2rad(q2_target_deg)

            closest_q2 = min(q2_set, key=lambda x: abs(x - abs(q2_target)))

 

            # 筛选该q2附近的步

            filtered = [(r.q1, r.determinant) for r in results

                        if abs(abs(r.q2) - closest_q2) < 0.01]

            if not filtered:

                continue

            filtered.sort(key=lambda x: x[0])

 

            q1_deg = [np.degrees(q1) for q1, _ in filtered]

            det_vals = [d for _, d in filtered]

            label = f'|q2|≈{np.degrees(closest_q2):.0f}°'

            ax.plot(q1_deg, det_vals, linewidth=1.2, label=label)

 

        ax.axhline(y=0, color='r', linestyle='-', linewidth=1)

        ax.axhline(y=0.01, color='orange', linestyle='--', linewidth=1,

                   label='奇异阈值 +0.01 m²')

        ax.axhline(y=-0.01, color='orange', linestyle='--', linewidth=1)

 

        ax.set_xlabel('关节1角度 (°)')

        ax.set_ylabel('det(J) (m²)')

        ax.set_title('雅可比行列式随q1变化(不同q2切片)')

        ax.grid(True, alpha=0.3)

        ax.legend(fontsize=8)

        plt.tight_layout()

        path = f"{self.results_dir}/01_determinant_vs_q1.png"

        plt.savefig(path, dpi=150)

        plt.close()

        return path

 

    def plot_manipulability_contour(self, results):

        """② 可操作度分布等值线图(网格扫描)"""

        fig, ax = plt.subplots(figsize=(8, 6))

 

        q1_list = list(set([r.q1 for r in results]))

        q2_list = list(set([r.q2 for r in results]))

        q1_list.sort()

        q2_list.sort()

 

        w_grid = np.zeros((len(q2_list), len(q1_list)))

        singular_mask = np.zeros((len(q2_list), len(q1_list)), dtype=bool)

 

        for r in results:

            i = q2_list.index(r.q2)

            j = q1_list.index(r.q1)

            w_grid[i, j] = r.manipulability

            singular_mask[i, j] = r.is_singular

 

        c = ax.contourf(np.degrees(q1_list), np.degrees(q2_list),

                        w_grid, levels=20, cmap='viridis')

        plt.colorbar(c, ax=ax, label='可操作度 |det(J)| (m²)')

 

        # 标记奇异点

        singular_q1 = [r.q1 * 180 / np.pi for r in results if r.is_singular]

        singular_q2 = [r.q2 * 180 / np.pi for r in results if r.is_singular]

        if singular_q1:

            ax.plot(singular_q1, singular_q2, 'rx', markersize=3,

                    label='奇异点 (|det|<0.01)')

 

        ax.set_xlabel('关节1角度 (°)')

        ax.set_ylabel('关节2角度 (°)')

        ax.set_title('可操作度分布等值线图(红×标记奇异点)\n'

                     '可操作度度量整体运动能力大小,非各方向均匀性')

        ax.legend(fontsize=8)

        ax.grid(True, alpha=0.3)

        plt.tight_layout()

        path = f"{self.results_dir}/02_manipulability.png"

        plt.savefig(path, dpi=150)

        plt.close()

        return path

 

    def plot_singular_points(self, results):

        """③ 奇异点在工作空间中的物理分布"""

        fig, ax = plt.subplots(figsize=(8, 8))

 

        l1 = 0.45

        l2 = 0.35

 

        x_vals = []

        y_vals = []

        colors = []

 

        for r in results:

            if not r.is_singular:

                continue

            q1, q2 = r.q1, r.q2

            x = l1 * np.cos(q1) + l2 * np.cos(q1 + q2)

            y = l1 * np.sin(q1) + l2 * np.sin(q1 + q2)

            x_vals.append(x)

            y_vals.append(y)

            colors.append(abs(r.determinant))

 

        if x_vals:

            sc = ax.scatter(x_vals, y_vals, c=colors, cmap='Reds',

                          s=15, edgecolors='k', linewidths=0.3, alpha=0.7)

            plt.colorbar(sc, ax=ax, label='|det(J)| (m²)')

 

        # 工作空间边界

        theta = np.linspace(0, 2*np.pi, 300)

        ax.plot((l1+l2)*np.cos(theta), (l1+l2)*np.sin(theta),

                'k--', alpha=0.4, linewidth=1, label='最大臂展 0.80 m')

        ax.plot(abs(l1-l2)*np.cos(theta), abs(l1-l2)*np.sin(theta),

                'k--', alpha=0.4, linewidth=1, label='最小内圈 0.10 m')

 

        ax.set_aspect('equal', adjustable='box')

        ax.set_xlabel('X (m)')

        ax.set_ylabel('Y (m)')

        ax.set_title('奇异点在工作空间中的分布\n'

                     '(q2≈0°时奇异点集中在最大臂展圆上,呈圆弧分布)')

        ax.grid(True, alpha=0.3)

        ax.legend(fontsize=8)

        plt.tight_layout()

        path = f"{self.results_dir}/03_singular_points.png"

        plt.savefig(path, dpi=150)

        plt.close()

        return path

 

    def plot_jacobian_heatmap(self, results, step_index=0):

        """④ 选定步的雅可比矩阵热力图"""

        fig, ax = plt.subplots(figsize=(5, 4))

 

        if step_index >= len(results) or results[step_index].jacobian is None:

            step_index = 0

        J = results[step_index].jacobian

        q1_deg = results[step_index].q1 * 180 / np.pi

        q2_deg = results[step_index].q2 * 180 / np.pi

 

        im = ax.imshow(J, cmap='coolwarm', aspect='auto')

        plt.colorbar(im, ax=ax, label='J(i,j)')

 

        ax.set_xticks([0, 1])

        ax.set_xticklabels(['关节1 (q̇1)', '关节2 (q̇2)'])

        ax.set_yticks(range(3))

        ax.set_yticklabels(['Vx', 'Vy', 'ωz'])

 

        ax.set_title(f'速度雅可比热力图 (v = J·q̇)\n'

                     f'步{step_index}: q1={q1_deg:.1f}°, q2={q2_deg:.1f}°')

        plt.tight_layout()

        path = f"{self.results_dir}/04_jacobian_heatmap.png"

        plt.savefig(path, dpi=150)

        plt.close()

        return path

 

5. 可视化要求

 

序号 图表文件 服务结论

① 

"01_determinant_vs_q1.png" 不同q2切片下行列式随q1变化,直观看到q2接近0°时行列式全程接近零,确认奇异构型位置

② 

"02_manipulability.png" 可操作度等值线分布,q2=±90°附近最大(0.1575 m²),红×标记奇异点

③ 

"03_singular_points.png" 奇异点在工作空间中的物理分布——集中在最大臂展圆上呈圆弧,对应q2≈0°共线姿态

④ 

"04_jacobian_heatmap.png" 选定步的3×2速度雅可比热力图,展示线速度+角速度映射的"齿轮比"分布

 

6. README使用说明 + 核心知识点卡片

 

快速开始

 

pip install -r requirements.txt

python -m pytest tests/test_jacobian_batch.py -v # 3/3 测试通过

python run_jacobian_batch.py # 运行雅可比批量计算

 

数据格式说明

 

# 修改关节步长配置

from robot.models import JointStepConfig

step_cfg = JointStepConfig(

    q1_start=-2.967, q1_end=2.967, q1_step=0.087, # 步长5°

    q2_start=-2.967, q2_end=2.967, q2_step=0.087

)

 

# 修改机器人构型

from robot.models import RobotArmConfig, LinkParams

cfg = RobotArmConfig(

    links=[LinkParams(name="大臂", a=0.45), LinkParams(name="小臂", a=0.35)]

)

 

# 修改奇异阈值(0.01 m² → |sin(q2)| < 0.0635 → |q2| < 3.64°)

from robot.singularity_analyzer import SingularityAnalyzer

analyzer = SingularityAnalyzer(cfg, singular_threshold=0.005)

 

参数 修改位置 说明

关节步长 

"JointStepConfig.q1_step" 0.087 rad ≈ 5°,越小精度越高

奇异阈值 

"SingularityAnalyzer(..., singular_threshold=)" 0.01 m² 对应 q2 在 ±3.64° 内标记奇异

臂长 

"LinkParams(a=)" 影响 det(J) = l1·l2·sin(q2) 的幅值

 

📌 知识点卡片

 

雅可比矩阵与奇异性分析(对应课本第3章3.4节、第9章9.2节)

 

- 速度雅可比:平面2R的J是3×2(Vx, Vy, ωz),元素为末端速度对关节角的偏导,物理上是"速度杠杆臂"

- 奇异位形:det(J_2×2) = l1·l2·sin(q2) = 0 当 q2=0°或180°(两杆共线),末端失去沿连杆方向的运动能力

- 可操作度 w = |det(J)|:度量整体运动能力大小(椭球体积),大 w ≠ 各方向均匀;均匀性由条件数 κ = σ_max/σ_min 独立度量

- 力映射:τ = Jᵀ·F,与速度雅可比 J 是转置关系,不是同一个矩阵的不同行

 

7. 总结与工程师思考

 

工业落地难处

 

一、连杆参数误差导致雅可比矩阵失真。 本篇用理想参数 l1=0.45 m、l2=0.35 m 计算雅可比。实际机器人的连杆长度存在加工公差(±0.5 mm),关节零点偏移(±0.1°),这些误差会让雅可比矩阵的元素产生偏差。在精密装配(如±0.02 mm重复精度要求)场景下,这种偏差会直接转化为末端位姿误差。工业上需要通过标定实验修正DH参数。

 

二、行列式判据不如条件数鲁棒。 本篇用 |det(J)| < 0.01 m² 标记奇异,这只能检测"完全共线"的极端情况。实际机器人接近奇异但行列式不为零时,末端速度方向已经严重畸变——此时应该用条件数 κ(J) = σ_max/σ_min 来量化各方向运动能力的差异。κ 越大说明越接近奇异,工业控制器中常用 κ > 50 作为奇异预警阈值。

 

三、雅可比矩阵只描述瞬时速度映射,不反映动力学耦合。 本篇算出的 J 是纯几何关系,但真实机器人各关节之间存在惯性耦合、科氏力和向心力。高速运动时,即使雅可比条件数良好,动力学奇异性(如惯性矩阵奇异)仍可能导致控制不稳定。离线仿真中无法体现这一点。

 

工程师心得

 

小郑把CSV文件和四张图表发到小周桌上:"4761步的雅可比全算完了。你看图②——可操作度在q2=±90°附近最大,约0.1575 m²,说明垂直姿态下机器人整体运动能力最强。图①中q2接近0°的切片,行列式全程在零附近,整个扫描范围都是奇异构型。图③标出了工作空间里所有奇异点的位置——集中在最大臂展圆上,呈圆弧分布,对应两杆共线的姿态。"

 

小周放大图②:"我把焊接点重新排一下,尽量让焊枪在q2=±90°附近工作,避开那条奇异圆弧。"

 

"对。不过记住——"小郑敲了敲屏幕,"这是离线仿真。真机上的雅可比得用实时反馈的关节角算,而且要考虑连杆柔性。仿真给你方向,真机给你答案。"

 

8. 写作与风格限制

 

本篇全部代码为课程离线仿真,运行在普通PC上,用于雅可比矩阵批量计算与奇异性分析的算法演示。代码中不包含任何真实机器人控制器实时运算或力控制接口。 完全去营销,中立客观。禁止"首先、其次、最后"等八股连接词。拒绝论文式生硬开头,保持故事叙事感。全部物理量标注完整单位。

 

9. 文末附录

 

环境配置

 

pip install numpy>=1.24.0 pandas>=2.0.0 matplotlib>=3.7.0

 

"requirements.txt" 内容:

 

numpy>=1.24.0

pandas>=2.0.0

matplotlib>=3.7.0

 

运行说明

 

cd jacobian_batch_simulation

pip install -r requirements.txt

python -m pytest tests/ -v

python run_jacobian_batch.py

 

参数修改指南

 

想改什么 改哪里 示例

关节步长 

"JointStepConfig.q1_step" 改为0.035 rad(2°)

扫描

利用AI解决实际问题,如果你觉得这个工具好用,欢迎关注长安牧笛!

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐