ros2_control_py 使用教程
系列文章目录
前言
使用 pybind11 为 ros2_control 进行 Python 绑定
一、安装
假设你的 ros2_control_py 工作区位于 ~/ros2_control_py_ws 目录中:
mkdir -p ~/ros2_control_py_ws/src
cd ~/ros2_control_py_ws/src
git clone https://github.com/Gepetto/ros2_control_py --recursive
cd ~/ros2_control_py_ws
rosdep install --from-paths src -y --ignore-src
编译
cd ~/ros2_control_py_ws
colcon build
测试
cd ~/ros2_control_py_ws
colcon build --cmake-args ' -DCMAKE_BUILD_TYPE=Debug' ' -DSANITIZE=True'
colcon test
使用方法
参见文档和测试。
二、目录
2.1 解析模块
- controller_interface
- controller_manager
- hardware_interface
- joint_limits
- ros2_control_test_assets
- transmission_interface
2.2 经测试的功能
hardware_interface
- parse_control_resources_from_urdf
- StateInterface / CommandInterface (see FloatRef and FloatRefProp)
- Actuator / Sensor / System
- ActuatorInterface / SensorInterface / SystemInterface
- ResourceManager
rclcpp
- FloatRef
2.3 新接口
2.3.1 rclcpp
因为 rclpy 和 rclcpp 是对 rclc 的绑定,而且 ros2 控制是通过 rclcpp 而不是 rclc 编写的、
我们需要在 rclcpp 和 rclcpp_lifecycle 上提供一些绑定,这样才能正常工作。
- init / shutdown / ok
- Time / Duration
- State
- LifecycleNodeInterface / CallbackReturn
- Node / NodeBase / NodeBaseInterface
- Executor / StaticSingleThreadedExecutor
- FloatRef / FloatRefProp
- VectorString / VectorDouble (see StlBindings)
2.3.2 FloatRef
FloatRef 是对 double 的引用,在 Python 中表现为类似浮点的对象。
在 C++ 中,对于需要它的接口,它可以分解为 double 或 double*。
其目的是与 StateInterface / CommandInterface 配合使用。
警告
虽然可以使用赋值运算符,如 +=、
但不能使用 = 对 FloatRef 进行赋值。
请参阅 FloatRefProp 中的 @ / @= / set_value。
使用方法
from ros2_control_py.hardware_interface import CommandInterface, HW_IF_VELOCITY
from ros2_control_py.rclcpp import FloatRef
fr = FloatRef(5)
assert fr == 5
fr += 3
assert fr == 8
fr.set_value(5 / 2)
assert fr == 2.5
fr @ 8
assert fr == 8
fr @= 7
assert fr == 7
ci = CommandInterface("name", HW_IF_VELOCITY, fr)
assert ci.get_value() == 7
fr @= 4
assert ci.get_value() == 4
ci.set_value(5)
assert fr == 5
2.3.3 FloatRefProp
FloatRefProp 是一个类似于属性的描述符,用于处理实例宽的 FloatRef,这样就可以像分配普通浮点数一样分配给它们。
使用方法
from ros2_control_py.hardware_interface import CommandInterface, HW_IF_VELOCITY
from ros2_control_py.rclcpp import FloatRefProp
class Dummy:fr = FloatRefProp(5)def __init__(self):self.ci = CommandInterface("name", HW_IF_VELOCITY, self.fr)d = Dummy()
assert d.fr == 5
assert d.ci.get_value() == 5
d.fr += 3
assert d.fr == 8
d.fr = 5 / 2
assert d.fr == 2.5
d.fr = 4
assert d.ci.get_value() == 4
d.ci.set_value(5)
assert d.fr == 5
2.3.4 StlBindings
在 python 界面中使用 stl 容器(std::vector、std::map、std::set 等)时、
时,我们需要使用专门的绑定来实现双向更改。
这只在某些情况下需要,主要是字符串/双数的容器、
在其他情况下,可以使用简单的列表,但要注意:当传递到 C++ 接口时,列表将被复制或移除。
(对于这些类型,不能对容器进行引用,而只能进行复制)。
所有这些绑定都位于 PascaleCase 的 rclcpp 模块中。
(例如:std::vector<std::string> => VectorString)。
三、生成器
解析 C++ 并输出 pybind11 绑定的可执行文件的内部文档。
3.1 编码约定
本项目使用 C++20 编写。
只有一个翻译单元(.cpp 文件),即 main.cpp。
程序的每一部分都有自己的头文件。
每个头文件都分为 .hpp 声明文件和 .hxx 定义文件。
3.2 项目结构
- utils/: 提供 utils.hxx 以外所有实用程序的文件夹。
- hash.hpp:boost 的 combine_hash 函数以及 std::pair 和 std::tuple 的 hash。
- ptr_iter.hpp:ptr_iter 函数,接收指针类对象的范围,并通过引用返回指向对象的范围。
- sep.hpp: 用于轻松输出范围的 Sep 结构,支持分隔符对象和元素投影函数。
- main.cpp: 协调程序并处理后解析步骤。
- parse.hpp: 处理预解析和解析步骤。
- structs.hpp: 存储已解析程序信息的结构体集合。
- utils.hpp: 提供 ASSERT 和 ASSERT_DIR 宏、字符串处理函数以及 utils/ 文件夹中的所有内容。
- write.hpp: 根据收集到的数据写入 pybind11 绑定。
3.3 程序步骤
预解析: 删除下一步不处理的 C++ 特征。
解析: CppParser 对修改后的源代码进行解析,并将收集到的信息重新组合为数据结构。
后解析: 这一步计算绑定所需的信息,如类层次结构,并添加 rclcpp 绑定。
编写: 然后,我们根据收集到的数据编写绑定。
3.3.1 前解析
调用 utils.hpp 中的 remove_attributes 函数移除解析器不支持的 C++ 特性。
该函数将删除
属性:又名 [[...]]
数字分隔符:又名 d'ddd'ddd,其中 d 代表数字,该操作保留了数字。
原始字符串文字:又名 R“(...)”,用空字符串替换。
模板参数:又名模板 <...>,用模板 <> 代替。
删除模板参数是因为 CppParser 不会处理非类型的模板参数。
3.3.2 解析
CppParser 被称为 CppParser,是 salehjg 的 CppParser 的分叉、
的 CppParser 的分叉。
salehjg 从项目中删除了 boost 的内部副本。
在我们的分叉中,我们修正了 CMakeLists 并重新修改了代码
使其不会产生警告。
3.3.3 后解析
这部分从上一步中提取数据结构,并以下列方式对其进行修改:
查找应绑定的 stl 容器。
加载 rclcpp 绑定。
查找所有母类,并找出哪些头文件需要其他头文件。
继承虚拟成员等。
找出哪些函数/成员函数被重载。
计算头文件的绑定顺序。
编写
TODO.