更新时间:2025-05-13 10:39:33下载pdf
本文详细介绍基于 tuyaos_robot_rvc_sdk
激光扫地机的实时地图与路径传输的业务流程、API 及地图坐标系说明。
对于激光扫地机,目前支持三种类型的文件:
缩略图数据及原始高清图片属于 AI 物体识别的能力,需要扫地机硬件上带有摄像头功能。开发者根据硬件条件,可在业务上选配。
TuyaOS SDK
的状态回调 ty_rvc_event_cb
,状态只有两种:开始 和 结束。aiHD_XXXX_YYYY.bin
,通过 TuyaOS SDK
传输到应用端。其中 XXXX_YYYY
是真实的物体坐标点,应用上根据真实的坐标点获取正确的原始高清图片数据,并调用发送原始高清图片数据接口,上报给 App 。/**
* @brief Structure for coordinate points
*/
typedef struct {
int x; // X value of the coordinate point
int y; // Y value of the coordinate point
} OBJECT_POINT_S;
/**
* @brief Names of recognized objects
*/
typedef enum {
OBJECTS_TYPE_WIRE = 0, // Wire
OBJECTS_TYPE_SHOES, // Shoes
OBJECTS_TYPE_SOCKS, // Socks
OBJECTS_TYPE_TOYS, // Toys
OBJECTS_TYPE_CHAIR, // Chair
OBJECTS_TYPE_TABLE, // Table
OBJECTS_TYPE_TRASH_CAN, // Trash can
OBJECTS_TYPE_POTTED_PLANT, // Potted plant
OBJECTS_TYPE_OTHER, // Other
OBJECTS_TYPE_MAX,
} AI_OBJECTS_TYPE_E;
物体类型 TuyaOS SDK
只定义了标准的几种方式,开发者如有新增的物体类型,需要在 OBJECTS_TYPE_OTHER
之后增加,并且需要与面板端的物体类型定义对齐。
/**
* @brief thumbnail param structure
*/
typedef struct {
OBJECT_POINT_S points; // Points of objects
AI_OBJECTS_TYPE_E object_type; // Recognized objects
unsigned char accuracy; // Recognition accuracy, report FF if not used
} OBJECT_THUMBNAILS_PARAM_S;
/**
* @brief AI thumbnail data structure
*/
typedef struct {
int map_id; // Map ID
int object_num; // Number of objects
OBJECT_THUMBNAILS_PARAM_S* param; // Points of objects,type and accuracy
} RVC_AI_OBJECT_THUMBNAILS_INFO_S;
RVC_AI_OBJECT_THUMBNAILS_INFO_S
结构体用来存放一张地图上所有物体的坐标、类型及准确率,OBJECT_THUMBNAILS_PARAM_S
结构体存放的是一个物体的坐标、类型及准确率。
/**
* @brief Parameters for the map protocol header of the vacuum cleaner
*/
typedef struct {
unsigned short map_id; // Map ID
int status; // Map status, 0 indicates unstable, 1 indicates stable
int height; // Height of the map in pixels
int width; // Width of the map in pixels
int resolution; // Map resolution (width/length of each grid), reported as original value, unit: meters
POINT_COOR_S origin; // Machine origin X/Y coordinates, given relative to the screen coordinate system, reported as original value
POINT_COOR_S charge_point; // Charging station X/Y coordinates, given relative to the screen coordinate system, reported as original value
int charge_angle; // Orientation of the charging station
} RVC_MAP_HEAD_INFO_S;
RVC_MAP_HEAD_INFO_S
是地图数据 RVC_MAP_DATA_S
结构体中的成员,表示扫地机地图数据的头部参数。
typedef struct {
int type; //path point type
POINT_COOR_S coordinates; //Path coordinate array, given with reference to the machine coordinate system
} PATH_POINT_S;
/**
* @brief Parameters for the path protocol of the vacuum cleaner
*/
typedef struct {
unsigned short path_id; // path ID Roadmap identification for a complete sweep
int type; //0x01 full path (normal mode) ,0x02 full path (complex mode), 0x03 Navigation path
int count; // Total number of waypoints (full)
int direction; // Device Heading Angle
PATH_POINT_S*path_point; //Path coordinate array, given with reference to the machine coordinate system, the original value is reported
} RVC_PATH_INFO_S;
RVC_PATH_INFO_S
结构体是地图路径数据,PATH_POINT_S
成员信息请参考 地图及路径数据结构说明。
/**
* @brief Pixel Point Types
*/
typedef enum {
RVC_PIXEL_BLANK=0, //Free area, laser scanning points of unpartitioned rooms.
RVC_PIXEL_OBS, // Obstacle
RVC_PIXEL_CARPET, // Carpet area
RVC_PIXEL_UNKNOW, // Unknown area, the background area of the map rectangular canvas.
RVC_PIXEL_MAX, // Pad data, no use
} RVC_PIXEL_TYPE_E;
RVC_PIXEL_TYPE_E
是地图中像素点的枚举值。特别需要注意是,在地图未稳定的情况下,激光扫描到非障碍物的点,都要使用RVC_PIXEL_BLANK
类型上报。在地图已经稳定的情况下,激光扫描到未分区的房间中非障碍物的点,也要使用RVC_PIXEL_BLANK
类型上报;扫描到已经分区的房间中的非障碍物的点使用RVC_PIXEL_UNKNOW
类型上报。
/**
* @brief Point in the corresponding coordinate system
*/
typedef struct {
float x; // X-axis coordinate in the corresponding coordinate system, unit: meters
float y; // Y-axis coordinate in the corresponding coordinate system, unit: meters
float z; // Z-axis coordinate in the corresponding coordinate system, unit: meters
}RVC_POINT_S;
/**
* @brief Polygon parameters
*/
typedef struct {
RVC_POINT_S* points; // Polygon points
int points_size; // Number of points
}ROOM_POLYGON_S;
/**
* @brief Area polygon parameters
*/
typedef struct {
int id; // Area ID
ROOM_POLYGON_S polygon; // Polygon
} RVC_ROOM_POLYGON_S;
RVC_ROOM_POLYGON_S
结构体用来存放多房间边界栅格坐标点信息,需要申请结构体的内存空间。注意该结构体内的 polygon
成员是个指针,需要给该指针对象赋值。
/**
* @brief map data structer
*/
typedef struct{
RVC_MAP_HEAD_INFO_S header; //Parameters for the map protocol header of the vacuum cleaner
unsigned char** map_buff; //Pixel data on the map
int room_polygons_count; //Number of Room Zones
RVC_ROOM_POLYGON_S* room_polygons_data; //Area polygon parameters
}RVC_MAP_DATA_S;
RVC_MAP_DATA_S
结构体是用来存放一张地图数据,结构体成员包含地图开头固定数据、像素点数据、房间分区数量及边界栅格坐标点数据。
用于流媒体服务初始化。只有调用了该接口,才能正常的进行实时地图及路径的传输。
/**
* @brief real time map and path trans cb
*/
typedef INT_T (*RVC_TRANS_EVENT_CB)(IN CONST int onoff);
/***********************************************************
*@Function: tuya_sdk_media_server_init
*@brief initialize media server
*@param[in] handler The status callback for real-time data transmission,
with statuses including: start and end.
*@return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
***********************************************************/
OPERATE_RET ty_rvc_server_init(RVC_TRANS_EVENT_CB handler);
参数说明
参数 | 说明 |
---|---|
handler | 实时地图及路径传输状态,开发者可根据状态值来判断是否开始上报数据 |
将实时地图结构化数据发送给 App 端显示,详细请参照 设备角度说明。
/***********************************************************
*@function: ty_rvc_rt_map_send_v2
*@brief: Send real-time structured map data to the app
*@param[in] map_info: map data, including fixed header data, pixel data,
the number of room zones, and boundary grid coordinate point data.
*@return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
** *********************************************************/
OPERATE_RET ty_rvc_rt_map_send_v2(RVC_MAP_DATA_S* map_info)
参数说明
参数 | 说明 |
---|---|
map_info | 地图数据,具体请参考 RVC_MAP_DATA_S 结构体 |
针对以上入参,详细请参照 地图及路径数据结构说明。
当开发者需要清空 App 端显示的地图时,调用该接口上报空地图。关于空地图的格式,在 RVC_MAP_DATA_S
结构体中:
header
成员赋值 0
map_buff
成员赋值 NULL
room_polygons_data
成员赋值 NULL
room_polygons_count
成员赋值 0
将实时路径结构化数据发送给 App 端显示。
/***********************************************************
*@function: ty_rvc_rt_cleanpath_send
*@brief send realtime cleanpath data to app
*@param[in] path_info: realtime path data which is need to be upload
*@return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
***********************************************************/
OPERATE_RET ty_rvc_rt_cleanpath_send_v2(IN RVC_PATH_INFO_S* path_info);
参数说明
参数 | 说明 |
---|---|
path_info | 实时路径数据的内容 ,详细请参照 地图及路径数据结构说明。 |
当开发者需要清空 App 端显示的路径时,调用该接口上报空路径。关于空路径的格式,在 RVC_PATH_INFO_S
结构体中,type
路径类型填普通模式,即 1
,其余都填 0
即可。
将 AI 缩略图文件数据发送给 App。 开发者根据 RVC_AI_OBJECT_THUMBNAILS_INFO_S
结构体的要求,将数据传给 TuyaOS SDK
即可,TuyaOS SDK
内部会对数据进行转化之后传输给 App。
/***********************************************************
*@function: ty_rvc_rt_ai_thumbnails_send
*@brief send realtime ai thumbnails to app
*@param[in] object_info:ai object thumbnails param
*@return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
***********************************************************/
OPERATE_RET ty_rvc_rt_ai_thumbnails_send(IN RVC_AI_OBJECT_THUMBNAILS_INFO_S* object_info);
参数说明
参数 | 说明 |
---|---|
object_info | 缩略图数据内容 |
用于注册 AI 原始高清图文件名获取回调。开发者根据 App 下发的文件名,通过 sets_handler
回调通知到业务端,业务端根据文件名获取对应的原始高清图片数据。
/**
* @brief AI 高清图片数据回调,App 下发的 AI 高清图片文件名称 `ai_name`
*/
typedef INT_T (*RVC_AI_DATA_LOAD_CB)(CHAR_T *ai_name);
/***********************************************************
*@function: ty_rvc_ai_original_init
*@brief Callback function for get aiHD name
*@param[in] sets_handler: set unpload aiHD name cb
*@return OPERATE_RET: 0 for success, other error codes indicate failure
***********************************************************/
OPERATE_RET ty_rvc_ai_original_init(RVC_AI_DATA_LOAD_CB sets_handler);
将原始高清图片数据发送给 App。开发者根据 App 下发的文件名,通过 sets_handler
回调通知到业务端,业务端根据文件名获取对应的原始高清图片数据,传回 TuyaOS SDK
发送出去。开发者需要将原始高清图片的数据内容、长度及图片格式传给 TuyaOS SDK
,TuyaOS SDK
内部会对数据进行转化之后传输给 App。
/***********************************************************
*@Function: ty_rvc_ai_original_send
*@brief send ai original to app
*@param[in] file_type: ai original types include jpg, png, gif, svg, bmp, ico
*@param[in] file_buff: ai original buff
*@param[in] file_len: ai original len
*@return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
***********************************************************/
OPERATE_RET ty_rvc_ai_original_send(IN CHAR_T* file_type, IN CHAR_T* file_buff, IN INT_T file_len);
参数说明
参数 | 说明 |
---|---|
file_type | 高清图片类型目前支持以下六种方式: jpg、png、gif、svg、bmp、ico。如需支持其他的图片类型,请找涂鸦项目经理咨询 |
file_buff | 原始高清图片数据的内容 |
file_len | 原始高清图片数据的长度 |
地图数据 RVC_MAP_DATA_S
由以下几类组成:
地图固定的头部数据,是由 RVC_MAP_HEAD_INFO_S
结构体成员组成。
序号 | 字段 | 数据类型 | 说明 |
---|---|---|---|
1 | map_id | UINT16_T |
|
2 | status | UINT32_T | 用于 App 控制面板 UI 交互使用:
|
3 | height | UINT32_T | 地图像素高度,如当前无地图,该参数则为 0 |
4 | width | UINT32_T | 地图像素宽度,如当前无地图,该参数则为 0 |
5 | resolution | UINT32_T | 地图分辨率(每个格子的宽长度),原始值上报,单位:厘米 |
6 | origin | UINT32_T | 机器原点 X/Y 坐标,参照屏幕坐标系给出,原始值上报,详细请参照 地图坐标系说明 |
7 | charge_point | UINT32_T | 充电桩 X/Y 坐标,参照屏幕坐标系给出,原始值上报,充电座坐标点 X 和 Y 同时等于 0 则不显示,详细请参照 地图坐标系说明 |
8 | charge_angle | UINT32_T | 当前点的设备航向角,详细请参照 设备角度说明 |
地图中所有的像素点数据,由 map_buff
成员表示,像素值可参考 RVC_PIXEL_TYPE_E
枚举值的定义。
根据地图的像素宽和像素高,按照二维扫描的方式在其区域内的左上角 R(0,0) 填入对应的像素点枚举值。如果当前像素点是障碍物,就用 1
表示;如果当前像素点是地毯,则用 2
表示。
地图中区域外边界信息由 RVC_ROOM_POLYGON_S
成员表示,它是指房间分区的边界点坐标。
如下图边界点:
边界栅格坐标点需要参考 屏幕坐标系 从左上角开始将每个栅格点坐标按照顺序填入内存空间中,最后回到起点,中间不要跳跃,不要拟合,否则面板端会出错。
地图上报例程
int main_test(void)
{
RVC_MAP_DATA_S rvc_map_info = { 0 };
int i = 0; int j = 0;
/*地图协议头部固定字节赋值*/
rvc_map_info.header.map_id = 0; //地图 ID
rvc_map_info.header.status = 1; //0 表示地图非稳定状态,1 表示地图稳定状态
rvc_map_info.header.width = 121; //地图像素宽度
rvc_map_info.header.height = 152; //地图像素高度
rvc_map_info.header.origin.x = 564; //机器原点 X 坐标
rvc_map_info.header.origin.y = 1357; //机器原点 Y 坐标
rvc_map_info.header.resolution = 5; //分辨率
rvc_map_info.header.charge_point.x = 0; //充电桩 X 坐标
rvc_map_info.header.charge_point.y = 0; //充电桩 Y 坐标
rvc_map_info.header.charge_angle = 0; //充电桩朝向
// 调用函数分配内存
rvc_map_info.map_buff = NULL;
char strFileInfo_buff[]; //开发者提供的原始像素点数据,按照以下二维数组的方式填充
//申请一个二维数组的内存,给 temp_map_buf 使用
for (i = 0; i < rvc_map_info.header.height; i++) {
for (j = 0; j < rvc_map_info.header.width; j++) {
rvc_map_info.map_buff[i][j] = strFileInfo_buff[rvc_map_info.header.width* i + j];
}
}
char map_buf_info[]; //开发者提供房间轮廓坐标点信息
if (map_buf_info!= NULL) { //判断数据是否有效
int room_count = 2; //开发者需要提供房间分区数量
RVC_ROOM_POLYGON_S *tmp_room_polygon = NULL;
int tmp_points_size = 0;
int offset_len = 0; //数据偏移
int all_points_size = 0; //所有坐标数量
for (i = 0; i < room_count; i++) { //遍历房间轮廓信息
all_points_size += xxxx; //开发者提供所有房间内的边界点坐标数量
}
tmp_room_polygon = (RVC_ROOM_POLYGON_S*)Malloc(room_count * sizeof(RVC_ROOM_POLYGON_S) + all_points_size * sizeof(RVC_POINT_S)); //需要申请结构体的内存空间;注意该结构体内的 `polygon` 成员是个指针,需要给该指针对象赋值。
rvc_map_info.room_polygons_data = tmp_room_polygon;
float tmp_origin_x = (float)map_header.origin.x /1.0f; //获取原点坐标
float tmp_origin_y = (float)map_header.origin.y /1.0f;
RVC_POINT_S* points_memory =(RVC_POINT_S*)(tmp_room_polygon + room_count); //坐标点数据指向二级指针
for ( i = 0; i < room_count; i++) { //遍历房间轮廓信息
tmp_room_polygon->id = (map_buf_info[4 + 8 * i ] << 24) | (map_buf_info[3 + 8 * i] << 16) | (map_buf_info[2 + 8 * i] << 8) | map_buf_info[1 + 8 * i];
tmp_room_polygon->polygon.points_size = (map_buf_info[8 + 8 * i] << 24) | (map_buf_info[7 + 8 * i] << 16) | (map_buf_info[6 + 8 * i] << 8) | map_buf_info[5 + 8 * i];
PR_DEBUG("room id :%d, points size :%d", tmp_room_polygon->id, tmp_room_polygon->polygon.points_size);
offset_len = 1 + 8 * room_count + tmp_points_size; //数据偏移
tmp_room_polygon->polygon.points = points_memory;
for (j = 0; j < tmp_room_polygon->polygon.points_size; j++) { //给面板的坐标需要按照屏幕坐标系来上报,开发者根据自身的地图算法做调整
memcpy(&tmp_room_polygon->polygon.points[j].x, map_buf_info.buff + (offset_len + j * 12), sizeof(RVC_POINT_S));
tmp_room_polygon->polygon.points[j].x = tmp_room_polygon->polygon.points[j].x * 100 / 5 + (tmp_origin_x); //根据分辨率及世界坐标系的原点坐标计算出屏幕坐标数据上报面板显示。
tmp_room_polygon->polygon.points[j].y = (tmp_origin_y) - (tmp_room_polygon->polygon.points[j].y * 100 / 5) ;
tmp_room_polygon->polygon.points[j].z = tmp_room_polygon->polygon.points[j].z * 100 / 5;
// PR_DEBUG("point[%d] x:%f,y:%f, z:%f", j, tmp_room_polygon->polygon.points[j].x, tmp_room_polygon->polygon.points[j].y,tmp_room_polygon->polygon.points[j].z);
}
tmp_points_size += (tmp_room_polygon->polygon.points_size * sizeof(RVC_POINT_S)); //累加上房间的坐标点数量
points_memory += tmp_room_polygon->polygon.points_size; //二级指针地址偏移
tmp_room_polygon++; //获取下一个房间轮廓信息
}
rvc_map_info.room_polygons_count = room_count;
int ret = ty_rvc_rt_map_send_v2(&rvc_map_info); //地图数据上报
if(rvc_map_info.room_polygons_data != NULL) {
Free(rvc_map_info.room_polygons_data);
}
}
//申请的内存在后面需要释放
return ret;
}
路径数据的格式,可参考 RVC_PATH_INFO_S
结构体。
序号 | 字段 | 数据类型 | 说明 |
---|---|---|---|
1 | path_id | UINT16_T | 一次完整清扫的路径图标识,该字段在每次清扫开始时,做增量上报 |
2 | type | UINT32_T | 路径类型:
|
3 | direction | UINT32_T | 设备航向角,详细请参照 设备角度说明 |
4 | count | UINT32_T | 路径点总个数(全量) |
5 | path_point | PATH_POINT_S | 路径坐标数组大小是 Count * 8,可参照 机器坐标系 给出,上报值 =(原始值 / 栅格长度)* 10 |
PATH_POINT_S
结构体中的 coordinates
成员是路径坐标数组,即 X 点、Y 点,每个点 [x,y] = 1 X 8 Bytes;type
成员是路径点类型。当路径类型的值 0x01
时,代表普通仅正常清扫点路径,路径点类型只有 0x00
;当路径类型为 0x02
时,代表复杂路径;当路径类型为 0x03
时,代表导航路径。
路径点类型:
0
:清扫路径点1
:转场路径点2
:回充路径点3
:拖地路径点(暂未支持)路径上报例程
/**
* @brief 路径数据转换
* @param [out] path_info 转换为 RVC_PATH_INFO_S,后输出
*/
void __ty_path_data_unpack(RVC_PATH_INFO_S* path_info)
{
/*路径是基于机器坐标系,输出的坐标原始数据为 Float 型,一般在算法阶段会对原始数据进行转换,路径坐标传输值 =(路径坐标原始值 / 0.05)* 10
如算法下面的处理方式:
path_point.x = htons(((int16_t)((path.poses[i].pose.position.x) / 0.05 * 10));
path_point.y = htons(((int16_t)((path.poses[i].pose.position.y) / 0.05 * 10);
把path_point.x和path_point.y传输给 path_buf 缓存并给到业务。
*/
char path_buf[1000] = {0}; //去算法中获取到的路径数据
path_info->path_id = 0; //每次清扫开始时,做增量上报
path_info->path_type = 3; //导航路径
path_info->direction = 88; //设备航向角
path_info->point_count = 10; //10 路径点总个数
path_info->path_points = Malloc(path_info->point_count * sizeof(PATH_POINT_S)); //申请路径坐标数组
char* dest_data = Malloc(path_info->point_count *4);
if(dest_data == NULL){
PR_ERR("malloc failed");
return;
}
memset(dest_data, 0,path_info->point_count *4);
memcpy(dest_data, &path_buf[0], path_info->point_count *4); //将路径原始值数据拷贝到内存中
for(int i = 0; i < path_info->point_count;i++){
path_info->path_points[i].coordinates.x = ((dest_data[4*i] << 8) & 0xff00) | (dest_data[4*i+1] & 0xff); //将设备 2 字节的有效坐标数据转换为 int 型 (x,y)
path_info->path_points[i].coordinates.y = (dest_data[4*i+2] << 8) | (dest_data[4*i+3]);
path_info->path_points[i].type = 0; //清扫路径点根据实际路径点类型填写
}
Free(dest_data);
}
/**
* @brief 路径上报的例子
* @param
*/
void test_main(void)
{
OPERATE_RET ret = OPRT_OK;
while (1) {
RVC_PATH_INFO_S path_data_proper = {0};
__ty_path_data_unpack(&path_data_proper);
ret = ty_rvc_rt_cleanpath_send_v2(&path_data_proper); //路径上报接口
if(path_data_proper.path_points != NULL) {
Free(path_data_proper.path_points);
}
sleep(5);
}
}
机器人进行一次新清扫任务时,以机器人从初始位置出发,边清扫边建图,形成清扫地图和清扫路径。地图逐渐扩大,路径不断累积,过程中涉及世界坐标系、机器坐标系以及 App 显示的屏幕坐标系统,其关系如下:
实际的物理坐标系,即房间的平面坐标系,机器人每次清扫时,以世界坐标系的某一个点作为起始点,而且在本次清扫中,这个起始点在世界坐标系中的位置是不变的。世界坐标系,用于参照和理解机器坐标系。
机器人以初始点为原点,参照世界坐标系,建立机器坐标系。清扫过程中,路径点均参照以该原点,得到坐标值。
App 绘图时使用,以屏幕左上角 R(0,0) 为坐标系原点。
地图数据
App 绘图时,以原点 O 作为固定,等同于世界坐标系下,机器人初始位置不变。
路径数据
路径数据是走过的位置点的集合,其坐标为 INT32 的值,当前位置点为 P(x,y),x/y 为 P 点相对于 O 点的值。
路径坐标传输值 =(路径坐标原始值 / 栅格长度)* 10,以兼顾精度与效率。如栅格长度为 0.05 米,即 路径坐标传输值 =(路径坐标原始值 / 0.05)* 10。
区域框等数据
区域清扫框、禁区框、虚拟墙线、指哪扫哪点等坐标数据,其坐标为 INT32 的值,坐标点 M(x,y),x/y为 M 点相对于 O 点的值,基于机器坐标系。
该内容对您有帮助吗?
是意见反馈该内容对您有帮助吗?
是意见反馈