本页介绍了基于Android SDK的一个简单Android 应用程序,其基于Android 基本控件,包含以下几个主要功能

  • 地图实时获取并绘制在界面上
  • 手动遥控控制底盘行走(前进,后退,左转,右转,停止)
  • 控制底盘规划路径移动到给定目标点
  • 获得底盘实时状态信息,包括运行状态,剩余电量,当前位置,当前朝向角等信息

此参考设计尽量去除了与SDK无关的技术细节,例如使用最简单的Android控件,使用基于多线程机制的定时任务刷新等。

其设计目标是

  • 提供一个Android 应用程序的最小系统给到客户,帮助客户减少学习成本,能够快速上手
  • 去除与SDK无关的技术信息,降低客户在此系统之上引入更多特性的修改成本


此参考例程代码没有经过详细的量产品质测试,因此无法直接用于产品级开发。

此例程仅用于展示如何使用SDK接口的常见接口函数,提供快速开发的参考样例。

直接使用此代码用于最终产品,并由此产生的错误或是故障,不在本司责任范围之内。


本页内容



参考例程下载

Android Reference Application

运行环境准备

  • 软件平台

    • Android Studio 3.1.3
    • Slamware Android SDK: Android SDK

    • RoboStudio(用于交叉验证):Robostudio installer

      使用不同版本的Android Studio可能会带来编译异常,请修改对应的Android SDK版本及Gradle版本

  • 硬件平台

          (以下任选其一)

    • Slamware SDP mini 
    • Slamware SDP
    • Zeus/Apollo等底盘系统


      对于首次使用Slamware SDK进行编程的用户来说,不建议在最开始使用基于自己底盘搭载Slamcore模块用于产品开发。此方式无法有效定位问题,即是基于SDK的应用程序问题,还是底盘部分存在故障。强烈建议选择以上列表中的一个用于初始开发。

编译运行

  1. 打开参考工程文件,打开Project Structure --> app --> Dependencies 检查SlamwareSDK是否添加到工程中, 然后编译工程


  2. 连接到底盘wifi(一般以SLAMWARE开头,后面跟着6位MAC地址)


  3. 在手机中运行参考程序,会弹出如下连接界面,点击“连接机器人”

    对于wifi连接,AP模式下,底盘默认IP地址为192.168.11.1,如果改为Station模式,则底盘的IP地址由底盘所连接的路由器动态分配,或者由用户指定,此时需使用此IP地址

    对于有线网络连接,IP地址默认为192.168.11.1,不可修改。

    注意

    1. 测试的手机需是ARM处理器的,×86的将无法运行

    2. 针对ARM CPU不同的架构,需要选择不同的librpsdk.so文件,本司提供ARM v7 和ARM v8的SDK

    2. 不支持模拟器运行

  4. 在如下界面中,左侧是地图显示,随着底盘的移动,地图内容会实时更新。右侧是控制区和状态显示区。

    操作指南
    1. 点击向前,向后,向左,向右,底盘会相应运动,每点击一次,底盘会运动一小段时间。如果需要保持运动,需要按下按钮不松开,若按钮松开,底盘则很快停止运动。


      通过此种方式控制底盘运动时,底盘处于遥控控制模式,底盘不会自动避障,故需留意周围环境,避免撞到人或物。

    2. 输入X,Y坐标后,点击到这去按键,底盘会运动到给定的目标点。X,Y坐标点的获得,可以根据当前位置显示的坐标记录
    3. 最上方是当前位置和机器人状态的实时显示,其中每一次底盘运动,运动状态都会相应改变;每次底盘位置或是朝向角发生改变,当前位置区域也会发生变化

程序说明

程序主要分成两个功能模块

  1. 控制底盘行走
    1. 遥控控制底盘行走
      遥控底盘需要使用moveBy接口,接口中传入动作类型指令,其代码主要位于每个按键的触摸响应函数中,以“向前”按键为例

      // go forward
      int delayTime = 300;
      button_forward.setLongClickRepeatListener(new LongClickButton.LongClickRepeatListener() {
          @Override
          public void repeatAction() {
              try {
                  moveAction = robotPlatform.moveBy(MoveDirection.FORWARD);
      
                  System.out.println("repeatAction===============");
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }
      }, delayTime);
    2. 自主规划路径行走

             button.setOnClickListener(new View.OnClickListener() {
                  @Override
                  public void onClick(View view) {
                      if(targetX.length()==0 || targetY.length()==0) {
                          Toast.makeText(MainActivity.this, "请输入目标点坐标", Toast.LENGTH_SHORT).show();
                      } else {
                          try {
                              float x = Float.parseFloat(targetX.getText().toString());
                              float y = Float.parseFloat(targetY.getText().toString());
      
                              MoveOption moveOption = new MoveOption();
                              moveOption.setPrecise(true);
                              moveOption.setMilestone(true);
      
                              Log.d(TAG, "Move To");
      
                              moveAction = robotPlatform.moveTo(new Location(x, y, 0), moveOption, 0);
      //                        action.waitUntilDone();
                          } catch (Exception e) {
                              e.printStackTrace();
                          }
                      }
                  }
              });

      自主规划路径使用moveTo接口,接口中传入目标位置坐标点,其代码位于“到这里”按键的点击监控函数中

  2. 实时更新底盘状态和地图

    实时更新是Android Runable多线程类实现的,Runable对象中的update方法 每100毫秒执行一次

    private Handler handler = new Handler();
    private Runnable runnable = new Runnable() {
        public void run() {
            this.update();
            handler.postDelayed(this, 100);// 间隔100ms
        }
    	...
    }

    以下是update中的刷新底盘Pose和剩余电量的代码。

    void update() {
        try {
            /* 刷新Pose */
            Pose pose = robotPlatform.getPose();
            current_location_x.setText(Float.toString(pose.getX()));
            current_location_y.setText(Float.toString(pose.getY()));
            current_location_yaw.setText(Float.toString(pose.getYaw()));
    
            /* 刷新电量 */
            int percentage = robotPlatform.getBatteryPercentage();
            current_battery_percentage.setText(Integer.toString(percentage));
    		...
    	} catch(Exception e) {
    	    ...
    	}
    }

    地图绘制是通过BitmapDrawable方法完成的,其使用getMa接口,从Slamcode获得实时的地图数据,该数据是像素点的原始数据(raw data),不存在任何图片格式信息,为了显示在界面上,将其封装成Bitmap ARGB_8888图像,之后显示在图形界面上。

    /* 获取地图并刷新 */
    int mapWidth =0;
    int mapHeight = 0;
    
    RectF knownArea = robotPlatform.getKnownArea(MapType.BITMAP_8BIT, MapKind.EXPLORE_MAP);
    map = robotPlatform.getMap(MapType.BITMAP_8BIT, MapKind.EXPLORE_MAP, knownArea);
    mapWidth = map.getDimension().getWidth();
    mapHeight = map.getDimension().getHeight();
    
    Bitmap bitmap = Bitmap.createBitmap(mapWidth, mapHeight, ARGB_8888);
    
    for (int posY = 0; posY < mapHeight; ++posY) {
        for (int posX = 0; posX < mapWidth; ++posX) {
            // get map pixel
            byte[] data = map.getData();
    
            // (-128, 127) to (0, 255)
    
            int rawColor = data[posX + posY * mapWidth];
    
            rawColor += 128;
    
            // fill the bitmap data, by data of B/G/R/A
            bitmap.setPixel(posX, posY, rawColor | rawColor<<8 | rawColor<<16 | 0xC0<<24);
        }
    }
    
    BitmapDrawable bmpDraw=new BitmapDrawable(bitmap);
    
    imageView.setImageDrawable(bmpDraw);

    上面的代码仅用于演示如何操作地图数据并将其显示。

    上述代码可能存在性能较低的问题,包括但不限于每次均重画所有像素数据,如果地图面积很大,会导致刷新速度较慢;每100毫秒重画一次地图,如果BitmapDrawable自身所需时间大于100ms,会造成严重的显示问题,等等。

    建议在实际编写应用程序时,需认真考虑地图绘制的时效性以及稳定性。

进一步阅读

3-例程-Slamware

6.1-常见问题列表

5-应用笔记