跳转至

4.2.   软件间通讯

本篇文章盘点赛队目前主要使用的软件间的通讯机制,希望大家能够据此了解各软件的定位,串联起整个系统

赛队定义

涉及软件

  • rbk:控制程序,负责决策,判断场上局势,发布运动指令
  • owl2:可视化工具,处理视觉以得到更为准确的位姿,同时显示辅助调试的信息
  • grSim(src-simulation):仿真器,可提供仿真环境下的相机视觉(裸视觉)、机器人反馈等信息
  • ssl-vision:视觉机,官方推荐使用,在实车模式中提供相机视觉
  • 裁判盒:发送裁判指令(Halt、Stop……),包括owl2自带的和GC

通讯汇总

1

  • Message:通讯传递的信息
    • Vision:视觉,包括裸视觉和滤波处理后的视觉
    • Command:运动指令
    • FeedBack:机器人的反馈,包括红外、踢球情况等
    • Referee:裁判指令
    • Option:模式选择,包括仿真或实车、选色、选边等
    • Debug:辅助调试语句
  • Protocol:通讯协议名称,箭头表示通讯方向,发送方->接收方
  • Protubuf:通讯协议的具体格式,仅用于网络通讯
  • Type:通讯协议的种类,主要有TCP、UDP两类,其中UDP Multicast为使用网络进行组播,可以给到同一网段的多个设备
  • IP Address:IP地址,仅用于网络通讯
  • Port:通讯端口,仅用于网络通讯
  • Mode:通讯存在的模式,Sim指仿真模式,Real为实车模式,Both即两者中都存在
  • 表格中的大部分通讯都是通过网络传播,也列举了软件和机器人(实车)之间的通讯机制,属于无线载波通讯,通讯协议详见机器人通讯协议

官方定义

  • 明确一些赛事通用软件间的通讯机制,统一通讯协议

2

知识补充

通讯协议

  • 通讯,目的是传递信息,简单来说有几个要素,发送设备、接收设备和通讯协议
  • 前两者好理解,通讯协议可以理解为双方统一使用的语言,否则一个说汉语,一个说英语,谁也听不懂谁
  • 网络中的通讯协议主要有两个,TCP和UDP
  • TCP是一种可靠通讯方式,双方先要经过三次握手建立连接,接收端每接收到新的信息还需要给予发送端反馈,以评价通路状况,避免阻塞。其准确率高,丢包率低,但速度受限
  • UDP是一种常在于网络通讯的方式,无需特意建立连接,发送端将信息发至网络,通过网络向某一端口传输,接收端绑定该端口,等待信息。特点是速度快,但丢包率和准确率不如TCP,很大程度上取决于网络状态
  • 所以综合两者优劣,考虑到比赛是个高帧率的场景,对通讯速度要求较高,绝大多数通讯采用UDP
  • 而几处网络通讯采用TCP是因为其可靠性更为重要,如AutoRef对GC的反馈,需要信息的准确,避免误判
  • UDP Multicast是使用UDP进行组播,如视觉机就有如此需求,比赛时各队主机均与视觉机相连(网线、交换机),处于同一局域网段,此时就需要建立一个组播群组,所有设备都在此注册,信息就可传递至每个设备。否则无论连接到局域网的客户端想不想接收该数据,服务端都会给客户端发送该数据(广播),进而造成客户端上数据的拥塞
  • 那么,通讯中的信息到底是什么?其实无论那种通讯协议,信息都以比特流的形式传递,信息如何负载在上面,又要如何恢复出来,就要涉及到具体的格式和编解码方法。而在网络通讯中,最为常用的protubuf就是来完成这两个工作的

Protubuf

  • protubuf其实是一种数据存储格式,因为其结构优异,且方便与比特流相互转化,因此被广泛使用
  • 回到我们的项目里,protubuf用.proto文件的形式编写,几个常用的语法:
  • syntax定义使用protubf的版本,不同版本语法有所差别
  • import类似C++中的include,引入其他文件,以使用其他文件中定义的信息格式
  • package也是一种引用
  • message定义了一种信息格式,具体内容包括:
    • 种类:
    • 类似C++中的bool、double、string,但没有int,但有uint32,也可自定义,即再定义一个message,语法相同
    • 前面需加一个前缀,补充信息特性:required 必要;optional 可缺失;repeated 重复,组成数组
    • 名称
    • 排序:这个最为重要,收发两端必须一致
  • 在C++中使用protubuf,需要先生成定义接口的文件(.pb.h、.pb.cc),使用protoc.exe,输入以下指令(一般都配有自动运行脚本),生成文件在同一路径下的cpp文件夹中

./protoc.exe --cpp_out=cpp *.proto

  • 下面给出一个.proto和C++相对应的示例:
syntax = "proto2";

package tutorial;

message Person {
  required string name = 1;
  required int32 id = 2;
  optional string email = 3;

  enum PhoneType {
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
  }

  message PhoneNumber {
    required string number = 1;
    optional PhoneType type = 2 [default = HOME];
  }

  repeated PhoneNumber phones = 4;
}

message AddressBook {
  repeated Person people = 1;
}
  // name
  inline bool has_name() const;
  inline void clear_name();
  inline const ::std::string& name() const;
  inline void set_name(const ::std::string& value);
  inline void set_name(const char* value);
  inline ::std::string* mutable_name();

  // id
  inline bool has_id() const;
  inline void clear_id();
  inline int32_t id() const;
  inline void set_id(int32_t value);

  // email
  inline bool has_email() const;
  inline void clear_email();
  inline const ::std::string& email() const;
  inline void set_email(const ::std::string& value);
  inline void set_email(const char* value);
  inline ::std::string* mutable_email();

  // phones
  inline int phones_size() const;
  inline void clear_phones();
  inline const ::google::protobuf::RepeatedPtrField< ::tutorial::Person_PhoneNumber >& phones() const;
  inline ::google::protobuf::RepeatedPtrField< ::tutorial::Person_PhoneNumber >* mutable_phones();
  inline const ::tutorial::Person_PhoneNumber& phones(int index) const;
  inline ::tutorial::Person_PhoneNumber* mutable_phones(int index);
  inline ::tutorial::Person_PhoneNumber* add_phones();
  • 具体接口函数可以在生成的文件中查找,几个常用的:
  • xxx():某一项的内容,无论.proto中变量名有无大写字母,xxx均为小写
  • mutable_xxx():返回指针,方便程序内修改数值
  • set_xxx(xxx):设置某一项内容
  • has_xxx():检查是否有此项
  • clear_xxx():归为默认值(0),这也是一个小缺憾,无法区分0到底是设置的还是默认的
  • xxx_size():数组大小
  • add_xxx():添加数组成员
  • SerializeToArray(xxx,size):转化为比特流
  • ParseFromArray(byte,size):从比特流中提取信息
  • 所以具体操作很明显了,发送端按信息格式填好后,转化为比特流发送;接收端将信息提取出后,可以直接利用。编解码方式在protubuf内部定义,使用方便
  • 下方附上两个示例,分别是接收和转发仿真器的机器人反馈,注意引用相关的接口文件

3

4


Reference