- 2025-03-26
-
发表了主题帖:
具身智能机器人系统五-具身智能机器人大模型
具身智能机器人系统五-具身智能机器人大模型
加速机器人计算
实时性对于机器人非常重要,但是这在进入具身智能时代前,实时性难以实现,主要原因有两个,机器人的算力要求很高,第二是机器人的算法比较碎片化难以实现单硬件实现加速。
因此,机器人计算加速非常重要,本书列举了硬件定制机器人加速,初次之外,使用gpu加速和fpga也是常用的方法。本书列举了机器人定位,规划和控制加速模块,我来介绍以下其中一种的加速方式。
机器人定位加速
由于机器人的定位模块有多种算法且更具场景各有好坏,比如我们可以分为,有无地图,有无gps信号来分类。在室内无地图环境下,采取slam,在室内有地图时直接使用配准算法简单高效,室外环境又要使用视觉惯性测距。
那么我们现在有3个算法要根据不同的情况进行选择,我们来设计一个面向这些算法的芯片。
首先我们需要一个共享的视觉前端用来提取特征并且始终处于激活状态,后端通过分类激活不同的数据流。
我们先来介绍视觉前端:
介绍前我们先解释一些词语:SPM
Spatial Pyramid Matching(SPM)
核心思想
通过将图像划分为不同尺度的子区域(如 1×1、2×2、4×4 等),在每个子区域内提取局部特征(如 SIFT、HOG 等),再将这些特征聚合起来,形成多尺度的全局特征表示。这种分层结构能更好地捕捉图像的局部细节和全局空间信息。
工作流程
分块:将图像按金字塔结构分层划分(例如 L0 层为整张图,L1 层分为 2×2 块,L2 层分为 4×4 块)。
特征提取:对每个子块提取局部特征。
特征聚合:将各层子块的特征加权组合,形成最终的全局特征向量。
分类/匹配:利用 SVM 等分类器进行图像分类或相似性匹配。
MUX
通常是 Multiplexer(多路复用器) 的缩写,其核心功能是将多个输入信号合并为单个输出信号传输,并在接收端分离还原。以下是它在图像处理中的几种常见应用场景:
用途:将多个摄像头或传感器的图像/视频流合并为单一信道传输,降低硬件复杂度。
在图像处理领域,SB 和 FIFO 是两种常见的技术术语,通常与数据处理、硬件架构或算法实现相关。以下是具体解释:
SB
Stencil Buffer(模板缓冲区)
定义:一种专用缓冲区。
FIFO(First-In-First-Out,先进先出队列)
在图像处理系统中,FIFO 是一种数据管理机制,用于确保数据按接收顺序处理:
数据流控制:在流水线架构中缓冲输入/输出数据,避免处理速度不匹配导致的丢帧或卡顿。
同步时序:协调不同模块(如传感器、处理器、显示器)的时钟差异。
这个前端一个立体视觉系统如何通过左右两个摄像头(类似人眼)拍摄的图像,计算出物体的深度(3D信息),最终输出精准的匹配结果。整个过程分三步走:前端处理 → 核心计算 → 优化输出。
一、输入与预处理阶段
1. 输入图像(Image@1)
输入源:双目摄像头同步采集的左右图像(Left/Right Image@1)。
数据格式:RAW图像数据(可能为Bayer格式),分辨率由摄像头参数决定(如1280x720@30fps)。
传输方式:通过DMA(Direct Memory Access)控制器,绕过CPU直接从片外DDR内存搬运至片上缓存(SPM)。
2. 图像滤波(图像滤波波)
功能:消除传感器噪声,保留边缘信息。
算法
:硬件优化的双边滤波(Bilateral Filter)或导向滤波(Guided Filter)。
硬件加速:滤波核并行计算(例如3x3窗口),通过SPM缓存局部像素,减少DDR访问次数。
输出:滤波后的左/右图像数据(Left/Right Filtered Image)。
3. 特征点检测(特征点检测)
目标:提取鲁棒性强的关键点(Keypoints)及其描述符。
算法选择:
ORB(Oriented FAST and Rotated BRIEF):适用于嵌入式系统的低复杂度特征。
硬件优化:FAST角点检测器并行化(图中“L'F(1)-”模块),通过SIMD指令加速像素梯度计算。
关键输出:
特征描述符(L'(0)/L'(+)):二进制描述符(如BRIEF)存储于SPM。
时空关联数据(Temporal correspondences):通过“XUM”模块关联连续帧特征点,解决动态场景匹配问题。
二、特征传输与立体匹配阶段
4. 特征数据缓存(SPM与FIFO)
SPM(Scratchpad Memory):
层级设计:分为多级(如L'(0)/LP(0)/$P(0)),分别存储当前帧特征、历史帧特征及匹配中间结果。
复用策略:通过“SB(Shared Bus)”模块动态分配访问权限,避免多模块争抢带宽。
FIFO(先进先出队列):
作用:解决左右图数据处理速度差异(如右图延迟补偿),确保时序对齐。
深度配置:根据流水线延迟设定队列深度(例如8级FIFO应对3帧延迟)。
5. 立体匹配(Stereo Matching)
核心模块:差分计算(差分计算) + 描述符匹配(DREAMUXIX模块) + 求解器(最小二零)。
流程分解:
代价计算(差分计算):
输入:左图特征描述符(L'(0))与右图描述符(Rp)通过FIFO对齐。
计算方式:基于Hamming距离或Census变换的代价矩阵(Cost Volume)生成。
代价聚合(DREAMUXIX模块):
算法:自适应窗口聚合(Adaptive Support Weight),利用局部纹理信息调整权重。
硬件加速:窗口滑动计算通过行缓冲区(Line Buffer)实现,减少重复数据加载。
视差求解(最小二零求解器):
算法:半全局匹配(SGM)的能量函数最小化,采用动态规划(Dynamic Programming)或置信传播(Belief Propagation)。
输出:初始视差图(Disparity Map r(0)),分辨率与输入图像一致。
三、后处理与优化阶段
6. 匹配优化(MO, Matching Optimization)
输入:初始视差图(r(0))及空间-时间关联约束(Temporal/spatial correspondences)。
输出:优化后的视差图(Refined Disparity Map)。
7. 误差平滑(DR, Error Smoothing)
功能:消除视差图的局部突变与量化误差。
算法实现:
边缘感知滤波:联合引导图像(原左图)与视差图,使用联合双边滤波(Joint Bilateral Filter)平滑非边缘区域。
多尺度融合:通过金字塔下采样与上采样操作,保留远距离物体的细节。
硬件加速:滤波核计算复用图像滤波阶段的SPM缓存结构。
图像中的橙色算子构成特征提取模块,蓝色的算子构成空间匹配模块,绿色的构成时间匹配模块,由于无论是vio,slam还是匹配算法都需要用到以上三个模块。
采用以上的前端的优势是对多种可能的计算进行并行计算。在高层次上,特征提取同时可以处理左右的图像可以并行处理,因为立体匹配需要两个图像生成的特征点,立体匹配需要完成特征点描述后才计算。时间匹配只需要左侧图像进行操作,因此左侧图像在图像滤波后就可以开始时间匹配。
第二个优势是利用多种储存方式混合而成的片上储存网络,尽量让数据留在片内而不是频繁的与片外数据进行交互。前端算法包含许多模板操作,例如图像滤波中的卷积和立体匹配中的快匹配,因此使用模板缓冲区可以用来捕获模板操作中的数据重用。许多模板操作会按顺序重列表中读取,在这种情况下使用fifo队列是合适的。
然后我们来讨论一下后端算法,后端算法本质上都有一个计算模块,我们的想法就是加速这些计算模块,为了避免资源浪费并且提高效率,设计一个灵活的硬件架构更加合适。
总之,定位加速模块的整体设计体现机器人加速芯片设计的一个重要思想,依托算子而非算法进行优化的芯片设计思路,尽管算法的碎片化程度很高但是一些算子任然可以通用。作者使用过一种FPGA实现了算法的算子加速,FPGA会和主机进行通信,主机会获得前端处理的结果。并且平均帧数和功耗都有所优化。作责也使用过GPU计算加速这需要一定的计算核的读取和建立时间。另外,算法中的大量的稀疏矩阵在GPU中并没有明显的优势。
除此之外本书还介绍了其他加速模块,机器人的实时性及其加速器设计才刚刚引起研究者的重视还没有达到成熟的地步,如何对具身智能大模型进行加速,这是对当前机器人计算系统实时性必须面对的重要问题。
- 2025-03-21
-
发表了主题帖:
ROS2智能机器人开发实践-阅读分享11-ros2编译colcon build详细解析
ROS2智能机器人开发实践-阅读分享11-ros2编译colcon build详细解析
对于和我一样不是计算机出生的同学们一定很疑惑,colcon build到底干了啥?我来大概讲一下我的理解,我们来以一个c++的ros2工作空间为例子,这是一个工作空间的项目录,它包含很多东西不仅仅有src文件夹还有python和scripts和include和config文件夹
root@ubuntu:~/dev_ws/src/origincar/origincar_base# tree
.
├── CMakeLists.txt
├── config
│ ├── ekf.yaml
│ └── imu.yaml
├── include
│ └── origincar_base
│ ├── origincar_base.h
│ └── Quaternion_Solution.h
├── launch
│ ├── base_serial.launch.py
│ ├── ekf.launch.py
│ ├── origincar_bringup.launch.py
│ ├── __pycache__
│ │ └── testtwo.launch.cpython-38.pyc
│ └── robot_mode_description.launch.py
├── msg
│ └── Position.msg
├── package.xml
├── scripts
│ └── cmd_vel_to_ackermann_drive.py
└── src
├── origincar_base.cpp
└── Quaternion_Solution.cpp
8 directories, 15 files
在 ROS 2 中,colcon build 是用于构建 ROS 包的现代构建工具。它会根据你的工作空间(workspace)中的包定义(如 CMakeLists.txt 和 package.xml),完成代码编译、依赖管理、安装部署等任务。然后调用构建工具(如 CMake 或 Python setuptools来进行打包工作空间。
我们仔细查看cmake和package的里面的内容。以下是cmakelist.txt的内容:
# 设置 CMake 的最低版本要求为 3.5,确保兼容性和功能支持
cmake_minimum_required(VERSION 3.5)
# 定义项目名称为 origincar_base,并隐式初始化 CMake 变量(如 PROJECT_NAME)
project(origincar_base)
# 设置默认的 C 和 C++ 语言标准
# 如果用户未指定 CMAKE_C_STANDARD,则默认使用 C99 标准
if(NOT CMAKE_C_STANDARD)
set(CMAKE_C_STANDARD 99) # C99 标准提供现代 C 特性
endif()
# 如果用户未指定 CMAKE_CXX_STANDARD,则默认使用 C++14 标准
if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 14) # C++14 提供平衡的现代特性和兼容性
endif()
# 针对 GCC 或 Clang 编译器启用额外警告选项,提升代码质量
# CMAKE_COMPILER_IS_GNUCXX 表示 GNU 编译器,Clang 的 ID 匹配则检测 Clang
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic) # 启用所有警告、额外警告和严格模式
endif()
# 查找并加载 ROS 2 的构建系统依赖包
find_package(ament_cmake REQUIRED) # ROS 2 的 CMake 基础工具
find_package(geometry_msgs REQUIRED) # 几何数据类型支持
find_package(tf2_geometry_msgs REQUIRED) # TF2 与几何消息的转换
find_package(nav2_msgs REQUIRED) # 导航2相关消息定义
find_package(nav_msgs REQUIRED) # 导航基础消息
find_package(rclcpp REQUIRED) # ROS 2 C++ 客户端库
find_package(rclpy REQUIRED) # ROS 2 Python 客户端库(此处可能未直接使用)
find_package(sensor_msgs REQUIRED) # 传感器数据类型
find_package(std_msgs REQUIRED) # 标准基础数据类型
find_package(tf2 REQUIRED) # TF2 库
find_package(tf2_ros REQUIRED) # TF2 的 ROS 接口
find_package(std_srvs REQUIRED) # 标准服务定义
find_package(ackermann_msgs REQUIRED) # 阿克曼转向消息类型(用户自定义或第三方包)
find_package(origincar_msg REQUIRED) # 项目自定义消息包
find_package(serial REQUIRED) # 串口通信库(如 libserial)
find_package(rosidl_default_generators REQUIRED) # ROS 接口定义语言生成器
find_package(origincar_description REQUIRED) # 机器人描述文件(如 URDF)
find_package(robot_localization REQUIRED) # 机器人定位算法库
find_package(ldlidar REQUIRED) # 特定型号激光雷达驱动
# 生成自定义 ROS 接口文件(消息、服务、动作)
# 使用 rosidl_generate_interfaces 生成 Position.msg 对应的 C++ 和 Python 代码
rosidl_generate_interfaces(${PROJECT_NAME}
"msg/Position.msg" # 消息文件路径,生成在 devel/include/<project>/msg/
)
# 若启用测试(BUILD_TESTING 默认为 ON),配置代码检查工具
if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED) # 自动化代码风格检查工具集
ament_lint_auto_find_test_dependencies() # 自动发现测试依赖的 linter 工具
endif()
# 添加头文件包含路径,确保编译器能找到项目的头文件
include_directories(
include # 项目自定义头文件目录
)
# 定义源文件列表变量 origincar_base_node_SRCS
# 包含两个源文件:主节点实现和四元数解算工具
set(origincar_base_node_SRCS
src/origincar_base.cpp
src/Quaternion_Solution.cpp
)
# 创建可执行文件 origincar_base_node,并指定源文件
add_executable(origincar_base_node
src/origincar_base.cpp
src/Quaternion_Solution.cpp
)
# 为可执行文件添加依赖的 ROS 包和第三方库
# ament_target_dependencies 会自动处理包含目录和链接库
ament_target_dependencies(origincar_base_node
tf2_ros # TF2 ROS 接口
tf2 # TF2 库
tf2_geometry_msgs # TF2 与几何消息转换
rclcpp # ROS 2 C++ 客户端库
std_msgs # 标准消息类型
robot_localization # 定位功能
nav_msgs # 导航消息
std_srvs # 标准服务
sensor_msgs # 传感器数据
ackermann_msgs # 阿克曼转向消息
serial # 串口库
origincar_msg # 自定义消息包
origincar_description # 机器人描述
)
# 安装 Python 脚本到 lib/<project_name> 目录,确保运行时可访问
install(PROGRAMS scripts/cmd_vel_to_ackermann_drive.py
DESTINATION lib/${PROJECT_NAME}
)
# 安装可执行文件到 ROS 2 的标准位置
install(TARGETS origincar_base_node
# 可执行文件安装到 lib/<project_name>
DESTINATION lib/${PROJECT_NAME}
# 以下为传统 catkin 兼容性设置,ROS 2 中可能不必要
ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)
# 安装 launch 和 config 目录到 share/<project_name>
# launch 包含启动文件,config 存放参数配置文件
install(
DIRECTORY launch config
DESTINATION share/${PROJECT_NAME}
)
# 完成 ROS 2 包的配置,生成必要的环境钩子和包信息文件
ament_package()
find packages
内容详解:里面find packages 道理在哪里find.在 ROS 2 中,find_package(ament_cmake REQUIRED) 是 CMake 的 find_package 命令,用于查找名为 ament_cmake 的包。它的搜索路径由 CMake 的搜索规则和 ROS 2 的环境变量共同决定。
这个ament_make非常重要,一般只有官方的包下面有这个ament_make,它是 ROS 2 的构建系统核心工具,提供 CMake 宏和函数,用于:
编译 ROS 2 包。
导出包的依赖关系(如头文件、库文件、消息接口等)。
生成包的配置文件(如 YourPackageConfig.cmake)。
除此之外我们自己colcon build的文件会生成ament_index放置cmake信息它是 ROS 2 的资源索引工具,用于:
记录包的资源路径(如消息定义、服务定义、启动文件等)。
在运行时动态查找包的资源(例如 ros2 pkg list 命令会用到 ament_index)。
所以我们的c++的cmakelist里面一定要find这个ament_cmake,其实这里一般也可以find比如OpenCV 库(前提是你要安装,而且在/usr/lib/aarch64-linux-gnu/cmake/里面有这个opencv的配置文件)
下面是ament_make的地点。
root@ubuntu:/opt/ros/foxy/share/ament_cmake# ls
cmake environment local_setup.bash local_setup.dsv local_setup.sh local_setup.zsh package.dsv package.xml
rosidl_generate_interfaces
这个是ros2的特定用来生成ros2的消息接口和服务类型的东西,如果你需要使用就调用这个。
install
这个就相当于复制过去,这样做的原因是为了可以方便使用罢了。
差不多就这样了,一般情况下由于python不需要编译成二进制文件一般都是直接检查有没有语法错误之后直接复制到install里面,但是c++不一样,他需要你自己找到依赖的包然后把二进制代码放进install里面。
下面这个是package.xml
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<!-- XML 版本和格式验证声明 -->
<package format="3">
<!-- 包的基本信息 -->
<name>origincar_base</name> <!-- 包名,需与文件夹名一致 -->
<version>0.0.0</version> <!-- 版本号,建议遵循语义化版本规则(如 0.1.0) -->
<description>ROS2 origincar_base for origincar_base</description> <!-- 描述需更具体,例如说明包的功能 -->
<maintainer email="ps-micro@todo.todo">ps-micro</maintainer> <!-- 维护者邮箱需替换为真实地址 -->
<license>TODO: License declaration</license> <!-- 必须填写有效许可证(如 Apache-2.0、BSD-3-Clause) -->
<!-- 构建工具依赖 -->
<buildtool_depend>ament_cmake</buildtool_depend> <!-- ROS 2 核心构建系统 -->
<buildtool_depend>rosidl_default_generators</buildtool_depend> <!-- 用于生成消息/服务接口代码 -->
<!-- 测试依赖 -->
<test_depend>ament_lint_auto</test_depend> <!-- 自动化代码检查工具 -->
<test_depend>ament_lint_common</test_depend> <!-- 提供通用 lint 规则 -->
<test_depend>boost</test_depend> <!-- Boost 库(仅测试阶段需要) -->
<!-- 构建依赖 -->
<build_depend>tf2_geometry_msgs</build_depend> <!-- 编译时需要 TF2 几何消息支持 -->
<!-- 构建导出依赖 -->
<build_export_depend>tf2_geometry_msgs</build_export_depend> <!-- 下游包构建时需依赖的库 -->
<!-- 运行时依赖 -->
<exec_depend>rosidl_default_runtime</exec_depend> <!-- ROS 接口运行时支持 -->
<exec_depend>ament_index_cpp</exec_depend> <!-- ROS 资源索引的 C++ 实现 -->
<exec_depend>geometry_msgs</exec_depend> <!-- 几何消息类型 -->
<exec_depend>rclcpp</exec_depend> <!-- ROS C++ 客户端库 -->
<exec_depend>rclcpp_action</exec_depend> <!-- Action 接口的 C++ 支持 -->
<exec_depend>sensor_msgs</exec_depend> <!-- 传感器消息类型 -->
<exec_depend>std_msgs</exec_depend> <!-- 标准消息类型 -->
<exec_depend>std_srvs</exec_depend> <!-- 标准服务类型 -->
<exec_depend>tf2</exec_depend> <!-- TF2 库 -->
<exec_depend>tf2_ros</exec_depend> <!-- TF2 的 ROS 接口 -->
<exec_depend>rclpy</exec_depend> <!-- ROS Python 客户端库(若包含 Python 代码) -->
<exec_depend>serial</exec_depend> <!-- 串口通信库 -->
<exec_depend>nav2_msgs</exec_depend> <!-- 导航2消息类型 -->
<exec_depend>nav_msgs</exec_depend> <!-- 导航消息类型 -->
<exec_depend>ackermann_msgs</exec_depend> <!-- 阿克曼转向消息 -->
<exec_depend>origincar_msg</exec_depend> <!-- 自定义消息包 -->
<exec_depend>origincar_description</exec_depend> <!-- 自定义描述包 -->
<exec_depend>robot_localization</exec_depend> <!-- 机器人定位功能包 -->
<!-- 接口包组声明 -->
<member_of_group>rosidl_interface_packages</member_of_group> <!-- 表示此包定义了接口(需在 CMakeLists 中生成接口) -->
<!-- 导出构建类型 -->
<export>
<build_type>ament_cmake</build_type> <!-- 声明使用 ament_cmake 构建系统 -->
</export>
</package>
首先我们要知道无论是cpp还是python都有package.xml,这是一个申明文件和那个cmakelist.txt不一样,
CMakeLists.txt 配置了构建规则(如编译哪些文件、链接哪些库), package.xml 的作用是 声明包的元数据和依赖关系,二者分工明确:
文件
作用
CMakeLists.txt
定义如何编译代码(如源文件、头文件路径、链接库、生成可执行文件等)。
package.xml
定义包的元数据(名称、版本、维护者)和依赖关系(构建、运行时、测试依赖)。
我们来举一个例子:
开发者 A
创建包时:
在 package.xml 中声明依赖 rclcpp 和 geometry_msgs。
开发者 B
拉取代码后:
运行 rosdep install,自动安装所有 package.xml 中声明的依赖。
colcon build 根据 CMakeLists.txt 编译代码,并链接已安装的库。
系统工具:
ros2 run、ros2 launch 通过 package.xml 的元数据找到包的可执行文件。
ok ,大概就这样,至于linux系统和cmake的具体东西我也不是很了解,我就解析一下cmakelist和package是用来干嘛的,干了哪些活就行。
- 2025-03-10
-
发表了主题帖:
具身智能机器人系统四-具身智能机器人大模型
具身智能机器人系统四-具身智能机器人大模型
在chatgpt推出之后,人们并没有意识到自然语言模型可以用来和机器人领域结合,但是大语言模型的优秀上下文理解能力使得人们发现这种的大模型可以用来给机械分配任务,随后人们将视觉听觉,文本,定位等多种数据编码到一个空间以实现队机器人的控制。
chatgpt gor robotics的设计原则和工作流程:
第一步:设计封装一个机器人的函数库,主要包括机器人的一些基础功能,比如移动,物体检测
第二步:编写清晰的描述性提示词,提供给大模型进行控制,提示词需要队函数库进行详细的解释
第三步,第四步:分别进行仿真环境和实际环境中执行chatgpt输出的代码。
但是这个模型有很多局限性:
没有解决low-level control:首先,如果我们将机器人的控制拆分成这几个部分:high-level task,mid-level step,low-level control,high-level task到mid-level step是planning,mid-level step到low-level control通过定义API库来实现,那么high-level task——mid-level step——low-level control在未来其实就可以通过LLM打通。但是low-level control其实是个没有完美解决的问题!底层的执行需要用到传统控制方法,它在某些场景不鲁棒,泛化性不强,而且同样需要大量手工设计。部分机器人的low-level control目前还存在瓶颈
某些场景,语料库不丰富,LLM可能也不知道如何planning,不知道如何连接high-level task到mid-level step(比如足式机器人,LLM可能说不准机器人的腿要抬到多高)
多模态大模型的应用:RT(robotic transformers)(感兴趣可以直接搜这个模型)
指令和图像tokenization:RT-1通过将最新的6帧图像输入到基于ImageNet预训练的EfficientNet-B3网络来tokenizes这些图像,该网络使用6张300x300的图像,输出一张9x9x512的空间特征图,然后变平成81个512维的token输给后面的网络层来处理;指令首先通过universal sentense encoder进行编码,得到一个512维的向量,这个embedding然后输入到 identity-initialized FiLM层,加入到预训练的EfficientNet中用来训练图像编码器。整个图像和指令tokenization网络共有约6M参数,包括26层MBConv块和FiLM层,最终产生81个指令图像token;
TokenLearner:TokenLearner对81视觉指令token进行采样,最终获得仅仅8个tokens,输入到后续的Transformer层;
Transformer: 历史6张张图像指令对应的8 个token拼接在一起形成48个token(包括增加的位置编码),输入到Transformer骨干。Transformer是一个 decoder-only序列模型,它有8个self-attention层,总共约19M参数,最终输出action tokens;
动作tokenization:每个连续的动作被离散到256维的bin中,并被映射到其中之一;
Loss:标准分类交叉熵和随机mask
实时推理加速:1,使用token learner对原始token进行压缩;2,推理时保留历史5帧图像对应的指令图像token,而不必全部重新计算;分别加速2.4倍和1.7倍。
未来机器人的大脑如何思考?
1.端到端的设计思路
这种设计思路是把绝大部分工作交给大模型来处理,包括传感器的信号采集,机器人的本体的控制,都依赖大模型来发出指令,这种系统设计的好处就是除了大模型其他的子模型的功能比较单一,设计比较简单,同时,对于系统核心的大模型来说,这种系统的设计完全由数据驱动,更高的数据集和更高的数据质量几乎是大模型的唯一的驱动力同时这种设计可以使得不了解传统大模型的人设计系统。
然而这种系统的设计方法由缺陷这是显而易见的:实时性。
一般情况下,机器人需要较低的延迟和较高的频率,当大模型成为模型核心时这是难以实现的(大模型的参数量很大),其次,大模型需要进步就需要更多的高质量数据来训练。
2.改进传统的机器人系统
这种系统的思想就是大模型的优点在于他的决策比较强,那么我们就让小车只负责决策,至于机器人原本的定位,路径规划,避障等模块任然存在,这种系统利用了大模型的智能化能力同时也对大模型的使用频率做了限制,保证了实时性的要求。
构建具身智能基础模型
1.元学习
meta-learning是一种机器学习方法,他的目标时使模型能够快速适应新任务并提高学习效率。元学习的核心思想是学习如何学习可以在面对新任务是能够迅速进行调整和适应。
通常在机器学习里,我们会使用某个场景的大量数据来训练模型;然而当场景发生改变,模型就需要重新训练。但是对于人类而言,一个小朋友成长过程中会见过许多物体的照片,某一天,当Ta(第一次)仅仅看了几张狗的照片,就可以很好地对狗和其他物体进行区分。
元学习Meta Learning,含义为学会学习,即learn to learn,就是带着这种对人类这种“学习能力”的期望诞生的。Meta Learning希望使得模型获取一种“学会学习”的能力,使其可以在获取已有“知识”的基础上快速学习新的任务,如:
让Alphago迅速学会下象棋
让一个猫咪图片分类器,迅速具有分类其他物体的能力
需要注意的是,虽然同样有“预训练”的意思在里面,但是元学习的内核区别于迁移学习(Transfer Learning)。
2.上下文学习
GPT-n 系列的模型都属于自回归类的语言模型,所谓自回归模型就是根据当前输入预测下一个词,然后将预测结果和输入拼接再当做模型的输入再预测下一个词,这样循环往复。
而自回归模型的训练目标也很简单,就是从超大规模语料库中采样训练样本,模型根据输入输出一个概率向量(概率向量包含所有词的预测概率,对于GPT-3 模型来说,维度约1千多万),而因为文本数据自带标注所以我们是知道真实的下一个词,所以损失函数就采用得交叉熵。
然后研究人员发现,预训练好的 GPT-3 模型拥有一项神奇的能力,后来被称为:上下文学习(In-Context Learning)。
这项能力简单来说就是,预训练好的 GPT-3 模型在迁移到新任务上的时候并不需要重新训练,而只需要提供任务描述(这个任务描述是可选项)接着提供几个示例(任务查询和对应答案,以一对对的形式组织),最后加上要模型回答的查询。将以上内容打包一起作为模型的输入,则模型就能正确输出最后一个查询对应的答案。
举个例子:
比如现在想用 GPT-3 来做个翻译任务,翻译英文为法文。输入的格式如下:
首先第一行是对任务描述,告诉模型要做翻译,接下来三行就是示例,英文单词和对应的法文单词对,最后一行就是待翻译的英文单词。将以上内容整体作为 GPT-3 的输入,让模型去补全输出就能得到 cheese 对应的法文单词。
上下文学习非常的灵活,除了上面展示的翻译任务,还可以做语法修饰甚至写代码。而神奇的地方就在于,在 GPT-3 的训练过程中是并没有显式的提供,类似测试阶段任务描述加示例这样的训练数据。
当然 GPT-3 的训练数据量非常巨大(比如包含了 wiki, 书本期刊,reddit 上的讨论等等),或许里面就已经就包含了各种任务类似结构的数据,GPT-3 模型容量足够大能够将所有训练数据都记了下来。
模型预训练与微调
模型预训练(Pre-training)和模型微调(Fine-tuning)是深度学习中两种重要的技术,通常结合使用以提升模型在特定任务上的性能。以下是详细介绍:
模型预训练特点:
• 数据规模大:通常使用大规模数据集(如ImageNet、Wikipedia文本数据)进行训练。
• 计算成本高:需要大量计算资源和时间。
• 通用性强:学习到的特征或知识可迁移到多种任务中。
应用场景
• 计算机视觉:在ImageNet上预训练卷积神经网络(如ResNet、EfficientNet)。
• 自然语言处理:在大型文本语料库上预训练语言模型(如BERT、GPT)。
• 多模态任务:在图文数据上预训练多模态模型(如CLIP、DALL·E)。
预训练方法
• 监督学习:使用标注数据训练模型(如ImageNet分类任务)。
• 自监督学习:利用数据本身生成伪标签进行训练(如BERT的掩码语言建模)。
• 对比学习:通过最大化正样本对和最小化负样本对的相似度学习特征(如SimCLR、CLIP)。
模型微调(Fine-tuning)
• 在预训练模型的基础上,针对特定任务(通常数据规模较小)进行进一步训练,调整模型参数以适应该任务。
特点
• 数据规模小:通常使用特定任务的小规模数据集。
• 计算成本低:相比预训练,微调所需资源和时间较少。
• 任务特定性强:模型参数被调整以优化特定任务的性能。
微调方法
• 全参数微调:调整预训练模型的所有参数。
• 部分参数微调:仅调整部分参数(如最后一层或特定模块),其他参数冻结。
• 提示微调(Prompt Tuning):通过设计提示词(Prompt)引导模型适应任务,而不改变模型参数。
• 适配器微调(Adapter Tuning):在模型中插入小型适配器模块,仅训练适配器参数。
预训练与微调的关系
• 预训练是基础:提供通用特征表示,为微调奠定基础。
• 微调是关键:将通用模型调整为任务特定模型,提升任务性能。
• 协同作用:预训练和微调结合,既利用大规模数据的优势,又适应特定任务的需求。
预训练与微调的优势
• 减少数据需求:通过预训练,模型在小规模任务数据上也能表现良好。
• 加速收敛:预训练模型提供了良好的初始化参数,微调过程更快收敛。
• 提升性能:预训练模型通常比从头训练的模型性能更好。
-
发表了主题帖:
具身智能机器人系统三-基础模块(规划与控制)
具身智能机器人系统三-基础模块(规划与控制)
自主机器人需要在复杂的动态环境中进行安全,高效的导航,这对导航系统提出了较高的要求,为了实现这一个目标,机器人必须具备能处理各种障碍物和状况的运动规划和控制系统。规划系统的核心在于路径规划和轨迹规划两部分,他们分别解决如何找到路径和如何在动态环境中生成时间参数化的运动轨迹的问题。
路径规划
路径规划旨在找到从起点到终点在空间中的无碰撞路径,仅考虑几何位置,不涉及时间、速度等动态参数。路径通常由一系列离散点或连续曲线表示,核心目标是避障和最小化路径长度、曲率等指标。
轨迹规划
轨迹规划在路径基础上引入时间参数和动力学约束,生成带有速度、加速度等运动参数的可执行轨迹。需满足机器人的动力学限制(如最大速度、加速度)和任务需求(如时间最优或能耗最小)。
在路径规划中我们难以找到最优规划路径(在各个约束下最优的路径,这和解决完全np问题一样难以解决),面对这种状况有三类方法解决。
1. 基于成本函数的最优路径规划(变分方法)
原理:将路径规划视为连续空间中的泛函优化问题,通过最小化成本函数(如路径长度、曲率)求解最优路径。
成本函数:
例如,最小化路径长度可表示为泛函:(函数没法打不打了,打了也懒得看)结合合障碍物势场时,成本函数可能包含距离障碍物的惩罚项。
变分法:
使用欧拉-拉格朗日方程求解泛函极值。例如,最短路径问题中,解为连接两点的直线;若考虑平滑性约束,则可能得到样条曲线。
2. 图搜索方法
原理:将环境离散化为图结构(节点表示位置,边表示可行移动),搜索最优路径。
典型算法:
Dijkstra算法:
从起点出发,逐步扩展到所有节点,保证找到全局最短路径。成本函数为累积移动代价,适用于无权图。
A*算法:
引入启发式函数 h(n)(如欧氏距离到终点的估计),优先扩展 f(n)=g(n)+h(n) 最小的节点,平衡效率与最优性。
特点:适用于离散空间(如栅格地图),保证最优性但计算复杂度随地图规模增长。
3. 增量搜索方法
原理:在动态环境中高效更新路径,避免重新规划。
典型算法:
D* Lite:
反向搜索(从目标到起点),利用先前搜索信息动态调整路径。当检测到障碍物变化时,局部更新受影响节点的代价,并重新计算路径。
特点:适合实时避障,计算效率高于全局重规划。
最优轨迹规划
一、六元组定义
最优轨迹规划问题通常由以下 六元组 定义:
其中各元素含义如下:
符号
描述
起始状态(位置、速度等)
目标状态
自由空间(无碰撞区域)
动力学约束(速度、加速度、加加速度限制等)
成本函数(如时间、能耗、平滑性等)
时间范围(固定或自由时间)
生成满足六元组约束的轨迹 ,使得:
动态规划是很复杂的,但是可以把轨迹规划转换成路径规划来解决 。
基于深度学习的规划与控制
他的基本原理如上图所示,就是通过端到端的训练来基于奖赏和惩罚,在一般的智能体中常常包含行为决策和直接控制层次。
对于行为决策的意思是在自动驾驶中仅仅遵守交通规则是完全不够的,人类的经验也很重要,比如我们可以设定(超车,保持距离,让道)三个决策行为,我们可以通过训练来获得最优决策。
直接控制的意思就是直接控制马力和转向角,一般这个控制有一个状态空间和动作空间,通过对状态的了解来做出动作。(具体的太复杂了大家了解到自己看书吧,这个参考了很多文献和方法具体的我真不懂)。
下一章节我们就可以来分析具身智能机器人的大模型了,前面的控制算法我个人也是稀里糊涂看过来的终于来到我最感兴趣的部分了,我们下一章节见。
- 2025-03-04
-
发表了主题帖:
具身智能机器人系统二-基础模块(环境感知和定位)
具身智能机器人系统二-基础模块(环境感知和定位)
机器人计算系统
机器人的定义非常宽广,一切模拟人类或者生物行为和思想的机器的统称,大规模运用的机械臂扫地机器人都是机器人的一种,机器人的计算系统通过运用软件和算法实现功能,机器人的计算系统需要适配合适的硬件来满足任务。在近年来,机器学习,人工智能和现代控制等技术的大规模的运用提高了机器人的性能和灵活性,也使得机器人的系统越来越复杂(功耗也有上升),这使得计算系统需要综合考虑精度,性能和功耗进行综合考虑。
自主机器人和非自住机器人
非自主机器人不能根据环境的变化来自主决策和执行任务,而自主机器人配置有各种传感器和控制器,可以适应环境并且算法更加复杂,本书将以自动驾驶为例子来介绍自主机器人的计算系统。
自主机器人计算系统
自主机器人计算系统是指为机器人提供感知、决策、控制等核心能力的软硬件集成系统,其目标是实现机器人在复杂环境中自主完成任务的能力。这类系统融合了传感器技术、人工智能、实时计算、通信技术和机械控制等多学科技术,是机器人实现智能化的核心支撑。
系统组成
硬件层
传感器系统:包括摄像头(视觉)、激光雷达(LiDAR)、毫米波雷达、超声波、IMU(惯性测量单元)等,用于实时采集环境数据。
计算单元:高性能嵌入式处理器(如NVIDIA Jetson、高通机器人平台)、FPGA或定制化ASIC芯片,用于处理传感器数据、运行算法。
执行机构:电机、机械臂、轮式/足式底盘等,负责执行物理动作。
通信模块:5G/Wi-Fi/蓝牙等无线通信,支持多机器人协作或云端交互。
软件层
操作系统:机器人专用OS(如ROS/ROS 2、Ubuntu Core)提供任务调度、硬件抽象和通信框架。
算法模块:
感知算法:SLAM(同步定位与建图)、目标检测(YOLO、PointNet)、语义分割等。
决策规划:路径规划(A、RRT)、任务分配(强化学习、博弈论)、动态避障(DWA算法)。
控制算法:PID控制、模型预测控制(MPC)、仿生运动控制(如波士顿动力Atlas的平衡算法)。
人机接口:自然语言交互(NLP)、手势识别、AR/VR远程操控等。
自动驾驶
(没找到合适的图片就先用这个吧) 我们可以看出自动驾驶依赖于多个传感器融合,这些传感器对于大家来说都很常见:摄像头,激光雷达,毫米波雷达,gps,imu和电机控制(可以获得电机的转速等信息,车辆转向),这些数据的数据量是非常大的,因此对于车辆终端的硬件有很高的要求。
在获得了一些传感器数据后自动驾驶需要有一下任务需要完成:
自动驾驶计算系统的感知(感知周围)
自动驾驶系统的定位系统
路径规划
自动驾驶控制(发动机,转向系统等)
具身智能计算系统
一。具身智能的软件栈
多模态大模型赋予机器人语言理解和逻辑推理能力,使其成为机器人的大脑,在训练多模态大模型上一般有两种组织形式,分别为端到端软件架构,模块化软件架构。
二。具身智能机器人硬件计算平台
具身智能的硬件产品需要要求高算力,高实时性,低功耗,高并发性的要求,这对于硬件设计要求更高。
自主机器人的感知系统
随着自主机器人的快速发展,准确感知周围的环境非常关键。为了解决这一个挑战,计算机视觉领域提出了多种物体检测,深度估计和运动分析的方法,我们来介绍一下这些技术吧,由于我对于这些技术不胜了解故简单介绍。
物体检测
识别定位周围的物体。
一、物体检测的主要流程
输入与预处理
输入为图像或视频帧,通常进行尺寸归一化、去噪、色彩空间转换(如RGB转灰度)等预处理操作,以提高后续处理的效率和准确性。
特征提取
通过深度学习模型(如卷积神经网络CNN)提取图像的深层特征。例如,YOLO将图像划分为网格单元,每个单元预测多个边界框和类别概率;Faster R-CNN通过区域提议网络(RPN)生成候选区域后提取特征。
候选区域生成(仅Two-Stage算法)
Two-Stage算法(如Faster R-CNN)需首先生成可能包含物体的候选区域(Region Proposals),通常通过RPN或传统方法(如选择性搜索)完成,再对每个候选区域进行精细分类和定位。
分类与定位
分类:判断候选区域内的物体类别(如“猫”“汽车”)。
定位:通过回归算法调整边界框的坐标(中心点、宽高),使其更贴合物体边缘。
后处理优化
非极大值抑制(NMS):去除冗余的重叠边界框,保留置信度最高的预测框。
置信度阈值过滤:剔除得分低于设定阈值的预测结果,确保输出可靠性。
二、主流物体检测算法
根据检测流程的差异,算法可分为Two-Stage(两阶段)和One-Stage(单阶段)两类:
Two-Stage算法
特点:先生成候选区域,再分类与定位,精度高但速度较慢。
R-CNN系列
R-CNN:首开深度学习检测先河,通过选择性搜索生成候选区域,但计算冗余(每个区域独立处理)。
Fast R-CNN:引入RoI池化层,共享卷积特征,减少重复计算。
Faster R-CNN:用RPN替代选择性搜索,实现端到端训练,速度与精度平衡。
适用场景:医疗影像分析、工业质检等高精度需求领域。
One-Stage算法
特点:直接预测物体位置与类别,速度快但精度略低。
YOLO系列(You Only Look Once)
将图像划分为网格,每个网格预测多个边界框及类别,单次前向传播完成检测,适合实时场景(如自动驾驶)。
SSD(Single Shot MultiBox Detector)
在不同尺度的特征图上预测边界框,兼顾小物体检测与速度优势,常用于机器人导航。
三、算法性能对比
算法类型
代表模型
优点
缺点
适用场景
Two-Stage
Faster R-CNN
高精度,定位准确
速度较慢,计算复杂度高
高精度需求(如医学)
One-Stage
YOLO/SSD
实时性强,资源消耗低
小物体检测易漏检
自动驾驶、视频监控
四、未来趋势
轻量化模型:针对移动端设备优化计算效率(如YOLO的Tiny版本)。
多模态融合:结合激光雷达、红外等传感器数据提升复杂环境下的鲁棒性。
自监督学习:减少对标注数据的依赖,通过预训练模型泛化到新场景。
语义分割
语义分割是计算机视觉中的一项关键技术,旨在对图像中的每个像素进行分类,赋予其对应的语义类别标签。以下是对语义分割的详细介绍:
核心概念
像素级分类:与仅识别整张图像类别的传统图像分类不同,语义分割需为每个像素分配类别(如“车”“树”“天空”)。
区分实例 vs. 仅类别:语义分割不区分同类物体的不同个体(例如不分开两辆车),而实例分割则进一步区分同一类别的不同实例。
关键技术
全卷积网络(FCN):2015年提出,首次用卷积层替代全连接层,使网络可输出像素级预测。
编码器-解码器结构:
编码器(如ResNet、VGG)提取高层语义特征。
解码器(如U-Net的对称结构)逐步恢复空间细节,通过上采样还原分辨率。
跳跃连接:U-Net等模型通过连接编码器与解码器的对应层,融合浅层细节与深层语义,提升边缘准确性。
注意力机制:如PSPNet的金字塔池化模块,通过多尺度上下文信息增强分类精度。
除此之外,本书还介绍了立体视觉和光流,鸟瞰视角感知等,这些本人一点不了解就不过多介绍了。
自主机器人的定位系统
自主机器人的定位系统是其实现导航、避障和任务执行的核心模块,通过融合多传感器数据和算法,实时确定机器人在环境中的位置和姿态(Position & Orientation)
一、定位系统的核心目标
位置估计
确定机器人相对于全局坐标系(如地图)或局部参考系的坐标(x, y, z)。
姿态估计
获取机器人的航向角(yaw)、俯仰角(pitch)和横滚角(roll)。
实时性与鲁棒性
在动态环境中保持高频率(如100Hz)更新,并应对传感器噪声、环境变化等问题。
二、定位技术分类
1. 基于传感器的定位
惯性导航系统(INS)
原理:通过加速度计和陀螺仪测量运动加速度和角速度,积分计算位置和姿态。
优点:不依赖外部信号,适用于无GPS环境(如室内或隧道)。
缺点:误差随时间累积(漂移),需与其他传感器融合(如视觉或轮式编码器)。
应用:无人机悬停、水下机器人。
全球导航卫星系统(GNSS)
原理:接收GPS、北斗等卫星信号,解算地理坐标。
增强技术:RTK(实时动态定位)可将精度提升至厘米级。
局限性:室内、高楼间或多路径效应环境下失效。
激光雷达(LiDAR)
原理:发射激光束并接收反射信号,通过点云匹配(如ICP算法)与已有地图对齐。
典型应用:自动驾驶汽车(如Waymo)的高精度定位。
视觉定位
单目/双目摄像头:通过特征点匹配(如ORB-SLAM)或深度学习(如PoseNet)估计位姿。
事件相机:基于动态像素响应,适用于高速运动场景。
2. 基于地图的定位
先验地图匹配
步骤:预先构建环境的高精度地图(如2D栅格地图或3D点云地图),通过传感器数据实时匹配定位。
算法:粒子滤波(PF)、卡尔曼滤波(KF)或其变种(如扩展卡尔曼滤波EKF)。
SLAM(同步定位与地图构建)
原理:在未知环境中同时构建地图并定位,分为激光SLAM(如Hector SLAM)和视觉SLAM(如ORB-SLAM3)。
关键挑战:闭环检测(Loop Closure)以消除累积误差,动态物体干扰处理。
3. 混合定位(多传感器融合)
传感器融合框架
滤波方法:卡尔曼滤波(KF)、粒子滤波(PF)或因子图优化(如GTSAM)。
深度学习融合:使用神经网络(如LSTM)直接学习多传感器数据到位姿的映射。
典型方案
GPS + INS + LiDAR:自动驾驶中常用组合,GPS提供全局定位,LiDAR修正局部误差,INS填补信号丢失时的空缺。
视觉-惯性里程计(VIO):如MSCKF、VINS-Fusion,结合IMU和摄像头数据,提升动态场景鲁棒性。
三、核心算法与技术
1. 滤波与优化算法
卡尔曼滤波(KF)
适用于线性高斯系统,通过预测-更新步骤估计状态。
扩展卡尔曼滤波(EKF):处理非线性系统,但需线性化近似。
粒子滤波(PF)
基于蒙特卡洛方法,用大量粒子表示概率分布,适用于非高斯、非线性场景(如复杂地形机器人)。
图优化(Graph Optimization)
将定位问题建模为因子图,通过最小化误差函数优化位姿(如g2o、Ceres Solver工具库)。
2. 闭环检测与重定位
词袋模型(Bag of Words)
将场景特征编码为视觉词汇,快速匹配历史关键帧(如DBoW2库)。
深度学习闭环检测
使用卷积神经网络(CNN)提取场景全局描述符(如NetVLAD),提升复杂环境下的识别率。
四、挑战与前沿技术
动态环境适应
动态SLAM:通过语义分割(如Mask R-CNN)区分静态与动态物体,提升定位鲁棒性。
轻量化与低功耗
边缘计算:在嵌入式平台部署定位算法,减少对云端的依赖,嵌入式soc是普遍使用的但是fpga和专用芯片在降低延迟和功耗上有较大优势,但是由于可编程差,流片成本高因此难以适使用。
好了,这一章节的介绍就到这里了,下面其实还是有路径规划的部分,但是我觉得这一章很有意思我要单独讲,所以我们下一章再见吧,本书的介绍中有很多我个人的介绍,这是因为本书的内容有一些部分很专业(没看懂)因此我就介绍了一些自己了解的部分,实话实说本书内容非常丰富,值得一看。
-
回复了主题帖:
具身智能机器人系统阅读分享一
ljg2np 发表于 2025-3-2 19:10
大模型的缺点中生成命令不合理难以控制的问题,我觉得可以通过设计环境上下文,并引入回馈机制来降低此类 ...
是这样的,大模型就像一个黑盒子,我们没法精准控制他的回答
- 2025-03-01
-
发表了主题帖:
具身智能机器人系统阅读分享一
具身智能机器人系统阅读分享一
https://gitee.com/ZY19917620057/embodied-intelligent-robot
现在开始我的正式分享:这本书一共有5部分我想我会每一部分来介绍,书本写的很好,从总体的大的方向介绍了具身智能没有纠结于一些小的问题和细节。
第一部分:具身智能的过去于未来
embodied artificial intelligence(EAI)是一个多学科融合的研究领域,旨在研究探讨智能体如何于外部互动的问题,具身智能不仅仅纯在于算法中更是体现在智能体于外部环境的交互上,智能体必须具有和外部交互的能力。
现在我们介绍一下技术方向,除了基于大模型的人工智能其余的是传统方向,虽然阅读了但是确实不是很了解。
基于行为的人工智能
这个方向强调通过简单的行为来堆叠可以创建复杂的行为而不是再系统内有大量的已经编程好的模型或者预先设计好的状态,智能体需要通过不断的学习和试错来学习和进化。
受神经生物学启发的人工智能
略
进化机器人学
机器人或者他的控制器被迭代选择,复制和变异来改善性能采用达尔文的适者生存的策略原则来进行迭代。
基于大模型的智能技术
大语言模型再具身智能的应用是多方面的,语言的理解和生成能力,个性化执行和多模态交互,这是之前的技术无法带来的,当然他也有一些缺点比如资源消耗非常大,生成命令不合理难以控制等问题。
具身智能的基础大模型的分类
1.视觉基础大模型:依赖于大量图片数据标注来获得理解环境的能力,但是在陌生的环境中识别效果很差。
2.视觉生成模型:生成新的视觉内容来增加机器人模拟训练的数据集。
3.大语言模型:比如gpt等,使得人类可以和机器进行流畅的语言交流也是机器可以理解人类指令的重要模型。
4.视觉语言模型:结合视觉和语言的处理能力
5.多模态模型:整合多个传感器的信息提高了机器人对于环境的综合处理能力。
具身智能的设计自动化
具身智能的研发效率和性能可以通过设计自动化大幅提升,而仿真技术是具身智能机器人自动化的关键,在仿真环境中研究人员可以无风险尝试各种算法因此对于具身智能来说或者未来的机器人设计,仿真是一个非常重要的手段,仿真也需要大量的并行计算(它包含内部物理计算和渲染等),所以gpu在未来的具身智能中有很大的作用。
后续我会继续分享剩下四个内容。
- 2025-02-20
-
回复了主题帖:
读书入围名单: 《具身智能机器人系统》
个人信息无误,确认可以完成阅读分享计划
- 2025-02-01
-
发表了主题帖:
ROS2智能机器人开发实践-阅读分享9-机器人建模仿真
ROS2智能机器人开发实践-阅读分享9-机器人建模仿真
首先我们要清楚建模和仿真并不是直接关联的,因为仿真使用的是单独的gazeboo,我们可以只建模不仿真这是完全没有问题的。
再建模中我们可以使用urdf文件
先新建一个工作包空间(c和python类型都行,因为不需要写代码只需要launch发布节点就行)
root@ubuntu:~/dev_ws_voice/src# ros2 pkg create mycar_description --build-type ament_cmake
在urdf里面分为连杆和关节,连杆可以通过关节来定义位置,并且关节之间的位置会被一些节点以tf形式发出。我们这里来建模一个麦克纳姆轮小车和一个摄像头(建模比较简单,大家感兴趣细节可以前往图书观看参数细节)
<?xml version="1.0" ?>
<!-- 定义一下这个urdf的名字 -->
<robot name="wheeltec_robot">
<!-- link也称作连杆 -->
<link name="base_link">
<visual>
<!-- 基座坐标可以高一点-->
<origin xyz=" 0 0 0.05" rpy="0 0 0" />
<geometry>
<box size="0.4 0.26 0.03"/>
</geometry>
<material name="white">
<color rgba="1 1 1 1"/>
</material>
</visual>
</link>
<!--关节-->
<!--关节是连接两个连杆之间的关系-->
<joint name="left_wheel_joint" type="continuous">
<origin xyz="-0.15 0.15 0.05" rpy="1.57 0 0"/>
<parent link="base_link"/>
<child link="left_wheel_link"/>
<axis xyz="0 1 0"/>
<!--定义关节旋转轴为y轴-->
</joint>
<link name="left_wheel_link">
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<cylinder radius="0.1" length = "0.04"/>
</geometry>
<material name="black">
<color rgba="0 0 0 1"/>
</material>
</visual>
</link>
<joint name="right_wheel_joint" type="continuous">
<origin xyz="-0.15 -0.15 0.05" rpy="1.57 0 0"/>
<parent link="base_link"/>
<child link="right_wheel_link"/>
<axis xyz="0 1 0"/>
</joint>
<link name="right_wheel_link">
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<cylinder radius="0.1" length = "0.04"/>
</geometry>
<material name="black">
<color rgba="0 0 0 1"/>
</material>
</visual>
</link>
<joint name="left_front_joint" type="continuous">
<origin xyz="0.15 0.15 0.05" rpy="1.57 0 0"/>
<parent link="base_link"/>
<child link="left_front_link"/>
<axis xyz="0 1 0"/>
</joint>
<link name="left_front_link">
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<cylinder radius="0.1" length = "0.04"/>
</geometry>
<material name="black">
<color rgba="0 0 0 1"/>
</material>
</visual>
</link>
<joint name="right_front_joint" type="continuous">
<origin xyz="0.15 -0.15 0.05" rpy="1.57 0 0"/>
<parent link="base_link"/>
<child link="right_front_link"/>
<axis xyz="0 1 0"/>
</joint>
<link name="right_front_link">
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<cylinder radius="0.1" length = "0.04"/>
</geometry>
<material name="black">
<color rgba="0 0 0 1"/>
</material>
</visual>
</link>
<!--除了几个轮子我这里还需要添加我的单目摄像头在前面-->
<joint name="camera_joint" type="fixed">
<origin xyz="0.2 0 0.05" rpy="0 0 0"/>
<parent link="base_link"/>
<child link="camera_link"/>
</joint>
<link name="camera_link">
<visual>
<origin xyz="0 0 0" rpy="0 0 0"/>
<geometry>
<box size="0.02 0.08 0.06"/>
</geometry>
<material name="blue">
<color rgba="0 0 1 1"/>
</material>
</visual>
</link>
</robot>
写完urdf文件之后,我们需要启动两个节点来使用功能包进行转化,我们在这个工作包下的launch文件夹下面新建一mycar.launch.py(名字自己起).
import os
from launch import LaunchDescription
from launch.substitutions import LaunchConfiguration
from launch_ros.actions import Node
from launch_ros.substitutions import FindPackageShare
def generate_launch_description():
package_name = 'mycar_description'
urdf_name = "mycar.urdf"
ld = LaunchDescription()
pkg_share = FindPackageShare(package=package_name).find(package_name)
urdf_model_path = os.path.join(pkg_share, f'urdf/{urdf_name}')
#发布joint_state话题
robot_state_publisher_node = Node(
package='robot_state_publisher',
executable='robot_state_publisher',
name='robot_state_publisher',
arguments=[urdf_model_path]
)
# 这个通常和urdf在一起发布一些joint
# 这个节点读取joint_states (sensor_msgs/JointState)然后转换转换成tf
# 可能开发者忘记删除了
# 具体作用可以查看ros2的wiki
# http://wiki.ros.org/joint_state_publisher
joint_state_publisher_node = Node(
package='joint_state_publisher',
executable='joint_state_publisher',
name='joint_state_publisher',
)
ld.add_action(robot_state_publisher_node)
ld.add_action(joint_state_publisher_node)
return ld
然后我们需要在cmakelist2把我们的文件复制到install里面
install(DIRECTORY launch urdf
DESTINATION share/${PROJECT_NAME}
)
随后编译执行就行了
colcon build
source install/setup.bash
ros2 launch (自己填)
打开虚拟机可以看一下 topic然后打开rviz2之后添加一下tf就可以了。
-
发表了主题帖:
ROS2智能机器人开发实践-阅读分享8-cartograph建图
ROS2智能机器人开发实践-阅读分享8-cartograph建图
在讲这个之前我们需要了解slam的主要建图方向,滤波和图优化。
SLAM问题主要分为滤波和图优化两大类。以扩展卡尔曼滤波为例,滤波就是通过当前时刻状态+输入+运动模型来估计下一时刻的状态,然后通过观测模型来纠正,因为其计算量小,只考虑相邻帧,因此被广泛应用到嵌入式中。在图优化(graph-based slam)的方法中,处理数据的方式就和滤波不同了,它不是在线的纠正位姿,而是把所有的数据记录下来,最后一次算总账。
可以参考文章如下:【转】graph slam tutorial:1 什么是图优化 - 知乎
这些细节当然很复杂,咱们就直接使用代码来建图吧。由于使用slam-toolbox有点问题我们直接使用cartographer来进行建图仿真。
本文前置环境介绍,up主手上的开发板是rdkx3,刷了官方的20.04+foxy版本(其实也不是官方版本,而是地平线智能车竞赛的版本,里面有内置的初始化ros2代码。ros2 launch origincar_base origincar_bringup.launch.py 主要是启动和stm32板子的联系并且发布/odom等话题,毕竟建图有一些依赖,毕竟我这不是仿真,大家可以看一看手头上有没有啥小车)
我的激光雷达使用的是乐动的stl06nbj,这个型号应该是stl06p同一个版本,我使用的是相同的驱动代码没啥问题。AlgoPathfinder/sdk_ldrobotsensorteam_stl: sdk for ldrobot lidar这是github历程(主要是这个激光雷达二手99也挺便宜的,使用一个ch340的串口就可以读取了)
然后我的的嵌入式板子使用的是轮趣科技的stm32的ros开发板(四驱的版本)
自己搞了一个四轮的麦克纳木轮小车
大家有任何疑问可以询问我,分享可能不够仔细。
重要参考文章:10.5配置Fishbot进行建图
注意注意这是foxy版本,大家的jazzy版本大致相同但是总有一些可能不同,遇到问题可以自己搜索。
其次就是本书内容(书本上的注释更多但是步骤有点不详细)
ros2 pkg create mycar_cartographer
cd mycar_cartographer
mkdir config
mkdir launch
mkdir rviz
在config目录下创建mycar_2d.lua文件,这是一个建图的配置文件,我们来一一解释。
include "map_builder.lua"
include "trajectory_builder.lua"
options = {
--下面两个不过多解释了
map_builder = MAP_BUILDER,
trajectory_builder = TRAJECTORY_BUILDER,
--下面5个仔细解释,看下文
map_frame = "map",
tracking_frame = "base_link",
published_frame = "odom_combined",
odom_frame = "odom_combined",
provide_odom_frame = false,
-- false改为true,仅发布2D位资
publish_frame_projected_to_2d = true,
-- false改为true,使用里程计数据,这里需要使用里程计话题
use_odometry = true,
--是否使用gps
use_nav_sat = false,
--是否使用路标
use_landmarks = false,
-- 0改为1,使用一个雷达
num_laser_scans = 1,
-- 1改为0,不使用多波雷达
num_multi_echo_laser_scans = 0,
-- 10改为1,1/1=1等于不分割,将一次/scan分成一次一次来处理
num_subdivisions_per_laser_scan = 1,
--是否使用点云数据(这个应该是深度相机)
num_point_clouds = 0,
--查找tf变换坐标的超时时间
lookup_transform_timeout_sec = 0.2,
--发布submap子图的周期,单位为s
submap_publish_period_sec = 0.3,
--发布姿态的周期
pose_publish_period_sec = 5e-3,
trajectory_publish_period_sec = 30e-3,
rangefinder_sampling_ratio = 1.,
odometry_sampling_ratio = 1.,
fixed_frame_pose_sampling_ratio = 1.,
imu_sampling_ratio = 1.,
landmarks_sampling_ratio = 1.,
}
-- false改为true,启动2D SLAM
MAP_BUILDER.use_trajectory_builder_2d = true
-- 0改成0.10,比机器人半径小的都忽略
TRAJECTORY_BUILDER_2D.min_range = 0.15
-- 30改成3.5,限制在雷达最大扫描范围内,越小一般越精确些
TRAJECTORY_BUILDER_2D.max_range = 3.5
-- 5改成3,传感器数据超出有效范围最大值
TRAJECTORY_BUILDER_2D.missing_data_ray_length = 3.
-- 不使用IMU数据,大家可以开启,然后对比下效果(这里如果开启会有一些问题)
-- 我的tf节点里面没有imu_link(他的名字是groy_link貌似是),这里必须要imu_link
-- 而且建议imu_link和base_link之间转换为零平移(重合)
TRAJECTORY_BUILDER_2D.use_imu_data = false
-- false改成true,使用实时回环检测来进行前端的扫描匹配
TRAJECTORY_BUILDER_2D.use_online_correlative_scan_matching = true
-- 1.0改成0.1,提高对运动的敏感度
-- 仅当角度变化超过0.1弧度(~5.7°)时处理新扫描,减少冗余计算
TRAJECTORY_BUILDER_2D.motion_filter.max_angle_radians = math.rad(0.1)
-- 0.55改成0.65,Fast csm的最低分数,高于此分数才进行优化。
POSE_GRAPH.constraint_builder.min_score = 0.65
--0.6改成0.7,全局定位最小分数,低于此分数则认为目前全局定位不准确
POSE_GRAPH.constraint_builder.global_localization_min_score = 0.7
-- 设置0可关闭全局SLAM
-- POSE_GRAPH.optimize_every_n_nodes = 0
return options
map_frame = "map",
tracking_frame = "base_link",
published_frame = "odom",
odom_frame = "odom",
provide_odom_frame = false,
以下的解释由deepseek解释:
这几个参数涉及到ROS中的坐标系(TF)设置,理解它们的含义对于正确配置SLAM系统非常重要。首先我们先看一下我们的tf图和节点图再来讨论。
map_frame
含义:map_frame 是地图的坐标系名称,通常为 "map"。
作用:SLAM系统会构建一个全局地图,并将机器人定位到这个地图中。map_frame 就是这个全局地图的坐标系。
实际情况:如果你的系统中已经有 map 坐标系(例如通过其他SLAM系统或手动发布),确保这里的 map_frame 名称与现有系统一致即可。
2. tracking_frame
含义:tracking_frame 是机器人本体的坐标系名称,通常为 "base_link"。
作用:SLAM系统需要知道机器人本体的坐标系,以便将传感器数据(如激光雷达、IMU等)与机器人本体对齐。
实际情况:我的机器人本体坐标系是 "base_link",这里不需要修改。
3. published_frame
含义:published_frame 是SLAM系统发布的坐标系名称,这里设置为 "odom_combined"。
作用:SLAM系统会发布从 map_frame 到 published_frame 的位姿变换(即 map 到 odom_combined 的变换)。这个变换表示机器人在地图中的位置。
实际情况:如果你已经有 odom_combined(通过EKF融合得到的里程计),可以将 published_frame 设置为 "odom_combined",这样SLAM系统会发布 map 到 odom_combined 的变换。
4. odom_frame
含义:odom_frame 是里程计的坐标系名称,通常为 "odom"。
作用:SLAM系统需要知道里程计的坐标系,以便将里程计数据与地图对齐。
实际情况:如果你已经有 odom_combined,可以将 odom_frame 设置为 "odom_combined",这样SLAM系统会使用 odom_combined 作为里程计的坐标系。
5. provide_odom_frame
含义:provide_odom_frame 表示是否由SLAM系统提供 odom_frame。
作用:
如果设置为 true,SLAM系统会发布 odom_frame 到 base_link 的变换。
如果设置为 false,SLAM系统不会发布 odom_frame 到 base_link 的变换,而是依赖外部系统(如EKF)提供这个变换。
实际情况:因为你已经有 odom_combined(通过EKF融合得到的里程计),所以这里应该设置为 false,表示SLAM系统不提供 odom_frame 到 base_link 的变换,而是使用 odom_combined。
我们来看一下实际的建图过程
[传感器数据源]
│
├─── /laserscan(激光雷达数据)
│
├─── /odom(里程计数据)
│
└─── /imu(IMU惯性测量单元数据)
│
│
▼
/cartographer_node(Cartographer核心处理节点)
│
├──── /submap_list(实时子地图列表)
│
└──── /occupancy_grid_node(最终栅格地图)
│
▼
/map(可视化地图)
[TF 坐标系流]
map(全局地图坐标系) → odom(里程计坐标系) → base_link(机器人本体坐标系)
▲ ▲
└───── 由 Cartographer 发布 ────────────┘
在路径src/mycar_cartographer/launch/下新建cartographer.launch.py文件,接着我们将cartographer两个节点加入到这个launch文件中。
import os
from launch import LaunchDescription
from launch.substitutions import LaunchConfiguration
from launch_ros.actions import Node
from launch_ros.substitutions import FindPackageShare
def generate_launch_description():
# 定位到功能包的地址
pkg_share = FindPackageShare(package='mycar_cartographer').find('mycar_cartographer')
#=====================运行节点需要的配置=======================================================================
# 是否使用仿真时间,我们用不使用gazeboo这里填写false
use_sim_time = LaunchConfiguration('use_sim_time', default='false')
# 地图的分辨率
resolution = LaunchConfiguration('resolution', default='0.05')
# 地图的发布周期
publish_period_sec = LaunchConfiguration('publish_period_sec', default='1.0')
# 配置文件夹路径
configuration_directory = LaunchConfiguration('configuration_directory',default= os.path.join(pkg_share, 'config') )
# 配置文件
configuration_basename = LaunchConfiguration('configuration_basename', default='mycar_2d.lua')
#=====================声明两个个节点,cartographer/occupancy_grid_node=================================
cartographer_node = Node(
package='cartographer_ros',
executable='cartographer_node',
name='cartographer_node',
output='screen',
parameters=[{'use_sim_time': use_sim_time}],
arguments=[
'-configuration_directory', configuration_directory,
'-configuration_basename', configuration_basename
],
# 添加重映射规则(格式:原始话题 → 新话题)
remappings=[
('/scan', '/scan'), # 激光雷达数据
('/odom', '/odom_combined'), # 里程计数据
# ('/imu', '/imu/data') # IMU 数据(按需添加)
]
)
occupancy_grid_node = Node(
package='cartographer_ros',
executable='occupancy_grid_node',
name='occupancy_grid_node',
output='screen',
parameters=[{'use_sim_time': use_sim_time}],
arguments=['-resolution', resolution, '-publish_period_sec', publish_period_sec])
#定义启动文件
ld = LaunchDescription()
ld.add_action(cartographer_node)
ld.add_action(occupancy_grid_node)
return ld
这里没有rviz2,大家可以自己在虚拟机执行。
打开CmakeLists.txt,添加下面一条指令,将三个目录安装到install目录。
install(
DIRECTORY config launch rviz
DESTINATION share/${PROJECT_NAME}
)
#编译执行
colcon build --packages-select mycar_cartographer
source install/setup.bash
ros2 launch mycar_cartographer cartographer.launch.py
我们打开rviz2来看一看咯
看见map显示没什么问题,我现在放在地上来跑一下。(在宿舍没啥位置后面去社团再试一试)
然后我们来保存一下地图
sudo apt install ros-foxy-nav2-map-server
先将地图保存到src/mycar_cartographer/map目录下
ros2 run nav2_map_server map_saver_cli -t map -f mycar_map
10.6导航地图概述
地图文件保存后我们可以直接加载使用了
本文tip(我的开发板上面有有好几个usb设备)
查看串口
ls命令:
ls -l /dev/tty*
查看有哪些设备连接在你的电脑上:
lsusb
产看串口个数以及对应的tty:
dmesg | grep ttyS*
-
发表了主题帖:
ROS2智能机器人开发实践-阅读分享7-ros2动作服务和参数
ROS2智能机器人开发实践-阅读分享7-ros2动作服务和参数
一个action里面常常含有请求,响应,还有一个反馈来实时检测响应的情况。
ros2 run learning_action action_move_server
ros2 run learning_action action_move_client
可以看到这里有feekback传回来,具体的代码大家可以自己下载源码自己查看,如有不会可以使用ai辅助,如图其实我也使用了marscode来辅助。
介绍参数
参数是ros2的一个全局字典,常常用来配置参数。举例:我们有一个相机的初始化函数,但是我们手上可能有多种不同类型的相机,他们的分辨率不一样我们不可能再源码中来不断的修改数据或者为了其他相机重建一个源码,因此我们需要再运行代码的时候传进去一个分辨率参数使得代码调整,这是python和c++非常常见的手段。
我们直接启用小乌龟节点
ros2 run turtlesim turtlesim_node
ros2 run turtlesim turtle_teleop_key
我们可以看到param的命令行使用,我们讲背景设置成绿色了,但是我们如果重启小乌龟节点后有变成蓝色了,这是因为重启后使用的是默认参数,因此如果我们想配置参数。
ros2 param dump turtlesim >> turtlesim.yaml #将某个节点参数保存到参数文件里面
ros2 param load turtlesim turtlesim.yaml #加载参数
大家可以保存完参数后关闭小乌龟然后重启加载配置。
使用完命令行后我们自己的工作包该如何使用参数来进行配置呢?
ros2 run learning_parameter param_declare
ros2 param set param_declare robot_name turtle
我这里修改了以下代码(如图),源代码并没有修改参数,ok就这样了。
最后的最后,再多介绍一点分布式通信远程登录,我们一般情况下会在板端部署ros2但是我们的开发板一般性能较弱对于可视化的支持不好,我们常常会使用各种可视化工具因此我们需要远程登录开发板获得话题信息再电脑端可视化。
这里我们假设自己手上有开发板比如:树莓派,地平线rdk,泰山派等
我们需要这些开发板连接一个路由器,然后电脑也连接同一个路由器然后插卡板卡的ip,如图我以我的rdkx3为例子:
这时我们的虚拟机的网络模式一定要设置为侨联模式,然后尝试 ping 192.168.142.91
可以ping通那说明没有问题!!!
以上就是关于本书的第六次分享,分享内容多为书本内容,本人在此由于篇幅有限只是介绍核心内容,其实还有很多没有讲,书中的前面的基础都已经讲完了,后面会分享以下ros2的常用工具!!!已经看的有点小累了,虽然书写的挺好,这个的电子版也有,本书使用说明 - 图书资源这本是古月居写的,可以在他们的官网看见,想要实体书的可以直接买。
-
发表了主题帖:
ROS2智能机器人开发实践-阅读分享6-ros2常用工具分享
ROS2智能机器人开发实践-阅读分享6-ros2常用工具分享
launch多节点启动和脚本配置
我们在启动节点时通常会想要一起启动多个节点时我们就可以使用launch工具:
ros2 launch learning_launch simple.launch.py
具体的代码如图所示,package是功能包的名字,executable是可执行文件的名字,除了需要编写launch.py文件,我们还需要把这个文件拷贝到install里面,这需要在setup.py里面实现。
data_files=[
('share/ament_index/resource_index/packages',
['resource/' + package_name]),
('share/' + package_name, ['package.xml']),
(os.path.join('share', package_name, 'launch'), glob(os.path.join('launch', '*.launch.py'))),
(os.path.join('share', package_name, 'config'), glob(os.path.join('config', '*.*'))),
(os.path.join('share', package_name, 'rviz'), glob(os.path.join('rviz', '*.*'))),
],
以上就是launch的使用方法,当然具体的细节大家可以自己查看代码理解。
tf机器人坐标管理系统
我们需要先下载tf的功能包:
sudo apt install ros-jazzy-turtle-tf2-py ros-jazzy-tf2-tools
#这里我的版本是foxy那么就将jazzy换成foxy
sudo pip3 install transforms3d
安装完成后
ros2 launch turtle_tf2_py turtle_tf2_demo.launch.py
ros2 run turtlesim turtle_teleop_key
然后我们可以查看tf树:
ros2 run tf2_tools view_frames
这里要注意啦,由于不同版本的执行文件的名字不同,如果无法执行可以尝试:
发现我的版本的名字果然不一样需要添加.py
然后打开这个pdf可以看见这个tf关系了。
当然这个很不直观我们可以使用rviz2来可视化:如图
以上我们了解了tf,然后我们来学习如何使用(发布接收tf),tf分为静态和动态,顾名思义,静态的一般发布后不再改变,动态需要实时更新。
可能会运行不成功,如果你发现你的static_tf_broadcaster里面的import tf_transformations 报错那么你需要再执行:sudo apt-get install ros-<ros_distro>-tf-transformations 里面的ros_distro是你的版本
ros2 run learning_tf static_tf_broadcaster
ros2 run tf2_tools view_frames
动态广播和静态及其类似,这里的动态广播是在启动小乌龟后,获得小乌龟的pose话题然后发出tf话题。
ros2 run turtlesim turtlesim_node
ros2 run learning_tf turtle_tf_broadcaster --ros-args -p turtlename:=turtle1
ros2 run tf2_tools view_frames
最后我们来看一下tf监听的代码
ros2 run turtlesim turtlesim_node
ros2 run learning_tf turtle_tf_broadcaster --ros-args -p turtlename:=turtle1
ros2 run learning_tf tf_listener --ros-args -p target_frame:=turtle1
ros2 run turtlesim turtle_teleop_key
随后我们操控键盘就可以看见tf在变化了,具体的代码大家可以自己看,其实不难理解,主要是一些消息接口大家可能有点迷糊可以使用ros2 interface show来查看。
gazebo:机器人仿真平台
gazebo分为两大版本类似于ros和ros2,gazebo classic是旧的版本(之前看鱼香ros的教程教的就是旧版本),于此对应的有gazebo sim 新版本。不同的ros2和gazebo版本有关,大家要自行选择啊,别安装了错误版本,但是gazebo官网有下载教程,网址如下:
Ubuntu 上的二进制安装 — Gazebo citadel documentation --- Binary Installation on Ubuntu — Gazebo citadel documentation
·
对于我个人的foxy版本对应的版本的安装代码(不用复制我的,前往官网复制):
sudo apt-get update
sudo apt-get install curl lsb-release gnupg
sudo curl https://packages.osrfoundation.org/gazebo.gpg --output /usr/share/keyrings/pkgs-osrf-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/pkgs-osrf-archive-keyring.gpg] http://packages.osrfoundation.org/gazebo/ubuntu-stable $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/gazebo-stable.list > /dev/null
sudo apt-get update
sudo apt-get install gz-harmonic
然后启动 ign gazebo
这个版本的ign gazebo取代了gz sim大家要注意,其他的倒没有什么关系。
rviz2 和rqt
这两个的使用大家后续用到再说吧,可以自己搜说一下如何下载,都是我们最为常用的工具。
以上就是关于本书的第六次分享,分享内容多为书本内容,本人在此由于篇幅有限只是介绍核心内容,本文我介绍了一些ros2常用工具,当然我介绍的并不详细,但是大家至少都知道了我们的主要的使用工具,在后续的ros2开发中同学们可以边开发边学习,后续我们会结合书本和rdkx3开发板尽可能以实物为主来给大家介绍(仿真实际上起到的作用有限,限于篇幅作用,我会优先结合实物进行分析)!!!呜呜呜,看书累死了。。。。。
-
发表了主题帖:
ROS2智能机器人开发实践-阅读分享5-ros2消息接口
ROS2智能机器人开发实践-阅读分享5-ros2消息接口
在前文学习中,我们学习topic和service都接触到了消息接口,他们分别是话题消息接口和服务消息接口,这些接口都是人为定义的,这些接口很重要,如图是三大主要服务的流程图,他们都有各自的消息接口。
这里的3个服务的话题类型如上图:所有的接口内部的数据的基础类型有以下几种:
zy@ubuntu:~/dev_ws$ ros2 interface package std_msgs
std_msgs/msg/UInt8
std_msgs/msg/Header
std_msgs/msg/MultiArrayDimension
std_msgs/msg/UInt64
std_msgs/msg/Float32MultiArray
std_msgs/msg/Int16MultiArray
std_msgs/msg/ByteMultiArray
std_msgs/msg/Char
std_msgs/msg/MultiArrayLayout
std_msgs/msg/UInt16MultiArray
std_msgs/msg/Int32
std_msgs/msg/Int32MultiArray
std_msgs/msg/UInt8MultiArray
std_msgs/msg/Int16
std_msgs/msg/ColorRGBA
std_msgs/msg/UInt32MultiArray
std_msgs/msg/Int8MultiArray
std_msgs/msg/Int64
std_msgs/msg/Int64MultiArray
std_msgs/msg/Bool
std_msgs/msg/Float64MultiArray
std_msgs/msg/UInt32
std_msgs/msg/UInt64MultiArray
std_msgs/msg/Byte
std_msgs/msg/UInt16
std_msgs/msg/Float64
std_msgs/msg/Int8
std_msgs/msg/Float32
std_msgs/msg/String
std_msgs/msg/Empty
通信接口命令行
ros2 interface list
ros2 interface show <interface_name>
ros2 interface package <package_name>
自定义接口
需要先创建一个c++的功能包
zy@ubuntu:~/dev_ws/src/ros2_21_tutorials$ ros2 pkg create --build-type ament_cmake learning_interface_zy
我们如图创建一个msg文件夹后创建一个.msg后缀的文件。
我们需要在cmakelist和package.xml里面添加东西:
cmakelist.txt
find_package(rosidl_default_generators REQUIRED)
rosidl_generate_interfaces(${PROJECT_NAME}
"msg/ObjectPosition.msg"
)
package.xml
<build_depend>rosidl_default_generators</build_depend>
<exec_depend>rosidl_default_runtime</exec_depend>
<member_of_group>rosidl_interface_packages</member_of_group>
colcon build --packages-select learning_interface_zy
source install/setup.bash
ros2 interface package learning_interface_zy
让背后你就看见了这个接口被定义好了,我们后续只需要调用就可以了。
同理,我们也可以定义srv
最后的action的接口我们后续有机会再讲吧。
以上就是关于本书的第五次分享,分享内容多为书本内容,本人在此由于篇幅有限只是介绍核心内容,本文我介绍了我们该如何创键我们自己的消息接口,在下次分享我将尽力分享动作action!!!
-
发表了主题帖:
ROS2智能机器人开发实践-阅读分享4-ros2服务通信
ROS2智能机器人开发实践-阅读分享4-ros2服务通信
服务通信
服务的通信类似与”你问我答“,当一个节点发送请求时,服务通信机制会前往一些节点获得反馈(response)。
并且服务通信是同步通信,它同时也有特定的服务接口,这和前面的topic接口不同,我们可以来查看一番,我们可以运行小乌龟程序然后咱们一起看一看有哪些service:
ros2 run turtlesim turtlesim_node
rcl_interfaces/srv/GetParameters服务
# TODO(wjwwood): Decide on the rules for grouping, nodes, and parameter "names"
# in general, then link to that.
#
# For more information about parameters and naming rules, see:
# https://design.ros2.org/articles/ros_parameters.html
# https://github.com/ros2/design/pull/241
# A list of parameter names to get.
string[] names
---
# List of values which is the same length and order as the provided names. If a
# parameter was not yet set, the value will have PARAMETER_NOT_SET as the
# type.
ParameterValue[] values
这个服务用于从 ROS 2 节点中获取参数值。以下是各字段的含义:
请求(输入)
string[] names:
这是一个字符串数组,每个字符串是你想要获取的参数的名称。例如:
names: ["param1", "param2"]
响应(输出)
ParameterValue[] values:
这是一个 ParameterValue 对象数组,对应于请求中参数的返回值。值的顺序与请求中的名称顺序一致。
如果某个参数未设置,其值的类型将为 PARAMETER_NOT_SET。
ParameterValue 是 rcl_interfaces 包中定义的一种 ROS 2 消息类型。它表示参数的值,可以是多种类型之一(例如整数、浮点数、字符串、布尔值等)。你可以通过以下命令查看其定义:
ros2 interface show rcl_interfaces/msg/ParameterValue
rcl_interfaces/srv/GetParameters 用于从节点中获取参数值。它接收一个参数名称列表作为输入,并返回对应的参数值。
服务通信编写代码
这样我们是不是就理解了,service的消息接口含有请求和响应,那么我们来写一个python的客户端和服务端。
我们新建两个python文件(在哪一个工作包都可以,我这里就在阅读分享2里面的工作包),service_adder_client.py和service_adder_server.py。
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#client文件
"""
@作者: 古月居(www.guyuehome.com)
@说明: ROS2服务示例-发送两个加数,请求加法器计算
"""
import sys
import rclpy # ROS2 Python接口库
from rclpy.node import Node # ROS2 节点类
from learning_interface.srv import AddTwoInts # 自定义的服务接口
class adderClient(Node):
def __init__(self, name):
super().__init__(name) # ROS2节点父类初始化
self.client = self.create_client(AddTwoInts, 'add_two_ints') # 创建服务客户端对象(服务接口类型,服务名)
while not self.client.wait_for_service(timeout_sec=1.0): # 循环等待服务器端成功启动
self.get_logger().info('service not available, waiting again...')
self.request = AddTwoInts.Request() # 创建服务请求的数据对象
def send_request(self): # 创建一个发送服务请求的函数
self.request.a = int(sys.argv[1])
self.request.b = int(sys.argv[2])
self.future = self.client.call_async(self.request) # 异步方式发送服务请求
def main(args=None):
rclpy.init(args=args) # ROS2 Python接口初始化
node = adderClient("service_adder_client") # 创建ROS2节点对象并进行初始化
node.send_request() # 发送服务请求
while rclpy.ok(): # ROS2系统正常运行
rclpy.spin_once(node) # 循环执行一次节点
if node.future.done(): # 数据是否处理完成
try:
response = node.future.result() # 接收服务器端的反馈数据
except Exception as e:
node.get_logger().info(
'Service call failed %r' % (e,))
else:
node.get_logger().info( # 将收到的反馈信息打印输出
'Result of add_two_ints: for %d + %d = %d' %
(node.request.a, node.request.b, response.sum))
break
node.destroy_node() # 销毁节点对象
rclpy.shutdown() # 关闭ROS2 Python接口
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#client文件
"""
@作者: 古月居(www.guyuehome.com)
@说明: ROS2服务示例-提供加法器的服务器处理功能
"""
import rclpy # ROS2 Python接口库
from rclpy.node import Node # ROS2 节点类
from learning_interface.srv import AddTwoInts # 自定义的服务接口
class adderServer(Node):
def __init__(self, name):
super().__init__(name) # ROS2节点父类初始化
self.srv = self.create_service(AddTwoInts, 'add_two_ints', self.adder_callback) # 创建服务器对象(接口类型、服务名、服务器回调函数)
def adder_callback(self, request, response): # 创建回调函数,执行收到请求后对数据的处理
response.sum = request.a + request.b # 完成加法求和计算,将结果放到反馈的数据中
self.get_logger().info('Incoming request\na: %d b: %d' % (request.a, request.b)) # 输出日志信息,提示已经完成加法求和计算
return response # 反馈应答信息
def main(args=None): # ROS2节点主入口main函数
rclpy.init(args=args) # ROS2 Python接口初始化
node = adderServer("service_adder_server") # 创建ROS2节点对象并进行初始化
rclpy.spin(node) # 循环等待ROS2退出
node.destroy_node() # 销毁节点对象
rclpy.shutdown() # 关闭ROS2 Python接口
写完代码后我们照常要在setup.py里面添加文件
entry_points={
'console_scripts': [
'node_helloworld_class = learning_pkg_python_zy.node_helloworld_class:main',
'topic_helloworld_sub = learning_pkg_python_zy.topic_helloworld_sub:main',
'topic_helloworld_pub = learning_pkg_python_zy.topic_helloworld_pub:main',
'service_adder_client = learning_pkg_python_zy.service_adder_client:main',
'service_adder_server = learning_pkg_python_zy.service_adder_server:main',
],
},
代码里面的服务接口的名称叫做AddTwoIntsm,这是一个自定义接口,需要我们自定义,这里可以看到这个消息接口的定义,输入为一个a和一个b输出为sum。
ok,我们在上面已经编译好了,然后我们首先要执行服务段:
ros2 run learning_pkg_python_zy service_adder_server
ros2 run learning_pkg_python_zy service_adder_client 2 3
效果如下:
服务通信补充
通常我们会编写终端指令来进行service,我们以上面的为例子:消息接口我们清楚那么来写一下,我们先打开服务端,然后再打开一个终端输入下面的指令,/add_two_ints是消息接口名称,后面是消息类型。
ros2 service call /add_two_ints learning_interface/srv/AddTwoI
nts "{a: 3, b: 5}"
- 2025-01-26
-
发表了主题帖:
ROS2智能机器人开发实践-阅读分享3-ros2节点和话题通信
ROS2智能机器人开发实践-阅读分享3-ros2节点和话题通信
话题是ros2最重要的通信方式,其余的三个通信都是以此为基础的。
继续上一次的分享,我们已经创建了一个python功能包,我建议大家可以在这个功能包里面添加功能文件,一个功能包可以添加很多个功能文件。
在此之前我们需要先配置开发环境,使用不方便的ubuntu自带的编辑器是很麻烦且不友好(其实书籍并没有提及这一点,因为此书主要是介绍ros2的),我在这里使用的是主机的vscode使用ssh连接虚拟机,实际上因为我比较懒不想再一次下载vscode了。如何连接大家可以自己搜索了解。
连接之后我们可以在我的指示的位置创建一个python文件,名字为node_helloworld_class.py
然后我们可以进行编写,代码如下:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
@作者: 古月居(www.guyuehome.com)
@说明: ROS2节点示例-发布“Hello World”日志信息, 使用面向对象的实现方式
"""
import rclpy # ROS2 Python接口库
from rclpy.node import Node # ROS2 节点类
import time
"""
创建一个HelloWorld节点, 初始化时输出“hello world”日志
"""
class HelloWorldNode(Node):
def __init__(self, name):
super().__init__(name) # ROS2节点父类初始化
while rclpy.ok(): # ROS2系统是否正常运行
self.get_logger().info("Hello World") # ROS2日志输出
time.sleep(0.5) # 休眠控制循环时间
def main(args=None): # ROS2节点主入口main函数
rclpy.init(args=args) # ROS2 Python接口初始化
node = HelloWorldNode("node_helloworld_class") # 创建ROS2节点对象并进行初始化
node.destroy_node() # 销毁节点对象
rclpy.shutdown() # 关闭ROS2 Python接口
这里的代码中我们创建一个类helloworld,它继承了Node类,在这个节点中我们不停的发布日志,最后我们实例化这个类。
随后我们需要对编译文件进行简单描述.在setup.py文件中修改文件如下:
'console_scripts': [
'node_helloworld_class = learning_pkg_python_zy.node_helloworld_class:main',
],
然后我们就可以编译运行了,这里的colcon build后面添加 --packages-select可以添加我们需要编译的功能包,我们这里只修改了一个功能包所以不要编译其他的。
cd ~/dev_ws
colcon build --packages-select learning_pkg_python_zy
source install/setup.bash
ros2 run learning_pkg_python_zy node_helloworld_class
在上面我们基本已经学会了使用节点,下面我们来添加一点其余东西-话题通信。
话题通信
我们要要了解话题是一个什么东西???我的理解是话题是一种数据形式,在代码中我们常用的有int,float等格式,但是对于ros2来说这些单独一个int数据几乎不能表达含义,比如一张rgb类型的image,我们需要数组来表示,除了图片外,我们通常还需要图片的拍摄时间,图片名字等,我们把他们组合在一起就是一个话题。
除了话题,我们还要了解通信机制特点,话题本身需要一个消息接口也就是数据的类型,如下图所示 sensor_msgs/msg/Image就是一种数据类型,这个数据的格式如下展现。
除了数据类型,话题通信的一大特点就是独立于节点,一个话题可以接收多个节点的数据,也可以给多个节点提供数据,话题和节点之间并没有耦合关系。
另外就是:异步。话题的发送和订阅是不同步的,发布节点和订阅节点并不知道这个话题啥时候订阅的也不知道啥时候发布的,所以叫做异步。
那么我们来写一个话题发布者和接收者吧!!!
我们在上节课node_helloworld_class.py的地方在此创建文件topic_helloworld_pub.py文件。
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
@作者: 古月居(www.guyuehome.com)
@说明: ROS2话题示例-发布“Hello World”话题
"""
import rclpy # ROS2 Python接口库
from rclpy.node import Node # ROS2 节点类
from std_msgs.msg import String # 字符串消息类型
"""
创建一个发布者节点
"""
class PublisherNode(Node):
def __init__(self, name):
super().__init__(name) # ROS2节点父类初始化
self.pub = self.create_publisher(String, "chatter", 10) # 创建发布者对象(消息类型、话题名、队列长度)
self.timer = self.create_timer(0.5, self.timer_callback) # 创建一个定时器(单位为秒的周期,定时执行的回调函数)
def timer_callback(self): # 创建定时器周期执行的回调函数
msg = String() # 创建一个String类型的消息对象
msg.data = 'Hello World' # 填充消息对象中的消息数据
self.pub.publish(msg) # 发布话题消息
self.get_logger().info('Publishing: "%s"' % msg.data) # 输出日志信息,提示已经完成话题发布
def main(args=None): # ROS2节点主入口main函数
rclpy.init(args=args) # ROS2 Python接口初始化
node = PublisherNode("topic_helloworld_pub") # 创建ROS2节点对象并进行初始化
rclpy.spin(node) # 循环等待ROS2退出
node.destroy_node() # 销毁节点对象
rclpy.shutdown() # 关闭ROS2 Python接口
然后我们创建订阅者,新建文件topic_helloworld_sub.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
@作者: 古月居(www.guyuehome.com)
@说明: ROS2话题示例-订阅“Hello World”话题消息
"""
import rclpy # ROS2 Python接口库
from rclpy.node import Node # ROS2 节点类
from std_msgs.msg import String # ROS2标准定义的String消息
"""
创建一个订阅者节点
"""
class SubscriberNode(Node):
def __init__(self, name):
super().__init__(name) # ROS2节点父类初始化
self.sub = self.create_subscription(\
String, "chatter", self.listener_callback, 10) # 创建订阅者对象(消息类型、话题名、订阅者回调函数、队列长度)
def listener_callback(self, msg): # 创建回调函数,执行收到话题消息后对数据的处理
self.get_logger().info('I heard: "%s"' % msg.data) # 输出日志信息,提示订阅收到的话题消息
def main(args=None): # ROS2节点主入口main函数
rclpy.init(args=args) # ROS2 Python接口初始化
node = SubscriberNode("topic_helloworld_sub") # 创建ROS2节点对象并进行初始化
rclpy.spin(node) # 循环等待ROS2退出
node.destroy_node() # 销毁节点对象
rclpy.shutdown() # 关闭ROS2 Python接口
前往setup.py
entry_points={
'console_scripts': [
'node_helloworld_class = learning_pkg_python_zy.node_helloworld_class:main',
'topic_helloworld_sub = learning_pkg_python_zy.topic_helloworld_sub:main',
'topic_helloworld_pub = learning_pkg_python_zy.topic_helloworld_pub:main',
],
},
随后编译执行:
colcon build
source install/setup.bash
ros2 run learning_pkg_python_zy topic_helloworld_pub
# 这里运行后要打开第二个终端,然后进入dev_ws路径下
source install/setup.bash
ros2 run learning_pkg_python_zy topic_helloworld_sub
效果如下:
以上就是关于本书的第三次分享,分享内容多为书本内容,本人在此由于篇幅有限只是介绍核心内容,本文我介绍了我们该如何创建一个节点和如何使用话题通信,在下次分享我将尽力分享服务,动作和参数通信!!!如果大家有任何疑問可以發我郵箱或者直接評論(不一定準時),郵箱3499248831@qq.com.
- 2025-01-24
-
发表了主题帖:
ROS2智能机器人开发实践-阅读分享2-ros2开发流程
ROS2智能机器人开发实践-阅读分享2-ros2开发流程
ros2机器人开发流程
1.创建工作空间
2.创建功能包
3.编写源代码
4.设置编译规则
5.编译调试
6.功能验证
详细例子分享
多说无益,我们直接来进行一个”机器人“功能的开发来学习机器人的开发流程!!!
首先我们需要一个工作空间,这里我们使用命令行创建:
mkdir -p ~/dev_ws/src
cd ~/dev_ws/src
git clone https://gitee.com/guyuehome/ros2_21_tutorials.git
学习代码已经开源,大家可以前往gitee或者github下载。
下载完成后进行编译
cd ~/dev_ws
colcon build
编译之后的文件夹里面除了我们自己建立的src还多了3个文件夹:
build:存放需要编译文件的目录,每一个功能包就会有一个文件夹。
install:安装目录,以及编译好的目录。
log:日志
src:功能包源码
这里我们需要执行的文件已经纯在与install里面了,如果我们想要执行使用ros2 run命令并不可以,因为这里面的文件并没有配置到环境中。
source install/setup.bash
然后我们就可以使用ros2 run或者ros2 launch来执行文件啦!!!
创建自己的python功能包
在ros2的开发中,我们使用两种语言c++和python,这是非常有意思的,c++和python之间有很多相似的地方,但是c++在大的开源ros2功能包中较为常见的,但是python有更多的开源库可以使用,在平常开发中我们可以两者都使用,这并不影响。
ros2 pkg create --build-type ament_python learning_pkg_python_zy
# 最后一个参数是功能包名字,大家可以自己命名
创建之后是这个样子的,后面的具体操作我们后续再讲!!!
以上就是关于本书的第二次分享,分享内容多为书本内容,本人在此由于篇幅有限只是介绍核心内容,本文我介绍了我们该如何创建一个工作空间并创建一个python类型功能包,后续我将介绍python类型功能包里面的话题,服务,参数,动作等,至于c++的内容我将不过多介绍(本人的c++确实不会,平常使用较少)!!!
-
回复了主题帖:
ROS2智能机器人开发实践-阅读分享1-ros2介绍和安装验证
Jacktang 发表于 2025-1-24 09:12
按这个步骤,先安装虚拟机,然后再虚拟机上安装ros2,虚拟机的安装可以在vmware等虚拟机软件上安装,,,,
...
是的,理解正确,当然安装ros2可以使用小鱼的ros2一键安装工具(没有最新版本),
- 2025-01-23
-
发表了主题帖:
ROS2智能机器人开发实践-阅读分享1-ros2介绍和安装验证
ROS2智能机器人开发实践-阅读分享1-ros2介绍和安装验证
ROS2与ROS1对比
如图所示:ros1的application_layer里面有一个master节点管理的角色,它类似于一个公司的ceo管理所有的节点,然而在ros2里面截然不同,ros2依赖与“数据分发服务”-DDS。
于此同时,ros2的系统层可以选择的更多,几乎所有主流操作系统都适用。
ros2的通信系统
DDS是物联网中一种广泛应用的一种系统通信机制,有许多不同的商家提供不同的DDS,每一家的性能和应用场景各不相同,ROS2为了提高软件复用率,定制了一个接口标准,当DDS供应商希望接入ros社区那么需要按照标准来适配接口,因此ros2用户可以方便的更换dds。
ros2的常见名词解释
在后续学习中我们经常会遇到以下名词,大家需要记住:
工作空间(workspace):放置所有开发文件的地方,命名含义就和写代码时一样,也就是存放项目文件的地方。
功能包(package):类似与代码里面的函数,我么可以调用这些功能包也可以将多个功能包一起调用。
节点(node):机器人的工作单元,代码编译生成的课执行文件。
话题(topic):节点之间转递数据的桥梁。
服务(service):节点之间的“你问我答”,用于调用机器人的功能或者进行机器人参数配置。
通信接口:数据传递的标准规范,规范数据传递的形式。
参数:机器人的配置参数。
ros2命令行工具
这是ubuntu20.04的foxy版本ros2的命令行工具,对于各个版本的命令行工具基本一样对于各个命令行大家可以在后续使用中学习到。书籍内选用的版本是ubuntu24.04的jazzy。
ubuntu和ros2下载
虽然ros2可以在各个平台运行但是选择使用linux操作系统任然是最优选择,建议使用vmware安装ubuntu操作系统(也可以选择使用wsl2安装),同时各个ros2版本通常依赖与固定版本ubuntu,这可能和各种环境有关,而且旧版本的开源包可能有些功能不支持,总之建议下载对应版本ros2,本书下载为ubuntu24.04+jazzy。
#### 下载完成验证
ros2 run turtlesim turtlesim_node
ros2 run turtlesim turtle_teleop_key
ros2常用命令示例
上文的乌龟的终端不要关闭!!!
以上就是关于本书的第一次分享,分享内容多为书本内容,本人在此由于篇幅有限只是介绍核心内容,在安装好ros2后我们将会详细的介绍它的使用!!!
对于ros2的安装,大家如果有疑惑可以搜说以下,总之就是先安装虚拟机,然后再虚拟机上安装ros2,虚拟机的安装可以在vmware等虚拟机软件上安装,然后建议安装书本的ubuntu24.04+jazzy版本,以防有问题!!
- 2025-01-21
-
回复了主题帖:
读书活动入围名单:《ROS2智能机器人开发实践》
个人信息无误,确认可以完成阅读分享计划