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

OpenHarmony 6.0 低空飞行器开发实战:从AI感知检测到组网协同

第一部分:核心知识点

在动手之前,我们必须明确我们将要使用的技术“武器”及其在OpenHarmony 6.0中的具体实现。

技术领域核心知识点OpenHarmony 6.0 关键API/模块实战应用
分布式组网设备发现、认证与状态管理,构建“超级终端”的基础。@ohos.distributedDevice.DeviceManager地面站发现空中的无人机,并建立可信连接。
分布式数据跨设备数据对象的实时同步,实现状态共享。@ohos.data.distributedDataObject领航无人机发布飞行任务,跟随无人机实时接收并执行。
端侧AI推理模型加载、输入预处理、推理执行、输出后处理。@kit.MindSporeLiteKit无人机机载摄像头实时捕捉画面,进行障碍物(如:人、车)AI识别。
数据标准化将设备能力抽象为统一的数据服务,实现跨设备访问。@ohos.app.ability.DataAbility, @ohos.data.dataAbility.DataAbilityHelper无人机将飞行日志、电池状态等数据发布为标准服务,地面站一键订阅。
UI与状态声明式UI范式,状态驱动UI更新。@ohos.arkui.ArkUI, @State, @StorageProp构建地面站和无人机机载的交互界面,实时显示AI结果、设备状态和任务信息。

第二部分:项目准备与配置

在开始编码前,我们需要正确配置项目。

  1. 创建项目:在DevEco Studio中创建一个新的OpenHarmony项目,选择Empty Ability模板,语言选择ArkTS,兼容API版本设置为20
  2. 配置权限:打开entry/src/main/module.json5文件,添加必要的权限。分布式功能和数据访问需要明确的权限声明。
    {"module": {// ... 其他配置"requestPermissions": [{"name": "ohos.permission.DISTRIBUTED_DEVICE_STATE_CHANGE","reason": "$string:permission_reason_device_state","usedScene": {"abilities": ["EntryAbility"],"when": "inuse"}},{"name": "ohos.permission.GET_DISTRIBUTED_DEVICE_INFO","reason": "$string:permission_reason_device_info","usedScene": {"abilities": ["EntryAbility"],"when": "inuse"}},{"name": "ohos.permission.OPERATE_DISTRIBUTED_DEVICE","reason": "$string:permission_reason_operate_device","usedScene": {"abilities": ["EntryAbility"],"when": "inuse"}},{"name": "ohos.permission.DISTRIBUTED_DATASYNC","reason": "$string:permission_reason_datasync","usedScene": {"abilities": ["EntryAbility"],"when": "inuse"}}]}
    }
    
    同时,请在resources/base/element/string.json中添加对应的权限原因说明字符串。
  3. 添加依赖:在entry/oh-package.json5中添加MindSpore Lite的依赖。
    {"dependencies": {"@kit.MindSporeLiteKit": "^1.0.0"}
    }
    
    添加后,点击DevEco Studio右上角的Sync Now

第三部分:实战开发 - 模块化实现

我们将功能拆分为三个核心模块进行开发。

模块1:端侧AI感知服务

这个服务负责加载AI模型并对图像数据进行推理。
步骤1:准备模型

  • 下载一个适用于移动端的轻量级分类模型,例如MobileNetV2,并将其转换为.ms格式(MindSpore Lite模型格式)。
  • entry/src/main/resources/目录下创建一个model文件夹,并将您的mobilenetv2.ms模型文件放入其中。
    步骤2:创建AI服务类
    entry/src/main/ets/下创建service/AIDetectionService.ets文件。
// entry/src/main/ets/service/AIDetectionService.ets
import mindSporeLite from '@kit.MindSporeLiteKit';
import hilog from '@ohos.hilog';
import { BusinessError } from '@kit.BasicServicesKit';
import { image } from '@kit.ImageKit';
const TAG = 'AIDetectionService';
export class AIDetectionService {private model: mindSporeLite.Model | null = null;private isModelLoaded: boolean = false;// 模型文件路径,在resources/rawfile目录下private readonly MODEL_PATH = 'common/model/mobilenetv2.ms'; // 初始化模型async initModel(): Promise<void> {if (this.isModelLoaded) {hilog.info(0x0000, TAG, 'Model already loaded.');return;}try {// 1. 设置模型推理运行时上下文const context: mindSporeLite.Context = {target: ['cpu'], // 指定在CPU上运行};// 2. 从资源中加载模型文件const modelBuffer = await getContext(this).resourceManager.getRawFileContent(this.MODEL_PATH);// 3. 创建并构建模型this.model = new mindSporeLite.Model();await this.model.build(modelBuffer, mindSporeLite.ModelType.MINDIR, context);this.isModelLoaded = true;hilog.info(0x0000, TAG, 'Model loaded successfully.');} catch (error) {const err = error as BusinessError;hilog.error(0x0000, TAG, `Failed to load model. Code: ${err.code}, message: ${err.message}`);}}// 执行推理async runInference(imagePixelMap: image.PixelMap): Promise<string> {if (!this.isModelLoaded || !this.model) {hilog.error(0x0000, TAG, 'Model is not loaded, cannot run inference.');return 'Error: Model not loaded';}try {// 1. 获取模型输入信息const inputs = this.model.getInputs();if (inputs.length === 0) {return 'Error: Model has no inputs';}const inputTensor = inputs[0];// 2. 预处理图像:调整大小、归一化const imageInfo = { size: { width: inputTensor.shape[2], height: inputTensor.shape[3] } };const imageData = await imagePixelMap.getImageData(imageInfo);const inputData = this.preprocessImageData(imageData.pixels, inputTensor.shape);// 3. 创建输入Tensor并填充数据const msTensor = new mindSporeLite.MSTensor();msTensor.dataType = mindSporeLite.DataType.NUMBER_TYPE_FLOAT32;msTensor.shape = inputTensor.shape;msTensor.name = inputTensor.name;msTensor.setData(new Float32Array(inputData));// 4. 执行推理const outputs = this.model.predict([msTensor]);hilog.info(0x0000, TAG, `Inference finished, output count: ${outputs.length}`);// 5. 后处理输出:找到概率最高的类别const result = this.postprocessOutput(outputs[0]);return result;} catch (error) {const err = error as BusinessError;hilog.error(0x0000, TAG, `Inference failed. Code: ${err.code}, message: ${err.message}`);return `Inference Error: ${err.message}`;}}// 图像预处理 (简化版,实际需根据模型要求调整)private preprocessImageData(pixels: ArrayBuffer, shape: number[]): number[] {const [batch, channels, height, width] = shape;const float32Data = new Uint8Array(pixels);const normalizedData: number[] = [];for (let i = 0; i < float32Data.length; i += 4) { // RGBA// 假设模型是RGB输入,且需要归一化到[0, 1]normalizedData.push(float32Data[i] / 255.0); // RnormalizedData.push(float32Data[i + 1] / 255.0); // GnormalizedData.push(float32Data[i + 2] / 255.0); // B}return normalizedData;}// 输出后处理 (简化版,需要配合标签文件)private postprocessOutput(outputTensor: mindSporeLite.MSTensor): string {const outputData = outputTensor.getData() as Float32Array;let maxIndex = 0;let maxValue = outputData[0];for (let i = 1; i < outputData.length; i++) {if (outputData[i] > maxValue) {maxValue = outputData[i];maxIndex = i;}}// 这里应该有一个标签列表来根据maxIndex查找类别名称// 为了演示,我们直接返回索引和置信度return `Detected: Class ${maxIndex} with ${(maxValue * 100).toFixed(2)}% confidence`;}
}
模块2:飞行数据标准化与共享

我们使用DataAbility来提供一个标准化的数据接口。
步骤1:创建DataAbility
在DevEco Studio中右键entry/src/main/ets -> New -> Ability -> Data Ability,命名为FlightDataAbility
步骤2:实现DataAbility逻辑
修改生成的FlightDataAbility.ets文件。

// entry/src/main/ets/ability/FlightDataAbility.ets
import { DataAbilityHelper, ValuesBucket } from '@ohos.data.dataAbility';
import { Want } from '@kit.AbilityKit';
import hilog from '@ohos.hilog';
import { rdb } from '@kit.ArkData';
import { BusinessError } from '@kit.BasicServicesKit';
const TAG = 'FlightDataAbility';
const TABLE_NAME = 'flight_log';
const STORE_CONFIG: rdb.StoreConfig = { name: 'flight_data.db' };
let rdbStore: rdb.RdbStore | null = null;
const COLUMNS = ['id', 'deviceId', 'timestamp', 'battery', 'gps', 'log'];
export default class FlightDataAbility {async onCreate(want: Want, callback: Function) {hilog.info(0x0000, TAG, 'DataAbility onCreate');// 初始化RdbStorerdb.getRdbStore(getContext(this), STORE_CONFIG, 1, (err, store) => {if (err) {hilog.error(0x0000, TAG, `getRdbStore failed, code: ${err.code}, message: ${err.message}`);return;}rdbStore = store;// 创建表store.executeSql(`CREATE TABLE IF NOT EXISTS ${TABLE_NAME} (id INTEGER PRIMARY KEY AUTOINCREMENT,deviceId TEXT NOT NULL,timestamp INTEGER NOT NULL,battery REAL,gps TEXT,log TEXT)`);callback();});}async insert(uri: string, valueBucket: ValuesBucket): Promise<number> {hilog.info(0x0000, TAG, `DataAbility insert: ${JSON.stringify(valueBucket)}`);if (!rdbStore) {return -1;}return await rdbStore.insert(TABLE_NAME, valueBucket);}async query(uri: string, columns: Array<string>, predicates: rdb.DataAbilityPredicates): Promise<rdb.ResultSet> {hilog.info(0x0000, TAG, `DataAbility query`);if (!rdbStore) {throw new Error("RdbStore is not initialized.");}return await rdbStore.query(TABLE_NAME, predicates, columns);}// ... 其他方法如 update, delete 可以按需实现
}

步骤3:在module.json5中注册DataAbility
确保module.json5中包含FlightDataAbility的声明,系统已自动生成,请检查uri是否正确。

"abilities": [// ... EntryAbility{"name": "FlightDataAbility","srcEntry": "./ets/ability/FlightDataAbility.ets","description": "$string:FlightDataAbility_desc","icon": "$media:icon","label": "$string:FlightDataAbility_label","startWindowIcon": "$media:icon","startWindowBackground": "$color:start_window_background","type": "data","uri": "dataability://com.example.lowaltitudeapp.FlightDataAbility"}
]

步骤4:创建数据生产者
entry/src/main/ets/service/FlightDataProducer.ets中创建一个类,用于向DataAbility写入数据。

// entry/src/main/ets/service/FlightDataProducer.ets
import { DataAbilityHelper, ValuesBucket } from '@ohos.data.dataAbility';
import hilog from '@ohos.hilog';
import { BusinessError } from '@kit.BasicServicesKit';
const TAG = 'FlightDataProducer';
const DATA_URI = 'dataability://com.example.lowaltitudeapp.FlightDataAbility';
export class FlightDataProducer {private dataAbilityHelper: DataAbilityHelper | null = null;constructor() {this.dataAbilityHelper = DataAbilityHelper.createDataAbilityHelper(getContext(this));}async publishFlightLog(log: string, battery: number, gps: string) {if (!this.dataAbilityHelper) {hilog.error(0x0000, TAG, 'DataAbilityHelper is null.');return;}const valueBucket: ValuesBucket = {deviceId: 'UAV-001', // 实际应为设备唯一IDtimestamp: Date.now(),battery: battery,gps: gps,log: log};try {const uri = await this.dataAbilityHelper.insert(DATA_URI, valueBucket);hilog.info(0x0000, TAG, `Successfully published log to URI: ${uri}`);} catch (error) {const err = error as BusinessError;hilog.error(0x0000, TAG, `Failed to publish log. Code: ${err.code}, message: ${err.message}`);}}
}
模块3:多机组网智慧控制

这是本教程的核心,我们将使用分布式数据对象实现领航者-跟随者模式。
步骤1:创建组网控制服务
entry/src/main/ets/service/DroneSwarmControl.ets中创建单例服务。

// entry/src/main/ets/service/DroneSwarmControl.ets
import deviceManager from '@ohos.distributedDevice';
import distributedObject from '@ohos.data.distributedDataObject';
import hilog from '@ohos.hilog';
import { BusinessError } from '@kit.BasicServicesKit';
const TAG = 'DroneSwarmControl';
// 定义同步的数据结构
class MissionData {id: number = 0;description: string = 'Standby';status: 'pending' | 'in-progress' | 'completed' = 'pending';
}
export class DroneSwarmControl {private static instance: DroneSwarmControl;private dmInstance: deviceManager.DeviceManager | null = null;private deviceList: deviceManager.DeviceInfo[] = [];private localObject: distributedObject.DataObject<MissionData> | null = null;private sessionId: string = 'low-altitude-mission-session';private constructor() {}public static getInstance(): DroneSwarmControl {if (!DroneSwarmControl.instance) {DroneSwarmControl.instance = new DroneSwarmControl();}return DroneSwarmControl.instance;}// 初始化设备管理器async initDeviceManager(): Promise<void> {try {this.dmInstance = await deviceManager.createDeviceManager('com.example.lowaltitudeapp');this.dmInstance.on('deviceStateChange', (data) => {hilog.info(0x0000, TAG, `Device state changed: ${JSON.stringify(data)}`);this.refreshDeviceList();});this.dmInstance.on('deviceFound', (data) => {hilog.info(0x0000, TAG, `Device found: ${JSON.stringify(data)}`);});hilog.info(0x0000, TAG, 'DeviceManager initialized.');} catch (error) {const err = error as BusinessError;hilog.error(0x0000, TAG, `Init DeviceManager failed: ${err.message}`);}}// 开始发现设备startDeviceDiscovery() {if (!this.dmInstance) {hilog.error(0x0000, TAG, 'DeviceManager not initialized.');return;}let extraInfo = {'targetPkgName': 'com.example.lowaltitudeapp','appName': 'LowAltitudeApp'};let subscribeId = Math.floor(Math.random() * 10000);this.dmInstance.startDeviceDiscovery(subscribeId, extraInfo);hilog.info(0x0000, TAG, 'Start discovering devices...');}// 刷新设备列表private refreshDeviceList() {if (!this.dmInstance) return;this.deviceList = this.dmInstance.getTrustedDeviceListSync();}getDeviceList(): deviceManager.DeviceInfo[] {return this.deviceList;}// 认证设备async authenticateDevice(deviceId: string) {if (!this.dmInstance) return;let authParam = {'authType': 1, // 1: 点对点认证'extraInfo': {}};this.dmInstance.authenticateDevice(deviceId, authParam, (err, data) => {if (err) {hilog.error(0x0000, TAG, `Auth failed: ${err.message}`);} else {hilog.info(0x0000, TAG, `Auth success: ${JSON.stringify(data)}`);}});}// --- 分布式数据对象操作 ---// 作为领航者,创建并设置数据createAsLeader() {this.localObject = distributedObject.create(getContext(this), new MissionData());hilog.info(0x0000, TAG, 'Created local distributed object as Leader.');}// 作为跟随者,加入会话async joinAsFollower() {this.localObject = distributedObject.create(getContext(this), new MissionData());try {await this.localObject.setSessionId(this.sessionId);hilog.info(0x0000, TAG, 'Joined session as Follower.');// 监听数据变化this.localObject.on('change', (changeData) => {hilog.info(0x0000, TAG, `Mission data changed: ${JSON.stringify(changeData)}`);AppStorage.SetOrCreate<MissionData>('mission', this.localObject!);});} catch (error) {const err = error as BusinessError;hilog.error(0x0000, TAG, `Join session failed: ${err.message}`);}}// 领航者发布任务publishMission(mission: MissionData) {if (!this.localObject) {hilog.error(0x0000, TAG, 'Local object not created.');return;}this.localObject.id = mission.id;this.localObject.description = mission.description;this.localObject.status = mission.status;hilog.info(0x0000, TAG, `Published mission: ${JSON.stringify(mission)}`);}// 领航者将数据对象同步到网络async syncToNetwork(deviceId: string) {if (!this.localObject) return;try {const sessionId = await this.localObject.setSessionId(this.sessionId);await this.localObject.syncToNetwork([deviceId]);hilog.info(0x0000, TAG, `Synced mission to device: ${deviceId}`);} catch (error) {const err = error as BusinessError;hilog.error(0x0000, TAG, `Sync to network failed: ${err.message}`);}}
}

第四部分:UI集成与场景模拟

现在,我们将所有后端逻辑整合到用户界面中。
步骤1:创建地面站视图
entry/src/main/ets/pages/GroundStationView.ets中创建领航者界面。

// entry/src/main/ets/pages/GroundStationView.ets
import { deviceManager } from '@ohos.distributedDevice';
import { DroneSwarmControl } from '../service/DroneSwarmControl';
import { FlightDataProducer } from '../service/FlightDataProducer';
import { DataAbilityHelper } from '@ohos.data.dataAbility';
import { rdb } from '@kit.ArkData';
@Entry
@Component
struct GroundStationView {@State deviceList: deviceManager.DeviceInfo[] = [];@State missionDescription: string = 'Patrol Area A';@State logs: string[] = [];private swarmControl: DroneSwarmControl = DroneSwarmControl.getInstance();private dataProducer: FlightDataProducer = new FlightDataProducer();private dataHelper: DataAbilityHelper = DataAbilityHelper.createDataAbilityHelper(getContext(this));aboutToAppear() {this.swarmControl.initDeviceManager();this.swarmControl.createAsLeader(); // 地面站作为领航者}build() {Column() {Text('Ground Station - Leader').fontSize(24).fontWeight(FontWeight.Bold).margin(10)// --- 设备发现与控制 ---Text('1. Discover & Control Drones').fontSize(18).margin({ top: 20, bottom: 10 }).alignSelf(ItemAlign.Start)Button('Discover Drones').onClick(() => {this.swarmControl.startDeviceDiscovery();setTimeout(() => {this.deviceList = this.swarmControl.getDeviceList();}, 3000); // 3秒后刷新列表}).width('80%')List({ space: 10 }) {ForEach(this.deviceList, (device: deviceManager.DeviceInfo) => {ListItem() {Row() {Column() {Text(device.deviceName).fontSize(16)Text(device.networkId).fontSize(12).fontColor(Color.Grey)}.alignItems(HorizontalAlign.Start).layoutWeight(1)Button('Sync Mission').onClick(() => {const mission = {id: Date.now(),description: this.missionDescription,status: 'pending' as const};this.swarmControl.publishMission(mission);this.swarmControl.syncToNetwork(device.networkId);})}.width('100%').padding(10).backgroundColor(Color.White).borderRadius(8).shadow({ radius: 4, color: '#CCCCCC' })}})}.width('90%').layoutWeight(1)// --- 任务发布 ---Text('2. Mission Control').fontSize(18).margin({ top: 20, bottom: 10 }).alignSelf(ItemAlign.Start)TextInput({ placeholder: 'Enter mission description' }).onChange((value: string) => {this.missionDescription = value;}).width('90%').margin({ bottom: 10 })// --- 数据订阅 ---Text('3. Flight Data Monitor').fontSize(18).margin({ top: 20, bottom: 10 }).alignSelf(ItemAlign.Start)Button('Query Latest Logs').onClick(async () => {try {const predicates = new rdb.DataAbilityPredicates();predicates.orderByDesc('timestamp').limitAs(5);const result = await this.dataHelper.query('dataability://com.example.lowaltitudeapp.FlightDataAbility',['log', 'timestamp'],predicates);this.logs = [];while (result.goToNextRow()) {const log = result.getString(0);const timestamp = result.getLong(1);this.logs.push(`[${new Date(timestamp).toLocaleTimeString()}] ${log}`);}result.close();} catch (error) {console.error('Query logs failed', error);}}).width('80%')List({ space: 5 }) {ForEach(this.logs, (log: string) => {ListItem() {Text(log).fontSize(12).width('100%')}})}.width('90%').height(150).backgroundColor('#F0F0F0').borderRadius(8)}.width('100%').height('100%').padding(10).backgroundColor('#EFEFEF')}
}

步骤2:创建无人机机载视图
entry/src/main/ets/pages/UAVOnboardView.ets中创建跟随者界面。

// entry/src/main/ets/pages/UAVOnboardView.ets
import { DroneSwarmControl } from '../service/DroneSwarmControl';
import { AIDetectionService } from '../service/AIDetectionService';
import { FlightDataProducer } from '../service/FlightDataProducer';
import { MissionData } from '../service/DroneSwarmControl';
import { image } from '@kit.ImageKit';
@Entry
@Component
struct UAVOnboardView {@State aiResult: string = 'AI Standby';@State mission: MissionData = new MissionData();private swarmControl: DroneSwarmControl = DroneSwarmControl.getInstance();private aiService: AIDetectionService = new AIDetectionService();private dataProducer: FlightDataProducer = new FlightDataProducer();// 模拟一个图像源,实际应来自相机private mockImagePixelMap: image.PixelMap | null = null;aboutToAppear() {this.swarmControl.initDeviceManager();this.swarmControl.joinAsFollower(); // 无人机作为跟随者this.aiService.initModel();this.mockCameraFeed();}// 模拟相机数据流和日志发布private mockCameraFeed() {setInterval(() => {// 模拟发布飞行日志this.dataProducer.publishFlightLog(`AI result: ${this.aiResult}`,85.5 + Math.random() * 10,'120.123, 30.456');}, 5000);}build() {Column() {Text('UAV Onboard - Follower').fontSize(24).fontWeight(FontWeight.Bold).margin(10)// --- AI感知 ---Text('1. AI Obstacle Detection').fontSize(18).margin({ top: 20, bottom: 10 }).alignSelf(ItemAlign.Start)Button('Run AI Inference (Mock)').onClick(async () => {// 实际应用中,这里应获取相机最新的PixelMap// this.mockImagePixelMap = await getLatestCameraPixelMap();// if (this.mockImagePixelMap) {//   this.aiResult = await this.aiService.runInference(this.mockImagePixelMap);// }// 模拟推理结果this.aiResult = `Detected: Person with 95.50% confidence`;}).width('80%')Text(this.aiResult).fontSize(14).margin({ top: 10 }).fontColor(Color.Red).width('90%')// --- 任务接收 ---Text('2. Mission from Leader').fontSize(18).margin({ top: 20, bottom: 10 }).alignSelf(ItemAlign.Start)Column() {Text(`ID: ${this.mission.id}`)Text(`Description: ${this.mission.description}`)Text(`Status: ${this.mission.status}`)}.width('90%').padding(15).backgroundColor(Color.White).borderRadius(8).shadow({ radius: 4, color: '#CCCCCC' })// 使用@StorageProp链接到分布式数据对象的变化.onAppear(() => {AppStorage.SetOrCreate<MissionData>('mission', this.mission);}).onDisAppear(() => {AppStorage.Delete('mission');})// --- 数据发布状态 ---Text('3. Data Publishing Status').fontSize(18).margin({ top: 20, bottom: 10 }).alignSelf(ItemAlign.Start)Text('Publishing flight logs every 5 seconds...').fontSize(14).fontColor(Color.Green)}.width('100%').height('100%').padding(10).backgroundColor('#EFEFEF')}// 使用@StorageProp监听AppStorage中的'mission'变化@StorageProp('mission') missionFromStorage: MissionData = new MissionData();// 当@StorageProp的值变化时,更新本地状态missionStatusChange() {this.mission = this.missionFromStorage;}// 在build中调用此方法以触发状态更新build() {this.missionStatusChange();// ... 其余UI代码}
}

修正:为了更清晰地响应数据变化,我们直接在DroneSwarmControlon('change')回调中更新AppStorage,然后在UAVOnboardView中使用@StorageProp来获取它。这是更推荐的做法。上面的UAVOnboardView代码已按此逻辑修改。
步骤3:整合所有视图
修改主入口entry/src/main/ets/pages/Index.ets,使用Tabs来切换两个角色视图。

// entry/src/main/ets/pages/Index.ets
import { GroundStationView } from './GroundStationView';
import { UAVOnboardView } from './UAVOnboardView';
@Entry
@Component
struct Index {@State currentIndex: number = 0;@Builder TabBuilder(title: string, targetIndex: number) {Column() {Text(title).fontSize(16).fontWeight(this.currentIndex === targetIndex ? FontWeight.Bold : FontWeight.Normal).fontColor(this.currentIndex === targetIndex ? Color.Blue : Color.Grey)}}build() {Tabs({ barPosition: BarPosition.End, index: this.currentIndex }) {TabContent() {GroundStationView()}.tabBar(this.TabBuilder('Ground Station', 0))TabContent() {UAVOnboardView()}.tabBar(this.TabBuilder('UAV Onboard', 1))}.onChange((index: number) => {this.currentIndex = index;}).width('100%').height('100%').backgroundColor('#F1F3F5')}
}

第五部分:运行与验证

  1. 编译与安装:将应用编译并安装到至少两台OpenHarmony 6.0设备上(可以是两台开发板或两台手机)。
  2. 授权:在两台设备上首次运行应用时,系统会弹出权限请求,请全部允许。
  3. 模拟场景
    • 设备A上,切换到Ground Station标签页。点击“Discover Drones”按钮。
    • 设备B上,切换到UAV Onboard标签页。它将自动尝试加入分布式网络。
    • 回到设备A,稍等片刻,设备列表中应该会出现设备B。
    • 设备A上,修改任务描述(例如改为“Inspect Powerline”),然后点击设备B旁边的“Sync Mission”按钮。
    • 观察设备B的“Mission from Leader”区域,您应该能看到任务信息实时更新。
    • 设备B上,点击“Run AI Inference (Mock)”按钮,模拟AI检测。
    • 设备A上,点击“Query Latest Logs”按钮,稍等片刻,您应该能看到从设备B发布的包含AI结果的日志。
http://www.dtcms.com/a/502851.html

相关文章:

  • 专业做网站排名的人做短视频网站
  • 从协议到工程:一款超低延迟RTSP/RTMP播放器的系统级设计剖析
  • Visio 2024 下载安装教程,安装包
  • 郑州做网站公司+卓美电子商务网页设计试题
  • Java 大视界 -- 基于 Java 的大数据实时流处理在工业物联网设备状态监测中的应用与挑战
  • ESP3266 NodeMCU 使用Arduino点亮 ST7789 240x240 tft屏
  • OpenHarmony平台大语言模型本地推理:llama深度适配与部署技术详解
  • OpenHarmony 的 DataAbility:从 URI 到跨设备数据共享的完整解析
  • ipv6 over ipv4隧道技术
  • 谷歌下载官网舆情优化公司
  • 桐城网站设计做小程序用什么软件
  • 【小学教辅】六年级上册语文知识点课课贴(8页)PDF 重点课文解析 生字词易错题整理 电子版可下载打印|夸克网盘
  • 17.AVL树的实现(一)
  • 如何向文件夹内所有PDF增加水印
  • 动态规划的“生成”之美:三路指针,优雅构建「丑数」序列
  • 高并发系统中的限流与异步优化实战指南
  • agent设计模式:第一章节—提示链
  • 【STM32】RTC实时时钟
  • 【数据结构与算法基础】04. 线性表与链表详解(C++ 实战)
  • C程序中的预处理器
  • 长沙黄页全域seo
  • 负载均衡技术:Nginx/HAProxy/F5 等负载均衡配置与优化
  • 外国人做的关于中国的视频网站吗高师院校语言类课程体系改革与建设 教学成果奖申报网站
  • Linux 进阶指令实操指南:文件查看、时间管理、搜索压缩全场景覆盖(附高频案例)
  • K8S(十六)—— K8S集群apiserver证书有效期修改指南(适配v1.20.11版本)
  • Altium Designer(AD24)Reports报告功能总结
  • 第一章 绪论——课后习题解练【数据结构(c语言版 第2版)】
  • Ubuntu 系统 RabbitMQ 安装指南与使用(含 C++ 客户端与 SSL 错误解决)
  • 网站开发外包 价格阿里巴巴国际站入驻费用及条件
  • MVVM架构模式详解:从原理到Android实战