Skip to content

Commit

Permalink
[doc] add 2_3_subtasks
Browse files Browse the repository at this point in the history
  • Loading branch information
Mark-tz committed Jul 14, 2024
1 parent a681d4c commit 3418f43
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 6 deletions.
Binary file added doc/img/2_3_subtask.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 9 additions & 5 deletions doc/posts/1_rocos_basic/1_4_1_settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@

我们以`CircleRun`中的代码为例,来讲解如何在c++中使用参数。

```cpp
```{code-block} c++
:linenos:
// 1. 引入头文件
#include "parammanager.h"
Expand All @@ -36,8 +37,9 @@ CCircleRun::CCircleRun(){
通过上述代码,我们可以在c++中使用参数。其中`loadParam`函数的参数依次为:参数变量、参数名、默认值。在构造函数中,我们通过`loadParam`函数读取参数的值,如果参数不存在,则使用默认值。

类似的接口也可以在lua中使用,我们以`config.lua`中的代码为例:

```lua

```{code-block} lua
:linenos:
IS_YELLOW = CGetSettings("ZAlert/IsYellow","Bool")
local team = IS_YELLOW and "Yellow" or "Blue"
IS_TEST_MODE = CGetSettings("ZAlert/"..team.."_IsTest","Bool")
Expand All @@ -57,7 +59,8 @@ IS_TEST_MODE = CGetSettings("ZAlert/"..team.."_IsTest","Bool")

上述的`loadParam`完整接口如下:

```cpp
```{code-block} c++
:linenos:
bool loadParam(QChar&, const QString&, QChar d = 0);
bool loadParam(int&, const QString&, int d = 0);
bool loadParam(double&, const QString&, double d = 0);
Expand All @@ -67,7 +70,8 @@ IS_TEST_MODE = CGetSettings("ZAlert/"..team.."_IsTest","Bool")

除了上面使用的`loadParam``parammanager`还有其他接口可以使用:

```cpp
```{code-block} c++
:linenos:
// 使用参数名直接修改参数
bool changeParam(const QString&, const QVariant&);
bool changeParam(const QString&, const QString&, const QVariant&);
Expand Down
53 changes: 52 additions & 1 deletion doc/posts/2_rocos_advanced/2_3_subtasks.md
Original file line number Diff line number Diff line change
@@ -1 +1,52 @@
# Skill - 链式调用[TODO]
# Skill - 链式调用

> 对于lua的策略来说,一个task只对应一个skill,但一个skill通常是可以继续拆分。除了速度规划本身,几乎任何skill都可以最终拆分成一个机器人到达某个位置的问题。这种拆分的方式,可以让我们更好地复用代码。链式调用是一种常见的拆分方式,通过链式调用,我们可以将一个skill拆分成多个子skill,每个子skill负责一个小的任务,最终完成整个skill的任务。
```{code-block} c++
:linenos:
#include "MyTouch.h"
void MyTouch::plan(const CVisionModule* pVision){
setSubTask("SmartGoto",task());
Skill::plan(pVision);
}
```

在之前的小节中,我们在skill的书写时使用了一个`setSubTask("SmartGoto",task());`,这就是一个最简单的链式调用。在这个例子中,我们将`SmartGoto`作为子skill,`task()`作为参数传入。在`SmartGoto`中,在做完路径规划之后会继续调用速度规划的Skill-`Goto`,这样就形成了一个链式调用。

## `setSubTask`的使用

`setSubTask`是一个在`Skill`中的函数,它的定义如下:

```cpp
// PlayerTask.h
void setSubTask(const std::string& name, const TaskT& task); // 设置子任务
```
`setSubTask`的作用是设置一个子任务,这个子任务会在当前任务执行完毕后继续执行。在`setSubTask`中,我们需要传入两个参数,第一个参数是子任务的名字,第二个参数是子任务的参数。在`setSubTask`中,我们会将子任务的名字和参数保存在`PlayerTask`中,等到当前任务执行完毕后,会通过`Skill::plan(pVision)`调用基类`Skill`的函数继续执行这个子任务。
## `setSubTask`的`TaskT`参数
```{code-block} c++
:linenos:
// misc_types.h
struct TaskT{
TaskT() : executor(0){ }
TaskT(const int executor) : executor(executor){ }
int executor; // 执行任务的队员号码
PlayerStatus player; // 队员的状态
stBallStatus ball; // 球的状态
};
```
上面的代码是`TaskT`的定义,`TaskT`是一个结构体,它包含了一个`executor`和两个状态,`player``ball`。在`setSubTask`中,我们传入的参数就是`TaskT`类型的,这个参数会在子任务中使用。在子任务中,我们可以通过这个参数获取到当前的队员号码,队员的状态和球的状态,这样我们就可以根据这些信息来做一些决策。关于机器人和球的状态的定义,可以在`misc_types.h`中找到。

## `setSubTask`的使用场景举例

我们以实际策略中的单机器人的拿球后踢球`GetBallAndKick`为例,来说明`setSubTask`的使用场景。在这个例子中,我们需要机器人拿到球,并将球踢出。这个任务可以拆分成两个子任务,一个是拿球,一个是踢球。在拿球时,由于球的运动方向以及最终踢球任务的目标朝向不同,可能存在**截球/追球/静态拿球/逼抢**等不同情况。在踢球时,可能会调用**动态摆脱/推球转身/马赛回旋**等有特色的技能来完成球的转移。这些情况可以进一步交给不同子任务来完成并在`GetBall`中整合,最终完成整个任务。而整个过程中,都会用到避障、路径规划等技能,所以最终的链时调用可能会是这样的:

```{thumbnail} ../../img/2_3_subtask.png
```

## 总结

链式调用是一种常见的拆分方式,通过链式调用,我们可以将一个skill拆分成多个子skill,每个子skill负责一个小的任务,最终完成整个skill的任务。在实际的策略中,我们可以根据任务的复杂程度,将任务拆分成多个子任务,通过链式调用的方式来完成整个任务。这样可以更好地复用代码,提高代码的可读性和可维护性。

0 comments on commit 3418f43

Please sign in to comment.