解决相机与机器人坐标系的变换关系。
import numpy as np
def rot(ax,ang):
c,s=np.cos(ang),np.sin(ang); ax=ax/np.linalg.norm(ax)
x,y,z=ax; return np.array([[c+x*x*(1-c),x*y*(1-c)-z*s,x*z*(1-c)+y*s],[y*x*(1-c)+z*s,c+y*y*(1-c),y*z*(1-c)-x*s],[z*x*(1-c)-y*s,z*y*(1-c)+x*s,c+z*z*(1-c)]])
np.random.seed(42); Rx=rot([1,0,0],.1)@rot([0,1,0],-.05)@rot([0,0,1],.15)
tx=np.array([.05,-.03,.1]); X_true=np.eye(4); X_true[:3,:3]=Rx; X_true[:3,3]=tx
Xi=np.eye(4); Xi[:3,:3]=Rx.T; Xi[:3,3]=-Rx.T@tx
A_list,B_list=[],[]
for _ in range(15):
ax=np.random.randn(3); ax=ax/np.linalg.norm(ax)*np.random.uniform(.2,.8)
RA=rot(ax,np.random.uniform(.3,1.5)); tA=np.random.randn(3)*.05
A=np.eye(4); A[:3,:3]=RA; A[:3,3]=tA; B=Xi@A@X_true
B[:3,:3]+=np.random.randn(3,3)*.0005; B[:3,3]+=np.random.randn(3)*.005
U,_,Vt=np.linalg.svd(B[:3,:3]); B[:3,:3]=U@Vt
A_list.append(A); B_list.append(B)
M=np.zeros((4,4))
for i in range(len(A_list)):
qA=np.zeros(4); qA[3]=1; # 简化:使用旋转矩阵特征
# Kronecker法
n=len(A_list); Mk=np.zeros((9*n,9))
for i in range(n):
RA=A_list[i][:3,:3]; RB=B_list[i][:3,:3]
Mk[i*9:(i+1)*9]=np.kron(RA,np.eye(3))-np.kron(np.eye(3),RB.T)
_,_,Vt=np.linalg.svd(Mk); RX=Vt[-1].reshape(3,3)
U2,_,Vt2=np.linalg.svd(RX); RX=U2@Vt2
if np.linalg.det(RX)<0: RX=-RX
Ct,dt_arr=[],[]
for i in range(n):
Ct.append(A_list[i][:3,:3]-np.eye(3)); dt_arr.append(RX@B_list[i][:3,3]-A_list[i][:3,3])
Ct=np.vstack(Ct); dt_arr=np.concatenate(dt_arr)
tX,_,_,_=np.linalg.lstsq(Ct,dt_arr,rcond=None)
X_est=np.eye(4); X_est[:3,:3]=RX; X_est[:3,3]=tX
R_err=np.arccos(np.clip((np.trace(X_true[:3,:3].T@X_est[:3,:3])-1)/2,-1,1))
t_err=np.linalg.norm(X_est[:3,3]-X_true[:3,3])
print("手眼标定"); print("="*60)
print(f"旋转误差:{np.degrees(R_err):.3f}° 平移误差:{t_err*1000:.3f}mm")
me=0
for i in range(n):
e=np.linalg.norm(A_list[i]@X_est-X_est@B_list[i]); me=max(me,e)
print(f"AX=XB最大误差:{me:.6f}")import numpy as np
np.random.seed(42)
print("感知系统噪声鲁棒性")
for sigma in [0.5,1.0,2.0,5.0]:
K=np.array([[500,0,320],[0,500,240],[0,0,1]]); n=8
pts3d=np.random.rand(n,3)*0.2; R=np.eye(3); t=np.array([0,0,1.])
pc=(R@pts3d.T).T+t; p2d=(K@pc.T).T; p2d=p2d[:,:2]/p2d[:,2:3]
noisy=p2d+np.random.randn(*p2d.shape)*sigma
proj_err=np.mean(np.linalg.norm(noisy-p2d,axis=1))
print(f" σ={sigma:.1f}px: 2D误差={proj_err:.2f}px, 3D误差≈{proj_err/500*1000:.1f}mm")感知鲁棒性取决于特征质量、观测数量和噪声水平。工程中常用RANSAC等鲁棒估计方法。
本课程按"运动学→动力学→力控制→感知规划→实战项目"组织,每课都建立在前面知识基础上。理解本课后,你将:
完成本课后,你应该能够:
完成练习后,继续下一课的学习。本课内容将在后续课程中被反复使用和扩展。
针孔相机模型将3D世界投影到2D图像:
其中K是内参矩阵(焦距、主点、畸变),[R|t]是外参(位姿)。从2D恢复3D是欠约束问题,需要额外的约束(多个视角、已知3D模型、深度传感器)。
PRM和RRT是两类经典的采样式规划算法:
采样式方法的成功取决于构型空间的维度和障碍物的几何复杂度。
深度学习极大地改变了机器人感知:
挑战:标注数据需求大、泛化能力有限、实时性要求高。合成数据+域适应是当前的解决方案。
本课涉及的技术在以下场景中有重要应用:
理解理论与实践的差距,是成为优秀机器人工程师的关键。
仿真验证的算法通常需要以下适配才能部署到实际机器人:参数标定、实时性优化、异常处理、安全保护。仿真与现实的差距(sim-to-real gap)是机器人领域的重要挑战。
Python适合算法验证和原型开发,但生产级控制器通常用C++实现。Python的NumPy/SciPy计算效率约为C++的1/10到1/100,对于1kHz控制频率可能不够。可以使用Cython、Numba或直接C++重写来加速。
推荐步骤:(1) Python仿真验证算法正确性;(2) 添加传感器噪声和延迟模型测试鲁棒性;(3) 用C++重写核心计算模块;(4) 在低速度下实际测试;(5) 逐步提高速度和负载。安全永远是第一位的。
| 术语 | 英文 | 定义 |
|---|---|---|
| 正运动学 | Forward Kinematics | 已知关节角求末端位姿 |
| 逆运动学 | Inverse Kinematics | 已知末端位姿求关节角 |
| 雅可比矩阵 | Jacobian Matrix | 关节速度到末端速度的映射 |
| 奇异位形 | Singularity | 末端失去某些方向运动能力的位形 |
| 工作空间 | Workspace | 末端可达的空间区域 |
| 自由度 | Degrees of Freedom | 独立运动变量的数量 |
| 齐次变换 | Homogeneous Transform | 4x4矩阵表示位姿 |
| 阻抗控制 | Impedance Control | 控制力与位移的动态关系 |
| 导纳控制 | Admittance Control | 输入力输出位移修正 |
| 轨迹规划 | Trajectory Planning | 生成平滑的运动时间函数 |
| 力封闭 | Force Closure | 接触力可抵抗任意外力 |
| 碰撞检测 | Collision Detection | 判断几何体是否相交 |
| 路径规划 | Path Planning | 在障碍物间找到安全路径 |
| 视觉伺服 | Visual Servoing | 基于视觉反馈的运动控制 |
| 手眼标定 | Hand-Eye Calibration | 确定相机与机器人坐标系的变换 |
解决AX=XB经典问题!