-
Notifications
You must be signed in to change notification settings - Fork 19.6k
Add support for multi-GCS operation #29252
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
base: master
Are you sure you want to change the base?
Conversation
Absolutely - I do this all the time |
No, you need to change The reason is that this information isn't just for other GCS. It is for components of the system to know what the system in charge is. If you don't set this to 0 then they will refuse commands from anything other than the GCS that used to be in control. |
FWIW My intent with this protocol was that parameter and mission data from a controlled system should be available to all GCS, in the same way as telemetry. However a controlled GCS should only accept a mission upload from its controlling GCS. The protocol wording is something like "a controlled component/system should only accept commands and command-like messages from its controller" so we should perhaps clarify that broadly speaking telemetry and metadata are expected to be available to all GCS. |
|
Thanks for the comments @hamishwillee. I would like to get the Ok from some of the AP dev team before proceeding with the changes. @rmackay9, could you take a look at this one? Thanks! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What GCS infrastructure supports this as it currently stands?
I will create a MAVProxy module at some stage to do this of course.
| } | ||
| void GCS_MAVLINK_Copter::set_sysid_my_gcs(uint8_t sysid) const | ||
| { | ||
| copter.g.sysid_my_gcs.set_and_save(sysid); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We do not do this - definitely not "save" and not "set".
We've learnt over time that changing the vehicle parameters based on random other mavlink messages leads to a lot of pain. The most obvious of these were the "set-speed" mavlink messages!
Alternative here would probably make SYSID_MYGCS a reboot-required parameter and initialise a little object at boot-time.
| { | ||
| return copter.g.sysid_my_gcs; | ||
| } | ||
| void GCS_MAVLINK_Copter::set_sysid_my_gcs(uint8_t sysid) const |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This absolutely doesn't belong in Copter. If you're finding it convenient to work only in the Copter directory, fine - but itreally should be up in libvraries/GCS_MAVLink
| { | ||
| return copter.g2.sysid_enforce; | ||
| } | ||
| bool GCS_MAVLINK_Copter::control_takeover_allowed() const |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This implementation might benefit from a little sub-object, in the same way we have for Params and MissionitemProtocol
| } | ||
| void GCS_MAVLINK_Copter::set_control_takeover_allowed(bool takeoverAllowed) const | ||
| { | ||
| return copter.g2.control_takeover_allowed.set_and_save(takeoverAllowed); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's your reasoning for persisting things?
| // We evaluate SYSID_ENFORCE parameter to send or not CONTROL_STATUS. This message is what | ||
| // populates GCS UI for multi GCS control | ||
| if (sysid_enforce()) { | ||
| set_mavlink_message_id_interval(MAVLINK_MSG_ID_CONTROL_STATUS, 5000); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Definite no-no. Should be in the streams.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did this one to not send this message unless really required. I thought it would be pointless to share it with other streams, as for the case of single GCS operations it would be a small but nonetheless waste of bandwidth, as it would not be used at all.
| return MAV_RESULT_FAILED; | ||
| } | ||
| // Release control | ||
| } else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pointless else
| send_control_status(); | ||
| return MAV_RESULT_ACCEPTED; | ||
| // A release control command should always be sent from the GCS in control. Return failed otherwise | ||
| } else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pointless else, and sysid check should be inverted
| // Allow REQUEST_OPERATOR_CONTROL packets. This is needed for a GCS not in control | ||
| // to communicate with the vehicle, so it can forward the request to GCS in control. | ||
| if (is_control_request_packet(msg)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why does the vehicle need to be in the middle of this negotiation?
I'm not saying it's wrong.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When we thought about this protocol we thought about this paradigm, and some reasons I remember:
-
We thought the vehicle should have the ultimate decision to proceed or not on such negotiations. Imagine in the future we add a layer of security to this, and it relies on some kind of key that all parties need to know. And even on current implementation we have certain control over it, for example disabling SYSID_ENFORCE parameter to disable the protocol completely, including GCS UI.
-
In the case of mesh radios I guess it would be good to talk GCS - GCS, but if these mesh radios don't work as they are supposed to, or if we are using non mesh radios, we rely on Autopilot mavlink router to make sure the messages arrive to the correct GCS. The initial idea was to handover control between 2 operators in mountain environment, so GCSs would not be having direct contact between them, only through vehicle.
| } | ||
| #endif // AP_MAVLINK_MSG_RELAY_STATUS_ENABLED | ||
|
|
||
| void GCS_MAVLINK::send_control_status() const |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Everything needs to be behind a feature define
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Noted. would MAVLINK_MULTIGCS_PROTOCOL_ENABLED work?
| return false; | ||
| } | ||
|
|
||
| bool GCS_MAVLINK::is_control_request_packet(const mavlink_message_t &msg) const |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an awfully expensive function to be doing on every incoming packet!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know it isn't ideal but:
-
It is only awfully expensive for command long and int. For the rest of the messages it only checks msgid, similar to what we are doing above with RADIO_STATUS messages.
-
What other approach could you think about to do this? We are in a situation where we are ignoring all messages from other GCS, but we really want control request commands to get in. Maybe using a different msgid for these messages we want to let in, so it is less expensive to check for them? We actually talked about this when we were building the protocol and after going back and forth several times we decided to stick with regular commands instead of specific new messages.
|
Thanks very much for the review Peter. What GCS infrastructure supports this as it currently stands? About my motivation to persisting data Next steps
I think the rest of the points are clear to me. I will be out a couple of weeks but later in April I will be available again to work on this. Thanks! |
|
Hi @Davidsastresas, Thanks for this, could you rebase on master? Also if possible if you could address PeterB's review comments? We're happy with the overall plan though |
|
@rmackay9 thanks for the answer. I will rebase and address the comments Peter made that are clear to me. However, I need some guidance/answers to complete all of them, I pointed them on my latest answer. If anybody could give me a hint it would be much appreciated, otherwise I have voids on the plan to complete this PR for it to be suitable for merging. Thanks.
|
FWIW Yes. The intent is to control what can command the vehicle, not reserve its telemetry or hide its mission. Generally you would not allow upload or setting of parameters. Note the allowed/denied should be managed by the autopilot, but a GCS aware of this feature should also not even try. |
|
So now we have #29252 which seeks to allow for multiple system IDs to be "mygcs". |
| if (msg.msgid == MAVLINK_MSG_ID_COMMAND_LONG) { | ||
| mavlink_command_long_t command; | ||
| mavlink_msg_command_long_decode(&msg, &command); | ||
| if (command.command == MAV_CMD_REQUEST_OPERATOR_CONTROL) { | ||
| result = true; | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| if (msg.msgid == MAVLINK_MSG_ID_COMMAND_LONG) { | |
| mavlink_command_long_t command; | |
| mavlink_msg_command_long_decode(&msg, &command); | |
| if (command.command == MAV_CMD_REQUEST_OPERATOR_CONTROL) { | |
| result = true; | |
| } | |
| } |
|
... oh, should have mentioned this before, but definitely need a timeout in here so if you haven't seen your GCS for a while something else can take control |
|
Hit an interesting thing where we want multiple GCSs to be considered "mygcs" - but do not want more than one to be able to provide RC inputs to the vehicle. |
|
Thanks for the feedback Peter. Would you mind moving the converstation here mavlink/mavlink#2313 (comment) Thanks! |
This PR adds support for the recently added multi-GCS mavlink subprotocol mavlink/mavlink#2158. This allows graceful change in control ownership on a system with multiple GCS. Please note this subprotocol does not attempt to cover security, it assumes all the operators are in contact between them and they work in a collaborative manner.
For more information about the protocol itself, please read that mavlink thread instead, and also please take a look at the corresponding QGroundControl PR here mavlink/qgroundcontrol#12410. I am also attaching a short video showing how this was tested in SITL:
multi-GCS-demo-2025-02-08_17.12.46.mp4
How the implementation works
GCS UI is populated based on CONTROL_STATUS being received. For single GCS operations, it is probably desirable to not show such UI, and not send that message at all. For that reason I left the evaluation to send this message or not at initialization, based on SYSID_ENFORCE parameter. If set to 0 at boot up, this message won't be sent at all, and no UI will be populated.
When the current GCS in control releases control, it only sets takeover allowed to true, but does not change sysid_mygcs. Mavlink protocol says we should use 0, no one in control, but maybe this is too hardcore at the moment, until we are sure all of this makes total sense. So the current solution of setting takeover allowed to true from my point of view is a balance. Let me know your thoughts.
As it is now it will assume all the GCS are connected to the same link. Is it worth implementing something so this can work between different links? for example a GCS connected on serialport x to a 900MHz radio telemetry and another GCS possibly connected to a different serial port to a serial to ethernet converter and a digital Ip link.
On link loss, when we stop seing heartbeats from the GCS in control, we set the allow override, so other GCS can take over automatically.
Right now, when a GCS that is not our SYSID_MYGCS sends a command it just ignores the command, so the GCS ack mechanism times out and we see a "no response" message. When we were working on this at the mavlink level, @hamishwillee suggested the use of a new MAV_RESULT_PERMISSION_DENIED, but in the end we ditched it due to the extra complexity. Maybe we should come back to this later if this concept works, to make more user friendly when a GCS not in control is exchanging messages with the vehicle.
In any case, I don't think this one in particular is too bad at the moment.
As we are basing this on sysid_enforce, when a GCS is not in control it will have trouble to download the mission and parameters. Maybe we should allow parameters and mission to be sent to GCS that are not in control?
Notes
We need this mavlink changes ArduPilot/mavlink#382 for this PR to build
Pending work to do
For awareness @rmackay9