Skip to content

Commit d28d6b0

Browse files
authored
Merge pull request #2738 from mavlink/pr-set-gps-origin
Add method set to GPS origin
2 parents ffe6735 + f5c1e1b commit d28d6b0

File tree

15 files changed

+1849
-82
lines changed

15 files changed

+1849
-82
lines changed

docs/en/cpp/api_reference/classmavsdk_1_1_action.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ void | [set_return_to_launch_altitude_async](#classmavsdk_1_1_action_1acdc4360c2
6969
[Result](classmavsdk_1_1_action.md#classmavsdk_1_1_action_1adc2e13257ef13de0e7610cf879a0ec51) | [set_return_to_launch_altitude](#classmavsdk_1_1_action_1a5b05e84d35fad5b0ba2837aae1b3686e) (float relative_altitude_m)const | Set the return to launch minimum return altitude (in meters).
7070
void | [set_current_speed_async](#classmavsdk_1_1_action_1afd210be0eba436c81da79107562a0b6c) (float speed_m_s, const [ResultCallback](classmavsdk_1_1_action.md#classmavsdk_1_1_action_1a70a7b6e742d0c86728dc2e1827dacccd) callback) | Set current speed.
7171
[Result](classmavsdk_1_1_action.md#classmavsdk_1_1_action_1adc2e13257ef13de0e7610cf879a0ec51) | [set_current_speed](#classmavsdk_1_1_action_1af3b74cf3912411d9476b6eeac0984afb) (float speed_m_s)const | Set current speed.
72+
[Result](classmavsdk_1_1_action.md#classmavsdk_1_1_action_1adc2e13257ef13de0e7610cf879a0ec51) | [set_gps_global_origin](#classmavsdk_1_1_action_1abf97620153fca87f2dd89a6eb96a8479) (double latitude_deg, double longitude_deg, float absolute_altitude_m)const | Set GPS Global Origin.
7273
const [Action](classmavsdk_1_1_action.md) & | [operator=](#classmavsdk_1_1_action_1a89482740f533e194fade200103b5adef) (const [Action](classmavsdk_1_1_action.md) &)=delete | Equality operator (object is not copyable).
7374

7475

@@ -972,6 +973,29 @@ This function is blocking. See 'set_current_speed_async' for the non-blocking co
972973

973974
 [Result](classmavsdk_1_1_action.md#classmavsdk_1_1_action_1adc2e13257ef13de0e7610cf879a0ec51) - Result of request.
974975

976+
### set_gps_global_origin() {#classmavsdk_1_1_action_1abf97620153fca87f2dd89a6eb96a8479}
977+
```cpp
978+
Result mavsdk::Action::set_gps_global_origin(double latitude_deg, double longitude_deg, float absolute_altitude_m) const
979+
```
980+
981+
982+
Set GPS Global Origin.
983+
984+
Sets the GPS coordinates of the vehicle local origin (0,0,0) position.
985+
986+
987+
This function is blocking.
988+
989+
**Parameters**
990+
991+
* double **latitude_deg** -
992+
* double **longitude_deg** -
993+
* float **absolute_altitude_m** -
994+
995+
**Returns**
996+
997+
 [Result](classmavsdk_1_1_action.md#classmavsdk_1_1_action_1adc2e13257ef13de0e7610cf879a0ec51) - Result of request.
998+
975999
### operator=() {#classmavsdk_1_1_action_1a89482740f533e194fade200103b5adef}
9761000
```cpp
9771001
const Action & mavsdk::Action::operator=(const Action &)=delete

examples/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ add_subdirectory(offboard)
4242
add_subdirectory(parachute)
4343
add_subdirectory(params)
4444
add_subdirectory(set_actuator)
45+
add_subdirectory(set_gps_origin)
4546
add_subdirectory(sniffer)
4647
add_subdirectory(system_info)
4748
add_subdirectory(takeoff_and_land)
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
cmake_minimum_required(VERSION 3.10.2)
2+
3+
set(CMAKE_CXX_STANDARD 17)
4+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
5+
6+
project(set_gps_origin)
7+
8+
add_executable(set_gps_origin
9+
set_gps_origin.cpp
10+
)
11+
12+
find_package(MAVSDK REQUIRED)
13+
14+
target_link_libraries(set_gps_origin
15+
MAVSDK::mavsdk
16+
)
17+
18+
if(NOT MSVC)
19+
add_compile_options(set_gps_origin PRIVATE -Wall -Wextra)
20+
else()
21+
add_compile_options(set_gps_origin PRIVATE -W2)
22+
endif()
Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
//
2+
// Example demonstrating how to set GPS global origin after flying to a new location.
3+
// This takes off, flies 50m north using offboard velocity control, lands,
4+
// and then sets the GPS global origin at the new location.
5+
//
6+
7+
#include <chrono>
8+
#include <cmath>
9+
#include <future>
10+
#include <iostream>
11+
#include <thread>
12+
13+
#include <mavsdk/mavsdk.h>
14+
#include <mavsdk/plugins/action/action.h>
15+
#include <mavsdk/plugins/offboard/offboard.h>
16+
#include <mavsdk/plugins/telemetry/telemetry.h>
17+
18+
using namespace mavsdk;
19+
using std::chrono::seconds;
20+
using std::this_thread::sleep_for;
21+
22+
void usage(const std::string& bin_name)
23+
{
24+
std::cerr << "Usage : " << bin_name << " <connection_url>\n"
25+
<< "Connection URL format should be :\n"
26+
<< " For TCP : tcp://[server_host][:server_port]\n"
27+
<< " For UDP : udp://[bind_host][:bind_port]\n"
28+
<< " For Serial : serial:///path/to/serial/dev[:baudrate]\n"
29+
<< "For example, to connect to the simulator use URL: udpin://0.0.0.0:14540\n";
30+
}
31+
32+
int main(int argc, char** argv)
33+
{
34+
if (argc != 2) {
35+
usage(argv[0]);
36+
return 1;
37+
}
38+
39+
Mavsdk mavsdk{Mavsdk::Configuration{ComponentType::GroundStation}};
40+
ConnectionResult connection_result = mavsdk.add_any_connection(argv[1]);
41+
42+
if (connection_result != ConnectionResult::Success) {
43+
std::cerr << "Connection failed: " << connection_result << '\n';
44+
return 1;
45+
}
46+
47+
auto system = mavsdk.first_autopilot(3.0);
48+
if (!system) {
49+
std::cerr << "Timed out waiting for system\n";
50+
return 1;
51+
}
52+
53+
// Instantiate plugins
54+
auto action = Action{system.value()};
55+
auto offboard = Offboard{system.value()};
56+
auto telemetry = Telemetry{system.value()};
57+
58+
// Wait for system to be ready
59+
while (!telemetry.health_all_ok()) {
60+
std::cout << "Waiting for system to be ready\n";
61+
sleep_for(seconds(1));
62+
}
63+
std::cout << "System is ready\n";
64+
65+
// Get initial GPS origin
66+
auto origin_result = telemetry.get_gps_global_origin();
67+
if (origin_result.first == Telemetry::Result::Success) {
68+
std::cout << "Initial GPS origin: " << origin_result.second << '\n';
69+
}
70+
71+
// Get initial position
72+
auto initial_position = telemetry.position();
73+
std::cout << "Initial position: lat=" << initial_position.latitude_deg
74+
<< ", lon=" << initial_position.longitude_deg
75+
<< ", alt=" << initial_position.absolute_altitude_m << "m AMSL\n";
76+
77+
// Arm
78+
std::cout << "Arming...\n";
79+
const auto arm_result = action.arm();
80+
if (arm_result != Action::Result::Success) {
81+
std::cerr << "Arming failed: " << arm_result << '\n';
82+
return 1;
83+
}
84+
std::cout << "Armed\n";
85+
86+
// Take off
87+
std::cout << "Taking off...\n";
88+
const auto takeoff_result = action.takeoff();
89+
if (takeoff_result != Action::Result::Success) {
90+
std::cerr << "Takeoff failed: " << takeoff_result << '\n';
91+
return 1;
92+
}
93+
94+
// Wait until in air
95+
auto in_air_promise = std::promise<void>{};
96+
auto in_air_future = in_air_promise.get_future();
97+
Telemetry::LandedStateHandle landed_handle = telemetry.subscribe_landed_state(
98+
[&telemetry, &in_air_promise, &landed_handle](Telemetry::LandedState state) {
99+
if (state == Telemetry::LandedState::InAir) {
100+
std::cout << "Takeoff finished\n";
101+
telemetry.unsubscribe_landed_state(landed_handle);
102+
in_air_promise.set_value();
103+
}
104+
});
105+
106+
if (in_air_future.wait_for(seconds(10)) == std::future_status::timeout) {
107+
std::cerr << "Takeoff timed out\n";
108+
telemetry.unsubscribe_landed_state(landed_handle);
109+
return 1;
110+
}
111+
112+
// Let it stabilize at takeoff altitude
113+
sleep_for(seconds(2));
114+
115+
// Start offboard mode and fly 50m north
116+
std::cout << "Starting offboard mode to fly 50m north...\n";
117+
118+
// Send setpoint once before starting offboard
119+
Offboard::VelocityNedYaw stay{};
120+
offboard.set_velocity_ned(stay);
121+
122+
Offboard::Result offboard_result = offboard.start();
123+
if (offboard_result != Offboard::Result::Success) {
124+
std::cerr << "Offboard start failed: " << offboard_result << '\n';
125+
return 1;
126+
}
127+
128+
// Fly north at 5 m/s for 10 seconds = 50m
129+
std::cout << "Flying north at 5 m/s for 10 seconds...\n";
130+
Offboard::VelocityNedYaw fly_north{};
131+
fly_north.north_m_s = 5.0f;
132+
fly_north.yaw_deg = 0.0f;
133+
offboard.set_velocity_ned(fly_north);
134+
sleep_for(seconds(10));
135+
136+
// Stop and hover
137+
std::cout << "Stopping...\n";
138+
offboard.set_velocity_ned(stay);
139+
sleep_for(seconds(2));
140+
141+
// Stop offboard mode
142+
offboard_result = offboard.stop();
143+
if (offboard_result != Offboard::Result::Success) {
144+
std::cerr << "Offboard stop failed: " << offboard_result << '\n';
145+
return 1;
146+
}
147+
148+
// Get position before landing
149+
auto position_before_landing = telemetry.position();
150+
std::cout << "Position before landing: lat=" << position_before_landing.latitude_deg
151+
<< ", lon=" << position_before_landing.longitude_deg
152+
<< ", alt=" << position_before_landing.absolute_altitude_m << "m AMSL\n";
153+
154+
// Land
155+
std::cout << "Landing...\n";
156+
const auto land_result = action.land();
157+
if (land_result != Action::Result::Success) {
158+
std::cerr << "Landing failed: " << land_result << '\n';
159+
return 1;
160+
}
161+
162+
// Wait until landed
163+
while (telemetry.in_air()) {
164+
std::cout << "Waiting for landing...\n";
165+
sleep_for(seconds(1));
166+
}
167+
std::cout << "Landed!\n";
168+
169+
// Wait a bit for things to settle
170+
sleep_for(seconds(2));
171+
172+
// Get final position
173+
auto final_position = telemetry.position();
174+
std::cout << "Final position: lat=" << final_position.latitude_deg
175+
<< ", lon=" << final_position.longitude_deg
176+
<< ", alt=" << final_position.absolute_altitude_m << "m AMSL\n";
177+
178+
// Set the GPS global origin at the current position
179+
std::cout << "Setting GPS global origin at current position...\n";
180+
const auto set_origin_result = action.set_gps_global_origin(
181+
final_position.latitude_deg,
182+
final_position.longitude_deg,
183+
final_position.absolute_altitude_m);
184+
185+
if (set_origin_result != Action::Result::Success) {
186+
std::cerr << "Setting GPS global origin failed: " << set_origin_result << '\n';
187+
return 1;
188+
}
189+
std::cout << "GPS global origin set successfully!\n";
190+
191+
// Verify by getting the new origin
192+
auto new_origin_result = telemetry.get_gps_global_origin();
193+
if (new_origin_result.first == Telemetry::Result::Success) {
194+
std::cout << "New GPS origin: " << new_origin_result.second << '\n';
195+
} else {
196+
std::cerr << "Failed to get new GPS origin: " << new_origin_result.first << '\n';
197+
}
198+
199+
// Wait a bit before exiting
200+
sleep_for(seconds(3));
201+
std::cout << "Finished!\n";
202+
203+
return 0;
204+
}

src/mavsdk/plugins/action/action.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,12 @@ Action::Result Action::set_current_speed(float speed_m_s) const
256256
return _impl->set_current_speed(speed_m_s);
257257
}
258258

259+
Action::Result Action::set_gps_global_origin(
260+
double latitude_deg, double longitude_deg, float absolute_altitude_m) const
261+
{
262+
return _impl->set_gps_global_origin(latitude_deg, longitude_deg, absolute_altitude_m);
263+
}
264+
259265
std::ostream& operator<<(std::ostream& str, Action::Result const& result)
260266
{
261267
switch (result) {

src/mavsdk/plugins/action/action_impl.cpp

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -767,6 +767,75 @@ Action::Result ActionImpl::set_current_speed(float speed_m_s)
767767
return fut.get();
768768
}
769769

770+
Action::Result ActionImpl::set_gps_global_origin(
771+
double latitude_deg, double longitude_deg, float absolute_altitude_m) const
772+
{
773+
const int32_t latitude_e7 = static_cast<int32_t>(std::round(latitude_deg * 1e7));
774+
const int32_t longitude_e7 = static_cast<int32_t>(std::round(longitude_deg * 1e7));
775+
const int32_t altitude_mm = static_cast<int32_t>(std::round(absolute_altitude_m * 1000.0f));
776+
777+
auto prom = std::promise<Action::Result>();
778+
auto fut = prom.get_future();
779+
std::atomic<bool> prom_already_set{false};
780+
781+
// Use a unique cookie for this handler
782+
const void* cookie = this;
783+
784+
// Register handler for GPS_GLOBAL_ORIGIN response.
785+
// Note: Older PX4 versions (pre-v1.17) had a race condition where they would
786+
// broadcast GPS_GLOBAL_ORIGIN with stale values immediately after receiving
787+
// SET_GPS_GLOBAL_ORIGIN, before EKF2 had processed the command. To handle
788+
// this, we wait for a response with matching values rather than accepting
789+
// the first response.
790+
_system_impl->register_mavlink_message_handler(
791+
MAVLINK_MSG_ID_GPS_GLOBAL_ORIGIN,
792+
[&prom, &prom_already_set, latitude_e7, longitude_e7, altitude_mm](
793+
const mavlink_message_t& message) {
794+
mavlink_gps_global_origin_t origin;
795+
mavlink_msg_gps_global_origin_decode(&message, &origin);
796+
797+
// Only signal success when we receive the values we set
798+
if (origin.latitude == latitude_e7 && origin.longitude == longitude_e7 &&
799+
origin.altitude == altitude_mm) {
800+
if (!prom_already_set.exchange(true)) {
801+
prom.set_value(Action::Result::Success);
802+
}
803+
}
804+
// Otherwise, keep waiting for the correct values (or timeout)
805+
},
806+
cookie);
807+
808+
// Send the SET_GPS_GLOBAL_ORIGIN message
809+
if (!_system_impl->queue_message([&](MavlinkAddress mavlink_address, uint8_t channel) {
810+
mavlink_message_t message;
811+
mavlink_msg_set_gps_global_origin_pack_chan(
812+
mavlink_address.system_id,
813+
mavlink_address.component_id,
814+
channel,
815+
&message,
816+
_system_impl->get_system_id(),
817+
latitude_e7,
818+
longitude_e7,
819+
altitude_mm,
820+
0);
821+
return message;
822+
})) {
823+
_system_impl->unregister_mavlink_message_handler(MAVLINK_MSG_ID_GPS_GLOBAL_ORIGIN, cookie);
824+
return Action::Result::ConnectionError;
825+
}
826+
827+
// Wait for response with timeout
828+
auto status = fut.wait_for(std::chrono::duration<double>(_system_impl->timeout_s()));
829+
830+
_system_impl->unregister_mavlink_message_handler(MAVLINK_MSG_ID_GPS_GLOBAL_ORIGIN, cookie);
831+
832+
if (status == std::future_status::timeout) {
833+
return Action::Result::Timeout;
834+
}
835+
836+
return fut.get();
837+
}
838+
770839
Action::Result ActionImpl::action_result_from_command_result(MavlinkCommandSender::Result result)
771840
{
772841
switch (result) {

src/mavsdk/plugins/action/action_impl.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,9 @@ class ActionImpl : public PluginImplBase {
9292
void set_current_speed_async(float speed_m_s, const Action::ResultCallback& callback);
9393
Action::Result set_current_speed(float speed_m_s);
9494

95+
Action::Result set_gps_global_origin(
96+
double latitude_deg, double longitude_deg, float absolute_altitude_m) const;
97+
9598
Action::Result set_return_to_launch_altitude(const float relative_altitude_m) const;
9699
std::pair<Action::Result, float> get_return_to_launch_altitude() const;
97100

src/mavsdk/plugins/action/include/plugins/action/action.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,20 @@ class Action : public PluginBase {
644644
*/
645645
Result set_current_speed(float speed_m_s) const;
646646

647+
/**
648+
* @brief Set GPS Global Origin.
649+
*
650+
* Sets the GPS coordinates of the vehicle local origin (0,0,0) position.
651+
*
652+
* This function is blocking.
653+
*
654+
655+
* @return Result of request.
656+
657+
*/
658+
Result set_gps_global_origin(
659+
double latitude_deg, double longitude_deg, float absolute_altitude_m) const;
660+
647661
/**
648662
* @brief Copy constructor.
649663
*/

0 commit comments

Comments
 (0)