From bc84ade4f94c529b34376a8d1791a3bbac22ba67 Mon Sep 17 00:00:00 2001 From: Shital Shah Date: Thu, 21 Jun 2018 21:06:05 -0700 Subject: [PATCH] added setSimSetObjectPose https://github.com/Microsoft/AirSim/pull/1161 --- AirLib/include/api/RpcLibClientBase.hpp | 3 +- AirLib/include/api/WorldSimApiBase.hpp | 2 + AirLib/src/api/RpcLibClientBase.cpp | 4 ++ AirLib/src/api/RpcLibServerBase.cpp | 3 + PythonClient/airsim/client.py | 2 + PythonClient/airsim/types.py | 3 + PythonClient/computer_vision/objects.py | 67 ++++++++++++++++-- PythonClient/computer_vision/setup_path.py | 20 +++--- PythonClient/multirotor/setup_path.py | 20 +++--- .../FlyingCPP/Maps/FlyingExampleMap.umap | Bin 9560409 -> 9573404 bytes Unreal/Plugins/AirSim/Source/WorldSimApi.cpp | 20 +++++- Unreal/Plugins/AirSim/Source/WorldSimApi.h | 1 + 12 files changed, 117 insertions(+), 28 deletions(-) diff --git a/AirLib/include/api/RpcLibClientBase.hpp b/AirLib/include/api/RpcLibClientBase.hpp index a4f7ac0eac..def8a852a7 100644 --- a/AirLib/include/api/RpcLibClientBase.hpp +++ b/AirLib/include/api/RpcLibClientBase.hpp @@ -37,7 +37,8 @@ class RpcLibClientBase { void simContinueForTime(double seconds); Pose simGetObjectPose(const std::string& object_name) const; - + bool simSetObjectPose(const std::string& object_name, const Pose& pose, bool teleport = true); + //task management APIs void cancelLastTask(const std::string& vehicle_name = ""); virtual RpcLibClientBase* waitOnLastTask(bool* task_result = nullptr, float timeout_sec = Utils::nan()); diff --git a/AirLib/include/api/WorldSimApiBase.hpp b/AirLib/include/api/WorldSimApiBase.hpp index 0084a1f758..806dc04699 100644 --- a/AirLib/include/api/WorldSimApiBase.hpp +++ b/AirLib/include/api/WorldSimApiBase.hpp @@ -26,6 +26,8 @@ class WorldSimApiBase { const std::string& message_param = "", unsigned char severity = 0) = 0; virtual Pose getObjectPose(const std::string& object_name) const = 0; + virtual bool setObjectPose(const std::string& object_name, const Pose& pose, bool teleport) = 0; + }; diff --git a/AirLib/src/api/RpcLibClientBase.cpp b/AirLib/src/api/RpcLibClientBase.cpp index d2eccd2767..d996aaa33a 100644 --- a/AirLib/src/api/RpcLibClientBase.cpp +++ b/AirLib/src/api/RpcLibClientBase.cpp @@ -213,6 +213,10 @@ msr::airlib::Pose RpcLibClientBase::simGetObjectPose(const std::string& object_n { return pimpl_->client.call("simGetObjectPose", object_name).as().to(); } +bool RpcLibClientBase::simSetObjectPose(const std::string& object_name, const msr::airlib::Pose& pose, bool teleport) +{ + return pimpl_->client.call("simSetObjectPose", object_name, RpcLibAdapatorsBase::Pose(pose), teleport).as(); +} CameraInfo RpcLibClientBase::simGetCameraInfo(const std::string& camera_name, const std::string& vehicle_name) const { diff --git a/AirLib/src/api/RpcLibServerBase.cpp b/AirLib/src/api/RpcLibServerBase.cpp index 71c8105256..d14a5dee0a 100644 --- a/AirLib/src/api/RpcLibServerBase.cpp +++ b/AirLib/src/api/RpcLibServerBase.cpp @@ -148,6 +148,9 @@ RpcLibServerBase::RpcLibServerBase(ApiProvider* api_provider, const std::string& const auto& pose = getWorldSimApi()->getObjectPose(object_name); return RpcLibAdapatorsBase::Pose(pose); }); + pimpl_->server.bind("simSetObjectPose", [&](const std::string& object_name, const RpcLibAdapatorsBase::Pose& pose, bool teleport) -> bool { + return getWorldSimApi()->setObjectPose(object_name, pose.to(), teleport); + }); pimpl_->server.bind("simGetGroundTruthKinematics", [&](const std::string& vehicle_name) -> RpcLibAdapatorsBase::KinematicsState { const Kinematics::State& result = *getVehicleSimApi(vehicle_name)->getGroundTruthKinematics(); diff --git a/PythonClient/airsim/client.py b/PythonClient/airsim/client.py index 148998ae27..4d6a4362bd 100644 --- a/PythonClient/airsim/client.py +++ b/PythonClient/airsim/client.py @@ -104,6 +104,8 @@ def simGetVehiclePose(self, vehicle_name = ''): def simGetObjectPose(self, object_name): pose = self.client.call('simGetObjectPose', object_name) return Pose.from_msgpack(pose) + def simSetObjectPose(self, object_name, pose, teleport = True): + return self.client.call('simSetObjectPose', object_name, pose, teleport) def simSetSegmentationObjectID(self, mesh_name, object_id, is_name_regex = False): return self.client.call('simSetSegmentationObjectID', mesh_name, object_id, is_name_regex) diff --git a/PythonClient/airsim/types.py b/PythonClient/airsim/types.py index faccbf702a..0469296db5 100644 --- a/PythonClient/airsim/types.py +++ b/PythonClient/airsim/types.py @@ -47,6 +47,9 @@ def __init__(self, x_val = np.float32(0), y_val = np.float32(0), z_val = np.floa self.y_val = y_val self.z_val = z_val + def __add__(self, other): + return Vector3r(self.x_val + other.x_val, self.y_val + other.y_val, self.z_val + other.z_val) + class Quaternionr(MsgpackMixin): w_val = np.float32(0) diff --git a/PythonClient/computer_vision/objects.py b/PythonClient/computer_vision/objects.py index e4e2f4b09e..09f61831cb 100644 --- a/PythonClient/computer_vision/objects.py +++ b/PythonClient/computer_vision/objects.py @@ -9,9 +9,64 @@ client = airsim.VehicleClient() client.confirmConnection() -#Go to object in Unreal Editor, click on it and then look for Tags property. -#Add a tag "MyObject" (without quotes), save and the query using following line -#see more about tags here: https://answers.unrealengine.com/questions/543807/whats-the-difference-between-tag-and-tag.html -pose = client.simGetObjectPose("MyObject"); -print("Position: %s, Orientation: %s" % (pprint.pformat(pose.position), - pprint.pformat(pose.orientation))) +# objects can be named in two ways: +# 1. In UE Editor, select and object and change its name to something else. Note that you must *change* its name because +# default name is auto-generated and varies from run-to-run. +# 2. OR you can do this: In UE Editor select the object and then go to "Actor" section, click down arrow to see "Tags" property and add a tag there. +# +# The simGetObjectPose and simSetObjectPose uses first object that has specified name OR tag. +# more info: https://answers.unrealengine.com/questions/543807/whats-the-difference-between-tag-and-tag.html +# https://answers.unrealengine.com/revisions/790629.html + +# below works in Blocks environment + +#------------------------------------ Get current pose ------------------------------------------------ + +# search object by name: +pose1 = client.simGetObjectPose("OrangeBall"); +print("OrangeBall - Position: %s, Orientation: %s" % (pprint.pformat(pose1.position), + pprint.pformat(pose1.orientation))) + +# search another object by tag +pose2 = client.simGetObjectPose("PulsingCone"); +print("PulsingCone - Position: %s, Orientation: %s" % (pprint.pformat(pose2.position), + pprint.pformat(pose2.orientation))) + +# search non-existent object +pose3 = client.simGetObjectPose("Non-Existent"); # should return nan pose +print("Non-Existent - Position: %s, Orientation: %s" % (pprint.pformat(pose3.position), + pprint.pformat(pose3.orientation))) + + +#------------------------------------ Set new pose ------------------------------------------------ + +# here we move with teleport enabled so collisions are ignored +pose1.position = pose1.position + airsim.Vector3r(-2, -2, -2) +success = client.simSetObjectPose("OrangeBall", pose1, True); +airsim.wait_key("OrangeBall moved. Success: %i" % (success)) + +# here we move with teleport enabled so collisions are not ignored +pose2.position = pose2.position + airsim.Vector3r(3, 3, -2) +success = client.simSetObjectPose("PulsingCone", pose2, False); +airsim.wait_key("PulsingCone moved. Success: %i" % (success)) + +# move non-existent object +success = client.simSetObjectPose("Non-Existent", pose2); # should return nan pose +airsim.wait_key("Non-Existent moved. Success: %i" % (success)) + +#------------------------------------ Get new pose ------------------------------------------------ + + +pose1 = client.simGetObjectPose("OrangeBall"); +print("OrangeBall - Position: %s, Orientation: %s" % (pprint.pformat(pose1.position), + pprint.pformat(pose1.orientation))) + +# search another object by tag +pose2 = client.simGetObjectPose("PulsingCone"); +print("PulsingCone - Position: %s, Orientation: %s" % (pprint.pformat(pose2.position), + pprint.pformat(pose2.orientation))) + +# search non-existent object +pose3 = client.simGetObjectPose("Non-Existent"); # should return nan pose +print("Non-Existent - Position: %s, Orientation: %s" % (pprint.pformat(pose3.position), + pprint.pformat(pose3.orientation))) \ No newline at end of file diff --git a/PythonClient/computer_vision/setup_path.py b/PythonClient/computer_vision/setup_path.py index 2232105778..362113fea8 100644 --- a/PythonClient/computer_vision/setup_path.py +++ b/PythonClient/computer_vision/setup_path.py @@ -35,18 +35,18 @@ def getParentDir(): @staticmethod def addAirSimModulePath(): # if airsim module is installed then don't do anything else - import pkgutil - airsim_loader = pkgutil.find_loader('airsim') - if airsim_loader is not None: - return - - grand_parent = SetupPath.getParentDir() - if grand_parent != '': - airsim_path = os.path.join(grand_parent, 'airsim') + #import pkgutil + #airsim_loader = pkgutil.find_loader('airsim') + #if airsim_loader is not None: + # return + + parent = SetupPath.getParentDir() + if parent != '': + airsim_path = os.path.join(parent, 'airsim') client_path = os.path.join(airsim_path, 'client.py') if os.path.exists(client_path): - sys.path.insert(0, grand_parent) + sys.path.insert(0, parent) else: - logging.warning("airsim module not found in grand parent folder. Using default installed module (if any).") + logging.warning("airsim module not found in parent folder. Using installed package (pip install airsim).") SetupPath.addAirSimModulePath() diff --git a/PythonClient/multirotor/setup_path.py b/PythonClient/multirotor/setup_path.py index 2232105778..362113fea8 100644 --- a/PythonClient/multirotor/setup_path.py +++ b/PythonClient/multirotor/setup_path.py @@ -35,18 +35,18 @@ def getParentDir(): @staticmethod def addAirSimModulePath(): # if airsim module is installed then don't do anything else - import pkgutil - airsim_loader = pkgutil.find_loader('airsim') - if airsim_loader is not None: - return - - grand_parent = SetupPath.getParentDir() - if grand_parent != '': - airsim_path = os.path.join(grand_parent, 'airsim') + #import pkgutil + #airsim_loader = pkgutil.find_loader('airsim') + #if airsim_loader is not None: + # return + + parent = SetupPath.getParentDir() + if parent != '': + airsim_path = os.path.join(parent, 'airsim') client_path = os.path.join(airsim_path, 'client.py') if os.path.exists(client_path): - sys.path.insert(0, grand_parent) + sys.path.insert(0, parent) else: - logging.warning("airsim module not found in grand parent folder. Using default installed module (if any).") + logging.warning("airsim module not found in parent folder. Using installed package (pip install airsim).") SetupPath.addAirSimModulePath() diff --git a/Unreal/Environments/Blocks/Content/FlyingCPP/Maps/FlyingExampleMap.umap b/Unreal/Environments/Blocks/Content/FlyingCPP/Maps/FlyingExampleMap.umap index 36a1ec38b5f6c9b0a40c11a3809423fe4d927377..f2d106570bb541c4c7b8c52c3b25a2d1133f0f7d 100644 GIT binary patch delta 122775 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 Cu^!3* delta 118599 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, FString(object_name.c_str())); - result = actor ? simmode_->getGlobalNedTransform().toLocalNed(actor->GetActorTransform()) + result = actor ? simmode_->getGlobalNedTransform().toGlobalNed(FTransform(actor->GetActorRotation(), actor->GetActorLocation())) : Pose::nanPose(); }, true); return result; } +bool WorldSimApi::setObjectPose(const std::string& object_name, const WorldSimApi::Pose& pose, bool teleport) +{ + bool result; + UAirBlueprintLib::RunCommandOnGameThread([this, &object_name, &pose, teleport, &result]() { + FTransform actor_transform = simmode_->getGlobalNedTransform().fromGlobalNed(pose); + AActor* actor = UAirBlueprintLib::FindActor(simmode_, FString(object_name.c_str())); + if (actor) { + if (teleport) + result = actor->SetActorLocationAndRotation(actor_transform.GetLocation(), actor_transform.GetRotation(), false, nullptr, ETeleportType::TeleportPhysics); + else + result = actor->SetActorLocationAndRotation(actor_transform.GetLocation(), actor_transform.GetRotation(), true); + } + else + result = false; + }, true); + return result; +} + diff --git a/Unreal/Plugins/AirSim/Source/WorldSimApi.h b/Unreal/Plugins/AirSim/Source/WorldSimApi.h index c07ac864eb..c53887ac3f 100644 --- a/Unreal/Plugins/AirSim/Source/WorldSimApi.h +++ b/Unreal/Plugins/AirSim/Source/WorldSimApi.h @@ -25,6 +25,7 @@ class WorldSimApi : public msr::airlib::WorldSimApiBase { const std::string& message_param = "", unsigned char severity = 0) override; virtual Pose getObjectPose(const std::string& object_name) const override; + virtual bool setObjectPose(const std::string& object_name, const Pose& pose, bool teleport) override; private: ASimModeBase* simmode_;