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的更多相关文章

  1. ROS进阶学习笔记(11)- Turtlebot Navigation and SLAM - ROSMapModify - ROS地图修改

    ROS进阶学习笔记(11)- Turtlebot Navigation and SLAM - 2 - MapModify地图修改 We can use gmapping model to genera ...

  2. 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 ...

  3. ROS进阶学习笔记(11)- Turtlebot Navigation and SLAM

    (写在前面: 这里参考rbx书中第八章和ROS社区教程进行学习,先看社区教程) ===  Doing the Turtlebot Navigation   === ref ros wiki: http ...

  4. ROS进阶学习笔记(10)- 搭建自己的Turtlebot(5) - Interactive Makers

    用interactive_makers控制Turtlebot移动 interactive_makers 是Willow Garage公司开发的一个虚拟控制工具,可通过鼠标在虚拟环境中的操作,完成实际机 ...

  5. Ext.Net学习笔记12:Ext.Net GridPanel Filter用法

    Ext.Net学习笔记12:Ext.Net GridPanel Filter用法 Ext.Net GridPanel的用法在上一篇中已经介绍过,这篇笔记讲介绍Filter的用法. Filter是用来过 ...

  6. SQL反模式学习笔记12 存储图片或其他多媒体大文件

    目标:存储图片或其他多媒体大文件 反模式:图片存储在数据库外的文件系统中,数据库表中存储文件的对应的路径和名称. 缺点:     1.文件不支持Delete操作.使用SQL语句删除一条记录时,对应的文 ...

  7. golang学习笔记12 beego table name `xxx` repeat register, must be unique 错误问题

    golang学习笔记12 beego table name `xxx` repeat register, must be unique 错误问题 今天测试了重新建一个项目生成新的表,然后复制到旧的项目 ...

  8. Spring MVC 学习笔记12 —— SpringMVC+Hibernate开发(1)依赖包搭建

    Spring MVC 学习笔记12 -- SpringMVC+Hibernate开发(1)依赖包搭建 用Hibernate帮助建立SpringMVC与数据库之间的联系,通过配置DAO层,Service ...

  9. Python3+Selenium3+webdriver学习笔记12(js操作应用:滚动条 日历 内嵌div)

    #!/usr/bin/env python# -*- coding:utf-8 -*-'''Selenium3+webdriver学习笔记12(js操作应用:滚动条 日历 内嵌div)'''from ...

随机推荐

  1. SDRAM---页读写

    SDRAM---页读写 1.SDRAM页访问 一页通俗的来讲就是一行. SDRAM页写操作时序图: 2.DDR(经常被提起,但是我和你不熟) DDR的连续访问操作 给DDR一个write命令,同时给出 ...

  2. GTP+SDI工程播出部分思路整理

    GTP+SDI工程播出部分思路整理 1.video_out_to_sdi模块 关于video_out_to_sdi模块的输出信号: tx_video_a_y[9:0] 这是要输入SDI IP核内的 t ...

  3. Winfrom窗体无法关闭问题--检查是否存在重写

    问题描述: Winfrom窗体无法关闭问题----点击关闭/最大/最小化无法正常相应. 问题来源: 老版本的程序要求使用无边框的Form窗体(实现功能——设置为无边框窗体并重写窗体的关闭.最大.最小化 ...

  4. python selenium right click on an href and choose Save link as... on Chrome.

    From:https://stackoverflow.com/questions/42781483/right-click-on-an-href-and-choose-save-link-as-in- ...

  5. 关于mybatis map foreach遍历

    map 数据如下 Map<String,List<Long>>. 测试代码如下: public void getByMap(){ Map<String,List<L ...

  6. Spring Boot 容器选择 Undertow 而不是 Tomcat

    Spring Boot 内嵌容器Undertow参数设置 配置项: # 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个线程 # 不要设置过大,如果过大,启动 ...

  7. mysql查询优化之一:mysql查询优化常用方式

    一.为什么查询速度会慢? 一个查询的生命周期大致可以按照顺序来看:从客户端,到服务器,然后在服务器上进行解析,生成执行计划,执行,并返回结果给客户端.其中在“执行”阶段包含了大量为了检索数据到存储引擎 ...

  8. 指定分隔符连接数组元素join()

    join()方法用于把数组中的所有元素放入一个字符串.元素是通过指定的分隔符进行分隔的. 语法: arrayObject.join(分隔符) 参数说明: 注意:返回一个字符串,该字符串把数组中的各个元 ...

  9. 【ZZ】谈谈持续集成,持续交付,持续部署之间的区别

    谈谈持续集成,持续交付,持续部署之间的区别 http://blog.flow.ci/cicd_difference/ 谈谈持续集成,持续交付,持续部署之间的区别 2016年08月03日 标签:beta ...

  10. ping一个网段的cmd程序

    ping一个网段的cmd程序 今天发现只在cmd命令行工具中输入: FOR /L %i IN (1,1,254) DO ping -n 1 192.168.1.%i 即可.