0

我正在尝试通过 Arduino 无线控制机器人(使用计算机上的 X360 控制器),这需要非常低的延迟。出于这个原因,我选择了 Wifi(事实上我将流式传输视频),经过一些测试,结果发现我在使用 TCP 时有很大的延迟。这正常吗(54Mbits/s,不应该!)?我怎样才能减少它是可控的?

服务器代码(Arduino 草图):

#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 先生

4

3 回答 3

4

TCP 连接需要更多的数据包来提供可靠的数据传输。TCP 不意味着低延迟,它意味着可靠地传输数据。例如,如果您要发送一个文件,您需要接收所有数据包并以正确的顺序拼凑在一起。

您会看到带宽和延迟不相关的事实。大多数流视频系统预先缓冲数据以提供传输流中没有中断的假象。潜在的行为是传输延迟可能是抖动的,但缓冲的数据保持连续流的感知。

对于您的应用程序,请考虑使用 UDP。

Arduino 上的 UDP

TCP是流,UDP是小消息。您的决定将围绕以下问题:

如果数据包已发送但从未收到,会有什么影响?

在控制器输入的情况下,最好简单地忽略丢失的数据并接收下一次传输。我从您的问题中推测您将重复发送控制器的状态(从左到右?)如果是这样,UDP 是您的选择。如果您选择 TCP,则行为将是在发送下一个数据之前不断重试以恢复丢失的数据。在您的用例中,您不妨发送下一个控制器状态,而不是尝试恢复流。

于 2013-06-10T06:38:23.343 回答
2

你检查过那些相当笼统的评论吗?

1) 你检查过你的 Wi-Fi 频谱吗?重叠的通道会导致丢包。这些数据包将在一个小的额外延迟下重新传输。一个很好的帮助工具:http ://www.metageek.net/products/inssider/

2)您的 Arduino 是否没有被网络上其他设备的广播数据包淹没。也许您的网络堆栈正忙于检查和忽略广播数据包。TCP 连接的数据包可以被丢弃,也可以重新传输。为您的 PC 和 Arduino 尝试一个私有 AP 并查看性能。

于 2013-06-09T21:48:50.617 回答
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(&centralW);

    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();
  }
}
于 2013-06-10T15:00:14.057 回答