Bhuman自带的demo中实现的功能是很基础的,很多功能需要自己找接口添加,其实bhuman发布出来的源码都是阉割版的,除了步态,simRobot,标定工具等调试工具很好用以外,许多图像识别并不好用,但是它给了一个很好的框架供我们开发(如果能有办法把例如halcon等软件的第三方库烧进去使用图像识别问题就能变得很轻松)
这里以修改哨声识别为例,简单讲下如何添加、修改Representation里的接口。
哨声识别可以在这个文件中找到,打开看
STREAMABLE(Whistle, COMMA public BHumanMessageParticle<idWhistle>
{
/** BHumanMessageParticle functions */
void operator >> (BHumanMessage& m) const override;
void operator << (const BHumanMessage& m) override;
bool handleArbitraryMessage(InMessage& m, const std::function<unsigned(unsigned)>& toLocalTimestamp) override,
(char)(0) confidenceOfLastWhistleDetection, /**< Confidence based on hearing capability */
(unsigned int)(0) lastTimeWhistleDetected, /**< Timestamp */
(unsigned int)(0) lastTimeOfIncomingSound, /**< The last point of time when the robot received audio data */
(std::string)("") whistleName, /**< Name of the last detected whistle */
});
STREAMABLE可以通过查找最终找到这是个宏定义的流结构体
判断是否听到哨声是通过判断confidenceOfLastWhistleDetection这个变量的值的大小。
在WhistleRecognizer里我们可以看到程序会根据不同的情况给confidenceOfLastWhistleDetection赋值,在STATE_SET的gamestate的情况下才会识别哨声
(顺带一提,theGameInfo.state这个成员是个很重要的变量,bhuamn中许多行为都是通过判断theGameInfo.state来执行。)
这里我们以识别哨声为例,想要在Options中添加一个行为,如果听到哨声就播放段声音。代码如下
initial_state(start)
{
transition
{
if(theWhistle.confidenceOfLastWhistleDetection>0)
{
goto playsound;
}
}
action
{
/**do something*/
}
}
在Options中的代码使用the+Representation中的类名.成员及可实现调用,(不用声明对象)需要注意的是,Options里可以自己定义变量,例如int a=10;但是不能对Representation中的类成员赋值,例如theWhistle.confidenceOfLastWhistleDetection=50; 则会报错。
此外,因为使用了Representation中的类,需要在BehaviorControl.h中添加头文件,并且REQUIRES(Whistle)
这样即可在听到哨声后跳到playsound state。
Representation中的类可以按照C++规则进行修改,添加变量或者改变逻辑。我们还是以哨声为例修改代码,实现一个功能,让机器人站起来以后(可以在simRobot中看到站起来后的theGameInfo.state是STATE_INITIAL)就能通过识别哨声playsound。
将这两处的theGameInfo.state判断改掉
改完后make Nao编译烧进Nao中的代码,按一次胸口Nao站起来,吹哨子就可以playsound了。
使用类的时候加the,如果想要查看或者全局搜索某个类搜the后面的字段,例如theGameInfo搜GameInfo才能找到这个类。
这里仅以哨声为例介绍如何修改程序,具体开发还需要自己研究。
之前说了在Modules中每个模块除了需要哪个类REQUIRES哪个类,还可以为类提供更新,PROVIDES,还是以哨声识别WhistleRecognizer.h为例,
REQUIRES(AudioData),
REQUIRES(CognitionStateChanges),
REQUIRES(DamageConfigurationHead),
REQUIRES(FrameInfo),
REQUIRES(GameInfo),
REQUIRES(GroundContactState),
REQUIRES(RobotInfo),
PROVIDES(Whistle),
他为Whistle类PROVIDES,每一个PROVIDES对应一个update函数,而且必须要有,为PROVIDES的类提供信息更新。
/**
* The method that detects the whistle
* @param Whistle The identified whistle
*/
void update(Whistle& whistle);
update函数中传入提供更新的类的引用,在函数的实现中可以给类中的变量赋值,例如赋值 currentWhistle.confidenceOfLastWhistleDetection = 33
(之前说过option中是不能给变量赋值的,赋值变量或者更改状态一般都是在update函数中完成的)
第一篇博客说过个人认为Modules里的模块是机器人开机后就不停执行的,update函数相当于模块获取外界信息,为Representation中的类信息提供更新。
顺带提一下在代码中经常会看这样的东西
DEBUG_RESPONSE_ONCE("module:WhistleRecognizer:whistle")
{
whistle.lastTimeWhistleDetected = theFrameInfo.time;
whistle.lastTimeOfIncomingSound = theFrameInfo.time;
whistle.confidenceOfLastWhistleDetection = 100;
return;
}
DEBUG_RESPONSE_ONCE("module:WhistleRecognizer:sound")
{
whistle.lastTimeOfIncomingSound = theFrameInfo.time;
return;
}
DECLARE_PLOT("module:WhistleRecognizer:whistleCorrelationChannel0");
DECLARE_PLOT("module:WhistleRecognizer:whistleCorrelationChannel1");
PLOT("module:WhistleRecognizer:whistleCorrelationChannel0", bestCorrelationChannel0);
PLOT("module:WhistleRecognizer:whistleCorrelationChannel1", bestCorrelationChannel1);
DECLARE_PLOT("module:WhistleRecognizer:audioInput60HzChannel0");
DECLARE_PLOT("module:WhistleRecognizer:audioInput60HzChannel1");
PLOT("module:WhistleRecognizer:audioInput60HzChannel0", inputChannel0.back());
PLOT("module:WhistleRecognizer:audioInput60HzChannel1", inputChannel1.back());
DECLARE_PLOT("module:WhistleRecognizer:currentVolume");
PLOT("module:WhistleRecognizer:currentVolume", currentVolume);
这些都是调试用的,在simRobot可以通过get,dr等指令查看这些调试信息,调试的时候非常有用,哨声的判断阈值等也可以通过set指令在simRobot中更改。
个人经验如有错误欢迎指出,欢迎讨论。