使用 Sphero 映射环境,如何获得更好的精度?

Posted

技术标签:

【中文标题】使用 Sphero 映射环境,如何获得更好的精度?【英文标题】:Using Sphero to map an environnement, how to get a better accuracy? 【发布时间】:2014-12-09 17:20:34 【问题描述】:

我为 sphero 开发了一个应用程序,让它可以在房子等环境中旅行。目的是绘制环境图。我设法恢复位置和碰撞点,但似乎这些碰撞会破坏位置。我认为 Sphero 在遇到障碍物时会偏离其路径。有没有人知道适当的设置以获得最佳精度?低速?基于方向等其他参数的校正?

这是我的代码,可以在https://github.com/scenaristeur/sphero-carto 下载应用程序:

package orbotix.uisample;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;  
import java.util.ArrayList;
import java.util.Random;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Environment;
import android.provider.Settings;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.CheckBox;
import android.widget.TextView;
import android.widget.Toast;
import orbotix.robot.app.ColorPickerActivity;
import orbotix.robot.base.CollisionDetectedAsyncData;
import orbotix.robot.base.Robot;
import orbotix.robot.base.CollisionDetectedAsyncData.CollisionPower;
import orbotix.robot.sensor.Acceleration;
import orbotix.robot.sensor.LocatorData;
import orbotix.robot.widgets.CalibrationImageButtonView;
import orbotix.robot.widgets.NoSpheroConnectedView;
import orbotix.robot.widgets.NoSpheroConnectedView.OnConnectButtonClickListener;
import orbotix.robot.widgets.SlideToSleepView;
import orbotix.robot.widgets.joystick.JoystickView;
import orbotix.sphero.CollisionListener;
import orbotix.sphero.ConnectionListener;
import orbotix.sphero.LocatorListener;
import orbotix.sphero.Sphero;
import orbotix.view.calibration.CalibrationView;
import orbotix.view.calibration.ControllerActivity;
import orbotix.view.connection.SpheroConnectionView;


public class UiSampleActivity extends ControllerActivity 
/** ID to start the StartupActivity for result to connect the Robot */
private final static int STARTUP_ACTIVITY = 0;
private static final int BLUETOOTH_ENABLE_REQUEST = 11;
private static final int BLUETOOTH_SETTINGS_REQUEST = 12;

/** ID to start the ColorPickerActivity for result to select a color */
private final static int COLOR_PICKER_ACTIVITY = 1;
private boolean mColorPickerShowing = false;

/** The Robot to control */
private Sphero mRobot;

/** One-Touch Calibration Button */
private CalibrationImageButtonView mCalibrationImageButtonView;

/** Calibration View widget */
private CalibrationView mCalibrationView;

/** Slide to sleep view */
private SlideToSleepView mSlideToSleepView;

/** No Sphero Connected Pop-Up View */
private NoSpheroConnectedView mNoSpheroConnectedView;

/** Sphero Connection View */
private SpheroConnectionView mSpheroConnectionView;

//Colors
private int mRed = 0xff;
private int mGreen = 0xff;
private int mBlue = 0xff;
private float positionX;
private float positionY;
private float velocityX;
private float velocityY;
private String couleur="vert";
private float angle=0f;
private float vitesse=0.6f;
private boolean stop;
private boolean auto;
private int countCollision=0;
private int maxCollision=10;
private float accelX;
private float accelY;
private float powerX;
private float powerY;
private static final String TAG = "OBX-LocatorSmag";
private Point point=new Point();
private ArrayList<Point> points=new ArrayList<Point>();
File repertoire= new File (Environment.getExternalStorageDirectory()+"/smag-sphero");

private LocatorListener mLocatorListener = new LocatorListener() 
    @Override
    public void onLocatorChanged(LocatorData locatorData) 
        Log.d(TAG, locatorData.toString());
        if (locatorData != null) 
            positionX=locatorData.getPositionX();
            positionY=locatorData.getPositionY();
            velocityX=locatorData.getVelocityX();
            velocityY=locatorData.getVelocityY();
            point=new Point(positionX,positionY,velocityX,velocityY,couleur);
            points.add(point);

            if ((auto)&&(Math.abs((velocityX))<.01)&&(Math.abs(velocityY)<.01))

                chercheDirection();
            
            //Toast.makeText(UiSampleActivity.this,"X:"+positionX, Toast.LENGTH_SHORT).show();
           // ((TextView) findViewById(R.id.txt_locator_x)).setText(locatorData.getPositionX() + " cm");
           // ((TextView) findViewById(R.id.txt_locator_y)).setText(locatorData.getPositionY() + " cm");
           // ((TextView) findViewById(R.id.txt_locator_vx)).setText(locatorData.getVelocityX() + " cm/s");
           // ((TextView) findViewById(R.id.txt_locator_vy)).setText(locatorData.getVelocityY() + " cm/s");
        
    
    ;

private BroadcastReceiver mColorChangeReceiver = new BroadcastReceiver() 
    @Override
    public void onReceive(Context context, Intent intent) 
        // update colors
        int red = intent.getIntExtra(ColorPickerActivity.EXTRA_COLOR_RED, 0);
        int green = intent.getIntExtra(ColorPickerActivity.EXTRA_COLOR_GREEN, 0);
        int blue = intent.getIntExtra(ColorPickerActivity.EXTRA_COLOR_BLUE, 0);

        // change the color on the ball
        mRobot.setColor(red, green, blue);
    
;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    if (!repertoire.exists()) 
          repertoire.mkdir();
        

    // Set up the Sphero Connection View
    mSpheroConnectionView = (SpheroConnectionView) findViewById(R.id.sphero_connection_view);

    mSpheroConnectionView.addConnectionListener(new ConnectionListener() 

        @Override
        public void onConnected(Robot robot) 
            // Set Robot
            mRobot = (Sphero) robot; // safe to cast for now
            //Set connected Robot to the Controllers
            setRobot(mRobot);
            //active LocatorListener;
            mRobot.getSensorControl().addLocatorListener(mLocatorListener);
            mRobot.getSensorControl().setRate(5);
            mRobot.getCollisionControl().addCollisionListener(mCollisionListener);
            mRobot.getCollisionControl().startDetection(45, 45, 45, 45, 100);
            // Make sure you let the calibration view knows the robot it should control
            mCalibrationView.setRobot(mRobot);

            // Make connect sphero pop-up invisible if it was previously up
            mNoSpheroConnectedView.setVisibility(View.GONE);
            mNoSpheroConnectedView.switchToConnectButton();
            Toast.makeText(UiSampleActivity.this,"Déplacez Sphéro, cliquez ensuite sur Sleep pour enregistrer le parcours dans /smag-sphero", Toast.LENGTH_SHORT).show();

            if (repertoire.isDirectory())
                Toast.makeText(UiSampleActivity.this,"positions enregistrées dans /smag-sphero", Toast.LENGTH_SHORT).show();

            else
                Toast.makeText(UiSampleActivity.this,"création du repertoire /smag-sphero", Toast.LENGTH_SHORT).show();
            
        

        @Override
        public void onConnectionFailed(Robot sphero) 
            // let the SpheroConnectionView handle or hide it and do something here...
        

        @Override
        public void onDisconnected(Robot sphero) 
            if (points!=null)ecrireFichier();
            mSpheroConnectionView.startDiscovery();

        
    );

    //Add the JoystickView as a Controller
    addController((JoystickView) findViewById(R.id.joystick));

    // Add the calibration view
    mCalibrationView = (CalibrationView) findViewById(R.id.calibration_view);

    // Set up sleep view
    mSlideToSleepView = (SlideToSleepView) findViewById(R.id.slide_to_sleep_view);
    mSlideToSleepView.hide();
    // Send ball to sleep after completed widget movement
    mSlideToSleepView.setOnSleepListener(new SlideToSleepView.OnSleepListener() 
        @Override
        public void onSleep() 
            mRobot.sleep(0);
        
    );

    // Initialize calibrate button view where the calibration circle shows above button
    // This is the default behavior
    mCalibrationImageButtonView = (CalibrationImageButtonView) findViewById(R.id.calibration_image_button);
    mCalibrationImageButtonView.setCalibrationView(mCalibrationView);
    // You can also change the size and location of the calibration views (or you can set it in XML)
    mCalibrationImageButtonView.setRadius(100);
    mCalibrationImageButtonView.setOrientation(CalibrationView.CalibrationCircleLocation.ABOVE);

    // Grab the No Sphero Connected View
    mNoSpheroConnectedView = (NoSpheroConnectedView) findViewById(R.id.no_sphero_connected_view);
    mNoSpheroConnectedView.setOnConnectButtonClickListener(new OnConnectButtonClickListener() 

        @Override
        public void onConnectClick() 
            mSpheroConnectionView.setVisibility(View.VISIBLE);
            mSpheroConnectionView.startDiscovery();
        

        @Override
        public void onSettingsClick() 
            // Open the Bluetooth Settings Intent
            Intent settingsIntent = new Intent(Settings.ACTION_BLUETOOTH_SETTINGS);
            UiSampleActivity.this.startActivityForResult(settingsIntent, BLUETOOTH_SETTINGS_REQUEST);
        
    );





/** Called when the user comes back to this app */
@Override
protected void onResume() 
    super.onResume();
    if (mColorPickerShowing) 
        mColorPickerShowing = false;
        return;
    

    Log.d("", "registering Color Change Listener");
    IntentFilter filter = new IntentFilter(ColorPickerActivity.ACTION_COLOR_CHANGE);
    registerReceiver(mColorChangeReceiver, filter);


/** Called when the user presses the back or home button */
@Override
protected void onPause() 
    super.onPause();
    if (mColorPickerShowing) return;
ecrireFichier();
    // Disconnect Robot properly
    if (mRobot != null) 
        mRobot.disconnect();
    
    try 
        unregisterReceiver(mColorChangeReceiver); // many times throws exception on leak
     catch (Exception e) 
    


@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) 
    super.onActivityResult(requestCode, resultCode, data);

    if (resultCode == RESULT_OK) 
        if (requestCode == COLOR_PICKER_ACTIVITY) 
            //Get the colors
            mRed = data.getIntExtra(ColorPickerActivity.EXTRA_COLOR_RED, 0xff);
            mGreen = data.getIntExtra(ColorPickerActivity.EXTRA_COLOR_GREEN, 0xff);
            mBlue = data.getIntExtra(ColorPickerActivity.EXTRA_COLOR_BLUE, 0xff);

            //Set the color
            mRobot.setColor(mRed, mGreen, mBlue);
         else if (requestCode == BLUETOOTH_ENABLE_REQUEST) 
            // User enabled bluetooth, so refresh Sphero list
            mSpheroConnectionView.setVisibility(View.VISIBLE);
            mSpheroConnectionView.startDiscovery();
        
     else 
        if (requestCode == STARTUP_ACTIVITY) 
            // Failed to return any robot, so we bring up the no robot connected view
            mNoSpheroConnectedView.setVisibility(View.VISIBLE);
         else if (requestCode == BLUETOOTH_ENABLE_REQUEST) 

            // User clicked "NO" on bluetooth enable settings screen
            Toast.makeText(UiSampleActivity.this,
                    "Enable Bluetooth to Connect to Sphero", Toast.LENGTH_LONG).show();
         else if (requestCode == BLUETOOTH_SETTINGS_REQUEST) 
            // User enabled bluetooth, so refresh Sphero list
            mSpheroConnectionView.setVisibility(View.VISIBLE);
            mSpheroConnectionView.startDiscovery();
        
    


/**
 * When the user clicks the "Color" button, show the ColorPickerActivity
 *
 * @param v The Button clicked
 */
public void onColorClick(View v) 

    mColorPickerShowing = true;
    Intent i = new Intent(this, ColorPickerActivity.class);

    //Tell the ColorPickerActivity which color to have the cursor on.
    i.putExtra(ColorPickerActivity.EXTRA_COLOR_RED, mRed);
    i.putExtra(ColorPickerActivity.EXTRA_COLOR_GREEN, mGreen);
    i.putExtra(ColorPickerActivity.EXTRA_COLOR_BLUE, mBlue);

    startActivityForResult(i, COLOR_PICKER_ACTIVITY);


public void onAutoClick(View v) 
    mRobot.setColor(150, 150, 0);
     Toast.makeText(UiSampleActivity.this,"Auto", Toast.LENGTH_SHORT).show();
    // for (int i=0;i<10;i++)
     auto=true;
     mRobot.drive(angle, vitesse);



/**
 * When the user clicks the "Sleep" button, show the SlideToSleepView shows
 *
 * @param v The Button clicked
 */
public void onSleepClick(View v) 
    ecrireFichier();
    mSlideToSleepView.show();


public void onWriteClick(View v)
    String message= "X :"+positionX+" Y : "+positionY;
     Toast.makeText(UiSampleActivity.this,"Ecriture fichier"+message, Toast.LENGTH_LONG).show();

    /* String NOTES ="info.txt";
     File file = new File(Environment.getExternalStorageDirectory(), NOTES);

     String Info = "Test Text";
     try 
        file.createNewFile();
         FileWriter filewriter = new FileWriter(file,false);
         filewriter.write(Info);
         filewriter.close();
         Toast.makeText(UiSampleActivity.this,"OK", Toast.LENGTH_LONG).show();

     catch (IOException e) 
        // TODO Auto-generated catch block
        e.printStackTrace();
         Toast.makeText(UiSampleActivity.this,"Ecriture plantée"+message, Toast.LENGTH_LONG).show();

    */


public void onStopClick(View v) 
    if (mRobot != null) 
        // Stop robot
        stop=true;
        auto=false;
        mRobot.stop();
    



@Override
public boolean dispatchTouchEvent(MotionEvent event) 
    mCalibrationView.interpretMotionEvent(event);
    mSlideToSleepView.interpretMotionEvent(event);
    return super.dispatchTouchEvent(event);

private final CollisionListener mCollisionListener = new CollisionListener() 
    public void collisionDetected(CollisionDetectedAsyncData collisionData) 
        mRobot.setColor(200, 0, 0);
        Acceleration acceleration = collisionData.getImpactAcceleration();
        CollisionPower power = collisionData.getImpactPower();
        stop=true;
        accelX=(float) acceleration.x;
        accelY=(float) acceleration.y;
        powerX=power.x;
        powerY=power.y;
        couleur="rouge";
        point=new Point(positionX,positionY,velocityX,velocityY,couleur);
        points.add(point);
        couleur="vert";
        countCollision++;

         Toast.makeText(UiSampleActivity.this,countCollision+" Collision "+angle, Toast.LENGTH_SHORT).show();
         if (auto)
             mRobot.setColor(20,0,0);
chercheDirection();

//mRobot.drive(angle,vitesse);
         
    /*    // Update the UI with the collision data
        Acceleration acceleration = collisionData.getImpactAcceleration();
        mAccelXValueLabel = (TextView) findViewById(R.id.accel_x_value);
        mAccelXValueLabel.setText("" + acceleration.x);

        mAccelYValueLabel = (TextView) findViewById(R.id.accel_y_value);
        mAccelYValueLabel.setText("" + acceleration.y);

        mAccelZValueLabel = (TextView) findViewById(R.id.accel_z_value);
        mAccelZValueLabel.setText("" + acceleration.z);

        mXAxisCheckBox = (CheckBox) findViewById(R.id.axis_x_checkbox);
        mXAxisCheckBox.setChecked(collisionData.hasImpactXAxis());

        mYAxisCheckBox = (CheckBox) findViewById(R.id.axis_y_checkbox);
        mYAxisCheckBox.setChecked(collisionData.hasImpactYAxis());

        CollisionPower power = collisionData.getImpactPower();
        mPowerXValueLabel = (TextView) findViewById(R.id.power_x_value);
        mPowerXValueLabel.setText("" + power.x);

        mPowerYValueLabel = (TextView) findViewById(R.id.power_y_value);
        mPowerYValueLabel.setText("" + power.y);

        mSpeedValueLabel = (TextView) findViewById(R.id.speed_value);
        mSpeedValueLabel.setText("" + collisionData.getImpactSpeed());

        mTimestampLabel = (TextView) findViewById(R.id.time_stamp_value);
        mTimestampLabel.setText(collisionData.getImpactTimeStamp() + " ms");*/
    
;


public void chercheDirection()
    //mRobot.drive(angle, vitesse);

    if((velocityX==0)&&(velocityY==0))

        mRobot.setColor(0, 0, 20);

    Random rand = new Random();
             float variation = rand.nextFloat();
        /* float sens=rand.nextFloat();
         if (sens>.5)
        angle=angle+(180f*variation);
         else
             angle=angle-(180f*variation); 
         */
        angle=angle+180f*variation;
        if(angle>=360f)
            angle-=360;
        
        mRobot.setColor(20,0,0);
        //vitesse=0.2f;
        // Toast.makeText(UiSampleActivity.this,"demi tour "+angle+" "+vitesse, Toast.LENGTH_SHORT).show();

        mRobot.drive(angle,vitesse);
        //vitesse=0.6f;
        try 
            Thread.sleep(250);
         catch (InterruptedException e) 
            // TODO Auto-generated catch block
            e.printStackTrace();
        
        mRobot.setColor(20,20,0);
        mRobot.drive(angle,vitesse);
    


public void ecrireFichier()
    String message= ""; 

    //ecriture
     String cheminTime ="smag-sphero/Smag-Sphero"+System.currentTimeMillis()+".txt";
    String cheminLast ="smag-sphero/Smag-Sphero-last.txt";
     File file = new File(Environment.getExternalStorageDirectory(), cheminTime);
     File fileLast = new File(Environment.getExternalStorageDirectory(), cheminLast);
     Point dernier=null;
      for (int i=0;i<points.size();i++)
          Point p=points.get(i);
          dernier=p;
          message=message+p.getX()+","+p.getY()+","+p.getVelX()+","+p.getVelY()+","+p.getCouleur()+","+p.getAccelX()+","+p.getAccelY()+","+p.getPowerX()+","+p.getPowerY()+"\n";
      
      message=message+dernier.getX()+","+dernier.getY()+","+dernier.getVelX()+","+dernier.getVelY()+",bleu,"+dernier.getAccelX()+","+dernier.getAccelY()+","+dernier.getPowerX()+","+dernier.getPowerY()+"\n";
     String Info = message;
     try 
        file.createNewFile();
     FileWriter filewriter = new FileWriter(file,true);
     filewriter.write(Info);
     filewriter.close();

        //pour last
     if (fileLast.exists())
         fileLast.delete();
         
        fileLast.createNewFile();
         FileWriter filewriterLast = new FileWriter(fileLast,true);
         filewriterLast.write(Info);
         filewriterLast.close();
         Toast.makeText(UiSampleActivity.this,"OK", Toast.LENGTH_LONG).show();
     catch (IOException e) 
        // TODO Auto-generated catch block
        e.printStackTrace();
     Toast.makeText(UiSampleActivity.this,"Ecriture plantée"+message, Toast.LENGTH_LONG).show();

    


【问题讨论】:

【参考方案1】:

如果您只想绘制环境边界(平面图),您可以沿着墙壁行驶

您可以分析 imu 传感器的滚动值,并在以 0.15 的功率行驶时将其与左侧墙壁保持 35° 左右。

当球体击中凹角时,速度会降至零,您必须向右转一点,直到他再次移动。

当球体沿凸角移动时,滚动值将下降到 0° 或更小。您必须保存该位置,停止 sphero 并计算返回该保存点的方向,以便从那里继续进行映射。

【讨论】:

以上是关于使用 Sphero 映射环境,如何获得更好的精度?的主要内容,如果未能解决你的问题,请参考以下文章

使用 nodeJS 进行 Sphero 编程。如何球体继续前进?

如何使用 sphero_ros Python API?

Sphero 如何计算速度?

Cocos2d-iphone - Sphero 集成

如何使用 Mac SDK 将大型(2 块)orbBasic 程序上传到 Sphero?

如何开始使用 C# 开发 Sphero-Ball Windows 应用程序?