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 ...
随机推荐
- Video Processing subsystem例程分析
Video Processing subsystem例程分析 1.memory_ss模块 slave端口: S00: 连接设备: microblaze_ss----M_AXI_DC 时钟来源: S01 ...
- 关于bool 与boolean
关于bool的介绍,整理如下: bool 关键字是 System.Boolean 的别名.它用于声明变量来存储布尔值 true 和 false. 如果需要一个也可以有 null 值的布尔型变量,请使用 ...
- Excel技巧--分类求和与空白批量填充
分类求和: 当我们要对以上多个分类空白求和时,可以巧用Alt+=键: 1.选择对应要求和的列: 2.点击“查找与选择”下拉列,选择“定位条件”,对话框选择“空值”,点确定.将列中的空白单元格选好: 3 ...
- elixir环境配置
mac下 brew install elixir debian下版本可能太低,需要kiex 安装 curl -sSL https://raw.githubusercontent.com/taylor/ ...
- 引用 自动化测试基础篇--Selenium简介
原文链接:http://www.cnblogs.com/sanzangTst/p/7452636.html 鸣谢参藏法师 一.软件开发的一般流程 二.什么叫软件测试? 软件测试(英语:Software ...
- 峰Redis学习(9)Redis 集群(概述)
第一节:Redis 集群概述 redis cluster是去中心化,去中间件的,也就是说,集群中的每个节点都是平等的关系,都是对等的,每个节点都保存各自的数据和整个集群的状态.每个节点都和其他所有节点 ...
- [C#][WebAPI]返回 json
//将webapi的返回值设为Json格式 var jsonFormatter = new JsonMediaTypeFormatter(); GlobalConfiguration.Configur ...
- idea关闭标签快捷键修改----兼 常用实用快捷键
还有一个快捷键: 自动补全返回值 : ctrl + alt + v alt + enter: 自动添加未定义的方法 idea 原本的关闭快捷键是: ctrl + F4,但是不好用,谁的手指伸这么长 修 ...
- IE浏览器强制不是要兼容视图
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta ht ...
- [UE4]AIPerception,AI感知