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文件,添加必要的权限。分布式功能和数据访问需要明确的权限声明。

    json 复制代码
    {
      "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的依赖。

    json 复制代码
    {
      "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文件。
typescript 复制代码
// 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); // R
      normalizedData.push(float32Data[i + 1] / 255.0); // G
      normalizedData.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文件。

typescript 复制代码
// 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');
    // 初始化RdbStore
    rdb.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是否正确。

json 复制代码
"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写入数据。

typescript 复制代码
// 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', // 实际应为设备唯一ID
      timestamp: 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中创建单例服务。

typescript 复制代码
// 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中创建领航者界面。

typescript 复制代码
// 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中创建跟随者界面。

typescript 复制代码
// 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来切换两个角色视图。

typescript 复制代码
// 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结果的日志。
相关推荐
大雷神6 小时前
【成长纪实】Flutter中Dart 与Harmony中 ArkTS 异步编程对比:从 Future 到 Promise
flutter·harmonyos
Swift社区6 小时前
在 HarmonyOS 中平滑切换“点状粒子”与“图片粒子”(含可运行 ArkTS 示例)
华为·harmonyos
猫林老师6 小时前
HarmonyOS多媒体开发:自定义相机与音频播放器实战
数码相机·音视频·harmonyos
逻极6 小时前
HarmonyOS 5 鸿蒙应用签名机制与安全开发实战指南
harmonyos
TTGGGFF7 小时前
机器视觉:智能车大赛视觉组技术文档——用 YOLO3 Nano 实现目标检测并部署到 OpenART
人工智能·目标检测·计算机视觉
CodeJourney.7 小时前
Python开发可视化音乐播放器教程(附代码)
数据库·人工智能·python
强德亨上校7 小时前
神经网络详解
人工智能·深度学习·神经网络
视***间7 小时前
275TOPS算力边缘计算盒子的价值洞察与市场定位---视程空间
人工智能·边缘计算
AI模块工坊7 小时前
AAAI 2025 | 即插即用,川大Mesorch刷新SOTA,用「介观」Transformer架构终结图像造假
人工智能·深度学习·计算机视觉·架构·transformer