Disclaimer: I know little of LabVIEW, I use C++ with the rest of 2200.
So I’ve got rectangle identification working quite smoothly in C++ but I’d like to see some target info on the LabVIEW Dashboard. Right now I’m just using the smart dashboard to print coordinates of the target.
The 2010 Vision Demonstration project in WindRiver contains code to send an arbitrarily sized array of vision targets to the dashboard using the high priority dashboard packer. I want to duplicate this for rectangles.
I know the following:
- Create a cRio Robot project in LabVIEW and make the Dashboard Datatype
- Send data in C++ (code below)
- Use the IMAQ Overlay Rectangle VI in a for loop (or a loop to iterate the results)
I’ve stripped down the dashboard to what I want and looked through some of the documentation in LabVIEW, but I’m missing a few concepts:
- How can I make an arbitrarily sized array of clusters (matching the below C++ code)?
- How can I loop through this in the Dashboard project to add multiple overlays?
void RectangleTracker::SendVisionData() {
Dashboard &d = DriverStation::GetInstance()->GetHighPriorityDashboardPacker();
int n = matches.size();
n = (n <= 10) ? n : 10;
d.AddCluster(); {
for (int i = 0; i < n; i++) {
d.AddCluster(); {
d.AddI32((INT32)matches*.corner[0].y);
d.AddI32((INT32)matches*.corner[0].x);
d.AddI32((INT32)matches*.corner[2].y);
d.AddI32((INT32)matches*.corner[2].x);
}
d.FinalizeCluster();
}
}
d.FinalizeCluster();
d.Finalize();
}
Thanks CD LabVIEWers. I’ll continue to do my Google-ing.****
My C++ skills are very rusty. Do you mind posting whatever you have attempted in LabVIEW so far? Could you post sample data from the string output of the high priority packet.
From what I read in this PDF (I have no idea if this PDF is even remotely on target.) I think I understand your structure. Essentially, you have to create a type-definition in LabVIEW to match the C++ code. (A custom control, with the typedef attribute set.) I believe that you have created a cluster with an array of clusters of box coordinates. If you do not wish to put anything else in that packet, the wrapping cluster is probably not needed.
I think below is what you want. The image is a snippet of the dashboard VI. The zip file, contains the typedef’d control. Apparently, CD doesn’t allow uploading of ctl files.
Dashboard TypeDef for basicxman.zip (4.13 KB)
Disclaimer: I am very rusty in C++, and don’t have any sample data to test this.
Dashboard TypeDef for basicxman.zip (4.13 KB)
Dashboard TypeDef for basicxman.zip (4.13 KB)
Laptop battery is about to die so I’ll post what I have so far tomorrow (I think what I have will be successful). But I found this:
http://code.google.com/p/team178first/source/browse/trunk/2010DashboardProject/Dashboard+Main.vi
I had to checkout the whole dashboard project, I ended up with a few cross-linked files when trying to open it in a standard project. After opening your whole Dashboard project I have noticed a few things.
-
LabVIEW version:
It appears that you may be using an older version of the FRC project/LabVIEW. I believe some of the FRC data structures have changed, and I don’t think this will work with the latest driver station.
-I was also missing a few library VIs when I attempted to open your project. I do not have FRC 2010 installed on my VM.
-
CommunicationMonitor.vi:
This VI has some program flow issues. The local variables will never update the output the way I think you are thinking of.
-
General Programming techniques:
The above VI is not the only part that has this issue. It seems that you are coding like a C programmer. (Expected, and I have seen this many times.) Unfortunately, this does not work well with LabVIEW’s data-flow oriented model. You have made fair use of local variables, something that I would suggest for you to avoid. The local/global variable name seems to mislead programmers when they first switch to LabVIEW. These are not for linear programming, but more for initialization, and thread messaging. (There are better ways to message between threads.)
-
Receive DS Packet.vi:
It appears that you have modified this VI. Even-though it is not in a library folder, I would still treat it like a library function. Modifying it, especially the connector pane, makes it more difficult for others to view, analyses, and edit surrounding code. All of the required data is passed out, and is available outside this VI.
-
DSPacketHighPriority.ctl:
Unless you have already changed your packet structure, this appears not to match the C++ code you provided.
-The unflatten string function is wired in a way where any errors will not be recorded if the low-priority packets are successfully decoded.
-The use of a local variable here is OK. I would use a different technique, but the world won’t end for using a local variable this way.
-
Railed CPU threads:
A few of the loops do not have any wait calls in them. The main receive loop is probably OK because of the network call will wait. The fault handling loop however will use 100% of a core. This consumption of resources will make the other loops sluggish as well.
-
Wire neatness:
This is more cosmetic, but highly important. There is a coding conventions page in the LabVIEW help. (F1 when using LabVIEW.) It specifies many conventions that LV programmers should follow. One is that wires should flow from left to right, and not loop back on them selves. Don’t let them hide behind other objects, and use as few bends as possible. This makes it much more readable. (By light years. This is even required in professional environments.)
I haven’t looked through that project much, it’s another teams I found on Google.
Generally in LabVIEW, data “flows” on wires. Data on all inputs to a program block must be available before the block can execute. Data on a wire is not available until the source function block has completed execution. A function block is any structure, native function, or subVI. This means that your vision loop would not execute until the receive data loop completed. (Meaning that this is serial, and not parallel) The value that the vision loop would use, would be the very last value that was received before the receive loop exited. One solution is to use a local variable. (I prefer something called “functional globals”/“LV2 style globals”, but they are a bit more advanced. There many articles about them.)
I have added a local variable below. I wouldn’t recommend hiding the control that it uses. (Poor style.) If you do, we can look at a different solution.
Dashboard Main_basicxman.vi (80.8 KB)
Dashboard Main_basicxman.vi (80.8 KB)
Dashboard Main_basicxman.vi (80.8 KB)
This builds and makes a lot more sense to me - shall test tomorrow, thanks!
Ran it, getting an image now. However the Unflatten from String VI is giving an error code - 116.
According to the manual:
Unflatten or byte stream read operation failed due to corrupt, unexpected, or truncated data.
EDIT: Not quite sure how to debug this one, really down to trial and error/Google.
EDIT 2: I forgot to use pack an array:
void RectangleTracker::SendVisionData() {
Dashboard &d = DriverStation::GetInstance()->GetHighPriorityDashboardPacker();
int n = (numCurrentMatches <= 10) ? numCurrentMatches : 10;
d.AddCluster(); {
d.AddArray(); {
for (int i = 0; i < n; i++) {
d.AddCluster(); {
d.AddI16((INT16)matches*.corner[0].y);
d.AddI16((INT16)matches*.corner[0].x);
d.AddI16((INT16)matches*.corner[2].y);
d.AddI16((INT16)matches*.corner[2].x);
}
d.FinalizeCluster();
}
}
d.FinalizeArray();
}
d.FinalizeCluster();
d.Finalize();
}