通过角度变化判断android设备是否处于静止状态 还是运动状态

本文介绍了一个基于Android平台的应用程序,该程序通过整合加速度计、磁力计和陀螺仪的数据来检测手机的姿态变化,并利用傅里叶变换分析加速度数据以判断手机是否处于移动状态。

https://blog.youkuaiyun.com/warren288/article/details/43274647


package com.shark.angletest.angletest;

import android.app.Activity;

import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Display;
import android.view.Surface;
import android.view.WindowManager;
import android.widget.TextView;


public class MainActivity extends AppCompatActivity {
    static final String TAG  = "sharkAngle";
    static final int SAMPLE_COUNT = 5;
    static final float MOVE_THRESHOLD = 1.0f;
    SensorCenter mSC;
    TextView mTV;
    float[] mAngles = new float[SAMPLE_COUNT];
    int mIndex = 0;
    //定义接口
    public interface IListener <T>{


        public void onCall(T obj);//(float value);
    }


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTV = (TextView) findViewById(R.id.textAngle);
    }


    @Override
    protected void onResume() {
        super.onResume();
        mSC = new SensorCenter();
        mSC.init(this);
        mSC.registerOrientationListener(new IListener() {
                @Override
                public void onCall(Object value) {
                    // TODO Auto-generated method stub


                    boolean isMove = false;
                    mAngles [mIndex++] = (float)value;
                    //Log.d(TAG, "onCall value:" + mAngles[mIndex-1]);
                    if (mIndex >= SAMPLE_COUNT) {
                        for (int i = 0; i < mIndex - 1; i++) {
                            float cur = Math.abs(mAngles[i + 1] - mAngles[i]);
                            //Log.d(TAG, "cur diff " + cur);


                            if (Math.abs(mAngles[i + 1] - mAngles[i]) > MOVE_THRESHOLD) {
                                isMove = true;
                            } else {
                                isMove = false;
                            }
                        }
                        mIndex = 0;


                        String result = isMove ? "moving....." : "standstill...";
                        mTV.setText(result/*String.valueOf((float)value)*/);
                    }
                }
            }
        );
    }


    @Override
    protected void onStop() {
        super.onStop();
        if (mSC != null) {
            mSC.unregister();
        }
    }


    /* 方向传感器中心。<br>
             * 使用方法为:创建对象后使用 {@link #init(Activity)} 初始化上下文环境,使用
             * {@link #registerOrientationListener(IListener)} 注册
             * 方向变化监听器,使用完毕后(一般在 {@link #init(Activity)}中所使用 {@link Activity}
             * 销毁时), {@link #unregister()}取消本类内部注册的 {@link SensorEventListener}
             * @author yangsheng
             * @date 2015年1月28日
             */
    private static class SensorCenter {


        private float[] accelerValues = new float[3];
        private float[] magneticValues = new float[3];


        private final SensorEventListener sensorListener = new SensorEventListener() {


            @Override
            public void onSensorChanged(SensorEvent event) {


                switch (event.sensor.getType()) {


                    // 深度复制,可避免部分设备下 SensorManager.getRotationMatrix 返回false的问题
                    case Sensor.TYPE_ACCELEROMETER:
                        for (int i = 0; i < 3; i++) {
                            accelerValues[i] = event.values[i];
                        }


                        if (listener != null) {
                            listener.onCall(getAngle());
                        }
                        break;
                    case Sensor.TYPE_MAGNETIC_FIELD:
                        for (int i = 0; i < 3; i++) {
                            magneticValues[i] = event.values[i];
                        }
                        break;
                    case Sensor.TYPE_GYROSCOPE:
                        break;
                    default:
                        break;
                }
            }


            @Override
            public void onAccuracyChanged(Sensor sensor, int accuracy) {
            }
        };


        private SensorManager sensorManager;
        private Activity activity;


        private IListener<Float> listener;


        private int axis_x;
        private int axis_y;


        public SensorCenter init(Activity activity) {


            this.activity = activity;
            this.sensorManager = (SensorManager) this.activity
                    .getSystemService(Context.SENSOR_SERVICE);


            // 注册三个传感器监听,加速度传感器、磁场传感器、陀螺仪传感器。
            // 陀螺仪传感器是可选的,也没有实际用处,但是在部分设备上,如果没注册它,会导致
            // SensorManager.getRotationMatrix 返回false
            sensorManager.registerListener(sensorListener,
                    sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
                    SensorManager.SENSOR_DELAY_UI);
            sensorManager.registerListener(sensorListener,
                    sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD),
                    SensorManager.SENSOR_DELAY_UI);
            sensorManager.registerListener(sensorListener,
                    sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE),
                    SensorManager.SENSOR_DELAY_UI);


            initIn();
            return this;
        }


        private void initIn() {


            // 根据当前上下文的屏幕方向调整与自然方向的相对关系。
            // 当设备的自然方向是竖直方向(比如,理论上说所有手机的自然方向都是都是是竖直方向),而应用是横屏时,
            // 需要将相对设备自然方向的方位角转换为相对水平方向的方位角。


            WindowManager wm = (WindowManager) activity.getSystemService(Context.WINDOW_SERVICE);
            Display dis = wm.getDefaultDisplay();
            int rotation = dis.getRotation();


            axis_x = SensorManager.AXIS_X;
            axis_y = SensorManager.AXIS_Y;
            switch (rotation) {
                case Surface.ROTATION_0:
                    break;
                case Surface.ROTATION_90:
                    axis_x = SensorManager.AXIS_Y;
                    axis_y = SensorManager.AXIS_MINUS_X;
                    break;
                case Surface.ROTATION_180:
                    axis_x = SensorManager.AXIS_X;
                    axis_y = SensorManager.AXIS_MINUS_Y;
                    break;
                case Surface.ROTATION_270:
                    axis_x = SensorManager.AXIS_MINUS_Y;
                    axis_y = SensorManager.AXIS_X;
                    break;


                default:
                    break;
            }
        }


        /**
         * 注册方向变换监听
         * @param listener, {@link IListener#onCall(Object)}
         *            中的参数为相对正北方向的方位角,范围:-180~180
         */
        public void registerOrientationListener(IListener<Float> listener) {
            this.listener = listener;
        }


        /**
         * 取方位角
         * @return
         */
        private float getAngle() {


            float[] inR = new float[9];
            // 第一个是方向角度参数,第二个参数是倾斜角度参数
            SensorManager.getRotationMatrix(inR, null, accelerValues, magneticValues);


            float[] outR = new float[9];
            float[] orientationValues = new float[3];


            // 根据当前上下文的屏幕方向调整与自然方向的相对关系。
            // 当设备的自然方向是竖直方向(比如,理论上说所有手机的自然方向都是都是是竖直方向,而有些平板的自然方向是水平方向),而应用是横屏时,
            // 需要将相对设备自然方向的方位角转换为相对水平方向的方位角。
            SensorManager.remapCoordinateSystem(inR, axis_x, axis_y, outR);
            SensorManager.getOrientation(outR, orientationValues);
            return (float) Math.toDegrees(orientationValues[0]);
        }


        /**
         * 取消注册的传感器监听
         */
        public void unregister() {
            try {
                sensorManager.unregisterListener(sensorListener);
            } catch (Exception e) {
                //LogTool.exception(e);
                Log.d(TAG, "error:" +e.toString());
            }
        }
}

}



利用傅里叶变换,效果好点

package com.shark.angletest.angletest;


import android.util.Log;


/**
 * Created by root on 7/12/18.
 */


public class MyFft {


 /*   public MyFft() {


    }


    *//*
    *实现倒码
    **//*
    public static Complex[] changedLow(Complex[] a, int length) {
        int mr = 0;


        for (int m = 1; m < length; ++m) {
            int l = length / 2;
            while (mr + l >= length) {
                l = l >> 1;    //右移相当于,l除以2
            }
            mr = mr % l + l;
            if (mr > m) {
                Complex t = new Complex();
                t = a[m];
                a[m] = a[mr];
                a[mr] = t;
            }
        }


        return a;
    }


    *//*
    *乘积因子
    **//*
    public Complex complex_exp(Complex z) {
        Complex r = new Complex();
        double expx = Math.exp(z.r);
        r.r = expx * Math.cos(z.i);
        r.i = expx * Math.sin(z.i);
        return r;
    }


    *//*
    *基-2 fft蝶形变换
    *fft_tepy=1正变换, -1反变换
    **//*
    public Complex[] fft_2(Complex[] a, int length, int fft_tepy) {


        double pisign = fft_tepy * Math.PI;
        // System.out.print(" pisign:"+pisign+"\n");
        Complex t = new Complex();
        int l = 1;


        while (l < length) {
            for (int m = 0; m < l; ++m) {
                int temp_int = l * 2; //左移相当于,l乘以2
                for (int i = m; temp_int < 0 ? i >= (length - 1) : i < length; i += temp_int) {
                    Complex temp = new Complex(0.0, m * pisign / l);


                    Complex temp_exp = complex_exp(temp);
                    t.r = a[i + l].r * temp_exp.r - a[i + l].i * temp_exp.i;
                    t.i = a[i + l].r * temp_exp.i + a[i + l].i * temp_exp.r;


                    a[i + l].r = a[i].r - t.r;
                    a[i + l].i = a[i].i - t.i;
                    a[i].r = a[i].r + t.r;
                    a[i].i = a[i].i + t.i;


                } // end for i


            } // end for m
            Log.d(MainActivity.TAG, "\n now is the loop and l=" + l + "\n");
            for (int c = 0; c < length; c++) {
                Log.d(MainActivity.TAG, a[c].r + "+j" + a[c].i + "\n");
            }


            l = l * 2;
        }//end while
        //左移相当于,l乘以2
        return a;
    }*/




     public Complex[] fft(Complex[] x) {
         int n = x.length;


         // 因为exp(-2i*n*PI)=1,n=1时递归原点
         if (n == 1){
             return x;
         }


         // 如果信号数为奇数,使用dft计算
         if (n % 2 != 0) {
             return dft(x);
         }


         // 提取下标为偶数的原始信号值进行递归fft计算
         Complex[] even = new Complex[n / 2];
         for (int k = 0; k < n / 2; k++) {
             even[k] = x[2 * k];
         }
         Complex[] evenValue = fft(even);


         // 提取下标为奇数的原始信号值进行fft计算
         // 节约内存
         Complex[] odd = even;
         for (int k = 0; k < n / 2; k++) {
             odd[k] = x[2 * k + 1];
         }
         Complex[] oddValue = fft(odd);


         // 偶数+奇数
         Complex[] result = new Complex[n];
         for (int k = 0; k < n / 2; k++) {
             // 使用欧拉公式e^(-i*2pi*k/N) = cos(-2pi*k/N) + i*sin(-2pi*k/N)
             double p = -2 * k * Math.PI / n;
             Complex m = new Complex(Math.cos(p), Math.sin(p));
             result[k] = evenValue[k].add(m.multiply(oddValue[k]));
             // exp(-2*(k+n/2)*PI/n) 相当于 -exp(-2*k*PI/n),其中exp(-n*PI)=-1(欧拉公式);
             result[k + n / 2] = evenValue[k].minus(m.multiply(oddValue[k]));
         }
         return result;
     }


     public Complex[] dft(Complex[] x) {
         int n = x.length;


         // 1个信号exp(-2i*n*PI)=1
         if (n == 1)
             return x;


         Complex[] result = new Complex[n];
         for (int i = 0; i < n; i++) {
             result[i] = new Complex(0, 0);
             for (int k = 0; k < n; k++) {
                 //使用欧拉公式e^(-i*2pi*k/N) = cos(-2pi*k/N) + i*sin(-2pi*k/N)
                 double p = -2 * k * Math.PI / n;
                 Complex m = new Complex(Math.cos(p), Math.sin(p));
                 result[i]=result[i].add(x[k].multiply(m));
                 //Log.d(MainActivity.TAG, "dft,p:"+String.valueOf(p)+";xk:"+x[k]+";m:"+m.toString()+";ret:"+result[i].toString());
             }
         }
         return result;
     }


}




package com.shark.angletest.angletest;


import android.app.Activity;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Display;
import android.view.Surface;
import android.view.WindowManager;
import android.widget.TextView;


public class MainActivity extends AppCompatActivity {
    static final String TAG  = "sharkAngle";
    static final int SAMPLE_COUNT = 6;
    static final float MOVE_THRESHOLD = 0.6f;
    SensorCenter mSC;
    TextView mTV;
    float[] mAngles = new float[SAMPLE_COUNT];
    int mIndex = 0;
    //定义接口
    public interface IListener <T>{


        public void onCall(T obj);//(float value);
        public void xonCall(boolean moving);
    }


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTV = (TextView) findViewById(R.id.textAngle);
    }


    @Override
    protected void onResume() {
        super.onResume();
        mSC = new SensorCenter();
        mSC.init(this);
        mSC.registerOrientationListener(new IListener() {
                @Override
                public void onCall(Object value) {
                    // TODO Auto-generated method stub
                    int standstillCnt = 0;
                    boolean isMove = true;
                    mAngles [mIndex++] = (float)value;
                    Log.d(TAG, "onCall value:" + mAngles[mIndex-1]);
                    if (mIndex >= SAMPLE_COUNT) {
                        for (int i = 0; i < mIndex - 1; i++) {
                            float cur = Math.abs(mAngles[i + 1] - mAngles[i]);
                            Log.d(TAG, "cur:"+cur);
                            if (Math.abs(mAngles[i + 1] - mAngles[i]) < MOVE_THRESHOLD) {
                                standstillCnt++;
                            }
                        }
                        mIndex = 0;


                        if (standstillCnt >= (SAMPLE_COUNT+1)/2) isMove = false;
                        Log.d(TAG, "standstillCnt: " + standstillCnt);


                        String result = isMove ? "moving....." : "standstill...";
                        mTV.setText(result/*String.valueOf((float)value)*/);
                    }
                }


                @Override
                public void xonCall(boolean moving) {
                    String result = moving ? "moving....." : "standstill...";
                    mTV.setText(result);
                }
            }
        );
    }


    @Override
    protected void onStop() {
        super.onStop();
        if (mSC != null) {
            mSC.unregister();
        }
    }


    /* 方向传感器中心。<br>
             * 使用方法为:创建对象后使用 {@link #init(Activity)} 初始化上下文环境,使用
             * {@link #registerOrientationListener(IListener)} 注册
             * 方向变化监听器,使用完毕后(一般在 {@link #init(Activity)}中所使用 {@link Activity}
             * 销毁时), {@link #unregister()}取消本类内部注册的 {@link SensorEventListener}
             * @author yangsheng
             * @date 2015年1月28日
             */
    private static class SensorCenter {


        private float[] accelerValues = new float[3];
        private float[] magneticValues = new float[3];


        //private float[] xarrays = new float[SAMPLE_COUNT];
        //private float[] yarrays = new float[SAMPLE_COUNT];
        //private float[] zarrays = new float[SAMPLE_COUNT];


        Complex []xb= new Complex[SAMPLE_COUNT];
        Complex []yb= new Complex[SAMPLE_COUNT];
        Complex []zb= new Complex[SAMPLE_COUNT];


        private int mIdx = 0;
        private int mStillCnt = 0;


        private final SensorEventListener sensorListener = new SensorEventListener() {


            @Override
            public void onSensorChanged(SensorEvent event) {


                switch (event.sensor.getType()) {


                    // 深度复制,可避免部分设备下 SensorManager.getRotationMatrix 返回false的问题
                    case Sensor.TYPE_ACCELEROMETER:
                        xb[mIdx] = new Complex(event.values[0], 0);
                        yb[mIdx] = new Complex(event.values[1], 0);
                        zb[mIdx] = new Complex(event.values[2], 0);
                        mIdx++;


                        /*for (int i = 0; i < 3; i++) {
                            accelerValues[i] = event.values[i];
                        }


                        xb[mIdx] = accelerValues[0];
                            yarrays[mIdx] = accelerValues[1];
                            zarrays[mIdx] = accelerValues[2];
                            mIdx++;
                        */
                        if (mIdx >= SAMPLE_COUNT) {
                            mIdx = 0;


                            int i;
                            MyFft mf = new MyFft();
                            final int n = SAMPLE_COUNT;
                            /*
                            int dd[] = new int[n];
                            for(i=0;i<n;i++){ //轮流赋值
                                xb[i] = new Complex(xarrays[i], 0);
                                yb[i] = new Complex(yarrays[i], 0);
                                zb[i] = new Complex(zarrays[i], 0);
                            }*/
                            Log.d(TAG, "-------------------------");
                            xb = mf.fft(xb);
                            for(i=0;i<n;i++){ Log.d(TAG, "fft xb["+i+"]"+xb[i].toString()); }
                            if(ismoving(xb)) {
                                mStillCnt=0;
                                listener.xonCall(true);
                                return;
                            }


                            yb = mf.fft(yb);
                            for(i=0;i<n;i++){ Log.d(TAG, "fft yb["+i+"]"+yb[i].toString()); }
                            if(ismoving(yb)) {
                                mStillCnt=0;
                                listener.xonCall(true);
                                return;
                            }
                            zb = mf.fft(zb);
                            for(i=0;i<n;i++){ Log.d(TAG, "fft zb["+i+"]"+zb[i].toString()); }
                            if(ismoving(zb)) {
                                mStillCnt=0;
                                listener.xonCall(true);
                                return;
                            }
                            if (listener != null) {
                                mStillCnt++;
                                if (mStillCnt > 3)
                                listener.xonCall(false);
                            }
                            Log.d(TAG, "-------------------------");
                            /*b=mf.changedLow(b,n);
                            for(i=0; i<n; i++) Log.d(TAG, b[i].toString());
                            b=mf.fft_2(b,n,-1);
                            for(i=0; i<n; i++) Log.d(TAG, b[i].toString());*/
                            /*float xcount = 0f, xavg = 0f;
                            float ycount = 0f, yavg = 0f;
                            float zcount = 0f, zavg = 0f;
                            for (i=0; i<SAMPLE_COUNT; i++) {
                                xcount += xarrays[i];
                                ycount += yarrays[i];
                                zcount += zarrays[i];
                            }
                            xavg = xcount / SAMPLE_COUNT;
                            yavg = ycount / SAMPLE_COUNT;
                            zavg = zcount / SAMPLE_COUNT;


                            float xspow=0f, yspow=0f, zspow=0f;
                            for (i=0; i<SAMPLE_COUNT; i++){
                                xspow += (xarrays[i] - xavg) * (xarrays[i] - xavg);
                                yspow += (yarrays[i] - xavg) * (yarrays[i] - yavg);
                                zspow += (zarrays[i] - xavg) * (zarrays[i] - zavg);
                            }
                            Log.d(TAG, "xshark xspow:"+xspow+";yspow:"+yspow+";zspow:"+zspow);*/
                        }


                        if (listener != null) {
                            //listener.onCall(getAngle());
                        }
                        break;
                    case Sensor.TYPE_MAGNETIC_FIELD:
                        for (int i = 0; i < 3; i++) {
                            magneticValues[i] = event.values[i];
                        }
                        break;
                    case Sensor.TYPE_GYROSCOPE:
                        break;
                    default:
                        break;
                }
            }


            @Override
            public void onAccuracyChanged(Sensor sensor, int accuracy) {
            }
            boolean ismoving(Complex[] x) {
                int i, t = 0,n = x.length;
                for(i=0;i<n;i++){
                    if (Math.abs(x[i].getM()) > 0.05f &&
                            Math.abs(x[i].getN()) > 0.05f) {
                        t++;
                    }
                }


                return t > (n/2+1);
            }
        };


        private SensorManager sensorManager;
        private Activity activity;


        private IListener<Float> listener;


        private int axis_x;
        private int axis_y;


        public SensorCenter init(Activity activity) {


            this.activity = activity;
            this.sensorManager = (SensorManager) this.activity
                    .getSystemService(Context.SENSOR_SERVICE);


            // 注册三个传感器监听,加速度传感器、磁场传感器、陀螺仪传感器。
            // 陀螺仪传感器是可选的,也没有实际用处,但是在部分设备上,如果没注册它,会导致
            // SensorManager.getRotationMatrix 返回false
            sensorManager.registerListener(sensorListener,
                    sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
                    SensorManager.SENSOR_DELAY_UI);
            sensorManager.registerListener(sensorListener,
                    sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD),
                    SensorManager.SENSOR_DELAY_UI);
            sensorManager.registerListener(sensorListener,
                    sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE),
                    SensorManager.SENSOR_DELAY_UI);


            initIn();
            return this;
        }


        private void initIn() {


            // 根据当前上下文的屏幕方向调整与自然方向的相对关系。
            // 当设备的自然方向是竖直方向(比如,理论上说所有手机的自然方向都是都是是竖直方向),而应用是横屏时,
            // 需要将相对设备自然方向的方位角转换为相对水平方向的方位角。


            WindowManager wm = (WindowManager) activity.getSystemService(Context.WINDOW_SERVICE);
            Display dis = wm.getDefaultDisplay();
            int rotation = dis.getRotation();


            axis_x = SensorManager.AXIS_X;
            axis_y = SensorManager.AXIS_Y;
            switch (rotation) {
                case Surface.ROTATION_0:
                    break;
                case Surface.ROTATION_90:
                    axis_x = SensorManager.AXIS_Y;
                    axis_y = SensorManager.AXIS_MINUS_X;
                    break;
                case Surface.ROTATION_180:
                    axis_x = SensorManager.AXIS_X;
                    axis_y = SensorManager.AXIS_MINUS_Y;
                    break;
                case Surface.ROTATION_270:
                    axis_x = SensorManager.AXIS_MINUS_Y;
                    axis_y = SensorManager.AXIS_X;
                    break;


                default:
                    break;
            }
        }


        /**
         * 注册方向变换监听
         * @param listener, {@link IListener#onCall(Object)}
         *            中的参数为相对正北方向的方位角,范围:-180~180
         */
        public void registerOrientationListener(IListener<Float> listener) {
            this.listener = listener;
        }


        /**
         * 取方位角
         * @return
         */
        private float getAngle() {


            float[] inR = new float[9];
            // 第一个是方向角度参数,第二个参数是倾斜角度参数
            SensorManager.getRotationMatrix(inR, null, accelerValues, magneticValues);


            float[] outR = new float[9];
            float[] orientationValues = new float[3];


            // 根据当前上下文的屏幕方向调整与自然方向的相对关系。
            // 当设备的自然方向是竖直方向(比如,理论上说所有手机的自然方向都是都是是竖直方向,而有些平板的自然方向是水平方向),而应用是横屏时,
            // 需要将相对设备自然方向的方位角转换为相对水平方向的方位角。
            SensorManager.remapCoordinateSystem(inR, axis_x, axis_y, outR);
            SensorManager.getOrientation(outR, orientationValues);
            return (float) Math.toDegrees(orientationValues[0]);
        }


        /**
         * 取消注册的传感器监听
         */
        public void unregister() {
            try {
                sensorManager.unregisterListener(sensorListener);
            } catch (Exception e) {
                //LogTool.exception(e);
                Log.d(TAG, "error:" +e.toString());
            }
        }
}

}



package com.shark.angletest.angletest;


/**
 * Created by root on 7/12/18.
 */


class Complex{
    private double m;// 实部
    private double n;// 虚部


    public Complex(double m, double n) {
        this.m = m;
        this.n = n;
    }


    // add
    public Complex add(Complex c) {
        return new Complex(m + c.m, n + c.n);
    }


    // minus
    public Complex minus(Complex c) {
        return new Complex(m - c.m, n - c.n);
    }


    // multiply
    public Complex multiply(Complex c) {
        return new Complex(m * c.m - n * c.n, m * c.n + n * c.m);
    }


    // divide
    public Complex divide(Complex c) {
        double d = Math.sqrt(c.m * c.m) + Math.sqrt(c.n * c.n);
        return new Complex((m * c.m + n * c.n) / d, Math.round((m * c.n - n * c.m) / d));
    }


    public double getM() { return m;}
    public double getN() { return n;}


    public String toString() {
        String rtr_str = "";
        if (n > 0)
            rtr_str = "(" + m + "+" + n + "i" + ")";
        if (n == 0)
            rtr_str = "(" + m + ")";
        if (n < 0)
            rtr_str = "(" + m + n + "i" + ")";
        return rtr_str;
    }
}

<think>首先,用户的问题是基于之前的上下文。用户提到了“参考站内引用”,但它声明“上述引用仅供参考”,所以我需要专注于用户当前的问题。用户说:“我想查找在CST模式下,判断EtherCAT 402伺服的伺服电机处于静止状态还是运动状态的方法”。 回顾系统指令:我需要使用中文回答,结构清晰,逐步解决问题。指令还包括使用特定的LaTeX格式,以及在回答末尾添加相关问题。 关键点是:用户在询问在CST模式下如何判断EtherCAT 402伺服的伺服电机是静止还是运动状态。 CST模式是什么?在EtherCAT和CANopen协议中,CST通常代表“Cyclic Synchronous Position Mode”或类似模式。在EtherCAT的CoE(CANopen over EtherCAT)中,CST是“Cyclic Synchronous Torque Mode”,但我需要确认。 用户提到了“EtherCAT 402伺服的”,这指的是EtherCAT从站设备遵循DS402(Device Profile for Drives and Motion Control)协议。 在DS402协议中,状态机定义了驱动器的状态,如“Drive Ready”、“Operation Enabled”、“Switched On”等。判断运动状态通常涉及到状态机的转换和特定对象字典条目。 对于静止运动状态,在伺服系统中,通常通过位置误差或速度来判断。在CST模式下,它可能涉及到实时监控。 CST模式的具体含义:在EtherCAT DS402中,CST通常是“Cyclic Synchronous Torque Mode”,其中驱动器接收扭矩命令。 要判断伺服电机是静止还是运动状态,我们需要监控某些参数。可能的参数包括: - 实际位置值(position actual value) - 实际速度值(velocity actual value) - 驱动器状态(status word) 在DS402状态机中,有一个“Operation Enabled”状态,但运动状态更具体地由实际运动来判断。 状态字(Status Word)的bit表示运动状态。例如,bit 10 (Target reached) 表示目标位置已到达,意味着静止;bit 12 (Velocity is zero) 表示速度为零。 在DS402协议中,Status Word的位定义如下: - Bit 0: Ready to switch on - Bit 1: Switched on - Bit 2: Operation enabled - Bit 3: Fault - Bit 4: Voltage enabled - Bit 5: Quick stop - Bit 6: Switch on disabled - Bit 7: Warning - Bit 8: Manufacturer specific - Bit 9: Remote - Bit 10: Target reached - Bit 11: Internal limit active - Bit 12: Homing attained - Bit 13: Homing error - Bit 14: ... 具体到运动状态: - Bit 10: Target reached – 当目标位置达到时设置,表示静止。 - Bit 12: Velocity is zero (在某些实现中) – 表示速度为零。 - 另外,bit 13 (Homing attained) 用于归位。 在CST模式下,既然是扭矩模式,我们可能更关心速度或位置变化。 用户指定了“CST模式”,所以我需要专注于此模式。 在Cyclic Synchronous Torque Mode (CST)中,驱动器接收扭矩命令,并反馈实际位置、速度等。 要判断静止运动状态,可以通过监控实际速度(velocity actual value)或位置误差。 在PLC或控制器侧,我们可以读取对象字典中的值。 对象字典条目: - Velocity actual value: Object 606C - Position actual value: Object 6064 - Status word: Object 6041 在Status Word中,bit 10 (Target reached) 是一个关键指标。当它设置为1时,表示驱动器已达到目标状态,通常意味着静止。 另外,bit 12 (Homing attained) 也可能相关,但主要针对归位。 在CST模式下,位置目标可能不是直接的,因为它是扭矩模式。但状态字仍然提供状态信息。 最佳实践是检查Status Word的bit 10是否设置,或者直接读取Velocity actual value。如果速度为零,则静止;否则运动。 在电机控制中,速度为零可能不是绝对的静止,因为有位置误差,但通常这是标准方法。 用户提到了“伺服电机处于静止状态还是运动状态”,所以速度或位置变化是关键。 现在,构建回答: 1. 结构清晰:从基础解释开始,然后逐步介绍方法。 2. 使用中文。 3. 添加LaTeX:由于涉及数字值或方程,可能不需要复杂的数学,但如果有位置或速度方程,可以用。例如,速度 $v$ 为零时静止。 4. 末尾添加相关问题。 回答步骤: - 解释CST模式:简要定义CST模式。 - 描述状态监测方法:通过EtherCAT读取对象字典值。 - 具体对象:Status Word (Object 6041h) 和 bit 10 (Target reached)。 - 其他方法:读取Velocity actual value (Object 606Ch)。 - 在实际应用中如何实现:使用编程库或工具。 - 确保回答真实可靠:基于EtherCAT和DS402协议知识。 用户说“参考用户上一次的问题和你的回答”,但在这个对话中,这是第一次关于这个主题的问题。之前的系统指令是通用的,用户没有其他上下文。所以,我直接回答。 最后,添加相关问题。 </think>### CST模式下判断EtherCAT 402伺服电机静止运动状态的方法 在EtherCAT伺服系统中,CST模式(Cyclic Synchronous Torque Mode,循环同步扭矩模式)是一种常见的控制模式,其中伺服驱动器接收周期性扭矩命令,并反馈实际运动参数。判断伺服电机是处于静止状态还是运动状态,主要通过监控EtherCAT DS402(Device Profile for Drives and Motion Control)协议中的特定对象字典(Object Dictionary)条目来实现。以下将逐步解释判断方法,基于协议规范和实际应用实践。方法分为核心原理、具体监控条目和实现步骤。 --- #### 步骤1: 理解CST模式下的状态监测原理 在CST模式下,伺服电机接收扭矩命令($T_{\text{cmd}}$),并通过位置反馈(如编码器)计算实际位置($x_{\text{act}}$)和实际速度($v_{\text{act}}$)。判断静止运动状态的关键是检测运动参数的实时变化: - **静止状态**:当实际速度 $v_{\text{act}} = 0$ 且位置误差 $\Delta x = |x_{\text{cmd}} - x_{\text{act}}| \approx 0$ 时,电机停止运动。 - **运动状态**:当 $v_{\text{act}} \neq 0$ 或 $\Delta x > \text{阈值}$ 时,电机正在运动。 在DS402协议中,这些参数通过状态字(Status Word)和特定对象字典条目提供实时数据,无需额外的传感器。状态字的位标识符直接反映驱动器内部状态,这是最可靠的判断方式[^1]。 --- #### 步骤2: 核心监控对象字典条目 在CST模式下,通过EtherCAT主站(如PLC或控制器)读取以下对象字典条目,以判断电机状态。这些条目基于DS402协议定义(索引以十六进制表示): 1. **状态字(Status Word, Object 6041h)**: - 这是最关键的对象,通过其位字段指示驱动器状态。 - Bit 10(“Target reached”,目标已达成):当设置为 `1` 时,表示电机已达到目标状态(通常意味着静止),否则为运动状态。 - 数学表示:如果状态字值 $S$ 满足 $S \ \& \ \text{0x0400} = \text{0x0400}$,则电机静止(其中 `0x0400` 是 bit 10 的掩码)。 - 其他相关位: - Bit 12(“Velocity is zero”,速度为零):在某些驱动器实现中,此位指示实际速度为零(静止),但并非所有驱动器都支持该位。 - Bit 13(“Homing attained”,归位完成):仅在归位模式下相关,通常不用于通用运动判断。 - 优点:状态字更新频率高(取决于EtherCAT周期时间,通常 $\leq 1$ ms),实时性强;缺点是需结合上下文解释位字段[^2]。 2. **实际速度值(Velocity Actual Value, Object 606Ch)**: - 直接读取实际速度 $v_{\text{act}}$(单位: rpm 或 rad/s)。 - 判断逻辑: - 如果 $|v_{\text{act}}| < \epsilon$(其中 $\epsilon$ 是一个小阈值,如 0.1 rpm),则电机静止。 - 否则,电机运动(例如 $v_{\text{act}} > 0$ 或 $v_{\text{act}} < 0$)。 - 优点:直观且数值化;缺点:阈值 $\epsilon$ 需根据应用设置,以避免噪音干扰。 3. **位置实际值(Position Actual Value, Object 6064h)**: - 读取实际位置 $x_{\text{act}}$,并结合目标位置(Object 607Ah)计算误差 $\Delta x = |x_{\text{cmd}} - x_{\text{act}}|$。 - 在CST模式下,位置目标可能不直接指定(因为是扭矩模式),但实际位置仍然反馈。 - 如果 $\Delta x \leq \delta$($\delta$ 为位置误差阈值,如 0.001 mm),且速度为零,则判定静止。 - 优点:适用于高精度应用;缺点:计算复杂,需额外读取目标位置对象。 在CST模式下,优先使用**状态字(Object 6041h)的 bit 10** 作为主要判断标准,因为它由驱动器内部逻辑直接维护,可靠性高。实际速度值可作为辅助验证。 --- #### 步骤3: 实现方法(通过EtherCAT主站) 在实际系统中,判断电机状态的步骤如下(以典型PLC或控制器为例): 1. **配置EtherCAT通信**: - 确保主站已启用PDO(Process Data Object)映射,将状态字(6041h)、实际速度(606Ch)等对象映射到周期性数据帧。 - 设置合适的EtherCAT周期时间(例如 100 μs 至 1 ms),以满足实时性要求。 2. **周期读取对象值**: - 使用编程库(如 TwinCAT、SOEM 或 EtherLab)读取对象字典。示例伪代码(基于C语言): ```c // 假设已初始化EtherCAT主站 uint16_t status_word = ecrt_sdo_read(0x6041); // 读取状态字 int32_t velocity_actual = ecrt_sdo_read(0x606C); // 读取实际速度 // 判断状态函数 bool isMotorStopped() { if ((status_word & 0x0400) != 0) { // 检查 bit 10 (0x0400) return true; // 静止状态 } else if (abs(velocity_actual) < VELOCITY_THRESHOLD) { return true; // 速度低于阈值,静止 } else { return false; // 运动状态 } } ``` - 注意:阈值 `VELOCITY_THRESHOLD` 需根据电机规格设置(例如 0.1 rpm)。 3. **处理异常情况**: - **干扰和噪音**:在CST模式下,扭矩命令可能导致微小抖动。添加滤波器(如移动平均)到速度值读数。 - **状态转换延迟**:状态字 bit 10 可能在运动停止后有一定延迟(典型 10–50 ms),需在主站逻辑中添加时间窗口判断。 - **错误状态**:检查状态字的故障位(如 bit 3),排除异常情况。 4. **工具支持**: - 使用诊断工具(如 Wireshark with EtherCAT plugin 或 Beckhoff TwinCAT Scope)实时监控对象值,简化调试。 --- #### 注意事项 - **模式依赖性**:在CST模式下,电机可能处于“Operation Enabled”状态(状态字 bit 2),但运动状态需单独判断静止条件不变。 - **性能优化**:对于高速应用,优先使用状态字减少读取延迟。 - **驱动器差异**:不同厂家(如 Beckhoff、Kollmorgen)可能扩展状态字位字段,需参考具体手册(如 CiA 402 规范)。 - **安全建议**:在关键应用中,结合多个信号(如状态字和速度值)进行冗余判断,以避免误判[^2]。 通过上述方法,您可以可靠地在CST模式下判断EtherCAT 402伺服电机的静止运动状态。如果需要特定代码实现或参数设置,请提供更多细节(如驱动器型号)。 ---
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值