HarmonyOS 6 悬浮导航 + 沉浸光感:打造鸿蒙智能体驱动的沉浸式AR导航助手
在城市中步行或骑行时,传统导航应用存在明显的体验断层:需要频繁低头看手机确认方向,在复杂路口容易走错,夜间骑行时屏幕亮度刺眼影响视线。AR导航虽然解决了部分问题,但大多数实现需要全程开启摄像头,功耗高、发热大,且遮挡真实视野。HarmonyOS 6(API 23)带来的悬浮导航(Floating Navigation)和沉浸光感(Immersive Lighting)能力,让我们有机会打造一个常驻

每日一句正能量
你以为的真理只是你的视角,你以为的不公只是你的执念。
我们常把自己的看法等同于事实。感到不公时,很可能是我们死死抓住某个角度不放。松开手,换一个位置看,世界会宽广很多。
一、前言:当AI导航伙伴常驻视野边缘
在城市中步行或骑行时,传统导航应用存在明显的体验断层:需要频繁低头看手机确认方向,在复杂路口容易走错,夜间骑行时屏幕亮度刺眼影响视线。AR导航虽然解决了部分问题,但大多数实现需要全程开启摄像头,功耗高、发热大,且遮挡真实视野。
HarmonyOS 6(API 23)带来的悬浮导航(Floating Navigation)和沉浸光感(Immersive Lighting)能力,让我们有机会打造一个常驻视野边缘的AI导航伙伴——它像一位熟悉路况的向导,在你行走时以极简悬浮窗显示关键转向信息,在复杂路口自动展开AR指引;它通过光效感知导航状态,在需要转弯时以颜色脉冲引导方向,在偏离路线时以警示光效提醒,让你无需低头就能感知导航意图。
核心创新点:
- 🧭 极简悬浮导航:胶囊形态常驻屏幕边缘,仅显示关键转向信息,不遮挡真实视野
- 💡 方向光感引导:通过设备边框光效颜色与脉冲方向,实现"余光导航"
- 🤖 情境感知智能体:自动识别步行/骑行/驾车模式,动态调整导航策略
- 🗺️ AR路口增强:复杂路口自动展开AR指引,结合实景与虚拟箭头
二、应用场景设计
2.1 场景一:步行余光导航
用户在陌生城市步行游览,手机放在口袋或手持自然下垂。悬浮胶囊常驻屏幕边缘显示"前方300米右转",设备右侧边框泛起绿色脉冲光效,提示"右转在即"。用户无需低头,余光即可感知导航意图。
2.2 场景二:夜间骑行安全导航
夜间骑行时,屏幕自动降低亮度,悬浮窗切换为深色模式。接近路口时,设备底部边框泛起琥珀色呼吸光效,配合轻微震动提示减速。转弯方向通过左侧或右侧边框光效明确指示,避免夜间看屏幕影响视线。
2.3 场景三:复杂路口AR增强
到达五岔路口时,悬浮窗自动展开为AR模式,摄像头画面叠加虚拟箭头,智能体语音播报"走中间第二条路,注意避开施工区域"。同时边框光效与AR箭头同步闪烁,多感官协同确保不迷路。
三、技术架构
┌─────────────────────────────────────────────────────────────┐
│ HarmonyOS AR Navigation Assistant Agent │
├─────────────┬─────────────┬─────────────┬───────────────────┤
│ 悬浮窗UI │ 沉浸光感 │ 智能体引擎 │ 位置服务模块 │
│ FloatUI │ Lighting │ AI Engine │ LocationService │
├─────────────┴─────────────┴─────────────┴───────────────────┤
│ 导航上下文层(Navigation Context) │
│ 路径规划 │ 路况分析 │ 模式识别 │ 路口检测 │ AR融合 │
├─────────────────────────────────────────────────────────────┤
│ HarmonyOS 6 (API 23) 系统服务层 │
│ 悬浮导航 │ 光感服务 │ 智能体框架 │ 定位服务 │ AR引擎 │ 传感器 │
└─────────────────────────────────────────────────────────────┘
四、核心代码实现
4.1 导航上下文感知引擎(NavigationContextEngine)
这是整个系统的"定位之眼",通过多源融合定位与传感器数据,实时分析导航状态。
// engine/NavigationContextEngine.ets
import { geoLocationManager } from '@kit.LocationKit';
import { sensor } from '@kit.SensorServiceKit';
import { BusinessError } from '@kit.BasicServicesKit';
export interface NavigationContext {
currentLocation: GeoLocation; // 当前位置
destination: GeoLocation; // 目的地
routePath: RouteSegment[]; // 路径段
currentSegment: number; // 当前路径段索引
nextManeuver: Maneuver; // 下一个操作
distanceToNext: number; // 距离下一个操作(米)
estimatedTime: number; // 预计到达时间(秒)
navigationMode: NavigationMode; // 导航模式
roadComplexity: number; // 道路复杂度 0-100
isDeviating: boolean; // 是否偏离路线
surroundings: Surroundings; // 周边环境
}
export interface GeoLocation {
latitude: number;
longitude: number;
altitude: number;
accuracy: number;
heading: number; // 方向角
speed: number; // 速度 m/s
}
export interface RouteSegment {
start: GeoLocation;
end: GeoLocation;
distance: number;
duration: number;
maneuver: Maneuver;
roadName: string;
roadType: string;
complexity: number;
}
export interface Maneuver {
type: 'straight' | 'turn_left' | 'turn_right' | 'uturn' |
'slight_left' | 'slight_right' | 'merge' | 'roundabout';
instruction: string;
exitNumber?: number; // 环岛出口编号
landmark?: string; // 显著地标
}
export enum NavigationMode {
WALKING = 'walking', // 步行
CYCLING = 'cycling', // 骑行
DRIVING = 'driving', // 驾车
PUBLIC_TRANSIT = 'transit' // 公交
}
export interface Surroundings {
poiDensity: number; // POI密度
roadWidth: number; // 道路宽度
hasSidewalk: boolean; // 是否有人行道
lightingCondition: 'bright' | 'normal' | 'dim' | 'dark';
weather: string;
}
export class NavigationContextEngine {
private locationManager: geoLocationManager.GeoLocationManager | null = null;
private sensorManager: sensor.SensorManager | null = null;
private contextCallbacks: Array<(context: NavigationContext) => void> = [];
private currentContext: NavigationContext | null = null;
private updateInterval: number = 0;
private routeData: RouteSegment[] = [];
private destination: GeoLocation | null = null;
/**
* 初始化导航上下文感知
* 亮点:多源融合定位(GPS + 基站 + WiFi + 传感器)
*/
async init(): Promise<void> {
try {
// 申请定位权限
const hasPermission = await this.requestLocationPermission();
if (!hasPermission) {
console.warn('[NavContext] 定位权限未授予');
return;
}
// 初始化定位管理器
this.locationManager = geoLocationManager.getLocationManager();
// 初始化传感器管理器
this.sensorManager = sensor.getSensorManager();
// 注册定位监听
this.locationManager.on('locationChange', {
priority: geoLocationManager.LocationRequestPriority.PRIORITY_HIGH_ACCURACY,
interval: 1000 // 1秒更新
}, (location) => {
this.handleLocationUpdate(location);
});
// 注册传感器监听(加速度计+陀螺仪)
this.registerSensors();
console.info('[NavContext] 导航上下文感知引擎初始化完成');
} catch (err) {
console.error(`[NavContext] 初始化失败: ${JSON.stringify(err)}`);
}
}
/**
* 设置导航路线
*/
setRoute(segments: RouteSegment[], dest: GeoLocation): void {
this.routeData = segments;
this.destination = dest;
}
/**
* 处理位置更新
*/
private handleLocationUpdate(location: geoLocationManager.Location): void {
const geoLoc: GeoLocation = {
latitude: location.latitude,
longitude: location.longitude,
altitude: location.altitude || 0,
accuracy: location.accuracy || 10,
heading: location.direction || 0,
speed: location.speed || 0
};
// 检测导航模式
const mode = this.detectNavigationMode(geoLoc);
// 计算当前路径段
const segmentIndex = this.calculateCurrentSegment(geoLoc);
// 计算下一个操作
const nextManeuver = this.routeData[segmentIndex]?.maneuver || {
type: 'straight',
instruction: '继续直行'
};
// 计算距离
const distance = this.calculateDistanceToNext(geoLoc, segmentIndex);
// 检测是否偏离
const isDeviating = this.checkDeviation(geoLoc);
// 分析周边环境
const surroundings = this.analyzeSurroundings(geoLoc);
// 计算道路复杂度
const complexity = this.calculateRoadComplexity(segmentIndex);
const context: NavigationContext = {
currentLocation: geoLoc,
destination: this.destination!,
routePath: this.routeData,
currentSegment: segmentIndex,
nextManeuver,
distanceToNext: distance,
estimatedTime: this.calculateETA(geoLoc, segmentIndex),
navigationMode: mode,
roadComplexity: complexity,
isDeviating,
surroundings
};
this.currentContext = context;
this.contextCallbacks.forEach(cb => cb(context));
}
/**
* 检测导航模式
* 基于速度、加速度、位置特征判断
*/
private detectNavigationMode(location: GeoLocation): NavigationMode {
const speed = location.speed;
if (speed < 1.5) return NavigationMode.WALKING; // < 5.4km/h
if (speed < 6) return NavigationMode.CYCLING; // < 21.6km/h
if (speed < 25) return NavigationMode.DRIVING; // < 90km/h
return NavigationMode.PUBLIC_TRANSIT;
}
/**
* 计算当前路径段
*/
private calculateCurrentSegment(location: GeoLocation): number {
let minDistance = Infinity;
let segmentIndex = 0;
this.routeData.forEach((segment, index) => {
const dist = this.calculatePointToSegmentDistance(location, segment);
if (dist < minDistance) {
minDistance = dist;
segmentIndex = index;
}
});
return segmentIndex;
}
/**
* 计算点到线段距离
*/
private calculatePointToSegmentDistance(point: GeoLocation, segment: RouteSegment): number {
// 简化:计算到起点的距离
const dx = point.latitude - segment.start.latitude;
const dy = point.longitude - segment.start.longitude;
return Math.sqrt(dx * dx + dy * dy) * 111000; // 粗略转换为米
}
/**
* 计算到下一个操作的距离
*/
private calculateDistanceToNext(location: GeoLocation, segmentIndex: number): number {
if (segmentIndex >= this.routeData.length - 1) return 0;
const nextSegment = this.routeData[segmentIndex + 1];
return this.calculatePointToSegmentDistance(location, nextSegment);
}
/**
* 检测是否偏离路线
*/
private checkDeviation(location: GeoLocation): boolean {
const segmentIndex = this.calculateCurrentSegment(location);
const segment = this.routeData[segmentIndex];
if (!segment) return false;
const distance = this.calculatePointToSegmentDistance(location, segment);
// 偏离超过50米认为偏离
return distance > 50;
}
/**
* 分析周边环境
*/
private analyzeSurroundings(location: GeoLocation): Surroundings {
// 简化实现,实际应调用地图服务
return {
poiDensity: 0.7,
roadWidth: 10,
hasSidewalk: true,
lightingCondition: this.detectLightingCondition(),
weather: 'sunny'
};
}
/**
* 检测光照条件
*/
private detectLightingCondition(): Surroundings['lightingCondition'] {
const hour = new Date().getHours();
if (hour >= 6 && hour < 18) return 'bright';
if (hour >= 18 && hour < 20) return 'dim';
return 'dark';
}
/**
* 计算道路复杂度
*/
private calculateRoadComplexity(segmentIndex: number): number {
let score = 0;
// 路口数量
const nearbyIntersections = this.countNearbyIntersections(segmentIndex);
score += nearbyIntersections * 15;
// 路径段转向角度
if (segmentIndex > 0 && segmentIndex < this.routeData.length - 1) {
const prev = this.routeData[segmentIndex - 1];
const curr = this.routeData[segmentIndex];
const next = this.routeData[segmentIndex + 1];
const angle = this.calculateTurnAngle(prev, curr, next);
score += Math.abs(angle) / 10;
}
// 道路类型
const segment = this.routeData[segmentIndex];
if (segment?.roadType === 'roundabout') score += 30;
if (segment?.roadType === 'intersection') score += 20;
return Math.min(score, 100);
}
private countNearbyIntersections(index: number): number {
// 简化:统计附近路径段数量
let count = 0;
for (let i = Math.max(0, index - 2); i <= Math.min(this.routeData.length - 1, index + 2); i++) {
if (this.routeData[i]?.roadType === 'intersection') count++;
}
return count;
}
private calculateTurnAngle(prev: RouteSegment, curr: RouteSegment, next: RouteSegment): number {
// 简化计算转向角度
const dx1 = curr.end.latitude - curr.start.latitude;
const dy1 = curr.end.longitude - curr.start.longitude;
const dx2 = next.end.latitude - next.start.latitude;
const dy2 = next.end.longitude - next.start.longitude;
const angle1 = Math.atan2(dy1, dx1);
const angle2 = Math.atan2(dy2, dx2);
return (angle2 - angle1) * 180 / Math.PI;
}
/**
* 计算预计到达时间
*/
private calculateETA(location: GeoLocation, segmentIndex: number): number {
let remainingTime = 0;
const speed = Math.max(location.speed, 1); // 避免除零
for (let i = segmentIndex; i < this.routeData.length; i++) {
remainingTime += this.routeData[i].distance / speed;
}
return Math.round(remainingTime);
}
private registerSensors(): void {
// 注册加速度计用于检测运动状态
this.sensorManager?.on(sensor.SensorId.SENSOR_TYPE_ACCELEROMETER, (data) => {
// 分析加速度数据,辅助判断导航模式
});
}
onContextChange(callback: (context: NavigationContext) => void): void {
this.contextCallbacks.push(callback);
}
getCurrentContext(): NavigationContext | null {
return this.currentContext;
}
private async requestLocationPermission(): Promise<<boolean> {
// 实际应调用权限申请API
return true;
}
destroy(): void {
this.locationManager?.off('locationChange');
clearInterval(this.updateInterval);
}
}
4.2 沉浸光感导航引导(NavigationLightingController)
光效是导航意图的"视觉化延伸",让用户无需看屏幕就能感知转向方向。
// lighting/NavigationLightingController.ets
import { lighting } from '@kit.ArkUI';
import { NavigationMode, Maneuver } from '../engine/NavigationContextEngine';
export class NavigationLightingController {
private currentMode: NavigationMode = NavigationMode.WALKING;
private isNightMode: boolean = false;
private lastManeuver: string = '';
/**
* 初始化导航光感
* 设计哲学:光效应成为"第六感导航",不干扰但能感知
*/
async init(): Promise<void> {
if (!lighting.isImmersiveLightSupported()) {
console.warn('[NavLight] 设备不支持沉浸光感');
return;
}
// 初始状态:柔和白色,表示导航就绪
await this.setLightEffect({
type: 'solid',
position: 'bottom_edge',
color: '#E0E0E0',
brightness: 20,
duration: 0
});
console.info('[NavLight] 导航光感初始化完成');
}
/**
* 根据导航状态更新光效
*/
async updateByNavigation(
maneuver: Maneuver,
distance: number,
mode: NavigationMode,
isDeviating: boolean,
complexity: number
): Promise<void> {
this.currentMode = mode;
// 偏离路线最高优先级
if (isDeviating) {
await this.setDeviationAlert();
return;
}
// 复杂路口提示
if (complexity > 70 && distance < 100) {
await this.setComplexIntersectionAlert(maneuver);
return;
}
// 根据距离和转向类型设置光效
if (distance < 50) {
// 即将到达:强烈提示
await this.setImmediateTurn(maneuver);
} else if (distance < 200) {
// 接近中:温和提示
await this.setApproachingTurn(maneuver, distance);
} else {
// 远距离:仅显示方向趋势
await this.setDirectionHint(maneuver);
}
}
/**
* 立即转向光效
* 强烈脉冲,明确方向
*/
private async setImmediateTurn(maneuver: Maneuver): Promise<void> {
const { position, color } = this.getManeuverLighting(maneuver.type);
await lighting.setImmersiveLight({
type: 'flashing',
position: position,
color: color,
brightness: 70,
duration: 0,
flashCount: -1,
frequency: 400 // 快速闪烁
});
}
/**
* 接近转向光效
* 呼吸灯,温和提醒
*/
private async setApproachingTurn(maneuver: Maneuver, distance: number): Promise<void> {
const { position, color } = this.getManeuverLighting(maneuver.type);
// 距离越近,频率越快
const frequency = 2000 - (200 - distance) * 8; // 200米->2000ms, 50米->800ms
await lighting.setImmersiveLight({
type: 'breathing',
position: position,
color: color,
brightness: 50,
duration: 0,
frequency: Math.max(frequency, 800)
});
}
/**
* 方向提示光效
* 常亮微光,指示大致方向
*/
private async setDirectionHint(maneuver: Maneuver): Promise<void> {
const { position, color } = this.getManeuverLighting(maneuver.type);
await lighting.setImmerseLight({
type: 'solid',
position: position,
color: color,
brightness: 25,
duration: 0
});
}
/**
* 偏离路线告警
*/
private async setDeviationAlert(): Promise<void> {
await lighting.setImmersiveLight({
type: 'flashing',
position: 'all_edges',
color: '#FF1744',
brightness: 60,
duration: 0,
flashCount: -1,
frequency: 600
});
}
/**
* 复杂路口提示
*/
private async setComplexIntersectionAlert(maneuver: Maneuver): Promise<void> {
await lighting.setImmersiveLight({
type: 'wave',
position: 'all_edges',
color: '#FF9100',
brightness: 55,
duration: 0,
direction: 'clockwise',
speed: 'medium'
});
}
/**
* 获取转向对应的光效配置
*/
private getManeuverLighting(type: string): { position: string; color: string } {
const lightingMap: Record<string, { position: string; color: string }> = {
'turn_left': { position: 'left_edge', color: '#00E676' }, // 左转:左侧绿
'turn_right': { position: 'right_edge', color: '#00E676' }, // 右转:右侧绿
'slight_left': { position: 'left_edge', color: '#69F0AE' }, // 微左转:左侧浅绿
'slight_right': { position: 'right_edge', color: '#69F0AE' }, // 微右转:右侧浅绿
'uturn': { position: 'all_edges', color: '#FF1744' }, // 掉头:全红
'straight': { position: 'bottom_edge', color: '#448AFF' }, // 直行:底部蓝
'merge': { position: 'bottom_edge', color: '#FFD600' }, // 并线:底部黄
'roundabout': { position: 'all_edges', color: '#E040FB' } // 环岛:全紫
};
return lightingMap[type] || { position: 'bottom_edge', color: '#448AFF' };
}
/**
* 夜间模式
*/
async setNightMode(enabled: boolean): Promise<void> {
this.isNightMode = enabled;
if (enabled) {
// 夜间降低亮度,避免刺眼
await lighting.setImmersiveLight({
type: 'solid',
position: 'bottom_edge',
color: '#1A237E',
brightness: 10,
duration: 0
});
}
}
/**
* 到达目的地庆祝
*/
async celebrateArrival(): Promise<void> {
const colors = ['#00E676', '#00B0FF', '#2979FF', '#7C4DFF', '#F50057', '#FF9100'];
for (const color of colors) {
await lighting.setImmersiveLight({
type: 'solid',
position: 'all_edges',
color: color,
brightness: 50,
duration: 200
});
await new Promise(resolve => setTimeout(resolve, 200));
}
await this.reset();
}
async reset(): Promise<void> {
await lighting.resetImmersiveLight();
}
}
4.3 导航智能体引擎(NavigationAgentEngine)
这是系统的"导航大脑",负责理解路况、生成语音播报、提供智能建议。
// agent/NavigationAgentEngine.ets
import { ai } from '@kit.AiKit';
import { NavigationContext, NavigationMode, Maneuver, Surroundings } from '../engine/NavigationContextEngine';
export interface NavigationResponse {
type: 'instruction' | 'warning' | 'suggestion' | 'arrival';
message: string;
priority: 'high' | 'medium' | 'low';
actions?: string[];
displayMode: 'capsule' | 'panel' | 'ar';
}
export class NavigationAgentEngine {
private agent: ai.AgentSession | null = null;
private lastInstruction: string = '';
private repeatCount: number = 0;
/**
* 初始化导航智能体
* 加载导航知识库
*/
async init(): Promise<void> {
const model = await ai.createModel({
modelId: 'harmonyos-navigation-v1',
type: ai.ModelType.LOCAL,
capabilities: ['route_analysis', 'traffic_prediction', 'natural_language']
});
this.agent = await ai.createAgentSession({
model: model,
systemPrompt: `你是一位专业的导航助手,精通城市道路与HarmonyOS导航服务。
请基于用户当前的导航上下文,生成:
1. 简洁准确的转向指令(不超过15字)
2. 路况预警与替代建议
3. 周边POI推荐
4. 安全提醒
回答要求:
- 指令必须简洁,适合语音播报
- 复杂路口提供分步指引
- 夜间/恶劣天气增加安全提醒
- 使用中文回答`
});
console.info('[NavAgent] 导航智能体初始化完成');
}
/**
* 生成导航指令
*/
async generateInstruction(context: NavigationContext): Promise<<NavigationResponse> {
if (!this.agent) {
return this.fallbackInstruction(context);
}
// 检查是否需要重复播报
const instruction = this.buildInstruction(context);
if (instruction === this.lastInstruction) {
this.repeatCount++;
if (this.repeatCount < 3) {
return { type: 'instruction', message: '', priority: 'low', displayMode: 'capsule' };
}
} else {
this.repeatCount = 0;
}
this.lastInstruction = instruction;
// 复杂路口使用AR模式
const displayMode = context.roadComplexity > 60 ? 'ar' :
context.distanceToNext < 100 ? 'panel' : 'capsule';
// 偏离检测
if (context.isDeviating) {
return {
type: 'warning',
message: '您已偏离路线,正在重新规划...',
priority: 'high',
displayMode: 'panel'
};
}
// 安全提醒
if (context.surroundings.lightingCondition === 'dark' && context.navigationMode === NavigationMode.WALKING) {
return {
type: 'warning',
message: '夜间步行,请注意安全',
priority: 'medium',
displayMode: 'capsule'
};
}
// 到达目的地
if (context.distanceToNext < 20 && context.currentSegment >= context.routePath.length - 1) {
return {
type: 'arrival',
message: '到达目的地附近,导航结束',
priority: 'high',
displayMode: 'panel'
};
}
return {
type: 'instruction',
message: instruction,
priority: context.distanceToNext < 50 ? 'high' : 'medium',
displayMode
};
}
/**
* 构建导航指令
*/
private buildInstruction(context: NavigationContext): string {
const maneuver = context.nextManeuver;
const distance = this.formatDistance(context.distanceToNext);
let instruction = '';
// 根据转向类型生成指令
switch (maneuver.type) {
case 'turn_left':
instruction = `${distance}后左转`;
break;
case 'turn_right':
instruction = `${distance}后右转`;
break;
case 'straight':
instruction = `${distance}后直行`;
break;
case 'uturn':
instruction = `${distance}后掉头`;
break;
case 'roundabout':
instruction = `进入环岛,${maneuver.exitNumber}出口驶出`;
break;
case 'merge':
instruction = `${distance}后并线`;
break;
default:
instruction = `${distance}后${maneuver.instruction}`;
}
// 添加地标提示
if (maneuver.landmark) {
instruction += `,注意${maneuver.landmark}`;
}
// 复杂路口额外提示
if (context.roadComplexity > 70) {
instruction += ',复杂路口请留意';
}
return instruction;
}
/**
* 生成路况建议
*/
async generateTrafficSuggestion(context: NavigationContext): Promise<<NavigationResponse> {
const prompt = `基于当前导航上下文生成路况建议:
当前模式:${context.navigationMode}
当前道路:${context.routePath[context.currentSegment]?.roadName}
道路复杂度:${context.roadComplexity}
周边环境:POI密度${context.surroundings.poiDensity},光照${context.surroundings.lightingCondition}
请给出1-2条实用的路况建议或周边推荐。`;
const result = await this.agent!.invoke({
input: { question: prompt },
options: { maxTokens: 256, temperature: 0.3 }
});
return {
type: 'suggestion',
message: result.data?.suggestion || '路况良好,请保持当前路线',
priority: 'low',
displayMode: 'capsule'
};
}
/**
* 生成AR指引
*/
async generateARGuidance(context: NavigationContext): Promise<<{
arrows: ARArrow[];
labels: ARLabel[];
warnings: string[];
}> {
const maneuver = context.nextManeuver;
const arrows: ARArrow[] = [];
const labels: ARLabel[] = [];
const warnings: string[] = [];
// 根据转向类型生成AR箭头
switch (maneuver.type) {
case 'turn_left':
arrows.push({ type: 'left', position: 'center', distance: context.distanceToNext });
break;
case 'turn_right':
arrows.push({ type: 'right', position: 'center', distance: context.distanceToNext });
break;
case 'straight':
arrows.push({ type: 'straight', position: 'center', distance: context.distanceToNext });
break;
case 'roundabout':
arrows.push({ type: 'roundabout', position: 'center', exitNumber: maneuver.exitNumber });
labels.push({ text: `${maneuver.exitNumber}出口`, position: 'top' });
break;
}
// 复杂路口警告
if (context.roadComplexity > 70) {
warnings.push('复杂路口,请减速慢行');
}
// 夜间警告
if (context.surroundings.lightingCondition === 'dark') {
warnings.push('夜间视线不佳,注意安全');
}
return { arrows, labels, warnings };
}
/**
* 格式化距离
*/
private formatDistance(meters: number): string {
if (meters < 50) return '即将';
if (meters < 1000) return `${Math.round(meters / 10) * 10}米`;
return `${(meters / 1000).toFixed(1)}公里`;
}
/**
* 降级指令生成
*/
private fallbackInstruction(context: NavigationContext): NavigationResponse {
return {
type: 'instruction',
message: `${this.formatDistance(context.distanceToNext)}后${context.nextManeuver.instruction}`,
priority: 'medium',
displayMode: 'capsule'
};
}
destroy(): void {
this.agent?.destroy();
}
}
// AR元素类型定义
interface ARArrow {
type: string;
position: string;
distance?: number;
exitNumber?: number;
}
interface ARLabel {
text: string;
position: string;
}
4.4 悬浮窗导航面板(NavigationFloatWindow)
// float/NavigationFloatWindow.ets
import { window } from '@kit.ArkUI';
import { emitter } from '@kit.BasicServicesKit';
import { NavigationContext } from '../engine/NavigationContextEngine';
import { NavigationResponse } from '../agent/NavigationAgentEngine';
export class NavigationFloatWindow {
private floatWin: window.Window | null = null;
private currentLevel: 'capsule' | 'panel' | 'ar' = 'capsule';
async create(): Promise<void> {
const option: window.WindowOption = {
name: 'ARNavigation',
windowType: window.WindowType.TYPE_FLOAT,
ctx: getContext(this)
};
this.floatWin = await window.createWindow(getContext(this), option);
// 胶囊形态:极简导航信息
await this.floatWin.resize({ width: 120, height: 80 });
await this.floatWin.moveWindowTo({ x: 900, y: 100 });
await this.floatWin.setWindowTouchable(true);
await this.floatWin.setUIContent('pages/NavCapsulePage');
await this.floatWin.showWindow();
}
async expandToPanel(context: NavigationContext, response: NavigationResponse): Promise<void> {
if (!this.floatWin) return;
this.currentLevel = 'panel';
await this.floatWin.resize({ width: 400, height: 600 });
await this.floatWin.moveWindowTo({ x: 620, y: 120 });
await this.floatWin.setUIContent('pages/NavPanelPage');
emitter.emit('showNavPanel', { data: { context, response } });
}
async expandToAR(context: NavigationContext, guidance: any): Promise<void> {
if (!this.floatWin) return;
this.currentLevel = 'ar';
await this.floatWin.resize({ width: '100%', height: '100%' });
await this.floatWin.moveWindowTo({ x: 0, y: 0 });
await this.floatWin.setUIContent('pages/NavARPage');
emitter.emit('showNavAR', { data: { context, guidance } });
}
async collapseToCapsule(): Promise<void> {
if (!this.floatWin) return;
this.currentLevel = 'capsule';
await this.floatWin.resize({ width: 120, height: 80 });
await this.floatWin.moveWindowTo({ x: 900, y: 100 });
await this.floatWin.setUIContent('pages/NavCapsulePage');
}
destroy(): void {
this.floatWin?.destroyWindow();
}
}
4.5 导航胶囊页面(NavCapsulePage)
// pages/NavCapsulePage.ets
import { emitter } from '@kit.BasicServicesKit';
import { NavigationMode } from '../engine/NavigationContextEngine';
@Entry
@Component
struct NavCapsulePage {
@State distance: string = '200米';
@State direction: string = '右转';
@State mode: NavigationMode = NavigationMode.WALKING;
@State eta: string = '15分钟';
@State isNight: boolean = false;
private modeIcons: Record<<NavigationMode, string> = {
[NavigationMode.WALKING]: '🚶',
[NavigationMode.CYCLING]: '🚴',
[NavigationMode.DRIVING]: '🚗',
[NavigationMode.PUBLIC_TRANSIT]: '🚌'
};
aboutToAppear() {
emitter.on('updateNavContext', (event) => {
const ctx = event.data;
this.distance = this.formatDistance(ctx?.distanceToNext);
this.direction = ctx?.nextManeuver?.instruction || '直行';
this.mode = ctx?.navigationMode || NavigationMode.WALKING;
this.eta = this.formatTime(ctx?.estimatedTime);
this.isNight = ctx?.surroundings?.lightingCondition === 'dark';
});
}
build() {
Stack() {
// 夜间模式遮罩
if (this.isNight) {
Column()
.width('100%')
.height('100%')
.backgroundColor('rgba(0,0,0,0.3)')
.borderRadius(16)
}
Column() {
// 模式图标
Text(this.modeIcons[this.mode])
.fontSize(20)
// 距离
Text(this.distance)
.fontSize(18)
.fontWeight(FontWeight.Bold)
.fontColor(this.isNight ? '#FFF' : '#333')
// 方向
Text(this.direction)
.fontSize(12)
.fontColor(this.isNight ? '#CCC' : '#666')
// ETA
Text(this.eta)
.fontSize(10)
.fontColor(this.isNight ? '#999' : '#999')
}
.width(120)
.height(80)
.justifyContent(FlexAlign.Center)
.backgroundColor(this.isNight ? 'rgba(30,30,30,0.95)' : 'rgba(255,255,255,0.95)')
.borderRadius(16)
.shadow({ radius: 12, color: 'rgba(0,0,0,0.15)' })
.gesture(
GestureGroup(GestureMode.Sequence,
TapGesture({ count: 1 })
.onAction(() => emitter.emit('expandToPanel')),
TapGesture({ count: 2 })
.onAction(() => emitter.emit('expandToAR')),
LongPressGesture({ duration: 800 })
.onAction(() => emitter.emit('toggleVoiceGuidance'))
)
)
}
.width('100%')
.height('100%')
.align(Alignment.Center)
}
private formatDistance(meters: number): string {
if (meters < 50) return '即将';
if (meters < 1000) return `${Math.round(meters / 10) * 10}米`;
return `${(meters / 1000).toFixed(1)}公里`;
}
private formatTime(seconds: number): string {
if (seconds < 60) return `${seconds}秒`;
if (seconds < 3600) return `${Math.round(seconds / 60)}分钟`;
return `${Math.floor(seconds / 3600)}小时${Math.round((seconds % 3600) / 60)}分钟`;
}
}
4.6 导航面板页面(NavPanelPage)
// pages/NavPanelPage.ets
import { emitter } from '@kit.BasicServicesKit';
import { NavigationContext, NavigationMode } from '../engine/NavigationContextEngine';
import { NavigationResponse } from '../agent/NavigationAgentEngine';
@Entry
@Component
struct NavPanelPage {
@State context: NavigationContext | null = null;
@State response: NavigationResponse | null = null;
aboutToAppear() {
emitter.on('showNavPanel', (event) => {
this.context = event.data?.context;
this.response = event.data?.response;
});
}
build() {
Column() {
// 顶部标题栏
Row() {
Text('🧭 导航')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.layoutWeight(1)
Button('收起')
.fontSize(12)
.backgroundColor('#f0f0f0')
.fontColor('#333')
.onClick(() => emitter.emit('collapseToCapsule'))
}
.width('100%')
.padding(16)
// 主要指令
Column() {
Text(this.response?.message || '继续直行')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.fontColor('#333')
.textAlign(TextAlign.Center)
if (this.context) {
Text(`${this.formatDistance(this.context.distanceToNext)} · ${this.formatTime(this.context.estimatedTime)}`)
.fontSize(14)
.fontColor('#666')
.margin({ top: 8 })
}
}
.width('100%')
.padding(20)
.backgroundColor('#f8f9fa')
.borderRadius(12)
.margin({ bottom: 12 })
// 路径概览
List() {
ForEach(this.context?.routePath || [], (segment: any, index: number) => {
ListItem() {
Row() {
// 路径点
Column() {
Circle()
.width(12)
.height(12)
.fill(index === this.context?.currentSegment ? '#2979FF' : '#E0E0E0')
if (index < (this.context?.routePath.length || 0) - 1) {
Column()
.width(2)
.height(30)
.backgroundColor('#E0E0E0')
}
}
.width(30)
.alignItems(HorizontalAlign.Center)
Column() {
Text(segment.roadName || '未知道路')
.fontSize(14)
.fontWeight(FontWeight.Medium)
.width('100%')
Text(`${this.formatDistance(segment.distance)} · ${segment.maneuver.instruction}`)
.fontSize(12)
.fontColor('#666')
.width('100%')
}
.layoutWeight(1)
.margin({ left: 8 })
}
.width('100%')
.padding({ top: 4, bottom: 4 })
}
})
}
.width('100%')
.layoutWeight(1)
// 底部操作
Row() {
Button('🗺️ 地图')
.fontSize(14)
.backgroundColor('#E3F2FD')
.fontColor('#2979FF')
.layoutWeight(1)
.onClick(() => emitter.emit('showMap'))
Button('🎙️ 语音')
.fontSize(14)
.backgroundColor('#E8F5E9')
.fontColor('#2E7D32')
.margin({ left: 8 })
.layoutWeight(1)
.onClick(() => emitter.emit('toggleVoiceGuidance'))
Button('⚙️ 设置')
.fontSize(14)
.backgroundColor('#f0f0f0')
.fontColor('#666')
.margin({ left: 8 })
.layoutWeight(1)
.onClick(() => emitter.emit('showSettings'))
}
.width('100%')
.padding({ top: 12 })
}
.width('100%')
.height('100%')
.padding(16)
.backgroundColor('#FFF')
.borderRadius(16)
}
private formatDistance(meters: number): string {
if (meters < 1000) return `${Math.round(meters)}米`;
return `${(meters / 1000).toFixed(1)}公里`;
}
private formatTime(seconds: number): string {
if (seconds < 60) return `${seconds}秒`;
return `${Math.round(seconds / 60)}分钟`;
}
}
4.7 主入口与系统集成(Index.ets)
// Index.ets
import { NavigationContextEngine, NavigationMode } from './engine/NavigationContextEngine';
import { NavigationLightingController } from './lighting/NavigationLightingController';
import { NavigationFloatWindow } from './float/NavigationFloatWindow';
import { NavigationAgentEngine } from './agent/NavigationAgentEngine';
import { emitter } from '@kit.BasicServicesKit';
@Entry
@Component
struct ARNavigationApp {
private navEngine: NavigationContextEngine = new NavigationContextEngine();
private lightController: NavigationLightingController = new NavigationLightingController();
private floatWindow: NavigationFloatWindow = new NavigationFloatWindow();
private agentEngine: NavigationAgentEngine = new NavigationAgentEngine();
aboutToAppear() {
this.initSystem();
}
aboutToDisappear() {
this.navEngine.destroy();
this.lightController.reset();
this.floatWindow.destroy();
this.agentEngine.destroy();
}
async initSystem() {
// 1. 初始化沉浸光感
await this.lightController.init();
// 2. 初始化悬浮窗
await this.floatWindow.create();
// 3. 初始化智能体引擎
await this.agentEngine.init();
// 4. 初始化导航引擎
await this.navEngine.init();
// 当导航上下文变化时,更新光效和悬浮窗
this.navEngine.onContextChange(async (context) => {
// 更新光效
await this.lightController.updateByNavigation(
context.nextManeuver,
context.distanceToNext,
context.navigationMode,
context.isDeviating,
context.roadComplexity
);
// 生成导航指令
const response = await this.agentEngine.generateInstruction(context);
// 根据响应类型调整悬浮窗
if (response.displayMode === 'ar' && context.roadComplexity > 60) {
const guidance = await this.agentEngine.generateARGuidance(context);
await this.floatWindow.expandToAR(context, guidance);
} else if (response.displayMode === 'panel') {
await this.floatWindow.expandToPanel(context, response);
}
// 更新悬浮窗
emitter.emit('updateNavContext', { data: context });
// 夜间模式
if (context.surroundings.lightingCondition === 'dark') {
await this.lightController.setNightMode(true);
}
});
// 5. 设置事件监听
this.setupEventListeners();
// 6. 设置模拟路线(实际应从地图服务获取)
this.navEngine.setRoute([
{
start: { latitude: 31.2304, longitude: 121.4737, altitude: 0, accuracy: 10, heading: 0, speed: 0 },
end: { latitude: 31.2354, longitude: 121.4787, altitude: 0, accuracy: 10, heading: 90, speed: 1.2 },
distance: 500,
duration: 300,
maneuver: { type: 'straight', instruction: '沿南京东路直行' },
roadName: '南京东路',
roadType: 'main',
complexity: 20
},
{
start: { latitude: 31.2354, longitude: 121.4787, altitude: 0, accuracy: 10, heading: 90, speed: 1.2 },
end: { latitude: 31.2354, longitude: 121.4800, altitude: 0, accuracy: 10, heading: 0, speed: 0.5 },
distance: 100,
duration: 60,
maneuver: { type: 'turn_right', instruction: '右转进入外滩' },
roadName: '中山东一路',
roadType: 'intersection',
complexity: 60
}
], { latitude: 31.2354, longitude: 121.4800, altitude: 0, accuracy: 10, heading: 0, speed: 0 });
}
private setupEventListeners() {
emitter.on('expandToPanel', async () => {
const context = this.navEngine.getCurrentContext();
if (context) {
const response = await this.agentEngine.generateInstruction(context);
await this.floatWindow.expandToPanel(context, response);
}
});
emitter.on('expandToAR', async () => {
const context = this.navEngine.getCurrentContext();
if (context) {
const guidance = await this.agentEngine.generateARGuidance(context);
await this.floatWindow.expandToAR(context, guidance);
}
});
emitter.on('collapseToCapsule', async () => {
await this.floatWindow.collapseToCapsule();
});
emitter.on('toggleVoiceGuidance', () => {
// 切换语音播报
});
}
build() {
Column() {
Text('🧭 HarmonyOS AR导航助手')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 8 })
Text('AI智能体正在为您导航...')
.fontSize(14)
.fontColor('#666')
Text('悬浮窗常驻显示,光效引导方向')
.fontSize(12)
.fontColor('#999')
.margin({ top: 4 })
.textAlign(TextAlign.Center)
// 导航统计
Column() {
Text('📊 本次导航')
.fontSize(16)
.fontWeight(FontWeight.Medium)
.margin({ bottom: 12 })
Row() {
Column() {
Text('1.2km')
.fontSize(28)
.fontWeight(FontWeight.Bold)
.fontColor('#2979FF')
Text('剩余距离')
.fontSize(12)
.fontColor('#999')
}
.layoutWeight(1)
Column() {
Text('15min')
.fontSize(28)
.fontWeight(FontWeight.Bold)
.fontColor('#00C853')
Text('预计到达')
.fontSize(12)
.fontColor('#999')
}
.layoutWeight(1)
Column() {
Text('🚶')
.fontSize(28)
Text('步行模式')
.fontSize(12)
.fontColor('#999')
}
.layoutWeight(1)
}
.width('100%')
}
.width('90%')
.padding(20)
.backgroundColor('#f8f9fa')
.borderRadius(12)
.margin({ top: 32 })
// 当前状态
Row() {
Text('导航状态:')
.fontSize(14)
.fontColor('#666')
Text('🟢 导航中')
.fontSize(14)
.fontColor('#00C853')
.fontWeight(FontWeight.Medium)
}
.margin({ top: 20 })
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
}
五、配置文件
// module.json5
{
"module": {
"name": "ARNavigation",
"type": "entry",
"description": "鸿蒙智能体AR导航助手",
"mainElement": "EntryAbility",
"deviceTypes": ["phone", "tablet", "2in1"],
"pages": "$profile:main_pages",
"abilities": [
{
"name": "EntryAbility",
"srcEntry": "./ets/entryability/EntryAbility.ets",
"description": "AR导航助手主入口",
"icon": "$media:layered_image",
"label": "AI AR导航",
"startWindowIcon": "$media:startIcon",
"startWindowBackground": "$color:start_window_background",
"exported": true,
"skills": [
{
"entities": ["entity.system.home"],
"actions": ["action.system.home"]
}
]
}
],
"requestPermissions": [
{
"name": "ohos.permission.SYSTEM_FLOAT_WINDOW",
"reason": "需要悬浮窗权限以常驻显示导航信息"
},
{
"name": "ohos.permission.LOCATION",
"reason": "需要定位权限以获取当前位置"
},
{
"name": "ohos.permission.LOCATION_IN_BACKGROUND",
"reason": "后台导航需要持续定位"
},
{
"name": "ohos.permission.ACCESS_SENSORS",
"reason": "读取传感器数据辅助导航"
},
{
"name": "ohos.permission.INTERNET",
"reason": "连接地图服务与云端智能体"
},
{
"name": "ohos.permission.ACCESS_AI_MODEL",
"reason": "使用端侧AI模型进行路况分析"
},
{
"name": "ohos.permission.CAMERA",
"reason": "AR导航需要摄像头权限"
}
]
}
}
六、效果展示与使用场景
6.1 典型导航场景
场景A:步行游览
游客小李在上海外滩步行游览,手机放在口袋,悬浮胶囊常驻屏幕边缘显示"前方300米右转"。设备右侧边框泛起绿色脉冲光效,提示右转在即。小李无需低头,余光感知到右侧光效,自然转向。到达复杂五岔路口时,悬浮窗自动展开AR模式,虚拟箭头叠加在实景上,语音播报"走中间第二条路"。
场景B:夜间骑行
骑手小王夜间骑行回家,屏幕自动切换深色模式。接近路口时,设备底部边框泛起琥珀色呼吸光效,配合轻微震动提示减速。需要左转时,左侧边框泛起绿色脉冲,右侧保持暗色,方向明确无误。遇到施工路段,智能体语音提醒"前方道路施工,建议绕行至左侧小路"。
场景C:复杂路口AR增强
到达环岛时,悬浮窗自动展开AR模式,摄像头画面中叠加紫色虚拟箭头,指示"第3出口驶出"。同时设备四周边框同步泛起紫色波浪光效,与AR箭头协同提示。智能体播报"进入环岛,注意第3出口有学校,请减速"。
6.2 光效语义设计
| 导航状态 | 光效表现 | 用户提示 |
|---|---|---|
| 直行 | 底部蓝色常亮 | 保持当前方向 |
| 左转在即 | 左侧绿色快闪 | 准备左转 |
| 右转在即 | 右侧绿色快闪 | 准备右转 |
| 接近转向 | 对应侧绿色呼吸 | 减速,准备转向 |
| 偏离路线 | 全边框红色快闪 | 已偏离,注意查看 |
| 复杂路口 | 全边框橙色波浪 | 复杂路口,开启AR |
| 夜间模式 | 深蓝微光 | 夜间导航,注意安全 |
| 到达目的地 | 彩虹渐变 | 导航结束,恭喜到达 |
七、性能与隐私优化
7.1 定位策略
// 定位优化策略
const locationStrategy = {
// 定位精度:根据场景动态调整
accuracy: {
walking: 'high', // 步行:高精度
cycling: 'balanced', // 骑行:平衡
driving: 'low' // 驾车:低功耗
},
// 更新频率
interval: {
walking: 1000, // 步行:1秒
cycling: 2000, // 骑行:2秒
driving: 5000 // 驾车:5秒
},
// 传感器融合
sensorFusion: {
accelerometer: true, // 加速度计辅助
gyroscope: true, // 陀螺仪辅助
compass: true // 电子罗盘
}
};
7.2 隐私保护
- 位置脱敏:仅上传路径哈希,不上传精确位置
- 本地优先:路径规划优先本地缓存,减少云端交互
- 权限最小化:非导航时段不获取位置
- 数据加密:导航历史本地加密存储
八、总结与展望
本文展示了如何基于HarmonyOS 6(API 23)的悬浮导航和沉浸光感能力,构建一个鸿蒙智能体驱动的沉浸式AR导航助手。与传统导航应用不同,它具备三个核心创新:
- 余光导航:悬浮窗常驻边缘+光效方向引导,无需低头即可感知导航意图
- 情境智能:AI智能体自动识别步行/骑行/驾车模式,动态调整导航策略
- 多感官协同:视觉(悬浮窗)+ 光感(边框)+ 听觉(语音)+ 触觉(震动)协同指引
未来演进方向:
- 多设备协同:手机悬浮窗 + 手表震动 + AR眼镜全息指引
- 预测性导航:基于日程和习惯,提前规划路线并推送
- 社交导航:多人同行时,设备光效同步显示队伍位置
- 无障碍导航:为视障用户提供增强光效+语音+震动的多模态指引
HarmonyOS 6的智能体框架和系统级交互创新,正在让"AI导航伙伴常驻身边"成为现实。对于城市出行者而言,这不仅是一个导航工具,更是一位24小时在线、懂路况、懂安全、懂你的智能出行伙伴。
转载自:https://blog.csdn.net/u014727709/article/details/161390379
欢迎 👍点赞✍评论⭐收藏,欢迎指正
更多推荐




所有评论(0)