microsoft / AirSim

Open source simulator for autonomous vehicles built on Unreal Engine / Unity, from Microsoft AI & Research
https://microsoft.github.io/AirSim/
Other
16.28k stars 4.53k forks source link

RPC Error Throwed When Running DroneShell #188

Closed JohnDing1995 closed 7 years ago

JohnDing1995 commented 7 years ago

When I tried to run droneshell, the shell command doesn't work. It always throwed exception as follow image The the Failsafe mode will be opened,and connection lost

Can someone tell me why? MUCH APPRECIATE! BTW. I tried requestcontrol before excuted moveto* command,but the problem still exists.

lovettchris commented 7 years ago

Yes, request control is optional now, the move commands automatically request control. But you have found an issue that we need to think about some more. The timeout is coming from the RPC layer, but the command takes a "duration" parameter which just happens to also default to 5 seconds, but the timeout happens just before that. But if user passes 20 seconds for duration, the RPC stack shouldn't complain. The reason RPC channels normally have small timeouts is because if the channel gets stuck you can't do anything else. For example, you might want to start moving by velocity in a certain direction for 30 seconds, but you might change your mind and want to 'hover' so it doesn't hit something. But with the current blocking RPC call that is not possible. I will discuss with Shital a change to the RPC Client API to make it non-blocking on all calls to allow for these scenarios. Previous he has pushed back saying "async" programming model is too hard (promise/continuation, etc). But I will make another pitch for async callback model that tells client when the command is actually finished in case you want to find that out too, but doesn't block for the "duration" of the command, in case you want to do something else. When you do something else it will automatically cancel the previous command, that cancellation model is already there, so no change to that. Make sense ?

JohnDing1995 commented 7 years ago

@lovettchris Thanks for your reply. You mean that the following command will be stucked before last command's duration is over?But , In practise, I can break the move command anytime by execute another command like "hover" My situation is that, even the move command is the first command I executed after I armed the drone, still, the rpc throw will be thrown, But fortunately, the drone can as I commanded. But, The real trouble which the rpc error brings to me is: When I tried to execute several move command in sequence at HelloDrone(which was modified by myself, for dataset collection), same error happens, and the program will interrupt. Like this: Aaron Swartz The text insides the window means that an unhandled exception "rpc::timeout" exists in HelloDrone.exe.

This problem makes it impossible to collect a complete and continues dataset for SLAM algorithm, because the program interrupt after excute each command I wrote in my HelloDrone. Thanks for your help!

appendix: My HelloDrone

#include <iostream>
#include <chrono>
#include "rpc/RpcLibClient.hpp"
#include "controllers/DroneControllerBase.hpp"
#include <thread>
#include <fstream>
#include <windows.h> 
STRICT_MODE_OFF
#ifndef RPCLIB_MSGPACK
#define RPCLIB_MSGPACK clmdep_msgpack
#endif // !RPCLIB_MSGPACK
#include "rpc/rpc_error.h"
STRICT_MODE_ON
std::mutex mtx;

void getImage()
{
    using namespace std;
    using namespace msr::airlib;
    using std::string;
    string timeStr;
    msr::airlib::RpcLibClient client;
    for (int j = 0; j <= 10000; j++)
    {
        client.setImageTypeForCamera(0, DroneControllerBase::ImageType::Scene);
        auto image = client.getImageForCamera(0, DroneControllerBase::ImageType::Scene);
        char a[10];
        string str;
        itoa(j, a, 10);
        str = a;
        SYSTEMTIME t;
        GetLocalTime(&t);
        char str1[20];
        char str2[6];
        string timeString;
        sprintf_s(str1, "%4d%02d%02d%02d%02d%02d", t.wYear, t.wMonth, t.wDay, t.wHour, t.wMinute, t.wSecond);
        sprintf_s(str2, "%04d", t.wMilliseconds);
        string timeFront = str1;
        string timeBack = str2;
        cout << "PNG images received bytes: " << image.size() << endl;
        //cout << "Press Enter to save image" << endl; cin.get();
        mtx.lock();
        ofstream file("c:\\temp\\"+timeFront+"."+timeBack+".png", ios::binary);
        file.write((char*)image.data(), image.size());

        file.close();
        _sleep(100);
        mtx.unlock();
    }

}

int main() 
{
    using namespace std;
    using namespace msr::airlib;

    // This assumes you are running DroneServer already on the same machine.
    // DroneServer must be running first.
    msr::airlib::RpcLibClient client;

    try {
        cout << "Waiting for drone to get a GPS position..." << endl;
        GeoPoint pos = client.getGpsLocation();
        while (pos.altitude == 0 && pos.latitude == 0 && pos.longitude == 0)
        {
            std::this_thread::sleep_for(std::chrono::duration<double>(1));
            pos = client.getGpsLocation();
        }

        cout << "Great, we have a GPS position: lat=" << pos.latitude << ", lon=" << pos.longitude << ", alt=" << pos.altitude << endl;

        cout << "Press Enter to arm the drone" << endl; cin.get();
        client.armDisarm(true);

        cout << "Press Enter to takeoff" << endl; cin.get();
        float takeoffTimeout = 10;
        client.takeoff(takeoffTimeout);

        // switch to explicit hover mode so that this is the fallback when 
        // move* commands are finished.
        std::this_thread::sleep_for(std::chrono::duration<double>(5));
        client.hover();
        cout << "Press Enter to request offboard control" << endl; cin.get();
        client.setOffboardMode(true);

        //cout << "Press Enter to Rotate" << endl; cin.get();

        auto position = client.getPosition();
        float z = position.z(); // 5 meters above current position (NED coordinate system).  
        const float speed = 5.0f;
        const float size = 50.0f;
        const float duration = size / speed;
//      int laps = 5;

        cout << "moveByVelocityZ(" << speed << ", 0, " << z << "," << duration << ")" << endl;
        client.moveByVelocityZ(speed, 0, z, duration);
        cout << "moveByVelocityZ(0, " << speed << "," << z << "," << duration << ")" << endl;
        client.moveByVelocityZ(0, speed, z, duration);
        cout << "moveByVelocityZ(" << -speed << ", 0, " << z << "," << duration << ")" << endl;
        client.moveByVelocityZ(-speed, 0, z, duration);
        cout << "moveByVelocityZ(0, " << -speed << "," << z << "," << duration << ")" << endl;
        client.moveByVelocityZ(0, -speed, z, duration);

        client.hover();

        //first.join();

    }
    catch (rpc::rpc_error&  e) {
        ofstream fout("output.txt");
        std::string msg = e.get_error().as<std::string>();
        cout << "Exception raised by the API, something went wrong." << endl << msg << endl;
        fout << msg << endl;
        fout << flush; 
        fout.close();
    }

    return 0;
}
lovettchris commented 7 years ago

Yes, consider this a bug in the current version, we are working on a fix. The fix will make all client calls asynchronous, meaning they will return immediately, allowing you to do new commands that interrupt the currently executing command.

Timebutt commented 7 years ago

Just as a side note: I am also waiting for this fix. Will be watching this issue to help verify the solution once it's added to the code base.

lovettchris commented 7 years ago

Ok, fix is now in, the API now always takes one of two possible parameters: float duration or: float max_wait_seconds

For example, like moveByVelocity takes float duration, and returns control immediately. User can therefore choose to sleep for this duration, or they can change their mind and call something else to cancel the moveByVelocity.

Similarly, takeoff, land, moveOnPath, moveToPosition, moveToZ, and so on take float max_wait_seconds which means the API will block this amount of time waiting for command to be successfully completed. If you want to wait for ever pass a big number. But if you want to be able to interrupt even these commands pass 0 and you can do something else or sleep as long as you want, etc. We would not recommend interrupting takeoff/land on a real drone, of course, as the results may be unpredictable.