- 2025-02-28
-
回复了主题帖:
RDK X3测评:8. SLAM建图
wangerxian 发表于 2025-2-28 16:30
小车上需要有激光传感器吧
嗯嗯是的,SLAM建图需要用到激光传感器
- 2025-02-24
-
回复了主题帖:
RDK X3测评:10. 手势控制小车
wangerxian 发表于 2025-2-24 09:02
识别的手指没有你这个点多,我用的是USB摄像头,不过我感觉和这个没关系。
这次我用的也是USB摄像头。感觉它程序里不仅包含了人手关键点检测,还融合了人体关键点检测,并且这两个是相互关联的。测试的时候,只有识别到人体关键点之后,才会进一步显示人手的关键点。
- 2025-02-22
-
回复了主题帖:
【Raspberry Pi 5测评】树莓派5学习笔记08(部署YOLOv5进行目标检测)
推理过程耗时快0.5秒了,如果视频流推理的话会不会很卡?树莓派能部署量化后的模型吗?量化模型推理应该会稍微快一点。
-
回复了主题帖:
RDK X3——摄像头的使用
Jacktang 发表于 2025-2-22 15:25
摄像头的排线看着不是很难装的那种
这个板子的FPC连接器是翻盖的,不是抽屉式的,安装起来还比较方便
-
回复了主题帖:
RDK X3测评:10. 手势控制小车
秦天qintian0303 发表于 2025-2-22 14:30
一般手势识别需要控制在怎样的频率范围比较合理?
感觉正常应该越大越好。频率越高,系统的实时性通常会更强,能够更快地响应用户的手势变化。但也要考虑到设备的性能,测试的时候识别帧率是十几Hz,控制起来基本没有明显的卡顿现象。
- 2025-02-21
-
回复了主题帖:
RDK X3测评:10. 手势控制小车
wangerxian 发表于 2025-2-21 17:46
我跑的例程感觉手势识别不对劲,你跑的也是官方例程吧?
大佬觉得那儿哪儿不对劲呀
-
回复了主题帖:
RDK X3测评:9. Nav2导航
freebsder 发表于 2025-2-21 16:01
学习一下,谢谢分享
:congratulate::congratulate::congratulate:
-
发表了主题帖:
RDK X3测评:10. 手势控制小车
# 简介
RDK X3 是一款强大的开发板,支持通过手势控制机器人小车的运动。本文将详细介绍如何利用 RDK X3 和其配套工具(如 MIPI 或 USB 摄像头)实现手势控制功能,并通过 ROS 2 的 cmd_vel topic 发布控制指令,驱动小车完成前进、后退、左右旋转等操作。此外,我们还将展示如何在 Gazebo 仿真环境中测试控制效果,确保系统的稳定性和可靠性。
# 系统架构
具体流程如下图所示:
相应的代码仓库:https://github.com/D-Robotics/gesture_control
板端程序通过cmd_vel topic发布控制指令,既能够控制PC端Gazebo仿真环境下的虚拟小车,也可以直接用于控制实物小车。
# 手势识别
首先将RDK X3联网,确保PC和RDK X3在同一网段下。
之后,在RDK X3中运行如下命令,启动USB摄像头采集,手势识别和控制,通过PC端web浏览器实时查看采集到的图像。
```basg
# 配置tros.b环境
source /opt/tros/humble/setup.bash
# 从tros.b的安装路径中拷贝出运行示例需要的配置文件。
cp -r /opt/tros/${TROS_DISTRO}/lib/mono2d_body_detection/config/ .
cp -r /opt/tros/${TROS_DISTRO}/lib/hand_lmk_detection/config/ .
cp -r /opt/tros/${TROS_DISTRO}/lib/hand_gesture_detection/config/ .
# 配置USB摄像头
export CAM_TYPE=usb
# 启动launch文件
ros2 launch gesture_control gesture_control.launch.py
```
如果要使用mipi摄像头,只需要将CAM_TYPE从usb修改为mipi即可。
之后,在PC端的浏览器中输入http://192.168.2.12:8000 即可查看摄像头采集的图像及算法渲染效果(其中192.168.2.12为RDK的IP地址)。下面是其中两种手势的是被效果图:
可以看到,系统能够准确识别当前的手势。并且在网页的左下角可以看到当前的系统状态信息
其中的fps为实时AI推理输出的帧率,当前为17帧。ai_delay为540,表示的是单帧推理的延迟为540ms。
# 手势控制
具体的控制方式可以从代码仓库的src/main.cpp代码文件中查看
相应的手势与对应的控制功能如下:
- Okay:Wake up
- Palm:Reset
- Awesome:Forward
- Victory:Backward
- ThumbRight:Turn Right
- ThumbLeft:Turn Left
下面我们在PC上新建终端,执行如下命令,查看不同手势下cmd_vel topic上发布的内容,其中包含线速度和角速度的信息。
```bash
ros2 topic echo /cmd_vel
```
我们分别做出了手掌和“V”手势,可以看到,分别发出了reset指令和后退指令。
# 仿真测试
下面我们在PC端运行Gazebo仿真程序,展示实际的控制效果。
首先执行如下命令,启动Gazebo仿真程序。
```bash
source /opt/ros/humble/setup.bash
export TURTLEBOT3_MODEL=burger
ros2 launch turtlebot3_gazebo empty_world.launch.py
```
运行的仿真环境如下图所示:
之后我们分别给出不同的手势,控制小车的运行状态。测试效果如下:
[localvideo]9c686641b8691a7db128cdd3accdc198[/localvideo]
# 小结
本节中,我们成功借助 RDK X3 实现了基于手势控制的机器人小车运动功能,包括前进、后退、左右旋转及重置等操作。通过集成手势识别算法与 ROS 2 的 cmd_vel 控制机制,系统展示了良好的实时性和稳定性。此外,结合仿真工具如 Gazebo,为算法的测试与调试提供更便捷的支持。
-
发表了主题帖:
RDK X3测评:9. Nav2导航
Nav2 是 ROS 2 的内置导航框架,专为移动机器人设计,支持从起点 A 到目标点 B 的安全导航。它具备动态路径规划、避障、多点路径规划和电机速度计算等功能。
本节基于之前通过 SLAM 构建的地图,介绍如何结合 Gazebo 和 Rviz2,在 PC 端搭建仿真环境并设置导航目标,最后在 RDK(Robot Development Kit)上运行 Nav2 实现自主导航任务。
# PC准备
PC环境为Ubuntu22.04,并已安装好ROS2 humble桌面版。运行如下命令,安装Gazebo和Turtlebot3相关的功能包
```bash
sudo apt-get install ros-humble-gazebo-*
sudo apt install ros-humble-turtlebot3*
sudo apt install ros-humble-navigation2
sudo apt install ros-humble-nav2-bringup
```
其中,Gazebo 是一个强大的机器人仿真工具,用于模拟机器人在虚拟环境中的行为。可以用它创建或加载虚拟场景,测试机器人的传感器、控制器和导航算法。
TurtleBot3 是一种流行的教育和研究用小型机器人平台,通常用于学习 ROS 和机器人技术。它提供了多种型号(如 Burger 和 Waffle),适合不同的应用场景。
# RDK准备
首先,需要将RDK X3联网,并确保PC和RDK X3在同一网段。
之后,运行如下命令,在RDK X3上安装navigation2 和 nav2-bringup这两个与机器人导航相关的软件包。
```bash
sudo apt update
sudo apt install ros-humble-navigation2
sudo apt install ros-humble-nav2-bringup
```
其中,navigation2 是 ROS 2 中用于实现机器人自主导航的核心包。它提供了一套工具和算法,帮助机器人在已知或未知环境中规划路径并避开障碍物。主要功能包括:全局路径规划、局部路径规划、地图构建与定位等。
nav2_bringup 是一个辅助包,主要用于快速配置和启动导航系统。提供了示例配置文件、启动脚本和测试环境,简化导航系统的部署。
# 仿真测试
首先,在 PC 上运行以下命令,启动 Gazebo 仿真环境
```bash
source /opt/tros/humble/setup.bash
export TURTLEBOT3_MODEL=waffle
ros2 launch turtlebot3_gazebo turtlebot3_world.launch.py
```
其中,TURTLEBOT3_MODEL 是一个环境变量,指定 TurtleBot3 的具体型号。不同的型号有不同的硬件配置(如尺寸、传感器布局、性能等),常见的型号包括:
- Burger:基础型号,体积较小,适合教学和简单实验。
- Waffle:高级型号,体积较大,配备更多传感器,适合复杂任务。
启动后,Gazebo 将显示一个虚拟环境,包含地图、障碍物以及小车的位置。如下图所示:
在 RDK 上新建一个终端,运行以下命令启动导航功能:
```bash
# 配置tros.b环境
source /opt/tros/humble/setup.bash
ros2 launch nav2_bringup bringup_launch.py use_sim_time:=True map:=/opt/ros/humble/share/nav2_bringup/maps/turtlebot3_world.yaml
```
这个命令用于启动 ROS 2 中的导航系统(nav2_bringup),并加载指定的地图文件。其中,use_sim_time:=True参数告诉导航系统使用仿真时间(simulated time),而不是实际的系统时间。map:=/opt/ros/humble/share/nav2_bringup/maps/turtlebot3_world.yaml参数指定了要加载的地图文件路径。地图文件通常是一个 YAML 文件,描述了机器人的工作环境(如墙壁、障碍物等)。在这个命令中,地图文件是 turtlebot3_world.yaml,它位于 nav2_bringup 包的地图目录中。
最后,PC端启动Rviz2工具,在 PC 上启动 Rviz2 工具,用于可视化机器人传感器数据、地图、路径规划结果以及机器人姿态等信息。
## 设置初始位置
Rviz2启动后,我们需要先点击上方菜单栏中的"2D Pose Estimate" 按钮,并在地图上点击gazebo仿真环境中机器人的位置来设置机器人初始位置 。如下图所示:
## 设置导航目标
之后,点击菜单栏中的"Navigaton2 Goal"按钮并选择一个目的地,就能够实现自动路径规划。
## 多点路径规划与避障
此外,如果想设置多点的路径规划,可以点击左下角的Waypoint mode,并使用"Navigaton2 Goal"按钮选择多个路径点。Nav2能够实时进行路径规划,在gazebo仿真环境中新增障碍物,Nav2能够即时更新路径并控制小车沿着新的路径行驶。具体效果如视频所示:
[localvideo]c3ca41eae61cb3d840666d070962b1f3[/localvideo]
# 小结
本节中,我们初步利用 Nav2 实现了机器人自主导航功能,包括路径规划、避障和多点路径规划。Nav2 是 ROS 2 中强大的导航工具,能够满足多种复杂场景下的需求。未来,我们可以进一步探索其高级功能,如动态点跟踪、自定义规划器等,以实现更复杂的机器人应用。
- 2025-02-17
-
回复了主题帖:
【回帖赢京东卡】无线技术盛宴第二趴
能否将多个天线芯片连接在一起? - 工程小贴士 - 工程和组件解决方案论坛 - 技术论坛 |Digi-Key
- 2025-02-16
-
发表了主题帖:
RDK X3测评:8. SLAM建图
SLAM(Simultaneous Localization and Mapping,即时定位与地图构建)是机器人领域的关键技术,旨在使机器人在未知环境中同时完成自身定位和环境地图构建。本节将介绍如何在RDK X3端使用SLAM-Toolbox部署SLAM建图算法;并在PC端使用Gazebo模拟物理环境,同时控制小车行驶建立地图,并通过Rviz2可视化建图效果。
# 环境准备
- 操作系统:Ubuntu 22.04 虚拟机
- 硬件设备:RDK X3
- 软件工具:ROS 2 Humble, Gazebo, Rviz2, SLAM-Toolbox
# SLAM简介
SLAM技术广泛应用于自动驾驶、无人机、AR/VR等领域。其核心思想是通过传感器数据(如激光雷达)实时估计机器人位姿和环境特征,逐步解决定位和建图问题。
## 基于激光雷达的SLAM原理
1. 定位:通过激光雷达扫描的数据,估计机器人当前的位置和姿态。
2. 建图:利用激光雷达数据,逐步构建环境的地图。
由于激光雷达提供高精度的距离信息,基于激光雷达的SLAM通常比视觉SLAM更稳定,尤其是在光照变化或纹理缺失的环境中。
## SLAM Toolbox工具箱
slam_toolbox 是一个基于激光雷达的SLAM实现工具包。它提供了开箱即用的SLAM功能,适合初学者快速上手。
### 核心功能
1. 基于图优化的SLAM:
- 使用图优化方法(如 g2o 或 Ceres Solver)来优化机器人的位姿和地图。
- 它能够处理大规模环境,并且支持回环检测。
2. 实时建图:
- 通过订阅激光雷达数据(scan话题),实时构建地图并发布到 map 话题。
3. 支持 2D 地图:
- slam_toolbox 主要用于构建 2D 占用栅格地图,适合地面机器人使用。
4. 保存和加载地图:
- 支持将构建的地图保存为文件(如 pgm 和 yaml格式),并可以在后续任务中加载。
# PC准备工作
由于在Ubuntu 24.04中,ROS 2的版本为Jazzy,不支持直接apt安装humble-turtlebot3,需要手动编译安装,比较麻烦。因此,这里使用了虚拟机安装Ubuntu 22.04,作为PC端进行仿真及可视化。
## 虚拟机访问局域网
首先需要解决的问题是虚拟机如何访问局域网,从而能够与RDK X3互联。在VirtualBox中,桥接网络是一种网络连接类型,它允许虚拟机连接到物理网络上的路由器或交换机,在物理网络上获得独立的网络地址和访问权限。具体设置如下:
1. 在VirtualBox软件中选择Ubuntu22.04虚拟机,点击“设置”按钮,选择“网络”选项卡。
2. 将“网络适配器”的附加类型设置为“桥接网络”。
设置完成后,启动虚拟机并使用ifconfig命令查看当前虚拟机的IP地址,可以看到虚拟机已经分配了一个同网段的IP。
使用ping命令,可以验证虚拟机与RDK X3设备的通信是否正常。
# 虚拟机安装ROS2
## 添加ROS2存储库
首先,确保 Ubuntu Universe存储库 已启用。
```bash
sudo apt install software-properties-common
sudo add-apt-repository universe
```
之后,使用 apt 添加 ROS2 GPG 密钥。
```bash
sudo apt update && sudo apt install curl -y
sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg
```
最后,将存储库添加到您的源列表中。
```bash
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(. /etc/os-release && echo $UBUNTU_CODENAME) main" | sudo tee /etc/apt/sources.list.d/ros2.list > /dev/null
```
### 可能遇到的问题
在添加 ROS 2 GPG 密钥时,可能会出现raw.githubusercontent.com无法访问的情况。
此时,可以尝试修改DNS,临时修改DNS的方法如下,使用文本编辑器打开/etc/resolv.conf文件:
```bash
sudo vi /etc/resolv.conf
```
之后修改其中的nameserver字段为8.8.8.8,保存并退出,之后即可正常添加了。
## 安装ROS2 Humble
首先运行如下命令更新软件包
```bash
sudo apt update
sudo apt upgrade
```
之后运行如下命令,安装桌面版的ros2 humble。
```bash
sudo apt install ros-humble-desktop
```
### 可能遇到的问题
在安装过程中可能会遇到如下的依赖问题
这是因为软件包版本冲突导致的,只需要按照提示,安装相关版本的软件包即可
```bash
sudo apt install libpulse0=1:15.99.1+dfsg1-1ubuntu1 libpulse-mainloop-glib0=1:15.99.1+dfsg1-1ubuntu1
sudo apt install libudev1=249.11-0ubuntu3.7
sudo apt install libusb-1.0-0=2:1.0.25-1ubuntu1
sudo apt install python3-minimal=3.10.6-1~22.04
sudo apt install libpython3-stdlib=3.10.6-1~22.04
sudo apt install python3=3.10.6-1~22.04
```
## 安装相关功能包
在成功安装ROS2之后,我们还需要运行如下命令,安装Gazebo和Turtlebot3相关的功能包
```bash
sudo apt-get install ros-humble-gazebo-*
sudo apt install ros-humble-turtlebot3
sudo apt install ros-humble-turtlebot3-bringup
sudo apt install ros-humble-turtlebot3-simulations
sudo apt install ros-humble-teleop-twist-keyboard
```
# 测试
## 启动仿真环境
在虚拟机中,我们首先新建一个终端,运行如下命令,启动Gazebo仿真环境
```bash
source /opt/ros/humble/setup.bash
source /usr/share/gazebo/setup.sh
export TURTLEBOT3_MODEL=burger
ros2 launch turtlebot3_gazebo turtlebot3_world.launch.py
```
启动的界面如下:
可以看到虚拟环境的地图和障碍物,以及其中的小车。
## 启动Rviz2
之后,我们在虚拟机中启动另一个新的终端,启动可视化工具Rviz2
```bash
source /opt/ros/humble/setup.bash
ros2 launch turtlebot3_bringup rviz2.launch.py
```
启动后的界面如下图所示:
在可视化界面中,可以看到当前小车的位置。我们在页面左下角点击Add按钮,选择添加map可视化选项,用于展示建立的地图。
之后在左侧的map一栏中,将topic改为map,接收map话题中发布的建图数据进行绘制
## 运行SLAM-Toolbox
在RDK X3端,我们首先运行如下命令,安装SLAM-Toolbox工具。
```bash
sudo apt-get install ros-humble-slam-toolbox
之后,启动SLAM-Toolbox程序
source /opt/tros/humble/setup.bash
ros2 launch slam_toolbox online_sync_launch.py
```
## 控制小车运动
在虚拟机中,我们另外新建一个终端,运行小车的控制程序:
```bash
source /opt/ros/humble/setup.bash
ros2 run teleop_twist_keyboard teleop_twist_keyboard
```
程序运行后,终端打印如下,描述了小车的控制方式:
具体控制方式如下:
- i:前进(增加线速度)
- ,:后退(减少线速度)
- j:左转(增加角速度)
- l:右转(减少角速度)
- k:停止所有运动
# 最终效果
通过以上步骤,可以在Rviz2中实时观察到小车的运动轨迹和地图构建过程。最终效果如图所示。
可以看到,小车能够正常探测出整张地图。
# 小结
本节中,我们初步学习了SLAM建图工具箱的使用,并且了解了Gazebo仿真工具和Rviz2可视化工具的使用。在ROS中,许多现成的工具箱可以直接使用,Gazebo仿真工具和Rviz2可视化工具是调试程序的强大助手,通过这些工具,可以快速对程序进行仿真验证。
- 2025-02-09
-
回复了主题帖:
RDK X3测评:1.环境搭建
springvirus 发表于 2025-2-8 11:31
不知资料和技术支持如何
资料很全面,有专门的资料网站https://developer.d-robotics.cc/rdk_doc/RDK。学习ros的话,非常方便
- 2025-02-07
-
发表了主题帖:
RDK X3测评:7.大语言模型测试
hobot_llm 是地瓜机器人RDK平台集成的端侧大语言模型(LLM)节点,用户可以在端侧体验大语言模型的强大功能。目前,hobot_llm 提供两种体验方式,一种直接终端输入文本聊天体验,一种订阅文本消息,然后将结果以文本方式发布出去。其代码仓库为: https://github.com/D-Robotics/hobot_llm.git
# 准备工作
## 安装hobot_llm
首先,确保系统中已安装 transformers 库。如果未安装,可以使用以下命令进行安装
```bash
pip3 install transformers -i https://pypi.tuna.tsinghua.edu.cn/simple
```
此外,系统中已默认安装了hobot_llm模块,如果未安装,可以运行以下命令进行安装:
```bash
sudo apt update
sudo apt install -y tros-humble-hobot-llm
```
安装后的目录为/opt/tros/${TROS_DISTRO}/lib/hobot_llm/,可以看到当前目录下已经包含了必要的组件。
## 下载模型文件
在安装完hobot_llm之后,我们还需要单独下载模型文件并解压至上述目录。
首先运行如下命令,下载并解压llm_model压缩文件
```bash
wget http://archive.d-robotics.cc/llm-model/llm_model.tar.gz
# 解压
sudo tar -xf llm_model.tar.gz -C /opt/tros/${TROS_DISTRO}/lib/hobot_llm/
```
压缩包大概有1.4G,需要等待一段时间。
在下载过程中可能会出现如下网站无法访问的情况
此时,可以尝试修改DNS,临时修改DNS的方法如下,使用文本编辑器打开/etc/resolv.conf文件:
```bash
vim /etc/resolv.conf
```
之后修改其中的nameserver字段为8.8.8.8,保存并退出,之后即可正常下载模型了。
# 系统参数修改
## 调整 ION 空间
由于运行大语言模型需要消耗较多的内存,因此需要修改ION memory大小,这个内存是给BPU使用的。
官方文档给出的方法是:使用命令srpi-config修改ION memory大小为1.9G。但是在实际使用过程中,发现修改并重启后ION memory依然是672MB,无法成功进行修改。
因此,参考了通过设备树修改BPU保留内存大小的方法,对ION memory大小进行修改,具体方法如下:
首先,使用如下命令确认我们的板卡类型:
```bash
cat /sys/firmware/devicetree/base/model
```
可以看到,当前为为RDK X3 v2.1硬件板卡,对于不同的板卡版本,其设备树文件的名称会有所区别。
之后使用如下命令把dtb文件转成dts文件,从而便于阅读(注意需要使用串口登录,否则使用ssh登录可能会提示权限不够)
```bash
dtc -I dtb -O dts -o edit_file.dts /boot/hobot/hobot-x3-pi_v2_1.dtb
```
可以看到当前目录下多出了一个edit_file.dts设备树文件,我们之后的修改就在该文件中进行
然后我们打开该文件并找到其中的ion_cma部分,并对其进行如下红框中的修改
这里是16进制格式,将内存大小修改为了1.7GB。
最后,备份原始dtb文件,将我们修改后的dts文件转换为dtb格式,并替换原始的dtb文件
```bash
cp /boot/hobot/hobot-x3-pi_v2_1.dtb ./hobot-x3-pi_v2_1.dtb
dtc -I dts -O dtb -o /boot/hobot/hobot-x3-pi_v2_1.dtb edit_file.dts
```
之后使用reboot命令重启系统,并使用如下命令查看ION memory的大小是否修改成功
```bash
sudo rdkos_info -s
```
可以看见,我们ION memory的大小已经修改成功,为1700MB。
## 调整性能模式
之后,我们需要设置CPU最高频率为1.5GHz,以及调度模式为performance,命令如下:
```bash
sudo bash -c 'echo 1 > /sys/devices/system/cpu/cpufreq/boost'
sudo bash -c 'echo performance > /sys/devices/system/cpu/cpufreq/policy0/scaling_governor'
```
# 运行测试
目前提供两种体验方式,一种直接终端输入文本继续宁对话聊天;另一种是订阅文本消息,然后将结果以文本方式发布出去。
## 终端交互
运行如下命令,即可运行程序,在终端进行交互对话:
```bash
source /opt/tros/humble/setup.bash
ros2 run hobot_llm hobot_llm_chat
```
## 订阅发布
首先我们需要启动hobot_llm节点,它能够接收信息,并调用大语言模型进行回答,最后将大语言模型的回答进行输出。
```bash
source /opt/tros/humble/setup.bash
ros2 run hobot_llm hobot_llm
```
成功运行后,我们查看当前的topic列表
可以看到其中有一个text_query和一个text_result。其中text_query就是向llm发送消息的topic,而text_result就是llm输出消息的topic。
之后,我们打开一个新的终端,订阅输出结果
```bash
source /opt/tros/humble/setup.bash
ros2 topic echo /text_result
```
最后,我们再新建一个终端,向text_query发布消息
```bash
source /opt/tros/humble/setup.bash
ros2 topic pub --once /text_query std_msgs/msg/String "{data: ""中国的面积有多大?""}"
```
返回结果如下:
结果不太准确。换一个问题:中国的首都是哪里?
这次回答正确了。可能是因为参数量太小,或者训练的不到位,所以有时候的回答不太准确。此外,关于这个大模型的细节,官方好像并没有给出。从源码上来看,它可能是基于bloom模型,并且参数量很可能不到1B。
# 小结
hobot_llm 为地瓜机器人RDK平台提供了一个端侧大语言模型的集成方案,用户可以通过终端交互或订阅发布的方式体验大语言模型的功能。虽然模型的参数量较小,导致部分回答不够准确,但对于端侧应用来说,hobot_llm 仍然是一个非常有潜力的工具。
-
回复了主题帖:
《深度学习的数学——使用Python语言》11.深入理解梯度下降
Chn 发表于 2025-2-5 14:36
感谢楼主分享,请问如果是0基础学习Python的话,要大改学习多久才能精通啊
快的话几周就能掌握Python基础语法。刚开始不用学的太深入,然后找些深度学习等自己感兴趣的项目,边学边用,效率更高。
- 2025-02-05
-
发表了主题帖:
《深度学习的数学——使用Python语言》11.深入理解梯度下降
本帖最后由 waterman 于 2025-2-5 12:41 编辑
梯度下降是机器学习和深度学习的核心优化算法,几乎所有的模型训练都离不开它。然而,梯度下降并不是一个单一的算法,而是一个庞大的家族,包含了许多变体和改进方法。本文将从最基础的梯度下降开始,逐步深入学习,并通过代码进一步理解每个算法的实现细节。
梯度下降的基本原理
梯度下降的核心思想是通过迭代的方式,沿着目标函数的负梯度方向逐步调整参数,最终找到函数的最小值。这个过程可以类比为一个盲人下山的过程:盲人无法看到整个山的地形,但可以通过脚下的坡度来判断下山的方向,并一步步向山脚移动。
一维梯度下降
让我们从一个简单的例子开始:一维函数 f(x)=x^2。这个函数的导数为 f′(x)=2x,因此梯度下降的更新规则为:
其中,η是学习率,控制着每一步的步长。以下是一个简单的 Python 实现:
def gradient_descent_1d(start, lr=0.1, epochs=100):
x = start
history = []
for _ in range(epochs):
grad = 2 * x # 计算梯度
x -= lr * grad # 更新参数
history.append(x)
return history
# 测试
start_value = 10.0
learning_rate = 0.1
result = gradient_descent_1d(start_value, lr=learning_rate, epochs=50)
print("最终结果:", result[-1])
在这个例子中,我们从初始值 x=10.0开始,经过 50 次迭代后,x的值会逐渐趋近于 0,即函数的最小值。
其中,学习率的选择对结果有重要影响,过大的学习率可能导致震荡,而过小的学习率则会导致收敛速度过慢。
二维梯度下降
接下来,我们考虑一个二维函数 f(x,y)=x^2+2y^2。这个函数的梯度为 ∇f=[2x,4y],因此梯度下降的更新规则为:
以下是二维梯度下降的 Python 实现:
def gradient_descent_2d(start, lr=0.1, epochs=100):
x, y = start
history = []
for _ in range(epochs):
grad_x = 2 * x # x 方向的梯度
grad_y = 4 * y # y 方向的梯度
x -= lr * grad_x # 更新 x
y -= lr * grad_y # 更新 y
history.append((x, y))
return history
# 测试
start_value = (5.0, 5.0)
learning_rate = 0.1
result = gradient_descent_2d(start_value, lr=learning_rate, epochs=50)
print("最终结果:", result[-1])
运行结果如下:
在这个例子中,我们可以看到梯度下降在不同维度上的收敛速度是不同的。由于y方向的梯度是x方向的两倍,因此在y方向上的收敛速度会更快。这种差异在某些情况下会导致优化路径呈现出椭圆形的轨迹。
随机梯度下降(SGD)
在实际的机器学习问题中,目标函数通常是基于大量数据的损失函数。传统的梯度下降需要计算整个数据集的梯度,这在数据量较大时会变得非常耗时。为了解决这个问题,随机梯度下降(Stochastic Gradient Descent, SGD)应运而生。
SGD 的基本原理
SGD 的核心思想是每次迭代只使用一个随机样本或一个小批量样本来估计梯度。虽然这种估计会引入噪声,但它在实践中通常能够显著加快收敛速度,尤其是在大规模数据集上。
以下是 SGD 的 Python 实现:
import numpy as np
def sgd(data, start, lr=0.1, epochs=100, batch_size=1):
x, y = start
history = []
n_samples = len(data)
for _ in range(epochs):
# 随机打乱数据
np.random.shuffle(data)
for i in range(0, n_samples, batch_size):
batch = data[i:i + batch_size]
grad_x = 2 * x # 假设梯度计算
grad_y = 4 * y # 假设梯度计算
x -= lr * grad_x # 更新 x
y -= lr * grad_y # 更新 y
history.append((x, y))
return history
# 测试
data = np.random.randn(100, 2) # 生成随机数据
start_value = (5.0, 5.0)
learning_rate = 0.1
result = sgd(data, start_value, lr=learning_rate, epochs=10, batch_size=10)
print("最终结果:", result[-1])
运行结果如下:
SGD 的优缺点
SGD 的主要优点是计算效率高,尤其是在大规模数据集上。然而,由于每次迭代只使用部分数据,SGD 的更新方向可能会引入较大的噪声,导致收敛路径不稳定。为了缓解这个问题,通常会采用学习率衰减策略,即在训练过程中逐渐减小学习率。
动量机制
尽管 SGD 在大规模数据上表现良好,但它仍然存在一些问题,尤其是在优化路径中存在大量震荡或噪声时。为了改善这种情况,动量机制(Momentum)被引入到梯度下降中。
动量的基本原理
动量机制的核心思想是引入一个速度变量,使得参数更新不仅依赖于当前的梯度,还依赖于之前的速度。具体来说,动量机制的更新规则为:
其中,γ是动量系数,通常取值在 0.9 左右。当γ=0时,退化为普通的SGD。以下是动量机制的 Python 实现:
def momentum(start, lr=0.1, gamma=0.9, epochs=100):
x, y = start
vx, vy = 0, 0 # 初始化速度
history = []
for _ in range(epochs):
grad_x = 2 * x # 计算梯度
grad_y = 4 * y
vx = gamma * vx + lr * grad_x # 更新速度
vy = gamma * vy + lr * grad_y
x -= vx # 更新参数
y -= vy
history.append((x, y))
return history
# 测试
start_value = (5.0, 5.0)
learning_rate = 0.1
momentum_coeff = 0.9
result = momentum(start_value, lr=learning_rate, gamma=momentum_coeff, epochs=100)
print("最终结果:", result[-1])
运行结果如下:
涅斯捷洛夫动量
涅斯捷洛夫动量(Nesterov Momentum)是动量机制的一个改进版本。它的核心思想是先根据当前的速度预估未来的参数位置,然后在该位置计算梯度。这种前瞻性的梯度计算能够进一步提高收敛速度。具体来说,其更新规则为:
以下是涅斯捷洛夫动量的 Python 实现:
import numpy as np
def nesterov_momentum(gradient, theta_init, learning_rate=0.1, momentum=0.9, num_iters=100):
"""
涅斯捷洛夫动量梯度下降算法实现
参数:
- gradient: 梯度函数,接受参数 theta,返回梯度值
- theta_init: 初始参数值
- learning_rate: 学习率 (默认 0.1)
- momentum: 动量系数 (默认 0.9)
- num_iters: 迭代次数 (默认 100)
返回:
- theta_history: 参数更新历史
- loss_history: 损失函数值历史
"""
theta = theta_init # 初始化参数
v = np.zeros_like(theta) # 初始化速度
theta_history = [theta.copy()] # 记录参数更新历史
loss_history = [] # 记录损失函数值历史
for i in range(num_iters):
# 计算前瞻位置的梯度
lookahead_theta = theta + momentum * v
grad = gradient(lookahead_theta)
# 更新速度
v = momentum * v - learning_rate * grad
# 更新参数
theta += v
# 记录历史
theta_history.append(theta.copy())
loss = theta[0]**2 + 2 * theta[1]**2 # 计算损失函数值
loss_history.append(loss)
return theta_history, loss_history
# 定义目标函数的梯度
def gradient_function(theta):
x, y = theta
return np.array([2 * x, 4 * y])
# 初始参数值
theta_init = np.array([5.0, 5.0]) # 初始点 (x, y) = (5, 5)
# 运行涅斯捷洛夫动量梯度下降
theta_history, loss_history = nesterov_momentum(gradient_function, theta_init, learning_rate=0.1, momentum=0.9, num_iters=50)
# 输出结果
print("最终参数值:", theta_history[-1])
print("最终损失值:", loss_history[-1])
# 可视化损失函数值的变化
import matplotlib.pyplot as plt
plt.plot(loss_history)
plt.xlabel("Iteration")
plt.ylabel("Loss")
plt.title("Loss over Iterations")
plt.show()
运行结果如下:
绘制出的loss曲线如下:
自适应梯度下降
随着深度学习模型的复杂性不断增加,传统的梯度下降方法在某些情况下可能表现不佳。为了应对这一问题,自适应梯度下降方法应运而生。这些方法通过动态调整每个参数的学习率,使得优化过程更加高效。
RMSprop
RMSprop 是一种常用的自适应梯度下降方法,它通过维护一个指数衰减的梯度平方均值来调整学习率。以下是 RMSprop 的 Python 实现:
def rmsprop(start, lr=0.1, decay_rate=0.9, eps=1e-8, epochs=100):
x, y = start
cache_x, cache_y = 0, 0 # 初始化缓存
history = []
for _ in range(epochs):
grad_x = 2 * x # 计算梯度
grad_y = 4 * y
cache_x = decay_rate * cache_x + (1 - decay_rate) * grad_x ** 2 # 更新缓存
cache_y = decay_rate * cache_y + (1 - decay_rate) * grad_y ** 2
x -= lr * grad_x / (np.sqrt(cache_x) + eps) # 更新参数
y -= lr * grad_y / (np.sqrt(cache_y) + eps)
history.append((x, y))
return history
# 测试
start_value = (5.0, 5.0)
learning_rate = 0.1
decay_rate = 0.9
result = rmsprop(start_value, lr=learning_rate, decay_rate=decay_rate, epochs=100)
print("最终结果:", result[-1])
运行结果如下:
Adam
Adam(Adaptive Moment Estimation)是目前最流行的自适应梯度下降方法之一。它结合了动量机制和 RMSprop 的优点,通过维护两个指数衰减的均值来调整学习率。以下是 Adam 的 Python 实现:
def adam(start, lr=0.001, beta1=0.9, beta2=0.999, eps=1e-8, epochs=100):
x, y = start
m_x, m_y = 0, 0 # 初始化一阶矩
v_x, v_y = 0, 0 # 初始化二阶矩
history = []
for t in range(1, epochs + 1):
grad_x = 2 * x # 计算梯度
grad_y = 4 * y
m_x = beta1 * m_x + (1 - beta1) * grad_x # 更新一阶矩
m_y = beta1 * m_y + (1 - beta1) * grad_y
v_x = beta2 * v_x + (1 - beta2) * grad_x ** 2 # 更新二阶矩
v_y = beta2 * v_y + (1 - beta2) * grad_y ** 2
# 偏差修正
m_x_hat = m_x / (1 - beta1 ** t)
m_y_hat = m_y / (1 - beta1 ** t)
v_x_hat = v_x / (1 - beta2 ** t)
v_y_hat = v_y / (1 - beta2 ** t)
x -= lr * m_x_hat / (np.sqrt(v_x_hat) + eps) # 更新参数
y -= lr * m_y_hat / (np.sqrt(v_y_hat) + eps)
history.append((x, y))
return history
# 测试
start_value = (5.0, 5.0)
learning_rate = 0.1
result = adam(start_value, lr=learning_rate, epochs=100)
print("最终结果:", result[-1])
运行结果如下:
可见Adam的收敛速度较慢。将epochs改为500,运行结果如下:
优化器选择指南
优化器的选择与特定的数据集有关。对于大多数的深度学习任务,Adam表现良好。但有时候,使用SGD也能调试出很好的结果。下面是关于优化器选择的总结:
优化器
优点
缺点
适用场景
SGD
简单、易于调参
收敛慢、易陷入局部最优
小数据集、简单任务
SGD+Momentum
加速收敛、减少振荡
需要调参
大数据集、复杂任务
NAG
比 Momentum 更快收敛
实现稍复杂
需要快速收敛的任务
Adam
自适应学习率、默认参数表现良好
可能在某些任务上过拟合
大多数深度学习任务
RMSProp
自适应学习率、适合非平稳目标
需要调参
RNN、非平稳目标函数
Adagrad
适合稀疏数据
学习率单调下降、可能过早停止
稀疏数据集(如 NLP)
Adadelta
无需设置初始学习率
收敛速度较慢
需要自适应学习率且不想调参的场景
总结
梯度下降作为机器学习和深度学习的核心优化算法,经历了从基础到现代的不断演进。从最简单的梯度下降,到随机梯度下降、动量机制,再到自适应梯度下降方法,每一步的改进都使得优化过程更加高效和稳定。
在实际应用中,选择合适的优化算法需要根据具体问题的特点来决定。对于简单的凸优化问题,传统的梯度下降或动量机制可能已经足够;而对于复杂的深度学习模型,Adam 等自适应方法则往往能够提供更好的性能。
- 2025-02-04
-
回复了主题帖:
RDK X3测评:5.ROS2命令行操作
Jacktang 发表于 2025-2-3 10:09
ROS2命令行工具确实很强大的
确实,值得深入去学习
-
发表了主题帖:
RDK X3测评:6.ROS开发流程
ROS架构
分布式系统设计
ROS采用独特的分布式架构设计,其核心思想是将机器人系统分解为多个独立运行的节点(Node)。这种设计模式带来三大优势:
模块解耦:每个节点负责单一功能(如传感器采集、运动控制等),通过标准接口通信。
跨平台部署:节点可运行在不同硬件平台(如工控机、嵌入式设备)。
动态重构:支持运行时节点的增删改操作。
核心通信机制
通信方式
传输协议
典型应用场景
Topic(话题)
发布/订阅
持续数据流(如传感器数据)
Service(服务)
请求/响应
即时指令执行(如启停控制)
Action(动作)
状态反馈
长时任务(如导航到目标点)
环境准备与基础概念
ROS 2开发环境要求
Ubuntu 22.04
ROS 2 Humble
Python 3.8+
colcon构建工具
sudo apt install python3-colcon-common-extensions
基础概念
功能包(Package)
功能包是ROS的原子单元,用于组织代码和资源。每个功能包通常包含一个特定功能或模块的代码、配置文件、启动文件等。
一个典型的功能包目录结构如下:
package_name/
├── CMakeLists.txt
├── package.xml
├── src/
├── include/
├── launch/
├── msg/
├── srv/
├── action/
└── scripts/
各目录的功能如下:
CMakeLists.txt:用于定义包的编译规则。
package.xml:包含包的元数据,如名称、版本、依赖等。
src/:存放C++源代码。
include/:存放C++头文件。
launch/:存放启动文件,用于启动多个节点。
msg/:存放自定义消息类型。
srv/:存放自定义服务类型。
action/:存放自定义动作类型。
scripts/:存放Python脚本。
节点(Node)
节点是ROS中的可执行进程,负责执行特定的任务。节点之间通过话题(Topic)、服务(Service)或动作(Action)进行通信。
在功能包的src/目录下创建C++或Python文件,从而创建相应的节点,例如my_node.cpp或my_node.py。
工作空间(Workspace)
工作空间是ROS开发的容器,用于组织和管理多个功能包。典型的工作空间结构如下:
workspace/
├── src/
├── install/
├── log/
└── build/
其中各目录的功能如下:
src/:存放所有功能包的源代码。
install/:存放编译后的可执行文件和库。
log/:存放日志文件。
build/:存放编译过程中生成的中间文件。
Python开发流程
以下是基于python的详细开发流程,包括功能包创建、包结构说明、节点开发以及编译配置等。
创建Python功能包
在ROS 2中,使用以下命令创建一个Python功能包:
source /opt/tros/humble/setup.bash
ros2 pkg create --build-type ament_python demo_py --dependencies rclpy std_msgs
--build-type ament_python:指定这是一个Python功能包,使用ament构建系统。
demo_py:功能包的名称。
--dependencies rclpy std_msgs:指定功能包的依赖项,rclpy是ROS 2的Python客户端库,std_msgs是标准消息类型。
创建的包结构
创建的功能包结构如下:
文件说明:
demo_py/:功能包的根目录。
demo_py/:Python模块目录,与功能包同名。
__init__.py:使该目录成为一个Python模块。
node_demo.py:Python节点代码文件(自行创建)。
package.xml:功能包的元数据文件,包含名称、版本、依赖等信息。
setup.cfg:配置Python包的安装方式。
setup.py:定义Python包的构建和安装规则。
Python节点开发示例
以下是一个简单的Python节点示例,发布消息到ROS 2话题。
# node_demo.py
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
class Talker(Node):
def __init__(self):
super().__init__('py_talker')
self.publisher = self.create_publisher(String, 'topic', 10)
self.timer = self.create_timer(1.0, self.timer_callback)
self.count = 0
def timer_callback(self):
msg = String()
msg.data = f'Hello ROS2: {self.count}'
self.publisher.publish(msg)
self.get_logger().info(f'Published: {msg.data}')
self.count += 1
def main(args=None):
rclpy.init(args=args)
node = Talker()
rclpy.spin(node)
rclpy.shutdown()
if __name__ == '__main__':
main()
代码说明:
Talker类:继承自Node,表示一个ROS 2节点。
create_publisher:创建一个发布者,发布String类型的消息到topic话题。
create_timer:创建一个定时器,每1秒触发一次timer_callback。
timer_callback:定时器回调函数,发布消息并打印日志。
main函数:初始化ROS 2,创建节点并运行。
配置编译规则
在ROS 2中,Python节点的编译和安装规则通过setup.py文件配置。
# setup.py关键配置
entry_points={
'console_scripts': [
'py_node = demo_py.node_demo:main',
],
}
entry_points:定义可执行脚本。
py_node:可执行命令的名称。
demo_py.node_demo:main:指定执行的Python模块和函数。
编译和运行节点
编译功能包
在工作空间根目录下运行以下命令:
colcon build --packages-select demo_py
colcon是ROS 2的构建工具。
--packages-select demo_py:仅编译demo_py功能包。
编译完成后的目录如下:
节点测试
首先我们运行创建的py_node节点。
#加载工作空间环境
source install/setup.bash
#运行节点
ros2 run demo_py py_node
输出如下:
可以看到每隔一秒会发布一个“Hello ROS2”的信息。
之后我们创建一个新的终端,查看我们创建的节点是否能够正常工作。首先查看话题列表
#查看话题列表
ros2 topic list
可以看到当前存在topic话题,之后查看话题内容
#查看话题内容
ros2 topic echo /topic
可以看到,当前话题中能够接收到我们创建的节点发送的信息,说明我们的节点运行功能正常。
C++开发流程
在ROS 2中,使用C++开发功能包和节点的流程与Python类似,但涉及更多的编译配置。以下是详细的C++开发全流程,包括功能包创建、节点开发、包结构说明以及编译配置。
创建C++功能包
使用以下命令创建一个C++功能包:
ros2 pkg create --build-type ament_cmake demo_cpp \--dependencies rclcpp std_msgs
--build-type ament_cmake:指定这是一个C++功能包,使用ament_cmake构建系统。
demo_cpp:功能包的名称。
--dependencies rclcpp std_msgs:指定功能包的依赖项,rclcpp是ROS 2的C++客户端库,std_msgs是标准消息类型。
创建的包结构
创建的功能包结构如下:
文件说明:
CMakeLists.txt:定义C++项目的编译规则。
include/:存放C++头文件。
src/:存放C++源代码文件。
package.xml:功能包的元数据文件,包含名称、版本、依赖等信息。
C++节点开发示例
以下是一个简单的C++节点示例,在src目录下创建cpp_node.cpp文件,订阅ROS 2话题并打印接收到的消息。
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
using namespace std::chrono_literals;
class Listener : public rclcpp::Node {
public:
Listener() : Node("cpp_listener") {
subscription_ = this->create_subscription<std_msgs::msg::String>(
"topic", 10,
[this](const std_msgs::msg::String::SharedPtr msg) {
RCLCPP_INFO(this->get_logger(), "Received: '%s'",
msg->data.c_str());
});
}
private:
rclcpp::Subscription<std_msgs::msg::String>::SharedPtr subscription_;
};
int main(int argc, char * argv[]) {
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<Listener>());
rclcpp::shutdown();
return 0;
}
代码说明:
Listener类:继承自Node,表示一个ROS 2节点。
create_subscription:创建一个订阅者,订阅String类型的消息。
回调函数:接收到消息后打印日志。
main函数:初始化ROS 2,创建节点并运行。
配置编译规则
在ROS 2中,C++节点的编译规则通过CMakeLists.txt文件配置。CMakeLists.txt 关键配置如下
cmake_minimum_required(VERSION 3.8)
project(demo_cpp)
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
# find dependencies
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)
add_executable(cpp_node src/cpp_node.cpp)
ament_target_dependencies(cpp_node rclcpp std_msgs)
if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
# the following line skips the linter which checks for copyrights
# comment the line when a copyright and license is added to all source files
set(ament_cmake_copyright_FOUND TRUE)
# the following line skips cpplint (only works in a git repo)
# comment the line when this package is in a git repo and when
# a copyright and license is added to all source files
set(ament_cmake_cpplint_FOUND TRUE)
ament_lint_auto_find_test_dependencies()
endif()
ament_package()
代码说明:
find_package:查找所需的依赖包。
add_executable:定义可执行文件。
ament_target_dependencies:为目标添加依赖。
ament_package:导出功能包的依赖。
编译和运行节点
使用colcon构建工具编译功能包
colcon build --symlink-install
其中,--symlink-install参数为:使用符号链接而不是复制文件。
之后运行节点
source install/setup.bash
ros2 run demo_cpp cpp_node
运行结果如下:
可以看到,此时能够正常接收到上面python节点发送的信息,说明我们的cpp_node节点能够正常工作。
- 2025-02-01
-
发表了主题帖:
RDK X3测评:5.ROS2命令行操作
ROS2(Robot Operating System 2)作为机器人开发领域的核心工具,其命令行操作是开发者与系统交互的重要桥梁。无论是启动节点、管理话题,还是调试参数、记录数据,ROS2命令行工具都提供了强大的支持。而TogetherROS是基于ROS2深度优化的,很多模块还是会复用ROS2中的功能,所有ROS2的原生功能也可以支持。本文将从整体上学习了解ROS2命令行的功能,并通过详细的示例和解释,深入分析其核心操作,全面掌握ROS2命令行的使用技巧。
ROS2命令行概览
ROS2的命令行工具基于ros2命令,它是ROS2生态系统的核心接口。通过ros2命令,开发者可以完成以下主要任务:
节点管理:启动、查看和调试节点。
话题操作:查看话题列表、监听话题消息、发布消息等。
服务操作:调用服务、查看服务列表。
参数管理:获取、设置和修改节点参数。
数据记录与回放:使用ros2 bag工具记录和回放话题数据。
系统调试:查看节点关系图、监控系统状态。
在命令行中直接输入ros2,可以看到相关命令的使用介绍。
接下来,我们将以上一节中的publisher_member_function和subscriber_member_function为例,逐步深入分析这些功能。
启动节点
启动Publisher节点
首先,我们启动一个发布者节点:
source /opt/tros/humble/setup.bash
ros2 run examples_rclcpp_minimal_publisher publisher_member_function
examples_rclcpp_minimal_publisher是包名,publisher_member_function是节点名(这里不起作用,节点名实际为minimal_publisher)。
这个节点会周期性地向/topic话题发布消息。
启动Subscriber节点
接下来,我们启动一个订阅者节点:
source /opt/tros/humble/setup.bash
ros2 run examples_rclcpp_minimal_subscriber subscriber_member_function
examples_rclcpp_minimal_subscriber是包名,subscriber_member_function是节点名(这里不起作用,节点名实际为minimal_subscriber)。
这个节点会订阅/topic话题,并打印接收到的消息。
节点管理
查看节点列表
启动节点后,可以使用如下命令查看当前运行的节点:
ros2 node list
输出示例:
这表示当前系统中运行了两个节点:/minimal_publisher和/minimal_subscriber。
查看节点信息
通过如下命令,可以获取节点的详细信息。例如,查看发布者节点的信息:
ros2 node info /minimal_publisher
输出示例:
这里显示了/minimal_publisher节点的发布者和服务信息。
同样,查看订阅者节点的信息:
ros2 node info /minimal_subscriber
输出示例:
这里显示了/minimal_subscriber节点的订阅者和服务信息。
话题操作
查看话题列表
使用如下命令可以查看当前所有的话题:
ros2 topic list
输出示例:
这里列出了系统中所有活跃的话题,包括/topic。
监听话题消息
通过如下命令,可以实时查看某个话题的消息内容。例如,监听/topic话题:
ros2 topic echo /topic
输出示例:
这里显示了/topic话题上发布的消息内容。
发布消息
使用ros2 topic pub命令,可以手动向某个话题发布消息。例如,向/topic话题发布一条消息:
ros2 topic pub /topic std_msgs/msg/String "{data: 'Hello from CLI'}"
这行命令向/topic话题发布了一条消息,内容为Hello from CLI。在subscriber端我们可以看到,接收到了同样的消息。
参数管理:配置节点的行为
查看参数列表
使用如下命令可以查看某个节点的所有参数。例如,查看发布者节点的参数:
ros2 param list /minimal_publisher
输出示例:
获取参数值
通过命令如下,可以获取某个参数的值。例如,获取use_sim_time参数的值:
ros2 param get /minimal_publisher use_sim_time
输出示例:
设置参数值
使用如下命令,可以修改某个参数的值。例如,将use_sim_time参数设置为True:
ros2 param set /minimal_publisher use_sim_time True
这行命令将/minimal_publisher节点的use_sim_time参数设置为True。
数据记录与回放
记录话题数据
使用ros2 bag record命令可以记录指定话题的数据。例如,记录/topic话题的数据:
ros2 bag record /topic
这行命令会开始记录/topic话题上的所有消息,并将其保存到一个bag文件中。
查看bag信息
通过如下命令,可以查看之前记录的bag文件信息:
ros2 bag info rosbag2_2025_02_01-19_50_01
输出示例:
回放话题数据
通过如下命令,可以回放之前记录的bag文件:
ros2 bag play rosbag2_2025_02_01-19_16_17
这行命令会回放指定bag文件中的数据,模拟话题消息的发布。
打开另一个终端,使用ros2 topic echo监听/topic话题:
ros2 topic echo /topic
我们可以看到以下的输出
系统调试
查看节点关系图
使用rqt_graph工具可以可视化节点、话题和服务之间的关系:
#安装
sudo apt-get install ros-humble-rqt
sudo apt-get install ros-humble-rqt-common-plugins
#运行
rqt_graph
这个命令会打开一个图形化界面,显示当前系统中所有节点和话题的连接关系。
监控系统状态
通过ros2 doctor命令,可以检查系统的健康状况:
ros2 doctor
这个命令会生成一份报告,列出系统中可能存在的问题。
总结
通过本文的详细介绍,我们以publisher_member_function和subscriber_member_function为例,初步探索了ROS2命令行的核心功能。从节点管理到话题操作,从参数配置到数据记录,ROS2命令行工具为开发者提供了强大的支持。
-
回复了主题帖:
《深度学习的数学——使用Python语言》10.全连接网络的反向传播
秦天qintian0303 发表于 2025-2-1 08:20
反向传播是一步一步反算,还是直接和反馈似的
反向传播是从输出层开始,一步一步往回计算误差,并调整每一层的参数。它利用了链式法则,每一步都依赖前一步的结果,是逐步反算的。
- 2025-01-31
-
发表了主题帖:
《深度学习的数学——使用Python语言》10.全连接网络的反向传播
基于书中第十章,本节中,我们将深入学习反向传播的原理,并通过MNIST手写数字识别任务,结合PyTorch代码实现,手动编写反向传播逻辑,从而加深对于反向传播内部机制的理解。
神经网络与反向传播的基本概念
神经网络是一种由多层神经元组成的计算模型,每一层神经元通过权重和偏置连接起来。神经网络的学习过程可以分为两个阶段:前向传播和反向传播。
前向传播:输入数据从输入层经过隐藏层,最终到达输出层,计算网络的预测结果。
反向传播:根据预测结果与真实标签之间的误差,从输出层逐层向前计算梯度,并更新网络的权重和偏置。
反向传播的核心是链式法则,它通过将误差从输出层传递回输入层,计算每一层参数的梯度,从而指导参数的更新。
MNIST手写数字识别任务
MNIST是一个经典的手写数字识别数据集,包含60,000张训练图像和10,000张测试图像,每张图像是一个28x28的灰度图,标签为0到9的数字。我们的目标是构建一个神经网络,能够正确识别这些手写数字。
反向传播的数学原理
为了更好地理解反向传播,我们需要从数学角度分析它的工作原理。假设我们有一个简单的两层神经网络,输入层大小为784(28x28),隐藏层大小为128,输出层大小为10(对应10个数字类别)。
前向传播
前向传播的计算过程如下:
输入层到隐藏层:
其中,X是输入数据,W1是输入层到隐藏层的权重矩阵,b1是偏置,σ是激活函数(如ReLU)。
隐藏层到输出层:
其中,W2是隐藏层到输出层的权重矩阵,b2是偏置,softmax函数用于将输出转换为概率分布。
损失函数
我们使用交叉熵损失函数来衡量预测结果与真实标签之间的差异:
其中,yi是真实标签的one-hot编码,a2i是输出层的预测概率。
反向传播
反向传播的目标是计算损失函数对每一层参数的梯度,并更新参数。具体步骤如下:
计算输出层的误差:
计算隐藏层的误差:
其中,σ′是激活函数的导数。
计算梯度并更新参数:
使用梯度下降法更新参数:
其中,η是学习率。
代码实现
下面我们通过PyTorch实现一个简单的两层神经网络,并手动编写反向传播逻辑,同时利用CUDA加速训练过程。
import torch
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
# 检查CUDA是否可用
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print(f'Using device: {device}')
# 定义数据预处理
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.5,), (0.5,))
])
# 加载MNIST数据集
train_dataset = datasets.MNIST(root='./data', train=True, download=True, transform=transform)
test_dataset = datasets.MNIST(root='./data', train=False, download=True, transform=transform)
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=64, shuffle=False)
# 定义神经网络模型
class SimpleNN:
def __init__(self, input_size, hidden_size, output_size):
# 初始化参数
self.W1 = torch.randn(input_size, hidden_size, device=device) * 0.01
self.b1 = torch.zeros(1, hidden_size, device=device)
self.W2 = torch.randn(hidden_size, output_size, device=device) * 0.01
self.b2 = torch.zeros(1, output_size, device=device)
def forward(self, X):
# 前向传播
self.z1 = torch.matmul(X, self.W1) + self.b1
self.a1 = torch.relu(self.z1)
self.z2 = torch.matmul(self.a1, self.W2) + self.b2
self.a2 = torch.softmax(self.z2, dim=1)
return self.a2
def backward(self, X, y, output, learning_rate):
# 反向传播
m = X.shape[0]
# 输出层误差
dz2 = output - y
dW2 = torch.matmul(self.a1.T, dz2) / m
db2 = torch.sum(dz2, dim=0, keepdim=True) / m
# 隐藏层误差
dz1 = torch.matmul(dz2, self.W2.T) * (self.a1 > 0).float()
dW1 = torch.matmul(X.T, dz1) / m
db1 = torch.sum(dz1, dim=0, keepdim=True) / m
# 更新参数
self.W2 -= learning_rate * dW2
self.b2 -= learning_rate * db2
self.W1 -= learning_rate * dW1
self.b1 -= learning_rate * db1
def train(self, train_loader, epochs, learning_rate):
for epoch in range(epochs):
for images, labels in train_loader:
# 将图像展平并移动到GPU
images = images.view(-1, 28*28).to(device)
# 将标签转换为one-hot编码并移动到GPU
y = torch.zeros(labels.size(0), 10, device=device)
y[torch.arange(labels.size(0)), labels] = 1
# 前向传播
output = self.forward(images)
# 反向传播
self.backward(images, y, output, learning_rate)
if (epoch+1) % 5 == 0:
print(f'Epoch [{epoch+1}/{epochs}]')
def evaluate(self, test_loader):
correct = 0
total = 0
for images, labels in test_loader:
images = images.view(-1, 28*28).to(device)
outputs = self.forward(images)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted.cpu() == labels).sum().item()
print(f'Test Accuracy: {100 * correct / total:.2f}%')
# 初始化模型
input_size = 28 * 28
hidden_size = 128
output_size = 10
model = SimpleNN(input_size, hidden_size, output_size)
# 训练模型
model.train(train_loader, epochs=20, learning_rate=0.1)
# 测试模型
model.evaluate(test_loader)
在SimpleNN的init部分,W1 和 W2 分别是输入层到隐藏层和隐藏层到输出层的权重矩阵。我们使用 torch.randn 生成服从标准正态分布的随机数,并乘以 0.01 来缩小初始值的范围。b1 和 b2 是偏置,初始化为零。所有参数都被放置在指定的设备(如GPU)上。
在forward()中,实现了前向传播的过程。首先,输入数据 X 与权重矩阵 W1 进行矩阵乘法,再加上偏置 b1,得到隐藏层的加权输入 z1。然后,通过ReLU激活函数对 z1 进行非线性变换,得到隐藏层的激活值 a1。之后,隐藏层的激活值 a1 与权重矩阵 W2 进行矩阵乘法,再加上偏置 b2,得到输出层的加权输入 z2。最后,通过softmax函数将 z2 转换为概率分布 a2,表示每个类别的预测概率。
在反向传播中,通过链式法则,误差从输出层逐层向前传递,计算每一层的梯度。首先,计算输出层的误差 dz2,即预测值 output 与真实标签 y 的差值。然后,计算权重 W2 的梯度 dW2,通过将隐藏层的激活值 a1 的转置与误差 dz2 相乘,并除以样本数 m。偏置 b2 的梯度 db2 是误差 dz2 的均值。之后,计算隐藏层的误差 dz1,通过将输出层的误差 dz2 与权重矩阵 W2 的转置相乘,再乘以ReLU激活函数的导数(即 a1 > 0 的布尔值转换为浮点数)。然后,计算权重 W1 的梯度 dW1,通过将输入数据 X 的转置与误差 dz1 相乘,并除以样本数 m。偏置 b1 的梯度 db1 是误差 dz1 的均值。最后,使用梯度下降法更新参数。权重和偏置分别减去学习率与对应梯度的乘积。
运行结果如下,可以看到通过上述代码,我们能够得到较高的准确率。
深入思考
反向传播是神经网络训练的核心算法,但它并非完美无缺。在实际应用中,我们可能会遇到以下问题:
梯度消失:在深层网络中,梯度可能会逐渐变小,导致靠近输入层的参数几乎无法更新。使用ReLU激活函数和批量归一化可以有效缓解这一问题。
过拟合:神经网络容易过拟合训练数据。可以通过正则化(如L2正则化)和Dropout来减少过拟合。
计算效率:反向传播的计算复杂度较高,尤其是在大规模数据集和深层网络中。使用GPU加速和分布式计算可以显著提高训练速度。
总结
通过MNIST手写数字识别任务,我们从理论和代码两个层面深入探讨了反向传播的原理和实现。不仅学习了反向传播的数学原理,还通过手动编写反向传播逻辑,更好地理解了其内部机制。