记一次“Axis2客户端调用产生大量CLOSE_WAIT连接”的解决过程

本文介绍了一种解决Axis2客户端因长时间未关闭连接而导致大量CLOSE_WAIT状态积累的方法。通过对GC机制的理解与调整,成功避免了连接泄露,提升了系统的稳定性。
部署运行你感兴趣的模型镜像
背景: 程序使用1.6.2版本的axis2做WebService客户端调用。
代码:
public static Object sendRequest(Object req) throws AxisFault
{
	String returnMsg = "";
	RPCServiceClient serviceClient = null;
	serviceClient = new RPCServiceClient();
	Options options = serviceClient.getOptions();
	EndpointReference targetEPR = new EndpointReference(PropertiesUtils.getproperty("send.ws.targetEPR", ""));
	
	options.setTo(targetEPR);
	options.setTimeOutInMilliSeconds(Long.valueOf(PropertiesUtils.getproperty("send.ws.timeout", "30000")));
	//设置客户端关闭连接
	//options.setProperty(HTTPConstants.HEADER_CONNECTION, HTTPConstants.HEADER_CONNECTION_CLOSE);
	
	QName opAddEntry = new QName(PropertiesUtils.getproperty("send.ws.namespace", ""), PropertiesUtils.getproperty("send.ws.method", ""));
	Object[] opAddEntryArgs = new Object[] { req };
	Class[] classes = new Class[] { String.class };
	log.info("发送请求XML:"+req);
	returnMsg = (String) serviceClient.invokeBlocking(opAddEntry, opAddEntryArgs, classes)[0];
	log.info("收到返回XML:"+returnMsg);
	return returnMsg;

}
 现象:程序上线后,观察WebService的调用情况,发现服务端响应很慢(服务端是另一家厂商,动不了他),每个请求延时都在5秒左右,在使用netstate查看连接数的时候,意外发现了程序产生大量CLOSE_WAIT状态的连接,而且数量稳定增长,一个小时就接近300个连接。
#netstat -tnp|grep 7011|grep CLOSE_WAIT|wc -l
#271

 

折腾:发现这个问题后,就开始baidu.然后各种折腾。axis2的HTTPConstants.HEADER_CONNECTION ,

HEADER_CONNECTION_KEEPALIVE参数沾边的都试了遍,无效果。

两个小时过去了,没有进展。

 

看着一直增长的连接数,好象血管在一直流血一样。这样下去内存应该也会增长吧。看看jvm内存是不是有对象泄漏,导致HttpClient没有回收(axis2底层使用这个类发送WebService请求)?

 

jmap -histo 18041 |grep com.xxxxxxx
果然,程序new出来的对象,每一分钟增长一次(对应每分钟循环一次)。

 

 

此时心中有一个大大的问号:  为什么GC没有回收掉?

如果GC回收了,TCP连接会Close吗? 带着这个疑问,用tcpdump监控下tcp连接。

 

jmap -histo:live 18041 |grep com.xxxxxx # live会引起GC:没有找到根据,但确实是这样。
 在GC的瞬间,tcpdump看到客户端发出的reset包,同时CLOSE_WAIT连接都消失了。 

 

 

15:27:50.152698 IP WOOS1.46262 > 137.32.25.62.talon-disc: R 1384067195:1384067195(0) ack 4094862191 win 72 <nop,nop,timestamp 536888568 1094736905>
15:27:50.152710 IP WOOS1.44490 > 137.32.25.62.talon-disc: R 1983758145:1983758145(0) ack 4199192243 win 72 <nop,nop,timestamp 536888568 1094821918>
15:27:50.152723 IP WOOS1.58174 > 137.32.25.62.talon-disc: R 1982916452:1982916452(0) ack 4178184596 win 72 <nop,nop,timestamp 536888568 1094806915>
15:27:50.152738 IP WOOS1.32940 > 137.32.25.62.talon-disc: R 1748662783:1748662783(0) ack 4206843128 win 72 <nop,nop,timestamp 536888568 1094826919>
15:27:50.152762 IP WOOS1.36913 > 137.32.25.62.talon-disc: R 815070216:815070216(0) ack 4200525319 win 72 <nop,nop,timestamp 536888568 1094826919>
15:27:50.152775 IP WOOS1.40743 > 137.32.25.62.talon-disc: R 4116878055:4116878055(0) ack 4209835792 win 72 <nop,nop,timestamp 536888568 1094836922>
15:27:50.152788 IP WOOS1.33267 > 137.32.25.62.talon-disc: R 2893572903:2893572903(0) ack 4218047967 win 72 <nop,nop,timestamp 536888568 1094841922>
15:27:50.152802 IP WOOS1.38644 > 137.32.25.62.talon-disc: R 1232744431:1232744431(0) ack 12002951 win 72 <nop,nop,timestamp 536888568 1094931936>
 
解决问题:
     既然GC可以关闭连接,只要能让jvm即时GC就可以解决问题。 考虑到程序时各种大量的List , Map的对象互相引用,是否是这个原因导致 不能及时GC?(这是工作以来,第一次遇到GC的问题,脑子里关于GC的知识太少)
    于是修改程序,在List , Map 超出作用域时,clear掉。 用jmap观察程序,对象不再无限制增长:增长到一定值后大减,被GC掉。同时连接数也不再无限制增长,最大到30左右连接就会被关闭掉。
教训:
代码中没有考虑GC效率问题,尤其是map,list对象很多,对象间引用关系复杂,导致GC无法及时回收过期对象。   对于java的GC策略也不了解。看来要花些时间了解GC了。
 
 

您可能感兴趣的与本文相关的镜像

Stable-Diffusion-3.5

Stable-Diffusion-3.5

图片生成
Stable-Diffusion

Stable Diffusion 3.5 (SD 3.5) 是由 Stability AI 推出的新一代文本到图像生成模型,相比 3.0 版本,它提升了图像质量、运行速度和硬件效率

#include "main.h" /*----------------------------------------------------------------------------------------------------------*/ // 添加全局变量 ConfigData g_config; uint8_t x_left_running = 0; //手动控制变量 uint8_t x_right_running = 0; //手动控制变量 uint8_t z_up_running = 0; //手动控制变量 uint8_t z_down_running = 0; //手动控制变量 uint8_t levelState; // 液面传感器状态(0:正常 1:异常) // 静态变量保存历史值,用于判断是否变化(仅初始化一次) static int32_t last_temp = 0; // 初始化为极小值,确保首次能触发更新 static int32_t last_temp2 = 0; // 初始化为极小值,确保首次能触发更新 static uint8_t last_level = 0; // 初始化为极大值,确保首次能触发更新 /*----------------------------------------------------------------------------------------------------------*/ // 初始化SD卡文件 FIL step_file; FRESULT fr; FIL log_file; FIL config_file; /*----------------------------------------------------------------------------------------------------------*/ extern volatile uint8_t x_right_limit_triggered; extern volatile uint8_t z_up_limit_triggered; /*----------------------------------------------------------------------------------------------------------*/ // 全局变量:录当前状态、延时开始时间、目标延时 StainState g_stain_state = STAIN_IDLE; // 当前染色状态 uint32_t g_delay_start_tick = 0; // 延时开始的时间戳(毫秒) uint32_t g_current_delay_s = 0; // 当前步骤的目标延时时长(毫秒) /*----------------------------------------------------------------------------------------------------------*/ // 从SD卡读取配置参数(扩展支持新参数) uint8_t read_config_from_sd(ConfigData *config) { FIL config_file; FRESULT fr; char line[4096]; fr = f_open(&config_file, "0:config.txt", FA_READ); delay_ms(5); if (fr != FR_OK) { printf("config2.t0.txt=\"配置文件打开失败\"\xff\xff\xff"); config->x_pos1 = 160000; config->x_pos2 = 510000; config->x_pos3 = 960000; config->x_pos4 = 1370000; config->x_pos5 = 1750000; config->x_pos6 = 2080000; config->x_pos7 = 2400000; config->x_pos8 = 2710000; config->x_pos9 = 3020000; config->x_pos10 = 80000; config->x_offset = 0; config->z_soak = 230000; config->z_lift = 480000; config->z_lift1 = 0; config->t_fix = 60; // 30min config->t_rinse = 10; // 30s config->t_acid = 60; // 30min config->t_stain = 60; // 45min config->t_dehy1 = 60; // 10min config->t_dehy2 = 60; config->t_dehy3 = 60; config->t_dehy4 = 60; config->t_dry = 10; // 20min write_config_to_sd(config); return 1; } delay_ms(5); while (f_gets(line, sizeof(line), &config_file)) { if (strstr(line, "X_POS1:")) sscanf(line, "X_POS1:%ld", &config->x_pos1); else if (strstr(line, "X_POS2:")) sscanf(line, "X_POS2:%ld", &config->x_pos2); else if (strstr(line, "X_POS3:")) sscanf(line, "X_POS3:%ld", &config->x_pos3); else if (strstr(line, "X_POS4:")) sscanf(line, "X_POS4:%ld", &config->x_pos4); else if (strstr(line, "X_POS5:")) sscanf(line, "X_POS5:%ld", &config->x_pos5); else if (strstr(line, "X_POS6:")) sscanf(line, "X_POS6:%ld", &config->x_pos6); else if (strstr(line, "X_POS7:")) sscanf(line, "X_POS7:%ld", &config->x_pos7); else if (strstr(line, "X_POS8:")) sscanf(line, "X_POS8:%ld", &config->x_pos8); else if (strstr(line, "X_POS9:")) sscanf(line, "X_POS9:%ld", &config->x_pos9); else if (strstr(line, "X_POS10:")) sscanf(line, "X_POS10:%ld", &config->x_pos10); else if (strstr(line, "X_OFFSET:")) sscanf(line, "X_OFFSET:%ld", &config->x_offset); else if (strstr(line, "Z_SOAK:")) sscanf(line, "Z_SOAK:%ld", &config->z_soak); else if (strstr(line, "Z_LIFT:")) sscanf(line, "Z_LIFT:%ld", &config->z_lift); else if (strstr(line, "Z_LIFT1:")) sscanf(line, "Z_LIFT1:%ld", &config->z_lift1); else if (strstr(line, "T_FIX:")) sscanf(line, "T_FIX:%lu", &config->t_fix); else if (strstr(line, "T_RINSE:")) sscanf(line, "T_RINSE:%lu", &config->t_rinse); else if (strstr(line, "T_ACID:")) sscanf(line, "T_ACID:%lu", &config->t_acid); else if (strstr(line, "T_STAIN:")) sscanf(line, "T_STAIN:%lu", &config->t_stain); else if (strstr(line, "T_DEHY1:")) sscanf(line, "T_DEHY1:%lu", &config->t_dehy1); else if (strstr(line, "T_DEHY2:")) sscanf(line, "T_DEHY2:%lu", &config->t_dehy2); else if (strstr(line, "T_DEHY3:")) sscanf(line, "T_DEHY3:%lu", &config->t_dehy3); else if (strstr(line, "T_DEHY4:")) sscanf(line, "T_DEHY4:%lu", &config->t_dehy4); else if (strstr(line, "T_DRY:")) sscanf(line, "T_DRY:%lu", &config->t_dry); } f_close(&config_file); return 0; } /*----------------------------------------------------------------------------------------------------------*/ // 写入配置参数到SD卡 void write_config_to_sd(ConfigData *config) { FIL config_file; FRESULT fr; UINT bw; char line[2048]; fr = f_open(&config_file, "0:config.txt", FA_WRITE | FA_CREATE_ALWAYS); if (fr != FR_OK) { printf("config2.b3.txt=\"配置文件打开失败\"\xff\xff\xff"); return; }else if(fr == FR_OK) { f_lseek(&config_file, 0); // 移至文件开头,准备覆盖写入 } sprintf(line, "X_POS1:%ld\r\n", config->x_pos1); f_write(&config_file, line, strlen(line), &bw); sprintf(line, "X_POS2:%ld\r\n", config->x_pos2); f_write(&config_file, line, strlen(line), &bw); sprintf(line, "X_POS3:%ld\r\n", config->x_pos3); f_write(&config_file, line, strlen(line), &bw); sprintf(line, "X_POS4:%ld\r\n", config->x_pos4); f_write(&config_file, line, strlen(line), &bw); sprintf(line, "X_POS5:%ld\r\n", config->x_pos5); f_write(&config_file, line, strlen(line), &bw); sprintf(line, "X_POS6:%ld\r\n", config->x_pos6); f_write(&config_file, line, strlen(line), &bw); sprintf(line, "X_POS7:%ld\r\n", config->x_pos7); f_write(&config_file, line, strlen(line), &bw); sprintf(line, "X_POS8:%ld\r\n", config->x_pos8); f_write(&config_file, line, strlen(line), &bw); sprintf(line, "X_POS9:%ld\r\n", config->x_pos9); f_write(&config_file, line, strlen(line), &bw); sprintf(line, "X_POS10:%ld\r\n", config->x_pos10); f_write(&config_file, line, strlen(line), &bw); sprintf(line, "X_OFFSET:%ld\r\n", config->x_offset); f_write(&config_file, line, strlen(line), &bw); sprintf(line, "Z_SOAK:%ld\r\n", config->z_soak); f_write(&config_file, line, strlen(line), &bw); sprintf(line, "Z_LIFT:%ld\r\n", config->z_lift); f_write(&config_file, line, strlen(line), &bw); sprintf(line, "Z_LIFT1:%ld\r\n", config->z_lift1); f_write(&config_file, line, strlen(line), &bw); sprintf(line, "T_FIX:%lu\r\n", config->t_fix); f_write(&config_file, line, strlen(line), &bw); sprintf(line, "T_RINSE:%lu\r\n", config->t_rinse); f_write(&config_file, line, strlen(line), &bw); sprintf(line, "T_ACID:%lu\r\n", config->t_acid); f_write(&config_file, line, strlen(line), &bw); sprintf(line, "T_STAIN:%lu\r\n", config->t_stain); f_write(&config_file, line, strlen(line), &bw); sprintf(line, "T_DEHY1:%lu\r\n", config->t_dehy1); f_write(&config_file, line, strlen(line), &bw); sprintf(line, "T_DEHY2:%lu\r\n", config->t_dehy2); f_write(&config_file, line, strlen(line), &bw); sprintf(line, "T_DEHY3:%lu\r\n", config->t_dehy3); f_write(&config_file, line, strlen(line), &bw); sprintf(line, "T_DEHY4:%lu\r\n", config->t_dehy4); f_write(&config_file, line, strlen(line), &bw); sprintf(line, "T_DRY:%lu\r\n", config->t_dry); f_write(&config_file, line, strlen(line), &bw); f_sync(&config_file); f_close(&config_file); delay_ms(10); printf("config2.b3.txt=\"配置成功\"\xff\xff\xff"); printf("config2.tm1.en=1\xff\xff\xff"); USART_ClearBuffer(); } void write_log(int32_t x, int32_t z) { FRESULT fr; UINT bw; char log_line[512]; char time_str[64]; // 存储时间字符串 "YYYY-MM-DD HH:MM:SS" // 1. 通过RTC获取当前时间 RTC_Get(); // 2. 格式化时间字符串 sprintf(time_str, "%04d-%02d-%02d %02d:%02d:%02d", calendar.w_year, calendar.w_month, calendar.w_date, calendar.hour, calendar.min, calendar.sec); // 3. 打开日志文件(追加模式) fr = f_open(&log_file, "0:log.txt", FA_WRITE | FA_OPEN_ALWAYS); if (fr != FR_OK) { OLED_ShowString(1, 1, "Log Open Err"); printf("config3.t0.txt=\"追踪文件打开失败\"\xff\xff\xff"); printf("config3.tm3.en=1\xff\xff\xff"); return; } // 4. 移动到文件末尾(准备追加内容) f_lseek(&log_file, f_size(&log_file)); // 5. 格式化日志内容(末尾仅添加一次 \r\n 换行) sprintf(log_line, "[%s] X_位置:%ld\r, Z_位置:%ld\r\n", time_str,x,z); // 关键修改 // 6. 写入文件并同步 f_write(&log_file, log_line, strlen(log_line), &bw); f_sync(&log_file); // 确保数据写入SD卡 f_close(&log_file); } /*----------------------------------------------------------------------------------------------------------*/ //查看配置参数 void usmart_view_config(void) { FIL file; FRESULT fr; char buffer[2048]; // 缓冲区,根据配置文件可能的大小调整 UINT br; DWORD pos = 0; // 打开文件 fr = f_open(&file, "0:config.txt", FA_READ); if (fr != FR_OK) { printf("config2.va1.txt=\"Open config.txt failed! Error: %d\r\n\"\xff\xff\xff", fr); USART_ClearBuffer(); return; } // 确定读取位置(配置文件通常需全量显示,从开头读取) pos = 0; f_lseek(&file, pos); // 读取文件内容到缓冲区(最多读取缓冲区大小) f_read(&file, buffer, sizeof(buffer) - 1, &br); // 留1字节给结束符 buffer[br] = '\0'; // 添加字符串结束符 // 打印配置文件内容 printf("config2.va1.txt=\"设备配置参数:\r\n%s\r\n\"\xff\xff\xff", buffer); delay_ms(5); // 关闭文件 f_close(&file); USART_ClearBuffer(); } // 参数: 配置项名称(字符串)、新值(整数) void setParam(const char* param_name, int32_t value) { if (strcmp(param_name, "T_FIX") == 0) g_config.t_fix = value; else if (strcmp(param_name, "T_RINSE") == 0) g_config.t_rinse = value; else if (strcmp(param_name, "T_ACID") == 0) g_config.t_acid = value; else if (strcmp(param_name, "T_STAIN") == 0) g_config.t_stain = value; else if (strcmp(param_name, "T_DEHY1") == 0) g_config.t_dehy1 = value; else if (strcmp(param_name, "T_DEHY2") == 0) g_config.t_dehy2 = value; else if (strcmp(param_name, "T_DEHY3") == 0) g_config.t_dehy3 = value; else if (strcmp(param_name, "T_DEHY4") == 0) g_config.t_dehy4 = value; else if (strcmp(param_name, "T_DRY") == 0) g_config.t_dry = value; else if (strcmp(param_name, "X_POS1") == 0) g_config.x_pos1 = value; else if (strcmp(param_name, "X_POS2") == 0) g_config.x_pos2 = value; else if (strcmp(param_name, "X_POS3") == 0) g_config.x_pos3 = value; else if (strcmp(param_name, "X_POS4") == 0) g_config.x_pos4 = value; else if (strcmp(param_name, "X_POS5") == 0) g_config.x_pos5 = value; else if (strcmp(param_name, "X_POS6") == 0) g_config.x_pos6 = value; else if (strcmp(param_name, "X_POS7") == 0) g_config.x_pos7 = value; else if (strcmp(param_name, "X_POS8") == 0) g_config.x_pos8 = value; else if (strcmp(param_name, "X_POS9") == 0) g_config.x_pos9 = value; else if (strcmp(param_name, "X_POS10") == 0) g_config.x_pos10 = value; else if (strcmp(param_name, "Z_LIFT") == 0) g_config.z_lift = value; else if (strcmp(param_name, "Z_LIFT1") == 0) g_config.z_lift1 = value; else if (strcmp(param_name, "Z_SOAK") == 0) g_config.z_soak = value; else if (strcmp(param_name, "X_OFFSET") == 0) { delay_ms(10); g_config.x_offset = value; g_config.x_pos2 = g_config.x_pos2+value; g_config.x_pos3 = g_config.x_pos3+value; g_config.x_pos4 = g_config.x_pos4+value; g_config.x_pos5 = g_config.x_pos5+value; g_config.x_pos6 = g_config.x_pos6+value; g_config.x_pos7 = g_config.x_pos7+value; g_config.x_pos8 = g_config.x_pos8+value; g_config.x_pos9 = g_config.x_pos9+value; g_config.x_pos10 = g_config.x_pos10+value; delay_ms(10); } // 其他参数... else { printf("config2.t0.txt=\"请检查参数,输入无效\"\xff\xff\xff"); USART_ClearBuffer(); return; } write_config_to_sd(&g_config); } //////查看log文件后10行 void usmart_view_log(void) { FIL file; FRESULT fr; char buffer[2048]; UINT br; DWORD file_size, pos; int line_count = 0; fr = f_open(&file, "0:log.txt", FA_READ); if (fr != FR_OK) { printf("config3.va0.txt=\"Open log.txt failed! Error: %d\r\n\"\xff\xff\xff", fr); USART_ClearBuffer(); return; } file_size = f_size(&file); // 从文件末尾向前查找10行 pos = file_size < sizeof(buffer) ? 0 : file_size - sizeof(buffer); f_lseek(&file, pos); f_read(&file, buffer, sizeof(buffer), &br); buffer[br] = '\0'; // 从后往前统计行数 for (int i = br - 1; i >= 0; i--) { if (buffer[i] == '\n') { line_count++; if (line_count >= 30) { pos = i + 1; break; } } } // 打印找到的内容 if (line_count < 20) pos = 0; // 如果不足10行则从头开始 printf("config3.va0.txt=\"\r\n%s\r\n\"\xff\xff\xff",buffer + pos); USART_ClearBuffer(); f_close(&file); } //删除log文件 void delete_log(void) { delay_ms(50); u8 ret = mf_unlink("log.txt"); if (ret == FR_OK) { printf("config3.t0.txt=\"日志删除成功\r\n日志删除成功\r\n\"\xff\xff\xff"); } else { printf("config3.t0.txt=\"日志删除失败\r\n日志删除失败\r\n\"\xff\xff\xff"); } } /*----------------------------------------------------------------------------------------------------------*/ void moveX(int32_t steps) { moveAxisCommon(AXIS_X,steps); } void moveZ(int32_t steps) { moveAxisCommon(AXIS_Z,steps); } /*----------------------------------------------------------------------------------------------------------*/ // 移动到指定染缸(1-9) void moveToTank(uint8_t tank_num) { if (tank_num < 1 || tank_num > 9) { OLED_ShowString(4,1,"Invalid Tank"); return; } // 获取目标缸X坐标 int32_t target_x = 0; switch(tank_num) { case 1: target_x = g_config.x_pos1; break; case 2: target_x = g_config.x_pos2; break; case 3: target_x = g_config.x_pos3; break; case 4: target_x = g_config.x_pos4; break; case 5: target_x = g_config.x_pos5; break; case 6: target_x = g_config.x_pos6; break; case 7: target_x = g_config.x_pos7; break; case 8: target_x = g_config.x_pos8; break; case 9: target_x = g_config.x_pos9; break; } // X轴移动到目标位置 int32_t x_steps = target_x - x_current_position; delay_ms(10); moveAxisCommon(AXIS_X, x_steps);//1 // Z轴下降() moveAxisCommon(AXIS_Z, g_config.z_lift);//2 } /*----------------------------------------------------------------------------------------------------------*/ // 染色流程状态机处理函数(在主循环中调用,非阻塞) void process_auto_stain(void) { if (g_stain_state == STAIN_IDLE) return; // 空闲状态不处理 get_sys_time_s(); uint32_t elapsed_ms = sys_time_s - g_delay_start_tick; // 已过去的时间 switch (g_stain_state) { // -------------------------- 步骤1:等待放入染架 -------------------------- case STAIN_STEP1_WAIT_LOAD_INIT: printf("move.t0.txt=\"请放入染架到固定缸\"\xff\xff\xff"); g_delay_start_tick = sys_time_s; // 录开始时间 g_current_delay_s = 3; // 等待 g_stain_state = STAIN_STEP1_WAIT_LOAD_DELAY; // 进入延时状态 break; case STAIN_STEP1_WAIT_LOAD_DELAY: if (elapsed_ms >= g_current_delay_s) { // 等待结束,进入固定缸初始化 g_stain_state = STAIN_STEP1_FIX_INIT; } break; // -------------------------- 步骤1:固定缸(1#) -------------------------- case STAIN_STEP1_FIX_INIT: printf("move.t2.bco=1024\xff\xff\xff"); g_delay_start_tick = sys_time_s; // 录开始时间 g_current_delay_s = g_config.t_fix; // 目标延时(用户配置) g_stain_state = STAIN_STEP1_FIX_DELAY; // 进入延时状态 printf("move.t0.txt=\"固定剩余时间:\"\xff\xff\xff"); printf("move.tm1.en=1\xff\xff\xff"); printf("move.t25.aph=127\xff\xff\xff"); printf("move.t26.aph=127\xff\xff\xff"); break; case STAIN_STEP1_FIX_DELAY: if (elapsed_ms >= g_current_delay_s) { printf("move.t25.txt=\"0\"\xff\xff\xff"); printf("move.tm1.en=0\xff\xff\xff"); printf("move.t25.aph=0\xff\xff\xff"); printf("move.t26.aph=0\xff\xff\xff"); g_stain_state = STAIN_STEP1_FIX_DONE; } break; case STAIN_STEP1_FIX_DONE: // 执行移动操作(非阻塞,假设moveAxisCommon内部已处理等待) printf("move.t0.txt=\"移动到冲洗缸\"\xff\xff\xff"); moveAxisCommon(AXIS_X, g_config.x_pos1); moveAxisCommon(AXIS_Z, g_config.z_lift); Xmove_right2(); //右移小距离,抓住染架 Zmove_up(); g_stain_state = STAIN_STEP2_RINSE_INIT; // 进入下一步 break; // -------------------------- 步骤2:冲洗缸(4#) -------------------------- case STAIN_STEP2_RINSE_INIT: printf("move.t8.bco=1024\xff\xff\xff"); moveToTank(4); // 移动到冲洗缸 up_down(); // 执行上下动作 g_delay_start_tick = sys_time_s; //这里不是current_tick,是因为运动会阻塞函数,无法运行 uint32_t current_tick = sys_time_s;获取当前时间 g_current_delay_s = g_config.t_rinse; // 冲洗延时 g_stain_state = STAIN_STEP2_RINSE_DELAY; printf("move.t0.txt=\"冲洗剩余时间:\"\xff\xff\xff"); printf("move.t25.aph=127\xff\xff\xff"); printf("move.t26.aph=127\xff\xff\xff"); printf("move.tm2.en=1\xff\xff\xff"); break; case STAIN_STEP2_RINSE_DELAY: if (elapsed_ms >= g_current_delay_s) { printf("move.t25.txt=\"0\"\xff\xff\xff"); printf("move.tm2.en=0\xff\xff\xff"); printf("move.t25.aph=0\xff\xff\xff"); printf("move.t26.aph=0\xff\xff\xff"); g_stain_state = STAIN_STEP2_RINSE_DONE; } break; case STAIN_STEP2_RINSE_DONE: printf("move.t0.txt=\"移动到酸化缸\"\xff\xff\xff"); Zmove_up(); g_stain_state = STAIN_STEP3_ACID_INIT; // 进入酸化缸 break; // -------------------------- 步骤3:酸化缸(2#) -------------------------- case STAIN_STEP3_ACID_INIT: printf("move.t4.bco=1024\xff\xff\xff"); moveToTank(2); // 移动到酸化缸 g_delay_start_tick = sys_time_s; g_current_delay_s = g_config.t_acid; // 酸化延时 g_stain_state = STAIN_STEP3_ACID_DELAY; printf("move.t0.txt=\"酸化剩余时间:\"\xff\xff\xff"); printf("move.t25.aph=127\xff\xff\xff"); printf("move.t26.aph=127\xff\xff\xff"); printf("move.tm3.en=1\xff\xff\xff"); break; case STAIN_STEP3_ACID_DELAY: if (elapsed_ms >= g_current_delay_s) { printf("move.t25.txt=\"0\"\xff\xff\xff"); printf("move.t25.aph=0\xff\xff\xff"); printf("move.t26.aph=0\xff\xff\xff"); printf("move.tm3.en=0\xff\xff\xff"); g_stain_state = STAIN_STEP3_ACID_DONE; } break; case STAIN_STEP3_ACID_DONE: printf("move.t0.txt=\"移动到冲洗缸\"\xff\xff\xff"); Zmove_up(); g_stain_state = STAIN_STEP4_RINSE_INIT; // 进入冲洗缸 break; // -------------------------- 步骤4:冲洗缸(4#) -------------------------- case STAIN_STEP4_RINSE_INIT: printf("move.t8.bco=1024\xff\xff\xff"); moveToTank(4); // 移动到冲洗缸 up_down(); g_delay_start_tick = sys_time_s; g_current_delay_s = g_config.t_rinse; // 冲洗延时 g_stain_state = STAIN_STEP4_RINSE_DELAY; printf("move.t0.txt=\"冲洗剩余时间:\"\xff\xff\xff"); printf("move.t25.aph=127\xff\xff\xff"); printf("move.t26.aph=127\xff\xff\xff"); printf("move.va3.val=%d\xff\xff\xff",g_config.t_rinse); printf("move.tm2.en=1\xff\xff\xff"); break; case STAIN_STEP4_RINSE_DELAY: if (elapsed_ms >= g_current_delay_s) { printf("move.t25.txt=\"0\"\xff\xff\xff"); printf("move.tm2.en=0\xff\xff\xff"); printf("move.t25.aph=0\xff\xff\xff"); printf("move.t26.aph=0\xff\xff\xff"); g_stain_state = STAIN_STEP4_RINSE_DONE; } break; case STAIN_STEP4_RINSE_DONE: printf("move.t0.txt=\"移动到染色缸\"\xff\xff\xff"); Zmove_up(); g_stain_state = STAIN_STEP5_STAIN_INIT; // 进入染色缸 break; // -------------------------- 步骤5:染色缸(3#) -------------------------- case STAIN_STEP5_STAIN_INIT: printf("move.t6.bco=1024\xff\xff\xff"); moveToTank(3); // 移动到染色缸 g_delay_start_tick = sys_time_s; g_current_delay_s = g_config.t_stain; // 染色延时 g_stain_state = STAIN_STEP5_STAIN_DELAY; printf("move.t0.txt=\"染色剩余时间:\"\xff\xff\xff"); printf("move.t25.aph=127\xff\xff\xff"); printf("move.t26.aph=127\xff\xff\xff"); printf("move.tm4.en=1\xff\xff\xff"); break; case STAIN_STEP5_STAIN_DELAY: if (elapsed_ms >= g_current_delay_s) { printf("move.t25.txt=\"0\"\xff\xff\xff"); printf("move.tm4.en=0\xff\xff\xff"); printf("move.t25.aph=0\xff\xff\xff"); printf("move.t26.aph=0\xff\xff\xff"); g_stain_state = STAIN_STEP5_STAIN_DONE; } break; case STAIN_STEP5_STAIN_DONE: printf("move.t0.txt=\"移动到冲洗缸\"\xff\xff\xff"); Zmove_up(); g_stain_state = STAIN_STEP6_RINSE_INIT; // 进入冲洗缸 break; // -------------------------- 步骤6:冲洗缸(4#) -------------------------- case STAIN_STEP6_RINSE_INIT: printf("move.t8.bco=1024\xff\xff\xff"); moveToTank(4); // 移动到冲洗缸 up_down(); g_delay_start_tick = sys_time_s; g_current_delay_s = g_config.t_rinse; // 冲洗延时 g_stain_state = STAIN_STEP6_RINSE_DELAY; printf("move.va3.val=%d\xff\xff\xff",g_config.t_rinse); printf("move.t0.txt=\"冲洗剩余时间:\"\xff\xff\xff"); printf("move.t25.aph=127\xff\xff\xff"); printf("move.t26.aph=127\xff\xff\xff"); printf("move.tm2.en=1\xff\xff\xff"); break; case STAIN_STEP6_RINSE_DELAY: if (elapsed_ms >= g_current_delay_s) { printf("move.t25.txt=\"0\"\xff\xff\xff"); printf("move.tm2.en=0\xff\xff\xff"); printf("move.t25.aph=0\xff\xff\xff"); printf("move.t26.aph=0\xff\xff\xff"); g_stain_state = STAIN_STEP6_RINSE_DONE; } break; case STAIN_STEP6_RINSE_DONE: printf("move.t0.txt=\"移动到梯度脱水1缸\"\xff\xff\xff"); Zmove_up(); g_stain_state = STAIN_STEP7_DEHY1_INIT; // 进入脱水1 break; // -------------------------- 步骤7:梯度脱水1(5#) -------------------------- case STAIN_STEP7_DEHY1_INIT: printf("move.t10.bco=1024\xff\xff\xff"); moveToTank(5); // 移动到脱水1缸 g_delay_start_tick = sys_time_s; g_current_delay_s = g_config.t_dehy1; // 脱水1延时 g_stain_state = STAIN_STEP7_DEHY1_DELAY; printf("move.t0.txt=\"梯度脱水1剩余时间:\"\xff\xff\xff"); printf("move.t25.aph=127\xff\xff\xff"); printf("move.t26.aph=127\xff\xff\xff"); printf("move.tm5.en=1\xff\xff\xff"); break; case STAIN_STEP7_DEHY1_DELAY: if (elapsed_ms >= g_current_delay_s) { printf("move.t25.txt=\"0\"\xff\xff\xff"); printf("move.tm5.en=0\xff\xff\xff"); printf("move.t25.aph=0\xff\xff\xff"); printf("move.t26.aph=0\xff\xff\xff"); g_stain_state = STAIN_STEP7_DEHY1_DONE; } break; case STAIN_STEP7_DEHY1_DONE: printf("move.t0.txt=\"移动到梯度脱水2缸\"\xff\xff\xff"); Zmove_up(); g_stain_state = STAIN_STEP8_DEHY2_INIT; // 进入脱水2 break; // -------------------------- 步骤8:梯度脱水2(6#) -------------------------- case STAIN_STEP8_DEHY2_INIT: printf("move.t12.bco=1024\xff\xff\xff"); moveToTank(6); // 移动到脱水2缸 g_delay_start_tick = sys_time_s; g_current_delay_s = g_config.t_dehy2; // 脱水2延时 g_stain_state = STAIN_STEP8_DEHY2_DELAY; printf("move.t0.txt=\"梯度脱水2剩余时间:\"\xff\xff\xff"); printf("move.t25.aph=127\xff\xff\xff"); printf("move.t26.aph=127\xff\xff\xff"); printf("move.tm6.en=1\xff\xff\xff"); break; case STAIN_STEP8_DEHY2_DELAY: if (elapsed_ms >= g_current_delay_s) { printf("move.t25.txt=\"0\"\xff\xff\xff"); printf("move.tm6.en=0\xff\xff\xff"); printf("move.t25.aph=0\xff\xff\xff"); printf("move.t26.aph=0\xff\xff\xff"); g_stain_state = STAIN_STEP8_DEHY2_DONE; } break; case STAIN_STEP8_DEHY2_DONE: printf("move.t0.txt=\"移动到梯度脱水3缸\"\xff\xff\xff"); Zmove_up(); g_stain_state = STAIN_STEP9_DEHY3_INIT; // 进入脱水3 break; // -------------------------- 步骤9:梯度脱水3(7#) -------------------------- case STAIN_STEP9_DEHY3_INIT: printf("move.t14.bco=1024\xff\xff\xff"); moveToTank(7); // 移动到脱水3缸 g_delay_start_tick = sys_time_s; g_current_delay_s = g_config.t_dehy3; // 脱水3延时 g_stain_state = STAIN_STEP9_DEHY3_DELAY; printf("move.t0.txt=\"梯度脱水3剩余时间:\"\xff\xff\xff"); printf("move.t25.aph=127\xff\xff\xff"); printf("move.t26.aph=127\xff\xff\xff"); printf("move.tm7.en=1\xff\xff\xff"); break; case STAIN_STEP9_DEHY3_DELAY: if (elapsed_ms >= g_current_delay_s) { printf("move.t25.txt=\"0\"\xff\xff\xff"); printf("move.tm7.en=0\xff\xff\xff"); printf("move.t25.aph=0\xff\xff\xff"); printf("move.t26.aph=0\xff\xff\xff"); g_stain_state = STAIN_STEP9_DEHY3_DONE; } break; case STAIN_STEP9_DEHY3_DONE: printf("move.t0.txt=\"移动到梯度脱水4缸\"\xff\xff\xff"); Zmove_up(); g_stain_state = STAIN_STEP10_DEHY4_INIT; // 进入脱水4 break; // -------------------------- 步骤10:梯度脱水4(8#) -------------------------- case STAIN_STEP10_DEHY4_INIT: printf("move.t16.bco=1024\xff\xff\xff"); moveToTank(8); // 移动到脱水4缸 g_delay_start_tick = sys_time_s; g_current_delay_s = g_config.t_dehy4; // 脱水4延时 g_stain_state = STAIN_STEP10_DEHY4_DELAY; printf("move.t0.txt=\"梯度脱水4剩余时间:\"\xff\xff\xff"); printf("move.t25.aph=127\xff\xff\xff"); printf("move.t26.aph=127\xff\xff\xff"); printf("move.tm8.en=1\xff\xff\xff"); break; case STAIN_STEP10_DEHY4_DELAY: if (elapsed_ms >= g_current_delay_s) { printf("move.t25.txt=\"0\"\xff\xff\xff"); printf("move.tm8.en=0\xff\xff\xff"); printf("move.t25.aph=0\xff\xff\xff"); printf("move.t26.aph=0\xff\xff\xff"); g_stain_state = STAIN_STEP10_DEHY4_DONE; } break; case STAIN_STEP10_DEHY4_DONE: printf("move.t0.txt=\"移动到梯度晾片缸\"\xff\xff\xff"); Zmove_up(); g_stain_state = STAIN_STEP11_DRY_INIT; // 进入晾片缸 break; // -------------------------- 步骤11:晾片缸(9#) -------------------------- case STAIN_STEP11_DRY_INIT: printf("move.t18.bco=1024\xff\xff\xff"); printf("move.t0.txt=\"晾片\"\xff\xff\xff"); moveToTank(9); // 移动到晾片缸 printf("move.tm11.en=1\xff\xff\xff"); g_delay_start_tick = sys_time_s; g_current_delay_s = g_config.t_dry; // 晾片延时 g_stain_state = STAIN_STEP11_DRY_DELAY; break; case STAIN_STEP11_DRY_DELAY: if (elapsed_ms >= g_current_delay_s) { printf("move.t25.txt=\"0\"\xff\xff\xff"); printf("move.tm11.en=0\xff\xff\xff"); printf("move.t25.aph=0\xff\xff\xff"); printf("move.t26.aph=0\xff\xff\xff"); g_stain_state = STAIN_STEP11_DRY_DONE; } break; case STAIN_STEP11_DRY_DONE: Xmove_left(); // 脱离晾片缸 g_stain_state = STAIN_FINISH; // 流程结束 break; // -------------------------- 流程结束 -------------------------- case STAIN_FINISH: printf("move.t0.txt=\"染色完成,请移走染架\"\xff\xff\xff"); g_stain_state = STAIN_IDLE; // 重置为空闲状态 break; default: g_stain_state = STAIN_IDLE; // 异常状态重置 break; } } /*----------------------------------------------------------------------------------------------------------*/ // 优化后的X轴移动函数 void Xmove_left(void) { delay_ms(10); moveAxisCommon(AXIS_X,g_config.x_pos1);//最后一次移动距离,脱离晾片缸 } void Xmove_right(void) { delay_ms(10); moveAxisCommon(AXIS_X,-g_config.x_pos1); } void Xmove_right2(void) { delay_ms(10); moveAxisCommon(AXIS_X,-g_config.x_pos10);//距离是需要右移或者左移抓染架的距离 } void Xmove_left2(void) { delay_ms(10); moveAxisCommon(AXIS_X,g_config.x_pos10);//距离是需要右移或者左移抓染架的距离 } // 优化后的Z轴移动函数 void Zmove_down(void) { delay_ms(10); moveAxisCommon(AXIS_Z,g_config.z_lift); } void Zmove_down_2(void) { delay_ms(10); moveAxisCommon(AXIS_Z,g_config.z_soak); } void Zmove_up(void) { delay_ms(10); moveAxisCommon(AXIS_Z,-g_config.z_lift); } void Zmove_up_2(void) { delay_ms(10); moveAxisCommon(AXIS_Z,-g_config.z_soak); } void up_down(void) { delay_ms(10); Zmove_up_2(); delay_ms(100); Zmove_down_2(); Zmove_up_2(); delay_ms(100); Zmove_down_2(); } /*----------------------------------------------------------------------------------------------------------*/ //一直移动函数GUI界面函数 // 处理触摸屏指令 void touch_command_process(uint8_t cmd) { switch(cmd) { case TOUCH_X_LEFT_PRESS: x_left_running = 1; x_right_running = 0; break; case TOUCH_X_LEFT_RELEASE: if(x_left_running) { motor_stop(AXIS_X); x_left_running = 0; } break; case TOUCH_X_RIGHT_PRESS: x_right_running = 1; x_left_running = 0; break; case TOUCH_X_RIGHT_RELEASE: if(x_right_running) { motor_stop(AXIS_X); x_right_running = 0; } break; case TOUCH_Z_UP_PRESS: z_up_running = 1; z_down_running = 0; break; case TOUCH_Z_UP_RELEASE: if(z_up_running) { motor_stop(AXIS_Z); z_up_running = 0; } break; case TOUCH_Z_DOWN_PRESS: z_down_running = 1; z_up_running = 0; break; case TOUCH_Z_DOWN_RELEASE: if(z_down_running) { motor_stop(AXIS_Z); z_down_running = 0; } break; case STAIN_STEP1_WAIT_LOAD_INIT_USART: if (g_stain_state == STAIN_IDLE) { g_stain_state = STAIN_STEP1_WAIT_LOAD_INIT; // 从第一步开始 } break; case STAIN_IDLE_USART: GPIO_SetBits(X_ENA_PORT, X_ENA_PIN); // 高电平禁用 GPIO_SetBits(Z_ENA_PORT, Z_ENA_PIN); reinitialize_system(); break; } } // 电机连续移动函数 void motor_continuous_move(AxisType axis, int32_t direction) { if(axis == AXIS_X) { if(!x_motor_running) { // 设置一个较大的目标步数实现连续移动 moveAxisCommon(AXIS_X, direction * 10000000); } } else if(axis == AXIS_Z) { if(!z_motor_running) { moveAxisCommon(AXIS_Z, direction * 10000000); } } } // 电机停止函数 void motor_stop(AxisType axis) { if(axis == AXIS_X) { x_motor_running = 0; disableMotor(AXIS_X); } else if(axis == AXIS_Z) { z_motor_running = 0; disableMotor(AXIS_Z); } } /*----------------------------------------------------------------------------------------------------------*/ void X_home(void) { Home_Motor(AXIS_X); } void Z_home(void) { Home_Motor(AXIS_Z); } /*----------------------------------------------------------------------------------------------------------*/ //初始化SD配置文件 void SD_config_init(void) { fr = f_open(&config_file, "0:config.txt", FA_READ); if (fr == FR_OK) { read_config_from_sd(&g_config); delay_ms(10); f_close(&config_file); } else { printf("not find config"); delay_ms(10); } fr = f_open(&log_file, "0:log.txt", FA_READ | FA_OPEN_ALWAYS); f_close(&log_file); } /*----------------------------------------------------------------------------------------------------------*/ /** * 读取温度状态并更新(仅在状态变化时执行操作) */ void prin_temp(void) { // 读取温度(已转为整数) int32_t current_temp = (int32_t)(SMBus_ReadTemp()); // 仅当当前温度与历史值不同时,才执行更新(避免无效操作) if (current_temp != last_temp) { if(current_temp < 150 && current_temp > -10) { // 调用公共函数更新显示和发送数据 printf("home.va0.val=%d\xff\xff\xff", current_temp); OLED_ShowNum(3,4, current_temp, 2); // 显示温度(占3位,适应更大数值) OLED_ShowString(3,6,"C"); OLED_ShowString(3,1,"T1:"); last_temp = current_temp; // 更新历史值 USART_ClearBuffer(); } } } /** * 读取温度状态并更新(仅在状态变化时执行操作) */ void prin_temp2(void) { // 读取温度(已转为整数) int32_t current_temp2 = (int32_t)DS18B20_Get_Temp()/ 10.0f; // 仅当当前温度与历史值不同时,才执行更新(避免无效操作) if (current_temp2 != last_temp2) { if(current_temp2 < 150 && last_temp2 > -10){ // 调用公共函数更新显示和发送数据 printf("home.va1.val=%d\xff\xff\xff", current_temp2); OLED_ShowNum(3,12, current_temp2, 2); // 显示温度(占3位,适应更大数值) OLED_ShowString(3,14,"C"); OLED_ShowString(3,9,"T2:"); last_temp2 = current_temp2; // 更新历史值 USART_ClearBuffer(); } } } /** * 读取液位状态并更新(仅在状态变化时执行操作) */ void print_level(void) { // 读取液位状态 uint8_t current_level = LevelSensor_GetState(); if(current_level==1)//出水,模式 { close_rinse_input(); open_rinse_output(); } if(current_level==0)//进水,判断,水满信号位置1 { open_rinse_input(); close_rinse_output(); } // 仅当当前状态与历史值不同时,才执行更新 if (current_level != last_level) { // 调用公共函数更新显示和发送数据 printf("move.va1.val=%d\xff\xff\xff", current_level); OLED_ShowString(1,1,"water_leve:"); OLED_ShowNum(1,14,current_level, 2); // 显示液位状态 last_level = current_level; // 更新历史值 USART_ClearBuffer(); } } /*----------------------------------------------------------------------------------------------------------*/ void moveAxisCommon(AxisType axis, int32_t target_steps) { delay_ms(10); // 启动带加速度的运动(假设此函数仅启动运动,不阻塞) moveStepsWithAccel(axis, target_steps); // 立即更新OLED显示(运动开始时的位置) OLED_ShowNum(2, 9, x_current_position, 7); OLED_ShowNum(4, 9, z_current_position, 7); while ((axis == AXIS_X && x_motor_running) || (axis == AXIS_Z && z_motor_running)) { delay_ms(1); // 短延迟,降低CPU占用 } delay_ms(10); // 稳定延迟 // 5. 写日志与显示 write_log(x_current_position, z_current_position); } /*--------------------------------------------------------------------------------------------------------------------------------*/ // 安全清理函数:释放资源,停止外设 void safe_cleanup(void) { // 1. 停止电机运动 motor_stop(AXIS_X); motor_stop(AXIS_Z); x_left_running = 0; x_right_running = 0; z_up_running = 0; z_down_running = 0; // 2. 关闭文件系统相关资源 f_close(&step_file); f_close(&log_file); f_close(&config_file); f_mount(NULL, "0:", 1); // 卸载SD卡 f_mount(NULL, "1:", 1); // 卸载FLASH // 3. 重置状态机和全局变量 g_stain_state = STAIN_IDLE; g_delay_start_tick = 0; g_current_delay_s = 0; // 4. 清空OLED显示 OLED_Clear(); delay_ms(500); } // 重新初始化函数(复用main中的初始化逻辑) void reinitialize_system(void) { safe_cleanup(); // 先清理资源 // 重新执行初始化(完全复用main中的初始化步骤) delay_init(72); delay_ms(10); OLED_Init(); OLED_Clear(); NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); StepMotor_GPIO_Init(AXIS_X); StepMotor_GPIO_Init(AXIS_Z); StepMotor_TIM_Init(AXIS_X); StepMotor_TIM_Init(AXIS_Z); delay_ms(10); my_mem_init(SRAMIN); delay_ms(10); SMBus_Init(); delay_ms(10); LevelSensor_Init(); delay_ms(10); DS18B20_Init(); delay_ms(10); // 重新初始化SD卡(带重试) while(SD_Init()) { OLED_ShowString(2, 1, "SD Error, Retry..."); delay_ms(1000); } delay_ms(10); // 重新初始化文件系统 while(exfuns_init()) { OLED_ShowString(2, 1, "FS Error, Retry..."); delay_ms(1000); } f_mount(fs[0], "0:", 1); f_mount(fs[1], "1:", 1); delay_ms(10); // 重新初始化串口和USMART USART1_Init(115200); delay_ms(10); usmart_dev.init(72); delay_ms(10); // 重新初始化RTC RTC_Init(); delay_ms(10); // 重新加载配置 SD_config_init(); delay_ms(10); // 重新初始化限位开关 LimitSwitch_Init(); CheckLimitSwitches(); delay_ms(10); /*************************开机初始化将配置参数传到move页面***************************************/ printf("move.va2.val=%d\xff\xff\xff",g_config.t_fix); printf("move.va3.val=%d\xff\xff\xff",g_config.t_rinse); printf("move.va4.val=%d\xff\xff\xff",g_config.t_acid); printf("move.va5.val=%d\xff\xff\xff",g_config.t_stain); printf("move.va6.val=%d\xff\xff\xff",g_config.t_dehy1); printf("move.va7.val=%d\xff\xff\xff",g_config.t_dehy2); printf("move.va8.val=%d\xff\xff\xff",g_config.t_dehy3); printf("move.va9.val=%d\xff\xff\xff",g_config.t_dehy4); printf("move.va10.val=%d\xff\xff\xff",g_config.t_dry); /***********************************************************************************************/ OLED_Clear(); printf("系统重新初始化完成\xFF\xFF\xFF"); delay_ms(1000); OLED_Clear(); } /*--------------------------------------------------------------------------------------------------------------------------------*/ int main(void) { int a=0; //温度循环值初始,1秒检测一次 g_stain_state = STAIN_IDLE; //初始化为空闲状态,状态机 delay_init(72); delay_ms(1000); OLED_Init(); NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组0 StepMotor_GPIO_Init(AXIS_X); StepMotor_GPIO_Init(AXIS_Z); StepMotor_TIM_Init(AXIS_X); StepMotor_TIM_Init(AXIS_Z); delay_ms(10); my_mem_init(SRAMIN); //初始化内部内存池 delay_ms(10); SMBus_Init(); // 初始化SMBus总线(用于MLX90614通信) delay_ms(10); LevelSensor_Init(); // 初始化液面传感器 delay_ms(10); DS18B20_Init(); // 初始化DS18B20温度传感器 delay_ms(10); while(SD_Init()) //检测不到SD卡,SDIO初始化 { OLED_ShowString(1,1,"SD Card Error!"); printf("SD Card Error!"); delay_ms(100); OLED_ShowString(1,1,"Please Check! "); delay_ms(100); } delay_ms(10); while(exfuns_init()) //FAFTS文件系统是否挂载失败 { OLED_ShowString(1,1," exfuns_init Error!"); printf("exfuns_init Error!"); delay_ms(1000); } f_mount(fs[0],"0:",1); //挂载SD卡 f_mount(fs[1],"1:",1); //挂载FLASH. /*************************初始化USMART通信********************************************************/ delay_ms(10); USART1_Init(115200); //串口初始化为115200 delay_ms(10); usmart_dev.init(72); //串口通信初始化 delay_ms(10); /*************************初始化RTC初始化********************************************************/ RTC_Init(); //RTC初始化 delay_ms(10); SD_config_init(); //初始化,将config文件的参数一次性更新到程序里 delay_ms(10); LimitSwitch_Init(); //运动限位最后初始化。 CheckLimitSwitches(); //检查限位,复位限位 OLED_Clear(); //oled显示屏幕清空 // Home_Motor(AXIS_Z); //初始化Z轴,Z轴回零 // Home_Motor(AXIS_X); //初始化X轴,X轴回零 OLED_Clear(); //清空屏幕 /*************************开机初始化将配置参数传到move页面***************************************/ printf("move.va2.val=%d\xff\xff\xff",g_config.t_fix); printf("move.va3.val=%d\xff\xff\xff",g_config.t_rinse); printf("move.va4.val=%d\xff\xff\xff",g_config.t_acid); printf("move.va5.val=%d\xff\xff\xff",g_config.t_stain); printf("move.va6.val=%d\xff\xff\xff",g_config.t_dehy1); printf("move.va7.val=%d\xff\xff\xff",g_config.t_dehy2); printf("move.va8.val=%d\xff\xff\xff",g_config.t_dehy3); printf("move.va9.val=%d\xff\xff\xff",g_config.t_dehy4); printf("move.va10.val=%d\xff\xff\xff",g_config.t_dry); /***********************************************************************************************/ while (1) { process_auto_stain(); if(x_left_running) { motor_continuous_move(AXIS_X, 1); // X轴左移 } if(x_right_running) { motor_continuous_move(AXIS_X, -1); // X轴右移 } if(z_up_running) { motor_continuous_move(AXIS_Z, -1); // Z轴上移 } if(z_down_running) { motor_continuous_move(AXIS_Z, 1); // Z轴下移 } a++; //累加计数 if(a==800){ prin_temp(); prin_temp2(); a=0; } print_level(); delay_ms(2); // 主循环基础延时(保持不变) } } 为什么初始化SD卡时容易失败
10-31
现有一通过摄像头结合yolo获取图像进行识别定位再通过机械臂运动控制程序进行臂迹规划进行抓取的项目,但是该项目中有一个现象是当机械臂开始运动时摄像头输出画面会自动锁死图像不再实时获取图像,以下是运动控制代码#!/usr/bin/python3 import os import math import copy import json import actionlib import control_msgs.msg from controller import ArmController from gazebo_msgs.msg import ModelStates import rospy from pyquaternion import Quaternion as PyQuaternion import numpy as np from gazebo_ros_link_attacher.srv import SetStatic, SetStaticRequest, SetStaticResponse from gazebo_ros_link_attacher.srv import Attach, AttachRequest, AttachResponse PKG_PATH = os.path.dirname(os.path.abspath(__file__)) MODELS_INFO = { "X1-Y2-Z1": { "home": [0.264589, -0.293903, 0.777] }, "X2-Y2-Z2": { "home": [0.277866, -0.724482, 0.777] }, "X1-Y3-Z2": { "home": [0.268053, -0.513924, 0.777] }, "X1-Y2-Z2": { "home": [0.429198, -0.293903, 0.777] }, "X1-Y2-Z2-CHAMFER": { "home": [0.592619, -0.293903, 0.777] }, "X1-Y4-Z2": { "home": [0.108812, -0.716057, 0.777] }, "X1-Y1-Z2": { "home": [0.088808, -0.295820, 0.777] }, "X1-Y2-Z2-TWINFILLET": { "home": [0.103547, -0.501132, 0.777] }, "X1-Y3-Z2-FILLET": { "home": [0.433739, -0.507130, 0.777] }, "X1-Y4-Z1": { "home": [0.589908, -0.501033, 0.777] }, "X2-Y2-Z2-FILLET": { "home": [0.442505, -0.727271, 0.777] } } for model, model_info in MODELS_INFO.items(): pass #MODELS_INFO[model]["home"] = model_info["home"] + np.array([0.0, 0.10, 0.0]) for model, info in MODELS_INFO.items(): model_json_path = os.path.join(PKG_PATH, "..", "models", f"lego_{model}", "model.json") # make path absolute model_json_path = os.path.abspath(model_json_path) # check path exists if not os.path.exists(model_json_path): raise FileNotFoundError(f"Model file {model_json_path} not found") model_json = json.load(open(model_json_path, "r")) corners = np.array(model_json["corners"]) size_x = (np.max(corners[:, 0]) - np.min(corners[:, 0])) size_y = (np.max(corners[:, 1]) - np.min(corners[:, 1])) size_z = (np.max(corners[:, 2]) - np.min(corners[:, 2])) #print(f"{model}: {size_x:.3f} x {size_y:.3f} x {size_z:.3f}") MODELS_INFO[model]["size"] = (size_x, size_y, size_z) # Compensate for the interlocking height INTERLOCKING_OFFSET = 0.019 SAFE_X = -0.40 SAFE_Y = -0.13 SURFACE_Z = 0.774 # Resting orientation of the end effector DEFAULT_QUAT = PyQuaternion(axis=(0, 1, 0), angle=math.pi) # Resting position of the end effector DEFAULT_POS = (-0.1, -0.2, 1.2) DEFAULT_PATH_TOLERANCE = control_msgs.msg.JointTolerance() DEFAULT_PATH_TOLERANCE.name = "path_tolerance" DEFAULT_PATH_TOLERANCE.velocity = 10 def get_gazebo_model_name(model_name, vision_model_pose): """ Get the name of the model inside gazebo. It is needed for link attacher plugin. """ models = rospy.wait_for_message("/gazebo/model_states", ModelStates, timeout=None) epsilon = 0.05 for gazebo_model_name, model_pose in zip(models.name, models.pose): if model_name not in gazebo_model_name: continue # Get everything inside a square of side epsilon centered in vision_model_pose ds = abs(model_pose.position.x - vision_model_pose.position.x) + abs(model_pose.position.y - vision_model_pose.position.y) if ds <= epsilon: return gazebo_model_name raise ValueError(f"Model {model_name} at position {vision_model_pose.position.x} {vision_model_pose.position.y} was not found!") def get_model_name(gazebo_model_name): return gazebo_model_name.replace("lego_", "").split("_", maxsplit=1)[0] def get_legos_pos(vision=False): #get legos position reading vision topic if vision: # 从视觉识别主题获取数据 legos = rospy.wait_for_message("/lego_detections", ModelStates, timeout=None) else: # 从传感器主题获取数据 models = rospy.wait_for_message("/gazebo/model_states", ModelStates, timeout=None) legos = ModelStates() for name, pose in zip(models.name, models.pose): if "X" not in name: continue name = get_model_name(name) legos.name.append(name) legos.pose.append(pose) return [(lego_name, lego_pose) for lego_name, lego_pose in zip(legos.name, legos.pose)] def straighten(model_pose, gazebo_model_name): x = model_pose.position.x y = model_pose.position.y z = model_pose.position.z model_quat = PyQuaternion( x=model_pose.orientation.x, y=model_pose.orientation.y, z=model_pose.orientation.z, w=model_pose.orientation.w) model_size = MODELS_INFO[get_model_name(gazebo_model_name)]["size"] """ Calculate approach quaternion and target quaternion """ facing_direction = get_axis_facing_camera(model_quat) approach_angle = get_approach_angle(model_quat, facing_direction) print(f"Lego is facing {facing_direction}") print(f"Angle of approaching measures {approach_angle:.2f} deg") # Calculate approach quat approach_quat = get_approach_quat(facing_direction, approach_angle) # Get above the object controller.move_to(x, y, target_quat=approach_quat) # Calculate target quat regrip_quat = DEFAULT_QUAT if facing_direction == (1, 0, 0) or facing_direction == (0, 1, 0): # Side target_quat = DEFAULT_QUAT pitch_angle = -math.pi/2 + 0.2 if abs(approach_angle) < math.pi/2: target_quat = target_quat * PyQuaternion(axis=(0, 0, 1), angle=math.pi/2) else: target_quat = target_quat * PyQuaternion(axis=(0, 0, 1), angle=-math.pi/2) target_quat = PyQuaternion(axis=(0, 1, 0), angle=pitch_angle) * target_quat if facing_direction == (0, 1, 0): regrip_quat = PyQuaternion(axis=(0, 0, 1), angle=math.pi/2) * regrip_quat elif facing_direction == (0, 0, -1): """ Pre-positioning """ controller.move_to(z=z, target_quat=approach_quat) close_gripper(gazebo_model_name, model_size[0]) tmp_quat = PyQuaternion(axis=(0, 0, 1), angle=2*math.pi/6) * DEFAULT_QUAT controller.move_to(SAFE_X, SAFE_Y, z+0.05, target_quat=tmp_quat, z_raise=0.1) # Move to safe position controller.move_to(z=z) open_gripper(gazebo_model_name) approach_quat = tmp_quat * PyQuaternion(axis=(1, 0, 0), angle=math.pi/2) target_quat = approach_quat * PyQuaternion(axis=(0, 0, 1), angle=-math.pi) # Add a yaw rotation of 180 deg regrip_quat = tmp_quat * PyQuaternion(axis=(0, 0, 1), angle=math.pi) else: target_quat = DEFAULT_QUAT target_quat = target_quat * PyQuaternion(axis=(0, 0, 1), angle=-math.pi/2) """ Grip the model """ if facing_direction == (0, 0, 1) or facing_direction == (0, 0, -1): closure = model_size[0] z = SURFACE_Z + model_size[2] / 2 elif facing_direction == (1, 0, 0): closure = model_size[1] z = SURFACE_Z + model_size[0] / 2 elif facing_direction == (0, 1, 0): closure = model_size[0] z = SURFACE_Z + model_size[1] / 2 controller.move_to(z=z, target_quat=approach_quat) close_gripper(gazebo_model_name, closure) """ Straighten model if needed """ if facing_direction != (0, 0, 1): z = SURFACE_Z + model_size[2]/2 controller.move_to(z=z+0.05, target_quat=target_quat, z_raise=0.1) controller.move(dz=-0.05) open_gripper(gazebo_model_name) # Re grip the model controller.move_to(z=z, target_quat=regrip_quat, z_raise=0.1) close_gripper(gazebo_model_name, model_size[0]) def close_gripper(gazebo_model_name, closure=0): set_gripper(0.81-closure*10) rospy.sleep(0.5) # Create dynamic joint if gazebo_model_name is not None: req = AttachRequest() req.model_name_1 = gazebo_model_name req.link_name_1 = "link" req.model_name_2 = "robot" req.link_name_2 = "wrist_3_link" attach_srv.call(req) def open_gripper(gazebo_model_name=None): set_gripper(0.0) # Destroy dynamic joint if gazebo_model_name is not None: req = AttachRequest() req.model_name_1 = gazebo_model_name req.link_name_1 = "link" req.model_name_2 = "robot" req.link_name_2 = "wrist_3_link" detach_srv.call(req) def set_model_fixed(model_name): req = AttachRequest() req.model_name_1 = model_name req.link_name_1 = "link" req.model_name_2 = "ground_plane" req.link_name_2 = "link" attach_srv.call(req) req = SetStaticRequest() print("{} TO HOME".format(model_name)) req.model_name = model_name req.link_name = "link" req.set_static = True setstatic_srv.call(req) def get_approach_quat(facing_direction, approach_angle): quat = DEFAULT_QUAT if facing_direction == (0, 0, 1): pitch_angle = 0 yaw_angle = 0 elif facing_direction == (1, 0, 0) or facing_direction == (0, 1, 0): pitch_angle = + 0.2 if abs(approach_angle) < math.pi/2: yaw_angle = math.pi/2 else: yaw_angle = -math.pi/2 elif facing_direction == (0, 0, -1): pitch_angle = 0 yaw_angle = 0 else: raise ValueError(f"Invalid model state {facing_direction}") quat = quat * PyQuaternion(axis=(0, 1, 0), angle=pitch_angle) quat = quat * PyQuaternion(axis=(0, 0, 1), angle=yaw_angle) quat = PyQuaternion(axis=(0, 0, 1), angle=approach_angle+math.pi/2) * quat return quat def get_axis_facing_camera(quat): axis_x = np.array([1, 0, 0]) axis_y = np.array([0, 1, 0]) axis_z = np.array([0, 0, 1]) new_axis_x = quat.rotate(axis_x) new_axis_y = quat.rotate(axis_y) new_axis_z = quat.rotate(axis_z) # get angle between new_axis and axis_z angle = np.arccos(np.clip(np.dot(new_axis_z, axis_z), -1.0, 1.0)) # get if model is facing up, down or sideways if angle < np.pi / 3: return 0, 0, 1 elif angle < np.pi / 3 * 2 * 1.2: if abs(new_axis_x[2]) > abs(new_axis_y[2]): return 1, 0, 0 else: return 0, 1, 0 #else: # raise Exception(f"Invalid axis {new_axis_x}") else: return 0, 0, -1 def get_approach_angle(model_quat, facing_direction):#get gripper approach angle if facing_direction == (0, 0, 1): return model_quat.yaw_pitch_roll[0] - math.pi/2 #rotate gripper elif facing_direction == (1, 0, 0) or facing_direction == (0, 1, 0): axis_x = np.array([0, 1, 0]) axis_y = np.array([-1, 0, 0]) new_axis_z = model_quat.rotate(np.array([0, 0, 1])) #get z axis of lego # get angle between new_axis and axis_x dot = np.clip(np.dot(new_axis_z, axis_x), -1.0, 1.0) #sin angle between lego z axis and x axis in fixed frame det = np.clip(np.dot(new_axis_z, axis_y), -1.0, 1.0) #cos angle between lego z axis and x axis in fixed frame return math.atan2(det, dot) #get angle between lego z axis and x axis in fixed frame elif facing_direction == (0, 0, -1): return -(model_quat.yaw_pitch_roll[0] - math.pi/2) % math.pi - math.pi else: raise ValueError(f"Invalid model state {facing_direction}") def set_gripper(value): goal = control_msgs.msg.GripperCommandGoal() goal.command.position = value # From 0.0 to 0.8 goal.command.max_effort = -1 # # Do not limit the effort action_gripper.send_goal_and_wait(goal, rospy.Duration(10)) return action_gripper.get_result() if __name__ == "__main__": print("Initializing node of kinematics") rospy.init_node("send_joints") controller = ArmController() # Create an action client for the gripper action_gripper = actionlib.SimpleActionClient( "/gripper_controller/gripper_cmd", control_msgs.msg.GripperCommandAction ) print("Waiting for action of gripper controller") action_gripper.wait_for_server() setstatic_srv = rospy.ServiceProxy("/link_attacher_node/setstatic", SetStatic) attach_srv = rospy.ServiceProxy("/link_attacher_node/attach", Attach) detach_srv = rospy.ServiceProxy("/link_attacher_node/detach", Attach) setstatic_srv.wait_for_service() attach_srv.wait_for_service() detach_srv.wait_for_service() controller.move_to(*DEFAULT_POS, DEFAULT_QUAT) print("Waiting for detection of the models") rospy.sleep(0.5) legos = get_legos_pos(vision=True) legos.sort(reverse=True, key=lambda a: (a[1].position.x, a[1].position.y)) for model_name, model_pose in legos: open_gripper() try: model_home = MODELS_INFO[model_name]["home"] model_size = MODELS_INFO[model_name]["size"] except ValueError as e: print(f"Model name {model_name} was not recognized!") continue # Get actual model_name at model xyz coordinates try: gazebo_model_name = get_gazebo_model_name(model_name, model_pose) except ValueError as e: print(e) continue # Straighten lego straighten(model_pose, gazebo_model_name) controller.move(dz=0.15) """ Go to destination """ x, y, z = model_home z += model_size[2] / 2 +0.004 print(f"Moving model {model_name} to {x} {y} {z}") controller.move_to(x, y, target_quat=DEFAULT_QUAT * PyQuaternion(axis=[0, 0, 1], angle=math.pi / 2)) # Lower the object and release controller.move_to(x, y, z) set_model_fixed(gazebo_model_name) open_gripper(gazebo_model_name) controller.move(dz=0.15) if controller.gripper_pose[0][1] > -0.3 and controller.gripper_pose[0][0] > 0: controller.move_to(*DEFAULT_POS, DEFAULT_QUAT) # increment z in order to stack lego correctly MODELS_INFO[model_name]["home"][2] += model_size[2] - INTERLOCKING_OFFSET print("Moving to Default Position") controller.move_to(*DEFAULT_POS, DEFAULT_QUAT) open_gripper() rospy.sleep(0.4) 以下是视觉识别代码: #! /usr/bin/env python3 import cv2 as cv import numpy as np import torch import message_filters import rospy import sys import time from sensor_msgs.msg import Image from cv_bridge import CvBridge from rospkg import RosPack # get abs path from os import path # get home path from gazebo_msgs.msg import ModelStates from geometry_msgs.msg import * from pyquaternion import Quaternion as PyQuaternion bridge = CvBridge() # Global variables path_yolo = path.join(path.expanduser('~'), 'yolov5') path_vision = RosPack().get_path('vision') path_weigths = path.join(path_vision, 'weigths') cam_point = (-0.44, -0.5, 1.58) height_tavolo = 0.74 dist_tavolo = None origin = None model = None model_orientation = None legoClasses = ['X1-Y1-Z2', 'X1-Y2-Z1', 'X1-Y2-Z2', 'X1-Y2-Z2-CHAMFER', 'X1-Y2-Z2-TWINFILLET', 'X1-Y3-Z2', 'X1-Y3-Z2-FILLET', 'X1-Y4-Z1', 'X1-Y4-Z2', 'X2-Y2-Z2', 'X2-Y2-Z2-FILLET'] argv = sys.argv a_show = '-show' in argv # Utility Functions def get_dist_tavolo(depth, hsv, img_draw): global dist_tavolo #color = (120,1,190) #mask = get_lego_mask(color, hsv, (5, 5, 5)) #dist_tavolo = depth[mask].max() #if dist_tavolo > 1: dist_tavolo -= height_tavolo dist_tavolo = np.nanmax(depth) def get_origin(img): global origin origin = np.array(img.shape[1::-1]) // 2 def get_lego_distance(depth): return depth.min() def get_lego_color(center, rgb): return rgb[center].tolist() def get_lego_mask(color, hsv, toll = (20, 20, 255)): thresh = np.array(color) mintoll = thresh - np.array([toll[0], toll[1], min(thresh[2]-1, toll[2])]) maxtoll = thresh + np.array(toll) return cv.inRange(hsv, mintoll, maxtoll) def getDepthAxis(height, lego): X, Y, Z = (int(x) for x in lego[1:8:3]) #Z = (0.038, 0.057) X = (0.031, 0.063) Y = (0.031, 0.063, 0.095, 0.127) rapZ = height / 0.019 - 1 pinZ = round(rapZ) rapXY = height / 0.032 pinXY = round(rapXY) errZ = abs(pinZ - rapZ) + max(pinZ - 2, 0) errXY = abs(pinXY - rapXY) + max(pinXY - 4, 0) if errZ < errXY: return pinZ, 2, pinZ == Z # pin, is ax Z, match else: if pinXY == Y: return pinXY, 1, True else: return pinXY, 0, pinXY == X def point_distorption(point, height, origin): p = dist_tavolo / (dist_tavolo - height) point = point - origin return p * point + origin def point_inverse_distortption(point, height): p = dist_tavolo / (dist_tavolo - height) point = point - origin return point / p + origin def myimshow(title, img): def mouseCB(event,x,y,a,b): print(x, y, img[y, x], "\r",end='',flush=True) print("\033[K", end='') cv.imshow(title, img) cv.setMouseCallback(title, mouseCB) cv.waitKey() # ----------------- LOCALIZATION ----------------- # def process_item(imgs, item): #images rgb, hsv, depth, img_draw = imgs #obtaining Yolo informations (class, coordinates, center) x1, y1, x2, y2, cn, cl, nm = item.values() mar = 15 x1, y1 = max(mar, x1), max(mar, y1) x2, y2 = min(rgb.shape[1]-mar, x2), min(rgb.shape[0]-mar, y2) boxMin = np.array((x1-mar, y1-mar)) x1, y1, x2, y2 = np.int0((x1, y1, x2, y2)) boxCenter = (y2 + y1) // 2, (x2 + x1) // 2 color = get_lego_color(boxCenter, rgb) hsvcolor = get_lego_color(boxCenter, hsv) sliceBox = slice(y1-mar, y2+mar), slice(x1-mar, x2+mar) #crop img with coordinate bounding box; computing all imgs l_rgb = rgb[sliceBox] l_hsv = hsv[sliceBox] if a_show: cv.rectangle(img_draw, (x1,y1),(x2,y2), color, 2) l_depth = depth[sliceBox] l_mask = get_lego_mask(hsvcolor, l_hsv) # filter mask by color l_mask = np.where(l_depth < dist_tavolo, l_mask, 0) l_depth = np.where(l_mask != 0, l_depth, dist_tavolo) #myimshow("asda", hsv) #getting lego height from camera and table l_dist = get_lego_distance(l_depth) l_height = dist_tavolo - l_dist #masking l_top_mask = cv.inRange(l_depth, l_dist-0.002, l_dist+0.002) #cv.bitwise_xor(img_draw,img_draw,img_draw, mask=cv.inRange(depth, l_dist-0.002, l_dist+0.002)) #myimshow("hmask", l_top_mask) # model detect orientation depth_borded = np.zeros(depth.shape, dtype=np.float32) depth_borded[sliceBox] = l_depth depth_image = cv.normalize( depth_borded, None, alpha=0, beta=255, norm_type=cv.NORM_MINMAX, dtype=cv.CV_8U ) depth_image = cv.cvtColor(depth_image, cv.COLOR_GRAY2RGB).astype(np.uint8) #yolo in order to keep te orientation #print("Model orientation: Start...", end='\r') model_orientation.conf = 0.7 results = model_orientation(depth_image) pandino = [] pandino = results.pandas().xyxy[0].to_dict(orient="records") n = len(pandino) #print("Model orientation: Finish", n) # Adjust prediction pinN, ax, isCorrect = getDepthAxis(l_height, nm) if not isCorrect and ax == 2: if cl in (1,2,3) and pinN in (1, 2): # X1-Y2-Z*, *1,2,2-CHAMFER cl = 1 if pinN == 1 else 2 # -> Z'pinN' elif cl in (7, 8) and pinN in (1, 2): # X1-Y4-Z*, *1,2 cl = 7 if pinN == 1 else 8 # -> Z'pinN' elif pinN == -1: nm = "{} -> {}".format(nm, "Target") else: print("[Warning] Error in classification") elif not isCorrect: ax = 1 if cl in (0, 2, 5, 8) and pinN <= 4: # X1-Y*-Z2, *1,2,3,4 cl = (2, 5, 8)[pinN-2] # -> Y'pinN' elif cl in (1, 7) and pinN in (2, 4): # X1-Y*-Z1, *2,4 cl = 1 if pinN == 2 else 7 # -> Y'pinN' elif cl == 9 and pinN == 1: # X2-Y2-Z2 cl = 2 # -> X1 ax = 0 else: print("[Warning] Error in classification") nm = legoClasses[cl] if n != 1: print("[Warning] Classification not found") or_cn, or_cl, or_nm = ['?']*3 or_nm = ('lato', 'lato', 'sopra/sotto')[ax] else: print() #obtaining Yolo informations (class, coordinates, center) or_item = pandino[0] or_cn, or_cl, or_nm = or_item['confidence'], or_item['class'], or_item['name'] if or_nm == 'sotto': ax = 2 if or_nm == 'lato' and ax == 2: ax = 1 #--- #creating silouette top surface lego contours, hierarchy = cv.findContours(l_top_mask, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE) top_center = np.zeros(2) for cnt in contours: tmp_center, top_size, top_angle = cv.minAreaRect(cnt) top_center += np.array(tmp_center) top_center = boxMin + top_center / len(contours) #creating silouette lego contours, hierarchy = cv.findContours(l_mask, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE) if len(contours) == 0: return None cnt = contours[0] l_center, l_size, l_angle = cv.minAreaRect(cnt) l_center += boxMin if ax != 2: l_angle = top_angle l_box = cv.boxPoints((l_center, l_size, l_angle)) if l_size[0] <=3 or l_size[1] <=3: cv.drawContours(img_draw, np.int0([l_box]), 0, (0,0,0), 2) return None # filter out artifacts if a_show: cv.drawContours(img_draw, np.int0([l_box]), 0, color, 2) # silouette distorption # get vertexs distance from origin top_box = l_box.copy() vertexs_norm = [(i, np.linalg.norm(vec - origin)) for vec, i in zip(l_box, range(4))] vertexs_norm.sort(key=lambda tup: tup[1]) # get closest vertex iver = vertexs_norm[0][0] vec = l_box[iver] # distorping closest vertex if or_nm == 'sopra': l_height -= 0.019 top_box[iver] = point_distorption(l_box[iver], l_height, origin) v0 = top_box[iver] - vec # adapt adiacents veretx v1 = l_box[iver - 3] - vec # i - 3 = i+1 % 4 v2 = l_box[iver - 1] - vec top_box[iver - 3] += np.dot(v0, v2) / np.dot(v2, v2) * v2 top_box[iver - 1] += np.dot(v0, v1) / np.dot(v1, v1) * v1 l_center = (top_box[0] + top_box[2]) / 2 if a_show: cv.drawContours(img_draw, np.int0([top_box]), 0, (5,5,5), 2) cv.circle(img_draw, np.int0(top_box[iver]),1, (0,0,255),1,cv.LINE_AA) #rotation and axis drawing if or_nm in ('sopra', 'sotto', 'sopra/sotto', '?'): # fin x, y directions (dirX, dirY) dirZ = np.array((0,0,1)) if or_nm == 'sotto': dirZ = np.array((0,0,-1)) projdir = l_center - top_center if np.linalg.norm(projdir) < l_size[0] / 10: dirY = top_box[0] - top_box[1] dirX = top_box[0] - top_box[-1] if np.linalg.norm(dirY) < np.linalg.norm(dirX): dirX, dirY = dirY, dirX projdir = dirY * np.dot(dirY, projdir) edgeFar = [ver for ver in top_box if np.dot(ver - l_center, projdir) >= 0][:2] dirY = (edgeFar[0] + edgeFar[1]) / 2 - l_center dirY /= np.linalg.norm(dirY) dirY = np.array((*dirY, 0)) dirX = np.cross(dirZ, dirY) elif or_nm == "lato": # find pin direction (dirZ) edgePin = [ver for ver in top_box if np.dot(ver - l_center, l_center - top_center) >= 0][:2] dirZ = (edgePin[0] + edgePin[1]) / 2 - l_center dirZ /= np.linalg.norm(dirZ) dirZ = np.array((*dirZ, 0)) if cl == 10: if top_size[1] > top_size[0]: top_size = top_size[::-1] if top_size[0] / top_size[1] < 1.7: ax = 0 if ax == 0: vx,vy,x,y = cv.fitLine(cnt, cv.DIST_L2,0,0.01,0.01) dir = np.array((vx, vy)) vertexs_distance = [abs(np.dot(ver - l_center, dir)) for ver in edgePin] iverFar = np.array(vertexs_distance).argmin() dirY = edgePin[iverFar] - edgePin[iverFar-1] dirY /= np.linalg.norm(dirY) dirY = np.array((*dirY, 0)) dirX = np.cross(dirZ, dirY) if a_show: cv.circle(img_draw, np.int0(edgePin[iverFar]), 5, (70,10,50), 1) #cv.line(img_draw, np.int0(l_center), np.int0(l_center+np.array([int(vx*100),int(vy*100)])),(0,0,255), 3) if ax == 1: dirY = np.array((0,0,1)) dirX = np.cross(dirZ, dirY) if a_show: cv.line(img_draw, *np.int0(edgePin), (255,255,0), 2) l_center = point_inverse_distortption(l_center, l_height) # post rotation extra theta = 0 if cl == 1 and ax == 1: theta = 1.715224 - np.pi / 2 if cl == 3 and or_nm == 'sotto': theta = 2.359515 - np.pi if cl == 4 and ax == 1: theta = 2.145295 - np.pi if cl == 6 and or_nm == 'sotto': theta = 2.645291 - np.pi if cl == 10 and or_nm == 'sotto': theta = 2.496793 - np.pi rotX = PyQuaternion(axis=dirX, angle=theta) dirY = rotX.rotate(dirY) dirZ = rotX.rotate(dirZ) if a_show: # draw frame lenFrame = 50 unit_z = 0.031 unit_x = 22 * 0.8039 / dist_tavolo x_to_z = lenFrame * unit_z/unit_x center = np.int0(l_center) origin_from_top = origin - l_center endX = point_distorption(lenFrame * dirX[:2], x_to_z * dirX[2], origin_from_top) frameX = (center, center + np.int0(endX)) endY = point_distorption(lenFrame * dirY[:2], x_to_z * dirY[2], origin_from_top) frameY = (center, center + np.int0(endY)) endZ = point_distorption(lenFrame * dirZ[:2], x_to_z * dirZ[2], origin_from_top) frameZ = (center, center + np.int0(endZ)) cv.line(img_draw, *frameX, (0,0,255), 2) cv.line(img_draw, *frameY, (0,255,0), 2) cv.line(img_draw, *frameZ, (255,0,0), 2) # --- # draw text if or_cl != '?': or_cn = ['SIDE', 'UP', 'DOWN'][or_cl] text = "{} {:.2f} {}".format(nm, cn, or_cn) (text_width, text_height) = cv.getTextSize(text, cv.FONT_HERSHEY_DUPLEX, 0.4, 1)[0] text_offset_x = boxCenter[1] - text_width // 2 text_offset_y = y1 - text_height box_coords = ((text_offset_x - 1, text_offset_y + 1), (text_offset_x + text_width + 1, text_offset_y - text_height - 1)) cv.rectangle(img_draw, box_coords[0], box_coords[1], (210,210,10), cv.FILLED) cv.putText(img_draw, text, (text_offset_x, text_offset_y), cv.FONT_HERSHEY_DUPLEX, 0.4, (255, 255, 255), 1) def getAngle(vec, ax): vec = np.array(vec) if not vec.any(): return 0 vec = vec / np.linalg.norm(vec) wise = 1 if vec[-1] >= 0 else -1 dotclamp = max(-1, min(1, np.dot(vec, np.array(ax)))) return wise * np.arccos(dotclamp) msg = ModelStates() msg.name = nm #fov = 1.047198 #rap = np.tan(fov) #print("rap: ", rap) xyz = np.array((l_center[0], l_center[1], l_height / 2 + height_tavolo)) xyz[:2] /= rgb.shape[1], rgb.shape[0] xyz[:2] -= 0.5 xyz[:2] *= (-0.968, 0.691) xyz[:2] *= dist_tavolo / 0.84 xyz[:2] += cam_point[:2] rdirX, rdirY, rdirZ = dirX, dirY, dirZ rdirX[0] *= -1 rdirY[0] *= -1 rdirZ[0] *= -1 qz1 = PyQuaternion(axis=(0,0,1), angle=-getAngle(dirZ[:2], (1,0))) rdirZ = qz1.rotate(dirZ) qy2 = PyQuaternion(axis=(0,1,0), angle=-getAngle((rdirZ[2],rdirZ[0]), (1,0))) rdirX = qy2.rotate(qz1.rotate(rdirX)) qz3 = PyQuaternion(axis=(0,0,1), angle=-getAngle(rdirX[:2], (1,0))) rot = qz3 * qy2 * qz1 rot = rot.inverse msg.pose = Pose(Point(*xyz), Quaternion(x=rot.x,y=rot.y,z=rot.z,w=rot.w)) #pub.publish(msg) #print(msg) return msg #image processing def process_image(rgb, depth): # img_draw = rgb.copy() # 创建RGB图像副本用于可视化[^4] # hsv = cv.cvtColor(rgb, cv.COLOR_BGR2HSV) # 转换为HSV色彩空间[^4] # get_dist_tavolo(depth, hsv, img_draw) # 深度信息与HSV结合处理 # get_origin(rgb) # 基于原始RGB图像确定坐标系原点 # #results collecting localization # #print("Model localization: Start...",end='\r') # model.conf = 0.6 # 设置检测置信度阈值 # results = model(rgb) # YOLO模型推理[^2] # pandino = results.pandas().xyxy[0].to_dict(orient="records") # 结果转为字典 # #print("Model localization: Finish ") # # ---- # if depth is not None: # imgs = (rgb, hsv, depth, img_draw) # 多源数据组合 # results = [process_item(imgs, item) for item in pandino] # 逐项处理 # # ---- # msg = ModelStates() # 创建ROS消息对象 # for point in results: # if point is not None: # msg.name.append(point.name) # 添加物体名称 # msg.pose.append(point.pose) # 添加位姿信息 # pub.publish(msg) #发布信息 # if a_show: # 显示控制标志 # cv.imshow("vision-results" \ # "", img_draw) # 显示结果图像 # cv.waitKey() #等待按键 # 移除原代码中的图像读取和显示等待逻辑 img_draw = rgb.copy() hsv = cv.cvtColor(rgb, cv.COLOR_BGR2HSV) # 简化深度处理(根据实际需求调整) if depth is not None: get_dist_tavolo(depth, hsv, img_draw) get_origin(rgb) # YOLO实时检测(调整置信度平衡速度精度) model.conf = 0.2 # 可降低至0.4提升速度 results = model(rgb) # 结果处理 pandino = results.pandas().xyxy[0].to_dict(orient="records") if depth is not None: imgs = (rgb, hsv, depth, img_draw) results = [process_item(imgs, item) for item in pandino] # 发布结果 msg = ModelStates() for point in results: if point is not None: msg.name.append(point.name) msg.pose.append(point.pose) pub.publish(msg) # 实时显示(可选) if a_show: cv.imshow("Camera", img_draw) cv.waitKey(25) & 0xFF # 关键修改:非阻塞等待 pass def process_CB(image_rgb, image_depth): t_start = time.time() #from standard message image to opencv image rgb = CvBridge().imgmsg_to_cv2(image_rgb, "bgr8") depth = CvBridge().imgmsg_to_cv2(image_depth, "32FC1") process_image(rgb, depth) print("Time:", time.time() - t_start) # rospy.signal_shutdown(0) pass #init node function def start_node(): global pub print("Starting Node Vision 1.0") rospy.init_node('vision') print("Subscribing to camera images") # viewer = ImageViewer() #topics subscription rgb = message_filters.Subscriber("/camera/color/image_raw", Image,queue_size=1) depth = message_filters.Subscriber("/camera/depth/image_raw", Image,queue_size=1) #publisher results pub=rospy.Publisher("lego_detections", ModelStates, queue_size=1) print("Localization is starting.. ") print("(Waiting for images..)", end='\r'), print(end='\033[K') #images synchronization syncro = message_filters.TimeSynchronizer([rgb, depth], 1, reset=True) syncro.registerCallback(process_CB) #keep node always alive rospy.spin() # Destroy OpenCV windows on shutdown cv.destroyAllWindows() pass def load_models(): global model, model_orientation #yolo model and weights classification print("Loading model best.pt") weight = path.join(path_weigths, 'best.pt') model = torch.hub.load(path_yolo,'custom',path=weight, source='local') #yolo model and weights orientation print("Loading model orientation.pt") weight = path.join(path_weigths, 'depth.pt') model_orientation = torch.hub.load(path_yolo,'custom',path=weight, source='local') pass # class ImageViewer: # def __init__(self): # # Initialize the CvBridge # self.bridge = CvBridge() # # Get parameters # self.view_image = rospy.get_param('~view_image', True) # self.input_image_topic = rospy.get_param('~input_image_topic', '/camera/color/image_raw') # # Subscribe to the image topic # self.image_sub = rospy.Subscriber(self.input_image_topic, Image, self.image_callback) # def image_callback(self, data): # if self.view_image: # # Convert the ROS Image message to OpenCV format # cv_image = self.bridge.imgmsg_to_cv2(data, "bgr8") # # Display the image using OpenCV # cv.imshow("Camera View", cv_image) # cv.waitKey(1) def image_callback(msg): """ROS图像消息回调函数""" try: # 将ROS图像消息转为OpenCV格式(BGR) cv_image = bridge.imgmsg_to_cv2(msg, "bgr8") # 调用处理函数 process_image(cv_image, None) # depth设为None except Exception as e: rospy.logerr(f"图像转换错误: {e}") if __name__ == '__main__': load_models() try: start_node() except rospy.ROSInterruptException: pass 要如何进行修改
最新发布
11-05
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值