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

SAICP(模拟退火迭代最近点)的实现

SAICP(模拟退火迭代最近点)的实现

注: 本系列所有文章在github开源, 也是我个人的学习笔记, 欢迎大家去star以及fork, 感谢!

仓库地址: pointcloud-processing-visualization

总结一下上周的学习情况

ICP会存在局部最小值的问题, 这个问题可能即使是没有实际遇到过, 也或多或少会在各种点云匹配算法相关博客中看到,

于是我去查了一些资料, 发现可以通过模拟退火算法解决, 或者说有概率可以跳出局部最小值

最后将两个算法结合了起来, 写了这个demo, 同时因为我也是初学者, 因此将执行改成了单步运行,

同时将ICP以及模拟退火ICP(SAICP)的执行效果通过PCL Viewer逐步展示出来.

参考资料

@智能算法 模拟退火算法详解

@维基百科 模拟退火

@chatGPT 4.0 这个有点可惜, 已经找不到当时跟GPT的聊天记录了

模拟退火的实现

扰动的生成

在进行点云匹配时, 如果要实现模拟退火中的随机干扰, 我使用的思路是生成一组随机变换, 即一个Eigen::Matrix4f randTrans

这个变换随机生成, 包含x, y, z三轴横移, 以及roll, pitch, yaw三轴转动

通过温度值(temperature)来控制变换的幅度大小

对应代码块

// 根据温度生成随机的变换矩阵
Eigen::Matrix4f generateAnnealingTransformation(double temperature) {unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();boost::random::mt19937 gen(seed);double tras_scale = 0.15;double rot_scale = 0.17 * M_PI;boost::random::uniform_real_distribution<> rand_dis(0, 1);boost::random::uniform_real_distribution<> dis(-tras_scale * temperature, tras_scale * temperature);boost::random::uniform_real_distribution<> angle_dis(-rot_scale * temperature, rot_scale * temperature);Eigen::Matrix4f transform = Eigen::Matrix4f::Identity();transform(0, 3) = dis(gen); // X-axis translation disturbancetransform(1, 3) = dis(gen); // Y-axis translation disturbancetransform(2, 3) = dis(gen); // Z-axis translation disturbance// Rotation disturbance around Z-axisfloat angleZ = angle_dis(gen);Eigen::Matrix4f rotZ = Eigen::Matrix4f::Identity();rotZ(0, 0) = cos(angleZ);rotZ(0, 1) = -sin(angleZ);rotZ(1, 0) = sin(angleZ);rotZ(1, 1) = cos(angleZ);// Rotation disturbance around Y-axisfloat angleY = angle_dis(gen);Eigen::Matrix4f rotY = Eigen::Matrix4f::Identity();rotY(0, 0) = cos(angleY);rotY(0, 2) = sin(angleY);rotY(2, 0) = -sin(angleY);rotY(2, 2) = cos(angleY);// Rotation disturbance around X-axisfloat angleX = angle_dis(gen);Eigen::Matrix4f rotX = Eigen::Matrix4f::Identity();rotX(1, 1) = cos(angleX);rotX(1, 2) = -sin(angleX);rotX(2, 1) = sin(angleX);rotX(2, 2) = cos(angleX);// Combine the transformationstransform = transform * rotZ * rotY * rotX;return transform;
}// 在原有变换的基础上添加模拟退火的随机扰动
Eigen::Matrix4f annealing_transform = generateAnnealingTransformation(temperature);
Eigen::Matrix4f perturbed_transformation = saicp_result * annealing_transform;// 应用带有扰动的变换进行ICP迭代
saicp.align(*saicp_cloud, perturbed_transformation);

公式的实现

模拟退火时, 如果当前结果优于上次的结果, 就采纳当前结果,

如果当前结果比上次的结果差, 按以下公式来选择是否会选择差的结果(跳出局部最优的手段)
e R 1 − R 0 T > r a n d ( ) e^\frac{R_1-R_0}{T} > rand() eTR1R0>rand()
其中R1为这次的结果, R0为上次的结果 T为当前温度, rand()是随机生成的一个数字

如果当前的结果比上次的差, 则R1-R0为负数, 因此概率会随着温度的降低越来越小, 随着误差的增大也越来越小

在ICP中, 可以用icp.getFitnessScore()作为误差的判断条件, 当前的fitnessscore比上次小, 则采纳当前结果, 反之根据两次fitnessscore的差值计算概率

对应代码块为

// 退火
double new_fitness_score = saicp.getFitnessScore();if (new_fitness_score < last_fitness_score) 
{saicp_result = new_icp_result; // 接受更好的变换last_fitness_score = new_fitness_score; // 更新最新的fitness score
} 
// 新值大于旧值说明结果差, 取反, 满足误差越大概率越小的条件
else if (exp((-(new_fitness_score - last_fitness_score)) / temperature) > ((double)rand() / RAND_MAX))
{saicp_result = perturbed_transformation; // 以一定概率接受较差的变换last_fitness_score = new_fitness_score; // 更新fitness score,即使它变差了
}
// 更新温度
temperature *= coolingRate;

完整代码

TODO: 计算两个变换之间的误差公式应该是有问题的, 即使匹配上输出的结果还是很差

main.cpp

#include <iostream>
#include <fstream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/common/transforms.h>
#include <pcl/registration/icp.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <pcl/filters/filter.h>
#include <pcl/filters/voxel_grid.h>
#include <pcl/filters/radius_outlier_removal.h>
#include <pcl/filters/statistical_outlier_removal.h>
#include <Eigen/Core>
#include <Eigen/Geometry>
#include <Eigen/Dense>
#include <boost/random.hpp>
#include <chrono>
#include <thread>
#include <filesystem>// 生成随机扰动(方便跳出循环)
Eigen::Matrix4f generateAnnealingTransformation(double temperature) {unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();boost::random::mt19937 gen(seed);double tras_scale = 0.15;double rot_scale = 0.17 * M_PI;boost::random::uniform_real_distribution<> rand_dis(0, 1);boost::random::uniform_real_distribution<> dis(-tras_scale * temperature, tras_scale * temperature);boost::random::uniform_real_distribution<> angle_dis(-rot_scale * temperature, rot_scale * temperature);Eigen::Matrix4f transform = Eigen::Matrix4f::Identity();transform(0, 3) = dis(gen); // X-axis translation disturbancetransform(1, 3) = dis(gen); // Y-axis translation disturbancetransform(2, 3) = dis(gen); // Z-axis translation disturbance// Rotation disturbance around Z-axisfloat angleZ = angle_dis(gen);Eigen::Matrix4f rotZ = Eigen::Matrix4f::Identity();rotZ(0, 0) = cos(angleZ);rotZ(0, 1) = -sin(angleZ);rotZ(1, 0) = sin(angleZ);rotZ(1, 1) = cos(angleZ);// Rotation disturbance around Y-axisfloat angleY = angle_dis(gen);Eigen::Matrix4f rotY = Eigen::Matrix4f::Identity();rotY(0, 0) = cos(angleY);rotY(0, 2) = sin(angleY);rotY(2, 0) = -sin(angleY);rotY(2, 2) = cos(angleY);// Rotation disturbance around X-axisfloat angleX = angle_dis(gen);Eigen::Matrix4f rotX = Eigen::Matrix4f::Identity();rotX(1, 1) = cos(angleX);rotX(1, 2) = -sin(angleX);rotX(2, 1) = sin(angleX);rotX(2, 2) = cos(angleX);// Combine the transformationstransform = transform * rotZ * rotY * rotX;return transform;
}Eigen::Matrix4f generateRandomTransformation() {unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();boost::random::mt19937 gen(seed);  // 随机数生成器boost::random::uniform_real_distribution<> dis(-200, 200);  // 位移范围boost::random::uniform_real_distribution<> angle_dis(-M_PI, M_PI);  // 旋转范围Eigen::Matrix4f transform = Eigen::Matrix4f::Identity();transform(0, 3) = dis(gen); // X轴平移transform(1, 3) = dis(gen); // Y轴平移transform(2, 3) = dis(gen); // Z轴平移// Rotation disturbance around Z-axisfloat angleZ = angle_dis(gen);Eigen::Matrix4f rotZ = Eigen::Matrix4f::Identity();rotZ(0, 0) = cos(angleZ);rotZ(0, 1) = -sin(angleZ);rotZ(1, 0) = sin(angleZ);rotZ(1, 1) = cos(angleZ);// Rotation disturbance around Y-axisfloat angleY = angle_dis(gen);Eigen::Matrix4f rotY = Eigen::Matrix4f::Identity();rotY(0, 0) = cos(angleY);rotY(0, 2) = sin(angleY);rotY(2, 0) = -sin(angleY);rotY(2, 2) = cos(angleY);// Rotation disturbance around X-axisfloat angleX = angle_dis(gen);Eigen::Matrix4f rotX = Eigen::Matrix4f::Identity();rotX(1, 1) = cos(angleX);rotX(1, 2) = -sin(angleX);rotX(2, 1) = sin(angleX);rotX(2, 2) = cos(angleX);// Combine the transformationstransform = transform * rotZ * rotY * rotX;return transform;
}// 函数:随机选择一个.pcd文件
std::string selectRandomPCDFile(const std::string& directory) {std::vector<std::string> file_names;for (const auto& entry : std::filesystem::directory_iterator(directory)) {if (entry.path().extension() == ".pcd") {file_names.push_back(entry.path().filename().string());}}// 如果没有找到任何文件,则返回空字符串if (file_names.empty()) {return "";}// 随机选择一个文件srand(static_cast<unsigned int>(time(NULL)));  // 初始化随机数生成器std::string selected_file = file_names[rand() % file_names.size()];// 返回完整的文件路径return directory + selected_file;
}void saveTransformation(const Eigen::Matrix4f &transform, const std::string &filename) {std::ofstream file(filename);if (file.is_open()) {file << transform;file.close();}
}struct TransformationError {float translationError;Eigen::Vector3f rotationError; // 存储绕X轴、Y轴和Z轴的旋转误差
};// 重载 << 运算符以打印 TransformationError
std::ostream& operator<<(std::ostream& os, const TransformationError& error) {os << "Translation Error: " << error.translationError << ", "<< "Rotation Error: [" << error.rotationError.transpose() << "]";return os;
}// 示例:计算两个变换矩阵之间的误差
TransformationError CalculateTransformationError(const Eigen::Matrix4f &matrix1, const Eigen::Matrix4f &matrix2) {TransformationError error;// 计算平移误差Eigen::Vector3f translation1 = matrix1.block<3,1>(0, 3);Eigen::Vector3f translation2 = matrix2.block<3,1>(0, 3);error.translationError = (translation2 - translation1).norm();// 计算旋转误差Eigen::Quaternionf quaternion1(matrix1.block<3,3>(0,0));Eigen::Quaternionf quaternion2(matrix2.block<3,3>(0,0));Eigen::Quaternionf deltaQuaternion = quaternion1.inverse() * quaternion2;Eigen::Vector3f deltaEulerAngles = deltaQuaternion.toRotationMatrix().eulerAngles(0, 1, 2); // X, Y, Zerror.rotationError = deltaEulerAngles.cwiseAbs(); // 绝对值误差return error;
}
Eigen::Matrix4f readMatrixFromFile(const std::string& filepath) {std::ifstream file(filepath);Eigen::Matrix4f matrix;if (file.is_open()) {for (int i = 0; i < 4; ++i) {for (int j = 0; j < 4; ++j) {if (!(file >> matrix(i, j))) {throw std::runtime_error("文件格式错误或数据不足以填充矩阵");}}}file.close();} else {throw std::runtime_error("无法打开文件: " + filepath);}return matrix;
}int main() {// 配置退火时的随机数种子srand(static_cast<unsigned int>(std::chrono::system_clock::now().time_since_epoch().count()));std::string directory = "/home/smile/Desktop/github/src/pointcloud-processing-visualization/pcd/";// 使用函数选择一个随机文件std::string file_to_load = selectRandomPCDFile(directory);// 加载点云pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_in(new pcl::PointCloud<pcl::PointXYZ>);if (pcl::io::loadPCDFile<pcl::PointXYZ>(file_to_load, *cloud_in) == -1) {PCL_ERROR("Couldn't read file\n");return -1;}// 移除NaN值std::vector<int> indices;pcl::removeNaNFromPointCloud(*cloud_in, *cloud_in, indices);pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZ>);// 进行体素滤波pcl::VoxelGrid<pcl::PointXYZ> voxel_grid;voxel_grid.setInputCloud(cloud_in);voxel_grid.setLeafSize(0.07f, 0.07f, 0.07f);  // 设置体素大小voxel_grid.filter(*cloud_filtered);// 模拟退火参数double temperature = 5.2; // 初始温度double coolingRate = 0.985; // 冷却率// 全局变量double last_fitness_score = std::numeric_limits<double>::max(); // 初始设置为最大值// 生成变换并保存到文件Eigen::Matrix4f base_transformation = generateRandomTransformation();Eigen::Matrix4f base_transformation_normal = base_transformation;std::cout << "Base Transformation Matrix:\n" << base_transformation << std::endl;saveTransformation(base_transformation, "/home/smile/Desktop/github/src/pointcloud-processing-visualization/saicp/result.txt");// 应用初始变换pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_transformed(new pcl::PointCloud<pcl::PointXYZ>);pcl::transformPointCloud(*cloud_filtered, *cloud_transformed, base_transformation);// 设置SAICP实例pcl::IterativeClosestPoint<pcl::PointXYZ, pcl::PointXYZ> saicp, normal_icp;saicp.setInputSource(cloud_transformed);saicp.setInputTarget(cloud_filtered);saicp.setMaximumIterations(1); // 每次调用align时执行一次迭代normal_icp.setInputSource(cloud_transformed);normal_icp.setInputTarget(cloud_filtered);normal_icp.setMaximumIterations(1);// 初始化可视化pcl::visualization::PCLVisualizer viewer("SAICP demo");viewer.setBackgroundColor(0, 0, 0);viewer.addPointCloud<pcl::PointXYZ>(cloud_filtered, "cloud_filtered");viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "cloud_filtered");viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 1.0, 0.0, 0.0, "cloud_filtered");pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_saicp(new pcl::PointCloud<pcl::PointXYZ>(*cloud_transformed));pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_icp_normal(new pcl::PointCloud<pcl::PointXYZ>(*cloud_transformed));viewer.addPointCloud<pcl::PointXYZ>(cloud_saicp, "cloud_saicp");viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "cloud_saicp");viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 0.0, 1.0, 0.0, "cloud_saicp");viewer.addPointCloud<pcl::PointXYZ>(cloud_icp_normal, "cloud_icp_normal");viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "cloud_icp_normal");viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 0.0, 0.0, 1.0, "cloud_icp_normal");viewer.addCoordinateSystem(1.0);viewer.initCameraParameters();// 创建初始变换的矩阵, 先验位姿的是一个单位阵, 即无先验位姿Eigen::Matrix4f saicp_result = Eigen::Matrix4f::Identity();Eigen::Matrix4f normal_icp_result = Eigen::Matrix4f::Identity();// 计数器int saicp_cnt = 1; // icp迭代次数int normal_icp_cnt = 1; // 先验icp迭代次数bool saicp_fitness_reached = false; bool normal_icp_fitness_reached = false;int iteration_counter = 0;  // 迭代频率计数器, 迭代的频率按照 10ms x iteration_counter 可以在下面的循环中修改int stop_iteration_cnt = 0;bool has_published = false;int bad_value_accetp_cnt = 0;while (!viewer.wasStopped()) {viewer.spinOnce();  // 图像化界面刷新频率10ms, 方便使用鼠标进行控制视角 std::this_thread::sleep_for(std::chrono::milliseconds(10));// 如果都完成了收敛, 则不再更新if((saicp_fitness_reached && normal_icp_fitness_reached) || (++stop_iteration_cnt >= 2000)) {if(!has_published){double icp_score = saicp.getFitnessScore();double icp_normal_score = normal_icp.getFitnessScore();std::cout << "SAICP迭代次数: " << saicp_cnt << " SAICP分数: " << icp_score <<std::endl;std::cout << "普通ICP迭代次数: " << normal_icp_cnt << " 普通ICP分数: " << icp_normal_score <<std::endl;std::cout << "迭代次数比率: " << (saicp_cnt-normal_icp_cnt)/normal_icp_cnt <<std::endl;std::cout << "分数差比率: " << std::abs((icp_score-icp_normal_score))/icp_normal_score <<std::endl;std::cout << "差值接收率: " << double(bad_value_accetp_cnt)/double(saicp_cnt) <<std::endl;std::cout << "[SAICP]变换矩阵 " << std::endl;std::cout << saicp.getFinalTransformation() << std::endl;std::cout << "[SAICP]误差 " << std::endl;Eigen::Matrix4f result = readMatrixFromFile("/home/smile/Desktop/github/src/pointcloud-processing-visualization/saicp/result.txt");std::cout << CalculateTransformationError(saicp.getFinalTransformation(),result) <<std::endl;std::cout << "-----------------------------------------------------------" << std::endl;std::cout << "[SAICP]变换矩阵 " <<std::endl;std::cout << normal_icp.getFinalTransformation() << std::endl;std::cout << "[SAICP]误差 " << std::endl;std::cout << CalculateTransformationError(normal_icp.getFinalTransformation(),result) <<std::endl;std::cout << "-----------------------------------------------------------" << std::endl;has_published = true;}continue;}// 创建icp之后的新点云pcl::PointCloud<pcl::PointXYZ>::Ptr saicp_cloud(new pcl::PointCloud<pcl::PointXYZ>);pcl::PointCloud<pcl::PointXYZ>::Ptr normal_icp_cloud(new pcl::PointCloud<pcl::PointXYZ>);// 每10ms x 100 = 1000ms = 1s 即每1秒做一次icp并更新点云if (++iteration_counter >= 2) {// 如果没有达到0.0001的分值, 则icp继续迭代if(!saicp_fitness_reached){// 在原有变换的基础上添加模拟退火的随机扰动Eigen::Matrix4f annealing_transform = generateAnnealingTransformation(temperature);Eigen::Matrix4f perturbed_transformation = saicp_result * annealing_transform;// 应用带有扰动的变换进行ICP迭代saicp.align(*saicp_cloud, perturbed_transformation);Eigen::Matrix4f new_icp_result = saicp.getFinalTransformation();// 检查是否收敛(肯定收敛, 因为最多迭代1次,所以每一次都会收敛)if (saicp.hasConverged()) {// 退火double new_fitness_score = saicp.getFitnessScore();if (new_fitness_score < last_fitness_score) {saicp_result = new_icp_result; // 接受更好的变换last_fitness_score = new_fitness_score; // 更新最新的fitness score} else if (exp((-(new_fitness_score - last_fitness_score)) / temperature) > ((double)rand() / RAND_MAX)){bad_value_accetp_cnt++;	saicp_result = perturbed_transformation; // 以一定概率接受较差的变换last_fitness_score = new_fitness_score; // 更新fitness score,即使它变差了}// 更新温度temperature *= coolingRate;// std::cout << "======================================================="<<std::endl;// std::cout << "当前温度: " << temperature <<std::endl;// std::cout << "======================================================="<<std::endl;double fitness_score = saicp.getFitnessScore();if(saicp_fitness_reached) saicp_cnt=saicp_cnt;else saicp_cnt += 1;// std::cout << "[ICP] 分数为 " << fitness_score <<std::endl;// 获取最新一次的变换, 并将该变换应用到带先验的点云上, 更新该点云base_transformation = saicp.getFinalTransformation().cast<float>();pcl::transformPointCloud(*cloud_transformed, *saicp_cloud, base_transformation);viewer.updatePointCloud<pcl::PointXYZ>(saicp_cloud, "cloud_saicp");//真正的停止条件(收敛条件)if(fitness_score<=0.001){saicp_fitness_reached = true;std::cout << "======================================================="<<std::endl;std::cout << "[SAICP]完成收敛 " <<std::endl;std::cout << "[SAICP]迭代次数为 " << saicp_cnt <<std::endl;std::cout << "[SAICP]变换矩阵 " << std::endl;std::cout << saicp.getFinalTransformation() << std::endl;std::cout << "======================================================="<<std::endl;}     }}   // 普通icp    if(!normal_icp_fitness_reached){normal_icp.align(*normal_icp_cloud, normal_icp_result);normal_icp_result = normal_icp.getFinalTransformation();// 同理, 这里并不是真正的停止条件if (normal_icp.hasConverged()) {double fitness_score_normal = normal_icp.getFitnessScore();if(normal_icp_fitness_reached) normal_icp_cnt = normal_icp_cnt;else normal_icp_cnt += 1;// std::cout << "[普通ICP] 分数为 " << fitness_score_normal <<std::endl;// 带先验的停止条件也是0.0001分以下终止if(fitness_score_normal<=0.001){normal_icp_fitness_reached = true;std::cout << "======================================================="<<std::endl;std::cout << "[普通ICP]完成收敛 " <<std::endl;std::cout << "[普通ICP]迭代次数为 " << normal_icp_cnt <<std::endl;std::cout << "[普通ICP]变换矩阵 " <<std::endl;std::cout << normal_icp.getFinalTransformation() << std::endl;std::cout << "======================================================="<<std::endl;}// 获取最新一次的变换, 并将该变换应用到带先验的点云上, 更新该点云base_transformation_normal = normal_icp.getFinalTransformation().cast<float>();pcl::transformPointCloud(*cloud_transformed, *normal_icp_cloud, base_transformation_normal);viewer.updatePointCloud<pcl::PointXYZ>(normal_icp_cloud, "cloud_icp_normal"); }}// 重置迭代计数器iteration_counter = 0;}}
return 0;
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.0 FATAL_ERROR)
project(saicp_example)# 设置 C++ 标准为 C++17
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)find_package(PCL 1.8 REQUIRED)include_directories(${PCL_INCLUDE_DIRS})
link_directories(${PCL_LIBRARY_DIRS})
add_definitions(${PCL_DEFINITIONS})add_executable(saicp_example main.cpp)# 链接 PCL 库和可能需要的 stdc++fs 库
target_link_libraries(saicp_example ${PCL_LIBRARIES})

相关文章:

SAICP(模拟退火迭代最近点)的实现

SAICP(模拟退火迭代最近点)的实现 注: 本系列所有文章在github开源, 也是我个人的学习笔记, 欢迎大家去star以及fork, 感谢! 仓库地址: pointcloud-processing-visualization 总结一下上周的学习情况 ICP会存在局部最小值的问题, 这个问题可能即使是没有实际遇到过, 也或多…...

FineBI实战项目一(23):订单商品分类词云图分析开发

点击新建组件&#xff0c;创建订单商品分类词云图组件。 选择词云&#xff0c;拖拽catName到颜色和文本&#xff0c;拖拽cat到大小。 将组件拖拽到仪表板。 结果如下&#xff1a;...

DOS命令

当使用DOS命令时&#xff0c;可以在命令提示符下输入各种命令以执行不同的任务。以下是一些常见DOS命令的详细说明&#xff1a; dir (Directory): 列出当前目录中的文件和子目录。 用法: dir [drive:][path][filename] [/p] [/w] cd (Change Directory): 更改当前目录。 用法: …...

【Python】torch中的.detach()函数详解和示例

在PyTorch中&#xff0c;.detach()是一个用于张量的方法&#xff0c;主要用于创建该张量的一个“离断”版本。这个方法在很多情况下都非常有用&#xff0c;例如在缓存释放、模型评估和简化计算图等场景中。 .detach()方法用于从计算图中分离一个张量&#xff0c;这意味着它创建…...

二级域名分发系统源码 对接易支付php源码 全开源

全面开源的易支付PHP源码分享&#xff1a;实现二级域名分发对接 首先&#xff0c;在epay的config.php文件中修改您的支付域名。 随后&#xff0c;在二级域名分发网站上做相应修改。 伪静态 location / { try_files $uri $uri/ /index.php?$query_string; } 源码下载&#…...

二分查找与搜索树的高频问题(算法村第九关白银挑战)

基于二分查找的拓展问题 山峰数组的封顶索引 852. 山脉数组的峰顶索引 - 力扣&#xff08;LeetCode&#xff09; 给你由整数组成的山脉数组 arr &#xff0c;返回满足 arr[0] < arr[1] < ... arr[i - 1] < arr[i] > arr[i 1] > ... > arr[arr.length - 1…...

Python爬虫快速入门

Python 爬虫Sutdy 1.基本类库 request(请求) 引入 from urllib import request定义url路径 url"http://www.baidu.com"进行请求,返回一个响应对象response responserequest.urlopen(url)读取响应体read()以字节形式打印网页源码 response.read()转码 编码 文本–by…...

部署MinIO

一、安装部署MINIO 1.1 下载 wget https://dl.min.io/server/minio/release/linux-arm64/minio chmod x minio mv minio /usr/local/bin/ # 控制台启动可参考如下命令, 守护进程启动请看下一个代码块 # ./minio server /data /data --console-address ":9001"1.2 配…...

RK3566环境搭建

环境&#xff1a;vmware16&#xff0c;ubuntu 18.04 安装依赖库&#xff1a; sudo apt-get install repo git ssh make gcc libssl-dev liblz4-tool expect g patchelf chrpath gawk texinfo chrpath diffstat binfmt-support qemu-user-static live-build bison flex fakero…...

精确掌控并发:滑动时间窗口算法在分布式环境下并发流量控制的设计与实现

这是《百图解码支付系统设计与实现》专栏系列文章中的第&#xff08;15&#xff09;篇&#xff0c;也是流量控制系列的第&#xff08;2&#xff09;篇。点击上方关注&#xff0c;深入了解支付系统的方方面面。 上一篇介绍了固定时间窗口算法在支付渠道限流的应用以及使用redis…...

Python展示 RGB立方体的二维切面视图

代码实现 import numpy as np import matplotlib.pyplot as plt# 生成 24-bit 全彩 RGB 立方体 def generate_rgb_cube():# 初始化一个 256x256x256 的三维数组rgb_cube np.zeros((256, 256, 256, 3), dtypenp.uint8)# 填充立方体for r in range(256):for g in range(256):fo…...

03 顺序表

目录 线性表顺序表练习 线性表(Linear list)是n个具有相同特性的数据元素的有限序列。线性表是一种在实际中广泛使用的数据结构&#xff0c;常见的线性表&#xff1a;顺序表、链表、栈、队列、字符串。。。 线性表在逻辑上时线性结构&#xff0c;是连续的一条直线。但在物理结…...

2023年全球软件开发大会(QCon北京站2023)9月:核心内容与学习收获(附大会核心PPT下载)

随着科技的飞速发展&#xff0c;全球软件开发大会&#xff08;QCon&#xff09;作为行业领先的技术盛会&#xff0c;为世界各地的专业人士提供了交流与学习的平台。本次大会汇集了全球的软件开发者、架构师、项目经理等&#xff0c;共同探讨软件开发的最新趋势、技术与实践。本…...

ChatGPT 和 文心一言 的优缺点及需求和使用场景

ChatGPT和文心一言是两种不同的自然语言生成模型&#xff0c;它们有各自的优点和缺点。 ChatGPT&#xff08;Generative Pre-trained Transformer&#xff09;是由OpenAI开发的生成式AI模型&#xff0c;它在庞大的文本数据集上进行了预训练&#xff0c;并可以根据输入生成具有上…...

架构师之超时未支付的订单进行取消操作的几种解决方案

今天给大家上一盘硬菜&#xff0c;并且是支付中非常重要的一个技术解决方案&#xff0c;有这块业务的同学注意自己尝试一把哈&#xff01; 一、需求如下&#xff1a; 生成订单30分钟未支付&#xff0c;自动取消 生成订单60秒后,给用户发短信 对上述的需求&#xff0c;我们给…...

【容器固化】 OS技术之OpenStack容器固化的实现原理及操作

1. Docker简介 要学习容器固化&#xff0c;那么必须要先了解下Docker容器技术。Docker是基于GO语言实现的云开源项目&#xff0c;通过对应用软件的封装、分发、部署、运行等生命周期的管理&#xff0c;达到应用组件级别的“一次封装&#xff0c;到处运行”。这里的应用软件&am…...

设置 SSH 通过密钥登录

我们一般使用 PuTTY 等 SSH 客户端来远程管理 Linux 服务器。但是&#xff0c;一般的密码方式登录&#xff0c;容易有密码被暴力破解的问题。所以&#xff0c;一般我们会将 SSH 的端口设置为默认的 22 以外的端口&#xff0c;或者禁用 root 账户登录。其实&#xff0c;有一个更…...

1.6 面试经典150题 - 买卖股票的最佳时机

买卖股票的最佳时机 给定一个数组 prices &#xff0c;它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。 你只能选择 某一天 买入这只股票&#xff0c;并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。 返回你可以从这笔交易…...

locust快速入门--使用分布式提高测试压力

背景&#xff1a; 使用默认的locust启动命令进行压测时&#xff0c;尽管已经将用户数设置大比较大&#xff08;400&#xff09;&#xff0c;但是压测的时候RPS一直在100左右。需要增加压测的压力。 问题原因&#xff1a; 如果你是通过命令行启动的或者参考之前文章的启动方式…...

K8s(三)Pod资源——pod亲和性与反亲和性,pod重启策略

目录 pod亲和性与反亲和性 pod亲和性 pod反亲和性 pod状态与重启策略 pod状态 pod重启策略 本文主要介绍了pod资源与pod相关的亲和性&#xff0c;以及pod的重启策略 pod亲和性与反亲和性 pod亲和性&#xff08;podAffinity&#xff09;有两种 1.podaffinity&#xff0c;…...

免费的域名要不要?

前言 eu.org的免费域名相比于其他免费域名注册服务&#xff0c;eu.org的域名后缀更加独特。同时&#xff0c;eu.org的域名注册也比较简单&#xff0c;只需要填写一些基本信息&#xff0c;就可以获得自己的免费域名。 博客地址 免费的域名要不要&#xff1f;-雪饼前言 eu.org…...

高通sm7250与765G芯片是什么关系?(一百八十一)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…...

[Python进阶] Python操作MySQL数据库:pymysql

7.7 操作MySQL数据库&#xff1a;pymysql 7.7.1 准备工作(创建mysql数据库) PHPStudy介绍&#xff1a; phpstudy是一款非常有用的PHP开发工具&#xff0c;旨在帮助开发者更加便捷地进行PHP程序的开发与调试。它提供了一个友好的图形用户界面&#xff0c;使得用户能够方便地进…...

Vue3实现带点击外部关闭对应弹出框(可共用一个变量)

首先&#xff0c;假设您在单文件组件(SFC)中使用了Vue3&#xff0c;并且有两个div元素分别通过v-if和v-else来切换显示一个带有.elpopver类的弹出组件。在这种情况下&#xff0c;每个弹出组件应当拥有独立的状态管理&#xff08;例如&#xff1a;各自的isOpen变量&#xff09;。…...

可视化试题(一)

1. 从可视化系统设计的角度出发&#xff0c;通常需要根据系统将要完成的任务的类型选择交互技术。按照任务类型分类可以将数据可视化中的交互技术分为选择、&#xff08; 重新配置 &#xff09;、重新编码、导航、关联、&#xff08; 过滤 &#xff09;、概览和细节等八…...

RHCE 【在openEuler系统中搭建基本论坛(网站)】

目录 网站需求&#xff1a; 准备工作&#xff1a; 1.基于域名[www.openlab.com](http://www.openlab.com)可以访问网站内容为 welcome to openlab!!! 测试&#xff1a; 2.给该公司创建三个子界面分别显示学生信息&#xff0c;教学资料和缴费网站&#xff0c;基于[www.openla…...

20240112让移远mini-PCIE接口的4G模块EC20在Firefly的AIO-3399J开发板的Android11下跑通【DTS部分】

20240112让移远mini-PCIE接口的4G模块EC20在Firefly的AIO-3399J开发板的Android11下跑通【DTS部分】 2024/1/12 16:20 https://blog.csdn.net/u010164190/article/details/79096345 [Android6.0][RK3399] PCIe 接口 4G模块 EC20 调试记录 https://blog.csdn.net/hnjztyx/artic…...

日志采集传输框架之 Flume,将监听端口数据发送至Kafka

1、简介 Flume 是 Cloudera 提供的一个高可用的&#xff0c;高可靠的&#xff0c;分布式的海量日志采集、聚合和传 输的系统。Flume 基于流式架构&#xff0c;主要有以下几个部分组成。 主要组件介绍&#xff1a; 1&#xff09;、Flume Agent 是一个 JVM 进程&#xf…...

关于Vue前端接口对接的思考

关于Vue前端接口对接的思考 目录概述需求&#xff1a; 设计思路实现思路分析1.vue 组件分类和获取数值的方式2.http 通信方式 分类 如何对接3.vue 组件分类和赋值方式&#xff0c; 参考资料和推荐阅读 Survive by day and develop by night. talk for import biz , show your p…...

【设计模式之美】SOLID 原则之三:里式替换(LSP)跟多态有何区别?如何理解LSP中子类遵守父类的约定

文章目录 一. 如何理解“里式替换原则”&#xff1f;二. 哪些代码明显违背了 LSP&#xff1f;三. 回顾 一. 如何理解“里式替换原则”&#xff1f; 子类对象能够替换程序中父类对象出现的任何地方&#xff0c;并且保证原来程序的逻辑行为不变及正确性不被破坏。 里氏替换原则…...

余姚做轴承网站/新闻头条最新消息今日头条

使用图&#xff08;graphs&#xff09;来表示计算任务 在被称之为会话&#xff08;Session&#xff09;的上下文&#xff08;context&#xff09;中被执行 使用tensor表示数据 通过变量&#xff08;Variable&#xff09;维护状态 使用feed和fetch可以为任意的操作赋值或者从…...

怎么建设一个手机网站/进入百度搜索网站

9月27日&#xff0c;云与大数据安全技术厂商亚信安全与全球性AI及医疗大数据平台零氪科技&#xff08;LinkDoc&#xff09;达成战略合作&#xff0c;双方将在医疗行业混合云安全、移动应用安全防护、数据隐私保护、态势感知平台、数据脱敏及攻防演练、渗透测试等方面开展深入合…...

如何做网站店铺的模板/网站怎么提升关键词排名

前言递归是算法中一种非常重要的思想&#xff0c;应用也很广&#xff0c;小到阶乘,再在工作中用到的比如统计文件夹大小&#xff0c;大到 Google 的 PageRank 算法都能看到&#xff0c;也是面试官很喜欢的考点最近看了不少递归的文章&#xff0c;收获不小&#xff0c;不过我发现…...

网站宣传与推广的指导思想/免费二级域名分发平台

题目链接https://code.mi.com/problem/list/view?id3 描述 两个长度超出常规整形变量上限的大数相减&#xff0c;请避免使用各语言内置大数处理库&#xff0c;如 Java.math.BigInteger 等。 输入 有 N 行测试数据&#xff0c;每一行有两个代表整数的字符串 a 和 b&#xf…...

wordpress 旅行社/百度教育官网

我们现在说Recursive backtracking&#xff1a; 迷宫的初始状态是墙壁都存在。选择一个开始区域。随机得选择一个没有访问过的邻接区域&#xff0c;并打通与它之间的墙壁。此邻接区域称为当前区域。如果所有周围的区域都是访问过的&#xff0c;则退回上一个区域进行挖据墙壁&am…...

网站和网页的设计原则/百度投诉中心在线申诉

– Start 点击此处观看本系列配套视频 废话少说&#xff0c;直接上代码。 package shangbo.kafka.example9;import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class App {…...