WARNING
🧪 Beta公测版本提示:教程主体已完成,正在优化细节,欢迎大家提Issue反馈问题或建议。
旋量代数 — demo.py 代码详解
运行方式
bash
cd docs/robotics/screw/code
python demo.pyCPU、NumPy 即可。一张图 screw_se2.png:小矩形沿固定螺旋「拧」七帧,颜色从 viridis 一头走到另一头;黑叉是瞬心
代码逐段详解
第1步:se2_exp —
python
def se2_exp(omega, vx, vy, t=1.0):
if abs(omega) < 1e-10:
return np.array([
[1.0, 0.0, vx * t],
[0.0, 1.0, vy * t],
[0.0, 0.0, 1.0],
])
w = omega * t
c, s = np.cos(w), np.sin(w)
trans = (1.0 / omega) * np.array([[s, -(1 - c)], [1 - c, s]]) @ np.array([vx, vy])
T = np.eye(3)
T[0, 0], T[0, 1], T[0, 2] = c, -s, trans[0]
T[1, 0], T[1, 1], T[1, 2] = s, c, trans[1]
return T:齐次阵就是平移 ,左上 为单位。阈值 ,与李群章小角度同一理由。 w = omega * t:有限转角。是沿螺旋走了多久,不是「另写一个角度」。 - 平移块:
。这是 的闭式,除的是 不是 。写成 1/w会错。 @ np.array([vx, vy]):2×2 乘二维速度。- 左上旋转
:与 线性代数 平面旋转相同。第三行 [0,0,1]来自eye(3)没改。 - 逐元素赋值
T[0,0], T[0,1], T[0,2] = ...:元组解包写进一行,避免再拼一个np.array切片广播出错。
返回
第2步:apply — 齐次坐标
python
def apply(T, pts):
h = np.c_[pts, np.ones(len(pts))]
return (T @ h.T).T[:, :2]np.c_[pts, ones]:在右边拼一列, pts是(N,2)→(N,3)。T @ h.T:(3,3)@(3,N),再转置,丢掉齐次,剩 。 - 为什么不直接
pts @ R.T + t? 可以,但指数映射给的就是,一次乘更贴 的写法。
第3步:纯转动的 (约定要记死)
python
omega = 1.2
q = np.array([0.8, 0.2])
v = omega * np.array([q[1], -q[0]])瞬心在 planar_v 必须返回
若改成
第4步:七帧矩形
python
square = np.array([
[0.15, 0.1], [0.35, 0.1], [0.35, 0.28], [0.15, 0.28], [0.15, 0.1],
])
for i, t in enumerate(np.linspace(0, 1.6, 7)):
T = se2_exp(omega, v[0], v[1], t)
p = apply(T, square)
ax.plot(p[:, 0], p[:, 1], color=plt.cm.viridis(i / 6), lw=2)- 五个点:闭合折线(首尾重复
)。四个顶点不够闭合。 linspace(0, 1.6, 7):含原位。 弧度级转角,看得到拧过去,又没叠成一团。 viridis(i/6):colormap 吃。 帧分母是 。 enumerate给。 scatter(*q, marker='x'):瞬心。矩形应绕叉转,不要绕原点转——若写错成 ,所有帧会绕原点。
set_aspect('equal') 否则转圈变椭圆,螺旋看起来假。
关键概念速查表
| 概念 | 数学 / 直觉 | 代码 |
|---|---|---|
| twist 平面 | se2_exp 参数 | |
| 纯平移 | 函数前半分支 | |
trans = (1/omega)*... | ||
| 齐次 | np.eye(3) 再填 | |
apply | np.c_ 拼 | |
| 瞬心 | 纯转中心 | 黑叉 |
v = omega * [q[1], -q[0]] | ||
viridis | 帧着色 | i / 6 |
| 闭合多边形 | 首尾同点 | square 五行 |
源码位置
clone 后打开(相对仓库根目录):
docs/robotics/screw/code/demo.py