鸿蒙跨端表情识别天气推荐系统开发指南 原创

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

鸿蒙跨端表情识别天气推荐系统开发指南

一、系统架构设计

基于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的分布式技术,我们构建了一个能够感知用户情绪的智能天气系统,为用户提供更加贴心和个性化的天气服务体验。

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