Fantastic question — this is exactly where null‑space control really shines.
When a manipulator is redundant, the null‑space term can hold multiple secondary objectives simultaneously — each “competing” to influence joint motion without breaking the primary end‑effector task.
Let’s go step‑by‑step.
Starting from the differential inverse kinematics solution:
[ \dot{\boldsymbol{\theta}} = J^{+}\dot{\mathbf{x}}
- (I - J^{+}J)\mathbf{z} \tag{77 revisited} ]
- The first term satisfies the primary (task‑space) objective.
- The second term is free null‑space motion, (\mathbf{z}), used for secondary goals.
For a single secondary goal (H(\boldsymbol{\theta})):
[ \mathbf{z} = -\alpha \nabla H(\boldsymbol{\theta}) ]
which yields:
[ \dot{\boldsymbol{\theta}} = J^{+}\dot{\mathbf{x}}
- \alpha (I - J^{+}J)\nabla H(\boldsymbol{\theta}) \tag{A} ]
If you have several scalar cost functions:
[
H_1(\theta),; H_2(\theta),; \ldots,; H_k(\theta)
]
each representing one constraint — e.g.
- (H_1): joint‑limit avoidance
- (H_2): preferred posture
- (H_3): manipulability enhancement
You can combine them linearly into a total cost:
[ H_{total} = \sum_{i=1}^k w_i,H_i(\theta) \tag{B} ]
where (w_i > 0) are priority or weight factors (how much each secondary goal matters).
Then:
[ \nabla H_{total} = \sum_{i=1}^k w_i \nabla H_i(\theta) ]
and you simply plug this combined gradient into (A):
[ \boxed{ \dot{\theta} = J^{+}\dot{x}
- \alpha (I - J^{+}J) !! \underbrace{\sum_{i=1}^k w_i, \nabla H_i(\theta) }_{\text{weighted combined gradient}} } \tag{C} ]
- The weighted sum of gradients defines a “compromise direction” in joint space.
- ( (I - J^{+}J) ) ensures that this secondary motion doesn’t disturb the primary end‑effector command.
- ( w_i ) tune how strongly each objective influences the posture.
You’re effectively performing multi‑objective optimization in the null space, subject to the primary task constraint.
Below is a direct numerical example.
import numpy as np
def truncated_pinv(J, eps=1e-4):
U, S, Vt = np.linalg.svd(J)
S_inv = np.array([1/s if s > eps else 0 for s in S])
return Vt.T @ np.diag(S_inv) @ U.T
def grad_joint_limit(theta, theta_c, theta_min, theta_max):
c = 1.0 / (theta_max - theta_min)**2
return 2 * c * (theta - theta_c)
def grad_posture(theta, theta_ref):
return (theta - theta_ref)
def diff_ik_multi_secondary(J, x_dot, theta, theta_c, theta_min, theta_max, theta_ref,
w_joint=1.0, w_posture=0.3, alpha=0.2):
J_pinv = truncated_pinv(J)
I = np.eye(J.shape[1])
# compute gradients
grad_H1 = grad_joint_limit(theta, theta_c, theta_min, theta_max)
grad_H2 = grad_posture(theta, theta_ref)
# weighted combination
grad_total = w_joint * grad_H1 + w_posture * grad_H2
q_dot = J_pinv @ x_dot - alpha * (I - J_pinv @ J) @ grad_total
return q_dot
# Example setup
theta = np.array([0.8, -1.0, 0.5])
theta_min = np.array([-1.5, -1.0, -1.0])
theta_max = np.array([ 1.5, 1.5, 1.5])
theta_c = (theta_max + theta_min)/2
theta_ref = np.array([0.0, 0.0, 0.0]) # nominal rest pose
x_dot = np.array([0.05, 0, 0, 0, 0, 0])
J = np.random.randn(6,3)
q_dot = diff_ik_multi_secondary(J, x_dot, theta, theta_c, theta_min, theta_max, theta_ref)
print("Joint velocity (combined secondary constraints):\n", q_dot)- The primary term, ( J^{+}\dot{x} ), moves the end‑effector along its commanded direction.
- The joint‑limit term keeps joints away from boundaries.
- The posture term gently pulls the joints back toward their neutral configuration.
- The weights let you balance them (e.g., emphasize safety over aesthetics).
In run‑time behavior, the arm will:
- Reach toward the target,
- Stay clear of joint limits,
- Drift toward a natural pose whenever possible.
If strict priority (not just weighting) is desired, you can use hierarchical projections.
For example, to ensure the second task doesn’t disturb the first secondary task either:
[ \begin{aligned} \dot{\theta} &= J_1^{+}\dot{x}_1
- (I - J_1^{+}J_1) J_2^{+} \dot{x}_2 \ &+ (I - J_1^{+}J_1)(I - J_2^{+}J_2),\mathbf{z}_3 \end{aligned} ]
This recursive projection builds strict task priorities:
- Task 1 (end‑effector) highest,
- Task 2 (e.g., posture) second,
- Task 3 (e.g., joint‑limit) third, etc.
But for most applications, the weighted‑sum approach (Eq. C) is simpler and effective.
| Concept | Formula | Effect |
|---|---|---|
| Weighted combination | ( \dot{\theta} = J^{+}\dot{x} - \alpha (I-J^{+}J)\sum_i w_i\nabla H_i ) | Blends multiple goals |
| Strict hierarchy | Recursive null‑space projections | Enforces strict priorities (task 1 ≫ task 2 ...) |
| (w_i) tuning | Adjusts influence of each secondary task | Balance between constraints |
Would you like me to show a hierarchical two‑level numerical example, demonstrating strict priority (e.g., manipulability optimization secondary to joint‑limit avoidance)?