相机不动,机构动作----Hands Eyes
最近在研究 手眼标定,发现大家都需付费,搞啥子,说好的开源。。。
以相机在上固定不动,机械手为 EPSON_Robot 为例,详细的一步一步实例操作指引 EPSON_Robot 的192.168.0.1 2004 Server
详细操作步骤
1. 启动程序
运行以下 Python 代码后,会弹出一个标题为 “手眼标定操作指引” 的 UI 窗口,窗口内顶部首先会显示一个文本标签提示 “当前步骤:0/5”,代表当前处于整个操作流程的第 0 步,预估总共有 5 步操作,下方会看到左右排列的 “实时标定” 和 “图像标定” 两个按钮,以及下方的 “采集手眼数据”、“执行手眼标定”、“显示采集的数据” 以及 “重置操作” 四个按钮(后四个按钮初始时部分功能未绑定,等待选择标定模式后确定具体功能)。
2. 选择标定模式(以下分两种情况说明)
实时标定模式选择及操作:
- 点击 “实时标定” 按钮,此时会先重置所有相关变量和操作步骤状态(通过调用
reset_operation
函数实现),并将标定模式设置为 “实时标定”,然后 “采集手眼数据” 按钮绑定的功能会变为collect_data_real_time
函数,“执行手眼标定” 按钮绑定的功能会变为perform_calibration_real_time
函数。
图像标定模式选择及操作:
- 点击 “图像标定” 按钮,同样先重置相关状态并设置标定模式为 “图像标定”,“采集手眼数据” 按钮绑定的功能会变为
collect_data_image
函数,“执行手眼标定” 按钮绑定的功能会变为perform_calibration_image
函数。
3. 实时标定模式下的具体操作步骤(假设已选择 “实时标定”)
-
采集第一组数据:
- 点击 “采集手眼数据” 按钮,此时会有以下操作和提示:
- 首先弹出操作提示框显示 “接下来需要获取 EPSON_Robot 机器人手(末端执行器)的位姿信息,当前代码中只是简单模拟生成了一个单位矩阵(np.eye (4))来表示手的位姿,在实际应用中,你需要通过和 EPSON_Robot 的控制系统进行通信来获取真实的末端执行器的位姿信息,这个位姿信息通常以齐次变换矩阵(4x4)的形式表示,包含了旋转和平移信息,并将其添加到 hand_poses 列表中。”,这里你需要按照 EPSON_Robot 提供的方式(比如通过网络通信、专用 API 等)获取真实的机器人末端位姿来替换模拟代码部分(后续步骤同此情况,暂用模拟数据示意流程)。
- 然后又会弹出操作提示框显示 “接下来将尝试打开默认的摄像头获取图像中的棋盘格角点坐标,程序会先打开摄像头读取一帧图像,转换为灰度图像后进行角点检测。如果检测到了符合设定棋盘格尺寸的棋盘格,会将角点坐标等相关数据添加;若未检测到或摄像头打开失败会弹出相应提示,请根据提示进行操作。”,程序会尝试打开摄像头,读取一帧图像并进行处理检测棋盘格角点。
- 如果摄像头成功打开且检测到了符合设定棋盘格尺寸(代码中设定为
board_size=(8, 6)
)的棋盘格,会将检测到的棋盘格角点坐标(以像素为单位,格式为Nx2
的numpy
数组,N
为角点数量)添加到image_points
列表中,同时对应的真实世界中的棋盘格角点坐标(在objp
中定义,根据设定的棋盘格方格边长square_size = 0.025
米和棋盘格布局生成的三维坐标)添加到object_points
列表中,并且弹出提示框告知 “已成功采集一组手眼数据!”,同时会在一个300 * 300
大小且居中的窗口显示当前实时图像 1 秒钟左右(方便你查看图像及棋盘格情况),便于确认采集的数据对应的图像情况。若未检测到棋盘格,会弹出提示框提示 “未检测到棋盘格,请调整相机视角后重新采集。”;若无法打开摄像头,则会弹出提示框提示 “无法打开摄像头,请检查设备连接。”。
- 点击 “采集手眼数据” 按钮,此时会有以下操作和提示:
-
采集多组数据:
重复点击 “采集手眼数据” 按钮,按照上述采集第一组数据的流程,操作 EPSON_Robot 使其末端执行器处于不同的位姿(比如通过编程控制机器人运动到不同位置、不同姿态角度等,具体操作依赖 EPSON_Robot 的操作手册和编程接口),同时确保相机能正常拍摄到棋盘格,每次成功采集都会更新 UI 上的步骤文本(如第二次点击变为 “当前步骤:2/5” 等),建议至少采集 4 组不同状态下的数据,确保机器人手和相机相对位置及姿态有足够的变化范围,这样可以提高标定的准确性。 -
执行标定:
当采集的数据组数满足至少 4 组后,点击 “执行手眼标定” 按钮,此时会发生以下操作及提示:- 首先
current_step
变量会再次自增 1,UI 上的步骤文本更新为对应的数字(例如采集了 4 次数据后点击此按钮,就变为 “当前步骤:5/5”),代表进入到执行标定这一关键步骤,同时会弹出操作提示框显示 “首先会检查采集的数据数量是否足够用于标定,需要至少采集 4 组数据,如果不足会弹出错误提示框并终止标定操作,请确保之前已采集足够的数据。” 用于提醒你确认数据数量情况。 - 程序会检查采集的数据数量,如果不足会弹出错误提示框提示 “采集的数据不足,请至少采集 4 组数据进行标定!” 并终止标定操作,同时步骤文本也停留在当前数字不再变化。
- 如果数据足够,会弹出操作提示框显示 “接下来将调用 cv2.calibrateHandEye 函数进行手眼标定,采用的是 Tsai-Lenz 算法,该函数需要传入机器人手的旋转矩阵列表(从 hand_poses 中提取每个位姿的前 3x3 部分表示旋转)、平移向量列表(提取每个位姿的前 3 列第 4 行表示平移)、图像中棋盘格角点坐标列表(转换格式并转换数据类型为合适的 numpy 数组形式)以及真实世界中的棋盘格角点坐标列表(object_points)作为参数。” 来告知你标定算法及参数传入相关操作,然后调用
cv2.calibrateHandEye
函数进行手眼标定。 - 如果标定过程顺利,会将得到的旋转向量通过
cv2.Rodrigues
函数转换为旋转矩阵,并与平移向量组合成一个 4x4 的齐次变换矩阵形式存储在calibration_result
变量中,表示手眼之间的坐标变换关系,最后弹出提示框展示这个标定得到的变换矩阵内容;若标定过程出现错误(例如数据格式不正确、算法计算出现异常等情况),会弹出错误提示框显示具体的错误信息,步骤文本同样停留在当前执行标定的这一步骤数字上。
- 首先
4. 图像标定模式下的具体操作步骤(假设已选择 “图像标定”)
-
采集第一组数据:
- 点击 “采集手眼数据” 按钮,会弹出操作提示框显示 “接下来将生成一个模拟的棋盘格图像作为示例图像用于采集数据,程序会自动创建并保存图像文件到 C:\BD.png,然后提取棋盘格角点坐标以及对应的机器人手位姿信息(这里假设你已经有相关对应关系的数据,实际需完善获取及关联逻辑),然后添加到相应列表中用于后续标定。”。
- 程序会自动生成一个简单的
4 * 4
棋盘格样式的图像(以白色方格和黑色背景组成,模拟棋盘格),并将其存储在C:\BD.png
,然后在一个300 * 300
大小且居中的窗口显示该图像 1 秒钟左右(方便你查看图像情况),之后模拟添加一组数据(这里暂时只是简单模拟添加,实际需要替换为真实从图像读取并关联对应机器人手位姿等逻辑),即添加一个模拟的机器人手位姿(np.eye(4)
模拟,实际要替换)到hand_poses
列表,模拟的图像中的角点坐标(corners = np.array([[100, 200], [110, 210], [120, 220], [130, 230]])
,实际要替换为真实从图像读取检测到的角点)到image_points
列表,以及对应的真实世界中的棋盘格角点坐标(objp[0]
)到object_points
列表,最后弹出提示框告知 “已成功采集一组手眼数据(从图像文件)!”。
-
采集多组数据:
重复点击 “采集手眼数据” 按钮,按照上述采集第一组数据的流程,每次点击都会模拟添加一组数据(实际需要你准备多组包含棋盘格的图像文件,并且有对应的机器人手位姿信息,通过相应代码逻辑读取图像、提取角点坐标以及关联正确的手位姿添加到对应列表中),每次成功采集都会更新 UI 上的步骤文本(如第二次点击变为 “当前步骤:2/5” 等),建议至少采集 4 组不同状态下的数据,确保机器人手和相机相对位置及姿态有足够的变化范围(这里体现为不同图像中棋盘格与模拟的机器人手位姿的对应关系多样性),这样可以提高标定的准确性。 -
执行标定:
当采集的数据组数满足至少 4 组后,点击 “执行手眼标定” 按钮,操作流程和提示信息基本与实时标定模式下执行标定类似,会有步骤更新提示、数据数量检查、标定算法及参数传入提示等,若数据足够会进行标定计算,成功则展示标定结果,出现错误则弹出相应错误提示框,步骤文本也会根据操作状态停留在相应数字上。
5. 查看采集的数据(可选步骤,两种标定模式通用)
在采集了一定组数的数据后,你可以点击 “显示采集的数据” 按钮,会弹出一个新的提示框,里面展示了已采集的数据组数、手的位姿示例(展示第一个采集的手位姿矩阵)以及图像点坐标示例(展示第一个采集的图像中棋盘格角点坐标数组)等信息,方便你确认采集的数据是否合理以及是否符合预期,此操作步骤文本不会改变。
6. 重置操作(可选步骤,两种标定模式通用)
如果想要重新开始手眼标定流程,例如采集的数据不理想或者想要重新进行整个操作演示等情况,可以点击 “重置操作” 按钮,程序会执行以下操作:
内部会将所有相关的全局变量(hand_poses
、image_points
、object_points
、calibration_result
以及 current_step
)恢复到初始状态,清空之前采集的数据以及重置步骤计数为 0,然后通过 update_step_text
函数将 UI 上的步骤文本更新回 “当前步骤:0/5”,同时弹出提示框告知 “已重置操作,可重新开始手眼标定流程。”,之后就可以再次按照上述步骤重新进行手眼标定操作了。
import cv2
import numpy as np
import tkinter as tk
from tkinter import messagebox, Label, Button
import socket
import struct
import os
import glob# 全局变量
hand_poses = [] # 存储机器人手(末端执行器)的位姿,以齐次变换矩阵形式(4x4)
image_points = [] # 存储图像中对应的棋盘格角点坐标(Nx2,N为角点数量)
object_points = [] # 存储真实世界中的棋盘格角点坐标(Nx3)
calibration_result = None # 存储标定结果
current_step = 0 # 当前操作步骤计数
total_steps = 5 # 预估的总操作步骤数(可根据实际情况调整)# 棋盘格尺寸(内角点数,例如8x6)
board_size = (8, 6)
square_size = 0.025 # 棋盘格方格边长,单位米(根据实际情况设置)# 生成真实世界中的棋盘格角点坐标
objp = np.zeros((board_size[0] * board_size[1], 3), np.float32)
objp[:, :2] = np.mgrid[0:board_size[0], 0:board_size[1]].T.reshape(-1, 2) * square_size
objp = [objp]def update_step_text():"""更新UI上显示的步骤文本"""step_text.set(f"当前步骤:{current_step}/{total_steps}")# 通过Socket通信获取EPSON_Robot机器人手位姿信息(根据给定协议模拟,实际需按详细规范调整)
def get_robot_pose_via_socket():"""通过Socket通信按照指定的IP、端口以及指令格式获取机器人末端执行器位姿信息"""try:# 创建Socket连接client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)client_socket.connect(("192.168.0.1", 2004))# 发送获取位姿数据的指令(按照给定格式)request_command = "Print #205, \"send data to pc\""client_socket.send(request_command.encode())# 接收数据(假设返回的数据格式是按一定顺序排列的浮点数表示4x4齐次变换矩阵,这里按此模拟解析,实际需按准确协议)received_data = client_socket.recv(1024)# 这里假设接收到的数据直接就是16个连续的浮点数表示位姿矩阵元素,实际可能需要更复杂的解析pose_data = struct.unpack('16f', received_data)pose_matrix = np.array(pose_data).reshape(4, 4)client_socket.close()return pose_matrixexcept socket.error as e:messagebox.showerror("Socket通信错误", f"Socket通信出现错误: {str(e)},请检查网络连接及机器人配置。")return np.eye(4) # 如果出现错误,返回单位矩阵作为占位(可根据实际情况调整错误处理方式)# 采集数据函数(用于实时标定)
def collect_data_real_time():"""实时采集手眼数据,包括获取EPSON_Robot机器人手的位姿和对应的图像中棋盘格角点坐标"""global current_stepcurrent_step += 1update_step_text()# 通过Socket通信获取机器人手位姿hand_pose = get_robot_pose_via_socket()hand_poses.append(hand_pose)# 提醒获取图像中棋盘格角点坐标相关操作messagebox.showinfo("操作提示", "接下来将尝试打开默认的摄像头获取图像中的棋盘格角点坐标,程序会先打开摄像头读取一帧图像,转换为灰度图像后进行角点检测。如果检测到了符合设定棋盘格尺寸的棋盘格,会将角点坐标等相关数据添加;若未检测到或摄像头打开失败会弹出相应提示,请根据提示进行操作。")# 打开摄像头获取图像中的棋盘格角点坐标cap = cv2.VideoCapture(0)found, frame = cap.read()if found:gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)ret, corners = cv2.findChessboardCorners(gray, board_size, None)if ret:image_points.append(corners)object_points.append(objp[0])messagebox.showinfo("提示", "已成功采集一组手眼数据!")# 显示实时图像在300*300窗口且居中display_image(frame)else:messagebox.showerror("错误", "未检测到棋盘格,请调整相机视角后重新采集。")else:messagebox.showerror("错误", "无法打开摄像头,请检查设备连接。")cap.release()def display_image(frame):"""在300*300窗口且居中显示图像"""height, width = frame.shape[:2]max_dim = max(height, width)scale = 300 / max_dimnew_width = int(width * scale)new_height = int(height * scale)resized_frame = cv2.resize(frame, (new_width, new_height))top = (300 - new_height) // 2left = (300 - new_width) // 2centered_frame = np.zeros((300, 300, 3), dtype=frame.dtype)centered_frame[top:top + new_height, left:left + new_width] = resized_framecv2.imshow('实时图像', centered_frame)cv2.waitKey(1000) # 显示1秒,可根据需要调整时间cv2.destroyAllWindows()# 执行实时标定函数
def perform_calibration_real_time():"""执行实时手眼标定操作,利用采集的数据计算手眼之间的变换关系"""global current_step, calibration_resultcurrent_step += 1update_step_text()# 提醒检查数据数量是否足够messagebox.showinfo("操作提示", "首先会检查采集的数据数量是否足够用于标定,需要至少采集4组数据,如果不足会弹出错误提示框并终止标定操作,请确保之前已采集足够的数据。")if len(hand_poses) < 4 or len(image_points) < 4:messagebox.showerror("错误", "采集的数据不足,请至少采集4组数据进行标定!")returntry:# 使用OpenCV的手眼标定函数(这里以Tsai-Lenz算法为例,实际可能有多种选择)# 提醒关于标定算法及参数传入相关操作messagebox.showinfo("操作提示", "接下来将调用cv2.calibrateHandEye函数进行手眼标定,采用的是Tsai-Lenz算法,该函数需要传入机器人手的旋转矩阵列表(从hand_poses中提取每个位姿的前3x3部分表示旋转)、平移向量列表(提取每个位姿的前3列第4行表示平移)、图像中棋盘格角点坐标列表(转换格式并转换数据类型为合适的numpy数组形式)以及真实世界中的棋盘格角点坐标列表(object_points)作为参数。")retval, rotation_vector, translation_vector = cv2.calibrateHandEye([pose[:3, :3] for pose in hand_poses],[pose[:3, 3] for pose in hand_poses],[points.reshape(-1, 2).astype(np.float64) for points in image_points],object_points)# 将旋转向量和平移向量转换为变换矩阵形式方便后续使用rotation_matrix, _ = cv2.Rodrigues(rotation_vector)calibration_result = np.hstack((rotation_matrix, translation_vector.reshape(3, 1)))calibration_result = np.vstack((calibration_result, np.array([0, 0, 0, 1])))messagebox.showinfo("提示", "手眼标定完成,得到变换矩阵:\n{}".format(calibration_result))except Exception as e:messagebox.showerror("错误", "标定过程出现错误:{}".format(str(e)))# 采集数据函数(用于图像标定)
def collect_data_image():"""从图像文件中采集手眼数据,读取真实图像及关联对应的机器人手位姿信息"""global current_stepcurrent_step += 1update_step_text()image_files = glob.glob("*.jpg") + glob.glob(r"c:\*.png") # 获取当前目录下所有图像文件(可按实际指定路径)if not image_files:messagebox.showerror("错误", "未找到可用的图像文件,请确保当前目录下有包含棋盘格的图像文件(支持jpg、png格式)。")returnfor image_file in image_files:image = cv2.imread(image_file)gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)ret, corners = cv2.findChessboardCorners(gray, board_size, None)if ret:# 假设对应的机器人手位姿信息存储在同名的.txt文件中(格式自定义,这里简单按行读取模拟解析)pose_file = os.path.splitext(image_file)[0] + ".txt"if os.path.exists(pose_file):with open(pose_file, 'r') as f:pose_data = [float(line.strip()) for line in f.readlines()]pose_matrix = np.array(pose_data).reshape(4, 4)hand_poses.append(pose_matrix)image_points.append(corners)object_points.append(objp[0])messagebox.showinfo("提示", f"已成功采集一组手眼数据(从 {image_file})!")else:messagebox.showerror("错误", f"未找到对应的机器人手位姿文件 {pose_file},请检查文件是否存在及命名规范。")else:messagebox.showerror("错误", f"在图像 {image_file} 中未检测到棋盘格,请检查图像内容。")if not hand_poses or not image_points:messagebox.showerror("错误", "采集的数据不完整,无法进行标定,请检查图像及对应位姿文件情况。")# 执行图像标定函数
def perform_calibration_image():"""执行基于图像文件的手眼标定操作,利用采集的数据计算手眼之间的变换关系"""global current_step, calibration_resultcurrent_step += 1update_step_text()# 提醒检查数据数量是否足够messagebox.showinfo("操作提示", "首先会检查采集的数据数量是否足够用于标定,需要至少采集4组数据,如果不足会弹出错误提示框并终止标定操作,请确保之前已采集足够的数据。")if len(hand_poses) < 4 or len(image_points) < 4:messagebox.showerror("错误", "采集的数据不足,请至少采集4组数据进行标定!")returntry:# 使用OpenCV的手眼标定函数(这里以Tsai-Lenz算法为例,实际可能有多种选择)# 提醒关于标定算法及参数传入相关操作messagebox.showinfo("操作提示", "接下来将调用cv2.calibrateHandEye函数进行手眼标定,采用的是Tsai-Lenz算法,该函数需要传入机器人手的旋转矩阵列表(从hand_poses中提取每个位姿的前3x3部分表示旋转)、平移向量列表(提取每个位姿的前3x3列第4行表示平移)、图像中棋盘格角点坐标列表(转换格式并转换数据类型为合适的numpy数组形式)以及真实世界中的棋盘格角点坐标列表(object_points)作为参数。")retval, rotation_vector, translation_vector = cv2.calibrateHandEye([pose[:3, :3] for pose in hand_poses],[pose[:3, 3] for pose in hand_poses],[points.reshape(-1, 2).astype(np.float64) for points in image_points],object_points)# 将旋转向量和平移向量转换为变换矩阵形式方便后续使用rotation_matrix, _ = cv2.Rodrigues(rotation_vector)calibration_result = np.hstack((rotation_matrix, translation_vector.reshape(3, 1)))calibration_result = np.vstack((calibration_result, np.array([0, 0, 0, 1])))messagebox.showinfo("提示", "手眼标定完成(基于图像文件),得到变换矩阵:\n{}".format(calibration_result))except Exception as e:messagebox.showerror("错误", "标定过程出现错误:{}".format(str(e)))# 显示采集的数据函数(可用于查看已采集的数据情况,方便调试等)
def show_collected_data():"""在UI中以文本形式简单展示已采集的数据组数、手的位姿示例、图像点坐标示例等信息"""data_info = "已采集数据组数:{}\n\n".format(len(hand_poses))if hand_poses:data_info += "手的位姿示例(第一个):\n{}\n\n".format(hand_poses[0])if image_points:data_info += "图像点坐标示例(第一个):\n{}\n\n".format(image_points[0])messagebox.showinfo("已采集数据详情", data_info)# 重置操作函数,用于重新开始整个手眼标定流程
def reset_operation():"""重置所有相关变量,重新开始手眼标定流程"""global hand_poses, image_points, object_points, calibration_result, current_stephand_poses = []image_points = []object_points = []calibration_result = Nonecurrent_step = 0update_step_text()messagebox.showinfo("提示", "已重置操作,可重新开始手眼标定流程。")root = tk.Tk()
root.title("手眼标定操作指引")
step_text = tk.StringVar()# 在UI上显示步骤文本的标签
step_label = Label(root, textvariable=step_text)
step_label.pack(pady=10)# 实时标定选择按钮
real_time_btn = Button(root, text="实时标定", command=lambda: (reset_operation(), set_calibration_mode('real_time')))
real_time_btn.pack(side=tk.LEFT, padx=10, pady=10)# 图像标定选择按钮
image_btn = Button(root, text="图像标定", command=lambda: (reset_operation(), set_calibration_mode('image')))
image_btn.pack(side=tk.RIGHT, padx=10, pady=10)# 用于记录当前选择的标定模式的变量
calibration_mode = Nonedef set_calibration_mode(mode):"""设置当前标定模式,并更新相关按钮的命令绑定"""global calibration_modecalibration_mode = modeif mode == 'real_time':collect_btn.config(command=collect_data_real_time)calibrate_btn.config(command=perform_calibration_real_time)elif mode == 'image':collect_btn.config(command=collect_data_image)calibrate_btn.config(command=perform_calibration_image)# 采集数据按钮(初始时无具体绑定,后续根据选择的标定模式绑定相应函数)
collect_btn = Button(root, text="采集手眼数据", command=None)
collect_btn.pack(pady=10)# 执行标定按钮(初始时无具体绑定,后续根据选择的标定模式绑定相应函数)
calibrate_btn = Button(root, text="执行手眼标定", command=None)
calibrate_btn.pack(pady=10)# 显示采集数据按钮
show_data_btn = Button(root, text="显示采集的数据", command=show_collected_data)
show_data_btn.pack(pady=10)# 重置操作按钮
reset_btn = Button(root, text="重置操作", command=reset_operation)
reset_btn.pack(pady=10)update_step_text()
root.mainloop()
相关文章:
相机不动,机构动作----Hands Eyes
最近在研究 手眼标定,发现大家都需付费,搞啥子,说好的开源。。。 以相机在上固定不动,机械手为 EPSON_Robot 为例,详细的一步一步实例操作指引 EPSON_Robot 的192.168.0.1 2004 Server 详细操作步骤 1. 启动程序 运…...
Jdk1.7到Jdk1.8 HashMap 发生了什么变化(底层)
从JDK 1.7到JDK 1.8,HashMap在底层实现上发生了显著的变化, 主要体现在数据结构、链表插入方式、哈希算法、扩容机制以及并发性方面。 以下是具体的变化点: 1. 数据结构的变化 JDK 1.7:HashMap的底层数据结构是数组单向链表。…...
微积分复习笔记 Calculus Volume 2 - 4.2 Direction Fields and Numerical Methods
4.2 Direction Fields and Numerical Methods - Calculus Volume 2 | OpenStax...
java后端环境配置
因为现在升学了,以前本来想毕业干java的,很多java的环境配置早就忘掉了(比如mysql maven jdk idea),想写个博客记录下来,以后方便自己快速搭建环境 JAVA后端开发配置 环境配置jdkideamavenMySQLnavicate17…...
Unity UI Button 事件优先级调整技术方案
Unity UI Button 事件优先级调整技术方案 在 Unity 项目开发过程中,针对 UI Button 的事件执行顺序控制是一个常见需求。本文详细阐述两种将新添加事件置于第一个执行位置的方法,旨在为开发者提供全面且专业的技术参考。 一、基于反射机制的事件插入方…...
【从零开始入门unity游戏开发之——C#篇04】栈(Stack)和堆(Heap),值类型和引用类型,以及特殊的引用类型string
文章目录 知识回顾一、栈(Stack)和堆(Heap)1、什么是栈和堆2、为什么要分栈和堆3、栈和堆的区别栈堆 4、总结 二、值类型和引用类型1、那么值类型和引用类型到底有什么区别呢?值类型引用类型 2、总结 三、特殊的引用类…...
PHP排序算法:数组内有A~E,A移到C或者C移到B后排序,还按原顺序排序,循环
效果 PHP代码 public function demo($params){function moveNext($arr){$length count($arr);$lastElement $arr[$length - 1];for ($i $length - 1; $i > 0; $i--) {$arr[$i] $arr[$i - 1];}$arr[0] $lastElement;return $arr;}function moveAndReplace($array, $from…...
keepalived的高可用集群
keepalived的概念 keepalived的工作原理 基于vrrp实现的调度器高可用方案 keepalived的配置实验 先在调度服务器上安装keepalived和ipvsadm apt -y install keepalived ipvsadm 复制keepalived的配置文件到/etc/keepalived/目录下 cp /usr/share/doc/keepalived/samples/keep…...
基于单片机的农田灌溉系统(论文+源码)
1.系统设计 本系统主要实现如下目标: 1.可以实时监测土壤湿度; 2.土壤湿度太低时,进行浇水操作; 3.可以按键设置湿度的触发阈值; 4. 可以实现远程操控 5.可以实现手…...
技术文档分享——绘制精准航海图:技术文档规划、表达与维护的艺术
绘制精准航海图:技术文档规划、表达与维护的艺术 方向一:技术文档的规划布局从技术文档的规划布局入手,探讨如何确定文档的整体架构,如章节设置、逻辑顺序等,以确保信息呈现的系统性与连贯性。1. 确定文档的目标和读者…...
43124123
📢博客主页:https://blog.csdn.net/2301_779549673 📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正! 📢本文由 JohnKi 原创,首发于 CSDN🙉 📢未来很长&#…...
Pytorch应用实战(1)- 基于YOLO的视频人脸马赛克处理
免费链接: Blogger(需翻Q), Github 文章目录 本文介绍给图片的人脸打码给视频的人脸打码本文介绍 YoloV11(Github)提供了非常方便的API帮助用户实现目标检测(detect)、语义分割(segement)、肢体识别(Pose)等功能。 本文将基于YoloV11的目标检测来实现一个视频人脸马…...
【Prompt Engineering】1.编写 Prompt 的原则
一、环境配置 使用 OpenAI 的 ChatGPT API,需要有 API_KEY,并安装 OpenAI 库。安装命令:pip install openai 和 pip install zhipuai。配置方法:直接设置 openai.api_key 或通过环境变量设置。 二、两个基本原则 2.1 原则一&am…...
非vip版opengl
环境搭建 安装编译器和构建工具:在 Windows 上可以使用 Visual Studio,在 Linux 上可以使用 GCC 等编译器。确保编译器已正确安装并配置好环境变量。安装 OpenGL 库和相关辅助库(以 GLUT 为例): Windows: 下…...
【深入理解Nginx】
深入理解Nginx 介绍 Nginx(发音为 “engine-x”)是一款高性能的HTTP服务器和反向代理服务器,同时支持IMAP/POP3协议。它以其高并发处理能力、稳定性、丰富的功能集、简单的配置和低资源消耗而受到广泛欢迎。Nginx特别适合提供静态文件服务、…...
番外篇 | Hyper-YOLO:超图计算与YOLO架构相结合成为目标检测新的SOTA !
前言:Hello大家好,我是小哥谈。Hyper-YOLO,该方法融合了超图计算以捕捉视觉特征之间复杂的高阶关联。传统的YOLO模型虽然功能强大,但其颈部设计存在局限性,限制了跨层特征的融合以及高阶特征关系的利用。Hyper-YOLO在骨干和颈部的联合增强下,成为一个突破性的架构。在COC…...
Microi吾码|开源低代码.NET、VUE低代码项目,表单引擎介绍
Microi吾码|开源低代码.NET、VUE低代码项目,表单引擎介绍 一、摘要二、Microi吾码介绍2.1 功能介绍2.2 团队介绍2.3 上线项目案例 三、Microi吾码表单引擎是什么?四、Microi吾码表单引擎功能4.1 模块引擎 - 由表单引擎驱动4.2 流程引擎 - 由表…...
css常用属性有哪些
在上篇文章我们知道了利用css选择器来对HTML进行简单装饰,就像做word文档一样,需要对哪一段落修改格式,就需要先选中,css选择器就是这意思。这格式如何修改,怎么放大字体,怎么加粗,怎么修改背景…...
Java设计模式 —— 【结构型模式】装饰者模式详解
文章目录 前言结构说明案例演示小结静态代理和装饰者的区别 前言 在日常生活中,我们常会遇到一种场景:去快餐店吃饭,里面琳琅满目的主食,还有各式各样的配菜作为消费者,只管挑选就行,但是如果让我们来设计…...
简道云与金蝶云星空无缝集成的技术探索
简道云数据集成到金蝶云星空的技术案例分享 在企业信息化建设中,数据的高效流动和准确对接是关键环节。本文将聚焦于一个实际运行的系统对接集成案例:简道云-其他入库单--->金蝶-其他入库单,通过轻易云数据集成平台实现这一目标。 案例背…...
单片机原理及应用笔记:单片机中断系统原理与项目实践
高金鹏:男,银川科技学院计算机与人工智能学院,2022级别计算机科学与技术本科生,单片机原理及应用课程第六组。 指导教师:王兴泽 电子邮件:高金鹏3535558665qq.com 个人CSDN:暴躁的海绵宝宝 暴躁的海绵宝…...
uniapp使用百度地图配置了key,但是显示Map key not configured
搞了我两天的一个问题。 hbuilderx版本:4.36 问题介绍: 我的项目是公司的项目,需要在H5端使用百度地图,使用vue-cli创建的uniapp,就是uni代码在src里的目录结构。就是使用这种方式才会遇到这个问题。 问题原因…...
CTFHub ssrf
第一关(内网访问) 尝试访问位于127.0.0.1的flag.php吧 第二关(伪协议读取文件) 尝试去读取一下Web目录下的flag.php吧 1.首先尝试http://127.0.0.1/flag.php 2.查看页面源代码 3.根据提示输入file:///var/www/html/flag.php 4.查看页面源代码 第三关&…...
字符串哈希
1. LC 3292 形成目标字符串需要的最少字符串数Ⅱ 这题在3291的基础上开大数据量了。 3291我是比较标准的dp字典树优化匹配。先把所有word扔到字典树里面,定义dp[i]表示到target[i]需要的最少次数。对于每个i进行target子串的最长前缀匹配,随后向后刷表…...
ensp 静态路由配置
A公司有广州总部、重庆分部和深圳分部3个办公地点,各分部与总部之间使用路由器互联。广州、重庆、深圳的路由器分别为R1、R2、R3,为路由器配置静态路由,使所有计算机能够互相访问,实训拓扑图如图所示 绘制拓扑图 给pc机配置ip地址…...
Android Room 数据库使用详解
一、Room介绍 Android Room 是 Google 提供的一个 Android 数据持久化库,是 Android Jetpack 组成部分之一。它提供了一个抽象层,使得 SQLite 数据库的使用更为便捷。通过 Room,开发者可以轻松地操作数据库,不需要直接编写繁琐的…...
Ubuntu安装或卸载mariadb-server软件包
1、安装mariadb-server sudo apt install mariadb-server 检查MariaDB服务器的服务状态 service mariadb status 仅需要卸载MariaDB,而不是删除所有MariaDB相关软件包 sudo apt-get remove mariadb-server 2、卸载MariaDB 从系统中完全删除MariaDB数据库…...
canal详解及demo
提示:如何保证Redis中的数据与数据库中的数据一致性?数据同步canal的介绍和demo、大型企业如何实现mysql到redis的同步?使用binlog实时更新redis缓存、canal的接入教程、win下canal的服务器端、canal客户端的创建、连接、测试教程、数据同步方…...
SQL语句
SQL(Structured Query Language,结构化查询语言)是用于管理和操作关系数据库系统的标准编程语言。它允许用户执行数据的定义、查询、更新和管理等操作。以下是一些常见的SQL语句及其简要说明: 数据定义语言(DDL&#…...
中间件 redis安装
redis官网地址:Redis - The Real-time Data Platform 环境 CentOS Linux release 7.9.2009 (Core) java version "17.0.12" 2024-07-16 LTS 1、通过压缩包安装redis 1,远程下载redis压缩包,或去官网下载:Downloads …...
设计模式12:抽象工厂模式
系列总链接:《大话设计模式》学习记录_net 大话设计-CSDN博客 参考: C设计模式:抽象工厂模式(风格切换案例)_c 抽象工厂-CSDN博客 1.概念 抽象工厂模式(Abstract Factory Pattern)是软件设计…...
学习笔记:从ncsi/nc-si协议和代码了解网络协议的设计范式
学习笔记:从ncsi/nc-si协议和代码了解网络协议的设计范式 参考文档: https://www.dmtf.org/standards/published_documents https://www.dmtf.org/dsp/DSP0222 https://www.dmtf.org/sites/default/files/standards/documents/DSP0222_1.2.0.pdf参考代…...
基础库urllib的使用
学习爬虫,其基本的操作便是模拟浏览器向服务器发出请求,那么我们需要从哪个地方做起呢?请求需要我们自己构造吗?我们需要关心请求这个数据结构怎么实现吗?需要了解 HTTP、TCP、IP层的网络传输通信吗?需要知道服务器如何响应以及响应的原理吗? 可能…...
SSM 电脑配件销售系统设计要点与 JSP 实现难点攻克
摘 要 随着科学技术的飞速发展,各行各业都在努力与现代先进技术接轨,通过科技手段提高自身的优势;对于电脑配件销售系统当然也不能排除在外,随着网络技术的不断成熟,带动了电脑配件销售系统,它彻底改变了过…...
AI前沿分析:ChatGPT搜索上线,Google搜索地位能否守住?
名人说:莫听穿林打叶声,何妨吟啸且徐行。—— 苏轼 Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 目录 引言:AI与搜索领域的激烈博弈一、ChatGPT搜索的优势是什么?1. 实时信息获取:超越传统搜索2. 对话式搜索:重构用户体验3. 个性化推荐:深度挖掘用户需求二、G…...
单片机:实现utf-8转gb2312(附带源码)
单片机实现UTF-8转GB2312 在嵌入式系统中,字符编码是常见的问题之一,尤其是在显示中文字符时。UTF-8和GB2312都是常见的字符编码标准,UTF-8广泛用于Web和现代操作系统中,而GB2312是中国大陆常用的中文字符集。在一些嵌入式系统中…...
VMProtect:软件保护与安全的全面解决方案
在当今数字化时代,软件的安全性和保密性愈发重要。VMProtect 作为一款备受瞩目的软件保护工具,因其强大的功能和广泛的应用而成为开发者保护软件的首选方案。 VMProtect 是一款新一代的软件保护实用程序,支持多个编译器平台,包括…...
linux下观察进程捕获信号的情况
linux观察进程收到信号的情况,信号可以来自外部进程、进程自身、内核定时器等。 观察捕获信号方法一:strace strace所有信号: strace -e signal -f -p $pid 过滤出某个信号: #grep 信号名,取自kill -l strace -e …...
VSCode编辑+GCC for ARM交叉编译工具链+CMake构建+OpenOCD调试(基于STM32的标准库/HAL库)
一、CMake安装 进入CMake官网的下载地址Get the Software,根据系统安装对应的Binary distributions。 或者在CMake——国内镜像获取二进制镜像安装包。 或者访问GitHub的xPack项目xPack CMake v3.28.6-1,下载即可。 记得添加用户/系统的环境变量&#…...
概率论得学习和整理26:EXCEL 关于plot 折线图--频度折线图的一些细节
目录 0 折线图有很多 1 频度折线图 1.1 直接用原始数据做的频度折线图 2 将原始数据生成数据透视表 3 这样可以做出了,频度plot 4 做按某字段汇总,成为累计plot分布 5 修改上面显示效果,做成百分比累计plot频度分布 0 折线图有很多 这…...
数据结构之栈和队列算法题
一:有效括号数 学了栈之后这一题就比较简单了。 思路:1、左括号进栈 2、右括号出栈匹配。 完整代码: 因为使用C语言写的,所以里面包含了栈的实现 #include<stdio.h> #include<stdlib.h> #include<assert.h>…...
离散数学---随机漫步
本文根据 MIT 计算机科学离散数学课程整理(Lecture 25)。 赌徒破产问题(Gamblers Ruin) 问题描述 初始为 n 元,对于每一次独立的赌注,都有 p 的概率赢得 1 元,(1-p) 的概率输掉 1 元。当输完…...
HCIA-Access V2.5_2_2网络通信基础_TCP/IP协议栈报文封装
TCP/IP协议栈的封装过程 用户从应用层发出数据先会交给传输层,传输层会添加TCP或者UDP头部,然后交给网络层,网络层会添加IP头部,然后交给数据链路层,数据链路层会添加以太网头部和以太网尾部,最后变成01这样…...
java开发入门学习一 -基本概念入门
目录 Java的开发环境搭建 开发环境简单认识 开发环境下载与安装 初始JAVA之helloWorld java基本运行原理 helloWord实例 JAVA文档注释和API 文档 注释 API文档地址 Java的优缺点 优点 缺点 JVM的简单介绍 功能 1. 实现JAVA程序的跨平台性 2. 自动内存管理(管理…...
Coturn 实战指南:WebRTC 中的 NAT 穿透利器
1. 什么是 Coturn? Coturn 是一种开源的 TURN(Traversal Using Relays around NAT)服务器,用于解决 NAT 穿透问题。它帮助客户端在受限网络环境(例如防火墙或 NAT 后面)中实现双向通信,常用于 WebRTC 应用、VoIP、在线游戏等场景。 2. Cotur…...
【每日一练 基础题】[蓝桥杯 2022 省 A] 求和
[蓝桥杯 2022 省 A] 求和 暴力破解会超时,用因式分解的平方差公式 a2 2abb2(a)2 a-2abb2(a-b)2 输出整数((a1a2a3…an)-a1-a2-a3-…-an)/2 import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner scanner new Scanner(System.in);l…...
华为ensp--BGP路径选择-Preferred Value
学习新思想,争做新青年。今天学习的是BGP路径选择-Preferred Value 实验目的 理解BGP路由信息首选值(Preferred Value)的作用 掌握修改Preferred Value属性的方法 掌握通过修改Preferred Value属性来实现流量分担的方法 实验拓扑 实验要求…...
Flink是什么?Flink技术介绍
官方参考资料:Apache Flink — Stateful Computations over Data Streams | Apache Flink Flink是一个分布式流处理和批处理计算框架,具有高性能、容错性和灵活性。以下是关于Flink技术的详细介绍: 一、Flink概述 定义:Fli…...
Java中的重要集合
目录 List 1.vector 2.ArrayList 3.CopyonWriteArraylist 4.ArrayList变成线程安全的方式 5.LinkList Map 1.HashTable 2.HashMap 3.ConcurrentHashMap Set 1.LinkedHashSe List 1.vector Vector 是线程安全的动态数组,其内部方法基本都经过synchroni…...
实景视频与模型叠加融合?
[视频GIS系列]无人机视频与与实景模型进行实时融合_无人机视频融合-CSDN博客文章浏览阅读1.5k次,点赞28次,收藏14次。将无人机视频与实景模型进行实时融合是一个涉及多个技术领域的复杂过程,主要包括无人机视频采集、实景模型构建、视频与模型…...