文章目录
-
- 每日一句正能量
- 一、前言:当烹饪遇上光感与智能
- 二、核心能力与技术架构
-
- [2.1 沉浸光感的烹饪场景适配](#2.1 沉浸光感的烹饪场景适配)
- [2.2 悬浮导航的烹饪交互范式](#2.2 悬浮导航的烹饪交互范式)
- [2.3 系统架构](#2.3 系统架构)
- 三、核心代码实战
-
- [3.1 厨房光感适配服务(KitchenLightAdapter.ets)](#3.1 厨房光感适配服务(KitchenLightAdapter.ets))
- [3.2 免触悬浮导航球(HandsFreeNavBall.ets)](#3.2 免触悬浮导航球(HandsFreeNavBall.ets))
- [3.3 自适应光感菜谱卡片(AdaptiveRecipeCard.ets)](#3.3 自适应光感菜谱卡片(AdaptiveRecipeCard.ets))
- [3.4 AI厨师智能体(AIChefAgent.ets)](#3.4 AI厨师智能体(AIChefAgent.ets))
- [3.5 火候可视化组件(HeatVisualizer.ets)](#3.5 火候可视化组件(HeatVisualizer.ets))
- [3.6 主烹饪工作室页面(CookingStudio.ets)](#3.6 主烹饪工作室页面(CookingStudio.ets))
- 四、关键技术难点与解决方案
-
- [4.1 油手操作的触控可靠性](#4.1 油手操作的触控可靠性)
- [4.2 多光源环境下的食材色彩还原](#4.2 多光源环境下的食材色彩还原)
- [4.3 蒸汽雾化导致的屏幕可读性](#4.3 蒸汽雾化导致的屏幕可读性)
- [4.4 烹饪节奏与光效同步](#4.4 烹饪节奏与光效同步)
- 五、效果展示与场景演示
- 六、总结与展望

每日一句正能量
播种和收获一定不在同一天。
任何值得的结果都有因果间隔期。焦虑往往源于想跳过中间环节。记住这句,就会在付出后多一份从容:只管耕耘,等待自有意义。
一、前言:当烹饪遇上光感与智能
在快节奏的现代生活中,"今天吃什么"和"怎么做"成为许多人的日常困扰。传统烹饪应用往往停留在"静态菜谱展示"层面:用户需要一边盯着手机屏幕看步骤,一边手忙脚乱地操作食材,屏幕沾满油渍、亮度不合适、步骤切换繁琐等问题频发。更关键的是,这些应用缺乏环境感知能力------无法根据厨房的实际光照条件(如背光操作台、油烟雾气、夜间烹饪)动态优化显示,也无法理解用户当前的操作进度和食材状态。
HarmonyOS 6(API 23)带来的**悬浮导航(Floating Navigation)与沉浸光感(Immersive Light Sensing)**两大核心能力,为智能烹饪应用开辟了全新的设计范式:
- 沉浸光感能够实时感知厨房环境的光照变化(如油烟机灯光、窗外自然光、台灯光源),自动调整菜谱界面的对比度、色温和字体大小,确保在油手操作、蒸汽弥漫等复杂环境下依然清晰可读
- 悬浮导航可在全屏烹饪模式下,以非侵入方式提供AI食材识别、步骤语音控制、火候提醒、营养分析等智能体交互入口,成为用户掌中的"数字 sous-chef(副厨)"
本文将实战演示如何构建**"光味智厨"**------一个能"看见"厨房环境、用光线引导烹饪节奏、通过悬浮AI助手提供实时烹饪指导的智能系统。系统不仅优化视觉体验,更能通过光感数据分析用户的操作节奏与疲劳度,动态调整教学语速与步骤提示强度。
二、核心能力与技术架构
2.1 沉浸光感的烹饪场景适配
HarmonyOS 6的AmbientLightFusion在API 23中针对烹饪场景进行了专项优化:
| 光感维度 | 烹饪场景映射 | 自适应策略 |
|---|---|---|
| 环境照度 | 厨房主灯/油烟机灯/自然光混合 | 动态调整菜谱文字对比度,防油手误触 |
| 色温变化 | 暖黄厨房灯 vs 冷白日光 | 食材图片色温校正,确保颜色真实 |
| 屏幕亮度 | 蒸汽/油烟导致的屏幕雾化 | 自动提升亮度,启用防雾显示模式 |
| 光照方向 | 背光操作台导致的阴影 | 高亮当前步骤区域,避开阴影覆盖 |
| 瞳孔变化 | 长时间专注切配导致的视觉疲劳 | 定时提醒休息,切换护眼模式 |
2.2 悬浮导航的烹饪交互范式
HarmonyOS 6的FloatingNavigation支持**"脏手免触"**模式------悬浮球支持手势控制(挥手、点头)和语音唤醒,用户无需触碰屏幕即可操作:
- 在食材准备阶段 → 显示"AI识别/称重/替换建议"
- 在烹饪阶段 → 显示"火候调节/计时器/下一步"
- 在装盘阶段 → 显示"摆盘建议/拍照分享/营养分析"
- 双手沾油时 → 语音控制"下一步""重复""暂停"
2.3 系统架构
LightFlavorKitchen/
├── entry/src/main/ets/
│ ├── pages/
│ │ └── CookingStudio.ets # 主烹饪工作室
│ ├── components/
│ │ ├── HandsFreeNavBall.ets # 免触悬浮导航球
│ │ ├── AdaptiveRecipeCard.ets # 自适应光感菜谱卡片
│ │ ├── AIChefPanel.ets # AI厨师面板
│ │ ├── IngredientScanner.ets # 食材扫描器
│ │ └── HeatVisualizer.ets # 火候可视化
│ ├── services/
│ │ ├── KitchenLightAdapter.ets # 厨房光感适配服务
│ │ ├── AIChefAgent.ets # AI厨师智能体
│ │ ├── CookingTimer.ets # 智能烹饪计时器
│ │ └── NutritionEngine.ets # 营养分析引擎
│ └── models/
│ ├── Recipe.ets # 菜谱模型
│ └── CookingState.ets # 烹饪状态模型
三、核心代码实战
3.1 厨房光感适配服务(KitchenLightAdapter.ets)
代码亮点: 本服务是系统的"厨房视觉管家"。它创新性地将厨房特有的光照挑战(油烟雾化、多光源混合、背光操作台)映射到UI适配策略。核心算法包括:蒸汽雾化补偿算法 通过检测环境湿度与光照散射度自动提升屏幕亮度和锐化度,多光源色温融合 分析厨房内所有光源的色温并计算综合白平衡,以及操作台阴影检测通过光照方向分析识别背光区域并高亮关键UI元素。
typescript
// services/KitchenLightAdapter.ets
import { sensor } from '@kit.SensorServiceKit';
import { display } from '@kit.ArkUI';
import { hilog } from '@kit.PerformanceAnalysisKit';
// 厨房环境特征
export interface KitchenContext {
lux: number; // 照度
colorTemperature: number; // 综合色温(K)
humidity: number; // 湿度(%)
steamDensity: number; // 蒸汽密度 0-1
lightSources: Array<{ // 检测到的光源
type: 'OVERHEAD' | 'RANGE_HOOD' | 'WINDOW' | 'TASK_LIGHT';
lux: number;
colorTemp: number;
direction: { x: number; y: number; z: number };
}>;
isBacklit: boolean; // 是否背光
timestamp: number;
}
// 烹饪UI适配状态
@Observed
export class CookingUIState {
screenBrightness: number = 80;
textContrast: number = 7.0; // 文字对比度
imageSaturation: number = 1.0; // 图片饱和度
sharpnessBoost: number = 0; // 锐化增强
colorTempOffset: number = 0; // 色温偏移
fontSize: number = 16;
buttonSize: number = 48; // 按钮尺寸(防误触)
isSteamMode: boolean = false; // 蒸汽模式
isNightCooking: boolean = false; // 夜间烹饪
}
export class KitchenLightAdapter {
private static instance: KitchenLightAdapter;
private kitchenContext: KitchenContext = {
lux: 300,
colorTemperature: 4000,
humidity: 50,
steamDensity: 0,
lightSources: [],
isBacklit: false,
timestamp: Date.now()
};
private stateListeners: Array<(state: CookingUIState) => void> = [];
private currentState: CookingUIState = new CookingUIState();
static getInstance(): KitchenLightAdapter {
if (!KitchenLightAdapter.instance) {
KitchenLightAdapter.instance = new KitchenLightAdapter();
}
return KitchenLightAdapter.instance;
}
async startAdaptation(): Promise<void> {
// 1. 注册环境光传感器
sensor.on(sensor.SensorId.AMBIENT_LIGHT, (data) => {
this.kitchenContext.lux = data.intensity;
this.processKitchenUpdate();
});
// 2. 注册湿度传感器(用于蒸汽检测)
sensor.on(sensor.SensorId.HUMIDITY, (data) => {
this.kitchenContext.humidity = data.value;
this.estimateSteamDensity();
});
// 3. 启动多光源分析(通过摄像头+光感融合)
this.startMultiSourceAnalysis();
// 4. 启动背光检测
this.startBacklightDetection();
hilog.info(0x0000, 'KitchenLight', '厨房光感适配服务启动');
}
private processKitchenUpdate(): void {
// 1. 计算综合色温(多光源加权)
const blendedTemp = this.calculateBlendedColorTemp();
this.kitchenContext.colorTemperature = blendedTemp;
// 2. 检测蒸汽密度
this.estimateSteamDensity();
// 3. 生成UI适配状态
const newState = this.generateUIState();
// 4. 平滑过渡
this.smoothTransition(newState);
}
private calculateBlendedColorTemp(): number {
const sources = this.kitchenContext.lightSources;
if (sources.length === 0) return 4000;
// 加权平均:越亮的光源权重越高
const totalLux = sources.reduce((sum, s) => sum + s.lux, 0);
const weightedTemp = sources.reduce((sum, s) =>
sum + s.colorTemp * (s.lux / totalLux), 0);
return Math.round(weightedTemp);
}
private estimateSteamDensity(): void {
// 蒸汽密度 = f(湿度变化率, 照度散射, 温度)
const humidityDelta = Math.max(0, this.kitchenContext.humidity - 50);
const lightScatter = this.kitchenContext.lux < 100 ? 0.3 : 0;
this.kitchenContext.steamDensity = Math.min(1,
(humidityDelta / 50) * 0.6 + lightScatter * 0.4);
}
private generateUIState(): CookingUIState {
const ctx = this.kitchenContext;
const state = new CookingUIState();
// 1. 屏幕亮度:蒸汽环境下大幅提升
state.screenBrightness = Math.min(100,
60 + ctx.steamDensity * 30 + (ctx.lux < 100 ? 20 : 0));
// 2. 文字对比度:低照度或蒸汽环境下增强
state.textContrast = Math.min(12,
7.0 + ctx.steamDensity * 3 + (ctx.lux < 50 ? 2 : 0));
// 3. 图片饱和度:蒸汽环境下增强,补偿雾化导致的色彩损失
state.imageSaturation = Math.min(1.5, 1.0 + ctx.steamDensity * 0.5);
// 4. 锐化增强:蒸汽环境下提升边缘清晰度
state.sharpnessBoost = ctx.steamDensity * 0.5;
// 5. 色温偏移:校正多光源导致的偏色
const targetTemp = 5500; // D55标准光源
state.colorTempOffset = (targetTemp - ctx.colorTemperature) / 100;
// 6. 字体和按钮:油手环境下增大触控区域
state.fontSize = ctx.steamDensity > 0.3 ? 18 : 16;
state.buttonSize = ctx.steamDensity > 0.3 ? 56 : 48;
// 7. 模式标记
state.isSteamMode = ctx.steamDensity > 0.4;
state.isNightCooking = ctx.lux < 80 && new Date().getHours() >= 20;
return state;
}
private smoothTransition(targetState: CookingUIState): void {
// 使用Animator实现平滑过渡,避免UI跳变
const animator = Animator.create({
duration: 500,
curve: Curve.EaseInOut,
iterations: 1
});
animator.onFrame = (value: number) => {
this.currentState = {
screenBrightness: this.lerp(this.currentState.screenBrightness, targetState.screenBrightness, value),
textContrast: this.lerp(this.currentState.textContrast, targetState.textContrast, value),
imageSaturation: this.lerp(this.currentState.imageSaturation, targetState.imageSaturation, value),
sharpnessBoost: this.lerp(this.currentState.sharpnessBoost, targetState.sharpnessBoost, value),
colorTempOffset: this.lerp(this.currentState.colorTempOffset, targetState.colorTempOffset, value),
fontSize: Math.round(this.lerp(this.currentState.fontSize, targetState.fontSize, value)),
buttonSize: Math.round(this.lerp(this.currentState.buttonSize, targetState.buttonSize, value)),
isSteamMode: targetState.isSteamMode,
isNightCooking: targetState.isNightCooking
};
this.notifyStateUpdate();
};
animator.play();
}
private lerp(start: number, end: number, t: number): number {
return start + (end - start) * t;
}
private startMultiSourceAnalysis(): void {
// 通过前置摄像头分析环境光的光谱分布
// 识别不同光源类型(LED、白炽灯、自然光)
// 简化实现:模拟多光源检测
setInterval(() => {
const hour = new Date().getHours();
const sources = [];
// 厨房主灯(始终存在)
sources.push({
type: 'OVERHEAD' as const,
lux: 200,
colorTemp: 3500,
direction: { x: 0, y: -1, z: 0 }
});
// 油烟机灯(烹饪时)
if (this.kitchenContext.humidity > 60) {
sources.push({
type: 'RANGE_HOOD' as const,
lux: 150,
colorTemp: 4000,
direction: { x: 0, y: 0, z: 1 }
});
}
// 自然光(白天)
if (hour >= 8 && hour <= 18) {
sources.push({
type: 'WINDOW' as const,
lux: 300,
colorTemp: 6500,
direction: { x: 1, y: 0.5, z: 0 }
});
}
this.kitchenContext.lightSources = sources;
}, 5000);
}
private startBacklightDetection(): void {
// 检测用户是否背对光源操作
// 通过分析面部光照与背景光照的差异
setInterval(() => {
// 简化:如果环境光主要来自后方且面部较暗,判定为背光
const windowLight = this.kitchenContext.lightSources.find(s => s.type === 'WINDOW');
this.kitchenContext.isBacklit = !!windowLight && windowLight.lux > 400;
}, 3000);
}
onStateChanged(callback: (state: CookingUIState) => void): void {
this.stateListeners.push(callback);
}
private notifyStateUpdate(): void {
this.stateListeners.forEach(cb => cb(this.currentState));
}
getCurrentState(): CookingUIState {
return this.currentState;
}
getKitchenContext(): KitchenContext {
return this.kitchenContext;
}
stopAdaptation(): void {
sensor.off(sensor.SensorId.AMBIENT_LIGHT);
sensor.off(sensor.SensorId.HUMIDITY);
this.stateListeners = [];
}
}
3.2 免触悬浮导航球(HandsFreeNavBall.ets)
代码亮点: 这是系统的"数字副厨"。悬浮球针对烹饪场景的"脏手"痛点进行了专项设计:支持手势控制 (挥手切换步骤)、语音唤醒("下一步""重复""暂停")、以及** proximity感应**(手靠近时自动放大)。悬浮球的颜色和脉动节奏反映当前烹饪阶段------准备阶段为绿色平缓,烹饪阶段为橙色急促(同步火候),装盘阶段为金色闪耀。
typescript
// components/HandsFreeNavBall.ets
import { FloatingNavigation } from '@kit.ArkUI';
import { AIChefAgent } from '../services/AIChefAgent';
import { KitchenLightAdapter, CookingUIState } from '../services/KitchenLightAdapter';
import { speechRecognizer } from '@kit.SpeechKit';
export enum CookingPhase {
PREP = 'PREP', // 食材准备
COOK = 'COOK', // 烹饪中
PLATE = 'PLATE', // 装盘
SERVE = 'SERVE' // 上菜
}
interface VoiceCommand {
keywords: string[];
action: () => void;
}
@Component
export struct HandsFreeNavBall {
@State ballScale: number = 1.0;
@State ballColor: ResourceColor = '#4CAF50';
@State isExpanded: boolean = false;
@State currentPhase: CookingPhase = CookingPhase.PREP;
@State isListening: boolean = false; // 语音识别中
@State proximityDetected: boolean = false; // 手靠近检测
@State currentStep: number = 1;
@State totalSteps: number = 8;
private aiChef: AIChefAgent = new AIChefAgent();
private lightAdapter: KitchenLightAdapter = KitchenLightAdapter.getInstance();
// 语音命令映射
private readonly VOICE_COMMANDS: VoiceCommand[] = [
{ keywords: ['下一步', '下一个', '继续'], action: () => this.nextStep() },
{ keywords: ['上一步', '返回', '回去'], action: () => this.prevStep() },
{ keywords: ['重复', '再说一遍', '没听清'], action: () => this.repeatStep() },
{ keywords: ['暂停', '等一下'], action: () => this.pauseCooking() },
{ keywords: ['计时', '倒计时'], action: () => this.startTimer() },
{ keywords: ['识别食材', '这是什么'], action: () => this.scanIngredient() },
{ keywords: ['火候', '温度'], action: () => this.showHeatGuide() },
{ keywords: ['营养', '热量'], action: () => this.showNutrition() }
];
aboutToAppear() {
// 监听烹饪阶段变化
this.aiChef.onPhaseChanged((phase) => {
this.currentPhase = phase;
this.updateBallByPhase(phase);
});
// 监听光感状态
this.lightAdapter.onStateChanged((state) => {
this.adaptToSteam(state);
});
// 启动语音识别
this.startVoiceRecognition();
// 启动接近感应
this.startProximityDetection();
// 启动阶段脉动动画
this.startPhasePulse();
}
private updateBallByPhase(phase: CookingPhase): void {
switch (phase) {
case CookingPhase.PREP:
this.ballColor = '#4CAF50'; // 绿色:准备
break;
case CookingPhase.COOK:
this.ballColor = '#FF9800'; // 橙色:烹饪
break;
case CookingPhase.PLATE:
this.ballColor = '#FFD700'; // 金色:装盘
break;
case CookingPhase.SERVE:
this.ballColor = '#E91E63'; // 粉色:完成
break;
}
}
private adaptToSteam(state: CookingUIState): void {
// 蒸汽环境下增大悬浮球,便于油手操作
if (state.isSteamMode) {
this.ballScale = 1.2;
} else {
this.ballScale = 1.0;
}
}
private startPhasePulse(): void {
setInterval(() => {
switch (this.currentPhase) {
case CookingPhase.PREP:
// 准备阶段:平缓呼吸
this.ballScale = 1.0 + Math.sin(Date.now() / 1000) * 0.05;
break;
case CookingPhase.COOK:
// 烹饪阶段:与火候同步的急促脉动
this.ballScale = 1.0 + Math.sin(Date.now() / 300) * 0.15;
break;
case CookingPhase.PLATE:
// 装盘阶段:优雅的慢速闪耀
this.ballScale = 1.0 + Math.sin(Date.now() / 1500) * 0.1;
break;
}
}, 50);
}
private startVoiceRecognition(): void {
// 持续监听语音命令
speechRecognizer.on('result', (result) => {
const text = result.text;
this.processVoiceCommand(text);
});
speechRecognizer.startListening({
language: 'zh-CN',
partialResults: true,
});
}
private processVoiceCommand(text: string): void {
this.isListening = true;
for (const cmd of this.VOICE_COMMANDS) {
if (cmd.keywords.some(k => text.includes(k))) {
cmd.action();
this.isListening = false;
return;
}
}
// 未识别的命令,询问AI厨师
this.aiChef.askQuestion(text);
this.isListening = false;
}
private startProximityDetection(): void {
// 通过接近传感器检测手是否靠近
// 简化实现:使用前置摄像头检测手部区域
setInterval(() => {
// 模拟接近检测
this.proximityDetected = Math.random() > 0.7;
}, 1000);
}
// ===== 动作实现 =====
private nextStep(): void {
if (this.currentStep < this.totalSteps) {
this.currentStep++;
this.aiChef.speakStep(this.currentStep);
this.flashBall();
}
}
private prevStep(): void {
if (this.currentStep > 1) {
this.currentStep--;
this.aiChef.speakStep(this.currentStep);
this.flashBall();
}
}
private repeatStep(): void {
this.aiChef.speakStep(this.currentStep);
this.flashBall();
}
private pauseCooking(): void {
this.aiChef.pause();
}
private startTimer(): void {
this.aiChef.startStepTimer(this.currentStep);
}
private scanIngredient(): void {
// 唤起摄像头进行食材识别
this.aiChef.identifyIngredient();
}
private showHeatGuide(): void {
// 显示火候指导
}
private showNutrition(): void {
// 显示营养分析
}
private flashBall(): void {
const originalScale = this.ballScale;
this.ballScale = 1.4;
setTimeout(() => {
this.ballScale = originalScale;
}, 200);
}
build() {
Stack() {
// 语音识别指示器(聆听中时显示)
if (this.isListening) {
Column() {
ForEach([1, 2, 3, 4], (item: number) => {
Column()
.width(4)
.height(8 + item * 4)
.backgroundColor('#FFFFFF')
.borderRadius(2)
.margin({ left: 2, right: 2 })
.animation({
duration: 400,
curve: Curve.EaseInOut,
iterations: -1,
playMode: PlayMode.Alternate
})
})
}
.flexDirection(FlexDirection.Row)
.position({ x: -40, y: 15 })
}
// 接近感应放大效果
if (this.proximityDetected && !this.isExpanded) {
Circle()
.width(80)
.height(80)
.fill('rgba(255,255,255,0.1)')
.position({ x: -13, y: -13 })
.animation({
duration: 300,
curve: Curve.EaseOut
})
}
// 扇形菜单(展开时)
if (this.isExpanded) {
// 步骤导航
this.MenuButton($r('app.media.ic_prev'), '上一步', () => this.prevStep(), 0)
this.MenuButton($r('app.media.ic_next'), '下一步', () => this.nextStep(), 1)
this.MenuButton($r('app.media.ic_timer'), '计时器', () => this.startTimer(), 2)
this.MenuButton($r('app.media.ic_camera'), '识食材', () => this.scanIngredient(), 3)
this.MenuButton($r('app.media.ic_nutrition'), '营养', () => this.showNutrition(), 4)
}
// 主悬浮球
Column() {
if (this.isListening) {
// 聆听中显示声波
Image($r('app.media.ic_mic'))
.width(28)
.height(28)
.fillColor('#FFFFFF')
} else {
// 显示当前步骤进度
Stack() {
Circle()
.width(54)
.height(54)
.stroke(this.ballColor)
.strokeWidth(3)
.fill('transparent')
Text(`${this.currentStep}`)
.fontSize(20)
.fontColor('#FFFFFF')
.fontWeight(FontWeight.Bold)
}
}
}
.width(54)
.height(54)
.backgroundColor(this.ballColor)
.borderRadius(27)
.scale({ x: this.ballScale, y: this.ballScale })
.shadow({
radius: 14,
color: this.ballColor,
offsetY: 4
})
.onClick(() => {
this.isExpanded = !this.isExpanded;
})
.gesture(
// 挥手检测:左右滑动切换步骤
PanGesture({ direction: PanDirection.Horizontal })
.onActionEnd((event) => {
if (event.offsetX > 50) {
this.nextStep();
} else if (event.offsetX < -50) {
this.prevStep();
}
})
)
.animation({
duration: 150,
curve: Curve.EaseInOut
})
}
.width(220)
.height(220)
.position({ x: 300, y: 600 })
}
@Builder
MenuButton(icon: Resource, label: string, action: () => void, index: number) {
Column() {
Image(icon)
.width(22)
.height(22)
.fillColor('#FFFFFF')
Text(label)
.fontSize(10)
.fontColor('#FFFFFF')
.margin({ top: 3 })
}
.width(50)
.height(50)
.backgroundColor('rgba(0,0,0,0.75)')
.borderRadius(25)
.position({
x: this.getMenuX(index),
y: this.getMenuY(index)
})
.onClick(() => {
action();
this.isExpanded = false;
})
.animation({
duration: 250,
curve: Curve.Spring,
delay: index * 40
})
}
private getMenuX(index: number): number {
const angles = [200, 230, 260, 290, 320];
const radius = 90;
const angle = (angles[index] || 260) * Math.PI / 180;
return 110 + radius * Math.cos(angle) - 25;
}
private getMenuY(index: number): number {
const angles = [200, 230, 260, 290, 320];
const radius = 90;
const angle = (angles[index] || 260) * Math.PI / 180;
return 110 + radius * Math.sin(angle) - 25;
}
}
3.3 自适应光感菜谱卡片(AdaptiveRecipeCard.ets)
代码亮点: 这是用户直接"看见"的核心界面。它集成了厨房光感适配服务,实现菜谱展示的实时动态优化。核心创新是**"食材真色还原"**------通过多光源色温分析,校正食材图片的显示色彩,确保用户看到的颜色与真实食材一致。蒸汽雾化补偿在检测到高湿度环境时,自动提升图片锐度和对比度,避免蒸汽导致的视觉模糊。
typescript
// components/AdaptiveRecipeCard.ets
import { KitchenLightAdapter, CookingUIState } from '../services/KitchenLightAdapter';
interface RecipeStep {
number: number;
description: string;
duration?: number; // 步骤耗时(秒)
temperature?: number; // 温度(℃)
imageUrl?: string;
tips: string[];
}
@Component
export struct AdaptiveRecipeCard {
@State uiState: CookingUIState = new CookingUIState();
@State currentStep: number = 1;
@State isCurrentStep: boolean = true;
private lightAdapter: KitchenLightAdapter = KitchenLightAdapter.getInstance();
// 示例菜谱步骤
private steps: RecipeStep[] = [
{
number: 1,
description: '将鸡胸肉切成2厘米见方的丁,加入少许盐和料酒腌制10分钟',
duration: 600,
tips: ['刀要锋利,切口平整', '腌制时放入冰箱']
},
{
number: 2,
description: '热锅凉油,油温六成热时下入鸡丁滑散至变色',
duration: 180,
temperature: 180,
tips: ['油温不要过高,避免外焦里生', '用筷子划散,不要翻炒']
},
{
number: 3,
description: '加入切好的青椒、红椒丁,大火快炒30秒',
duration: 30,
tips: ['保持大火,锁住蔬菜水分']
}
];
aboutToAppear() {
this.lightAdapter.onStateChanged((state) => {
this.uiState = state;
});
this.lightAdapter.startAdaptation();
}
// 色温校正:将图片色彩调整至接近真实
private colorCorrectImage(): object {
const offset = this.uiState.colorTempOffset;
return {
brightness: 1.0,
contrast: this.uiState.textContrast / 7.0,
saturate: this.uiState.imageSaturation,
// 色温偏移通过滤镜实现
// 正偏移:偏冷(增加蓝)
// 负偏移:偏暖(增加红/黄)
};
}
// 蒸汽模式下的锐化滤镜
private getSteamFilter(): object {
if (!this.uiState.isSteamMode) return {};
return {
// 使用高对比度+锐化模拟
contrast: 1.2,
// 实际应使用Canvas滤镜
};
}
build() {
Column() {
// 步骤标题栏
Row() {
Text(`步骤 ${this.currentStep}/${this.steps.length}`)
.fontSize(this.uiState.fontSize + 2)
.fontColor('#FFFFFF')
.fontWeight(FontWeight.Bold)
Blank()
// 蒸汽模式指示
if (this.uiState.isSteamMode) {
Row() {
Image($r('app.media.ic_steam'))
.width(18)
.height(18)
.fillColor('#FF9800')
Text('蒸汽模式')
.fontSize(12)
.fontColor('#FF9800')
.margin({ left: 4 })
}
}
// 夜间模式指示
if (this.uiState.isNightCooking) {
Row() {
Image($r('app.media.ic_moon'))
.width(18)
.height(18)
.fillColor('#9C27B0')
Text('夜间烹饪')
.fontSize(12)
.fontColor('#9C27B0')
.margin({ left: 4 })
}
.margin({ left: 8 })
}
}
.width('100%')
.height(50)
.padding({ left: 16, right: 16 })
.backgroundColor('rgba(0,0,0,0.6)')
// 步骤内容区
Scroll() {
Column() {
// 步骤图片(带色温校正)
if (this.steps[this.currentStep - 1]?.imageUrl) {
Image(this.steps[this.currentStep - 1].imageUrl)
.width('100%')
.height(200)
.objectFit(ImageFit.Cover)
// 应用色温校正和蒸汽滤镜
.brightness(1.0 + this.uiState.sharpnessBoost * 0.2)
.contrast(this.uiState.textContrast / 7.0)
.saturate(this.uiState.imageSaturation)
}
// 步骤描述
Text(this.steps[this.currentStep - 1]?.description || '')
.fontSize(this.uiState.fontSize)
.fontColor('#FFFFFF')
.margin({ top: 16, left: 16, right: 16 })
.lineHeight(this.uiState.fontSize * 1.6)
// 温度/时间指示
if (this.steps[this.currentStep - 1]?.temperature) {
Row() {
Image($r('app.media.ic_thermometer'))
.width(20)
.height(20)
.fillColor('#F44336')
Text(`${this.steps[this.currentStep - 1].temperature}℃`)
.fontSize(this.uiState.fontSize)
.fontColor('#F44336')
.margin({ left: 6 })
}
.margin({ top: 12, left: 16 })
}
if (this.steps[this.currentStep - 1]?.duration) {
Row() {
Image($r('app.media.ic_timer'))
.width(20)
.height(20)
.fillColor('#2196F3')
Text(`${this.steps[this.currentStep - 1].duration}秒`)
.fontSize(this.uiState.fontSize)
.fontColor('#2196F3')
.margin({ left: 6 })
}
.margin({ top: 8, left: 16 })
}
// 小贴士
Column() {
Text('💡 小贴士')
.fontSize(this.uiState.fontSize - 2)
.fontColor('#FFD700')
.fontWeight(FontWeight.Bold)
ForEach(this.steps[this.currentStep - 1]?.tips || [], (tip: string) => {
Text(`• ${tip}`)
.fontSize(this.uiState.fontSize - 2)
.fontColor('rgba(255,255,255,0.8)')
.margin({ top: 6 })
})
}
.width('100%')
.padding(12)
.backgroundColor('rgba(255,255,255,0.1)')
.borderRadius(8)
.margin({ top: 16, left: 16, right: 16 })
// 底部留白(避免被悬浮球遮挡)
Blank()
.height(100)
}
.width('100%')
}
.width('100%')
.layoutWeight(1)
}
.width('100%')
.height('100%')
.backgroundColor('#1a1a1a')
}
}
3.4 AI厨师智能体(AIChefAgent.ets)
代码亮点: 这是系统的"数字主厨"。它基于端侧大模型,提供实时烹饪指导。核心创新是**"光感感知式教学"------当检测到厨房光线昏暗(如夜间烹饪)时,AI会自动放慢语速、使用更简洁的指令;当检测到蒸汽弥漫时,会提高音量并重复关键步骤。同时支持"食材替代智能推荐"**,根据用户冰箱内的现有食材,动态调整菜谱。
typescript
// services/AIChefAgent.ets
import { KitchenLightAdapter } from './KitchenLightAdapter';
import { textToSpeech } from '@kit.SpeechKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
export interface ChefGuidance {
speech: string;
visualHint?: string; // 视觉提示(如"大火""小火"的光效)
urgency: 'low' | 'medium' | 'high'; // 紧急程度
estimatedTime: number; // 预计执行时间
}
export class AIChefAgent {
private lightAdapter: KitchenLightAdapter;
private currentRecipe: any;
private currentStep: number = 0;
private isPaused: boolean = false;
private phaseListeners: Array<(phase: string) => void> = [];
private currentPhase: string = 'PREP';
constructor() {
this.lightAdapter = KitchenLightAdapter.getInstance();
}
async loadRecipe(recipeId: string): Promise<void> {
// 加载菜谱数据
this.currentRecipe = await this.fetchRecipe(recipeId);
this.currentStep = 0;
}
async speakStep(stepNumber: number): Promise<void> {
if (!this.currentRecipe || this.isPaused) return;
const step = this.currentRecipe.steps[stepNumber - 1];
if (!step) return;
this.currentStep = stepNumber;
// 根据厨房环境调整语音风格
const style = this.adaptSpeechStyle();
// 构建语音内容
let speech = '';
if (style.isQuiet) {
// 夜间/安静环境:简短指令
speech = `第${stepNumber}步。${step.shortDescription}。`;
} else {
// 正常环境:详细指导
speech = `第${stepNumber}步,共${this.currentRecipe.steps.length}步。${step.description}。`;
if (step.tips && step.tips.length > 0) {
speech += `小贴士:${step.tips[0]}。`;
}
if (step.duration) {
speech += `预计耗时${step.duration}秒。`;
}
}
if (style.isLoud) {
// 嘈杂环境:重复关键信息
speech += `重复:${step.keyAction}。`;
}
// 生成视觉提示
const visualHint = this.generateVisualHint(step);
// 播放语音
await this.speak(speech, style);
// 更新烹饪阶段
this.updatePhase(step);
}
private adaptSpeechStyle(): {
isQuiet: boolean;
isLoud: boolean;
speed: number;
pitch: number;
} {
const ctx = this.lightAdapter.getKitchenContext();
const hour = new Date().getHours();
const isNight = hour >= 22 || hour <= 6;
const isNoisy = ctx.steamDensity > 0.5; // 油烟机噪音大
const isDim = ctx.lux < 100;
return {
isQuiet: isNight || isDim,
isLoud: isNoisy,
speed: isNight ? 0.8 : (isNoisy ? 0.9 : 1.0),
pitch: isNoisy ? 1.1 : 1.0 // 嘈杂环境提高音调
};
}
private generateVisualHint(step: any): string {
// 根据步骤类型生成光效提示
if (step.temperature && step.temperature > 200) {
return 'HEAT_HIGH'; // 大火:红色脉冲
}
if (step.temperature && step.temperature < 100) {
return 'HEAT_LOW'; // 小火:蓝色稳定
}
if (step.duration && step.duration < 30) {
return 'QUICK_ACTION'; // 快速操作:黄色闪烁
}
return 'NORMAL';
}
private async speak(text: string, style: { speed: number; pitch: number }): Promise<void> {
try {
await textToSpeech.speak({
text: text,
voice: 'zh-CN-female-warm',
speed: style.speed,
pitch: style.pitch,
volume: style.isLoud ? 1.0 : 0.7
});
} catch (err) {
hilog.error(0x0000, 'AIChef', `语音播放失败: ${err.message}`);
}
}
private updatePhase(step: any): void {
let newPhase = this.currentPhase;
// 根据步骤内容推断阶段
if (step.description.includes('切') || step.description.includes('腌')) {
newPhase = 'PREP';
} else if (step.description.includes('炒') || step.description.includes('煮') || step.description.includes('煎')) {
newPhase = 'COOK';
} else if (step.description.includes('盘') || step.description.includes('摆')) {
newPhase = 'PLATE';
} else if (step.description.includes('上桌') || step.description.includes('完成')) {
newPhase = 'SERVE';
}
if (newPhase !== this.currentPhase) {
this.currentPhase = newPhase;
this.phaseListeners.forEach(cb => cb(newPhase));
}
}
async identifyIngredient(): Promise<void> {
// 唤起摄像头,使用端侧视觉模型识别食材
const result = await this.callVisionModel();
const speech = `识别结果:${result.name}。${result.description}。建议烹饪方式:${result.suggestedMethods.join('、')}。`;
await this.speak(speech, { speed: 0.9, pitch: 1.0 });
}
async suggestSubstitute(missingIngredient: string): Promise<void> {
// 基于知识图谱推荐替代品
const substitutes = await this.querySubstitute(missingIngredient);
let speech = `${missingIngredient}可以用以下食材替代:`;
substitutes.forEach((sub, i) => {
speech += `${i + 1}、${sub.name},${sub.ratio}。`;
});
await this.speak(speech, { speed: 0.9, pitch: 1.0 });
}
async startStepTimer(stepNumber: number): Promise<void> {
const step = this.currentRecipe?.steps[stepNumber - 1];
if (!step?.duration) return;
// 启动倒计时
const duration = step.duration;
// 最后10秒语音提醒
setTimeout(async () => {
await this.speak('还有10秒', { speed: 1.0, pitch: 1.1 });
}, (duration - 10) * 1000);
// 时间到提醒
setTimeout(async () => {
await this.speak('时间到!请进行下一步。', { speed: 1.0, pitch: 1.2 });
}, duration * 1000);
}
async askQuestion(question: string): Promise<void> {
// 处理用户的烹饪问题
const answer = await this.callCookingLLM(question);
await this.speak(answer, { speed: 0.9, pitch: 1.0 });
}
pause(): void {
this.isPaused = true;
}
resume(): void {
this.isPaused = false;
}
onPhaseChanged(callback: (phase: string) => void): void {
this.phaseListeners.push(callback);
}
private async fetchRecipe(recipeId: string): Promise<any> {
// 从本地或云端加载菜谱
return {
id: recipeId,
name: '宫保鸡丁',
steps: [
{ number: 1, description: '将鸡胸肉切成2厘米见方的丁...', shortDescription: '切鸡丁腌制', duration: 600, keyAction: '切鸡丁' },
{ number: 2, description: '热锅凉油,油温六成热时下入鸡丁...', shortDescription: '滑炒鸡丁', duration: 180, temperature: 180, keyAction: '滑炒' }
]
};
}
private async callVisionModel(): Promise<any> {
// 调用端侧视觉模型识别食材
return { name: '鸡胸肉', description: '新鲜鸡胸肉,适合快炒', suggestedMethods: ['滑炒', '煎炸'] };
}
private async querySubstitute(ingredient: string): Promise<any[]> {
// 查询食材替代知识图谱
return [
{ name: '鸡腿肉', ratio: '等量替换,口感更嫩' },
{ name: '猪里脊', ratio: '等量替换,需减少烹饪时间' }
];
}
private async callCookingLLM(question: string): Promise<string> {
// 调用端侧烹饪大模型
return '根据您的提问,建议...';
}
}
3.5 火候可视化组件(HeatVisualizer.ets)
代码亮点: 这是系统的"火候仪表盘"。它通过Canvas 2D绘制动态热力图,将抽象的"大火""中火""小火"转化为直观的视觉反馈。支持光感联动------在昏暗环境下自动增强亮度和对比度,确保用户能清晰看到火候指示。同时与悬浮球同步脉动,形成"视觉-触觉"双通道提醒。
typescript
// components/HeatVisualizer.ets
import { KitchenLightAdapter, CookingUIState } from '../services/KitchenLightAdapter';
@Component
export struct HeatVisualizer {
@State heatLevel: number = 0; // 0-100,对应火候
@State targetHeat: number = 0;
@State uiState: CookingUIState = new CookingUIState();
private lightAdapter: KitchenLightAdapter = KitchenLightAdapter.getInstance();
private canvasCtx: CanvasRenderingContext2D | null = null;
private heatAnimator?: Animator;
aboutToAppear() {
this.lightAdapter.onStateChanged((state) => {
this.uiState = state;
});
this.startHeatAnimation();
}
private startHeatAnimation(): void {
const animate = () => {
this.drawHeatGauge();
requestAnimationFrame(animate);
};
animate();
}
setTargetHeat(level: number): void {
this.targetHeat = Math.max(0, Math.min(100, level));
}
private drawHeatGauge(): void {
if (!this.canvasCtx) return;
const ctx = this.canvasCtx;
const width = 200;
const height = 200;
const centerX = width / 2;
const centerY = height / 2;
const radius = 80;
// 清空
ctx.clearRect(0, 0, width, height);
// 当前火候向目标火候平滑过渡
this.heatLevel += (this.targetHeat - this.heatLevel) * 0.05;
// 根据火候计算颜色
const color = this.heatToColor(this.heatLevel);
// 根据光感状态调整亮度
const brightnessBoost = this.uiState.screenBrightness / 80;
// 绘制外圈(目标火候)
ctx.beginPath();
ctx.arc(centerX, centerY, radius, 0, Math.PI * 2);
ctx.strokeStyle = `rgba(100, 100, 100, 0.3)`;
ctx.lineWidth = 8;
ctx.stroke();
// 绘制内圈(当前火候)
const startAngle = -Math.PI / 2;
const endAngle = startAngle + (this.heatLevel / 100) * Math.PI * 2;
ctx.beginPath();
ctx.arc(centerX, centerY, radius, startAngle, endAngle);
ctx.strokeStyle = color;
ctx.lineWidth = 10;
ctx.lineCap = 'round';
ctx.shadowColor = color;
ctx.shadowBlur = 20 * brightnessBoost;
ctx.stroke();
// 绘制中心文字
ctx.fillStyle = '#FFFFFF';
ctx.font = `bold ${24 * brightnessBoost}px HarmonyOS Sans`;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(`${Math.round(this.heatLevel)}%`, centerX, centerY - 10);
// 火候名称
ctx.font = `${14 * brightnessBoost}px HarmonyOS Sans`;
ctx.fillStyle = 'rgba(255,255,255,0.7)';
ctx.fillText(this.getHeatName(this.heatLevel), centerX, centerY + 15);
// 绘制热力粒子(大火时更多)
if (this.heatLevel > 50) {
this.drawHeatParticles(ctx, centerX, centerY, radius, this.heatLevel, color);
}
}
private heatToColor(heat: number): string {
// 0-30: 蓝色(小火)
// 30-60: 黄色(中火)
// 60-100: 红色(大火)
if (heat < 30) {
const t = heat / 30;
return `rgb(${Math.round(100 + t * 155)}, ${Math.round(150 + t * 105)}, 255)`;
} else if (heat < 60) {
const t = (heat - 30) / 30;
return `rgb(255, ${Math.round(255 - t * 100)}, ${Math.round(255 * (1 - t))})`;
} else {
const t = (heat - 60) / 40;
return `rgb(255, ${Math.round(155 - t * 155)}, 0)`;
}
}
private getHeatName(heat: number): string {
if (heat < 20) return '小火';
if (heat < 40) return '中小火';
if (heat < 60) return '中火';
if (heat < 80) return '中大火';
return '大火';
}
private drawHeatParticles(
ctx: CanvasRenderingContext2D,
cx: number, cy: number, radius: number,
heat: number, color: string
): void {
const particleCount = Math.floor(heat / 10);
const time = Date.now() / 1000;
for (let i = 0; i < particleCount; i++) {
const angle = (i / particleCount) * Math.PI * 2 + time;
const dist = radius + 10 + Math.sin(time * 2 + i) * 5;
const x = cx + Math.cos(angle) * dist;
const y = cy + Math.sin(angle) * dist;
const size = 2 + Math.sin(time * 3 + i) * 1.5;
ctx.beginPath();
ctx.arc(x, y, size, 0, Math.PI * 2);
ctx.fillStyle = color;
ctx.globalAlpha = 0.6;
ctx.fill();
}
ctx.globalAlpha = 1.0;
}
build() {
Canvas(this.canvasCtx)
.width(200)
.height(200)
.onReady((context) => {
this.canvasCtx = context.renderingContext;
})
}
}
3.6 主烹饪工作室页面(CookingStudio.ets)
typescript
// pages/CookingStudio.ets
import { AdaptiveRecipeCard } from '../components/AdaptiveRecipeCard';
import { HandsFreeNavBall } from '../components/HandsFreeNavBall';
import { HeatVisualizer } from '../components/HeatVisualizer';
import { AIChefAgent } from '../services/AIChefAgent';
import { KitchenLightAdapter } from '../services/KitchenLightAdapter';
@Entry
@Component
struct CookingStudio {
@State showHeatVisualizer: boolean = false;
@State currentHeat: number = 0;
private aiChef: AIChefAgent = new AIChefAgent();
private lightAdapter: KitchenLightAdapter = KitchenLightAdapter.getInstance();
aboutToAppear() {
this.aiChef.loadRecipe('kung-pao-chicken');
this.lightAdapter.startAdaptation();
}
aboutToDisappear() {
this.lightAdapter.stopAdaptation();
}
build() {
Stack() {
// 底层:菜谱卡片
AdaptiveRecipeCard()
.width('100%')
.height('100%')
// 火候可视化(烹饪阶段显示)
if (this.showHeatVisualizer) {
Column() {
HeatVisualizer()
.width(200)
.height(200)
Text('当前火候')
.fontSize(14)
.fontColor('rgba(255,255,255,0.7)')
.margin({ top: 8 })
}
.position({ x: '50%', y: '20%' })
.markAnchor({ x: 0.5, y: 0 })
.backgroundColor('rgba(0,0,0,0.7)')
.borderRadius(16)
.padding(16)
}
// 顶部状态栏
Row() {
Text('光味智厨')
.fontSize(16)
.fontColor('#FFFFFF')
.fontWeight(FontWeight.Bold)
Blank()
// 环境光指示
Row() {
Circle()
.width(8)
.height(8)
.fill(this.lightAdapter.getKitchenContext().lux > 200 ? '#4CAF50' : '#FF9800')
Text(`${this.lightAdapter.getKitchenContext().lux} lux`)
.fontSize(12)
.fontColor('rgba(255,255,255,0.7)')
.margin({ left: 6 })
}
}
.width('100%')
.height(44)
.padding({ left: 16, right: 16 })
.backgroundColor('rgba(0,0,0,0.5)')
// 免触悬浮导航球
HandsFreeNavBall()
.position({ x: '82%', y: '75%' })
}
.width('100%')
.height('100%')
.backgroundColor('#1a1a1a')
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])
}
}
四、关键技术难点与解决方案
4.1 油手操作的触控可靠性
问题: 烹饪时手指沾油/水,导致屏幕触控失灵或误触。
方案:
- 悬浮球增大:蒸汽模式下自动放大至120%
- 手势替代:挥手切换步骤,无需触碰屏幕
- 语音优先:嘈杂环境下提高语音识别灵敏度
- 接近感应:手靠近时自动高亮悬浮球,减少寻找时间
4.2 多光源环境下的食材色彩还原
问题: 厨房内LED灯(6500K)、白炽灯(2700K)、自然光混合,导致食材照片偏色。
方案:
- 光谱分析:通过摄像头分析环境光的光谱分布
- 动态白平衡:计算综合色温并实时校正图片显示
- 食材真色库:建立标准光源下的食材色彩基准库
4.3 蒸汽雾化导致的屏幕可读性
问题: 炒菜时的蒸汽导致屏幕雾化,文字模糊。
方案:
- 亮度补偿:检测到高湿度时自动提升亮度30%
- 锐化增强:应用边缘增强算法
- 高对比度模式:蒸汽环境下切换至高对比度主题
- 语音播报:关键步骤自动语音朗读,减少看屏需求
4.4 烹饪节奏与光效同步
问题: 不同烹饪阶段需要不同的提醒强度。
方案:
- 阶段脉动:悬浮球颜色/频率反映当前阶段
- 火候联动:大火时红色急促脉动,小火时蓝色稳定
- 倒计时光效:最后10秒屏幕边缘红色闪烁
- 完成庆祝:装盘阶段金色粒子特效
五、效果展示与场景演示
场景一:夜间安静烹饪
- 环境特征: 22:00后,厨房仅开油烟机灯(照度80lux,色温3500K)
- 系统响应:
- 自动切换至"夜间烹饪模式",屏幕亮度降低但对比度增强
- AI厨师降低音量,使用简短指令:"第3步。下鸡丁。滑散。"
- 悬浮球呈绿色平缓脉动,不打扰家人休息
- 检测到蒸汽时自动提升亮度,语音重复关键步骤
- 用户体验: 无需吵醒家人,安静完成烹饪
场景二:周末家庭聚餐备菜
- 环境特征: 白天自然光+厨房主灯,多人在厨房走动
- 系统响应:
- 检测到多光源混合,自动校正食材图片色温
- 双手沾油时,挥手即可切换步骤
- 询问"这是什么"自动识别台面上的食材
- 发现缺少花生,AI推荐"可用腰果替代,等量"
- 大火爆炒阶段,火候可视化显示红色粒子特效
- 用户体验: 手忙脚乱时也能从容操作,食材替代建议避免中断烹饪
场景三:新手学做复杂菜品
- 环境特征: 傍晚时分,厨房灯光偏黄,用户首次尝试红烧鱼
- 系统响应:
- 检测到新手模式(首次做此菜),AI使用详细指导
- 煎鱼步骤时,火候可视化实时显示油温
- 翻面时机:AI通过计时+语音提醒"现在翻面,小心溅油"
- 检测到用户犹豫(无操作超过30秒),自动重复当前步骤
- 完成时播放庆祝音效,生成美食照片+步骤分享卡
- 用户体验: 像有一位专业厨师在旁指导,新手也能做出餐厅级菜品
六、总结与展望
本文基于HarmonyOS 6(API 23)的悬浮导航 与沉浸光感能力,实战构建了一个**"光味智厨"**智能烹饪系统。核心技术突破包括:
- 厨房光感适配引擎:针对油烟、蒸汽、多光源等厨房特有环境,实现动态UI优化
- 免触悬浮导航:支持手势、语音、接近感应的多模态交互,解决油手操作痛点
- 光感感知式AI教学:根据厨房环境动态调整语音指导风格和内容复杂度
- 火候可视化:将抽象火候转化为直观的光效动画,降低烹饪门槛
未来可拓展方向:
- 结合HarmonyOS分布式能力,将菜谱同步至冰箱屏、油烟机屏,实现多屏协同烹饪
- 接入华为智选智能灶具,实现火候的自动调节与闭环控制
- 开发PC端鸿蒙应用,利用大屏优势实现"菜谱规划+采购清单+烹饪指导"全流程管理
- 引入生成式AI菜谱创作,根据用户口味偏好和冰箱食材自动生成原创菜谱
转载自:https://blog.csdn.net/u014727709/article/details/161649656
欢迎 👍点赞✍评论⭐收藏,欢迎指正