ROS 进阶学习笔记(12) - Communication with ROS through USART Serial Port
Communication with ROS through USART Serial Port
We always need to communicate with ROS through serial port as we have many devices like sensors, touch-screen, actuators to be controlled through USART serial protocol.
After some investigation, I found several ways that can make ROS work with the serial-port-devices:
- Use the package: rosserial. It seems like only the "connected rosserial-enabled device" can work upon that, including Aduino, Windows, XBee, etc.(ROS
community said: rosserial is used with mcus that have rosserial code running on them.) - Use the driver package: serial. It seems like a serial port driver for ROS programming. This should be the most free way but I didn't find out how to use it.
- Use the package: cereal_port, a serial port library for ROS, you can find it in theserial_communication stack, it's easy to use.
- The package:rosbridgemay also suit your needs (full disclosure, author of rosbridge here). Check outthis
video of using rosbridge with an Arduino (though the principles apply to any uController). - A simple approach is to write a ROS node that can read/write text messages exchanged with the microcontroller over a serial port and convert these serial messages to ROS style std_msgs/String messages.Here we are going to share this way first.
- Kevin: a service given by Kevin. It seems to work pretty good. This works well cuz a lot of our serial stuff is sending a message and receiving a response back, instead of separate publish/subscribes.reference
web
A simple approach is to write a ROS node that can read/write text messages exchanged with the microcontroller over a serial port and convert these serial messages to ROS style std_msgs/String messages. Here we are going to share this way first. ref:reference
web
In that page, Bart says:
"A simple approach is to write a ROS node that can read/write text messages exchanged with the microcontroller over a serial port and convert these serial messages to ROS style std_msgs/String messages. A separate ROS node can then be written to convert
the simple text messages to specific ROS message formats (cmd_vel, odom, etc) based on whatever message protocol was defined for the exchange with the microcontroller.
Attached below is the source for code that I used for this purpose. There is nothing original in the code, but it may be helpful to someone new to ROS and Unix/Linux serial communication. Posting this type of a listing may be a bit unconventional
for this site as short answers are generally preferred. Hopefully no one is offended. I would be interested in hearing if there are other successful approaches to the problem."
Let's implement this approach:
Enter the catkin workspace, build the package: (tutorial page)
$ cd ~/catkin_ws/src
$ catkin_create_pkg r2serial_driver std_msgs rospy roscpp
$ cd ~/catkin_ws
$ catkin_make
To add the workspace to your ROS environment you need to source the generated setup file:
$ . ~/catkin_ws/devel/setup.bash
$ cd ~/catkin_ws/src/r2serial_driver/src
Add a file: "r2serial.cpp", Source code:
/*
* Copyright (c) 2010, Willow Garage, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the Willow Garage, Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/ //r2serial.cpp // communicate via RS232 serial with a remote uController.
// communicate with ROS using String type messages.
// subscribe to command messages from ROS
// publish command responses to ROS // program parameters - ucontroller# (0,1), serial port, baud rate //Thread main
// Subscribe to ROS String messages and send as commands to uController
//Thread receive
// Wait for responses from uController and publish as a ROS messages #include "ros/ros.h"
#include "std_msgs/String.h"
#include <sstream>
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#include <stdlib.h> #define DEFAULT_BAUDRATE 19200
#define DEFAULT_SERIALPORT "/dev/ttyUSB0" //Global data
FILE *fpSerial = NULL; //serial port file pointer
ros::Publisher ucResponseMsg;
ros::Subscriber ucCommandMsg;
int ucIndex; //ucontroller index number //Initialize serial port, return file descriptor
FILE *serialInit(char * port, int baud)
{
int BAUD = 0;
int fd = -1;
struct termios newtio;
FILE *fp = NULL; //Open the serial port as a file descriptor for low level configuration
// read/write, not controlling terminal for process,
fd = open(port, O_RDWR | O_NOCTTY | O_NDELAY );
if ( fd<0 )
{
ROS_ERROR("serialInit: Could not open serial device %s",port);
return fp;
} // set up new settings
memset(&newtio, 0,sizeof(newtio));
newtio.c_cflag = CS8 | CLOCAL | CREAD; //no parity, 1 stop bit newtio.c_iflag = IGNCR; //ignore CR, other options off
newtio.c_iflag |= IGNBRK; //ignore break condition newtio.c_oflag = 0; //all options off newtio.c_lflag = ICANON; //process input as lines // activate new settings
tcflush(fd, TCIFLUSH);
//Look up appropriate baud rate constant
switch (baud)
{
case 38400:
default:
BAUD = B38400;
break;
case 19200:
BAUD = B19200;
break;
case 9600:
BAUD = B9600;
break;
case 4800:
BAUD = B4800;
break;
case 2400:
BAUD = B2400;
break;
case 1800:
BAUD = B1800;
break;
case 1200:
BAUD = B1200;
break;
} //end of switch baud_rate
if (cfsetispeed(&newtio, BAUD) < 0 || cfsetospeed(&newtio, BAUD) < 0)
{
ROS_ERROR("serialInit: Failed to set serial baud rate: %d", baud);
close(fd);
return NULL;
}
tcsetattr(fd, TCSANOW, &newtio);
tcflush(fd, TCIOFLUSH); //Open file as a standard I/O stream
fp = fdopen(fd, "r+");
if (!fp) {
ROS_ERROR("serialInit: Failed to open serial stream %s", port);
fp = NULL;
}
return fp;
} //serialInit //Process ROS command message, send to uController
void ucCommandCallback(const std_msgs::String::ConstPtr& msg)
{
ROS_DEBUG("uc%dCommand: %s", ucIndex, msg->data.c_str());
fprintf(fpSerial, "%s", msg->data.c_str()); //appends newline
} //ucCommandCallback //Receive command responses from robot uController
//and publish as a ROS message
void *rcvThread(void *arg)
{
int rcvBufSize = 200;
char ucResponse[rcvBufSize]; //response string from uController
char *bufPos;
std_msgs::String msg;
std::stringstream ss; ROS_INFO("rcvThread: receive thread running"); while (ros::ok()) {
bufPos = fgets(ucResponse, rcvBufSize, fpSerial);
if (bufPos != NULL) {
ROS_DEBUG("uc%dResponse: %s", ucIndex, ucResponse);
msg.data = ucResponse;
ucResponseMsg.publish(msg);
}
}
return NULL;
} //rcvThread int main(int argc, char **argv)
{
char port[20]; //port name
int baud; //baud rate char topicSubscribe[20];
char topicPublish[20]; pthread_t rcvThrID; //receive thread ID
int err; //Initialize ROS
ros::init(argc, argv, "r2serial_driver");
ros::NodeHandle rosNode;
ROS_INFO("r2serial_driver starting"); //Open and initialize the serial port to the uController
if (argc > 1) {
if(sscanf(argv[1],"%d", &ucIndex)==1) {
sprintf(topicSubscribe, "uc%dCommand",ucIndex);
sprintf(topicPublish, "uc%dResponse",ucIndex);
}
else {
ROS_ERROR("ucontroller index parameter invalid");
return 1;
}
}
else {
strcpy(topicSubscribe, "uc0Command");
strcpy(topicPublish, "uc0Response");
} strcpy(port, DEFAULT_SERIALPORT);
if (argc > 2)
strcpy(port, argv[2]); baud = DEFAULT_BAUDRATE;
if (argc > 3) {
if(sscanf(argv[3],"%d", &baud)!=1) {
ROS_ERROR("ucontroller baud rate parameter invalid");
return 1;
}
} ROS_INFO("connection initializing (%s) at %d baud", port, baud);
fpSerial = serialInit(port, baud);
if (!fpSerial )
{
ROS_ERROR("unable to create a new serial port");
return 1;
}
ROS_INFO("serial connection successful"); //Subscribe to ROS messages
ucCommandMsg = rosNode.subscribe(topicSubscribe, 100, ucCommandCallback); //Setup to publish ROS messages
ucResponseMsg = rosNode.advertise<std_msgs::String>(topicPublish, 100); //Create receive thread
err = pthread_create(&rcvThrID, NULL, rcvThread, NULL);
if (err != 0) {
ROS_ERROR("unable to create receive thread");
return 1;
} //Process ROS messages and send serial commands to uController
ros::spin(); fclose(fpSerial);
ROS_INFO("r2Serial stopping");
return 0;
}
Modify the CMakeList.txt file:
$ cd ~/catkin_ws/src/r2serial_driver
$ gedit CMakeList.txt
add/modify these lines as below:
find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
) include_directories(
${catkin_INCLUDE_DIRS}
) add_executable(r2serial_driver src/r2serial.cpp) add_dependencies(r2serial_driver ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS}) target_link_libraries(r2serial_driver
${catkin_LIBRARIES}
)
Save and Build the package again:
$ cd ~/catkin_ws
$ catkin_make -DCMAKE_BUILD_TYPE=Release
He also offered a simple launch file that interfaces to two microcontrollers using two serial connections, put the code into
"~/catkin_ws/src/r2serial_driver/launch/r2serial_driver.launch" file:
<launch>
<node pkg="r2serial_driver" type="r2serial_driver" name="r2serial0" args="0 /dev/ttyUSB0 9600" output="screen" >
<remap from="ucCommand" to="uc0Command" />
<remap from="ucResponse" to="uc0Response" />
</node> <node pkg="r2serial_driver" type="r2serial_driver" name="r2serial1" args="1 /dev/ttyUSB1 9600" output="screen" >
<remap from="ucCommand" to="uc1Command" />
<remap from="ucResponse" to="uc1Response" />
</node> </launch>
Usage:
$ rosrun r2serial_driver r2serial_driver 0 /dev/ttyUSB0 9600
or
$ roslaunch r2serial_driver r2serial_driver.launch
try:
$ rostopic pub -r 1 /uc0Command std_msgs/String hello_my_serial
$ rostopic echo /uc0Response
You may also need to remap the /dev/ttyUSB* to some name you like cuz you may have several usb-serial devices.
If so, just Ref:重新指派usb转串口模块在linux系统中的设备调用名称(English Version: remap your usb-serial devices' names in Linux
)
But Kevin mentioned a question: "Interesting, but a lot of my serial stuff is send a message and receive a response back. Instead of separate publish/subscribes have you tried a service? That seems like it would work nicely for this."
I also noticed a problem: The Linux's System RAM and CPU will be much costed by r2serial_driver when it is running.
Next time, we'll see how to use Kevin's service to play ROS serial communication.
2. Use Service to play ROS-Serial communication
see this blog brother below...
http://blog.csdn.net/sonictl/article/details/51372534
ROS 进阶学习笔记(12) - Communication with ROS through USART Serial Port的更多相关文章
- ROS进阶学习笔记(11)- Turtlebot Navigation and SLAM - ROSMapModify - ROS地图修改
ROS进阶学习笔记(11)- Turtlebot Navigation and SLAM - 2 - MapModify地图修改 We can use gmapping model to genera ...
- ROS 进阶学习笔记(13) - Combine Subscriber and Publisher in Python, ROS
Combine Subscriber and Publisher in Python, ROS This article will describe an example of Combining S ...
- ROS进阶学习笔记(11)- Turtlebot Navigation and SLAM
(写在前面: 这里参考rbx书中第八章和ROS社区教程进行学习,先看社区教程) === Doing the Turtlebot Navigation === ref ros wiki: http ...
- ROS进阶学习笔记(10)- 搭建自己的Turtlebot(5) - Interactive Makers
用interactive_makers控制Turtlebot移动 interactive_makers 是Willow Garage公司开发的一个虚拟控制工具,可通过鼠标在虚拟环境中的操作,完成实际机 ...
- Ext.Net学习笔记12:Ext.Net GridPanel Filter用法
Ext.Net学习笔记12:Ext.Net GridPanel Filter用法 Ext.Net GridPanel的用法在上一篇中已经介绍过,这篇笔记讲介绍Filter的用法. Filter是用来过 ...
- SQL反模式学习笔记12 存储图片或其他多媒体大文件
目标:存储图片或其他多媒体大文件 反模式:图片存储在数据库外的文件系统中,数据库表中存储文件的对应的路径和名称. 缺点: 1.文件不支持Delete操作.使用SQL语句删除一条记录时,对应的文 ...
- golang学习笔记12 beego table name `xxx` repeat register, must be unique 错误问题
golang学习笔记12 beego table name `xxx` repeat register, must be unique 错误问题 今天测试了重新建一个项目生成新的表,然后复制到旧的项目 ...
- Spring MVC 学习笔记12 —— SpringMVC+Hibernate开发(1)依赖包搭建
Spring MVC 学习笔记12 -- SpringMVC+Hibernate开发(1)依赖包搭建 用Hibernate帮助建立SpringMVC与数据库之间的联系,通过配置DAO层,Service ...
- Python3+Selenium3+webdriver学习笔记12(js操作应用:滚动条 日历 内嵌div)
#!/usr/bin/env python# -*- coding:utf-8 -*-'''Selenium3+webdriver学习笔记12(js操作应用:滚动条 日历 内嵌div)'''from ...
随机推荐
- 前端之js-本地存储-localStorage && IndexedDB
1.LocalStorage示例 var Config = function ( name ) { //storage为空时,初始化的信息 var storage = { 'name': 'test' ...
- 在VMware中安装Mac OS
macOS与Darwin http://blog.csdn.net/hintcnuie/article/details/38468093 OS X 是整个操作系统的一个集体名称.而Darwin 就是其 ...
- Git忽略规则和.gitignore规则不生效的解决办法
Git忽略规则和.gitignore规则不生效的解决办法 Git忽略规则: 在git中如果想忽略掉某个文件,不让这个文件提交到版本库中,可以使用修改根目录中 .gitignore 文件的方法(如果 ...
- spring 整合Junit学习
测试一般是测试的局部功能,使用时需要自己写个测试相关的spring配置文件,比较费劲,所以常用的是纯配置的方式来实现测试. @RunWith(SpringJUnit4ClassRunner.class ...
- 【java】之java代码的执行机制
要在JVM中执行java代码必须要编译为class文件,JDK是如何将Java代码编译为class文件,这种机制通常被称为Java源码编译机制. 1.JVM定义了class文件的格式,但是并没有定义如 ...
- LeetCode——14. Longest Common Prefix
一.题目链接:https://leetcode.com/problems/longest-common-prefix/ 二.题目大意: 给定若干个字符串,找出它们的最长公共子串. 三.题解: 这道题目 ...
- csv文件操作
1.python2中: import csv infos = [ ['peter','male'], ['marry','female'], ['johon','male'], ['rose','fe ...
- Docker启动一个Centos镜像
docker镜像的获取与使用 docker中使用centos7镜像 接着上文,我们下载完成一个Centos镜像之后,开始启动 #运行命令 docker run -d -i -t <imageID ...
- tpadmin的坑收集 nginx下配置tp5失败
如下: 1.ADMIN模块如要关联查询,model的函数名一定不要有“_”下划线,否则找不到 /common/model/**.php 如果把函数名file写成“**_file”,调用时,$vo.** ...
- Java常用的线程池
Java通过Executors提供四种线程池,分别为:newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程.newFixe ...