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

Added camera plugin with basic functionality #338

Merged
merged 89 commits into from
Apr 23, 2018
Merged

Added camera plugin with basic functionality #338

merged 89 commits into from
Apr 23, 2018

Conversation

julianoes
Copy link
Collaborator

@julianoes julianoes commented Mar 23, 2018

Depends on mavlink/mavlink#872.

Todos:

  • take photos, videos
  • docstrings
  • change mode
  • integration tests
  • clean up timeouts
  • integration tests for camera status and modes
  • test against SITL (can follow later)
  • add example (can follow later)
  • camera definition and settings
  • test against Yuneec E90
  • test against Yuneec E50
  • test against Yuneec ET

camera definition and settings inspired by https://github.com/mavlink/qgroundcontrol/blob/master/src/Camera/QGCCameraControl.cc.

Fixes #154.

@julianoes julianoes self-assigned this Mar 23, 2018
@julianoes julianoes force-pushed the add-camera branch 2 times, most recently from c14cc29 to ce6cb7a Compare March 23, 2018 21:00
Copy link
Contributor

@shakthi-prashanth-m shakthi-prashanth-m left a comment

Choose a reason for hiding this comment

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

Nice! Does the plugin deal only with first camera ?

We need to implement MAVLink Camera protocol isn't it?
I mean that, we should first request for camera information and on response we receive available cameras, etc. I can probably contribute that part if you are ok with that.

We don't seem to handle heartbeat of camera component ? Did you disable autopilot heartbeats while testing ?

return camera_result_from_command_result(
_parent.send_command_with_ack(
MAV_CMD_IMAGE_START_CAPTURE,
MavlinkCommands::Params {0.0f, // all camera IDs
Copy link
Contributor

Choose a reason for hiding this comment

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

What is the difference between Camera ID and Camera Component ID ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Camera ID should actually be a thing of the past. I removed the comments.

1.0f, // take only one picture
float(_capture_sequence++),
NAN, NAN, NAN},
MAV_COMP_ID_CAMERA));
Copy link
Contributor

Choose a reason for hiding this comment

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

As we discussed, there'll be one instance per camera; so, camera component ID would be a member. Isn't it ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That's right. For now I'm just going to add the current single device plugin. Later we can extend it.

class Device;

/**
* @brief The Camera class allows to control generic mavlink cameras.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Perhaps to

The Camera class can be used to manage cameras that implement the MAVLink Camera Protocol: https://mavlink.io/en/protocol/camera.html

Copy link
Collaborator

Choose a reason for hiding this comment

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

This plugin is different in that multiple instances can be used with one device. We should capture that here too.

{
public:
/**
* @brief Constructor. Creates the plugin for a specific Device.
Copy link
Collaborator

Choose a reason for hiding this comment

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

FYI, if that rename for System goes in then this will have to change.

@hamishwillee
Copy link
Collaborator

This looks like it supports just a single camera per device (if not, then need to explain the "how"here). There is also no example.

Copy link
Contributor

@shakthi-prashanth-m shakthi-prashanth-m left a comment

Choose a reason for hiding this comment

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

Looks good :) I just added few suggestions and questions.

auto prom = std::make_shared<std::promise<void>>();
auto ret = prom->get_future();

// Let's get the mode first
Copy link
Contributor

Choose a reason for hiding this comment

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

We're setting mode, not get isn't it ?

Copy link
Contributor

Choose a reason for hiding this comment

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

I suspect this comment is here by mistake, possibly due to the copy paste error: https://github.com/dronecore/DroneCore/pull/338/files#diff-ac9feaa7a785b954819058e5ec940b4fR18

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, fixed in 3bba620.

* @param result The enum value for which a human readable string is required.
* @return Human readable string for the Camera::Result.
*/
static const char *result_str(Result result);
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should make

static std::string result_str(Result result);

in all the plugins.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I agree. Will change it.

/**
* @brief Stop video capture (synchronous).
*
* This stops a video recording again.
Copy link
Contributor

Choose a reason for hiding this comment

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

"again" ?

Copy link
Collaborator

Choose a reason for hiding this comment

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

See comments for stopping image capture.

~CameraDefinition();


void load_file(const char *filename);
Copy link
Contributor

Choose a reason for hiding this comment

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

Should be

void load_file(const std::string &filepath);

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That's nicer, I'll change it.

void load_string(const std::string &content);

const char *get_vendor() const;
const char *get_model() const;
Copy link
Contributor

Choose a reason for hiding this comment

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

const std::string &get_vendor() const;
const std::string &get_model() const;

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done but not with a reference but a copy.


TEST(CameraDefinition, LoadE90InfoFile)
{
CameraDefinition cd;
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice name cd :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

😄

MAV_COMP_ID_CAMERA);

_parent.send_command_with_ack_async(
MAV_CMD_REQUEST_STORAGE_INFORMATION,
Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yer we should probably remove the WIP soon. It has been there for a long time.

mavlink_camera_information_t camera_information;
mavlink_msg_camera_information_decode(&message, &camera_information);

load_definition_file(camera_information.cam_definition_uri);
Copy link
Contributor

Choose a reason for hiding this comment

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

Don't we store camera definition version ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Not yet, good point.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

So as long as we don't cache the file there is no value in having the definition version.

std::this_thread::sleep_for(std::chrono::seconds(2));

set_mode(camera, Camera::Mode::VIDEO);
std::this_thread::sleep_for(std::chrono::seconds(2));
Copy link
Contributor

Choose a reason for hiding this comment

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

I assume this is just like pausing and not mandatory for set mode activity ? Because, I see you're already waiting in set_mode().

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 catch, double is probably not needed.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I think I removed most of these.


LogDebug() << "Possible settings in photo mode: ";
for (auto setting : settings) {
LogDebug() << "- " << setting.second;
Copy link
Contributor

@shakthi-prashanth-m shakthi-prashanth-m Mar 26, 2018

Choose a reason for hiding this comment

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

It looks good if both name and value are printed. Isn't it ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I think so, I need to work on this, it's far from finished.

/**
* @brief Information about a picture just captured.
*/
struct CaptureInfo {
Copy link
Contributor

Choose a reason for hiding this comment

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

We can add a flag that tells whether its a standalone camera.

struct CaptureInfo {
    bool is_standalone;
    struct Position { ... };
    struct Quaternion { ... };
    ...
};

So, if is_standalone is true, then application need not look into position and quaternion details.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I would say if it is standalone you actually need to look into it because you can't look at the autopilot's telemetry.

bool get_possible_settings(std::map<std::string, std::string> &settings);
bool get_possible_options(const std::string &setting_name, std::vector<std::string> &options);

typedef std::function<void(Result, const std::string &)> get_option_callback_t;
Copy link
Contributor

Choose a reason for hiding this comment

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

Just a suggestion: How about grouping all typedefs together to ease reading ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I tried to group together with methods that use the typedefs, so that you don't need to scroll around when looking at a method signature.

Copy link
Contributor

Choose a reason for hiding this comment

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

OK

* @return Result of request.
*/
Result stop_photo_interval();

Copy link
Contributor

@shakthi-prashanth-m shakthi-prashanth-m Mar 26, 2018

Choose a reason for hiding this comment

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

How about providing a static method that tells the number of cameras on the drone.

static size_t Camera::how_many_cameras() const;

OR

static size_t Camera::total_cameras() const;

whatever is more intuitive. We get to know this info in CAMERA_INFORMATION. We get one response for each camera found on the drone.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

As said, I'll work on multi camera support when single camera support is merged and properly working. I can't do all at once 😄.

Copy link
Collaborator

@hamishwillee hamishwillee Apr 19, 2018

Choose a reason for hiding this comment

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

Lazy! So hard to find good help now days :-)

Copy link
Contributor

Choose a reason for hiding this comment

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

@hamishwillee in what context ? :-)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I have a gazillion things I could do every day and I'm always trying to figure out what's most relevant 😕.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Just teasing (I thought obviously?). You do plenty of good.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I understood that actually 😃 and it's probably how it always goes.

bool get_possible_settings(std::map<std::string, std::string> &settings);
bool get_possible_options(const std::string &setting_name, std::vector<std::string> &options);

typedef std::function<void(Result, const std::string &)> get_option_callback_t;
Copy link
Contributor

Choose a reason for hiding this comment

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

OK

std::cout << content;
}

const char *CameraDefinition::get_model() const
Copy link
Contributor

Choose a reason for hiding this comment

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

Same as here.

FirstChildElement("model")->GetText();
}

const char *CameraDefinition::get_vendor() const
Copy link
Contributor

Choose a reason for hiding this comment

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

Same as here.

{
}

void CameraDefinition::load_file(const char *filename)
Copy link
Contributor

Choose a reason for hiding this comment

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

Same as here.

{
std::vector<std::string> exclusion_parameter_names {};

parameters.clear();
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice

LogWarn() << "Error: no camera defnition available yet.";
if (callback) {
callback(Camera::Result::ERROR, "");
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I am not sure. Don't we need to return from here ? It seems falling through.

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!

void update_setting(const std::string &name, const ParameterValue &value);

typedef std::map<std::string, std::shared_ptr<Parameter>> parameter_map_t;
bool get_parameters(parameter_map_t &parameters, bool filter_possible);
Copy link
Contributor

Choose a reason for hiding this comment

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

Its a selector, isn't it ? If so, it should be

bool get_parameters(parameter_map_t &parameters, bool filter_possible) const;

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I tried to make a few of these methods const but could not because when the map[key] is used it can insert an entry in the map if not available and thus change the map. Presumably map[key] is the wrong way and map.at(key) should be used instead.


using namespace dronecore;

static const char *e90_unit_test_file = "extensions/plugins/camera/e90_unit_test.xml";
Copy link
Contributor

Choose a reason for hiding this comment

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

std::string is better. :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Changed.

}

Camera::Result CameraImpl::start_video()
{
Copy link
Contributor

Choose a reason for hiding this comment

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

Don't we need to check whether video capture in progress ? Also, we need to make sure we're in Video mode. Same applies to async versions.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I made a TODO.

Copy link
Contributor

Choose a reason for hiding this comment

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

OK.

{
std::lock_guard<std::mutex> lock(_capture.mutex);

return camera_result_from_command_result(
Copy link
Contributor

Choose a reason for hiding this comment

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

Don't we need to make sure we're in Photo mode? Same applies to async versions.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yap, thx.

}

Camera::Result CameraImpl::start_video()
{
Copy link
Contributor

Choose a reason for hiding this comment

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

OK.

void receive_int_param(const std::string &name, bool success, int value);
void receive_float_param(const std::string &name, bool success, float value);

CameraDefinition *_camera_definition = nullptr;
Copy link
Contributor

Choose a reason for hiding this comment

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

It could be a unique pointer, I guess.

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 idea.

if(APPLE)
# We need a define if on APPLE
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same thing here: if(APPLE) add_definitions("-DAPPLE") tells me that "if APPLE is defined, we add the definition called 'APPLE'". And I assume it is needed, otherwise we would not do it.

The comment represents 25% of this piece of code and just says exactly the same :P. Even worse: maybe at some point, the comment will get disconnected from the add_definitions call, and it will be misguiding.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I guess I wrote these comments because I'm a cmake noob so for me it wasn't trivial that the variable APPLE would not result in a define APPLE while compiling. Now I know 😄 .

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 it :)

@@ -40,3 +40,6 @@ set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG")
set(CMAKE_CXX_FLAGS_COVERAGE "${CMAKE_CXX_FLAGS_COVERAGE} --coverage")
set(CMAKE_EXE_LINKER_FLAGS_COVERAGE "${CMAKE_EXE_LINKER_FLAGS_COVERAGE} --coverage")
set(CMAKE_LINKER_FLAGS_COVERAGE "${CMAKE_LINKER_FLAGS_COVERAGE} --coverage")

# Otherwise tinyxml2 complains.
Copy link
Collaborator

Choose a reason for hiding this comment

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

And I believe this one is useful, because that's a bit peculiar.

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, anyway I appreciate the discussion 👍

std::mutex mutex {};
Camera::mode_callback_t callback {nullptr};
void *timeout_cookie {nullptr};
} _get_mode;
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should store camera mode here. We can use this mode to validate whether we're in Photo or Video mode in APIs like start_video(), take_photo(), etc. Isn't it ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We actually store the camera mode in the camera_definition, so I'm not sure if we need it here as well.

@shakthi-prashanth-m
Copy link
Contributor

@julianoes please rebase to develop. Thanks.

MAV_CMD_VIDEO_START_CAPTURE,
MAVLinkCommands::Params {0.0f, // Reserved, set to 0
NAN, // fps not set yet
NAN, // resolution not set yet
Copy link
Contributor

Choose a reason for hiding this comment

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

There is no resolution parameter in Video capture command.

2500 MAV_CMD_VIDEO_START_CAPTURE Starts video capture (recording). Use NAN for reserved values.
   Mission Param #1 Reserved (Set to 0)
  Mission Param #2 Frequency CAMERA_CAPTURE_STATUS messages should be sent while recording (0 for no messages, otherwise frequency in Hz)
  Mission Param #3 Reserved (all remaining params)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

oh you're right, that changed.

@julianoes
Copy link
Collaborator Author

Ok, integration tests work against E90, E50, ET, and I've tried to address all comments so this should be ok to merge, finally.

Copy link
Contributor

@shakthi-prashanth-m shakthi-prashanth-m left a comment

Choose a reason for hiding this comment

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

Good work @julianoes !

* MAVLink Camera Protocol: https://mavlink.io/en/protocol/camera.html.
*
* Currently only a single camera is supported.
* When multiple cameras are supported the plugin will need to instantiated
Copy link
Collaborator

Choose a reason for hiding this comment

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

need to be



/**
* @brief Get camera status.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Get camera status (asynchronous).

(just for consistency with all other methdos)

uint16_t rotation_deg = 0u; /**< @brief Video image rotation clockwise (0-359 degrees). */
std::string uri {}; /**< @brief Video stream URI. */

void set_highest()
Copy link
Collaborator

Choose a reason for hiding this comment

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

Missing documentation

bit_rate_b_s = BIT_RATE_AUTO;
}

constexpr static const float FRAME_RATE_HIGHEST = -1.0f;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Missing documentation for these ones.

@hamishwillee
Copy link
Collaborator

Looks good. I added some minor docs fixes. There is still a method and a few constants to document (marked up).

@shakthi-prashanth-m
Copy link
Contributor

MAVLink Video stream spec needs to be changed as raised here: mavlink/mavlink#883. So, we've a dependency on mavlink/mavlink#885.

@julianoes
Copy link
Collaborator Author

@hamishwillee thanks for making the doc fixes, appreciated!

@julianoes julianoes merged commit 15f6d91 into develop Apr 23, 2018
@julianoes julianoes deleted the add-camera branch April 23, 2018 13:44
rt-2pm2 pushed a commit to rt-2pm2/DronecodeSDK that referenced this pull request Nov 27, 2018
Added camera plugin with basic functionality
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