
鸿蒙跨端表情识别天气推荐系统开发指南 原创
鸿蒙跨端表情识别天气推荐系统开发指南
一、系统架构设计
基于HarmonyOS的AI能力和分布式技术,构建表情识别的个性化天气推荐系统:
表情识别层:通过摄像头捕捉用户表情
情感分析层:分析用户情绪状态
风格匹配层:根据情绪匹配天气播报风格
跨端同步层:多设备间同步用户偏好和播报风格
!https://example.com/harmony-weather-system-arch.png
二、核心代码实现
表情识别服务
// EmotionService.ets
import ai from ‘@ohos.ai’;
import camera from ‘@ohos.multimedia.camera’;
import distributedData from ‘@ohos.distributedData’;
import { EmotionType, WeatherStyle } from ‘./WeatherTypes’;
class EmotionService {
private static instance: EmotionService = null;
private modelManager: ai.ModelManager;
private dataManager: distributedData.DataManager;
private listeners: EmotionListener[] = [];
private constructor() {
this.initModelManager();
this.initDataManager();
public static getInstance(): EmotionService {
if (!EmotionService.instance) {
EmotionService.instance = new EmotionService();
return EmotionService.instance;
private initModelManager(): void {
try {
this.modelManager = ai.createModelManager(getContext());
// 加载表情识别模型
this.modelManager.loadModel({
modelName: 'emotion_recognition',
modelPath: 'resources/rawfile/emotion.model',
callback: (err, data) => {
if (err) {
console.error('加载表情模型失败:', JSON.stringify(err));
}
});
// 加载天气风格推荐模型
this.modelManager.loadModel({
modelName: 'weather_style',
modelPath: 'resources/rawfile/weather_style.model',
callback: (err, data) => {
if (err) {
console.error('加载天气风格模型失败:', JSON.stringify(err));
}
});
catch (err) {
console.error('初始化模型管理器失败:', JSON.stringify(err));
}
private initDataManager(): void {
this.dataManager = distributedData.createDataManager({
bundleName: ‘com.example.weather’,
area: distributedData.Area.GLOBAL,
isEncrypted: true
});
this.dataManager.registerDataListener('emotion_sync', (data) => {
this.handleSyncData(data);
});
public async requestPermissions(): Promise<boolean> {
try {
const permissions = [
'ohos.permission.USE_AI',
'ohos.permission.CAMERA',
'ohos.permission.DISTRIBUTED_DATASYNC'
];
const result = await abilityAccessCtrl.requestPermissionsFromUser(
getContext(),
permissions
);
return result.grantedPermissions.length === permissions.length;
catch (err) {
console.error('请求权限失败:', JSON.stringify(err));
return false;
}
public async detectEmotion(imageData: ArrayBuffer): Promise<EmotionType> {
try {
const input = {
data: imageData,
width: 128,
height: 128,
format: ‘RGB’
};
const output = await this.modelManager.runModel({
modelName: 'emotion_recognition',
input: input
});
return output.result.emotion;
catch (err) {
console.error('表情识别失败:', JSON.stringify(err));
throw err;
}
public async recommendWeatherStyle(emotion: EmotionType, weatherData: any): Promise<WeatherStyle> {
try {
const input = {
emotion: emotion,
weather: weatherData
};
const output = await this.modelManager.runModel({
modelName: 'weather_style',
input: input
});
return output.result.style;
catch (err) {
console.error('天气风格推荐失败:', JSON.stringify(err));
throw err;
}
public async syncUserPreference(deviceId: string, preference: WeatherStyle): Promise<void> {
this.dataManager.syncData(‘emotion_sync’, {
type: ‘user_preference’,
data: {
deviceId: deviceId,
preference: preference
},
timestamp: Date.now()
});
private handleSyncData(data: any): void {
if (!data) return;
switch (data.type) {
case 'user_preference':
this.notifyPreferenceUpdated(data.data);
break;
}
private notifyPreferenceUpdated(preference: any): void {
this.listeners.forEach(listener => {
listener.onPreferenceUpdated?.(preference);
});
public addListener(listener: EmotionListener): void {
if (!this.listeners.includes(listener)) {
this.listeners.push(listener);
}
public removeListener(listener: EmotionListener): void {
this.listeners = this.listeners.filter(l => l !== listener);
}
interface EmotionListener {
onPreferenceUpdated?(preference: { deviceId: string, preference: WeatherStyle }): void;
export const emotionService = EmotionService.getInstance();
天气服务封装
// WeatherService.ets
import http from ‘@ohos.net.http’;
import { WeatherData, WeatherStyle } from ‘./WeatherTypes’;
class WeatherService {
private static instance: WeatherService = null;
private httpClient: http.HttpClient;
private apiKey: string = ‘YOUR_WEATHER_API_KEY’;
private constructor() {
this.httpClient = http.createHttp();
public static getInstance(): WeatherService {
if (!WeatherService.instance) {
WeatherService.instance = new WeatherService();
return WeatherService.instance;
public async getCurrentWeather(location: string): Promise<WeatherData> {
try {
const url = https://api.weatherapi.com/v1/current.json?key={this.apiKey}&q={location};
const response = await this.httpClient.request(url, {
method: 'GET',
header: { 'Content-Type': 'application/json' }
});
if (response.responseCode !== 200) {
throw new Error(天气请求失败: ${response.responseCode});
return JSON.parse(response.result as string);
catch (err) {
console.error('获取天气数据失败:', JSON.stringify(err));
throw err;
}
public applyStyle(weatherData: WeatherData, style: WeatherStyle): any {
// 根据风格调整天气数据展示方式
switch (style) {
case ‘cheerful’:
return {
…weatherData,
style: {
bgColor: ‘#FFF9C4’,
textColor: ‘#FF9800’,
animation: ‘sunny’
};
case 'calm':
return {
...weatherData,
style: {
bgColor: '#E3F2FD',
textColor: '#2196F3',
animation: 'cloudy'
};
case 'serious':
return {
...weatherData,
style: {
bgColor: '#F5F5F5',
textColor: '#607D8B',
animation: 'rainy'
};
default:
return weatherData;
}
export const weatherService = WeatherService.getInstance();
主界面实现
// MainScreen.ets
import { emotionService } from ‘./EmotionService’;
import { weatherService } from ‘./WeatherService’;
import { EmotionType, WeatherStyle } from ‘./WeatherTypes’;
@Component
export struct MainScreen {
@State hasPermission: boolean = false;
@State isDetecting: boolean = false;
@State currentEmotion: EmotionType | null = null;
@State weatherData: any = null;
@State weatherStyle: WeatherStyle = ‘neutral’;
@State previewSurfaceId: string = ‘’;
@State showCamera: boolean = false;
@State location: string = ‘北京’;
private deviceId: string = ‘device_001’;
build() {
Column() {
// 标题栏
Row() {
Text(‘表情天气’)
.fontSize(24)
.fontWeight(FontWeight.Bold)
.layoutWeight(1)
Button(this.hasPermission ? '检测表情' : '授权')
.width(100)
.onClick(() => {
if (this.hasPermission) {
this.showCamera = true;
else {
this.requestPermissions();
})
.padding(10)
.width('100%')
// 天气展示区域
Column() {
if (this.weatherData) {
// 根据风格动态调整展示样式
Column()
.width('100%')
.height(200)
.backgroundColor(this.weatherData.style.bgColor)
.borderRadius(8) {
Row() {
Column() {
Text(this.weatherData.location.name)
.fontSize(20)
.fontColor(this.weatherData.style.textColor)
.fontWeight(FontWeight.Bold)
Text(${this.weatherData.current.temp_c}°C)
.fontSize(36)
.fontColor(this.weatherData.style.textColor)
.margin({ top: 10 })
.layoutWeight(1)
Image(this.getWeatherIcon())
.width(80)
.height(80)
.padding(20)
Row() {
Text(天气: ${this.weatherData.current.condition.text})
.fontSize(16)
.fontColor(this.weatherData.style.textColor)
Text(湿度: ${this.weatherData.current.humidity}%)
.fontSize(16)
.fontColor(this.weatherData.style.textColor)
.margin({ left: 20 })
.padding(10)
if (this.currentEmotion) {
Text(检测到您今天心情${this.getEmotionText(this.currentEmotion)})
.fontSize(14)
.fontColor(this.weatherData.style.textColor)
.margin({ top: 10 })
}
else {
Column() {
Text('暂无天气数据')
.fontSize(18)
.margin({ bottom: 10 })
Button('获取天气')
.width(200)
.onClick(() => {
this.fetchWeather();
})
.width(‘100%’)
.height(200)
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
}
.width('100%')
.margin({ bottom: 20 })
// 位置选择
Row() {
Text('位置:')
.fontSize(16)
.margin({ right: 10 })
TextInput({ text: this.location })
.width(150)
.onChange((value: string) => {
this.location = value;
})
.margin({ bottom: 20 })
// 相机界面
if (this.showCamera) {
Stack() {
// 相机预览Surface
Surface({
id: this.previewSurfaceId,
type: SurfaceType.SURFACE_TEXTURE,
width: '100%',
height: 300
})
.onAppear(() => {
this.previewSurfaceId = preview_${Date.now()};
cameraService.startPreview(this.previewSurfaceId);
})
// 操作按钮
Column() {
if (!this.isDetecting) {
Button('识别表情')
.width(150)
.height(50)
.fontSize(18)
.onClick(() => {
this.detectEmotion();
})
else {
Progress({})
.width(50)
.height(50)
Button(‘关闭’)
.width(150)
.height(50)
.fontSize(18)
.margin({ top: 20 })
.onClick(() => {
this.showCamera = false;
cameraService.stopPreview();
})
.width(‘100%’)
.height('100%')
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
.width(‘100%’)
.height(300)
.position({ x: 0, y: 0 })
}
.width('100%')
.height('100%')
.padding(20)
.onAppear(() => {
this.checkPermissions();
emotionService.addListener({
onPreferenceUpdated: (preference) => {
if (preference.deviceId !== this.deviceId) {
this.weatherStyle = preference.preference;
this.applyWeatherStyle();
}
});
})
.onDisappear(() => {
emotionService.removeListener({
onPreferenceUpdated: (preference) => {
if (preference.deviceId !== this.deviceId) {
this.weatherStyle = preference.preference;
this.applyWeatherStyle();
}
});
})
private getWeatherIcon(): Resource {
if (!this.weatherData) return $r('app.media.ic_weather_default');
const code = this.weatherData.current.condition.code;
if (code === 1000) { // Sunny
return $r('app.media.ic_sunny');
else if (code >= 1003 && code <= 1009) { // Cloudy
return $r('app.media.ic_cloudy');
else if (code >= 1030 && code <= 1087) { // Rainy/Snowy
return $r('app.media.ic_rainy');
else {
return $r('app.media.ic_weather_default');
}
private getEmotionText(emotion: EmotionType): string {
const texts = {
‘happy’: ‘愉快’,
‘sad’: ‘低落’,
‘angry’: ‘愤怒’,
‘surprised’: ‘惊讶’,
‘neutral’: ‘平静’
};
return texts[emotion] || '平静';
private async checkPermissions(): Promise<void> {
try {
const permissions = [
'ohos.permission.USE_AI',
'ohos.permission.CAMERA',
'ohos.permission.DISTRIBUTED_DATASYNC'
];
const result = await abilityAccessCtrl.verifyPermissions(
getContext(),
permissions
);
this.hasPermission = result.every(perm => perm.granted);
catch (err) {
console.error('检查权限失败:', JSON.stringify(err));
this.hasPermission = false;
}
private async requestPermissions(): Promise<void> {
this.hasPermission = await emotionService.requestPermissions();
if (!this.hasPermission) {
prompt.showToast({ message: '授权失败,无法使用表情识别功能' });
}
private async fetchWeather(): Promise<void> {
try {
const data = await weatherService.getCurrentWeather(this.location);
this.weatherData = weatherService.applyStyle(data, this.weatherStyle);
catch (err) {
console.error('获取天气失败:', JSON.stringify(err));
prompt.showToast({ message: '获取天气失败,请重试' });
}
private async detectEmotion(): Promise<void> {
try {
this.isDetecting = true;
const frame = await cameraService.captureFrame();
const emotion = await emotionService.detectEmotion(frame);
this.currentEmotion = emotion;
const style = await emotionService.recommendWeatherStyle(emotion, this.weatherData);
this.weatherStyle = style;
this.applyWeatherStyle();
// 同步用户偏好
await emotionService.syncUserPreference(this.deviceId, style);
catch (err) {
console.error('表情识别失败:', JSON.stringify(err));
prompt.showToast({ message: '表情识别失败,请重试' });
finally {
this.isDetecting = false;
}
private applyWeatherStyle(): void {
if (this.weatherData) {
this.weatherData = weatherService.applyStyle(this.weatherData, this.weatherStyle);
}
类型定义
// WeatherTypes.ets
export type EmotionType = ‘happy’ ‘sad’ ‘angry’ ‘surprised’
‘neutral’;
export type WeatherStyle = ‘cheerful’ ‘calm’ ‘serious’
‘neutral’;
export interface WeatherData {
location: {
name: string;
region: string;
country: string;
};
current: {
temp_c: number;
condition: {
text: string;
code: number;
};
humidity: number;
};
style?: {
bgColor: string;
textColor: string;
animation: string;
};
三、项目配置与权限
权限配置
// module.json5
“module”: {
"requestPermissions": [
“name”: “ohos.permission.USE_AI”,
"reason": "使用AI模型识别表情"
},
“name”: “ohos.permission.CAMERA”,
"reason": "捕捉用户表情"
},
“name”: “ohos.permission.DISTRIBUTED_DATASYNC”,
"reason": "同步用户偏好"
],
"abilities": [
“name”: “MainAbility”,
"type": "page",
"visible": true
},
“name”: “CameraAbility”,
"type": "service",
"backgroundModes": ["camera"]
]
}
四、总结与扩展
本表情识别天气系统实现了以下核心功能:
实时表情识别:准确识别用户当前情绪状态
个性化推荐:根据情绪匹配最适合的天气播报风格
多设备同步:跨设备同步用户偏好和播报风格
动态界面调整:根据风格动态调整UI展示效果
扩展方向:
语音播报风格:根据情绪调整语音播报的语气和节奏
天气预警增强:在用户情绪不佳时加强天气预警提示
历史情绪记录:记录用户情绪变化与天气关系
智能提醒:根据情绪和天气提供穿衣、出行建议
社交分享:分享个性化天气卡片到社交平台
AR天气展示:通过AR技术展示天气效果
通过HarmonyOS的分布式技术,我们构建了一个能够感知用户情绪的智能天气系统,为用户提供更加贴心和个性化的天气服务体验。
