11 Commits

Author SHA1 Message Date
c3dbb2cff0 v0.1.6.3(2024/06/18)
1. [current.py] 适配电机电流中速度使用hw_joint_vel_feedback的数据,取消对device_servo_vel_feedback的支持,后续所有涉及到速度相关的数据均已前者为准,现已完成对单轴和场景的适配

> !!WARNING:目前版本的电机电流程序还支持DriverMaster采集的数据处理,等明确后,将不再支持,也即所有的电机电流数据(工业+协作),都是用诊断曲线来采集
2024-06-18 20:42:48 +08:00
4ba8af842c unused 2024-06-17 15:07:28 +08:00
a0fe9b5939 fix merging 2024-06-16 14:49:46 +08:00
2f2f0d430d fix merging 2024-06-16 14:30:12 +08:00
78a79e4aa0 v0.1.6.2(2024/06/16)
1. [current.py] 修改了max/avg相关功能中对于返回值的处理逻辑,并在输出框以行的形式打印出来
2024-06-16 14:25:41 +08:00
346379ec1a re-commit 2024-06-16 14:09:26 +08:00
215e312480 iv0.1.6.1(2024/06/16)
1. [wavelogger.py]: bugfix single_file_proc函数中,修改_start起始点的计算逻辑
2. [wavelogger.py]: bugfix find_point函数中,当判断条件为临界值 2.0 的时候,针对forward和backward两种情况,对row_target做与判断逻辑相同的处理,目的是避免形成死循环
2024-06-16 14:07:36 +08:00
5ba3c76386 modify vers info 2024-06-15 19:32:57 +08:00
224af36bfe change to new version because of new function added 2024-06-15 19:27:31 +08:00
95071f363b fix merging 2024-06-15 19:17:53 +08:00
fc30fcde80 v0.1.5.4(2024/06/15)
[aio.py]: 新增wavelogger处理界面
[wavelogger.py]: 新增精度数据处理模块
2024-06-15 19:14:34 +08:00
7 changed files with 287 additions and 44 deletions

1
.gitignore vendored
View File

@ -4,3 +4,4 @@ aio/.idea/
aio/code/__pycache__/
aio/package/
aio/venv
aio/__pycache__/

View File

@ -4,6 +4,7 @@
1. 制动数据单轴数据处理3min以内
2. 电机电流数据全部轴数据处理1min以内
3. ISO激光数据整理1min以内
4. wavelogger波形处理几乎不花费时间
### 使用方法
@ -19,7 +20,7 @@
```
pyinstaller.exe -F --version-file file_version_info.txt -i .\icon.ico .\aio.py -p .\brake.py -p .\current.py
pyinstaller --noconfirm --onedir --windowed --add-data "C:/Users/Administrator/AppData/Local/Programs/Python/Python312/Lib/site-packages/customtkinter;customtkinter/" --version-file ..\assets\file_version_info.txt -i ..\assets\icon.ico ..\code\aio.py -p ..\code\brake.py -p ..\code\iso.py -p ..\code\current.py
pyinstaller --noconfirm --onedir --windowed --add-data "C:/Users/Administrator/AppData/Local/Programs/Python/Python312/Lib/site-packages/customtkinter;customtkinter/" --version-file ..\assets\file_version_info.txt -i ..\assets\icon.ico ..\code\aio.py -p ..\code\brake.py -p ..\code\iso.py -p ..\code\current.py -p ..\code\wavelogger.py
```
---
@ -92,6 +93,13 @@ pyinstaller --noconfirm --onedir --windowed --add-data "C:/Users/Administrator/A
c. ISO-V100.pdf
d. iso-results.xlsx
#### wavelogger波形数据
1. 需要提前将 .xdt 波形数据转换成 .csv 文件
2. 组织目录下只允许有 .csv 文件,对文件夹无要求
3. 运行结束后,会生成 result.xlsx 文件,结果按照 .csv 文件名存放
4. 采集数据时,不同轮次数据时间间隔最好大于 2 倍的周期时间,否则会出现采集的轮数不正确的情况,但数据是完整的
#### 其他
customtkinter的tabview组件不支持修改字体大小可以参考 [Changing Font of a Tabview](https://github.com/TomSchimansky/CustomTkinter/issues/2296) 进行手动修改源码实现:
a. 运行 `pip show customtkinter`,获取到库的路径
@ -180,32 +188,49 @@ v0.1.4(2024/06/06)
6. 支持工业/协作两条产品线的电机电流数据处理包括单轴场景max/avg计算
v0.1.5(2024/06/12)
1. [aio.py]: 主界面切换不同功能时保持placehold一致
2. [brake.py]: 由于制动采集模板和内容的更改,适配了新的数据,更新了算法
3. [aio.py]: 新增tabview组件区分数据处理和自动化测试功能
4. [aio.py]: 重新调整界面配色
5. [aio.py]: 修改了write2textbox函数定制化显示每一行的颜色针对每一行可自定义输出内容颜色
6. [brake.py/iso.py/current.py]: 由于第 5 点的更改,同时修改了其他文件相关引用的部分
7. [aio.py]: 更改label/entry/optionmenu等控件的生成方式使用循环实现更加简洁和容易维护
8. [aio.py]: 修改customtkinter库中C:\Users\Administrator\AppData\Local\Programs\Python\Python312\Lib\site-packages\customtkinter\windows\widgets\ctk_tabview.py文件参考https://github.com/TomSchimansky/CustomTkinter/issues/2296实现修改tabview组件的字体大小
9. [aio.py]: 修改menu_main->menu_main_dpmenu_sub->menu_sub_dp为后续其他tab功能按钮做扩展是针对第三点做出的相应调整
10. [layout.xlsx]: 添加了各个功能的流程图
1. [aio.py] 主界面切换不同功能时保持placehold一致
2. [brake.py] 由于制动采集模板和内容的更改,适配了新的数据,更新了算法
3. [aio.py] 新增tabview组件区分数据处理和自动化测试功能
4. [aio.py] 重新调整界面配色
5. [aio.py] 修改了write2textbox函数定制化显示每一行的颜色针对每一行可自定义输出内容颜色
6. [brake.py/iso.py/current.py] 由于第 5 点的更改,同时修改了其他文件相关引用的部分
7. [aio.py] 更改label/entry/optionmenu等控件的生成方式使用循环实现更加简洁和容易维护
8. [aio.py] 修改customtkinter库中C:\Users\Administrator\AppData\Local\Programs\Python\Python312\Lib\site-packages\customtkinter\windows\widgets\ctk_tabview.py文件参考https://github.com/TomSchimansky/CustomTkinter/issues/2296实现修改tabview组件的字体大小
9. [aio.py] 修改menu_main->menu_main_dpmenu_sub->menu_sub_dp为后续其他tab功能按钮做扩展是针对第三点做出的相应调整
10. [layout.xlsx] 添加了各个功能的流程图
v0.1.5.1(2024/06/12)
[current.py]: 修改cycle功能中数据清理范围为70000行并将threshold从2调整为5
[current.py]: 修改位置超限提示,使更清楚了解问题原因
[current.py]: 修改find_point函数中错误提示增加定位信息
[README.md]: 精简打包命令
[requirements.txt]: 新增必要库配置文件
1. [current.py] 修改cycle功能中数据清理范围为70000行并将threshold从2调整为5
2. [current.py] 修改位置超限提示,使更清楚了解问题原因
3. [current.py] 修改find_point函数中错误提示增加定位信息
4. [README.md] 精简打包命令
5. [requirements.txt] 新增必要库配置文件
v0.1.5.2(2024/06/13)
[brake.py/aio.py]: 将sto修改为estop
[brake.py]: 修改了速度计算逻辑新版本的vel列数据遵循如下规则av = vel * 180 / pi根据av再计算speed
[brake.py]: 将threshold修改为常量50
[brake.py]: 提高了输出提示语的明确性,删除了不必要的省略号
[brake.py]: 更正了之前的数据copy错误重新优化了estop处是否达到指定百分比的判定逻辑
1. [brake.py/aio.py]: 将sto修改为estop
2. [brake.py] 修改了速度计算逻辑新版本的vel列数据遵循如下规则av = vel * 180 / pi根据av再计算speed
3. [brake.py] 将threshold修改为常量50
4. [brake.py] 提高了输出提示语的明确性,删除了不必要的省略号
5. [brake.py] 更正了之前的数据copy错误重新优化了estop处是否达到指定百分比的判定逻辑
v0.1.5.3(2024/06/14)
[aio.py]: 修改w_param为84适配14寸电脑屏幕
[brake.py]: 将判定合规逻辑修改为角速度超过指定角速度的95%
[README.md]: 稍作修改,包括打包方式,功能特性等
1. [aio.py] 修改w_param为84适配14寸电脑屏幕
2. [brake.py] 将判定合规逻辑修改为角速度超过指定角速度的95%
3. [README.md] 稍作修改,包括打包方式,功能特性等
v0.1.6.0(2024/06/15)
[aio.py] 新增wavelogger处理界面
[wavelogger.py] 新增精度数据处理模块
v0.1.6.1(2024/06/16)
1. [wavelogger.py] bugfix single_file_proc函数中修改_start起始点的计算逻辑
2. [wavelogger.py] bugfix find_point函数中当判断条件为临界值 2.0 的时候针对forward和backward两种情况对row_target做与判断逻辑相同的处理目的是避免形成死循环
v0.1.6.2(2024/06/16)
1. [current.py] 修改了max/avg相关功能中对于返回值的处理逻辑并在输出框以行的形式打印出来
v0.1.6.3(2024/06/18)
1. [current.py] 适配电机电流中速度使用hw_joint_vel_feedback的数据取消对device_servo_vel_feedback的支持后续所有涉及到速度相关的数据均已前者为准现已完成对单轴和场景的适配
> WARNING目前版本的电机电流程序还支持DriverMaster采集的数据处理等明确后将不再支持也即所有的电机电流数据工业+协作),都是用诊断曲线来采集

View File

@ -6,8 +6,8 @@ VSVersionInfo(
ffi=FixedFileInfo(
# filevers and prodvers should be always a tuple with four items: (1, 2, 3, 4)
# Set not needed items to zero 0.
filevers=(0, 1, 5, 3),
prodvers=(0, 1, 5, 3),
filevers=(0, 1, 6, 3),
prodvers=(0, 1, 6, 3),
# Contains a bitmask that specifies the valid bits 'flags'r
mask=0x3f,
# Contains a bitmask that specifies the Boolean attributes of the file.
@ -31,12 +31,12 @@ VSVersionInfo(
'040904b0',
[StringStruct('CompanyName', 'Rokae - https://www.rokae.com/'),
StringStruct('FileDescription', 'All in one automatic toolbox'),
StringStruct('FileVersion', '0.1.5.3 (2024-06-14)'),
StringStruct('FileVersion', '0.1.6.3 (2024-06-18)'),
StringStruct('InternalName', 'AIO.exe'),
StringStruct('LegalCopyright', '© 2024-2024 Manford Fan'),
StringStruct('OriginalFilename', 'AIO.exe'),
StringStruct('ProductName', 'AIO'),
StringStruct('ProductVersion', '0.1.5.3 (2024-06-14)')])
StringStruct('ProductVersion', '0.1.6.3 (2024-06-18)')])
]),
VarFileInfo([VarStruct('Translation', [1033, 1200])])
]

View File

@ -1 +1 @@
0.1.5.3 @ 06/14/2024
0.1.6.3 @ 06/18/2024

View File

@ -3,7 +3,7 @@ from os import getcwd
from threading import Thread
import tkinter.messagebox
import customtkinter
import brake, current, iso
import brake, current, iso, wavelogger
from time import time, strftime, localtime
from urllib.request import urlopen
from socket import setdefaulttimeout
@ -72,7 +72,7 @@ class App(customtkinter.CTk):
btns['log']['btn'].configure(command=lambda: self.thread_it(self.func_log_callback))
btns['end']['btn'].configure(command=lambda: self.thread_it(self.func_end_callback))
# create version info
self.label_version = customtkinter.CTkLabel(self.frame_func, justify='left', text="Vers: 0.1.5.3\nDate: 06/14/2024", font=self.my_font, text_color="#4F4F4F")
self.label_version = customtkinter.CTkLabel(self.frame_func, justify='left', text="Vers: 0.1.6.3\nDate: 06/18/2024", font=self.my_font, text_color="#4F4F4F")
self.frame_func.rowconfigure(6, weight=1)
self.label_version.grid(row=6, column=0, padx=20, pady=20, sticky='s')
# =====================================================================
@ -82,7 +82,7 @@ class App(customtkinter.CTk):
self.tabview.add("Data Process")
self.tabview.add("Automatic Test")
# create main menu
self.menu_main_dp = customtkinter.CTkOptionMenu(self.tabview.tab('Data Process'), values=["INIT", "brake", "current", "iso"], font=self.my_font, text_color='yellow', button_color='red', fg_color='green', command=self.func_main_callback)
self.menu_main_dp = customtkinter.CTkOptionMenu(self.tabview.tab('Data Process'), values=["init", "brake", "current", "iso", "wavelogger"], font=self.my_font, text_color='yellow', button_color='red', fg_color='green', command=self.func_main_callback)
self.menu_main_dp.grid(row=1, column=1, sticky='we', padx=5, pady=5)
self.menu_main_dp.set("Start Here!")
# create sub menu
@ -169,7 +169,7 @@ class App(customtkinter.CTk):
elif widgit in ['trqh',]:
widgits[widgit]['label'].configure(text_color="red")
widgits[widgit]['optionmenu'].configure(state='normal')
elif func_name == 'iso':
elif func_name == 'iso' or func_name == 'wavelogger':
for widgit in widgits:
if widgit in ['path',]:
widgits[widgit]['label'].configure(text_color='red')
@ -301,6 +301,15 @@ class App(customtkinter.CTk):
return 3, path
else:
return 0, 0
# =======================================================
elif func_name == 'wavelogger':
path = widgits['path']['entry'].get().strip()
c1 = exists(path)
if c1:
return 4, path
else:
return 0, 0
# =======================================================
else:
return 0, 0
@ -309,13 +318,15 @@ class App(customtkinter.CTk):
self.textbox.delete(index1='1.0', index2='end')
flag, *args = self.check_param()
func_dict = {1: brake.main, 2: current.main, 3: iso.main}
func_dict = {1: brake.main, 2: current.main, 3: iso.main, 4: wavelogger.main}
if flag == 1:
func_dict[flag](path=args[0], av=args[1], rr=args[2], axis=args[3], vel=args[4], trq=args[5], estop=args[6], w2t=self.write2textbox)
elif flag == 2:
func_dict[flag](path=args[0], sub=args[1], rcs=args[2], vel=args[3], trq=args[4], trqh=args[5], dur=args[6], rpm=args[7], w2t=self.write2textbox)
elif flag == 3:
func_dict[flag](path=args[0], w2t=self.write2textbox)
elif flag == 4:
func_dict[flag](path=args[0], w2t=self.write2textbox)
else:
tkinter.messagebox.showerror(title="参数错误", message="请检查对应参数是否填写正确!", )

View File

@ -81,7 +81,7 @@ def initialization(path, sub, w2t):
def current_max(data_files, rcs, trqh, w2t):
current = {1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0}
current = {1: [], 2: [], 3: [], 4: [], 5: [], 6: [], 7: []}
for data_file in data_files:
if data_file.endswith('.data'):
df = read_csv(data_file, sep='\t')
@ -96,15 +96,23 @@ def current_max(data_files, rcs, trqh, w2t):
scale = 1 if data_file.endswith('.csv') else 1000
_ = abs(c_max/scale*rca)
current[axis] = _
current[axis].append(_)
w2t(f"{data_file}: {_:.4f}")
w2t("【MAX】数据处理完毕......")
for axis, cur in current.items():
if not cur:
continue
else:
w2t(f"{axis}轴数据:", 1, 0, 'purple')
for value in cur:
w2t(f"{value:.4f} ", 1, 0, 'purple')
w2t('')
w2t("\n【MAX】数据处理完毕......")
return current
def current_avg(data_files, rcs, trqh, w2t):
current = {1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0}
current = {1: [], 2: [], 3: [], 4: [], 5: [], 6: [], 7: []}
for data_file in data_files:
if data_file.endswith('.data'):
df = read_csv(data_file, sep='\t')
@ -120,10 +128,18 @@ def current_avg(data_files, rcs, trqh, w2t):
scale = 1 if data_file.endswith('.csv') else 1000
_ = (abs(c_avg)+c_std)/scale*rca
current[axis] = _
current[axis].append(_)
w2t(f"{data_file}: {_:.4f}")
w2t("【AVG】数据处理完毕......")
for axis, cur in current.items():
if not cur:
continue
else:
w2t(f"{axis}轴数据:", 1, 0, 'purple')
for value in cur:
w2t(f"{value:.4f} ", 1, 0, 'purple')
w2t('')
w2t("\n【AVG】数据处理完毕......")
return current
@ -216,10 +232,12 @@ def p_single(wb, single, vel, trq, rpm, w2t):
axis = int(data_file.split('\\')[-1].split('_')[0].removeprefix('j'))
shtname = f"J{axis}"
ws = wb[shtname]
addition = 1
set_option("display.precision", 2)
if data_file.endswith('.data'):
df = read_csv(data_file, sep='\t')
rr = float(wb['统计'].cell(row=2, column=axis+1).value)
addition = 180 / 3.1415926 * 60 / 360 * rr
elif data_file.endswith('.csv'):
df = read_csv(data_file, sep=',', encoding='gbk', header=8)
csv_reader = reader(open(data_file))
@ -233,7 +251,7 @@ def p_single(wb, single, vel, trq, rpm, w2t):
ws["H11"] = cycle
col_names = list(df.columns)
df_1 = df[col_names[vel-1]].multiply(rpm)
df_1 = df[col_names[vel-1]].multiply(rpm*addition)
df_2 = df[col_names[trq-1]].multiply(scale)
df = concat([df_1, df_2], axis=1)
@ -293,10 +311,12 @@ def p_scenario(wb, single, vel, trq, rpm, dur, w2t):
axis = int(data_file.split('\\')[-1].split('_')[0].removeprefix('j'))
shtname = f"J{axis}"
ws = wb[shtname]
addition = 1
set_option("display.precision", 2)
if data_file.endswith('.data'):
df = read_csv(data_file, sep='\t')
rr = float(wb['统计'].cell(row=2, column=axis+1).value)
addition = 180 / 3.1415926 * 60 / 360 * rr
elif data_file.endswith('.csv'):
df = read_csv(data_file, sep=',', encoding='gbk', header=8)
csv_reader = reader(open(data_file))
@ -310,7 +330,7 @@ def p_scenario(wb, single, vel, trq, rpm, dur, w2t):
ws["H11"] = cycle
col_names = list(df.columns)
df_1 = df[col_names[vel-1]].multiply(rpm)
df_1 = df[col_names[vel-1]].multiply(rpm*addition)
df_2 = df[col_names[trq-1]].multiply(scale)
df = concat([df_1, df_2], axis=1)

186
aio/code/wavelogger.py Normal file
View File

@ -0,0 +1,186 @@
import os
import random
from pandas import read_csv
from csv import reader
from sys import argv
from os.path import exists
from os import scandir, remove
from openpyxl import Workbook
from random import randint
def traversal_files(path, w2t):
# 功能:以列表的形式分别返回指定路径下的文件和文件夹,不包含子目录
# 参数:路径
# 返回值:路径下的文件夹列表 路径下的文件列表
if not exists(path):
msg = f'数据文件夹{path}不存在,请确认后重试......'
w2t(msg, 0, 1, 'red')
else:
dirs = []
files = []
for item in scandir(path):
if item.is_dir():
dirs.append(item.path)
elif item.is_file():
files.append(item.path)
return dirs, files
def find_point(bof, step, pos, data_file, flag, df, row, w2t):
# bof: backward or forward
# pos: used for debug
# flag: greater than or lower than
if flag == 'gt':
while 0 < row < df.index[-1]-100:
_value = df.iloc[row, 2]
if _value > 2:
if bof == 'backward':
row -= step
elif bof == 'forward':
row += step
continue
else:
if bof == 'backward':
row_target = row - step
elif bof == 'forward':
row_target = row + step
break
else:
if bof == 'backward':
w2t(f"[{pos}] 在 {data_file} 中,无法正确识别数据,需要确认...", 0, 2, 'red')
elif bof == 'forward':
row_target = row + 100
elif flag == 'lt':
while 0 < row < df.index[-1]-100:
_value = df.iloc[row, 2]
if _value < 2:
if bof == 'backward':
row -= step
elif bof == 'forward':
row += step
continue
else:
if bof == 'backward':
row_target = row - step
elif bof == 'forward':
row_target = row + step
break
else:
if bof == 'backward':
w2t(f"[{pos}] 在 {data_file} 中,无法正确识别数据,需要确认...", 0, 3, 'red')
elif bof == 'forward':
row_target = row + 100
return row_target
def get_cycle_info(data_file, df, row, step, w2t):
# end -> middle: low
# middle -> start: high
# 1. 从最后读取数据无论是大于1还是小于1都舍弃找到相反的值的起始点
# 2. 从起始点,继续往前寻找,找到与之数值相反的中间点
# 3. 从中间点,继续往前寻找,找到与之数值相反的结束点,至此,得到了高低数值的时间区间以及一轮的周期时间
if df.iloc[row, 2] < 2:
row = find_point('backward', step, 'a1', data_file, 'lt', df, row, w2t)
_row = find_point('backward', step, 'a2', data_file, 'gt', df, row, w2t)
_row = find_point('backward', step, 'a3', data_file, 'lt', df, _row, w2t)
row_end = find_point('backward', step, 'a4', data_file, 'gt', df, _row, w2t)
row_middle = find_point('backward', step, 'a5', data_file, 'lt', df, row_end, w2t)
row_start = find_point('backward', step, 'a6', data_file, 'gt', df, row_middle, w2t)
return row_end-row_middle, row_middle-row_start, row_end-row_start
def initialization(path, w2t):
_, data_files = traversal_files(path, w2t)
for data_file in data_files:
if not data_file.lower().endswith('.csv'):
w2t(f"{data_file} 文件后缀错误,只允许 .csv 文件,需要确认!", 0, 1, 'red')
return data_files
def preparation(data_file, wb, w2t):
shtname = data_file.split('\\')[-1].split('.')[0]
ws = wb.create_sheet(shtname)
csv_reader = reader(open(data_file))
i = 0
begin = 70
for row in csv_reader:
i += 1
if i == 1:
begin = int(row[1])
break
df = read_csv(data_file, sep=',', encoding='gbk', skip_blank_lines=False, header=begin - 1, on_bad_lines='warn')
low, high, cycle = get_cycle_info(data_file, df, df.index[-1]-110, 5, w2t)
return ws, df, low, high, cycle
def single_file_proc(ws, data_file, df, low, high, cycle, w2t):
_row = _row_lt = _row_gt = count = 1
_step = 5
_data = {}
row_max = df.index[-1]-100
print(data_file)
while _row < row_max:
if count not in _data.keys():
_data[count] = []
_value = df.iloc[_row, 2]
if _value < 2:
_row_lt = find_point('forward', _step, 'c'+str(_row), data_file, 'lt', df, _row, w2t)
_start = int(_row_gt + (_row_lt - _row_gt - 50) / 2)
_end = _start + 50
value = df.iloc[_start:_end, 2].mean() + df.iloc[_start:_end, 2].std()
_data[count].append(value)
else:
_row_gt = find_point('forward', _step, 'c'+str(_row), data_file, 'gt', df, _row, w2t)
if _row_gt - _row_lt > cycle * 2:
count += 1
_row = max(_row_gt, _row_lt)
for i in range(2, 10):
ws.cell(row=1, column=i).value = f"{i-1}次测试"
ws.cell(row=i, column=1).value = f"{i-1}次精度变化"
print(_data)
for i in sorted(_data.keys()):
_row = 2
_column = i + 1
for value in _data[i]:
ws.cell(row=_row, column=_column).value = float(value)
_row += 1
def execution(data_files, w2t):
wb = Workbook()
for data_file in data_files:
ws, df, low, high, cycle = preparation(data_file, wb, w2t)
print(f"low = {low}")
print(f"high = {high}")
print(f"cycle = {cycle}")
single_file_proc(ws, data_file, df, low, high, cycle, w2t)
wd = data_files[0].split('\\')
del wd[-1]
wd = '\\'.join(wd)
filename = wd + '\\result.xlsx'
wb.save(filename)
wb.close()
w2t('----------------------------------------')
w2t('所有文件均已处理完毕')
def main(path, w2t):
data_files = initialization(path, w2t)
execution(data_files, w2t)
if __name__ == '__main__':
main(path=argv[1], w2t=argv[2])