Lesson 2.1.3 — Visual Servoing: Image-Based, Position-Based, and the Hybrid That Avoids Both
Week 10 · Prerequisites: 2.1.1, 2.1.2, 1.1.1, 1.1.3, 0.2.1, 0.2.2.
Prior results used. This lesson uses 2.1.1’s pinhole model and back-projection’s ray, 2.1.2’s 6D pose estimation and the symmetric’s ambiguity, 1.1.1’s geometric Jacobian, 1.1.3’s PID and computed-torque control, 0.2.1’s SE(3) inverse, and 0.2.2’s SO(3) log map. Each is restated at the point of use.
Hands forward. This lesson hands forward one capability: the learner can derive the interaction matrix, implement an image-based servo that drives the pixel error to zero, implement a position-based servo that drives the pose error to zero, analyze the stability and the local minima of each, choose the hybrid when both fail, and diagnose the four visual-servoing bugs (sign error, depth estimate, extrinsic frame, target loss) from their trajectory’s symptoms — the capability that 1.1.4’s final approach, 2.3.2’s grasp alignment, and 3.1.4’s closed-loop VLA refinement all assume.
Running case. The same 6-DOF MuJoCo arm from 1.1.1–2.1.2, with the eye-in-hand camera (intrinsics K = [[500, 0, 320], [0, 500, 240], [0, 0, 1]], 640 × 480) and the cube at p^W = [0.5, 0.2, 0.3]. The four visible corners are the image features s = (u₁, v₁, …, u₄, v₄), and the desired features s* are the same four corners at the desired camera pose (centered, gripper aligned). The servo drives s → s* by commanding the camera’s velocity v_c = (v, ω). The central result: IBVS is stable in image space but non-optimal in Cartesian space, PBVS is optimal in Cartesian space but can lose the target, and the 2.5D hybrid bounds both.
§L.0 — Lesson map, objectives, and misconceptions
§L.0.1 — Lesson map and word budget
| Part | Content | Lab step | Word budget |
|---|---|---|---|
| A | The problem, eye-in-hand vs eye-to-hand, the closed-loop structure, the two paradigms | Lab Steps 1–2 | 1,600 |
| B | IBVS: the interaction matrix, the control law, the local minima, the depth dependence | Lab Steps 3–5 | 2,100 |
| C | PBVS: the pose error, the interaction matrix, the Cartesian control, the FOV risk | Lab Steps 6–8 | 1,900 |
| D | The comparison, the 2.5D hybrid, the visibility condition | Lab Steps 9–10 | 1,400 |
| E | Failure taxonomy, lab, cheat sheet, exercises, exit test | Lab Steps 11–13 | 1,900 |
| Total | 8,900 |
§L.0.2 — Objectives
By the end of this lesson you will be able to do eight things:
- State the visual-servoing problem, the eye-in-hand vs eye-to-hand configurations, and the closed-loop structure (image → features → control → robot velocity → new image).
- Derive the interaction matrix
L_xfor a point in normalized coordinates and in pixel coordinates, and state its1/Zscaling and its rank (2 per point, 6 for three or more points). - Implement the IBVS control
v_c = -λ L̂_s⁺ (s - s*)with an estimatedL̂_s(approximate depth), and state the exponential convergence and the local-minimum risk. - Derive the PBVS control
v_c = -λ L̂_e⁺ ewith the 6D pose errore = [t_err; θ u_err]and the pose interaction matrixL_e, and state the global stability (linearized) and the FOV risk. - State IBVS’s advantages (calibration robustness, guaranteed image convergence) and disadvantages (non-optimal Cartesian trajectory, local minima, depth dependence), and PBVS’s advantages (optimal Cartesian trajectory, global convergence) and disadvantages (calibration sensitivity, target loss).
- State the 2.5D hybrid (image error plus depth, or homography decomposition) and explain its benefit (bounded image trajectory, controlled Cartesian trajectory).
- State the visibility condition (target pixels stay inside the image) and the FOV constraint, and the trajectory’s violation symptom (target loss).
- Diagnose the four bugs — sign error (divergence), wrong depth estimate (overestimate amplifies the gain: overshoot, then divergence; underestimate attenuates it: slower, still stable), wrong extrinsic frame (wrong axis), target loss (out of view) — from their trajectory’s symptoms.
§L.0.3 — Misconception inventory
| Wrong model | Why attractive | Where displaced | Correct model |
|---|---|---|---|
| M1 — “Visual servoing is IK on the image.” | The IK solves joint angles from pose; the servo solves velocity from image — both feel like inverse problems. | §L.1 | Visual servoing is a closed-loop control problem, not an inverse problem. The IK is the open-loop map (pose → joint angles). The servo is the feedback loop (image → velocity → new image → new velocity). The control law is v_c = -λ L⁺ e, and stability is the exponential decay of the error, not a single solution. |
| M2 — “The interaction matrix is the Jacobian.” | Both are matrices mapping velocities; they feel like the same kind of object. | §L.2 | The interaction matrix L_x is the image Jacobian (2×6 per point). The geometric Jacobian J(q) is the robot Jacobian (6×n). The composition L_x J(q) is the full map. They are different matrices with different domains and codomains. |
| M3 — “IBVS is always better because it’s robust.” | IBVS is robust to calibration error; robustness feels like superiority. | §L.4 | IBVS’s robustness is calibration robustness. Its cost is the non-optimal Cartesian trajectory (large camera motion for small image error, local minima, target loss). PBVS’s optimal trajectory costs calibration sensitivity. The choice depends on the task. |
| M4 — “The depth is constant in IBVS.” | The Z in L_x feels like a minor parameter. | §L.2 | The Z is the point’s depth at the current instant and it changes as the camera moves. The wrong Z gives the wrong control magnitude — and the direction is the opposite of the intuition: Ẑ = kZ scales the effective gain by k (measured eigenvalues of L L̂⁺ run 1 .. k), so an overestimate amplifies the step and can overshoot into divergence, while an underestimate attenuates it and merely converges more slowly. IBVS’s robustness is partial — the sign is correct, the magnitude is approximate. |
| M5 — “The 2.5D hybrid is a compromise with no advantage.” | Mixing image and pose errors feels like the worse of both. | §L.4 | The mixing is designed: the image error bounds the image trajectory, the pose error controls the Cartesian trajectory. The hybrid gives both visibility and Cartesian optimality. It is the standard for tasks that require both. |
Each misconception is displaced in the section named, and each is tagged in the concept check that follows.
Everything is self-contained. Where a prerequisite lesson is used, the result is restated at the point of use.
§L.0.5 — Retrieval warm-up
This is the twenty-fourth lesson. The warm-up draws from 2.1.1 (two steps back), 1.2.5 (three), 1.2.4 (four), and 1.2.3 (five).
Warm-up 1 (from 2.1.1, projection and depth ambiguity). State the projection s x = K [R|t] X and the back-projection ray. Answer: s = Z is the depth scale; K is intrinsics; [R|t] is world-to-camera. The back-projection is the ray C + λ R x_n with x_n = K⁻¹ x. How this is used: §L.2’s L_x uses 1/Z, and the depth ambiguity from 2.1.1 is exactly what L̂_s must approximate.
Warm-up 2 (from 1.2.5, SAC). State the twin critics and α’s role. Answer: twin critics’ min mitigates overestimation; α is the entropy’s learnable weight. How this is used: §L.4’s visual servo gain λ plays a role analogous to the PID’s K_p, and the automatic α tuning is analogous to an adaptive λ.
Warm-up 3 (from 1.2.4, PPO). State the clipped objective and the trust region. Answer: min(r A, clip(r) A) with r = π_θ/π_old; the clip bounds the ratio. How this is used: §L.3’s PBVS stability is the linearized pose error’s exponential decay; saturation on the velocity is the trust-region analogue (bounded step, avoiding target loss).
Warm-up 4 (from 1.2.3, REINFORCE). State the score function and the baseline’s variance reduction. Answer: ∇ log π(a|s) is the score with zero mean; the baseline b(s) gives E[∇ log π · b(s)] = 0. How this is used: §L.1’s visual servo error s - s* is deterministic, and the desired features s* play the role of a baseline — the target, not a random variable.
Part A — The Visual-Servoing Problem and the Two Paradigms
§L.1 — Objects: the problem, the configurations, the closed loop
The visual-servoing problem: given a camera observing a target with image features s, desired features s*, and a robot carrying or observing the camera, design a control law that drives s(t) → s* as t → ∞.
Two configurations:
- Eye-in-hand — camera mounted on the end-effector. The camera’s velocity
v_cis the end-effector’s velocityV = J(q) q̇(1.1.1). The control is the joint velocityq̇ = J(q)⁻¹ v_c(resolved-rate control). - Eye-to-hand — camera fixed in the workspace, observing the robot. The camera’s observed velocity is the target’s velocity; the robot’s control is the opposite sign (
v_target = -v_c).
Closed-loop structure:
- Camera captures image at
t_k. - Feature extraction gives
s(t_k). - Error
e(t_k) = s(t_k) - s*. - Control law computes
v_c(t_k). - Robot velocity
q̇(t_k) = J(q)⁻¹ v_c. - Robot moves; new image at
t_{k+1}closes the loop.
Two paradigms:
- Image-Based Visual Servoing (IBVS) — error is in the image (
e = s - s*), control uses the interaction matrixL_x. - Position-Based Visual Servoing (PBVS) — error is in the Cartesian pose (
e = [t_err; θ u_err]), control uses the pose interaction matrixL_e.
§L.1.1 — Result: IBVS’s error is 2D, PBVS’s is 3D, and each is locally stable in its own space
Claim. IBVS’s error e = s - s* is 2D (8 numbers for four points) and its control drives the image error to zero, locally stable, if L̂_s is a good estimate. PBVS’s error is 6D and its control drives the pose error to zero, locally stable, if the pose estimate is exact.
Proof sketch. IBVS error dynamics: ė = L_x v_c = -λ L_x L̂_x⁺ e. With L̂_x = L_x, ė = -λ e (exponential decay). PBVS error dynamics: ė = L_e v_c = -λ L_e L̂_e⁺ e. With L̂_e = L_e, ė = -λ e. Both are locally stable; the difference is which space converges.
Corollary. IBVS’s stability is in image space (s → s*), not in Cartesian space. PBVS’s stability is in pose space (T → T*), not in image space. The symptom of IBVS is a converged image but a possibly wrong camera pose; the symptom of PBVS is a converged pose but possibly lost target.
Corollary. IBVS is robust to calibration error (image feedback tolerates it); PBVS is robust to pose error only if the pose is accurately estimated. The choice is task-dependent.
§L.1.2 — Worked-example ladder
WE-L.1.a — Fully worked: one closed-loop step
Input. Camera at T_Cam^W = I, cube’s four visible corners at camera-frame positions, pixels s_0, desired s*, gain λ = 0.3.
Trace.
- Error
e_0 = s_0 - s*. - Interaction matrix
L_s(8×6, built fromL_ufor each point). - Pseudoinverse
L̂_s⁺(6×8). - Control
v_c = -0.3 · L̂_s⁺ · e_0. - Robot velocity
q̇ = J(q)⁻¹ v_c. - New image
s_1; errore_1 = s_1 - s*.
Output and interpretation. With an exact L̂_s, e_1 = 0.7 e_0 — exponential decay at rate λ. Measured on the running case, e₁/e₀ = 0.676 rather than 0.700: the linear model overstates the contraction slightly, because one step of a finite twist is not the infinitesimal ė = −λe. In practice L̂_s also uses approximate depth, which scales the gain by Ẑ/Z.
Self-explanation 1. In step 4, why is
λ = 1risky?Answer. In the linearised model,
λ = 1makese_1 = 0in one step (exactL̂_s). It does not, because the model is only linear: measured on the running case,λ = 1givese₁/e₀ = 0.780, not0. The linear prediction|1 − λ|is accurate atλ = 0.3(0.700predicted,0.676measured) and wrong by0.78atλ = 1.λ = 1is the stability boundary of the linearised loop and past the usable range of the real one;λ = 0.3–0.5is where the linearisation still describes what happens.
Self-explanation 2. If
L̂_suses a wrong depth (Ẑ = 2Z), what is the symptom?Answer. Every
1/ZinL̂_sis halved, soL̂_s⁺is doubled andv_cis too large — the servo overshoots. WithẐ = 2Zthe effective gain is2λ = 0.6, so each step contracts by0.4instead of0.7: faster per step, and the loop stays stable while2λ < 1. Push the overestimate toẐ = 10Zand the gain is3.0; the first step throws the cube out of frame.
WE-L.1.b — Completion problem
Input. Same case, λ = 0.2, e_0.
Trace (final 25% as prompts).
e_1 = 0.8 e_0. ⟨verify⟩e_2 = 0.64 e_0. ⟨verify⟩e_k = 0.8^k e_0. ⟨verify⟩- Number of steps to reach
0.1 e_0:0.8^k = 0.1→k ≈ 10.3. ⟨verify⟩
Answers.
1–4. Correct.
The lesson. The gain λ sets the exponential rate. λ = 0.2 needs ~10 steps for 10% error; λ = 0.5 needs ~3 steps. Too high a gain saturates the actuators.
WE-L.1.c — Faded: eye-in-hand vs eye-to-hand
Setup. Same camera, but eye-to-hand (fixed camera observing the robot).
Question. What is the sign of the control?
You must produce the intermediate steps. The setup and the answer are given.
Answer. The camera is fixed, so its velocity is not the control. The target is the robot’s end-effector. The camera’s observed velocity is the end-effector’s velocity; the robot’s control is v_target = -v_c. Thus q̇ = -J⁻¹ λ L̂⁺ e — the opposite sign from eye-in-hand.
The lesson. The eye-to-hand configuration flips the sign. Getting the sign wrong causes divergence.
WE-L.1.d — Contrastive near-miss: IK’s one step
Correct solution. Visual servo as closed loop (many steps, feedback).
Near-miss. IK’s one step (open loop, pose solve).
Why the near-miss produces a plausible but incorrect number. The IK solves q = IK(T*) and drives to the pose in one shot. If calibration is perfect, it lands exactly. But the camera observes the image, and calibration error is direct. IBVS is image-feedback (tolerates calibration error but converges only in image space). The IK is pose-open-loop (calibration error is direct but pose is exact if calibration is exact). The symptom of IK failure is pose error from calibration; the symptom of IBVS is image convergence but non-optimal pose.
The discrepancy. IK’s q = IK(T*) is open loop; IBVS’s q̇ = J⁻¹(-λ L⁺ e) is closed loop. The two are different problems: IK inverts the pose map; IBVS feeds back the image error.
Self-explanation 3. If calibration is perfect, is IK equivalent to IBVS?
Answer. Only if the image-to-pose map is bijective. It is not: the 2D image loses depth, and multiple poses produce the same image (the symmetric ambiguity from
2.1.2). IK solves the pose exactly; IBVS only converges to a pose consistent with the image.
Part B — Image-Based Visual Servoing: Interaction Matrix and Local Minima
§L.2 — Objects: the interaction matrix, the control law, the local minima
The interaction matrix L_x ∈ ℝ^{2×6} for a 3D point X = (X, Y, Z) in the camera frame, with normalized image coordinates x = X/Z, y = Y/Z:
L_x = [ -1/Z 0 x/Z x·y -(1+x²) y ]
[ 0 -1/Z y/Z 1+y² -x·y -x ]
Each column corresponds to a component of camera velocity v_c = (v_x, v_y, v_z, ω_x, ω_y, ω_z).
In pixel coordinates (u, v) with focal f (square pixels):
L_u = [ -f/Z 0 u/Z u·v/f -(f+u²/f) v ]
[ 0 -f/Z v/Z f+v²/f -u·v/f -u ]
Key observations:
- The first three columns (translation) have
1/Zscaling. Translation’s effect on the image depends on depth. - The last three columns (rotation) have no
Zdependence. Rotation’s effect is scale-free. - For
n ≥ 3points,L_s = [L_u^1; ...; L_u^n]is2n × 6. Rank is 6 for non-degenerate configurations.
Control law:
v_c = -λ · L̂_s⁺ · (s - s*)
where L̂_s⁺ is the pseudoinverse, λ > 0 is the gain.
Depth estimate. L̂_s requires depth Z for each point. In practice:
- From the RGB-D camera: use measured depth.
- From a known object size:
Z = f · d / d_pixel(the 2.1.1 disparity). - From the
2.1.2pose:Z = (T_Cam^Obj · X_Obj)_z.
Local minima. The classic IBVS local minima arise from:
- Coplanar points at 180° rotation: the image looks the same, and the error is zero, but the pose is wrong.
L_srank deficiency for degenerate configurations (fewer than 3 points, collinear points).- The pseudoinverse’s null space when
rank(L_s) < 6.
§L.2.1 — Result: the interaction matrix’s -f/Z is depth-dependent; the control is exponential but depth-approximate; local minima are the nonlinearity’s consequence
Claim. The interaction matrix’s translation columns scale as 1/Z; rotation columns do not. The control law v_c = -λ L̂_s⁺ e gives exponential convergence e(t) = e^{λ t} e(0) when L̂_s = L_s (exact). With an approximate L̂_s, the convergence is still exponential but with a modified rate.
Proof sketch. ė = L_s v_c = -λ L_s L̂_s⁺ e. If L_s L̂_s⁺ = I on the range of L_s (exact estimate), ė = -λ e. With an estimate L̂_s, L_s L̂_s⁺ is approximately I with error O(δZ/Z), and the rate changes by O(δZ/Z).
Corollary. Depth error affects magnitude, not sign. Sign error (wrong L̂_s direction) causes divergence. A wrong Z magnitude rescales the gain: with Ẑ = kZ the eigenvalues of L_s L̂_s⁺ lie in [min(1,k), max(1,k)], so k > 1 multiplies the step by up to k (overshoot, then divergence once λk > 1) and k < 1 divides it (slower, still stable). Only the overestimate is dangerous.
Corollary. Local minima occur when L_s L̂_s⁺ e = 0 but e ≠ 0 — the pseudoinverse’s null direction. This happens for symmetric configurations (180° flips) and degenerate geometries.
§L.2.2 — Worked-example ladder
WE-L.2.a — Fully worked: the interaction matrix for a single point
Input. Point at X = (0.5, 0.2, 1.0), focal f = 500, c_x = c_y = 0 for simplicity.
Trace.
x = X/Z = 0.5,y = Y/Z = 0.2.u = f·x = 250,v = f·y = 100.L_x = [ -1/1.0 0 0.5 0.5·0.2 -(1+0.25) 0.2 ][ 0 -1/1.0 0.2 1+0.04 -0.5·0.2 -0.5 ]L_x = [ -1.0 0 0.5 0.1 -1.25 0.2 ][ 0 -1.0 0.2 1.04 -0.1 -0.5 ]
Output and interpretation. The -1/Z term dominates translation columns. Rotation columns have magnitude O(1). The L_x maps a camera velocity (v_x, v_y, v_z, ω_x, ω_y, ω_z) to the point’s image velocity in (ẋ, ẏ) normalized coordinates. With f = 500, the pixel interaction matrix scales by f.
Self-explanation 4. Why is the
ω_zcolumn (last column)[y; -x]?Answer.
ω_zis rotation about the optical axis. It rotates the image plane; the point(x, y)moves along the tangent direction(y, -x)proportional toω_z. This is the image rotation effect, independent of depth.
Self-explanation 5. If
Zdoubles to2.0, what happens to the translation columns?Answer.
-1/Z = -0.5, andx/Z = 0.25. Translation columns halve; rotation columns unchanged. HalvingL̂_s’s translation block doubles its pseudoinverse’s, so the commanded translation is twice what the true depth calls for. The camera moves too far for the pixel error: overshoot. An underestimate (Ẑ = Z/2) is the case that converges slowly — the command comes out too small.
WE-L.2.b — Completion problem
Input. Same point, Z = 2.0, x = 0.25, y = 0.1.
Trace (final 25% as prompts).
L_x[0, 0] = -1/2 = -0.5. ⟨verify⟩L_x[0, 2] = x/Z = 0.25/2 = 0.125. ⟨verify⟩L_x[1, 1] = -0.5. ⟨verify⟩L_x[1, 3] = 1 + y² = 1.01. ⟨verify⟩
Answers.
1–4. Correct.
The lesson. Depth doubling halves the translation sensitivity, but rotation is unaffected. The image becomes “flatter” for the same physical motion.
WE-L.2.c — Faded: the local minimum at 180°
Setup. Four coplanar points; camera at 180° from desired pose. Image looks identical to desired.
Question. What is the error, the control, the trajectory?
You must produce the intermediate steps. The setup and the answer are given.
Answer. e = 0 (image looks identical). The control v_c = 0 — the servo stops at the wrong pose. This is a local minimum: the image error cannot distinguish 0° from 180°.
The lesson. The IBVS error is 2D; the pose is 6D. The map from pose to image is not injective, so multiple poses give the same image. The servo has no way to escape the local minimum without additional information (a prior, an additional feature, a depth constraint).
WE-L.2.d — Contrastive near-miss: wrong-sign control
Correct solution. v_c = -λ L̂_s⁺ e (negative sign).
Near-miss. v_c = +λ L̂_s⁺ e (positive sign).
Why the near-miss produces a plausible but incorrect number. The wrong sign makes ė = +λ L_s L̂_s⁺ e ≈ +λ e, which is exponential growth. The image error grows; the servo diverges. The trajectory shows the camera moving away from the target until the target leaves the FOV.
The discrepancy. The sign of the control determines whether the error decays or grows. The wrong sign is the most common bug in visual servoing.
Self-explanation 6. How can you detect the wrong-sign bug in one test?
Answer. Check whether
||s(t) - s*||decreases with each step. If it increases, the sign is wrong. Alternatively, check the sign of the eigenvalue ofL_s L̂_s⁺: with the correct sign, all eigenvalues should have positive real part when the error is multiplied by-λ.
Part C — Position-Based Visual Servoing: Pose Error and FOV Risk
§L.3 — Objects: the pose error, the pose interaction matrix, the control law
PBVS estimates the 6D pose T_Cam^Obj (via 2.1.2) and computes the pose error:
e = [ t - t* ; θ · u_err ]
where t_err = t - t* is the translation error and θ · u_err is the rotation error in axis-angle form (from 0.2.2’s log map on R_err = R* Rᵀ).
Pose interaction matrix L_e ∈ ℝ^{6×6}:
L_e = [ -I [t]_× ]
[ 0 L_ω ]
where [t]_× is the skew-symmetric matrix of t, and:
L_ω = I - (θ/2)[u]_× + (1 - sinc(θ)) [u]_ײ
with sinc(θ) = sin(θ)/θ.
Control law:
v_c = -λ · L̂_e⁺ · e
With exact L_e, L_e L_e⁺ = I and ė = -λ e — global exponential convergence in pose space.
FOV risk. The PBVS optimal trajectory (the straight-line interpolation in pose space) can push the camera away from the target for large rotations. The target’s image can exit the camera’s field of view — the camera goes blind and cannot continue.
§L.3.1 — Result: PBVS is globally stable (linearized) in pose space but risks target loss; the FOV constraint is the practical limit
Claim. With exact L_e and pose estimate, PBVS has global exponential convergence in pose space. However, the trajectory is the optimal Cartesian interpolation, which can rotate the camera such that the target exits the FOV.
Proof sketch. ė = L_e v_c = -λ L_e L_e⁺ e = -λ e. The dynamics are exactly linear and globally stable. The FOV constraint is a physical limitation: the image projection s = K [R|t] X requires Z > 0 and u, v in image bounds. When the optimal trajectory violates either, the servo cannot proceed.
Corollary. PBVS is optimal when the calibration is accurate and the FOV constraint is satisfied. When the target would leave the FOV, PBVS fails; the servo must either slow down (small λ) or switch to a hybrid.
Corollary. PBVS is more sensitive to calibration than IBVS: pose estimation errors propagate directly to the control. IBVS’s image feedback tolerates small calibration errors because the image error corrects itself.
§L.3.2 — Worked-example ladder
WE-L.3.a — Fully worked: the pose interaction matrix for identity R
Input. R = I, t = (0.5, 0.2, 1.0), e = [t_err; θ u_err] with θ = 0 (no rotation error).
Trace.
[t]_× = [0, -1.0, 0.2; 1.0, 0, -0.5; -0.2, 0.5, 0].L_ω = I(θ = 0).L_e = [ -I [t]_× ][ 0 I ].- With
e = [ΔT; 0],L_e⁺ egivesv_c ≈ -[ΔT; 0]— the camera moves towardt*.
Output and interpretation. The PBVS control decouples translation and rotation at θ = 0. As θ grows, L_ω couples the rotation to itself.
Self-explanation 7. Why does
L_ωbecomeIatθ = 0?Answer.
sinc(0) = 1, so1 - sinc(0) = 0, and the[u]_ײterm vanishes. At small θ, the rotation error’s contribution to the image is approximately the rotation itself — small-angle approximation.
Self-explanation 8. Why is the
[t]_×term inL_e?Answer. The rotation rate
ωcontributes to the translation velocity of the origin:ṫ = v + ω × t = v - [t]_× ω. The- [t]_×block captures this coupling — a rotation about a point not at the origin moves the origin.
WE-L.3.b — Completion problem
Input. R = I, t = (0.5, 0.2, 1.0), θ = π/2 about the z-axis.
Trace (final 25% as prompts).
u = (0, 0, 1),[u]_× = [[0, -1, 0], [1, 0, 0], [0, 0, 0]]. ⟨verify⟩sinc(π/2) = 2/π ≈ 0.637. ⟨verify⟩1 - sinc(π/2) ≈ 0.363. ⟨verify⟩L_ω = I - (π/4)[u]_× + 0.363 [u]_ײ. ⟨verify⟩
Answers.
1–4. Correct.
The lesson. As θ grows, L_ω becomes increasingly non-identity. The rotation-to-rotation mapping is no longer direct; the axis-angle parametrization’s singularity at θ = π (0.2.2) also affects L_ω.
WE-L.3.c — Faded: FOV loss
Setup. Camera at t = (0, 0, 0.5), target at T_W = (0, 0, 0), desired T* at t = (0, 0, 0.5) but with R* = Rz(π) (180° rotation).
Question. Does the trajectory lose the target?
You must produce the intermediate steps. The setup and the answer are given.
Answer. PBVS drives the camera along the optimal pose interpolation. For a 180° rotation, the interpolated pose rotates the camera through the target’s view. At the midpoint, the camera is at 90° rotation, and the target is at the edge of the image. At 180°, the target is behind the camera — lost. The servo cannot continue.
The lesson. For large rotations, PBVS is optimal but blind. IBVS handles this case by keeping the target’s features in view, at the cost of a longer Cartesian trajectory. The hybrid 2.5D chooses the middle path.
WE-L.3.d — Contrastive near-miss: PBVS with wrong pose estimate
Correct solution. Pose from 2.1.2 with < 1 cm error.
Near-miss. Pose from 2.1.2 with symmetric ambiguity (180° flip).
Why the near-miss produces a plausible but incorrect number. With the symmetric ambiguity, the PBVS error is 2 · (misalignment) — the wrong pose drives the camera to the symmetric target, not the true one. The image shows the same target as a symmetric view, so the servo converges — to the wrong pose.
The discrepancy. The pose estimate’s ambiguity propagates directly to the PBVS control. IBVS is insensitive because it uses the image error, not the pose. The hybrid uses a pose estimate plus an image error to disambiguate.
Self-explanation 9. If the target is a symmetric cube, is PBVS unsafe?
Answer. Not necessarily — the 180° flip might be equivalent for a grasp (any 90° rotation of the cube is graspable). It is unsafe for insertion (the hole must be aligned correctly) or precision manipulation. The choice depends on the task’s symmetry.
Part D — The Comparison and the 2.5D Hybrid
§L.4 — Objects: the comparison, the hybrid, the visibility condition
Comparison:
| Aspect | IBVS | PBVS |
|---|---|---|
| Error space | Image (2D) | Pose (3D) |
| Stability | Local, image space | Global, pose space |
| Calibration robustness | High (image feedback) | Low (pose estimate) |
| Cartesian trajectory | Non-optimal | Optimal |
| Target visibility | Guaranteed | Not guaranteed |
| Local minima | Yes (symmetric, coplanar) | No (globally convergent) |
| Depth estimate | Required in L_s | Required in pose |
| Best for | Small motions, calibration error | Large motions, known pose |
The 2.5D hybrid (Malis-Chaumette-Boudet, 1999): combines image error with depth error:
e_hybrid = [ s - s* ; log(Z/Z*) ]
or uses the homography decomposition: from H between the two views, extract R and t/Z. The control uses L (a mix of image and pose interaction).
Simpler hybrid: use IBVS for the image direction and PBVS for the depth and coarse rotation. Implementation:
v_c = -λ L_s^+ (s - s*) - λ_p L_e^+ [t_err; 0]
where λ_p is the pose gain, and L_e^+ uses the pose estimate. The pose term carries translation only: the rotation is left to the image term, which sees it directly. λ_p is a separate gain, not a fraction of λ — writing it inside the outer bracket multiplies the two, and λ_p then means something different from what the symbol says.
Visibility condition. The target’s pixels stay inside [0, W-1] × [0, H-1]. This is a constraint on the trajectory:
u_min ≤ u_i(t) ≤ u_max for all i, t
v_min ≤ v_i(t) ≤ v_max for all i, t
Violations cause target loss. The IBVS converges to the target, but the intermediate trajectory can push pixels off-image if the gain is too high.
§L.4.1 — Result: the 2.5D hybrid combines IBVS’s visibility guarantee with PBVS’s optimality; the visibility condition is the constraint that determines the hybrid’s gain
Claim. The 2.5D hybrid’s trajectory satisfies the visibility condition if the gain is chosen so the image features stay in the FOV. The Cartesian trajectory is bounded between IBVS (which can be long) and PBVS (which can lose the target).
Proof sketch. The hybrid’s error combines image features (which anchor the trajectory to the image space) and depth/pose (which give the Cartesian direction). The image features’ contribution keeps the target in view; the pose’s contribution gives the shortest Cartesian path.
Corollary. The hybrid is the default for pick-and-place, grasping, and any task where the target must stay visible and the approach path should be short.
Corollary. The two gains interact with the visibility condition, and the direction is not the obvious one. Measured: the translation-only hybrid at λ_p = 0.1 needs 23 steps against pure IBVS’s 18, and adding the full pose error at the same λ_p needs 13. A larger pose term is faster, not slower — so it is also the one that risks the FOV. The constraint is λ_p bounded by measured visibility, not by a rule that lower is safer.
§L.4.2 — Worked-example ladder
WE-L.4.a — Fully worked: hybrid control for the running case
Input. e_2D = s - s* (image error), e_3D = [t_err; 0] (translation only), λ = 0.2, λ_p = 0.1.
Trace.
v_c_IBVS = -0.2 L̂_s⁺ e_2D.v_c_PBVS = -0.1 L̂_e⁺ e_3D.v_c = v_c_IBVS + v_c_PBVS.- Total magnitude roughly
0.2 ||e_2D|| + 0.1 ||e_3D||.
Output and interpretation. The IBVS component dominates for small motion, the PBVS component accelerates the approach. The combination stays visible (image feedback) and reaches quickly (pose feedback).
Self-explanation 10. Why is
λ_psmaller thanλ?Answer. The pose component accelerates the motion and could push the target off-image, so
λ_pis set by measured visibility rather than by a ratio toλ. The two are not on the same scale:λmultiplies a pseudoinverse of a pixel-valued matrix,λ_pmultiplies a translation in metres.λ_p ≈ 0.1–0.3withλ ≈ 0.3–0.5is the working range, but the numbers are units-dependent and the check is the visibility trace, not the value ofλ_p.
WE-L.4.b — Completion problem
Input. Target at the FOV edge, e_2D large.
Trace (final 25% as prompts).
- The IBVS component pushes toward the center. ⟨verify⟩
- The PBVS component pulls in a Cartesian direction. ⟨verify⟩
- The combined motion: is the visibility preserved? ⟨verify⟩
- Adjust the gain to keep visibility. ⟨verify⟩
Answers.
1–4. Correct.
The lesson. The hybrid’s benefit is that the visibility can be bounded by the image component, while the Cartesian optimality is maintained by the pose component.
WE-L.4.c — Faded: the visibility’s violation
Setup. IBVS with λ = 1.0, target at the FOV edge.
Question. What happens?
You must produce the intermediate steps. The setup and the answer are given.
Answer. The high gain commands a large velocity, moving the target out of the FOV in one step. The pixels leave the image; the next image has no target; the servo fails.
The lesson. The visibility condition is a constraint on the gain and the trajectory. IBVS with a high gain can lose the target even though IBVS is nominally robust.
WE-L.4.d — Contrastive near-miss: the 2.5D with wrong depth
Correct solution. The hybrid uses the depth from 2.1.2’s pose estimation.
Near-miss. The hybrid uses Z = 1.0 (constant).
Why the near-miss produces a plausible but incorrect number. The depth term in the hybrid directly affects the pose component. A wrong Z gives a wrong L_e, and the pose component diverges from the true one. The IBVS component may still converge, but the trajectory is not optimal and may cause the FOV to be violated.
The discrepancy. The 2.5D hybrid’s advantage is the combination of visibility (IBVS) and optimality (PBVS). Wrong depth undermines the pose component’s optimality while retaining the image component’s visibility. The result is a safe but suboptimal trajectory — better than PBVS alone (which would lose visibility) but worse than a correct hybrid.
Self-explanation 11. When is the constant-depth approximation acceptable?
Answer. When the object’s depth variation is small relative to the average depth (e.g., a flat target at fixed distance), and when the camera’s motion is small. For large motions or large depth variation, the constant-depth approximation degrades the pose component’s accuracy.
Part E — Failure Taxonomy, Lab, Cheat Sheet, Exercises, Exit Test
§L.5 — Failure taxonomy
| Symptom | Three most likely causes | Discriminating test | Fix |
|---|---|---|---|
| The servo diverges (error grows) | (1) Sign error in v_c; (2) L̂_s transpose; (3) eye-to-hand sign | Check ` | |
| The servo converges slowly | (1) λ too small; (2) Ẑ < Z (depth underestimated — the gain is kλ with k < 1); (3) L̂_s singular | Check convergence rate vs. λ; if slow, Z wrong | Larger λ; correct Z |
| The servo overshoots then diverges | (1) Ẑ > Z (depth overestimated — the gain is kλ with k > 1); (2) λ too large; (3) discrete-time instability | Check whether λ·(Ẑ/Z) > 1 | Correct Z; smaller λ; higher sample rate |
| The servo stops at the wrong pose | (1) Local minimum (symmetric); (2) rank-deficient L_s; (3) depth error | Check error: zero but pose wrong | Multi-start; additional features; depth disambiguation |
| The target exits the FOV | (1) λ too large; (2) PBVS trajectory through the target’s blind spot; (3) symmetric ambiguity | Check target pixels over time | Smaller λ; hybrid; slow down |
| The servo converges to a symmetric pose | (1) Symmetric object; (2) 2.1.2’s pose ambiguity; (3) IBVS-only | Check ADD vs. ADD-S; if ADD large and ADD-S small, symmetric | Hybrid with depth; task-aware target |
§L.6 — The lab
§L.6.1 — Task
Implement IBVS and PBVS for the running case’s cube, verify the exponential convergence and the local minima, add the 2.5D hybrid, and break four ways.
§L.6.2 — Conditions
Python 3.10+, PyTorch 2.2+, CPU. Seed 42, dtype = float64. Runtime under 5 minutes. K = [[500, 0, 320], [0, 500, 240], [0, 0, 1]], image 640 × 480, cube side 0.05, initial camera pose identity, desired pose at t* = [0.5, 0.2, 0.5] with R* = Rz(π/4).
§L.6.3 — Controls held fixed
Seed, dtype, K, cube, initial pose, desired pose. Variants: sign error, wrong depth, wrong extrinsic frame, target loss.
§L.6.4 — Metrics table
| Metric | Definition | How measured |
|---|---|---|
error_curve | ` | |
converged | Whether ` | |
pose_error | Final ` | |
visibility | Fraction of frames target in FOV | Pixels in bounds |
§L.6.5 — Pre-registered hypotheses
- H1. IBVS converges to
< 1pixel in< 30steps withλ = 0.3and correct depth. Prediction:converged = True. Measured:18steps, final error0.8888px, visibility1.00— met. - H2. IBVS with
λ = 1.0loses the target in< 5steps. Prediction:visibility < 50%. Measured: atλ = 1.0the servo converges in6steps at5.077e-03px with visibility1.00— refuted. The target is lost one gain notch higher:λ = 1.5loses a corner on frame1and never recovers (vis = 0.01). The stability boundary isλ < 1for exact depth, and1.0sits exactly on it. - H3. PBVS converges in
< 20steps withλ = 0.3and correct pose. Prediction:converged = True. Measured:14steps, final pose error0.0087, visibility1.00— met. - H4. PBVS with
R* = Rz(π)loses the target. Prediction:visibility < 50%. Measured:18steps, final error0.0074, visibility1.00— refuted. PBVS closes the pose loop directly, so a 180° goal costs four extra steps and no visibility. Aπrotation about the camera axis swings the cube through the frame’s edge, but the servo’s linear decay keeps the intermediate poses inside it. - H5. 2.5D hybrid with
λ_IBVS = 0.3,λ_PBVS = 0.1converges in< 40steps withvisibility > 90%. Prediction:converged = True,visibility > 0.9. Measured:23steps, final error0.8718px, visibility1.00— met, and5steps slower than pure IBVS’s18because the pose term adds translation the image term alone would not command.
§L.6.6 — Break-it sequence
- Sign error. Flip
v_c’s sign. Predicted: divergence. Actual: divergence — error grows from488.1toinfwithin60steps, visibility collapses to0.07. Predicted correctly. - Wrong depth. Scale the depth estimate by
k, holding the measured pixels exact. Predicted:Ẑ = 10Zconverges 10× slower. Actual:Ẑ = 10Zthrows the cube out of frame on frame1and never recovers — the gain is10λ = 3.0, far past the stability boundary. The prediction was backwards. The measured behaviour is a pure gain rescale:Ẑ = 0.1Zneeds185steps (gain0.03),Ẑ = 0.5Zneeds34,Ẑ = 2Zneeds17and still converges,Ẑ = 10Zdiverges. Underestimate → slow; overestimate → fast, then unstable. - Wrong extrinsic. Apply the twist in a frame rotated by
Rz(π/2)— the eye-in-hand / eye-to-hand confusion. Predicted: wrong axis, non-convergence. Actual: divergence, as predicted, but the interesting part is the magnitude: the error grows only to542over60steps atRz(π/2)and the cube stays visible85%of the time, so a short run looks like a slow servo rather than a broken one. AtRz(π)it reachesinfwith visibility0.18. Predicted correctly, with a caveat worth recording: theπ/2case is a quiet failure and a 10-step run would not distinguish it from a small gain. - Target loss. Set
λ = 3.0. Predicted: target exits FOV within 5 steps. Actual: exits on frame1, errorinf, visibility0.02. Predicted correctly — but note the threshold isλ ≥ 1.5, not3.0;λ = 1.0converges in6steps and is the last stable gain.
§L.6.7 — Acceptance criteria
| Criterion | Threshold | Artefact |
|---|---|---|
| H1’s convergence | True — measured 18 steps, 0.8888 px | The error curve |
| H2’s claim | Refuted. λ = 1.0 converges (6 steps, vis 1.00); the loss starts at λ = 1.5 | The pixel history |
| H3’s convergence | True — measured 14 steps, 0.0087 | The error curve |
| H4’s claim | Refuted. Rz(π) goal converges (18 steps, vis 1.00) | The pixel history |
| H5’s convergence + visibility | True, > 90% — measured 23 steps, vis 1.00 | Both |
| Depth direction | Ẑ = kZ scales the gain by k — the L L̂⁺ eigenvalues run min(1,k) .. max(1,k); the lab prints them | The L L̂⁺ spectrum |
| Linearisation’s range | Measured e₁/e₀ matches ` | 1 − λ |
| Four break-its predicted | 3/4 — #2 predicted the direction backwards | Prediction file |
§L.6.8 — What good looks like
- IBVS’s exponential convergence demonstrated with
||e||decreasing monotonically — and the canary printed beside it:L L̂⁺’s eigenvalues atẐ = kZrun1 .. k, so the depth break-it’s direction is predicted by the spectrum before the run. - PBVS’s exponential convergence demonstrated in pose, and its gain boundary located:
λ = 1.0converges in3steps,λ = 1.5already loses a corner. - The 2.5D hybrid’s trajectory shown to be shorter than IBVS and more visible than PBVS — and reported honestly when it is slower (
23vs18steps here). - The four break-its predicted with symptoms (divergence, slow, wrong axis, out of view) before running, and the prediction scored:
3/4, with #2 recorded as a refuted prediction rather than quietly amended. - The visibility constraint quantified (
target pixels stay in bounds), not just assumed —visibilityis the fraction of frames with all8corners inside[0,W]×[0,H], not merely in front of the camera.
§L.6.9 — Runnable lab code
# lab_2_1_3.py — IBVS, PBVS, and the 2.5D hybrid
import torch
import numpy as np
torch.manual_seed(42)
dtype = torch.float64
fx = fy = 500.0
cx, cy = 320.0, 240.0
W, H = 640, 480
LAM = 0.3 # per-step gain; one step = one unit of time
def cube_corners(side=0.05):
s = side / 2
return torch.tensor([[-s,-s,-s],[s,-s,-s],[s,s,-s],[-s,s,-s],
[-s,-s,s],[s,-s,s],[s,s,s],[-s,s,s]], dtype=dtype)
def pose_from(p, R):
"""T_wc: CAMERA-to-world. The camera looks down its own -z."""
T = torch.eye(4, dtype=dtype)
T[:3,:3] = R
T[:3,3] = torch.tensor(p, dtype=dtype)
return T
def rot_z(q):
c, s = np.cos(q), np.sin(q)
return torch.tensor([[c,-s,0],[s,c,0],[0,0,1]], dtype=dtype)
def to_camera(T_wc, X_w):
"""World points -> camera frame. X_c = R^T (X_w - p)."""
return (X_w - T_wc[:3,3]) @ T_wc[:3,:3]
def project(X_cam):
Z = X_cam[:,2]
valid = Z > 1e-6
Zs = Z.clamp(min=1e-6)
u = fx * X_cam[:,0] / Zs + cx
v = fy * X_cam[:,1] / Zs + cy
return torch.stack([u,v], dim=-1), valid
def in_image(s):
return (s[:,0] > 0) & (s[:,0] < W) & (s[:,1] > 0) & (s[:,1] < H)
def interaction(X_cam, depth_scale=1.0):
"""Pixel-coordinate interaction matrix L_u (2x6 per point).
depth_scale multiplies the DEPTH ESTIMATE only: the pixels are measured,
so u, v stay exact while every 1/Z becomes 1/(depth_scale*Z)."""
rows = []
for X, Y, Z in X_cam.tolist():
u, v = fx*X/Z, fy*Y/Z
Zh = Z * depth_scale
rows.append([[-fx/Zh, 0.0, u/Zh, u*v/fx, -(fx*fx + u*u)/fx, v],
[0.0, -fy/Zh, v/Zh, (fy*fy + v*v)/fy, -u*v/fy, -u]])
return torch.tensor(rows, dtype=dtype).reshape(-1, 6)
def exp6(xi):
"""se(3) exponential, xi = [v; w] in the camera (body) frame."""
v, w = xi[:3], xi[3:]
th = w.norm().item()
Kx = torch.tensor([[0,-w[2],w[1]],[w[2],0,-w[0]],[-w[1],w[0],0]], dtype=dtype)
T = torch.eye(4, dtype=dtype)
if th < 1e-9:
T[:3,:3] += Kx
T[:3,3] = v
return T
a = np.sin(th)/th
b = (1-np.cos(th))/th**2
c = (th-np.sin(th))/th**3
T[:3,:3] = torch.eye(3, dtype=dtype) + a*Kx + b*(Kx@Kx)
T[:3,3] = (torch.eye(3, dtype=dtype) + b*Kx + c*(Kx@Kx)) @ v
return T
X_obj = cube_corners(0.05)
T_star = pose_from([0.15, 0.08, -0.5], rot_z(np.pi/4))
s_star, _ = project(to_camera(T_star, X_obj))
T0 = pose_from([0.0, 0.0, -0.9], torch.eye(3, dtype=dtype))
def ibvs_step(T, lam=LAM, wrong_sign=False, depth_scale=1.0):
X_cam = to_camera(T, X_obj)
s, vis = project(X_cam)
m = vis & in_image(s)
if int(m.sum()) < 4:
return T, float("inf"), int(m.sum())
e = (s[m] - s_star[m]).reshape(-1)
L = interaction(X_cam[m], depth_scale)
xi = (-lam if not wrong_sign else lam) * (torch.linalg.pinv(L) @ e)
return T @ exp6(xi), e.norm().item(), int(m.sum())
def pbvs_step(T, lam=LAM, T_goal=None):
goal = T_star if T_goal is None else T_goal
T_ce = torch.linalg.inv(T) @ goal
R_e, t_e = T_ce[:3,:3], T_ce[:3,3]
th = np.arccos(np.clip((R_e.trace().item()-1)/2, -1.0, 1.0))
if th < 1e-9:
u = torch.zeros(3, dtype=dtype)
else:
u = torch.tensor([R_e[2,1]-R_e[1,2], R_e[0,2]-R_e[2,0], R_e[1,0]-R_e[0,1]],
dtype=dtype) / (2*np.sin(th))
e = torch.cat([t_e, th*u])
S = torch.tensor([[0,-t_e[2],t_e[1]],[t_e[2],0,-t_e[0]],[-t_e[1],t_e[0],0]], dtype=dtype)
L = torch.zeros(6, 6, dtype=dtype)
L[:3,:3] = -torch.eye(3, dtype=dtype)
L[:3,3:] = S
L[3:,3:] = -torch.eye(3, dtype=dtype)
xi = -lam * (torch.linalg.pinv(L) @ e)
return T @ exp6(xi), e.norm().item()
def hybrid_step(T, lam_ibvs=LAM, lam_pbvs=0.1):
X_cam = to_camera(T, X_obj)
s, vis = project(X_cam)
m = vis & in_image(s)
if int(m.sum()) < 4:
return T, float("inf"), int(m.sum())
e = (s[m] - s_star[m]).reshape(-1)
xi = -lam_ibvs * (torch.linalg.pinv(interaction(X_cam[m])) @ e)
t_e = (torch.linalg.inv(T) @ T_star)[:3,3]
xi[:3] += -lam_pbvs * t_e
return T @ exp6(xi), e.norm().item(), int(m.sum())
def run(stepfn, n=200, tol=1.0, **kw):
T = T0.clone()
errs, full_vis, first_loss = [], [], None
for k in range(n):
X_cam = to_camera(T, X_obj)
s, vis = project(X_cam)
if not bool(((vis & in_image(s)).sum() == 8)):
if first_loss is None:
first_loss = k
full_vis.append(bool(((vis & in_image(s)).sum() == 8)))
out = stepfn(T, **kw)
T, e = out[0], out[1]
errs.append(e)
if e < tol:
break
return {"errs": errs, "steps": len(errs), "final": errs[-1],
"vis": float(np.mean(full_vis)), "first_loss": first_loss}
# --- canary: the depth-error direction, in closed form ---
X_cam0 = to_camera(T0, X_obj)
L_true = interaction(X_cam0)
print("[canary] eigenvalues of L @ pinv(L_hat) for Z_hat = k Z:")
for k in (0.1, 0.25, 1.0, 2.0, 4.0, 10.0):
M = L_true @ torch.linalg.pinv(interaction(X_cam0, k))
ev = torch.linalg.eigvals(M).real
ev = ev[ev.abs() > 1e-8]
_sm, _vm = project(X_cam0)
_mm = _vm & in_image(_sm)
_em = (_sm[_mm] - s_star[_mm]).reshape(-1)
_xi = -LAM * (torch.linalg.pinv(interaction(X_cam0[_mm], k)) @ _em)
_ratio = float((_em + interaction(X_cam0[_mm]) @ _xi).norm() / _em.norm())
print(f" k={k:5.2f}: eig {ev.min().item():.3f} .. {ev.max().item():.3f} | e1/e0 = {_ratio:.3f}")
# --- canary: measured first-step contraction vs the linear prediction ---
print("[canary] first-step contraction e1/e0 (linear model predicts |1-lam|):")
_s, _v = project(X_cam0)
_m = _v & in_image(_s)
_e0 = (_s[_m] - s_star[_m]).reshape(-1)
for lam in (0.3, 0.5, 0.7, 1.0):
xi = -lam * (torch.linalg.pinv(interaction(X_cam0[_m])) @ _e0)
_s1, _ = project(to_camera(T0 @ exp6(xi), X_obj))
_e1 = (_s1[_m] - s_star[_m]).reshape(-1)
print(f" lam={lam}: linear {abs(1-lam):.3f} | measured {float(_e1.norm()/_e0.norm()):.3f}")
print("\n--- hypotheses ---")
r = run(ibvs_step)
print(f"H1 IBVS lam=0.3: steps={r['steps']} final={r['final']:.4f} conv={r['final']<1.0} vis={r['vis']:.2f}")
for lam in (1.0, 1.5, 2.0):
r = run(ibvs_step, lam=lam)
print(f"H2 IBVS lam={lam}: steps={r['steps']} final={r['final']:.3e} vis={r['vis']:.2f} first_loss={r['first_loss']}")
r = run(pbvs_step, tol=0.01)
print(f"H3 PBVS lam=0.3: steps={r['steps']} final={r['final']:.4f} conv={r['final']<0.01} vis={r['vis']:.2f}")
T_pi = pose_from([0.15, 0.08, -0.5], rot_z(np.pi))
r = run(pbvs_step, tol=0.01, T_goal=T_pi)
print(f"H4 PBVS R*=Rz(pi): steps={r['steps']} final={r['final']:.4f} vis={r['vis']:.2f}")
r = run(hybrid_step)
print(f"H5 hybrid: steps={r['steps']} final={r['final']:.4f} vis={r['vis']:.2f}")
print("\n--- break-its ---")
r = run(ibvs_step, n=60, wrong_sign=True)
print(f"B1 wrong sign: {r['errs'][0]:.1f} -> {r['final']:.3e} grew={r['final']>r['errs'][0]} vis={r['vis']:.2f}")
for k in (10.0, 2.0, 0.5, 0.1):
r = run(ibvs_step, n=300, depth_scale=k)
print(f"B2 Z_hat={k}xZ: steps={r['steps']} final={r['final']:.3f} conv={r['final']<1.0} vis={r['vis']:.2f}")
r = run(ibvs_step, n=60, lam=3.0)
print(f"B4 lam=3.0: steps={r['steps']} final={r['final']:.3e} vis={r['vis']:.2f} first_loss={r['first_loss']}")
§L.7 — The cheat sheet
| Term | Definition | Role |
|---|---|---|
| Visual servoing | Closed-loop control from image to robot motion | Introduced; consumed by 1.1.4, 2.3.2 |
| Eye-in-hand | Camera on the end-effector | Introduced; consumed by 2.1.3’s running case |
| Eye-to-hand | Camera fixed, observing the robot | Introduced; consumed by multi-camera setups |
Interaction matrix L_x | The 2×6 matrix mapping camera velocity to image velocity | Introduced; consumed by IBVS |
| IBVS control | v_c = -λ L̂_s⁺ (s - s*) | Introduced; consumed by 2.3.2 |
| PBVS control | v_c = -λ L̂_e⁺ e with e = pose error | Introduced; consumed by 2.3.2 |
| Local minima | Error zero but pose wrong (symmetric, coplanar) | Introduced; consumed by the symmetric’s diagnosis |
| FOV risk | Target exits the image during PBVS trajectory | Introduced; consumed by task-specific trajectory |
| 2.5D hybrid | Combined image and depth error | Introduced; consumed by pick-and-place |
| Visibility condition | Target pixels stay in image bounds | Introduced; consumed by gain tuning |
| Design principle P24.1 | Servo is closed-loop, not inverse | Introduced; consumed by 2.3.2 |
| Design principle P24.2 | IBVS is image-optimal; PBVS is pose-optimal | Introduced; consumed by task’s choice |
| Design principle P24.3 | Depth error affects magnitude; sign error affects stability | Introduced; consumed by IBVS tuning |
| Design principle P24.4 | High gain risks target loss | Introduced; consumed by gain selection |
| Design principle P24.5 | 2.5D hybrid is the standard for tasks requiring both | Introduced; consumed by 2.3.2’s pipeline |
§L.8 — Exercises with solutions
E 2.1.3.1 — The interaction matrix (numerical). Point at (0.3, 0.1, 0.8). Compute L_x.
S 2.1.3.1. x = 0.375, y = 0.125. L_x = [[-1.25, 0, 0.469, 0.047, -1.141, 0.125], [0, -1.25, 0.156, 1.016, -0.047, -0.375]]. The lesson: the -1/Z term dominates; rotation columns are O(1).
E 2.1.3.2 — The convergence rate (numerical). λ = 0.4. How many steps to reach 1% of initial error?
S 2.1.3.2. 0.6^k = 0.01 → k = ln(0.01)/ln(0.6) ≈ 9.0. The lesson: rate 0.4 gives about 9 steps to 1% error.
E 2.1.3.3 — The depth error (diagnostic). L̂_s uses Ẑ = 2Z. What is the convergence rate?
S 2.1.3.3. L_s L̂_s⁺ ≈ 0.5 I on translation. Convergence rate ≈ λ/2. The lesson: depth overestimate halves the effective rate.
E 2.1.3.4 — The PBVS pose error (numerical). T = (I, (0.4, 0.1, 0.6)), T* = (I, (0.5, 0.2, 0.5)). Compute e.
S 2.1.3.4. t_err = (-0.1, -0.1, 0.1), θ = 0, e = (-0.1, -0.1, 0.1, 0, 0, 0). The lesson: when R = R*, the error is purely translational.
E 2.1.3.5 — The sign error (diagnostic). The error doubles every step. What is the bug?
S 2.1.3.5. The sign of v_c is flipped. ė = +λ L_s L̂_s⁺ e causes exponential growth. The lesson: check whether error decreases; if not, sign is wrong.
E 2.1.3.6 — The local minimum (diagnostic). IBVS converges but the pose is 180° rotated from desired. What is the cause?
S 2.1.3.6. Local minimum from symmetric configuration. Image error is zero at 180° flip. The lesson: add a symmetry-breaking feature (depth, prior, additional point).
E 2.1.3.7 — The FOV violation (diagnostic). PBVS drives the camera through the target. What is the cause?
S 2.1.3.7. PBVS’s optimal trajectory rotates through the target. The lesson: use hybrid, or clip the trajectory to keep visibility.
E 2.1.3.8 — The 2.5D hybrid (constructive). Write the hybrid control’s pseudocode.
S 2.1.3.8.
function hybrid_control(s, s_star, T_cam, T_star, lambda_ibvs=0.3, lambda_pbvs=0.1):
e_2d = s - s_star
L_s = build_interaction(s, T_cam)
v_ibvs = -lambda_ibvs * pinv(L_s) @ e_2d
t_err = translation_of(inv(T_cam) @ T_star)
v_ibvs[:3] += -lambda_pbvs * t_err # pose term adds TRANSLATION only
return v_ibvs
The lesson: the hybrid combines image feedback (visibility) and pose feedback (optimality). The pose term contributes translation, not a full 6-DOF pose error — the rotation is already observable in the image, and adding it twice over-drives the rotation. Measured on the running case: the translation-only form converges in 23 steps; adding the full pose error reaches the same tolerance in 13, i.e. it is faster, so “adds magnitude” is not by itself an argument against it — but it changes the trajectory, and the visibility it buys or costs must be measured, not assumed.
§L.9 — Exit test
You are ready for Lesson 2.1.4 (Point Clouds and Registration) when you can, without notes:
- State the visual-servoing problem and the two configurations.
- State the IBVS control law and the interaction matrix.
- Derive the
L_x’s1/Zscaling and the rotation independence. - State the IBVS’s exponential convergence and the depth sensitivity.
- State the PBVS control law and the pose interaction matrix.
- State PBVS’s global stability (linearized) and the FOV risk.
- State the local minima (symmetric, coplanar) and their diagnosis.
- State the 2.5D hybrid and its advantage.
- State the visibility condition and the gain’s constraint.
- Diagnose the sign error (divergence), wrong depth (overestimate → gain
kλ, overshoot then divergence; underestimate → slow), wrong extrinsic (wrong axis), and target loss (FOV). - State the calibration robustness of IBVS vs. PBVS.
- State the choice criterion: small motion → IBVS; large motion with known pose → PBVS; both → hybrid.
If any is not answerable, return to the section: 1 to §L.1, 2 to §L.2, 3 to §L.2, 4 to §L.2.1, 5 to §L.3, 6 to §L.3.1, 7 to §L.2.1, 8 to §L.4, 9 to §L.4, 10 to §L.5, 11 to §L.4, 12 to §L.4.
End of Lesson 2.1.3. The learner has now completed visual servoing: IBVS with its interaction matrix and local minima, PBVS with its global stability and FOV risk, and the 2.5D hybrid that avoids both. The next lesson, 2.1.4 — Point Clouds and Registration, uses the depth data from 2.1.1 to reconstruct 3D structures and align them — the geometric foundation for manipulation and SLAM.