نمایش مکان های نزدیک در AR در Android (Kotlin)

1. قبل از شروع

خلاصه

این آزمایشگاه کد به شما می آموزد که چگونه از داده های پلتفرم نقشه های گوگل برای نمایش مکان های نزدیک در واقعیت افزوده (AR) در اندروید استفاده کنید.

2344909dd9a52c60.png

پیش نیازها

  • درک اولیه توسعه اندروید با استفاده از اندروید استودیو
  • آشنایی با کاتلین

چیزی که یاد خواهید گرفت

  • برای دسترسی به دوربین و مکان دستگاه از کاربر اجازه درخواست کنید.
  • با Places API ادغام کنید تا مکان‌های اطراف را در اطراف مکان دستگاه واکشی کنید.
  • با ARCore یکپارچه شوید تا سطوح صفحه افقی را پیدا کنید تا اشیاء مجازی را بتوان با استفاده از Sceneform در فضای سه بعدی لنگر انداخت و قرار داد.
  • با استفاده از SensorManager اطلاعاتی درباره موقعیت دستگاه در فضا جمع آوری کنید و از Maps SDK for Android Utility Library برای قرار دادن اشیاء مجازی در عنوان صحیح استفاده کنید.

آنچه شما نیاز دارید

2. راه اندازی شوید

اندروید استودیو

این کد لبه از اندروید 10.0 (سطح API 29) استفاده می‌کند و نیاز دارد که سرویس‌های Google Play را در Android Studio نصب کرده باشید. برای نصب هر دوی این وابستگی ها، مراحل زیر را انجام دهید:

  1. به مدیر SDK بروید، که می‌توانید با کلیک کردن روی Tools > SDK Manager به آن دسترسی پیدا کنید.

6c44a9cb9cf6c236.png

  1. بررسی کنید که آیا Android 10.0 نصب شده است یا خیر. در غیر این صورت، آن را با انتخاب کادر کنار Android 10.0 (Q) نصب کنید، سپس روی OK کلیک کنید و در نهایت مجدداً در گفتگوی ظاهر شده روی OK کلیک کنید.

368f17a974c75c73.png

  1. در نهایت، با رفتن به تب ابزار SDK ، سرویس‌های Google Play را نصب کنید، کادر کنار خدمات Google Play را انتخاب کنید، روی OK کلیک کنید، سپس دوباره تأیید را در گفتگوی ظاهر شده انتخاب کنید**.**

497a954b82242f4b.png

API های مورد نیاز

در مرحله 3 از بخش زیر، Maps SDK برای Android و Places API را برای این کد لبه فعال کنید.

با پلتفرم Google Maps شروع کنید

اگر قبلاً از Google Maps Platform استفاده نکرده‌اید، راهنمای Get Started with Google Maps Platform را دنبال کنید یا لیست پخش Started with Google Maps Platform را برای تکمیل مراحل زیر تماشا کنید:

  1. یک حساب صورتحساب ایجاد کنید.
  2. یک پروژه ایجاد کنید.
  3. APIها و SDKهای پلتفرم Google Maps را فعال کنید (در قسمت قبل فهرست شده است).
  4. یک کلید API ایجاد کنید.

اختیاری: شبیه ساز اندروید

اگر دستگاهی با پشتیبانی ARCore ندارید، می‌توانید از شبیه‌ساز Android برای شبیه‌سازی صحنه واقعیت افزوده و همچنین جعل موقعیت مکانی دستگاه خود استفاده کنید. با توجه به اینکه در این تمرین از Sceneform نیز استفاده خواهید کرد، باید مراحل زیر "پیکربندی شبیه ساز برای پشتیبانی از Sceneform" را نیز دنبال کنید.

3. شروع سریع

برای شروع هرچه سریع‌تر، در اینجا چند کد شروع وجود دارد که به شما کمک می‌کند تا این نرم‌افزار را دنبال کنید. از شما استقبال می شود که به سراغ راه حل بروید، اما اگر می خواهید تمام مراحل را ببینید، به خواندن ادامه دهید.

اگر git را نصب کرده اید، می توانید مخزن را کلون کنید.

git clone https://github.com/googlecodelabs/display-nearby-places-ar-android.git

همچنین می‌توانید روی دکمه زیر کلیک کنید تا کد منبع را دانلود کنید.

پس از دریافت کد، ادامه دهید و پروژه ای را که در دایرکتوری starter یافت می شود باز کنید.

4. نمای کلی پروژه

کدی را که از مرحله قبل دانلود کرده اید کاوش کنید. در داخل این مخزن، باید یک ماژول واحد به نام app پیدا کنید که حاوی بسته com.google.codelabs.findnearbyplacesar است.

AndroidManifest.xml

ویژگی‌های زیر در فایل AndroidManifest.xml اعلان شده‌اند تا بتوانید از ویژگی‌های مورد نیاز در این لبه کد استفاده کنید:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

<!-- Sceneform requires OpenGL ES 3.0 or later. -->
<uses-feature
   android:glEsVersion="0x00030000"
   android:required="true" />

<!-- Indicates that app requires ARCore ("AR Required"). Ensures the app is visible only in the Google Play Store on devices that support ARCore. For "AR Optional" apps remove this line. -->
<uses-feature android:name="android.hardware.camera.ar" />

برای uses-permission ، که مشخص می کند چه مجوزهایی باید توسط کاربر قبل از استفاده از آن قابلیت ها اعطا شود، موارد زیر اعلام شده است:

  • android.permission.INTERNET — این به این دلیل است که برنامه شما می تواند عملیات شبکه را انجام دهد و داده ها را از طریق اینترنت واکشی کند، مانند اطلاعات مکان ها از طریق Places API.
  • android.permission.CAMERA — دسترسی به دوربین مورد نیاز است تا بتوانید از دوربین دستگاه برای نمایش اشیا در واقعیت افزوده استفاده کنید.
  • android.permission.ACCESS_FINE_LOCATION - دسترسی به مکان مورد نیاز است تا بتوانید مکان‌های نزدیک را نسبت به موقعیت مکانی دستگاه واکشی کنید.

برای uses-feature ، که مشخص می کند کدام ویژگی های سخت افزاری مورد نیاز این برنامه است، موارد زیر اعلام شده است:

  • OpenGL ES نسخه 3.0 مورد نیاز است.
  • دستگاه با قابلیت ARCore مورد نیاز است.

علاوه بر این، تگ های ابرداده زیر در شی برنامه اضافه می شوند:

<application
  android:allowBackup="true"
  android:icon="@mipmap/ic_launcher"
  android:label="@string/app_name"
  android:roundIcon="@mipmap/ic_launcher_round"
  android:supportsRtl="true"
  android:theme="@style/AppTheme">
  
  <!-- 
     Indicates that this app requires Google Play Services for AR ("AR Required") and causes
     the Google Play Store to download and install Google Play Services for AR along with
     the app. For an "AR Optional" app, specify "optional" instead of "required". 
  -->

  <meta-data
     android:name="com.google.ar.core"
     android:value="required" />

  <meta-data
     android:name="com.google.android.geo.API_KEY"
     android:value="@string/google_maps_key" />

  <!-- Additional elements here --> 

</application>

اولین ورودی متا داده نشان می دهد که ARCore برای اجرای این برنامه الزامی است و دومی نحوه ارائه کلید API پلتفرم Google Maps خود به Maps SDK برای Android است.

build.gradle

در build.gradle ، وابستگی های اضافی زیر مشخص شده است:

dependencies {
    // Maps & Location
    implementation 'com.google.android.gms:play-services-location:17.0.0'
    implementation 'com.google.android.gms:play-services-maps:17.0.0'
    implementation 'com.google.maps.android:maps-utils-ktx:1.7.0'

    // ARCore
    implementation "com.google.ar.sceneform.ux:sceneform-ux:1.15.0"

    // Retrofit
    implementation "com.squareup.retrofit2:retrofit:2.7.1"
    implementation "com.squareup.retrofit2:converter-gson:2.7.1"
}

در اینجا شرح مختصری از هر وابستگی آورده شده است:

  • کتابخانه‌هایی با شناسه گروه com.google.android.gms ، یعنی play-services-location و play-services-maps ، برای دسترسی به اطلاعات موقعیت مکانی دستگاه و دسترسی به عملکردهای مرتبط با Google Maps استفاده می‌شوند.
  • com.google.maps.android:maps-utils-ktx کتابخانه برنامه‌های افزودنی Kotlin (KTX) برای Maps SDK برای کتابخانه ابزار Android است. در این کتابخانه از عملکرد برای قرار دادن اشیاء مجازی در فضای واقعی استفاده خواهد شد.
  • com.google.ar.sceneform.ux:sceneform-ux ux کتابخانه Sceneform است که به شما امکان می دهد صحنه های سه بعدی واقعی را بدون نیاز به یادگیری OpenGL ارائه دهید.
  • وابستگی‌های درون گروه ID com.squareup.retrofit2 com.squareup.retrofit2 هستند که به شما امکان می‌دهند به سرعت یک کلاینت HTTP بنویسید تا با Places API تعامل داشته باشید.

ساختار پروژه

در اینجا بسته ها و فایل های زیر را خواهید یافت:

  • **api—**این بسته شامل کلاس هایی است که برای تعامل با Places API با استفاده از Retrofit استفاده می شود.
  • **ar—**این بسته شامل تمامی فایل های مربوط به ARCore می باشد.
  • **model—**این بسته شامل یک کلاس داده واحد Place است که برای کپسوله کردن یک مکان واحد که توسط Places API برگردانده می شود استفاده می شود.
  • MainActivity.kt — این تنها Activity موجود در برنامه شما است که نقشه و نمای دوربین را نمایش می دهد.

5. تنظیم صحنه

در اجزای اصلی برنامه با قطعات واقعیت افزوده شروع کنید.

MainActivity شامل یک SupportMapFragment است که نمایش شی نقشه را انجام می دهد و یک زیر کلاس از یک ArFragment - PlacesArFragment - که نمایش صحنه واقعیت افزوده را کنترل می کند.

راه اندازی واقعیت افزوده

جدا از نمایش صحنه واقعیت افزوده، PlacesArFragment همچنین درخواست مجوز دوربین از کاربر را در صورتی که قبلاً اعطا نشده باشد، رسیدگی خواهد کرد. مجوزهای اضافی را نیز می توان با نادیده گرفتن روش getAdditionalPermissions درخواست کرد. با توجه به اینکه برای اعطای مجوز مکان نیز نیاز دارید، آن را مشخص کنید و روش getAdditionalPermissions را لغو کنید:

class PlacesArFragment : ArFragment() {

   override fun getAdditionalPermissions(): Array<String> =
       listOf(Manifest.permission.ACCESS_FINE_LOCATION)
           .toTypedArray()
}

آن را اجرا کنید

ادامه دهید و کد اسکلت را در starter دایرکتوری در Android Studio باز کنید. اگر روی Run > Run 'app' از نوار ابزار کلیک کنید و برنامه را در دستگاه یا شبیه ساز خود اجرا کنید، ابتدا باید از شما خواسته شود مجوز مکان و دوربین را فعال کنید. ادامه دهید و روی Allow کلیک کنید و پس از انجام این کار، یک نمای دوربین و یک نمای نقشه را در کنار هم مانند این مشاهده خواهید کرد:

e3e3073d5c86f427.png

شناسایی هواپیماها

با نگاهی به محیطی که با دوربین خود در آن هستید، ممکن است متوجه چند نقطه سفید روی سطوح افقی شوید که شبیه به نقاط سفید روی فرش در این تصویر است.

2a9b6ea7dcb2e249.png

این نقاط سفید دستورالعمل هایی هستند که توسط ARCore برای نشان دادن اینکه یک صفحه افقی شناسایی شده است ارائه شده است. این هواپیماهای شناسایی شده به شما این امکان را می دهند که چیزی به نام "لنگر" ایجاد کنید تا بتوانید اشیاء مجازی را در فضای واقعی قرار دهید.

برای اطلاعات بیشتر در مورد ARCore و نحوه درک آن از محیط اطراف شما، در مورد مفاهیم اساسی آن مطالعه کنید.

6. مکان های نزدیک را دریافت کنید

در مرحله بعد، باید به مکان فعلی دستگاه دسترسی داشته باشید و سپس مکان‌های اطراف را با استفاده از Places API واکشی کنید.

راه اندازی نقشه ها

کلید API پلتفرم نقشه های گوگل

پیش از این، یک کلید API پلتفرم Google Maps ایجاد کردید تا جستجو در Places API را فعال کند و بتوانید از Maps SDK برای اندروید استفاده کنید. ادامه دهید و فایل gradle.properties را باز کنید و رشته "YOUR API KEY HERE" را با کلید API که ایجاد کرده اید جایگزین کنید.

نمایش مکان دستگاه روی نقشه

هنگامی که کلید API خود را اضافه کردید، یک راهنما بر روی نقشه اضافه کنید تا به کاربران در جهت دهی آنها نسبت به نقشه کمک کند. برای انجام این کار، به روش setUpMaps و در تماس mapFragment.getMapAsync ، googleMap.isMyLocationEnabled را روی true. با انجام این کار، نقطه آبی روی نقشه نشان داده می شود.

private fun setUpMaps() {
   mapFragment.getMapAsync { googleMap ->
       googleMap.isMyLocationEnabled = true
       // ...
   }
}

مکان فعلی را دریافت کنید

برای دریافت موقعیت مکانی دستگاه، باید از کلاس FusedLocationProviderClient استفاده کنید. به دست آوردن یک نمونه از این قبلاً در روش onCreate MainActivity انجام شده است. برای استفاده از این شی، متد getCurrentLocation را پر کنید، که آرگومان لامبدا را می‌پذیرد تا یک مکان به فراخواننده این متد ارسال شود.

برای تکمیل این روش، می‌توانید به ویژگی lastLocation شی FusedLocationProviderClient و سپس افزودن یک addOnSuccessListener به این صورت دسترسی داشته باشید:

fusedLocationClient.lastLocation.addOnSuccessListener { location ->
    currentLocation = location
    onSuccess(location)
}.addOnFailureListener {
    Log.e(TAG, "Could not get location")
}

متد getCurrentLocation از داخل لامبدای ارائه شده در getMapAsync در متد setUpMaps فراخوانی می شود که مکان های نزدیک از آن واکشی می شوند.

تماس شبکه مکان ها را آغاز کنید

در فراخوانی متد getNearbyPlaces ، توجه داشته باشید که پارامترهای زیر به متد placesServices.nearbyPlaces منتقل می‌شوند—یک کلید API، مکان دستگاه، شعاع بر حسب متر (که روی 2 کیلومتر تنظیم شده است) و نوع مکان (در حال حاضر تنظیم شده است). park کردن).

val apiKey = "YOUR API KEY"
placesService.nearbyPlaces(
   apiKey = apiKey,
   location = "${location.latitude},${location.longitude}",
   radiusInMeters = 2000,
   placeType = "park"
)

برای تکمیل تماس شبکه، ادامه دهید و کلید API را که در فایل gradle.properties خود تعریف کرده اید، وارد کنید. قطعه کد زیر در فایل build.gradle شما در قسمت android > defaultConfig تعریف شده است:

android {
   defaultConfig {
       resValue "string", "google_maps_key", (project.findProperty("GOOGLE_MAPS_API_KEY") ?: "")
   }
}

این باعث می شود که مقدار منبع رشته google_maps_key در زمان ساخت در دسترس باشد.

برای تکمیل تماس شبکه، می‌توانید به سادگی این منبع رشته را از طریق getString در شی Context بخوانید.

val apiKey = this.getString(R.string.google_maps_key)

7. مکان ها در AR

تا به حال کارهای زیر را انجام داده اید:

  1. هنگامی که برای اولین بار برنامه را اجرا کرد، از کاربر مجوز دوربین و موقعیت مکانی درخواست کرد
  2. برای شروع ردیابی صفحات افقی، ARCore را راه اندازی کنید
  3. Maps SDK را با کلید API خود تنظیم کنید
  4. مکان فعلی دستگاه را دریافت کردم
  5. مکان‌های نزدیک (مخصوصاً پارک‌ها) با استفاده از Places API واکشی شد

مرحله باقی مانده برای تکمیل این تمرین این است که مکان هایی را که می گیرید در واقعیت افزوده قرار دهید.

درک صحنه

ARCore قادر است صحنه های دنیای واقعی را از طریق دوربین دستگاه با تشخیص نقاط جالب و متمایز به نام نقاط ویژگی در هر فریم تصویر درک کند. هنگامی که این نقاط ویژگی در یک صفحه افقی مشترک قرار دارند، مانند جداول و طبقات، ARCore می تواند این ویژگی را به عنوان یک صفحه افقی در دسترس برنامه قرار دهد.

همانطور که قبلاً دیدید، ARCore با نمایش نقاط سفید به کاربر کمک می کند تا زمانی که هواپیما شناسایی شده است.

2a9b6ea7dcb2e249.png

اضافه کردن لنگرها

هنگامی که یک هواپیما شناسایی شد، می توانید جسمی به نام لنگر را متصل کنید. از طریق یک لنگر، می توانید اشیاء مجازی را قرار دهید و تضمین کنید که آن اشیا در همان موقعیت در فضا باقی می مانند. پیش بروید و کد را تغییر دهید تا زمانی که هواپیما شناسایی شد، یکی را به آن متصل کنید.

در setUpAr ، یک OnTapArPlaneListener به PlacesArFragment متصل است. این شنونده هر زمان که یک هواپیما در صحنه AR ضربه می زند فراخوانی می شود. در این فراخوانی، می توانید یک Anchor و یک AnchorNode از HitResult ارائه شده در شنونده ایجاد کنید:

arFragment.setOnTapArPlaneListener { hitResult, _, _ ->
   val anchor = hitResult.createAnchor()
   anchorNode = AnchorNode(anchor)
   anchorNode?.setParent(arFragment.arSceneView.scene)
   addPlaces(anchorNode!!)
}

AnchorNode جایی است که می‌توانید اشیاء گره فرزند - نمونه‌های PlaceNode - را در صحنه‌ای که در فراخوانی متد addPlaces مدیریت می‌شود، وصل کنید.

آن را اجرا کنید

اگر برنامه را با تغییرات بالا اجرا می کنید، به اطراف خود نگاه کنید تا زمانی که هواپیما شناسایی شود. پیش بروید و روی نقاط سفیدی که نشان دهنده یک هواپیما هستند ضربه بزنید. پس از انجام این کار، اکنون باید نشانگرهای تمام نزدیکترین پارک های اطراف خود را روی نقشه ببینید. با این حال، اگر متوجه شده باشید، اشیاء مجازی روی لنگر ایجاد شده چسبیده اند و نسبت به جایی که آن پارک ها در فضا هستند قرار نمی گیرند.

f93eb87c98a0098d.png

برای آخرین مرحله، این مورد را با استفاده از Maps SDK for Android Utility Library و SensorManager در دستگاه اصلاح خواهید کرد.

8. مکان یابی

برای اینکه بتوانید نماد مکان مجازی را در واقعیت افزوده در یک عنوان دقیق قرار دهید، به دو اطلاعات نیاز دارید:

  • جایی که شمال واقعی است
  • زاویه بین شمال و هر مکان

تعیین شمال

شمال را می توان با استفاده از سنسورهای موقعیت (ژئومغناطیسی و شتاب سنج) موجود در دستگاه تعیین کرد. با استفاده از این دو سنسور می توانید اطلاعات لحظه ای در مورد موقعیت دستگاه در فضا جمع آوری کنید. برای اطلاعات بیشتر درباره حسگرهای موقعیت، جهت گیری دستگاه را محاسبه کنید .

برای دسترسی به این حسگرها، باید یک SensorManager و سپس یک SensorEventListener در آن سنسورها ثبت کنید. این مراحل قبلاً در روش‌های چرخه حیات MainActivity برای شما انجام شده است:

override fun onCreate(savedInstanceState: Bundle?) {
   super.onCreate(savedInstanceState)
   // ...
   sensorManager = getSystemService()!!
   // ...
}

override fun onResume() {
   super.onResume()
   sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD)?.also {
       sensorManager.registerListener(
           this,
           it,
           SensorManager.SENSOR_DELAY_NORMAL
       )
   }
   sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)?.also {
       sensorManager.registerListener(
           this,
           it,
           SensorManager.SENSOR_DELAY_NORMAL
       )
   }
}

override fun onPause() {
   super.onPause()
   sensorManager.unregisterListener(this)
}

در روش onSensorChanged ، یک شی SensorEvent ارائه می‌شود که حاوی جزئیاتی در مورد داده‌های یک سنسور معین که در طول زمان تغییر می‌کند. ادامه دهید و کد زیر را به آن متد اضافه کنید:

override fun onSensorChanged(event: SensorEvent?) {
   if (event == null) {
       return
   }
   if (event.sensor.type == Sensor.TYPE_ACCELEROMETER) {
       System.arraycopy(event.values, 0, accelerometerReading, 0, accelerometerReading.size)
   } else if (event.sensor.type == Sensor.TYPE_MAGNETIC_FIELD) {
       System.arraycopy(event.values, 0, magnetometerReading, 0, magnetometerReading.size)
   }

   // Update rotation matrix, which is needed to update orientation angles.
   SensorManager.getRotationMatrix(
       rotationMatrix,
       null,
       accelerometerReading,
       magnetometerReading
   )
   SensorManager.getOrientation(rotationMatrix, orientationAngles)
}

کد بالا نوع سنسور را بررسی می کند و بسته به نوع، قرائت سنسور مناسب (شتاب سنج یا مغناطیس سنج) را به روز می کند. با استفاده از این خوانش‌های حسگر، اکنون می‌توان مقدار چند درجه از شمال نسبت به دستگاه را تعیین کرد (یعنی مقدار orientationAngles[0] ).

عنوان کروی

اکنون که شمال مشخص شده است، گام بعدی تعیین زاویه بین شمال و هر مکان است و سپس با استفاده از آن اطلاعات مکان ها را در عنوان صحیح در واقعیت افزوده قرار می دهیم.

برای محاسبه عنوان، از Maps SDK for Android Utility Library استفاده می‌کنید، که حاوی تعدادی توابع کمکی برای محاسبه فاصله‌ها و سرفصل‌ها از طریق هندسه کروی است. برای اطلاعات بیشتر، این نمای کلی از کتابخانه را بخوانید.

در مرحله بعد، از روش sphericalHeading Heading در کتابخانه ابزار استفاده خواهید کرد، که عنوان/برینگ را بین دو شی LatLng محاسبه می کند. این اطلاعات در داخل متد getPositionVector تعریف شده در Place.kt مورد نیاز است. این روش در نهایت یک شی PlaceNode Vector3 عنوان موقعیت محلی خود در فضای AR استفاده می شود.

ادامه دهید و تعریف عنوان را در آن متد با موارد زیر جایگزین کنید:

val heading = latLng.sphericalHeading(placeLatLng)

انجام این کار باید به تعریف روش زیر منجر شود:

fun Place.getPositionVector(azimuth: Float, latLng: LatLng): Vector3 {
   val placeLatLng = this.geometry.location.latLng
   val heading = latLng.sphericalHeading(placeLatLng)
   val r = -2f
   val x = r * sin(azimuth + heading).toFloat()
   val y = 1f
   val z = r * cos(azimuth + heading).toFloat()
   return Vector3(x, y, z)
}

موقعیت محلی

آخرین مرحله برای جهت دهی صحیح مکان ها در AR استفاده از نتیجه getPositionVector زمانی است که اشیاء PlaceNode به صحنه اضافه می شوند. ادامه دهید و به addPlaces در MainActivity بروید، درست زیر خطی که والد در هر placeNode تنظیم شده است (راست زیر placeNode.setParent(anchorNode) ). localPosition را در placeNode با نتیجه فراخوانی getPositionVector تنظیم کنید:

val placeNode = PlaceNode(this, place)
placeNode.setParent(anchorNode)
placeNode.localPosition = place.getPositionVector(orientationAngles[0], currentLocation.latLng)

به‌طور پیش‌فرض، متد getPositionVector فاصله y گره را همانطور که توسط مقدار y در متد getPositionVector مشخص می‌شود، 1 متر تعیین می‌کند. اگر می‌خواهید این فاصله را تنظیم کنید، مثلاً 2 متر، ادامه دهید و آن مقدار را در صورت نیاز تغییر دهید.

با این تغییر، اشیاء اضافه شده PlaceNode اکنون باید در عنوان صحیح جهت گیری شوند. حالا پیش بروید و برنامه را اجرا کنید تا نتیجه را ببینید!

9. تبریک می گویم

تبریک می گویم برای رسیدن به اینجا!

بیشتر بدانید