2018-10-17 20:57:25 juoduomade 阅读数 958
  • Unity 值得看的500+ 技术内容列表

    Unity3D是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

高德地图今天宣布推出车载 AR 导航。该产品借助高德地图专业的交通大数据和车道级导航引擎,以及阿里技术共建的图像识别 AI 技术能力,将真实的道路场景与虚拟的导航指引有机结合,给驾驶员带来更直观的实景导航体验。

  该产品计划首批应用在智能后视镜上,做能力验证,后续重点将拓展至仪表盘、车机中控屏以及 HUD 平视系统等更多使用场景,针对不同的展示载体打磨最优的用户体验效果。据悉,该产品也是国内首个真正落地的可多场景使用的车载 AR 导航。


*高德地图车载 AR 导航可在复杂路口处进行清晰的方向指引,避免用户错过关键路口

  地图导航虽然现在普及率很高,但人们在使用时仍然需要一定的理解成本,尤其一些复杂的岔路口,理解起来需要更长时间,但哪怕只是几秒,在高速行驶过程中就有可能错过关键路口。

  AR(Augmented Reality,增强现实技术)是一种创新的交互方式,也给地图导航带来新的思路。与传统地图导航不同的是,车载 AR 导航首先利用摄像头将前方道路的真实场景实时捕捉下来,再结合汽车当前定位、地图导航信息以及场景 AI 识别,进行融合计算,然后生成虚拟的导航指引模型,并叠加到真实道路上,从而创建出更贴近驾驶者真实视野的导航画面,大幅降低了用户对传统 2D 或 3D 电子地图的读图成本。


*高德地图车载 AR 导航提供更直观、更及时的转弯指示和红绿灯提醒

  高德地图相关产品负责人表示,其车载 AR 导航可以在多种驾车场景下指引用户做关键性动作。举例来说,它可在道路上直观地提示用户何处该转弯,何处需要提前变道并线,以及在岔路口等复杂路况下做更清晰地方向指引,避免用户在高速行驶中因决策延误而导致错过路口。

  此外,高德地图车载 AR 导航还能够对过往车辆、行人、车道线、红绿灯位置以及颜色、限速牌等周边环境,进行智能的图像识别,从而为驾驶员提供跟车距离预警、压线预警、红绿灯监测与提醒、前车启动提醒、提前变道提醒等一系列驾驶安全辅助,给用户带来比传统地图导航更加精细、更加安全的服务体验。

  车载 AR 实现难度非常高

  业内人士指出,由于驾车的场景复杂,行驶速度快,实时性要求高,因此车载的 AR 导航技术实现难度非常高,对导航精度、图像识别的准确度与速度,以及 AR 算法灵敏度等都有极高的要求。此前市场上也有一些车载 AR 导航产品,但由于数据和技术差强人意,导致普遍体验较差。而高德地图与阿里机器智能领域视觉智能实验室来使用体验上的巨大提升。

  专业的车道级导航能力。高德地图在导航领域积淀多年,拥有非常丰富和专业的道路与交通大数据,道路里程覆盖 820 万+公里,仅道路属性信息就超过 400 种,实时路况覆盖全国 360 多个城市和所有高速公路,并实现分钟级发布。准确的道路大数据与专业的导航引擎,使得高德地图能为用户提供非常精细化的导航服务,也是实现 AR 导航的基础能力。比如,在需要转向的路口前,高德地图可对用户进行车道级的引导,近期甚至还新增了区分不同转向的车道级实时路况信息。

  全球领先的图像智能检测与识别能力。图像识别是实现 AR 导航非常核心的技术,高德地图与阿里视觉实验室共同合作,汇聚了全球顶尖的图像算法技术,可对车辆、车道线、红绿灯位置&颜色等道路场景进行智能的图像检测、分割、识别与追踪。

  据了解,高德地图的图像团队已经成立多年,一直深耕于地图数据领域,积累了上百万公里的实际道路场景数据,凭借计算机视觉和深度学习技术能力,屡次在文字识别和卫星影像分割等国际竞赛中夺魁。而阿里视觉实验室在国际最大的自动驾驶计算机视觉算法集 KITTI 上,也曾囊括过三项道路场景分割任务的第一名。


*高德地图车载 AR 导航对车辆、车道线和限速标志等各类对象,均有很好的识别效果

  更值得一提的是,在复杂的驾车场景与较快的行驶速度下要实现快速、准确的图像识别,往往需要极大的计算资源,而后视镜产品和当前大多数车载设备的硬件性能普遍不高。为了能保证产品体验,高德地图与阿里院视觉实验室共同研发和采用了多任务学习、模型量化、迁移学习等深度学习的技术手段,提高模型运算速度和优化效果,从而实现了众多不同场景的实时检测和识别。这也意味着,拥有了这些技术能力,高德地图的车载 AR 导航方案可以兼容众多低成本设备,适用于更多载体。


*高德地图车载 AR 导航可智能识别前方车辆,并提供跟车距离预警

  除了强大的图像算法外,高德地图还拥有业内最大规模的自采道路图像数据和海量的 UGC 数据,都可为 AI 算法提供最充足的数据“营养”。

  积累深厚的 AR 融合算法。在图像识别的基础上,高德地图的车载 AR 导航解决方案,还能够对定位、地图导航、道路交通大数据进行融合运算,并把导航信息在实景图像上实时渲染呈现,提供精准的沉浸式的导航体验。以转弯路口为例,由于路口缺乏车道引导线,单纯基于图像识别,并不能提供最好的用户体验。而高德地图根据 AR 融合算法,可循着路口转弯的角度调整引导线的弧度,从而对车辆实际转弯轨迹做出准确引导。

传播一个好消息,阿里云,腾讯云白菜价啦!云服务器最低不到300元/年。这里有一份云计算优惠活动列表,来不及解释了,赶紧上车!


本文转自https://news.cnblogs.com/n/609757/

2018-12-07 08:32:21 baidu_33643757 阅读数 1088
  • Unity 值得看的500+ 技术内容列表

    Unity3D是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

大概操作步骤按笔记006执行。

1.背景

环境:

OS:Windows10
Android Studio3.2
Unity 2018.1.2f1

思路

Unity端:主调方,UI
Android端:底层接口,打包aar方式

2.步骤

2.1 Android Studio基础配置

2.1.1 新建工程、导入Unity接口包。

对比百度demo,导入百度lib,如图:
在这里插入图片描述
模仿demo,新建Activity,如下:
在这里插入图片描述

2.1.2 配置app的build.gradle

//apply plugin: 'com.android.application'
apply plugin: 'com.android.library'

android {
    compileSdkVersion 28

    // Add
    sourceSets {
        main {
            //Path to your source code
            main {
                jniLibs.srcDir('libs')
                jni.srcDirs = []
            }
            java {
                srcDir 'src/main/java'
            }
        }
    }

    defaultConfig {
//        applicationId "com.polin.baiduarmapsdk"
        minSdkVersion 15
        targetSdkVersion 28
//        versionCode 1
//        versionName "1.0"
//        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    // Add
    lintOptions {
        abortOnError false
    }
}

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
    implementation files('libs/android-support-v4.jar')
    implementation files('libs/BaiduLBS_Android.jar')
    implementation files('libs/classes.jar')
    implementation files('libs/gson-2.2.4.jar')
    implementation files('libs/org.apache.http.legacy.jar')
}

// Add Backup
//task to delete the old jar
task deleteOldJar(type: Delete) {
    delete 'release/BaiduMapKit.jar'
}

//task to export contents as jar
task exportJar(type: Copy) {
    from('build/intermediates/packaged-classes/release/')
    into('release/')
    include('classes.jar')
    ///Rename the jar
    rename('classes.jar', 'BaiduMapKit.jar')
}
exportJar.dependsOn(deleteOldJar, build)

2.1.3 配置AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.polin.baiduarmapsdk">

    <!--访问相机权限-->
    <uses-permission android:name="android.permission.CAMERA"/>
    <!--访问闪光灯-->
    <uses-permission android:name="android.permission.FLASHLIGHT"/>
    <!-- 这个权限用于进行网络定位-->
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"></uses-permission>
    <!-- 这个权限用于访问GPS定位-->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"></uses-permission>
    <!-- 用于访问wifi网络信息,wifi信息会用于进行网络定位-->
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"></uses-permission>
    <!-- 获取运营商信息,用于支持提供运营商信息相关的接口-->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>
    <!-- 这个权限用于获取wifi的获取权限,wifi信息会用来进行网络定位-->
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"></uses-permission>
    <!-- 用于读取手机当前的状态-->
    <uses-permission android:name="android.permission.READ_PHONE_STATE"></uses-permission>
    <!-- 写入扩展存储,向扩展卡写入数据,用于写入离线定位数据-->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
    <!-- 访问网络,网络定位需要上网-->
    <uses-permission android:name="android.permission.INTERNET"/>

    <application
        android:allowBackup="true"
        android:label="@string/app_name">

        <!-- 开发密钥 -->
        <meta-data
            android:name="com.baidu.lbsapi.API_KEY"
            android:value="****">
        </meta-data>

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <meta-data
                android:name="unityplayer.UnityActivity"
                android:value="true" />
        </activity>
        <activity android:name=".ArActivity" >
            <meta-data
                android:name="unityplayer.UnityActivity"
                android:value="true" />
        </activity>
        <activity android:name=".BuildingArActivity" >
            <meta-data
                android:name="unityplayer.UnityActivity"
                android:value="true" />
        </activity>
        <activity android:name=".SceneryArActivity">
            <meta-data
                android:name="unityplayer.UnityActivity"
                android:value="true" />
        </activity>
        <!-- 接入百度地图定位SDK -->
        <service android:name="com.baidu.location.f" android:enabled="true" android:process=":remote"></service>
    </application>

</manifest>

2.1.4 尝试打包成aar

在这里插入图片描述
在该目录下找到aar包,连同AndroidManifest.xml一起,即是我们将放入Unity中的Android资源。
在这里插入图片描述
至此,Android Studio端的配置完成。

2.2 Unity端基础配置

2.2.1 新建工程

2.2.2 填入Android工程包名

2.3 编写与测试

2.3.1 尝试在AndroidManifest.xml中添加百度Key,并获取在其中注册获取手机权限

将MainActivity作为入口,编写工具类LocSdkClient、PermissionsChecker、UntiyClient。
LocSdkClient:

public class LocSdkClient {
    /**
     *
     * 百度定位sdk 坐标获取工具类
     */
    private static LocSdkClient mInstance = null;
    public LocationClient mLocationClient = null;

    private LocSdkClient(Context context) {

        mLocationClient = new LocationClient(context.getApplicationContext());
        // 声明LocationClient类
        // mLocationClient.registerLocationListener(myListener);
        // 注册监听函数

        LocationClientOption option = new LocationClientOption();

        option.setLocationMode(LocationClientOption.LocationMode.Hight_Accuracy);
        // 可选,设置定位模式,默认高精度
        // LocationMode.Hight_Accuracy:高精度;
        // LocationMode. Battery_Saving:低功耗;
        // LocationMode. Device_Sensors:仅使用设备;

        option.setCoorType("bd09");
        // 可选,设置返回经纬度坐标类型,默认gcj02
        // gcj02:国测局坐标;
        // bd09ll:百度经纬度坐标;
        // bd09:百度墨卡托坐标;
        // 海外地区定位,无需设置坐标类型,统一返回wgs84类型坐标

        option.setScanSpan(2000);
        // 可选,设置发起定位请求的间隔,int类型,单位ms
        // 如果设置为0,则代表单次定位,即仅定位一次,默认为0
        // 如果设置非0,需设置1000ms以上才有效

        option.setOpenGps(true);
        // 可选,设置是否使用gps,默认false
        // 使用高精度和仅用设备两种定位模式的,参数必须设置为true

        option.setLocationNotify(true);
        // 可选,设置是否当GPS有效时按照1S/1次频率输出GPS结果,默认false

        option.setIgnoreKillProcess(false);
        // 可选,定位SDK内部是一个service,并放到了独立进程。
        // 设置是否在stop的时候杀死这个进程,默认(建议)不杀死,即setIgnoreKillProcess(true)

        option.SetIgnoreCacheException(false);
        // 可选,设置是否收集Crash信息,默认收集,即参数为false

        option.setWifiCacheTimeOut(5 * 60 * 1000);
        // 可选,7.2版本新增能力
        // 如果设置了该接口,首次启动定位时,会先判断当前WiFi是否超出有效期,若超出有效期,会先重新扫描WiFi,然后定位

        option.setEnableSimulateGps(false);
        // 可选,设置是否需要过滤GPS仿真结果,默认需要,即参数为false

        mLocationClient.setLocOption(option);
        // mLocationClient为第二步初始化过的LocationClient对象
        // 需将配置好的LocationClientOption对象,通过setLocOption方法传递给LocationClient对象使用
        // 更多LocationClientOption的配置,请参照类参考中LocationClientOption类的详细说明

        mLocationClient.start();
        // mLocationClient为第二步初始化过的LocationClient对象
        // 调用LocationClient的start()方法,便可发起定位请求e
    }

    public LocationClient getLocationStart() {
        return mLocationClient;
    }

    public static LocSdkClient getInstance(Context context) {
        if (mInstance == null) {
            Class var0 = LocSdkClient.class;
            synchronized (LocSdkClient.class) {
                if (mInstance == null) {
                    mInstance = new LocSdkClient(context);
                }
            }
        }
        return mInstance;
    }
}

PermissionsChecker:

/**
 * 权限申请
 */

public class PermissionsChecker {

    private final Context mContext;

    public PermissionsChecker(Context context) {
        mContext = context.getApplicationContext();
    }
    // 判断权限集合
    public boolean lacksPermissions() {
        String[] permissions = new String[] {
//                Manifest.permission.FLASHLIGHT,
                Manifest.permission.ACCESS_COARSE_LOCATION,
                Manifest.permission.ACCESS_FINE_LOCATION,
                Manifest.permission.CAMERA,
                Manifest.permission.WRITE_EXTERNAL_STORAGE,
                Manifest.permission.INTERNET,
                Manifest.permission.READ_PHONE_STATE,
                Manifest.permission.CHANGE_WIFI_STATE,
                Manifest.permission.ACCESS_NETWORK_STATE,
                Manifest.permission.ACCESS_WIFI_STATE
        };
        for (String permission : permissions) {
            if (lacksPermission(permission)) {
                return true;
            }
        }
        return false;
    }
    // 判断是否缺少权限
    private boolean lacksPermission(String permission) {
        return ContextCompat.checkSelfPermission(mContext, permission) == PackageManager.PERMISSION_DENIED;
    }
}

UntiyClient:

public class UntiyClient extends UnityPlayerActivity {
    /**
     * unity项目启动时的的上下文
     */
    private static Activity _unityActivity;
    /**
     * 获取unity项目的上下文
     * @return
     */
    public static Activity GetActivity(){
        if(null == _unityActivity) {
            try {
                Class<?> classtype = Class.forName("com.unity3d.player.UnityPlayer");
                Activity activity = (Activity) classtype.getDeclaredField("currentActivity").get(classtype);
                _unityActivity = activity;
            } catch (ClassNotFoundException e) {

            } catch (IllegalAccessException e) {

            } catch (NoSuchFieldException e) {

            }
        }
        return _unityActivity;
    }

    /**
     * 调用Unity的方法
     * @param gameObjectName    调用的GameObject的名称
     * @param functionName      方法名
     * @param args              参数
     * @return                  调用是否成功
     */
    public static boolean CallUnity(String gameObjectName, String functionName, String args){
        try {
            Class<?> classtype = Class.forName("com.unity3d.player.UnityPlayer");
            Method method =classtype.getMethod("UnitySendMessage", String.class,String.class,String.class);
            method.invoke(classtype,gameObjectName,functionName,args);
            return true;
        } catch (ClassNotFoundException e) {

        } catch (NoSuchMethodException e) {

        } catch (IllegalAccessException e) {

        } catch (InvocationTargetException e) {

        }
        return false;
    }
}

MainActivity:

public class MainActivity extends UnityPlayerActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
//        setContentView(R.layout.activity_main);
    }

        // MainActivity初始化函数
    public void init(){
        // 判断权限
        PermissionsChecker permissionsChecker = new PermissionsChecker(GetActivity());
        if (permissionsChecker.lacksPermissions()) {
            Toast.makeText(GetActivity(), "缺少权限,请开启权限!", Toast.LENGTH_LONG).show();
            openSetting();
        }
        else{
            Toast.makeText(GetActivity(), "权限已开启!", Toast.LENGTH_LONG).show();
        }
    }

    /**
     * 打开设置权限界面
     *
     * @param
     */
    public void openSetting() {
        Intent intent = new Intent();
        intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
        Uri uri = Uri.fromParts("package", getPackageName(), null);
        intent.setData(uri);
        startActivity(intent);
    }
}

在Unity中创建MainActivity初始化测试按钮:
在这里插入图片描述

导出aar包与AndroidManifest.xml到Unity的Plugins/Android中,编写Unity主调脚本:

public class UIControl : MonoBehaviour {

	// Use this for initialization
	void Start () {
        transform.Find("MainActivityInit").GetComponent<Button>().onClick.AddListener(InitMainActivity);       
    }

    public void InitMainActivity()
    {
        AndroidJavaClass unity = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
        AndroidJavaObject currentActivity = unity.GetStatic<AndroidJavaObject>("currentActivity");
        currentActivity.Call("init");
    }
}

打包apk
报错1.
在这里插入图片描述
解决方案1.
把Unity打包方式从Gradle改为Internal。
报错2.
在这里插入图片描述
解决方案2.
报错信息显示:aar包中的Unity接口包classes.jar重复了,那就删掉。
用压缩软件打开aar包,找到libs目录下的classes.jar,删除此包。
在这里插入图片描述
报错3.
在这里插入图片描述
解决方案3.
这些资源找不到,再次打开aar包,找到./res/values/values.xml并进行修改:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorAccent">#D81B60</color>
    <color name="colorPrimary">#008577</color>
    <color name="colorPrimaryDark">#00574B</color>
    <string name="app_name">BaiduArMapSDK</string>
    <style name="AppTheme" parent="android:Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="android:colorPrimary">@color/colorPrimary</item>
        <item name="android:colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="android:colorAccent">@color/colorAccent</item>
    </style>
</resources>

报错4.
在这里插入图片描述
解决方案4.
依然找不到这个主题,我们就不用它了

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorAccent">#D81B60</color>
    <color name="colorPrimary">#008577</color>
    <color name="colorPrimaryDark">#00574B</color>
    <string name="app_name">BaiduArMapSDK</string>
    <style name="AppTheme" parent="android:Theme.Light">
        <!-- Customize your theme here. -->
        <item name="android:colorPrimary">@color/colorPrimary</item>
        <item name="android:colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="android:colorAccent">@color/colorAccent</item>
    </style>
</resources>

报错5.
在这里插入图片描述
解决方案5.
layout.xml报错,我们去aar包中找到它:./res/layout/activity_main.xml,删掉这些:
在这里插入图片描述
保存,在Unity中reimport,继续打包。
报错6.
在这里插入图片描述
解决方案6.
LocSdkClient类名不一致,删掉此类,重新建类。
依然报错,clean project,rebuild project,重新导包。
此步骤成功。_
在这里插入图片描述
经测试,aar包与AndroidManifest.xml缺一不可!

2.3.2 AR模块初始化

包括手机权限申请和百度Key认证
修改MainActivity.xml:

public class MainActivity extends UnityPlayerActivity implements OnGetDataResultListener, OnGetPoiSearchResultListener{

    public static ArInfoScenery arInfoScenery; // 景区
    public static ArBuildingResponse arBuildingResponse; // 识楼
    public static List<PoiInfoImpl> poiInfos; // 探索
    private PoiSearch mPoiSearch = null;
    private ArSdkManager mArSdkManager = null;
    private LatLng center = new LatLng(22.866084, 108.285501);
    int radius = 500; // 500米半径
    private int loadIndex = 0;
    // 自定义多点数据
    private ArLatLng[] latLngs = {
            new ArLatLng(22.865926, 108.284935),
            new ArLatLng(22.866817, 108.286763),
            new ArLatLng(22.866484, 108.287801),
            new ArLatLng(22.866531, 108.285363),
            new ArLatLng(22.863861, 108.285308)
    };

    // MainActivity初始化函数
    public void init(){
        // ArSDK模块初始化
        ArSdkManager.getInstance().initApplication(GetActivity().getApplication(), new MyGeneralListener());

        // 若用百度定位sdk,需要在此初始化定位SDK
        LocSdkClient.getInstance(GetActivity()).getLocationStart();

        // 若用探索功能需要再这集成检索模块 在使用 SDK 各组间之前初始化 context 信息,传入 ApplicationContext
        SDKInitializer.initialize(GetActivity().getApplication());
        // 检索模块 自4.3.0起,百度地图SDK所有接口均支持百度坐标和国测局坐标,用此方法设置您使用的坐标类型.
        // 包括BD09LL和GCJ02两种坐标,默认是BD09LL坐标。
        SDKInitializer.setCoordType(CoordType.BD09LL);

        // 如果需要检索,初始化搜索模块,注册搜索事件监听
        mPoiSearch = PoiSearch.newInstance();
        mPoiSearch.setOnGetPoiSearchResultListener(this);
        // 如果需要Ar景区功能、Ar识楼功能要注册监听
        mArSdkManager = ArSdkManager.getInstance();
        mArSdkManager.setOnGetDataResultListener(this);

        // 判断权限
        PermissionsChecker permissionsChecker = new PermissionsChecker(GetActivity());
        if (permissionsChecker.lacksPermissions()) {
            Toast.makeText(GetActivity(), "缺少权限,请开启权限!", Toast.LENGTH_LONG).show();
            openSetting();
        }
        else{
            Toast.makeText(GetActivity(), "权限已开启!", Toast.LENGTH_LONG).show();
        }
    }

    /**
     * 打开设置权限界面
     *
     * @param
     */
    public void openSetting() {
        Intent intent = new Intent();
        intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
        Uri uri = Uri.fromParts("package", getPackageName(), null);
        intent.setData(uri);
        startActivity(intent);
    }

    static class MyGeneralListener implements MKGeneralListener {
        // 1、事件监听,用来处理通常的网络错误,授权验证错误等
        @Override
        public void onGetPermissionState(int iError) {
            // 2、非零值表示key验证未通过
            if (iError != 0) {
                // 授权Key错误:
                Toast.makeText(GetActivity(),"arsdk 验证异常,请在AndoridManifest.xml中输入正确的授权Key,并检查您的网络连接是否正常!error: " + iError, Toast
                                .LENGTH_LONG).show();
            } else {
                Toast.makeText(GetActivity(), "key认证成功", Toast.LENGTH_LONG).show();
            }
        }

        // 回调给ArSDK获取坐标(demo调用百度定位sdk)
        @Override
        public ArBDLocation onGetBDLocation() {
            // 3、用于传递给ArSdk经纬度信息
            // a、首先通过百度地图定位SDK获取经纬度信息
            // b、包装经纬度信息到ArSdk的ArBDLocation类中 return即可
            BDLocation location =
                    LocSdkClient.getInstance(ArSdkManager.getInstance().getAppContext()).getLocationStart()
                            .getLastKnownLocation();
            if (location == null) {
                return null;
            }
            ArBDLocation arBDLocation = new ArBDLocation();
            // 设置经纬度信息
            arBDLocation.setLongitude(location.getLongitude());
            arBDLocation.setLatitude(location.getLatitude());
            return arBDLocation;
        }
    }

    @Override
    public void onGetPoiResult(PoiResult result) {
        if (result == null || result.error == SearchResult.ERRORNO.RESULT_NOT_FOUND) {
            Toast.makeText(this, "未找到结果", Toast.LENGTH_LONG)
                    .show();
            return;
        }
        if (result.error == SearchResult.ERRORNO.NO_ERROR) {
            poiInfos = new ArrayList<PoiInfoImpl>();
            for (PoiInfo poi : result.getAllPoi()) {
                ArPoiInfo poiInfo = new ArPoiInfo();
                ArLatLng arLatLng = new ArLatLng(poi.location.latitude, poi.location.longitude);
                poiInfo.name = poi.name;
                poiInfo.location = arLatLng;
                PoiInfoImpl poiImpl = new PoiInfoImpl();
                poiImpl.setPoiInfo(poiInfo);
                poiInfos.add(poiImpl);
            }
            Toast.makeText(this, "查询到: " + poiInfos.size() + " ,个POI点", Toast.LENGTH_SHORT).show();
            Intent intent = new Intent(MainActivity.this, ArActivity.class);
            MainActivity.this.startActivity(intent);
            return;
        }
        if (result.error == SearchResult.ERRORNO.AMBIGUOUS_KEYWORD) {

            // 当输入关键字在本市没有找到,但在其他城市找到时,返回包含该关键字信息的城市列表
            String strInfo = "在";
            for (CityInfo cityInfo : result.getSuggestCityList()) {
                strInfo += cityInfo.city;
                strInfo += ",";
            }
            strInfo += "找到结果";
            Toast.makeText(this, strInfo, Toast.LENGTH_LONG)
                    .show();
        }
    }

    @Override
    public void onGetPoiDetailResult(PoiDetailResult result) {
        if (result.error != SearchResult.ERRORNO.NO_ERROR) {
            Toast.makeText(this, "抱歉,未找到结果", Toast.LENGTH_SHORT)
                    .show();
        } else {
            Toast.makeText(this, result.getName() + ": " + result.getAddress(), Toast.LENGTH_SHORT)
                    .show();
        }
    }

    @Override
    public void onGetPoiDetailResult(PoiDetailSearchResult poiDetailSearchResult) {}

    @Override
    public void onGetPoiIndoorResult(PoiIndoorResult poiIndoorResult) {}

    @Override
    public void onGetSceneryResult(ArSceneryResponse var1){}

    @Override
    public void onGetBuildingResult(ArBuildingResponse var1){}
}

重新打包,成功。
在这里插入图片描述

2.3.2 测试Activity跳转

在MainActivity.xml中添加onClick()方法监听Unity中场景转换按键事件:

public void onClick(String index){
        switch (index){
            // 单点数据展示
            case ConstantValue.singlePointShow:
                Intent intent2SinglePoint = new Intent();
                intent2SinglePoint.setClass(GetActivity(),ArActivity.class);
                GetActivity().startActivity(intent2SinglePoint);
                break;
            // 多点数据展示
            case ConstantValue.multiPointsShow:
                Intent intent2MultiPoint = new Intent();
                intent2MultiPoint.setClass(GetActivity(),ArActivity.class);
                GetActivity().startActivity(intent2MultiPoint);
                break;
            // 景区功能 传入uid信息
            case ConstantValue.sceneryArShow:
                break;
            // 识楼功能
            case ConstantValue.buildingArShow:
                break;
            // 探索功能
            case ConstantValue.exploreArShow:
                break;
            default:
                break;
        }
    }

修改activity_ar.xml,默认布局会报错。证明Unity中调用的Android原生布局不能为空。以下xml为测试用。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <EditText
        android:id="@+id/editText2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:ems="10"
        android:inputType="textPersonName"
        android:text="This is ArActivity" />
</LinearLayout>

修改Unity控制脚本UIControl:

public class UIControl : MonoBehaviour {

    AndroidJavaClass unity;
    AndroidJavaObject currentActivity;

    private Button singlePointShowBtn;
    private Button multiPointsShowBtn;
    private Button sceneryArShowBtn;
    private Button buildingArShowBtn;
    private Button exploreArShowBtn;

    private void Awake()
    {
        transform.Find("MainActivityInit").GetComponent<Button>().onClick.AddListener(InitMainActivity);
        singlePointShowBtn = transform.Find("SinglePointShow").GetComponent<Button>();
        multiPointsShowBtn = transform.Find("MultiPointsShow").GetComponent<Button>();
        sceneryArShowBtn = transform.Find("SceneryArShow").GetComponent<Button>();
        buildingArShowBtn = transform.Find("BuildingArShow").GetComponent<Button>();
        exploreArShowBtn = transform.Find("ExploreArShow").GetComponent<Button>();
    }

    // Use this for initialization
    void Start () {
        unity = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
        currentActivity = unity.GetStatic<AndroidJavaObject>("currentActivity");

        singlePointShowBtn.onClick.AddListener(delegate () { OnClick(singlePointShowBtn.name); });
        multiPointsShowBtn.onClick.AddListener(delegate () { OnClick(multiPointsShowBtn.name); });
        sceneryArShowBtn.onClick.AddListener(delegate () { OnClick(sceneryArShowBtn.name); });
        buildingArShowBtn.onClick.AddListener(delegate () { OnClick(buildingArShowBtn.name); });
        exploreArShowBtn.onClick.AddListener(delegate () { OnClick(exploreArShowBtn.name); });
    }

    public void InitMainActivity()
    {       
        currentActivity.Call("init");
    }

    public void OnClick(string id)
    {
        switch (id)
        {
            case ConstantValue.singlePointShow:
                currentActivity.Call("onClick", ConstantValue.singlePointShow);
                break;
            case ConstantValue.multiPointsShow:
                currentActivity.Call("onClick", ConstantValue.multiPointsShow);
                break;
            case ConstantValue.sceneryArShow:
                currentActivity.Call("onClick", ConstantValue.sceneryArShow);
                break;
            case ConstantValue.buildingArShow:
                currentActivity.Call("onClick", ConstantValue.buildingArShow);
                break;
            case ConstantValue.exploreArShow:
                currentActivity.Call("onClick", ConstantValue.exploreArShow);
                break;
            default:
                break;
        }
    }
}

重新编译,打包。
在这里插入图片描述
成功跳转。此外,到目前为止已成功实现Unity中调用Android原生Activity、layout。

2.2.3 参考百度demo完成ArActivity

报错1. unity调用Android,在真机上闪退
原因:aar包中的so库与unity库冲突。
解决方法:将aar包中的so库移出aar包,并删除多余空目录
在这里插入图片描述
显示成功
在这里插入图片描述
报错2.AR食堂图标依旧没有显示
原因:
在这里插入图片描述

待续…

[注] 若在Unity中出现不能打包的情况,可试着配置gradle.properties
注释掉该句:

org.gradle.jvmargs=-Xmx1536m

参考:
link百度API
link最简单四步查看Android studio SHA1.不用输入命令。

2017-07-31 10:01:53 weixin_33810302 阅读数 415
  • Unity 值得看的500+ 技术内容列表

    Unity3D是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

ARKit + CoreLocation

ARKit:使用摄像头和运动数据来绘制用户移动时本地的地图。

CoreLocation:使用WiFi和GPS数据来确定位置,精度比较低。

ARKit + CoreLocation:将高精度的AR与GPS数据相结合。

Points of interest demoNavigation demo

将这些技术结合起来的潜力是巨大的,在许多不同的领域有着很多的应用。 这个库有两个主要的特点:

  • 允许使用真实世界的坐标将元素放置在AR世界中。
  • 利用最近的位置数据点,并结合有关AR世界运动的知识,极大地提高了定位精度。

位置精度的改进目前还处于实验阶段,但在未来可能是最重要的组成部分。

由于目前还有一些工作尚未完成,并且存在其他方面的原因,因此最好由一个开放的社区来为这个项目服务,而不是通过GitHub Issues的方式。 所以我开放了一个Slack组,任何人都可以加入进来,讨论有关这个库的改进和大家的工作。

加入Slack社区

软硬件要求

ARKit需要iOS 11系统,并支持以下设备:

  • iPhone 6S及以上
  • iPhone SE
  • iPad(2017)
  • iPad Pro的所有型号

iOS 11可以从Apple的开发者网站上下载。

使用方法

这个库包含了ARKit和CoreLocation框架, 同时还包含了一个类似于Demo 1的演示程序。

使用CocoaPods进行设置

  1. 将本库添加到你的podfile中:

    `pod 'ARCL'`
  2. 在终端中,进入你的项目目录,然后执行:

    `pod update`
    `pod install`
  3. 添加 NSCameraUsageDescriptionNSLocationWhenInUseUsageDescription 到 plist文件中以添加一个简短说明(请参考演示项目)

手工设置

  1. ARKit+CoreLocation/Source目录中的所有文件添加到项目中。
  2. 导入ARKit、SceneKit、CoreLocation和MapKit。
  3. 添加 NSCameraUsageDescriptionNSLocationWhenInUseUsageDescription 到 plist文件中以添加一个简短说明(请参考演示项目)

快速入门指南

要在一座建筑物上放置一个标注点,例如伦敦的金丝雀码头,我们要使用包含ARCL的主要类:SceneLocationView

首先,导入ARCL和CoreLocation,然后将SceneLocationView声明为属性:

import ARCL
import CoreLocation

class ViewController: UIViewController {
  var sceneLocationView = SceneLocationView()
}

当获取到焦点时,调用sceneLocationView.run(),当中断时,调用sceneLocationView.pause(),例如移动到不同的视角或者退出应用程序。

func viewDidLoad() {
  override func viewDidLoad() 
  super.viewDidLoad()

  sceneLocationView.run()
  view.addSubview(sceneLocationView)
}

在调用run()之后,就可以添加坐标了。 ARCL附带一个名为LocationNode的类,它是3D场景中的一个对象,具有真实世界的位置,并且可以在世界范围内显示其他一些属性。 LocationNode是SceneKit的SCNNode的子类,也可以进一步子类化。 对于这个例子,我们将使用一个名为LocationAnnotationNode的子类,我们用它在世界上显示一个总是面向我们的2D图像:

let coordinate = CLLocationCoordinate2D(latitude: 51.504571, longitude: -0.019717)
let location = CLLocation(coordinate: coordinate, altitude: 300)
let image = UIImage(named: "pin")!

let annotationNode = LocationAnnotationNode(location: location, image: image)

默认情况下,你设置的图像会以给定的尺寸显示出来,例如,如果你提供了一个100x100的图像,则会在屏幕上显示为100x100像素大小。 这意味着远处的注释点与近处的注释点看起来大小是相同的。 如果你希望按距离的远近进行缩放,可以将LocationAnnotationNode的scaleRelativeToDistance设置为true

sceneLocationView.addLocationNodeWithConfirmedLocation(locationNode: annotationNode)

就是这样了。 如果你设置sceneLocationView的帧,则现在应该会看到Canary Wharf上方悬停的标注点了。

附加功能

这个库以及演示程序附带了一些额外的配置功能。这些都在文档里做了详细介绍,一定要去看看。

SceneLocationView是ARSCNView的一个子类。 请注意,虽然这样可以让你完全访问ARSCNView以通过其他方式去使用它,但你不应将委托设置为另一个类。 如果你需要使用委托功能,那么应该将SceneLocationView子类化。

正北校准

有一个我个人无法攻克的难题,目前iPhone正北校准精度最高为15º。这对地图导航来说还好,但是要将东西放在AR世界上时,它就成为了一个问题。

我相信通过使用各种AR技术可以攻克这个问题。

为了改善这个问题,我在库中添加了一些函数,可以用来调整正北方向:

  • sceneLocationView.moveSceneHeadingClockwise
  • sceneLocationView.moveSceneHeadingAntiClockwise
  • sceneLocationView.resetSceneHeading

要使用这些函数,你要将sceneLocationView.useTrueNorth设置为false,然后在开始之前将设备指向北方的大致一个方向,这样它才能更接近正北。将useTrueNorth设置为true(默认),它会根据自己的感觉不断调整正北方。

在演示程序中,有一个名为adjustNorthByTappingSidesOfScreen的属性,它用于访问这些函数,默认是禁用的。一旦启用这个属性,则允许通过点击屏幕的左侧或右侧来调整场景方向。

我的建议是你所处位置的正北方的附近放置一个地标,使用坐标在那个地方放置一个对象,然后使用moveSceneHeading函数来调整场景,直到它们排成一条线。

改进的定位精度

CoreLocation每1-15秒更新一次位置,精度从150米到4米不等。有时候,你会收到更精确的读数,如4米或8米。同时,AR使用运动和相机数据来创建本地世界的地图。

用户可能会收到精度为4米的位置,然后他们向北走10米,并接收到另一个位置,读数精确到65米。 这个精度为65米的读数是CoreLocation可以提供的最好的精度,但是当获取到4米精度的读数时,程序知道用户在AR场景中的位置,以及接着朝北走了10米,我们可以通过转换这个数据来提供一个新的精度为4米的坐标。这样就精确到了大概100米。

问题

我曾经提到,目前这些尚处于实验阶段。当用户经过某个场景的时候,ARKit偶尔会发生错乱,并给出错误地位置数据。 这个问题似乎也影响到了“欧拉角度”或者有关设备的方向信息。所以,在走过一小段距离之后,它可能会认为你目前正向着另外一个方向行走。

虽然苹果今后会改进ARKit,但我认为,为了避免出现这些问题,我们需要自己进行改进,例如识别出何时会出现问题并进行纠正,以及通过将位置数据与我们假定的位置进行比较,以确定是否已经超过了某个值。

定位算法改进

我们需要进一步优化定位算法。

例如,有一种技术是查看最近的位置数据,转换用户行程中的每一个点,然后使用数据点之间的重叠来更精确地确定用户可能的位置。

鸣谢

作者@AndrewProjDent,以及相关社区。

MIT许可证的条款下开源。

文章原标题《ARKit-CoreLocation》,作者:Andrew Hart,译者:夏天,审校:主题曲。

文章为简译,更为详细的内容,请查看原文

2019-04-28 14:34:28 weixin_41814169 阅读数 3449
  • Unity 值得看的500+ 技术内容列表

    Unity3D是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

最近在研究一个问题,就是通过Unity平台做出AR导航功能,类似于地图的样子,但不是通过GPS定位,室内应用的那种。

大概的效果图是:

 

 

现在功能还没有实现,有同在做这方面的小伙伴加群学习:663515959

2016-03-11 11:46:42 wuyt2008 阅读数 17227
  • Unity 值得看的500+ 技术内容列表

    Unity3D是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

首先说,这个尝试失败,属于死在去医院的路上那种。


基于地理定位的增强现实,AR全息实景,是一种高大上的说法,说直白点就是山寨类似随便走这样的应用。

打开应用,搜索周边信息,然后再把信息叠加在摄像头拍摄到的内容上面。



思路:用手机移动来控制unity中的camrea,将摄像头拍摄到的内容作为背景。获取地理信息,将信息转化成文字添加到unity的世界中。


1、用手机移动控制unity中的camrea。

这段代码中unity的论坛中找到,但是时间很久远,改了下发现能用。

http://forum.unity3d.com/threads/sharing-gyroscope-controlled-camera-on-iphone-4.98828/

using UnityEngine;
using System.Collections;

public class CameraManager : MonoBehaviour {



	private bool gyroBool;
	private Gyroscope gyro;
	private Quaternion rotFix;

	public void Start ()
	{
		Transform currentParent = transform.parent;
		GameObject camParent = new GameObject ("GyroCamParent");
		camParent.transform.position = transform.position;
		transform.parent = camParent.transform;
		GameObject camGrandparent = new GameObject ("GyroCamGrandParent");
		camGrandparent.transform.position = transform.position;
		camParent.transform.parent = camGrandparent.transform;
		camGrandparent.transform.parent = currentParent;

		gyroBool = SystemInfo.supportsGyroscope;

		if (gyroBool) {

			gyro = Input.gyro;
			gyro.enabled = true;

			if (Screen.orientation == ScreenOrientation.LandscapeLeft) {
				camParent.transform.eulerAngles = new Vector3 (90, 90, 0);
			} else if (Screen.orientation == ScreenOrientation.Portrait) {
				camParent.transform.eulerAngles = new Vector3 (90, 180, 0);
			} else if (Screen.orientation == ScreenOrientation.PortraitUpsideDown) {
				camParent.transform.eulerAngles = new Vector3 (90, 180, 0);
			} else if (Screen.orientation == ScreenOrientation.LandscapeRight) {
				camParent.transform.eulerAngles = new Vector3 (90, 180, 0);
			} else {
				camParent.transform.eulerAngles = new Vector3 (90, 180, 0);
			}

			if (Screen.orientation == ScreenOrientation.LandscapeLeft) {
				rotFix = new Quaternion (0, 0,0.7071f,0.7071f);
			} else if (Screen.orientation == ScreenOrientation.Portrait) {
				rotFix = new Quaternion (0, 0, 1, 0);
			} else if (Screen.orientation == ScreenOrientation.PortraitUpsideDown) {
				rotFix = new Quaternion (0, 0, 1, 0);
			} else if (Screen.orientation == ScreenOrientation.LandscapeRight) {
				rotFix = new Quaternion (0, 0, 1, 0);
			} else {
				rotFix = new Quaternion (0, 0, 1, 0);
			}

			//Screen.sleepTimeout = 0;
		} else {
			#if UNITY_EDITOR
			print("NO GYRO");
			#endif
		}
	}

	public void Update ()
	{
		if (gyroBool) {
			Quaternion quatMap;
			#if UNITY_IOS
			quatMap = gyro.attitude;
			#elif UNITY_ANDROID
			quatMap = new Quaternion(gyro.attitude.x,gyro.attitude.y,gyro.attitude.z,gyro.attitude.w);
			#endif
			transform.localRotation = quatMap * rotFix;
		}
	}
}


2、背景摄像头显示摄像机内容

摄像头的内容可以显示在guitexure上也可以显示在plan上,但是在guitexrue上显示的时候,方向转了90度,最后只好显示在plan上。

using UnityEngine;
using System.Collections;

public class WebCamManager : MonoBehaviour {

	// Use this for initialization
	void Start () {

		WebCamTexture webcamTexture = new WebCamTexture ();

		//如果有后置摄像头,调用后置摄像头
		for (int i = 0; i < WebCamTexture.devices.Length; i++) {
			if (!WebCamTexture.devices [i].isFrontFacing) {
				webcamTexture.deviceName = WebCamTexture.devices [i].name;
				break;
			}
		}

		Renderer renderer = GetComponent<Renderer>();  
		renderer.material.mainTexture = webcamTexture;  
		webcamTexture.Play();  
	}
}


3、调用高德地图的地理定位和搜索附近

详细内容请看我之前的博客

http://blog.csdn.net/wuyt2008/article/details/50774017

http://blog.csdn.net/wuyt2008/article/details/50789423


4、当搜索到内容以后,将名称信息添加到unity的世界里。

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

public class ARMange : MonoBehaviour {

	public List<PlaceInfo> places = new List<PlaceInfo>();
	public GameObject perfab;
	public PlaceInfo location = new PlaceInfo ();

	public void ShowPlaces(){
		ClearPlace ();

		for (int i = 0; i < places.Count; i++) {

			GameObject newPlace = Instantiate<GameObject> (perfab);
			newPlace.transform.parent = this.transform;

			double posZ = places [i].Latitude - location.Latitude;
			double posX = places [i].Longitude - location.Longitude;

			float z = 0;
			float x = 0;
			float y = 0;

			if (posZ > 0) {
				z = 500f;
			} else {
				z = -500f;
			}

			if (posX > 0) {
				x = 500f;
			} else {
				x = -500f;
			}

			z = z + (float)(posZ * 1000);
			x = x + (float)(posX * 1000);
			y = y + i * 20;

			newPlace.transform.position = new Vector3 (x, y, z);
			newPlace.transform.LookAt (this.transform);
			newPlace.transform.Rotate (new Vector3 (0f, 180f, 0f));

			newPlace.gameObject.GetComponentInChildren<Text> ().text = places [i].Name;
		}
	}

	private void ClearPlace(){
		GameObject[] oldPlaces = GameObject.FindGameObjectsWithTag ("Place");
		for (int i = 0; i < oldPlaces.Length; i++) {
			Destroy (oldPlaces [i].gameObject);
		}
	}

}


5、这个时候显示内容没问题,但是方向会偏移。于是加了个指南针来矫正方向

using UnityEngine;
using System.Collections;
using UnityEngine.UI;

public class CompassManage : MonoBehaviour {

	public Transform cam;

	void Start () {
		Input.location.Start ();
		Input.compass.enabled = true;
	}
	
	// Update is called once per frame
	void Update () {
		transform.rotation = Quaternion.Euler(0, cam.eulerAngles.y-Input.compass.trueHeading, 0);
	}
}


6、最后遇到的,我无法解决的问题

简单一句话,就是滤波。这个应用需要准确稳定的判断出当前手机方向位置状态,但是,输入的内容,重力,罗盘,加速度都是在不断变化,并且会有偏移的量,需要滤波。

虽然大致知道了是应该用互补滤波和卡尔曼滤波,但是,我的水平只能看懂名字,看不懂内容。

数学无力的我只好放弃。等遇到别人写好的代码再抄下吧。


这是死在半路上的结果的样子




这样的结果呢,当然是不甘心的,但是没时间去仔细研究这个问题了,所以只好放弃。如果哪位大侠知道怎么根据重力,罗盘,加速判断手机状态的,在这里跪求先。

源码和编译的apk:http://download.csdn.net/detail/wuyt2008/9458508

====================



在SearchManage.cs文件中,我把搜索范围限定在了昆明,

			//txtInfo.text = txtInfo.text + "\r\n";
			AndroidJavaObject query = amapHelper.Call<AndroidJavaObject>("getPoiSearch",inputQuery.text,"","0871");
			txtInfo.text = txtInfo.text + "query get...";
将0871改成其他地方的区号就可以了,(为空是全国范围,但是没验证过)