microsoft / AirSim

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

Expose setKinematics to API #879

Closed mathieu-reymond closed 6 years ago

mathieu-reymond commented 6 years ago

Hello everyone,

I've been playing around with AirSim for a few days now, it's great (I built it on Ubuntu 17.10 with UE 4.18 without any problems, just had to revert tornado to 4.x).

I ultimately want to perform rollouts from a specific state, so I want to be able to reset the vehicle with specific kinematics (I'm focusing on the Multirotor).

I found the VehiclePawnWrapper::setKinematics method, and my initial idea was to expose it to the API, by propagating it through VehiclePawnWrapperMultiRotorConnectorVehicleConnectorBaseMultirotorAPIMultirotorRpcLibServer, and then use the python client.

This did not work: I could change the kinematics, but they would not be updated when I moved the vehicle. So I wrote my own, in order to not change the kinematics_ pointer in VehiclePawnWrapper (I know it's ugly):

void VehiclePawnWrapper::setKinematics2(const msr::airlib::Kinematics::State* kinematics)
{
    //kinematics_ = kinematics;
    auto tmp = const_cast<msr::airlib::Kinematics::State*>(kinematics_);
    tmp->pose = kinematics->pose;
    tmp->twist = kinematics->twist;
    tmp->accelerations = kinematics->accelerations;
}

And this seems to work, my quadrocopter gets teleported to the correct position, with desired velocities and accelerations.

So my question is:

sytelus commented 6 years ago

Above shouldn't be used because it replaces the pointer that everyone else has reference to.

Could you please explain what do you mean by "perform rollouts from a specific state"? There is reset() API which will teleport quadrotor to original state.

You can't have setKinematics() API in AirSim because kinematics is computed by physics engine. The simulation means physics will decide the kinematics of the vehicle. You can assign the initial kinematics but that is pretty much it. The best way to do this is to create a new setting in settings.json, say "InitialKinematics" and then use those values in initialization here.

mathieu-reymond commented 6 years ago

Thanks for the quick response!

By rollout (it's reinforcement learning jargon) I mean that I want to perform a sequence of actions from a specific state, thus getting a quadrotor trajectory. The thing is that I want to sample multiple of those trajectories, all from the exact same startpoint. And by startpoint I don't mean only the same position and orientation (I could do that using simSetPose, which is already provided by the API), but also the same initial kinematics.

So I know the kinematics will be computed by the physics engine (and will observe those changes on the sampled trajectories), but I need to be able to reset the quadrotor to a specific state.

I guess having an "initialKinematics" in settings.json is a first step, but ideally I would like to be able to reset programmatically, to a provided kinematic state (I hope this makes sense).

So now I wrote this hacky setKinematics2 method, but changing reset() to reset(&Kinematics::State) would do exactly the same in my setting.

sytelus commented 6 years ago

In that case may be reset() API should be extended to accept parameter for initial kinematics. Should be easy to do... It would be great to have PR if you decide to implement this.

mathieu-reymond commented 6 years ago

Alright, I'll have a go at it :)

But back to my original question: does changing the kinematics mid-flight (like I did with my hacky solution) break anything I'm not aware of? Is the physics engine still valid?

Imagine I fly, say, to position p1, then record the kinematics kp1, then fly from there to p2 by following a sequence of actions (a1, ..., an) (which could be angles or velocities for example). If I then reset the quadrotor to kp1, thus teleporting me to p1, will executing the same sequence of actions (a1, ..., an) lead me to p2 again? Or are there external factors (air pressure?) that got corrupted? If so, is there any way I can recover them?

Edit: Currently implementing the reset, I can build the project perfectly (using build.sh), but when I open the UE editor, I get this:

The following modules are missing or built with a different engine version: libUE4Editor-Blocks.so libUE4Editor-AirSim.so Would you like to rebuild them now?

That's normal, but then I get:

Blocks could not be compiled. Try rebuilding from source manually.

Any idea how to get more useful information than this?

sytelus commented 6 years ago

If your initial kinematics is same then performing same sequence of action would yield same final kinematics. There is obviously some randomization because of noise etc but things should be approximately same.

For the error, I would say look at the console window. Failing that look in to Saved folder for log file. The log file almost always has full error.

melange396 commented 6 years ago

I also have a need for this... Can I help you out in any way?

mathieu-reymond commented 6 years ago

Sorry for the late reply. First of all thank you for pointing me to the log file. It did not have the full error, but helped me figure out what was wrong. The environment compiles now too.

I did a fork so you can look at the code (the diff with master is visible in this commit). The multirotor gets teleported to the correct position, but I've probably missed something, because from there it starts to fly off somewhere. Here is a gif to show what I mean: reset_kinematics

  1. I fly off to a position where I hover for a few seconds, then save the kinematics
  2. I fly to another position
  3. I reset to the saved kinematics. It works, but then the multirotor starts flying upwards (I was expecting it to hover) You can reproduce this by executing this simple python script:
    
    from AirSimClient import *
    from time import sleep

c = MultirotorClient() c.enableApiControl(True)

c.moveToPosition(10, 10, -10, 5) sleep(5) k = c.getMultirotorState().kinematics_true c.moveToPosition(0, 0, -10, 5) sleep(5) c.resetWith(k)

sytelus commented 6 years ago

We don't recommend changing kinematics like this, however its good to see lot of progress!