1.4 Ubuntu18.04 ROS udp Client通信实现

Ubuntu18.04 ROS udp Client通信实现

此小节介绍udp Client收发数据。udp通信属于帧传输,在帧传输过程中对于消息的次序和到达情况没有需求,没有进行校验,所以UDP属于不可靠传输,但是由于缺少的校验时间,udp通信在一些环境下(比如无线传输,网络信号较差时)通信效率较高,此通信方式通常用于实时性要求较高的传感器信息回传(比如视频流,语音流的回传)。
下一节介绍介绍Ubuntu18.04 ROS与Android tcp/ip的通信实现,后续Ubuntu介绍串口通信的灵活运用,通过Android与工控机串口的配合实现车辆遥控操作。
本小节测试工具,Windows使用USR-TCP232-Test-V1.3 udp或者TCP&UDP测试工具,Ubuntu18.04/ROS udp/client进行连接。
关于Ubuntu ros下tcp/ip udp/Server服务器客户端的简单实现可以参考1.1 ; 1.2;1.3

测试过程和效果

我的测试平台为Ubuntu18.04 与Windows系统上的USR-TCP232-Test-V1.3 测试工具进行通信测试。
以下的1,2步骤前三节介绍类似.
1.保证两台电脑在同一个网络下,并查看Ubuntu的本机IP,在设置->wifi->中可查看,如下图,192.168.2.204,为本机IP。
1.4 Ubuntu18.04 ROS udp Client通信实现_第1张图片

  1. 相互ping另外一台电脑的ip,通则说明两台电脑在同一网络下连接成功。windows打开网络串口助手可自动获取本机IP,也可以在网络状态->详细信息中查看IP。如下图,如果ping失败请查看防护墙是否关闭。
    1.4 Ubuntu18.04 ROS udp Client通信实现_第2张图片
    1.4 Ubuntu18.04 ROS udp Client通信实现_第3张图片

3 ping成功之后,进行通信效果测试,首先启动USR-TCP232-Test-V1.3测试工具,然后Ubuntu运行roscore,再然后启动服务器节点,运行效果如下:
Windows:
1.4 Ubuntu18.04 ROS udp Client通信实现_第4张图片
Ubuntu:
1.4 Ubuntu18.04 ROS udp Client通信实现_第5张图片

ROS工作区间和功能包的创建

ROS工作区间和功能包的创建网上资料比较多,这里简单说明。其中使用RoboWare Studio,这个过程变的更简单。

#创建工作空间
mkdir catkin_ws #区间名称
cd catkin_ws 
mkdir src #创建代码空间
cd src
catkin_init_workspace #初始化位ROS工作空间
cd ..
catkin_make
source ~/catkin_ws/devel/setup.bash
#创建功能包
cd ~/catkin_ws/src
catkin_create_pkg ros_socket std_msgs rospy roscpp
#创建通信节点
#在src目录下打开终端
touch udp_client.cpp
#在CMakeLists.txt中添加以下
add_executable(udp_client
  src/udp_client.cpp
)
add_dependencies(udp_client ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
target_link_libraries(udp_client
  ${catkin_LIBRARIES}
)


ROS UDP Client的实现代码

本代码里没有进行话题的订阅和发布,大家可以自行添加测试,代码注释比较清楚。
代码主要包括以下步骤:
1 .创建ros句柄
2.创建一个socket
3.输入服务器IP和端口
4.打包要发送的数据和进行校验,也可以在话题回调函数中进行数据打包。
5.接收数据并显示
程序注释的比较清楚,可以看看。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define DEST_PORT 1024
#define DSET_IP_ADDRESS  "192.168.2.130"

int main(int argc, char** argv)
{
    ros::init(argc, argv, "udp_port");
    //创建句柄(虽然后面没用到这个句柄,但如果不创建,运行时进程会出错)
    ros::NodeHandle n;

     int sock_fd;
  /* udp socket */
     sock_fd = socket(AF_INET, SOCK_DGRAM, 0); //创建套接字

    if(sock_fd < 0)
    {
            perror("socket");
            exit(1);
    }

  struct sockaddr_in addr_serv;
  int len;
 //将addr_serv数组全体内存空间按字节整体清零
  memset(&addr_serv, 0, sizeof(addr_serv));
  addr_serv.sin_family = AF_INET; // 地址族
  addr_serv.sin_addr.s_addr = inet_addr(DSET_IP_ADDRESS); //服务器ip,不是本机ip
  addr_serv.sin_port = htons(DEST_PORT);//服务器端口
  //就是求数组addr_serv占用的所有内存尺寸,不会理会其中装载什么数据。
  len = sizeof(addr_serv);

  int send_num;
  int recv_num;
  char send_buf[100]={0};//存储要发的数据

//数据打包,发送0-71
  for (int i = 0; i < 71; i++)
  {
        send_buf[i] = i;
  }
  
    uint8_t mycheck;
    int i=0;

    char recv_buf[100];//存储接收的数据
    ros::Rate loop_rate(50);//while以50Hz循环

    while(ros::ok()) 
    {
        for(i=0;i<71;i++)
        {
          mycheck ^= send_buf[i]; //对要发送的数据亦或校验,接收端再次校验,保证接收端的数据正确
        }
        send_buf[71]= mycheck; //存储校验数
        send_buf[72]=0xEE;
        send_buf[73]=0xDD;

        //ssize_t sendo(ints,const void *msg,size_t len,int flags,const struct sockaddr *to,socklen_ttolen);
        /*
        2>函数功能:
        向目标主机发送消息
        3>函数形参:
        Ø  s:套接字描述符。
        Ø  *msg:发送缓冲区
        Ø  len:待发送数据的长度
        Ø  flags:控制选项,一般设置为0或取下面的值
        (1)MSG_OOB:在指定的套接字上发送带外数据(out-of-band data),该类型的套接字必须支持带外数据(eg:SOCK_STREAM).
        (2)MSG_DONTROUTE:通过最直接的路径发送数据,而忽略下层协议的路由设置。
        Ø  to:用于指定目的地址
        Ø  tolen:目的地址的长度。
        4>函数返回值:
        执行成功后返回实际发送数据的字节数,出错返回-1,错误代码存入errno中。
        */
        send_num = sendto(sock_fd, send_buf, 74, 0, (struct sockaddr *)&addr_serv, len);

        if(send_num < 0) //判断发送是否成功
        {
            perror("sendto error:");
            exit(1);
        }
        /*
        1>函数原型:
        #include
        #include
        ssize_t recvfrom(int s,void *buf,size_t len,intflags,struct sockaddr *from,socklen_t *fromlen);
        2>函数功能:接收数据
        3>函数形参:
        Ø  int s:套接字描述符
        Ø  buf:指向接收缓冲区,接收到的数据将放在这个指针所指向的内存空间。
        Ø  len:指定了缓冲区的大小。
        Ø  flags:控制选项,一般设置为0或取以下值
        (1)MSG_OOB:请求接收带外数据
        (2)MSG_PEEK:只查看数据而不读出
        (3)MSG_WAITALL:只在接收缓冲区时才返回。
        Ø  *from:保存了接收数据报的源地址。
        Ø  *fromlen:参数fromlen在调用recvfrom前为参数from的长度,调用recvfrom后将保存from的实际大小。
        4>函数返回值:
        执行成功后返回实际接收到数据的字节数,出错时则返回-1,错误代码存入errno中。
        */
        recv_num = recvfrom(sock_fd, recv_buf, sizeof(recv_buf), 0, (struct sockaddr *)&addr_serv, (socklen_t *)&len);

        if(recv_num < 0) //判断数据是否接收成功
        {
            perror("recvfrom error:");
            exit(1);
        }

         recv_buf[recv_num] = '\0';
         printf("client receive %d bytes: %s\n", recv_num, recv_buf);//打印接收到的数据

         ros::spinOnce(); 
        loop_rate.sleep(); 
    }
         close(sock_fd);
         return 0;
}



功能包程序。
欢迎大家批评指正!!!

你可能感兴趣的:(udp,网络,网络协议)