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 example #116

Merged
merged 7 commits into from Oct 20, 2017
Merged

Offboard example #116

merged 7 commits into from Oct 20, 2017

Conversation

shakthi-prashanth-m
Copy link
Contributor

Adding Offboard velocity control app.

Offboard velocity control is demonstrated using

  • Local NED
  • Body NED

Shakthi Prashanth M added 2 commits October 17, 2017 17:57
Offboard velocity control is demonstrated using
* Local NED
* Body NED
Offboard velocity control is demonstrated using
* Local NED
* Body NED
Copy link
Collaborator

@julianoes julianoes left a comment

Choose a reason for hiding this comment

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

I made a couple of comments but I think this is a very nice example to have. Thanks!

.gitignore Outdated
@@ -3,3 +3,8 @@
**/install
**/logs
.vscode
DroneCore.config
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you make a comment where these are from?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok, I'll add it. It comes from Qt Creator

@@ -0,0 +1,229 @@
//
// Example that demonstrates Offboard velocity control using LOCAL_NED & BODY_NED
Copy link
Collaborator

Choose a reason for hiding this comment

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

What about?
Example that demonstrates offboard velocity control in local NED and body coordinates

Copy link
Contributor Author

Choose a reason for hiding this comment

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

right..

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.


void wait_msec(int msec)
{
std::this_thread::sleep_for(std::chrono::milliseconds(msec));
Copy link
Collaborator

Choose a reason for hiding this comment

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

You could alternatively do this:

using std::this_thread::sleep_for;
using std::chrono::milliseconds;

sleep_for(milliseconds(100));


void wait_sec(int sec)
{
std::this_thread::sleep_for(std::chrono::seconds(sec));
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same here.


// Wait for device to connect via heartbeat.
wait_sec(2);
Device &device = dc.device();
Copy link
Collaborator

Choose a reason for hiding this comment

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

const float one_cycle = 2.0f * (float)M_PI;
const unsigned steps = (unsigned)(one_cycle / step_size);

std::cout << "[" << offb_mode << "]" << " Go right & oscilate" << std::endl;
Copy link
Collaborator

Choose a reason for hiding this comment

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

oscilate -> oscillate

wait_sec(5);

//////////////////////////////////////////////////////////////////////////////
if (offb_mode == "local-ned") {
Copy link
Collaborator

Choose a reason for hiding this comment

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

What about having two functions for the two if cases. That would make it easier to read and not require /////....

Copy link
Member

Choose a reason for hiding this comment

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

It would certainly allow for more modular code

Copy link
Collaborator

Choose a reason for hiding this comment

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

I tend to think that the two frame cases are separate, and users will tend to copy the code for the frame they are interested in using. Furthermore, what if PX4 adds additional frame support? So I would tend to have completely separate code blocks - and even only use functions if it aids the explanation.

@mrpollo
Copy link
Member

mrpollo commented Oct 18, 2017

@shakthi-prashanth-m can you please rebase this and push to upstream? I think Travis needs a little push

DroneCore dc;
std::string offb_mode = (argc > 1) ? argv[1] : "";

if (argc == 1 || (offb_mode != "local-ned" && offb_mode != "body-ned")) {
Copy link
Member

Choose a reason for hiding this comment

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

can we have this default to local? this would simplify the main routine, and perhaps use "body" instead of "body-ned" for the argument, just to simplify this

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok.. Do you mean when the example is launched with no arguments, it should start offboard with local NED coordinates mode ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As of now, we're demonstrating both NED & Body co-ordinates. So, removed command-line handling.


if (argc == 1 || (offb_mode != "local-ned" && offb_mode != "body-ned")) {
std::cerr << "Usage: offboard <coordinate-frame>\n" <<
"coordinate-frame: \"local-ned\", or \"body-ned\""
Copy link
Member

Choose a reason for hiding this comment

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

if we default to local then this would explain that we are using local by default and explain how to enable body ned

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We need to report usage of the example anyway even if we default to local. So, I thought we shall leave this to user.

Copy link
Collaborator

Choose a reason for hiding this comment

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

IMO code should show both modes at work - not an either/or choice determined by user. That way you can show off the nuances of using each approach.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@hamishwillee that is fair enough..

wait_sec(2);
Device &device = dc.device();

while (!device.telemetry().health_all_ok()) {
Copy link
Member

Choose a reason for hiding this comment

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

what about health_all_ok_async?

although this would force you to split your code even further

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@mrpollo, thanks. I will rebase with changes suggested and push new changes may be tomorrow.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Disagree - this synchronous is better.

@mrpollo The code can't progress until health is achieved so if you were to do this aysnc style you'd end up using promises to effectively create a synchronous solution. The only reason to use async here would be if the code was a library potentially running in a context that could block other code (e.g. UI). There are very few cases with single-vehicle drones where using the async approach makes - because we're only every waiting on one instruction, and instructions are mostly daisychained.
It might be worth using async to manage multiple vehicles, or if we can find a case where we need code to run in parallel.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@hamishwillee: I agree. I feel its fine to use sync API. There is no vital task to perform until device.telemetry().health_all_ok() return true. @mrpollo is that ok ?

}

std::cout << "In Air" << std::endl;
wait_sec(5);
Copy link
Member

Choose a reason for hiding this comment

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

you could use [in_air_async](https://github.com/dronecore/DroneCore/blob/develop/plugins/telemetry/telemetry_impl.h#L65) instead of waiting

Copy link
Collaborator

Choose a reason for hiding this comment

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

Generally I don't see much use for the async APIs for managing a single vehicle.

In any case though we might use this as an opportunity to show off some "comprehensive" takeoff code, as per this PR

  • Close app if vehicle not calibrated
  • Print what we're waiting on (maybe)
  • Check that arming occurred before calling takeoff
  • Check that we reached takeoff height rather than using timer. This is a bit better than in_air() because you actually know you have completed the takeoff.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@mrpollo, this is basically to let drone reach takeoff altitude before we start issuing OFFBOARD commands. I think it should be fine to sleep for 5 seconds, similar to the way done in takeoff land example.

wait_sec(5);

//////////////////////////////////////////////////////////////////////////////
if (offb_mode == "local-ned") {
Copy link
Member

Choose a reason for hiding this comment

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

It would certainly allow for more modular code

std::cout << "[" << offb_mode << "]" << " Go UP 2 m/s, Turn clock-wise 180 deg" << std::endl;
for (unsigned i = 0; i < 400; ++i) {
device.offboard().set_velocity_ned({0.0f, 0.0f, -2.0f, 180.0f});
wait_msec(10);
Copy link
Member

Choose a reason for hiding this comment

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

@julianoes I loathe waiting using timers to catch up with movement it can get unpredictable and unstable, this is something I always wanted to fix in DroneKit, we should come up with a way to either block for N time by an argument on set_velocity or find a way to have a callback

This isn't something you should worry on this PR @shakthi-prashanth-m, I'm just venting here, I would like to break this pattern in UAV development, we should be able to be more precise

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 know what you mean by "catch up with movement".

Generally for velocity control examples using timers makes sense because they are something like "travel this far for X time". If we wanted to control on distance we'd use the position variants of the functions. If we do want to control on distance anyway, then we might add telemetry to measure that distance and block on meeting targets.

@coveralls
Copy link

Coverage Status

Coverage remained the same at 48.37% when pulling 6ea8c21 on shakthi-prashanth-m:offboard_example into f96c303 on dronecore:develop.

if (result != Action::Result::SUCCESS) {
std::cerr << ERROR_CONSOLE_TEXT << message << Action::result_str(
result) << NORMAL_CONSOLE_TEXT << std::endl;
exit(EXIT_FAILURE);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Just for me, where is EXIT_FAILURE defined?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

stdlib.h

offboard_log(offb_mode, " Let yaw settle...");
for (unsigned i = 0; i < 100; ++i) {
device.offboard().set_velocity_ned({0.0f, 0.0f, 0.0f, 90.0f});
sleep_for(milliseconds(10));
Copy link
Collaborator

Choose a reason for hiding this comment

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

@julianoes Are we expecting the API client to maintain the resend rate? Not saying we shouldn't, just that we should think about whether there is any way to make this easier for developers.
I don't think it matters too much for velocity control, but definitely for position control it would be much better if the developer could send a message "move to position" and not have to control resends.

Perhaps I am overthinking this.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I agree that we might want to handle that internally.

@hamishwillee
Copy link
Collaborator

@shakthi-prashanth-m Great job!

@hamishwillee
Copy link
Collaborator

@shakthi-prashanth-m Would be good to add the calibration checks (at least) before the health check.

@shakthi-prashanth-m
Copy link
Contributor Author

@hamishwillee Thanks!
Will look into adding callbacks. Thanks for the suggestion.

@julianoes julianoes merged commit ce714f5 into mavlink:develop Oct 20, 2017
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.

None yet

5 participants