阅读 461

EasyAR4.0使用说明(Unity3D)七----稀疏空间地图

稀疏空间地图的对应用环境的要求和平面图像识别可以比照理解,周围环境需要足够丰富,不能有大片的单色区域,透明区域。此外,光照,角度都会对建立地图和定位产生影响。

官方给出了建立地图和定位地图的建议。help.easyar.cn/EasyAR%20Se…

总体说明

稀疏空间地图的基础是运动跟踪,所有在场景种首先要有运动跟踪的全套游戏对象包括设置。然后主要的是 SparseSpatialMapWorker 和 SparseSpatialMap 这两个游戏对象。

总体说明

SparseSpatialMapWorker 游戏对象相关

SparseSpatialMapWorker游戏对象相关

  • Locailzation Mode 属性在建立地图的时候通常选“UntilSuccess”,在定位的时候,通常选“KeepUpdate”。
  • Use Global Service Config 选项可以设置是否使用全局定义的稀疏空间地图信息。
  • BuilderMapController.Host(...)方法是保存地图的方法,需要输入的参数是地图的名称和地图的缩略图,缩略图可以输入“null”。
  • BuilderMapController.MapHost 事件用于返回地图保存情况的事件。事件有 3 个参数,是地图保存成功后的名称,ID,是否保存成功的状态,还有错误信息。
  • Localizer.startLocalization()和 Localizer.stopLocalization()方法是用来启动和停止本地稀疏空间定位的方法。如果 SparseSpatialMap 游戏对象设置了地图的 ID 和名称的时候,默认会自动启动地图定位。

SparseSpatialMap 游戏对象相关

SparseSpatialMap 游戏对象是稀疏空间地图在 Unity 中的载体,每个稀疏空间地图在定位的时候对应一个 SparseSpatialMap 游戏对象,同一个场景可以同时有多个稀疏空间地图。希望在某个稀疏空间地图中放置的虚拟物体,将其对应的游戏对象放置到对应的 SparseSpatialMap 游戏对象下成为其子游戏对象即可。

SparseSpatialMap游戏对象相关

  • Source Type 属性用于设置稀疏空间地图的作用,即是用于建立地图“Map Builder”还是用于定位“Map Manager”。
  • Map Worker 属性必须关联对应的 SparseSpatialMapWorker 游戏对象。通常不需要设置。
  • Show Point Cloud 选项可以设置是否点云的效果。在建图的时候,显示点云的效果能帮助使用者更好的建立稀疏空间地图。
  • MapLoad 事件是指定的稀疏空间地图从服务器端下载到本地触发的事件。
  • MapLocalized、MapStopLocalize 事件是地图实现定位和停止定位的事件。MapLocalized 可以被触发多次,或者理解为可以不断修正位置。

建立地图

  • 设置场景中的 Main Camera 的 Clear Flags 属性为 Solid Color。
  • 将 EasyAR/Prefabs/Composites 目录下的 EasyAR_SparseSpatialMapWorker 预制件拖到场景中。

建立地图

  • 将 EasyAR/Prefabs/Primitives 目录下的 WorldRoot 预制件拖到场景中。
  • 选中 EasyAR_ SparseSpatialMapWorker 游戏对象,将 WorldRoot 游戏对象拖到 World Root Controller 属性中为其赋值。

建立地图

  • 将 EasyAR/Prefabs/Primitives 目录下的 SparseSpatialMap 预制件拖到场景中。

建立地图

在 SparseSpatialMap 游戏对象下再添加用于分辨方向的模型,用形状来和 WorldRoot 游戏对象下的进行区分。

建立地图

添加界面和脚本。界面中有保存按钮和显示反馈信息的文本框。

建立地图

脚本说明: 保存完地图需要将获取的ID和Name保存下来。官方没有提供稀疏空间地图访问的API。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using easyar;
using System;

public class BuildMapController : MonoBehaviour
{
    //稀疏空间地图相关对象
    private ARSession session;
    private SparseSpatialMapWorkerFrameFilter mapWorker;
    private SparseSpatialMapController map;
    /// <summary>
    /// 保存按钮
    /// </summary>
    private Button btnSave;
    /// <summary>
    /// 显示文本
    /// </summary>
    private Text text;

    void Start()
    {
        //稀疏空间地图初始
        session = FindObjectOfType<ARSession>();
        mapWorker = FindObjectOfType<SparseSpatialMapWorkerFrameFilter>();
        map = FindObjectOfType<SparseSpatialMapController>();
        //注册追踪状态变化事件
        session.WorldRootController.TrackingStatusChanged += OnTrackingStatusChanged;
        //初始化保存按钮
        btnSave = GameObject.Find("/Canvas/Button").GetComponent<Button>();
        btnSave.onClick.AddListener(Save);
        btnSave.interactable = false;
        if (session.WorldRootController.TrackingStatus == MotionTrackingStatus.Tracking)
        {
            btnSave.interactable = true;
        }
        else
        {
            btnSave.interactable = false;
        }
        //初始化显示文本
        text = GameObject.Find("/Canvas/Panel/Text").GetComponent<Text>();
    }

    /// <summary>
    /// 保存地图方法
    /// </summary>
    private void Save()
    {
        btnSave.interactable = false;
        //注册地图保存结果反馈事件
        mapWorker.BuilderMapController.MapHost += SaveMapHostBack;
        //保存地图
        try
        {
            //保存地图
            mapWorker.BuilderMapController.Host("LearnMap" + DateTime.Now.ToString("yyyyMMddHHmm"), null);
            text.text = "开始保存地图,请稍等。";
        }
        catch (Exception ex)
        {
            btnSave.interactable = true;
            text.text = "保存出错:" + ex.Message;
        }
    }

    /// <summary>
    /// 保存地图反馈
    /// </summary>
    /// <param name="mapInfo">地图信息</param>
    /// <param name="isSuccess">成功标识</param>
    /// <param name="error">错误信息</param>
    private void SaveMapHostBack(SparseSpatialMapController.SparseSpatialMapInfo mapInfo, bool isSuccess, string error)
    {
        if (isSuccess)
        {
            PlayerPrefs.SetString("MapID", mapInfo.ID);
            PlayerPrefs.SetString("MapName", mapInfo.Name);
            text.text = "地图保存成功。\r\nMapID:" + mapInfo.ID + "\r\nMapName:" + mapInfo.Name;
        }
        else
        {
            btnSave.interactable = true;
            text.text = "地图保存出错:" + error;
        }
    }

    /// <summary>
    /// 摄像机状态变化
    /// </summary>
    /// <param name="status">状态</param>
    private void OnTrackingStatusChanged(MotionTrackingStatus status)
    {
        if (status == MotionTrackingStatus.Tracking)
        {
            btnSave.interactable = true;
            text.text = "进入跟踪状态。";
        }
        else
        {
            btnSave.interactable = false;
            text.text = "退出跟踪状态。" + status.ToString();
        }
    }
}

复制代码

打包以后在设备上运行。进入以后,会显示点云效果。稀疏空间地图SparseSpatialMap游戏对象和运动跟踪WorldRoot游戏对象两者的原点方向都是一致的。当扫描完周围空间以后,点击保存按钮,就可以将地图保存到服务器。在提示文本中会显示地图的ID和名称。

建立地图

本地化地图

  • 将建立地图场景另存为作为本地化的场景。
  • 选中SparseSpatialMapWorker游戏对象,修改Localization Mode属性为Keep Update。

本地化地图

  • 选中SparseSpatialMap游戏对象,修改Source Type属性为Map Manager。

本地化地图

修改界面,设置界面里有2个输入框,分别用于输入地图ID和地图名称,有一个文本框显示提示内容,2个按钮,用于本地化地图和停止定位。

本地化地图

替换脚本。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System;
using easyar;


public class LocalizeMapController : MonoBehaviour
{
    //稀疏空间地图相关对象
    private ARSession session;
    private SparseSpatialMapWorkerFrameFilter mapWorker;
    private SparseSpatialMapController map;
    /// <summary>
    ///地图 ID输入框
    /// </summary>
    public InputField inputID;
    /// <summary>
    /// 地图名称输入框
    /// </summary>
    public InputField inputName;
    /// <summary>
    /// 文本显示
    /// </summary>
    public Text text;


    void Start()
    {
        //稀疏空间地图初始
        session = FindObjectOfType<ARSession>();
        mapWorker = FindObjectOfType<SparseSpatialMapWorkerFrameFilter>();
        map = FindObjectOfType<SparseSpatialMapController>();

        //如果之前有建立过地图且文本框没有预设值
        if (inputID.text.Length <= 0)
        {
            inputID.text = PlayerPrefs.GetString("MapID", "");
            inputName.text = PlayerPrefs.GetString("MapName", "");
        }

        map.MapLoad += MapLoadBack; //注册地图加载事件
        map.MapLocalized += LocalizedMap;   //注册定位成功事件
        map.MapStopLocalize += StopLocalizeMap; //注册停止定位事件

        StartLocalization();
    }

    /// <summary>
    /// 地图加载反馈
    /// </summary>
    /// <param name="mapInfo">地图信息</param>
    /// <param name="isSuccess">是否成功</param>
    /// <param name="error">错误信息</param>
    private void MapLoadBack(SparseSpatialMapController.SparseSpatialMapInfo mapInfo, bool isSuccess, string error)
    {
        if (isSuccess)
        {
            text.text = "地图" + mapInfo.Name + "加载成功。";
        }
        else
        {
            text.text = "地图加载失败。" + error;
        }
    }
    /// <summary>
    /// 地图定位成功
    /// </summary>
    private void LocalizedMap()
    {
        text.text = "稀疏空间地图定位成功。" + DateTime.Now.ToShortTimeString();
    }
    /// <summary>
    /// 停止地图定位
    /// </summary>
    private void StopLocalizeMap()
    {
        text.text = "稀疏空间地图停止定位。" + DateTime.Now.ToShortTimeString();
    }
    /// <summary>
    /// 开始本地化地图
    /// </summary>
    public void StartLocalization()
    {
        //文本框内容不为空
        if (inputID.text.Length > 0 && inputName.text.Length > 0)
        {
            map.MapManagerSource.ID = inputID.text;
            map.MapManagerSource.Name = inputName.text;
        }
        text.text = "开始本地化地图。";
        mapWorker.Localizer.startLocalization();
    }
    /// <summary>
    /// 停止本地化
    /// </summary>
    public void StopLocalization()
    {
        mapWorker.Localizer.stopLocalization();
    }

}
复制代码

打包后在设备上运行,默认获取之前建立的地图。期间可以停止定位以后,输入新的地图ID和名称再点击Start开始本地化地图。

地图的原点和方向即SparseSpatialMap游戏对象的位置方向和本地化时候设备的状态无关,和建立地图时候的状态一致。

本地化地图

视频版地址:www.bilibili.com/video/BV1Xg…