当前位置: 首页 > news >正文

MATLAB - 机械臂手眼标定(眼在手内) - 估计安装在机器人上的移动相机的姿态

系列文章目录


前言

        本示例展示了如何为装有手眼构型摄像头的机械臂或机械手执行和验证手眼校准。


一、概述

        执行手眼校准有助于操作配备末端执行器(简称 “手”)的机械臂,该末端执行器依赖于摄像头提供的视觉数据。一旦完成了眼在手外的校准,机械臂就能准确地移动到摄像头识别的特定像素位置。这种能力是执行精确拾放任务(如分类、堆垛和分拣)的基础,即使在摄像机的确切位置和方向未知的情况下也是如此。

        机器人-摄像机系统有两种构型:眼在手内和眼在手外。在眼在手内构型中,摄像头直接安装在机器人手臂的末端执行器上。相反,在眼在手的构型中,摄像头固定在一个静止的物体上,机器人手臂在其视野内。本示例主要针对眼在手内的构型进行手眼校准,但所述原理和技术也可适用于眼在手外的构型。

        在本示例中,首先要校准摄像头,以确定其固有参数。然后进行手眼在手内校准。然后利用校准结果将图像点转换为机器人世界坐标系中的坐标。本示例需要计算机视觉工具箱和机器人系统工具箱。

二、估算相机内在参数

        相机校准是眼在手内校准的第一步。这一步的重点是估算相机的固有参数(焦距、畸变等),这是消除图像畸变和估算相机相对于校准板的姿态所必需的。

        要准确校准相机,请按照 “准备相机并捕捉用于相机校准的图像 ”中列出的指南收集 10-20 张校准图像。在本示例中,使用 “使用单相机校准器应用程序 ”以棋盘格校准模式校准了相机,并将相机参数导出到 cameraParams.mat。

        将这些估算的摄像机固有参数加载到 MATLAB 中。

ld = load("cameraParams.mat");
intrinsics = ld.cameraParams.Intrinsics;

三、收集用于手眼校准的图像

        为进行手眼校准,您必须从不同角度拍摄 15-30 张照片。应确保机器人姿势集涵盖机器人机械手工作空间的广泛范围。利用这些图像和摄像头的固有参数,可以确定校准板和摄像头之间的变换。

        在整个过程中,必须确保校准板在摄像机的视野内完全可见。为了优化精确度,在摄像头校准过程中,要捕捉一组与眼在手内校准不同的图像。每个姿势都要保存一张摄像头图像和机器人关节角度。本示例使用的是 KINOVA Gen3 机械臂,其末端执行器附近安装有摄像头。用于 KINOVA Gen3 机械手的机器人系统工具箱支持包除了提供控制机械臂运动的功能外,还包括用于收集摄像头图像及其相应姿势角度的工具。将摄像头图像加载到 MATLAB 中的图像数据库中。

numPoses = 30;
imds = imageDatastore("calibrationData");
montage(imds)

四、估算摄像机外在参数

        在此步骤中,您需要估算摄像机的外特征参数,包括确定每个姿态下从校准板原点到摄像机的变换。外特征参数估算过程可确定未扭曲图像中校准棋盘点的位置。然后利用这些点确定摄像机相对于校准板的位置和方向。有关摄像机外差估计过程的详细信息,请参阅 estimateExtrinsics。

squareSize = 0.022; % Measured in meters
camExtrinsics(numPoses,1) = rigidtform3d;% Estimate transform from the board to the camera for each pose.
for i = 1:numPoses% Undistort the image using the estimated camera intrinsics.calibrationImage = readimage(imds,i);undistortedImage = undistortImage(calibrationImage,intrinsics);% Estimate the extrinsics while disabling the partial checkerboard% detections to ensure consistent checkerboard origin across poses. [imagePoints, boardSize] = detectCheckerboardPoints(undistortedImage,PartialDetections=false);worldPoints = patternWorldPoints("checkerboard",boardSize,squareSize);camExtrinsics(i) = estimateExtrinsics(imagePoints,worldPoints,intrinsics);
end

五、计算机器人末端执行器到基座的变换

        在这一步中,您将使用之前收集的机器人姿势,计算每个姿势下机器人末端执行器关节到基座的变换。将姿势数据载入 MATLAB。

jointConfiguration = load("calibrationData/jntconfig.mat");
jointPositionsDeg = jointConfiguration.jointPositions;

        使用 loadrobot 加载 Kinova Gen3 机器人的刚体树模型。支持的机器人列表请点击此处。

robotModel = loadrobot("kinovaGen3");
robotModel.DataFormat = "column";

针对每个校准姿势,计算从机器人末端执行器关节到基座的 4×4 变换。这些变换中的距离以米为单位。

endEffectorToBaseTform(numPoses,1) = rigidtform3d; 
for i = 1:numPoses   jointPositionsRad = deg2rad(jointPositionsDeg(i,:))'; % Convert the pose angles from degrees to radians.endEffectorToBaseTform(i) = getTransform(robotModel,jointPositionsRad,"EndEffector_Link");
end

六、估算摄像机到末端执行器的变换

        使用 helperEstimateHandEyeTransform 函数估算 “眼在手内 ”构型的摄像机到末端执行器的变换。要使用 “眼在手外 ”构型,请参阅 “估算固定摄像机相对于机器人底座的姿态(机器人系统工具箱)”示例。

config = "eye-in-hand";
cameraToEndEffectorTform = helperEstimateHandEyeTransform(camExtrinsics,endEffectorToBaseTform,config)
cameraToEndEffectorTform = rigidtform3d with properties:Dimensionality: 3Translation: [-0.0079 0.0475 0.0075]R: [3×3 double]A: [-0.9953    0.0949   -0.0168   -0.0079-0.0951   -0.9954    0.0141    0.0475-0.0154    0.0156    0.9998    0.00750         0         0    1.0000]

        辅助函数会返回一个 rigidtform3d 对象,其中包含从摄像机到末端执行器关节的变换,可用于计算摄像机到机器人基座的变换。

七、通过执行物体拾取验证校准

        通过指示机器人拾取摄像头视野内检测到的物体,来验证已校准的系统。本示例使用附在立方体上的 AprilTag 演示验证过程。AprilTags 是一种靶标,有助于对摄像头进行精确的外部估计,从而使机器人能够接近并拾取立方体。

        首先定位机器人手臂,使 AprilTag 位于摄像头的视野内。然后,捕捉图像并记录机器人的当前姿势,包括关节角度。

% Load the test pose angles.
load("testData/poses.mat");
testPose = pose_angles';% Load and undistort the test image.
testImage = imread("testData/im1.png");
undistortedTestImage = undistortImage(testImage,ld.cameraParams);% Compute the end-effector joint to base transformation for the test pose.
testPoseRad = deg2rad(testPose);
endEffectorToBaseTformTest = getTransform(robotModel,testPoseRad,"EndEffector_Link");

        使用 readAprilTag 函数计算从 AprilTag 到相机的转换。

% Specify the tag family and tag size of the AprilTag.
tagFamily = 'tag36h11';
tagSize = .049; % AprilTag size in meters% Detect AprilTag in test image.
[~,~,aprilTagToCameraTform] = readAprilTag(undistortedTestImage,tagFamily,intrinsics,tagSize);

        现在,将变换相乘,以确定 AprilTag 相对于机器人底座的变换。

% Find the transformation from the robot base to the camera.
cameraToBaseTestTform = rigidtform3d(endEffectorToBaseTformTest * cameraToEndEffectorTform.A);% Find the transformation from the robot base to the April Tag.
tagToBaseTestTform = cameraToBaseTestTform.A * aprilTagToCameraTform.A;
cubePosition = tagToBaseTestTform(1:3,4)
cubePosition = 3×10.6936-0.00420.1760

        AprilTag 的位置在机器人前方 0.69 米、左侧 0.004 米和底座上方 0.176 米处。

        为了验证计算的准确性,请绘制一张显示机器人、摄像头和立方体位置的曲线图。然后可以将直观图与初始测试设置进行比较。

% Show the 3D Robot model, with the base at the origin.
show(robotModel,testPoseRad);
hold on% Show the estimated positions and orientations of the camera and cube.
plotCamera(AbsolutePose = cameraToBaseTestTform, Opacity=0, size=0.02)
scatter3(cubePosition(1), cubePosition(2), cubePosition(3), 300, 'square', 'filled')
cubeRotationQuaternion = rotm2quat(tagToBaseTestTform(1:3,1:3));
plotTransforms(cubePosition', cubeRotationQuaternion)
title("Robot Arm and Estimated Position of AprilTag Cube")
xlim([-.3,.9])
ylim([-.5, .5])
zlim([-.2,.9])

        鉴于摄像头和立方体的绘图标记大致处于正确的位置和方向,将立方体的坐标传递给机器人,并命令它将末端执行器关节移动到正确的位置。机器人系统工具箱和 ROS 工具箱提供的函数可以计算将末端执行器关节移动到位置所需的电机输入,并将这些控制发送给机器人。机器人系统工具箱中的逆运动学(inverseKinematics)函数提供了一个求解器,用于找到一组关节角度,以完成末端执行器关节相对于机器人基座的定位和旋转。

        在下面的视频中,机器人通过手眼在手外校准计算出立方体的位置,并将其抓手移动到立方体的坐标位置将其抓起。

八、结论

        在本示例中,机器人的眼在手内校准促进了精确的拾放操作,使机械臂能够定位摄像头视野中的物体,并将该位置转换到机器人的坐标系中。在相机的精确位置未知或难以测量的情况下,手眼校准对于将相机集成到机械臂系统中非常有用。

九、辅助函数

        HelperEstimateHandEyeTransform 函数遵循 Tsai 和 Lenz [1] 的算法,用于估算从末端执行器关节到摄像头的变换。

function cameraToEndEffectorTform = helperEstimateHandEyeTransform(boardToCameraTform, endEffectorToBaseTform, configuration)argumentsboardToCameraTform (:,1) rigidtform3dendEffectorToBaseTform (:,1) rigidtform3dconfiguration {mustBeMember(configuration, ["eye-in-hand","eye-to-hand"])}endnumPoses = size(boardToCameraTform,1);% In the eye-to-hand case, the camera is mounted in the environment and% the calibration board is mounted to the robot end-effector joint.if configuration == "eye-to-hand"for i = 1:numPosescurrAInv = boardToCameraTform(i).invert().A;endEffectorToBaseTform(i) = rigidtform3d(currAInv);endend% Reorder the poses to have greater angles between each pair.orderOfPoses = helperOptimalPoseOrder(boardToCameraTform);boardToCameraTform(:,:,:) = boardToCameraTform(:,:,orderOfPoses);endEffectorToBaseTform(:,:,:) = endEffectorToBaseTform(:,:,orderOfPoses);PEndEffectorIToJ=repmat(zeros(3,1), 1, 1, numPoses-1);PCameraIToJ=PEndEffectorIToJ;% Iterate through pairs of poses to determine transformation angle.for i = 1:numPoses-1j=i+1;% Collect the 4 transforms of interest.TCameraI = boardToCameraTform(i).A;TCameraJ = boardToCameraTform(j).A; TEndEffectorI = endEffectorToBaseTform(i).A;TEndEffectorJ = endEffectorToBaseTform(j).A;% Get transforms grom end-effector joint I to end-effector joint J, and for camera I and% camera J.TEndEffectorIJ = TEndEffectorJ\TEndEffectorI;TCameraIJ = (TCameraI'\TCameraJ')';% Get the axes and angles of the for both transforms.axangCIJ = tform2axang(TCameraIJ);axangGIJ = tform2axang(TEndEffectorIJ);thetaCIJ = axangCIJ(4);thetaGIJ = axangGIJ(4);PCameraIToJax = axangCIJ(1:3);PEndEffectorIToJax = axangGIJ(1:3);% P vectors contain the axis of rotation and are scaled to represent% the amount of rotation using Rodrigues' rotation formula.PCameraIToJ(:,:,i) = 2*sin(thetaCIJ/2)*PCameraIToJax';PEndEffectorIToJ(:,:,i) = 2*sin(thetaGIJ/2)*PEndEffectorIToJax';end% Set up least squares problem for rotation estimation.A=zeros((numPoses-1)*3,3);b=zeros((numPoses-1)*3,1);for i = 1:numPoses-1A((i-1)*3+1:i*3,:) = helperSkewMatrix(PEndEffectorIToJ(:,:,i) + PCameraIToJ(:,:,i));b((i-1)*3+1:i*3,1) = PCameraIToJ(:,:,i) - PEndEffectorIToJ(:,:,i);end[PEndEffectorToCameraUnscaled,~] = lsqr(A,b);PEndEffectorToCameraScaled = (2 * PEndEffectorToCameraUnscaled) / sqrt(1 + norm(PEndEffectorToCameraUnscaled)^2);% Find rotation given in Tsai, Lenz Equation 10.PSkew = helperSkewMatrix(PEndEffectorToCameraScaled);REndEffectorToCamera = (1 - (0.5 * norm(PEndEffectorToCameraScaled)^2)) * eye(3) + 0.5 * (PEndEffectorToCameraScaled * ...PEndEffectorToCameraScaled' + sqrt(4 - norm(PEndEffectorToCameraScaled)^2) * PSkew);% Clear A and b.A(:,:) = 0;b(:,:) = 0;% Iterate through numPoses-1 pairs of poses to find the translation part% of the transformation.for i = 1:numPoses-1j = i+1;% Use known transforms to compute transforms between poses.TCameraI = boardToCameraTform(i).A;TCameraJ = boardToCameraTform(j).A;TEndEffectorI = endEffectorToBaseTform(i).A;TEndEffectorJ = endEffectorToBaseTform(j).A;TEndEffectorIJ = TEndEffectorJ \ TEndEffectorI;TCameraIJ = (TCameraI' \ TCameraJ')';% Set up least squares to estimate translation.A((i-1)*3+1:i*3,:) = TEndEffectorIJ(1:3,1:3)-eye(3);b((i-1)*3+1:i*3,1) = REndEffectorToCamera*TCameraIJ(1:3,4)-TEndEffectorIJ(1:3,4);end% Compute translation using least squares.[TranslationEndEffectorToCamera,~] = lsqr(A,b);TEndEffectorToCamera = trvec2tform(TranslationEndEffectorToCamera')*rotm2tform(REndEffectorToCamera);cameraToEndEffectorTform = rigidtform3d(TEndEffectorToCamera);
end

        helperSkewMatrix 函数从 3d 向量创建一个 3x3 倾斜对称矩阵,用于 helperEstimateHandEyeTransform。

function skew = helperSkewMatrix(v)skew = [0,-v(3), v(2) ;v(3), 0 , -v(1);-v(2), v(1), 0];   
end

        辅助最优姿势排序(helperOptimalPoseOrder)函数给出了机器人手臂姿势的贪婪最优排序,使每对连续姿势之间的相机位置角度差达到最大,而不会重复使用姿势。事实证明,增加每对姿势之间的角度可以提高 Tsai 和 Lenz 算法的精确度 [1]。

function orderOfPoses = helperOptimalPoseOrder(TCameraToBoard)% Create necessary vectors.numPoses = size(TCameraToBoard,3);anglesBetween = ones(numPoses-1,1);orderOfPoses = 1:numPoses;% Iterate over the indices to choose each subsequent pose.for i = 1:numPoses-2TCameraI = TCameraToBoard(:,:,i);% Collect the angles between pose i and the remaining poses.for j = i+1:numPosesTCameraJ = TCameraToBoard(:,:,j);TCameraIJ = TCameraI \ TCameraJ;axangcij = tform2axang(TCameraIJ);anglesBetween(j) = axangcij(4);end% Select the pose with the maximum angle to appear next in the% ordering.[~, idMax] = max(anglesBetween(i:end));tmp = orderOfPoses(i+1);orderOfPoses(i+1) = orderOfPoses(idMax+i-1);orderOfPoses(idMax+i-1) = tmp;end
end

References

[1] Tsai, R.Y. and Lenz, R.K., 1989. A new technique for fully autonomous and efficient 3d robotics hand/eye calibration. IEEE Transactions on robotics and automation5(3), pp.345-358.

 

相关文章:

MATLAB - 机械臂手眼标定(眼在手内) - 估计安装在机器人上的移动相机的姿态

系列文章目录 前言 本示例展示了如何为装有手眼构型摄像头的机械臂或机械手执行和验证手眼校准。 一、概述 执行手眼校准有助于操作配备末端执行器(简称 “手”)的机械臂,该末端执行器依赖于摄像头提供的视觉数据。一旦完成了眼在手外的校准&…...

【Unity】TextMeshPro 3.0.9无法显示emoji表情问题

需要下载TextMeshPro 3.2.x-pre.xxx版本,重新生成Sprite Asset文件解决 注意:若Package Manager没有搜到pre版本,那么可以去github下载到本地,再解压后,将文件夹移动到工程Packages文件夹下,然后打开Packa…...

金九银十软件测试面试题(800道)

今年你的目标是拿下大厂offer?还是多少万年薪?其实这些都离不开日积月累的过程。 为此我特意整理出一份(超详细笔记/面试题)它几乎涵盖了所有的测试开发技术栈,非常珍贵,人手一份 肝完进大厂 妥妥的&#…...

中国剩余定理 C++

题目 解题思路 原链接:https://www.acwing.com/solution/content/3539/ 大致步骤: 将第2,3,4…n个方程不断与第一个方程合并,得到方程a1k1a2k2m2-m1;用扩展欧几里得算法解出a1k1a2k2gcd(a1, a2)的结果,再将结果扩大(m2-m1)/d倍即…...

动态规划lc

先找到规律,然后找边界情况;部分特殊情况分类讨论 *递归 70.爬楼梯 简单 提示 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢? 示例 1: 输入&#xff1a…...

介绍xshell的使用技巧

使用技巧目录 1. 开启左键选中即复制,右键点击即粘贴2. 开启撰写功能3. 开启日志记录功能 1. 开启左键选中即复制,右键点击即粘贴 参考:https://blog.csdn.net/chirrupy_hamal/article/details/108619262 2. 开启撰写功能 使用场景&#x…...

揭秘语音识别巨头1:国内外顶尖技术服务商全解析01(万字长文)

一、学习导航 解密语音识别巨头:国内顶尖技术服务商全解析00:学习地图 解密语音识别巨头:国内顶尖技术服务商全解析01:微软语音,商业No.1 解密语音识别巨头:国内顶尖技术服务商全解析02:百度…...

JAVA使用SM2算法生成密钥对加密解密加签验签

简介 SM2是非对称加密算法,一提非对称加密算法,第一想到的是RSA,没错,这个就是替代RSA的。它是基于椭圆曲线密码的公钥密码算法标准,其秘钥长度256bit,包含数字签名、密钥交换和公钥加密,用于替…...

uniapp(vue)打包web项目页面刷新后报404解决方案

一、问题概述 uniapp是一款优秀的跨平台开发框架,它可以帮助开发者快速构建出适用于多端的应用程序。然而,在项目打包后,有可能发现页面在刷新时会出现404错误。这无疑给用户体验带来了极大的困扰,下面我们就来分析一下这个问题。…...

ansible学习之ansible-vault

相关文档参考:http://www.ansible.com.cn/docs/playbooks_vault.html#what-can-be-encrypted-with-vault ansible-vault 功能介绍 Ansible-Vault是一个用于加密和管理Ansible playbook中敏感数据的工具。通过创建、编辑、加密、解密、查看和重置密码,可以安全地存储…...

封装el-upload组件,用于上传图片和视频的组件

使用环境 vue3element plus 需要根据后端返回结构修改的函数&#xff1a;onPreview onRemove onSuccess 组件使用 基本使用 源代码&#xff1a; <script setup> import AutoUploadFile from /components/auto-upload-file/index.vue function change(urls){console.log…...

6.将扩散模型与其他生成模型的关联(2)

1.归一化流与扩散模型 自一化流(Normalizing Flow)是生成模型&#xff0c;通过将易于处理的分布进行变换以队对高维数据进行建模。归一化流可以将简单的概率分布转化为极其复杂的分布&#xff0c;并用于强化学习、变分推理等领域。 现有的归一化流是基于变量替换公式构…...

【C++】基于红黑树封装set和map

&#x1f680;个人主页&#xff1a;小羊 &#x1f680;所属专栏&#xff1a;C 很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~ 目录 前言一、更高维度的泛型二、模版参数三、比较逻辑的重写四、迭代器4.1 const迭代器4.2 重载4.3 - -重载 五、完整代…...

24最新新手入门指南:Stable Diffusion!

前言 Stable Diffusion&#xff0c;一款新兴的开源AI绘画软件&#xff0c;正逐渐成为数字艺术家和爱好者的新宠。它的强大功能让用户能够轻松创造出令人印象深刻的数字艺术作品。 无论你是专业艺术家还是艺术新手&#xff0c;Stable Diffusion都为你提供了一个探索创造力的新…...

Java-基础

1. 导入模块不能纯粹的复制粘贴&#xff0c;要从new里导入&#xff0c;因为前者建立不了关联 2. 数组 String[] name{"张三","李四","王五"};int[] numsnew int[]{1,2,3};//二维String[][] names{{"张三","李四"},{"…...

二、后台管理系统布局菜单可拖动

前两天产品提出了一个需求,说后台管理系统的左边菜单的名称字数过多,遮挡了。希望能让客户能够看到全部的名称,给左侧菜单增加一个可拖动的功能,经过我的研究,这个功能最终也做出来了,先看效果,双击查看。 下面咱们进入实现步骤 第一步,找到文件。一般的项目中都存在l…...

socket和http区别

socket和http区别&#xff1a;1、主体不同&#xff1b;2、所处层次不同&#xff1b;3、连接状态不同&#xff1b;4、传输数据量不同&#xff1b;5、数据安全性不同&#xff1b;6、连接方式不同。其中&#xff0c;主体不同指的是socke是一个调用接口&#xff08;API&#xff09;…...

算法:974.和可以被K整除的子数组

题目 链接:leetcode链接 思路分析&#xff08;前缀和 同余定理&#xff09; 首先&#xff0c;我们要了解一下什么是同余定理 同余定理&#xff1a; 如果&#xff08;a - b&#xff09;/ p k …… 0 则 a % p b % p 证明我写在草稿纸上&#xff0c;如下图&#xff1a; 初…...

QD1-P8 HTML 格式化标签(font、pre、b、strong、i、u、del、s、sub、sup)

本节学习&#xff1a;HTML 格式化标签。 本节视频 www.bilibili.com/video/BV1n64y1U7oj?p8 ‍ 一、font 标签 用途&#xff1a;定义文本的字体大小、颜色和 face&#xff08;字体类型&#xff09;。 示例 <!DOCTYPE html> <html><head><meta cha…...

红米Turbo 3工程固件预览 修复底层 体验原生态系统 默认开启diag端口

红米Turbo 3机型代码:peridot 国外版本:POCO F6 用于以下型号的小米机型:24069RA21C, 24069PC21G, 24069PC21I。搭载1.5K OLED屏、骁龙8s处理器、5000mAh电池+90W快充、5000万像素主摄。 通过博文了解 1💝💝💝-----此机型工程固件的资源刷写注意事项 2💝💝�…...

3.3.1_1 检错编码(奇偶校验码)

从这节课开始&#xff0c;我们会探讨数据链路层的差错控制功能&#xff0c;差错控制功能的主要目标是要发现并且解决一个帧内部的位错误&#xff0c;我们需要使用特殊的编码技术去发现帧内部的位错误&#xff0c;当我们发现位错误之后&#xff0c;通常来说有两种解决方案。第一…...

线程同步:确保多线程程序的安全与高效!

全文目录&#xff1a; 开篇语前序前言第一部分&#xff1a;线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分&#xff1a;synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分&#xff…...

零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)

本期内容并不是很难&#xff0c;相信大家会学的很愉快&#xff0c;当然对于有后端基础的朋友来说&#xff0c;本期内容更加容易了解&#xff0c;当然没有基础的也别担心&#xff0c;本期内容会详细解释有关内容 本期用到的软件&#xff1a;yakit&#xff08;因为经过之前好多期…...

人工智能(大型语言模型 LLMs)对不同学科的影响以及由此产生的新学习方式

今天是关于AI如何在教学中增强学生的学习体验&#xff0c;我把重要信息标红了。人文学科的价值被低估了 ⬇️ 转型与必要性 人工智能正在深刻地改变教育&#xff0c;这并非炒作&#xff0c;而是已经发生的巨大变革。教育机构和教育者不能忽视它&#xff0c;试图简单地禁止学生使…...

LRU 缓存机制详解与实现(Java版) + 力扣解决

&#x1f4cc; LRU 缓存机制详解与实现&#xff08;Java版&#xff09; 一、&#x1f4d6; 问题背景 在日常开发中&#xff0c;我们经常会使用 缓存&#xff08;Cache&#xff09; 来提升性能。但由于内存有限&#xff0c;缓存不可能无限增长&#xff0c;于是需要策略决定&am…...

es6+和css3新增的特性有哪些

一&#xff1a;ECMAScript 新特性&#xff08;ES6&#xff09; ES6 (2015) - 革命性更新 1&#xff0c;记住的方法&#xff0c;从一个方法里面用到了哪些技术 1&#xff0c;let /const块级作用域声明2&#xff0c;**默认参数**&#xff1a;函数参数可以设置默认值。3&#x…...

【Linux】Linux安装并配置RabbitMQ

目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的&#xff0c;需要先安…...

Vue 3 + WebSocket 实战:公司通知实时推送功能详解

&#x1f4e2; Vue 3 WebSocket 实战&#xff1a;公司通知实时推送功能详解 &#x1f4cc; 收藏 点赞 关注&#xff0c;项目中要用到推送功能时就不怕找不到了&#xff01; 实时通知是企业系统中常见的功能&#xff0c;比如&#xff1a;管理员发布通知后&#xff0c;所有用户…...

基于Uniapp的HarmonyOS 5.0体育应用开发攻略

一、技术架构设计 1.混合开发框架选型 &#xff08;1&#xff09;使用Uniapp 3.8版本支持ArkTS编译 &#xff08;2&#xff09;通过uni-harmony插件调用原生能力 &#xff08;3&#xff09;分层架构设计&#xff1a; graph TDA[UI层] -->|Vue语法| B(Uniapp框架)B --&g…...

未授权访问事件频发,我们应当如何应对?

在当下&#xff0c;数据已成为企业和组织的核心资产&#xff0c;是推动业务发展、决策制定以及创新的关键驱动力。然而&#xff0c;未授权访问这一隐匿的安全威胁&#xff0c;正如同高悬的达摩克利斯之剑&#xff0c;时刻威胁着数据的安全&#xff0c;一旦触发&#xff0c;便可…...