-
Notifications
You must be signed in to change notification settings - Fork 22
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
Adding Charging Powercurve Selection for EV Charging #69
Comments
@the-bay-kay I believe that the plan is for the "optimized curve" to be computed on the car.
Note that the important part, per DJ and Jacob, is (2) since the car should have control of the charging schedule for the departure time. We are only implementing (1) because it lets us showcase multiple SASchedules without fully supporting tariffs (and because I want to 😄 ) So you might want to tackle (2) first, since even if we don't finish (1), we will have a clean demo. |
Aaah, right! Cool cool -- sounds like a good plan! |
Updated Timeline and Issue name to reflect the project's immediate goals. |
note that node-red has a charts module: https://stevesnoderedguide.com/using-the-node-red-chart-node |
Let's start by investigating the first option. As currently configured, our Node-RED does have a chart function, though it seems to be geared to presentation-style charts (Bar Charts, Pie Charts, etc.). I don't believe we would be able to plot the anticipated equations. Node-RED does have a curve plotting module we could add, that seems geared to plotting functions specifically! (link)
Likewise, this looks like a good option -- may be more flexible than the curve module, will compare the two options and go forward from there! |
Assessing our options:
Let's take a shot at (2b) first, before exploring alternatives. My hope is that we can insert the |
can't you also just plot a line graph and have DJ give you a set of coordinates? |
I was searching for an option to plot via coefficients like we discussed a few days ago, but that is an option. I'll go ahead with that approach! |
Right, I think that plotting a standard quadratic curve would be more efficient, but if that is complex, we can fall back to the option that is known to work, at least for now |
Just wasted a solid hour and a half trying to fix this bug... turns out the old curve module I was experimenting with bricked the UI... Anyway, we've got a basic chart running: let's write the function and generate some datapoints... |
When attempting to plot an array of data, we do so for the points, but do not receive a line as we would expect. My intuition says this is either (i) an issue with how we're formatting the inbound data, or (ii) an issue with how the chart is configured to accept the data. Another minor issue with the line-chart method of plotting is that I have not figured out how to use non-timestamps within the X axis label. We can theoretically define custom labels (setting to "automatic" attempts to format as a time stamp), but it's uncertain if we the custom labels need be tied to a timestamp format like the defaults (HH:MM, for example) Screenshots / RecordingsQuadraticScreen.Recording.2024-08-15.at.3.05.05.PM.movLinearScreen.Recording.2024-08-15.at.3.12.14.PM.movLinear w / Timestamps as X valScreen.Recording.2024-08-15.at.3.14.06.PM.mov |
The x axis does represent time - the curve represents power drawn during the charge session, right? |
Yup, this was my problem. Changing the way the arrays were nested, we get an OK result. Let's add some templates for the powercurve data next |
Before making the changes to everest-core/modules/EvseManager/EvseManager.cpp to enable MQTT broadcasting, let's better understand a few things, including
|
Effectively, there's two modes for our graph: "Preview", and "Actual". Since we've got the rough preview working, let's see how we'll graph the "Actual" curve, once the optimizer is run on the SASchedule. To re-render the graph, it seems like we receive ChargeParameterDiscoveryResponse (and likewise, the SASchedule) in evcc/states/din_spec_states.py, specifically in ChargeParameterDiscovery(). My first thought is that we will want to utilize EVEREST_CTX's Regardless, it's good to find this chunk of code where we find the SASchedule -- once we add DJ's optimizer, it should slot in roughly here. While tracing these calls, I've been sketching out a rough sequence diagram to highlight the important calls within this process (attached below the cut). |
An aside -- here is a sample of the powercurve graph preview, changing dynamically with the scale and departureTime -- this is with a dummy linear function for now, but switching to a different (quadratic) function should be as straight forward as swapping the function. In testing, I into some limitations -- Node-RED freezes when attempting to chart a large number of points with a large range. If necessary, we can limit the "sample rate" of the visualization, to reduce the number of points being charted (e.g., plotting the value every minute instead of every second may increase the speed of rendering more complex functions) Recording of PowerCurve "dummy demo"Screen.Recording.2024-08-21.at.12.47.38.PM.mov |
We know the answer to the first half, at least. We subscribe to the context publications within JsEvManager's index.js. Let's start by close reading this file to look examples of MQTT publication -- after all, JsEvManager receives the initial commands through an MQTT subscription... |
While we subscribe to MQTT broadcasts in EVManager's car_simulatorImpl.cpp, it appears we do not publish to MQTT as we do in EVSEManager's evse_managerImpl.cpp. Since PowerCurve selection should occur within the EV (context), we'll likely need to add publication to this file. So, follow up question: how will JSEvManager communicate with car_simulatorImpl? Let's investigate... |
This is the |
Out of the myriad of locations where we find PowerDeliveryReq, it seems that we want to add the optimizer to the async process_message function within the PreCharge Class in |
DIN != ISO, it is a precursor to ISO. I would look to see where I had to edit the departure time in my previous set of changes. Maybe it is in the |
In
|
After some discussion with the team, we've opted to go for "B" : Running the python script from Node-RED for the preview. This will leave us with less overhead in the preview (we won't need to wait for the MQTT messages to bounce around), and saves the hassle of re-inventing the wheel. Currently, we've got basic files running on Node-RED -- an example script can be launched from the UI, and an output can be read. The kicker is installing additional Python packages. We need both Numpy and Control, and ideally want to upgrade to Python 3.x (Our Node-RED image is currently on 2.7). From what I understand, we'll need to re-build the docker image, editing the dockerfile to install the correct pip packages (relevant thread here). So, we need to (i) The relevant Dockerfile is here: so, let's make the changes... |
Changes have been made to File Changes# More above...
USER root
RUN apk update && \
apk add --virtual python-dev
USER node-red
# More below... docker-compose.ocpp201.yml# More above...
nodered:
# image: ghcr.io/everest/everest-demo/nodered:${TAG}
build: ./nodered
# More below... |
Is there no existing python install on that container? Did you try |
why not use conda like we use elsewhere? You can download miniconda like we do in e-mission, it can set up python3 and comes with pip |
Not a bad idea -- that seems cleaner than doing the python package management within the Dockerfile (e.g., I've successfully updated Python, still having issues with pip...). Let me try adding miniconda to the container now! |
I tried creating the container using
I can retry with the full multi-tier docker compose but let me see if I can figure it out with this first. |
Idea from: https://stackoverflow.com/a/62555259, ensurepip docs: https://docs.python.org/3/library/ensurepip.html Something as simple as `python3 -m ensurepip` seems to work
The problem with this is that we shouldn't really be installing `numpy` using `pip`, it doesn't have as many precompiled optimizations, which is why we use conda. But it looks like it works, so let's go with it!
|
Had to futz with some of the inputs, but we now can use the UI to change these values! There seems to be little change when the Currently editing the python script to "return" a JSON object, so that the chart can read these values in the correct format! Screen Recording of FunctionalityRuntime.mov |
We're finally plotting the curve!! The output is a bit wonky -- there are some issues plotting the To Reproduce:
python3 -m ensurepip
/usr/src/node-red/.local/bin/pip3 install numpy control
TODO:
Curve VisualizationCurveRuntime.mp4 |
Adding a simple EDIT: It should be noted that this may not necessarily be an issue with
|
It seems the existing build_current_demand_data is only for DC, so if we want to modify the AC charging event, we may need to modify this... First, let's explore the EVCC's simulator.py, specifically get_charge_params_v2 |
To get a hack-y demo working, our current progress has included:
|
Finally got the curve data sent to MQTT!! Had to return the curve data with |
We've got a working demo! 🥳 Below is a recording of the demo -- highlights include:
There are definitely some kinks that we need to iron out, but exciting to see it working. Once I get the relevant code committed, I'll go ahead and post steps to re-produce this demo. FinalCurveScreenCap.mp4 |
With the Node-RED visualiations working, and the powercurve correctly added, the next step is to adjust the simulation process in order to reflect the EVCC's powercurve. I believe the best way to do so is by generating a
One drawback to this approach is the limitation of the Below is a sequence diagram roughly outlining the flow of PowerCurve Generation & Adoption: |
@the-bay-kay what is the timing of these steps? Is there actually potential to wait for the user to select a power curve? |
Good question -- as written, we immediately shoot off the PowerDeliveryReq after receiving the ChargeParameterDsicoveryRequest. Looking at the spec...
Other than these 2 clauses (additionally, V2G2-848 for the "stop" PowerDeliveryReq), it seems there are no restrictions on timings within the spec. I see no reason why we couldn't pause for user input at this point, when we want to add algorithm selection down the road! EDIT: this diagram suggests there should be a 250 second requirement after the ChargeParameterDiscoveryRequest, but I could not verify this in the spec. Correct me if I'm missing something! Likewise, we'd be making the changes after the Resolution, so even if this were the case, I'm not entirely sure it'd apply |
I've official got the new schedule generating, as described above, but am running into a timeout when attempting to send the entire schedule. Below is a copy of the error, and the two messages being sent (the latter causing the error). Currently investigating what may cause, this, will update accordingly... My first thought is to try a shorter schedule, perhaps this is too much text for a single message? EDIT: Yup, that's looking like the issue. Paring down the new schedule to 4 items as a test, we start charging O.K.... So what gives? I guess Table 71 of ISO 15118-2 says we can have a maximum of 24, but maybe EVerest doesn't support that yet... Let's see exactly how many we can fit in before we reach the timeout EDIT 2: Well, when we pass a schedule of length 23, it works fine!! Weird... We can investigate that discrepancy later -- for now, this seems to be working!! I'll include a video and some future steps after this. OLD: Screenshot of Error, JSON messagesScreenshot of TimeoutOriginal Message (no Timeout){
"V2G_Message": {
"Header": {
"SessionID": "11442E78D7FBFFF3"
},
"Body": {
"PowerDeliveryReq": {
"ChargeProgress": "Start",
"SAScheduleTupleID": 1,
"ChargingProfile": {
"ProfileEntry": [
{
"ChargingProfileEntryStart": 0,
"ChargingProfileEntryMaxPower": {
"Value": 15000,
"Multiplier": 0,
"Unit": "W"
}
},
{
"ChargingProfileEntryStart": 8400,
"ChargingProfileEntryMaxPower": {
"Value": 0,
"Multiplier": 0,
"Unit": "W"
}
}
]
}
}
}
}
} New Schedule Msg (Timeout){
"V2G_Message": {
"Header": {
"SessionID": "81D3EF0FB732B27D"
},
"Body": {
"PowerDeliveryReq": {
"ChargeProgress": "Start",
"SAScheduleTupleID": 1,
"ChargingProfile": {
"ProfileEntry": [
{
"ChargingProfileEntryStart": 0,
"ChargingProfileEntryMaxPower": {
"Value": 15000,
"Multiplier": 0,
"Unit": "W"
}
},
{
"ChargingProfileEntryStart": 60,
"ChargingProfileEntryMaxPower": {
"Value": 56,
"Multiplier": 0,
"Unit": "W"
}
},
{
"ChargingProfileEntryStart": 120,
"ChargingProfileEntryMaxPower": {
"Value": 53,
"Multiplier": 0,
"Unit": "W"
}
},
{
"ChargingProfileEntryStart": 181,
"ChargingProfileEntryMaxPower": {
"Value": 50,
"Multiplier": 0,
"Unit": "W"
}
},
{
"ChargingProfileEntryStart": 241,
"ChargingProfileEntryMaxPower": {
"Value": 47,
"Multiplier": 0,
"Unit": "W"
}
},
{
"ChargingProfileEntryStart": 302,
"ChargingProfileEntryMaxPower": {
"Value": 44,
"Multiplier": 0,
"Unit": "W"
}
},
{
"ChargingProfileEntryStart": 362,
"ChargingProfileEntryMaxPower": {
"Value": 41,
"Multiplier": 0,
"Unit": "W"
}
},
{
"ChargingProfileEntryStart": 423,
"ChargingProfileEntryMaxPower": {
"Value": 39,
"Multiplier": 0,
"Unit": "W"
}
},
{
"ChargingProfileEntryStart": 483,
"ChargingProfileEntryMaxPower": {
"Value": 36,
"Multiplier": 0,
"Unit": "W"
}
},
{
"ChargingProfileEntryStart": 543,
"ChargingProfileEntryMaxPower": {
"Value": 34,
"Multiplier": 0,
"Unit": "W"
}
},
{
"ChargingProfileEntryStart": 604,
"ChargingProfileEntryMaxPower": {
"Value": 32,
"Multiplier": 0,
"Unit": "W"
}
},
{
"ChargingProfileEntryStart": 664,
"ChargingProfileEntryMaxPower": {
"Value": 30,
"Multiplier": 0,
"Unit": "W"
}
},
{
"ChargingProfileEntryStart": 725,
"ChargingProfileEntryMaxPower": {
"Value": 29,
"Multiplier": 0,
"Unit": "W"
}
},
{
"ChargingProfileEntryStart": 785,
"ChargingProfileEntryMaxPower": {
"Value": 27,
"Multiplier": 0,
"Unit": "W"
}
},
{
"ChargingProfileEntryStart": 846,
"ChargingProfileEntryMaxPower": {
"Value": 25,
"Multiplier": 0,
"Unit": "W"
}
},
{
"ChargingProfileEntryStart": 906,
"ChargingProfileEntryMaxPower": {
"Value": 24,
"Multiplier": 0,
"Unit": "W"
}
},
{
"ChargingProfileEntryStart": 966,
"ChargingProfileEntryMaxPower": {
"Value": 22,
"Multiplier": 0,
"Unit": "W"
}
},
{
"ChargingProfileEntryStart": 1027,
"ChargingProfileEntryMaxPower": {
"Value": 21,
"Multiplier": 0,
"Unit": "W"
}
},
{
"ChargingProfileEntryStart": 1087,
"ChargingProfileEntryMaxPower": {
"Value": 20,
"Multiplier": 0,
"Unit": "W"
}
},
{
"ChargingProfileEntryStart": 1148,
"ChargingProfileEntryMaxPower": {
"Value": 19,
"Multiplier": 0,
"Unit": "W"
}
},
{
"ChargingProfileEntryStart": 1208,
"ChargingProfileEntryMaxPower": {
"Value": 17,
"Multiplier": 0,
"Unit": "W"
}
},
{
"ChargingProfileEntryStart": 1269,
"ChargingProfileEntryMaxPower": {
"Value": 16,
"Multiplier": 0,
"Unit": "W"
}
},
{
"ChargingProfileEntryStart": 1329,
"ChargingProfileEntryMaxPower": {
"Value": 15,
"Multiplier": 0,
"Unit": "W"
}
},
{
"ChargingProfileEntryStart": 8400,
"ChargingProfileEntryMaxPower": {
"Value": 0,
"Multiplier": 0,
"Unit": "W"
}
}
]
}
}
}
}
} |
Good news -- @shankari , like we expected, we're able to interrupt the charging initialization during MqttStartCharging.mp4 |
Looking for our options to dynamically display a choice to the user, this appears to be non-trivial. At first I thought nodered-dashboard's ui-notificaiton would be our answer, but this does not allow us to display selections. EDIT: Yup, I was misunderstanding -- it appears RED.notify is used for the implementation of Node-RED modules. If we were designing our own modules, this may be helpful. Unfortunately, we need something a little more "out of the box"... Much to my chagrin, I think the best option (to get this working, at least) is to have a |
Done! I need to fix the curve preview again, but that should be straight forward. Good news is, the mid-charge selection process is now working! Below is a video of this in action -- for the sake of our code and my sanity, I'll wrap up tomorrow after some sleep 😀 Mid-Charge.Algorithm.Selection.mp4 |
We've got a functioning demo! Below is a video of the demo being run: the user may select from one of two "preset curves" (a stand in for our algorithm selection), and then the charge session will occur accordingly. Users are able to preview curves during the While exciting, this isn't necessarily a "camera-ready" demo -- I still want to fix the notification process, for example. After showing this off to the team tomorrow, I'll create a series of clean-up tasks for this project. FullDemo.mov |
Tasks for Demo CleanupNow that we've got a 90% working demo, there are several steps we need to take in order to make this production worthy / "showroom" worthy. Below is a list of the changes that need to take place in order to finish this current iteration (roughly in order):
Future WorkThere are some other ways in which we can improve this functionality. Because these projects have a much larger scale (implementing entirely new portions of ISO15118), I've separated them out into their own list.
|
Facing a bizarre issue trying to implement this. Below is a video: TL;DR, the MQTT exchange I've set up works perfect the first time the Node-RED UI is loaded, and then fails subsequent charging sessions. I've spent a few hours trying fixes -- this issue has never occurred with the algorithm selection process, which made me think it was a timing issue. Delaying the messages to avoid race conditions (e.g., node-red publication prior to simulator subscription), re-arranging messages, and none of it seems to work.... will update accordingly once I find the cause. MQTT exchange issueMQTT.Error.mov |
We've made the change to selecting our algorithm prior to the charging session (as opposed to during the PowerDeliveryReq handshake). EDIT: It seems my intuition was correct, but my method of execution was wrong. Adding a Node-Red "Delay" node fixed the issue, no need to futz with I've included a timing chart with my current theory as to the cause of this issue. Because of the behavior when re-publishing the message, I believe the issue may be that we subscribe to the Timing Sequence & Screen RecordingBelow is a video of the behavior occurring. First, we "stall out" on waiting for the publication of Message.Timings.1.mp4 |
I've cleaned up and uploaded the docker images to my personal fork -- once I figure out someissues with GPG I'm having, I'll push the updated scripts / demo-repo files. Once that is done, we'll close this issue and move our work to #71 ! |
@shankari , this demo should be ready for presentation. To reproduce, you should be able to spin up my branch.... git clone https://github.com/the-bay-kay/everest-demo.git; cd everest-demo; git checkout powercurve-addition; bash demo-iso15118-2-ac-plus-ocpp.sh -r $(pwd) -b test-demo -3 ... and start the manager as usual with *There is a slim chance the manager may need to be re-started after a fresh clone (charging will stall on the initialization sequence)... I haven't been able to reproduce this in 3-4 attempts, but am investigating further. Will update this comment and push the patch once I've got the fix nailed down! |
It seems that we stall out when we process the "ChargeParameterDiscoveryRequest" (PrepareCharging)... It appears to be an issue with how we initialize EAmount or DepartureTime within Node-RED. That is, on a fresh clone, we stall until we set the value. The demo works otherwise (e.g., a fresh clone where you punch in the numbers should work OK). Let me confirm this theory and I'll push an updated image! EDIT: Changes pushed -- while doing so, I fixed the formatting for the powercurve visualizations. Doing another pass to make sure the numbers are OK and things perform as expected |
@shankari I've pushed the changes that fix the PowerDeliveryRequest -- upon a fresh clone (e.g., deleting old containers & images), it is working as expected for me. LMK if you cannot replicate this on your end! Screen.Recording.2024-10-01.at.2.19.12.PM.mov |
Just confirming that, after deleting all instances and all images, I am now able to see values for the curve. |
Glad it's working on your end! I just tested Car Control + Grid Control, and everything looks A-OK there! I did notice what I bevel is a regression in the "no DepartureTime" behavior (grid only) -- When setting the 4Hour 10A grid clamp, we're receiving a PMax of 2500W, not the expected 6900W. I think this is because there is a default DT of 86400 somewhere -- once that's patched, I'll push the image and close this issue. EDIT: Yup, just set the default when I was rewriting the Node-RED flows. Closing once the changes are pushed |
Context
This PR builds off @shankari and I's work in Issue #64 . This work will be done on the functional DepartureTime demos.
High Level Goal:
... to present the user with multiple charging profiles, which they then can select and use for their current charging session
Subsequent Goals:
Edit: Updated timeline to reflect 2-week sprint goals. Multiple SASchedules is a stretch goal, implementation of power curve selection is higher priority.
EDIT 2: Changed goals to reflect implementation adjustments.
The text was updated successfully, but these errors were encountered: