Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

SDK_Reference_Android.7z

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

...


...


参考例程下载

View filenameSDK_Reference_Android.7zheight250Android Reference Application

运行环境准备

  • 软件平台

    • Android Studio
    • Slamware Android SDK: 
      View file
      nameandroid_sdk_2.6_20180803_1146.7z
      height250
      Android SDK
    • RoboStudio(用于交叉验证):Robostudio installer 
    • Sample Code: 


      使用不同版本版本的Android Studio可能会带来编译异常。使用Android Studio 2010(无SP1)可能会因为无法与.Net Framework兼容而报编译错误,此时增加SP1更新包即可解决问题

  • 硬件平台

          (以下任选其一)

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


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

编译运行

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

    Image Added

    打开参考工程文件,右键打开属性选项,将Slamware SDK 的include目录和lib目录添加到工程,然后编译工程

    Image Removed

    Slamware SDK的include和lib目录无需复制到参考例程目录,只需在Visual Studio里指定路径即可。

    如果不指定此两个路径,编译中会报找不到.h文件,或者找不到库函数等错误。

    编译过程中的warning可以忽略


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

  3. 运行参考程序,会弹出如下对话框,点击“连接机器人”

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

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

    Image Added

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

    操作指南
    1. 点击向前,向后,左转,右转,底盘会相应运动,点击停止运动,底盘立即停止


      每点击一次,底盘只运动一小段时间,如果需要保持运动,需要不停点击。例如,如果希望底盘持续前进,需要持续点击向前按钮,一旦停止点击,底盘很快即停止运动


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

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

...

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


      1
      Code Block
      2
      language
      3
      java
      4
      linenumbers
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      true
      // 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. 自主规划路径行走自主规划路径使用moveTo接口,接口中传入目标位置坐标点,其代码位于“到这里”按键的点击响应函数中

      Code Block
      1
      linenumbers
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      true
             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. 实时更新底盘状态和地图

    实时更新是基于两个定时超时中断的定时器,其中,更新地图的定时器每33毫秒超时中断一次,底盘状态信息的定时器每100毫秒超时中断一次,这两个定时器都在程序初始化时设定


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    BOOL CSDK_Ref_win32Dlg::OnInitDialog()
    {
        CDialogEx::OnInitDialog();
        ...... 
     
        // set timer#1 with interval 100 milisecond for status update
        SetTimer(1, 100, NULL);
        // set timer@4 with interval 33 milisecond for map update
        SetTimer(4, 33, NULL);
     
        ......
    }


    定时器超时后,会进入OnTimer函数中做相应处理,以下是OnTimer的代码。通过判断不同定时器的编号,执行不同的操作


    1
    Code Block
    2
    firstline
    3
    1
    4
    linenumbers
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    true
    /* 获取地图并刷新 */
    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 += 127;
    
            // fill the bitmap data
            bitmap.setPixel(posX, posY, rawColor | rawColor<<8 | rawColor<<16 | 0xC0<<24);
    
        }
    }
    
    BitmapDrawable bmpDraw=new BitmapDrawable(bitmap);
    
    imageView.setImageDrawable(bmpDraw);
    void CSDK_Ref_win32Dlg::OnTimer(UINT_PTR nIDEvent)
    {
        if(nIDEvent == 1)
        {
            // TODO: Add your message handler code here and/or call default
            UpdateData(TRUE);
            try{
                // update current pose (position + heading angle)
                rpos::core::Pose pose = robot.getPose();
                m_info_x = pose.x();
                m_info_y = pose.y();
                m_info_yaw = pose.yaw();
     
                // update battery status
                m_info_battery = robot.getBatteryPercentage();
     
                // update action status
                if(moveAction.isEmpty())
                    m_info_action_status = "Idle";
                else
                {
                    switch(moveAction.getStatus())
                    {
                    case ActionStatusWaitingForStart:
                        m_info_action_status = "Waiting For Start";
                        break;
     
                    case ActionStatusRunning:
                        m_info_action_status = "Running";
                        break;
     
                    case ActionStatusFinished:
                        m_info_action_status = "Finished";
                        break;
     
                    case ActionStatusPaused:
                        m_info_action_status = "Paused";
                        break;
     
                    case ActionStatusStopped:
                        m_info_action_status = "Stopped";
                        break;
     
                    case ActionStatusError:
                        m_info_action_status = "Error";
                        break;
     
                    default:
                        m_info_action_status = "Error";
                        break;
                    }
                }
            }catch(const ConnectionFailException &e)
            {
                MessageBox(_T("连接异常,程序关闭"), _T("错误"), MB_OK);
                CDialogEx::OnCancel();
            }catch(const std::exception &e)
            {
                MessageBox(_T("发生异常,程序关闭"), _T("错误"), MB_OK);
                CDialogEx::OnCancel();
            }
            // update the display
            UpdateData(FALSE);
        }
        else
        {
            // update map display
            drawMap();
        }
        CDialogEx::OnTimer(nIDEvent);
    }


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


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    void CSDK_Ref_win32Dlg::drawMap(void)
    {
        BITMAPINFO _mapDesc ;
        DWORD* pData ;
        HBITMAP hBitmap ;
     
        try{
            // get map from slamware
            rpos::core::RectangleF knownArea = robot.getLocationProvider().getKnownArea(MapTypeBitmap8Bit, location_provider::EXPLORERMAP);
            location_provider::Map map = robot.getMap(MapTypeBitmap8Bit, knownArea, rpos::features::location_provider::EXPLORERMAP);
            int mapWidth = map.getMapDimension().x();
            int mapHeight = map.getMapDimension().y();
     
            // initialize the bitmap header structure
            memset(&_mapDesc, 0, sizeof(_mapDesc));                         // clear BITMAPHEADER
            _mapDesc.bmiHeader.biSize = sizeof(_mapDesc);                   // size of BITMAPHEADER
            _mapDesc.bmiHeader.biWidth = mapWidth;                          // width by pixels
            _mapDesc.bmiHeader.biHeight = mapHeight;                        // height by pixels
            _mapDesc.bmiHeader.biPlanes = 1;                                // 1 plane
            _mapDesc.bmiHeader.biBitCount = 32;                             // 24bit Color
            _mapDesc.bmiHeader.biCompression = BI_RGB;                      // no compression
            _mapDesc.bmiHeader.biSizeImage = mapHeight * mapWidth;          // size by pixels
     
            // create DIB image to fill the map data
            hBitmap = CreateDIBSection (::GetDC(NULL), (BITMAPINFO *) &_mapDesc, DIB_RGB_COLORS, (void**)&pData, NULL, 0);
            // fill the map data
            for (int posY = 0; posY < mapHeight; ++posY)
            {
                for (int posX = 0; posX < mapWidth; ++posX)
                {           
                    // get map pixel
                    rpos::system::types::_u8 mapValue_8bit = map.getMapData()[posX + posY * mapWidth] + 127;
                    // fill the bitmap data
                    pData[posX + posY*mapWidth] = RGB(mapValue_8bit,mapValue_8bit,mapValue_8bit);
                }
            }
            // update bitmap map file to picture control component
            m_pic_map.SetBitmap(hBitmap);
     
        }catch(const ConnectionFailException &e)
        {
            MessageBox(_T("连接异常,程序关闭"), _T("错误"), MB_OK);
            CDialogEx::OnCancel();
        }catch(const std::exception &e)
        {
            MessageBox(_T("发生异常,程序关闭"), _T("错误"), MB_OK);
            CDialogEx::OnCancel();
        }
    }



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

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

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

...