GEE+本地XGboot分类

GEE+本地XGboot分类

我想做提取耕地提取,想到了一篇董金玮老师的一篇论文,这个论文是先提取的耕地,再做作物分类,耕地的提取代码是开源的。

但这个代码直接在云端上进行分类,GEE会爆内存,因此我准备把数据下载到本地,使用GPU加速进行XGboot提取耕地。

董老师的代码涉及到了100多个波段特征,我删减到了45个波段,然后分块进行了数据下载:

数据下载代码:

// ========================================
// 1. 初始化与区域选择
// ========================================

// 合并训练数据
var trainTable = trainTable_crop.merge(trainTable_other);

// 选择第一个区域作为AOI
var aoiFeature = fenqu.first();
var aoi = aoiFeature.geometry();

// 可视化AOI(可选)
Map.addLayer(aoi, {color: 'blue'}, 'AOI');

// 中心定位到AOI,缩放级别10(可选)
Map.centerObject(aoi, 10);

// ========================================
// 2. 划分AOI为16个块
// ========================================

// 定义划分块数(4x4网格)
var numCols = 4;
var numRows = 4;

// 获取AOI的边界和范围
var aoiBounds = aoi.bounds();
var coords = ee.List(aoiBounds.coordinates().get(0));
var xMin = ee.Number(ee.List(coords.get(0)).get(0));
var yMin = ee.Number(ee.List(coords.get(0)).get(1));
var xMax = ee.Number(ee.List(coords.get(2)).get(0));
var yMax = ee.Number(ee.List(coords.get(2)).get(1));

// 计算AOI的宽度和高度
var aoiWidth = xMax.subtract(xMin);
var aoiHeight = yMax.subtract(yMin);

// 计算每个块的宽度和高度
var tileWidth = aoiWidth.divide(numCols);
var tileHeight = aoiHeight.divide(numRows);

// 要排除的块的ID
var excludeTiles = ee.List(['0_3', '0_2', '3_0']);

// 生成4x4网格,但排除特定块
var grid = ee.FeatureCollection(
  ee.List.sequence(0, numCols - 1).map(function(col) {
    return ee.List.sequence(0, numRows - 1).map(function(row) {
      // 将数字转换为整数字符串
      var colStr = ee.Number(col).int();
      var rowStr = ee.Number(row).int();
      var tileId = ee.String(colStr).cat('_').cat(ee.String(rowStr));
      
      var xmin = xMin.add(tileWidth.multiply(ee.Number(col)));
      var ymin = yMin.add(tileHeight.multiply(ee.Number(row)));
      var xmax = xmin.add(tileWidth);
      var ymax = ymin.add(tileHeight);
      var rectangle = ee.Geometry.Rectangle([xmin, ymin, xmax, ymax]);
      return ee.Feature(rectangle, {
        'tile': tileId
      });
    });
  }).flatten()
).filter(ee.Filter.inList('tile', excludeTiles).not());

// 可视化网格
Map.addLayer(grid, {color: 'red'}, 'Grid');
print('Filtered tile count:', grid.size());

// 打印tile ID以验证格式
print('Tile IDs:', grid.aggregate_array('tile'));

// ========================================
// 3. 定义数据处理和导出函数
// ========================================

function processAndExport(tileFeature) {
  var tileID = ee.String(tileFeature.get('tile'));
  print('Processing Tile:', tileID);
  
  var region = tileFeature.geometry();
  

  
  // 2. 定义时间范围、波段及区域
  var year = 2023;
  var startDate = ee.Date.fromYMD(year, 1, 1);
  var endDate = ee.Date.fromYMD(year, 12, 31);
  
  var bands = ['B2', 'B3', 'B4', 'B8']; // 蓝、绿、红、近红外
  
  // 3. 云掩膜函数:基于SCL波段
  function maskS2clouds(image) {
    var scl = image.select('SCL');
    // SCL分类值: 3(云)、8(阴影云)
    var cloudMask = scl.neq(3).and(scl.neq(8));
    return image.updateMask(cloudMask)
                .clip(region)
                .copyProperties(image, ["system:time_start"]);
  }
  
  // 4. 添加光谱指数函数
  function addSpectralIndices(image) {
    // 计算NDVI
    var ndvi = image.normalizedDifference(['B8', 'B4']).rename('NDVI');
    
    // 计算EVI
    var evi = image.expression(
      '2.5 * ((NIR - RED) / (NIR + 6 * RED - 7.5 * BLUE + 1))', {
        'NIR': image.select('B8'),
        'RED': image.select('B4'),
        'BLUE': image.select('B2')
      }
    ).rename('EVI');
    
    // 计算GNDVI
    var gndvi = image.normalizedDifference(['B8', 'B3']).rename('GNDVI');
    
    // 计算SAVI
    var savi = image.expression(
      '((NIR - RED) / (NIR + RED + 0.5)) * 1.5', {
        'NIR': image.select('B8'),
        'RED': image.select('B4')
      }
    ).rename('SAVI');
    
    // 计算MSAVI2
    var msavi2 = image.expression(
      '0.5 * (2 * NIR + 1 - sqrt((2 * NIR + 1)**2 - 8 * (NIR - RED)))', {
        'NIR': image.select('B8'),
        'RED': image.select('B4')
      }
    ).rename('MSAVI2');
    
    // 计算NDWI
    var ndwi = image.normalizedDifference(['B3', 'B8']).rename('NDWI');
    
    // 计算NDSI
    var ndsi = image.normalizedDifference(['B3', 'B11']).rename('NDSI');
    
    // 计算NDSVI
    var ndsvi = image.normalizedDifference(['B11', 'B4']).rename('NDSVI');
    
    // 计算NDTI
    var ndti = image.normalizedDifference(['B11', 'B12']).rename('NDTI');
    
    // 计算RENDVI
    var rendvi = image.normalizedDifference(['B8', 'B5']).rename('RENDVI');
    
    // 计算REP
    var rep = image.expression(
      '(705 + 35 * ((0.5 * (B6 + B4) - B2) / (B5 - B2))) / 1000', {
        'B2': image.select('B2'),
        'B4': image.select('B4'),
        'B5': image.select('B5'),
        'B6': image.select('B6'),
        'B8': image.select('B8')
      }
    ).rename('REP');
    
    // 添加所有计算的波段
    return image.addBands([ndvi, evi, gndvi, savi, msavi2, ndwi, ndsi, ndsvi, ndti, rendvi, rep]);
  }
  
  // 5. 加载并预处理Sentinel-2 L2A影像集合
  var sentinel = ee.ImageCollection("COPERNICUS/S2_SR"); // 确保使用正确的Sentinel-2影像集合
  var s2 = sentinel
    .filterBounds(region)
    .filterDate(startDate, endDate)
    .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 20))  // 初步云量过滤
    .map(maskS2clouds)
    .map(addSpectralIndices)
    .select(['B2', 'B3', 'B4', 'B8', 'NDVI', 'EVI', 'GNDVI', 'SAVI', 'MSAVI2', 'NDWI', 'NDSI', 'NDSVI', 'NDTI', 'RENDVI', 'REP']);
  
  // 6. 计算月度NDVI最大值
  var months = ee.List.sequence(1, 12);
  
  var monthlyMaxNDVI = months.map(function(month) {
    var monthStart = ee.Date.fromYMD(year, month, 1);
    var monthEnd = monthStart.advance(1, 'month');
    
    var monthlyNDVI = s2
      .filterDate(monthStart, monthEnd)
      .select('NDVI')
      .max();
    
    // 使用 ee.String 和 .cat() 正确拼接字符串
    var bandName = ee.String('NDVI_month_').cat(ee.Number(month).format('%02d'));
    return monthlyNDVI.rename(bandName);
  });
  print(monthlyMaxNDVI,"monthlyMaxNDVI" )
  // 将所有月份的最大NDVI合并为一个图像
  var monthlyMaxNDVIImage = ee.Image(monthlyMaxNDVI.get(0));
  for (var i = 1; i < 12; i++) {
    monthlyMaxNDVIImage = monthlyMaxNDVIImage.addBands(ee.Image(monthlyMaxNDVI.get(i)));
  }
  print(monthlyMaxNDVIImage,"monthlyMaxNDVIImage" )
  
  // 7. 提取年度统计特征
  var Year_Bands = ['B2', 'B3', 'B4', 'B8', 'NDVI', 'EVI', 'GNDVI', 'SAVI', 'MSAVI2', 'NDWI', 'NDSI', 'NDSVI', 'NDTI', 'RENDVI', 'REP'];
  
  var annualStats = s2.select(Year_Bands)
      .reduce(ee.Reducer.mean()  

        .combine(ee.Reducer.max(), null, true)
        .combine(ee.Reducer.stdDev(), null, true));

  
  // 重命名年度统计特征的波段
  var statNames = ['mean', 'max', 'stdDev'];
  var newBandNames = [];
  Year_Bands.forEach(function(band) {
    statNames.forEach(function(stat) {
      newBandNames.push(band + '_' + stat);
    });
  });
  annualStats = annualStats.rename(newBandNames);
  
  // 将月度NDVI最大值和年度统计特征合并
  annualStats = ee.Image.cat([annualStats, monthlyMaxNDVIImage]);
  
 
  
  // 9. 合并所有特征
  var finalImage = annualStats.clip(region);
  print(finalImage,"finalImage")                
  // 可视化示例(可选)
  // Map.addLayer(finalImage.select('NDVI_seasonal'), {min: 0, max: 1, palette: ['white', 'green']}, 'NDVI Seasonal');
  
  // 10. 导出数据到Google Drive
  var output_name='tile_' + tileID.getInfo()
  var name2=output_name.replace('.', '').replace('.', '')
  print(finalImage.toFloat())
  Export.image.toDrive({
    image: finalImage.toFloat(),
    description:  name2,
    scale: 10,
    folder: "download_tiles_HENAN_FENQU1",
    region: region,  
    maxPixels: 1e13
  });
}

// ========================================
// 4. 应用函数到每个块
// ========================================

// 注意:Google Earth Engine 同时只能运行有限的Export任务(通常为3个)。
// 因此,建议分批次运行或手动触发每个块的导出任务。

// 将网格转换为特征集合列表
var gridFeatures = grid.toList(grid.size());

// 获取总块数
var totalTiles = grid.size().getInfo();

// 定义每批次导出的数量(如果需要批量控制,可以在这里调整)
var batchSize = 1;

// 处理并导出每个块
// 注意:Google Earth Engine 不支持并行启动大量导出任务,请手动管理导出任务
gridFeatures.evaluate(function(list) {
  list.forEach(function(feature) {
    processAndExport(ee.Feature(feature));
  });
});

// 打印总块数和导出说明
print('Total tiles:', totalTiles);
print('导出已启动。请在任务管理器中检查导出状态。');

然后下载完成后,用gdal做一下镶嵌(设置tile为256,LZW压缩),波段太多,导致数据非常大。最好再做一个金字塔

import os
from osgeo import gdal

# 输入和输出路径
input_dir = r"几十个波段数据"
output_file = "mosaic_result_gdal.tif"

# 获取所有tif文件
tif_files = []
for file in os.listdir(input_dir):
    if file.endswith('.tif'):
        tif_files.append(os.path.join(input_dir, file))

# 构建VRT
vrt = gdal.BuildVRT("temp.vrt", tif_files)
vrt = None

# 转换VRT为GeoTiff
gdal.Translate(
    output_file,
    "temp.vrt",
    format="GTiff",
    creationOptions=[
        "COMPRESS=LZW",
        "TILED=YES",
        "BLOCKXSIZE=256",
        "BLOCKYSIZE=256",
        "BIGTIFF=YES"
    ]
)

镶嵌完,可以放进GIS软件中查看一下。

数据分类

在此之前,需要先准备点数据,我是准备了两个点数据矢量(耕地矢量和非耕地矢量),字段属性crop为1代表耕地,0代表非耕地。如果你是做多类别,你可以多做几个矢量。

然后开始安装环境:

(1)安装CUDA,用GPU加速运行,也可以CPU,都差不多,xgboot计算量不大;

(2)安装conda,然后使用下面的命令安装环境:

conda create --prefix D:\conda_ENV\xgboot_env python=3.10
conda activate D:\conda_ENV\xgboot_env
conda install -c conda-forge numpy pandas geopandas rasterio scikit-learn tqdm

然后就可以开始分类了,代码如下:

import geopandas as gpd
import rasterio
from rasterio.sample import sample_gen
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
import xgboost as xgb
from sklearn.metrics import accuracy_score, confusion_matrix, classification_report, roc_auc_score
from tqdm import tqdm  # 用于进度指示

# 读取矢量数据
CROP_FILE = r"耕地样本点.shp"
OTHERS_FILE = r"非耕地样本点.shp"
TIF_PATH = r"mosaic_result_gdal.tif"

cropland = gpd.read_file(CROP_FILE)
non_cropland = gpd.read_file(OTHERS_FILE)
cropland['crop'] = 1
non_cropland['crop'] = 0
samples = pd.concat([cropland, non_cropland], ignore_index=True)

with rasterio.open(TIF_PATH) as src:
    band_count = src.count
    coords = [(point.x, point.y) for point in samples.geometry]
    pixel_values = list(src.sample(coords))
    pixel_values = np.array(pixel_values)

feature_columns = [f'band_{i+1}' for i in range(band_count)]
features = pd.DataFrame(pixel_values, columns=feature_columns)
features['crop'] = samples['crop'].values

# 保存特征名称以供预测阶段使用
feature_names = feature_columns.copy()

# 数据预处理
features.dropna(inplace=True)
X = features.drop('crop', axis=1)
y = features['crop']
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42, stratify=y
)

# 训练模型
dtrain = xgb.DMatrix(X_train, label=y_train, feature_names=feature_names)
dtest = xgb.DMatrix(X_test, label=y_test, feature_names=feature_names)

params = {
    'objective': 'binary:logistic',
    'tree_method': 'hist',         # 修改为 'hist'
    'device': 'gpu',               # 添加 'device' 参数
    'eval_metric': 'auc',
    'eta': 0.1,
    'max_depth': 10,
    'subsample': 0.8,
    'colsample_bytree': 0.8,
    'seed': 42
}

evallist = [(dtest, 'eval'), (dtrain, 'train')]
num_round = 100

print("开始训练模型...")
bst = xgb.train(params, dtrain, num_round, evallist, early_stopping_rounds=10, verbose_eval=True)
print("模型训练完成。\n")

# 评估模型
print("开始评估模型...")
y_pred_prob = bst.predict(dtest)
y_pred = (y_pred_prob > 0.5).astype(int)
accuracy = accuracy_score(y_test, y_pred)
auc = roc_auc_score(y_test, y_pred_prob)
conf_matrix = confusion_matrix(y_test, y_pred)
report = classification_report(y_test, y_pred)

print(f'Accuracy: {accuracy}')
print(f'AUC: {auc}')
print('Confusion Matrix:')
print(conf_matrix)
print('Classification Report:')
print(report)
print("模型评估完成。\n")

# 应用模型进行栅格分类
print("开始进行栅格分类...")
with rasterio.open(TIF_PATH) as src:
    profile = src.profile.copy()
    profile.update(
        dtype=rasterio.uint8,
        count=1,
        compress='lzw'
    )

    # 计算窗口总数用于进度指示
    windows = list(src.block_windows(1))
    total_windows = len(windows)

    with rasterio.open('classified.tif', 'w', **profile) as dst:
        for ji, window in tqdm(windows, total=total_windows, desc="栅格分类进度"):
            data = src.read(window=window)
            # data.shape = (bands, height, width)
            bands, height, width = data.shape
            data = data.reshape(bands, -1).transpose()  # shape: (num_pixels, bands)
            
            # 创建 DataFrame 并赋予特征名称
            df = pd.DataFrame(data, columns=feature_names)
            
            # 创建 DMatrix
            dmatrix = xgb.DMatrix(df, feature_names=feature_names)
            
            # 预测
            predictions = bst.predict(dmatrix)
            predictions = (predictions > 0.5).astype(np.uint8)
            
            # 重塑为原窗口形状
            out_image = predictions.reshape(height, width)
            
            # 写入输出栅格
            dst.write(out_image, 1, window=window)
print("栅格分类完成。")

训练完成后,就开始分类了,就出结果了:

自此,从数据下载到分类处理完毕。

样本数据多的话,也可以考虑用CNN,但分类速度比不上xgboot。

参考:

You N , Dong J , Huang J ,et al.The 10-m crop type maps in Northeast China during 2017–2019[J].Scientific Data, 2021, 8(1).DOI:10.1038/s41597-021-00827-9.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/938959.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Spring Boot 集成 MyBatis 全面讲解

Spring Boot 集成 MyBatis 全面讲解 MyBatis 是一款优秀的持久层框架&#xff0c;与 Spring Boot 集成后可以大大简化开发流程。本文将全面讲解如何在 Spring Boot 中集成 MyBatis&#xff0c;包括环境配置、基础操作、高级功能和最佳实践。 一、MyBatis 简介 1. SqlSession …

Web 毕设篇-适合小白、初级入门练手的 Spring Boot Web 毕业设计项目:电影院后台管理系统(前后端源码 + 数据库 sql 脚本)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 项目介绍 2.0 用户登录功能 3.0 用户管理功能 4.0 影院管理功能 5.0 电影管理功能 6.0 影厅管理功能 7.0 电影排片管理功能 8.0 用户评论管理功能 9.0 用户购票功…

【字符串匹配算法——BF算法】

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” 文章目录 BF算法介绍及过程演示代码实现过程下节预告KMP算法利用next数组存储子串中j回退的位置&#xff08;…

单幅图像合成 360° 3D 场景的新方法:PanoDreamer,可同时生成全景图像和相应的深度信息。

论文介绍了一种从单幅图像合成 360 3D 场景的新方法。该方法以连贯的方式生成全景图及其相应的深度&#xff0c;解决了现有最先进方法&#xff08;如 LucidDreamer 和 WonderJourney 的局限性。这些方法按照生成轨迹依次添加细节&#xff0c;通常在循环回输入图像时导致可见的接…

【蓝桥杯】46195.水仙花数

水仙花数 问题描述 打印所有100至999之间的水仙花数。所谓水仙花数是指满足其各位数字立方和为该数字本身的整数&#xff0c;例如 153135333。 样例输入 无 样例输出 153 370 371 407解题思路 遍历100到999之间的所有整数。对每个整数&#xff0c;计算其各位数字的立方和…

#思科模拟器通过服务配置保障无线网络安全Radius

演示拓扑图&#xff1a; 搭建拓扑时要注意&#xff1a; 只能连接它的Ethernet接口&#xff0c;不然会不通 MAC地址绑定 要求 &#xff1a;通过配置MAC地址过滤禁止非内部员工连接WiFi 打开无线路由器GUI界面&#xff0c;点开下图页面&#xff0c;配置路由器无线网络MAC地址过…

cpolar使用步骤

功能&#xff1a;内网穿透 下载地址&#xff1a;cpolar - secure introspectable tunnels to localhost 1 找到安装目录 2 进入命令行 目录处输入 cmd 3 验证 authtoken 不同用户 验证码不同。 注册后可以使用 cpolar.exe authtoken MzBlNzMwODktZjA3Yi00ZjJlLWJiMzQtNWU…

【排序算法】——插入排序

目录 前言 简介 基本思想 1.直接插入排序 2.希尔排序 代码实现 1.直接插入排序 2.希尔排序 总结 1.时空复杂度 2.稳定性 尾声 前言 排序(Sorting) 是计算机程序设计中的一种重要操作&#xff0c;它的功能是将一个数据元素&#xff08;或记录&#xff09;的任意序列&…

MySQL学习之DDL操作

目录 数据库的操作 创建 查看 选择 删除 修改 数据类型 表的创建 表的修改 表的约束 主键 PRIMARY KEY 唯一性约束 UNIQUE 非空约束 NOT NULL 外键约束 约束小结 索引 索引分类 常规索引 主键索引 唯一索引 外键索引 优点 缺点 视图 创建 删除 修改…

四、网络层:数据平面,《计算机网络(自顶向下方法 第7版,James F.Kurose,Keith W.Ross)》

文章目录 零、导论0.1 网络层服务0.2 网络层的关键功能0.3 网络层&#xff1a;数据平面、控制平面0.4 传统方式&#xff1a;每一路由器&#xff08;Per-router&#xff09;控制平面0.5 传统方式&#xff1a;路由和转发的相互作用0.6 SDN方式&#xff1a;逻辑集中的控制平面0.7 …

Java每日一题(1)

给定n个数a1,a2,...an,求它们两两相乘再相加的和。 即&#xff1a;Sa1*a2a1*a3...a1*ana2*a3...an-2*an-1an-2*anan-1*an 第一行输入的包含一个整数n。 第二行输入包含n个整数a1,a2,...an。 样例输入 4 1 3 6 9 样例输出 117 答案 import java.util.Scanner; // 1:无…

(2024.12自用存档)Ubuntu20.04——DynSLAM运行命令

前面忘记记录了&#xff0c;大概记一下后面 看了很多大佬的文章&#xff08;感谢&#xff01;&#xff09;&#xff0c;包括但不限于以下参考文章&#xff1a; Ubuntu16.04编译dynslam总结-CSDN博客 ubuntu14.04 CUDA8.0 DynSLAM编译与运行-CSDN博客 【视觉SLAM十四讲】Pa…

【阅读笔记】Android AMS forcestop停止应用

根据这篇文章作的笔记 基于Android 12的force-stop流程分析_android forcestop-CSDN博客 在AMS中&#xff0c;停止指定的应用是一个常用的功能&#xff0c;在代码里可以看到 Override 6806 public void forceStopPackage(final String packageName, int userId) { 6807 …

uniapp连接蓝牙操作(蓝牙设备地锁)

介绍&#xff1a; 本文采用uni-app框架来创建一个简单的用户界面&#xff0c;用于搜索、连接和发送命令给蓝牙设备。 1.打开蓝牙适配器 function openBluetooth() {uni.openBluetoothAdapter({success() {uni.offBluetoothDeviceFound();// 监听新设备发现事件uni.onBlueto…

《拉依达的嵌入式\驱动面试宝典》—前言目录篇

《拉依达的嵌入式\驱动面试宝典》—前言&目录篇 你好&#xff0c;我是拉依达。 感谢所有阅读关注我的同学支持&#xff0c;目前博客累计阅读 27w&#xff0c;关注1.5w人。其中博客《最全Linux驱动开发全流程详细解析&#xff08;持续更新&#xff09;-CSDN博客》已经是 Lin…

【博弈模型】古诺模型、stackelberg博弈模型、伯特兰德模型、价格领导模型

博弈模型 1、古诺模型&#xff08;cournot&#xff09;&#xff08;1&#xff09;假设&#xff08;2&#xff09;行为分析&#xff08;3&#xff09;经济后果&#xff08;4&#xff09;例题 2、stackelberg博弈模型&#xff08;产量领导模型&#xff09;&#xff08;1&#xff…

如何利用Python爬虫获得1688商品详情

在这个信息爆炸的时代&#xff0c;数据就像是一块块美味的奶酪&#xff0c;而爬虫就是我们手中的瑞士军刀。今天&#xff0c;我要带你一起潜入1688这个巨大的奶酪洞穴&#xff0c;用Python爬虫捞起那些香气四溢的商品详情。别担心&#xff0c;我们的工具箱里有各种各样的工具&a…

blender 制作莫比乌斯带

创建 Curve -> Cycle 在 Edit 模式下&#xff0c;选择&#xff1a; 选中两个点&#xff0c;按 delete 删除 Segment 如下选中&#xff1a; 选中最上面的点&#xff0c;然后按 E 将它拖到右边的点上。 按 R 旋转 90 度。 依次调整参数&#xff1a; 回到 Object 模式下&#x…

《云原生安全攻防》-- K8s安全框架:认证、鉴权与准入控制

从本节课程开始&#xff0c;我们将来介绍K8s安全框架&#xff0c;这是保障K8s集群安全比较关键的安全机制。接下来&#xff0c;让我们一起来探索K8s安全框架的运行机制。 在这个课程中&#xff0c;我们将学习以下内容&#xff1a; K8s安全框架&#xff1a;由认证、鉴权和准入控…

研华运动控制卡 (如PCI1245)单轴编辑路

问题描述: 单轴如何编辑路径&#xff1f; n 问题分析及处理办法– 步骤 在utility软件中&#xff0c;编辑路径和运行路径只能在多轴运动这个界面&#xff0c;而且&#xff0c;使用函数来加载路径Acm_GpLoadPath&#xff0c;也是需要多个轴 ​ 如果只运行一个轴&#xff0c;需…