RSSI BLE android的距离计算

时间:2016-04-04 10:14:54

标签: android bluetooth-lowenergy rssi

我知道有关我的问题的stackoverflow有很多问题,但我想知道是否有任何方法可以获得与RSSI的确切距离。

我已关注this link和其他git library methods for distance calculation以及此tutorial。但我无法得到正确的解决方案。

这就是我用来衡量距离的方法:

private BluetoothAdapter.LeScanCallback mLeScanCallback =
        new BluetoothAdapter.LeScanCallback() {

            @Override
            public void onLeScan(final BluetoothDevice device, final int rssi, final byte[] scanRecord) {
                runOnUiThread(new Runnable() {

                    @Override
                    public void run() {

                        BluetoothDeviceModel bluetoothDeviceModel = new BluetoothDeviceModel(device, rssi);

                        mLeDeviceListAdapter.addDevice(bluetoothDeviceModel, rssi, bluetoothDeviceModel.device.getAddress());
                        mLeDeviceListAdapter.notifyDataSetChanged();

                        if (mapBluetooth != null) {
                            mapBluetooth.put(bluetoothDeviceModel.getDevice().getAddress(), bluetoothDeviceModel);
                            mLeDeviceListAdapter.refresh(mapBluetooth);
                        }
                    }
                });
            }
        };

当我调用这个方法时,我传递了standard和rssi我从我的mLeScanCallBack()获取的内容

 app.controller('MainCtrl', $scope)
    {
        var quan = $scope.quantity;
        alert(quan);
    }
  

我面临什么问题?

上面的代码没有任何问题。它给了我距离,但我不满意,因为它不是正确的距离。那么有人能告诉我是否可以通过上述方法获得确切的距离或者是否有其他方式?

2 个答案:

答案 0 :(得分:2)

您无法获得“精确距离” ,因为您获得的RSSI确实很差,并且如果BLE标签的距离> 1米,则RSSI振荡会变得非常高,并且您将增加距离,更远的是计算出的距离。

您测量的RSSI取决于很多因素,例如环境(内部或外部),所用标签的类型,所用接收器的类型,以及标签和接收器之间是否有东西,如果您的标签附近有标签ecc ecc ecc

您无法使用蓝牙精确测量距离

但是您可以通过执行一些操作来平滑获得的高斯(测量值的波动)来更好地测量距离。

例如,如果您知道标签将始终处于相同的距离,则可以对所获得的Rssi值进行平均。例如,您计算最近1分钟内获得的Rssi的平均值。

这样,您可以获得更好的距离值,但标签必须保持在相同位置。

请记住,用于计算与rssi和txPower的距离的方式: (0.89976)* Math.pow(ratio,7.7095)+ 0.111;

在此公式中,您有3个常量,这些常量是标签和蓝牙接收器专用的。因此,为了提高精度,您应该为正在使用的每种标签以及正在使用的智能手机的每种蓝牙模块计算这3个常数。

如果标签移动了,则可以实现其他功能来平滑值,例如卡尔曼滤波器。

(信用: wouterbulten - kalmanjs - GitHub

Java中的卡尔曼过滤器(在Android上使用):

private class KalmanFilter implements Serializable {

    private double R;   //  Process Noise
    private double Q;   //  Measurement Noise
    private double A;   //  State Vector
    private double B;   //  Control Vector
    private double C;   //  Measurement Vector

    private Double x;   //  Filtered Measurement Value (No Noise)
    private double cov; //  Covariance

    public KalmanFilter(double r, double q, double a, double b, double c) {
        R = r;
        Q = q;
        A = a;
        B = b;
        C = c;
    }

    public KalmanFilter(double r, double q){
        R = r;
        Q = q;
        A = 1;
        B = 0;
        C = 1;
    }

    /** Public Methods **/
    public double applyFilter(double rssi){
        return applyFilter(rssi, 0.0d);
    }

    /**
     * Filters a measurement
     *
     * @param measurement The measurement value to be filtered
     * @param u The controlled input value
     * @return The filtered value
     */
    public double applyFilter(double measurement, double u) {
        double predX;           //  Predicted Measurement Value
        double K;               //  Kalman Gain
        double predCov;         //  Predicted Covariance
        if (x == null) {
            x = (1 / C) * measurement;
            cov = (1 / C) * Q * (1 / C);
        } else {
            predX = predictValue(u);
            predCov = getUncertainty();
            K = predCov * C * (1 / ((C * predCov * C) + Q));
            x = predX + K * (measurement - (C * predX));
            cov = predCov - (K * C * predCov);
        }
        return x;
    }

    /** Private Methods **/
    private double predictValue(double control){
        return (A * x) + (B * control);
    }

    private double getUncertainty(){
        return ((A * cov) * A) + R;
    }

    @Override
    public String toString() {
        return "KalmanFilter{" +
                "R=" + R +
                ", Q=" + Q +
                ", A=" + A +
                ", B=" + B +
                ", C=" + C +
                ", x=" + x +
                ", cov=" + cov +
                '}';
    }
}

用法:

private KalmanFilter mKalmanFilter; // Property of your class to store kalman filter values
    
mKalmanFilter = new KalmanFilter(KALMAN_R, KALMAN_Q); // init Kalman Filter
    
// Method Apply Filter
private void applyKalmanFilterToRssi(){
     mFilteredRSSI = mKalmanFilter.applyFilter(mRSSI);
}

常量值:

// Kalman R & Q
    private static final double KALMAN_R = 0.125d;
    private static final double KALMAN_Q = 0.5d;

+ KALMAN_R是过程噪声

+ KALMAN_Q是测量噪声

您应该更改这两个值,以查看测量值和使用案例。 更改这两个值,您将更改过滤后的测量值从一个值更改为另一个值的速度。 因此,如果您的噪声值很多,并且想减慢测量值变化的速度(以平滑高斯),则应尝试增加KALMAN_R和KALMAN_Q值。 如果我没记错的话,这里的KALMAN_R和KALMAN_Q的值是我为BLE设备编程时使用的值,因此KALMAN_R和KALMAN_Q的这两个值已经“很大”,因为BLE设备的RSSI变化很大。 / p>

我建议您使用卡尔曼滤波器来平滑您的值。

希望这对您有所帮助,祝您度过愉快的一天,并且编码愉快!

上传,这些是我的TagBLE类:

低功耗蓝牙标签的基类:

public class TagBLE extends RealmObject implements Parcelable {

    // Field Names
    public static final String FIELD_ID = "id";
    public static final String FIELD_MAC = FIELD_ID;

    @Expose
    @PrimaryKey
    @SerializedName("tag_mac")
    private String id;
    @Expose
    @SerializedName("tag_nome")
    private String mName;
    @Expose
    @SerializedName("tx_power")
    private int mTxPower;

    public TagBLE(){}

    public TagBLE(String mac, String name, int txPower){
        id = mac;
        mName = name;
        mTxPower = txPower;
    }

    public TagBLE(TagBLE tag){
        id = tag.getMAC();
        mName = tag.getName();
        mTxPower = tag.getTxPower();
    }

    /** Private Constructors **/
    private TagBLE(Parcel in){
        id = in.readString();
        mName = in.readString();
        mTxPower = in.readInt();
    }

    /** Public Static Factory Methods **/
    public static TagBLE initInstanceFromScanResult(ScanResult result, int txPower){
        BluetoothDevice bDevice = result.getDevice();
        return new TagBLE(bDevice.getAddress(), bDevice.getName(), txPower);
    }

    /** Parcelling Methods **/
    public static Parcelable.Creator<TagBLE> CREATOR = new TagBLECreator();

    /** Override Parcelable Methods **/
    @Override
    public int describeContents(){
        return 0x0;
    }

    @Override
    public void writeToParcel(Parcel out, int flags){
        out.writeString(id);
        out.writeString(mName);
        out.writeInt(mTxPower);
    }

    /** Getter Methods **/
    public String getId(){
        return id;
    }

    public String getMAC() {
        return id;
    }

    public String getName() {
        return mName;
    }

    public int getTxPower() {
        return mTxPower;
    }

    /** Setter Methods **/
    public void setId(String id) {
        this.id = id;
    }

    public void setName(String name) {
        mName = name;
    }

    public void setTxPower(int txPower) {
        mTxPower = txPower;
    }

    /** Public Methods **/
    public double getDistance(int rssi){
        return getDistance((double) rssi);
    }

    public double getDistance(double rssi){
        return Math.pow(10, ((mTxPower - rssi) * 1.0) / 20);
    }

    @Override
    public String toString() {
        return "TagBLE{" +
                "id='" + id + '\'' +
                ", mName='" + mName + '\'' +
                ", mTxPower=" + mTxPower +
                '}';
    }

    /** Private Static Class - Parcelable Creator **/
    private static class TagBLECreator implements Parcelable.Creator<TagBLE> {
        @Override
        public TagBLE createFromParcel(Parcel in) {
            return new TagBLE(in);
        }

        @Override
        public TagBLE[] newArray(int size) {
            return new TagBLE[size];
        }
    }

}

用于管理数据的特定类(在我的情况下,我需要管理距离,如果距离设备很近或很远,但是我从类代码中删除了这些部分)使用KalmanFilter):

public class DataTagBLE extends RealmObject {

    // Field Names
    public static final String FIELD_ID = "id";

    // Kalman R & Q
    private static final double KALMAN_R = 0.125d;
    private static final double KALMAN_Q = 0.5d;

    @PrimaryKey
    private String id;
    @Expose
    @SerializedName("tag")
    private TagBLE mTag;
    @Expose
    @SerializedName("acquired")
    private Date mAcquired;
    @Expose
    @SerializedName("rssi")
    private int mRSSI;
    @Expose
    @SerializedName("filtered_rssi")
    private double mFilteredRSSI;
    @Ignore
    private KalmanFilter mKalmanFilter;

    private double mDistance;

    public DataTagBLE(){}

    public DataTagBLE(TagBLE tag){
        id = UUID.randomUUID().toString();
        mTag = tag;
        mAcquired = new Date();
        mRSSI = 0x0;
        mFilteredRSSI = 0x0;
        mKalmanFilter = new KalmanFilter(KALMAN_R, KALMAN_Q);
    }

    /** Private Constructors **/
    private DataTagBLE(TagBLE tag, int rssi){
        id = UUID.randomUUID().toString();
        mTag = tag;
        mAcquired = new Date();
        mRSSI = rssi;
    }

    /** Public Static Factory Methods **/
    public static DataTagBLE initInstanceDataTagFound(@NonNull ScanResult scanResult, int txPower){
        return new DataTagBLE(TagBLE.initInstanceFromScanResult(scanResult, txPower));
    }

    /** Getter Methods **/
    public TagBLE getTag(){
        return mTag;
    }

    public Date getAcquired() {
        return mAcquired;
    }

    public int getRSSI(){
        return mRSSI;
    }

    public double getFilteredRSSI(){
        return this.mFilteredRSSI;
    }

    public KalmanFilter getKalmanFilter() {
        return mKalmanFilter;
    }

    /** Setter Methods **/
    public void setTag(TagBLE tag){
        mTag = tag;
    }

    public void setAcquired(Date acquired) {
        this.mAcquired = acquired;
    }

    public void setRSSI(int rssi){
        mRSSI = rssi;
    }

    public void setFilteredRSSI(int rssi){
        this.mFilteredRSSI = rssi;
    }

    public void setKalmanFilter(KalmanFilter kalmanFilter) {
        this.mKalmanFilter = kalmanFilter;
    }

    /** TagBLE Getter Methods **/
    public String getTagMac() {
        if (mTag != null) {
            return mTag.getMAC();
        } else {
            return null;
        }
    }

    /** TagBLE Setter Methods **/
    public void setTagNameAndTxPower(String tagName, int txPower){
        if(mTag != null){
            mTag.setName(tagName);
            mTag.setTxPower(txPower);
        }
    }

    /** Public Methods **/
    public void generateNewID(){
        id = UUID.randomUUID().toString();
    }

    public void onNewDataTagAcquired(DataTagBLE dataTagFound){
        setRSSI(dataTagFound.getRSSI());
        applyKalmanFilterToRssi();
        TagBLE tagFound = dataTagFound.getTag();
        if(tagFound != null) {
            setTagNameAndTxPower(tagFound.getName(), tagFound.getTxPower());
        }
        setAcquired(new Date());
    }

    public void store(){
        generateNewID();
        RealmHelper rHelper = new RealmHelper();
        rHelper.saveUpdateRealmObject(this);
        rHelper.close();
    }

    /** Distance & RSSI Filtering Methods **/
    public double getDistanceFiltered(){
        return mTag.getDistance(mFilteredRSSI);
    }

    public double getDistance(){
        return mTag.getDistance(mRSSI);
    }

    /** Private Methods **/
    private void applyKalmanFilterToRssi(){
        mFilteredRSSI = mKalmanFilter.applyFilter(mRSSI);
    }

    @Override
    public String toString() {
        return "DataTagBLE{" +
                "id='" + id + '\'' +
                ", mTag=" + mTag +
                ", mAcquired=" + mAcquired +
                ", mRSSI=" + mRSSI +
                ", mFilteredRSSI=" + mFilteredRSSI +
                ", mKalmanFilter=" + mKalmanFilter +
                ", mDistance=" + mDistance +
                '}';
    }

    /** Private Classes **/
    /*
        SOURCE: https://github.com/wouterbulten/kalmanjs/blob/master/dist/kalman.js
    */
    private class KalmanFilter implements Serializable {

        private double R;   //  Process Noise
        private double Q;   //  Measurement Noise
        private double A;   //  State Vector
        private double B;   //  Control Vector
        private double C;   //  Measurement Vector

        private Double x;   //  Filtered Measurement Value (No Noise)
        private double cov; //  Covariance

        public KalmanFilter(double r, double q, double a, double b, double c) {
            R = r;
            Q = q;
            A = a;
            B = b;
            C = c;
        }

        public KalmanFilter(double r, double q){
            R = r;
            Q = q;
            A = 1;
            B = 0;
            C = 1;
        }

        /** Public Methods **/
        public double applyFilter(double rssi){
            return applyFilter(rssi, 0.0d);
        }

        /**
         * Filters a measurement
         *
         * @param measurement The measurement value to be filtered
         * @param u The controlled input value
         * @return The filtered value
         */
        public double applyFilter(double measurement, double u) {
            double predX;           //  Predicted Measurement Value
            double K;               //  Kalman Gain
            double predCov;         //  Predicted Covariance
            if (x == null) {
                x = (0x1 / C) * measurement;
                cov = (0x1 / C) * Q * (0x1 / C);
            } else {
                predX = predictValue(u);
                predCov = getUncertainty();
                K = predCov * C * (0x1 / (C * predCov * C + Q));
                x = predX + K * (measurement - (C * predX));
                cov = predCov - (K * C * predCov);
            }
            return x;
        }

        /** Private Methods **/
        private double predictValue(double control){
            return (A * x) + (B * control);
        }

        private double getUncertainty(){
            return (A * cov * A) + R;
        }

        @Override
        public String toString() {
            return "KalmanFilter{" +
                    "R=" + R +
                    ", Q=" + Q +
                    ", A=" + A +
                    ", B=" + B +
                    ", C=" + C +
                    ", x=" + x +
                    ", cov=" + cov +
                    '}';
        }
    }

}

在我的情况下,当我发现Tag BLE的唯一MAC时,我实例化了一个“ DataTagBLE”(该类尚未完成,我删除了所有正在使用的距离检查)。 我第一次通过以下方式初始化实例:

DataTagBLE initInstanceDataTagFound(@NonNull ScanResult scanResult, int txPower)

然后,每次我找到相同的标签(它将具有不同的RSSI)时,我都会通过查看MAC地址来获取该标签的DataTagBLE(我在服务上使用的是HashMap )。 。 然后,当我有实例时,我将使用:

myDataTagBLEInstance.onNewDataTagAcquired(DataTagBLE newDataTagBLEInstance)

(我有一些基本服务,这些服务总是会向我返回一个制成的DataTagBLE实例,因此我将使用上述方法用新的实例数据更新旧的实例) 这只是为了回答以下问题,KalmanFilter必须是相同标签的相同实例!

从信标获取txPower:

它使用:'com.neovisionaries:nv-bluetooth:1.8'

public static int getBeaconTxPower(ScanResult result){
        if(result != null) {
            // This part uses the library above
            if(result.getScanRecord() != null) {
                List<ADStructure> structures = ADPayloadParser.getInstance().parse(result.getScanRecord().getBytes());
                if (structures != null && structures.size() > 0x0) {
                    for (ADStructure st : structures) {
                        if (st instanceof IBeacon) {
                            IBeacon beacon = (IBeacon) st;
                            if(beacon.getPower() != 0x0) {
                                return beacon.getPower();
                            }
                        }
                    }
                }
            }
            // Add case here if the Tag doesn't have the txPower setted. For example: if(MAC ADDRESS contains "XX:XX:XX..." then txPower = Y"
            if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
                return result.getTxPower();
            }
        }
        return 0x0;
    }

使用HashMap的伪代码KalmanFilter:

// Store here KalmanFilters associated to every MAC Address
HashMap<String, KalmanFilter> mKalmanFilters;

// When you find a tag:
if mKalmanFilters.keySet().contains(tagFound.mac){
   KalmanFilter mKalman = mKalmanFilters.get(tagFound.mac());

   // This will give you a smoothed RSSI value because 'x == lastRssi'
   double smoothed = mKalman.applyFilter(tagFound.rssi);

   // Do what you want with this rssi
} else {
   KalmanFilter mKalman = new KalmanFilter(valR, valQ);
   
   /* This will set the first measurement, so the 'x', of the KalmanFilter. Next time you find the tag and you use the 'if part' you will get a smoothed rssi value.
This rssi value will be smoothed depending on the 'C' value (so Measurement Vector) setted in your KalmanFilter instance. If C == 1 => smoothed == rssi. */
   double smoothed = mKalman.applyFilter(tagFound.rssi);
   mKalmanFilters.put(tagFound.mac, mKalmanFilter);
}

答案 1 :(得分:1)

我也在做同样的事情。使用此方法,您可以计算距离,但该距离经常变化。这是因为RSSI值也经常变化。

您需要做的是平滑您的结果,并且您必须应用卡尔曼滤波器或(线性二次估计)。如果你想坚持卡尔曼过滤器,那么从这里开始。 Beacon Tracking

我仍在为我的项目寻找更好的卡尔曼滤波器实现。或者,您可以平滑RSSI值

  

h = * +(1 - )* -1

是最近的值,-1是之前的平均值。

从0到1不等[考虑= 0.75]

来源: - RSSI Smoothing