-
-
Notifications
You must be signed in to change notification settings - Fork 159
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
feature: V2 Autopilot API #1596
Conversation
@sbender9 would appreciate your feedback as to any forseen issues with the proposed API based on the current signalk-autopilot plugin functionality. |
@seandepagnier would appreciate your feedback with regards to the "Signal K aware" pilot scenario where the plugin requirements are more focussed on sending commands. Just as an FYI, Freeboard-SK implements a PoC PyPilot plugin that requires
I'm not sure if this is the preferred method, but was a convenient method for the PoC. |
I think this is a good start, but I see several issues here:
I remember you mentioning that you have a PoC of pypilot autopilot plugin - is it somewhere to be found? All in all I think it would be a good idea to work this out end to end: have a working or at least conceptually sound autopilot plugin wired together with this and maybe even a rudimentary standaloe web ui at the other end. In the past we have multiple cases where we worked from the specification that turned out to be more theoretical than practical. |
This API is implemented in Freeboard-SK today to interface with PyPilot. v2.3.0 branch is a more complete implementation.
They are mentioned in the context that autopilot operation is already supported by Signal K through the
Yes.. as per the text uses
Originally this API was based on the Resources API model that supports multiple providers. |
I am following this, and dont have much to add, except that, yes, I do have 2 pypilot installed on my boat. Generally one of them is off to save power, but it is essentially a backup pilot. Right now, if one pypilot has a wind sensor attached, the other pypilot can use it if both pypilots are connected to the same signalk server. It would be possible for example for each pilot to be wired to a wind sensor. Then if both pilots are on, whichever wind sensor is to windward could be used. There are other examples, but supporting multiple autopilots is a good idea, however be aware that multiple pypilots in the future may detect each other and communicate directly (outside of signalk) as well. |
With regards to operation with one or more autopilots I see the following scenarios:
Given the above scenarios, the breadth of devices and that the plugin is managing communication with the autopilot I see the following options:
I'm leaning towards option 1. send commands to all plugins and let them sort it out. |
I've been mulling this over, here are my thoughts: Looking at the API definition and the api routes it looks like the Autopilot api would not be overly complex in TypeScript - it would look like this
or am I missing something? Leaving routes implementation, validation and emitting deltas to each autopilot integration plugin sounds like a recipe for having as many different behaviors & intrepretations of how exactly the whole thing works as there are autopilot integration plugins. Why would we not got the "provider" way? Meaning have the server implement routes, validation, emitting deltas and access control and then delegate the operations to the actual AP plugin integration code? This would decouple the AP integration from the server, leaving configuring and routing autopilots to the server. As for the multiple autopilot case: I think the plugins will need to sort out is not a good enough strategy. Take for instance just the engange operation: the user most definitely must decide what autopilot the operation is about. In this case the REST resource structure would actually work pretty well: mount each autopilot's api at In general I think it is a good idea to sort the multiple instances case sooner rather than later, even if out there the single cases will far outnumber the multiple ones and it is kind of a sidetrack. Retrofitting support afterwards is much harder. We need also a way for an autopilot integration to send updates about changes coming from non-SK controlled autopilot devices: the user adjusting the target or mode via pushbuttons on the unit or for N2K devices over CAN, Sean's comment about autopilots changing state outside the SK APIs control etc. So the server api should provide a way for the ingeration to emit autopilot state updates (deltas?). As for multiple autopilot integrations of a single kind: the idea for adding support for instantiating a plugin multiple times, with multiple configurations has been there for quite a while. Maybe it's time for that? People have requested for example multiple separate configurations for sk-to-nmea0183. BTW I think APB and MWV are not really autopilot control: APB is 100% derived from the data in SK Course api and vessel position and heading and MWV is just wind sensor data. Did you put the pypilot code in Freeboard for convenience? Seems like an odd tangle, when it could as easily be an independent plugin. Or are you waiting for the capabilities API? |
Consider the common use case of two devices running opencpn or avnav, lets say a raspberry pi running signalk server and pypilot, and a laptop. If a route is activated in opencpn, normally it sends commands to steer the autopilot without needing signalk. However, only one instance of opencpn can do this at a time. There is some integration between opencpn instances, but if the idea is to make this universal, it must work with other plotters, like avnav and communicate with signalk. So for example, if you activated a route in opencpn, it would send the route via signalk, and other instances of other plotters would see this, create the route and activate it similarly. If the route is de-activated from any plotter it would deactivate from all of them, across plotters, and autopilots. The use case of multiple plotters and a single pilot is much more common. Typically multiple autopilots are for backup and only one is even powered on at a time. |
No disagreement from me! Sadly all interest in OpenCPN seems to be in integrating O instances, not universal APIs and communications (Headless use case, Route sharing). Now that O has the networking infrastructure for http and ws the foundation is there, but nobody seems to be building on it. Any ideas how to promote cross app cooperation appreciated. But that is mostly about course api (activating route, advancing to next waypoint etc), not the Autopilot api (engange/disengage, change mode) under discussion here. They are pretty tightly related, but separate: you can use a an autopilot without a plotter and a plotter without an autopilot. |
Another thing to consider is 'trajectories' This is a logical advancement in route following. What this means, is the autopilot actually tries to follow spline or beizer curve based on the route rather than route segments. It is something consider as there are a few more parameters, and the intended 'trajectory' should be rendered in the plotter. |
@seandepagnier Is there a way to run pypilot in ”test” mode so that from the outside it looks like everything works as it should even though no actual mechanism is connected? Would be useful for testing Adrian’s pypilot integration end2end on the desktop. |
I think we are missing a few use cases:
|
From an API perspective you could just use the
I feel this can be reflected in the
|
There needs to be a mechanism for the plugin to provide updates from the autopilot via the plugin to the API.... traditionally a plugin would use Perhaps include a method e.g. apUpdate() in the interface. This way the Autopilot API can receive the value, perform the necessary processing and emit the necessary deltas. export interface AutopilotApi {
register(
pluginId: string,
provider: AutopilotProvider,
primary?: boolean
): void
unRegister(pluginId: string): void
/* method for plugin to provide Autopilot API updated values from autopilot */
apUpdate(pluginId: string, attrib: string, value: any): void
}
|
I agree on the AutopilotProvider needing the ability to send updates. Isn't the basic case being able to update I don't think we can build things so that in SK land there is only a single, primary autopilot. The user may press disengage on one pilot and engage on another and we need to be able to deal with that. All in all I don't think the current method where there is only one connected (not "primary" but "only this one is connected") is flexible enough for even the simplest cases with >1 autopilots.
Target is separate from rudder control. Example use case:
|
Added to feature list (at top) for implementation. |
I'd like to focus the discussion on operation with multiple autopilots. Firstly some assumptions (feel free to correct any that are not correct):
So here is an attempt at expected operation:
1. Autopilot device led operations:
2. For API lead operations:
Does this cover the MVP for Autopilot API operation? |
Why do we need or want to make this assumption? I honestly think that the api would be simpler and more flexible to different real world scenarios if the autopilots work independent of each other. Like if the user engages multiple APs outside the api's control per your scenario above. |
So if assumption 1 is not valid, does that impact assumption 7? |
can come up with mostly theoretical setups where there are multiple actuators, like a main rudder and a windvane type auxiliary feathering rudder. But that is not the point. I'd like think that goal here is to come up with a an API that does not make too rigid assumptions but still makes the the basic cases easy. To me this includes
So please look beyond "but it does not make sense to engage /have in control more than one autopilot". I believe my points above invalidate assumptions 1, 2 and 7. |
So I interpret this as a client just being able to Maybe the wording was a bit clumsy but this is the intent of assumption 2. |
I agree with this statement.
This is unrealistic mainly because engaging autopilots almost always sets the target heading to the current heading. Changing course and pilot at the same time is possible but I don't think it is a very useful operation or very important.
This is the same as the first point again. Normally you would engage the pilot first, and then set the target heading. Please elaborate on why the opposite order (that you suggest) is useful. About multiple autopilots: The most obvious use cases for actually powering up multiple pilots would be for example: If we can map out all the actual use cases that make sense we can ensure the api supports these. What other reasons would multiple pilots be used? |
Just so that it is not just me coming up with weird, unrealistic scenarios: people do have multiple autopilots fully installed https://forums.sailinganarchy.com/threads/autopilot-backup-install-or-spares.225694/ |
Again: my point is that we should not build assumptions like engaging autopilots always sets the target heading. You did not say that: if we assume your engaging autopilots almost always sets the target heading then the API should be flexible enough to support cases where the target is not automatically set.
I think we can list use cases, but it is foolish to assume that we can map all use cases. Some use cases may not even make sense (like having multiple active ap's), but arise from the messy real world (user errors, flaky comms, race conditions etc). Rereading my own comments I think we should be in pretty good shape re: multiple aps if the api supports
Moving forward towards solutions...I think we have several ways to solve these
I am wondering if it is time to break the SK v1 mechanism where delta paths map 1:1 to http paths. In practise we could use Sidenote: I don't think we want to reflect the internals of SK server in the API, so ap's in the API should have just a single identifier, not providerId.deviceId. We can internally derive the identifier from plugin id and instance id. But if for example pypilot would implement this natively the two parts would not make much sense. Oh I can come up with one more desirable API feature: steer developers dealing with the API to think about multiple APs and not just build "oh there's only ever going to be one" systems. btw thanks for insightful discussion! I feel we are making progress here. |
FYI resources API uses the approach of a query parameter to target the provider.
|
Yep. Maybe we could have picked a little bit more generic name for that parameter, but I think the abstraction there is correct and not too tied to current SK server. |
Whichever is more clean, but a way to specifically address a particular autopilot vs broadcast to all pilots. In the case of broadcasting the signalk server may need to be aware of which pilot is "active" and for this it might need a separate key to allow clients to inform the service which pilot to use so that broadcast requests without a particular target will only enable the primary autopilot. |
The following WIP plugins have been updated to align with this PR and can be used for testing and / or template. |
2caec45
to
b3a38c0
Compare
I squashed the branch to a single commit, as the history was pretty messy and not worth preserving. Befofe overwriting I pushed the original as v2_api_autopilot_orig, in case I messed something up. @panaaj could you please review that everything seems to be in order here in the squashed version? |
Correct value to contain `autopilots` not `autopilot`
It looks good. |
It is great to see progress on this. I am trying to understand the design concept because it seems there are 2 possibilities for pypilot.
Currently pypilot does communicate with signalk for sensor data, like wind, gps and many others, but not directly for autopilot commands. It would be good to support autopilot commands. Are they ready? If so, which programs are you using to generate them? I am unaware of any, for example, a signalk-autopilot-pi for opencpn could be useful, but also via node red or? I need to do this to implement support. In this case a special signalk plugin (pypilot-autopilot-provider) would not be required as the signalk translation occurs within pypilot, and the pypilot_web is not needed. I suggest implementing both approaches, and then comparing the results in real use. Eventually one or the other may be deprecated, but probably not for years, and in the meantime serves the goal of giving the user the maximum possible flexibility in configuring the system. I will have to put safeguards if the pypilot-autopilot-provider signalk plugin is active to ignore control of the autopilot through a direct signalk connection. A direct connection via signalk may provide lower latency for manual control, but a signal plugin which directly sends UDP packets to pypilot (bypassing the web and signalk layer completely) could potentially offer lower latency. This is a reason to try both ways to find the best way for manual control. low latency gives the best responsiveness for open-loop manual control. With actual rudder angles sent rather than relative movement when rudder feedback sensor exists it is less of an issue, but always matters. The other pilots, the n2k ones. It seems they also have 2 paths? Now both may be signalk plugins, but in both cases these plugins are converting signalk into n2k paths for autopilots. So is this by design? Are both used (one for input and one for output) or? I am not understanding why the n2k-signalk is not able to provide all the conversions needed. |
@panaaj how about the task list in the PR description - lots of open items? |
A bit of clarification:
@panaaj has been working on autopilot providers and can fill in the details. Please have also a look at the descriptions Adrian wrote for this PR. Meanwhile the idea is to merge this PR, publish a new server version with it and see how far it takes us. |
These are all done. I was looking to tick them off after review to reflect the wider position not just mine. |
@seandepagnier
The details have been included in the server documention and OpenAPI definitions. With regards 2. I interpret this as the "Signal K aware" autopilot or "no plugin" approach. There is more work to do in this space and I would certainly be keen to open a dialog around this to determine how we can move forward. We have an autopilot channel in the SignalK Discord which would be the preferred way to capture ideas. With regards avoiding NMEA0183, the Course API (which has been imlemented for about 18 months or so) exposes course information under the path
Not sure if this completely covers that statement but FYI the documentation can be found here |
Version 2 Autopilot API:
Goals:
steering.autopilot
path with$source
= the autopilot device identifiersteering.autopilot.default
to notify clients when the default autopilot device is set / changedAPI will compliment these Signal K Server features to support autopilot operation:
Course API
: To set the destination and enable course data calculations to create a cohesive data set for navigation operations.signalk-to-nmea0183
plugin: Deliver the necessary NMEA sentences APB (for route control) and MWV (for wind steer) in the NMEA0183 data stream.Overview:
Features:
REST API
/signalk/v2/api/vessels/self/steering/autopilots
GET /signalk/v2/api/vessels/self/steering/autopilots
/signalk/v2/api/vessels/self/steering/autopilots/{device_id}/*
/signalk/v2/api/vessels/self/steering/autopilots/_default/*
POST /signalk/v2/api/vessels/self/steering/autopilots/_providers/_default/{device_id}
GET /signalk/v2/api/vessels/self/steering/autopilots/{device_id}
PUT /signalk/v2/api/vessels/self/steering/autopilots/{device_id}/mode
PUT /signalk/v2/api/vessels/self/steering/autopilots/{device_id}/target
DELTAS
vessels.self.steering.autopilot.*
with$source
set to thedevice_id
vessels.self.steering.autopilot.default
when default device is set / changedNotifications
Server / Provider Interface
Provider
Plugins (under development):
Related links:
Course API
Course API Definition
signalk-autopilot issue 1
Extending autopilot paths