前面的几篇文章我们简单介绍了ROS的一些基本操作和数据获取的实现。下面,我们将更深入地研究ROS的数据传递,因为这关系到能不能真正把ROS用于实际的工程项目中。
如果工程只用ROS搭建,那么是不需要用到命题所说的LCM的。但如果ROS只是整体系统的一部分,则需要利用内存共享或者网络传输等方式与其他程序进行数据交互,这时候LCM就是一种比较简单的选择(如果不清楚什么是LCM,可以参考这篇文章)。
下面,我们将逐步介绍如何在ROS系统中使用LCM。
联系前面,我们已经知道ROS归根结底是一个多进程的管理工具,因此我们可以把上面的工作分成两步。
第一步:在Ubuntu下把LCM搭建起来,可以直接发送和接收数据。
第二步:把上面做完的用LCM发送和接收的代码移植到ROS中。
下面我们来做第一步(这里只用C++实现,其他版本类似):
先安装编译LCM会用到的依赖项:
sudo apt-get install build-essential autoconf automake autopoint libglib2.0-dev libtool openjdk-8-jdk python-dev
到github上下载源码包,这里本人下的是”lcm-1.4.0.zip”
下完后直接解压缩,然后cd进去解压后的文件夹:
mkdir build
cd build
cmake ..
make
等待编译完成(意外的快),然后安装:
sudo make install
完成这一步就算搞定了,但这个时候系统不知道lib的库在哪里,因此需要告诉它。
为lcm创建一个ld.so.conf文件:
export LCM_INSTALL_DIR=/usr/local/lib
sudo sh -c "echo $LCM_INSTALL_DIR > /etc/ld.so.conf.d/lcm.conf"
更新
sudo ldconfig
配置pkgconfig来查找lcm.pc
export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$LCM_INSTALL_DIR/pkgconfig
做完上面的步骤,则LCM就全部安装配置完成了。接着我们可以写一个客户端和服务器测试下。
首先写服务器,LCM_Client.cpp:
#include <iostream>
#include "lcm/lcm-cpp.hpp"
int main()
{
lcm::LCM lcm;
if (!lcm.good())
{
return 1;
}
char data[5];
data[0] = 1;
data[1] = 5;
data[2] = 1;
data[3] = 2;
data[4] = 1;
lcm.publish("EXAMPLE", data,5);//第一个参数是通道名,第二个参数是数据指针,第三个参数是长度
std::cout << "发送成功!";
return 0;
}
然后编译+链接:
g++ `pkg-config --cflags lcm` -I. -o Client.o -c LCM_Client.cpp
g++ -o Client Client.o `pkg-config --libs lcm`
其次写客户端,LCM_Server.cpp:
#include <iostream>
#include "lcm/lcm-cpp.hpp"
class MyMessageHandler
{
public:
void onMessage(const lcm::ReceiveBuffer* rbuf, const std::string& channel)
{
std::cout << (int)((unsigned char*)rbuf->data)[0] << std::endl;
std::cout << (int)((unsigned char*)rbuf->data)[1] << std::endl;
std::cout << (int)((unsigned char*)rbuf->data)[2] << std::endl;
std::cout << (int)((unsigned char*)rbuf->data)[3] << std::endl;
std::cout << (int)((unsigned char*)rbuf->data)[4] << std::endl;
std::cout << "接收成功!";
}
};
int main() {
lcm::LCM lcm;
MyMessageHandler handler;
lcm.subscribe("EXAMPLE", &MyMessageHandler::onMessage, &handler);
while (true)
lcm.handle();
return 0;
}
同样编译+链接:
g++ `pkg-config --cflags lcm` -I. -o Server.o -c LCM_Server.cpp
g++ -o Server Server.o `pkg-config --libs lcm`
这样我们就得到了LCM的服务器和客户端,运行效果如下:
接下来我们看看怎么把LCM放到ROS里面跑。
在我们前面文章创建的rosOpenCV包下新建两个文件,分别为“LCM_Client.cpp”、“LCM_Server.cpp”,内容如下:
LCM_Client.cpp
#include <ros/ros.h>
#include <iostream>
#include "lcm/lcm-cpp.hpp"
int main(int argc, char** argv)
{
ros::init(argc, argv, "image_publisher");
ros::NodeHandle nh;
lcm::LCM lcm;
if (!lcm.good())
{
return 1;
}
char data[5];
data[0] = 1;
data[1] = 5;
data[2] = 1;
data[3] = 2;
data[4] = 1;
lcm.publish("EXAMPLE", data,5);//第一个参数是通道名,第二个参数是数据指针,第三个参数是长度
std::cout << "发送成功!";
ros::spinOnce();
}
LCM_Server.cpp
#include <ros/ros.h>
#include <iostream>
#include "lcm/lcm-cpp.hpp"
class MyMessageHandler
{
public:
void onMessage(const lcm::ReceiveBuffer* rbuf, const std::string& channel)
{
std::cout << (int)((unsigned char*)rbuf->data)[0] << std::endl;
std::cout << (int)((unsigned char*)rbuf->data)[1] << std::endl;
std::cout << (int)((unsigned char*)rbuf->data)[2] << std::endl;
std::cout << (int)((unsigned char*)rbuf->data)[3] << std::endl;
std::cout << (int)((unsigned char*)rbuf->data)[4] << std::endl;
std::cout << "接收成功!";
}
};
int main(int argc, char** argv)
{
ros::init(argc, argv, "image_publisher");
ros::NodeHandle nh;
lcm::LCM lcm;
MyMessageHandler handler;
lcm.subscribe("EXAMPLE", &MyMessageHandler::onMessage, &handler);
while (true)
lcm.handle();
ros::spin();
}
接着修改“CmakeLists.txt”,主要内容如下:
include_directories(${catkin_INCLUDE_DIRS})
add_executable(LCM_Client /home/weixin/桌面/lcmdemo/src/printHelloRosPK/src/LCM_Client.cpp)
target_link_libraries(LCM_Client lcm)
target_link_libraries(LCM_Client ${catkin_LIBRARIES})
add_executable(LCM_Server /home/weixin/HelloRos/src/rosOpenCV/src/LCM_Server.cpp)
target_link_libraries(LCM_Server lcm)
target_link_libraries(LCM_Server ${catkin_LIBRARIES})