1、dora简介
Dora-rs[1] 是一个基于 Rust 实现的化机器人框架,其具有极高的实时性能。Dora-rs使用Rust语言做数据流的传输和调度管理,可以大大减少了数据的重复拷贝和传输。它提供了Rust语言和Python语言之间的无缝集成,减少了跨语言的性能代价。Dora-rs通过YAML脚本配置节点、节点之间的数据流。
多语言支持:Dora 目前提供 Rust 、Cpp 、Python三种语言。
性能:Dora-rs 性能是ROS2 Python API 的 17 倍!是 ROS2 Rust API 的 10 倍!与 ROS2 C/Cpp API 共享内存快 0.06 ms。(图片来源于github[1]) (对Python有很强的优化能力,对C++性能提升不大)
2、dora安装
dora_安装文档(官方提供三种安装方法,推荐使用二进制安装):https://dora.carsmos.ai/docs/guides/Installation/installing/
注意python版本 必须要3.11.0
我使用的环境是ubuntu20.04 + conda, 推荐安装conda然后再安装python https://blog.csdn.net/wyf2017/article/details/118676765
2.1 二进制安装
创建一个install_dora.sh文件,填入以下内容
export DORA_VERSION=v0.3.0 # Check for the latest release
export ARCHITECTURE=$(uname -m)
wget https://github.com/dora-rs/dora/releases/download/${DORA_VERSION}/dora-${DORA_VERSION}-${ARCHITECTURE}-Linux.zip
unzip dora-${DORA_VERSION}-${ARCHITECTURE}-Linux.zip
pip install dora-rs==${DORA_VERSION} ## For Python API
PATH=$PATH:$(pwd)
dora --help
在终端中运行
sudo ./install_dora.sh
若新建一个终端无法识别dora命令 则把 PATH=$PATH:$(pwd) 加入到 .bashrc中最后一行
如果提示以下错误
更新一下rustc的版本
apt autoremove rustc
apt install rustc
或者 通过以下命令更新rustc版本
sudo apt autoremove rustc
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
export PATH=~/.cargo/bin:$PATH
sudo apt install cargo
3、运行测试程序
与官方给出的demo程序相同,我们先试用python创建程序验证程序dora-rs是否安装完整
3.1 first project
在终端中输入以下命令,创建dora工程(工程名为abc_project )
dora new abc_project --lang python
cd abc_project
打开该文件夹,这个工程下面创建一个yaml文件、一个节点文件夹,两个操作符(dora中称作operator,有点类似于功能节点、算子的概念), 该工程目录结构如下
├── dataflow.yml
├── node_1
│ └── node_1.py
├── op_1
│ └── op_1.py
└── op_2└── op_2.py
3.2 编写节点
1、其中dataflow.yml 文件的内容为:
nodes:- id: op_1operator:python: op_1/op_1.pyinputs:tick: dora/timer/millis/100outputs:- some-output- id: op_2operator:python: op_2/op_2.pyinputs:tick: dora/timer/secs/2outputs:- some-output- id: custom-node_1custom:source: python3args: ./node_1/node_1.pyinputs:tick: dora/timer/secs/1input-1: op_1/some-outputinput-2: op_2/some-output
2、node_1.py 文件的内容为:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-from dora import Nodenode = Node()event = node.next()
if event["type"] == "INPUT":print(f"""Node received:id: {event["id"]},value: {event["value"]},metadata: {event["metadata"]}""")
3、op_1.py 文件的内容为:
from typing import Callable, Optionalfrom dora import DoraStatusclass Operator:"""Template docstring"""def __init__(self):"""Called on initialisation"""passdef on_event(self,dora_event: dict,send_output: Callable[[str, bytes, Optional[dict]], None],) -> DoraStatus:if dora_event["type"] == "INPUT":return self.on_input(dora_event, send_output)return DoraStatus.CONTINUEdef on_input(self,dora_input: dict,send_output: Callable[[str, bytes, Optional[dict]], None],):"""Args:dora_input (dict): Input dict containing an `id`, `data` and `metadata`.send_output Callable[[str, bytes | pa.Array, Optional[dict]], None]:Function for sending output to the dataflow:- First argument is the `output_id`- Second argument is the data as either bytes or `pa.Array`- Third argument is dora metadata dicte.g.: `send_output("bbox", pa.array([100], type=pa.uint8()), dora_event["metadata"])`Returns:DoraStatus:CONTINUE means that the operator willkeep listening for further inputs.STOP means that the operator stop listening for inputs."""print(f"Received input {dora_input['id']}, with data: {dora_input['value']}")return DoraStatus.CONTINUEdef __del__(self):"""Called before being deleted"""pass
4、op_2.py 文件的内容为:
from typing import Callable, Optionalfrom dora import DoraStatusclass Operator:"""Template docstring"""def __init__(self):"""Called on initialisation"""passdef on_event(self,dora_event: dict,send_output: Callable[[str, bytes, Optional[dict]], None],) -> DoraStatus:if dora_event["type"] == "INPUT":return self.on_input(dora_event, send_output)return DoraStatus.CONTINUEdef on_input(self,dora_input: dict,send_output: Callable[[str, bytes, Optional[dict]], None],):"""Args:dora_input (dict): Input dict containing an `id`, `data` and `metadata`.send_output Callable[[str, bytes | pa.Array, Optional[dict]], None]:Function for sending output to the dataflow:- First argument is the `output_id`- Second argument is the data as either bytes or `pa.Array`- Third argument is dora metadata dicte.g.: `send_output("bbox", pa.array([100], type=pa.uint8()), dora_event["metadata"])`Returns:DoraStatus:CONTINUE means that the operator willkeep listening for further inputs.STOP means that the operator stop listening for inputs."""print(f"Received input {dora_input['id']}, with data: {dora_input['value']}")return DoraStatus.CONTINUEdef __del__(self):"""Called before being deleted"""pass
3.3 启动程序
1、启动数据流
dora start dataflow.yml --name first-dataflow
开启程序以后在终端会输出一个类似于 “6a9279a7-e048-4e28-9616-cb3ae0adb774” 的一长串数字,这是数据流ID
参数 --name 后面的名称是我们自定义的节点名称,后续查看日志文件等操作,可以利用该自定义的名称替换数据流ID
2、结束该数据流
dora stop --name first-dataflow
3.4 查看节点输出
dora 可以通过log文件 查看节点输出(这一点不方便)
dora logs first-dataflow op_1
dora logs first-dataflow op_2
dora logs first-dataflow custom-node_1
其中 first-dataflow 是该程序的名称,也就是我们再启动命令时候 "- -name"后面跟的参数; custom-node_1 是yaml文件中一个节点的名称,
参考资料
[1] https://github.com/dora-rs/dora
[2] https://dora.carsmos.ai/docs/guides/Installation/installing
dora-rs目前资料较少 欢迎大家点赞在评论区交流讨论(cenruping@vip.qq.com) O(∩_∩)O
或者加群水一波(1149897304)