Skip to content

Commit d664de0

Browse files
Jackie ChanjonyMarino
authored andcommitted
Add payload lowering
1 parent 1b9c930 commit d664de0

File tree

7 files changed

+121
-11
lines changed

7 files changed

+121
-11
lines changed

client/python/example_user_scripts/payload.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ async def main():
7070

7171
# Command the drone to move up in NED coordinate system at 1 m/s for 3 seconds
7272
move_up_task = await drone.move_by_velocity_async(
73-
v_north=0.0, v_east=0.0, v_down=-1.0, duration=3.0
73+
v_north=0.0, v_east=0.0, v_down=-1.0, duration=2.0
7474
)
7575
projectairsim_log().info("Move-Up invoked")
7676

@@ -108,27 +108,42 @@ async def main():
108108
await move_north_task
109109
projectairsim_log().info("Move-North completed")
110110

111-
# Command the drone to move down in NED coordinate system at 1 m/s for 3.5 seconds
111+
# Command the drone to move down in NED coordinate system at 1 m/s for 3.0 seconds
112112
move_down_task = await drone.move_by_velocity_async(
113113
v_north=0.0, v_east=0.0, v_down=1.0, duration=3.0
114114
)
115115
projectairsim_log().info("Move-Down invoked")
116116
await move_down_task
117117
projectairsim_log().info("Move-Down completed")
118118

119+
await asyncio.sleep(1)
120+
121+
# Command the drone to lower payload
122+
projectairsim_log().info("Lowering payload")
123+
success = drone.lower_payload(15)
124+
projectairsim_log().info(f"Payload was successfully lowered: {success}")
125+
119126
# Command the drone to detach payload
120127
projectairsim_log().info("Detaching payload")
121128
success = drone.detach_payload()
122129
projectairsim_log().info(f"Payload was successfully detached: {success}")
123130

124-
# Command the drone to move north in NED coordinate system at 1 m/s for 1 second
131+
# Command the drone to move north-east in NED coordinate system at 1 m/s for 1 second
125132
move_north_task = await drone.move_by_velocity_async(
126-
v_north=1.0, v_east=1.0, v_down=0.0, duration=1.0
133+
v_north=1.0, v_east=1.0, v_down=0.0, duration=2.0
127134
)
128135
projectairsim_log().info("Move-North invoked")
129136
await move_north_task
130137
projectairsim_log().info("Move-North completed")
131138

139+
# Command the drone to move down in NED coordinate system at 2 m/s for 3 seconds
140+
move_down_task = await drone.move_by_velocity_async(
141+
v_north=0.0, v_east=0.0, v_down=2.0, duration=2.0
142+
)
143+
projectairsim_log().info("Move-Down invoked")
144+
await move_down_task
145+
projectairsim_log().info("Move-Down completed")
146+
132147
projectairsim_log().info("land_async: starting")
133148
land_task = await drone.land_async()
134149
await land_task

client/python/example_user_scripts/sim_config/payload_actor.jsonc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@
2929
"visual": {
3030
"geometry": {
3131
"type": "unreal_mesh",
32-
"name": "/Drone/Quadrotor1"
32+
"name": "/Engine/BasicShapes/Cube",
33+
"scale": "0.5 1.0 0.5"
3334
}
3435
}
3536
}

client/python/projectairsim/src/projectairsim/drone.py

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -831,7 +831,12 @@ async def rotate_to_yaw_async(
831831
asyncio.Task: An awaitable task wrapping the async coroutine
832832
"""
833833

834-
params: Dict = {"yaw": yaw, "timeout_sec": timeout_sec, "margin": margin, "yaw_rate": yaw_rate}
834+
params: Dict = {
835+
"yaw": yaw,
836+
"timeout_sec": timeout_sec,
837+
"margin": margin,
838+
"yaw_rate": yaw_rate,
839+
}
835840

836841
# print("params: {}".format(params))
837842
req: Dict = {
@@ -1527,7 +1532,7 @@ def set_external_force(self, ext_force: List[float]) -> bool:
15271532

15281533
def attach_payload(self, payload_id: str) -> bool:
15291534
"""
1530-
Attach a payload actor in the scene below the drone. The position offset between
1535+
Attach a payload actor in the scene below the drone. The position offset between
15311536
the drone and payload at the time of this call will be saved.
15321537
15331538
Args:
@@ -1557,4 +1562,29 @@ def detach_payload(self) -> bool:
15571562
"version": 1.0,
15581563
}
15591564
success = self.client.request(detach_payload_req)
1560-
return success
1565+
return success
1566+
1567+
def lower_payload(self, dz: float, v_down: float = 1.0) -> bool:
1568+
"""
1569+
Lowers payload towards the ground. Assumes payload is already attached
1570+
and user will call detach_payload() subsequently.
1571+
1572+
TODO: make it work with raising payloads too
1573+
1574+
Args:
1575+
dz (float): how much to lower the payload by from the resting
1576+
position under the drone (dz >= 0). If dz exceeds the
1577+
distance from the resting position to the ground, the payload
1578+
will be lowered until it makes contact with the ground.
1579+
v_down (float): how fast to lower the payload by (v_down > 0)
1580+
1581+
Returns:
1582+
bool: True if payload is lowered successfully
1583+
"""
1584+
lower_payload_req: Dict = {
1585+
"method": f"{self.world_parent_topic}/LowerPayloadActor",
1586+
"params": {"drone_name": self.name, "dz": dz, "vz": v_down},
1587+
"version": 1.0,
1588+
}
1589+
success = self.client.request(lower_payload_req)
1590+
return success

client/python/projectairsim/src/projectairsim/payload_actor.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from projectairsim.utils import projectairsim_log
88
from typing import Dict
99

10+
1011
class PayloadActor(object):
1112
def __init__(self, client: ProjectAirSimClient, world: World, name: str):
1213
"""ProjectAirSim Payload Actor Interface

core_sim/include/core_sim/scene.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,9 @@ class Scene {
117117
const std::vector<float>& vel_x_lin, const std::vector<float>& vel_y_lin,
118118
const std::vector<float>& vel_z_lin);
119119

120+
bool LowerPayloadActorServiceMethod(const std::string& drone_name,
121+
const float dz, const float vz);
122+
120123
void RegisterServiceMethod(const ServiceMethod& method,
121124
MethodHandler method_handler);
122125

core_sim/src/actor/payload_actor.cpp

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,10 @@ void PayloadActor::SetPoseOffset(const Pose& offset) {
153153
static_cast<PayloadActor::Impl*>(pimpl_.get())->SetPoseOffset(offset);
154154
}
155155

156+
const Pose& PayloadActor::GetPoseOffset() const {
157+
return static_cast<PayloadActor::Impl*>(pimpl_.get())->GetPoseOffset();
158+
}
159+
156160
void PayloadActor::UpdateKinematics(const Kinematics& kinematics) {
157161
static_cast<PayloadActor::Impl*>(pimpl_.get())->UpdateKinematics(kinematics);
158162
}
@@ -313,9 +317,8 @@ const Kinematics& PayloadActor::Impl::ComputeAttachedKinematics(
313317
kinematics.pose.position.y() + payload_offset_.position.y(),
314318
kinematics.pose.position.z() + payload_offset_.position.z());
315319

316-
return Kinematics(
317-
Pose(new_pos, kinematics.pose.orientation * payload_offset_.orientation),
318-
kinematics.twist, kinematics.accels);
320+
return Kinematics(Pose(new_pos, payload_offset_.orientation),
321+
kinematics.twist, kinematics.accels);
319322
}
320323

321324
const bool PayloadActor::Impl::InAttachedState() const {
@@ -336,9 +339,14 @@ void PayloadActor::Impl::UpdateEnvironment() {
336339
}
337340

338341
void PayloadActor::Impl::SetPoseOffset(const Pose& offset) {
342+
std::lock_guard<std::mutex> lock(update_lock_);
339343
payload_offset_ = offset;
340344
}
341345

346+
const Pose& PayloadActor::Impl::GetPoseOffset() const {
347+
return payload_offset_;
348+
}
349+
342350
bool PayloadActor::Impl::GetStartLanded() const { return start_landed_; }
343351

344352
void PayloadActor::Impl::SetStartLanded(bool start_landed) {

core_sim/src/scene.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,9 @@ class Scene::Impl : public ComponentWithTopicsAndServiceMethods {
179179
Vector3 GetWindVelocity();
180180
void UpdateWindVelocity();
181181

182+
bool LowerPayloadActorServiceMethod(const std::string& drone_name,
183+
const float dz, const float vz);
184+
182185
std::string CallBackAfter(int t_secs);
183186

184187
void RegisterServiceMethod(const ServiceMethod& method,
@@ -380,6 +383,11 @@ const Vector3 Scene::GetWindVelocity() const {
380383
return pimpl_->GetWindVelocity();
381384
}
382385

386+
bool Scene::LowerPayloadActorServiceMethod(const std::string& drone_name,
387+
const float dz, const float vz) {
388+
return pimpl_->LowerPayloadActorServiceMethod(drone_name, dz, vz);
389+
}
390+
383391
void Scene::AddNEDTrajectory(
384392
const std::string& traj_name, const std::vector<float>& time,
385393
const std::vector<float>& pose_x, const std::vector<float>& pose_y,
@@ -505,6 +513,44 @@ void Scene::Impl::UpdateWindVelocity() {
505513
}
506514
}
507515

516+
bool Scene::Impl::LowerPayloadActorServiceMethod(const std::string& drone_name,
517+
const float dz,
518+
const float vz) {
519+
TimeSec start = SimClock::Get()->NowSimSec();
520+
TimeSec curr_time = start;
521+
TimeSec timeout = 180; // 3 minutes seems reasonable
522+
523+
auto& actors = GetActors();
524+
int actor_idx = GetActorIndex(drone_name);
525+
auto& actor_ref = actors[actor_idx];
526+
auto& actor = static_cast<Robot&>(actor_ref.get());
527+
PayloadActor* payload_actor = actor.GetPayloadActor();
528+
529+
if (!payload_actor) {
530+
logger_.LogError(name_, "Drone '%hs' is not attached to a PayloadActor.",
531+
drone_name.c_str());
532+
throw Error("Drone is not attached to a PayloadActor.");
533+
} else {
534+
Vector3 payload_pos = payload_actor->GetPoseOffset().position;
535+
float z = payload_pos.z();
536+
float z_final = z + dz;
537+
538+
while (z < z_final) {
539+
auto payload_collision_info = payload_actor->GetCollisionInfo();
540+
if (payload_collision_info.has_collided) break;
541+
542+
z = z + vz * (SimClock::Get()->NowSimSec() - curr_time);
543+
Vector3 new_pos(payload_pos.x(), payload_pos.y(), z);
544+
payload_actor->SetPoseOffset(
545+
Pose(new_pos, payload_actor->GetPoseOffset().orientation));
546+
547+
if (curr_time - start > timeout) return false;
548+
curr_time = SimClock::Get()->NowSimSec();
549+
} // TODO: split into robot.cpp?
550+
}
551+
return true;
552+
}
553+
508554
void Scene::Impl::SetCallbackPhysicsStart(
509555
const std::function<void()>& callback) {
510556
physics_start_callback_ = callback;
@@ -917,6 +963,12 @@ void Scene::Impl::RegisterServiceMethods() {
917963
auto get_wind_vel_handler =
918964
get_wind_vel.CreateMethodHandler(&Scene::Impl::GetWindVelocity, *this);
919965
RegisterServiceMethod(get_wind_vel, get_wind_vel_handler);
966+
967+
auto lower_payload_actor =
968+
ServiceMethod("LowerPayloadActor", {"drone_name", "dz", "vz"});
969+
auto lower_payload_actor_handler = lower_payload_actor.CreateMethodHandler(
970+
&Scene::Impl::LowerPayloadActorServiceMethod, *this);
971+
RegisterServiceMethod(lower_payload_actor, lower_payload_actor_handler);
920972
}
921973

922974
void Scene::Impl::UnregisterAllServiceMethods() {

0 commit comments

Comments
 (0)