Page tree
Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 11 Next »



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

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

此参考设计尽量去除了与SDK无关的技术细节,例如使用最简单的MFC对话框程序,使用基于定时器的地图绘制,没有引入多线程机制等。

其设计目标是

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

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

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

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



本页内容



参考例程下载

SDK_Ref_win32.7z


运行环境准备

  • 软件平台

    • Visual Studio 2010  SP1
    • Slamware Windows SDK:Slamware Windows SDK
    • RoboStudio(用于交叉验证):Robostudio installer 
    • Sample Code: 

      使用更高版本的Visual Studio可能会带来编译异常。

      使用Visual Studio 2010(无SP1)可能会因为无法与.Net Framework兼容而报编译错误,此时增加SP1更新包即可解决问题

  • 硬件平台

          (以下任选其一)

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

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

编译运行

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

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

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

    编译过程中的warning可以忽略

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


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

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

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

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

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

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

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

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

程序说明

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

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

      void CSDK_Ref_win32Dlg::OnBnClickedBtnForward()
      {
      	// TODO: Add your control notification handler code here
      	try{
      		rpos::core::Direction direction(rpos::core::ACTION_DIRECTION::FORWARD);
      		moveAction = robot.moveBy(direction);
      	}catch(const ConnectionFailException &e) 
      	{
      		MessageBox(_T("连接异常,程序关闭"), _T("错误"), MB_OK);
      		CDialogEx::OnCancel();
          } catch(const RequestTimeOutException &e) 
      	{
      		MessageBox(_T("请求超时,请重试"), _T("错误"), MB_OK);
          }catch(const std::exception &e)
      	{
      		MessageBox(_T("发生异常,程序关闭"), _T("错误"), MB_OK);
      	}
      }
    2. 自主规划路径行走
      自主规划路径使用moveTo接口,接口中传入目标位置坐标点,其代码位于“到这里”按键的点击响应函数中

      void CSDK_Ref_win32Dlg::OnBnClickedBtnMove()
      {
      	// TODO: Add your control notification handler code here
      	double _x = _tstof(m_dest_x);
      	double _y = _tstof(m_dest_y);
      
      
      	try{
      		rpos::core::Location loc(_x, _y);
      		moveAction = robot.moveTo(loc, false, true);
      	}catch(const ConnectionFailException &e) 
      	{
      		MessageBox(_T("连接异常,程序关闭"), _T("错误"), MB_OK);
      		CDialogEx::OnCancel();
          }catch(const std::exception &e)
      	{
      		MessageBox(_T("发生异常,程序关闭"), _T("错误"), MB_OK);
              CDialogEx::OnCancel();
      	}
      }
  2. 实时更新底盘状态和地图

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

    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的代码。通过判断不同定时器的编号,执行不同的操作

    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图像,之后显示在图形界面上。

    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,会造成严重的显示问题,等等。

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

  • No labels