前言
ROS2好啊,ROS2得学。
0. 基础知识
0.0 ROS基础知识
0.0.1 什么是ros
0.0.2 ros和ros2有什么区别
0.1 ROS2系统信息相关指令
查询系统版本
printenv ROS_DISTRO
0.2 常用软件安装
一键安装ros 教程
wget http://fishros.com/install -O fishros && . fishros
1. ros2 新建工作空间和节点
1.1新建工作空间
1.1.1 工作空间简介
ROS 2(Robot Operating System 2)中的工作空间(Workspace)是组织和管理 ROS 2 软件包(packages)的核心目录结构。它类似于一个“项目文件夹”,用于存放源代码、编译产物、依赖配置等,是开发、构建和运行 ROS 2 节点的基础环境。
1.1.2 创建工作空间
mkdir -p 工作空间名称_ws/src #src主要用来存放代码
cd 工作空间名称_ws #进入工作空间
mkdir 命令后面的 -p 选项是 “parents”(父目录) 的缩写,它的作用是:
递归创建目录 —— 如果指定的路径中某些父目录不存在,mkdir -p 会自动把它们一并创建出来;如果目录已存在,则不会报错,而是静默忽略。
1.1.3 编译工作空间
colcon build
构建完成后,我们应该看到build、install和log目录:
ROS系统中一个典型的工作空间结构如上所示,这个yahboomcar_ws就是工作空间的根目录,里边会有四个子目录,或者叫做四个子空间。
-
src,代码空间,未来编写的代码、脚本,都需要人为的放置到这里;
-
build,编译空间,保存编译过程中产生的中间文件;
-
install,安装空间,放置编译得到的可执行文件和脚本;
-
log,日志空间,编译和运行过程中,保存各种警告、错误、信息等日志。
总体来讲,这四个空间的文件夹,我们绝大部分操作都是在src中进行的,编译成功后,就会执行install里边的结果,build和log两个文件夹用的很少。
这里也要强调一点,工作空间的名称我们可以自己定义,数量也并不是唯一的,比如:
工作空间1:ros2_ws_a, 用于A机器人的开发
工作空间1:ros2_ws_b, 用于B机器人的开发
工作空间1:ros2_ws_c, 用于C机器人的开发
以上情况是完全允许的,就像是我们在集成开发环境中创建了多个新工程一样,都是并列存在的关系。
1.1.4 设置环境变量
编译成功后,为了让系统能够找到我们的功能包和可执行文件,还需要设置环境变量:
# 仅在当前终端生效
source install/setup.bash
# 所有终端均生效
echo "source install/setup.bash" >> ~/.bashrc
至此,我们就完成了工作空间的创建、编译和配置。
1.2 新建功能包
1.2.1 功能包简介
每个机器人可能有很多功能,比如移动控制、视觉感知、自主导航等,如果我们把这些功能的源码都放到一起当然也是可以的,但是当我们想把其中某些功能分享给别人时,就会发现代码都混合到了一起,很难拆分出来。
功能包就是这个原理,我们把不同功能的代码划分到不同的功能包中,尽量降低他们之间的耦合关系,当需要在ROS社区中分享给别人的时候,只需要说明这个功能包该如何使用,别人很快就可以用起来了。
所以功能包的机制,是提高ROS中软件复用率的重要方法之一。
1.2.2 创建功能包
如何在ROS2中创建一个功能包呢?我们可以使用这个指令:
ros2 pkg create <package_name> –build-type <build-type> –dependencies <dependencies> –node-name <node-name>
ros2命令中:
-
pkg:表示功能包相关的功能;
-
create:表示创建功能包;
-
package_name:新建功能包的名字;
-
build-type:表示新创建的功能包是C++还是Python的,如果使用C++或者C,那这里就跟ament_cmake,如果使用Python,就跟ament_python;
-
dependencies:表示功能包的依赖项,C++功能包需包含rclcpp,Python功能包需包含rclpy ,还有其它需要的依赖;
-
node-name:可执行程序的名称,会自动生成对应的源文件并生成配置文件;
比如在终端中分别创建C++和Python版本的功能包:
cd 工作空间名称_ws/src
# 创建C++功能包
ros2 pkg create pkg_helloworld_cpp –build-type ament_cmake –dependencies rclcpp –node-name helloworld
# 创建Python功能包
ros2 pkg create pkg_helloworld_py –build-type ament_python –dependencies rclpy –node-name helloworld
1.2.3 编译功能包
在创建好的功能包中,我们可以继续完成代码的编写,之后需要编译和配置环境变量,才能正常运行:
cd ~/工作空间_ws
# 编译工作空间所有功能包
colcon build
# 编译指定功能包(一个或多个)
colcon build –packages-select 功能包列表
#配置环境变量
source install/setup.bash
1.2.4 带功能包的完整工作空间结构
ROS2工作空间的目录结构如下:
WorkSpace — 自定义的工作空间。
|— build:存储中间文件的目录,该目录下会为每一个功能包创建一个单独子目录。
|— install:安装目录,该目录下会为每一个功能包创建一个单独子目录。
|— log:日志目录,用于存储日志文件。
|— src:用于存储功能包源码的目录。
|– C++功能包
|– package.xml:包信息,比如:包名、版本、作者、依赖项。
|– CMakeLists.txt:配置编译规则,比如源文件、依赖项、目标文件。
|– src:C++源文件目录。
|– include:头文件目录。
|– msg:消息接口文件目录。
|– srv:服务接口文件目录。
|– action:动作接口文件目录。
|– Python功能包
|– package.xml:包信息,比如:包名、版本、作者、依赖项。
|– setup.py:与C++功能包的CMakeLists.txt类似。
|– setup.cfg:功能包基本配置文件。
|– resource:资源目录。
|– test:存储测试相关文件。
|– 功能包同名目录:Python源文件目录。
另外,无论是Python功能包还是C++功能包,都可以自定义一些配置文件相关的目录。
|– C++或Python功能包
|– launch:存储launch文件。
|– rviz:存储rviz2配置相关文件。
|– urdf:存储机器人建模文件。
|– params:存储参数文件。
|– world:存储仿真环境相关文件。
|– map:存储导航所需地图文件。
|– ……
上述这些目录也可以定义为其他名称,或者根据需要创建其他一些目录。
1.3 新建工作节点
1.3.1 节点简介
在通信时,不论采用何种方式,通信对象的构建都依赖于节点(Node),在ROS2中,一般情况下每个节点都对应某一单一的功能模块(例如:雷达驱动节点可能负责发布雷达消息,摄像头驱动节点可能负责发布图像消息)。一个完整的机器人系统可能由许多协同工作的节点组成,ROS2中的单个可执行文件(C++程序或Python程序)可以包含一个或多个节点。
1.3.2 创建节点流程
编程接口初始化
创建节点并初始化
实现节点功能
销毁节点并关闭接口
1.3.2 Hello World节点案例
1.3.2.1 创建python功能包
cd ~/工作空间名称_ros2_ws/工作空间名称_ws/src
ros2 pkg create pkg_helloworld_py –build-type ament_python –dependencies rclpy –node-name helloworld
1.3.2.2 编写代码
执行上面命令后会创建pkg_helloworld_py,同时会创建helloworld.py文件来编写节点:

删除原本 helloworld.py 的代码,写你自己想写的代码。
完成代码的编写后需要设置功能包的编译选项,让系统知道Python程序的入口,打开功能包的setup.py文件,加入如下入口点的配置:

1.3.2.3 编译功能包
cd ~/工作空间名称_ros2_ws/工作空间名称_ws
colcon build #编译全部
colcon build –packages-select pkg_helloworld_py #pkg_helloworld_py:要编译的包名字
source install/setup.bash #配置环境变量
1.3.2.4 运行节点
ros2 run pkg_helloworld_py helloworld
运行成功后,可以在终端中看到循环打印“Hello World”字符串的效果:

2. ROS2通信相关
2.1 modbus RTU通信
主要使用python3-pymodbus库
# 查看是否有可用的官方包
sudo apt search python3-pymodbus
# 如果存在则安装
sudo apt install python3-pymodbus
# 查看版本
python3 -c "from pymodbus import __version__; print(__version__)"
查找USB串口
lsusb
# 查找设备文件名(通过插拔操作识别,通常是 /dev/ttyUSB0)
ls /dev/tty*
# 识别用户组
ls -a -g /dev/ttyUSB*
# 当前用户加入用户组
sudo usermod -a -G dialout $USER
#查看当前用户是否在设备组
groups $USER
应用代码和测试报文
# pymodbus 3.6.9
from pymodbus.client import ModbusSerialClient as ModbusClient
from pymodbus.transaction import ModbusRtuFramer
import time
def run_rs485_master():
# 创建Modbus RTU客户端
client = ModbusClient(
method='rtu',
port='/dev/ttyUSB0',
baudrate=9600,
timeout=1, # 增加超时时间
parity='N',
stopbits=1,
bytesize=8,
retries=3, # 自动重试次数
strict=True
)
# 连接设备
connection = client.connect()
print(f"Connected: {connection}")
try:
while True:
# 读取保持寄存器 (功能码03)
result = client.read_holding_registers(
address=0,
count=10,
slave=1 # 从站地址
)
if not result.isError():
print(f"保持寄存器值: {result.registers}")
else:
print(f"读取错误: {result}")
# 写入单个寄存器 (功能码06)
write_result = client.write_register(
address=0,
value=1234,
slave=1
)
if not write_result.isError():
print("写入寄存器成功")
time.sleep(1)
except KeyboardInterrupt:
print("用户中断")
finally:
client.close()
if __name__ == "__main__":
run_rs485_master()
测试报文
主机(客户端): 01 03 00 00 00 0A C5 CD 从机(服务器): 01 03 14 00 01 00 02 00 03 00 04 00 05 00 06 00 07 00 08 00 09 00 0A 8F 16 主机(客户端): 01 06 00 00 04 D2 0B 57 从机(服务器): 01 06 00 00 04 D2 0B 57
后面这块儿主要参考古月居上的文献:
PLC与ROS2通信模型: 对于典型的PLC与ROS2系统的数据通信,通信的基本公式为:![]()
其中:
-
DataPLC表示从PLC读取到的数据。
-
Modbus()表示通过MODBUS协议与PLC进行通信。
-
IPPLC和PortPLC是PLC的IP地址和端口。
-
Function Code表示操作码,如读取寄存器等。
-
Data Address表示需要读取的PLC寄存器地址。
ROS2节点的数据发布与订阅模型: ROS2使用发布-订阅模型进行数据交换,公式如下:
![]()
其中:
-
MsgROS2为ROS2节点发布的数据消息。
-
f(DataPLC)表示ROS2对PLC数据进行处理后的消息生成过程。
控制指令的生成与下发: 生成控制指令的基本过程可以表示为:
![]()
其中:
-
CmdPLC是ROS2系统向PLC下发的控制指令。
-
Control Logic()表示控制逻辑算法,根据处理后的数据生成控制命令。
-
Processed Data是经过ROS2处理的数据。
2.2 部署流程
安装ROS2: 安装ROS2 Foxy版本,通过以下命令安装:
sudo apt update
sudo apt install ros-foxy-desktop
配置ROS2工作空间:
mkdir -p ~/ros2_ws/src
cd ~/ros2_ws
colcon build
source install/setup.bash
安装Modbus库: 安装用于与PLC进行Modbus通信的库,如modbus_tk:
pip install modbus_tk
配置PLC与ROS2的通信: 配置PLC与ROS2之间的通信,通过MODBUS/TCP协议进行数据交互。在ROS2节点中,通过modbus_tk库创建与PLC的连接。
编写ROS2节点与PLC交互: 创建ROS2节点用于与PLC进行数据交互和控制,下面是一个简单的示例代码。
#include <rclcpp/rclcpp.hpp>
#include <modbus_tk/modbus_tcp.h>
class PLCROS2Node : public rclcpp::Node {
public:
PLCROS2Node() : Node("plc_ros2_node") {
modbus::TCPClient client;
try {
client.connect("192.168.1.100", 502); // PLC的IP地址和端口
RCLCPP_INFO(this->get_logger(), "Connected to PLC");
// 创建定时器每秒钟读取一次PLC数据
timer_ = this->create_wall_timer(
std::chrono::seconds(1), std::bind(&PLCROS2Node::read_data, this));
} catch (const std::exception &e) {
RCLCPP_ERROR(this->get_logger(), "Failed to connect to PLC: %s", e.what());
}
}
private:
void read_data() {
try {
// 从PLC读取寄存器值
uint16_t data = client.read_holding_registers(0, 1)[0]; // 读取寄存器0的值
RCLCPP_INFO(this->get_logger(), "Received data from PLC: %d", data);
// 在这里,可以将数据发布到ROS2主题或进行进一步处理
} catch (const std::exception &e) {
RCLCPP_ERROR(this->get_logger(), "Failed to read data from PLC: %s", e.what());
}
}
modbus::TCPClient client;
rclcpp::TimerBase::SharedPtr timer_;
};
int main(int argc, char **argv) {
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<PLCROS2Node>());
rclcpp::shutdown();
return 0;
}
代码解读
modbus::TCPClient:这个类用于建立与PLC设备的TCP连接,发送Modbus请求并读取PLC寄存器。
read_data():该函数在每次定时器触发时运行,读取PLC的一个寄存器数据并输出。
timer_:定时器每隔一秒触发一次read_data(),实现周期性的读取PLC数据。
连接与通信:client.connect("192.168.1.100", 502)
用来连接PLC设备,client.read_holding_registers(0, 1)用来读取指定寄存器的数据。
运行效果说明
1. 系统初始化
-
PLC与ROS2节点的连接:在系统启动时,ROS2节点通过MODBUS协议连接到PLC设备。成功建立连接后,ROS2节点会在终端输出一条信息,表示PLC已成功连接。
输出示例:
[INFO] [1637841204.123456789] [plc_ros2_node]: Connected to PLC
-
定时器启动:定时器在节点启动时也开始工作。每秒触发一次read_data函数。通过此定时器,ROS2节点会周期性地读取PLC的数据,并执行相应的处理逻辑。
输出示例:
[INFO] [1637841205.123456789] [plc_ros2_node]: Received data from PLC: 1023
[INFO] [1637841206.123456789] [plc_ros2_node]: Received data from PLC: 1025在每次read_data函数被调用时,ROS2节点会尝试从PLC设备读取特定的寄存器(例如寄存器地址0),并输出从PLC中读取的数据。每次读取的值会根据PLC中传感器或控制器的状态而变化。
2. 数据流
-
PLC数据采集:PLC系统周期性地收集来自现场设备(如传感器、执行器等)的数据。数据通常是实时变化的,如传感器采集到的温度、压力等值。这些数据被存储在PLC的寄存器中,ROS2节点通过MODBUS协议定期读取。
-
数据交换:每次ROS2节点读取数据时,数据会通过MODBUS协议进行传输。典型的数据格式为16位的寄存器值,这些值在ROS2节点中可以进一步处理。
3. 实时反馈
-
数据处理:ROS2节点会将PLC中获取到的数据(如温度、压力等)进行处理。处理后的数据可以用来更新系统的状态或者生成控制指令。比如,可以对数据进行滤波、计算平均值、进行阈值判断等。
输出示例(数据处理后的反馈):
[INFO] [1637841207.123456789] [plc_ros2_node]: Processed data: 23.5 (Temperature)
在该示例中,温度数据从PLC中获取,并经过处理(如单位转换)后,输出为23.5°C。
-
控制指令生成:根据处理后的数据,ROS2节点可能会生成控制指令,反馈到PLC中控制设备。例如,如果温度超过预设的阈值,ROS2节点可以生成控制指令要求PLC启动冷却设备。
控制指令输出示例:
[INFO] [1637841210.123456789] [plc_ros2_node]: Control command: Turn on cooling system
4. 异常处理与错误信息
-
通信失败:如果在ROS2节点读取PLC数据时出现问题(如网络不通、PLC未响应等),系统会在终端输出错误信息。例如,当无法连接到PLC时,ROS2节点会打印出以下错误:
[ERROR] [1637841211.123456789] [plc_ros2_node]: Failed to connect to PLC: Connection timeout
-
数据读取失败:如果数据读取操作失败(如由于设备故障或其他原因),ROS2节点也会打印出相关的错误信息:
[ERROR] [1637841212.123456789] [plc_ros2_node]: Failed to read data from PLC: Modbus error
5. 系统监控与调试
-
状态监控:在实际部署时,可以使用ROS2的rqt_graph等工具来监控ROS2系统中各个节点的状态。这些工具可以帮助开发人员确认PLC与ROS2节点之间的通信是否正常,是否有数据延迟或丢失。
例如,使用以下命令启动ROS2的图形界面工具:
ros2 run rqt_graph rqt_graph
在rqt_graph中,用户可以清晰地看到各个ROS2节点之间的通信流和数据流。
-
日志输出:在系统运行过程中,ROS2会记录日志信息,可以使用ros2 log命令查看详细日志。这些日志包括了各个节点的启动、通信、数据处理和错误信息,对于调试和故障排查非常有帮助。
查看日志示例:
ros2 log plc_ros2_node
6. 性能与实时性
-
数据采集频率:ROS2节点通过定时器控制数据的采集频率。在代码示例中,定时器每秒钟触发一次read_data函数。然而,实际应用中,数据采集频率可以根据需要进行调整(例如通过修改定时器的时间间隔)。
-
响应时间:由于ROS2节点本身支持实时调度,可以确保在一定时间内(如毫秒级别)对PLC数据进行处理和响应。控制系统可以根据实时处理结果立即采取动作,保证工业自动化系统的实时性。
-
系统稳定性:由于ROS2和PLC都具有高度稳定性,系统能够在长时间运行中保持稳定的通信与数据交换。即使在PLC设备或网络存在短暂波动时,ROS2节点也能通过异常处理机制重新连接并恢复通信。
远程桌面设置
使用windows自带的远程桌面
ubuntu22.04安装IP查询工具
sudo apt install net-tools
IP查询命令:
ifconfig -a
SSH服务安装
# 安装ssh服务: sudo apt-get install ssh # 启动SSH服务: sudo /etc/init.d/ssh start # 开机自启 sudo systemctl enable ssh
安装防火墙
sudo apt install firewalled
# 如无法连接,开通22端口: firewall-cmd –add-port=22/tcp –permanent firewall-cmd –reload
开放3389端口
# 开启3389端口:
firewall-cmd –add-port=3389/tcp –permanent
firewall-cmd –reload
一、串口调试
1. 安装串口调试工具
安装串口调试工具:
sudo apt-get install cutecom
打开串口工具:
Sudo cutecom
2. MODBUS RTU通信
# 查看是否有可用的官方包
sudo apt search python3-pymodbus
# 如果存在则安装
sudo apt install python3-pymodbus
#查看版本
python3 -c "from pymodbus import __version__; print(__version__)"
安装3.0以版本的pymodbus
# 1. 卸载可能存在的旧版(可选)
sudo apt remove python3-pymodbus
# 2. 使用 –user 安装到用户目录(无需 sudo,不污染系统)
pip3 install –user –upgrade pymodbus
# 3. 验证版本
python3 -c "from pymodbus import __version__; print(__version__)"
验证是否可以用于ros2
在你的 ROS 2 工作空间中运行:
source /opt/ros/rolling/setup.bash
python3 -c "from pymodbus.client import ModbusSerialClient; print('OK')"
如果输出 OK,说明 ROS 2 能正常调用新版 pymodbus。
验证pymodbus是否可用
python3 -c "from pymodbus.client import ModbusSerialClient; print('✅ pymodbus 导入成功')"
如果输出 ✅ pymodbus 导入成功,说明安装成功且 Python 能找到它。
回到工作空间
cd ~/huizhoucar_ws
查看目录结构,安装tree命令
sudo apt update
sudo apt install tree
查看目录结构
tree
编译工作空间
colcon build –symlink-install
–symlink-install 让 Python 文件修改后无需重新编译即可生效
再次查看目录结构
tree
激活工作空间
先激活系统级 ROS 2 环境(Humble)
source /opt/ros/humble/setup.bash
编译工作空间
colcon build –symlink-install
激活你的工作空间(叠加到系统环境)
source install/setup.bash
完整激活流程(每次新开终端都要做)
# 1. 激活 ROS 2 Humble
source /opt/ros/humble/setup.bash
# 2. 进入工作空间
cd ~/huizhoucar_ws
# 3. 激活你的工作空间
source install/setup.bash
建议将前两行加入 ~/.bashrc,避免重复输入:
echo "source /opt/ros/humble/setup.bash" >> ~/.bashrc
echo "source ~/huizhoucar_ws/install/setup.bash" >> ~/.bashrc
然后执行
source ~/.bashrc
或重启终端即可永久生效。
验证激活是否成功
1 检查环境变量
echo $AMENT_PREFIX_PATH
✅ 正常输出应包含:
/home/你的用户名/huizhoucar_ws/install:/opt/ros/humble
2. 检查能否找到你的包
ros2 pkg list | grep pkg_interfaces
✅ 如果看到 pkg_interfaces,说明自定义服务接口已加载。
# 查找USB转串口
lsusb
# 查找设备文件名(通过插拔操作识别,通常是 /dev/ttyUSB0)
ls /dev/tty*
# 识别用户组
ls -a -g /dev/ttyUSB*
# 当前用户加入用户组
sudo usermod -a -G dialout $USER
#查看当前用户是否在设备组
groups $USER
应用代码及测试报文:
# pymodbus 3.6.9
from pymodbus.client import ModbusSerialClient as ModbusClient
from pymodbus.transaction import ModbusRtuFramer
import time
def run_rs485_master():
# 创建Modbus RTU客户端
client = ModbusClient(
method='rtu',
port='/dev/ttyUSB0',
baudrate=9600,
timeout=1, # 增加超时时间
parity='N',
stopbits=1,
bytesize=8,
retries=3, # 自动重试次数
strict=True
)
# 连接设备
connection = client.connect()
print(f"Connected: {connection}")
try:
while True:
# 读取保持寄存器 (功能码03)
result = client.read_holding_registers(
address=0,
count=10,
slave=1 # 从站地址
)
if not result.isError():
print(f"保持寄存器值: {result.registers}")
else:
print(f"读取错误: {result}")
# 写入单个寄存器 (功能码06)
write_result = client.write_register(
address=0,
value=1234,
slave=1
)
if not write_result.isError():
print("写入寄存器成功")
time.sleep(1)
except KeyboardInterrupt:
print("用户中断")
finally:
client.close()
if __name__ == "__main__":
run_rs485_master()
总结
提示:这里对文章进行总结: 例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。
参考文献:
ROS2 编程-一般过程 -学习笔记 ubuntu24 + ros2 rolling+modbus-rtu通信 – 辛河 – 博客园
三木地带你手搓ROS应用之传统PLC系统集成



