【运动控制】CNC三轴小线段路径规划

news/2024/5/11 11:03:58/文章来源:https://blog.csdn.net/qq_44940689/article/details/129179813

CNC三轴小线段路径规划

文章目录

  • CNC三轴小线段路径规划
    • 一、项目说明
    • 二、具体实现
      • 1、速度规划
      • 2、小线段插补
      • 3、运动学逆解刀轴插补点
      • 4、差分处理得到实际的速度和加速度
      • 5、加速度滑动平均
      • 6、实现的效果如图所示
    • 三、Reference

写在前面,本文是作为一个练手小项目的总结,方便以后自己查看,也欢迎大家批评指正。项目地址: GitHub

一、项目说明

参照论文《An optimal feedrate model and solution algorithm for a high-speed machine of small line blocks with look-ahead 》给出的方法给五轴机床做速度规划。输入原始的刀心数据(x,y,z,i,j,k),对其进行速度规划和插补,获得插补后的数据(x,y,z,i,j,k),然后通过运动学逆解转化成刀轴数据(x,y,z,b,c)。通过差分计算实际的速度和加速度与规划的速度进行对比,最后观察平滑滤波后的实际加速度。

二、具体实现

1、速度规划

首先进行速度规划,按照如上的流程图(参考论文)对刀心的速度进行规划,计算得到每个刀心点对应的 ,具体的计算过程是先设定前瞻数,这里设定为4,然后通过判断上述循环条件,在

公式中选取对应的值作为当前的 ,具体的实现是代码中的speed_planning函数,函数的详细解释如下:

def speed_planning(traj_data, max_speed=MAX_SPEED, max_accel=MAX_ACCEL, corner_time=CORNER_TIME, period=PERIOD):""":@func:  参考《An optimal feedrate model and solution algorithm for a high-speed machine of small line blocks with look-ahead》给出的方法,进行速度规划:param traj_data: 路径信息shape(num,3) (x,y,z):param max_speed=0.05: 最大合成速率 m/s:param max_accel=0.5: 最大合成加速度 m/s^2:param corner_time=0.003: 拐弯时间 s:param period = 0.001: 插补周期 s:return:"""# 定义常量block_max = 4   # 最大前瞻数num = len(traj_data)# 开辟存储空间a = np.zeros([num])b = np.zeros([num])e = np.zeros([num])c = np.zeros([num])d = np.power(max_speed,2)Vs = np.zeros([num,1])# 节点差分dp = np.diff(traj_data, axis=0)# 初始化e[0] = db[0] = 0b[1] = 2*max_accel*np.sqrt(np.power(dp[0],2).sum())for i in range(1,num-1):  # Vs中第一个速度和最后一个速度都为0j = 0if j < block_max:if i+j <num:j=j+1b[i+j] = 2*max_accel*np.sqrt(np.power(dp,2).sum())e[i+j-1] = corner_time*max_accel*max_accel / (2*(1-(dp[i+j-2,0]*dp[i+j-1,0]+dp[i+j-2,1]*dp[i+j-1,1]+dp[i+j-2,2]*dp[i+j-1,2])/(np.sqrt(np.power(dp[i+j-2],2).sum())*np.sqrt(np.power(dp[i+j-1],2).sum()))))c[i+j-1] = np.min([e[i+j-1],d])if c[i+j-1] > b[i+j]:sum = 0for k in range(i+1,i+j):sum += b[k]if sum >= c[i]:Vs[i]=np.sqrt(np.min([Vs[i-1]*Vs[i-1]+b[i], e[i]]))elif c[i+j-1] <= b[i+j]:sum = 0for k in range(i+1,i+j):sum += b[k]Vs[i] = np.sqrt(np.min([sum+c[i+j-1], Vs[i-1]*Vs[i-1]+b[i], e[i]]))elif i+j >= num:sum = 0for k in range(i+1,i+j):sum += b[k]Vs[i] = np.sqrt(np.min([sum, Vs[i-1]*Vs[i-1]+b[i], e[i]]))elif j>= block_max:sum = 0for k in range(i+1,i+j):sum += b[k]Vs[i] = np.sqrt(np.min([sum, Vs[i-1]*Vs[i-1]+b[i], e[i]]))return Vs

2、小线段插补

然后再按照上图的方法计算每一段插值时所需要的信息

具体实现函数是calc_lines_info

def calc_lines_info(path_data, plan_vels, max_speed=MAX_SPEED, max_accel=MAX_ACCEL):""":@func: 三轴小线段计算中间信息函数:param path_data:  初始路径信息 shape(n,6) (x,y,z,i,j,k):param plan_vels:  初始路径点的合成规划速度 shape(n,1):param max_speed = 0.05: 三轴最大合成速度  m/s:param max_accel = 0.5: 三轴最大合成加速度 m/s^2:return : """num = path_data.shape[0]# distances = np.zeros([num -1, 1])  #插值点间的距离shape(n-1, 3)dp = np.diff(path_data[:,0:3],axis=0)# 开辟存储空间vels_m = np.zeros([num -1])s1 = np.zeros([num -1])s2 = np.zeros([num -1])s3 = np.zeros([num -1])ta = np.zeros([num -1])td = np.zeros([num -1])tl = np.zeros([num -1])for i in range(num -1):# 计算距离# distances[i] = np.sqrt(np.power(path_data[i+1,0:3] - path_data[i,0:3],2).sum()) dis_i = np.sqrt(np.power(dp[i],2).sum())# 计算合成速度vi = plan_vels[i]vi_1 = plan_vels[i+1]# vels_m[i] = min(np.sqrt((np.power(vi,2) + np.power(vi_1,2) + 2*max_accel*distances[i])/2), max_speed)vels_m[i] = min(np.sqrt((np.power(vi,2) + np.power(vi_1,2) + 2*max_accel*dis_i)/2), max_speed)s1[i] = (np.power(vels_m[i],2) - np.power(vi,2))/2s3[i] = (np.power(vels_m[i],2) - np.power(vi_1,2))/2s2[i] = dis_i - s1[i] -s3[i]ta[i] = (vels_m[i] - vi) / max_acceltd[i] = (vels_m[i] - vi_1) / max_acceltl[i] = s2[i] / vels_m[i]return vels_m, ta, td, tl

然后我们进行单条线段的插补,函数实现是calc_axis_point

def calc_axis_point(start, end, Vi, Vi_1, Vm, timea, timed, timel, max_accel=MAX_ACCEL, period=PERIOD):""":@func:  计算一段的插补点:param start:  shape(6,):param end: shape(6,):param Vm: 该段的最大速度:param Vi: 起始速度:param Vi_1: 终点速度:param timea: 加速时间:param timed: 匀速时间:param timel: 减速时间:return: 返回给定两点之间的插补点,shape(n,6)"""dist = np.sqrt(np.power(end[0:3]-start[0:3],2).sum())# 计算三轴x,y,z分别所占的比例k1 = (end[0:3]-start[0:3])[0] / np.sqrt(np.power(end[0:3]-start[0:3],2).sum())k2 = (end[0:3]-start[0:3])[1] / np.sqrt(np.power(end[0:3]-start[0:3],2).sum())k3 = (end[0:3]-start[0:3])[2] / np.sqrt(np.power(end[0:3]-start[0:3],2).sum())# 计算法向量的比例delta = (end[3:]-start[3:]) / (timea+timed+timel)# 插值周期数t_count = 1V1 = Vi         #起始点速度V2 = Vi_1       #终点速度# 开辟存储空间line_points = np.zeros([1,6])dis = 0while(t_count*period <= timea+timed+timel and dis<=dist):# 计算加速时间内的插值点if t_count*period <= timea:dis += V1*period + 0.5*max_accel*period*periodV1 += max_accel*periodif V1 > Vm:V1 = Vmt_count += 1delta_vec = delta * t_count * perioddelta_xyz = np.concatenate([k1*dis, k2*dis, k3*dis], axis=0)dp = np.concatenate([delta_xyz, delta_vec], axis=0)p = start + dpline_points = np.vstack([line_points, p])# 计算匀速时间内的插值点elif timea < t_count*period <= timea+timel and timel!=0:dis += V1 * periodt_count += 1delta_vec = delta * t_count * perioddelta_xyz = np.concatenate([k1*dis, k2*dis, k3*dis], axis=0)dp = np.concatenate([delta_xyz, delta_vec], axis=0)p = start + dpline_points = np.vstack([line_points, p])# 计算减速时间内的插值点elif timea+timel < t_count*period <= timea+timel+timed:dis += V1*period - 0.5*max_accel*period*periodV1 = V1 - max_accel*periodif V1 < V2:V1 = V2t_count += 1delta_vec = delta * t_count * perioddelta_xyz = np.concatenate([k1*dis, k2*dis, k3*dis], axis=0)dp = np.concatenate([delta_xyz, delta_vec], axis=0)p = start + dpline_points = np.vstack([line_points, p])return line_points[1:]

再进行多条线段插补,函数实现是calc_axis_points

def calc_axis_points(path_data, plan_vels, max_speed=MAX_SPEED, max_accel=MAX_ACCEL, corner_time=CORNER_TIME, period=PERIOD):""":@func:  计算所有的插补点:param path_data:  shape(num,6):param plan_vels: shape(num,):param max_speed = 0.05: 三轴最大合成速度  m/s:param max_accel = 0.5: 三轴最大合成加速度 m/s^2:param corner_time = 0.003: 拐弯时间 s:param period = 0.001: 固定插补周期 s:return: 所有的插补点,shape(n,6)"""vels_m, ta, td, tl = calc_lines_info(path_data, plan_vels, max_speed, max_accel)# 开辟存储空间axis_points = np.zeros([1,6])num = len(path_data) - 1for i in range(num):line_points = calc_axis_point(path_data[i], path_data[i+1], plan_vels[i], plan_vels[i+1], vels_m[i], ta[i], td[i], tl[i], max_accel, period)axis_points = np.vstack([axis_points, line_points])return axis_points[1:]

3、运动学逆解刀轴插补点

然后使用运动学逆解,求出刀轴的坐标,函数实现是inv_kinema

def inv_kinema(path_data):""":@func: 求解逆运动学:param path_data: 刀心路径点信息 shape(num,6) (x,y,z,i,j,k):return :  求解的刀轴信息 shape(num,5) (x,y,z,b,c)"""#加载数据points = read_path_data(DATA_PATH)curve_points = points[:,0:3]    # 路径normal_vectors = points[:,3:]  # 法向量# 旋转轴初始方向wc = sympy.Matrix([[0], [0], [1]])wb = sympy.Matrix([[0], [0.5 * sympy.sqrt(2)], [0.5 * sympy.sqrt(2)]])theta_b, theta_c ,x,y,z= sympy.symbols('theta_b theta_c x y z')# 对C旋转cRodrigues = Rodrigues(wc, theta_c)# 对B旋转bRodrigues = Rodrigues(wb, theta_b)# print(bRodrigues)zero=np.zeros((3,1))# 生成旋转矩阵ec=np.append(cRodrigues,zero,axis=1)e_bu=np.array([0,0,0,1]).reshape(1,4)e_c=np.append(ec,e_bu,axis=0).reshape(4,4)eb=np.append(bRodrigues,zero,axis=1)e_b=np.append(eb,e_bu,axis=0).reshape(4,4)# print(eb,e_b)e_x=np.array([1,0,0,x,0,1,0,0,0,0,1,0,0,0,0,1]).reshape(4,4)e_y=np.array([1,0,0,0,0,1,0,y,0,0,1,0,0,0,0,1]).reshape(4,4)e_z=np.array([1,0,0,0,0,1,0,0,0,0,1,z,0,0,0,1]).reshape(4,4)# 初始位型mt0 = np.array([1,0,0,10,0,1,0,20,0,0,1,100,0,0,0,1]).reshape(4,4)gmw=np.eye(4)gmt1=np.dot(e_x,e_y)gmt2= np.dot(gmt1,e_z)gmt3=np.dot(e_c,e_b)gmt4=np.dot(gmt3,mt0)gmt=np.dot(gmt2,gmt4)# 转移矩阵gm=np.dot(gmw,gmt)# 带入解析式,求解bx = normal_vectors[:,0]/np.linalg.norm(normal_vectors[:,0])#归一化by = normal_vectors[:,1]/np.linalg.norm(normal_vectors[:,1])bz = normal_vectors[:,2]/np.linalg.norm(normal_vectors[:,2])x = curve_points[:,0]y = curve_points[:,1]z = curve_points[:,2] print(2*bz-1)theta_b = np.arccos(2*bz-1)theta_c = np.arcsin(((bz-1)*bx+np.sqrt(2)*np.sqrt(bz-bz**2)*by)/(1-bz**2))# 存数据myfile = open("path_interpolation_xyzbc.txt", "w")myfile.write('x   y   z   b   c\n')for i in range(0, normal_vectors.__len__()):myfile.write('{:>6.3f} '.format(x[i]))myfile.write('{:>6.3f} '.format(y[i]))myfile.write('{:>6.3f} '.format(z[i]))myfile.write('{:>6.3f} '.format(theta_b[i]))myfile.write('{:>6.3f}\n'.format(theta_c[i]))myfile.close()

4、差分处理得到实际的速度和加速度

接着用速度差分查看实际的速度,函数实现是diff_vel_accel

def diff_vel_accel(axis_points, period=PERIOD):""":@func:  通过五轴插补点计算实际速度和实际加速度:param axis_points:  实际插补点:param period: 插补周期:return: 实际速度, 实际加速度"""delta_d = np.diff(axis_points[:,0:3], axis=0)dv = delta_d / periodreal_vels = np.sqrt(np.power(dv,2).sum(axis=1))delta_v = np.diff(dv, axis=0)da = delta_v / periodreal_accels = np.sqrt(np.power(da,2).sum(axis=1))# print(real_vels[0:5])# print(real_accels[0:5])# 将实际速度写入到文件real_velocities.txt中write2file('real_velocities.txt', real_vels, string = "real velocities")# 将实际加速度写入到文件real_accelerations.txt中write2file('real_accelerations.txt', real_accels, string="real accelerations")return real_vels, real_accels

5、加速度滑动平均

最后对实际的加速度进行滑动平均,与设定值进行比较,函数实现是sliding_average

def sliding_average(data, window_size):""":@func: 实现滑动平均滤波:param data: 滑动滤波的数据:param window_size:  滑动窗口大小"""filtered_data = []for i in range(len(data)):if i < window_size:filtered_data.append(sum(data[:i+1]) / (i+1))else:filtered_data.append(sum(data[i-window_size+1:i+1]) / window_size)write2file("filtered_accelerations.txt", filtered_data, string = "filtered accelerations")return filtered_data

6、实现的效果如图所示

其中,蓝色的点是原始路径点,红色的线是由插补点绘制的小线段轨迹。

三、Reference

1、《An optimal feedrate model and solution algorithm for a high-speed machine of small line blocks with look-ahead 》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_72971.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

论文浅尝 | Deep Reinforcement Learning for Entity Alignment

笔记整理&#xff1a;陈鹏&#xff0c;天津大学硕士链接&#xff1a;https://aclanthology.org/2022.findings-acl.217.pdf动机实体对齐(Entity Alignment&#xff09;的基本目标在于发现两个知识图谱间指向同一现实对象的实体对&#xff0c;以便将不同知识图谱连接起来&#x…

Interview系列 - 06 Java | ArrayList底层源码分析 | 遍历集合时如何删除集合中的元素

文章目录1. 底层源码分析01. 属性02. 构造方法03. 在数组的末尾添加元素 add(E e)04. 在数组的指定位置添加元素 add(int index, E element)05. 替换指定位置的元素 set(int index, E element)06. 获取指定索引位置处的元素 get(int index)07. 删除指定位置的元素 remove(int i…

华为OD机试真题 用 C++ 实现 - 众数和中位数 | 多看题,提高通过率

最近更新的博客 华为OD机试 - 入栈出栈(C++) | 附带编码思路 【2023】 华为OD机试 - 箱子之形摆放(C++) | 附带编码思路 【2023】 华为OD机试 - 简易内存池 2(C++) | 附带编码思路 【2023】 华为OD机试 - 第 N 个排列(C++) | 附带编码思路 【2023】 华为OD机试 - 考古…

SpringCloud - Feign远程调用

目录 Feign的远程调用 RestTemplate方式调用存在的问题 介绍与初步使用 Feign的自定义配置 Feign运行自定义配置来覆盖默认配置&#xff0c;可以修改的配置如下&#xff1a; 配置Feign日志有两种方式&#xff1a; Feign性能优化 Feign底层的客户端实现&#xff1a; 连…

AI作画—中国画之山水画

山水画&#xff0c;简称“山水”&#xff0c;中国画的一种&#xff0c;描写山川自然景色为主体的绘画。山水画在我国绘画史中占有重要的地位。 山水画形成于魏晋南北朝时期&#xff0c;但尚未从人物画中完全分离。隋唐时始终独立&#xff0c;五代、北宋时趋于成熟&#xff0c;…

Linux多媒体子系统01:从用户空间使用V4L2子系统

1 V4L2应用编程基础1.1 概述V4L2应用编程需要使用如下系统调用&#xff0c;open(): 打开V4L2设备 close(): 关闭V4L2设备 ioctl(): 向V4L2设备驱动程序发送控制命令 mmap(): 将V4L2设备驱动程序分配的缓冲区内存映射到用户空间 read()或write(): 这2个系统调用是否支持取决于流…

领导催我优化SQL语句,我求助了ChatGPT。这是ChatGPT给出的建议,你们觉得靠谱吗

作为一个程序员&#xff0c;无论在面试还是工作中&#xff0c;优化SQL都是绕不过去的难题。 为啥&#xff1f;工作之后才会明白&#xff0c;随着公司的业务量增多&#xff0c;SQL的执行效率对程系统运行效率的影响逐渐增大&#xff0c;相对于改造代码&#xff0c;优化SQL语句是…

LeetCode-93. 复原 IP 地址

目录题目思路回溯法题目来源 93. 复原 IP 地址 题目思路 意识到这是切割问题&#xff0c;切割问题就可以使用回溯搜索法把所有可能性搜出来&#xff0c;和131.分割回文串就十分类似了。 回溯法 1.递归参数 startIndex一定是需要的&#xff0c;因为不能重复分割&#xff0c…

实战:手把手教你colossal-AI复现Chatgpt的流程

相信很多人都看了使用colossal-AI复现Chatgpt的流程的文章&#xff0c;但实际上看过了&#xff0c;不免有人发出“说得贼明白&#xff0c;就是自己做不出来”的感叹吧。本人公开一下实战过程&#xff0c;给有兴趣复现chatgpt流程的朋友一个参考。 一、环境搭建&#xff1a; 1…

ES6-ES11基本全部语法

在进入es6语法之前&#xff0c;先走一波es5遍历迭代Api&#xff0c;&#xff0c;它们的作用&#xff0c;应用场景&#xff0c;参数&#xff0c;以及返回值分别是什么。&#xff08;forEach、map、some、every、filter&#xff09;我们统一设定一个初始数组&#xff1a;let arra…

【likeshop多商户】电子面单商家直播上线啦~

likeshop多商户商城v2.2.0版本更新啦&#xff01; 新增功能&#xff1a; 商家直播 单子面单 优化&#xff1a; 个人中心优惠券数量统计优化 修复&#xff1a; 秒杀商品待审核时&#xff0c;下单价格计算错误 个人中心修改头像后地址保存错误 「商家直播」 提升品牌知名度…

华为OD机试真题 用 C++ 实现 - 子序列长度 | 多看题,提高通过率

最近更新的博客 华为OD机试 - 入栈出栈(C++) | 附带编码思路 【2023】 华为OD机试 - 箱子之形摆放(C++) | 附带编码思路 【2023】 华为OD机试 - 简易内存池 2(C++) | 附带编码思路 【2023】 华为OD机试 - 第 N 个排列(C++) | 附带编码思路 【2023】 华为OD机试 - 考古…

2-并发篇

线程有哪些状态 java的线程状态有6种&#xff1a; 操作系统中有5状态的说明 注意java的runnable对应了就绪、运行、阻塞I/O 线程池的核心参数 主要是说线程池的一个实习类 threadPoolExecutor.class 1.corePoolSize 核心线程数据&#xff08;可以为0&#xff09; 最多保…

JavaTCP通信程序

3 TCP通信程序 3.1 TCP通信原理 TCP通信协议是一种可靠的网络协议&#xff0c; 它在通信的两端名建立一个Socke对象&#xff0c; 从而在通信的两端形成网络虚拟链路一旦建立了 虚拟的网络链路&#xff0c;两端的程序就可以通过虚拟链路进行通信Java对基于TCP协议的的网络提供…

Python-生成列表

1.生成列表使用列表前必须先生成列表。1.1使用运算符[ ]生成列表在运算符[ ]中以逗号隔开各个元素会生成包含这些元素的新列表。另外&#xff0c;如果[ ]中没有元素就会生成空列表示例>>> list01 [] >>> list01 [] >>> list02 [1, 2, 3] >>…

LeetCode 206. 反转链表

LeetCode 206. 反转链表 难度&#xff1a;easy\color{Green}{easy}easy 题目描述 给你单链表的头节点 headheadhead &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[5,4,3,2,1]示例 2&a…

Java Stream、File、IO 超详细整理,适合新手入门

目录 Java Stream Java File Java IO Java Stream Java Stream 是 Java 8 中引入的一种新的抽象数据类型&#xff0c;它允许开发人员使用函数式编程的方式来处理集合数据。 使用 Java Stream 可以方便地进行过滤、映射、排序和聚合等操作。下面是一个简单的示例&#xff1a;…

BatchNorm与LayerNorm的比较

Batch Normalization存在的一些问题 &#xff08;1&#xff09;BN在mini-batch较小的情况下不太适用 BN是对整个mini-batch的样本统计均值和方差 当训练样本数很少时&#xff0c;样本的均值和方差不能反映全局的统计分布信息&#xff0c;从而导致效果下降 &#xff08;2&am…

IM即时通讯构建企业协同生态链

在当今互联网信息飞速发展的时代&#xff0c;随着企业对协同办公要求的提高&#xff0c;协同办公的定义提升到了智能化办公的范畴。大多企业都非常重视构建连接用户、员工和合作伙伴的生态平台&#xff0c;利用即时通讯软件解决企业内部的工作沟通、信息传递和知识共享等问题。…

【NestJS】JWT 鉴权

Passport 是一个 NodeJS 鉴权库 JWT 认证的交互流程&#xff1a;浏览器发起请求&#xff0c;服务端对用户名和密码进行验证。如果身份验证通过&#xff0c;服务端会基于用户信息生成 token 字符串&#xff0c;并将其响应给浏览器。浏览器会将 token 字符串存储起来。往后的每次…