Representation和Module理论详解

BHuman框架中,最为核心的部分就是Representation和Module,几乎所有的重要功能都是由这种体系来实现的。如果需要重构BHuman的代码框架添加自己需要的功能,或者借鉴Bhuman重写一个新的机器人框架,学习Representation和Module理论都是首要的。

Representation和Module的功能描述

根据BHuman开源库中Coderelease2019里的描述,Module的主要功能是执行某一特定的任务,比如图像处理、定位或者行走,而Representation则是作为数据接口来传入或者传出数据。
Representation都是用STREAMABLE类型定义的,而Module都是由MODULE定义的。
此时,我们可以将Representation视为类似于数据库,而Module因为要执行特定的功能,比如拿图像处理举例,Module首先要从某个Representation中获得原始图像数据,再经过它处理后,把诸如识别结果等数据存入到另外一个Representation中,这样就可以从这个Representation中拿到经过处理的数据了。

Representation和Module间数据流通的规则

首先介绍Module中的一些用于声明的宏定义调用接口,当Module要与某个Representation建立连接时,需要先用这些接口中的某一个进行声明。下面假设name为某个指定的Representation的名字:

  • REQUIRES(name):从name中获得数据,同时要确保name中的所有数据刚刚被向name提供数据的Module更新过
  • USES(name):从name中获得数据,但是name中的所有数据不需要被即时更新过
  • PROVIDES(name):向name提供数据

由于Module可以同时引用和更新多个Representation,所以显然,由于REQUIRES的存在,在Module和Representation之间存在一种特殊的拓扑序列,在Module调用某个Representation的信息时,需要确保更新这个Representation的前置的Module已经执行完成了。
这种拓扑序列是更为复杂的,因为它还和线程有关。在BHuman框架中存在四个线程,同一个Representation可能在不同的线程中被更新,这种关系具体的情况可以参考文件threads.cfg文件,其中声明了Representation和哪些线程有关,文件路径为:

Config/Scenarios/Default/threads.cfg

Representation和线程的绑定也说明了为什么BHuman中的数据可以实时更新,在需要预更新数据时,内部会根据情况选择线程来完成更新。

如何自定义Representation和Module

假设我们需要实现一个新的Representation,这个Representation的功能是用来存储球的位置信息,这个信息可以从另外一个已有的Representation,即fieldBall中获得到。
首先,进入以下文件路径:

Src/Representations/Modeling

建立文件BallPosition.h,这个文件即代表着我们新建的Representation,代码内容如下:

#pragma once

#include "Tools/Math/Eigen.h"
#include "Tools/Streams/AutoStreamable.h"

STREAMABLE(BallPosition,
{,
  //The ball position in global field coordinates
  (Vector2f)(Vector2f::Zero()) positionOnField,

  // The ball position in relative robot coordinates
  (Vector2f)(Vector2f::Zero()) positionRelative,
});

这个Representation只声明了两个变量,现在我们还需要定义一个用来更新这两个变量的Module,这样这两个变量才是有效的。
进入以下文件路径:

Src/Modules/Modeling

建立文件夹BallPositionProvider.h,具体代码为:

#pragma once

#include "Tools/Module/Module.h"
#include "Representations/BehaviorControl/FieldBall.h"
#include "Representations/Modeling/BallPosition.h"

MODULE(BallPositionProvider,
{,
  REQUIRES(FieldBall),
  PROVIDES(BallPosition),
  DEFINES_PARAMETERS(
  {,
    //if need, you can define parameters here
  }),
});
class BallPositionProvider : public BallPositionProviderBase
{
public:
  /** Constructor */
  BallPositionProvider();

private:
  void update(BallPosition& ballPosition) override;
};

从代码上看,除了需要引用定义MODULE结构的文件以外,还引用了FieldBall.h和BallPosition.h两个头文件,然后分别用REQUIRES和PROVIDES来引入,这和前文是一致的。到BallPositionProvider这个类中,最重要的就是这个update函数,该函数的传入参数即为需要更新信息的Representation,在这个案例中为BallPosition。如果一个Module中PROVIDES了几个Representation,需要同时更新多个数据,就需要重载多个update函数,每个update函数对应一个Representation。
最后是新建BallPositionProvider.cpp文件,这个文件是用来实现BallPositionProvider.h中声明的函数的,这两个文件需要在同一个目录下。代码如下:

#include "BallPositionProvider.h"

MAKE_MODULE(BallPositionProvider, modeling);

BallPositionProvider::BallPositionProvider()
{
    //Constructor is empty now
}

void BallPositionProvider::update(BallPosition& ballPosition)
{
    ballPosition.positionOnField=theFieldBall.positionOnField;
    ballPosition.positionRelative=theFieldBall.positionRelative;
}

代码中引入了对应的头文件,然后就可以使用在头文件中REQUIRES的Representation,在update函数中更新数据。值得一提的是,MAKE_MODULE中的第二个参数是当前.cpp文件属于的文件夹名,首字母小写,具体可参考同级目录下的其它文件的写法。

以上,我们就完整的实现了自定义的Representation和Module的主体代码,最后只需要在参数文件中添加引用关系即可。
进入前文提到的threads.cfg文件中:

Config/Scenarios/Default/threads.cfg

选择对应的线程,具体的选择要根据Representation的性质来决定,我们这里选择的是Cognition,并在RepresentationProvider中添加一行:

{representation = BallPosition; provider = BallPositionProvider;}

完成后就可以进行编译了,如果没有报错说明添加成功。

小结

能够自定义Representation和Module的主要意义在于我们可以在这个基础上添加我们需要的功能,比如在后续的研究中如果可以成功导入第三方库,那就能加入我们自定义的图像处理等;或者说抛开BHuman的整体框架,只保留CABSL这样的重要结构,简化代码后,更易于我们在这个基础上建立新的框架。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值