diff --git a/assets/files/examples/自动测试/电机电流/NB4h_R580_3G/NB4h_R580_3G.zip b/assets/files/examples/自动测试/电机电流/NB4h_R580_3G/NB4h_R580_3G.zip index ef5993e..59cb029 100644 Binary files a/assets/files/examples/自动测试/电机电流/NB4h_R580_3G/NB4h_R580_3G.zip and b/assets/files/examples/自动测试/电机电流/NB4h_R580_3G/NB4h_R580_3G.zip differ diff --git a/assets/files/projects/NB4h_R580_3G.zip b/assets/files/projects/NB4h_R580_3G.zip index 3ef67a6..59cb029 100644 Binary files a/assets/files/projects/NB4h_R580_3G.zip and b/assets/files/projects/NB4h_R580_3G.zip differ diff --git a/assets/files/projects/autotest.xml b/assets/files/projects/autotest.xml index 6f1db45..8b12819 100644 --- a/assets/files/projects/autotest.xml +++ b/assets/files/projects/autotest.xml @@ -17,7 +17,7 @@ - + @@ -37,7 +37,7 @@ - + @@ -57,7 +57,7 @@ - + @@ -77,7 +77,7 @@ - + @@ -97,7 +97,7 @@ - + @@ -117,7 +117,7 @@ - + @@ -137,7 +137,7 @@ - + @@ -157,7 +157,7 @@ - + @@ -177,7 +177,7 @@ - + @@ -197,7 +197,7 @@ - + @@ -217,7 +217,7 @@ - + @@ -237,7 +237,7 @@ - + @@ -257,7 +257,7 @@ - + @@ -277,7 +277,7 @@ - + @@ -297,7 +297,7 @@ - + @@ -317,7 +317,7 @@ - + @@ -337,7 +337,7 @@ - + @@ -357,7 +357,7 @@ - + @@ -377,7 +377,7 @@ - + @@ -397,87 +397,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -497,7 +417,7 @@ - + @@ -517,7 +437,7 @@ - + @@ -537,7 +457,7 @@ - + @@ -557,7 +477,7 @@ - + @@ -577,7 +497,7 @@ - + @@ -597,7 +517,7 @@ - + @@ -617,7 +537,7 @@ - + @@ -637,7 +557,7 @@ - + @@ -657,7 +577,7 @@ - + @@ -677,7 +597,7 @@ - + @@ -697,7 +617,7 @@ - + @@ -717,7 +637,7 @@ - + @@ -737,7 +657,7 @@ - + @@ -757,7 +677,7 @@ - + @@ -777,7 +697,7 @@ - + @@ -797,7 +717,7 @@ - + @@ -817,7 +737,7 @@ - + @@ -837,7 +757,7 @@ - + @@ -857,35 +777,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -905,7 +797,7 @@ - + @@ -925,27 +817,7 @@ - - - - - - - - - - - - - - - - - - - - - + diff --git a/code/aio.py b/code/aio.py index 80815f1..2c78cee 100644 --- a/code/aio.py +++ b/code/aio.py @@ -224,9 +224,7 @@ class App: for idx in sorted(infos.keys()): for k, v in infos[idx].items(): self.__w2t(f"{k}: {v}\n") - t = threading.Thread(target=get_robot_info) - t.daemon = True - t.start() + self.__thread_it(get_robot_info) def __trig_estop(self): def trig_estop(): @@ -234,9 +232,9 @@ class App: self.text_output.delete("1.0", "end") self.__w2t("触发软急停信号已发送...\n") clibs.c_md.r_soft_estop(0) - t = threading.Thread(target=trig_estop) - t.daemon = True - t.start() + clibs.running = False + raise Exception("StopProcRunning") + self.__thread_it(trig_estop) def __reset_estop(self): def reset_estop(): @@ -245,15 +243,14 @@ class App: self.__w2t("解除软急停信号已发送...\n") clibs.c_md.r_soft_estop(1) clibs.c_md.r_clear_alarm() - t = threading.Thread(target=reset_estop) - t.daemon = True - t.start() + self.__thread_it(reset_estop) - def __thread_it(self, func, *args): + @staticmethod + def __thread_it(func, *args): """ 将函数打包进线程,必须使用 lambda """ - self.myThread = threading.Thread(target=func, args=args) - self.myThread.daemon = True # 主线程退出就直接让子线程跟随退出,不论是否运行完成。 - self.myThread.start() + t = threading.Thread(target=func, args=args) + t.daemon = True # 主线程退出就直接让子线程跟随退出,不论是否运行完成。 + t.start() def __set_geometry(self, widget, width, height): x = self.root.winfo_x() @@ -327,7 +324,8 @@ class App: def init_op(): if self.__is_running("开始") == "running": - return + return "running" + self.text_output.delete("1.0", ctk.END) self.tabview_bottom.set("输出") clibs.tl_prg = self.__toplevel_progress @@ -341,7 +339,9 @@ class App: self.__get_realtime_log() def exec_function(): - init_op() + if init_op() == "running": + return + if self.tabview_top.get() == "数据处理": clibs.running = True clibs.data_dp = get_data_dp() @@ -368,37 +368,44 @@ class App: self.entry_ip_atv.set("192.168.0.160") self.entry_path_atv.set("数据文件夹路径") self.entry_path_atv.set("数据文件夹路径") + try: + clibs.c_hr.close() + clibs.c_md.close() + except Exception: + ... - if self.__is_running("重置") == "running": - return + def do_reset(): + if self.__is_running("重置") == "running": + return - if clibs.db_state == "readwrite": - res = messagebox.askyesno(title="状态重置", message="这将清空本次所有的输出以及日志记录,且不可恢复,请确认!", default=messagebox.NO, icon=messagebox.WARNING) - if res: - self.text_output.delete("1.0", ctk.END) - try: - clibs.lock.acquire(True) - clibs.cursor.execute("delete from logs") - clibs.cursor.execute("delete from sqlite_sequence") # 主键归零 - except Exception: - self.__w2t("主键归零操作失败", "red") - finally: - clibs.lock.release() + if clibs.db_state == "readwrite": + res = messagebox.askyesno(title="状态重置", message="这将清空本次所有的输出以及日志记录,且不可恢复,请确认!", default=messagebox.NO, icon=messagebox.WARNING) + if res: + self.text_output.delete("1.0", ctk.END) + try: + clibs.lock.acquire(True) + clibs.cursor.execute("delete from logs") + clibs.cursor.execute("delete from sqlite_sequence") # 主键归零 + except Exception: + ... + finally: + clibs.lock.release() - self.treeview_logs.delete(*self.treeview_logs.get_children()) - self.label_pages_logs.set("-.-.-.-.-.-") - reset_methods() - elif clibs.db_state == "readonly": - res = messagebox.askyesno(title="状态重置", message="这将清空本次所有的输出以及恢复本次的日志记录,请确认!", default=messagebox.NO, icon=messagebox.INFO) - if res: - self.text_output.delete("1.0", ctk.END) - clibs.cursor.close() - clibs.conn.close() - clibs.conn = self.conn - clibs.cursor = self.cursor - clibs.db_state = "readwrite" - self.__get_realtime_log() - reset_methods() + self.treeview_logs.delete(*self.treeview_logs.get_children()) + self.label_pages_logs.set("-.-.-.-.-.-") + reset_methods() + elif clibs.db_state == "readonly": + res = messagebox.askyesno(title="状态重置", message="这将清空本次所有的输出以及恢复本次的日志记录,请确认!", default=messagebox.NO, icon=messagebox.INFO) + if res: + self.text_output.delete("1.0", ctk.END) + clibs.cursor.close() + clibs.conn.close() + clibs.conn = self.conn + clibs.cursor = self.cursor + clibs.db_state = "readwrite" + self.__get_realtime_log() + reset_methods() + self.__thread_it(do_reset) @clibs.db_lock def __get_realtime_log(self): @@ -995,9 +1002,21 @@ class App: ... finally: self.btn_conn.configure(state="normal", fg_color="#979DA2") - t = threading.Thread(target=conn_or_disconn) - t.daemon = True - t.start() + self.__thread_it(conn_or_disconn) + + def pending_a_while(event): + def pending(): + self.btn_start.configure(state="disabled") + time.sleep(2) + self.btn_start.configure(state="normal") + self.__thread_it(pending) + + def change_sub_at(event): + def detect_change(): + if self.om_main_at.get() == "current": + self.om_sub_at.set("tool100") + self.root.after(200, detect_change) + # ======================================================================== self.root.rowconfigure(0, weight=1) self.root.rowconfigure(1, weight=99) @@ -1013,6 +1032,7 @@ class App: self.label_logo.grid(row=0, column=0, sticky="new", padx=20, pady=(20, 40)) self.btn_start.grid(row=1, column=0, sticky="new", padx=15, pady=1, ipady=4) self.btn_reset_state.grid(row=2, column=0, sticky="new", padx=15, pady=(0, 1), ipady=4) + self.btn_start.bind("", pending_a_while) # ======================================================================== # self.tabview_top.tab("数据处理").grid_rowconfigure([0, 1], weight=1) self.tabview_top.tab("数据处理").grid_columnconfigure(11, weight=1) @@ -1052,6 +1072,8 @@ class App: self.btn_trig_estop.grid(row=0, column=1, padx=(0, 10), pady=0) self.btn_reset_estop.grid(row=0, column=2, padx=(0, 10), pady=0) + # self.om_main_at.bind("<>", change_sub_at) + self.om_main_at.bind("", change_sub_at) self.entry_path_at.bind("", select_path) self.entry_ip_at.bind("", show_popupmenu_ip, add="+") self.btn_conn.bind("", conn_change) @@ -1102,9 +1124,7 @@ class App: def show(self): if self.server_vers: # if True: - t = threading.Thread(target=self.__detect_network) - t.daemon = True - t.start() + self.__thread_it(self.__detect_network) self.root.mainloop() diff --git a/code/automatic_test/do_brake.py b/code/automatic_test/do_brake.py index ed25197..052735e 100644 --- a/code/automatic_test/do_brake.py +++ b/code/automatic_test/do_brake.py @@ -1,3 +1,4 @@ +import random import time import os import paramiko @@ -111,10 +112,15 @@ def gen_result_file(path, axis, t_end, reach, load, speed, speed_target, rounds, d_stop.extend(d_item) idx = d_stop.index(0) - av_estop = sum(d_vel[idx - 20:idx])/20 * clibs.RADIAN + av_estop = abs(sum(d_vel[idx - 20:idx])/20 * clibs.RADIAN) if av_estop / speed_target < threshold: w2t(f"[av_estop: {av_estop:.2f} | shouldbe: {speed_target:.2f}] 本次触发 ESTOP 时未采集到指定百分比的最大速度,即将重试!\n", "#8A2BE2") - return False + clibs.count += 1 + if clibs.count < 3: + return "retry" + else: + clibs.count = 0 + w2t(f"尝试三次后仍无法获取正确数据,本次数据无效,继续执行...\n", "red") df1 = pandas.DataFrame.from_dict({"hw_joint_vel_feedback": d_vel}) df2 = pandas.DataFrame.from_dict({"device_servo_trq_feedback": d_trq}) @@ -122,14 +128,18 @@ def gen_result_file(path, axis, t_end, reach, load, speed, speed_target, rounds, df = pandas.concat([df1, df2, df3], axis=1) filename = f"{path}/j{axis}/reach{reach}_load{load}_speed{speed}/reach{reach}_load{load}_speed{speed}_{rounds}.data" df.to_csv(filename, sep="\t", index=False) - return True + + +def change_curve_state(hr, stat_1, stat_2): + display_pdo_params = [{"name": name, "channel": chl} for name in ["hw_joint_vel_feedback", "device_servo_trq_feedback"] for chl in range(6)] + display_pdo_params.append({"name": "device_safety_estop", "channel": 0}) + hr.execution("diagnosis.open", open=stat_1, display_open=stat_2, overrun=True, turn_area=True, delay_motion=False) + hr.execution("diagnosis.set_params", display_pdo_params=display_pdo_params, frequency=50, version="1.4.1") def run_rl(path, sub, hr, md, config_file, prj_file, result_dirs, avs, w2t): count, total, speed_target = 0, 63, 0 prj_name = prj_file.split("/")[-1].split(".")[0] - display_pdo_params = [{"name": name, "channel": chl} for name in ["hw_joint_vel_feedback", "device_servo_trq_feedback"] for chl in range(6)] - display_pdo_params.append({"name": "device_safety_estop", "channel": 0}) wb = openpyxl.load_workbook(config_file, read_only=True) ws = wb["Target"] write_diagnosis = float(ws.cell(row=2, column=2).value) @@ -147,8 +157,6 @@ def run_rl(path, sub, hr, md, config_file, prj_file, result_dirs, avs, w2t): else: w2t("configs.xlsx 中 Target 页面 B5 单元格填写不正确,检查后重新运行...", "red", "DirectionError") - hr.execution("diagnosis.open", open=True, display_open=True, overrun=True, turn_area=True, delay_motion=False) - hr.execution("diagnosis.set_params", display_pdo_params=display_pdo_params, frequency=50, version="1.4.1") for condition in result_dirs: reach = condition.split("_")[0].removeprefix("reach") load = condition.split("_")[1].removeprefix("load") @@ -226,12 +234,13 @@ def run_rl(path, sub, hr, md, config_file, prj_file, result_dirs, avs, w2t): if (time.time() - t_start) > 20: w2t("20s 内未收到机器人的运行信号,需要确认 RL 程序编写正确并正常执行...", "red", "ReadySignalTimeoutError") # 4. 找出最大速度,传递给RL程序,最后清除相关记录 - time.sleep(10) # 消除前 10s 的不稳定数据 + time.sleep(5) # 消除前 5s 的不稳定数据 + change_curve_state(hr, True, True) start_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time())) time.sleep(get_init_speed) # 指定时间后获取实际【正|负】方向的最大速度,可通过configs.xlsx配置 end_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time())) hr.execution("rl_task.stop", tasks=["brake"]) - time.sleep(5) # 确保数据都拿到 + change_curve_state(hr, False, False) # 找出最大速度 @clibs.db_lock @@ -284,12 +293,13 @@ def run_rl(path, sub, hr, md, config_file, prj_file, result_dirs, avs, w2t): else: w2t("3s 内未收到机器人的运行信号,需要确认 RL 程序配置正确并正常执行...", "red", "ReadySignalTimeoutError") - time.sleep(10) # 排除从其他位姿到零点位姿,再到轴极限位姿的时间 + time.sleep(5+random.randint(1, 5)) # 排除从其他位姿到零点位姿,再到轴极限位姿的时间 def exec_brake(): flag, start, data, record = True, time.time(), None, None + change_curve_state(hr, True, True) while flag: - time.sleep(0.1) + time.sleep(0.05) if time.time() - start > 20: w2t("20s 内未触发急停,需排查......", "red", "BrakeTimeoutError") @@ -306,24 +316,22 @@ def run_rl(path, sub, hr, md, config_file, prj_file, result_dirs, avs, w2t): continue speed_moment = clibs.RADIAN * sum(item["value"]) / len(item["value"]) - if (pon == "positive" and speed_moment > 0) or (pon == "negative" and speed_moment < 0): - if abs(speed_moment) > speed_target * 0.95: + if abs(speed_moment) > speed_target * 0.95: + if (pon == "positive" and speed_moment > 0) or (pon == "negative" and speed_moment < 0): clibs.c_ec.setdo_value(io_name, "false") - time.sleep(3) + time.sleep(2) + change_curve_state(hr, False, False) flag = False - break return time.time() t_end = exec_brake() # 6. 保留数据并处理输出 ret = gen_result_file(path, axis, t_end, reach, load, speed, speed_target, rounds, w2t) - if ret: + if ret != "retry": + clibs.count = 0 break - else: w2t(f"\n{sub.removeprefix("tool")}%负载的制动性能测试执行完毕,如需采集其他负载,须切换负载类型,并更换其他负载,重新执行。\n", "green") - hr.execution("diagnosis.open", open=False, display_open=False, overrun=True, turn_area=True, delay_motion=False) - hr.execution("diagnosis.set_params", display_pdo_params=[], frequency=50, version="1.4.1") def main(): diff --git a/code/automatic_test/do_current.py b/code/automatic_test/do_current.py index c29595d..4cbd504 100644 --- a/code/automatic_test/do_current.py +++ b/code/automatic_test/do_current.py @@ -121,9 +121,14 @@ def gen_result_file(path, number, start_time, end_time, scenario_time): p.start() +def change_curve_state(hr, stat_1, stat_2): + display_pdo_params = [{"name": name, "channel": chl} for name in ["hw_joint_vel_feedback", "device_servo_trq_feedback", "hw_sensor_trq_feedback"] for chl in range(6)] + hr.execution("diagnosis.open", open=stat_1, display_open=stat_2, overrun=True, turn_area=True, delay_motion=False) + hr.execution("diagnosis.set_params", display_pdo_params=display_pdo_params, frequency=50, version="1.4.1") + + def run_rl(path, prj_file, hr, md, sub, w2t): prj_name = prj_file.split("/")[-1].split(".")[0] - display_pdo_params = [{"name": name, "channel": chl} for name in ["hw_joint_vel_feedback", "device_servo_trq_feedback", "hw_sensor_trq_feedback"] for chl in range(6)] c_regular = [ "scenario(0, j1_p, j1_n, p_speed, p_tool, i_tool)", "scenario(0, j2_p, j2_n, p_speed, p_tool, i_tool)", @@ -155,8 +160,6 @@ def run_rl(path, prj_file, hr, md, sub, w2t): conditions, disc = c_inertia, disc_inertia # 打开诊断曲线,触发软急停,并解除,目的是让可能正在运行着的机器停下来 - hr.execution("diagnosis.open", open=True, display_open=True, overrun=True, turn_area=True, delay_motion=False) - hr.execution("diagnosis.set_params", display_pdo_params=display_pdo_params, frequency=50, version="1.4.1") md.r_soft_estop(0) md.r_soft_estop(1) md.r_clear_alarm() @@ -200,6 +203,7 @@ def run_rl(path, prj_file, hr, md, sub, w2t): # 4. 执行采集 time.sleep(10) # 消除前 10s 的不稳定数据 + change_curve_state(hr, True, True) start_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time())) single_time, stall_time, scenario_time = 40, 10, 0 if number < 6: # 单轴 @@ -222,15 +226,14 @@ def run_rl(path, prj_file, hr, md, sub, w2t): # 5.停止程序运行,保留数据并处理输出 end_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time())) hr.execution("rl_task.stop", tasks=["current"]) - time.sleep(5) # 确保数据都拿到 + time.sleep(2) # 确保数据都拿到 + change_curve_state(hr, False, False) gen_result_file(path, number, start_time, end_time, scenario_time) else: if sub == "tool100": w2t("单轴和场景电机电流采集完毕,如需采集惯量负载,须切换负载类型,并更换惯量负载,重新执行。\n", "green") elif sub == "inertia": w2t("惯量负载电机电流采集完毕,如需采集单轴/场景/保持电机电流,须切换负载类型,并更换偏置负载,重新执行。\n", "green") - hr.execution("diagnosis.open", open=False, display_open=False, overrun=True, turn_area=True, delay_motion=False) - hr.execution("diagnosis.set_params", display_pdo_params=[], frequency=50, version="1.4.1") def main(): diff --git a/code/common/clibs.py b/code/common/clibs.py index 61173bc..7d3bd57 100644 --- a/code/common/clibs.py +++ b/code/common/clibs.py @@ -107,9 +107,7 @@ conn, cursor, w2t, tl_prg, f_records, stop, running = None, None, None, None, No ip_addr = "192.168.0.160" ssh_port, socket_port, xService_port, external_port, modbus_port, upgrade_port = 22, 5050, 6666, 8080, 502, 4567 username, password = "luoshi", "luoshi2019" -interval = 0.5 # interval after actions being triggered, such as modbus/socket/external communication operations -RADIAN = 57.3 # 180 / 3.1415926 -MAX_FRAME_SIZE = 1024 +interval, RADIAN, MAX_FRAME_SIZE, count = 0.5, 57.3, 1024, 0 c_md, c_hr, c_ec, c_pd = None, None, None, None lock = threading.Lock()