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

OPC UA + ABP vNext 企业级实战:高可用数据采集框架指南

🚀📊 OPC UA + ABP vNext 企业级实战:高可用数据采集框架指南 🚀


📑 目录

  • 🚀📊 OPC UA + ABP vNext 企业级实战:高可用数据采集框架指南 🚀
    • 一、前言 🎯
    • 二、系统架构 🏗️
    • 三、配置与校验 🔧
      • `appsettings.json`
      • 校验示例
      • 📜 配置校验流程
    • 四、OpcUaService 增强:线程安全 + Polly 重试 🔐🔄
      • 🔒 OPC UA 会话重连流程
    • 五、数据采集作业:异常隔离 + 告警上报 🚨
      • 📥 数据采集 & 缓存流程
    • 六、模块注册补全 🎛️
      • `OpcUaHealthCheck` 示例
    • 七、证书 & Kubernetes 部署 ☸️
      • 1. 生成并信任证书(Linux)
      • 📦 证书生成与挂载流程
      • 2. Kubernetes Secret 示例
      • 3. Pod 挂载
      • 4. Liveness/Readiness Probes
      • ☸️ K8s 探针配置流程
    • 八、日志采集与可观测性 🔍
    • 九、结语 🎉


一、前言 🎯

本文基于企业级生产环境需求,全面重构 OPC UAABP vNext 集成框架,涵盖:

  • 配置集中化 & 校验
  • 安全封装 & Polly 重试 🔄
  • 原生作业调度 (BackgroundWorkerBase) ⏱️
  • 分布式缓存 & 更新 幂等 🔒
  • 健康检查 & 告警事件 🚨
  • OpenTelemetry 跟踪 🕵️
  • 证书管理 & Kubernetes 部署 ☸️

实现「即克隆、即运行、即监控」的工业数据平台!✨


二、系统架构 🏗️

🔍 读数据
OPC UA Server
OpcUaService
OpcUaCollectorWorker
ApplicationService
EF Core / PostgreSQL
Redis 缓存 IDistributedCache
UI Layer

三、配置与校验 🔧

appsettings.json

"OpcUa": {"Endpoint": "opc.tcp://localhost:4840","NodeIds": ["ns=2;s=Device1", "ns=2;s=Device2"],"CacheDurationSeconds": 120,"AutoAcceptUntrusted": false,"Certificate": {"StorePath": "/etc/opcua/certs","SubjectName": "CN=OpcAbpIntegration"}
}

校验示例

public override void OnApplicationInitialization(ApplicationInitializationContext context)
{var config = context.ServiceProvider.GetRequiredService<IConfiguration>();var section = config.GetSection("OpcUa");if (!section.Exists())throw new ConfigurationErrorsException("🔴 OpcUa 配置节缺失!");var endpoint = section["Endpoint"];if (string.IsNullOrWhiteSpace(endpoint))throw new ConfigurationErrorsException("🔴 OpcUa.Endpoint 不能为空!");var nodeIds = section.GetSection("NodeIds").Get<string[]>();if (nodeIds == null || nodeIds.Length == 0)throw new ConfigurationErrorsException("🔴 OpcUa.NodeIds 至少配置一个!");
}

📜 配置校验流程

读取 appsettings.json OpcUa 节
节是否存在?
抛出 “配置节缺失” 异常
读取 Endpoint 与 NodeIds
Endpoint 非空?
抛出 “Endpoint 不能为空” 异常
NodeIds 数组长度 > 0?
抛出 “NodeIds 至少配置一个” 异常
配置校验通过 🎉

四、OpcUaService 增强:线程安全 + Polly 重试 🔐🔄

public class OpcUaService : IOpcUaService, ISingletonDependency
{private readonly IOptions<OpcUaOptions> _options;private readonly ILogger<OpcUaService> _logger;private Session? _session;private readonly SemaphoreSlim _lock = new(1, 1);public OpcUaService(IOptions<OpcUaOptions> options, ILogger<OpcUaService> logger){_options = options;_logger = logger;}public async Task<Session> EnsureSessionAsync(){await _lock.WaitAsync();try{if (_session?.Connected == true) return _session;var config = new ApplicationConfiguration{ApplicationName         = "OpcAbpIntegration",ApplicationUri          = "urn:abp:opcua",ApplicationType         = ApplicationType.Client,SecurityConfiguration   = new SecurityConfiguration{ApplicationCertificate = new CertificateIdentifier{StoreType   = "Directory",StorePath   = _options.Value.Certificate.StorePath,SubjectName = _options.Value.Certificate.SubjectName},AutoAcceptUntrustedCertificates = _options.Value.AutoAcceptUntrusted},ClientConfiguration    = new ClientConfiguration { DefaultSessionTimeout = 60000 },TransportQuotas        = new TransportQuotas { OperationTimeout = 15000, MaxMessageSize = 4_194_304 }};await config.Validate(ApplicationType.Client);var endpointDesc = CoreClientUtils.SelectEndpoint(_options.Value.Endpoint, false);var endpoint     = new ConfiguredEndpoint(null, endpointDesc, EndpointConfiguration.Create(config));_session = await Session.Create(config, endpoint, false, "OPC UA", 60000, new UserIdentity(), null);_logger.LogInformation("✅ OPC UA 会话已连接:{Endpoint}", _options.Value.Endpoint);return _session;}finally{_lock.Release();}}public async Task<string> ReadNodeAsync(string nodeId){return await Policy.Handle<Exception>().WaitAndRetryAsync(retryCount: 3,sleepDurationProvider: attempt => TimeSpan.FromSeconds(1 << attempt),onRetry: (ex, delay) => _logger.LogWarning(ex, "重试读取节点 {NodeId}", nodeId)).ExecuteAsync(async () =>{var session = await EnsureSessionAsync();_logger.LogDebug("📡 读取节点 {NodeId}", nodeId);var node    = new ReadValueId { NodeId = new NodeId(nodeId), AttributeId = Attributes.Value };var results = new DataValueCollection();await session.Read(null, 0, TimestampsToReturn.Both, new[] { node }, out results, out _);return results.FirstOrDefault()?.Value?.ToString() ?? "";});}
}

🔒 OPC UA 会话重连流程

调用 ReadNodeAsync(nodeId)
Policy 重试入口
确保获取 Session:EnsureSessionAsync
Session 已连接?
直接返回同一 Session
创建 ApplicationConfiguration
SelectEndpoint & ConfiguredEndpoint
Session.Create 建立会话
返回新会话
执行 Read 操作
返回节点值 或 抛出异常

五、数据采集作业:异常隔离 + 告警上报 🚨

public class OpcUaCollectorWorker : BackgroundWorkerBase
{private readonly IOpcUaService _opcUa;private readonly IDistributedCache<MyDeviceCacheItem> _cache;private readonly IMyDeviceRepository _repository;private readonly IDistributedEventBus _eventBus;private readonly IOptions<OpcUaOptions> _options;private readonly ILogger<OpcUaCollectorWorker> _logger;public override float DelayFactor => 1; // 可配置执行间隔public OpcUaCollectorWorker(IOpcUaService opcUa,IDistributedCache<MyDeviceCacheItem> cache,IMyDeviceRepository repository,IDistributedEventBus eventBus,IOptions<OpcUaOptions> options,ILogger<OpcUaCollectorWorker> logger){_opcUa      = opcUa;_cache      = cache;_repository = repository;_eventBus   = eventBus;_options    = options;_logger     = logger;}[UnitOfWork]public override async Task ExecuteAsync(CancellationToken stoppingToken){var failedNodes = new List<string>();var sw          = Stopwatch.StartNew();foreach (var nodeId in _options.Value.NodeIds){try{var value = await _opcUa.ReadNodeAsync(nodeId);await _repository.InsertOrUpdateAsync(new MyDeviceData(nodeId, value),existing => existing.Update(value));await _cache.SetAsync(nodeId,new MyDeviceCacheItem(value),new DistributedCacheEntryOptions {AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(_options.Value.CacheDurationSeconds)});_logger.LogInformation("📥 节点 {NodeId} 数据已更新", nodeId);}catch (Exception ex){_logger.LogError(ex, "❌ 读取节点 {NodeId} 失败", nodeId);failedNodes.Add(nodeId);}}sw.Stop();_logger.LogInformation("🔄 本次采集用时 {Elapsed} ms", sw.ElapsedMilliseconds);if (failedNodes.Count > 0){await _eventBus.PublishAsync(new NodeReadFailedEvent(failedNodes));_logger.LogWarning("⚠️ 发布读取失败告警,节点:{Nodes}", string.Join(',', failedNodes));}}
}

📥 数据采集 & 缓存流程

Worker 启动 ExecuteAsync
遍历 OpcUaOptions.NodeIds
调用 ReadNodeAsync(nodeId)
读取成功?
InsertOrUpdate 到数据库
SetAsync 到 Redis 缓存
记录失败节点到 failedNodes
继续下一个 nodeId
循环结束?
failedNodes 非空?
Publish NodeReadFailedEvent
完成,结束本次作业

六、模块注册补全 🎛️

[DependsOn(typeof(AbpEntityFrameworkCoreModule),typeof(AbpDistributedCacheModule),typeof(AbpBackgroundWorkersModule),typeof(AbpAutofacModule))]
public class OpcUaModule : AbpModule
{public override void ConfigureServices(ServiceConfigurationContext context){// 1️⃣ 配置绑定与校验(见 OnApplicationInitialization)context.Services.Configure<OpcUaOptions>(context.Services.GetConfiguration().GetSection("OpcUa"));// 2️⃣ 核心服务注册context.Services.AddSingleton<IOpcUaService, OpcUaService>();// 3️⃣ EF Core & 仓储context.Services.AddAbpDbContext<MyDbContext>(opts =>{opts.AddDefaultRepositories(includeAllEntities: true);});// 4️⃣ Background Workercontext.Services.AddBackgroundWorker<OpcUaCollectorWorker>();// 5️⃣ 健康检查context.Services.AddHealthChecks().AddCheck<OpcUaHealthCheck>("opcua").AddNpgSql("YourPostgreConnection").AddRedis("localhost");// 6️⃣ OpenTelemetry 跟踪context.Services.AddOpenTelemetryTracing(builder =>{builder.AddAspNetCoreInstrumentation().AddHttpClientInstrumentation().AddEntityFrameworkCoreInstrumentation().AddSource("OpcUaService").AddJaegerExporter();});}
}

OpcUaHealthCheck 示例

public class OpcUaHealthCheck : IHealthCheck
{private readonly IOpcUaService _opcUa;public OpcUaHealthCheck(IOpcUaService opcUa) => _opcUa = opcUa;public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken token = default){try{await _opcUa.EnsureSessionAsync();return HealthCheckResult.Healthy("OPC UA session is healthy");}catch (Exception ex){return HealthCheckResult.Unhealthy("OPC UA session failed", ex);}}
}

七、证书 & Kubernetes 部署 ☸️

1. 生成并信任证书(Linux)

openssl genrsa -out client.key 2048
openssl req -new -x509 -key client.key -out client.crt -days 365 \-subj "/CN=OpcAbpIntegration"
mkdir -p /etc/opcua/certs/trusted
cp client.crt /etc/opcua/certs/trusted/

📦 证书生成与挂载流程

Kubernetes 部署
本地生成证书
Pod spec 中挂载 volume
创建 Secret opcua-certs
容器启动时可读 /etc/opcua/certs
openssl req -new -x509 client.crt
openssl genrsa client.key
mkdir /etc/opcua/certs/trusted
cp client.crt 到 trusted 目录

2. Kubernetes Secret 示例

apiVersion: v1
kind: Secret
metadata:name: opcua-certsnamespace: your-ns
stringData:client.crt: |-----BEGIN CERTIFICATE-----...base64...-----END CERTIFICATE-----

3. Pod 挂载

volumeMounts:
- name: opcua-certsmountPath: /etc/opcua/certs
volumes:
- name: opcua-certssecret:secretName: opcua-certs

4. Liveness/Readiness Probes

readinessProbe:httpGet:path: /health/readyport: 5000initialDelaySeconds: 10periodSeconds: 30livenessProbe:httpGet:path: /health/liveport: 5000initialDelaySeconds: 30periodSeconds: 60

☸️ K8s 探针配置流程

容器启动
InitContainer 挂载证书
主容器启动 ABP 应用
应用暴露 /health/ready 与 /health/live
K8s ReadinessProbe 调用 /health/ready
K8s LivenessProbe 调用 /health/live
Ready?
开始接收流量
持续探测
Alive?
重启 Pod
继续运行

八、日志采集与可观测性 🔍

  • 推荐安装 NuGet 包:
    • OpenTelemetry.Extensions.Hosting
    • OpenTelemetry.Instrumentation.Http, AspNetCore, EntityFrameworkCore
  • 日志平台:SeqELKJaeger
  • ABP 自带日志面板可实时查看采集结果

九、结语 🎉

此版本已实现企业级「高可用、可复现、可维护」规范,覆盖从 证书配置作业调度缓存优化健康检查可观测 的全链路实践。

📦 推荐 将此框架部署于 IoT EdgeKubernetes,并结合 CI/CD自动化证书脚本,打造工业物联网的实时采集+可视化体系!


相关文章:

OPC UA + ABP vNext 企业级实战:高可用数据采集框架指南

&#x1f680;&#x1f4ca; OPC UA ABP vNext 企业级实战&#xff1a;高可用数据采集框架指南 &#x1f680; &#x1f4d1; 目录 &#x1f680;&#x1f4ca; OPC UA ABP vNext 企业级实战&#xff1a;高可用数据采集框架指南 &#x1f680;一、前言 &#x1f3af;二、系统…...

MySQL库级管理:数据库管理与存储引擎剖析

引言 各位数据库爱好者们好&#xff01;今天我们要深入探讨MySQL数据库的基本操作&#xff0c;这是每位开发者必须掌握的"内功心法" &#x1f4aa;。无论你是刚接触MySQL的小白&#xff0c;还是需要复习基础的老手&#xff0c;这篇教程都将带你系统学习数据库的核心…...

LeetCode 2094.找出 3 位偶数:遍历3位偶数

【LetMeFly】2094.找出 3 位偶数&#xff1a;遍历3位偶数 力扣题目链接&#xff1a;https://leetcode.cn/problems/finding-3-digit-even-numbers/ 给你一个整数数组 digits &#xff0c;其中每个元素是一个数字&#xff08;0 - 9&#xff09;。数组中可能存在重复元素。 你…...

机器学习-计量经济学

机器学习 不要事前决定变量关系&#xff0c;关键是谁也不知道啊&#xff0c;机器学习学习的模型&#xff08;那也不是真实的关系啊&#xff09; 这就是自然学科的好处&#xff1a;只要不断的优化这个未知的东西&#xff08;函数&#xff09;&#xff0c;然后在数据上&#xff…...

工具篇-扣子空间MCP,一键做游戏,一键成曲

一、登陆扣子空间 地址如下&#xff1a; 扣子空间 打开&#xff0c;然后登陆扣子 登陆之后快速开始&#xff1a; 二、生成游戏 小试牛刀&#xff0c;我们让它做一个打地鼠的游戏&#xff1a; 已经开始设计制作&#xff1a; 制作完成&#xff1a; 三、制作音乐 新…...

5.6 - 5.9 MySQL

数据库&#xff1a;存储和管理数据的仓库DB。 数据库管理系统&#xff1a;操纵和管理数据库的大型软件DBMS。 关系型数据库 一个数据库内可以创建多张表&#xff0c;在一个表内能存放多个数据。 SQL语句&#xff1a; DDL&#xff1a; 存储字符串用varchar。&#xff08;类似于…...

C# WinForm 如何高效地将大量数据从 CSV 文件导入 DataGridView

如果你有非常多的csv文件&#xff0c;每个文件包含N多行与M多列&#xff0c;如&#xff1a;18000 行和 27 列。现在&#xff0c;想制作一个 Windows 窗体应用程序&#xff0c;导入它们并在 datagridview 中显示&#xff0c;然后进行一些数学运算。可是&#xff0c;发现数据导入…...

【redis】redis常见数据结构及其底层,redis单线程读写效率高于多线程的理解,

redis常用数据结构及底层 string字符串、list链表、set无序集合、zset有序集合、hash哈希 1.string 底层结构是SDS简单动态字符串 struct sdshdr {int len; // 已用长度&#xff08;字符串实际长度&#xff09;int free; // 剩余可用空间char buf[]; // 数组&#…...

2025年5月AI科技领域周报(5.5-5.11):AGI研究进入关键验证期 具身智能开启物理世界交互新范式

2025年5月AI科技领域周报&#xff08;5.5-5.11&#xff09;&#xff1a;AGI研究进入关键验证期 具身智能开启物理世界交互新范式 一、本周热点回顾1. OpenAI发布GPT-5多模态大模型 突破通用智能关键阈值2. 特斯拉Optimus机器人量产版发布 具身智能进入工业场景3. 百度文心ERNIE…...

SQLPub:一个提供AI助手的免费MySQL数据库服务

给大家介绍一个免费的 MySQL 在线数据库环境&#xff1a;SQLPub。它提供了最新版本的 MySQL 服务器测试服务&#xff0c;可以方便开发者和测试人员验证数据库功能&#xff0c;也可以用于学习 MySQL。 免费申请 在浏览器中输入以下网址&#xff1a; https://sqlpub.com/ SQLP…...

URP相机如何将场景渲染定帧模糊绘制

1&#xff09;URP相机如何将场景渲染定帧模糊绘制 2&#xff09;为什么Virtual Machine会随着游戏时间变大 3&#xff09;出海项目&#xff0c;打包时需要勾选ARMv7吗 4&#xff09;Unity是手动还是自动调用GC.Collect 这是第431篇UWA技术知识分享的推送&#xff0c;精选了UWA社…...

WeakAuras Lua Script ICC (BarneyICC)

WeakAuras Lua Script ICC &#xff08;BarneyICC&#xff09; https://wago.io/BarneyICC/69 全量英文字符串&#xff1a; !WA:2!S33c4TXX5bQv0kobjnnMowYw2YAnDKmPnjnb4ljzl7sqcscl(YaG6HvCbxaSG7AcU76Dxis6uLlHNBIAtBtRCVM00Rnj8Y1M426ZH9XDxstsRDR)UMVCTt0DTzVhTjNASIDAU…...

为什么 mac os .bashrc 没有自动加载?

原因说明 在macOS中&#xff0c;默认情况下&#xff0c;终端使用的是Bash或Zsh作为shell。对于较新版本的macOS&#xff08;从Catalina开始&#xff09;&#xff0c;默认的shell已经切换为Zsh。因此&#xff0c;如果你正在使用Zsh&#xff0c;.bashrc文件不会自动生效&#xf…...

FramePack - 开源 AI 视频生成工具

&#x1f3ac; 项目简介 由开发者 lllyasviel 创建的一个轻量级动画帧处理工具库&#xff0c;专门用于游戏开发、动画制作和视频处理中的帧序列打包与管理。该项目采用高效的算法实现&#xff0c;能够显著提升动画资源的处理效率。 此 AI 视频生成项目&#xff0c;旨在通过低显…...

断点续传使用场景,完整前后端实现示例,包括上传,下载,验证

断点续传在多个场景中非常有用&#xff0c;包括但不限于大文件上传、跨国或跨区域文件传输、移动设备文件传输、备份和同步以及软件更新等。接下来&#xff0c;我将为你提供一个基于Java的后端实现示例&#xff0c;结合前端逻辑来完成整个断点续传的功能&#xff0c;包括上传、…...

【行为型之迭代器模式】游戏开发实战——Unity高效集合遍历与场景管理的架构精髓

文章目录 &#x1f504; 迭代器模式&#xff08;Iterator Pattern&#xff09;深度解析一、模式本质与核心价值二、经典UML结构三、Unity实战代码&#xff08;背包系统遍历&#xff09;1. 定义迭代器与聚合接口2. 实现具体聚合类&#xff08;背包物品集合&#xff09;3. 实现具…...

Vuetify框架使用(一)之v-snackbar 组件封装及全局使用

说明&#xff1a;v-snackbar 组件适用于统一管理消息提示框(操作反馈的提示) 看效果&#xff1a; 1、在状态管理中创建文件&#xff0c;统一管理 // stores/snackbar.js /*** 统一管理消息提示框(操作反馈的提示)*/import { defineStore } from pinia; // 消息类型 export co…...

FPGA: UltraScale+ bitslip实现(方案+代码)

收获 一晃五年~ 五年前那个夏夜&#xff0c;我对着泛蓝的屏幕敲下《给十年后的自己》&#xff0c;在2020年的疫情迷雾中编织着对未来的想象。此刻回望&#xff0c;第四届集创赛的参赛编号仍清晰如昨&#xff0c;而那个在家熬夜焊电路板的"不眠者"&#xff0c;现在…...

【SpeechLMs】语音大型语言模型综述《A Survey on Speech Large Language Models》

摘要 大型语言模型 (LLM) 表现出强大的上下文理解能力和显著的多任务性能。 因此&#xff0c;研究人员一直在寻求将 LLM 整合到更广泛的语音语言理解 (SLU) 领域。 与传统方法不同&#xff0c;传统方法是将 LLM 级联以处理自动语音识别 (ASR) 生成的文本&#xff0c;而新方法则…...

C# 实现雪花算法(Snowflake Algorithm)详解与应用

在现代分布式系统中&#xff0c;生成全局唯一的标识符&#xff08;ID&#xff09;是一个非常重要的问题。随着微服务架构和分布式系统的普及&#xff0c;传统的单机数据库生成 ID 的方式已无法满足高并发和高可用的需求。为了解决这个问题&#xff0c;Twitter 提出了 雪花算法&…...

吴恩达机器学习笔记:特征与多项式回归

1.特征和多项式回归 如房价预测问题&#xff0c; ℎθ (x) θ0 θ1 frontage θ2 deptℎ x1 frontage&#xff08;临街宽度&#xff09;&#xff0c;x2 deptℎ&#xff08;纵向深度&#xff09;&#xff0c;x frontage ∗ deptℎ area &#xff08;面积&#xff09;…...

Flutter 与HarmonyOS Next 混合渲染开发实践:以 fluttertpc_scan 三方库为例

一、背景与价值 在跨平台开发中&#xff0c;Flutter 以其高效的 UI 构建能力著称&#xff0c;而鸿蒙 Next&#xff08;OpenHarmony&#xff09;则提供了深度系统集成的原生能力。将两者结合&#xff0c;可实现 UI 跨平台 原生功能深度融合 的混合渲染模式。本文以扫描库 flut…...

LangChain4j正式发布-简化将 LLM 集成到 Java 应用程序过程

LangChain4j 的目标是简化将 LLM 集成到 Java 应用程序中的过程。 官网地址 源码地址 开源协议&#xff1a;Apache License 2.0 实现方法 统一 API&#xff1a;LLM 提供程序&#xff08;如 OpenAI 或 Google Vertex AI&#xff09;和嵌入&#xff08;矢量&#xff09;存储…...

【C++】汇编角度分析栈攻击

栈攻击 介绍原理示例代码汇编分析 介绍原理 核心原理是通过 缓冲区溢出&#xff08;Buffer Overflow&#xff09; 等漏洞&#xff0c;覆盖栈上的关键数据&#xff08;如返回地址、函数指针&#xff09;&#xff0c;从而改变程序执行流程&#xff1b; 在 C 中&#xff0c;每个…...

Vue 3 打开 el-dialog 时使 el-input 获取焦点

运行代码&#xff1a;https://andi.cn/page/622178.html 效果&#xff1a;...

C++23 views::repeat (P2474R2) 深入解析

文章目录 引言C20 Ranges库回顾什么是Rangesstd::views的作用 views::repeat概述基本概念原型定义工作原理应用场景初始化容器模拟测试数据 总结 引言 在C的发展历程中&#xff0c;每一个新版本都会带来一系列令人期待的新特性&#xff0c;这些特性不仅提升了语言的性能和表达…...

HTML5 定位详解:相对定位、绝对定位和固定定位

在HTML5和CSS中&#xff0c;定位(positioning)是控制元素在页面上位置的重要机制。主要有四种定位方式&#xff1a;静态定位(static)、相对定位(relative)、绝对定位(absolute)和固定定位(fixed)。下面我将详细讲解这三种非静态定位方式&#xff0c;并提供相应的源代码示例。 …...

OpenCv高阶(4.0)——案例:海报的透视变换

文章目录 前言一、工具函数模块1.1 图像显示函数1.2 保持宽高比的缩放函数1.3 坐标点排序函数 二、透视变换核心模块2.1 四点透视变换实现 三、主流程技术分解3.1 图像预处理3.2 轮廓检测流程3.3 最大轮廓处理 四、后处理技术4.1 透视变换4.2 形态学处理 五、完整代码总结 前言…...

光谱相机的图像预处理技术

光谱相机的图像预处理技术旨在消除噪声、增强有效信息&#xff0c;为后续分析提供高质量数据。 一、预处理流程与技术要点 ‌辐射校正‌ ‌辐射定标‌&#xff1a;将图像灰度值转换为绝对辐射亮度&#xff0c;常用反射率法、辐亮度法和辐照度法消除传感器响应差异&#xff0…...

CSS 溢出内容处理、可见性控制与盒类型设置深度解析

CSS溢出内容处理、可见性控制与盒类型设置深度解析 一、溢出内容处理&#xff08;Overflow&#xff09; 在网页设计中&#xff0c;内容超出容器边界是常见问题。CSS提供了overflow属性及其变体来控制这种情况。 1.1 溢出基本属性 核心属性&#xff1a; overflow: visible&…...