鸿蒙植物识别小助手开发指南 原创

进修的泡芙
发布于 2025-6-22 16:30
浏览
0收藏

鸿蒙植物识别小助手开发指南

一、系统架构设计

基于HarmonyOS的AI能力和分布式技术,我们设计了一套植物识别小助手系统,主要功能包括:
植物拍照:使用设备相机拍摄植物照片

AI识别:识别植物种类和相关信息

知识库展示:显示植物详细信息

多设备同步:跨设备同步识别记录和收藏

社区分享:分享植物发现到社区

!https://example.com/harmony-plant-arch.png

二、核心代码实现
相机服务管理

// PlantCameraService.ets
import camera from ‘@ohos.multimedia.camera’;
import image from ‘@ohos.multimedia.image’;

class PlantCameraService {
private static instance: PlantCameraService;
private cameraManager: camera.CameraManager;
private cameraInput: camera.CameraInput | null = null;
private previewOutput: camera.PreviewOutput | null = null;
private photoOutput: camera.PhotoOutput | null = null;

private constructor() {
this.cameraManager = camera.getCameraManager();
public static getInstance(): PlantCameraService {

if (!PlantCameraService.instance) {
  PlantCameraService.instance = new PlantCameraService();

return PlantCameraService.instance;

public async initCamera(previewSurfaceId: string): Promise<void> {

const cameras = this.cameraManager.getSupportedCameras();
if (cameras.length === 0) {
  throw new Error('No camera available');

this.cameraInput = this.cameraManager.createCameraInput(cameras[0]);

await this.cameraInput.open();

const previewProfile = this.cameraManager.getSupportedOutputCapability(
  cameras[0],
  camera.ProfileMode.PROFILE_MODE_DEFAULT
).previewProfiles[0];

this.previewOutput = this.cameraManager.createPreviewOutput(
  previewProfile,
  previewSurfaceId
);

const photoProfiles = this.cameraManager.getSupportedOutputCapability(
  cameras[0],
  camera.ProfileMode.PROFILE_MODE_DEFAULT
).photoProfiles;

this.photoOutput = this.cameraManager.createPhotoOutput(
  photoProfiles[0]
);

public async takePlantPhoto(): Promise<image.Image> {

if (!this.photoOutput) {
  throw new Error('Photo output not initialized');

const photoSettings = {

  quality: camera.QualityLevel.QUALITY_LEVEL_HIGH,
  rotation: camera.ImageRotation.ROTATION_0
};

return new Promise((resolve, reject) => {
  this.photoOutput!.capture(photoSettings, (err, image) => {
    if (err) {
      reject(err);

else {

      resolve(image);

});

});

}

export const plantCameraService = PlantCameraService.getInstance();

植物识别服务

// PlantRecognitionService.ets
import image from ‘@ohos.multimedia.image’;
import http from ‘@ohos.net.http’;

class PlantRecognitionService {
private static instance: PlantRecognitionService;
private httpClient: http.HttpRequest;
private apiKey = ‘YOUR_PLANT_API_KEY’;

private constructor() {
this.httpClient = http.createHttp();
public static getInstance(): PlantRecognitionService {

if (!PlantRecognitionService.instance) {
  PlantRecognitionService.instance = new PlantRecognitionService();

return PlantRecognitionService.instance;

public async recognizePlant(image: image.Image): Promise<PlantInfo> {

const formData = new FormData();
formData.append('image', image);
formData.append('api_key', this.apiKey);

return new Promise((resolve, reject) => {
  this.httpClient.request(
    'https://plant-api.example.com/identify',

method: ‘POST’,

      header: { 'Content-Type': 'multipart/form-data' },
      extraData: formData
    },
    (err, data) => {
      if (err) {
        reject(err);

else {

        const result = JSON.parse(data.result);
        resolve(this.processPlantInfo(result));

}

  );
});

private processPlantInfo(rawData: any): PlantInfo {

return {
  name: rawData.species.commonNames[0] || '未知植物',
  scientificName: rawData.species.scientificName,
  probability: rawData.probability,
  description: rawData.species.description,
  careTips: rawData.species.careTips || [],
  similarPlants: rawData.similarSpecies || []
};

}

export const plantRecognitionService = PlantRecognitionService.getInstance();

多设备同步服务

// PlantSyncService.ets
import distributedData from ‘@ohos.data.distributedData’;
import deviceManager from ‘@ohos.distributedHardware.deviceManager’;

class PlantSyncService {
private static instance: PlantSyncService;
private kvManager: distributedData.KVManager;
private kvStore: distributedData.KVStore;

private constructor() {
this.initKVStore();
private async initKVStore(): Promise<void> {

const config = {
  bundleName: 'com.example.plantAssistant',
  userInfo: { userId: 'currentUser' }
};

this.kvManager = distributedData.createKVManager(config);
this.kvStore = await this.kvManager.getKVStore('plant_store', {
  createIfMissing: true
});

this.kvStore.on('dataChange', (data) => {
  this.handleRemoteUpdate(data);
});

public static getInstance(): PlantSyncService {

if (!PlantSyncService.instance) {
  PlantSyncService.instance = new PlantSyncService();

return PlantSyncService.instance;

public async syncPlantRecord(record: PlantRecord): Promise<void> {

await this.kvStore.put(record_${record.id}, JSON.stringify(record));

public async getPlantRecords(): Promise<PlantRecord[]> {

const entries = await this.kvStore.getEntries('record_');
return Array.from(entries).map(([_, value]) => JSON.parse(value));

public async syncFavorite(plantId: string, isFavorite: boolean): Promise<void> {

await this.kvStore.put(favorite_${plantId}, JSON.stringify(isFavorite));

public async isFavorite(plantId: string): Promise<boolean> {

const value = await this.kvStore.get(favorite_${plantId});
return value ? JSON.parse(value) : false;

private handleRemoteUpdate(data: distributedData.ChangeInfo): void {

if (data.deviceId === deviceInfo.deviceId) return;

const key = data.key as string;
if (key.startsWith('record_')) {
  const record = JSON.parse(data.value);
  EventBus.emit('plantRecordUpdated', record);

else if (key.startsWith(‘favorite_’)) {

  const [plantId, isFavorite] = [key.split('_')[1], JSON.parse(data.value)];
  EventBus.emit('favoriteUpdated', { plantId, isFavorite });

}

export const plantSyncService = PlantSyncService.getInstance();

植物知识库服务

// PlantKnowledgeService.ets
import http from ‘@ohos.net.http’;
import preferences from ‘@ohos.data.preferences’;

class PlantKnowledgeService {
private static instance: PlantKnowledgeService;
private httpClient: http.HttpRequest;
private localCache: Record<string, PlantDetail> = {};

private constructor() {
this.httpClient = http.createHttp();
public static getInstance(): PlantKnowledgeService {

if (!PlantKnowledgeService.instance) {
  PlantKnowledgeService.instance = new PlantKnowledgeService();

return PlantKnowledgeService.instance;

public async getPlantDetail(scientificName: string): Promise<PlantDetail> {

if (this.localCache[scientificName]) {
  return this.localCache[scientificName];

const url = https://plant-api.example.com/details/${encodeURIComponent(scientificName)};

return new Promise((resolve, reject) => {
  this.httpClient.request(
    url,

method: ‘GET’ },

    (err, data) => {
      if (err) {
        reject(err);

else {

        const detail = JSON.parse(data.result);
        this.localCache[scientificName] = detail;
        resolve(detail);

}

  );
});

public async searchPlants(query: string): Promise<PlantSearchResult[]> {

const url = https://plant-api.example.com/search?q=${encodeURIComponent(query)};

return new Promise((resolve, reject) => {
  this.httpClient.request(
    url,

method: ‘GET’ },

    (err, data) => {
      if (err) {
        reject(err);

else {

        resolve(JSON.parse(data.result));

}

  );
});

}

export const plantKnowledgeService = PlantKnowledgeService.getInstance();

三、主界面实现
相机识别界面

// PlantCameraView.ets
@Component
struct PlantCameraView {
@State previewSurfaceId: string = ‘’;
@State capturedImage: image.Image | null = null;
@State isRecognizing: boolean = false;

aboutToAppear() {
this.initCamera();
build() {

Stack() {
  // 相机预览
  XComponent({
    id: 'plantCameraPreview',
    type: 'surface',
    libraryname: 'libcamera.so',
    controller: this.previewController
  })
  .onLoad(() => {
    this.previewSurfaceId = this.previewController.getXComponentSurfaceId();
    plantCameraService.initCamera(this.previewSurfaceId);
  })
  .width('100%')
  .height('100%')
  
  // 拍照按钮
  Button('识别植物')
    .onClick(() => this.captureAndRecognize())
    .position({ x: '50%', y: '90%' })
  
  // 识别状态
  if (this.isRecognizing) {
    LoadingProgress()
      .position({ x: '50%', y: '50%' })

}

private async captureAndRecognize(): Promise<void> {

this.isRecognizing = true;
this.capturedImage = await plantCameraService.takePlantPhoto();
const plantInfo = await plantRecognitionService.recognizePlant(this.capturedImage);

// 保存识别记录
const record: PlantRecord = {
  id: generateId(),
  plantInfo,
  timestamp: Date.now(),
  location: await this.getCurrentLocation()
};

await plantSyncService.syncPlantRecord(record);
EventBus.emit('plantIdentified', plantInfo);
this.isRecognizing = false;

private async getCurrentLocation(): Promise<Location | null> {

try {
  const geoLocationManager = geoLocation.createGeoLocationManager();
  const location = await geoLocationManager.getCurrentLocation();
  return {
    latitude: location.latitude,
    longitude: location.longitude
  };

catch (err) {

  console.error('获取位置失败:', err);
  return null;

}

function generateId(): string {

return Math.random().toString(36).substring(2) + Date.now().toString(36);

植物详情界面

// PlantDetailView.ets
@Component
struct PlantDetailView {
@State plantInfo: PlantInfo | null = null;
@State plantDetail: PlantDetail | null = null;
@State isFavorite: boolean = false;

aboutToAppear() {
EventBus.on(‘plantIdentified’, (info) => {
this.plantInfo = info;
this.loadPlantDetail();
this.checkFavoriteStatus();
});
build() {

Column() {
  if (this.plantInfo && this.plantDetail) {
    Scroll() {
      Column() {
        // 植物基本信息
        Text(this.plantInfo.name)
          .fontSize(24)
          .fontWeight(FontWeight.Bold)
          
        Text(this.plantInfo.scientificName)
          .fontSize(16)
          .fontColor('#666666')
          
        Text(识别准确率: ${(this.plantInfo.probability * 100).toFixed(1)}%)
          .fontSize(14)
          .margin({ top: 8 })
        
        // 植物图片
        Image(this.plantDetail.imageUrl)
          .width('100%')
          .aspectRatio(1)
          .margin({ top: 16 })
        
        // 植物详情
        Text('植物描述')
          .fontSize(18)
          .fontWeight(FontWeight.Bold)
          .margin({ top: 24 })
          
        Text(this.plantDetail.description)
          .fontSize(16)
          .margin({ top: 8 })
        
        // 养护指南
        if (this.plantDetail.careGuide) {
          Text('养护指南')
            .fontSize(18)
            .fontWeight(FontWeight.Bold)
            .margin({ top: 24 })
            
          ForEach(this.plantDetail.careGuide, (item) => {
            Column() {
              Text(• {item.title}: {item.description})
                .fontSize(16)
                .margin({ top: 8 })

})

}

      .padding(16)

// 收藏按钮

    Button(this.isFavorite ? '已收藏' : '收藏')
      .onClick(() => this.toggleFavorite())
      .margin(16)

}

private async loadPlantDetail(): Promise<void> {

if (this.plantInfo) {
  this.plantDetail = await plantKnowledgeService.getPlantDetail(
    this.plantInfo.scientificName
  );

}

private async checkFavoriteStatus(): Promise<void> {
if (this.plantInfo) {
this.isFavorite = await plantSyncService.isFavorite(this.plantInfo.scientificName);
}

private async toggleFavorite(): Promise<void> {
if (this.plantInfo) {
const newStatus = !this.isFavorite;
await plantSyncService.syncFavorite(this.plantInfo.scientificName, newStatus);
this.isFavorite = newStatus;
}

识别记录界面

// PlantHistoryView.ets
@Component
struct PlantHistoryView {
@State records: PlantRecord[] = [];

aboutToAppear() {
this.loadRecords();
EventBus.on(‘plantRecordUpdated’, () => this.loadRecords());
build() {

Column() {
  Text('识别记录')
    .fontSize(24)
    .fontWeight(FontWeight.Bold)
    .margin({ top: 16, left: 16 })
  
  if (this.records.length === 0) {
    Text('暂无识别记录')
      .fontSize(16)
      .margin({ top: 32 })

else {

    List({ space: 10 }) {
      ForEach(this.records, (record) => {
        ListItem() {
          PlantRecordItem({ record })

})

.layoutWeight(1)

}

private async loadRecords(): Promise<void> {

this.records = await plantSyncService.getPlantRecords();
this.records.sort((a, b) => b.timestamp - a.timestamp);

}

@Component
struct PlantRecordItem {
private record: PlantRecord;

build() {
Row() {
Image(this.record.plantInfo.imageUrl || ‘resources/plant_placeholder.png’)
.width(80)
.height(80)
.objectFit(ImageFit.Cover)
.borderRadius(8)

  Column() {
    Text(this.record.plantInfo.name)
      .fontSize(18)
      .fontWeight(FontWeight.Bold)
    
    Text(this.record.plantInfo.scientificName)
      .fontSize(14)
      .fontColor('#666666')
      .margin({ top: 4 })
    
    Text(formatDate(this.record.timestamp))
      .fontSize(12)
      .fontColor('#999999')
      .margin({ top: 8 })

.margin({ left: 12 })

  .layoutWeight(1)

.padding(12)

.onClick(() => {
  EventBus.emit('showPlantDetail', this.record.plantInfo);
})

}

function formatDate(timestamp: number): string {
const date = new Date(timestamp);
return {date.getFullYear()}-{date.getMonth() + 1}-{date.getDate()} {date.getHours()}:${date.getMinutes()};

四、高级功能实现
多设备协同识别

// CollaborativePlantRecognition.ets
class CollaborativePlantRecognition {
private static instance: CollaborativePlantRecognition;

private constructor() {}

public static getInstance(): CollaborativePlantRecognition {
if (!CollaborativePlantRecognition.instance) {
CollaborativePlantRecognition.instance = new CollaborativePlantRecognition();
return CollaborativePlantRecognition.instance;

public async startGroupRecognition(groupId: string): Promise<void> {

const devices = await deviceManager.getTrustedDevices();
await Promise.all(devices.map(device => 
  this.inviteDeviceToGroup(device.id, groupId)
));

private async inviteDeviceToGroup(deviceId: string, groupId: string): Promise<void> {

const ability = await featureAbility.startAbility({
  bundleName: 'com.example.plantAssistant',
  abilityName: 'PlantGroupAbility',
  deviceId
});

await ability.call({
  method: 'joinGroup',
  parameters: [groupId]
});

public async sharePlantRecord(record: PlantRecord, groupId: string): Promise<void> {

await plantSyncService.syncPlantRecord(record);

const devices = await deviceManager.getTrustedDevices();
await Promise.all(devices.map(device => 
  this.sendRecordToDevice(device.id, record, groupId)
));

private async sendRecordToDevice(deviceId: string, record: PlantRecord, groupId: string): Promise<void> {

const ability = await featureAbility.startAbility({
  bundleName: 'com.example.plantAssistant',
  abilityName: 'PlantGroupAbility',
  deviceId
});

await ability.call({
  method: 'receivePlantRecord',
  parameters: [record, groupId]
});

}

export const collaborativeRecognition = CollaborativePlantRecognition.getInstance();

植物社区功能

// PlantCommunityService.ets
import http from ‘@ohos.net.http’;

class PlantCommunityService {
private static instance: PlantCommunityService;
private httpClient: http.HttpRequest;

private constructor() {
this.httpClient = http.createHttp();
public static getInstance(): PlantCommunityService {

if (!PlantCommunityService.instance) {
  PlantCommunityService.instance = new PlantCommunityService();

return PlantCommunityService.instance;

public async sharePlantDiscovery(record: PlantRecord, comment: string): Promise<void> {

const url = 'https://plant-community.example.com/posts';

return new Promise((resolve, reject) => {
  this.httpClient.request(
    url,

method: ‘POST’,

      header: { 'Content-Type': 'application/json' },
      extraData: JSON.stringify({
        plantRecord: record,
        comment,
        userId: 'currentUser',
        timestamp: Date.now()
      })
    },
    (err, data) => {
      if (err) {
        reject(err);

else {

        resolve();

}

  );
});

public async getRecentDiscoveries(): Promise<CommunityPost[]> {

const url = 'https://plant-community.example.com/posts/recent';

return new Promise((resolve, reject) => {
  this.httpClient.request(
    url,

method: ‘GET’ },

    (err, data) => {
      if (err) {
        reject(err);

else {

        resolve(JSON.parse(data.result));

}

  );
});

}

export const plantCommunityService = PlantCommunityService.getInstance();

植物养护提醒

// PlantCareReminder.ets
import reminderAgent from ‘@ohos.reminderAgent’;
import preferences from ‘@ohos.data.preferences’;

class PlantCareReminder {
private static instance: PlantCareReminder;

private constructor() {}

public static getInstance(): PlantCareReminder {
if (!PlantCareReminder.instance) {
PlantCareReminder.instance = new PlantCareReminder();
return PlantCareReminder.instance;

public async scheduleCareReminder(plantId: string, careType: string, intervalDays: number): Promise<void> {

const reminderRequest: reminderAgent.ReminderRequest = {
  reminderType: reminderAgent.ReminderType.REMINDER_TYPE_TIMER,
  actionButton: [{ title: '已完成' }, { title: '稍后提醒' }],
  wantAgent: {
    pkgName: 'com.example.plantAssistant',
    abilityName: 'PlantCareReminderAbility'
  },
  ringDuration: 60,
  snoozeTimes: 3,
  timeInterval: intervalDays  24  60 * 60,
  title: 植物养护提醒,
  content: 该给您的{this.getPlantName(plantId)}{this.getCareAction(careType)}了,
  expiredContent: "养护提醒已过期"
};

const reminderId = await reminderAgent.publishReminder(reminderRequest);
await this.saveReminderId(plantId, careType, reminderId);

private async saveReminderId(plantId: string, careType: string, reminderId: number): Promise<void> {

const prefs = await preferences.getPreferences('plant_reminders');
await prefs.put({plantId}_{careType}, reminderId.toString());
await prefs.flush();

private getCareAction(careType: string): string {

switch (careType) {
  case 'water': return '浇水';
  case 'fertilize': return '施肥';
  case 'prune': return '修剪';
  default: return '养护';

}

private async getPlantName(plantId: string): Promise<string> {
const records = await plantSyncService.getPlantRecords();
const record = records.find(r => r.plantInfo.scientificName === plantId);
return record?.plantInfo.name || ‘植物’;
}

export const plantCareReminder = PlantCareReminder.getInstance();

五、总结

本植物识别小助手实现了以下核心价值:
精准识别:利用AI技术准确识别常见室内外植物

知识丰富:提供详细的植物信息和养护指南

多设备协同:支持跨设备同步识别记录和收藏

社区互动:分享植物发现到社区,与其他植物爱好者交流

扩展方向:
增加AR植物识别功能

开发植物生长追踪功能

集成智能家居系统,自动调节植物生长环境

增加病虫害识别和防治建议

注意事项:
需要申请ohos.permission.CAMERA权限

植物识别准确率受图片质量和植物特征可见度影响

部分功能需要网络连接

多设备协同需保持网络连接

位置服务需要ohos.permission.LOCATION权限

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
收藏
回复
举报
回复
    相关推荐