机械人的浪漫:嘉然为你画爱心(python实现)
Posted 可可卷
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了机械人的浪漫:嘉然为你画爱心(python实现)相关的知识,希望对你有一定的参考价值。
👨🎓 作者简介:大家好,我是可可卷,欢迎大家关注,一起学习交流 ~
📜主攻领域:【python算法】【数据分析】【数学建模】【机器学习】【深度学习】【数据可视化】
📖个人主页:可可卷的博客
文章目录
0 写在前面
🔥多图预警,请连WIFI!
全文图片较多,建议大家先收藏再阅读哦~
1 必要准备
安装依赖:
numpy
matplotlib
嘉⭐然:
2 生成爱❤心轨迹
计算爱❤心坐标
# 0.01为步长,步长越小,爱心曲线越逼真
x_ = np.arange(-1.15,1.16,0.01).astype(np.complex64)
y1 = (1/2*((x_**2)**(1/3)+((x_**4)**(1/3)-4*x_**2+4)**(1/2))).real
y2 = (1/2*((x_**2)**(1/3)-((x_**4)**(1/3)-4*x_**2+4)**(1/2))).real
画出爱❤心曲线
plt.plot(x_,y1) # 爱心上半部分
plt.plot(x_,y2) # 爱心下半部分
plt.show()
过滤烦人的警告
# 过滤烦人的warnings
import warnings
warnings.filterwarnings('ignore')
设置爱心为粉色
plt.plot(x_,y1,c='pink')
plt.plot(x_,y2,c='pink')
效果如下
3 放入嘉⭐然
ax=plt.subplot(111)
ax.plot(x_,y1)
ax.plot(x_,y2)
img=plt.imread('Diana.jpg')
im = OffsetImage(img,zoom=.520)
ab = AnnotationBbox(im, (0.520, 0.520), xycoords='axes fraction')
ax.add_artist(ab)
ax.figure.savefig("Diana.svg", transparent=True, dpi=600, bbox_inches="tight")
plt.show()
效果如下
眼尖的鼠鼠可能已经发现了,为什么
im = OffsetImage(img,zoom=.520)
ab = AnnotationBbox(im, (0.520, 0.520), xycoords='axes fraction')
参数里会出现520呢?
因为这是♥的坐标~
到目前为止,全部的代码如下
import numpy as np
from matplotlib import pyplot as plt
from matplotlib.offsetbox import OffsetImage, AnnotationBbox
# 过滤烦人的warnings
import warnings
warnings.filterwarnings('ignore')
x_ = np.arange(-1.15,1.16,0.01).astype(np.complex64)
y1 = (1/2*((x_**2)**(1/3)+((x_**4)**(1/3)-4*x_**2+4)**(1/2))).real
y2 = (1/2*((x_**2)**(1/3)-((x_**4)**(1/3)-4*x_**2+4)**(1/2))).real
ax=plt.subplot(111)
ax.plot(x_,y1,c='pink')
ax.plot(x_,y2,c='pink')
img=plt.imread('Diana.jpg')
im = OffsetImage(img,zoom=.520)
ab = AnnotationBbox(im, (0.520, 0.520), xycoords='axes fraction')
ax.add_artist(ab)
ax.figure.savefig("Diana.svg", transparent=True, dpi=600, bbox_inches="tight")
plt.show()
4 机械臂的概览
4.0 必要准备
import numpy as np
from matplotlib.offsetbox import OffsetImage, AnnotationBbox
from numpy import cos, sin, arccos, arctan2
import matplotlib.pyplot as plt
# 过滤烦人的warnings
import warnings
warnings.filterwarnings('ignore')
# 目标点
(target_x, target_y) = (0, 0)
# 爱心的x,y坐标
x_ = np.arange(-1.15,1.16,0.05).astype(np.complex64)
y1 = (1/2*((x_**2)**(1/3)+((x_**4)**(1/3)-4*x_**2+4)**(1/2))).real
y2 = (1/2*((x_**2)**(1/3)-((x_**4)**(1/3)-4*x_**2+4)**(1/2))).real
# 用于坐标迭代的生成器
sx=(float(x) for x in x_)
sy=(float(y) for y in y1)
flag=False
forever=True
# 记录机械臂arm2的坐标
dx,dy=[],[]
4.1 创建对象
# 创建对象,嘉然小姐~
class Diana:
def __init__(self):
pass
def update_arms(self, angles):
pass
# 前向运动
def forward_kinematics(self):
pass
# 反解位姿
def inverse_kinematic(self, x, y):
pass
def plot(self):
pass
def animation(self, x, y):
pass
def 画爱心(self):
pass
4.2 来到天堂
def Heaven():
嘉然 = Diana()
while forever:
嘉然.画爱心()
if __name__ == "__main__":
Heaven()
5 机械臂的细节
5.0 初始化对象
def __init__(self, angles=[0, 0]):
# 第0个关节固定坐标
self.arm0 = np.array([-0.1314, -0.520])
# 两段机械臂的长度
self.link_lengths = [1, 1]
# 机械臂初始角度
self.update_arms(angles)
这时又会有眼尖的鼠鼠提问了,老师老师,为什么arm0
的坐标是[-0.1314, -0.520]
呢?
别问,问就是♥的坐标
5.1 更新机械臂
def update_arms(self, angles):
self.arm_angles = angles
self.forward_kinematics()
5.2 前向运动
# 前向运动
def forward_kinematics(self):
# 计算关节1的位置(a0,a1分别是第0和第1个关节的关节角)
a0 = self.arm_angles[0]
l0 = self.link_lengths[0]
self.arm1 = self.arm0 + [l0 * cos(a0), l0 * sin(a0)]
# 计算关节2的位置
a1 = self.arm_angles[1]
l1 = self.link_lengths[1]
self.arm2 = self.arm1 + [l1 * cos(a0 + a1), l1 * sin(a0 + a1)]
5.3 逆向运动
# 反解位姿
def inverse_kinematic(self, x, y):
l0 = self.link_lengths[0]
l1 = self.link_lengths[1]
a1 = arccos((x ** 2 + y ** 2 - l0 ** 2 - l1 ** 2) / (2 * l0 * l1))
a0 = arctan2(y, x) - arctan2(l1 * sin(a1), l1 * cos(a1) + l0)
return [a0, a1]
5.4 轨迹作图
def plot(self):
# 清理坐标系中的内容
plt.cla()
# 显示嘉然小姐~
ax = plt.subplot(111)
ax.plot(dx[5:],dy[5:],zorder=5,c='pink')
img = plt.imread('Diana.jpg')
im = OffsetImage(img, zoom=0.520,zorder=0)
ab = AnnotationBbox(im, (0.5, 0.520), xycoords='axes fraction',frameon=False)
ax.add_artist(ab)
# 三个关节的坐标
x = [self.arm0[0], self.arm1[0], self.arm2[0]+0.1314]
y = [self.arm0[1], self.arm1[1], self.arm2[1]+0.520]
# 记录爱心坐标
if len(dx)<520:
dx.append(x[-1])
dy.append(y[-1])
# magic point
if dx[-1]==0.9761696712838817:
dx[-1]*=-1
dy[-1]=-0.04573089606848885
# 绘制机械臂
ax.plot(x, y, c="red", zorder=4)
# 绘制机械臂的关节
ax.scatter(x[1:], y[1:], c="black", marker='*',zorder=5)
# 固定坐标系
plt.xlim(-1.314, 1.314)
plt.ylim(-1.314, 1.314)
让我们来统计一下,这里面包含了多大的♥~
520 | 1个 |
---|---|
0.520 | 3个 |
0.1314 | 1个 |
1.314 | 4个 |
这是巧合吗?
这是♥捏~
5.5 绘制动作
def animation(self, x, y):
angles = self.inverse_kinematic(x, y)
# 分解N步慢动作
actions_num = 2
angles_per_action = (np.array(angles) - np.array(self.arm_angles)) / actions_num
for action_i in range(actions_num):
self.arm_angles = np.array(self.arm_angles) + angles_per_action
self.update_arms(self.arm_angles)
self.plot()
plt.pause(0.001) # 暂停的时间
5.6 画爱♥心
def 画爱心(self):
global target_x, target_y
global sx,sy,flag
try:
target_x = next(sx)
target_y = next(sy)
self.animation(target_x, target_y)
except:
if flag:
sx = (float(x) for x in x_)
sy = (float(y) for y in y1)
flag=False
else:
sx = (float(x) for x in reversed(x_))
sy = (float(y) for y in y2)
flag=True
6 全部代码
import numpy as np
from matplotlib.offsetbox import OffsetImage, AnnotationBbox
from numpy import cos, sin, arccos, arctan2
import matplotlib.pyplot as plt
# 过滤烦人的warnings
import warnings
warnings.filterwarnings('ignore')
# 目标点
(target_x, target_y) = (0, 0)
# 爱心的x,y坐标
x_ = np.arange(-1.15,1.16,0.05).astype(np.complex64)
y1 = (1/2*((x_**2)**(1/3)+((x_**4)**(1/3)-4*x_**2+4)**(1/2))).real
y2 = (1/2*((x_**2)**(1/3)-((x_**4)**(1/3)-4*x_**2+4)**(1/2))).real
# 用于坐标迭代的生成器
sx=(float(x) for x in x_)
sy=(float(y) for y in y1)
flag=False
forever=True
# 记录机械臂arm2的坐标
dx,dy=[],[]
# 嘉然小姐~
class Diana:
def __init__(self, angles=[0, 0]):
# 第0个关节固定坐标
self.arm0 = np.array([-0.1314, -0.520])
# 两段机械臂的长度
self.link_lengths = [1, 1]
# 机械臂初始角度
self.update_arms(angles)
def update_arms(self, angles):
self.arm_angles = angles
self.forward_kinematics()
# 前向运动
def forward_kinematics(self):
# 计算关节1的位置(a0,a1分别是第0和第1个关节的关节角)
a0 = self.arm_angles[0]
l0 = self.link_lengths[0]
self.arm1 = self.arm0 + [l0 * cos(a0), l0 * sin(a0)]
# 计算关节2的位置
a1 = self.arm_angles[1]
l1 = self.link_lengths[1]
self.arm2 = self.arm1 + [l1 * cos(a0 + a1), l1 * sin(a0 + a1)]
# 反解位姿
def inverse_kinematic(self, x, y):
l0 = self.link_lengths[0]
l1 = self.link_lengths[1]
a1 = arccos((x ** 2 + y ** 2 - l0 ** 2 - l1 ** 2) / (2 * l0 * l1))
a0 = arctan2(y, x) - arctan2(l1 * sin(a1), l1 * cos(a1) + l0)
return [a0, a1]
def plot(self):
# 清理坐标系中的内容
plt.cla()
# 显示嘉然小姐~
ax = plt.subplot(111)
ax.plot(dx[5:],dy[5:],zorder=5,c='pink')
img = plt.imread('Diana.jpg')
im = OffsetImage(img, zoom=0.520,zorder=0)
ab = AnnotationBbox(im, (0.5, 0.520), xycoords='axes fraction',frameon=False)
ax.add_artist(ab)
# 三个关节的坐标
x = [self.arm0[0], self.arm1[0], self.arm2[0]+0.1314]
y = [self.arm0[1], self.arm1[1], self.arm2[1]+0.520]
# 记录爱心坐标
if len(dx)<520:
dx.append(x[-1])
dy.append(y[-1])
# magic point
if dx[-1]==0.9761696712838817:
dx[-1]*=-1
dy[-1]=-0.04573089606848885
# 绘制机械臂
ax.plot(x, y, c="red", zorder=4)
# 绘制机械臂的关节
ax.scatter(x[1:], y[1:], c="black", marker='*',zorder=5)
# 固定坐标系
plt.xlim(-1.314, 1.314)
plt.ylim(-1.314, 1.314)
def animation(self, x, y):
angles = self.inverse_kinematic(x, y)
# 分解N步慢动作
actions_num = 2
angles_per_action = (np.array(angles) - np.array(self.arm_angles)) / actions_num
for action_i in range(actions_num):
self.arm_angles = np.array(self.arm_angles) + angles_per_action
self.update_arms(self.arm_angles)
self.plot()
plt.pause(0.001) # 暂停的时间
def 画爱心(self):
global target_x, target_y
global sx,sy,flag
try:
target_x = next(sx)
target_y = next(sy)
self.animation(target_x, target_y)
except:
if flag:
sx = (float(x) for x in x_)
sy = (float(y) for y in y1)
flag=False
else:
sx = (float(x) for x in reversed(x_))
sy = (float(y) for y in y2)
flag=True
def Heaven():
嘉然 = Diana()
while forever:
嘉然.画爱心()
if __name__ == "__main__":
Heaven()
效果如下
7 全是细节
7.0 好学的嘉心糖
作为一个好学的嘉心糖,在写完代码后自然要好好总结捏~
7.1 中文变量
python3是支持中文作为变量的哦,而且不需要引入任何库和第三方工具
大家可以测试以下代码,试试中文变量名的使用效果
love=True
嘉然=True
print(嘉然 is love)
''' 我的测试结果 '''
>>> love=True
>>> 嘉然=True
>>> 嘉然 is love
True
7.2 坐标迭代
-
因为我采用了分别画出上、下部分爱心再结合的方式,因此在坐标迭代部分需要通过
flag
对上、下部分爱心做一个标记 -
在具体迭代上,通过
(float(x) for x in x_)
构造生成器,通过next()
取出生成器的下一个元素当元素取尽时会抛出
StopIteration
异常,这时通过except重新构造新的生成器 -
大家可以通过下面的测试代码来体会生成器的用法~
>>> x=[1,2,3] >>> y=(i for i in x) >>> next(y) 1 >>> next(y) 2 >>> next(y) 3 >>> next(y) Traceback (most recent call last): File "<pyshell#7>", line 1, in <module> next(y) StopIteration >>>
7.3 zorder
matplotlib中轴的默认绘制顺序是补丁,线条,文本。 此顺序由zorder属性确定。 设置以下默认值
Artist | Z-order |
---|---|
Patch / PatchCollection | 1 |
Line2D / LineCollection | 2 |
Text | 3 |
类比PS,zorder可以看成一个控制图层上下顺序的参数
更多详细用法,可以查看官方文档:https://www.matplotlib.org.cn/gallery/misc/zorder_demo.html
8 写在后面
-
本文爱心曲线参考了@FrigidWinter大佬的博客📚工程师的浪漫:用机械臂画一个爱心
-
同时本文机械臂的逆运动学实现部分参考了GitHub项目:https://github.com/varyshare/easy_slam_tutorial/tree/master/joint_robot_simulation
-
更多机器人相关课程,详见:
https://robotacademy.net.au/lesson/inverse-kinematics-for-a-2-joint-robot-arm-using-geometry/
9 偷偷的说
如果把嘉然偷偷换成海子姐,会怎么样呢QAQ
以上是关于机械人的浪漫:嘉然为你画爱心(python实现)的主要内容,如果未能解决你的问题,请参考以下文章