5

I'm working on an application, a simulator, where a quadrotor flies from waypoint to waypoint. In my code I've implemented a function to calculate the yaw using atan2 function. But when the quadrotor turns over 360° it doesn't move through the shortest way but it move all around the 360° range to reached the new direction.

Here I've posted a video. Take a look on its behavior when it across 360°.

Ok guys here the complete function now:

geometry_msgs::Pose getYaw( double x1, double x2, double y1, double y2 ) {

geometry_msgs::Pose output_trajectory;

/* Extrapolate the yaw information between two contigous points */
double yaw = atan2( ( y2 - y1 ), ( x2 - x1 ) );

  if( yaw < 0.0f )  // * read later on
    yaw += 2.0f * M_PI;

 output_trajectory.orientation = tf::createQuaternionMsgFromYaw( yaw );

  return output_trajectory;
}

where tf::createQuaternionMsgFromYaw is a library from the ROS framework. Here the defintion: link. geometry_msgs::Pose is simply a container: link.

*: here I've read related topics and questions here in stackoverflow and this function maps the returned output of atan2 into 0°-360°

UPDATE: here an extract from the yaw value:

...
Yaw: 131.3678
Yaw: 133.3495
Yaw: 135.6426
Yaw: 138.3442
Yaw: 141.5859
Yaw: 145.5487
Yaw: 150.4813
Yaw: 156.7167
Yaw: 164.6657
Yaw: 174.7288
Goal reached
Moving to the 3 waypoint
Yaw: 174.7288
Yaw: 186.4225
Yaw: 196.3789
Yaw: 204.1349
Yaw: 210.1296
Yaw: 214.7946
Yaw: 218.4716
Yaw: 221.4110
Yaw: 223.7921
Yaw: 225.7431
Yaw: 227.3565
...

As you can see the across point is "continuos", but it turns from 174° to 186° not in the right (the smallest) direction.

What i expect is that the quadrotor moves by small adjustments and rotatinng all around 360° insted of a few degree.

How can I get rid of this problem? I need a smooth yaw movement in my application. Regards

Dave
  • 349
  • 4
  • 22
  • It's not clear what you're asking here. There has to be a cross-over point somewhere. What behaviour are you expecting? – Oliver Charlesworth Dec 20 '14 at 21:00
  • So the problem is you're interpolating from 360 back down to 0 so the copter does a 360, or did I misunderstand? At which point that's an interpolation issue more than an atan issue. (P.S: Nice project) – Borgleader Dec 20 '14 at 21:04
  • 4
    This bit `yaw + 2.0f * M_PI;` of the code does not make any sense. The value is not stored or used. – wilx Dec 20 '14 at 21:05
  • Try this: http://pastebin.com/7hf0U5Vu Use it like: `yaw = clampRadians(atan2(y2 - y1, x2 - x1));` – Brandon Dec 20 '14 at 21:05
  • 1
    @VáclavZeman Probably OP meant to do += – Borgleader Dec 20 '14 at 21:05
  • @DietmarKühl: http://en.wikipedia.org/wiki/Euler_angles#Tait.E2.80.93Bryan_angles – wilx Dec 20 '14 at 21:07
  • Also, since you are using `double` type, it makes no sense to use the `f` suffix for the numbers as that makes them `float`. Just use `0.0` and `2.0` if you really want to use the zero in the first decimal place. Or just use `0` and `2`. – wilx Dec 20 '14 at 21:09
  • Also, again, why `double`s passed by references? – wilx Dec 20 '14 at 21:17
  • @Brandon is the statement `while` in your code right? Or did you meant `if` ?? – Dave Dec 20 '14 at 21:18
  • @VáclavZeman not anymore... :) – Dave Dec 20 '14 at 21:20
  • @OliverCharlesworth I've updated my topic above. Now my question should be much more clear – Dave Dec 20 '14 at 21:24
  • The problem seems to be in the interpolation between your given yaw values. Are you interpolating that yourself or are you using some library? – rodrigo Dec 20 '14 at 21:30
  • @Brandon your solution doesn't works neither... :( – Dave Dec 20 '14 at 21:30
  • @rodrigo you are right...I#m using later a library for calculating quaternions. Maybe the problem is not in atan2... – Dave Dec 20 '14 at 21:39
  • @Dave: Probably... But good quaternion libraries will do interpolation using the shortest path... Maybe you are using it wrong? You could post the quaternion interpolation code... – rodrigo Dec 20 '14 at 22:01
  • @rodrigo you are right. I ve updated the topic now with the used library – Dave Dec 20 '14 at 22:23
  • The fact that the flip is visible at all suggest that you are actually interpolating the yaw position between frames instead of applying it directly. Probably something done by a library you use. Consider that you might *really* want to calculate the change in the yaw, the incremental number. Always much less than 360 degrees. Interesting accuracy and wind-up problems after doing this for a couple of days :) – Hans Passant Dec 20 '14 at 23:25

2 Answers2

0

I don't think atan gives you the right angle. Atan gives -pi/2 ~ +pi/2 ranged results.

If you want to get exact angle in radians, you might have to write something like this (which i did before, worked nice):

// First find the section in which your coordinate is, then add the needed (x*pi) value to result:
double result = atan2(..);
if((x2 - x1 > 0) && (y2 - y1 > 0)){
  //section = 1;
  result += 0;
}
else if((x2 - x1 < 0) && (y2 - y1 > 0)){
   //section = 2;
  result += pi;
}
else if((x2 - x1 < 0) && (y2 - y1 < 0)){
   //section = 3
  result += pi;
}
else if((x2 - x1 > 0) && (y2 - y1 > 0)){
   //section = 4
  result += 2*pi;
}
else if(x2 == x1){
    if(y2 > y1){result = pi/2);
    if(y1 > y2){result = -pi/2);
}
else if(y2 == y1){
   if(x2 > x1){result = 0;}
   if(x1 > x1){result = pi;}
}
else if((x1 == x2) && (y1 == y2)){
   std::cout << "This is not line, just a point\n"; // :P
}
gokaysatir
  • 125
  • 2
  • 11
  • sorry kobay but I don't think that atan2 is the problem :( Angles are right, I think the problem is elsewhere – Dave Dec 20 '14 at 22:25
  • Hmm, hope you find the answer. – gokaysatir Dec 20 '14 at 22:35
  • If i am not wrong, you're trying to slice the route into equal parts. What if the first pair of x-y coordinates are miscalculated? – gokaysatir Dec 20 '14 at 22:47
  • :) ok...that's not the case, because the function which calls what I've posted here, takes care about the right order of the given point. I'm now pretty sure that the problem is not in my code – Dave Dec 21 '14 at 08:46
0

Ok. I got it. After hours of investigation I realized that the problem it not related to atan2() function or some sign change of the angle when it comes to jump over 180° or 360°.

Reading carefully the following code as as example

#include <string>
#include <ros/ros.h>
#include <sensor_msgs/JointState.h>
#include <tf/transform_broadcaster.h>

int main(int argc, char** argv) {
    ros::init(argc, argv, "state_publisher");
    ros::NodeHandle n;
    ros::Publisher joint_pub = n.advertise<sensor_msgs::JointState>("joint_states", 1);
    tf::TransformBroadcaster broadcaster;
    ros::Rate loop_rate(30);

    const double degree = M_PI/180;

    // robot state
    double tilt = 0, tinc = degree, swivel=0, angle=0, height=0, hinc=0.005;

    // message declarations
    geometry_msgs::TransformStamped odom_trans;
    sensor_msgs::JointState joint_state;
    odom_trans.header.frame_id = "odom";
    odom_trans.child_frame_id = "axis";

    while (ros::ok()) {
        //update joint_state
        joint_state.header.stamp = ros::Time::now();
        joint_state.name.resize(3);
        joint_state.position.resize(3);
        joint_state.name[0] ="swivel";
        joint_state.position[0] = swivel;
        joint_state.name[1] ="tilt";
        joint_state.position[1] = tilt;
        joint_state.name[2] ="periscope";
        joint_state.position[2] = height;


        // update transform
        // (moving in a circle with radius=2)
        odom_trans.header.stamp = ros::Time::now();
        odom_trans.transform.translation.x = cos(angle)*2;
        odom_trans.transform.translation.y = sin(angle)*2;
        odom_trans.transform.translation.z = .7;
        odom_trans.transform.rotation = tf::createQuaternionMsgFromYaw(angle+M_PI/2);

        //send the joint state and transform
        joint_pub.publish(joint_state);
        broadcaster.sendTransform(odom_trans);

        // Create new robot state
        tilt += tinc;
        if (tilt<-.5 || tilt>0) tinc *= -1;
        height += hinc;
        if (height>.2 || height<0) hinc *= -1;
        swivel += degree;
        angle += degree/4;

        // This will adjust as needed per iteration
        loop_rate.sleep();
    }


    return 0;
}

which I found here I realized that the variable angle is incremented every time of a small amount and then passed to the quaternion library tf::createQuaternionMsgFromYaw() This means 2 things:

  1. You never take care of the jump across 180° or 360°;
  2. more important thing, you never pass an absolute angle. For istance you never call in your code tf::createQuaternionMsgFromYaw(degtorad(179)) and then tf::createQuaternionMsgFromYaw(degtorad(182)), but tf::createQuaternionMsgFromYaw(angle + delta_angle);

Regards

Dave
  • 349
  • 4
  • 22