Ros proto

protobuf

Protobuf的数据结构定义的语法,以及如何编译proto文件,以及相关的主要读写proto文件结构中的API
说明:
  读bin文件API  是bin文件二进制形式,
  Prototxt文件  是prototxt形式.ProtoBuf数据保存的一种形式,主要是以txt形式.最主要的功能是可视化,
步骤
 Define message formats in a .proto file.
 Use the protocol buffer compiler.
 Use the C++ protocol buffer API to write and read messages.
语法
 syntax = "proto3" 表示使用proto3版本,默认使用proto2版本。
 optional 表示当前字段可选,非必填。
 string name = 1 每个字段需要有一个唯一的号码,必须大于0。
 enum 表示枚举类型。
 repeated 表示可重复, 
 message  可以嵌套
protobuf 的一大特点就是通过 “代码生成” 数据结构类的方式来序列化、反序列化二进制数据
protobuf 使用方式
   01.手动调用 protoc 来编译文件,然后引入自己的项目。
   02.使用 CMake 提供的 find_package 脚本找到 protobuf,得到一些变量。
   03.使用 CMake 下载指定版本 protobuf,源码编译 protobuf,然后用编译生成的 protoc 来编译。

C++

代码示例

#include <vector>
#include <iostream>
#include <ros/types.h>
#include <std_msgs/String.h>
#include <rosbag/view.h>
#include <rosbag/bag.h>
#include "test.pb.h"
#define foreach BOOST_FOREACH
#include <boost/foreach.hpp>
using namespace std;

int main(){
    rosbag::Bag bag;
    bag.open("/data/test/test.bag", rosbag::bagmode::Read);
    std::vector<std::string> topics; //设置需要遍历的topic
    topics.push_back(std::string("/test/sensor/test_person"));
    rosbag::View view(bag, rosbag::TopicQuery(topics));;  
    //rosbag::View view_all(view); //读取全部topic如果全读,第二个参数不写,如下

    PersonObstacle Person;
    foreach(rosbag::MessageInstance const m, view)
    {
    //此处为消息头文件中的ConstPtr
      std_msgs::String::ConstPtr s = m.instantiate<std_msgs::String>();
        if (s != NULL){
         std::cout << "the null message"<<std::endl;
         std::cout <<  Person.ParseFromString(s->data) <<std::endl;
         std::cout <<  Person.id() <<std::endl;
         std::cout <<  Person.mstatus()<<std::endl;
         std::cout <<  Person.mstatus_Name<<std::endl;
        } 
        else{
             std::cout << "the  message"<<std::endl;
        }          
    }
    bag.close();
    return 0;
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.0.2)
 project(parseBag)
 
 find_package(catkin REQUIRED COMPONENTS
   rosbag
   roscpp
   rospy
   std_msgs
 )
 
 set(CMAKE_CXX_FLAGS "-std=c++17  -pthread")
 
 INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
 
 #Find required protobuf package
 find_package(Protobuf REQUIRED)
 if(PROTOBUF_FOUND)
     message(STATUS "protobuf library found")
 else()
     message(FATAL_ERROR "protobuf library is needed but cant be found")
 endif()
 include_directories(
               ${Protobuf_INCLUDE_DIRS}
               ${CMAKE_CURRENT_BINARY_DIR}/..
            )
 message(STATUS "protobuf library includ: ${Protobuf_INCLUDE_DIRS}")

 #Find required opncv package
 set(OpenCV2_DIR /usr/share/OpenCV)
 find_package(OpenCV REQUIRED)
 include_directories( 
         ${OpenCV_INCLUDE_DIRS}
         ${OpenCV_INCLUDE_DIRS}/opencv2
         )
 link_libraries( ${OpenCV_LIBS})
 
 ###########
 ## Build ##
 ###########
 include_directories(
   ./include
   ${catkin_INCLUDE_DIRS}
   ${OpenCV_INCLUDE_DIRS}
 )
 add_executable(${PROJECT_NAME}_node ./src/bagParse.cpp ./include/test.pb.cc ./include/test2.pb.cc ) 
 target_link_libraries(${PROJECT_NAME}_node
    ${catkin_LIBRARIES}  ${PROTOBUF_LIBRARIES}
  )

C++调试

未声明的引用是头文件引入错误,
  未定义的引用是函数的实现没有引入。

python3

1.安装和使用:
   1.安装protobuf   ,安装完成后 protoc --version,如果将输出版本号
   2.2.python安装protobuf,直接通过pip安装 pip3 install protobuf
   3.根据协议生成python文件 protocol -I=    --python_out  
       --python_out    --python3_out
   4.引入脚本,使用api

2.报错原因:
 01.(unicode error) 'utf-8' codec can't d --Python版本问题	
 02.locale问题

3.说明
    python2
       SerializeToString(): serializes the message and returns it as a string. Note that the bytes are binary, not text; we only use the str type as a convenient container.
       ParseFromString(data)
    python3
    序列化的时候,pyhont3 使用 protobuf3 的接口变了。
          encode_to_bytes  替换了 SerializeToString
          parse_from_bytes 替换了  ParseFromString 
    	  
    说明
       ParseFromString是一种方法-它不返回任何内容,而是填充self已解析的内容

python 安装proto

details on the protoc and google.protobuf versions.

[root@fedora ]# protoc --version
libprotoc 25.0
[root@fedora ]# python3
>>> import google.protobuf
>>> print(google.protobuf.__version__)
4.25.0

下载地址

https://github.com/protocolbuffers/protobuf/releases

 下载文件:
    protoc-25.1-linux-x86_64.zip  -- 选择了对应平台的包
	protobuf-25.1.zip
1. protoc-25.1-linux-x86_64.zip压后打开该目录的bin目录,复制路径到环境变量中


2.解压protobuf-23.1.zip文件,找到python目录,并在protobuf中的python目录打开cmd将下面三条命令敲入
  (pip的protobuf和下载的protobuf版本最好一致)–要在当前目录下的cmd噢
    pip install protobuf
    python setup.py build
    python setup.py install
	### 安装命令会将当前的 Python 应用安装到当前 Python 环境的 site-packages 目录下,这样其他程序就可以像导入标准库一样导入该应用的代码了。

概览

protocol buffers是一个灵活的、高效的、自动化的用于对结构化数据进行序列化的协议,
    与json、xml相比,protocol buffers序列化后的码流更小、速度更快、操作更简单。
    tcp协议,用的比较多的是protobuffer

使用步骤

1.Define message formats in a .proto file.
  2.Use the protocol buffer compiler.
  3.Use the C++ protocol buffer API to write and read messages.

1.需要将要被序列化的数据结构定义一次(使用.proto文件定义) 
eg: //xxx.proto
    通过在.proto文件中定义protocol buffer的message类型来指定你想要序列化的数据结构

2.使用特别生成的源代码(使用protobuf提供的生成工具protoc) 轻松的使用不同的数据流完成对这些结构数据的读写操作, 
    使用protocol buffer提供的编译工具protoc来编译.proto文件生成数据访问类
   protoc -I=$SRC_DIR --cpp_out=$DST_DIR $SRC_DIR/addressbook.proto	
   protoc --proto_path=. --cpp_out=. ./format/error_code.proto 
  # # 查看版本 
   protoc --version
   –proto_path=PATH。它表示要在哪个路径下搜索.proto文件,这个参数既可以用-I指定,也可以使用–proto_path=指定。-I参数没有等于号
   即–cpp_out=,–python_out=等。如果protoc已经内置语言对应的编译插件,则无需再安装。如果没有内置语言,就需要单独安装插件,比如–go_out=,对应的就是protoc-gen-g
   
   addressbook.pb.h, the header which declares your generated classes.
   addressbook.pb.cc, which contains the implementation of your classes
   
   eg://xxx.pb.h 
 
     python 
        生成的*_pb2.py文件
        生成的*_pb2.py文件
3.使用
     提供了简单的访问器(比如name()和set_name()),
	 同时还提供了 将整个结构化数据序列化为原始字节数据  以及 从原始字节数据反序列化为结构化数据的方法 
     C++使用
	   #include "common/proto/sensor.pb.h"
 
     对于 C++,编译器从每个 .proto 生成一个 .h 和 .cc 文件,其中包含文件中描述的每种 message 类型对应的类
	   你在使用 enum 的 .proto 上运行 protocol buffer 编译器时,生成的代码将具有相应的用于 Java 或 C++ 的 enum
	方法和属性   
	 the setter methods begin with set_.
	 There are also has_ methods for each singular (required or optional) 
	 clear_ method that un-sets the field back to its empty state.
     the numeric id field
    序列化和反序列化方法 Parsing and Serialization	
        bool SerializeToString(string* output) const  以及  bool ParseFromString(const string& data);		
	    bool SerializeToOstream(ostream* output) const以及  bool ParseFromIstream(istream* input);

proto文件具体详情

1..proto文件 最开始建议说明使用proto2还是proto3语法的声明:syntax = "proto2";,如果不声明默认使用proto2的语法
 2..proto文件 必须以package xxxx;声明开头,作为协议唯一的标识,避免不同项目的命名冲突
 3.引用其他proto文件 import
   message 类型已在另一个 .proto 文件中定义,该怎么办?可以通过导入来使用其他 .proto 文件中的定义
 4. message数据格式 
  一个 message 相当于一个指定类型的集合,一个message可以直接嵌套另一个message使用     组合messages 
  
    包含了一系列的 name-value 对
    字段声明是required||repeated||optional   缺省默认就设置为 optional)
	   required: 格式良好的 message 必须包含该字段一次。 在 proto3 中已经为兼容性彻底抛弃 required
	   optional: 格式良好的 message 可以包含该字段零次或一次(不超过一次)
	   repeated: 该字段可以在格式良好的消息中重复任意多次(包括零) 或者在最后使用 [packed = true]
	
	标量类型
	复合类型,包括 枚举 和其它的 message 类型
  构成
	字段声明 名称  类型 唯一编号
	   唯一编号 identify the unique field number that field uses in the binary encoding
	
	可以将默认值指定为 message 描述的一部分。optional int32 result_per_page = 3 [default = 10];

使用

pyhont3 使用 protobuf3 的接口变化了。
         encode_to_bytes 替换了原来的 SerializeToString
         parse_from_bytes 替换了原来的 ParseFromString 
		 
 C++ 		 
   Cmake
    Cmake有官方的modules,可以通过简单的几个命令protobuf_generate_cpp来生成对应的.pb.cc和.pb.h。
	
	PROTOBUF_INCLUDE_DIRS - protobuf的头文件
    PROTOBUF_LIBRARIES - protobuf库

参考

https://protobuf.dev/reference/cpp/	
在 CMake 项目中使用 protobuf 
rosbag遍历数据出错:(unicode error) 'utf-8' codec can't d https://www.apude.com/blog/15289.html
https://developers.google.com/protocol-buffers/docs/pythontutorial
【ROS】读写rosbag(c++) 	 
 ROS(melodic)创建工作空间,功能包(Ubuntu18.04)