SimCore是一个极致轻量化的框架,旨解耦和管理ROS2中的仿真节点启动脚本.
- 解耦设计: 所有仿真节点都是独立的类,仿真节点之间可以完全分离开,仅仅只需要知道其他仿真节点的构造方法
- 依赖注入: 自动处理
Singleton(共享实例)和Transient(新实例)的依赖 - 集中管理: 能够扫描所有仿真节点并且直接从框架启动,无需亲自查找仿真节点的代码
- 克隆项目到ROS2工作空间
- 安装依赖:
pip3 install cmd2
- 构建
colcon build --packages-select sim_core . install/setup.bash
simc是SimCore提供的CLI/REPL工具,是整个框架的入口
在终端输入simc即可打开交互式终端
$ simc
SimCore Shell
Type 'help' or '?' to list commands.
simc> scan
[*] Scanning nodes...
[+] Found 3 nodes:
1. chassis_node (sim_pkg.chassis)
2. lidar_node (sim_pkg.lidar)
3. brain_node (sim_pkg.brain)
simc> run brain_node
[*] Loading 1 nodes...
[*] Bringing up nodes:
-> Starting chassis_node...
[OK] chassis_node is up.
-> Starting lidar_node...
[OK] lidar_node is up.
-> Starting brain_node...
[OK] brain_node is up.
[+] All tasks finished.
simc> quit
[*] Cleaning up...
[+] Bye.
# 先扫描所有仿真节点
simc scan
# 检查所有依赖项
simc check
# 启动仿真节点,并自动启动其依赖项
simc run brain_node| 命令 | 描述 |
|---|---|
| scan | 发现在工作区中所有可用的 sim_node 类 |
| run ... | 加载并启动节点 (自动bringup所有依赖项),如果加上--only,则依赖项只会构造而不会bringup |
| check | 执行静态分析以查找缺失的依赖项 |
| deps | 显示特定节点的依赖树 |
| info | 显示节点的代码位置 (模块) 和文档 |
| status | 显示已扫描和当前已加载/正在运行的节点计数 |
| version | 显示当前的 SimCore 框架版本 |
| quit / exit | 退出 shell 并清理所有正在运行的节点 |
在python文件中继承sim_node即可
示例: my_robot.py
import logging
from simcore import sim_node
class EmptyWorld(sim_node):
name='empty_world'
def bringup(self):
logging.info('empty_world开始启动')
logging.info('empty_world启动完成')
def cleanup(self):
logging.info('empty_world清理完成')
class TestWorld(sim_node):
name='test_world'
singletons=['empty_world']
para:int = 0
def bringup(self):
logging.info('test_world启动')
logging.info(f'para:{self.para}')
def cleanup(self):
logging.info('test_world清理')
class RobotA(sim_node):
name='robot_a'
singletons=[('test_world',{'para':1}),
('test_world',{'para':2})
]
def bringup(self):
logging.info('robot_a 启动')
def cleanup(self):
logging.info('robot_a 清理')
def hello(self):
print(self.name+' saying ciallo')
class RobotB(sim_node):
name='robot_b'
singletons=[('test_world',{'para':3}),
('test_world',{'para':2})
]
transients=[('robot_a',{})]
#声明,后续会通过依赖注入赋值
#如果不声明也能用,声明只是为了方便代码提示和编译检查
robot_a: RobotA
def bringup(self):
logging.info('robot_b 启动')
logging.info('robot_b正在调用robot_a')
self.robot_a.hello()
def cleanup(self):
logging.info('robot_b 清理')SimCore 需要知道在哪里可以找到您的新节点。您可以通过在 package.xml 文件的 部分中添加 一个 标签来注册它。
示例: package.xml
<package format="3">
<name>my_sim_package</name>
<!-- ... other tags ... -->
<export>
<build_type>ament_python</build_type>
<!-- 在这里声明包含 sim_node 类的文件,支持模式匹配,允许多行或者使用逗号,分号分隔 -->
<sim_core_entrypoint>
scripts/my_robot.py <!--这个文件默认应该放在ros2包内的{ros2包名}/{ros2包名}/script/my_robot.py-->
<!--也就是说这个目录并不是在项目中的目录,而是ros2包安装后share目录下的相对路径-->
</sim_core_entrypoint>
</export>
</package>如果使用ament_cmake,需要在CMakeLists.txt加上这一段:
# 把python入口文件安装到share目录下
install(PROGRAMS
my_robot.py
DESTINATION share/${PROJECT_NAME}
)
# 安装package.xml (可选,如果使用默认入口文件名sim_core_entrypoint.py可以不写)
install(FILES
package.xml
DESTINATION share/${PROJECT_NAME}
)如果使用ament_python,需要在setup.py修改:
data_files=[
('share/ament_index/resource_index/packages',
['resource/' + package_name]),
('share/' + package_name, [
'package.xml',
package_name+'/srcipt/sim_core_entrypoint.py' #添加这一项,并填入实际路径
#如果有通配符,推荐使用os.path + glob来实现,具体请查阅相关资料
]),
],