Open abhiramdapke opened 3 years ago
Try using mavros_msgs::Altitude::amsl
as home altitude.
double latitude, longitude;
bool gps_received = false;
void get_global_pose (const sensor_msgs::NavSatFix::ConstPtr& msg) {
latitude = msg->latitude;
longitude = msg->longitude;
gps_received = true;
}
double altitude;
void get_altitude(const mavros_msgs::Altitude::ConstPtr &msg)
{
altitude = msg->amsl;
}
Set home_alt
to altitude
when gps_received
becomes true, and publish to setpoint_position/global
using geographic_msgs::GeoPoseStamped
.
I've also been trying to control the drone with the same type of message, but with python. I've noticed issues too. Here is what I do:
AUTO.TAKEOFF
). mavros/extended_state
and mavros/global_position/rel_alt
messages. I don't move on until landed_state == 2
and I've reached a high enough relative altitude (in my case I need to be at least 2 meters up).mavros/global_position/global
messages. I check to see how many meters away my target position is from my current position. And this is where I get stuck.The drone flies to the right latitude and longitude, but it's altitude is wrong. Here are the details from my log message:
distance: 47.21 meters
LATITUDE LONGITUDE ALTITUDE
current position: 47.3978429, 8.545869, 597.5515792976987
target position: 47.39784293752265, 8.545869057172045, 550.3417375283871
The altitude accounts for all the distance.
@burak-yildizoz I've also tried sending GeoPoseStamped
messages and I get the same problem where the drone flies 47.2 meters too high.
By the way, I calculate the distance by converting both my current position and target position to X, Y, Z values in WGS84 coordinates (each component is in meters), sometimes this is called ECEF. Then I use the Pythagorean theorem to find the distance. Getting the X, Y, and Z values is tricky and I can share some python with you, if you'd like. But I use the tricks from the n-vector page for the heavy lifting.
@burak-yildizoz I'm not sure what you mean by:
Try using mavros_msgs::Altitude::amsl as home altitude.
and
Set home_alt to altitude when gps_received becomes true
Does the drone's home altitude have something to do with how GeoPoseStamped
messages are interpreted? When I run rostopic echo mavros/home_position/home
Here's what I see
geo:
latitude: 47.3978435
longitude: 8.5458706
altitude: 535.3505773470537
position:
x: 19.823331832885742
y: 10.315902709960938
z: 0.02930159494280815
Should I change position.z
to 535
?
@murphym18 I have pointed out this problem in GPS altitude is different in subscriber and receiver. It seems like someone needs to update the documentation. For now, issues are the documentation.
``` c++
#include
Hi @burak-yildizoz, Thank you so much for the quick response and the code. I have a couple of doubts though. I am trying to do the same in ardupilot by changing the mode to guided but the drone is not taking off to the desired altitude. I can do that with a takeoff client but have you implemented any logic for navigating it to multiple waypoints? Also, do we need to write a logic for checking if the waypoint is reached, or can we directly pass in waypoints one by one by using geographic_msgs::geoposestamped?
Hi murphym18, Thank you for your comment. I am also having similar issues with mavros/global_position/global topic. Are you trying to implement a multiple waypoint mission in the WGS84? Could you share the python code for checking the distance between the current position and target position? I am trying to check that with the waypoint_reached function using the Haversine formula but it's not working.
@murphym18 I have pointed out this problem in GPS altitude is different in subscriber and receiver. It seems like someone needs to update the documentation. For now, issues are the documentation.
Full code
@burak-yildizoz I checked out #1414. Do you think mavros_msgs::GlobalPositiontarget has a problem or it can work if the altitude issues are solved by using another subscriber callback for the altitude like you did? Or using geographic_msgs::Geoposestamped is the only option?
Hi murphym18, Thank you for your comment. I am also having similar issues with mavros/global_position/global topic. Are you trying to implement a multiple waypoint mission in the WGS84? Could you share the python code for checking the distance between the current position and target position? I am trying to check that with the waypoint_reached function using the Haversine formula but it's not working.
I can share the code we use to check the distance. We created a math lib in python. Here is the repo. To find the distance from this example:
LATITUDE LONGITUDE ALTITUDE
current position: 47.3978429, 8.545869, 597.5515792976987
target position: 47.39784293752265, 8.545869057172045, 550.3417375283871
Here is the python:
from droneresponse_mathtools import Lla
current_pos = Lla(47.3978429, 8.545869, 597.5515792976987)
target_pos = Lla(47.39784293752265, 8.545869057172045, 550.3417375283871)
distance = current_pos.distance(target_pos)
assert 47.21 - distance < 0.001
The complete source code for the lib is here (don't worry it's not too long). And here is the most relevant excerpt :
def distance(self, other):
p1 = self.to_pvector().as_array()
p2 = other.to_pvector().as_array()
resid = p1 - p2
resid_sq = resid ** 2
resid_sum_sq = resid_sq.sum()
dist = np.sqrt(resid_sum_sq)
return dist
The code converts both points to p-vectors. You can think of a p-vector as an X, Y, and Z value that specifies a position relative to Earth (the "p" in p-vector is for position). Here are the details about the coordinate system:
The line of code that says p1 - p2
finds the vector that points from p2
to p1
. Then we find the magnitude of that vector by squaring all the elements, adding them up and finding the square root. If you've never seen this before, we're implementing the euclidean distance.
In case it's helpful, current_pos.to_pvector()
creates something like a vec3 with these values:
(4277613.251663816, 642794.9997351523, 4672256.91285744)
And target_pos.to_pvector()
creates something like a vec3 with these values:
(4277581.646276147, 642790.2547726352, 4672222.165857614)
The units are in meters.
If you calculated the euclidean distance by hand you'd find the points are about 47.2 meters apart.
If you want something similar in C++ the n-vector page has a download link for a C++ lib. Given a latitude, longitude, and altitude, you can calculate a p-vector in C++ using the lib. You can use function 1 on the n-vector page to get an n-vector. Once you have the n-vector you can then calculate a p-vector using function 5. If you're not sure, you can see how it's done in the python code I linked. Or you can study the n-vector page if you want the complete details.
Are you trying to implement a multiple waypoint mission in the WGS84?
I'm working on some code that has a drone search along a path of way-points until it finds someone, then it will follow the person. Another program actually generates the search path and sends it to us. So when we get the list of way points we start off by fly to each one. In that sense it's like we're flying a mission. But we need to use set points because at any time, we might need to change course.
I think I'm going to solve the altitude problem by undoing the ASML to ellipsoid height conversion found here and here. I looked at how MAVROS calculates the ellipsoid height, and found the code here. I've not decided how I'm going to do the EGM96 conversion yet. This gist looks like a promising approach.
But I think I want to avoid getting my position from more than one message. I don't want to get my latitude and longitude from mavros/global_position/global
while my altitude comes from another message. The reason is I don't want to piece together my position by looking at the time stamp of each message and combining the data of one message with interpolated/extrapolated data from another. To do that well, I'd need to re-create much of the estimator logic and that's a pretty big undertaking. To be fair, this might not be a problem. Perhaps I could combine the data from more than one message without any complicated logic so long as the time stamps are close enough (though I don't know what time delta is close enough). And I don't know what I'd do if the time values drifted to far apart. They probably wouldn't drift, but who knows.
I think it would be better for everyone if MAVROS just added an AMSL
field to the mavros/global_position/global
message. I'm even considering forking the project just for this one tiny change that is essential for my use case (and from what I gather on GitHub, it looks like I'm not the only one who needs this).
Anyway, I hope that helps.
Edit: I had some time to think about the problem. In some ways it seems unfortunate that the drone accepts setpoints where the altitude is specified in meters AMSL (using EGM96) but the altitude gets reported as meters above the ellipsoid. I've been looking into this problem for the past few days and it is more complicated than I thought. It's possible that this is one of the least bad ways to do it.
I found this video helpful. It provides lots of practical information and explains the difference between ellipsoid height and AMSL. After watching it, I've concluded that the drone's onboard GPS probably reports altitude in meters above MSL (using EGM96 to figure out where MSL is). According to the video, that's what most inexpensive and consumer-grade GPS units do.
This leads me to recognize that I want all my distance calculations to be done with the altitudes specified as meters above the ellipsoid's surface (assuming I continue to do distance calculations in ECEF coordinates). I don't think I'd have any problems if I calculated distance when both altitudes are specified in meters AMSL. It's probably fine so long as the points are reasonably nearby. However, I would expect to have problems if the distances were too big. And to avoid problems all together, I will make sure both points have their altitudes specified as meters above the ellipsoid. With that said, I think I'm going to hide all this from the rest of my application and use meters AMSL for all interactions with other subsystems.
Thanks a lot for the detailed explanation, @murphym18. I'll get back to you after reading, researching, and implementing the stuff you just mentioned. In the meantime, let me know if you figure out navigating to multiple waypoints. I'll also try to find a workaround.
I also think sensor_msgs::Natsatfix should provide all the 3 fields according to the drone's position in real-time as it seems quite noncoherent writing another subscriber callback just for the altitude.
Hi everyone,
I am currently facing similar issues as described above. I am not able to send any right /mavaros/setpoin_raw/target_global
I always get lat=0, long=0 and alt=0
except at auto.takeoff, where I also get the altitude mismatch.
Currently I tried sending geographic_msgs::GeoPoseStamped
message through /mavaros/setpoin_position/global
and mavros_msgs::GlobalPositionTarget
through /mavaros/setpoin_raw/global
with same results... I have tried ROS Melodic with Ubuntu 18 and mavros/bionic 1.8.0-1bionic.20210505.151420 and ROS Noetic with Ubuntu 20 and mavros/focal,1.8.0-1focal.20210505.164222. With ARM64 and AMD64 architectures, in simulation and on flights...
Is there any results from your side?
Thanks in advance!
Currently I tried sending
geographic_msgs::GeoPoseStamped
message through/mavaros/setpoin_position/global
andmavros_msgs::GlobalPositionTarget
through/mavaros/setpoin_raw/global
with same results
I'm not sure this helps but I noticed a typo in /mavaros/setpoin_position/global
. I recognize that it could be correct in your code but I thought I'd point that out just in case.
In case it helps, the rest of this comment explains how we do what you're describing. We use ubuntu 20.04 and ROS Noetic.
We're able to use geographic_msgs::GeoPoseStamped
messages with mavros. We publish those messages to /mavros/setpoint_position/global
. Here are some excerpts:
geographic_msgs::GeoPoseStamped
messagesfrom geographic_msgs.msg import GeoPoseStamped
from tf.transformations import quaternion_from_euler
def _build_global_setpoint2(latitude, longitude, altitude, yaw=0.0):
"""Builds a message for the /mavros/setpoint_position/global topic
"""
geo_pose_setpoint = GeoPoseStamped()
geo_pose_setpoint.header.stamp = rospy.Time.now()
geo_pose_setpoint.pose.position.latitude = latitude
geo_pose_setpoint.pose.position.longitude = longitude
geo_pose_setpoint.pose.position.altitude = altitude
roll = 0.0
pitch = 0.0
q = quaternion_from_euler(roll, pitch, yaw)
geo_pose_setpoint.pose.orientation.x = q[0]
geo_pose_setpoint.pose.orientation.y = q[1]
geo_pose_setpoint.pose.orientation.z = q[2]
geo_pose_setpoint.pose.orientation.w = q[3]
return geo_pose_setpoint
We publish those messages with rospy
using this Publisher
:
import rospy
_setpoint_global_pub = rospy.Publisher(uav_name + "/mavros/setpoint_position/global", GeoPoseStamped, queue_size=1)
setpoint = _build_global_setpoint2(1, 2, 3) # just an example setpoint
_setpoint_global_pub.publish(setpoint)
We namespace mavros nodes by UAV name. But if you're flying one drone, your code might be simpler.
The way we repeatedly send setpoint messages is beyond the scope of this comment. But hopefully that helps.
@murphym18 do not forget to fill header.stamp
for any *Stamped
message.
@murphym18 thanks! it's a typo while I was writing here, my bad, I wrote it right in my code.
I have the code like that -in C++, but is equivalent- and I am publishing at 10Hz rate. Is there a reason why you don't add the header of the GeoPoseStamped?
Thanks again :)
do not forget to fill header.stamp for any *Stamped message.
@vooon Thanks! I will make that change.
Is there a reason why you don't add the header of the GeoPoseStamped?
@IbonDLucas I think that was just an unintentional omission
@vooon Thanks again! I edited my earlier GitHub comment and I changed the code in my project. Turns out, this thread is showing up when I run web searches related to this topic. I figured it's better to replace the incorrect code with better code so that others will have an easier time finding examples. Thanks for the help.
Mavros: 1.5 ROS: Melodic Ubuntu: 18.04
[ Y ] ArduPilot
Hi guys, I am trying to implement a GPS function by passing multiple waypoints to the drone using mavros and would like to implement it with a real drone with a companion computer. Currently, I am trying to accomplish it in the simulation space(Gazebo). I am using mavros_msgs::globalpositiontarget msg to pass the waypoints and sensor_msgs::NatSatFix to get the location of the drone.
The subscriber callback for getting the global position is -
For this, the subscriber is -
The function for the waypoint is -
For this, the Publisher is -
A snippet of the script that I am using is like this -
For checking if the waypoint is reached, I am using Haversine's formula to get the distance between two GPS waypoints like this. It is taking the target location and subtracting that from the current location(which is derived from the global_Position_cb) -
There are a multitude of issues I am facing -
when I run the above script, the drone takes-off at the provided altitude but before reaching that altitude, it starts navigating towards the waypoint provided by set_waypoint. so, it doesn't actually reach the given altitude i.e. the function doesn't get completed thoroughly but the program moves on to the next line. I am using a temporary sleep() so that it reaches the provided altitude but need a more concrete solution.
The same is happening with the set_waypoint function. On the terminal, I get ROS_INFO("GPS position sent: [%f %f %f]", waypointlist[i].lat, waypointlist[i].lon, waypointlist[i].alt) msg for all the 4 waypoints almost immediately so what happens is that the drone doesn't reach the provided waypoint and turns around towards the next one. Set_waypoint is not getting completed and the program is moving to the next line. I surmised this might be because of the while(rosok()) loop and the rate set for receiving msgs but couldn't figure out a workaround. That is why I wrote a waypoint_reached function to check if the waypoint is actually reached before the program moves ahead. Not sure if anything is wrong with the logic of the set_waypoint or the waypoint_reached function or implementation of both of them. When running this program with the waypoint_reached function uncommented, I only get GPS position sent: once and nothing happens after that as the drone is flying off to the waypoint.
Echoing the topic /mavros/global_position/global fluctuates the lat lon values and those values are not necessarily within the waypoints specified. I suppose the home position in gazebo is lat = -35.362 and lon = 149.161 because that's what I get when I echo this topic. Not sure how the gazebo sets its home position and whether it is possible to visualize the drone actually moving to multiple waypoints in the gazebo in WGS84. The reason I say this is because no matter what waypoint is provided to the set_waypoint, it goes in the direction of the waypoint indefinitely. I am not sure if the drone will stop after reaching that waypoint or just crash.
Echoing the topic /mavros/global_position/global shows an altitude of 608m but the drone rises to only 5m with the takeoff client. I found an issue on Github that mentions that the altitude is either in ASML or WGS84 which might cause an issue but didn't find a solution to fix it so that I can visualize the correct altitude in the gazebo during the simulation.
In the rqt graph, I am not sure is everything is connected where it needs to be. If you could confirm that, that would be great. Also, what is /rostopic_10114_161547 and /rostopic_10144_161547?
Log of rostopic echo /mavros/global_position/global
Log of rostopic echo /mavros/setpoint_raw/global
Image of rqt graph
I am working on these for the past few weeks and not yet able to accomplish a working GPS function so any insights or guidance would be highly appreciated. Please feel free to share a snippet of your code as that becomes easier to understand. Thanks in advance!