Rust ROS2通信中间件库r2r的使用,实现高效机器人操作系统2(ROS2)的节点通信与消息处理
Rust ROS2通信中间件库r2r的使用,实现高效机器人操作系统2(ROS2)的节点通信与消息处理
R2R是一个易于使用、运行时无关的异步Rust绑定库,用于ROS2。它不需要接入ROS2构建基础设施,只需使用cargo build
即可完成构建。通过调用C自省库生成方便的Rust类型,绕过了ROS2的.msg/.idl管道,依赖于已生成的C代码。
使用方法
- 确保已安装libclang(例如Ubuntu上的libclang-dev)
- 在Cargo.toml中添加依赖:
r2r = "0.9.5"
- 在构建/运行前需要source您的ROS2安装环境
- 绑定会在您source工作空间后自动重建
- 如果修改了现有消息类型,运行
cargo clean -p r2r_msg_gen
强制在下一次构建时重新编译Rust消息类型
示例使用:
. /opt/ros/humble/setup.sh
cargo build
cargo run --example subscriber
# 在另一个终端
ros2 topic pub /topic std_msgs/msg/String "data: 'Hello, world'"
构建时间说明
由于默认行为是构建所有已source的消息类型,对于较大的工作空间,构建时间可能会迅速增加。为避免构建所有内容,可以使用环境变量IDL_PACKAGE_FILTER
声明仅需要的消息包。
当前功能
- 支持最新的ROS2版本(Foxy、Galactic、Humble、Iron、Jazzy)
- 构建Rust类型
- 发布/订阅
- 服务
- 动作
- 参数处理
- 模拟时间(构建时确保已source
rosgraph_msgs
以启用) - 可在Linux、OSX和Windows上运行
完整示例代码
下面是一个完整的Rust ROS2节点通信示例,包含发布者和订阅者:
use r2r;
use std::sync::{Arc, Mutex};
use tokio::task;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// 初始化ROS2上下文
let ctx = r2r::Context::create()?;
let mut node = r2r::Node::create(ctx, "rust_node", "")?;
// 创建发布者
let publisher = node.create_publisher::<r2r::std_msgs::msg::String>("/topic")?;
// 创建订阅者
let subscriber = node.subscribe::<r2r::std_msgs::msg::String>("/topic")?;
// 使用Arc和Mutex共享数据
let counter = Arc::new(Mutex::new(0));
// 启动发布任务
let pub_counter = counter.clone();
task::spawn(async move {
let mut msg = r2r::std_msgs::msg::String::default();
loop {
{
let mut count = pub_counter.lock().unwrap();
*count += 1;
msg.data = format!("Hello from Rust! Count: {}", *count);
}
publisher.publish(&msg).unwrap();
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
}
});
// 处理订阅消息
while let Ok(msg) = subscriber.recv().await {
println!("Received: {}", msg.data);
}
Ok(())
}
服务示例
use r2r;
use tokio::task;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let ctx = r2r::Context::create()?;
let mut node = r2r::Node::create(ctx, "rust_service_node", "")?;
// 创建服务客户端
let client = node.create_client::<r2r::example_interfaces::srv::AddTwoInts>("/add_two_ints")?;
// 创建服务服务器
let server = node.create_service::<r2r::example_interfaces::srv::AddTwoInts, _>(
"/add_two_ints",
move |req| {
println!("Received request: {} + {}", req.a, req.b);
let sum = req.a + req.b;
Ok(r2r::example_interfaces::srv::AddTwoIntsResponse { sum })
},
)?;
// 发送服务请求
task::spawn(async move {
let mut req = r2r::example_interfaces::srv::AddTwoIntsRequest::default();
req.a = 2;
req.b = 3;
match client.request(&req).await {
Ok(response) => println!("Got response: {}", response.sum),
Err(e) => println!("Error: {:?}", e),
}
});
// 保持节点运行
loop {
node.spin_once(std::time::Duration::from_millis(100)).await;
}
}
动作(Action)服务完整示例
下面是一个实现ROS2动作服务的完整示例:
use r2r;
use tokio::task;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let ctx = r2r::Context::create()?;
let mut node = r2r::Node::create(ctx, "rust_action_node", "")?;
// 创建动作服务器
let action_server = node.create_action_server::<
r2r::example_interfaces::action::Fibonacci,
>(
"/fibonacci",
|goal_handle| async move {
println!("Received goal request with order: {}", goal_handle.goal.order);
let mut feedback = r2r::example_interfaces::action::FibonacciFeedback::default();
let mut result = r2r::example_interfaces::action::FibonacciResult::default();
let mut sequence = vec![0, 1];
for i in 1..goal_handle.goal.order as usize {
let next = sequence[i-1] + sequence[i];
sequence.push(next);
// 发送反馈
feedback.sequence = sequence.clone();
goal_handle.publish_feedback(&feedback).unwrap();
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
}
result.sequence = sequence;
goal_handle.succeed(result).unwrap();
Ok(())
},
)?;
// 创建动作客户端
let action_client = node.create_action_client::<
r2r::example_interfaces::action::Fibonacci,
>("/fibonacci")?;
// 发送动作请求
task::spawn(async move {
let mut goal = r2r::example_interfaces::action::FibonacciGoal::default();
goal.order = 5;
match action_client.send_goal(&goal).await {
Ok((goal_handle, result_future)) => {
// 处理反馈
let feedback_stream = goal_handle.get_feedback_stream();
tokio::spawn(async move {
while let Ok(feedback) = feedback_stream.recv().await {
println!("Received feedback: {:?}", feedback.sequence);
}
});
// 等待结果
match result_future.await {
Ok(result) => println!("Final result: {:?}", result.sequence),
Err(e) => println!("Error: {:?}", e),
}
}
Err(e) => println!("Failed to send goal: {:?}", e),
}
});
// 保持节点运行
loop {
node.spin_once(std::time::Duration::from_millis(100)).await;
}
}
参数处理完整示例
use r2r;
use tokio::task;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let ctx = r2r::Context::create()?;
let mut node = r2r::Node::create(ctx, "rust_param_node", "")?;
// 声明参数
node.declare_parameter("my_int_param", 42)?;
node.declare_parameter("my_string_param", "hello")?;
node.declare_parameter("my_double_param", 3.14)?;
// 设置参数
node.set_parameter("my_int_param", 100)?;
// 获取参数
match node.get_parameter::<i32>("my_int_param") {
Ok(value) => println!("Got int parameter: {}", value),
Err(e) => println!("Failed to get parameter: {:?}", e),
}
// 列出所有参数
for param in node.list_parameters()? {
println!("Parameter: {}", param);
}
// 保持节点运行
loop {
node.spin_once(std::time::Duration::from_millis(100)).await;
}
}
许可证
除非在源文件中另有说明,所有代码均采用MIT许可证。包含的来自rclrust包的部分代码采用Apache 2.0许可证。
1 回复
Rust ROS2通信中间件库r2r使用指南
简介
r2r是一个纯Rust实现的ROS2客户端库,允许开发者用Rust语言创建ROS2节点,实现与ROS2系统的通信。它提供了发布/订阅模式、服务调用、参数管理等核心ROS2功能,同时利用Rust的安全性和高性能特性。
主要特性
- 纯Rust实现,无C++依赖
- 支持ROS2 DDS中间件通信
- 线程安全的设计
- 支持常见ROS2消息类型
- 异步/同步API
安装
在Cargo.toml中添加依赖:
[dependencies]
r2r = "0.7"
完整示例代码
1. 完整的发布-订阅示例
use r2r::{Context, Node, QosProfile};
use std::time::Duration;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// 初始化上下文和节点
let ctx = Context::create()?;
let mut node = Node::create(ctx, "rust_pubsub_node", "")?;
// 创建发布者 - 发布String消息到/rust_chatter话题
let publisher = node.create_publisher::<r2r::std_msgs::msg::String>(
"/rust_chatter",
QosProfile::default()
)?;
// 创建订阅者 - 订阅/rust_chatter话题
let mut subscriber = node.subscribe::<r2r::std_msgs::msg::String>(
"/rust_chatter",
QosProfile::default()
)?;
// 异步处理接收到的消息
tokio::spawn(async move {
while let Ok(msg) = subscriber.recv().await {
println!("[Subscriber] 收到消息: {}", msg.data);
}
});
// 发布10条消息
for i in 0..10 {
let msg = r2r::std_msgs::msg::String {
data: format!("这是来自Rust的第{}条消息", i+1),
};
publisher.publish(&msg)?;
println!("[Publisher] 已发送: {}", msg.data);
tokio::time::sleep(Duration::from_millis(500)).await;
}
Ok(())
}
2. 完整的服务通信示例
use r2r::{Context, Node, QosProfile};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let ctx = Context::create()?;
let mut node = Node::create(ctx, "rust_service_node", "")?;
// 创建加法服务服务器
let mut add_service = node.create_service::<r2r::example_interfaces::srv::AddTwoInts, _>(
"/add_two_ints_rust",
Box::new(|request, _header| {
println!("[Server] 收到请求: {} + {}", request.a, request.b);
let sum = request.a + request.b;
Ok(r2r::example_interfaces::srv::AddTwoIntsResponse { sum })
}),
)?;
// 创建服务客户端
let client = node.create_client::<r2r::example_interfaces::srv::AddTwoInts>(
"/add_two_ints_rust"
)?;
// 等待服务可用
println!("[Client] 等待服务准备就绪...");
while !client.service_ready()? {
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
}
// 发送3个加法请求
for i in 1..=3 {
let req = r2r::example_interfaces::srv::AddTwoIntsRequest {
a: i * 10,
b: i * 5
};
println!("[Client] 发送请求: {} + {}", req.a, req.b);
match client.request(&req)?.await {
Ok(response) => println!("[Client] 收到响应: 和 = {}", response.sum),
Err(e) => println!("[Client] 请求失败: {}", e),
}
tokio::time::sleep(std::time::Duration::from_millis(500)).await;
}
Ok(())
}
3. 带参数管理的完整节点示例
use r2r::{Context, Node, ParameterValue};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let ctx = Context::create()?;
let mut node = Node::create(ctx, "rust_param_node", "")?;
// 声明节点参数
node.declare_parameter("robot_name", "Rusty")?;
node.declare_parameter("speed_limit", 2.5)?;
node.declare_parameter("enable_logging", true)?;
// 获取并打印参数值
if let Some(name) = node.get_parameter("robot_name")? {
println!("机器人名称: {}", name.value);
}
if let Some(speed) = node.get_parameter("speed_limit")? {
println!("速度限制: {} m/s", speed.value);
}
if let Some(logging) = node.get_parameter("enable_logging")? {
println!("日志记录已{}", if logging.value { "启用" } else { "禁用" });
}
// 修改参数值
node.set_parameter("speed_limit", 3.0)?;
println!("已更新速度限制参数");
// 列出所有参数
println!("当前所有参数:");
for param in node.list_parameters()? {
println!("- {}: {:?}", param.name, param.value);
}
Ok(())
}
性能优化建议
- 使用
QosProfile
调整服务质量策略 - 对于高频消息,考虑使用零拷贝技术
- 合理使用异步API避免阻塞
- 批量处理消息减少系统调用
注意事项
- 确保ROS2环境已正确设置(source ROS2环境)
- 目前r2r支持ROS2 Foxy和Humble版本
- 某些高级ROS2功能可能尚未实现
r2r为Rust开发者提供了与ROS2生态系统交互的高效方式,结合了Rust的性能优势与ROS2的机器人开发能力。