一、问题提出
在多移动机器人协作系统项目中有一个很重要的环节就是协作系统环境地图数据库的建立。目前移动机器人工作环境地图表示方式有三种:拓扑图、特征图和栅格图。栅格表示法是目前最常用的环境建模方法,特别适合于将来自于不同类型的传感器(如超声波、红外、视觉等)的数据进行统一描述,且对于超声波这样易产生不精确测量数据的传感器具有较强的适应力,所以本文中涉及的系统采用的是栅格地图表示方式。
栅格表示法(grid map)就是在世界坐标下,将机器人的作业空间分成若干个栅格,利用传感器测得的信息,确定障碍物位置并映射到相应的栅格上。每个栅格具有一个可信度值(Certainty Value),用于表示在该区域内障碍物分布状况[1]。
多移动机器人系统中包括了水陆两栖机器人、野外移动机器人和城市机器人,工作环境复杂多变,为了演示多移动机器人协作系统的协作功能,我们选定了北京航空航天大学的求是广场,该广场内有树木、草地和石凳等,可以保证所有的机器人都能在其中运动。
整个移动机器人系统采用分层控制结构,有一台服务器用于所有机器人的全局路径规划,然后每个机器人对应的控制计算机用于局部路径规划和导航控制,这种控制结构降低了全局路径规划的难度,保证了机器人系统即使在全局地图比较粗糙的情况下仍然能够很好地工作。基于以上的设计,决定采用人工实测的地图作为全局路径规划的地图。
人工测量的场地大小为130米长、88米宽,采用AutoCAD绘制整个地图。由于AutoCAD绘制的图纸是矢量图,提供的是地图中物体的形状信息,而协作系统软件需要的栅格地图提供的是障碍物的位置信息,不能直接使用AutoCAD工程图作为地图,所以需要进行地图格式转换。
一般情况下将整个位图进行整体扫描,获取每个像素是否对应障碍物的信息,然后再对扫描后获得的信息进行整合处理,分配给每一个栅格。而本文介绍了一种首先对位图进行分块然后再扫描提取地图数据并填充到SQL Server的解决方案,并利用Visual C++ 6.0开发工具编程进行了实现。
二、解决方案
步骤1:在AutoCAD中执行export命令,选择输出格式为位图(*.bmp),然后在视图中选择要输出的区域,则选择区域部分将转化为Windows位图文件。由于位图文件是以像素来记录信息的,位图的像素数越大,所记录的信息就越多,所以尽量将图像放大将增加获取的信息。我们将130×88米的地图放大到了接近于屏幕大小,最终输出的位图像素大小为905×614。
步骤2:利用Visual C++编写的程序算法将位图读取到内存中进行分块。
步骤3:对每小块位图(该小块将作为一个地图栅格)进行扫描,获取图像中是否有障碍物的信息。
步骤4:将扫描的小块位图的障碍物信息与该小块位图在整个大块位图中的位置一起写入到SQL Server数据库中。
三、分块扫描位图的方法
1、位图分块及扫描逻辑过程简介
对位图的分块处理是通过Windows GDI编程来实现的,GDI的所有操作都是在DC(设备上下文)上进行的。CDC是设备上下文的基类,除了一般的窗口显示外,还用于基于桌面的全屏幕绘制和非屏幕显示的打印机输出。CDC类封装了所有图形输出函数,包括矢量、光栅和文本输出。
在Visual C++6.0中处理位图文件必须首先将位图文件读取到内存中,然后在内存中利用CDC类成员函数对位图进行处理。整个位图处理的逻辑过程如图1所示。

实际地图是130×88米的,地图分块以接近于机器人的尺寸为依据,机器人本身是55×44厘米的,所以选择0.5米作为栅格的边长,将地图初步分为260×176个栅格。位图像素大小是905×614,将其按照260×176个栅格进行划分,划分结果使像素数为一个非整数值3.x,将该数转换为整数值后为3,按照原来的栅格数处理的位图像素数为780×528,与原位图的像素数有一定的差距,为减少这种差距,程序中设定了自动增加栅格数的算法,经过调整后,可以使最终没有处理的像素数在长度和宽度方向上都小于每个栅格的像素数值。该处理方法适合于各种大小的位图。
2、图像分块处理代码
图像分块处理采用了分为两步来完成,首先将存储在硬盘上的bmp文件读取到内存中,然后对内存中的图像进行处理。
将图像文件读取到内存中利用WIN32 API编程实现,原理是首先打开文件,然后获取文件大小,根据文件大小分配相应的内存,最后读文件并将其存储到内存中。代码如下(因为篇幅限制,删除了部分无关紧要的代码):
void LoadPictureFile(HDC hdc, LPCTSTR szFile, CBitmap* pBitmap, CSize&
mSize)
// 打开文件
HANDLE hFile = CreateFile(szFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
// 获取文件大小
DWORD dwFileSize = GetFileSize(hFile, NULL);
LPVOID pvData = NULL;
// 根据文件大小分配内存
HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, dwFileSize);
pvData = GlobalLock(hGlobal);
DWORD dwBytesRead = 0;
// 读文件并将其存储到全局内存中
BOOL bRead = ReadFile(hFile, pvData, dwFileSize, &dwBytesRead, NULL);
GlobalUnlock(hGlobal);
CloseHandle(hFile);
LPSTREAM pstm = NULL;
// 从全局内存创建IStream*
HRESULT hr = CreateStreamOnHGlobal(hGlobal, TRUE, &pstm);
// 从图像文件创建IPicture
LPPICTURE gpPicture;
hr = ::OleLoadPicture(pstm, dwFileSize, FALSE, IID_IPicture, (LPVOID *)&gpPicture);
pstm->Release();
OLE_HANDLE m_picHandle;
gpPicture->get_Handle(&m_picHandle);
pBitmap->DeleteObject();
pBitmap->Attach((HGDIOBJ) m_picHandle);
BITMAP bm;
GetObject(pBitmap->m_hObject, sizeof(bm), &bm);
}
在内存中对位图进行分块是通过CDC类的StretchBlt函数实现的,原理是根据地图栅格的大小从整个位图中相应位置读取一部分位图作为一个新的位图对象,代码如下:
void CopyBitmap(CDC* dc, CBitmap& mRes, const CBitmap& hbmp, RECT r)
{
if(!hbmp.m_hObject) return;
int w = r.right - r.left, h = r.bottom - r.top;
CDC memdc, hDC
hDC.CreateCompatibleDC(dc);
hDC.SelectObject((HBITMAP) mRes);
memdc.CreateCompatibleDC(dc);
memdc.SelectObject(hbmp);
hDC.StretchBlt(0, 0, w, h, &memdc, r.left, r.top, w, h, SRCCOPY);
hDC.DeleteDC();
memdc.DeleteDC();
}
四、填充SQL Server数据库的方法
多移动机器人协作系统地图数据库利用SQL Server创建,创建过程是:首先使用企业管理器创建一个MapInfo数据库,然后在其中建立一个Map_Result表,在该表中设置POX、POY、OCCUPATION、GRIDID四个字段。POX和POY分别表示栅格所在的行和列;OCCUPATION表示栅格中是否有障碍物;GRIDID表示填充的是第几个栅格,将其设为主键用于确保填充数据的顺序正确。
在该项目中,对数据库编程采用ODBC API实现,其数据库编程的基本步骤如下:
⑴ 为ODBC分配环境句柄。
⑵ 分配一个连接句柄。
⑶ 连接到数据库。
⑷ 为SQL命令分配一个语句句柄。
⑸ 传送该命令。
⑹ 关闭连接。
⑺ 解除连接并释放环境句柄。
程序对OCBC API进行了进一步的封装,这里主要介绍使用SQL语句进行访问的代码:
BOOL CODBCDatabase::Execute(CHAR *szSqlStr)
{
SQLRETURN ret;
SQLHSTMT hStmt = NULL;
SQLINTEGER nRowCount;
//分配一个语句句柄
SQLAllocHandle(SQL_HANDLE_STMT, m_hDbc, &hStmt);
ret = SQLExecDirect(hStmt, (SQLCHAR*)szSqlStr, SQL_NTS);
//向数据源传送一个SQL语句
SQLRowCount(hStmt, &nRowCount);
m_nRowsAffected = nRowCount;
//释放语句句柄
SQLFreeHandle(SQL_HANDLE_STMT,hStmt); return ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO;
}
需要特别注意的是,执行一个语句要及时释放语句句柄,否则将要引起内存泄漏。
五、结论
本文提出了读取位图文件,分块进行扫描并将扫描结果填入数据库的方法,给出源程序的示例。经过实验验证,方法可行,扫描图像迅速准确,很好的实现了将工程图转换为栅格地图数据的任务,为多移动机器人协作系统运行奠定了基础。
参考文献:
[1] 洪伟, 田彦涛, 董再励, 周淼磊.




