作为一种半自主或完全自助的机器人,服务机器人能够为人类执行有用的服务工作,如搬运、清洁和救援。随着服务机器人逐渐走进人们的社会生活,如今广泛应用于展厅、餐厅、酒店等公共场所,对提高人类生活质量和服务业产生深远影响。
通常,服务机器人配备高精度激光雷达、语音采集模块、运动模块、显示屏、无线连接模块等主要设备。机器人利用激光雷达构建高精度室内地图,语音模块接收语音指令,运动模块将机器人移动到目标位置,无线模块接收控制器远程指令,实现远程智能控制。
服务机器人的运动过程需要经过地图构建、路径规划、速度规划、路障规避等功能,如下图所示。 本文搭建了一个移动机器人demo,采用全向移动底盘,使机器人能够在小范围内完成转向、平移等动作。
远程监控模块:stm32F407 +wifi模式+STONE STVC043WT-01显示
机器人主机控制中心:stm32F707扩展板(移植到系统更自由)
运动控制模块:stm32f407+电机驱动
语音采集模块:讯飞六脉冲语音阵列
激光雷达:士兰科技LPR激光雷达
移动底盘:ROTACASTER 125mm *3 这里我们以一个高端写字楼的单层场景为例。机器人需要定期为大楼内的上班族服务,任务要求机器人通过内在或外在的传感器感知环境和自身的运动状态,并进行从机器人当前位置到目标位置的无碰撞运动根据最佳时间、最短路径或最低能耗的原则。
为了成功执行此任务,首先使用 LIDAR 扫描地板环境,构建室内地图并用坐标指示任务点的位置: 机器人在执行任务时,操作员无法实时了解机器人的状态,也无法及时下达指令。
为了使控制器能够远程访问机器人状态并从控制中心下达控制命令,我使用STM32F407主板通过外接网卡和用于UI设计的石头STVC043WT-01显示器与机器人进行交互。提高操作者的使用效率,设计示意图如下: 本文将重点介绍如何使用石头STVC043WT-01设计一个UI来实时显示服务机器人的状态。在进行石头显示之前,我们需要编写一个简单的测试demo,确保遥控模块接收到机器人的当前状态(机器人ID、目的地、x/y/z电机速度、机器人当前位置坐标等)及时通过无线网络。
下图显示了机器人在原点旋转时产生的机器人状态数据,从数据可以看出,机器人在旋转时x、y、z电机的速度应该是一致的: 石材STVC043WT-01显示器的程序四通提供了一套简单易用的显示器设计工具,通过公司的STV系列显示器,我们可以在极短的时间内完成显示器演示的开发。因此,选择了 Stone STVC043WT-01 显示器来实时监控远程机器的状态。显示器的开发主要分为三个步骤:
1. 设计显示器UI
2. 开发相应的嵌入式主机程序,主要设计
显示器和嵌入式主机的UART通讯调试。 用户界面设计1.新建项目首先,从石头官网下载最新版的TOOL-2019工具。
根据显示参数,创建对应的工程,这里以STVC043WT-01为例。(480*272)。
选择 File -> New,将项目屏幕像素设置为 480*272: 2.插入图片和设计UI右键工程文件->添加插入用其他工具绘制的图片,注意图片大小必须与显示一致,本文使用的图片大小为480×272。 设置跳转链接为首页当前状态: 然后我们用石头工具在返回按钮处添加挑战链接到首页:工具栏->硬件参数配置0,在返回按钮处插入一个窗口并修改名称定义和页面切换使其能够跳转到首页页面通过返回按钮。 在 Robot ID 和 Destination 中插入 text 变量,在 Motor speed 和 current location 中插入 data 变量,如下图所示。需要注意的是,Variable memory address 表示存储数据或文本的地址,word Length 表示可显示字节的最大长度。 3.编译下载完成上述步骤后,检查屏幕配置,确保波特率为 115200,命令处理程序为 0xA5 0x5A。检查完成后,点击编译选项,将文件下载到显示器。 4.调试系统启动时,显示屏显示主页,其中包含六个选项。当我们点击连接选项时,系统会在远程控制台和机器人之间建立无线连接;相反,断开连接将断开无线连接。显示地图选项将显示由机器人创建的室内环境地图;clear state 选项将初始化系统并发送命令中断机器人的任务执行,使其返回起点;当前状态选项显示和更改机器人的状态;并且返回原点选项会提示机器人返回起点。 选择当前状态以检查当前机器人状态。机器人在起点时,显示屏显示机器人状态初始值: 设置好机器人的目标后,机器人开始执行任务,此时显示屏会实时显示机器人的ID、目的地、速度和坐标,如下图,机器人在坐标(174,269)处旋转: 选择返回选项,显示将返回主页。
检查远程控制台发送到显示器的UART数据是否与屏幕显示匹配: A5 5A 0b 82 01 20 53 48 2D 4B 4C 2D 30 31 //机器人 ID:SH-KL_01
A5 5A 09 82 01 9B 6F 66 66 69 63 65 //目的地:办公室
A5 5A 05 82 01 14 0f 09 //电机x 速度 3849 r/min
A5 5A 05 82 01 18 0f 09 //电机 y 速度 3849 r/min
A5 5A 05 82 01 1a 0f 09 //电机 z 速度 3849 r/min
A5 5A 05 82 01 1c 00 ae //坐标轴 X :174
A5 5A 05 82 01 1e 01 0d //坐标轴 X :269 5.部分代码分享 Serial_debug.CPP: #include
#include "serial_debug.h"
using namespace std;
int debug_wait() {
#if DEBUG
cout << "press any key to contine" << endl;
getchar();
#endif
return 0;
}
bool creat_serial(HANDLE &hComm,string strPort) {
//Second, for the serial port number greater than 9 (for example, COM12), the serial port name in the CreateFile() function should be written "\. \COM12"。
// hComm = CreateFile(TEXT("com6"), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
hComm = CreateFile(strPort.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hComm == INVALID_HANDLE_VALUE) {
cout << "Fail to open Serial Port.!!!" << endl;
debug_wait();
return FALSE;
}
else {
cout << "Success to open Serial Port. " << endl;
debug_wait();
return TRUE;
}
}
bool close_serial(HANDLE &hComm) {
CloseHandle(hComm);
return TRUE;
}
bool set_serial(HANDLE &hComm)
{
DCB dcb;
GetCommState(hComm, &dcb);
dcb.BaudRate = CBR_115200;
dcb.ByteSize = 8;
dcb.StopBits = ONESTOPBIT;
SetCommState(hComm, &dcb);
return TRUE;
}
bool WriteSerial(char* btData, ULONG ulSize, HANDLE hSerial)
{
DWORD dwNumBytes, dwRet;
dwRet = WriteFile(hSerial, //Handle
btData, //Data buffer
ulSize, //Buffer size
&dwNumBytes, //written bytes
NULL); //don't support
return (dwRet != 0);
};
int serial_process()
{
HANDLE hComm;
string portname = "com6";
bool ret = FALSE;
string buff = "serial opened";
char c[20]="";//A5 5A 03 81 00 01
// strcpy_s(c, buff.c_str());
ret=creat_serial(hComm, portname);
if (!ret)
return 0;
set_serial(hComm);
if (!WriteSerial(c, 7, hComm))//Send data hello to com1
printf("ERROR!!");
wait_for_end();// pending until uart is not need.
close_serial(hComm);
return 0;
}
|