我的AI之路(27)--用C++实现机器人与机器人后端控制系统之间的双向通讯

51c自动驾驶~合集57 多目标多头蒸馏模块:我们看到轨迹模仿学习之后的轨迹还过了其他的MLP,这就是其他头,它的目标也是不一样的,第一个是跟碰撞相关的,第二个是跟行驶区域相关的,第三个是跟舒适度相关的,也就是说不同的评判指标都有一个teacher,之前的模仿学习就是人类的teacher,那么这些teacher是怎么来的呢?本文作者提出的新的范式,就是规划模块是多模的输出,同时,目标也是多样性的,即不仅是GT的轨迹也同时引入了更多的正样本,由不同的专家给出的。还是那句话,小米汽车的成功,是小米价值观,小米模式,和小米方法论的成功。 阅读详情

      前面说过,机器人和机器人后端管理系统之间通常要求频繁双向传输数据、尤其需要最好保持长连接的通讯需求,使用WebSocket连接是最好的选择,可以基于RosBridge来实现,但是RosBridge实现的是websocket server,对于要求机器人后端管理系统在系统启动时主动连接机器人的需求是比较合适的,对于反过来要求机器人启动时作为client主动连接机器人管理系统的这种更常见需求,RosBridge不能满足要求,一个快捷实现办法就是安装python版的websocket-client,然后基于python版websocket-client进行python编程,但是我感觉对于有复杂类型的交互数据需要传输和处理并且需要性能尽可能快的话,当然还是自己使用C++来实现一个ROS通讯节点作为与机器人后端通讯的websocket client比较好。

  自己用C++实现websocket那些协议内容显然要费不少时间去编写和调试,对于项目要求进度尽可能快的情况下不大现实,于是网上找了评价不错的C++实现的开源websocket框架websocketpp来基于它开发,仔细看了它的代码,找到一个比较合适的client示例代码,在它基础上做开发比较合适:

#include <websocketpp/config/asio_no_tls_client.hpp>
#include <websocketpp/client.hpp>

#include <websocketpp/common/thread.hpp>
#include <websocketpp/common/memory.hpp>

#include <cstdlib>
#include <iostream>
#include <map>
#include <string>
#include <sstream>

typedef websocketpp::client<websocketpp::config::asio_client> client;

class connection_metadata {
public:
    typedef websocketpp::lib::shared_ptr<connection_metadata> ptr;

    connection_metadata(int id, websocketpp::connection_hdl hdl, std::string uri)
      : m_id(id)
      , m_hdl(hdl)
      , m_status("Connecting")
      , m_uri(uri)
      , m_server("N/A")
    {}

    void on_open(client * c, websocketpp::connection_hdl hdl) {
        m_status = "Open";

        client::connection_ptr con = c->get_con_from_hdl(hdl);
        m_server = con->get_response_header("Server");
    }

    void on_fail(client * c, websocketpp::connection_hdl hdl) {
        m_status = "Failed";

        client::connection_ptr con = c->get_con_from_hdl(hdl);
        m_server = con->get_response_header("Server");
        m_error_reason = con->get_ec().message();
    }
    
    void on_close(client * c, websocketpp::connection_hdl hdl) {
        m_status = "Closed";
        client::connection_ptr con = c->get_con_from_hdl(hdl);
        std::stringstream s;
        s << "close code: " << con->get_remote_close_code() << " (" 
          << websocketpp::close::status::get_string(con->get_remote_close_code()) 
          << "), close reason: " << con->get_remote_close_reason();
        m_error_reason = s.str();
    }

    void on_message(websocketpp::connection_hdl, client::message_ptr msg) {
        if (msg->get_opcode() == websocketpp::frame::opcode::text) {
            m_messages.push_back("<< " + msg->get_payload());
        } else {
            m_messages.push_back("<< " + websocketpp::utility::to_hex(msg->get_payload()));
        }
    }

    websocketpp::connection_hdl get_hdl() const {
        return m_hdl;
    }
    
    int get_id() const {
        return m_id;
    }
    
    std::string get_status() const {
        return m_status;
    }

    void record_sent_message(std::string message) {
        m_messages.push_back(">> " + message);
    }

    friend std::ostream & operator<< (std::ostream & out, connection_metadata const & data);
private:
    int m_id;
    websocketpp::connection_hdl m_hdl;
    std::string m_status;
    std::string m_uri;
    std::string m_server;
    std::string m_error_reason;
    std::vector<std::string> m_messages;
};

std::ostream & operator<< (std::ostream & out, connection_metadata const & data) {
    out << "> URI: " << data.m_uri << "\n"
        << "> Status: " << data.m_status << "\n"
        << "> Remote Server: " << (data.m_server.empty() ? "None Specified" : data.m_server) << "\n"
        << "> Error/close reason: " << (data.m_error_reason.empty() ? "N/A" : data.m_error_reason) << "\n";
    out << "> Messages Processed: (" << data.m_messages.size() << ") \n";

    std::vector<std::string>::const_iterator it;
    for (it = data.m_messages.begin(); it != data.m_messages.end(); ++it) {
        out << *it << "\n";
    }

    return out;
}

class websocket_endpoint {
public:
    websocket_endpoint () : m_next_id(0) {
        m_endpoint.clear_access_channels(websocketpp::log::alevel::all);
        m_endpoint.clear_error_channels(websocketpp::log::elevel::all);

        m_endpoint.init_asio();
        m_endpoint.start_perpetual();

        m_thread = websocketpp::lib::make_shared<websocketpp::lib::thread>(&client::run, &m_endpoint);
    }

    ~websocket_endpoint() {
        m_endpoint.stop_perpetual();
        
        for (con_list::const_iterator it = m_connection_list.begin(); it != m_connection_list.end(); ++it) {
            if (it->second->get_status() != "Open") {
                // Only close open connections
                continue;
            }
            
            std::cout << "> Closing connection " << it->second->get_id() << std::endl;
            
            websocketpp::lib::error_code ec;
            m_endpoint.close(it->second->get_hdl(), websocketpp::close::status::going_away, "", ec);
            if (ec) {
                std::cout << "> Error closing connection " << it->second->get_id() << ": "  
                          << ec.message() << std::endl;
            }
        }
        
        m_thread->join();
    }

    int connect(std::string const & uri) {
        websocketpp::lib::error_code ec;

        client::connection_ptr con = m_endpoint.get_connection(uri, ec);

        if (ec) {
            std::cout << "> Connect initialization error: " << ec.message() << std::endl;
            return -1;
        }

        int new_id = m_next_id++;
        connection_metadata::ptr metadata_ptr = websocketpp::lib::make_shared<connection_metadata>(new_id, con->get_handle(), uri);
        m_connection_list[new_id] = metadata_ptr;

        con->set_open_handler(websocketpp::lib::bind(
            &connection_metadata::on_open,
            metadata_ptr,
            &m_endpoint,
            websocketpp::lib::placeholders::_1
        ));
        con->set_fail_handler(websocketpp::lib::bind(
            &connection_metadata::on_fail,
            metadata_ptr,
            &m_endpoint,
            websocketpp::lib::placeholders::_1
        ));
        con->set_close_handler(websocketpp::lib::bind(
            &connection_metadata::on_close,
            metadata_ptr,
            &m_endpoint,
            websocketpp::lib::placeholders::_1
        ));
        con->set_message_handler(websocketpp::lib::bind(
            &connection_metadata::on_message,
            metadata_ptr,
            websocketpp::lib::placeholders::_1,
            websocketpp::lib::placeholders::_2
        ));

        m_endpoint.connect(con);

        return new_id;
    }

    void close(int id, websocketpp::close::status::value code, std::string reason) {
        websocketpp::lib::error_code ec;
        
        con_list::iterator metadata_it = m_connection_list.find(id);
        if (metadata_it == m_connection_list.end()) {
            std::cout << "> No connection found with id " << id << std::endl;
            return;
        }
        
        m_endpoint.close(metadata_it->second->get_hdl(), code, reason, ec);
        if (ec) {
            std::cout << "> Error initiating close: " << ec.message() << std::endl;
        }
    }

    void send(int id, std::string message) {
        websocketpp::lib::error_code ec;
        
        con_list::iterator metadata_it = m_connection_list.find(id);
        if (metadata_it == m_connection_list.end()) {
            std::cout << "> No connection found with id " << id << std::endl;
            return;
        }
        
        m_endpoint.send(metadata_it->second->get_hdl(), message, websocketpp::frame::opcode::text, ec);
        if (ec) {
            std::cout << "> Error sending message: " << ec.message() << std::endl;
            return;
        }
        
        metadata_it->second->record_sent_message(message);
    }

    connection_metadata::ptr get_metadata(int id) const {
        con_list::const_iterator metadata_it = m_connection_list.find(id);
        if (metadata_it == m_connection_list.end()) {
            return connection_metadata::ptr();
        } else {
            return metadata_it->second;
        }
    }
private:
    typedef std::map<int,connection_metadata::ptr> con_list;

    client m_endpoint;
    websocketpp::lib::shared_ptr<websocketpp::lib::thread> m_thread;

    con_list m_connection_list;
    int m_next_id;
};

int main() {
    bool done = false;
    std::string input;
    websocket_endpoint endpoint;

    while (!done) {
        std::cout << "Enter Command: ";
        std::getline(std::cin, input);

        if (input == "quit") {
            done = true;
        } else if (input == "help") {
            std::cout
                << "\nCommand List:\n"
                << "connect <ws uri>\n"
                << "send <connection id> <message>\n"
                << "close <connection id> [<close code:default=1000>] [<close reason>]\n"
                << "show <connection id>\n"
                << "help: Display this help text\n"
                << "quit: Exit the program\n"
                << std::endl;
        } else if (input.substr(0,7) == "connect") {
            int id = endpoint.connect(input.substr(8));
            if (id != -1) {
                std::cout << "> Created connection with id " << id << std::endl;
            }
        } else if (input.substr(0,4) == "send") {
            std::stringstream ss(input);
            
            std::string cmd;
            int id;
            std::string message;
            
            ss >> cmd >> id;
            std::getline(ss,message);
            
            endpoint.send(id, message);
        } else if (input.substr(0,5) == "close") {
            std::stringstream ss(input);
            
            std::string cmd;
            int id;
            int close_code = websocketpp::close::status::normal;
            std::string reason;
            
            ss >> cmd >> id >> close_code;
            std::getline(ss,reason);
            
            endpoint.close(id, close_code, reason);
        } else if (input.substr(0,4) == "show") {
            int id = atoi(input.substr(5).c_str());

            connection_metadata::ptr metadata = endpoint.get_metadata(id);
            if (metadata) {
                std::cout << *metadata << std::endl;
            } else {
                std::cout << "> Unknown connection id " << id << std::endl;
            }
        } else {
            std::cout << "> Unrecognized Command" << std::endl;
        }
    }

    return 0;
}

上面的代码实现了websocket连接的建立和数据的收发,不过经过试用实验发现有以下缺点需要改进:

1) websocket_endpoint::connect()里只是发了连接请求,没有等待连接完成成功就返回,另外对于因为IP或端口错误或网络不通的情况也没做检查和判断;

2)同一连接上的数据的发送和接收之间的对应关系没有管理,对于请求-响应式通讯在发送数据后等待响应数据到来后才做后面的处理的逻辑没有实现。

另外,实现为ROS节点需要增加代码,此节点收到来自机器人后端管理系统发来的数据后,可以解析后通过RosBridge Server遵循rosbridge v2.0 Protocol Specification调用相应的其他ROS节点的service或给其他Ros节点发布消息,也可以对来自机器人后端管理系统发来的数据不做任何解析直接通过Publish/Subscribe机制转发给其他相应节点后再解析处理,这个就看你想怎么设计。

为实现上述目标,我对上面的源码做了修改和增加,具体作用参见代码附近的注释:

#include <ros/ros.h>
#include <std_msgs/String.h>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <unistd.h>

#include <websocketpp/config/asio_no_tls_client.hpp>
#include <websocketpp/client.hpp>

#include <websocketpp/common/thread.hpp>
#include <websocketpp/common/memory.hpp>

#include <cstdlib>
#include <iostream>
#include <map>
#include <string>
#include <sstream>

typedef websocketpp::client<websocketpp::config::asio_client> client;
class websocket_endpoint;

class connection_metadata {
public:
    typedef websocketpp::lib::shared_ptr<connection_metadata> ptr;

    connection_metadata(int id, websocketpp::connection_hdl hdl, std::string uri,websocket_endpoint* endpoint);

    void on_open(client * c, websocketpp::connection_hdl hdl) {
        m_status = "Open";

        client::connection_ptr con = c->get_con_from_hdl(hdl);
        m_server = con->get_response_header("Server");
    }

    void on_fail(client * c, websocketpp::connection_hdl hdl) {
        m_status = "Failed";

        client::connection_ptr con = c->get_con_from_hdl(hdl);
        m_server = con->get_response_header("Server");
        m_error_reason = con->get_ec().message();
    }
    
    void on_close(client * c, websocketpp::connection_hdl hdl) {
        m_status = "Closed";
        client::connection_ptr con = c->get_con_from_hdl(hdl);
        std::stringstream s;
        s << "close code: " << con->get_remote_close_code() << " (" 
          << websocketpp::close::status::get_string(con->get_remote_close_code()) 
          << "), close reason: " << con->get_remote_close_reason();
        m_error_reason = s.str();
    }
    void on_message(websocketpp::connection_hdl, client::message_ptr msg);


    websocketpp::connection_hdl get_hdl() const {
        return m_hdl;
    }
    
    int get_id() const {
        return m_id;
    }
    
    std::string get_status() const {
        return m_status;
    }

    void record_sent_message(std::string message) {
        m_messages.push_back(">> " + message);
    }

    friend std::ostream & operator<< (std::ostream & out, connection_metadata const & data);
private:
    int m_id;
    websocketpp::connection_hdl m_hdl;
    std::string m_status;
    std::string m_uri;
    std::string m_server;
    std::string m_error_reason;
    std::vector<std::string> m_messages;
    websocket_endpoint* m_endpoint;
};

class websocket_endpoint {
public:
    websocket_endpoint () : m_next_id(0) {
        m_endpoint.clear_access_channels(websocketpp::log::alevel::all);
        m_endpoint.clear_error_channels(websocketpp::log::elevel::all);

        m_endpoint.init_asio();
        m_endpoint.start_perpetual();

        m_thread = websocketpp::lib::make_shared<websocketpp::lib::thread>(&client::run, &m_endpoint);
    }

    ~websocket_endpoint() {
        m_endpoint.stop_perpetual();
        
        for (con_list::const_iterator it = m_connection_list.begin(); it != m_connection_list.end(); ++it) {
            if (it->second->get_status() != "Open") {
                // Only close open connections
                continue;
            }
            
            ROS_INFO_STREAM("> Closing connection " << it->second->get_id() );
            
            websocketpp::lib::error_code ec;
            m_endpoint.close(it->second->get_hdl(), websocketpp::close::status::going_away, "", ec);
            if (ec) {
                ROS_ERROR_STREAM("> Error closing connection " << it->second->get_id() << ": "  
                          << ec.message() );
            }
        }
        
        m_thread->join();
    }

    int connect(std::string const & uri,int timeout) { //timeout unit: second
        websocketpp::lib::error_code ec;

        client::connection_ptr con = m_endpoint.get_connection(uri, ec);

        if (ec) {
            ROS_ERROR_STREAM("> Connect initialization error: " << ec.message() );
            return -1;
        }

        int new_id = m_next_id++;
        connection_metadata::ptr metadata_ptr = websocketpp::lib::make_shared<connection_metadata>(new_id, con->get_handle(), uri,this);
        m_connection_list[new_id] = metadata_ptr;

        con->set_open_handler(websocketpp::lib::bind(
            &connection_metadata::on_open,
            metadata_ptr,
            &m_endpoint,
            websocketpp::lib::placeholders::_1
        ));
        con->set_fail_handler(websocketpp::lib::bind(
            &connection_metadata::on_fail,
            metadata_ptr,
            &m_endpoint,
            websocketpp::lib::placeholders::_1
        ));
        con->set_close_handler(websocketpp::lib::bind(
            &connection_metadata::on_close,
            metadata_ptr,
            &m_endpoint,
            websocketpp::lib::placeholders::_1
        ));
        con->set_message_handler(websocketpp::lib::bind(
            &connection_metadata::on_message,
            metadata_ptr,
            websocketpp::lib::placeholders::_1,
            websocketpp::lib::placeholders::_2
        ));

        m_endpoint.connect(con);
        //等待连接完成非常重要!!!
        int count=0;
        while(metadata_ptr->get_status() != "Open" && ++count <= timeout*10 ){
            usleep(100000);
        }
        m_conn_id = new_id;
        if(metadata_ptr->get_status() != "Open"){ //连接超时!
            ROS_INFO_STREAM("Timeout! failed to connect to "<<uri<<" in "<<timeout<<" seconds!");
            m_conn_id = -1;
            return -1;
        }
        else
            return new_id;
    }

    void close(int id, websocketpp::close::status::value code, std::string reason) {
        websocketpp::lib::error_code ec;
        
        con_list::iterator metadata_it = m_connection_list.find(id);
        if (metadata_it == m_connection_list.end()) {
            ROS_INFO_STREAM("> No connection found with id " << id );
            return;
        }
        
        m_endpoint.close(metadata_it->second->get_hdl(), code, reason, ec);
        if (ec) {
           ROS_ERROR_STREAM( "> Error initiating close: " << ec.message() );
        }
    }

    void send(int id, std::string message) {
        websocketpp::lib::error_code ec;
        
        con_list::iterator metadata_it = m_connection_list.find(id);
        if (metadata_it == m_connection_list.end()) {
            ROS_INFO_STREAM("> No connection found with id " << id );
            return;
        }
        
        m_endpoint.send(metadata_it->second->get_hdl(), message, websocketpp::frame::opcode::text, ec);
        if (ec) {
            ROS_ERROR_STREAM("> Error sending message: " << ec.message());
            return;
        }
        m_sendRecvMap[id]=false;
        //metadata_it->second->record_sent_message(message);
    }

    connection_metadata::ptr get_metadata(int id) const {
        con_list::const_iterator metadata_it = m_connection_list.find(id);
        if (metadata_it == m_connection_list.end()) {
            return connection_metadata::ptr();
        } else {
            return metadata_it->second;
        }
    }

    std::map<int,bool> getSendRecvMap(){
        return m_sendRecvMap;
    }

    void setPublisher(ros::Publisher pub) {
        m_pub = pub;
    }

    //MC -> CCP
    void MC2CCPCallback(const std_msgs::String::ConstPtr& msg)
    {
        std::string str = msg->data;
        ROS_INFO_STREAM("[MC2CCPCallback]msg received: " << str <<", send to connection:"<<m_conn_id);
        send(m_conn_id,str);
    }

    //CCP -> MC
    void handleTextMessage(int id,std::string message) {
        ROS_INFO_STREAM( "[handleTextMessage]" << message );

        //publish CCP's msg to MC, connection id is reserved for future use.
        std_msgs::String str;
        str.data = message.c_str();
        m_pub.publish(str);
        m_sendRecvMap[id]=true;
    }

    void handleBinaryMessage(int id,std::string message) {

    }


private:
    typedef std::map<int,connection_metadata::ptr> con_list;

    client m_endpoint;
    websocketpp::lib::shared_ptr<websocketpp::lib::thread> m_thread;

    con_list m_connection_list;
    int m_next_id;
    int m_conn_id=-1;
    std::map<int,bool> m_sendRecvMap;

    ros::Publisher m_pub;
};

std::ostream & operator<< (std::ostream & out, connection_metadata const & data) {
    out << "> URI: " << data.m_uri << "\n"
        << "> Status: " << data.m_status << "\n"
        << "> Remote Server: " << (data.m_server.empty() ? "None Specified" : data.m_server) << "\n"
        << "> Error/close reason: " << (data.m_error_reason.empty() ? "N/A" : data.m_error_reason) << "\n";
    out << "> Messages Processed: (" << data.m_messages.size() << ") \n";

    std::vector<std::string>::const_iterator it;
    for (it = data.m_messages.begin(); it != data.m_messages.end(); ++it) {
        out << *it << "\n";
    }

    return out;
}

connection_metadata::connection_metadata(int id, websocketpp::connection_hdl hdl, std::string uri,websocket_endpoint* endpoint)
  : m_id(id)
  , m_hdl(hdl)
  , m_status("Connecting")
  , m_uri(uri)
  , m_server("N/A"),
  m_endpoint(endpoint)
{}

void connection_metadata::on_message(websocketpp::connection_hdl, client::message_ptr msg) {
        if (msg->get_opcode() == websocketpp::frame::opcode::text) {
            //m_messages.push_back("<< " + msg->get_payload());
            m_endpoint->handleTextMessage(m_id,msg->get_payload());
        } else {
            //m_messages.push_back("<< " + websocketpp::utility::to_hex(msg->get_payload()));
            m_endpoint->handleTextMessage(m_id,websocketpp::utility::to_hex(msg->get_payload()));
        }
    }


bool bShutDown = false;

void shutdownCallback(const std_msgs::StringConstPtr& msg)
{
    std::string str = msg->data;
    if (str=="MC_CMD_SHUTDOWN")
        bShutDown = true;
}

int main(int argc, char ** argv) {

    std::string input;
    // 初始化节点
    ros::init(argc, argv, "ws_server");
    ros::NodeHandle node;
    ros::Rate rate(100.0);
    ros::Subscriber sub_shutdown = node.subscribe("ws_server/MC2WSS", 10, &shutdownCallback);

    //目前只支持创建一个到CCP的websocket连接
    websocket_endpoint endpoint;

    //CCP -> Robot's ws_server -> Robot's MC
    ros::Publisher pub =
            node.advertise<std_msgs::String>("ws_server/CCP2MC", 10);
    endpoint.setPublisher(pub);
    //Robot's MC -> Robot's ws_server -> CCP
    ros::Subscriber sub = node.subscribe("ws_server/MC2CCP", 10, &websocket_endpoint::MC2CCPCallback,&endpoint);

    std::string test_mode;
    int timeout;
    node.getParam("/ws_server/test_mode", test_mode);
    node.getParam("/ws_server/timeout", timeout);

    //ROS_INFO_STREAM("test_mode="<< test_mode);
    if (test_mode == "yes") { // yes/no
        std::vector<int> ids;
        while (!bShutDown) {
            ROS_INFO_STREAM( "Enter Command: ");
            std::getline(std::cin, input);

            if (input == "quit") {
                bShutDown = true;
                int id;
                int close_code = websocketpp::close::status::normal;
                std::string reason="quit";
                std::vector<int>::const_iterator it;
                for (it = ids.begin(); it != ids.end(); ++it) {
                        id =  *it;
                       // endpoint.close(id, close_code, reason);
                 }
            } else if (input == "help") {
                ROS_INFO_STREAM("\nCommand List:\n" << "connect <ws uri>\n"
                        << "send <connection id> <message>\n"
                        << "close <connection id> [<close code:default=1000>] [<close reason>]\n"
                        << "show <connection id>\n"
                        << "help: Display this help text\n"
                        << "quit: Exit the program\n" );
            } else if (input.substr(0, 7) == "connect") {
                int id = endpoint.connect(input.substr(8),timeout);
                if (id != -1) {
                    ids.push_back(id);
                    ROS_INFO_STREAM( "> Created connection with id " << id);
                }
            } else if (input.substr(0, 4) == "send") {
                std::stringstream ss(input);

                std::string cmd;
                int id;
                std::string message;

                ss >> cmd >> id;
                std::getline(ss, message);

                endpoint.send(id, message);
            } else if (input.substr(0, 5) == "close") {
                std::stringstream ss(input);

                std::string cmd;
                int id;
                int close_code = websocketpp::close::status::normal;
                std::string reason;

                ss >> cmd >> id >> close_code;
                std::getline(ss, reason);

                endpoint.close(id, close_code, reason);
            } else if (input.substr(0, 4) == "show") {
                int id = atoi(input.substr(5).c_str());

                connection_metadata::ptr metadata = endpoint.get_metadata(id);
                if (metadata) {
                    std::cout << *metadata << std::endl;
                } else {
                    ROS_INFO_STREAM("> Unknown connection id " << id );
                }
            } else {
                ROS_INFO_STREAM("> Unrecognized Command" );
            }
        }
    } else {
        std::string protocol;
        std::string ip;
        int port;
        std::string entry;
        std::string uri;
        std::stringstream ss;
        node.getParam("/ws_server/protocol", protocol);
        node.getParam("/ws_server/ip", ip);
        node.getParam("/ws_server/port", port);
        node.getParam("/ws_server/entry", entry);
        ss<<port;
        uri = protocol + "://" + ip + ":" + ss.str() + entry;

        //目前只Robot到CCP创建支持一个websocket连接,如果Robot需要同时创建多个websocket连接连接到不同的控制系统,两种改造办法:
        //1.创建多个websocket_endpoint实例,分别用于不同的连接,这种办法最简单;
        //2.只创建一个websocket_endpoint实例,调用多次connect()连接到不同到系统并记录connect()返回的connection id,
        //  收发数据时都得根据connection id识别不同的连接,另外pub和sub都得改造:
        //    publish时需要把connection id传给MC并记录,subscriber的callback()也要把MC传来的connection id传给send()以发送数据给对应的CP
        int id = endpoint.connect(uri,timeout);
        ROS_INFO_STREAM("connect "<< uri <<",id="<<id );
        if(id<0)
            return -1;

        //Shutdown procedure:
        //1. CCP sends a shutdown cmd to MC  via ws_server by websocket and publishing
        //2. MC does something before shutting down,and sends a shutdown message to ws_server by publishing.
        //3. ws_server exits the while-loop and shuts down
        while(node.ok() && !bShutDown ){
            rate.sleep();  //rate is 100 HZ, so this sleep time is  equal to or less than 10 miliseconds
            ros::spinOnce();
            //ROS_INFO_STREAM("while-loop");
        }

#if 1
        /*
        //Robot notifies CCP of that it is shutting down.
        std::string robot_id = "J1001";
        std::string message="Robot "+robot_id+" shut down!"; //this is only an example,actually this message should be issued by MC.
        endpoint.send(id, message); //send shutdown msg back to CCP
        */
        connection_metadata::ptr metadata = endpoint.get_metadata(id);
        if (metadata) {
          std::cout << *metadata << std::endl;
          ROS_INFO_STREAM( "=====shut down======" );
        }
        int close_code = websocketpp::close::status::normal; //1000
        std::string reason="normal exit";
        std::map<int,bool> sendRecvMap = endpoint.getSendRecvMap();
        std::map<int,bool>::iterator it = sendRecvMap.find(id);
        int count=0;
        if(it!=sendRecvMap.end()) {
            while(!it->second && ++count <= timeout*10 ){
                usleep(100000);
            }
        }
        endpoint.close(id,close_code,reason);
#endif
    }

    //ROS_INFO_STREAM("shut down...");
    ros::shutdown();
    return 0;
}

    上面 CCP表示机器人后端管理系统,MC表示机器人本体的中控系统,上面实现的这个ROS节点就是在CCP和MC之间起到桥梁作用,对CCP和MC之间的通讯数据没做任何解析处理,只负责转发。

    原始代码中需要通过命令行输入来测试websocket连接的建立/关闭和数据的收发,我对其予以保留,考虑到正式运行时不需要这个命令行输入测试的功能,在ws_server.launch文件里增加了test_mode来控制,想测试用命令行测试websocket连接和数据收发的话,把test_mode设置为yes即可,正式运行前把test_mode设置为no即可,正式运行模型下,先配置并启动一个websocket server(比如基于Tomcat配置并运行一个能简单基于websocket收发数据并打印出数据的测试web app)来模拟CCP,执行ws_server.launch启动节点连接到此websocket server后,可以手工执行 rostopic pub -1  /ws_server/MC2CCP std_msgs/String "test data" 之类命令,即可看到web app收到并打印出"test data",如果web app在收到数据后并立即原样发回数据,执行rostopic echo  /ws_server/CCP2MC即可看到web app发来的数据。

ROSBridge - ROS系统与非ROS外部系统的通信的C++客户端实现 ROSBridge可以实现ROS系统与非ROS外部系统的通信,文档比较多比较杂,在Windows客户端的实现大多是Java, Python, JS等,这里对C++客户端使用Websockets通信进行了测试,C++ ROSBridge的客户端有轻量级,解耦合的优点。...... 阅读详情

相关推荐

KUKA机器人远程桌面服务

UKA机器人远程桌面REMOTESERVICE软件包 安装于KUKA机器人系统中。

基于Qt与C++控制真实UR3机器人

基于Qt与C++通过TCP控制真实UR3机器人

mai0026的博客 4130

ros2-web-bridge:将浏览器桥接到ROS 2.0

ros2-web-bridge rosbridge v2协议的服务器实现 ros2-web-bridge利用客户端,通过采用为提供JSON接口。 该网桥可以通过WebSockets上的JSON处理命令。 ROS 2支持 该ros2幅桥默认支持(目前最新的ROS 2的稳定版本 ),请访问检查出的信息。 任何想要在每晚版本ROS 2上运行的人,请更改文件的dependencies部分以安装其他版本的 。 受支持的客户 客户端是使用JSON API与ros2-web-bridge通信的程序。 客户包括: roslibjs-一个JavaScript API,可通过WebSockets与ros2-web-bridge通信。 安装 准备ROS 2请参考文档以安装ROS 2。 安装Node.js您可以安装Node.js: 从Node.js官方网站下载并安装。 使用节点版本管理器( nvm

websocket 测试

#include <boost/algorithm/string.hpp> #include <string> #include <vector> #include <list> #include <iostream> #include <websocketpp/config/asio_no_tls.hpp> #include <websocketpp/server.hpp> //名称与值数据对 struct Nam.

how0723的博客 597

嵌入式SD/TF卡通用协议-SDIO协议

为了驱动SD卡,首先的第一步就是观察SD卡引脚接线情况,通常我们在画原理图时,都是使用的SD卡卡槽与MCU进行接线。SD卡可以使用SDIO协议和SPI协议进行通信,这里我们主要介绍SDIO协议。下图中的TF卡只是容量小一点的SD卡。以F1C100S为例,接线图如下所示:我们发现上图中的TF卡使用的协议为SDIO协议,主要有4根数据线,1根命令线,1根时钟线组合而成。为了驱动这个SD/TF卡我们就需要去了解SDIO协议的时序。

qq_1033275663的博客 3052

HCIA重点知识

华为认证ICT工程师,是华为认证体系中用于标识个人能力在某一技术领域达到工程师级别的证明,表示通过认证的人员具备安装、配置、运行 ICT 设备,并进行基础故障排除的能力。抽象语言 -----> 编码 -----> 二进制 -----> 电信号 -----> 处理电信号信号的转换标准:协议,是一种允许电子设备相互交流的一系列标准的规则。即开放系统参考模型OSI/RM,简称“OSI”。OSI参考模型将计算机网络分为七个层次,自上而下依次为:应用层,表示层,会话层,传输层,网络层,数据链路层,物理层。1、应用层:

Awen0201的博客 2542

C++ - 使用Websocket++编写客户端连接WebSocket服务器并进行通信

文章目录 1 库依赖 2 根据官方示例代码修改封装的WebsocketClient类 2.1 WebsocketClient代码 2.2 WebsocketClient类使用代码 3 Websocket++官方编写客户端示例教程 4 与Websocket++官方示例客户端的不同 1 库依赖 Websocket++/Websocketpp依赖于boost(使用boost 1.74),Websocket++ 0.8.2版本,因为暂时没有使用wss,所以没有集成Openssl。 2 根据官方示例代码修改封.

翻肚鱼儿的博客 2770

数学史学习资料:史前数学

著名物理学家伽莫夫(宇宙大爆炸理论以及核聚变理论的提出者)在他的科普书《从1到无穷大》中举了这样一个例子。两个原始部落的酋长比赛,看谁说的数字大。第一个酋长说了三,第二个酋长想了半天,然后说你赢了。你今天听起来觉得这两个人真可笑,至少说出四,岂不就赢了?但是对这两位酋长来讲,所拥有的东西就很少超过三个,比三个多的东西他们就觉得没有必要数清楚了,就用许多来形容了。也就是说,他们生活的环境限制了他们的认知。

howard2005的专栏 902

websocketpp服务器优雅关闭??

websocket

南沙的星星 742

Ardupilot chibios IO固件,IO与FMU通信,固件下载(3)

目录 文章目录目录摘要 摘要 本节主要记录自己学习IO与FMU通信

陌城烟雨 2801

【第48例】BLM模型之市场洞察-五看工具篇(一)

BLM市场洞察

产品线负责人 2110

CnOpenData 线上招聘网站信息数据

CnOpenData线上招聘网站信息数据库是一个覆盖中国主流招聘平台、时间跨度长、更新频率高的劳动力市场需求侧大数据集合。本数据库汇集了,为学术界和业界提供了一扇实时观察中国劳动力市场动态的窗口。

CnOpenData_wj的博客 2639

使用R语言实现循环神经网络RNN进行文本生成

首先,让我们回顾一下循环神经网络(Recurrent Neural Networks,RNN)的基本概念。RNN是一种深度学习技术,特别适合处理序列数据,如时间序列和文本数据。RNN的关键特性是它的隐藏状态,这个状态可以捕捉到当前输入之前的信息。这使得RNN能够利用前面的信息来影响后续的输出,从而对序列数据进行有效的处理。

斌擎科技 725

C++项目:网络版本在线五子棋对战

通过实现五子棋服务器,能够让用户通过浏览器访问服务器,进行用户的注册,登录,对战匹配,实时对战,实时聊天等功能,项目实现包含数据管理模块,网络服务模块,session管理模块,在线用户管理模块,游戏房间管理模块,对战匹配管理模块,业务处理。

终为nullptr 3295

uniapp [全端兼容] - 详细实现全局修改设置字体大小功能效果,统一修改页面字号字体大小,一键改变字号整个项目立即生效,大中小字号预设、老年人大字模式场景,uniApp全平台兼容控制页面字号大小

uni-app,h5移动端网页,微信小程序,支付宝小程序,Android安卓,苹果ios,app,nvue,修改字体大小,一键调整UniApp字体大小,实时改变不用刷新,动态设置字体大小,uniapp全局修改字体字号大小,如何控制全局字号,可调整显示的字体,全项目生效,老年人大字模式,改变所有页面的字体,全局设置字体大小,实现小中大的字体切换,设置改变全局字体大小功能,如何做到全平台兼容,H5APP实现全局字体大小调整,不兼容报错,用户设置字号,全局设置应用字体大小,大中小字体修改,页面设置page-me

🏆 前端领域优质创作者 1万+

ROS 之 使用yocs_velocity_smoother对机器人速度进行限制

yocs_velocity_smoother是一个速度、加速度限制器,用来防止robot navigation的速度/转速过快,加速度/快减速过大。Bound incoming velocity messages according to robot velocity and acceleration limits. The velocity smoother nodelet runs together with the kobuki_node to apply robot’s velocity and .

https://shixuebin.gitee.io/hexo-blog/ 2623

【ROS基础】多机器人通信(1)-实现两台机器人通信

ROS是一个分布式的计算环境。一个正在运行的ROS可以在多个机器人之间分布成几十甚至上百个节点。取决于系统的配置方式,任何节点可能需要随时与任何其他节点进行通信,因此,ROS有确切的网络配置要求:1.机器之间的所有端口都能实现双向连接;2.每台机器都必须用所有其他机器都能解析的名称来公布自己。 ping是用来探测本机与网络中另一主机之间是否可达的命令,如果两台主机之间ping不通,则表明这两台主机不能建立起连接。

欢迎时常光顾我的干货超市,热衷于AIoT!! 1万+

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

【Package】RosBridge——打通Ros与非Ros环境的数据壁垒 #2 C++端实现功能ppianpak / rosbridgecpp

rosbridge tcp udp websocket

君君的博客 1133

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

Windows下使用websocketpp

WebSocket protocol 是HTML5一种新的协议,它是实现了浏览器与服务器全双工通信。WebSocket协议解析参考这篇文章http://www.cnblogs.com/chyingp/p/websocket-deep-in.html 一、下载websocketpp、boost、openssl WebSocketpp只是一个库,本身不需要搭建什么环境,只要新建的项目引入相关的库就行...

深之JohnChen的专栏 1万+

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

robomaster裁判系统个人整理

英联sudo战队

Dreamer_xc的博客 6241

websocketpp实例

//服务端 //WebSocketServerOpreate.h #pragma once #include &lt;boost/algorithm/string.hpp&gt; #include &lt;string&gt; #include &lt;vector&gt; #include &lt;iostream&gt; #include &lt;boost/thread.hpp&gt...

深之JohnChen的专栏 1万+

WebSocket入门到精通,就差这一篇了

希望通过这篇WebSocket 的介绍后,能够对websocket有一个深刻印象,希望你会更清楚 WebSocket++ 能为你的项目带来什么样的优势。

2201_75373858的博客 3235

Websocket++库分帧发送message_ptr参数设置

websocket++分帧发送message_ptr参数对象的创建及设置

wu10188的博客 1448

一个用C++编写的WebSocket客户端示例代码

上次写了WebSocket服务端,这次工作当中需要一个用C++实现的WebSocket客户端,写好后这里记一下,免得以后忘记。 本示例共有三个文件组成,依赖Websocket++第三方库 其中main.cpp是使用示例 #include <iostream> #include <string> #include <sstream> #include "websocket_endpoint.h" int main(int argc, char **argv) ..

翻肚鱼儿的博客 1852

WebSocketPP常见问题解答与技术指南

WebSocketPP是一个功能强大的C++ WebSocket库,为开发者提供了构建WebSocket客户端和服务器的完整工具集。本文将深入解析该库使用过程中的常见问题和技术要点,帮助开发者更好地理解和应用这个库。 ## 核心使用问题 ### 连接建立后修改或移除处理器 在WebSocketPP中,端点(Endpoint)上设置的处理器会被自动复制到该端点创建的所有连接中。修改端点上的处理...

gitblog_00371的博客 760
上一篇: Android资源文件中特殊字符未转义引起的编译错误
下一篇: 启用NVIDIA GPU驱动后每次Linux内核更新后图形界面进不去的问题
Arnold-FY-Chen
博客等级 码龄19年 925粉丝 204原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Arnold-FY-Chen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值