Skip to content

Commit

Permalink
🔧 StateInfo 增加对数值状态类型的支持
Browse files Browse the repository at this point in the history
  • Loading branch information
TooPretty0108 committed Jan 9, 2025
1 parent 6498bd3 commit 453400c
Show file tree
Hide file tree
Showing 18 changed files with 261 additions and 169 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# 配置文件
.vscode
.idea
.cache

# 编译文件
*build*
Expand Down
1 change: 1 addition & 0 deletions cmake/RMVLCompilerOptions.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ if(NOT HAVE_CXX17)
endif()

set(CMAKE_CXX_STANDARD ${max_version})
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# ----------------------------------------------------------------------------
# Detect target platform architecture
Expand Down
11 changes: 11 additions & 0 deletions cmake/templates/python/rmvl_pygen.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ def split_parameters(params: str) -> Tuple[List[str], List[str], List[str]]:
"float": "float",
"double": "float",
"bool": "bool",
"char": "str",
"string": "str",
"string_view": "str",
# cv types
Expand Down Expand Up @@ -257,6 +258,14 @@ def convert_pair_tuple(match: re.Match) -> str:
converted = ", ".join(type_convert(x.strip()) for x in inner.split(","))
return f"tuple[{converted}]"

def convert_variant(match: re.Match) -> str:
"""
Convert `variant<...>` to `Union[...]`
"""
inner: str = match.group(1)
converted = ", ".join(type_convert(x.strip()) for x in inner.split(","))
return f"Union[{converted}]"

def convert_unordered_map(match: re.Match) -> str:
"""
Convert `unordered_map<...>` to `dict[...]`
Expand Down Expand Up @@ -342,6 +351,8 @@ def convert_simple_vec(match: re.Match) -> str:
cpp_type = re.sub(r"^array<([^,]+),\s*\d+>$", convert_array, cpp_type)
# pair<...>, tuple<...> -> tuple[...]
cpp_type = re.sub(r"^(pair|tuple)<(.+)>$", convert_pair_tuple, cpp_type)
# variant<...> -> Union[...]
cpp_type = re.sub(r"^variant<(.+)>$", convert_variant, cpp_type)
# unordered_map<...> -> dict[...]
cpp_type = re.sub(r"^unordered_map<(.+)>$", convert_unordered_map, cpp_type)
# optional<...> -> Union[... | None]
Expand Down
5 changes: 4 additions & 1 deletion doc/tools/pyrmvl_fns.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -207,11 +207,14 @@ rm::Subscriber Subscriber sub_name,addr[,port[,users]]
rm::Subscriber subscribe pub_name,fields nds

##################################### types ####################################
rm::StateInfo add type None
rm::StateInfo add key,val None
rm::StateInfo add key,str None
rm::StateInfo remove key None
rm::StateInfo contains key [[Is_Contained_?]]
rm::StateInfo clear "" None
rm::StateInfo empty "" [[Is_Empty_?]]
rm::StateInfo at_numeric key val
rm::StateInfo at_string key val
rm::StateInfo [[get]] key val
rm::StateInfo [[set]] key,val None

Expand Down
94 changes: 54 additions & 40 deletions doc/tutorials/modules/tools/serial.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,66 +14,80 @@

相关类 rm::SerialPort

### 简介
### 1. 简介

串口通信是一种通过串行数据传输进行通信的方式。它使用串行接口将数据以逐位的方式进行传输,常用于连接计算机与外部设备、嵌入式系统之间的数据传输。

串口通信一般涉及两个主要概念: **串口****波特率**

- 串口:指的是通信中的物理接口,常见的有`RS-232``RS-485``UART`等标准。每个串口都有相应的引脚用于发送和接收数据以及控制信号。
- 波特率:也称作数据传输速率,指的是每秒钟传输的位数。波特率决定了传输速度的快慢,通常使用常见的波特率如9600、115200等
- 串口:指的是通信中的物理接口,常见的有 `RS-232``RS-485``UART` 等标准。每个串口都有相应的引脚用于发送和接收数据以及控制信号。
- 波特率:也称作数据传输速率,指的是每秒钟传输的位数。波特率决定了传输速度的快慢,通常使用常见的波特率如 9600、115200 等

[Unix](https://unix.org/) 中,Termios 是一个用于串口通信的 API ,它提供了对串口终端设备进行配置和控制的功能。
### 2. 使用{#serialport_how_to_use}

Termios 接口允许开发人员通过编程来设置串口终端的各种参数,如波特率、数据位数、校验位、流控制等。通过使用 Termios 接口,可以实现对串口通信的灵活控制,以满足各种应用需求。
#### 2.1 初始化{#serialport_init}

主要的 Termios 函数包括
rm::SerialPort 的构造函数原型如下

```cpp
SerialPort(std::string_view device, SerialPortMode mode = {});
```
| 函数 | 意义 |
| :----------------------------: | :-------------------------------------------------------: |
| `tcgetattr()` | 获取当前的终端属性(设置),将属性保存到 Termios 结构体中 |
| `tcsetattr()` | 设置终端属性,通过提供 Termios 结构体中的属性值进行设置 |
| `cfsetispeed(), cfsetospeed()` | 设置串口的输入和输出波特率 |
| `tcflush()` | 刷新输入或输出缓冲区 |
| `tcdrain()` | 等待所有输出数据完成发送后再返回 |
| `tcflow()` | 控制数据流向 |
| 参数 | 含义 |
| :------: | :-----------------------------------------------------------------------: |
| `device` | 设备名,在 Windows 上一般是 `COMx`,Linux 上一般为 `ttyUSBx` 或 `ttyACMx` |
| `mode` | 串口通信模式,包含波特率 rm::BaudRate 和读取模式 rm::SerialReadMode |
### 使用{#serialport_how_to_use}
#### 2.2 数据 I/O {#serialport_io}
#### 初始化{#serialport_init}
rm::SerialPort 提供了极其方便的串口读取、写入的接口。
rm::SerialPort 的构造函数原型如下
###### 读取函数原型
```cpp
explicit SerialPort(const std::string &device = {}, int baud_rate = B115200);
template <typename AggregateT>
bool read(uint8_t head, uint8_t tail, AggregateT &data); // (1) 指定头尾帧的读取结构化数据
```

| 参数 | 含义 |
| :---------: | :----------------------------------------------------------: |
| `device` | 设备名,如果为空,则会自动搜寻名为 `ttyUSBx` 或 `ttyACMx` 的串口文件,如果不为空,则直接打开指定的设备 |
| `baud_rate` | 波特率,默认为 `B115200`,所有可用的波特率都被标准化为 `B` 开头的宏定义 |
```cpp
template <typename AggregateT>
bool read(AggregateT &data); // (2) 读取结构化数据
```
#### 数据 I/O {#serialport_io}
```cpp
bool read(std::string &data); // (3) 读取字符串数据
```

rm::SerialPort 提供了极其方便的串口读取、写入的接口。
```cpp
template <typename AggregateOrStringT>
SerialPort &operator>>(AggregateOrStringT &data); // (4) 串口读取操作符重载
```

###### 写入函数原型

```cpp
template <typename AggregateT>
bool write(const AggregateT &data); // (5) 写入结构化数据
```
```cpp
bool write(std::string_view data); // (6) 写入字符串数据
```

```cpp
template <typename AggregateOrStringT>
SerialPort &operator<<(const AggregateOrStringT &data); // (7) 串口写入操作符重载
```

@param data 读取、写入的数据
@param head 帧头
@param tail 帧尾

| 功能 | 函数 | 功能 |
| :--: | :-----: | :------------------------------------------------: |
| 读取 | `read` | 指定头尾帧,读取缓冲区中最新的能够组成结构体的数据 |
| 写入 | `write` | 传入待写入的结构体完成数据的写入 |
**注解**

- 读取函数原型
```cpp
template <typename Tp>
inline bool read(unsigned char head_flag, unsigned char tail_flag, Tp &data)
```
- 写入函数原型
```cpp
template <typename Tp>
inline bool write(Tp &data_struct);
```
<div style="margin-left: 40px;">(3) 和 (5) 带有 Python 接口</div>

#### 链路层协议 {#serialport_protocol}
#### 2.3 链路层协议 {#serialport_protocol}

**写数据(视觉端 → 电控端)**

Expand All @@ -86,5 +100,5 @@ SerialPort 的通信协议一般就是指数据链路层的协议,采用封装
需要注意的是,在读取数据前需要留意

- 协议(结构体)内容
- 内存对齐规则,关于标准的内存对齐可使用 `#pragma once` 宏或 `alignas` 关键字
- 内存对齐规则,关于内存对齐可使用 `#pragma pack` 宏或使用 C++ 标准的 `alignas` 关键字
- 头尾帧是否一致
72 changes: 6 additions & 66 deletions extra/combo/include/rmvl/combo/armor.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,88 +173,28 @@ inline cv::Ptr<cv::ml::SVM> Armor::_svm = nullptr;
*
* @param[in] armor_size 装甲板大小类型
*/
constexpr const char *to_string(ArmorSizeType armor_size)
{
switch (armor_size)
{
case ArmorSizeType::SMALL:
return "small";
case ArmorSizeType::BIG:
return "big";
default:
return "unknown";
}
}
const char *to_string(ArmorSizeType armor_size);

/**
* @brief 字符串转为装甲板大小类型
* @brief StateType 转为装甲板大小类型
*
* @param[in] str 字符串
*/
constexpr ArmorSizeType to_armor_size_type(std::string_view str)
{
if (str == "small")
return ArmorSizeType::SMALL;
else if (str == "big")
return ArmorSizeType::BIG;
return ArmorSizeType::UNKNOWN;
}
ArmorSizeType to_armor_size_type(const StateType &tp);

/**
* @brief 机器人类型转为字符串
*
* @param[in] robot 机器人类型
*/
constexpr const char *to_string(RobotType robot)
{
switch (robot)
{
case RobotType::HERO:
return "hero";
case RobotType::ENGINEER:
return "engineer";
case RobotType::INFANTRY_3:
return "infantry_3";
case RobotType::INFANTRY_4:
return "infantry_4";
case RobotType::INFANTRY_5:
return "infantry_5";
case RobotType::OUTPOST:
return "outpost";
case RobotType::BASE:
return "base";
case RobotType::SENTRY:
return "sentry";
default:
return "unknown";
}
}
const char *to_string(RobotType robot);

/**
* @brief 字符串转为机器人类型
* @brief StateType 转为机器人类型
*
* @param[in] str 字符串
*/
constexpr RobotType to_robot_type(std::string_view str)
{
if (str == "hero")
return RobotType::HERO;
else if (str == "engineer")
return RobotType::ENGINEER;
else if (str == "infantry_3")
return RobotType::INFANTRY_3;
else if (str == "infantry_4")
return RobotType::INFANTRY_4;
else if (str == "infantry_5")
return RobotType::INFANTRY_5;
else if (str == "outpost")
return RobotType::OUTPOST;
else if (str == "base")
return RobotType::BASE;
else if (str == "sentry")
return RobotType::SENTRY;
return RobotType::UNKNOWN;
}
RobotType to_robot_type(const StateType &type);

//! @} combo_armor

Expand Down
76 changes: 76 additions & 0 deletions extra/combo/src/armor/armor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,82 @@
namespace rm
{

const char *to_string(ArmorSizeType armor_size)
{
switch (armor_size)
{
case ArmorSizeType::SMALL:
return "small";
case ArmorSizeType::BIG:
return "big";
default:
return "unknown";
}
}

ArmorSizeType to_armor_size_type(const StateType &tp)
{
if (tp.index() == 0)
return ArmorSizeType::UNKNOWN;

const auto &str = std::get<std::string>(tp);
if (str == "small")
return ArmorSizeType::SMALL;
else if (str == "big")
return ArmorSizeType::BIG;
return ArmorSizeType::UNKNOWN;
}

const char *to_string(RobotType robot)
{
switch (robot)
{
case RobotType::HERO:
return "hero";
case RobotType::ENGINEER:
return "engineer";
case RobotType::INFANTRY_3:
return "infantry_3";
case RobotType::INFANTRY_4:
return "infantry_4";
case RobotType::INFANTRY_5:
return "infantry_5";
case RobotType::OUTPOST:
return "outpost";
case RobotType::BASE:
return "base";
case RobotType::SENTRY:
return "sentry";
default:
return "unknown";
}
}

RobotType to_robot_type(const StateType &type)
{
if (type.index() == 0)
return RobotType::UNKNOWN;

const auto &str = std::get<std::string>(type);
if (str == "hero")
return RobotType::HERO;
else if (str == "engineer")
return RobotType::ENGINEER;
else if (str == "infantry_3")
return RobotType::INFANTRY_3;
else if (str == "infantry_4")
return RobotType::INFANTRY_4;
else if (str == "infantry_5")
return RobotType::INFANTRY_5;
else if (str == "outpost")
return RobotType::OUTPOST;
else if (str == "base")
return RobotType::BASE;
else if (str == "sentry")
return RobotType::SENTRY;
return RobotType::UNKNOWN;
}

std::shared_ptr<Armor> Armor::make_combo(LightBlob::ptr p_left, LightBlob::ptr p_right, const ImuData &imu_data,
double tick, ArmorSizeType armor_size_type)
{
Expand Down
2 changes: 1 addition & 1 deletion extra/decider/src/rune_decider/rune_decider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ DecideInfo RuneDecider::decide(const std::vector<group::ptr> &groups, const Stat
for (auto &p_tracker : groups.front()->data())
if (p_tracker->state().at("rune") == flag.at("rune"))
true_trackers.emplace_back(p_tracker);
bool is_active = flag.at("rune") == "active";
bool is_active = flag.at_string("rune") == "active";

if (last_target != nullptr)
for (auto &p_tracker : true_trackers)
Expand Down
2 changes: 1 addition & 1 deletion extra/detector/src/armor_detector/find.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ void ArmorDetector::eraseErrorArmors(std::vector<Armor::ptr> &armors)
void ArmorDetector::eraseFakeArmors(std::vector<Armor::ptr> &armors)
{
armors.erase(remove_if(armors.begin(), armors.end(), [&](Armor::ptr &it) {
return it->state().at("robot") == "unknown";
return it->state().at_string("robot") == "unknown";
}),
armors.end());
}
Expand Down
2 changes: 1 addition & 1 deletion extra/detector/src/gyro_detector/find.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ void GyroDetector::eraseErrorArmors(std::vector<Armor::ptr> &armors)
void GyroDetector::eraseFakeArmors(std::vector<Armor::ptr> &armors)
{
armors.erase(std::remove_if(armors.begin(), armors.end(), [](Armor::const_ptr it) {
return it->state().at("robot") == "unknown";
return it->state().at_string("robot") == "unknown";
}),
armors.end());
}
Expand Down
Loading

0 comments on commit 453400c

Please sign in to comment.