Singularity and manipulability analysis

This example collects four complementary local analyses using the same SVD-based task-Jacobian policy as Moro’s velocity inverse kinematics.

1. Planar 2R singularity analysis

from sympy import pi

from moro import Robot
from moro.abc import l1, l2, q1, q2
from moro.differential_kinematics import (
    singular_values,
    jacobian_rank,
    condition_number,
    is_singular,
    manipulability,
)

robot = Robot(
    (l1, 0, 0, q1, "r"),
    (l2, 0, 0, q2, "r"),
)

parameters = {l1: 1.0, l2: 0.8}
task = ("vx", "vy")

Compare a regular posture with a fully extended one:

regular_q = [0, pi / 2]
singular_q = [0, 0]

for q in (regular_q, singular_q):
    print(singular_values(robot, q, task=task, parameters=parameters))
    print(jacobian_rank(robot, q, task=task, parameters=parameters))
    print(condition_number(robot, q, task=task, parameters=parameters))
    print(is_singular(robot, q, task=task, parameters=parameters))

At the fully extended posture, the planar position Jacobian loses one independent direction, so its rank drops and its condition number becomes infinite under the automatic threshold.

2. Planar 2R manipulability

For the same planar position task, the analytical Yoshikawa metric is

\[ w=|l_1l_2\sin q_2|. \]

A small sweep can compare the numerical result with this expression:

from math import sin

for q2_value in [0.0, 0.25, 0.5, 1.0, float(pi / 2)]:
    numerical = manipulability(
        robot,
        [0.0, q2_value],
        task=task,
        parameters=parameters,
    )
    analytical = abs(parameters[l1] * parameters[l2] * sin(q2_value))
    print(q2_value, numerical, analytical)

At \(q_2=0\), manipulability is exactly zero. At \(q_2=\pi/2\), it reaches \(l_1l_2\).

3. Singularity depends on the selected task

Consider a planar 3R robot:

from moro.abc import q3

robot3 = Robot(
    (1, 0, 0, q1, "r"),
    (1, 0, 0, q2, "r"),
    (1, 0, 0, q3, "r"),
)

q = [0.2, 0.8, -0.4]

print(is_singular(robot3, q, task=("vx", "vy")))
print(is_singular(robot3, q, task=("vx", "vy", "vz")))

The planar translational task ("vx", "vy") can have full row rank two, while adding the unattainable vz component creates a three-row task whose rank remains two. The same configuration is therefore regular for the reduced task and singular for the larger task.

4. Condition number and manipulability measure different things

For any regular one-dimensional task the condition number is one because there is only one singular value. Its absolute magnitude can still be very small.

Using the planar 2R robot near a posture where the \(x\)-velocity row is small:

q = [0.0, 1e-3]
task_1d = ("vx",)

print(condition_number(robot, q, task=task_1d, parameters=parameters))
print(manipulability(robot, q, task=task_1d, parameters=parameters))

The condition number is one while manipulability is small. This illustrates that condition number measures anisotropy, whereas manipulability also reflects absolute local velocity scale.

Interpretation cautions

Manipulability depends on the chosen task and units. A task that mixes translational and angular rows also mixes their scales. Moro 0.5.0 does not introduce a characteristic length or automatic weighting, so the task should be chosen explicitly according to the quantity being studied.