python机器人编程——一种3D骨架动画逆解算法的启示(上)
目录
- 一、前言
- 二、fabrik 算法
- 三、python实现
- 结论
- PS.扩展阅读
- ps1.六自由度机器人相关文章资源
- ps2.四轴机器相关文章资源
- ps3.移动小车相关文章资源
- ps3.wifi小车控制相关文章资源
一、前言
我们用blender等3D动画软件时,会用到骨骼的动画,通过逆向IK动力学,可以实现控制少量点,就能控制一个复杂的骨架运动。这种IK动力学几乎是实时的,非常的高效。这种IK动力学算法的代表是fabrik 算法,该算法应被用于UE虚幻引擎、Unity等3D软件中。大至效果是,可以实现骨架的目标跟随,而且几乎是“实时”的:
这种感觉,不就是机械臂的虚拟拖拽吗?是否可以给机械臂的逆解,或者是虚拟化示教带来一些启发,是一个有意思的应用。本篇先来初步研究一下fabrik 算法。
二、fabrik 算法
该算法在文章 FABRIK: A fast, iterative solver for the Inverse Kinematics problem中有详细说明,FABRIK算法是一种用于解决逆运动学问题的启发式方法。它通过迭代地调整关节链,使末端执行器逐渐接近目标位置。与传统方法相比,FABRIK算法不需要使用旋转角度或矩阵,而是通过在线段上定位点来找到每个关节的位置,这使得它在计算上更加高效,并且能够产生视觉上现实的关节姿势。
三、python实现
网上已经有很多实现的python算法,这里,主要是利用实现的3D算法,实现在matplot中的IK,即,任意点击3D坐标点,实现IK,骨架的末端移动到目标点,就像开始的blender一样。
首先,我们需要导入一些必要的Python库,包括NumPy、Math、Matplotlib等,用于数学运算和图形绘制。
import numpy as np
import math
import matplotlib as mpl
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import axes3d
import re
单位向量函数
定义了一个函数 unitVector,它接受一个向量作为输入,并返回该向量的单位向量。
def unitVector(vector):return vector / np.linalg.norm(vector)
Segment3D 类用于存储逆运动学链的一部分。它接受参考点坐标、段长度和初始角度作为参数,并计算出该段的新坐标。
class Segment3D:def __init__(self, referenceX, referenceY, referenceZ, length, zAngle, yAngle):self.zAngle = zAngleself.yAngle = yAngleself.length = lengthdeltaX = math.cos(math.radians(zAngle)) * lengthdeltaY = math.sin(math.radians(zAngle)) * lengthdeltaZ = math.sin(math.radians(yAngle)) * lengthnewX = referenceX + deltaXnewY = referenceY + deltaYnewZ = referenceZ + deltaZself.point = np.array([newX, newY, newZ])
FabrikSolver3D 类是FABRIK算法的核心,它初始化了一个3D逆运动学求解器,并提供了添加段、检查可达性、迭代求解和绘图等功能。
class FabrikSolver3D:""" An inverse kinematics solver in 3D. Uses the Fabrik Algorithm."""def __init__(self, baseX=0, baseY=0, baseZ=0, marginOfError=0.01):# Create the base of the chain.self.basePoint = np.array([baseX, baseY, baseZ])# Initialize empty segment array -> [].self.segments = []# Initialize length of the chain -> 0.self.armLength = 0# Initialize the margin of error.self.marginOfError = marginOfErrorself.targetpoint=Noneself.fig = plt.figure()self.ax1 = self.fig.add_subplot(111, projection="3d")self.mousep=Nonedef addSegment(self, length, zAngle, yAngle):if len(self.segments) > 0:segment = Segment3D(self.segments[-1].point[0], self.segments[-1].point[1], self.segments[-1].point[2], length, zAngle + self.segments[-1].zAngle, self.segments[-1].yAngle + yAngle)else:# Maak een segment van de vector beginpoint, lengte en hoek.segment = Segment3D(self.basePoint[0], self.basePoint[1], self.basePoint[2], length, zAngle, yAngle)# Voeg lengte toe aan de totale armlengte.self.armLength += segment.length# Voeg de nieuwe segment toe aan de list.self.segments.append(segment)def isReachable(self, targetX, targetY, targetZ):if np.linalg.norm(self.basePoint - np.array([targetX, targetY, targetZ])) < self.armLength:return Truereturn Falsedef inMarginOfError(self, targetX, targetY, targetZ):if np.linalg.norm(self.segments[-1].point - np.array([targetX, targetY, targetZ])) < self.marginOfError:return Truereturn False def iterate(self, targetX, targetY, targetZ):target = np.array([targetX, targetY, targetZ])# Backwards.for i in range(len(self.segments) - 1, 0, -1):if i == len(self.segments) - 1:self.segments[i-1].point = (unitVector(self.segments[i-1].point - target) * self.segments[i].length) + targetelse:self.segments[i-1].point = (unitVector(self.segments[i-1].point - self.segments[i].point) * self.segments[i].length) + self.segments[i].point# Forwards.for i in range(len(self.segments)):if i == 0:self.segments[i].point = (unitVector(self.segments[i].point - self.basePoint) * self.segments[i].length) + self.basePointelif i == len(self.segments) - 1:self.segments[i].point = (unitVector(self.segments[i-1].point - target) * self.segments[i].length * -1) + self.segments[i-1].pointelse:self.segments[i].point = (unitVector(self.segments[i].point - self.segments[i-1].point) * self.segments[i].length) + self.segments[i-1].pointdef compute(self, targetX, targetY, targetZ):if self.isReachable(targetX, targetY, targetZ):while not self.inMarginOfError(targetX, targetY, targetZ):self.iterate(targetX, targetY, targetZ) self.targetpoint=[targetX, targetY, targetZ]else:print('Target not reachable.')sys.exit()def plot(self, save=False, name="graph"):self.ax1.clear() # 清除当前轴ax1=self.ax1# Plot arm.for i, segment in enumerate(self.segments):#ax1.scatter(segment.point[2], segment.point[0], segment.point[1], c='b')ax1.scatter(segment.point[0], segment.point[1], segment.point[2], c='b')if i > 0: # Connect to the previous segmentax1.plot([self.segments[i-1].point[0], segment.point[0]],[self.segments[i-1].point[1], segment.point[1]],[self.segments[i-1].point[2], segment.point[2]], 'b-')"""ax1.plot([self.segments[i-1].point[2], segment.point[2]],[self.segments[i-1].point[0], segment.point[0]],[self.segments[i-1].point[1], segment.point[1]], 'b-')"""# Connect the last segment to the base point"""ax1.plot([self.basePoint[2], self.segments[0].point[2]],[self.basePoint[0], self.segments[0].point[0]],[self.basePoint[1], self.segments[0].point[1]], 'b-')"""ax1.plot([self.basePoint[0], self.segments[0].point[0]],[self.basePoint[1], self.segments[0].point[1]],[self.basePoint[2], self.segments[0].point[2]], 'b-')# Start point#ax1.scatter(self.basePoint[2], self.basePoint[0], self.basePoint[1], c='g')ax1.scatter(self.basePoint[0], self.basePoint[1], self.basePoint[2], c='g')ax1.scatter(self.targetpoint[0],self.targetpoint[1],self.targetpoint[2], c='r',alpha=0.6, s=100)ax1.set_ylabel('y-axis')ax1.set_zlabel('z-axis')ax1.set_xlabel('x-axis')# Set the view angle so that the z-axis is pointing upwardsax1.view_init(elev=45., azim=45.)plt.pause(0.01)def extract_coordinates(self,s):# 使用正则表达式匹配x, y, z的值# 注意:负号前面加上了反斜杠进行转义# 使用正则表达式提取实数值,包括负号pattern = r'−?\d+\.\d+'matches = re.findall(pattern, s)values = [float(match.replace('−', '-')) for match in matches]x, y, z=values return x, y, zdef show(self):self.plot()self.fig.canvas.mpl_connect('button_press_event', self.on_click) # 连接点击事件self.fig.canvas.mpl_connect('motion_notify_event', self.on_motion) plt.show()def on_motion(self,event):if event.inaxes is not None:x, y = event.xdata, event.ydatasting = self.ax1.format_coord(x, y) x,y,z=self.extract_coordinates(sting)#print(x,y,z) self.mousep=(x,y,z)def on_click(self, event):# 检查点击事件是否在坐标轴内if event.inaxes is not None:print(f'Clicked on axis {event.inaxes}')# 触发你的函数print(self.mousep)if not type(self.mousep) == type(None): x,y,z=self.mousepprint("compute")self.compute(x, y, z)self.plot()# 获取点击的坐标值#self.compute(x, y, z)# 重绘图形#
可以通过如下步骤实现逆解:
-
添加关节段:通过addSegment方法,我们可以为机械臂添加多个关节段,每个关节段都有自己的长度和初始角度。
-
检查可达性:isReachable方法检查目标位置是否在机械臂的可达范围内。
-
迭代计算:iterate方法执行一次FABRIK算法迭代,调整关节位置以接近目标。
-
绘图显示:plot方法用于在3D空间中绘制机械臂的当前状态,show方法则显示最终的图形界面,并允许用户通过点击来选择目标位置。
-
事件处理:on_click和on_motion方法用于处理用户的点击和鼠标移动事件,以便动态地调整目标位置。
if __name__ == "__main__": arm = FabrikSolver3D() arm.addSegment(0, 0, 0)arm.addSegment(50, 0, 0)arm.addSegment(50, 0, 0)arm.addSegment(50, 0, 0)arm.addSegment(50, 0, 0)arm.compute(50, 50, 100) arm.show()
结论
通过这个Python实现,我们可以看到FABRIK算法在解决3D逆运动学问题中的强大能力。它不仅能够快速找到解决方案,还能够实时响应用户的交互,这在模拟和实际应用中都是非常有价值的。接下来我们尝试丰富这个算法,在各关节添加约束,并应用到6轴机械臂的IK计算中,看看能否获得预期效果。
[------------本篇完-------------]
PS.扩展阅读
————————————————————————————————————————
对于python机器人编程感兴趣的小伙伴,可以进入如下链接阅读相关咨询
ps1.六自由度机器人相关文章资源
(1) 对六自由度机械臂的运动控制及python实现(附源码)
(2) N轴机械臂的MDH正向建模,及python算法
ps2.四轴机器相关文章资源
(1) 文章:python机器人编程——用python实现一个写字机器人
(2)python机器人实战——0到1创建一个自动是色块机器人项目-CSDN直播
(3)博文《我从0开始搭建了一个色块自动抓取机器人,并实现了大模型的接入和语音控制-(上基础篇)》的vrep基础环境
(3)博文《我从0开始搭建了一个色块自动抓取机器人,并实现了大模型的接入和语音控制-(上基础篇)》的vrep基础环境
(4)实现了语音输入+大模型指令解析+机器视觉+机械臂流程打通
ps3.移动小车相关文章资源
(1)python做了一个极简的栅格地图行走机器人,到底能干啥?[第五弹]——解锁蒙特卡洛定位功能-CSDN博客
(2) 对应python资源:源码地址
(3)python机器人编程——差速AGV机器、基于视觉和预测控制的循迹、自动行驶(上篇)_agv编程-CSDN博客
(4)python机器人编程——差速AGV机器、基于视觉和预测控制的循迹、自动行驶(下篇)_agv路线规划原则python-CSDN博客
对应python及仿真环境资源:源码链接
ps3.wifi小车控制相关文章资源
web端配套资源源代码已经上传(竖屏版),下载地址
仿真配套资源已经上传:下载地址
web端配套资源源代码已经上传(横屏版),下载地址
在这里插入代码片
相关文章:
python机器人编程——一种3D骨架动画逆解算法的启示(上)
目录 一、前言二、fabrik 算法三、python实现结论PS.扩展阅读ps1.六自由度机器人相关文章资源ps2.四轴机器相关文章资源ps3.移动小车相关文章资源ps3.wifi小车控制相关文章资源 一、前言 我们用blender等3D动画软件时,会用到骨骼的动画,通过逆向IK动力学…...
Flutter开发者必备面试问题与答案02
Flutter开发者必备面试问题与答案02 视频 https://youtu.be/XYSxTb0iA9I https://www.bilibili.com/video/BV1Zk2dYyEBr/ 前言 原文 Flutter 完整面试问题及答案02 本文是 flutter 面试问题的第二讲,高频问答 10 题。 正文 11. PageRoute 是什么? …...
拥抱真实:深度思考之路,行动力的源泉
在纷繁复杂的现代社会,人们往往被表象迷惑,忙碌于各种事务之中,却很少停下来进行深度思考。这种忙碌往往是表面的、无效的,因为它缺乏对自我和目标的深刻理解与追求。提升行动力,避免假勤奋,关键在于深度思…...
【Python爬虫实战】深入理解Python异步编程:从协程基础到高效爬虫实现
#1024程序员节|征文# 🌈个人主页:易辰君-CSDN博客 🔥 系列专栏:https://blog.csdn.net/2401_86688088/category_12797772.html 目录 前言 一、异步 (一)核心概念 (二)…...
OpenCV图像处理方法:腐蚀操作
腐蚀操作 前提 图像数据为二值的(黑/白) 作用 去掉图片中字上的毛刺 显示图片 读取一个图像文件,并在一个窗口中显示它。用户可以查看这个图像,直到按下任意键,然后程序会关闭显示图像的窗口 # cv2是OpenCV库的P…...
PG数据库之流复制详解
一、流复制的定义 PostgreSQL流复制(Streaming Replication)是一种数据复制技术,它允许实时传输数据更改,从而在主服务器和一个或多个备用服务器之间保持数据同步。流复制是PostgreSQL数据库管理系统(DBMS)…...
Python酷库之旅-第三方库Pandas(174)
目录 一、用法精讲 801、pandas.Categorical类 801-1、语法 801-2、参数 801-3、功能 801-4、返回值 801-5、说明 801-6、用法 801-6-1、数据准备 801-6-2、代码示例 801-6-3、结果输出 802、pandas.Categorical.from_codes方法 802-1、语法 802-2、参数 802-3、…...
【Linux网络】基于TCP的全连接队列与文件、套接字、内核之间的关系
W...Y的主页 😊 代码仓库管理💕 前言:之前我们已经学习了TCP传输协议,而无论是TCP还是UDP都是使用socket套接字进行网络传输的,而TCP的socket是比UDP复杂的,当时我们学习TCPsocket编程时使用listen函数进行…...
IDE(集成开发环境)
IDE(集成开发环境)是软件开发过程中不可或缺的工具,它集成了代码编写功能、分析功能、编译器、调试器等开发工具,旨在提高开发效率。不同的IDE支持不同的语言和框架,下面是一些通用的IDE使用技巧和插件推荐,…...
一键导入Excel到阿里云PolarDB-MySQL版
今天,我将分享如何一键导入Excel到阿里云PolarDB-MySQL版数据库。 准备数据 这里,我们准备了一张excel表格如下: 连接到阿里云PolarDB 打开的卢导表,点击新建连接-选择阿里云PolarDB-MySQL版。如果你还没有这个工具,…...
Oracle有哪些版本
目录 Oracle 1(1979年) Oracle 2(1983年) Oracle 7(1992年) Oracle 8i(1999年) Oracle 9i(2001年) Oracle 10g(2004年) Oracle 11g(2007年) Oracle 12c(2013年) Oracle 18c(2018年) Oracle 19c(2019年) Oracle 21c(2023年) Oracle 23ai(202…...
先来先服务(FCFS,First-Come, First-Served)调度算法
有利于CPU繁忙作业的原因 充分利用CPU资源: 当一个CPU繁忙型的作业到达后,它会立即被执行,并且在没有其他作业等待的情况下,可以一直占用CPU直到完成。这使得CPU能够持续地执行作业,最大化利用CPU资源。 减少上下文切换…...
Windows操作系统忘记密码怎么办 这个方法屡试不爽 还不来试一下
Windows操作系统重置密码的操作步骤如下: 本方法适用于Windows Server 2008R2及其之后的操作系统。 第一步:从Windows 2008R2之后的操作系统光盘启动到安装界面,一直下一步到磁盘分区界面,按shiftF10调出cmd命令行界面。 第二步&…...
基于java的山区环境监督管理系统(源码+定制+开发)环境数据可视化、环境数据监测、 环境保护管理 、污染防治监测系统 大数据分析
博主介绍: ✌我是阿龙,一名专注于Java技术领域的程序员,全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师,我在计算机毕业设计开发方面积累了丰富的经验。同时,我也是掘金、华为云、阿里云、InfoQ等平台…...
jQuery Mobile 表单输入
jQuery Mobile 表单输入 引言 在移动设备上,表单输入是用户与移动应用交互的重要方式。jQuery Mobile 是一个基于 jQuery 的移动设备友好的开发框架,它提供了丰富的组件和工具来帮助开发者创建响应式和交互式的移动界面。本文将详细介绍如何使用 jQuery Mobile 来创建和定制…...
IoC详解
共有两类注解类型可以实现: 1. 类注解:Controller、Service、Repository、Component、Configuration. 2. 方法注解:Bean. 类注解 Controller(控制器存储) 使⽤Controller存储bean的代码如下所⽰: Con…...
基于 ThinkPHP+Mysql 灵活用工_灵活用工系统_灵活用工平台
基于 ThinkPHPMysql 灵活用工灵活用工平台灵活用工系统灵活用工小程序灵活用工源码灵活用工系统源码 开发语言 ThinkPHPMysql 源码合作 提供完整源代码 软件界面展示 一、企业管理后台 二、运用管理平台 三、手机端...
etcd之etcd分布式锁及事务(四)
1、etcd分布式锁及事务 1.1 前言 分布式锁是控制分布式系统之间同步访问共享资源的一种方式。在分布式系统中,常常需要协调他们的动作。如 果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要…...
智慧旅游微信小程序平台
作者介绍:✌️大厂全栈码农|毕设实战开发,专注于大学生项目实战开发、讲解和毕业答疑辅导。 🍅获取源码联系方式请查看文末🍅 推荐订阅精彩专栏 👇🏻 避免错过下次更新 Springboot项目精选实战案例 更多项目…...
C++设计模式创建型模式———简单工厂模式、工厂方法模式、抽象工厂模式
文章目录 一、引言二、简单工厂模式三、工厂方法模式三、抽象工厂模式四、总结 一、引言 创建一个类对象的传统方式是使用关键字new , 因为用 new 创建的类对象是一个堆对象,可以实现多态。工厂模式通过把创建对象的代码包装起来,实现创建对…...
C++ 类与对象(中) 默认成员函数
我们知道在类中,有成员变量和成员函数,我们可以通过创造不同的成员函数来实现这个类不同的功能,如果我们创造一个类,却不实现它的成员函数会如何呢?这个就涉及到类中的默认成员函数的概念了。但在本文我们主要介绍以下…...
中间人攻击(https降级攻击)和iptables命令分析
中间人攻击 以下是一个简单的中间人攻击示例,结合 ARP 欺骗和流量修改: 1. 进行 ARP 欺骗 首先,使用 arpspoof 进行 ARP 欺骗,将受害者的流量重定向到攻击者的机器上: sudo arpspoof -i eth0 -t 172.29.144.50 172…...
开源生活-分布式管理
开源竞争(当自己没有办法彻底掌握一门技术的时候就彻底开源掉;培养出更多的依赖,让更多人帮助你完善你的技术,那么这不就是在砸罐子吗?一个行业里面总会有人先砸罐子的,你不如先砸罐子,还能听个…...
华为OD机试真题- 关联子串
该专栏题目包含两部分: 100 分值部分题目 200 分值部分题目 所有题目都会陆续更新,订阅防丢失 题目描述: 给定两个字符串str1和str2,如果字符串str1中的字符,经过排列组合后的字符串中,只要有一个字符串是…...
云智慧完成华为原生鸿蒙系统的适配, 透视宝 APM 为用户体验保驾护航
2024 年 10 月 22 日,首个国产移动操作系统 —— 华为原生鸿蒙操作系统 HarmonyOS NEXT 正式面世,成为继 iOS 和 Android 后的全球第三大移动操作系统。HarmonyOS NEXT,从系统内核、数据库根基,到编程语言创新、AI(人工…...
QT 多语言转换 ts、qm
QT开发之路 企业级开发系列文章,主要目标快速学习、完善、提升 相关技能 高效完成企业级项目开发 分享在企业中积累的实用技能和经验。 通过具体的编码过程、代码示例、步骤详解、核心内容和展示的方法解决遇到的实际问题。 阅读前声明 本系列文章属于付费内容 禁止…...
C++学习:类和对象(二)
一、默认成员函数 1. 什么是默认成员函数? 在C中,每个类都有一些特殊的成员函数,如果程序员没有显式地声明,编译器会自动为类生成这些函数,这些函数称为默认成员函数 2. 默认成员函数列表 默认构造函数(…...
深度学习(五):语音处理领域的创新引擎(5/10)
一、深度学习在语音处理中的崛起 在语音处理领域,传统方法如谱减法、维纳滤波等在处理复杂语音信号时存在诸多局限性。这些方法通常假设噪声是平稳的,但实际噪声往往是非平稳的,导致噪声估计不准确。同时,为了去除噪声࿰…...
双曲函数(Hyperbolic functuons)公式
在python等语言里有双曲函数库和反双曲函数库,但是并没有包含所有的双曲函数。以numpy为例子,numpy只提供了sinh、cosh、tanh、arcsinh、arccosh、arctanh六种函数,那么其余的就需要用公式计算了。 转换公式 对于函数库不能直接计算的&#…...
【CSS/SCSS】@layer的介绍及使用方法
目录 基本用法layer 的作用与优点分离样式职责,增强代码可读性和可维护性防止无意的样式冲突精确控制样式的逐层覆盖提高复用性 兼容性实际示例:使用 import 管理加载顺序实际示例:混入与 layer 结合使用 layer 是 CSS 中用于组织和管理样式优…...
橙子建站网/广告优化
“ 关键字:微信 小程序 ”正文:小程序 会议助手01—会议助手小程序,主要功能可以为参会人介绍公司、介绍议程、带领签到,查询座位等,会议助手。同时为举办方和与会人员提供便利,建立纽带。此会议助手主要针…...
聊城做网站推广哪家好/广告优化师培训
1、针对布局加载Xml文件的优化,我们使用了异步Inflate的方式,即AsyncLayoutInflater。它的核心原理是在子线程中对我们的Layout进行加载,而加载完成之后会将View通过Handler发送到主线程来使用。所以不会阻塞我们的主线程,加载的时…...
北斗导航2022最新版手机版/seo推广小分享
http://blog.csdn.net/u012926924/article/details/50606195 最简android之wifi调试 做android开发的时候,经常遇到的一个问题就是真机调试次数多了,会导致usb口,损坏,而且长期给手机充电也会损坏手机,所以我想了想是…...
郑州建设企业网站公司/怎么找到精准客户资源
4.2 长训练序列的生成 从时域上来看,帧结构在短训练序列之后是长训练序列,其长度为8us,其中包括二个有效OFDM符号的长度(每个3.2us)和一个长型保护间隔的长度(1.6us)。 长训练序列主要用于精确的…...
广西南宁房产网站建设/网站seo推广优化教程
Debugging with Assertions (断言调试) 断言是一个假设逻辑条件为真的运行时检查机制.在执行任意代码之前,可以使用断言来确认一个基本的条件情况.如果条件判断为真,代码将继续执行,如果条件为假,代码将会结束,应 用程序也会退出. 如果在调试环境中,运行的代码触发了一个断言…...
合肥网站seo服务/如何优化关键词排名到首页
剑指 Offer 10- II. 青蛙跳台阶问题 题目链接:题目链接 这个题和斐波那契数列是一个问题,用的是斐波那契的递推公司。经典爬楼梯问题 一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。 答…...