ROSBridge - ROS系统与非ROS外部系统的通信的C++客户端实现
文章目录
- 小结
- 回顾
- rosserial
- ROSBridge
- ROSBridge Client的C++实现
- ROSBridge Client的其它实现
- 实现及测试
- 启动服务器端测试环境
- 初始化ROSBridge客户端
- ROSBridge测试/listener的topic
- ROSBridge测试/cmd_vel的topic
- ROSBridge测试/add_two_ints服务
- ROSBridge测试/set_bool服务
- 参考
小结
ROSBridge可以实现ROS系统与非ROS外部系统的通信,文档比较多比较杂,在Windows客户端的实现大多是Java, Python, JS等,这里对C++客户端使用Websockets通信进行了测试,C++ ROSBridge的客户端有轻量级,解耦合的优点。
回顾
rosserial
参考使用rosserial实现ROS与Windows的service服务通信,使用rosserial
也是可以实现ROS系统与非ROS外部系统的通信。
ROSBridge
有关ROSBridge的官方文档可以参见: ROS Wiki - rosbridge_suite
ROSBridge Client的C++实现
用C++实现ROSBridge的客户端主要有两个Github,
1,Github: Sanic/rosbridge2cpp 这个实现不支持Websockets,所以没有进行测试。
2,本文主要参考了Github-C++ Rosbridge client uses Websockets and Rapidjson: ppianpak/rosbridgecpp
Rapidjson主要参考Rapidjson Tutorial。
另外也借鉴了Rosbridge的JS客户端的实现:RobotWebTools/roslibjs/examples/simple.html。
具体使用的ROSBridge的数据协议详情可以参考:Github: RobotWebTools/rosbridge_suite: rosbridge v2.0 Protocol Specification
ROSBridge Client的其它实现
Github: C# rosbridge-csharp-connection
ROSBridgeClient in Java
Github: ROSBridgeTestclient in Python
实现及测试
启动服务器端测试环境
参考ROS的roslibjs基本功能使用测试:
#启动roscore
roscore
#在/listener这个topic中发布一个"Hello, World"消息
rostopic pub /listener std_msgs/String "Hello, World"
#监听/cmd_vel的消息
rostopic echo /cmd_vel
#启动add_two_ints_server服务
rosrun rospy_tutorials add_two_ints_server
#启动rosbridge_server
roslaunch rosbridge_server rosbridge_websocket.launch
初始化ROSBridge客户端
RosbridgeWsClient rbc("192.168.238.129:9090");
这里192.168.238.129是服务器地址,端口9090。
ROSBridge测试/listener的topic
发布/listener的topic并订阅/listener的topic:
rbc.addClient("topic_advertiser");rbc.advertise("topic_advertiser", "/listener", "std_msgs/String");rbc.addClient("topic_subscriber");rbc.subscribe("topic_subscriber", "/listener", subscriberCallback);
subscriberCallback()
如下:
void subscriberCallback(std::shared_ptr<WsClient::Connection> /*connection*/, std::shared_ptr<WsClient::InMessage> in_message)
{std::cout << "subscriberCallback(): Message Received: " << in_message->string() << std::endl;
}
先前服务器已经发布了rostopic pub /listener std_msgs/String "Hello, World"
,客户端会有以下输出:
topic_subscriber: Sending message: {"op":"subscribe", "topic":"/listener"}
subscriberCallback(): Message Received: {"op": "publish", "topic": "/listener", "msg": {"data": "Hello, World"}}
也可以通过ROSBridge C++客户端的代码进行发布:
void publisherThread(RosbridgeWsClient& rbc, const std::future<void>& futureObj)
{rbc.addClient("topic_publisher");rapidjson::Document d;d.SetObject();d.AddMember("data", "Test message from John /listener", d.GetAllocator());while (futureObj.wait_for(std::chrono::milliseconds(1)) == std::future_status::timeout){rbc.publish("/listener", d);std::this_thread::sleep_for(std::chrono::milliseconds(1000));}std::cout << "publisherThread stops()" << std::endl;
}
也就是客户端自己发布,自己接收,ROSBridge C++客户端输出如下:
publish_client: Opened connection
publish_client: Sending message: {"op":"publish", "topic":"/listener", "msg":{"data":"Test message from John /listener"}}
有关发送消息的结构是string data
如下:
john@ubuntu:~$ rostopic list
/client_count
/cmd_vel
/connected_clients
/listener
/rosout
/rosout_agg
john@ubuntu:~$ rostopic info /listener
Type: std_msgs/StringPublishers: * /rostopic_5673_1660196707336 (http://ubuntu:35063/)Subscribers: Nonejohn@ubuntu:~$ rosmsg show std_msgs/String
string datajohn@ubuntu:~$
ROSBridge测试/cmd_vel的topic
/cmd_vel这个消息结构比较常用但是有些复杂,需要使用好Rapidjson。
消息结构如下:
john@ubuntu:~$ rostopic info /cmd_vel
Type: geometry_msgs/TwistPublishers: * /rosbridge_websocket (http://ubuntu:38019/)Subscribers: * /rostopic_5710_1660196719502 (http://ubuntu:37659/)
john@ubuntu:~$
john@ubuntu:~$ rosmsg show geometry_msgs/Twist
geometry_msgs/Vector3 linearfloat64 xfloat64 yfloat64 z
geometry_msgs/Vector3 angularfloat64 xfloat64 yfloat64 z
ROSBridge C++客户端实现代码如下:
void publisherTwist(RosbridgeWsClient& rbc)
{rbc.addClient("topic_publisher_Twist");rapidjson::Document d;d.SetObject();float x = 0.1;float y = 0.2;float z = 0.3;rapidjson::Value data(rapidjson::kObjectType);data.AddMember("x", x, d.GetAllocator());data.AddMember("y", y, d.GetAllocator());data.AddMember("z", z, d.GetAllocator());d.AddMember("linear", data, d.GetAllocator());x = -0.1;y = -0.2;z = -0.3;rapidjson::Value dataAngular(rapidjson::kObjectType);dataAngular.AddMember("x", x, d.GetAllocator());dataAngular.AddMember("y", y, d.GetAllocator());dataAngular.AddMember("z", z, d.GetAllocator());d.AddMember("angular", dataAngular, d.GetAllocator());rbc.publish("/cmd_vel", d);std::cout << "topic_publisher_Twist stops()" << std::endl;
}
ROSBridge C++客户端输出如下:
publish_client: Opened connection
publish_client: Sending message: {"op":"publish", "topic":"/cmd_vel", "msg":{"linear":{"x":0.10000000149011612,"y":0.20000000298023225,"z":0.30000001192092898},"angular":{"x":-0.10000000149011612,"y":-0.20000000298023225,"z":-0.30000001192092898}}}
发布完数据后,ROSBridge服务器端接收:
john@ubuntu:~$ rostopic echo /cmd_vel
WARNING: topic [/cmd_vel] does not appear to be published yet
linear: x: 0.10000000149011612y: 0.20000000298023224z: 0.30000001192092896
angular: x: -0.10000000149011612y: -0.20000000298023224z: -0.30000001192092896
---
ROSBridge测试/add_two_ints服务
先前在服务器端启动了add_two_ints服务
john@ubuntu:~$ rosrun rospy_tutorials add_two_ints_server
传输数据格式如下:
john@ubuntu:~$ rosservice info /add_two_ints
Node: /add_two_ints_server
URI: rosrpc://ubuntu:33809
Type: rospy_tutorials/AddTwoInts
Args: a b
john@ubuntu:~$ john@ubuntu:~$ rossrv show rospy_tutorials/AddTwoInts
int64 a
int64 b
---
int64 sumjohn@ubuntu:~$
ROSBridge C++客户端实现代码如下:
rapidjson::Value dataTwoInts(rapidjson::kObjectType);rapidjson::Document documentAddTwoInt(rapidjson::kObjectType);int a = 5;int b = 6;documentAddTwoInt.AddMember("a", a, documentAddTwoInt.GetAllocator());documentAddTwoInt.AddMember("b", b, documentAddTwoInt.GetAllocator());rbc.callService("/add_two_ints", callServiceCallbackAddtwo, documentAddTwoInt);
callServiceCallbackAddtwo
函数如下:
void callServiceCallbackAddtwo(std::shared_ptr<WsClient::Connection> connection, std::shared_ptr<WsClient::InMessage> in_message)
{std::string messagebuf = in_message->string();std::cout << "callServiceCallbackAddtwo(): Message Received: " << messagebuf << std::endl;rapidjson::Document document;if (document.Parse(messagebuf.c_str()).HasParseError()){std::cerr << "advertiseServiceCallback(): Error in parsing service request message: " << messagebuf << std::endl;return;}std::cout << "The returned sum is: " << std::to_string(document["values"]["sum"].GetInt64()) << std::endl;connection->send_close(1000);
}
ROSBridge C++客户端输出如下:
call_service_client: Opened connection
call_service_client: Sending message: {"op":"call_service", "service":"/add_two_ints", "args":{"a":5,"b":6}}
callServiceCallbackAddtwo(): Message Received: {"op": "service_response", "service": "/add_two_ints", "values": {"sum": 11}, "result": true}
The returned sum is: 11
ROSBridge服务器端输出如下:
john@ubuntu:~$ rosrun rospy_tutorials add_two_ints_server
Returning [5 + 6 = 11]
ROSBridge测试/set_bool服务
SetBool服务的数据结构如下,输入为bool data
,输出为bool success string message
john@ubuntu:~$ rosservice info /set_bool
Node: /rosbridge_websocket
URI: rosrpc://ubuntu:39413
Type: std_srvs/SetBool
Args: data
john@ubuntu:~$ rossrv show std_srvs/SetBool
bool data
---
bool success
string message
ROSBridge C++客户端发布这个服务:
rbc.addClient("service_advertiser");rbc.advertiseService("service_advertiser", "/set_bool", "std_srvs/SetBool", advertiseServiceCallback);
advertiseServiceCallback
函数如下, 有返回就回调。可以看到ROSBridge C++客户端使用document["args"]["data"].GetBool()
来查看ROSBridge服务器返回的SetBool的值。再返回给ROSBridge服务器bool success string message
的内容。
void advertiseServiceCallback(std::shared_ptr<WsClient::Connection> /*connection*/, std::shared_ptr<WsClient::InMessage> in_message)
{// message->string() is destructive, so we have to buffer it firststd::string messagebuf = in_message->string();std::cout << "advertiseServiceCallback(): Message Received: " << messagebuf << std::endl;rapidjson::Document document;if (document.Parse(messagebuf.c_str()).HasParseError()){std::cerr << "advertiseServiceCallback(): Error in parsing service request message: " << messagebuf << std::endl;return;}bool result = document["args"]["data"].GetBool();rapidjson::Document values(rapidjson::kObjectType);rapidjson::Document::AllocatorType& allocator = values.GetAllocator();values.AddMember("success", document["args"]["data"].GetBool(), allocator);values.AddMember("message", "from advertiseServiceCallback", allocator);rbc.serviceResponse(document["service"].GetString(), document["id"].GetString(), true, values);
}
ROSBridge C++客户端调用SetBool
服务:
rapidjson::Document document(rapidjson::kObjectType);document.AddMember("data", true, document.GetAllocator());rbc.callService("/set_bool", callServiceCallback, document);
callServiceCallback
的回调函数如下:
void callServiceCallback(std::shared_ptr<WsClient::Connection> connection, std::shared_ptr<WsClient::InMessage> in_message)
{std::cout << "serviceResponseCallback(): Message Received: " << in_message->string() << std::endl;connection->send_close(1000);
}
ROSBridge C++客户端自发自收的效果如下:
ROSBridge C++客户端调用/set_bool
服务:
service_advertiser: Opened connection
service_advertiser: Sending message: {"op":"advertise_service", "service":"/set_bool", "type":"std_srvs/SetBool"}
call_service_client: Opened connection
call_service_client: Sending message: {"op":"call_service", "service":"/set_bool", "args":{"data":true}}
ROSBridge C++客户端接收到了/set_bool
服务调用:
advertiseServiceCallback(): Message Received: {"op": "call_service", "id": "service_request:/set_bool:1", "service": "/set_bool", "args": {"data": true}}
ROSBridge C++客户端响应/set_bool
调用后,进行应答rbc.serviceResponse(document["service"].GetString(), document["id"].GetString(), true, values);
结果如下:
service_response_client: Opened connection
service_response_client: Sending message: {"op":"service_response", "service":"/set_bool", "result":true, "id":"service_request:/set_bool:1", "values":{"success":true,"message":"from advertiseServiceCallback"}}
ROSBridge C++客户端接收到/set_bool
调用的应答:
serviceResponseCallback(): Message Received: {"op": "service_response", "service": "/set_bool", "values": {"success": true, "message": "from advertiseServiceCallback"}, "result": true}
参考
我的AI之路(25)–ROSBridge:机器人与外部系统之间的通讯解决方案
我的AI之路(27)–用C++实现机器人与机器人后端控制系统之间的双向通讯
我的AI之路(29)–使用Boost Asio和Rapidjson实现机器人与机器人后端系统之间的自定义协议的通讯
Github: RobotWebTools/rosbridge_suite
ROS Wiki - rosbridge_suite
使用rosserial实现ROS与Windows的service服务通信
Package RosBridge——打通Ros与非Ros环境的数据壁垒
Package RosBridge——打通Ros与非Ros环境的数据壁垒 #2 C++端实现功能ppianpak / rosbridgecpp
ROSBridge简介以及理解使用(上)
ROSBridge简介以及理解使用(下)
转:使用rosbridge协议实现安卓跟ros的解耦
古月居:ROSBridge简介以及理解使用
Github: RobotWebTools/rosbridge_suite: rosbridge v2.0 Protocol Specification
ROS Wiki Rosbridge in C++
Github: Sanic/rosbridge2cpp
Request for help regarding ROS-> C++ Application (windows) communication problem for video transmission.
Github: C# rosbridge-csharp-connection
ROSBridgeClient in Java
Android使用ROSBridge与ROS通信 简单使用
Github: ROSBridgeTestclient in Python
ROS Wiki How to send data in Json format using service client in c++ code
Rapidjson Tutorial
Github-C++ Rosbridge client uses Websockets and Rapidjson: ppianpak/rosbridgecpp
RobotWebTools/roslibjs/examples/simple.html
ROSBridge - ROS系统与非ROS外部系统的通信的C++客户端实现相关推荐
- ORB-SLAM2环境配置以及运行详解(一):ROS与非ROS环境下的安装、编译、离线数据集测试
系统环境:Ubuntu 16.04 LTS ROS版本:Kinetic Kame ORB-SLAM2源码的编译部分有ROS版本和非ROS版本,非ROS版本不需要安装ROS也可以运行,主要针对离线数据集 ...
- 树莓派装linux ros,树莓派安装Ubuntu16.04 MATE系统以及ROS(kinetic)的安装
安装ubuntu16.04 MATE系统 1.安装Ubuntu16.04镜像 从官网下载并按照树莓派刷写系统的流程将ubuntu16.04 MATE烧写到树莓派的SD卡上 2.设置WiFi 将树莓派外 ...
- ROS入门-4.安装ROS系统(ubuntu20.04版本安装ros的noetic版本)
ubuntu20.04版本安装ros的noetic版本 1.添加软件源 2.添加密钥 3.更新 4.安装ROS 5.初始化rosdep 6.设置环境变量 7.测试ROS安装是否成功 1.添加软件源 2 ...
- DELL戴尔Win10双硬盘安装Ubuntu20.04双系统(附带ROS安装教程)
DELL戴尔Win10双硬盘安装Ubuntu20.04双系统(附带ROS安装教程) 1.安装场景及注意事项 1.1.确认电脑的引导方式 1.2.关闭电脑BitLocker硬盘保护 2.安装步骤 2.1 ...
- Windows系统下安装ROS系统
Windows上安装ROS系统 请参照以下链接和说明,完成Windows上安装ROS系统. Installation/Windows - ROS Wiki 注意事项: Windows版本:64-bit ...
- [基于STM32底盘控制与ROS上层导航小车制作] 第五节 实现STM32与ubuntu系统下的ROS串口DMA通信,传输底盘速度等信息
系列文章目录 第一节 stm32电机驱动与编码器读取反馈 第二节 stm32电机pid控制 第三节 stm32线速度标定 第四节 stm32添加mpu6050得到angle角度 第五节 实现STM32 ...
- Ubuntu系统下使用ROS(moveit )连接ABB实体机器人(irb120)(1.控制仿真)
前言: 经过两天的踩坑,终于将ROS与ABB连接成功,可以通过rviz的gui界面拖动机械臂来控制实体ABB机械臂进行运动,因为网络上的教程(几位博主和ROS WIKI等)个别步骤的不完整,所以走了很 ...
- ROS系列——如何在ros多机系统中使用多个Velodyne VLP16
ROS系列--如何在ros多机系统中使用多个Velodyne VLP16 说明 修改launch文件 关键点 说明 本文主要介绍ROS多机系统中如何使用多个VLP16,比如A上配备了一个VLP16,B ...
- 【ROS教程 001】ROS机器人系统简介及安装
1 ROS简介 ROS(Robot Operating System)是一种得到广泛使用的机器人操作与系统软件框架,现在很多研究机构通过增加ROS支持的硬件或开放软件源代码的方式加入ROS系统的开发中 ...
- [转]使用rosbridge协议实现安卓跟ros的解耦
安卓与ROS通信的现状 因为ROS官方支持的语言绑定只有C++和Python,所以目前安卓想与ROS通信,必须借助半官方的rosjava包,而Rosjava太重了,因为它跟C++/Python一样,是 ...
最新文章
- java cdata xml 解析,如何解析lt;![CDATA []]gt;的XML
- python 3.9 新特性 简介
- 微众WeCross 跨链平台(1)平台介绍
- hibernate异常之--count查询异常
- C++求复数的角度_人教A版高中数学必修二7.1 复数的概念优质课公开课课件、教案...
- python建模仿真 matlab_清华大学出版社-图书详情-《仿真建模与MATLAB实用教程》
- 腾讯2020校园招聘后台开发面试编程题
- Docker网络解决方案-Weave部署记录
- .Net Micro Framework移植基础(包编译通过)
- 天刀服务器未响应,天涯明月刀卡顿怎么办 教你如何优化游戏不在卡顿
- 衡量计算机储存容量的常用计量单位是,衡量存储器的单位是什么
- Oracle 对表空间无操作权限
- 九峰影业创始人_九峰百度百科
- 4.13nbsp;经济分析
- 安卓手机开机动画制作修改教程
- TCP的滑动窗口与拥塞窗口
- 【32最小系统板】pid循迹小车(铁丝循迹)
- css一些美化页面的方法
- mysql的cj是什么包_com.mysql.cj.jdbc.driver
- 为什么总是显示没有java_为什么总是提示错误:无法读取:HelloWorld.java