使用环境为ubuntu16.04,如果出现动态库.so的依赖问题,将相应的文件放到依赖的目录即可。
比如复制
sudo cp ./ld-linux-aarch64.so.1 /lib/ld-linux-aarch64.so.1
一、单机测试
服务器端代码,用来监听客户端发送的消息
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <iostream>
#include <cstring>
#include <vector>
#include <unistd.h>
#include <typeinfo>
using namespace std;
//创建新的套接字之前需要调用一个引入Ws2_32.dll库的函数,否则服务器和客户端连接不上
#pragma comment(lib,"ws2_32.lib")
struct TEST
{
int test;
};
struct INFO
{
int year;
int month;
int day;
TEST test_struct;
double temperature;
};
int main(int argc, char* argv[])
{
struct sockaddr_in Server; //创建服务端sockaddr_in结构体
//建立一个数据报类型的UDP套接字 ******************//
int serverSocket = socket(PF_INET, SOCK_DGRAM, 0); //配置模式,
//设置服务器地址addrSrv和监听端口
Server.sin_family = AF_INET;
Server.sin_addr.s_addr = inet_addr("10.0.0.3"); //设置服务器主机ip地址(与接收方客户端的IP对应)
Server.sin_port = htons(8001); //发送用的端口,可以根据需要更改
//使用bind()函数绑定监听端口,将socket文件描述符sockSrv与地址类型变量(struct sockaddr_in )进行绑定
//int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
bind(serverSocket, (sockaddr *)&Server, sizeof(sockaddr));
//*******************************************************************************************//
struct sockaddr_in Client; //创建客户端sockaddr_in结构体
int clientSocket = socket(PF_INET, SOCK_DGRAM, 0); //配置模式,
Client.sin_family = AF_INET;
Client.sin_addr.s_addr = inet_addr("127.0.0.1");
Client.sin_port = htons(8001);
//*******************************************************************************************************
//创建发送和接受的结构体以及转化时用到的字符串,用作测试
INFO sendinfo,recvinfo;
char send_info_buf[100], recv_info_buf[100];
//创建发送和接受的字符串,用作测试
char sendbuf[100]= "Hello!", recvbuf[100];
//对发送的结构体成员赋值
sendinfo.year = 1999;
sendinfo.month = 11;
sendinfo.day = 20;
sendinfo.test_struct.test = 10;
sendinfo.temperature = 18.30;
//将发送的结构体sendinfo转化为字符串send_info_buf
memcpy(send_info_buf, &sendinfo, sizeof(sendinfo));
//********************************************************************************************************
while (1)
{
//发送
/*
int sendto(int s, const void* buf, int len, unsigned int flags, const struct sockaddr* to, int tolen);
s: 接收端的socket描述符
buf: UDP数据报缓存区(包含待发送数据)
len: UDP数据报长度
flags:调用方式标志位(一般设置为0)
to: 指向接收数据的主机地址信息的结构体,(sockaddr_in需类型转换);
tolen:同所指的结构体的长度
*/
//字符串发送和打印所用语句
//int t = sendto(clientSocket, sendbuf, sizeof(sendbuf), 0, (sockaddr*)&Client, sizeof(Client));
//cout << "sendto_len: "<<t << endl;//若发送失败。则返回-1
//结构体发送和打印所用语句
//int t = sendto(clientSocket, send_info_buf, sizeof(send_info_buf)+1 , 0, (sockaddr*)&Client, sizeof(Client));
//cout << "sendto_len: "<<t << endl<<endl;//若发送失败。则返回-1
//接收
/*
int recvfrom(int s, void *buf, int len, unsigned int flags,struct sockaddr *from, int *fromlen);
s: socket描述符
buf: UDP数据报缓存区(包含所要接受数据)
len: 缓存区长度
flags: 调用方式标志位(一般设置为0)
from: 指向发送数据的客户端地址信息的结构体,(sockaddr_in需类型转换);
fromlen:指针,指向from中结构体的长度
*/
socklen_t len = sizeof(sockaddr);
//字符串接收和打印所用语句
//recvfrom(serverSocket, recvbuf, sizeof(recvbuf), 0, (sockaddr*)&Server, &len);
//printf("%s\n\n", recvbuf);
//结构体接收和打印所用语句
recvfrom(serverSocket, recv_info_buf, sizeof(recv_info_buf), 0, (sockaddr *)&Server, &len);
memcpy(&recvinfo, recv_info_buf, sizeof(recv_info_buf));
cout << "Today is " << recvinfo.year << "." << recvinfo.month<<"." << recvinfo.day << endl;
cout << "The template is " << recvinfo.temperature << endl;
cout << "The struct-test number is " << recvinfo.test_struct.test << endl<<endl;
}
// close(clientSocket);
close(serverSocket);
return 0;
}
客户端代码,用来发送的消息
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <iostream>
#include <cstring>
#include <vector>
#include <unistd.h>
#include <typeinfo>
using namespace std;
//创建新的套接字之前需要调用一个引入Ws2_32.dll库的函数,否则服务器和客户端连接不上
#pragma comment(lib,"ws2_32.lib")
struct TEST
{
int test;
};
struct INFO
{
int year;
int month;
int day;
TEST test_struct;
double temperature;
};
int main(int argc, char* argv[])
{
struct sockaddr_in Server; //创建服务端sockaddr_in结构体
//建立一个数据报类型的UDP套接字 ******************//
int serverSocket = socket(PF_INET, SOCK_DGRAM, 0); //配置模式,
//设置服务器地址addrSrv和监听端口
Server.sin_family = AF_INET;
Server.sin_addr.s_addr = inet_addr("127.0.0.1"); //设置服务器主机ip地址(与接收方客户端的IP对应)
Server.sin_port = htons(8001); //发送用的端口,可以根据需要更改
//使用bind()函数绑定监听端口,将socket文件描述符sockSrv与地址类型变量(struct sockaddr_in )进行绑定
//int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
bind(serverSocket, (sockaddr *)&Server, sizeof(sockaddr));
//*************************************************************************************************************//
struct sockaddr_in Client; //创建客户端sockaddr_in结构体
int clientSocket = socket(PF_INET, SOCK_DGRAM, 0); //配置模式,
Client.sin_family = AF_INET;
Client.sin_addr.s_addr = inet_addr("10.0.0.3");
Client.sin_port = htons(8001);
//*************************************************************************************************************//
//创建发送和接受的结构体以及转化时用到的字符串,用作测试
INFO sendinfo,recvinfo;
char send_info_buf[100], recv_info_buf[100];
//创建发送和接受的字符串,用作测试
char sendbuf[100]= "How are you?", recvbuf[100];
//对发送的结构体成员赋值
sendinfo.year = 2021;
sendinfo.month = 7;
sendinfo.day = 27;
sendinfo.test_struct.test = 10;
sendinfo.temperature = 23.50;
//将发送的结构体sendinfo转化为字符串send_info_buf
memcpy(send_info_buf, &sendinfo, sizeof(sendinfo));
//************************************************************************************************************//
while (1)
{
usleep(2000000);
//发送
/*
int sendto(int s, const void* buf, int len, unsigned int flags, const struct sockaddr* to, int tolen);
s: Code1 接收端的socket描述符
buf: UDP数据报缓存区(包含待发送数据)
len: UDP数据报长度
flags: 调用方式标志位(一般设置为0)
to: 指向接收数据的主机地址信息的结构体,(sockaddr_in需类型转换);
tolen:所指的结构体的长度
*/
//字符串发送和打印所用语句
//int t = sendto(clientSocket, sendbuf, sizeof(sendbuf), 0, (sockaddr*)&Client, sizeof(Client));
//cout << "sendto_len: "<<t << endl<< endl;//若发送失败。则返回-1
//结构体发送和打印所用语句
int t = sendto(clientSocket, send_info_buf, sizeof(send_info_buf)+1 , 0, (sockaddr*)&Client, sizeof(Client));
cout << "sendto_len: "<<t << endl<< endl;//若发送失败。则返回-1
//接收
/*
int recvfrom(int s, void *buf, int len, unsigned int flags,struct sockaddr *from, int *fromlen);
s: socket描述符
buf: UDP数据报缓存区(包含所要接受数据)
len: 缓存区长度
flags: 调用方式标志位(一般设置为0)
from: 指向发送数据的客户端地址信息的结构体,(sockaddr_in需类型转换);
fromlen:指针,指向from中结构体的长度
*/
//字符串接收和打印所用语句
//socklen_t len = sizeof(sockaddr);
//recvfrom(serverSocket, recvbuf, sizeof(recvbuf), 0, (sockaddr*)&Server, &len);
//printf("%s\n\n", recvBuf);
//结构体接收和打印所用语句
//recvfrom(serverSocket, recv_info_buf, sizeof(recv_info_buf), 0, (sockaddr *)&Server, &len);
//memcpy(&recvinfo, recv_info_buf, sizeof(recv_info_buf));
//cout << "Today is " << recvinfo.year << "." << recvinfo.month<<"." << recvinfo.day << endl;
//cout << "The template is " << recvinfo.temperature << endl;
//cout << "The struct-test number is " << recvinfo.test_struct.test << endl<< endl;
}
close(clientSocket);
// close(serverSocket);
return 0;
}
进行编译
g++ client.cpp -o client -pthread
g++ server.cpp -o server -pthread
两个terminal分别运行,server端收到相应的信息。
./client
./server
两个terminal分别运行,server端收到相应的信息。将主机与机器人用网线链接,并设置主机ip与机器人为同一网段。主机端执行./server即可发现能够收到相应的信息。服务器端代码,用来监听客户端发送的消息。ssh登录进去后,执行./client。发送客户端可执行程序到机器人。客户端代码,用来发送的消息。
二、双机测试
将主机与机器人用网线链接,并设置主机ip与机器人为同一网段
发送客户端可执行程序到机器人
scp ./client user@10.0.0.34:/home/user
ssh登录进去后,执行./client
主机端执行./server即可发现能够收到相应的信息。
三、Python版本
主机代码server.py,更加简洁,不需要编译。主机端在不停的发送字符串
#!/usr/bin/env python
import socket
import time
address=('192.168.31.20',10000)
s=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
s.bind(address)
data,addr=s.recvfrom(2048)
print "got data from",addr
#if (i%50) <= 25:
sdata=" 0.2 0.0"
#else:
#sdata="-0.2 0.0"
for i in range(1000):
time.sleep(1/10.0)
if (i%50) <= 25:
sdata="-0.2 0.0"
else:
sdata="-0.2 0.0"
s.sendto(sdata,addr)
s.close()
客户端代码client.py,此处两段代码并不对应
#!/usr/bin/env python
import socket
addr=('192.168.31.247',10000)
s=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
while 1:
data=raw_input()
if not data:
break
s.sendto(data,addr)
s.close()
实际使用laikago的sdk,客户端为C++,其数据解析及实现代码如下:
/************************************************************************
Copyright (c) 2018-2019, Unitree Robotics.Co.Ltd. All rights reserved.
Use of this source code is governed by the MPL-2.0 license, see LICENSE.
************************************************************************/
#include "laikago_sdk/laikago_sdk.hpp"
#include <math.h>
#include <iostream>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#define DEST_PORT 10000 //端口号
#define DSET_IP_ADDRESS "192.168.31.20 " // server文件所在PC的IP
using namespace laikago;
// 16B
char send_buf[20] = "ready";
int send_num;
int recv_num;char recv_buf[20];
int sock_fd;
struct sockaddr_in addr_serv;
int len;
float f = 0.0, v = 0.0;
typedef union {
struct {
uint8_t R1 :1;
uint8_t L1 :1;
uint8_t start :1;
uint8_t select :1;
uint8_t R2 :1;
uint8_t L2 :1;
uint8_t F1 :1;
uint8_t F2 :1;
uint8_t A :1;
uint8_t B :1;
uint8_t X :1;
uint8_t Y :1;
uint8_t up :1;
uint8_t right :1;
uint8_t down :1;
uint8_t left :1;
} components;
uint16_t value;
} xKeySwitchUnion;
typedef union {
struct {
uint8_t useAppBtJoy :1; // use bluetooth joy or not
// uint8_t L1 :1;
// uint8_t start :1;
// uint8_t select :1;
// uint8_t R2 :1;
// uint8_t L2 :1;
// uint8_t F1 :1;
// uint8_t F2 :1;
} components;
uint8_t value;
} xAPPSwitchUnion;
// 40 Byte (now used 24B)
typedef struct {
uint8_t head[2];
xKeySwitchUnion btn;
float lx;
float rx;
float ry;
float L2;
float ly;
// xKeySwitchUnion app_btn; //this way is better
uint8_t useAppBtJoy;
} xRockerBtnDataStruct;
class CustomData
{
public:
CustomData(): udp(HIGH_CMD_LENGTH, HIGH_STATE_LENGTH){
this->motiontime = 0;
this->cmd = {0};
this->state = {0};
}
UDP udp;
HighCmd cmd;
HighState state;
int motiontime;
xRockerBtnDataStruct joy_wifi2Pc;
};
void UDPRecv(void *param)
{
UDP *data = (UDP *)param;
data->Recv();
}
double toDouble(const char* s, int start, int stop) {
unsigned long long int m = 1;
double ret = 0;
for (int i = stop; i >= start; i--) {
ret += (s[i] - '0') * m;
m *= 10;
}
return ret;
}
void RobotControl(void *param)
{
char *subString;
char *subStringNext;
float linvel;
float rotvel;
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);
recv_buf[4]='\0';
linvel = atof(recv_buf);
rotvel = atof(&recv_buf[5]);
printf("linevel: %f\n", linvel);
printf("rotvel: %f\n", rotvel);
CustomData *data = (CustomData *)param;
data->motiontime += 2;
data->udp.GetRecv(data->state);
// printf("%f\n", state.forwardSpeed);
data->cmd.forwardSpeed = 0.0f;
data->cmd.sideSpeed = 0.0f;
data->cmd.rotateSpeed = 0.0f;
data->cmd.bodyHeight = 0.0f;
data->cmd.mode = 0;
data->cmd.roll = 0;
data->cmd.pitch = 0;
data->cmd.yaw = 0;
// data->joy_wifi2Pc.useAppBtJoy = true;
// memcpy(data->cmd.AppBtCmd, &data->joy_wifi2Pc, 40);
data->cmd.mode = 2;
data->cmd.forwardSpeed = linvel; // -1 ~ +1
//printf("linevel: %f\n", data->cmd.forwardSpeed);
//data->cmd.rotateSpeed = rotvel; // turn
// if(data->motiontime>20000 ){
// data->cmd.mode = 1;
// data->joy_wifi2Pc.useAppBtJoy = false;
// memcpy(data->cmd.AppBtCmd, &data->joy_wifi2Pc, 40);
// }
data->udp.Send(data->cmd);
}
int main(void)
{
// added by Yuan Gao
/************************************************/
/* 建立udp socket */
sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
if(sock_fd < 0)
{
perror("socket");
exit(1);
}
/* 设置address */
// struct sockaddr_in addr_serv;
// int len;
memset(&addr_serv, 0, sizeof(addr_serv));
addr_serv.sin_family = AF_INET;
addr_serv.sin_addr.s_addr = inet_addr(DSET_IP_ADDRESS);
addr_serv.sin_port = htons(DEST_PORT);
len = sizeof(addr_serv);
// int send_num;
// int recv_num;
// char send_buf[20] = "ready";
// char recv_buf[20];
// printf("client send: %s\n", send_buf);
// send_num = sendto(sock_fd, send_buf, strlen(send_buf), 0, (struct sockaddr *)&addr_serv, len);
// if(send_num < 0)
// {
// perror("sendto error:");
// exit(1);
// }
// 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);
// close(sock_fd);
/************************************************/
// added by Yuan Gao
printf("client send: %s\n", send_buf);
send_num = sendto(sock_fd, send_buf, strlen(send_buf), 0, (struct sockaddr *)&addr_serv, len);
if(send_num < 0)
{
perror("sendto error:");
exit(1);
}
std::cout << "Control level is set to HIGH-level." << std::endl
<< "WARNING: Make sure the robot is standing on the ground." << std::endl
<< "Press Enter to continue..." << std::endl;
std::cin.ignore();
Control control(HIGHLEVEL);
CustomData custom;
control.InitCmdData(custom.cmd);
control.loop.RegistFunc("UDP/Send", RobotControl, &custom);
control.loop.RegistFunc("UDP/Recv", UDPRecv, &custom.udp);
control.loop.Start();
close(sock_fd);
return 0;
}