1. 四足机器人单腿逆运动学:从“我想去哪”到“腿该怎么动”

如果你玩过遥控四足机器人,或者看过波士顿动力的机器狗视频,可能会好奇:我们只告诉它“向前走”,它怎么知道每条腿的每个关节该转多少度呢?这背后最关键的一步,就是逆运动学

简单来说,正运动学是已知每个关节的角度,去计算机器人脚掌(足端)最终落在哪里。这就像你知道自己胳膊肘和手腕弯了多少度,能大概比划出手掌的位置。而逆运动学则恰恰相反:我们先指定脚掌要到达的目标位置(比如向前迈步时脚掌的落点),然后反推回去计算每个关节需要转动的角度。对于四足机器人,这通常是针对单条腿进行的计算。

为什么这很重要?因为在实际控制中,我们规划的是机器人的步态——也就是每条腿在什么时间、踩在什么位置。规划好的足端轨迹是一个个三维空间坐标点(x, y, z)。控制器拿到这些坐标点后,必须立刻解算出对应三个舵机(或电机)应该转动的角度,才能让腿准确地摆到那个位置。这个过程必须是实时的、快速的,所以我们需要一个高效、可靠的求解方法。

对于像我们常见的四足机器人单腿(通常由髋关节侧摆、大腿、小腿三个旋转关节构成),逆运动学的求解方法主要有两大类:解析法(几何法/代数法)数值迭代法。数值法通用性强但计算量大,可能不保证实时性;而解析法,特别是我们今天要深入探讨的几何法,计算速度快、解精确,非常适合这种关节数不多、结构特定的情况。

我刚开始做四足机器人时,也尝试过直接调用现成的机器人工具箱来算逆解,但总觉得是个黑盒,心里不踏实。后来自己动手用几何关系推导了一遍,不仅彻底搞懂了原理,在调试和排查问题时也更有底气了。这篇文章,我就带你一起,像解一道立体几何题一样,把四足机器人单腿的逆运动学“手推”出来,并最终用MATLAB代码实现它。

2. 建立模型:把你的机器人腿“画”在纸上

在开始数学推导前,我们必须先把机器人的一条腿用数学语言描述清楚。我们以最常见的3自由度串联腿结构为例,它从上到下依次是:

  1. 髋关节侧摆关节:控制腿在身体侧面(冠状面)的摆动。
  2. 髋关节俯仰关节:控制大腿的前后摆动。
  3. 膝关节俯仰关节:控制小腿的屈伸。

为了简化,我们通常将这三个旋转轴假设为两两垂直。现在,我们为这条腿建立一个坐标系。

2.1 定义坐标系与关键参数

我们以机器人的髋关节侧摆轴的中心为坐标系原点 O。这个点也是整条腿运动的根节点。

  • Z轴:指向机器人身体的正上方(垂直向上)。
  • Y轴:指向机器人身体的侧向(对于右前腿,通常指向右侧)。
  • X轴:根据右手定则,指向机器人身体的前方。

现在,定义我们的三个连杆长度:

  • h:从髋关节侧摆轴中心到髋关节俯仰轴中心的垂直偏移。你可以把它想象成“髋部”的厚度。它是一个固定值,不随关节转动而改变。
  • hu:大腿的长度(上臂)。
  • hl:小腿的长度(下臂)。

最后,定义我们的三个关节角(按照原文的约定,顺时针旋转为正方向):

  • gamma (γ):髋关节侧摆角。在静止状态下,它决定了腿在YZ平面(后视图)内的投影方向。
  • alpha (α):髋关节俯仰角。
  • beta (β):膝关节俯仰角。

我们的目标就是:给定足端点 P 在腿坐标系下的坐标 (x, y, z),求解出对应的 (gamma, alpha, beta)

2.2 视角分解:化三维为二维

直接处理三维空间中的角度关系会比较复杂。一个非常有效的技巧是进行视角分解,将三维问题投影到两个互相垂直的二维平面上来解决,这也是几何法的核心思想。

我们来看两个关键视图:

  1. 后视图(YZ平面):从这个方向看,我们主要关心 gamma 角,以及腿在侧面方向上的投影长度。这个视图忽略了X方向的前后运动。
  2. 侧视图(XZ平面,或经过处理的XZ‘平面):从这个方向看,我们主要关心 alphabeta 角,它们决定了腿在前后方向上的伸展。但需要注意的是,由于 gamma 角的存在,真正的侧视图会发生倾斜。因此,我们需要一个“修正”后的侧视图平面,这个平面垂直于由 gamma 角决定的大腿投影线。

通过这种分解,一个三维空间中的连杆运动问题,就被巧妙地拆解成了两个二维平面内的几何问题,大大降低了求解难度。接下来,我们就分别在这两个视图里“作战”。

3. 几何法逐步推导:像解几何题一样求角度

现在,我们已知足端点 P(x, y, z) 和三个杆长 h, hu, hl。我们的任务是求出三个角度。

3.1 求解髋关节侧摆角 gamma

首先,我们聚焦于后视图(YZ平面)。在这个视图里,我们暂时忽略X坐标。点O和点P在YZ平面上的投影点构成了一个直角三角形。

我们定义几个中间变量来帮助思考:

  • dyz:原点O到足端点P的连线在YZ平面上的投影长度。根据勾股定理,dyz = sqrt(y^2 + z^2)。它就是点P到Z轴的垂直距离。
  • lyz:这是一个关键的中间长度。它是从髋关节俯仰轴中心(图中A点)到足端点P在YZ平面投影点(图中P’点)的水平距离。注意,A点是由于h这个垂直偏移而产生的,它并不在O点。通过另一个直角三角形(O、A、P’),我们可以得到 lyz = sqrt(dyz^2 - h^2)。这个lyz非常重要,它将是连接两个视图的桥梁。

现在,观察后视图中的角度关系:

  1. gamma_yz:这是向量OP’与Z轴负方向的夹角。根据三角函数,gamma_yz = -arctan(y / z)。负号来源于我们对正方向的约定(顺时针为正),具体需根据坐标系定义调整。
  2. gamma_h_offset:这是由于髋部偏移h造成的附加角度。在三角形OAP’中,tan(gamma_h_offset) = h / lyz,所以 gamma_h_offset = arctan(h / lyz)。方向同样需要考虑正负。

最终,髋关节侧摆角 gamma 就是这两个角度的代数和:gamma = gamma_yz - gamma_h_offset。这个公式的物理意义很直观:足端点的方向角,减去髋部结构偏移造成的固定偏角,就得到了关节实际需要转动的角度。

3.2 求解膝关节俯仰角 beta

求解 beta 需要用到我们刚才计算出的 lyz。现在,我们将注意力转移到修正后的侧视图平面。这个平面垂直于线段AP’(即lyz所在直线),因此在这个新平面里,大腿和小腿的运动被“展平”了。

在这个新平面里,我们定义一个新的水平距离 lxz’。它是从A点到足端点P的真实空间距离在“修正侧视平面”上的体现。由于这个平面包含了X方向的前后运动,所以 lxz’ = sqrt(lyz^2 + x^2)。你可以把它理解为腿在除去侧摆影响后,在前进方向上的“伸展量”。

现在,问题转化为了一个经典的平面二连杆逆解问题:已知基座(A点)、大腿长hu、小腿长hl,以及末端点(P点)到基座的距离 lxz’,求膝关节角度 beta

我们通过构造辅助线(如图中的m和n),可以列出关于huhllxz’以及n(膝关节到P点连线在基座方向上的投影)的方程组。联立求解后,可以得到: n = (lxz’^2 - hl^2 - hu^2) / (2 * hu)

这里 n 的物理意义是:从髋关节俯仰轴(B点)向小腿方向做垂线,垂足到膝关节(C点)的距离在AB方向上的投影。得到 n 之后,在三角形BCP中,根据余弦定理,膝关节角 beta 满足 cos(beta) = n / hl。因此: beta = -arccos(n / hl) 同样,负号与我们的角度正方向定义有关(通常膝关节伸直时角度为0或负值)。

3.3 求解髋关节俯仰角 alpha

在同一个修正侧视平面中,alpha 角由两部分组成:

  1. alpha_xz’:这是线段AP’(长度lyz)与X轴负方向的夹角。因为在这个视图里,前后方向是X,上下方向是Z‘(与lyz垂直)。所以 alpha_xz’ = -arctan(x / lyz)
  2. alpha_off:这是大腿连杆(AB)与连线AP’之间的夹角。在三角形ABP’中,已知边AB=hu,边AP’=lxz’,边BP’=hl(注意,这里BP’是虚拟连线,其长度等于小腿长hl,因为beta角已经确定)。根据余弦定理,cos(alpha_off) = (hu^2 + lxz’^2 - hl^2) / (2 * hu * lxz’)。但更直观地,从之前的几何图可以看出,cos(alpha_off) = (hu + n) / lxz’

因此,髋关节俯仰角 alpha 为: alpha = alpha_xz’ + alpha_off

至此,我们仅通过初等几何和三角函数,就完成了从足端坐标 (x, y, z) 到三个关节角 (gamma, alpha, beta) 的完整解析求解。整个过程清晰明了,没有涉及复杂的矩阵运算。

4. MATLAB代码实现:把公式变成可运行的脚本

理论推导完成后,用代码实现就是水到渠成的事情。MATLAB非常适合做这种数学计算和算法验证。下面我将给出一个完整、健壮且带有必要注释的MATLAB函数。

function [gamma, alpha, beta] = single_leg_ik(x, y, z, h, hu, hl)
% 单腿逆运动学求解函数 (几何法)
% 输入:
%   x, y, z: 足端目标点在腿坐标系下的坐标 (单位:米)
%   h:  髋关节偏移量 (大腿关节中心到侧摆关节中心的垂直距离)
%   hu: 大腿长度
%   hl: 小腿长度
% 输出:
%   gamma, alpha, beta: 计算得到的关节角度 (单位:弧度)
%                       顺序为:髋侧摆(gamma), 髋俯仰(alpha), 膝俯仰(beta)
% 注意:角度正方向约定为顺时针旋转为正。

    % 1. 计算投影长度 dyz 和 lyz
    dyz = sqrt(y^2 + z^2);
    lyz = sqrt(dyz^2 - h^2);
    
    % 安全校验:确保目标点在可达工作空间内
    if dyz < abs(h)
        error('目标点距离原点太近,lyz将为虚数,无解。请检查坐标。');
    end
    
    % 2. 求解髋关节侧摆角 gamma
    % 注意:atan2(y, z) 比 atan(y/z) 更稳定,能处理z为0的情况
    gamma_yz = -atan2(y, z); 
    gamma_h_offset = -atan2(h, lyz); 
    gamma = gamma_yz - gamma_h_offset;
    
    % 3. 求解膝关节俯仰角 beta
    % 计算修正侧视图平面内的距离 lxzp
    lxzp = sqrt(lyz^2 + x^2);
    
    % 工作空间校验:三角形不等式
    if lxzp > (hu + hl) || lxzp < abs(hu - hl)
        error('目标点距离 lxzp = %.3f 超出大腿(%0.3f)和小腿(%0.3f)的可达范围。', lxzp, hu, hl);
    end
    
    % 计算中间变量 n
    n = (lxzp^2 - hl^2 - hu^2) / (2 * hu);
    
    % 数值稳定性校验:确保acos的输入在[-1, 1]范围内
    n_over_hl = n / hl;
    if n_over_hl > 1
        n_over_hl = 1;
    elseif n_over_hl < -1
        n_over_hl = -1;
    end
    
    beta = -acos(n_over_hl); % 根据模型,膝关节伸直时beta通常为负值或0
    
    % 4. 求解髋关节俯仰角 alpha
    alpha_xzp = -atan2(x, lyz);
    
    % 计算 alpha_off,同样进行数值保护
    cos_alpha_off = (hu + n) / lxzp;
    if cos_alpha_off > 1
        cos_alpha_off = 1;
    elseif cos_alpha_off < -1
        cos_alpha_off = -1;
    end
    alpha_off = acos(cos_alpha_off);
    
    alpha = alpha_xzp + alpha_off;
    
    % 可选:将角度转换到合理的范围,例如 [-pi, pi]
    % gamma = wrapToPi(gamma);
    % alpha = wrapToPi(alpha);
    % beta = wrapToPi(beta);
end

这个函数不仅实现了核心算法,还增加了输入校验和数值保护,这是工程实践中非常重要的一步,可以避免因为输入了超出工作空间的目标点而导致程序报错或计算出无意义的结果(如 acos(1.001))。

你可以这样调用它:

% 定义机器人腿的参数 (单位:米)
h = 0.05;   % 髋部偏移
hu = 0.20;  % 大腿长度
hl = 0.25;  % 小腿长度

% 给定一个目标足端位置
x_desired = 0.15;
y_desired = 0.10;
z_desired = -0.30; % Z通常向下为负

% 计算逆运动学
[gamma_rad, alpha_rad, beta_rad] = single_leg_ik(x_desired, y_desired, z_desired, h, hu, hl);

% 转换为角度制便于观察
gamma_deg = rad2deg(gamma_rad);
alpha_deg = rad2deg(alpha_rad);
beta_deg = rad2deg(beta_rad);

fprintf('计算结果:\n');
fprintf('  髋侧摆 gamma = %.2f° (%.4f rad)\n', gamma_deg, gamma_rad);
fprintf('  髋俯仰 alpha = %.2f° (%.4f rad)\n', alpha_deg, alpha_rad);
fprintf('  膝俯仰 beta  = %.2f° (%.4f rad)\n', beta_deg, beta_rad);

5. 验证、可视化与多解处理

写完代码不算完,我们还需要验证它的正确性,并理解其局限性。

5.1 工作空间与解的存在性

不是任意一个 (x, y, z) 点机器人的腿都能够到。腿能够到达的所有点构成的区域称为工作空间。对于我们这种3自由度腿,其工作空间是一个三维的球壳状区域。我们的代码中已经加入了初步的校验:

  • dyz < abs(h) 时无解:这意味着目标点在YZ平面上的投影甚至落不到髋关节俯仰轴所在的水平面上。
  • lxzp > (hu + hl)lxzp < abs(hu - hl) 时无解:这对应着在修正侧视平面中,目标点距离超出了大腿和小腿长度之和(够不着)或小于两者之差(缩得太近,关节干涉)。

在实际应用中,步态规划器生成的足端轨迹必须始终位于工作空间内,否则逆解计算会失败。

5.2 正运动学验证

最直接的验证方法是:用我们求出的 (gamma, alpha, beta) 代入正运动学公式,计算得到的足端坐标应该与最初输入的 (x, y, z) 一致(在允许的数值误差内)。

我们可以编写一个简单的正运动学函数:

function [x_fk, y_fk, z_fk] = single_leg_fk(gamma, alpha, beta, h, hu, hl)
% 单腿正运动学函数
% 根据关节角计算足端位置
    % 从髋关节侧摆中心O开始计算
    % 1. 经过侧摆关节gamma
    y1 = h * sin(gamma);
    z1 = -h * cos(gamma); % 注意Z轴方向
    % A点坐标 (0, y1, z1)
    
    % 2. 在大腿平面内(由gamma角确定的方向)进行俯仰运动
    % 该平面内,前进方向为 (sin(gamma), cos(gamma)) 在XY平面的投影,简化处理
    % 更严谨的做法是使用旋转矩阵,这里用几何关系简化表达
    l_proj = hu * sin(alpha); % 在前进方向(X)的投影
    l_down = hu * cos(alpha); % 在向下方向(Z)的投影
    
    x2 = l_proj * cos(gamma); % 注意投影到世界坐标系X方向
    y2 = y1 + l_proj * sin(gamma);
    z2 = z1 - l_down;
    % B点(膝关节)坐标 (x2, y2, z2)
    
    % 3. 小腿运动 (beta角是相对于大腿的)
    leg_angle = alpha + beta; % 小腿相对于水平面的绝对角度
    l_proj_lower = hl * sin(leg_angle);
    l_down_lower = hl * cos(leg_angle);
    
    x_fk = x2 + l_proj_lower * cos(gamma);
    y_fk = y2 + l_proj_lower * sin(gamma);
    z_fk = z2 - l_down_lower;
end

然后进行验证:

% 使用之前逆解算出的角度
[x_calc, y_calc, z_calc] = single_leg_fk(gamma_rad, alpha_rad, beta_rad, h, hu, hl);

error = norm([x_desired, y_desired, z_desired] - [x_calc, y_calc, z_calc]);
fprintf('正运动学验证误差:%.6f 米\n', error);

如果误差在毫米级甚至更小,就证明我们的逆解算法是正确的。

5.3 多解问题与选解

对于平面二连杆(相当于我们修正侧视图中的大腿和小腿),逆运动学通常存在两个解:一个“肘部向上”的构型,一个“肘部向下”的构型。在我们的几何推导中,beta = -acos(n/hl) 只给出了一个解(对应膝关节弯曲方向,通常是我们想要的“膝部向前”构型)。实际上,acos 函数在 [0, pi] 范围内,我们取负号后得到的是负角度(膝部弯曲)。

另一个解是 beta = acos(n/hl),对应膝关节反方向弯曲。在大多数四足机器人设计中,膝关节通常设计为只能向一个方向弯曲(类似动物后腿),所以我们会舍弃另一个解。但在通用机械臂中,就需要根据避障、能耗等原则来选择其中一个解。

我们的几何法清晰地揭示了这一点。在求解 alpha_off 时,我们使用了 acos,它也只返回 [0, pi] 范围内的一个值。这对应了侧视平面内的一种三角形构成方式。另一种方式(三角形外翻)对应 alpha_off 为负值,但通常不被采用,因为它可能导致腿的构型非常不自然。

5.4 使用MATLAB进行运动可视化

为了更直观地理解,我们可以用MATLAB的绘图功能将机器人的腿画出来。下面是一个简单的可视化脚本:

function visualize_leg(gamma, alpha, beta, h, hu, hl)
    % 计算正运动学得到各关键点
    [O, A, B, P] = calculate_leg_points(gamma, alpha, beta, h, hu, hl);
    
    figure('Position', [100, 100, 1200, 400]);
    
    % 子图1:三维视图
    subplot(1,3,1);
    plot3([O(1), A(1), B(1), P(1)], ...
          [O(2), A(2), B(2), P(2)], ...
          [O(3), A(3), B(3), P(3)], 'o-', 'LineWidth', 2, 'MarkerSize', 8);
    hold on;
    grid on; axis equal;
    xlabel('X (前)'); ylabel('Y (侧)'); zlabel('Z (上)');
    title('单腿三维构型');
    view(135, 30); % 设置一个较好的观察角度
    
    % 标记关节点
    text(O(1), O(2), O(3), ' O (髋侧摆)', 'VerticalAlignment','bottom');
    text(A(1), A(2), A(3), ' A (髋俯仰)', 'VerticalAlignment','bottom');
    text(B(1), B(2), B(3), ' B (膝)', 'VerticalAlignment','bottom');
    text(P(1), P(2), P(3), ' P (足端)', 'VerticalAlignment','top');
    
    % 子图2:后视图 (YZ平面)
    subplot(1,3,2);
    plot([O(2), A(2), B(2), P(2)], ...
         [O(3), A(3), B(3), P(3)], 'o-', 'LineWidth', 2);
    hold on; grid on; axis equal;
    xlabel('Y (侧)'); ylabel('Z (上)');
    title('后视图 (YZ平面)');
    % 画出lyz和dyz辅助线
    plot([A(2), P(2)], [A(3), P(3)], 'r--');
    plot([O(2), P(2)], [O(3), P(3)], 'g--');
    legend('腿部', 'lyz', 'dyz', 'Location', 'best');
    
    % 子图3:侧视图 (XZ平面投影)
    subplot(1,3,3);
    % 注意:这里需要将坐标投影到垂直于lyz的平面上,简化显示X与sqrt(Y^2+Z^2)的关系
    % 简化处理:绘制X与足端到O点距离在侧平面的投影
    plot([0, A(1), B(1), P(1)], ...
         [0, -h, -h - hu*cos(alpha), -h - hu*cos(alpha) - hl*cos(alpha+beta)], 'o-', 'LineWidth', 2);
    hold on; grid on; axis equal;
    xlabel('X (前)'); ylabel('Z (下)');
    title('侧视图投影 (显示alpha, beta)');
end

function [O, A, B, P] = calculate_leg_points(gamma, alpha, beta, h, hu, hl)
    % 计算各点坐标
    O = [0, 0, 0];
    % A点:经过侧摆
    A = [0, h*sin(gamma), -h*cos(gamma)]; % Z向下为负
    % B点:大腿末端
    B = [hu * sin(alpha) * cos(gamma), ...
         A(2) + hu * sin(alpha) * sin(gamma), ...
         A(3) - hu * cos(alpha)];
    % P点:小腿末端(足端)
    total_angle = alpha + beta;
    P = [B(1) + hl * sin(total_angle) * cos(gamma), ...
         B(2) + hl * sin(total_angle) * sin(gamma), ...
         B(3) - hl * cos(total_angle)];
end

运行这个可视化函数,你可以清晰地看到腿在三维空间中的姿态,以及它在两个投影平面上的几何关系,这能极大地帮助你理解几何法推导的每一步。

6. 从仿真到实践:注意事项与扩展

掌握了单腿逆运动学的核心算法后,我们就可以将其应用到真正的四足机器人控制中。但在那之前,还有一些重要的实践细节需要考虑。

6.1 角度范围与奇异点

我们的推导假设了 atan2acos 函数总能给出解。但实际机器人的关节是有物理限位的。例如:

  • gamma 通常有较小的活动范围(如 ±45°),防止腿打到身体或其他腿。
  • alpha 的活动范围较大,可能接近 ±90°。
  • beta 通常被限制在 0° 到 -120° 之间(0°为完全伸直)。

在将计算出的弧度值发送给舵机或电机之前,必须进行限幅,确保其在物理允许的范围内。此外,当 lyz 接近 h 时,gamma_h_offset 计算可能不稳定;当 lxzp 非常接近 hu+hl|hu-hl| 时,三角形趋于一条直线,处于奇异点附近,此时数值计算精度会下降,关节速度可能变得极大。在实际步态规划中,应避免让足端轨迹过于接近工作空间的边界。

6.2 从关节角到舵机脉冲

计算出的 gamma, alpha, beta 是连杆坐标系中的角度。然而,舵机的零位安装可能与此不一致。例如,舵机中位可能对应着关节角为0的位置,但我们的几何模型0度可能对应着腿垂直于地面的状态。因此,需要一个映射关系

舵机目标脉冲宽度(或角度) = 关节角 * 比例系数 + 舵机中位值

这个比例系数由舵机的运动范围(如180°对应0.5ms到2.5ms的脉冲宽度)和关节的传动比决定。你需要通过实际测量来校准这个映射关系。通常的做法是:让机器人腿摆到一个已知的几何位置(比如完全伸直垂直向下),读取此时计算出的关节角,再读取此时舵机的实际脉冲值,两者相减就得到了偏移量。

6.3 集成到步态控制器中

在一个完整的四足机器人系统中,逆运动学求解模块会被步态生成器频繁调用。步态生成器会以固定的频率(如100Hz)输出四条腿的目标足端轨迹 (x_i, y_i, z_i), i=1,2,3,4。对于每条腿,控制器需要:

  1. 将世界坐标系下的足端轨迹,转换到该腿自身的髋关节坐标系下(这涉及到机器人的机体姿态补偿,如果机体在滚动、俯仰)。
  2. 调用 single_leg_ik 函数计算关节角。
  3. 将关节角通过校准映射转换为舵机指令。
  4. 发送指令给舵机控制器。

整个过程必须在几毫秒内完成,因此我们推导的解析几何法的高效性就至关重要。如果是用Python在树莓派上运行,也可以直接将上述MATLAB逻辑移植过去,用 numpy 进行数学计算。

6.4 探索其他结构与算法

本文介绍的是最常见的3自由度串联腿。有些四足机器人采用并联腿结构(如MIT Cheetah的膝关节驱动方式),其逆运动学求解公式会有所不同,但核心思想依然是几何分解。此外,对于更复杂的关节构型或者需要更高鲁棒性的情况,可能会用到数值迭代法(如雅可比矩阵伪逆法),这种方法通过不断迭代逼近解,通用性更强,但计算量也更大。

我建议在彻底理解并实现了这种几何法之后,再去探索数值法。这会让你对逆运动学问题的本质有更深刻的认识。当你看到自己编写的算法成功驱动一条真实的机器腿准确地踩到预设的位置时,那种成就感是无可比拟的。从纸上的公式,到屏幕上的仿真,再到现实中钢铁之躯的精准运动,这正是机器人学的魅力所在。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐