鸿蒙跨端植物健康诊断系统开发指南
·
鸿蒙跨端植物健康诊断系统开发指南
一、项目概述
本文基于HarmonyOS的AI植物病害检测能力和分布式技术,开发一款智能植物健康诊断系统。该系统能够通过摄像头识别植物病害,记录植物位置信息,并将诊断结果同步到多设备,借鉴了《鸿蒙跨端U同步》中多设备数据同步的技术原理。
二、系统架构
+---------------------+ +---------------------+ +---------------------+
| 主设备 |<----->| 分布式数据总线 |<----->| 从设备 |
| (手机/平板) | | (Distributed Bus) | | (电脑/其他终端) |
+----------+----------+ +----------+----------+ +----------+----------+
| | |
+----------v----------+ +----------v----------+ +----------v----------+
| 病害检测模块 | | 位置记录模块 | | 数据同步模块 |
| (Disease Detect) | | (Location Tracker) | | (Data Sync) |
+---------------------+ +---------------------+ +---------------------+
三、核心代码实现
1. 植物健康服务
// src/main/ets/service/PlantHealthService.ts
import { distributedData } from '@ohos.data.distributedData';
import { BusinessError } from '@ohos.base';
import { camera } from '@ohos.multimedia.camera';
import { image } from '@ohos.multimedia.image';
import { plantDisease } from '@ohos.ai.plantDisease';
import { geolocation } from '@ohos.geolocation';
interface PlantRecord {
id: string;
name: string;
type: string;
location: {
latitude: number;
longitude: number;
address?: string;
};
images: string[];
diseases: PlantDisease[];
healthScore: number;
timestamp: number;
}
interface PlantDisease {
name: string;
confidence: number;
treatment: string[];
prevention: string[];
}
export class PlantHealthService {
private static instance: PlantHealthService;
private kvStore: distributedData.KVStore | null = null;
private readonly STORE_ID = 'plant_health_store';
private cameraInput: camera.CameraInput | null = null;
private previewOutput: camera.PreviewOutput | null = null;
private diseaseDetector: plantDisease.PlantDiseaseDetector | null = null;
private currentRecord: PlantRecord | null = null;
private plantRecords: PlantRecord[] = [];
private constructor() {
this.initKVStore();
this.initCamera();
this.initDiseaseDetector();
}
public static getInstance(): PlantHealthService {
if (!PlantHealthService.instance) {
PlantHealthService.instance = new PlantHealthService();
}
return PlantHealthService.instance;
}
private async initKVStore(): Promise<void> {
try {
const options: distributedData.KVManagerConfig = {
bundleName: 'com.example.planthealth',
userInfo: {
userId: '0',
userType: distributedData.UserType.SAME_USER_ID
}
};
const kvManager = distributedData.createKVManager(options);
this.kvStore = await kvManager.getKVStore({
storeId: this.STORE_ID,
options: {
createIfMissing: true,
encrypt: false,
backup: false,
autoSync: true,
kvStoreType: distributedData.KVStoreType.SINGLE_VERSION
}
});
this.kvStore.on('dataChange', distributedData.SubscribeType.SUBSCRIBE_TYPE_REMOTE, (data) => {
data.insertEntries.forEach((entry: distributedData.Entry) => {
if (entry.key === 'plant_records') {
this.notifyRecordsChange(entry.value.value as PlantRecord[]);
}
});
});
} catch (e) {
console.error(`Failed to initialize KVStore. Code: ${e.code}, message: ${e.message}`);
}
}
private async initCamera(): Promise<void> {
try {
const cameraManager = camera.getCameraManager();
const cameras = cameraManager.getSupportedCameras();
if (cameras.length === 0) {
console.error('No camera available');
return;
}
// 使用后置摄像头
this.cameraInput = cameraManager.createCameraInput(cameras[0]);
await this.cameraInput.open();
// 创建预览输出
const surfaceId = 'previewSurface';
this.previewOutput = cameraManager.createPreviewOutput(surfaceId);
// 创建会话并开始预览
const captureSession = cameraManager.createCaptureSession();
await captureSession.beginConfig();
await captureSession.addInput(this.cameraInput);
await captureSession.addOutput(this.previewOutput);
await captureSession.commitConfig();
await captureSession.start();
} catch (e) {
console.error(`Failed to initialize camera. Code: ${e.code}, message: ${e.message}`);
}
}
private async initDiseaseDetector(): Promise<void> {
try {
const config: plantDisease.PlantDiseaseDetectorConfig = {
modelType: plantDisease.ModelType.LITE,
scoreThreshold: 0.5
};
this.diseaseDetector = await plantDisease.createPlantDiseaseDetector(config);
} catch (e) {
console.error(`Failed to initialize disease detector. Code: ${e.code}, message: ${e.message}`);
}
}
public async createNewRecord(plantName: string, plantType: string): Promise<PlantRecord> {
const location = await this.getCurrentLocation();
const newRecord: PlantRecord = {
id: `plant_${Date.now()}`,
name: plantName,
type: plantType,
location,
images: [],
diseases: [],
healthScore: 100, // 初始健康分数
timestamp: Date.now()
};
this.currentRecord = newRecord;
this.plantRecords.unshift(newRecord);
await this.syncRecords();
return newRecord;
}
private async getCurrentLocation(): Promise<PlantRecord['location']> {
try {
const location = await geolocation.getCurrentLocation({
priority: geolocation.LocationRequestPriority.FIRST_FIX,
timeInterval: 5,
distanceInterval: 0
});
return {
latitude: location.latitude,
longitude: location.longitude,
address: location.address?.addressLines?.join(', ')
};
} catch (e) {
console.error(`Failed to get location. Code: ${e.code}, message: ${e.message}`);
return { latitude: 0, longitude: 0 };
}
}
public async captureAndDiagnose(): Promise<PlantDisease[] | null> {
if (!this.currentRecord || !this.previewOutput || !this.diseaseDetector) return null;
try {
const imageObj = await this.previewOutput.getFrame();
const results = await this.diseaseDetector.detect(imageObj);
if (results && results.length > 0) {
this.currentRecord.diseases = results.map(result => ({
name: result.diseaseName,
confidence: result.score,
treatment: this.getTreatmentSuggestions(result.diseaseName),
prevention: this.getPreventionSuggestions(result.diseaseName)
}));
this.currentRecord.healthScore = this.calculateHealthScore(this.currentRecord.diseases);
this.currentRecord.timestamp = Date.now();
// 保存图片
const imageUri = await this.saveImage(imageObj);
if (imageUri) {
this.currentRecord.images.push(imageUri);
}
await this.syncCurrentRecord();
return this.currentRecord.diseases;
}
return null;
} catch (e) {
console.error(`Failed to diagnose plant. Code: ${e.code}, message: ${e.message}`);
return null;
}
}
private async saveImage(imageObj: image.Image): Promise<string | null> {
// 实际应用中应将图片保存到设备存储并返回URI
// 这里简化为返回空字符串
return `plant_image_${Date.now()}.jpg`;
}
private getTreatmentSuggestions(diseaseName: string): string[] {
// 简化的治疗方案 (实际应用中应有更专业的数据库)
const treatments: Record<string, string[]> = {
'rust': ['移除受感染叶片', '使用杀菌剂喷雾', '改善空气流通'],
'powdery_mildew': ['喷洒硫磺粉', '使用碳酸氢钾溶液', '减少浇水频率'],
'leaf_spot': ['剪除病叶', '使用铜基杀菌剂', '避免叶片湿润']
};
return treatments[diseaseName] || ['咨询专业园艺师'];
}
private getPreventionSuggestions(diseaseName: string): string[] {
// 简化的预防措施
const preventions: Record<string, string[]> = {
'rust': ['保持植物间距', '早晨浇水', '定期检查叶片'],
'powdery_mildew': ['避免过度施肥', '确保充足阳光', '使用抗病品种'],
'leaf_spot': ['清理落叶', '避免顶部浇水', '保持良好排水']
};
return preventions[diseaseName] || ['保持植物健康状态'];
}
private calculateHealthScore(diseases: PlantDisease[]): number {
if (diseases.length === 0) return 100;
const weightedScore = diseases.reduce((score, disease) => {
return score - (disease.confidence * 10);
}, 100);
return Math.max(0, weightedScore);
}
private async syncCurrentRecord(): Promise<void> {
if (!this.currentRecord) return;
// 更新记录列表
const index = this.plantRecords.findIndex(r => r.id === this.currentRecord?.id);
if (index >= 0) {
this.plantRecords[index] = this.currentRecord;
} else {
this.plantRecords.unshift(this.currentRecord);
}
await this.syncRecords();
}
private async syncRecords(): Promise<void> {
if (this.kvStore) {
try {
await this.kvStore.put('plant_records', { value: this.plantRecords });
} catch (e) {
console.error(`Failed to sync plant records. Code: ${e.code}, message: ${e.message}`);
}
}
}
private notifyRecordsChange(newRecords: PlantRecord[]): void {
// 合并新旧记录,保留最新的版本
const mergedRecords = [...this.plantRecords];
newRecords.forEach(newRecord => {
const existingIndex = mergedRecords.findIndex(r => r.id === newRecord.id);
if (existingIndex >= 0) {
if (newRecord.timestamp > mergedRecords[existingIndex].timestamp) {
mergedRecords[existingIndex] = newRecord;
}
} else {
mergedRecords.push(newRecord);
}
});
this.plantRecords = mergedRecords.sort((a, b) => b.timestamp - a.timestamp);
}
public async getPlantRecords(): Promise<PlantRecord[]> {
if (!this.kvStore) return this.plantRecords;
try {
const entry = await this.kvStore.get('plant_records');
return entry?.value || this.plantRecords;
} catch (e) {
console.error(`Failed to get plant records. Code: ${e.code}, message: ${e.message}`);
return this.plantRecords;
}
}
public async getCurrentRecord(): Promise<PlantRecord | null> {
return this.currentRecord;
}
public async destroy(): Promise<void> {
if (this.kvStore) {
this.kvStore.off('dataChange');
}
if (this.cameraInput) {
await this.cameraInput.close();
}
if (this.diseaseDetector) {
this.diseaseDetector.release();
}
}
}
2. 植物诊断组件
// src/main/ets/components/PlantDiagnosis.ets
@Component
export struct PlantDiagnosis {
private plantService = PlantHealthService.getInstance();
@State currentRecord: PlantRecord | null = null;
@State showNewRecordDialog: boolean = false;
@State newPlantName: string = '';
@State newPlantType: string = '';
@State previewSurfaceId: string = 'previewSurface';
aboutToAppear(): void {
this.loadRecords();
}
private async loadRecords(): Promise<void> {
this.plantRecords = await this.plantService.getPlantRecords();
}
build() {
Stack() {
// 摄像头预览
CameraPreview({ surfaceId: this.previewSurfaceId })
.width('100%')
.height('60%');
// 诊断控制面板
Column() {
if (this.currentRecord) {
this.buildCurrentPlantInfo();
} else {
Button('新建植物记录')
.type(ButtonType.Capsule)
.width('80%')
.backgroundColor('#4CAF50')
.fontColor('#FFFFFF')
.onClick(() => {
this.showNewRecordDialog = true;
});
}
if (this.currentRecord) {
Button('诊断健康状况')
.type(ButtonType.Capsule)
.width('80%')
.backgroundColor('#2196F3')
.fontColor('#FFFFFF')
.margin({ top: 20 })
.onClick(() => {
this.diagnosePlant();
});
}
}
.width('100%')
.position({ x: 0, y: '70%' })
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center);
// 新建记录对话框
if (this.showNewRecordDialog) {
Dialog.show({
title: '新建植物记录',
content: this.buildNewRecordDialog(),
confirm: {
value: '创建',
action: () => {
this.createNewRecord();
this.showNewRecordDialog = false;
}
},
cancel: () => {
this.showNewRecordDialog = false;
}
});
}
// 诊断结果对话框
if (this.diagnosisResult && this.currentRecord) {
Dialog.show({
title: '诊断结果',
content: this.buildDiagnosisResult(),
confirm: {
value: '确定',
action: () => {
this.diagnosisResult = null;
}
}
});
}
}
.width('100%')
.height('100%');
}
@Builder
private buildCurrentPlantInfo() {
if (!this.currentRecord) return;
Column() {
Text(this.currentRecord.name)
.fontSize(20)
.fontWeight(FontWeight.Bold);
Text(`类型: ${this.currentRecord.type}`)
.fontSize(16)
.fontColor('#666666')
.margin({ top: 5 });
Row() {
Image($r('app.media.ic_location'))
.width(20)
.height(20)
.margin({ right: 5 });
Text(this.currentRecord.location.address || '未知位置')
.fontSize(14)
.fontColor('#666666');
}
.margin({ top: 10 });
// 健康状态指示器
Row() {
Circle()
.width(15)
.height(15)
.fillColor(this.getHealthColor(this.currentRecord.healthScore))
.margin({ right: 10 });
Text(`健康分数: ${this.currentRecord.healthScore}`)
.fontSize(16)
.fontColor(this.getHealthColor(this.currentRecord.healthScore));
}
.margin({ top: 15 });
}
.width('80%')
.padding(15)
.backgroundColor('#FFFFFF')
.borderRadius(15)
.shadow({ radius: 5, color: '#E0E0E0', offsetX: 0, offsetY: 2 })
.position({ x: '10%', y: '10%' });
}
@Builder
private buildNewRecordDialog() {
Column() {
TextInput({ placeholder: '植物名称', text: this.newPlantName })
.width('100%')
.margin({ bottom: 15 })
.onChange((value: string) => {
this.newPlantName = value;
});
TextInput({ placeholder: '植物类型', text: this.newPlantType })
.width('100%')
.margin({ bottom: 15 })
.onChange((value: string) => {
this.newPlantType = value;
});
}
.width('100%')
.padding(10);
}
@Builder
private buildDiagnosisResult() {
if (!this.diagnosisResult || !this.currentRecord) return;
Column() {
if (this.diagnosisResult.length > 0) {
Text('检测到以下病害:')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 10 });
ForEach(this.diagnosisResult, (disease) => {
Column() {
Row() {
Text(disease.name)
.fontSize(16)
.fontWeight(FontWeight.Bold)
.layoutWeight(1);
Text(`${Math.floor(disease.confidence * 100)}%`)
.fontSize(14)
.fontColor('#F44336');
}
.width('100%')
.margin({ bottom: 5 });
Text('治疗方案:')
.fontSize(14)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 5 });
ForEach(disease.treatment, (item) => {
Text(`• ${item}`)
.fontSize(14)
.margin({ bottom: 3 });
});
Text('预防措施:')
.fontSize(14)
.fontWeight(FontWeight.Bold)
.margin({ top: 10, bottom: 5 });
ForEach(disease.prevention, (item) => {
Text(`• ${item}`)
.fontSize(14)
.margin({ bottom: 3 });
});
}
.width('100%')
.padding(10)
.backgroundColor('#FFF3E0')
.borderRadius(10)
.margin({ bottom: 10 });
})
} else {
Text('未检测到病害,植物健康状态良好')
.fontSize(16)
.fontColor('#4CAF50');
}
}
.width('100%')
.height(300)
.padding(10);
}
private async createNewRecord(): Promise<void> {
if (!this.newPlantName.trim()) {
prompt.showToast({ message: '请输入植物名称', duration: 2000 });
return;
}
this.currentRecord = await this.plantService.createNewRecord(this.newPlantName, this.newPlantType);
this.newPlantName = '';
this.newPlantType = '';
}
private async diagnosePlant(): Promise<void> {
if (!this.currentRecord) return;
this.diagnosisResult = await this.plantService.captureAndDiagnose();
if (this.diagnosisResult && this.diagnosisResult.length > 0) {
this.currentRecord = await this.plantService.getCurrentRecord();
}
}
private getHealthColor(score: number): string {
if (score >= 80) return '#4CAF50';
if (score >= 50) return '#FFC107';
return '#F44336';
}
}
3. 主界面实现
// src/main/ets/pages/PlantHealthPage.ets
import { PlantHealthService } from '../service/PlantHealthService';
import { PlantDiagnosis } from '../components/PlantDiagnosis';
@Entry
@Component
struct PlantHealthPage {
@State activeTab: number = 0;
@State deviceList: string[] = [];
private plantService = PlantHealthService.getInstance();
build() {
Column() {
// 标题
Text('植物健康诊断')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 20 });
// 标签页
Tabs({ barPosition: BarPosition.Start }) {
TabContent() {
// 诊断标签页
PlantDiagnosis()
}
.tabBar('健康诊断');
TabContent() {
// 记录标签页
this.buildRecordsTab()
}
.tabBar('植物记录');
TabContent() {
// 设备标签页
this.buildDevicesTab()
}
.tabBar('设备管理');
}
.barWidth('100%')
.barHeight(50)
.width('100%')
.height('80%')
}
.width('100%')
.height('100%')
.padding(20)
.onAppear(() => {
// 模拟获取设备列表
setTimeout(() => {
this.deviceList = ['我的手机', '平板电脑', '园艺电脑'];
}, 1000);
});
}
@Builder
private buildRecordsTab() {
Column() {
Text('植物健康记录')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 20 });
if (this.plantRecords.length > 0) {
List({ space: 15 }) {
ForEach(this.plantRecords, (record) => {
ListItem() {
this.buildRecordItem(record);
}
})
}
.width('100%')
.layoutWeight(1);
} else {
Text('暂无植物记录')
.fontSize(16)
.fontColor('#666666')
.margin({ top: 50 });
}
}
.width('100%')
.height('100%')
.padding(10);
}
@Builder
private buildRecordItem(record: PlantRecord) {
Row() {
Column() {
Text(record.name)
.fontSize(16)
.fontWeight(FontWeight.Bold);
Text(record.type)
.fontSize(14)
.fontColor('#666666')
.margin({ top: 5 });
}
.layoutWeight(1);
Column() {
Row() {
Circle()
.width(10)
.height(10)
.fillColor(this.getHealthColor(record.healthScore))
.margin({ right: 5 });
Text(`${record.healthScore}`)
.fontSize(16)
.fontColor(this.getHealthColor(record.healthScore));
}
Text(new Date(record.timestamp).toLocaleDateString())
.fontSize(12)
.fontColor('#999999')
.margin({ top: 5 });
}
}
.width('100%')
.padding(15)
.backgroundColor('#FFFFFF')
.borderRadius(10)
.onClick(() => {
this.viewRecordDetails(record);
});
}
@Builder
private buildDevicesTab() {
Column() {
Text('已连接设备')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 20 });
if (this.deviceList.length > 0) {
List({ space: 15 }) {
ForEach(this.deviceList, (device) => {
ListItem() {
Row() {
Image($r('app.media.ic_device'))
.width(40)
.height(40)
.margin({ right: 15 });
Text(device)
.fontSize(16)
.layoutWeight(1);
if (device === '我的手机') {
Text('主设备')
.fontSize(14)
.fontColor('#4CAF50');
}
}
.width('100%')
.padding(15)
.backgroundColor('#FFFFFF')
.borderRadius(10)
}
})
}
.width('100%')
.layoutWeight(1);
} else {
Text('没有连接的设备')
.fontSize(16)
.fontColor('#666666')
.margin({ top: 50 });
}
Button('添加设备')
.type(ButtonType.Capsule)
.width('80%')
.margin({ top: 30 })
.backgroundColor('#2196F3')
.fontColor('#FFFFFF');
}
.width('100%')
.height('100%')
.padding(10);
}
private getHealthColor(score: number): string {
if (score >= 80) return '#4CAF50';
if (score >= 50) return '#FFC107';
return '#F44336';
}
private viewRecordDetails(record: PlantRecord): void {
// 实际应用中应导航到详情页面
console.log('View record details:', record);
}
}
四、与游戏同步技术的结合点
- 分布式数据同步:借鉴游戏中多玩家状态同步机制,实现植物健康记录的跨设备同步
- 实时图像处理:类似游戏中的实时数据流,处理摄像头图像数据
- 设备角色分配:类似游戏中的主机/客户端角色,确定主诊断设备和从属设备
- 时间同步机制:确保多设备间的时间戳一致,类似游戏中的时间同步
- 数据压缩传输:优化图像和诊断结果的传输效率,类似游戏中的网络优化
五、关键特性实现
-
植物病害检测:
const results = await this.diseaseDetector.detect(imageObj); -
地理位置获取:
const location = await geolocation.getCurrentLocation(); -
健康评分计算:
const healthScore = 100 - (disease.confidence * 10); -
数据同步:
await this.kvStore.put('plant_records', { value: this.plantRecords });
六、性能优化策略
-
图像采样率优化:
const imageStreamInfo: image.ImageStreamInfo = { size: { width: 1280, height: 720 }, // 720p分辨率 format: image.ImageFormat.JPEG, quality: 80 // 80%质量 }; -
批量数据同步:
private scheduleSync(): void { if (this.syncTimer) clearTimeout(this.syncTimer); this.syncTimer = setTimeout(() => { this.syncData(); this.syncTimer = null; }, 2000); // 2秒内多次更新只同步一次 } -
本地缓存优先:
public async getPlantRecords(): Promise<PlantRecord[]> { // 先返回本地缓存 const cachedRecords = this.plantRecords; // 异步从分布式存储获取最新记录 if (this.kvStore) { this.kvStore.get('plant_records').then((entry) => { if (entry?.value) { this.plantRecords = entry.value; } }); } return cachedRecords; } -
资源释放管理:
public async destroy(): Promise<void> { if (this.cameraInput) { await this.cameraInput.close(); } if (this.diseaseDetector) { this.diseaseDetector.release(); } }
七、项目扩展方向
- 植物数据库:建立更完善的植物和病害数据库
- 养护提醒:根据诊断结果设置养护提醒
- 社区分享:分享植物健康状态和养护经验
- 专家咨询:连接专业园艺师进行远程诊断
- 历史趋势:记录植物健康变化趋势
八、总结
本植物健康诊断系统实现了以下核心功能:
- 基于HarmonyOS AI能力的植物病害检测
- 植物位置信息记录与展示
- 健康状态评估与治疗建议
- 多设备间的数据同步
- 直观的用户界面和交互体验
通过借鉴游戏中的多设备同步技术,我们构建了一个实用的园艺健康管理工具。该项目展示了HarmonyOS在AI图像处理和分布式技术方面的强大能力,为开发者提供了农业科技和智能家居应用开发的参考方案。
更多推荐


所有评论(0)