关于QGroundControl的软件架构的理解
首先QGC是基于QT平台开发,个人理解软件架构即为项目前后端结构,以及前后端数据交互的逻辑。下面是对QGroundControl源码的一些个人理解,写这个博客只是为了记录下来,防止时间久了忘记,过程中看了一些大佬的博客来帮助理解,感谢一下大佬:
火山上的企鹅
阿木实验室
望天边星宿
前言
- 前端qml结构
QGC的前端主要为五个主要页面和一个顶栏:
MainToolBar(顶栏)
AppSetting.qml
SetupView.qml
PlanView.qml
FlightDispalyView.qml
AnalyzeView.qml
- 后端类结构

前端显示与后端逻辑进行数据交互,即实现了QGC地面站功能。
架构理解
架构理解可以从QGC的运行main函数开始逐步理解。
- main.cc
在main.cc中有几个关键程序
创建QGCApplication实例,QGCApplication是QGroundControl中的核心类,提供了应用程序的生命周期管理、连接管理、飞行日志管理、地图和任务管理、设置和配置管理,以及警报和通知管理等功能。它扮演着关键角色。这个类只提供了唯一的实例,另外提供了一个访问它的全局函数QGCApplication* qgcApp(void){}
QGCApplication* app = new QGCApplication(argc, argv, runUnitTests);
在_initCommon()中将C++类注册,在前端qml中可以调用,实现前后端数据交互。在
qgroundcontrolQmlGlobalSingletonFactory中将QGroundControlQmlGlobal类实例化为单例工厂QGroundControl。
app->_initCommon()
{static const char* kRefOnly = "Reference only";static const char* kQGroundControl = "QGroundControl";static const char* kQGCControllers = "QGroundControl.Controllers";static const char* kQGCVehicle = "QGroundControl.Vehicle";QSettings settings;// Register our Qml objectsqmlRegisterType<QGCPalette> ("QGroundControl.Palette", 1, 0, "QGCPalette");qmlRegisterType<QGCMapPalette> ("QGroundControl.Palette", 1, 0, "QGCMapPalette");qmlRegisterUncreatableType<Vehicle> (kQGCVehicle, 1, 0, "Vehicle", kRefOnly);qmlRegisterUncreatableType<MissionManager> (kQGCVehicle, 1, 0, "MissionManager", kRefOnly);qmlRegisterUncreatableType<ParameterManager> (kQGCVehicle, 1, 0, "ParameterManager", kRefOnly);qmlRegisterUncreatableType<VehicleObjectAvoidance> (kQGCVehicle, 1, 0, "VehicleObjectAvoidance", kRefOnly);qmlRegisterUncreatableType<QGCCameraManager> (kQGCVehicle, 1, 0, "QGCCameraManager", kRefOnly);qmlRegisterUncreatableType<QGCCameraControl> (kQGCVehicle, 1, 0, "QGCCameraControl", kRefOnly);qmlRegisterUncreatableType<QGCVideoStreamInfo> (kQGCVehicle, 1, 0, "QGCVideoStreamInfo", kRefOnly);qmlRegisterUncreatableType<LinkInterface> (kQGCVehicle, 1, 0, "LinkInterface", kRefOnly);qmlRegisterUncreatableType<MissionController> (kQGCControllers, 1, 0, "MissionController", kRefOnly);qmlRegisterUncreatableType<GeoFenceController> (kQGCControllers, 1, 0, "GeoFenceController", kRefOnly);qmlRegisterUncreatableType<RallyPointController> (kQGCControllers, 1, 0, "RallyPointController", kRefOnly);qmlRegisterUncreatableType<MissionItem> (kQGroundControl, 1, 0, "MissionItem", kRefOnly);qmlRegisterUncreatableType<VisualMissionItem> (kQGroundControl, 1, 0, "VisualMissionItem", kRefOnly);qmlRegisterUncreatableType<CoordinateVector> (kQGroundControl, 1, 0, "CoordinateVector", kRefOnly);qmlRegisterUncreatableType<QmlObjectListModel> (kQGroundControl, 1, 0, "QmlObjectListModel", kRefOnly);qmlRegisterUncreatableType<MissionCommandTree> (kQGroundControl, 1, 0, "MissionCommandTree", kRefOnly);qmlRegisterUncreatableType<CameraCalc> (kQGroundControl, 1, 0, "CameraCalc", kRefOnly);qmlRegisterUncreatableType<LogReplayLink> (kQGroundControl, 1, 0, "LogReplayLink", kRefOnly);qmlRegisterType<LogReplayLinkController> (kQGroundControl, 1, 0, "LogReplayLinkController");
#if defined(QGC_ENABLE_MAVLINK_INSPECTOR)qmlRegisterUncreatableType<MAVLinkChartController> (kQGroundControl, 1, 0, "MAVLinkChart", kRefOnly);
#endif
#if defined(QGC_ENABLE_PAIRING)qmlRegisterUncreatableType<PairingManager> (kQGroundControl, 1, 0, "PairingManager", kRefOnly);
#endifqmlRegisterUncreatableType<AutoPilotPlugin> ("QGroundControl.AutoPilotPlugin", 1, 0, "AutoPilotPlugin", kRefOnly);qmlRegisterUncreatableType<VehicleComponent> ("QGroundControl.AutoPilotPlugin", 1, 0, "VehicleComponent", kRefOnly);qmlRegisterUncreatableType<JoystickManager> ("QGroundControl.JoystickManager", 1, 0, "JoystickManager", kRefOnly);qmlRegisterUncreatableType<Joystick> ("QGroundControl.JoystickManager", 1, 0, "Joystick", kRefOnly);qmlRegisterUncreatableType<QGCPositionManager> ("QGroundControl.QGCPositionManager", 1, 0, "QGCPositionManager", kRefOnly);qmlRegisterUncreatableType<FactValueSliderListModel>("QGroundControl.FactControls", 1, 0, "FactValueSliderListModel", kRefOnly);qmlRegisterUncreatableType<QGCMapPolygon> ("QGroundControl.FlightMap", 1, 0, "QGCMapPolygon", kRefOnly);qmlRegisterUncreatableType<QGCGeoBoundingCube> ("QGroundControl.FlightMap", 1, 0, "QGCGeoBoundingCube", kRefOnly);qmlRegisterUncreatableType<TrajectoryPoints> ("QGroundControl.FlightMap", 1, 0, "TrajectoryPoints", kRefOnly);qmlRegisterType<QGCMapCircle> ("QGroundControl.FlightMap", 1, 0, "QGCMapCircle");qmlRegisterType<ParameterEditorController> (kQGCControllers, 1, 0, "ParameterEditorController");qmlRegisterType<ESP8266ComponentController> (kQGCControllers, 1, 0, "ESP8266ComponentController");qmlRegisterType<ScreenToolsController> (kQGCControllers, 1, 0, "ScreenToolsController");qmlRegisterType<PlanMasterController> (kQGCControllers, 1, 0, "PlanMasterController");qmlRegisterType<ValuesWidgetController> (kQGCControllers, 1, 0, "ValuesWidgetController");qmlRegisterType<QGCFileDialogController> (kQGCControllers, 1, 0, "QGCFileDialogController");qmlRegisterType<RCChannelMonitorController> (kQGCControllers, 1, 0, "RCChannelMonitorController");qmlRegisterType<JoystickConfigController> (kQGCControllers, 1, 0, "JoystickConfigController");qmlRegisterType<LogDownloadController> (kQGCControllers, 1, 0, "LogDownloadController");qmlRegisterType<SyslinkComponentController> (kQGCControllers, 1, 0, "SyslinkComponentController");qmlRegisterType<EditPositionDialogController> (kQGCControllers, 1, 0, "EditPositionDialogController");#ifndef __mobile__
#ifndef NO_SERIAL_LINKqmlRegisterType<FirmwareUpgradeController> (kQGCControllers, 1, 0, "FirmwareUpgradeController");
#endif
#endifqmlRegisterType<GeoTagController> (kQGCControllers, 1, 0, "GeoTagController");qmlRegisterType<MavlinkConsoleController> (kQGCControllers, 1, 0, "MavlinkConsoleController");
#if defined(QGC_ENABLE_MAVLINK_INSPECTOR)qmlRegisterType<MAVLinkInspectorController> (kQGCControllers, 1, 0, "MAVLinkInspectorController");
#endif// Register Qml SingletonsqmlRegisterSingletonType<QGroundControlQmlGlobal> ("QGroundControl", 1, 0, "QGroundControl", qgroundcontrolQmlGlobalSingletonFactory);qmlRegisterSingletonType<ScreenToolsController> ("QGroundControl.ScreenToolsController", 1, 0, "ScreenToolsController", screenToolsControllerSingletonFactory);qmlRegisterSingletonType<ShapeFileHelper> ("QGroundControl.ShapeFileHelper", 1, 0, "ShapeFileHelper", shapeFileHelperSingletonFactory);
}
在_initForNormalAppBoot()中进行启动初始化,加载qml文件。
app->_initForNormalAppBoot()QQmlApplicationEngine* QGCCorePlugin::createRootWindow(QObject *parent)
{QQmlApplicationEngine* pEngine = new QQmlApplicationEngine(parent);pEngine->addImportPath("qrc:/qml");// 注册为对象在qml中直接使用,不需要importpEngine->rootContext()->setContextProperty("joystickManager", qgcApp()->toolbox()->joystickManager());pEngine->rootContext()->setContextProperty("debugMessageModel", AppMessages::getModel());pEngine->load(QUrl(QStringLiteral("qrc:/qml/MainRootWindow.qml")));return pEngine;
}
- 整体架构
想要搞明白整体架构就需要理解QGCApplication类、QGCToolBox类、QGroundControlQmlGlobal类和QGCtool类。
QGCApplication类中新建了QGCToolbox类,在QGCToolbox类初始化时需要传入QGCApplication类,这样通过qgcAPP()->toolBox()就可以获取到功能类的所有属性。
QGCToolBox类是一个大的工具类,QGC的所有功能类都在这个toolBox中进行实例化,使用toolBox提供的接口即可调用类的属性和方法。
在QGroundControlQmlGlobal中通过Q_PROPERTY将功能类的属性和方法共前端可以调用,setToolbox()方法将所有功能类使用指针地址连接到QGCToolBox定义的功能类,所以QGroundControlQmlGlobal是前端获取后端数据的总接口。上边的单例工厂实例化将QGroundControlQmlGlobal实例化为QGroundControl,则前端qml通过import 即可实现调用C++类。
还有一个QGCTool类,所有的功能类都继承了QGCTool。在QGCTool类型定义了QGCApplication* _app;和QGCToolbox* _toolbox;和(QGCApplication* app, QGCToolbox* toolbox)方法,即每个功能类都都有QGCApplication和QGCToolbox,并且都指向同一个QGCApplication和QGCToolbox,即QGCApplication中的_app和_toolBox.这样实现了各个功能类的互通。
QGCToolbox::QGCToolbox(QGCApplication* app)
{// SettingsManager must be first so settings are available to any subsequent tools_settingsManager = new SettingsManager (app, this);//-- Scan and load plugins_scanAndLoadPlugins(app);_audioOutput = new AudioOutput (app, this);_factSystem = new FactSystem (app, this);_firmwarePluginManager = new FirmwarePluginManager (app, this);
#ifndef __mobile___gpsManager = new GPSManager (app, this);
#endif_imageProvider = new QGCImageProvider (app, this);_joystickManager = new JoystickManager (app, this);_linkManager = new LinkManager (app, this);_mavlinkProtocol = new MAVLinkProtocol (app, this);_missionCommandTree = new MissionCommandTree (app, this);_multiVehicleManager = new MultiVehicleManager (app, this);_mapEngineManager = new QGCMapEngineManager (app, this);_uasMessageHandler = new UASMessageHandler (app, this);_qgcPositionManager = new QGCPositionManager (app, this);_followMe = new FollowMe (app, this);_videoManager = new VideoManager (app, this);_mavlinkLogManager = new MAVLinkLogManager (app, this);_adsbVehicleManager = new ADSBVehicleManager (app, this);
#if defined(QGC_ENABLE_PAIRING)_pairingManager = new PairingManager (app, this);
#endif//-- Airmap Manager//-- This should be "pluggable" so an arbitrary AirSpace manager can be used//-- For now, we instantiate the one and only AirMap provider
#if defined(QGC_AIRMAP_ENABLED)_airspaceManager = new AirMapManager (app, this);
#else_airspaceManager = new AirspaceManager (app, this);
#endif
#if defined(QGC_GST_TAISYNC_ENABLED)_taisyncManager = new TaisyncManager (app, this);
#endif
#if defined(QGC_GST_MICROHARD_ENABLED)_microhardManager = new MicrohardManager (app, this);
#endif
}void QGCToolbox::setChildToolboxes(void)
{// SettingsManager must be first so settings are available to any subsequent tools_settingsManager->setToolbox(this);_corePlugin->setToolbox(this);_audioOutput->setToolbox(this);_factSystem->setToolbox(this);_firmwarePluginManager->setToolbox(this);
#ifndef __mobile___gpsManager->setToolbox(this);
#endif_imageProvider->setToolbox(this);_joystickManager->setToolbox(this);_linkManager->setToolbox(this);_mavlinkProtocol->setToolbox(this);_missionCommandTree->setToolbox(this);_multiVehicleManager->setToolbox(this);_mapEngineManager->setToolbox(this);_uasMessageHandler->setToolbox(this);_followMe->setToolbox(this);_qgcPositionManager->setToolbox(this);_videoManager->setToolbox(this);_mavlinkLogManager->setToolbox(this);_airspaceManager->setToolbox(this);_adsbVehicleManager->setToolbox(this);
#if defined(QGC_GST_TAISYNC_ENABLED)_taisyncManager->setToolbox(this);
#endif
#if defined(QGC_GST_MICROHARD_ENABLED)_microhardManager->setToolbox(this);
#endif
#if defined(QGC_ENABLE_PAIRING)_pairingManager->setToolbox(this);
#endif
}void QGCToolbox::_scanAndLoadPlugins(QGCApplication* app)
{
#if defined (QGC_CUSTOM_BUILD)//-- Create custom plugin (Static)_corePlugin = (QGCCorePlugin*) new CUSTOMCLASS(app, app->toolbox());if(_corePlugin) {return;}
#endif//-- No plugins found, use default instance_corePlugin = new QGCCorePlugin(app, app->toolbox());
}
在前端获取后端类的属性的总借口即为QGroundControl,例如
property var _vehicle: QGroundControl.multiVehicleManager.getVehicleById(1)
在功能类中向获取其他功能类的属性即使用qgcApp()->toolbox(),如果功能类继承了QGCTool则直接可以使用_toolbox->...即可。
_mavlink = qgcApp()->toolbox()->mavlinkProtocol();
_mavlinkProtocol = _toolbox->mavlinkProtocol();
其实只要理解这几个总文件之间的关系后,就理解了QGC的整体结构。_app和_toolbox是QGC整个软件的总接口,_app和_toolbox都在QGCApplication类中,然后所有功能类都在QGCToolBox类中新建,想要获取功能类的属性就需要通过_toolbox。然后QGroundControlQmlGlobal中通过指针地址获取到了所有功能类,然后又通过Q_PROPERTY将类以属性的形式开放给前端,再加上单例模式直接在前端import QGroundControl即可获取到后端属性。这样即完成了前后端数据连通,也解决了各个功能类之间的属性获取。
以上是个人理解,很多地理解的不是太透彻,等有新的理解再更新。有错误的地方也望大佬指正,共同进步。
相关文章:
关于QGroundControl的软件架构的理解
首先QGC是基于QT平台开发,个人理解软件架构即为项目前后端结构,以及前后端数据交互的逻辑。下面是对QGroundControl源码的一些个人理解,写这个博客只是为了记录下来,防止时间久了忘记,过程中看了一些大佬的博客来帮助理…...
Android 文本识别:MLKIT + PreviewView
随着移动设备的普及和摄像头的高像素化,利用相机进行文本识别成为了一种流行的方式。MLKit 是 Google 提供的一款机器学习工具包,其中包含了丰富的图像和语言处理功能,包括文本识别。PreviewView 是 Android Jetpack 的一部分,它提…...
刮泥机的分类有哪些及组成部分
刮泥机的分类有哪些及组成部分 刮泥机的分类: 刮泥机主要包括:周边传动刮泥机、中心传动浓缩刮泥机。 1、中心传动浓缩刮泥机:主要由溢流装置、大梁及拦杆、进口管、传动装置、电器箱、稳流筒、主轴、浮渣耙板、刮集装置、水下轴承、小刮刀、…...
Qt编程基础 | 第六章-窗体 | 6.2、VS导入资源文件
一、VS导入资源文件 1.1、导入资源文件 步骤一: 将所有图片放到各自文件夹下,并将文件夹拷贝到资源文件(.qrc文件)的同级目录下,如下: 步骤二: 新建VS项目的时候,系统会自动建好一…...
NET框架程序设计-第4章类型基础
4.1 所有类型的基类型:System.Object CLR 要求每个类型最终都要继承自 System.Object 类型。 两种类型定义: 1)隐式继承 //隐式继承 Object class Employee{}2)显式继承 class Employee:System.Object{}System.Object 主要的公…...
Java设计模式-备忘录模式
简介 在软件开发中,设计模式是为了解决常见问题而提出的一种经过验证的解决方案。备忘录模式(Memento Pattern)是一种行为型设计模式,它允许我们在不破坏封装性的前提下,捕获和恢复对象的内部状态。 备忘录模式是一种…...
Zookeeper集群 + Kafka集群
Zookeeper 概述 Zookeeper 定义 Zookeeper是一个开源的分布式的,为分布式框架提供协调服务的Apache项目。 Zookeeper 工作机制 Zookeeper从设计模式角度来理解:是一个基于观察者模式设计的分布式服务管理框架,它负责存储和管理大家都关心的数…...
“邮件营销新趋势,这个平台让你收获颇丰!
随着各媒体平台的迅速发展,2023年大家更专注于视频营销、网红营销、直播营销等营销方式。可以见得,数字媒介手段的发展,对于营销方式也产生了巨大的影响。但是,企业在拥抱新兴的营销方式的同时,也不要忽视传统的营销方…...
Python列表推导
列表推导式 列表推导式创建列表的方式更简洁。常见的用法为,对序列或可迭代对象中的每个元素应用某种操作,用生成的结果创建新的列表;或用满足特定条件的元素创建子序列。 例如,创建平方值的列表: squares [] for …...
git使用查看分支、创建分支、合并分支
一、查看分支 查看的git命令如下: git branch 列出本地已经存在的分支,并且当前分支会用*标记 git branch -r 查看远程版本库的分支列表 git branch -a 查看所有分支列表(包括本地和远程,remotes/开头的表示远程分支)…...
vue3.0与vue2.0
一、生命周期的变化 1.vue2.响应式架构 2.vue3.0 响应式架构图 Vue3.0响应式框架在设计上,将视图渲染和数据响应式完全分离开来。将响应式核心方法effect从原有的Watcher中抽离。这样,当我们只需要监听数据响应某种逻辑回调(例如监听某个text属性的变化…...
HTML 中的常用标签用法
HTML是构建Web页面的基础语言,其中包含许多不同类型的标签。这些标签由尖括号包围,以指示浏览器如何呈现文本。下面是HTML中的一些常用标签以及它们的使用方法: 标题标签(h1-h6) 标题标签用于标识页面内容的标题&…...
【C++】指针 - 定义和使用,所占内存空间,空指针,野指针,const 修饰指针,指针和数组,指针和函数
文章目录 1. 定义和使用2. 所占内存空间3. 空指针4. 野指针5. const 修饰指针6. 指针和数组7. 指针和函数 1. 定义和使用 数据类型 * 变量名; 指针的作用是,可以通过指针间接访问内存。 内存编号是从 0 开始记录的,一般用十六进制数字表示。可以利用指…...
新规之下产业园区如何合理收费水电费用
一、政策背景 2018年3月30日,国家发改委发布《国家发展改革委关于降低一般工商业电价有关事项的通知》。明确提出进一步规范和降低电网环节收费,一是提高两部制电价的灵活性;二是全面清理规范电网企业在输配电价之外的收费项目,重…...
1011. 在 D 天内送达包裹的能力
传送带上的包裹必须在 days 天内从一个港口运送到另一个港口。 传送带上的第 i 个包裹的重量为 weights[i]。每一天,我们都会按给出重量(weights)的顺序往传送带上装载包裹。我们装载的重量不会超过船的最大运载重量。 返回能在 days 天内将…...
基于SpringBoot养老院管理系统
目录 一、项目介绍 二. 运行环境 三、项目技术 四、部署项目 五、项目运行 六、项目展示 五、项目下载 一、项目介绍 基于springboot的养老院管理系统拥有多种角色账号:管理员和用户 管理员:管理员管理、用户管理、健康管理、病例方案管理、药品…...
1.3 eBPF的工作原理初探
写在前面 上一节提到过,eBPF程序是面向BPF体系结构指令集编写的,它并不直接运行在Linux内核中,我们可以理解为它是运行在eBPF虚拟机,由eBPF虚拟机来执行eBPF字节码,就像java运行在jvm一样。 我们用一张原理图来看下eBPF程序的编译,加载,验证,钩子,映射等结点。 如上是…...
【CH32】| 02——常用外设 | GPIO
系列文章目录 【CH32】| 00——开发环境搭建 【CH32】| 01——新建工程 | 下载 | 运行 |调试 【CH32】| 02——常用外设 | GPIO 失败了也挺可爱,成功了就超帅。 文章目录 前言1. GPIO简介2. IO口的内部结构框图保护二极管上下拉电阻施密特触发器两个MOS管输出寄存器…...
第四章 测试用例编
本科程目标 1.什么是测试用例 2.测试用例的重要性 3.测试用例的八大要素(重点) 4.测试用例的评审 一、什么叫软件测试用例 测试用例(TestCase)是为项目需求而编制的一组测试输入、执行条件以及预期结果,以便测试…...
解决dpdk reserve的内存返回的虚拟地址和iova地址一样的问题
1. 背景: 在ubuntu20.04上用dpdk API: rte_memzone_reserve_aligned("L1L2_PCIE_MEMORY", 1.5*1024*1024*1024, rte_socket_id(), RTE_MEMZONE_1GB|RTE_MEMZONE_IOVA_CONTIG, RTE_CACHE_LINE_SIZE); 分配1.5…...
内存分配函数malloc kmalloc vmalloc
内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...
遍历 Map 类型集合的方法汇总
1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...
QMC5883L的驱动
简介 本篇文章的代码已经上传到了github上面,开源代码 作为一个电子罗盘模块,我们可以通过I2C从中获取偏航角yaw,相对于六轴陀螺仪的yaw,qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...
uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖
在前面的练习中,每个页面需要使用ref,onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入,需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...
CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...
MySQL中【正则表达式】用法
MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现(两者等价),用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例: 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...
初学 pytest 记录
安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...
Linux系统部署KES
1、安装准备 1.版本说明V008R006C009B0014 V008:是version产品的大版本。 R006:是release产品特性版本。 C009:是通用版 B0014:是build开发过程中的构建版本2.硬件要求 #安全版和企业版 内存:1GB 以上 硬盘…...
从“安全密码”到测试体系:Gitee Test 赋能关键领域软件质量保障
关键领域软件测试的"安全密码":Gitee Test如何破解行业痛点 在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的"神经中枢"。从国防军工到能源电力,从金融交易到交通管控,这些关乎国计民生的关键领域…...
LOOI机器人的技术实现解析:从手势识别到边缘检测
LOOI机器人作为一款创新的AI硬件产品,通过将智能手机转变为具有情感交互能力的桌面机器人,展示了前沿AI技术与传统硬件设计的完美结合。作为AI与玩具领域的专家,我将全面解析LOOI的技术实现架构,特别是其手势识别、物体识别和环境…...
