新闻 – 软件开发_上位机开发_物联网开发_APP开发_深圳软件定制开发公司 https://www.unicrom.cn 由你创 Sat, 28 Jun 2025 09:57:03 +0000 zh-CN hourly 1 https://wordpress.org/?v=6.5.5 https://unicrom.oss-cn-hangzhou.aliyuncs.com/2024/11/官网网址-1.png 新闻 – 软件开发_上位机开发_物联网开发_APP开发_深圳软件定制开发公司 https://www.unicrom.cn 32 32 下位机开发公司哪家好? https://www.unicrom.cn/8664.html Sat, 28 Jun 2025 09:55:54 +0000 https://www.unicrom.cn/?p=8664 在工业自动化、智能设备、物联网终端等领域的核心战场,下位机如同设备的“大脑”,其性能与稳定性直接决定了整个系统的成败。选择一家技术实力雄厚、经验丰富、服务到位的下位机开发公司,是您项目成功的关键一步。面对市场上众多的选择,哪家公司真正值得信赖?深圳市由你创科技有限公司,正是您寻找的专业下位机开发合作伙伴。

为什么选择由你创?

  • 技术为本,实力铸就: 由你创科技的核心团队拥有深厚的嵌入式系统开发功底,精通 ARM Cortex-M系列、ESP32、RISC-V等主流平台,熟练掌握 C/C++、RTOS(如FreeRTOS、RT-Thread)、Linux嵌入式开发,并对各类通信协议(如UART, SPI, I2C, CAN, Modbus, Ethernet, 4G/5G, LoRa, Bluetooth, WiFi)有深刻理解和丰富实践经验。我们紧跟技术前沿,确保为您打造性能卓越、稳定可靠的下位机系统。
  • 经验丰富,行业深耕: 我们不是纸上谈兵的理论派。由你创科技在工业控制、智能家居、医疗设备、物联网终端、消费电子等多个领域成功交付了众多复杂下位机项目。无论是高精度数据采集、实时运动控制、低功耗无线连接,还是复杂的多任务调度,我们都积累了宝贵的实战经验和成熟的解决方案库,能快速理解您的需求,规避潜在风险。
  • 需求导向,量身定制: 我们深知,没有放之四海皆准的方案。由你创科技坚持深度需求分析,从您的具体应用场景、功能要求、性能指标、成本预算出发,提供高度定制化的下位机软硬件开发服务。我们不仅仅是写代码,更是为您打造解决实际问题的核心引擎
  • 流程规范,质量保障: 从需求确认、方案设计、硬件选型(或设计)、软件编码、单元测试、系统集成测试到严格的老化测试、压力测试、EMC测试,我们遵循严谨的开发流程和质量控制体系。确保交付给您的产品性能稳定、运行可靠、符合行业标准,经得起市场和时间的考验。
  • 响应迅捷,服务无忧: 选择由你创科技,您获得的不仅仅是一个开发团队,更是一个值得信赖的技术后盾。我们提供及时高效的沟通快速响应的技术支持,无论是开发过程中的需求微调,还是产品上市后的维护升级,我们都将全力以赴,确保您的项目顺利推进和产品长期稳定运行。
  • 立足深圳,辐射全国: 深圳作为中国电子信息和硬件创新的高地,拥有最完善的产业链和人才资源。由你创科技扎根于此,能够快速整合优质资源,为您提供高性价比的解决方案,并高效服务全国客户。

成功案例印证实力

  • 某智能工厂产线控制系统: 为其开发了基于STM32和CAN总线的多节点实时PLC控制器,实现高精度运动控制和设备联动,提升生产效率25%,故障率显著降低。
  • 某高端医疗检测设备主控板: 完成了核心下位机软硬件开发,确保高速数据采集(精度达0.01%)多传感器同步以及符合严格医疗法规的稳定性和安全性,助力设备成功通过认证并上市。
  • 某物联网环境监测终端: 设计了超低功耗的LoRaWAN终端节点,集成多类传感器,实现野外环境数据的长周期、远距离、可靠传输,电池寿命超过3年。

选择由你创科技,您将获得:

  • 强大的专业技术团队支撑
  • 经过验证的成熟行业解决方案
  • 稳定可靠的高质量产品交付
  • 灵活高效的定制开发服务
  • 省心无忧的全流程项目管理和技术支持
  • 极具竞争力的成本效益

下位机开发流程

]]>
Unity+nodejs简单实现webscoket聊天室 https://www.unicrom.cn/8646.html Sat, 28 Jun 2025 09:37:06 +0000 https://www.unicrom.cn/?p=8646 一、前言

在这个互联网时代,几乎每个人都有微信或QQ这类实时通讯工具,现在很多网络游戏也带有实时聊天功能,那这样一个很常见的功能需要怎么实现呢?接下来我们就通过Unity+Nodejs来实现一个简单的聊天室吧!

二、首先我们用unity搭建一个简单聊天室的页面:

将左右两边的消息对象做成预制体方便后面创建;

接着我们来实现C#代码:

基础变量信息定义:

接收消息方法定义,nodejs会转发json字符串到客户端,客户端再对消息进行解析处理

接着,我们就可以开始编写连接websocket的代码,要记得和nodejs定义和通讯地址和端口,否则会连接不上哦!

写好连接代码和接收消息的方法,我们还缺少一个发送消息的方法:

将发送消息方法和发送按钮绑定

这样,客户端的代码我们就写好了,只需要将脚本挂载到场景上了

三、实现node代码

nodejs代码的实现也不复杂,nodejs引入ws,就可以实现webscoket连接了,代码实现如下:

接着就打开我们的cmd窗口,运行我们的nodejs脚本

如果你没有安装ws模块,就会遇到下面这个报错

我们需要用npm安装下ws模块

安装成功后就可以再次执行node server.js命令了

四、聊天室启动

将应用打包后,我们打开两个窗口,一个为昵称A同学,一个昵称为B同学,他们两个就可以进行对话了

在运行nodejs的cmd窗口我们也打印了客户端传过来的消息

一个unity+nodejs的聊天室就这么搞定了,除了可以通过webscoket实现聊天室实时通讯以外,我们也可以实现游戏的多人实时联机,但这比聊天室复杂多了,因为要考虑到游戏的环境信息同步,玩家操作同步,这就留着下次有机会再讲吧!

]]>
AI驱动的机器视觉技术 https://www.unicrom.cn/8642.html Sat, 28 Jun 2025 09:23:33 +0000 https://www.unicrom.cn/?p=8642  工业视觉算法,AI视觉模型这些都是老生常谈了,本期要讲的是升级版. ‌自进化检测系统.

   目前大多数据视觉检测算法,都是在某些固定场景,通过流水线或者多轴机械臂,来自动化检测目标状态,常见缺陷检测,或者目标计数等, 大多都能满足使用场景.

   随着越来越多的场景使用视觉自动化之后,有些场景对于固定的视觉算法,或者模型不能满足同场景下,目标环境多变的情况. 下发分享我们的一个实战案例.

   显微镜(3轴)自动识别玻片上的活体细胞,通过不通项目的玻片识别出活体细胞用于病变分析.  起初始项目给人类使用.  项目采用python+yolo算法+3轴PLC对玻片进行切片分析.

   随着客户的业务发展,客户将该产品应用到宠物上. 但是由于不同生物的情况不一样.导致识别率下降. 客户希望这个识别算法能自动升级.

   最终我们在这个基础上做出升级, 把一台算力好的电脑做为增量训练的服务器(云端的成本贵), 让所有显微镜,通过MQTT服务(云端)把数据传到本地服务器上.  由于在识别的时候,增加了把图像和识别目标信息打包成可用于增量训练的格式.   

   服务器每天把采集到的数据保存在本地,等训练结束之后. 立马开始新一轮增量训练.

   当训练出来的新模型文件,通过验证之后,识别率增加2%,就提示所有客户端,有新的更新.  用户 可以点击更新,或者稍后更新.  客户端(显微镜)通过下载新的模型文件,然后算法重新初始化,可以做在线热更新算法.  

‌   自进化检测系统,支持数据清洗与模型自训练,适应产线实时变化。这是一套低成本的实现方式.

]]>
如何选择一家靠谱的上位机软件开发服务商?  https://www.unicrom.cn/8450.html Wed, 16 Apr 2025 01:44:06 +0000 https://www.unicrom.cn/?p=8450 引言:上位机软件开发——企业智能化的核心引擎

在工业4.0与数字化转型的浪潮下,上位机软件作为连接设备、数据与用户的“核心引擎”,其开发质量直接影响企业的生产效率与竞争力。然而,面对技术门槛高、需求多变、交付周期不可控等难题,如何选择一家技术过硬、服务可靠的上位机开发服务商?

深圳市由你创科技有限公司全栈技术能力、行业深耕经验与准时交付承诺,成为众多企业做上位机开发的长期合作伙伴。本文从选择标准出发,深度解析由你创科技如何以技术赋能企业,助力智能化升级。

一、选择上位机开发服务商的五大核心标准

1. 技术实力:全栈开发与软硬协同能力

  • 编程语言与框架:精通C#(WPF/WinForms)、Python、C++(Qt)、Electron等主流技术栈,支持跨平台开发(Windows/Linux/Web/移动端)。
  • 硬件协议支持:深度适配Modbus、OPC UA、CAN、MQTT等工业与物联网协议,支持私有协议定制化开发。
  • 软硬件协同能力:与FPGA、嵌入式系统联合调试经验,确保数据链路低延迟、高可靠。

深圳由你创科技优势

  • 自研通信中间件:优化FPGA与上位机数据传输,延迟降低35%;
  • 微服务架构:支持高并发数据处理,实测10万+设备同时在线无压力。

2. 行业经验:垂直领域的场景化落地

  • 行业理解深度:服务商需熟悉目标行业标准(如医疗DICOM、工业ISA-95)与法规要求(FDA/ISO认证)。
  • 成功案例验证:需验证服务商是否具备复杂场景(如多设备协同、高精度控制)的落地能力。

由你创科技实践

  • 工业自动化:为某汽车制造厂开发MES监控系统,集成300+台设备,实现生产异常秒级报警;
  • 医疗设备:设计符合FDA认证的超声影像工作站,支持AI辅助诊断与DICOM云端传输。

3. 服务流程:标准化与透明化

  • 需求分析:提供交互式原型设计(Demo),确保需求精准对齐;
  • 项目管理:明确开发周期、测试节点(单元测试/压力测试)与交付物清单,支持客户全程参与。

由你创科技流程

  • 阶段一:需求调研与架构设计(5-7个工作日);
  • 阶段二:核心功能开发与多设备联调(6-10周);
  • 阶段三:用户培训与交付(1-2周),确保“零障碍”交接。

4. 交付准时性:从承诺到落地的保障

  • 敏捷开发模式:采用Scrum框架,分阶段交付可验证版本,降低项目延期风险;
  • 供应链支持:与硬件厂商深度合作,确保FPGA、传感器等外设调试资源充足。

由你创科技承诺

  • 合同签订后严格按里程碑交付,历史项目准时交付率超95%;
  • 2023年某智慧物流项目中,提前10天完成500台AGV调度系统上线。

5. 售后服务:长期价值守护

  • 代码可维护性:提供规范代码注释、API文档及Git版本管理;
  • 升级支持:支持功能扩展、跨平台迁移及第三方系统对接。

由你创科技服务保障

  • 交付后1年内免费维护,7×12小时技术响应;
  • 提供源码托管与知识产权保护,确保客户资产安全。

二、由你创科技的核心优势——技术+服务双驱动

1. 技术实力:软硬一体,性能卓越

  • 全栈开发能力:覆盖FPGA逻辑设计、上位机软件开发、云端数据平台集成,提供一站式解决方案。案例:为某半导体厂商开发“晶圆检测系统”,FPGA实现图像预处理,上位机集成AI缺陷分类,检测效率提升3倍。
  • 高性能架构设计:采用分布式微服务架构,支持10万+设备高并发接入,数据吞吐量达百万条/秒。

2. 行业深耕:技术复用与快速交付

  • 工业领域:开发SCADA系统、设备健康管理(PHM)平台,客户覆盖大金、镭晨、源控、利元亨等知名企业;
  • 新能源领域:设计BMS上位机软件,支持ISO 26262功能安全认证,客户量产交付超50万套。

3. 服务口碑:以客户为中心

  • 交付准时:2023年某智能工厂项目中,75天完成上位机系统开发,支持200台机器人协同作业;
  • 成本优化:“标准化模块+定制开发”模式,节省客户30%以上开发成本。

三、成功案例:由你创科技如何助力企业降本增效?

案例1:生物工艺罐体系统

  • 客户需求: 缓冲液配置、存储、分配 。
  • 解决方案:实现设备控制、设备状态监测、设备数据管理、配方管理、异常报警等功能。
  • 成果:质量保证的同时解决了工艺工程、电气工程和软件工程之间80%的协调工作。。

案例2:高端医疗影像处理平台

  • 客户痛点:原有系统数据处理慢,无法满足实时超声成像需求。
  • 由你创方案:FPGA端:部署实时滤波与图像增强算法;上位机端:开发DICOM标准接口,支持云端存储与AI诊断。
  • 成果:图像处理延时从50ms降至8ms,通过CFDA认证并批量投产。

四、合作流程:四步实现高效落地

  1. 需求沟通:提交功能清单、性能指标及硬件环境说明;
  2. 方案设计:5个工作日内获取技术方案与报价;
  3. 开发实施:敏捷开发模式,支持分阶段验收;
  4. 交付培训:提供操作手册、技术培训与售后支持。

结语:选择由你创科技,让智能升级更省心

选择一家靠谱的上位机开发服务商,既要看技术实力,也要看服务沉淀与交付保障。深圳市由你创科技有限公司“技术领先、服务贴心、交付准时”为核心竞争力,通过全栈开发能力行业化解决方案,为企业提供高效、可靠的数字化转型支持。

深圳市由你创科技有限公司是一家专业从事上位机开发的国家高新企业。技术团队有十年以上的项目研发经验。精通c++,c#,PYTHON,labview,java,matlab等多种冷门技术栈。业务覆盖生物医药、汽车电子、高端装备、机器人、材料化工、检验检测等前沿高科技行业。为行业领军企业、科研机构提供长期研发技术服务。做上位机开发,选由你创,研发服务的源头工厂。

#上位机开发# #上位机软件开发# #上位机开发哪家好# #靠谱的上位机开发服务商#

]]>
DeepSeek本地化部署 | AI应用开发  https://www.unicrom.cn/8077.html Tue, 18 Feb 2025 05:47:30 +0000 https://www.unicrom.cn/?p=8077 一、deepseek简介

在数字化转型的浪潮中,AI 技术已成为企业提升竞争力的关键。DeepSeek是一款开源AI平台,通过全栈开源架构实现技术共享。其核心能力涵盖多模态交互、垂直领域优化及轻量化部署,支持企业快速定制AI助手,应用于智能客服、数据分析等场景,提升业务流程效率,灵活适配金融、制造等行业需求,推动AI技术从实验室到产业的高效转化,为各行各业赋能。

二、参数选择

deepseek-R1不同参数体量的模型综合表现评分如下,综合官网的评分以及网上的实机评测, 大致性能分为三个档次, 1.5b, 7b, 8b 三个一档, 对硬件性能要求最低, 逻辑思维能力较差的基础类型, 14b和32b一档, 对硬件性能要求中等, 有一定的逻辑思维能力, 在处理例如 复杂代码, 文章生成, 重点总结等复杂任务中也有不错的表现, 70b, 671b一档, 对硬件要求较高, 需要专业的高性能显卡或者显卡集群来执行, 处理能力优秀, 70b参数的性能和GPT-4的综合表现相当, 671b参数的deepseek-R1综合表现接近 OpenAI-o1。

想要本地部署, 需要根据自己的设备的性能来部署, 以下是硬件和参数体量对应的大致关系, 不绝对准确, 仅供参考。

我这里用的是12G显存的RTX4080显卡+13thi9CPU +32GRAM主机, 选择的是14b的模型(根据实际使用经验, 显存是比较关键的硬件参数,其他硬件参数可以利用各种优化技术来适配, 但是尽量保证显存符合要求,不然严重影响本地部署的使用体验)。

三、部署流程

整体部署流程分为两部分:

后端: 根据本地设备的性能选择合适参数体量的DeepSeek-R1模型, 配置本地环境并下载模型数据,。

前端: 安装浏览器插件, 优化与Ai交互的UI界面, 并在必要的情况下搭建网页服务器, 确保局域网内成员可用。

 ①后端部署

Ollama是一个专为在本地环境中运行和定制大型语言模型而设计的工具。它提供了一个简单而高效的接口,用于创建、运行和管理这些模型,

Ollama支持多种操作系统,包括但不限于:

macOS:适用于所有现代版本的macOS。

Windows:支持Windows 10及更高版本。

Linux:支持多种Linux发行版,如Ubuntu、Fedora等。

Docker:通过Docker容器,Ollama可以在几乎任何支持Docker的环境中运行。

windows环境下下载ollamaSetup.exe, 执行安装程序安装完成后, 命令行中执行命令:

ollama --version

如果安装成功则会显示ollama的版本信息如下格式:

ollama几乎支持目前所有主流的开源大语言模型, 当然也包括如今大火的deepseek。

官网程序下载, 本地安装好ollama后, 执行命令:

ollama run deepseek-r1:14b

开始下载模型,过程如图所示:

下载完成后, 后端部署完成。

②前端部署

为提升交互体验, 可以安装浏览器插件, 以聊天框的UI界面和AI进行问答(默认只能从命令行聊天)

浏览器插件如下, google浏览器和edge浏览器均可以安装。

安装好之后前端界面如下:

以上就是本地部署deepseek的基本流程。

四、使用体验

输出速度:

deepseek-r1提供了 1.5b/7b/8b/14b/32b/70b/671b一共七个参数体量的模型, 参数体量也是最直观的可以看出模型性能的参数, 由上面的图片可以看到, 模型运行时, GPU和显存几乎都是满负荷的状态, 正常情况下, 14b的模型运行起来大约需要20G左右的显存, ollama利用多种优化技术使得其在显存更小的设备上也可以运行。

Ollama优化特性(包括但不限于):

  • 智能量化:支持2-8bit动态量化(精度损失<3%)
  • 内存压缩:采用Huffman编码压缩权重(压缩率40-60%)
  • 层卸载:自动将非活跃层转存至系统内存/SSD
  • 即时编译:针对不同GPU架构自动生成优化内核

如图所示, 左侧为设备型号及参数, 根据网上的资料显示, 14b体量的模型需要RTX3090以及至少14G的显存, 实际测试使用效果, RTX4080Laptop +12G显存也是可以正常运行14b的deepseek-r1的, (原理上ollama会利用多种技术来减低大语言模型的本地化部署对于硬件的性能要求, 常用的就是量化技术, 简单地说就是降低模型参数精度, 使得模型可以在不太影响输出效果的前提下, 一定程度的减少硬件参数要求) 实机使用, deepseek-r1 14b版本的输出速度在25~40token/s区间, 可以说输出速度非常快, 使用起来比较流畅。

输出效果

输出效果相对于输出速度, 更加主观, 我们召集了多位同事和本地部署的deepseek-r1:14b进行对话并反馈使用体验, 并做如下总结:

  • 相较于deepseek-r1 1.5b/7b/8b 版本, 14b版本明显在 对话/梳理/思考/总结 等方面有较大的提升, 逻辑完整性较高
  • 仅4080显卡的笔记本上就可以运行deepseek-r1:14b, 真正的实现了AI大语言模型走进生活各处
  • 可以给模型定向投喂数据, 增强模型对特定领域的问题的理解及回复精准度, 且不担心重要数据的泄露
  • 比如联网采集数据后, 由deepseek来分辨,过滤, 总结结论的能力上, 由于网络上信息量大, 无效信息占比较高, 14b体量的deepseek无法有效分辨来源及准确性, 满血版(671b参数)的deepseek-R1, 对关键数据的敏感程度, 以及总结有效数据的能力明显更强
  • 对于复杂问题的处理能力上, 稍显不足, 编程, 数学题, 以及特定专精领域的专业知识, 由于训练的资源有限, 回答的准确率有待提升.

(图示中deepseek-r1:14b虽然理解了题目, 但是给出的答案并不符合题目要求)

(图示中deepseek-r1:14b一度思维混乱, 反复否定自己之前的分析, 最后艰难的得出正确结果)

五、输出调优

给deepseek喂特定数据, 强化deepseek在需要的特定领域强化回答能力. 

①下载并加载量化工具(如nomic-embed-text),在命令行中执行以下命令:

ollama pull nomic-embed-text

在Page Assist的设置中,找到RAG设置,将文本嵌入模型设置为nomic-embed-text。

②添加知识到知识库:

回到Page Assist的首页,选择添加知识库。

通过Page Assist的界面,投喂数据(如pdf、文本等)到DeepSeek模型中(因为很多数据涉及到公司内部资料或者商业机密, 不适合在公开的AI 接口中直接投放, 本地部署deepseek, 可以规避这类问题)

通过知识库添加本地数据, 进而增强模型的回答和理解能力。

]]>
MCU驱动TF-card具体实现 https://www.unicrom.cn/7949.html Wed, 25 Dec 2024 02:36:11 +0000 https://www.unicrom.cn/?p=7949 嵌入式开发中, 经常会有主控板识别TF卡+读取数据+写入数据的 需求, 下面尽量用简介的描述详细说明一下, 相关驱动的实现细节,

实现TF-card的驱动需要配置的东西比较多, 基本流程就是:

①配置芯片的相关引脚  ②引用FatFS文件系统的三方库 ③根据具体需求配置相关引脚的参数 ④在main函数中调用相关功能的初始化函数初始化对应的硬件⑤实际跑通并测试相关功能(识别+读取+写入)⑥实际应用TF-card驱动功能

①配置相关引脚

这一步相对简单, 如图所示配置 数据线SDIO_D0~SDIO_D3,时钟线SDIO_CLK以及SDIO_CMD

这里我们配置了 四位的数据通讯(除非是引脚不够, 或者数据较少, 才会用一位的)

 ②引用FatFS文件系统的三方库 

这个是必要的文件系统配置, 想要和 TF-card交互数据, 卡必须有文件系统, 通常情况下使用FatFS

配置较多, 扇区大小, 编码方式等, 相关配置如图所示

③+④基本参数配置好之看一下初始化代码, SDIO的相关参数配置需要在使用前初始化, 代码比较清晰, 不多加赘述

void HAL_SD_MspInit(SD_HandleTypeDef* sdHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(sdHandle->Instance==SDIO)
  {
  /* USER CODE BEGIN SDIO_MspInit 0 */

  /* USER CODE END SDIO_MspInit 0 */
    /* SDIO clock enable */
    __HAL_RCC_SDIO_CLK_ENABLE();
  
    __HAL_RCC_GPIOC_CLK_ENABLE();
    __HAL_RCC_GPIOD_CLK_ENABLE();
    /**SDIO GPIO Configuration    
    PC8     ------> SDIO_D0
    PC9     ------> SDIO_D1
    PC10     ------> SDIO_D2
    PC11     ------> SDIO_D3
    PC12     ------> SDIO_CK
    PD2     ------> SDIO_CMD 
    */
    GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11 
                          |GPIO_PIN_12;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF12_SDIO;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_2;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF12_SDIO;
    HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);

  /* USER CODE BEGIN SDIO_MspInit 1 */

  /* USER CODE END SDIO_MspInit 1 */
  }
}

void HAL_SD_MspDeInit(SD_HandleTypeDef* sdHandle)
{

  if(sdHandle->Instance==SDIO)
  {
  /* USER CODE BEGIN SDIO_MspDeInit 0 */

  /* USER CODE END SDIO_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_SDIO_CLK_DISABLE();
  
    /**SDIO GPIO Configuration    
    PC8     ------> SDIO_D0
    PC9     ------> SDIO_D1
    PC10     ------> SDIO_D2
    PC11     ------> SDIO_D3
    PC12     ------> SDIO_CK
    PD2     ------> SDIO_CMD 
    */
    HAL_GPIO_DeInit(GPIOC, GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11 
                          |GPIO_PIN_12);

    HAL_GPIO_DeInit(GPIOD, GPIO_PIN_2);

  /* USER CODE BEGIN SDIO_MspDeInit 1 */

  /* USER CODE END SDIO_MspDeInit 1 */
  }
} 

⑤这里基本就配置完了, 验证一下功能, (验证功能仅适用一位数据的交互方式)基本逻辑就是通过串口打印一下函数执行的结果

void SD_Test(void)
{
  FRESULT res; /* FatFs function common result code */
  uint32_t byteswritten, bytesread; /* File write/read counts */
  uint8_t wtext[] = "This is STM32 working with FatFs.\n"; /* File write buffer */
  uint8_t rtext[100]; /* File read buffer */

  /* Register the file system object to the FatFs module */
  if (f_mount(&SDFatFS, (TCHAR const*)PATH, 0) != FR_OK)
  {
    printf("Failed to mount SD card\r\n");
    return;
  }

  /* Create/open a file, name is specified by Path with FileName */
  res = f_open(&SDFile, "STM32.TXT", FA_CREATE_ALWAYS | FA_WRITE);
  if (res != FR_OK)
  {
    printf("Failed to open/create file (error code: %d)\r\n", res);
    return;
  }

  /* Write data to the file */
  res = f_write(&SDFile, wtext, sizeof(wtext), (void *)&byteswritten);
  if ((byteswritten < sizeof(wtext)) || (res != FR_OK))
  {
    printf("Failed to write file (error code: %d)\r\n", res);
    return;
  }

  /* Close the file */
  res = f_close(&SDFile);
  if (res != FR_OK)
  {
    printf("Failed to close file (error code: %d)\r\n", res);
    return;
  }

  printf("File written successfully: %s\r\n", wtext);

  /* Open the file, name is specified by Path with FileName */
  res = f_open(&SDFile, "STM32.TXT", FA_READ);
  if (res != FR_OK)
  {
    printf("Failed to open file (error code: %d)\r\n", res);
    return;
  }

  /* Read string from the file */
  res = f_read(&SDFile, rtext, sizeof(rtext), (void *)&bytesread);
  if ((bytesread == 0) || (res != FR_OK))
  {
    printf("Failed to read file (error code: %d)\r\n", res);
    return;
  }

  /* Close the file */
  res = f_close(&SDFile);
  if (res != FR_OK)
  {
    printf("Failed to close file (error code: %d)\r\n", res);
    return;
  }

  printf("Read from file: %s\r\n", rtext);
}

最终执行结果是,  407通过串口打印了相关测试函数的执行结果信息,显示识别并写入成功, 将SD接到电脑上, 可以看到一个 名为STM32.TXT的文件, 说明SD卡驱动已经成功跑通.

当然这里仅作为说明驱动使能, 简化了很多细节没有说明.

比如说 正常情况下, SD卡是有第九个引脚需要通过电位高低来识别SD-card存在, 以此来实现SD卡热插拔的功能,或者软件方式通过定时器定时轮询检测也可以实现, 

又比如说,FatFS只是比较常见的文件系统, 如果 设备作为某个体系中的一部分, 需要用和整体一样的系统, 可能就无法通过cubeMX直接引用 FatFS的文件系统 的三方库, 那么相关的文件系统的配置 就需要一系列繁琐的配置.

]]>
关于WPF如何连接SQLite或MySQL数据库进行简单增删改查 https://www.unicrom.cn/7941.html Thu, 19 Dec 2024 08:10:16 +0000 https://www.unicrom.cn/?p=7941 以下是使用 Visual Studio WPF 项目来连接数据库操作人物角色数据的完整教程,使用 SQLite 和 MySQL 两种数据库的实现方案。你可以选择其中一种,根据你的需求实施。


步骤 1:环境准备

  1. 安装 Visual Studio
    确保已安装 Visual Studio,并选择支持 WPF 的 .NET 框架 开发功能。
  2. 创建 WPF 项目
    • 启动 Visual Studio,选择 新建项目 > WPF 应用程序
    • 选择目标框架(如 .NET 6 或 .NET Framework)。
  3. 安装数据库驱动程序
    • 如果使用 SQLite
      打开 NuGet 包管理器,搜索并安装 System.Data.SQLite
    • 如果使用 MySQL
      搜索并安装 MySql.Data 或 MySqlConnector
  4. 安装数据库管理工具,例如Navicat等,以及安装对应版本MySQL。
  5. 创建数据库和表
    准备一个数据库文件或者数据库实例,创建存储角色数据的表。

步骤 2:SQLite 实现

1. 创建数据库和表

创建一个 SQLite 数据库文件(如 genshin.db),并用工具或代码创建表 Characters

SQL 示例:

CREATE TABLE Characters (
    Id INT AUTO_INCREMENT PRIMARY KEY,
    Name VARCHAR(255) NOT NULL,
    Element VARCHAR(255) NOT NULL,
    Rarity INT NOT NULL
) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

2. 设计 WPF 界面

在 MainWindow.xaml 中设计界面,允许用户输入角色数据并执行增删改查操作。

<Window x:Class="GenshinDatabaseApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="角色管理" Height="450" Width="800">
    <Grid Margin="10">
        <StackPanel Orientation="Vertical" HorizontalAlignment="Center">
            <TextBox x:Name="NameBox" Width="200" PlaceholderText="角色名" Margin="5"/>
            <TextBox x:Name="ElementBox" Width="200" PlaceholderText="元素" Margin="5"/>
            <TextBox x:Name="RarityBox" Width="200" PlaceholderText="稀有度 (1-5)" Margin="5"/>
            <Button Content="添加角色" Width="200" Click="AddCharacter_Click" Margin="5"/>
            <Button Content="更新角色" Width="200" Click="UpdateCharacter_Click" Margin="5"/>
            <Button Content="删除角色" Width="200" Click="DeleteCharacter_Click" Margin="5"/>
            <Button Content="查询角色" Width="200" Click="SearchCharacter_Click" Margin="5"/>
        </StackPanel>
        <DataGrid x:Name="CharacterGrid" AutoGenerateColumns="True" Margin="10,200,10,10" />
    </Grid>
</Window>

3. 后端代码

在 MainWindow.xaml.cs 中实现增删改查逻辑。

using System;
using System.Collections.ObjectModel;
using System.Data.SQLite;
using System.Windows;

namespace GenshinDatabaseApp
{
    public partial class MainWindow : Window
    {
        private string connectionString = "Data Source=genshin.db";

        public ObservableCollection<Character> Characters { get; set; }

        public MainWindow()
        {
            InitializeComponent();
            Characters = new ObservableCollection<Character>();
            CharacterGrid.ItemsSource = Characters;
            LoadCharacters();
        }

        private void LoadCharacters()
        {
            Characters.Clear();
            using (var connection = new SQLiteConnection(connectionString))
            {
                connection.Open();
                string query = "SELECT * FROM Characters";
                using (var command = new SQLiteCommand(query, connection))
                using (var reader = command.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        Characters.Add(new Character
                        {
                            Id = reader.GetInt32(0),
                            Name = reader.GetString(1),
                            Element = reader.GetString(2),
                            Rarity = reader.GetInt32(3)
                        });
                    }
                }
            }
        }

        private void AddCharacter_Click(object sender, RoutedEventArgs e)
        {
            using (var connection = new SQLiteConnection(connectionString))
            {
                connection.Open();
                string query = "INSERT INTO Characters (Name, Element, Rarity) VALUES (@Name, @Element, @Rarity)";
                using (var command = new SQLiteCommand(query, connection))
                {
                    command.Parameters.AddWithValue("@Name", NameBox.Text);
                    command.Parameters.AddWithValue("@Element", ElementBox.Text);
                    command.Parameters.AddWithValue("@Rarity", int.Parse(RarityBox.Text));
                    command.ExecuteNonQuery();
                }
            }
            LoadCharacters();
        }

        private void UpdateCharacter_Click(object sender, RoutedEventArgs e)
        {
            if (CharacterGrid.SelectedItem is Character selectedCharacter)
            {
                using (var connection = new SQLiteConnection(connectionString))
                {
                    connection.Open();
                    string query = "UPDATE Characters SET Name=@Name, Element=@Element, Rarity=@Rarity WHERE Id=@Id";
                    using (var command = new SQLiteCommand(query, connection))
                    {
                        command.Parameters.AddWithValue("@Name", NameBox.Text);
                        command.Parameters.AddWithValue("@Element", ElementBox.Text);
                        command.Parameters.AddWithValue("@Rarity", int.Parse(RarityBox.Text));
                        command.Parameters.AddWithValue("@Id", selectedCharacter.Id);
                        command.ExecuteNonQuery();
                    }
                }
                LoadCharacters();
            }
        }

        private void DeleteCharacter_Click(object sender, RoutedEventArgs e)
        {
            if (CharacterGrid.SelectedItem is Character selectedCharacter)
            {
                using (var connection = new SQLiteConnection(connectionString))
                {
                    connection.Open();
                    string query = "DELETE FROM Characters WHERE Id=@Id";
                    using (var command = new SQLiteCommand(query, connection))
                    {
                        command.Parameters.AddWithValue("@Id", selectedCharacter.Id);
                        command.ExecuteNonQuery();
                    }
                }
                LoadCharacters();
            }
        }

        private void SearchCharacter_Click(object sender, RoutedEventArgs e)
        {
            Characters.Clear();
            using (var connection = new SQLiteConnection(connectionString))
            {
                connection.Open();
                string query = "SELECT * FROM Characters WHERE Name LIKE @Name";
                using (var command = new SQLiteCommand(query, connection))
                {
                    command.Parameters.AddWithValue("@Name", $"%{NameBox.Text}%");
                    using (var reader = command.ExecuteReader())
                    {
                        while (reader.Read())
                        {
                            Characters.Add(new Character
                            {
                                Id = reader.GetInt32(0),
                                Name = reader.GetString(1),
                                Element = reader.GetString(2),
                                Rarity = reader.GetInt32(3)
                            });
                        }
                    }
                }
            }
        }
    }

    public class Character
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Element { get; set; }
        public int Rarity { get; set; }
    }
}

步骤 3:运行程序

  1. 确保 genshin.db 文件存在于项目根目录中,并已包含 Characters 表。
  2. 运行 WPF 应用程序,可以通过界面添加、修改、删除和查询角色信息。

MySQL 实现

如果需要使用 MySQL,将 SQLiteConnection 替换为 MySqlConnection,并调整连接字符串为:

string connectionString = "Server=your_server;Database=genshin.sql;User=your_user;Password=your_password;";

其余逻辑与 SQLite 类似。

推荐字符集和排序规则

  1. 字符集utf8mb4
    • utf8mb4 是 MySQL 8.0 推荐的字符集,它支持 Unicode 完整字符集,包括多字节字符和表情符号(Emoji)。
  2. 排序规则:根据需求选择以下之一:
    • 通用排序规则utf8mb4_general_ci
      适合大部分普通应用,排序和比较效率较高。
    • 准确排序规则utf8mb4_unicode_ci
      遵循 Unicode 排序规则,支持多语言排序,但性能略逊于 utf8mb4_general_ci
    • 区分大小写utf8mb4_bin
      精确比较,区分大小写,适用于需要精确匹配的场景。

创建数据库的 SQL 示例

CREATE DATABASE genshin_db
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;

选用排序规则的考虑

  • utf8mb4_general_ci
    • 不区分大小写(Case Insensitive)。
    • 性能较好,适合通用性需求。
    • 不完全遵循 Unicode 标准,排序结果可能略有偏差。
  • utf8mb4_unicode_ci
    • 不区分大小写(Case Insensitive)。
    • 完全遵循 Unicode 排序规则,支持更准确的多语言排序。
    • 性能略低于 utf8mb4_general_ci
  • utf8mb4_bin
    • 区分大小写(Case Sensitive)。
    • 精确比较,适合密码存储、唯一标识符等场景。

希望这篇文章能帮助你快速上手开发,顺利实现 WPF 程序对数据库的增删改查管理角色数据!

]]>
Python爬虫的运行效率过低该怎么处理 https://www.unicrom.cn/7851.html Wed, 23 Oct 2024 08:44:31 +0000 https://www.unicrom.cn/?p=7851 一、网络请求优化

  1. 选择高效的请求库:
    • 使用requests库结合会话对象可重用连接,减少开销。
   import requests

   session = requests.Session()
   response = session.get('https://www.example.com')
  • 对于异步请求,可使用aiohttp库能同时发起多个请求,提高并发性能。
   import aiohttp
   import asyncio

   async def fetch(url):
       async with aiohttp.ClientSession() as session:
           async with session.get(url) as response:
               return await response.text()

   urls = ['https://www.example.com', 'https://www.example2.com']
   tasks = [fetch(url) for url in urls]
   asyncio.run(asyncio.wait(tasks))
  1. 设置合理请求头:
    • 模拟真实浏览器请求头,如User-AgentAccept-Language等,增加请求真实性,避免被网站拒绝或限速。
   headers = {
       'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
       'Accept-Language': 'en-US,en;q=0.9'
   }
   response = requests.get('https://www.example.com', headers=headers)
  • 合理设置Referer等其他请求头。
  1. 调整请求超时时间:
   response = requests.get('https://www.example.com', timeout=5)

根据网络和目标网站响应速度设置合理超时时间,避免过长等待或因过短而频繁超时。

二、数据解析优化

  1. 挑选高效解析库:
    • lxml库基于 C 语言实现,解析速度比BeautifulSoup更快。
   from lxml import html

   tree = html.fromstring(response.text)
   elements = tree.xpath('//div[@class="example"]')
  • 若只需提取特定信息,可考虑正则表达式,但要注意性能开销。
   import re

   pattern = r'<div class="example">(.*?)</div>'
   matches = re.findall(pattern, response.text)
  1. 避免重复解析:
    • 一次性解析出所有需要的内容,避免重复解析同一文档,可将解析结果缓存起来提高效率。

三、并发与分布式爬取

  1. 多线程或多进程:
    • 使用multiprocessingthreading模块可实现多线程或多进程同时运行爬虫任务。多线程适用于 I/O 密集型任务,多进程适用于 CPU 密集型任务。
   import threading

   def crawl(url):
       # 爬取逻辑

   urls = ['https://www.example.com', 'https://www.example2.com']
   threads = []
   for url in urls:
       t = threading.Thread(target=crawl, args=(url,))
       threads.append(t)
       t.start()

   for t in threads:
       t.join()
  • 注意控制线程或进程数量,避免资源竞争。
  1. 分布式爬取:
    • 大规模爬虫项目可采用分布式架构,利用分布式任务队列(如 Celery)管理任务分配和调度。但需解决节点通信、任务分配、数据存储等问题。

四、数据存储优化

  1. 合适存储方式选择:
    • 根据数据特点和需求选择存储方式。数据量小可用文本文件、CSV 文件等;数据量大可使用数据库(如 MySQL、MongoDB 等)。
   import pymongo

   client = pymongo.MongoClient('mongodb://localhost:27017/')
   db = client['mydatabase']
   collection = db['mycollection']
   data = {'key': 'value'}
   collection.insert_one(data)
  • 关系型数据库适用于频繁查询和更新的数据,NoSQL 数据库适用于非结构化数据。
  1. 批量存储:
    • 缓存数据,达到一定数量后批量存储,减少存储操作次数。

五、其他优化措施

  1. 去除不必要操作:
    • 去除不必要的打印输出、调试语句等,减少计算和 I/O 开销。
  2. 优化算法和逻辑:
    • 检查爬虫算法和逻辑,优化遍历网页链接等操作,避免重复访问和死循环。
  3. 监控和调整:
    • 监控爬取速度、请求成功率、存储效率等性能指标,根据结果调整参数和策略。

总结

提高 Python 爬虫运行效率需要从网络请求、数据解析、并发与分布式爬取、数据存储以及其他方面进行综合优化。通过选择高效的请求库、设置合理的请求头和超时时间、挑选合适的解析库、采用多线程或多进程以及分布式架构、优化存储方式、去除不必要操作、优化算法逻辑并进行监控调整,可以显著提高爬虫的效率和性能,使其能够更快速、稳定地完成数据爬取任务。

]]>
Python识别空气断路器开状态 https://www.unicrom.cn/7839.html Mon, 21 Oct 2024 02:54:12 +0000 https://www.unicrom.cn/?p=7839 先说背景:在一个控制室里面有很多空气断路器,在不通过外接其它设备去监听这个开关的状态,可以通过高清摄像头去做图像识别,用于实时监听所有开关的状态。

这里面会有一个上位机去查看每个摄像头画面,然后通过拖拽框选空气断路器,且标注名称。这里我们跳过。直接看看如何快速识别开关的状态。

先看看空气断路器图像:

我们在实际场景中识别图像,最好按图像的现有特征来找。比如:这个空气断路器,这个开关是红色的。向上打,就是开的状态。

思路就是通过 红色,快速找出这个轮廓。然后我们知道这个是开状态。那么可以使用它做为模板。去对比后面的图像,当关开状态的轮廓而得出结果。

直接上代码。

import cv2
import numpy as np

# 读取图像
image = cv2.imread('on.png')  # 替换为你的图像路径

# 将图像转换为HSV色彩空间
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)

# 定义红色的HSV范围
lower_red1 = np.array([0, 100, 100])
upper_red1 = np.array([10, 255, 255])
lower_red2 = np.array([160, 100, 100])
upper_red2 = np.array([180, 255, 255])

# 创建一个掩码,找出红色区域
mask1 = cv2.inRange(hsv, lower_red1, upper_red1)
mask2 = cv2.inRange(hsv, lower_red2, upper_red2)
mask = cv2.bitwise_or(mask1, mask2)

# 查找红色的轮廓
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# 如果找到了红色的轮廓
if contours:
    # 找到最大的轮廓
    largest_contour = max(contours, key=cv2.contourArea)
    # 创建一个全黑的掩码
    largest_mask = np.zeros_like(mask)
    # 在新的掩码上绘制最大的轮廓
    cv2.drawContours(largest_mask, [largest_contour], -1, 255, thickness=cv2.FILLED)
    # 使用新的掩码与原始图像进行结合
    result = cv2.bitwise_and(image, image, mask=largest_mask)
    # 可选:显示结果
    cv2.imshow('Largest Red Area', result)
else:
    print("没有检测到红色开关")

# 可选:显示图像和掩码
cv2.imshow('Original Image', image)
# cv2.imshow('Mask', mask)
cv2.waitKey(0)
cv2.destroyAllWindows()

可以看到。精准提取到这个开关的轮廓。这里上一个关状态的图,

可以看看:

有这开状态的轮廓,和关状态的轮廓。那么后面就简单多了。
先加载 开状态 和关状态 的轮廓。 做为源头。然后不停的从相机获取图像,然后提取红色最大轮廓,用来比对。
开和关都比对。 哪个更接近,就是哪个状态。(注意这里没有只比对开状态,然后不是开状态就是关。这里开关都比对,会更精准),上代码 。

import cv2
import numpy as np

openContour = None
closeContour = None

# 定义红色的HSV范围
lower_red1 = np.array([0, 100, 100])
upper_red1 = np.array([10, 255, 255])
lower_red2 = np.array([160, 100, 100])
upper_red2 = np.array([180, 255, 255])


def init_open_contour():
    global openContour, lower_red1, upper_red1, lower_red2, upper_red2
    # 加载之前保存的轮廓图像
    open_contour = cv2.imread('on.png')
    # 将图像转换为HSV色彩空间
    open_hsv = cv2.cvtColor(open_contour, cv2.COLOR_BGR2HSV)
    open_mask1 = cv2.inRange(open_hsv, lower_red1, upper_red1)
    open_mask2 = cv2.inRange(open_hsv, lower_red2, upper_red2)
    open_mask = cv2.bitwise_or(open_mask1, open_mask2)
    cts, _ = cv2.findContours(open_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    if cts:
        # 找到最大的轮廓
        openContour = max(cts, key=cv2.contourArea)


def init_close_contour():
    global closeContour, lower_red1, upper_red1, lower_red2, upper_red2
    # 加载之前保存的轮廓图像
    contour = cv2.imread('off.png')
    # 将图像转换为HSV色彩空间
    open_hsv = cv2.cvtColor(contour, cv2.COLOR_BGR2HSV)
    open_mask1 = cv2.inRange(open_hsv, lower_red1, upper_red1)
    open_mask2 = cv2.inRange(open_hsv, lower_red2, upper_red2)
    open_mask = cv2.bitwise_or(open_mask1, open_mask2)
    cts, _ = cv2.findContours(open_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    if cts:
        # 找到最大的轮廓
        closeContour = max(cts, key=cv2.contourArea)


def run():
    global openContour, closeContour, lower_red1, upper_red1, lower_red2, upper_red2
    # 这里加载要识别的图像
    new_largest_contour = cv2.imread('on.png')
    hsv = cv2.cvtColor(new_largest_contour, cv2.COLOR_BGR2HSV)
    # 创建一个掩码,找出红色区域
    mask1 = cv2.inRange(hsv, lower_red1, upper_red1)
    mask2 = cv2.inRange(hsv, lower_red2, upper_red2)
    mask = cv2.bitwise_or(mask1, mask2)
    # 查找红色的轮廓
    contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    # 如果找到了红色的轮廓
    if contours:
        # 找到最大的轮廓
        largest_contour = max(contours, key=cv2.contourArea)
        # 对比轮廓
        # 使用cv2.matchShapes比较两个轮廓的相似度
        similarity_score = cv2.matchShapes(openContour, largest_contour, cv2.CONTOURS_MATCH_I1, 0.0)
        similarity_score2 = cv2.matchShapes(closeContour, largest_contour, cv2.CONTOURS_MATCH_I1, 0.0)
        # 输出相似度
        print(f"轮廓相似度: 开状态:{similarity_score}    关状态:{similarity_score2}")
        # 根据相似度判断开关状态
        if similarity_score < similarity_score2:
            print("开关状态: 开")
        else:
            print("开关状态: 关")
    else:
        print("没有检测到新的红色区域")


if __name__ == '__main__':
    init_open_contour()
    init_close_contour()
    run()

  以上是识别开关的全部代码。可以直接运行。 有了这个开关识别算法,那么这个小的上位机软件就基本没有难度了。

]]>
scada数据采集与监视控制系统定制开发 https://www.unicrom.cn/7792.html Mon, 14 Oct 2024 09:21:17 +0000 https://www.unicrom.cn/?p=7792 SCADA系统,即监控与数据采集系统,是一种在工业自动化领域中扮演着至关重要角色的自动化系统。它能够实时采集工业现场的各种数据,并通过强大的监控和控制功能,确保生产过程的稳定与安全。

一、SCADA系统的基本组成

SCADA系统主要包括四个部分:数据采集、数据传输、数据处理与存储、人机界面。

数据采集:通过各种传感器、执行器等设备,实时采集现场的各种工艺参数,如温度、压力、流量等。

数据传输:将采集到的数据通过有线或无线通信传输到上位机。

数据处理与存储:对采集到的数据进行处理、分析,并将数据存储到数据库中,以便后续查询和分析。

人机界面:通过图形化界面展示现场数据,提供操作员进行监控和控制。

二、SCADA系统定制开发的关键技术

硬件选型与集成:根据项目需求和现场环境,选择合适的硬件设备,如数据采集模块、通信模块、上位机等,并进行集成。

软件开发:根据用户需求,开发具有针对性的监控和控制功能。主要包括数据处理、报警与预警、趋势分析、报表生成等模块。

通信协议:定制开发适合现场通信的协议,实现数据在不同设备间的无缝传输。

信息安全:确保数据传输和存储的安全性,防止数据泄露和恶意攻击。

用户界面:根据用户习惯和企业文化,设计简洁易用的操作界面。

三、SCADA系统的典型架构

现场设备层:包括传感器、执行器等,负责数据的采集。

本地控制层:通常由PLC或RTU组成,负责现场数据的初步处理和控制。

监督控制层:主SCADA服务器或监控站,用于收集和分析来自多个本地控制站的数据,并提供人机界面。

制造执行系统(MES)层:负责生产过程的管理,如订单跟踪、生产安排等。

企业业务系统层:包括ERP和CRM等,管理组织的业务运营。

四、SCADA系统定制开发的实践案例

某石油化工企业需要监控生产过程中的温度、压力、流量等参数,并对设备进行远程控制。针对企业需求,我们定制开发了一套SCADA系统。

硬件选型:选用具备良好稳定性和性能的采集模块、通信模块和上位机。

软件开发:采用成熟的数据处理算法,实现实时数据的采集、处理和存储;设计报警与预警功能,对异常数据进行实时报警;开发趋势分析模块,便于用户了解生产过程的走势;生成报表模块,方便管理人员进行数据分析。

通信协议:根据现场设备情况,定制开发了适应多种通信方式的协议,实现数据在不同设备间的稳定传输。

信息安全:采用加密算法,确保数据传输和存储的安全性。

用户界面:设计简洁直观的界面,展示实时数据、报警信息、趋势分析等,并提供远程控制功能。

五、结论

随着工业自动化和信息化技术的发展,SCADA系统在各个行业的应用越来越广泛。为满足不同行业、不同企业的个性化需求,定制开发具有针对性的SCADA数据采集与监视控制系统至关重要。通过对硬件选型、软件开发、通信协议、信息安全等方面的探索与实践,为我国工业生产提供高效、稳定、安全的监控与控制手段。

在这一领域,由你创以其专业的技术实力,为复杂电子与软件系统的研发和设备定制提供了强有力的支持。位于深圳龙华的由你创科技,拥有超过100名员工的团队,其中70%以上是中高级研发人员。公司自有的研发基地超过2000平米,研发能力在业界领先。

由你创在工业研发服务和实验室研发服务两大核心领域积累了丰富的行业案例,专注于提供高质量、可交付、长期持续的高端研发技术服务。我们的服务覆盖机械设计、电子工程、软件开发和系统集成等多个学科领域,以技术引领变革,以优秀实践推动发展。公司自主开发的“既攻®”研发技术平台,在板卡开发、嵌入式开发、FPGA开发、鸿蒙开发;上位机开发、SCADA、IOT开发、仿真、算法开发、AI模型训练;以及工业设备、实验室设备、机器人研发等技术服务领域有着广泛的应用和实践。至今,已为近百家科研机构、高等院校、上市公司、行业头部企业提供了专业的服务。

]]>