...
打开参考工程文件,右键打开属性选项,将Slamware SDK 的include目录和lib目录添加到工程,然后编译工程
Slamware SDK的include和lib目录无需复制到参考例程目录,只需在Visual Studio里指定路径即可。
如果不指定此两个路径,编译中会报找不到.h文件,或者找不到库函数等错误。
编译过程中的warning可以忽略
连接到底盘wifi(一般以SLAMWARE开头,后面跟着6位MAC地址)
运行参考程序,会弹出如下对话框,点击连接
运行参考程序,会弹出如下对话框,点击“连接机器人”对于wifi连接,AP模式下,底盘默认IP地址为192.168.11.1,如果改为Station模式,则底盘的IP地址由底盘所连接的路由器动态分配,或者由用户指定,此时需使用此IP地址
对于有线网络连接,IP地址默认为192.168.11.1,不可修改。
- 在如下对话框里,左侧是地图显示,随着底盘的移动,地图内容会实时更新。右侧是控制区和状态显示区。
操作指南点击向前,向后,左转,右转,底盘会相应运动,点击停止运动,底盘立即停止
每点击一次,底盘只运动一小段时间,如果需要保持运动,需要不停点击。例如,如果希望底盘持续前进,需要持续点击向前按钮,一旦停止点击,底盘很快即停止运动
通过此种方式控制底盘运动时,底盘处于遥控控制模式,底盘不会自动避障,故需留意周围环境,避免撞到人或物。
- 输入X,Y坐标后,点击到这去按键,底盘会运动到给定的目标点。X,Y坐标点的获得,可以根据当前位置显示的坐标记录
- 最上方是当前位置和机器人状态的实时显示,其中每一次底盘运动,运动状态都会相应改变;每次底盘位置或是朝向角发生改变,当前位置区域也会发生变化点击退出按键可以退出程序
程序说明
程序主要分成两个功能模块
- 控制底盘行走
遥控控制底盘行走
遥控底盘需要使用moveBy接口,接口中传入动作类型指令,其代码主要位于每个按键的触摸响应函数中,以“向前”按键为例123456789101112131415161718// 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);自主规划路径行走
自主规划路径使用moveTo接口,接口中传入目标位置坐标点,其代码位于“到这里”按键的点击响应函数中1234567891011121314151617181920void
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();
}
}
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);
moveAction = robotPlatform.moveTo(new Location(x, y, 0), moveOption, 0);
// action.waitUntilDone();
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
实时更新底盘状态和地图
实时更新是基于两个定时超时中断的定时器,其中,更新地图的定时器每33毫秒超时中断一次,底盘状态信息的定时器每100毫秒超时中断一次,这两个定时器都在程序初始化时设定
123456789101112BOOL
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的代码。通过判断不同定时器的编号,执行不同的操作
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071void
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图像,之后显示在图形界面上。
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849void
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,会造成严重的显示问题,等等。
建议在实际编写应用程序时,需认真考虑地图绘制的时效性以及稳定性。
...