Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

offboard: automatically send setpoins at set rate #134

Merged
merged 18 commits into from
Oct 30, 2017
Merged

Conversation

julianoes
Copy link
Collaborator

This changes the offboard API so that it is no longer required to send
setpoints at least every 0.5 seconds. Instead, this enables to set a
setpoint once and only set it again when it changes. The implementation
will automatically send it at 20 Hz internally.

If the setpoints need to be sent at a higher rate, e.g. for very smooth
control, that is still possible by just setting the setpoints at high
rate. Whenever a setpoint changes, it is immediately sent to the
vehicle.

Tested in SITL using the integration tests as well as the offboard example.

@coveralls
Copy link

Coverage Status

Coverage increased (+6.5%) to 55.131% when pulling 31a1407 on improve-offboard into e12c969 on develop.

@coveralls
Copy link

Coverage Status

Coverage increased (+6.5%) to 55.131% when pulling 2c066b6 on improve-offboard into e12c969 on develop.

}
}
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this return here so that the following code isn't executed?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very good catch, thank you!

@hamishwillee
Copy link
Collaborator

In the example

/**
* @name connected device
* @brief Does Offboard control using BODY co-ordinates
* returns true if everything went well in Offboard control, exits with a log otherwise.
*/

Seems odd to have @ docs for examples. Even so, should be correct - what is @name ? should it be @returns ?

@hamishwillee
Copy link
Collaborator

hamishwillee commented Oct 25, 2017

Looks awesome. Some thoughts:

  1. Is it possible for the vehicle to decide to switch mode due to RC or whatever while the script thinks it is in RC mode? In this case, what should happen / how should the script respond?
  2. We should add a paragraph up in the class description explaining a) how message resending should be used b)need to call setpoint before calling mode.
    E.g. update along the lines of:
/**
 * @brief This class is used to control a drone with velocity commands.
 *
 * The module is called offboard because the velocity commands can be sent from external sources
 * as opposed to onboard control right inside the autopilot "board".
 *
 * Client code must specify a setpoint before starting offboard mode.
 * DroneCore automatically resends setpoints at 20Hz (PX4 Offboard mode requires that setpoints are
 * minimally resent at 2Hz). If more precise control is required, clients can call the
 * setpoint methods at whatever rate is required.
 *
 * **Attention:** this is work in progress, use with caution!
 */
  1. Is there ever a case where someone might want to resend at lower than 20Hz?
  2. I still think it would be nice to automatically put into the mode rather than requiring "start" semantics. This is however fairly easy to understand.

Copy link
Collaborator

@hamishwillee hamishwillee left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added some feedback. Will leave for @shakthi-prashanth-m to do technical C++ review.

@hamishwillee
Copy link
Collaborator

hamishwillee commented Oct 26, 2017

@julianoes As part of this can I please also get a small docs update. For all the members in VelocityNEDYaw and VelocityBodyYawspeed add "@brief" (easiest way to fix output). So

float north_m_s; /**< Velocity North in metres/second. */

to

float north_m_s; /**< @brief Velocity North in metres/second. */

etc.

And also, in particular can I also get a closing ")" on these two:

  • float yaw_deg; /**< Yaw in degrees (0 North, positive is clock-wise looking from above. */
  • float yawspeed_deg_s; /**< Yaw angular rate in degrees/second (positive for clock-wise looking from above. */

@@ -76,12 +78,9 @@ bool offb_ctrl_ned(Device &device)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@julianoes In line above, why sleep for one second after starting offboard? API should not require artificial timing breaks.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed.

@@ -76,12 +78,9 @@ bool offb_ctrl_ned(Device &device)

// Let yaw settle.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't think we need this note - since we show it as log in next line.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed.

@@ -76,12 +78,9 @@ bool offb_ctrl_ned(Device &device)

// Let yaw settle.
offboard_log(offb_mode, " Let yaw settle...");
Copy link
Collaborator

@hamishwillee hamishwillee Oct 26, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This log is confusing - because at this point we haven't told the user we're yawing. We also report done, which I think is redundant since we're not actually checking that we're done and we then say the next step.

Suggest replace with following text - states that we're yawing and gives user 3 seconds to watch that behaviour. We don't need to tell the watcher that yaw is settling, but we add a comment so programmers know why we added that sleep.

    offboard_log(offb_mode, " Turn (yaw) clockwise to face East (90 deg.)");
    device.offboard().set_velocity_ned({0.0f, 0.0f, 0.0f, 90.0f});
    sleep_for(seconds(3));  // Let yaw settle

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

makes sense

@@ -99,31 +98,24 @@ bool offb_ctrl_ned(Device &device)
// NOTE: Use sleep_for() after each velocity-ned command to closely monitor their behaviour.
Copy link
Collaborator

@hamishwillee hamishwillee Oct 26, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Section above is confusing to watch and the code is a bit complicated:

  • Description incorrect
    • The vehicle does not "go right". It turns to face east unless it is already facing east.
    • It technically does "oscillate", but that could mean almost anything.
  • Propose change text to "Oscillate along a North-South path. Turn clockwise to face East (90 deg.)"
  • Propose make this do two cycles to be easier to observe when running the example (ie. const unsigned steps = (unsigned)(one_cycle / step_size); to const unsigned steps = 2 * (unsigned)(one_cycle / step_size);
  • Maybe "Done" is OK here because the action is quite extended.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, changed it along these lines.

@@ -99,31 +98,24 @@ bool offb_ctrl_ned(Device &device)
// NOTE: Use sleep_for() after each velocity-ned command to closely monitor their behaviour.

offboard_log(offb_mode, " Turn clock-wise 270 deg");
Copy link
Collaborator

@hamishwillee hamishwillee Oct 26, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Turn clockwise to face West (270 deg.)"

}
device.offboard().set_velocity_ned({0.0f, 0.0f, 0.0f, 270.0f});
sleep_for(seconds(2));

offboard_log(offb_mode, " Done");

offboard_log(offb_mode, " Go UP 2 m/s, Turn clock-wise 180 deg");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Go UP 2 m/s. Turn clockwise to face South (180 deg.)"

}
device.offboard().set_velocity_ned({0.0f, 0.0f, 0.0f, 270.0f});
sleep_for(seconds(2));

offboard_log(offb_mode, " Done");

offboard_log(offb_mode, " Go UP 2 m/s, Turn clock-wise 180 deg");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@julianoes When this command executes the vehicle should turn clockwise from 270 through to 0 and back round to 180 - because the yaw value is positive. What I'm seeing in SITL is that it goes anticlockwise back to 180 irrespective of the sign on the yaw value. So either a bug in our docs, or dronecore or PX4.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It just goes the quickest way to the new setpoint. I'll get rid of the "(anti-)clock-wise" indications.

sleep_for(milliseconds(10));
}
device.offboard().set_velocity_ned({0.0f, 0.0f, -2.0f, 180.0f});
sleep_for(seconds(4));
offboard_log(offb_mode, " Done...");

offboard_log(offb_mode, " Turn clock-wise 90 deg");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Turn clockwise to face East (90 deg.)"

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would also be good if at least one of these cases turned anticlockwise to demonstrate that working. Certainly we need that in the test code.

Copy link
Collaborator Author

@julianoes julianoes Oct 26, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As said, it's up to the controller. We can only really check it for body coordinates where we control yaw rate.

sleep_for(milliseconds(10));
}
device.offboard().set_velocity_ned({0.0f, 0.0f, 0.0f, 90.0f});
sleep_for(seconds(4));
offboard_log(offb_mode, " Done...");

offboard_log(offb_mode, " Go DOWN 1.0 m/s");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Go DOWN 1.0 m. Turn clockwise to face North (0 deg.)

@@ -152,53 +144,39 @@ bool offb_ctrl_body(Device &device)

// Turn around yaw and climb
offboard_log(offb_mode, " Turn around yaw & climb");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Text should be: " Go UP 1 m/s. Turn clockwise at 60 degrees/second."

sleep_for(milliseconds(10));
}
device.offboard().set_velocity_body({0.0f, 0.0f, -1.0f, 60.0f});
sleep_for(seconds(2));

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't all these cases print "Done" for consistency? Either all should do or all should not do.

Copy link
Collaborator Author

@julianoes julianoes Oct 26, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've removed all.

sleep_for(milliseconds(10));
}
device.offboard().set_velocity_body({0.0f, 0.0f, -1.0f, 60.0f});
sleep_for(seconds(2));

// Turn back
offboard_log(offb_mode, " Turn back");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

" Turn the other direction (anticlockwise) at 60 degrees/second."

sleep_for(milliseconds(10));
}
device.offboard().set_velocity_body({0.0f, 0.0f, -1.0f, 60.0f});
sleep_for(seconds(2));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe sleep 5 seconds - so this is easy to observe for someone viewing the example.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok.

sleep_for(milliseconds(10));
}
device.offboard().set_velocity_body({0.0f, 0.0f, 0.0f, -60.0f});
sleep_for(seconds(2));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe sleep 5 seconds - so this is easy to observe for someone viewing the example.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok.

sleep_for(milliseconds(10));
}
device.offboard().set_velocity_body({0.0f, 0.0f, 0.0f, 0.0f});
sleep_for(seconds(2));
// NOTE: Use sleep_for() after each velocity-ned command to closely monitor their behaviour.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate Note. If this is to stay, make sure it changes to "after velocity-body".

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed.

device.offboard().set_velocity_body({5.0f, 0.0f, 0.0f, 60.0f});
sleep_for(milliseconds(10));
}
device.offboard().set_velocity_body({5.0f, 0.0f, 0.0f, 60.0f});
Copy link
Collaborator

@hamishwillee hamishwillee Oct 26, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Propose rotate at 30.0f (bigger circle) and wait for 15 seconds. This is much more visible in QGC., and it doesn't hurt to show different rates at work.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure.

sleep_for(milliseconds(10));
}
device.offboard().set_velocity_body({0.0f, 0.0f, 0.0f, 0.0f});
sleep_for(seconds(5));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change to at least 8 seconds.

@julianoes
Copy link
Collaborator Author

julianoes commented Oct 26, 2017

Seems odd to have @ docs for examples. Even so, should be correct - what is @name ? should it be @returns ?

Agreed, I would remove the whole comment, or at least remove the @s.

@julianoes
Copy link
Collaborator Author

julianoes commented Oct 26, 2017

  1. Is it possible for the vehicle to decide to switch mode due to RC or whatever while the script thinks it is in RC mode? In this case, what should happen / how should the script respond?

The script would keep on sending setpoints but not try to enter offboard mode again. I guess we could check if we're still in the correct mode and complain otherwise. The problem is though that the setter does not return with an error.

What about having a function to check if offboard is still running?

  1. We should add a paragraph up in the class description explaining a) how message resending should be used b)need to call setpoint before calling mode.
    E.g. update along the lines of:

Thanks, I copied what you suggested.

  1. Is there ever a case where someone might want to resend at lower than 20Hz?

Maybe on a really slow link. We could default to 10 Hz which should work for all cases I can think of.

  1. I still think it would be nice to automatically put into the mode rather than requiring "start" semantics. This is however fairly easy to understand.

The benefit of having start() is that you can check the error of start. If it is implicit in set(), you'll need to check for an error with every set().

@julianoes
Copy link
Collaborator Author

@julianoes As part of this can I please also get a small docs update. For all the members in VelocityNEDYaw and VelocityBodyYawspeed add "@brief" (easiest way to fix output). So

Sure, done.

// reschedule the next call, so we don't send setpoints too often.
_parent->reset_call_every(_call_every_cookie);
}
_velocity_ned_yaw = velocity_ned_yaw;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't we assign this before entering calling send_velocity_ned() at line:191.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good spotting! Call every will only schedule it and not run it immediately, however, it is still a race, so you're right!
Changed in 2c0a887.

// reschedule the next call, so we don't send setpoints too often.
_parent->reset_call_every(_call_every_cookie);
}
_velocity_body_yawspeed = velocity_body_yawspeed;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't this happen before Line 218 ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, same thing.

@@ -164,17 +247,19 @@ void OffboardImpl::set_velocity_ned(Offboard::VelocityNEDYaw velocity_ned_yaw)
//const static uint16_t IGNORE_YAW = (1 << 10);
const static uint16_t IGNORE_YAW_RATE = (1 << 11);

const float yaw = to_rad_from_deg(velocity_ned_yaw.yaw_deg);
_mutex.lock();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this lock makes sure _velocity_body_ned_yaw has updated value?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, I guess the lock prevents the race from happening. Still changed it makes more sense.

This adds a class to call a callback et a constant rate (constant
interval). Presumably, this should be done using some sort of proper
event loop but this could be good enough for now.
This means that we can reschedule the next time call_every is triggered,
so the interval time is reset.
This changes the offboard API so that it is no longer required to send
setpoints at least every 0.5 seconds. Instead, this enables to set a
setpoint once and only set it again when it changes. The implementation
will automatically send it at 20 Hz internally.

If the setpoints need to be sent at a higher rate, e.g. for very smooth
control, that is still possible by just setting the setpoints at high
rate. Whenever a setpoint changes, it is immediately sent to the
vehicle.
The new API does not require the setpoint to be constantly set.
We don't need to check whether a command times out because that is
reliably handled inside device_impl.
This adds an interface to check if offboard is still active which means
the vehicle is still in offboard mode and offboard setpoints are still
sent.

If the vehicle drops out of offboard mode, we stop sending setpoints and
it is required to re-initiate sending offboard commands.
This prevents a race between the first time `send_velocity_` is called
and setting the cached setpoint that it will send.
@julianoes
Copy link
Collaborator Author

@julianoes One more doc fix please. For stop and stop_async in API it would be good to note that when offboard mode stops we put it the vehicle into Hold mode.

Added, thx.

If I move into Hold mode without calling stop(), will DroneCore realise that it has to stop sending setpoints?

Note yet, but it makes sense to add a check for it and stop sending setpoints... done in a9c83ec.

@coveralls
Copy link

Coverage Status

Coverage increased (+6.5%) to 55.131% when pulling 2c0a887 on improve-offboard into 97d98b9 on develop.

@julianoes
Copy link
Collaborator Author

julianoes commented Oct 27, 2017

We could merge this for now. I'll figure out the CI issues in a separate pull request. The problem is that timing sensitive tests fail on slow or busted CI instances. The solution will be to mock time.

Copy link
Collaborator

@hamishwillee hamishwillee left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good to me!

@julianoes julianoes merged commit 09053c3 into develop Oct 30, 2017
@julianoes julianoes deleted the improve-offboard branch October 30, 2017 18:05
@julianoes julianoes mentioned this pull request Nov 1, 2017
@julianoes
Copy link
Collaborator Author

CI troubles addressed in #147.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants