我正在尝试通过Arduino(使用计算机上的X360控制器)无线控制机器人,这需要非常低的延迟。我之所以选择Wifi(以及我将要播放视频的事实),经过一点点测试后发现我使用TCP有很大的延迟。这是正常的(54Mbits / s,它不应该!)?如何减少它是可控的?
服务器代码(Arduino sketch):
#include <SPI.h>
#include <Ethernet.h>
byte mac[] = { 0x90, 0xA2, 0xDA, 0x0D, 0x48, 0x0D };
byte ip[] = { 192, 168, 0, 11 };
byte gateway[] = { 192, 168, 0, 254 };
byte subnet[] = { 255, 255, 255, 0 };
byte localPort = 99;
EthernetServer server = EthernetServer(localPort);
void setup()
{
// initialize the ethernet device
Ethernet.begin(mac, ip, gateway, subnet);
Serial.begin(9600);
// start listening for clients
server.begin();
Serial.println("Server ready");
}
void loop()
{
// if an incoming client connects, there will be bytes available to read:
EthernetClient client = server.available();
if (client == true) {
Serial.println("Received:");
byte received = 0;
while((received = client.read()) != -1) {
Serial.println(received);
server.write(received);
}
Serial.println("Over\n");
}
}
客户端代码(PC,QtCreator):
#include <QTextStream>
#include <QTCPSocket>
QString arduinoIP = "192.168.0.11";
char arduinoPort = 99;
int main(void)
{
QTcpSocket socket;
QTextStream in(stdin);
QTextStream out(stdout);
out << "Connection... "; out.flush();
socket.connectToHost(arduinoIP, arduinoPort);
if(!socket.waitForConnected(5000)) {
out << socket.errorString() << "\n";
}
else {
out << "OK\n"; out.flush(); //I don't know why \n doesn't flush
out << "Type a message to send to the Arduino or quit to exit\n"; out.flush();
QString command;
in >> command;
while(command != "quit") {
QByteArray bufOut = command.toUtf8();
socket.write(bufOut);
socket.waitForReadyRead(1000); //Wait for answer (temp)
out << "Answer: " << socket.readAll() << "\n";
}
}
return 0;
}
提前感谢您的帮助。
此致 Mystère先生
答案 0 :(得分:4)
TCP连接需要更多数据包才能提供可靠的数据传输。 TCP不用于低延迟,它用于可靠地传输流数据。例如,如果要发送文件,则需要接收所有数据包并按正确顺序拼接在一起。
您看到带宽和延迟无关的事实。大多数流式视频系统预先缓冲数据以提供传输流中没有中断的错觉。潜在的行为是传输延迟可能是紧张的,但缓冲的数据可以保持连续流的感知。
对于您的应用程序,请考虑使用UDP。
TCP是一个流,UDP用于小消息。您的决定将围绕这个问题:
如果发送但未收到数据包会有什么影响?
在控制器输入的情况下,最好简单地忽略丢失的数据并接收下一次传输。我从你的问题中假设你将重复发送控制器的状态(向上向左下?)如果是这样,UDP就是你的选择。如果选择TCP,则行为将是在发送下一个数据之前继续重试以恢复丢失的数据。在您的用例中,您也可以发送下一个控制器状态,而不是尝试恢复流。
答案 1 :(得分:2)
您是否检查了那些非常一般性的评论?
1)您是否检查过Wi-Fi频谱?重叠通道会导致数据包丢失。这些数据包将以较小的额外延迟重新传输。一个很好的帮助工具:http://www.metageek.net/products/inssider/
2)您的Arduino是否未充斥您网络上其他设备的广播数据包。也许您的网络堆栈非常忙于检查和忽略广播数据包。可以删除并重新传输TCP连接的数据包。尝试为您的PC和Arduino使用私有AP,并查看性能。
答案 2 :(得分:0)
UDP非常快,我使用了这个测试代码:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QTimer>
#include <QVBoxLayout>
#include <QSpinBox>
#include <QtNetwork/QUdpSocket>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
public slots:
void onTimer();
private:
QTimer timer;
QVBoxLayout layout;
QWidget centralW;
QSpinBox sendBox;
QSpinBox receiveBox;
QUdpSocket *socket;
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
connect(&timer, SIGNAL(timeout()), this, SLOT(onTimer()));
timer.start(100);
sendBox.setMaximum(1000);
layout.addWidget(&sendBox);
receiveBox.setMaximum(1000);
layout.addWidget(&receiveBox);
centralW.setLayout(&layout);
setCentralWidget(¢ralW);
socket = new QUdpSocket(this);
}
MainWindow::~MainWindow()
{
}
void MainWindow::onTimer() {
QByteArray datagram = QByteArray::number(sendBox.value());
socket->writeDatagram(datagram.data(), datagram.size(), QHostAddress("192.168.0.11"), 99);
if(socket->hasPendingDatagrams()) {
datagram.resize(socket->pendingDatagramSize());
socket->readDatagram(datagram.data(), datagram.size());
receiveBox.setValue(QString(datagram.data()).toInt());
}
}
在服务器端:
#include <SPI.h> // needed for Arduino versions later than 0018
#include <Ethernet.h>
#include <EthernetUdp.h> // UDP library from: bjoern@cs.stanford.edu 12/30/2008
byte mac[] = { 0x90, 0xA2, 0xDA, 0x0D, 0x48, 0x0D };
byte ip[] = { 192, 168, 0, 11 };
byte gateway[] = { 192, 168, 0, 254 };
byte subnet[] = { 255, 255, 255, 0 };
byte localPort = 99;
EthernetUDP Udp;
void setup() {
Ethernet.begin(mac,ip);
Udp.begin(localPort);
Serial.begin(9600);
}
void loop() {
// if there's data available, read a packet
int packetSize = Udp.parsePacket();
if(packetSize)
{
static char buffer[UDP_TX_PACKET_MAX_SIZE]; //buffer to hold incoming packet,
//Udp.read does not erase the rest of the buffer, without that we would
//get 989 instead of 98 after having entered 999 for example
for(int i = 0; i < UDP_TX_PACKET_MAX_SIZE; ++i) buffer[i] = 0;
Udp.read(buffer,UDP_TX_PACKET_MAX_SIZE);
Serial.println(buffer);
// send a reply, to the IP address and port that sent us the packet we received
Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
Udp.write(buffer);
Udp.endPacket();
}
}